summaryrefslogtreecommitdiffstats
path: root/trunk/etherpad/src/static
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/etherpad/src/static')
-rw-r--r--trunk/etherpad/src/static/crossdomain.xml12
-rw-r--r--trunk/etherpad/src/static/css/admin/admin-stats.css183
-rw-r--r--trunk/etherpad/src/static/css/beta.css49
-rw-r--r--trunk/etherpad/src/static/css/broadcast.css386
-rw-r--r--trunk/etherpad/src/static/css/connection_diagnostics.css13
-rw-r--r--trunk/etherpad/src/static/css/etherpad.css770
-rw-r--r--trunk/etherpad/src/static/css/fluxbb.css55
-rw-r--r--trunk/etherpad/src/static/css/framedpage.css175
-rw-r--r--trunk/etherpad/src/static/css/global-pro-account.css52
-rw-r--r--trunk/etherpad/src/static/css/home-opensource.css44
-rw-r--r--trunk/etherpad/src/static/css/home.css264
-rw-r--r--trunk/etherpad/src/static/css/lib/jquery.contextmenu.css244
-rw-r--r--trunk/etherpad/src/static/css/pad.css1000
-rw-r--r--trunk/etherpad/src/static/css/pad2_ejs.css889
-rw-r--r--trunk/etherpad/src/static/css/pne-manual.css143
-rw-r--r--trunk/etherpad/src/static/css/pricing.css153
-rw-r--r--trunk/etherpad/src/static/css/pro-signup.css69
-rw-r--r--trunk/etherpad/src/static/css/pro/account.css254
-rw-r--r--trunk/etherpad/src/static/css/pro/framedpage-pro.css125
-rw-r--r--trunk/etherpad/src/static/css/pro/padlist.css115
-rw-r--r--trunk/etherpad/src/static/css/pro/payment-required.css39
-rw-r--r--trunk/etherpad/src/static/css/pro/pro-admin.css343
-rw-r--r--trunk/etherpad/src/static/css/pro/pro-home.css65
-rw-r--r--trunk/etherpad/src/static/css/stats.css71
-rw-r--r--trunk/etherpad/src/static/css/store/eepnet-checkout.css284
-rw-r--r--trunk/etherpad/src/static/css/store/ondemand-billing.css170
-rw-r--r--trunk/etherpad/src/static/css/store/store.css90
-rw-r--r--trunk/etherpad/src/static/favicon.icobin0 -> 1354 bytes
-rw-r--r--trunk/etherpad/src/static/img/about/appjet-logo-large.gifbin0 -> 11045 bytes
-rw-r--r--trunk/etherpad/src/static/img/about/appjet-logo-medium.pngbin0 -> 4127 bytes
-rw-r--r--trunk/etherpad/src/static/img/about/investors/mitchkapor.jpgbin0 -> 30223 bytes
-rw-r--r--trunk/etherpad/src/static/img/about/investors/pb.jpgbin0 -> 23929 bytes
-rw-r--r--trunk/etherpad/src/static/img/about/investors/pg.jpgbin0 -> 28915 bytes
-rw-r--r--trunk/etherpad/src/static/img/about/investors/sanjeev.jpgbin0 -> 23342 bytes
-rw-r--r--trunk/etherpad/src/static/img/about/investors/seth.jpgbin0 -> 27346 bytes
-rw-r--r--trunk/etherpad/src/static/img/about/people/aaron-david-iphones-thumb.jpgbin0 -> 145654 bytes
-rw-r--r--trunk/etherpad/src/static/img/about/people/aaron-david-iphones.jpgbin0 -> 145654 bytes
-rw-r--r--trunk/etherpad/src/static/img/about/people/aaron-google-air.jpgbin0 -> 163592 bytes
-rw-r--r--trunk/etherpad/src/static/img/about/people/aaron-headshot-thumb.jpgbin0 -> 11310 bytes
-rw-r--r--trunk/etherpad/src/static/img/about/people/aaron-headshot.jpgbin0 -> 83517 bytes
-rw-r--r--trunk/etherpad/src/static/img/about/people/aaron-headshot2-thumb.jpgbin0 -> 29884 bytes
-rw-r--r--trunk/etherpad/src/static/img/about/people/aaron-headshot2.jpgbin0 -> 99738 bytes
-rw-r--r--trunk/etherpad/src/static/img/about/people/aaron-headshot3-thumb.jpgbin0 -> 27864 bytes
-rw-r--r--trunk/etherpad/src/static/img/about/people/aaron-headshot3.jpgbin0 -> 195114 bytes
-rw-r--r--trunk/etherpad/src/static/img/about/people/daniel-headshot-thumb.jpgbin0 -> 21991 bytes
-rw-r--r--trunk/etherpad/src/static/img/about/people/david-headshot-thumb.jpgbin0 -> 18650 bytes
-rw-r--r--trunk/etherpad/src/static/img/about/people/david-headshot.jpgbin0 -> 63296 bytes
-rw-r--r--trunk/etherpad/src/static/img/about/people/davy-headshot.jpgbin0 -> 20175 bytes
-rw-r--r--trunk/etherpad/src/static/img/about/people/jd-headshot-thumb.jpgbin0 -> 13008 bytes
-rw-r--r--trunk/etherpad/src/static/img/about/people/jd-headshot.jpgbin0 -> 37777 bytes
-rw-r--r--trunk/etherpad/src/static/img/about/people/rhonda-headshot-thumb.jpgbin0 -> 17360 bytes
-rw-r--r--trunk/etherpad/src/static/img/about/people/rhonda-headshot.jpgbin0 -> 133259 bytes
-rw-r--r--trunk/etherpad/src/static/img/about/pier38.pngbin0 -> 66877 bytes
-rw-r--r--trunk/etherpad/src/static/img/about/quote-close.pngbin0 -> 1361 bytes
-rw-r--r--trunk/etherpad/src/static/img/about/quote-open.pngbin0 -> 1341 bytes
-rw-r--r--trunk/etherpad/src/static/img/about/screencastpreview800x600.jpgbin0 -> 248364 bytes
-rw-r--r--trunk/etherpad/src/static/img/account/betawarn.jpgbin0 -> 13535 bytes
-rw-r--r--trunk/etherpad/src/static/img/acecarets/000000.gifbin0 -> 41 bytes
-rw-r--r--trunk/etherpad/src/static/img/acecarets/666666.gifbin0 -> 41 bytes
-rw-r--r--trunk/etherpad/src/static/img/acecarets/999999.gifbin0 -> 41 bytes
-rw-r--r--trunk/etherpad/src/static/img/acecarets/default.gifbin0 -> 43 bytes
-rw-r--r--trunk/etherpad/src/static/img/apr09/backgrad.pngbin0 -> 2276 bytes
-rw-r--r--trunk/etherpad/src/static/img/apr09/black35.pngbin0 -> 221 bytes
-rw-r--r--trunk/etherpad/src/static/img/apr09/blank.gifbin0 -> 129 bytes
-rw-r--r--trunk/etherpad/src/static/img/apr09/modalbar.gifbin0 -> 145 bytes
-rw-r--r--trunk/etherpad/src/static/img/apr09/newpadicon.gifbin0 -> 89 bytes
-rw-r--r--trunk/etherpad/src/static/img/apr09/shadbot.pngbin0 -> 149 bytes
-rw-r--r--trunk/etherpad/src/static/img/apr09/shadleft.pngbin0 -> 142 bytes
-rw-r--r--trunk/etherpad/src/static/img/apr09/shadleftbot.pngbin0 -> 172 bytes
-rw-r--r--trunk/etherpad/src/static/img/apr09/shadlefttop.pngbin0 -> 929 bytes
-rw-r--r--trunk/etherpad/src/static/img/apr09/shadright.pngbin0 -> 136 bytes
-rw-r--r--trunk/etherpad/src/static/img/apr09/shadrightbot.pngbin0 -> 174 bytes
-rw-r--r--trunk/etherpad/src/static/img/apr09/shadrighttop.pngbin0 -> 954 bytes
-rw-r--r--trunk/etherpad/src/static/img/apr09/topbar.gifbin0 -> 180 bytes
-rw-r--r--trunk/etherpad/src/static/img/apr09/topbarlogo.gifbin0 -> 1784 bytes
-rw-r--r--trunk/etherpad/src/static/img/apr09/widthfull.gifbin0 -> 104 bytes
-rw-r--r--trunk/etherpad/src/static/img/apr09/widthfullactive.gifbin0 -> 104 bytes
-rw-r--r--trunk/etherpad/src/static/img/apr09/widthlim.gifbin0 -> 102 bytes
-rw-r--r--trunk/etherpad/src/static/img/apr09/widthlimactive.gifbin0 -> 102 bytes
-rw-r--r--trunk/etherpad/src/static/img/billing/amex.gifbin0 -> 995 bytes
-rw-r--r--trunk/etherpad/src/static/img/billing/creditcard.gifbin0 -> 1229 bytes
-rw-r--r--trunk/etherpad/src/static/img/billing/csc-help.gifbin0 -> 9430 bytes
-rw-r--r--trunk/etherpad/src/static/img/billing/disc.gifbin0 -> 370 bytes
-rw-r--r--trunk/etherpad/src/static/img/billing/invoice.gifbin0 -> 424 bytes
-rw-r--r--trunk/etherpad/src/static/img/billing/mc.gifbin0 -> 1370 bytes
-rw-r--r--trunk/etherpad/src/static/img/billing/paypal.gifbin0 -> 812 bytes
-rw-r--r--trunk/etherpad/src/static/img/billing/visa.gifbin0 -> 724 bytes
-rw-r--r--trunk/etherpad/src/static/img/blog/posts/new-features/fullwidth.gifbin0 -> 7328 bytes
-rw-r--r--trunk/etherpad/src/static/img/blog/posts/new-features/importexport.gifbin0 -> 9758 bytes
-rw-r--r--trunk/etherpad/src/static/img/blog/posts/new-features/richtext.gifbin0 -> 2146 bytes
-rw-r--r--trunk/etherpad/src/static/img/blog/posts/new-features/viewzoom.gifbin0 -> 8257 bytes
-rw-r--r--trunk/etherpad/src/static/img/blog/posts/pricing-survey-results.pngbin0 -> 12994 bytes
-rw-r--r--trunk/etherpad/src/static/img/blog/posts/pricing-survey.pngbin0 -> 10589 bytes
-rw-r--r--trunk/etherpad/src/static/img/blog/posts/time-slider-screenshot.gifbin0 -> 6544 bytes
-rw-r--r--trunk/etherpad/src/static/img/davy/bg/home-createpad.pngbin0 -> 4327 bytes
-rw-r--r--trunk/etherpad/src/static/img/davy/bg/home-features-bottom.gifbin0 -> 308 bytes
-rw-r--r--trunk/etherpad/src/static/img/davy/bg/home-features-free-bottom.gifbin0 -> 298 bytes
-rw-r--r--trunk/etherpad/src/static/img/davy/bg/home-features-paid-top.gifbin0 -> 459 bytes
-rw-r--r--trunk/etherpad/src/static/img/davy/bg/home-features-top.gifbin0 -> 489 bytes
-rw-r--r--trunk/etherpad/src/static/img/davy/bg/home-nav-selected.pngbin0 -> 195 bytes
-rw-r--r--trunk/etherpad/src/static/img/davy/bg/home-screencast.pngbin0 -> 5738 bytes
-rw-r--r--trunk/etherpad/src/static/img/davy/bg/home2.pngbin0 -> 367 bytes
-rw-r--r--trunk/etherpad/src/static/img/davy/bg/product-nav-selected-white.pngbin0 -> 196 bytes
-rw-r--r--trunk/etherpad/src/static/img/davy/bg/product-nav-selected.pngbin0 -> 198 bytes
-rw-r--r--trunk/etherpad/src/static/img/davy/bg/product.pngbin0 -> 161 bytes
-rw-r--r--trunk/etherpad/src/static/img/davy/btn/createpad-home.gifbin0 -> 5839 bytes
-rw-r--r--trunk/etherpad/src/static/img/davy/btn/createpad-large.gifbin0 -> 6614 bytes
-rw-r--r--trunk/etherpad/src/static/img/davy/btn/createpad-small.gifbin0 -> 3148 bytes
-rw-r--r--trunk/etherpad/src/static/img/davy/btn/intro-screencast.pngbin0 -> 815 bytes
-rw-r--r--trunk/etherpad/src/static/img/davy/btn/intro-testimonials.pngbin0 -> 816 bytes
-rw-r--r--trunk/etherpad/src/static/img/davy/btn/learnmore.gifbin0 -> 3103 bytes
-rw-r--r--trunk/etherpad/src/static/img/davy/btn/signup-home-2.gifbin0 -> 5827 bytes
-rw-r--r--trunk/etherpad/src/static/img/davy/btn/signup-home-3.gifbin0 -> 5735 bytes
-rw-r--r--trunk/etherpad/src/static/img/davy/btn/signup-home-4.gifbin0 -> 5818 bytes
-rw-r--r--trunk/etherpad/src/static/img/davy/btn/signup-home.gifbin0 -> 5815 bytes
-rw-r--r--trunk/etherpad/src/static/img/davy/btn/uses-more.gifbin0 -> 1485 bytes
-rw-r--r--trunk/etherpad/src/static/img/davy/gfx/32/114.pngbin0 -> 2424 bytes
-rw-r--r--trunk/etherpad/src/static/img/davy/gfx/32/15.pngbin0 -> 2904 bytes
-rw-r--r--trunk/etherpad/src/static/img/davy/gfx/32/65.pngbin0 -> 3028 bytes
-rw-r--r--trunk/etherpad/src/static/img/davy/gfx/32/78.pngbin0 -> 2208 bytes
-rw-r--r--trunk/etherpad/src/static/img/davy/gfx/bullet.gifbin0 -> 55 bytes
-rw-r--r--trunk/etherpad/src/static/img/davy/gfx/home-logo2.gifbin0 -> 7136 bytes
-rw-r--r--trunk/etherpad/src/static/img/davy/gfx/home-screencast.pngbin0 -> 65832 bytes
-rw-r--r--trunk/etherpad/src/static/img/davy/gfx/plane.gifbin0 -> 56 bytes
-rw-r--r--trunk/etherpad/src/static/img/davy/gfx/product-logo.gifbin0 -> 2222 bytes
-rw-r--r--trunk/etherpad/src/static/img/davy/gfx/screenshot.gifbin0 -> 23708 bytes
-rw-r--r--trunk/etherpad/src/static/img/davy/gfx/use-meetings.gifbin0 -> 10521 bytes
-rw-r--r--trunk/etherpad/src/static/img/davy/gfx/use-meetings.pngbin0 -> 25506 bytes
-rw-r--r--trunk/etherpad/src/static/img/davy/gfx/use-programming.gifbin0 -> 13655 bytes
-rw-r--r--trunk/etherpad/src/static/img/davy/gfx/use-programming.pngbin0 -> 56154 bytes
-rw-r--r--trunk/etherpad/src/static/img/davy/gfx/use-writing.gifbin0 -> 22275 bytes
-rw-r--r--trunk/etherpad/src/static/img/davy/gfx/use-writing.pngbin0 -> 38264 bytes
-rw-r--r--trunk/etherpad/src/static/img/davy/txt/home-button.gifbin0 -> 4749 bytes
-rw-r--r--trunk/etherpad/src/static/img/featuretour/code.gifbin0 -> 27794 bytes
-rw-r--r--trunk/etherpad/src/static/img/featuretour/edits.gifbin0 -> 24788 bytes
-rw-r--r--trunk/etherpad/src/static/img/featuretour/editsandusers.gifbin0 -> 21815 bytes
-rw-r--r--trunk/etherpad/src/static/img/featuretour/padlock.pngbin0 -> 13061 bytes
-rw-r--r--trunk/etherpad/src/static/img/featuretour/revisions.gifbin0 -> 20820 bytes
-rw-r--r--trunk/etherpad/src/static/img/featuretour/users.gifbin0 -> 8843 bytes
-rw-r--r--trunk/etherpad/src/static/img/feb09/framedheaderback.gifbin0 -> 606 bytes
-rw-r--r--trunk/etherpad/src/static/img/feb09/framedheaderlogo.gifbin0 -> 8177 bytes
-rw-r--r--trunk/etherpad/src/static/img/feb09/home_firstp.gifbin0 -> 7754 bytes
-rw-r--r--trunk/etherpad/src/static/img/feb09/home_firstp.pngbin0 -> 15320 bytes
-rw-r--r--trunk/etherpad/src/static/img/feb09/home_firstp2.gifbin0 -> 7919 bytes
-rw-r--r--trunk/etherpad/src/static/img/feb09/home_h1.gifbin0 -> 11332 bytes
-rw-r--r--trunk/etherpad/src/static/img/feb09/home_h1.pngbin0 -> 28857 bytes
-rw-r--r--trunk/etherpad/src/static/img/feb09/home_newpadbutton.gifbin0 -> 6828 bytes
-rw-r--r--trunk/etherpad/src/static/img/feb09/home_newpadbutton.pngbin0 -> 10554 bytes
-rw-r--r--trunk/etherpad/src/static/img/feb09/home_newpadbutton2.gifbin0 -> 7112 bytes
-rw-r--r--trunk/etherpad/src/static/img/feb09/home_newpadbutton_eepnet.gifbin0 -> 5442 bytes
-rw-r--r--trunk/etherpad/src/static/img/feb09/hometop_back.gifbin0 -> 2743 bytes
-rw-r--r--trunk/etherpad/src/static/img/feb09/nav1.gifbin0 -> 10901 bytes
-rw-r--r--trunk/etherpad/src/static/img/feb09/nav1_back.gifbin0 -> 150 bytes
-rw-r--r--trunk/etherpad/src/static/img/feb09/nav2.gifbin0 -> 17028 bytes
-rw-r--r--trunk/etherpad/src/static/img/feb09/screencast.gifbin0 -> 20091 bytes
-rw-r--r--trunk/etherpad/src/static/img/home/etherpad-mainheader1.jpgbin0 -> 48871 bytes
-rw-r--r--trunk/etherpad/src/static/img/home/headergradient.gifbin0 -> 246 bytes
-rw-r--r--trunk/etherpad/src/static/img/home/homeheader1.jpgbin0 -> 33227 bytes
-rw-r--r--trunk/etherpad/src/static/img/home/homeheader2.jpgbin0 -> 33259 bytes
-rw-r--r--trunk/etherpad/src/static/img/home/leftgrad.gifbin0 -> 113 bytes
-rw-r--r--trunk/etherpad/src/static/img/home/pencilpaperback.pngbin0 -> 83487 bytes
-rw-r--r--trunk/etherpad/src/static/img/home/screencapture1.gifbin0 -> 106694 bytes
-rw-r--r--trunk/etherpad/src/static/img/home/underdevicon.gifbin0 -> 98 bytes
-rw-r--r--trunk/etherpad/src/static/img/icon/downarrow.gifbin0 -> 376 bytes
-rw-r--r--trunk/etherpad/src/static/img/icon/feed.gifbin0 -> 1135 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/backgrad.gifbin0 -> 697 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/bottomareagfx.gifbin0 -> 1045 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/colorpicker.gifbin0 -> 1806 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/connectingbar.gifbin0 -> 10819 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/connectionindicator.gifbin0 -> 1185 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/docbarstates.pngbin0 -> 3314 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/docbarstates2.pngbin0 -> 4902 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/docbarstates3.pngbin0 -> 4990 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/docpaneledge.pngbin0 -> 589 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/docpaneledge2.pngbin0 -> 635 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/docpanelmiddle.pngbin0 -> 240 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/docpanelmiddle2.pngbin0 -> 295 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/editbar.gifbin0 -> 4667 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/editbar2.gifbin0 -> 9156 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/editbar3.pngbin0 -> 16869 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/editbar3.xcfbin0 -> 79212 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/editbarback.gifbin0 -> 368 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/feedbackbox2.gifbin0 -> 6262 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/fileicons.gifbin0 -> 1397 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/hdraggie.gifbin0 -> 453 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/inviteshare.gifbin0 -> 511 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/inviteshare2.gifbin0 -> 1836 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/layoutbuttons.gifbin0 -> 3750 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/overlay.pngbin0 -> 141 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/overlay2.pngbin0 -> 149 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/padtop.gifbin0 -> 8055 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/padtop2.gifbin0 -> 6168 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/padtop3.gifbin0 -> 7511 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/padtop4.gifbin0 -> 8192 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/padtop4.pngbin0 -> 17161 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/padtop4.xcfbin0 -> 41184 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/padtop5.pngbin0 -> 18850 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/padtop5.xcfbin0 -> 66525 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/padtopback.gifbin0 -> 553 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/padtopback2.gifbin0 -> 384 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/protop.pngbin0 -> 6768 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/protop.xcfbin0 -> 16565 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/public.gifbin0 -> 1141 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/savedrevarrows.gifbin0 -> 866 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/savedrevsgfx2.gifbin0 -> 1904 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/sharebox2.gifbin0 -> 8836 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/sharebox3.gifbin0 -> 6056 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/sharebox4.gifbin0 -> 5788 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/sharedistri.gifbin0 -> 85 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/syncdone.gifbin0 -> 211 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/syncing.gifbin0 -> 673 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/syncing2.gifbin0 -> 172 bytes
-rw-r--r--trunk/etherpad/src/static/img/jun09/pad/viewbargfx.gifbin0 -> 155 bytes
-rw-r--r--trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-gloss-cyan-menu-item-hover.gifbin0 -> 52 bytes
-rw-r--r--trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-gloss-menu-item-hover.gifbin0 -> 52 bytes
-rw-r--r--trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-gloss-semitransparent-menu-item-hover.pngbin0 -> 2837 bytes
-rw-r--r--trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-human-menu-item-hover.gifbin0 -> 195 bytes
-rw-r--r--trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-osx-menu-item-hover.gifbin0 -> 87 bytes
-rw-r--r--trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-vista-bg.gifbin0 -> 64 bytes
-rw-r--r--trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-vista-menu-item-hover.gifbin0 -> 347 bytes
-rw-r--r--trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-xp-bg.gifbin0 -> 223 bytes
-rw-r--r--trunk/etherpad/src/static/img/may09/bold.gifbin0 -> 70 bytes
-rw-r--r--trunk/etherpad/src/static/img/may09/doc.gifbin0 -> 632 bytes
-rw-r--r--trunk/etherpad/src/static/img/may09/doc.pngbin0 -> 3317 bytes
-rw-r--r--trunk/etherpad/src/static/img/may09/html.gifbin0 -> 1040 bytes
-rw-r--r--trunk/etherpad/src/static/img/may09/html.pngbin0 -> 3468 bytes
-rw-r--r--trunk/etherpad/src/static/img/may09/italic.gifbin0 -> 73 bytes
-rw-r--r--trunk/etherpad/src/static/img/may09/leftarrow.gifbin0 -> 1016 bytes
-rw-r--r--trunk/etherpad/src/static/img/may09/leftarrow2.gifbin0 -> 950 bytes
-rw-r--r--trunk/etherpad/src/static/img/may09/link.gifbin0 -> 622 bytes
-rw-r--r--trunk/etherpad/src/static/img/may09/link.pngbin0 -> 3323 bytes
-rw-r--r--trunk/etherpad/src/static/img/may09/odt.gifbin0 -> 405 bytes
-rw-r--r--trunk/etherpad/src/static/img/may09/odt.pngbin0 -> 3341 bytes
-rw-r--r--trunk/etherpad/src/static/img/may09/padlock.gifbin0 -> 1053 bytes
-rw-r--r--trunk/etherpad/src/static/img/may09/padlockopen.gifbin0 -> 109 bytes
-rw-r--r--trunk/etherpad/src/static/img/may09/passwordlocked.gifbin0 -> 1053 bytes
-rw-r--r--trunk/etherpad/src/static/img/may09/passwordlocked_cropped.gifbin0 -> 114 bytes
-rw-r--r--trunk/etherpad/src/static/img/may09/passwordnone.gifbin0 -> 636 bytes
-rw-r--r--trunk/etherpad/src/static/img/may09/paypal.gifbin0 -> 3794 bytes
-rw-r--r--trunk/etherpad/src/static/img/may09/pdf.gifbin0 -> 398 bytes
-rw-r--r--trunk/etherpad/src/static/img/may09/pdf.pngbin0 -> 3320 bytes
-rw-r--r--trunk/etherpad/src/static/img/may09/redo.gifbin0 -> 78 bytes
-rw-r--r--trunk/etherpad/src/static/img/may09/txt.gifbin0 -> 381 bytes
-rw-r--r--trunk/etherpad/src/static/img/may09/txt.pngbin0 -> 3139 bytes
-rw-r--r--trunk/etherpad/src/static/img/may09/underline.gifbin0 -> 81 bytes
-rw-r--r--trunk/etherpad/src/static/img/may09/undo.gifbin0 -> 79 bytes
-rw-r--r--trunk/etherpad/src/static/img/miniplane.gifbin0 -> 70 bytes
-rw-r--r--trunk/etherpad/src/static/img/misc/diagnostic-links.gifbin0 -> 10132 bytes
-rw-r--r--trunk/etherpad/src/static/img/misc/status-ball.gifbin0 -> 1553 bytes
-rw-r--r--trunk/etherpad/src/static/img/misc/traclogo.gifbin0 -> 5684 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/atlonglast.gifbin0 -> 4901 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/banner1.jpgbin0 -> 19897 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/banner2.jpgbin0 -> 45052 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/banner3.jpgbin0 -> 38726 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/banner4.jpgbin0 -> 39563 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/banner5.gifbin0 -> 24046 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/banner6.gifbin0 -> 23655 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/banner7.gifbin0 -> 24352 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/banner8.gifbin0 -> 24724 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/banner9.gifbin0 -> 24363 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/bannerback5.gifbin0 -> 2957 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/bannerback6.gifbin0 -> 2140 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/bodyback1.gifbin0 -> 488 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/bodyback2.gifbin0 -> 560 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/bodyback3.gifbin0 -> 608 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/bodyback4.gifbin0 -> 964 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/bodyback5.gifbin0 -> 579 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/bodybacktop1.gifbin0 -> 2991 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/computers.gifbin0 -> 27542 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/computers2.gifbin0 -> 27434 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/glossyblue.gifbin0 -> 1521 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/glossyblue2.gifbin0 -> 994 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/glossyblueh.gifbin0 -> 920 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/insetrect.gifbin0 -> 7056 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/minilogo1-05e.gifbin0 -> 2201 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/minilogo1-07f.gifbin0 -> 2252 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/minilogo3.jpgbin0 -> 12805 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/minitopback1.gifbin0 -> 954 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/minitopback2.gifbin0 -> 1598 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/minitopbar1-05e.gifbin0 -> 284 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/minitopbar2-05e.gifbin0 -> 330 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/minitopbar2-07f.gifbin0 -> 330 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/minitopbar3.jpgbin0 -> 12805 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/minitopbar4.gifbin0 -> 2818 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/minitoplogo1.gifbin0 -> 4184 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/minitoplogo2.gifbin0 -> 3255 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/newpadmain.gifbin0 -> 1172 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/newpadmainback.gifbin0 -> 801 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/newpadmainbackh.gifbin0 -> 801 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/pageshot.pngbin0 -> 151570 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/pageshotmini.pngbin0 -> 80505 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/sidehead-gradhilite.gifbin0 -> 288 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/tinytriangle.gifbin0 -> 62 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/topnav1.gifbin0 -> 12521 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/topnav2.gifbin0 -> 11286 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/topnav3.gifbin0 -> 12363 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/topnav4.gifbin0 -> 11803 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/topnav5.gifbin0 -> 11650 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/topnav6.gifbin0 -> 11295 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/topnavback1.gifbin0 -> 1594 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/topnavback2.gifbin0 -> 1299 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/topnavback3.gifbin0 -> 380 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/usecasesnavdown.gifbin0 -> 1388 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/usecasesnavdownh.gifbin0 -> 1337 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/usecasesnavup.gifbin0 -> 1119 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/usecasesnavuph.gifbin0 -> 720 bytes
-rw-r--r--trunk/etherpad/src/static/img/oct/watchscreencast.gifbin0 -> 25840 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/animated-orb-orange-12.gifbin0 -> 2614 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/backgrad.pngbin0 -> 1290 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/backshadow/backshadow-940-20-eee-20.gifbin0 -> 1052 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/backshadow/backshadow-940-20-fff-20.gifbin0 -> 1052 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/backshadow/backshadow-940-20-fff-40.gifbin0 -> 1009 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/backshadow/backshadow-940-20-fff-60.gifbin0 -> 1123 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/backshadow/botshadow-940-20-eee-20.gifbin0 -> 1746 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/etherpad-logo-small-grad.gifbin0 -> 1537 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/etherpad-logo-small.gifbin0 -> 6664 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/etherpad-logo-small2.gifbin0 -> 6646 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/expandy-arrow-down.gifbin0 -> 500 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/expandy-arrow-right.gifbin0 -> 296 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/expandy-arrow6-down-active.gifbin0 -> 57 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/expandy-arrow6-down.gifbin0 -> 57 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/expandy-arrow6-right-active.gifbin0 -> 61 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/expandy-arrow6-right.gifbin0 -> 61 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/header-revgrad.gifbin0 -> 598 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/newpad.gifbin0 -> 251 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/orb-greenred-12.gifbin0 -> 1105 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/padbg1.jpgbin0 -> 120888 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/padbg2.jpgbin0 -> 44119 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/padbg3.jpgbin0 -> 12577 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/padbg4.jpgbin0 -> 12696 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/padbg5.jpgbin0 -> 8158 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/padhead1.jpgbin0 -> 13413 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/padhead2.jpgbin0 -> 14104 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/padhead3.jpgbin0 -> 6750 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/pencil-icon-small-blue.gifbin0 -> 84 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/sidehead-grad.gifbin0 -> 292 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/timeslider/button_depressed.pngbin0 -> 4610 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/timeslider/button_undepressed.pngbin0 -> 4625 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/timeslider/crushed_button_depressed.pngbin0 -> 4134 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/timeslider/crushed_button_undepressed.pngbin0 -> 4166 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/timeslider/crushed_current_location.pngbin0 -> 1009 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/timeslider/crushed_pause.pngbin0 -> 2876 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/timeslider/crushed_play.pngbin0 -> 2946 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/timeslider/crushed_play_button.pngbin0 -> 4305 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/timeslider/crushed_timeslider_mockup.pngbin0 -> 8164 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/timeslider/current_location.gifbin0 -> 1502 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/timeslider/current_location.pngbin0 -> 1100 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/timeslider/pause.gifbin0 -> 3320 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/timeslider/pause.pngbin0 -> 2883 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/timeslider/play.gifbin0 -> 3297 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/timeslider/play.pngbin0 -> 3017 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/timeslider/play_button.pngbin0 -> 4867 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/timeslider/star.gifbin0 -> 3511 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/timeslider/star.pngbin0 -> 3241 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/timeslider/star_selected.pngbin0 -> 3242 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/timeslider/stepper_buttons.pngbin0 -> 4858 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/timeslider/timeslider_background.pngbin0 -> 915 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/timeslider/timeslider_left.pngbin0 -> 1653 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/timeslider/timeslider_mockup.pngbin0 -> 4860 bytes
-rw-r--r--trunk/etherpad/src/static/img/pad/timeslider/timeslider_right.pngbin0 -> 1581 bytes
-rw-r--r--trunk/etherpad/src/static/img/pricing/free.gifbin0 -> 7419 bytes
-rw-r--r--trunk/etherpad/src/static/img/pricing/group.gifbin0 -> 6783 bytes
-rw-r--r--trunk/etherpad/src/static/img/pricing/on-demand.gifbin0 -> 5791 bytes
-rw-r--r--trunk/etherpad/src/static/img/pricing/private-network.gifbin0 -> 4677 bytes
-rw-r--r--trunk/etherpad/src/static/img/pricing/support.gifbin0 -> 2028 bytes
-rw-r--r--trunk/etherpad/src/static/img/pro/billing/cards-button.gifbin0 -> 9524 bytes
-rw-r--r--trunk/etherpad/src/static/img/pro/box/blue-boxtop.gifbin0 -> 523 bytes
-rw-r--r--trunk/etherpad/src/static/img/pro/buttons/bluebutton120.gifbin0 -> 951 bytes
-rw-r--r--trunk/etherpad/src/static/img/pro/header/pro-header-back.gifbin0 -> 213 bytes
-rw-r--r--trunk/etherpad/src/static/img/pro/header/pro-header-logo.pngbin0 -> 5527 bytes
-rw-r--r--trunk/etherpad/src/static/img/pro/header/pro-header-logo.xcfbin0 -> 13274 bytes
-rw-r--r--trunk/etherpad/src/static/img/pro/header/pro-header-plustopnav-back.gifbin0 -> 474 bytes
-rw-r--r--trunk/etherpad/src/static/img/pro/padlist/gear-drop.gifbin0 -> 300 bytes
-rw-r--r--trunk/etherpad/src/static/img/pro/padlist/paper-icon.gifbin0 -> 619 bytes
-rw-r--r--trunk/etherpad/src/static/img/pro/padlist/trash-icon.gifbin0 -> 1080 bytes
-rw-r--r--trunk/etherpad/src/static/img/pro/topnav/pro-topnav-back.gifbin0 -> 137 bytes
-rw-r--r--trunk/etherpad/src/static/img/pro/topnav/pro-topnav-notch.gifbin0 -> 92 bytes
-rw-r--r--trunk/etherpad/src/static/img/tinyplane.gifbin0 -> 59 bytes
-rw-r--r--trunk/etherpad/src/static/img/wavejet.jpgbin0 -> 55379 bytes
-rw-r--r--trunk/etherpad/src/static/js/ace.js29
-rw-r--r--trunk/etherpad/src/static/js/billing.js111
-rw-r--r--trunk/etherpad/src/static/js/billing_shared.js94
-rw-r--r--trunk/etherpad/src/static/js/broadcast.js607
-rw-r--r--trunk/etherpad/src/static/js/broadcast_revisions.js119
-rw-r--r--trunk/etherpad/src/static/js/broadcast_slider.js401
-rw-r--r--trunk/etherpad/src/static/js/collab_client.js628
-rw-r--r--trunk/etherpad/src/static/js/colorutils.js91
-rw-r--r--trunk/etherpad/src/static/js/confirmation.js21
-rw-r--r--trunk/etherpad/src/static/js/connection_diagnostics.js126
-rw-r--r--trunk/etherpad/src/static/js/cssmanager_client.js88
-rw-r--r--trunk/etherpad/src/static/js/domline_client.js210
-rw-r--r--trunk/etherpad/src/static/js/draggable.js60
-rw-r--r--trunk/etherpad/src/static/js/easysync2_client.js1777
-rw-r--r--trunk/etherpad/src/static/js/etherpad.js217
-rwxr-xr-xtrunk/etherpad/src/static/js/jquery-1.2.6.js3549
-rw-r--r--trunk/etherpad/src/static/js/jquery-1.3.2.js4376
-rw-r--r--trunk/etherpad/src/static/js/json2.js498
-rw-r--r--trunk/etherpad/src/static/js/lib/jquery.contextmenu.js284
-rw-r--r--trunk/etherpad/src/static/js/linestylefilter_client.js252
-rw-r--r--trunk/etherpad/src/static/js/pad.js.old1984
-rw-r--r--trunk/etherpad/src/static/js/pad2.js591
-rw-r--r--trunk/etherpad/src/static/js/pad_chat.js295
-rw-r--r--trunk/etherpad/src/static/js/pad_connectionstatus.js63
-rw-r--r--trunk/etherpad/src/static/js/pad_cookie.js101
-rw-r--r--trunk/etherpad/src/static/js/pad_docbar.js347
-rw-r--r--trunk/etherpad/src/static/js/pad_editbar.js107
-rw-r--r--trunk/etherpad/src/static/js/pad_editor.js136
-rw-r--r--trunk/etherpad/src/static/js/pad_impexp.js187
-rw-r--r--trunk/etherpad/src/static/js/pad_modals.js364
-rw-r--r--trunk/etherpad/src/static/js/pad_savedrevs.js408
-rw-r--r--trunk/etherpad/src/static/js/pad_userlist.js604
-rw-r--r--trunk/etherpad/src/static/js/pad_utils.js359
-rw-r--r--trunk/etherpad/src/static/js/pricing.js19
-rw-r--r--trunk/etherpad/src/static/js/pro/guest-knock-client.js53
-rw-r--r--trunk/etherpad/src/static/js/pro/pro-padlist-client.js104
-rw-r--r--trunk/etherpad/src/static/js/pro/signin-client.js27
-rw-r--r--trunk/etherpad/src/static/js/pulse.jquery.js105
-rw-r--r--trunk/etherpad/src/static/js/statpage.js143
-rw-r--r--trunk/etherpad/src/static/js/store.js116
-rw-r--r--trunk/etherpad/src/static/js/swfobject.js24
-rw-r--r--trunk/etherpad/src/static/js/timeslider.js663
-rw-r--r--trunk/etherpad/src/static/js/undo-xpopup.js25
-rwxr-xr-xtrunk/etherpad/src/static/swf/vidplayer.swfbin0 -> 41390 bytes
423 files changed, 26420 insertions, 0 deletions
diff --git a/trunk/etherpad/src/static/crossdomain.xml b/trunk/etherpad/src/static/crossdomain.xml
new file mode 100644
index 0000000..9e76390
--- /dev/null
+++ b/trunk/etherpad/src/static/crossdomain.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<cross-domain-policy xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:noNamespaceSchemaLocation="http://www.adobe.com/xml/schemas/PolicyFile.xsd">
+<site-control permitted-cross-domain-policies="all"/>
+<allow-http-request-headers-from domain="*" headers="*"/>
+<allow-access-from domain="*.pad.spline.de" to-ports="*"/>
+<allow-access-from domain="pad.spline.de" to-ports="*"/>
+<allow-access-from domain="*.pad.spline.inf.fu-berlin.de" to-ports="*"/>
+<allow-access-from domain="pad.spline.inf.fu-berlin.de" to-ports="*"/>
+<allow-access-from domain="*.pad.spline.nomad" to-ports="*"/>
+<allow-access-from domain="pad.spline.nomad" to-ports="*"/>
+</cross-domain-policy>
diff --git a/trunk/etherpad/src/static/css/admin/admin-stats.css b/trunk/etherpad/src/static/css/admin/admin-stats.css
new file mode 100644
index 0000000..94e0d19
--- /dev/null
+++ b/trunk/etherpad/src/static/css/admin/admin-stats.css
@@ -0,0 +1,183 @@
+#backtoadmin {
+ color: #88f;
+ padding: 4px;
+ text-decoration: none;
+}
+
+#topnav {
+ margin-top: .5em;
+ margin-bottom: 1em;
+ font-family: Verdana, sans-serif;
+ font-size: 1.2em;
+}
+
+#topnav ul {
+ padding: 0;
+ margin: 0 0 0 12px;
+}
+
+#topnav ul li {
+ float: left;
+ display: inline;
+}
+#topnav ul li a {
+ display: block;
+ padding: .4em 1em;
+ text-decoration: none;
+ color: blue;
+}
+#topnav ul li.selected a {
+ background: #fff;
+ color: black;
+ border-bottom: 1px solid black;
+}
+
+/* ----- */
+
+/*.statbox {
+ display: box;
+ overflow: hidden;
+ padding-left: 8px;
+}
+*/
+
+.latesttable {
+ border-top: 1px solid #ccc;
+ border-left: 1px solid #ccc;
+}
+
+.latesttable td {
+ border-right: 1px solid #ccc;
+ border-bottom: 1px solid #ccc;
+ padding: 2px 6px;
+}
+
+/*
+.statbox table td span { }
+
+.statbox .stat-title {
+ display: block;
+ font-family: Verdana, sans-serif;
+ font-size: 1.4em;
+ text-decoration: none;
+ border-bottom: 1px solid #bbb;
+ margin-top: 1em;
+}
+
+.statbox .stat-table {
+ float: left;
+ padding: 4px;
+}
+.statbox .stat-graph {
+ float: left;
+}
+*/
+form#statprefs {
+ background: #eee;
+ padding: 1em;
+ border-top: 1px solid #ccc;
+ border-bottom: 1px solid #ccc;
+ margin-top: 1em;
+}
+
+a.viewall {
+ margin-left: 8px;
+}
+
+div.statentry {
+/* width: 800px;*/
+ border: 1px solid #060;
+ background: #afa;
+ margin: 1em;
+}
+
+body {
+ margin: 0;
+ min-width: 800px;
+}
+
+div.warning {
+ background: #ffa;
+ border: 1px solid #630;
+}
+
+div.error {
+ background: #faa;
+ border: 1px solid #600;
+}
+
+.statentry h2 {
+ font-size: 13pt;
+ font-family: sans-serif;
+ background: #0a0;
+ color: white;
+ padding: 5px;
+ margin: 0;
+ cursor: pointer;
+}
+
+.statentry h3 {
+ font-size: 13pt;
+ font-family: sans-serif;
+ font-weight: normal;
+ margin: 3px;
+ padding: 0;
+}
+
+.statentry h4 {
+ font-size: 12pt;
+ font-weight: bold;
+ margin: 3px;
+}
+
+.statentry h2:hover {
+ text-decoration: underline;
+}
+
+.warning h2 {
+ background: #ea0;
+}
+
+.error h2 {
+ background: #a00;
+}
+
+.statentry table {
+ width: 100%;
+}
+
+.statentry .graph {
+ background: white;
+ padding: 2px;
+ width: 600px;
+}
+
+.graph .datalinks {
+ margin-top: 10px;
+ font-size: .8em;
+ text-align: right;
+ color: gray;
+}
+
+.graph .datalinks a {
+ color: gray;
+}
+
+.statentry .latest {
+ background: white;
+ vertical-align: top;
+ font-size:;
+}
+
+.statbody {
+ display: none;
+}
+
+/*div.categorywrapper {
+ -moz-column-width: 500px;
+ -moz-column-gap: 20px;
+ -webkit-column-width: 500px;
+ -webkit-column-gap: 20px;
+ column-width: 500px;
+ column-gap: 20px;
+}*/ \ No newline at end of file
diff --git a/trunk/etherpad/src/static/css/beta.css b/trunk/etherpad/src/static/css/beta.css
new file mode 100644
index 0000000..afba271
--- /dev/null
+++ b/trunk/etherpad/src/static/css/beta.css
@@ -0,0 +1,49 @@
+div.beta-signup { }
+
+div.right {
+ float: right;
+ width: 500px;
+}
+div.left {
+ width: 224x;
+ float: left;
+ text-align: center;
+ padding: 60px 0 0 80px;
+}
+
+form#beta-signup-form {
+ border: 1px solid #ccc;
+ margin: 2em 0;
+ padding: 1em;
+ background: #eee;
+}
+
+form#beta-signup-form p { margin: 0; }
+
+form input {
+ border: 1px solid #3773c6;
+ font-size: 14pt;
+}
+
+form button {
+ border: 0;
+ cursor: pointer;
+ color: #fff;
+ font-weight: bold;
+ overflow: visible;
+ padding: 0;
+ background: #70a4ec;
+ border: 1px solid #3773c6;
+ padding: 4px 6px;
+ margin-top: 4px;
+}
+
+#error-msg {
+ margin: 0;
+ padding: .5em;
+ margin-bottom: .5em;
+ border: 1px solid red;
+ background: #fee;
+ font-weight: bold;
+}
+
diff --git a/trunk/etherpad/src/static/css/broadcast.css b/trunk/etherpad/src/static/css/broadcast.css
new file mode 100644
index 0000000..afb65b8
--- /dev/null
+++ b/trunk/etherpad/src/static/css/broadcast.css
@@ -0,0 +1,386 @@
+*,html.body { margin: 0; padding: 0; }
+h1, h2, h3, h4, h5, h6 { display: inline; line-height: 2em; }
+
+.clear { clear: both; }
+
+html { font-size: 62.5%; }
+
+body { background: #ebebeb url(/static/img/jun09/pad/backgrad.gif) repeat-x left top; }
+body, textarea { font-family: Arial, sans-serif; }
+
+#topbar { height: 25px; background: #326cbd url(/static/img/jun09/pad/padtopback2.gif) repeat-x left top;
+ position: relative; }
+
+#padpage { margin-left: auto; margin-right: auto; width: 914px; vertical-align: top;}
+
+#topbarleft { float: left; height: 100%; overflow: hidden;
+ background: url(/static/img/jun09/pad/padtop4.png) no-repeat left top; width: 5px; }
+#topbarright { float: right; height: 100%; overflow: hidden;
+ background: url(/static/img/jun09/pad/padtop4.png) no-repeat right top; width: 5px; }
+
+
+.propad #topbar { background: #2c2c2c url(/static/img/jun09/pad/protop.png) repeat-x 0 -25px; }
+.propad #topbarleft { background: url(/static/img/jun09/pad/protop.png) no-repeat left top; }
+.propad #topbarright { background: url(/static/img/jun09/pad/protop.png) no-repeat right top; }
+
+a#backtoprosite, #accountnav {
+ display: block; position: absolute; height: 15px; line-height: 15px;
+ width: auto; top: 5px; font-size: 1.2em;
+}
+a#backtoprosite, #accountnav a { color: #cde7ff; text-decoration: underline; }
+#accountnav { right: 10px; color: #fff; }
+
+
+#topbarcenter { margin-left: 150px; margin-right: 150px; }
+a#topbaretherpad { margin-left: auto; margin-right: auto; display: block; width: 127px;
+ position: relative; top: 0px; height: 0; padding-top: 25px;
+ background: url(/static/img/jun09/pad/padtop4.png) no-repeat -397px 0px; overflow: hidden; }
+
+.propad a#topbaretherpad { background: url(/static/img/jun09/pad/protop.png) no-repeat -397px 0px; }
+
+#padmain {
+ margin: 7px;
+ margin-top: 5px;
+ margin-right: 0px;
+ padding: 19px;
+ padding-top:16px;
+ border: 1px solid rgb(194, 194, 194);
+ background-color: white;
+ min-height: 500px;
+ font-family: Arial, sans-serif;
+ font-size: 1.2em;
+ line-height: 17px;
+ width: 670px;
+ position: absolute;
+ top:27px;
+}
+
+/*
+ * Fancy title bar
+ */
+#padmain h1 {
+ font-family: Verdana, sans-serif;
+ font-size: 1.5em;
+ font-weight: 400;
+ display: inline-block;
+ display: -moz-inline-box;
+ padding-top: 4px;
+ padding-bottom: 10px;
+}
+
+#padcontent {
+ font-size: 0.93em;
+ line-height: 1.5em;
+ font-weight: 25;
+}
+
+#titlebar {
+ margin-bottom: 25px;
+ height: 20px;
+ width: auto;
+}
+
+#titlebar #revision {
+ float: right;
+ width: auto;
+ text-align: right;
+ vertical-align: top;
+}
+
+#revision #revision_label {
+ font-weight: bold;
+ font-size: 1.0em;
+ line-height: 1.4em;
+}
+
+#revision #revision_date {
+ font-weight: light;
+ font-size: 0.8em;
+ color: rgb(184, 184, 184);
+}
+
+#rightbars {
+ margin-left: 730px;
+ margin-top: 5px;
+ margin-bottom: 5px;
+ margin-right: 7px;
+ position: absolute;
+ top:27px;
+}
+
+#rightbar {
+ width: 143px;
+ background-color: white;
+ border: 1px solid rgb(194, 194, 194);
+ padding: 16px;
+ padding-top: 13px;
+ font-size: 1.20em;
+ line-height: 1.8em;
+ vertical-align: top;
+}
+
+#rightbars h2 {
+ font-weight: 700;
+ font-size: 1.2em;
+ padding-top: 20px;
+ padding-bottom: 4px;
+}
+
+#rightbar img {
+ padding-left: 4px;
+ padding-right: 8px;
+ vertical-align: text-bottom;
+}
+#rightbar a {
+ color: rgb(50, 132, 213);
+ text-decoration: none;
+}
+
+#legend {
+ width: 143px;
+ background-color: white;
+ border: 1px solid rgb(194, 194, 194);
+ padding: 16px;
+ padding-top: 0px;
+ font-size: 1.20em;
+ line-height: 1.8em;
+ vertical-align: top;
+ margin-top: 10px;
+}
+#legend h2 {
+ padding-top: 10px;
+}
+
+#authorstable {
+ vertical-align: middle;
+}
+
+#authorstable div.swatch {
+ width:15px;
+ height:15px;
+ margin: 5px;
+ margin-top:3px;
+ margin-right: 14px;
+ border: rgb(149, 149, 149) 1px solid;
+}
+
+#rightbar h2 {
+ font-weight: 700;
+ font-size: 1.2em;
+ padding-top: 20px;
+ padding-bottom: 4px;
+}
+
+#rightbar a {
+ color: rgb(50, 132, 213);
+ text-decoration: none;
+}
+
+#timeslider-wrapper {
+ position: relative;
+ left: 0px;
+ right: 0px;
+ top: 0px;
+}
+
+#timeslider-left {
+ position: absolute;
+ left:-2px;
+ background-image: url(/static/img/pad/timeslider/timeslider_left.png);
+ width: 134px;
+ height: 63px;
+}
+
+#timeslider-right {
+ position: absolute;
+ top:0px;
+ right:-2px;
+ background-image: url(/static/img/pad/timeslider/timeslider_right.png);
+ width: 155px;
+ height: 63px;
+}
+
+
+#timeslider {
+ margin:7px;
+ margin-bottom: 0px;
+ width: 894px;
+ height: 63px;
+ margin-left: 9px;
+ margin-right: 9px;
+ background-image: url(/static/img/pad/timeslider/timeslider_background.png);
+ position: relative;
+}
+
+div#timeslider #timeslider-slider {
+ position: absolute;
+ left: 0px;
+ top: 1px;
+ height: 61px;
+ width: 100%;
+}
+
+div#ui-slider-handle {
+ width: 13px;
+ height: 61px;
+ background-image: url(/static/img/pad/timeslider/crushed_current_location.png);
+ cursor: pointer;
+ -moz-user-select: none;
+ -khtml-user-select: none;
+ user-select: none;
+ position: absolute;
+ top: 0;
+ left: 0;
+}
+* html div#ui-slider-handle { /* IE 6/7 */
+ background-image: url(/static/img/pad/timeslider/current_location.gif);
+}
+
+div#ui-slider-bar {
+ position: relative;
+ margin-right: 148px;
+ height: 35px;
+ margin-left: 5px;
+ top: 20px;
+ cursor: pointer;
+ -moz-user-select: none;
+ -khtml-user-select: none;
+ user-select: none;
+
+}
+
+div#timeslider div#playpause_button {
+ background-image: url(/static/img/pad/timeslider/crushed_button_undepressed.png);
+ width: 47px;
+ height: 47px;
+ position: absolute;
+ right: 77px;
+ top: 9px;
+}
+
+div#timeslider div#playpause_button div#playpause_button_icon {
+ background-image: url(/static/img/pad/timeslider/play.png);
+ width: 47px;
+ height: 47px;
+ position: absolute;
+ top :0px;
+ left:0px;
+}
+* html div#timeslider div#playpause_button div#playpause_button_icon {
+ background-image: url(/static/img/pad/timeslider/play.gif); /* IE 6/7 */
+}
+
+div#timeslider div#playpause_button div.pause#playpause_button_icon {
+ background-image: url(/static/img/pad/timeslider/pause.png);
+}
+* html div#timeslider div#playpause_button div.pause#playpause_button_icon {
+ background-image: url(/static/img/pad/timeslider/pause.gif); /* IE 6/7 */
+}
+
+div #timeslider div#steppers div#leftstar {
+ position: absolute;
+ right: 34px;
+ top: 8px;
+ width:30px;
+ height:21px;
+ background: url(/static/img/pad/timeslider/stepper_buttons.png) 0px 44px;
+ overflow:hidden;
+}
+
+div #timeslider div#steppers div#rightstar {
+ position: absolute;
+ right: 5px;
+ top: 8px;
+ width:29px;
+ height:21px;
+ background: url(/static/img/pad/timeslider/stepper_buttons.png) 29px 44px;
+ overflow:hidden;
+}
+
+div #timeslider div#steppers div#leftstep {
+ position: absolute;
+ right: 34px;
+ top: 33px;
+ width:30px;
+ height:21px;
+ background: url(/static/img/pad/timeslider/stepper_buttons.png) 0px 22px;
+ overflow:hidden;
+}
+
+div #timeslider div#steppers div#rightstep {
+ position: absolute;
+ right: 5px;
+ top: 33px;
+ width:29px;
+ height:21px;
+ background: url(/static/img/pad/timeslider/stepper_buttons.png) 29px 22px;
+ overflow:hidden;
+}
+
+#timeslider div.star {
+ position: absolute;
+ top: 40px;
+ background-image: url(/static/img/pad/timeslider/star.png);
+ width: 15px;
+ height: 16px;
+ cursor: pointer;
+}
+* html #timeslider div.star {
+ background-image: url(/static/img/pad/timeslider/star.gif); /* IE 6/7 */
+}
+
+#timeslider div#timer {
+ position: absolute;
+ font-family: Arial, sans-serif;
+ left: 7px;
+ top: 9px;
+ width: 122px;
+ text-align: center;
+ color: white;
+ font-size: 11px;
+}
+
+#padcontent ul, ol, li {
+ padding: 0;
+ margin: 0;
+}
+#padcontent ul { margin-left: 1.5em; }
+#padcontent ul ul { margin-left: 0 !important; }
+#padcontent ul.list-bullet1 { margin-left: 1.5em; }
+#padcontent ul.list-bullet2 { margin-left: 3em; }
+#padcontent ul.list-bullet3 { margin-left: 4.5em; }
+#padcontent ul.list-bullet4 { margin-left: 6em; }
+#padcontent ul.list-bullet5 { margin-left: 7.5em; }
+#padcontent ul.list-bullet6 { margin-left: 9em; }
+#padcontent ul.list-bullet7 { margin-left: 10.5em; }
+#padcontent ul.list-bullet8 { margin-left: 12em; }
+
+#padcontent ul { list-style-type: disc; }
+#padcontent ul.list-bullet1 { list-style-type: disc; }
+#padcontent ul.list-bullet2 { list-style-type: circle; }
+#padcontent ul.list-bullet3 { list-style-type: square; }
+#padcontent ul.list-bullet4 { list-style-type: disc; }
+#padcontent ul.list-bullet5 { list-style-type: circle; }
+#padcontent ul.list-bullet6 { list-style-type: square; }
+#padcontent ul.list-bullet7 { list-style-type: disc; }
+#padcontent ul.list-bullet8 { list-style-type: circle; }
+
+#error {
+ position: absolute;
+ margin-left: 9px;
+ top:4px;
+ left: 0px;
+ right:9px;
+/* width:894px;*/
+ height:34px;
+ background-color: rgb(247, 247, 247);
+ z-index:10;
+ text-align: center;
+ font-family: Verdana;
+ padding-top: 20px;
+ font-size: 16px;
+}
+#error a {
+ color: rgb(50, 132, 213);
+ text-decoration: none;
+}
diff --git a/trunk/etherpad/src/static/css/connection_diagnostics.css b/trunk/etherpad/src/static/css/connection_diagnostics.css
new file mode 100644
index 0000000..fc040d0
--- /dev/null
+++ b/trunk/etherpad/src/static/css/connection_diagnostics.css
@@ -0,0 +1,13 @@
+#content {
+ text-align: center;
+}
+
+#statusmsg {
+ font-size: 200%;
+ color: #444;
+}
+
+#emailform { margin: 2em; padding: 1em; background: #eee; border: 1px solid #999; }
+#emailform p { font-size: 200%; color: #444; }
+#emailform input { font-size: 200%; margin-bottom: 1em; }
+#emailform #email { color: #555; }
diff --git a/trunk/etherpad/src/static/css/etherpad.css b/trunk/etherpad/src/static/css/etherpad.css
new file mode 100644
index 0000000..70bf464
--- /dev/null
+++ b/trunk/etherpad/src/static/css/etherpad.css
@@ -0,0 +1,770 @@
+/*-----
+ Reset
+-----*/
+
+ html, body, div, span, applet, object, iframe,
+ h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+ a, abbr, acronym, address, big, cite, code,
+ del, dfn, em, font, img, ins, kbd, q, s, samp,
+ small, strike, strong, sub, sup, tt, var,
+ dl, dt, dd, ol, ul, li,
+ fieldset, form, label, legend,
+ table, caption, tbody, tfoot, thead, tr, th, td {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ outline: 0;
+ font-weight: inherit;
+ font-style: inherit;
+ font-size: 1em;
+ font-family: inherit;
+ vertical-align: baseline;
+ }
+ :focus {
+ outline: 0;
+ }
+ body {
+ line-height: 1;
+ color: #333;
+ background: #f7f7f7;
+ font-size: 75%;
+ }
+ html>body {
+ font-size: 12px;
+ }
+ ol, ul {
+ list-style: none;
+ }
+ table {
+ border-collapse: separate;
+ border-spacing: 0;
+ }
+ caption, th, td {
+ text-align: left;
+ font-weight: normal;
+ }
+ blockquote:before, blockquote:after,
+ q:before, q:after {
+ content: "";
+ }
+ blockquote, q {
+ quotes: "" "";
+ }
+
+/*----------------------------------------------------------------*/
+/* global */
+/*----------------------------------------------------------------*/
+a.obfuscemail {
+ text-decoration: none;
+}
+
+a:link,a:visited {
+ text-decoration: none;
+ color: #004ca8;
+}
+a:hover {
+ text-decoration: underline;
+ color: #005CCB;
+}
+.clear {
+ clear: both;
+}
+em {
+ font-style: italic;
+}
+strong {
+ font-weight: bold;
+}
+
+/*----------------------------------------------------------------*/
+/* newpad button */
+/*----------------------------------------------------------------*/
+
+.fpcontent .newpadbuttonwrap {
+ width: 152px;
+ height: 35px;
+ background: url(/static/img/davy/btn/createpad-small.gif) no-repeat bottom left;
+}
+.fpcontent .newpadbuttonwrap a {
+ width: 152px;
+ position: relative;
+ padding: 35px 0 0 0;
+ overflow: hidden;
+ background: transparent url(/static/img/davy/btn/createpad-small.gif) no-repeat top left;
+ height: 0px;
+ display: block;
+}
+
+/*----------------------------------------------------------------*/
+/* 500 error page */
+/*----------------------------------------------------------------*/
+#errorpage .error500 {
+ font-size: 1em;
+ background: #fcc;
+ border: 1px solid #f00;
+ padding: 1em;
+ margin: 1em 2em;
+ font-weight: bold;
+}
+
+/*----------------------------------------------------------------*/
+/* padviewpage */
+/*----------------------------------------------------------------*/
+body#padviewbody {
+ background-color: #ebebeb;
+}
+#padviewpage a {
+ text-decoration: underline;
+}
+#padviewpage #padviewheader {
+ margin: 8px;
+ padding: 8px;
+ border: 1px solid #ccc;
+ background-color: #e0e0ff;
+ line-height: 160%;
+}
+#padviewpage #padviewheader h1 {
+ font-weight: bold;
+ font-family: "Lucida Grande","Lucida Sans Unicode",sans-serif;
+ font-size: 2em;
+}
+#padviewpage .metadata {
+ color: #333;
+}
+#padviewpage .rlabel {
+ font-weight: bold;
+}
+#padviewpage p {
+ margin-top: 2px;
+}
+#padviewpage #padcontent {
+ border: 1px solid #ccc;
+ margin: 8px;
+ padding: 8px;
+ font-family: sans-serif;
+ font-size: 12px;
+ background-color: #fff;
+ line-height: 130%;
+}
+#padviewpage #padviewfooter {
+ margin: 8px;
+ padding: 8px;
+ font-size: 12px;
+}
+
+
+#padviewpage #export td.exportpic a img {
+ border: 0;
+}
+
+#padviewpage #export a.disabledexport {
+ color: gray;
+ text-decoration: none;
+}
+
+#padviewpage #export {
+ font-size: 1em;
+}
+
+#padviewpage #export .exportlink {
+ margin: 2px 0;
+}
+
+#padviewpage #export td.exportpic {
+ padding-left: 10px;
+}
+
+#padviewpage #export td.labelcell {
+ padding-left: 4px;
+}
+
+#export img {
+ vertical-align: middle;
+ padding: 4px;
+ padding-bottom: 8px;
+ padding-left: 3px;
+}
+
+#export span.titlelabel {
+ vertical-align: top;
+ padding-right: 12px;
+ font-size: 1.3em;
+ color: rgb(0, 0, 0);
+ font-weight: bold;
+ margin-top: 10px;
+}
+
+#export td.labelcell a {
+
+ vertical-align: middle;
+ font-size: 1em;
+ padding-right: 12px;
+ color: rgb(0, 52, 143);
+ font-weight: bold;
+}
+
+
+/*----------------------------------------------------------------*/
+/* feature tour page */
+/*----------------------------------------------------------------*/
+#featuretourpage {}
+#featuretourpage p { margin-top: 1em; }
+#featuretourpage #screencastmsg { margin: 2em 0; }
+#featuretourpage .featurebox {
+ clear: both;
+ padding: 1em 1em;
+ margin-top: 2em;
+ margin-left: auto;
+ margin-right: auto;
+ background: #eee;
+ border: 1px solid #ccc;
+}
+#featuretourpage .featurebox .featureprose {
+}
+#featuretourpage .featurebox .featureprose h2 {
+}
+#featuretourpage .featurebox .featureprose p {
+ padding: 0;
+ margin: 1em 0;
+}
+#featuretourpage .featurebox img {
+ border: 1px solid #aaa;
+ margin: 0 1em 1em 1em;
+}
+#featuretourpage #usersbox div.featureprose { }
+#featuretourpage #usersimg { float: right; }
+#featuretourpage #editsimg { padding: 0; margin: 0; }
+#featuretourpage #neverlosework img { float: left; }
+#featuretourpage #neverlosework div.featureprose { }
+#featuretourpage #lockimg { float: right; border: 0;}
+#featuretourpage #revisionsimg { float: left; margin-left: 0; }
+#featuretourpage #codeimg { float: left; margin-left: 0; }
+#featuretourpage h2 {
+ margin-top: 0;
+ font-size: 1.5em;
+ font-weight: bold;
+}
+#featuretourpage p {
+ font-size: 1.1em;
+}
+
+/*----------------------------------------------------------------*/
+/* product page */
+/*----------------------------------------------------------------*/
+
+#productpage p {
+ font-size: 1.2em;
+ color: #333;
+}
+
+#productpage h1 {
+}
+
+#productpage h2 {
+ font-size: 1.6em;
+ font-weight: bold;
+ font-family: inherit;
+ color: #399;
+ font-style: italic;
+ border-bottom: 1px solid #399;
+ margin-top: 1.5em;
+ margin-bottom: 0.3em;
+}
+
+#productpage #howuse {
+ margin-left: 80px;
+ margin-right: 80px;
+}
+
+#productpage #howuse p {
+ font-size: 1.2em;
+ line-height: 150%;
+}
+
+#productpage .tourbar { width: 100%; }
+#productpage .tourbar td { padding: 3px; }
+#productpage .tourbar .left { text-align: left; font-weight: bold; font-size: 1.6em; }
+#productpage .tourbar .right { text-align: right; }
+
+#productpage .tourbar a {
+ color: #33f;
+ font-size: 1.4em;
+ font-weight: bold;
+}
+
+#productpage #tourtop { border-bottom: 1px solid #999; }
+/* #productpage #tourbot { border-top: 1px solid #999; } */
+
+#productpage #pageshot img {
+ display: block;
+ margin-top: 10px;
+ margin-bottom: 10px;
+ margin-left: auto;
+ margin-right: auto;
+ padding: 0;
+}
+
+#productpage .javascripton #tourbody {
+ /*height: 650px;*/
+ padding-top: 1px;
+ padding-bottom: 1px;
+}
+#productpage .javascripton .tourprose {
+ display: none;
+}
+#productpage #usecases table {
+ height: 300px;
+ padding: 20px auto;
+ border: 1px solid #aaa;
+ width: 100%;
+}
+#productpage #usecases td {
+ vertical-align: top;
+}
+
+#productpage #usecases p {
+ font-family: Georgia, serif;
+ font-size: 1.3em;
+ line-height: 1.3;
+}
+
+#productpage #usecases h3 {
+ padding: 0; margin: 0;
+ font-weight: bold;
+ color: black;
+ font-family: inherit;
+ font-size: 1.6em;
+ margin-top: 0.5em;
+}
+
+#productpage #usecases strong {
+ font-style: normal;
+ font-weight: normal;
+ background-color: #ffc;
+}
+
+#productpage #usecases p.intro { margin: 0.5em 0;}
+
+#productpage #usecases #prosecell {
+ padding-left: 20px;
+ padding-right: 20px;
+ padding-top: 15px;
+ border-left: 1px solid #ccc;
+ background: #fff url(/static/img/oct/insetrect.gif) no-repeat right top;
+}
+
+#productpage #usecases #prosecell p {
+ padding: 0;
+ margin: 0;
+ margin-bottom: 0.8em;
+}
+
+#productpage .showpageshot #usecases { display: none; }
+#productpage .showusecases #pageshot { display: none; }
+
+#productpage #tourleftnavcell { width: 200px; }
+
+#productpage ul#tourleftnav { margin: 0; padding: 0; list-style: none; }
+#productpage ul#tourleftnav li { margin: 0; padding: 0; background: #fff; }
+#productpage ul#tourleftnav li a { color: #33f; text-decoration: none; }
+#productpage ul#tourleftnav li a:hover { text-decoration: underline; }
+#productpage ul#tourleftnav li a { outline: none; }
+#productpage ul#tourleftnav li { background: url(/static/img/oct/usecasesnavup.gif) repeat-x left center; border-bottom: 1px solid #ccc; }
+/*#productpage ul#tourleftnav li:hover { background: url(/static/img/oct/usecasesnavuph.gif) repeat-x left center; }*/
+#productpage ul#tourleftnav li.selected { background: url(/static/img/oct/usecasesnavdown.gif) repeat-x left center }
+/*#productpage ul#tourleftnav li.selected:hover { background: url(/static/img/oct/usecasesnavdownh.gif) repeat-x left center; }*/
+#productpage ul#tourleftnav li.selected a { color: #000; background: url(/static/img/oct/tinytriangle.gif) no-repeat 95% center; }
+
+#productpage #tourleftnav a {
+ font-size: 1.2em;
+ padding: 0.4em 0.4em;
+ display: block;
+ font-weight: bold;
+ font-family: inherit;
+ font-style: italic;
+ cursor: pointer;
+}
+
+.fpcontent .newpadbuttonwrap {
+ margin: 0 auto;
+}
+
+
+/*----------------------------------------------------------------*/
+/* faq page */
+/*----------------------------------------------------------------*/
+#faqpage hr {
+ width: 100%;
+ margin-top: 2em;
+ margin-bottom: 2em;
+ margin-left: auto;
+ margin-right: auto;
+ color: #ccc;
+}
+div#faqpage h2 {
+ border-bottom: 1px solid #aaa;
+ margin: 0;
+}
+#faqpage div.answer {
+ color: #222;
+ padding: 1em;
+}
+#faqpage ul.qlist { margin: 2em 0 4em 1.4em; }
+#faqpage ul.qlist li { margin: .5em 0; }
+
+/*----------------------------------------------------------------*/
+/* contact page */
+/*----------------------------------------------------------------*/
+#contactpage div.cbox {
+ padding: 1em;
+ margin: 2em 0;
+ width: 330px;
+ height: 200px;
+}
+#contactpage h2 {
+ margin: 0;
+}
+#contactpage #boxleft {
+ float: left;
+ margin-left: 1em;
+}
+#contactpage #boxright {
+ float: right;
+ margin-right: 1em;
+}
+#contactpage #boxright p {
+ font-size: 1.4em;
+ font-family: serif;
+ color: #222;
+ padding-left: 2em;
+}
+/*----------------------------------------------------------------*/
+/* company page */
+/*----------------------------------------------------------------*/
+
+#companypage div#appjetinc { width: 300px; float: left; padding: 2em; }
+#companypage div#appjetinc p { padding-left: 1em; }
+#companypage img#ajlogo { margin-top: 1em; border: 0; }
+#companypage img#pier38 { border: 1px solid #888; float: right; margin-top: 1em; }
+#companypage table img { border: 1px solid #444; margin-top: 4px; }
+#companypage table td { padding: 15px; vertical-align: top; }
+#companypage table td p.intro { margin-top: 0; }
+
+/*----------------------------------------------------------------*/
+/* blog */
+/*----------------------------------------------------------------*/
+
+.blogbody { background: #f8f8f8; }
+
+.blogpage a#subscribelink {
+ font-size: .92em;
+ display: block;
+ background: #fff;
+ border: 1px solid #666;
+ font-weight: bold;
+ margin-bottom: 1em;
+ padding: 4px 8px;
+ color: #049;
+ text-decoration: none;
+}
+
+.blogpage div#subscribewrap a:hover {
+ background: #def;
+}
+.blogpage a#subscribelink img {
+ border: 0;
+ float: left;
+}
+.blogpage div#subscribewrap span.subtext {
+ display: block;
+ float: left;
+ padding-left: 8px;
+ padding-top: 2px;
+}
+
+div#blogcol1 {
+ width: 520px;
+ padding-left: 50px;
+ float: left;
+ font-size: .9em;
+}
+
+div#blogcol2 {
+ float: left;
+ width: 240px;
+ margin-top: 24px;
+ margin-left: 1em;
+}
+
+div#recentpostsbox {
+ border: 1px solid #ccc;
+ background: #fefefe;
+ padding: 12px 6px;
+ font-size: .8em;
+}
+div#recentpostsbox p {
+ margin: 0;
+ font-weight: bold;
+ padding-left: .5em;
+}
+div#recentpostsbox a {
+ color: #049;
+ text-decoration: none;
+}
+div#recentpostsbox a:hover {
+ text-decoration: underline;
+}
+div#recentpostsbox ul li {
+ margin: 0;
+ margin-top: .4em;
+}
+
+.blogpage div.bpheader {
+ border-top: 1px solid #ccc;
+ border-bottom: 1px solid #ccc;
+ border-right: 1px solid #ccc;
+ border-left: 1px solid #ccc;
+ background: #eee;
+ margin-top: 24px;
+ padding: 1em;
+}
+
+.blogpage div.blogpost_mainpage_content {
+ background: #fff;
+ padding: 0 1em;
+ padding-top: 0.1em;
+ padding-bottom: 0.5em;
+ border-right: 1px solid #ccc;
+ border-left: 1px solid #ccc;
+ border-bottom: 1px solid #ccc;
+ font-size: 100%;
+ line-height: 1.4;
+}
+.blogpage div.blogpost_mainpage_content a {
+ text-decoration: underline;
+}
+.blogpage div.blogpost_mainpage_content a:hover {
+ color: red;
+}
+
+.blogpage div.bpheader div.postdate a {
+ text-decoration: none;
+ font-size: 1em;
+ font-weight: bold;
+ font-style: italic;
+ color: #555;
+}
+.blogpage div.bpheader h2 { margin: 0; border: 0; }
+.blogpage div.bpheader h2 a {
+ font-size: 1em;
+ text-decoration: none;
+ color: #049;
+ margin: 0;
+ font-style: normal;
+}
+.blogpage div#disqus_thread {
+ margin-top: 40px;
+}
+.blogpage .commentslink {
+ text-align: right;
+}
+.singleblogpost #blogposttop {
+ margin-left: 50px;
+}
+
+.blogpage h3 {
+ font-weight: bold;
+ font-size: 1em;
+ color: #050;
+}
+
+.blogpost_mainpage_content ol { }
+.blogpost_mainpage_content ol li {
+ margin-top: 1em;
+ margin-left: 2em;
+ list-style: decimal;
+}
+
+.blogpage div.code {
+ font-family: monospace;
+ border: 1px solid yellow;
+ padding: 0.5em;
+ background: #ffd;
+}
+
+.blogpage tt {
+ font-family: monospace;
+}
+
+
+/*----------------------------------------------------------------*/
+/* create pad */
+/*----------------------------------------------------------------*/
+#createpadpage form {
+ width: 80%;
+ margin-left: auto;
+ margin-right: auto;
+ border: 1px solid #ddd;
+ background: #eef;
+ font-size: 1.8em;
+ text-align: center;
+ padding: 2em;
+}
+#createpadpage #padurl {
+ background: #fff;
+ border: 1px solid #ccc;
+ padding: 1em;
+}
+#createpadpage input {
+ font-size: 1.8em;
+}
+/*----------------------------------------------------------------*/
+/* pad full */
+/*----------------------------------------------------------------*/
+#padfullpage #msg {
+ margin: 2em 0;
+ padding: 2em;
+ background: #eee;
+ border: 1px solid #aaa;
+ font-size: 1.3em;
+}
+#padfullpage #padurlwrap {
+ text-align: center;
+ margin-bottom: 3em;
+}
+#padfullpage #padurl {
+ background: #fff;
+ border: 1px solid #ccc;
+ padding: 1em;
+}
+/*----------------------------------------------------------------*/
+/* beta signup */
+/*----------------------------------------------------------------*/
+#betasignuppage img#betasign { float: left; margin-top: 20px; }
+#betasignuppage div#betaformwrap {
+ margin: 15px;
+ padding: 20px;
+ margin-left: 200px;
+ border: 1px solid #ccc;
+ background: #eee;
+}
+#betasignuppage div#betaformwrap p {
+ margin: 0;
+ color: #333;
+ margin-bottom: 1em;
+}
+#betasignuppage div#betaform { padding: 2em 0; }
+#betasignuppage div#betaform input#email { font-size: 1.6em; color: #555; }
+#betasignuppage div#betaform button { font-size: 1.6em; }
+#betasignuppage div#confirm { display: none; }
+#betasignuppage div#error {
+ margin: 1em 3em 2em 2em;
+ color: red;
+ display: none;
+}
+#betasignuppage div#confirm {
+ margin: 2em 2em 2em 0em;
+ color: green;
+ font-weight: bold;
+ display: none;
+}
+#betasignuppage div#subtext { font-size: .9em; color: #666; }
+
+/*----------------------------------------------------------------*/
+/* time slider */
+/*----------------------------------------------------------------*/
+
+body#padsliderbody {
+ font-size: 1.2em;
+ padding: 20px;
+ background-color: #fff;
+}
+
+#padsliderbody #stuff {
+ width: 600px;
+ margin-top: 10px;
+}
+
+#padsliderbody #sliderui {
+ margin: 10px;
+}
+
+#padsliderbody #controls {
+ background: #eee;
+ padding: 5px;
+ border: 1px solid #999;
+}
+
+#padsliderbody #currevdisplay {
+ margin-top: 3px;
+}
+
+#padsliderbody #controls a {
+ color: #00f;
+ text-decoration: underline;
+ cursor: pointer;
+}
+
+#padsliderbody #stuff {
+ padding: 5px;
+}
+
+/*----------------------------------------------------------------*/
+/* testimonials */
+/*----------------------------------------------------------------*/
+
+#testimonials {
+ padding: 0 3em;
+ font-family: times serif;
+}
+
+#testimonials .head {
+ font-weight: bold;
+ padding-top: 4px;
+ padding-right: 80px;
+}
+
+#testimonials .quote-open {
+ background: url(/static/img/about/quote-open.png) no-repeat left top;
+ padding-left: 80px;
+ margin-top: 2em;
+}
+
+#testimonials .quote-close {
+ background: url(/static/img/about/quote-close.png) no-repeat right bottom;
+ padding-right: 80px;
+ text-align: justify;
+ color: #222;
+}
+
+#testimonials .attrib {
+ font-style: italic;
+ text-align: right;
+ padding-left: 2em;
+ padding-right: 80px;
+ color: #444;
+}
+
+#testimonials .attrib p {
+ margin: 0;
+}
+
+
+/* pne faq */
+
+div.pne-faq dt {
+ font-size: 1.1em;
+ border-bottom: 1px solid #444;
+ color: #666;
+ margin: 1.6em 0 0.75em 0;
+}
+
+/* support page */
+
+div#support-content {
+ margin: 0 4em;
+}
+
+div#forums-content {
+ margin: 0 4em;
+}
diff --git a/trunk/etherpad/src/static/css/fluxbb.css b/trunk/etherpad/src/static/css/fluxbb.css
new file mode 100644
index 0000000..844ceca
--- /dev/null
+++ b/trunk/etherpad/src/static/css/fluxbb.css
@@ -0,0 +1,55 @@
+/* fluxbb-specific CSS rules go here. */
+
+/*----------------------------------------------------------------*/
+/* Etherpad overriding shit */
+/*----------------------------------------------------------------*/
+
+div.epforum {
+ width: 780px;
+ padding-top: 1em;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+div#punwrap div.pun {
+ font-size: 120%;
+}
+
+div#idx1 h2 { display: none; }
+
+div#punwrap div.pun h2 {
+ color: white;
+ font-style: normal;
+ font-weight: normal;
+ font-family: sans-serif;
+ font-size: 1.2em;
+}
+
+div#punwrap div.pun p { margin: 0; }
+
+.pun div.box {
+ border: 1px solid #ccc;
+}
+
+div#brdheader div.box {
+ background: #fff;
+ border: 0;
+ padding: 0;
+ margin: 0;
+}
+
+div#brdheader div#brdtitle {
+ padding: 0;
+}
+div#brdheader h1 {
+ font-family: "Lucida Grande","Lucida Sans Unicode",sans-serif;
+ color: #666;
+ border-bottom: 1px solid #666;
+ font-size: 1.8em;
+}
+
+div#brdwelcome {
+ margin-top: 1em;
+ border: 1px solid #ccc;
+ background: #f1f1f1;
+}
diff --git a/trunk/etherpad/src/static/css/framedpage.css b/trunk/etherpad/src/static/css/framedpage.css
new file mode 100644
index 0000000..a99554b
--- /dev/null
+++ b/trunk/etherpad/src/static/css/framedpage.css
@@ -0,0 +1,175 @@
+/*------
+ Global Container
+------*/
+
+#container {
+ font-family: Arial, Helvetica, Calibri, sans-serif;
+}
+body.home {
+ background: #f7f7f7 url(/static/img/davy/bg/home2.png) repeat-x top;
+}
+.home #container {
+ width: 920px; margin: 0 auto;
+}
+body.nothome {
+ background: #f7f7f7 url(/static/img/davy/bg/product.png) repeat-x top;
+}
+.nothome #container {
+ width: 910px; margin: 0 auto;
+}
+
+/*------
+ Layout
+------*/
+
+#navigation,
+.home #top,
+.home #bottom,
+#footer {
+ width: 888px;
+ margin: 0 auto;
+}
+
+/* framed page general */
+div.fpcontent {
+ width: 848px;
+ margin: 0 auto;
+
+ font-size: 1.3em;
+ padding: 20px;
+
+ background-color: #fff;
+ border-left: 1px solid #ccc;
+ border-right: 1px solid #ccc;
+ border-bottom: 1px solid #ccc;
+ border-top: 0;
+}
+div.fpcontent h1 {
+ color: #666;
+ border-bottom: 1px solid #666;
+ margin: .8m 0 1em 0;
+ font-size: 1.8em;
+}
+div.fpcontent h2 {
+ color: #666;
+ border-bottom: 1px solid #666;
+ font-size: 1.4em;
+ margin: 1em 0;
+}
+div.fpcontent p {
+ margin: 1em 0;
+ line-height: 150%;
+}
+div.fpcontent ul {
+ list-style: disc;
+ padding-left: 1.5em;
+}
+div.fpcontent ul li {
+ margin: 1em 0;
+ padding-left: .5em;
+}
+
+/*----------
+ Navigation
+----------*/
+
+#topnav_wrap {
+ background: url(/static/img/davy/bg/product.png) repeat-x top;
+}
+#navigation {
+ height: 38px;
+ overflow: hidden;
+ background: url(/static/img/davy/bg/home2.png) repeat-x top;
+}
+#navigation h1 a {
+ display: block;
+ width: 120px;
+ position: relative;
+ padding: 38px 0 0 0;
+ overflow: hidden;
+ background: transparent url(/static/img/davy/gfx/product-logo.gif) no-repeat 0 6px;
+ height: 0px;
+ float: left;
+}
+.home #navigation h1 a {
+ display: none;
+}
+#navigation ul {
+ margin-right: -10px;
+}
+#navigation li {
+ display: inline;
+}
+#navigation li a {
+ font-family: Calibri, "Trebuchet MS", Trebuchet, Arial, sans-serif;
+ font-size: 1.208em;
+ text-transform: uppercase;
+ color: #fff;
+ text-shadow: 0 1px 0 #223f6b;
+ letter-spacing: 1px;
+ display: block;
+ padding: 11px 10px 13px 10px;
+ float: right;
+}
+.home #navigation .topnav_pricing a {
+ color: #ffc261;
+}
+.home #navigation .topnav_pricing a:hover {
+ color: #FEAC59;
+}
+#navigation li.selected a {
+ color: #bddbff;
+}
+.home #navigation li.selected a {
+ background: url(/static/img/davy/bg/home-nav-selected.png) no-repeat center 32px;
+}
+.nothome #navigation li.selected a {
+ background: url(/static/img/davy/bg/product-nav-selected-white.png) no-repeat center 32px;
+}
+#navigation li a:hover {
+ color: #DEEDFF;
+ text-decoration: none;
+}
+
+
+/*------
+ Footer
+------*/
+
+.home #footer {
+ border-top: 1px solid #d9d9d9;
+ margin-top: 24px;
+}
+
+.nothome #footer {
+ margin-top: 0px;
+}
+
+ #footer-inner {
+ border-top: 1px solid #f9f9f9;
+ padding: 12px 0;
+ color: #666;
+ font-size: .917em;
+ }
+
+#footer-left {
+ float: left;
+ width: 700px;
+}
+ #footer ul,
+ #footer li {
+ display: inline;
+ }
+ #footer li {
+ margin-left: 1em;
+ }
+
+ #footer #appjet {
+ float: right;
+ margin-right: -12px;
+ }
+ #footer #appjet a {
+ background: url(/static/img/davy/gfx/plane.gif) no-repeat right center;
+ padding-right: 12px;
+ }
+
diff --git a/trunk/etherpad/src/static/css/global-pro-account.css b/trunk/etherpad/src/static/css/global-pro-account.css
new file mode 100644
index 0000000..6c34446
--- /dev/null
+++ b/trunk/etherpad/src/static/css/global-pro-account.css
@@ -0,0 +1,52 @@
+div.error {
+ border: 1px solid red;
+ background: #fee;
+ padding: 1em;
+ margin: 1em 0;
+ width: 600px;
+ font-weight: bold;
+}
+
+form#global-sign-in {
+ background: #eeeef6;
+ padding: 1em;
+ border: 1px solid #ddd;
+ margin: 1em 0;
+ width: 600px;
+}
+
+form label {
+ color: #444;
+ margin-bottom: .2em;
+}
+
+form input {
+ border: 1px solid #377ec6;
+}
+
+form#global-sign-in label {
+ display: block;
+ margin-top: 1em;
+}
+
+form#global-sign-in button {
+ border: 0;
+ cursor: pointer;
+ color: #fff;
+ font-weight: bold;
+ overflow: visible;
+ padding: 0;
+ background: #70a4ec;
+ border: 1px solid #3773c6;
+ padding: 4px 16px;
+ margin-top: 14px;
+}
+
+.global-pro-account p {
+ font-size: 86%;
+}
+
+div.tip {
+ margin: .5em 0;
+ font-size: 90%;
+}
diff --git a/trunk/etherpad/src/static/css/home-opensource.css b/trunk/etherpad/src/static/css/home-opensource.css
new file mode 100644
index 0000000..0d6da6d
--- /dev/null
+++ b/trunk/etherpad/src/static/css/home-opensource.css
@@ -0,0 +1,44 @@
+#home {
+ width: 600px;
+ margin: 0 auto;
+ padding: 4em;
+ text-align: center;
+}
+
+#home #title {
+ font-size: 3.6em;
+}
+
+#home #buttons {
+ padding-top: 5em;
+}
+
+#home a#home-newpad, #home a#home-newsite {
+ padding: 1em;
+ margin: 12px 40px;
+ font-size: 1.6em;
+ border: 1px solid black;
+ background: #049;
+ color: #fff;
+ width: 30%;
+}
+
+#home a#home-newpad:hover, #home a#home-newsite:hover {
+ background: #26b;
+ text-decoration: none;
+}
+
+#tos {
+ margin-top: 8em;
+ color: #222;
+}
+
+#tos h1, #tos p {
+ margin: 1.5em 0;
+}
+
+#tos h1 {
+ font-weight: bold;
+ font-size: 1.1em;
+}
+
diff --git a/trunk/etherpad/src/static/css/home.css b/trunk/etherpad/src/static/css/home.css
new file mode 100644
index 0000000..797a8a7
--- /dev/null
+++ b/trunk/etherpad/src/static/css/home.css
@@ -0,0 +1,264 @@
+/*--------
+ Homepage
+ --------*/
+
+/* Top */
+
+#top {
+ height: 349px;
+ position: relative;
+ background: url(/static/img/davy/gfx/screenshot.gif) no-repeat bottom right;
+}
+
+#homepage-notice {
+ margin: 10px auto;
+ width: 888px;
+ background: #ffc;
+ color: #000;
+ border: 2px solid #550;
+ font-size: 1.4em;
+ padding: 8px;
+}
+
+#intro-left {
+ width: 340px;
+ float: left;
+ font-family: Arial, Helvetica, sans-serif;
+ text-shadow: 0 0 1px #18487F;
+ padding-top: 10px;
+}
+ #intro-left h1 {
+ width: 210px;
+ position: relative;
+ padding: 57px 0 0 0;
+ overflow: hidden;
+ background: transparent url(/static/img/davy/gfx/home-logo2.gif) no-repeat top left;
+ height: 0px;
+ margin: 0 0 5px -8px;
+ }
+ #intro-left h2 {
+ color: #d1e5ff;
+ font-size: 1.5em;
+ font-weight: bold;
+ line-height: 1.2;
+ }
+ #intro-left h2 a {
+ color: #9ac6ff;
+ font-style: italic;
+ border-bottom: 2px solid;
+ }
+ #intro-left h2 a:hover {
+ text-decoration: none;
+ color: #4A91EE;
+ }
+ #intro-left p {
+ color: #fff;
+ font-size: 1.167em;
+ line-height: 1.3;
+ margin: 10px 0;
+ }
+ #intro-links {
+ position: absolute;
+ bottom: 17px;
+ left: -1px;
+ width: 500px;
+ }
+ #intro-links a {
+ display: block;
+ float: left;
+ position: relative;
+ padding: 64px 0 0 0;
+ overflow: hidden;
+ background-repeat: no-repeat;
+ background-position: top left;
+ height: 0;
+ }
+ #newpadbutton {
+ width: 212px;
+ background-image: url(/static/img/davy/btn/createpad-home.gif);
+ margin-right: 11px;
+ }
+ #betabutton {
+ width: 220px;
+ background-image: url(/static/img/davy/btn/signup-home-4.gif);
+ }
+
+/* Bottom */
+
+#bottom {
+ padding-top: 28px;
+}
+
+#quote {
+ border-bottom: 1px solid #e0e0e0;
+ padding-bottom: 24px;
+ text-shadow: 0 0 1px #F7F7F7;
+}
+ #quote q {
+ width: 680px;
+ float: left;
+ font-size: 1.33em;
+ height: 1.5em;
+ }
+ #quote #quote-right {
+ width: 200px;
+ float: right;
+ text-align: right;
+ }
+ #quote #quote-right cite {
+ display: block;
+ font-size: 1.33em;
+ font-style: italic;
+ margin-bottom: 8px;
+ }
+
+#features {
+ border-top: 1px solid #fff;
+ padding-top: 22px;
+ text-shadow: 0 0 1px #F7F7F7;
+}
+ #features li {
+ width: 213px;
+ float: left;
+ margin-left: 12px;
+ }
+ #features li.first {
+ margin-left: 0;
+ }
+ #features li img {
+ float: left;
+ margin-right: 8px;
+ }
+ #features li strong,
+ #features li span {
+ display: block;
+ margin-left: 40px;
+ }
+ #features li strong {
+ font-size: 16px;
+ font-weight: normal;
+ margin-bottom: 6px;
+ }
+ #features li span {
+ line-height: 17px;
+ }
+
+#uses {
+width: 675px;
+float: left;
+ margin-bottom: 28px;
+ margin-right: -12px;
+position: relative;
+}
+#uses li {
+width: 213px;
+float: left;
+ margin-right: 12px;
+}
+#uses li a {
+ font-size: 1.5em;
+ font-family: Calibri, Arial, Helvetica, sans-serif;
+ text-shadow: 0 1px 0 #fff;
+color: #214b7e;
+}
+#uses li a:hover {
+ text-decoration: none;
+color: #30619c;
+}
+#uses li .thumb {
+display: block;
+width: 213px;
+position: relative;
+padding: 123px 0 0 0;
+overflow: hidden;
+ background-repeat: no-repeat;
+ background-position: top left;
+height: 0px;
+ margin-top: 10px;
+}
+#uses #use-meetings .thumb { background-image: url(/static/img/davy/gfx/use-meetings.png); }
+#uses #use-programming .thumb { background-image: url(/static/img/davy/gfx/use-programming.png); }
+#uses #use-writing .thumb { background-image: url(/static/img/davy/gfx/use-writing.png); }
+#uses .more {
+display: block;
+position: absolute;
+padding: 21px 0 0 0;
+overflow: hidden;
+background: transparent url(/static/img/davy/btn/uses-more.gif) no-repeat top left;
+height: 0px;
+width: 58px;
+top: 0;
+right: 12px;
+ z-index: 10;
+}
+* html #uses .more {
+right: 24px;
+}
+
+#blog {
+width: 213px;
+float: left;
+ margin-right: 12px;
+clear: left;
+}
+#blog h3 {
+ font-family: Calibri, "Trebuchet MS", Trebuchet, Arial, Helvetica, sans-serif;
+color: #666;
+ text-transform: uppercase;
+ text-shadow: 0 1px 0 #fff;
+ letter-spacing: 1px;
+}
+#blog li {
+margin: 1.25em 0;
+}
+#blog li strong a {
+ font-size: 1.33em;
+ font-family: Calibri, Arial, Helvetica, sans-serif;
+ font-weight: bold;
+}
+#blog li small {
+display: block;
+ font-size: .917em;
+ font-family: Calibri, Arial, Helvetica, sans-serif;
+color: #666;
+margin: 2px 0;
+}
+#blog li small a {
+color: #4182c2;
+}
+#blog li span {
+ line-height: 1.3;
+}
+
+#quotes {
+width: 406px;
+float: left;
+background: #e0ecf9;
+border: 1px solid #8ba9cc;
+ border-left: 0;
+ border-right: 0;
+color: #334050;
+ font-family: Calibri, Arial, Helvetica, sans-serif;
+padding: 12px 16px 0;
+ text-shadow: 0 0 1px #e0ecf9;
+}
+#quotes q {
+ font-size: 1.5em;
+ line-height: 1.22;
+display: block;
+ text-indent: -.45em;
+}
+#quotes cite {
+ font-size: 1.33em;
+display: block;
+ font-style: italic;
+ text-align: right;
+margin: .5em 0 1em;;
+}
+#quotes .more {
+ text-align: right;
+ margin-bottom: 1em;
+ position: relative;
+ right: -.75em;
+ font-size: 1.167em;
+}
diff --git a/trunk/etherpad/src/static/css/lib/jquery.contextmenu.css b/trunk/etherpad/src/static/css/lib/jquery.contextmenu.css
new file mode 100644
index 0000000..15a69aa
--- /dev/null
+++ b/trunk/etherpad/src/static/css/lib/jquery.contextmenu.css
@@ -0,0 +1,244 @@
+/* Classic Windows Theme (default) */
+/* =============================== */
+.context-menu-theme-default {
+ border:2px outset white;
+ background-color:#D4D0C8;
+}
+.context-menu-theme-default .context-menu-item {
+ text-align:left;
+ cursor:pointer;
+ padding:4px 28px 4px 16px;
+ color:black;
+ font-family:Tahoma,Arial;
+ font-size:11px;
+}
+.context-menu-theme-default .context-menu-separator {
+ margin:4px 2px;
+ font-size:0px;
+ border-top:1px solid #808080;
+ border-bottom:1px solid white;
+}
+.context-menu-theme-default .context-menu-item-disabled {
+ color:#808080;
+}
+.context-menu-theme-default .context-menu-item .context-menu-item-inner {
+ background:none no-repeat fixed 999px 999px; /* Make sure icons don't appear */
+}
+.context-menu-theme-default .context-menu-item-hover {
+ background-color:#0A246A;
+ color:white;
+}
+.context-menu-theme-default .context-menu-item-disabled-hover {
+ background-color:#0A246A;
+}
+
+/* Windows XP Theme */
+/* ================ */
+.context-menu-theme-xp {
+ border:1px solid #666;
+ padding:1px;
+ background:#F9F8F7 url(/static/img/lib/jquery.contextmenu.images/cmenu-xp-bg.gif) repeat-y top left;
+}
+.context-menu-theme-xp .context-menu-separator {
+ margin:4px 2px;
+ font-size:0px;
+ border-top:1px solid #808080;
+ border-bottom:1px solid white;
+}
+.context-menu-theme-xp .context-menu-item {
+ text-align:left;
+ color:black;
+ font-family:arial;
+ font-size:11px;
+ cursor:pointer;
+}
+.context-menu-theme-xp .context-menu-item .context-menu-item-inner {
+ background:none no-repeat 2px center;
+ padding:4px 10px 4px 30px;
+}
+.context-menu-theme-xp .context-menu-item-hover .context-menu-item-inner {
+ background:#B6BDD2 none no-repeat 2px center;
+ padding:3px 9px 3px 29px;
+ border:1px solid #0A246A;
+}
+
+/* Windows Vista Theme */
+/* =================== */
+.context-menu-theme-vista {
+ background:#FAFAFA url(/static/img/lib/jquery.contextmenu.images/cmenu-vista-bg.gif) repeat-y left top;
+ border:1px solid #868686;
+}
+.context-menu-theme-vista .context-menu-item {
+ text-align:left;
+ cursor:pointer;
+ color:black;
+ font-family:Tahoma,Arial;
+ font-size:11px;
+}
+.context-menu-theme-vista .context-menu-separator {
+ margin:0px 0px 0px 32px;
+ font-size:0px;
+ border-top:1px solid #C5C5C5;
+ border-bottom:1px solid #F5F5F5;
+}
+.context-menu-theme-vista .context-menu-item-hover {
+ background:transparent url(/static/img/lib/jquery.contextmenu.images/cmenu-vista-menu-item-hover.gif) repeat-x left center;
+ border:1px solid #D7D0B3;
+}
+.context-menu-theme-vista .context-menu-item .context-menu-item-inner {
+ padding:4px 16px 4px 35px;
+ margin-left:1px;
+ background-color:none;
+ background-repeat:no-repeat;
+ background-position:3px center;
+ background-image:none;
+}
+.context-menu-theme-vista .context-menu-item-hover .context-menu-item-inner {
+ padding:3px 15px 3px 35px;
+ margin-left:0px;
+}
+.context-menu-theme-vista .context-menu-item-disabled {
+ color:#A7A7A7;
+}
+
+/* OSX Theme */
+/* ========= */
+.context-menu-theme-osx {
+ background-color:white;
+ opacity: .93;
+ filter: alpha(opacity=93);
+ zoom:1.0;
+ border:1px solid #b2b2b2;
+}
+.context-menu-theme-osx .context-menu-item {
+ text-align:left;
+ cursor:pointer;
+ color:black;
+ font-family:Lucida Grande,Arial;
+ font-weight:700;
+ font-size:12px;
+ opacity: 1.0;
+ filter: alpha(opacity=100);
+ z-index:1;
+}
+.context-menu-theme-osx .context-menu-separator {
+ margin:5px 1px 4px 1px;
+ font-size:0px;
+ border-top:1px solid #e4e4e4;
+}
+.context-menu-theme-osx .context-menu-item-hover {
+ background-color:#1C44F2;
+ color:white;
+}
+.context-menu-theme-osx .context-menu-item .context-menu-item-inner {
+ padding:2px 10px 2px 22px;
+ background-color:none;
+ background-repeat:no-repeat;
+ background-position:4px center;
+ background-image:none;
+}
+.context-menu-theme-osx .context-menu-item-disabled {
+ color:#939393;
+}
+
+/* Linux Human Theme */
+/* ================= */
+.context-menu-theme-human {
+ background:#F9F5F2;
+ border:1px solid #963;
+}
+.context-menu-theme-human .context-menu-item {
+ text-align:left;
+ cursor:pointer;
+ color:black;
+ font-family:Helvetica,DejaVu Sans,Arial;
+ font-size:12px;
+ line-height:20px;
+ height:28px;
+ border:1px solid #F9F5F2;
+ border-left:0;
+ border-right:0;
+}
+.context-menu-theme-human .context-menu-separator {
+ margin:0px 0px 0px 32px;
+ font-size:0px;
+ border-top:1px solid #C5C5C5;
+ border-bottom:1px solid #F5F5F5;
+}
+.context-menu-theme-human .context-menu-item-hover {
+ background:transparent url(/static/img/lib/jquery.contextmenu.images/cmenu-human-menu-item-hover.gif) repeat-x left center;
+ border-color:#963;
+}
+.context-menu-theme-human .context-menu-item .context-menu-item-inner {
+ padding:4px 16px 4px 35px;
+ margin-left:0px;
+ background-color:none;
+ background-repeat:no-repeat;
+ background-position:3px center;
+ background-image:none;
+}
+.context-menu-theme-human .context-menu-item-hover .context-menu-item-inner {
+}
+.context-menu-theme-human .context-menu-item-disabled {
+ color:#A7A7A7;
+}
+
+/* Gloss Theme */
+/* =========== */
+.context-menu-theme-gloss {
+ background:#f4f4f4 url(/static/img/lib/jquery.contextmenu.images/cmenu-gloss-bg.gif) repeat-y left center;
+ border:1px solid #f4f4f4;
+ padding:1px;
+ padding-right:0;
+}
+.context-menu-theme-gloss .context-menu-item {
+ text-align:left;
+ cursor:pointer;
+ color:black;
+ font-family:Helvetica,DejaVu Sans,Arial;
+ font-size:12px;
+ line-height:20px;
+ height:27px;
+ /*border:1px solid transparent;*/
+ border:1px solid #f4f4f4; /* IE6 doesn't have "transparent" -- DG */
+}
+.context-menu-theme-gloss .context-menu-separator {
+ margin:0px 0px 0px 32px;
+ font-size:0px;
+ border-top:1px solid #C5C5C5;
+ border-bottom:1px solid #F5F5F5;
+}
+.context-menu-theme-gloss .context-menu-item-hover {
+ background:transparent url(/static/img/lib/jquery.contextmenu.images/cmenu-gloss-menu-item-hover.gif) repeat-x left center;
+ color:#fff;
+ border-color:#000;
+ border-radius: 3px;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+}
+.context-menu-theme-gloss .context-menu-item .context-menu-item-inner {
+ padding:4px 16px 4px 35px;
+ margin-left:0px;
+ background-color:none;
+ background-repeat:no-repeat;
+ background-position:3px center;
+ background-image:none;
+}
+.context-menu-theme-gloss .context-menu-item-hover .context-menu-item-inner {
+}
+.context-menu-theme-gloss .context-menu-item-disabled {
+ color:#A7A7A7;
+}
+
+.context-menu-theme-gloss-cyan .context-menu-item-hover {
+ background-image:url(/static/img/lib/jquery.contextmenu.images/cmenu-gloss-cyan-menu-item-hover.gif);
+ border-color:#00c;
+}
+
+.context-menu-theme-gloss-semitransparent .context-menu-item-hover {
+ background-image:url(/static/img/lib/jquery.contextmenu.images/cmenu-item-gloss-semitransparent-menu-item-hover.png);
+ border-color:#00c;
+ background-color:#30f;
+}
+
+
diff --git a/trunk/etherpad/src/static/css/pad.css b/trunk/etherpad/src/static/css/pad.css
new file mode 100644
index 0000000..02c341f
--- /dev/null
+++ b/trunk/etherpad/src/static/css/pad.css
@@ -0,0 +1,1000 @@
+*,html.body,p { margin: 0; padding: 0; }
+html {
+ font-size: 62.5%;
+}
+div.hidden { display: none; }
+
+/*----------------------------------------------------------------*/
+/* pad */
+/*----------------------------------------------------------------*/
+body#padbody {
+ font-family: verdana, helvetica, sans-serif;
+ background: white;
+ color: black;
+}
+
+body#padbody.limwidth {
+ background: #d2d2d2 url(/static/img/apr09/backgrad.png) repeat-x left top;
+}
+
+body #padoutertable { width: 100%; }
+body.limwidth #padoutertable { width: 940px; margin-left: auto; margin-right: auto; }
+#padoutertable td#pot_main, #padoutertable td#pot_top, #padoutertable td.potshad {
+ vertical-align: top; zoom: 1; position: relative; }
+#padoutertable #pot_main { background: white; padding-left: 12px; padding-right: 12px;
+ padding-top: 2px;
+}
+
+body.fullwidth #padoutertable .potshad { display: none; }
+
+/* Achieve side drop shadows on top of background gradient using two drop shadow
+ images for each side (one with gradient in background, one to repeat down the
+ page). Each side drop shadow gets a column of the padoutertable, but split into
+ two cells, because if a single cell is used on each side with rowspan=2 with a
+ tall image in it, IE 6 chooses an unsightly initial height for the top bar.
+*/
+body.limwidth #padoutertable .potshad { width: 4px; }
+body.limwidth #padoutertable .potshad div { height: 200px; }
+body.limwidth #padoutertable #pot_shadleft {
+ background: url(/static/img/apr09/shadleft.png) repeat-y right top; }
+body.limwidth #padoutertable #pot_shadleft div,
+body.limwidth #padoutertable #pot_shadlefttopseg {
+ background: url(/static/img/apr09/shadlefttop.png) no-repeat right top; }
+body.limwidth #padoutertable #pot_shadright {
+ background: url(/static/img/apr09/shadright.png) repeat-y left top; }
+body.limwidth #padoutertable #pot_shadright div,
+body.limwidth #padoutertable #pot_shadrighttopseg {
+ background: url(/static/img/apr09/shadrighttop.png) no-repeat left top; }
+body.limwidth #padoutertable #pot_main, body.limwidth #padoutertable #pot_top {
+ border-left: 1px solid #333;
+ border-right: 1px solid #333;
+}
+body.limwidth #padoutertable #pot_main { border-bottom: 1px solid #333; }
+
+#padoutertable #pot_top { background: #2e609e url(/static/img/apr09/topbar.gif) repeat-x left top;
+ height: 28px; vertical-align: middle;
+ border-bottom: 1px solid #333;
+ padding-left: 1px; padding-right: 1px; /* a little padding helps "active" rects not extend outside */
+}
+
+#padpage #pot_top a#headhomelink {
+ display: block; float: left;
+ height: 0; width: 88px;
+ padding-top: 28px;
+ overflow: hidden;
+ text-decoration: none;
+ background: url(/static/img/apr09/topbarlogo.gif) no-repeat left top;
+}
+#padpage #pot_top a#widthlink {
+ display: block; float: right;
+ height: 0; width: 28px;
+ padding-top: 28px;
+ overflow: hidden;
+ text-decoration: none;
+}
+
+#padpage #pot_top, #padpage #pot_top a { color: #cbd7e7; }
+#padpage #pot_top a:focus { outline: 0; } /* for firefox */
+
+body.limwidth #padpage #pot_top a#widthlink {
+ background: url(/static/img/apr09/widthfull.gif) no-repeat center 8px; }
+body.fullwidth #padpage #pot_top a#widthlink {
+ background: url(/static/img/apr09/widthlim.gif) no-repeat center 8px; }
+body.limwidth #padpage #pot_top a#widthlink:hover {
+ background: url(/static/img/apr09/widthfullactive.gif) no-repeat center 8px; }
+body.fullwidth #padpage #pot_top a#widthlink:hover {
+ background: url(/static/img/apr09/widthlimactive.gif) no-repeat center 8px; }
+
+#padpage #pot_top #headurl {
+ margin-left: 30px;
+ margin-top: 5px;
+ float: left;
+ margin-right: 20px;
+ padding: 2px;
+ height: 15px;
+ line-height: 14px;
+ font-size: 1.1em;
+}
+
+#padpage #pot_top #shareurl { font-weight: bold; }
+
+#padpage #pot_top #newpadlink {
+ display: block; float: right; margin-right: 30px; font-size: 1.0em;
+ font-weight: bold; text-decoration: none;
+ height: 0;
+ padding-top: 8px;
+ padding-bottom: 20px;
+ padding-left: 20px;
+ padding-right: 6px;
+ overflow: hidden;
+ background: url(/static/img/apr09/newpadicon.gif) no-repeat 2px 8px;
+}
+#padpage #pot_top a#newpadlink:hover {
+ text-decoration: underline; color: white;
+}
+
+/*
+body#padbody.fullwidth {
+ background: #ddd;
+}
+#padpage {
+
+}
+body.limwidth #padcontent {
+ width: 940px;
+ margin-left: auto;
+ margin-right: auto;
+}
+#padpage #padhead {
+ height: 38px;
+ text-align: center;
+ margin-bottom: 4px;
+}
+#padpage #padhead_inner {
+ width: 938px;
+ margin-left: auto;
+ margin-right: auto;
+ padding: 0 1px;
+ background: url(/static/img/oct/minitopback2.gif) repeat center top;
+ border: 1px solid #666;
+ border-top: 0;
+}
+#padpage #padhead a#headhomelink {
+ display: block;
+ float: left;
+ height: 0;
+ width: 154px;
+ padding-top: 37px;
+ overflow: hidden;
+ text-decoration: none;
+ background: url(/static/img/oct/minitoplogo2.gif) no-repeat left top;
+}
+#padpage #headnewpad {
+ float: right;
+}
+#padpage #headnewpad #newbutton {
+ margin-top: 5px;
+ margin-right: 20px;
+}
+#padpage #headurl {
+ margin-left: 70px;
+ margin-top: 5px;
+ float: left;
+ font-size: 1.4em;
+ margin-right: 20px;
+ padding: 5px 5px 5px 5px;
+ height: 17px;
+ line-height: 17px;
+}
+#padpage #headurl label {
+ font-weight: bold;
+ font-family:"Lucida Grande", Tahoma, Arial, Verdana, sans-serif;
+ font-size:1em;
+ position: relative;
+ top: 1px;
+}
+#padpage #headurl #shareurl {
+ border: 1px solid #999;
+ padding: 3px;
+}
+#padpage #padtablediv {
+ margin: 0;
+}
+body.limwidth #padpage #padtablediv {
+ margin: 0 20px;
+}
+*/
+
+#padpage #padtable {
+ width: 100%;
+}
+#padpage #padtable td#topbar {
+ height: 16px;
+}
+#padpage #padtable div#topbarmsg {
+ display: none;
+ float: left;
+ color: #642;
+ font-size: 1.0em;
+ padding-top: 2px;
+ padding-left: 4px;
+ border-left: 1px solid #ccc;
+}
+
+#padpage #topbar #connectionstatus {
+ float: left;
+ padding-top: 2px;
+ padding-right: 4px;
+ padding-left: 16px;
+ height: 16px;
+ font-size: 1.0em;
+ color: #666;
+}
+#padpage #topbar .connecting {
+ background: url(/static/img/pad/animated-orb-orange-12.gif) no-repeat left center;
+}
+#padpage #topbar .connected {
+ background: url(/static/img/pad/orb-greenred-12.gif) no-repeat left 3px;
+}
+#padpage #topbar .disconnected {
+ background: url(/static/img/pad/orb-greenred-12.gif) no-repeat left -17px;
+}
+#padpage #padtable a.showhide {
+ display: block;
+ margin: 0;
+ font-size: 1em;
+ text-decoration: none;
+}
+#padpage #padtable a.showhide:hover {
+ text-decoration: underline;
+}
+#padpage #padtable a.showhide, #padpage #padtable a.showhide:visited {
+ color: #66f;
+}
+#padpage #padtable a#showsidebar {
+ float: right;
+ display: none;
+ position: relative;
+ top: 2px;
+}
+#padpage #padtable a#hidesidebar {
+ float: left;
+ display: none;
+}
+/*#padpage #editorcontainer {
+ display: none;
+}*/
+/*#padpage #toptoolbar {
+ display: none;
+}*/
+#padpage .editorcell_loaded #editorcontainer {
+ background: #fff;
+ overflow: hidden;
+ display: block;
+}
+#padpage #toptoolbar {
+ border-bottom: 1px solid #666;
+ height: 30px;
+ background: #eee;
+ display: block;
+ position: relative; /* make it an offsetParent for padtitle stuff */
+ overflow: hidden;
+}
+#padpage #bottoolbar {
+ border-top: 1px solid #666;
+ height: 30px;
+ background: #eee;
+ display: none; /* set in pad.js */
+}
+#padpage #bottoolbar #viewzoom {
+ padding-left: 5px;
+ padding-top: 5px;
+ padding-right: 5px;
+ float: left;
+}
+#padpage #bottoolbar #viewfont {
+ padding-left: 5px;
+ padding-top: 5px;
+ padding-right: 5px;
+ float: left;
+}
+#padpage #editorcell {
+ border: 1px solid #666;
+}
+#padpage #toptoolbar a.toptoolbarbutton {
+ float: left;
+ height: 20px;
+ width: 20px;
+ border: 1px solid #999;
+ background-color: #eee;
+ background-repeat: no-repeat;
+ background-position: center center;
+ margin-top: 5px;
+ text-decoration: none;
+}
+#padpage #toptoolbar.disabledtoolbar a.toptoolbarbutton {
+ opacity: 0.5;
+ filter: alpha(opacity = 50); /* IE */
+ zoom: 1;
+ cursor: auto;
+}
+#padpage #toptoolbar #padtitle {
+ float: left;
+ margin-top: 5px;
+ margin-left: 20px;
+ line-height: 20px;
+ width: 400px;
+ height: 20px;
+ overflow: hidden;
+ display: none;
+}
+#padpage #toptoolbar .padtitlepad {
+ font-style: italic;
+ color: #666;
+ font-size: 1.2em;
+}
+#padpage #toptoolbar #padtitletitle {
+ font-weight: bold;
+ font-size: 1.2em;
+}
+#padpage #toptoolbar .editlink {
+ font-size: 1em;
+ color: #666;
+}
+#padpage #toptoolbar .oklink {
+ display: none;
+ z-index: 2;
+ position: absolute;
+ line-height: 20px;
+ font-size: 1em;
+}
+#padpage #toptoolbar #padtitleedit {
+ z-index: 2;
+ position: absolute;
+ left: 0;
+ top: 0;
+ display: none;
+}
+#padpage #toptoolbar a:focus {
+ outline: 0;
+}
+#padpage #toptoolbar .bold { background-image: url(/static/img/may09/bold.gif); }
+#padpage #toptoolbar .italic { background-image: url(/static/img/may09/italic.gif); }
+#padpage #toptoolbar .underline { background-image: url(/static/img/may09/underline.gif); }
+#padpage #toptoolbar .undo { background-image: url(/static/img/may09/undo.gif); }
+#padpage #toptoolbar .redo { background-image: url(/static/img/may09/redo.gif); }
+#padpage #toptoolbar .bold, #padpage #toptoolbar .undo {
+ margin-left: 5px;
+}
+#padpage #toptoolbar #passwordlock {
+ float: right;
+ margin-top: 5px;
+ margin-right: 5px;
+ width: 22px;
+ height: 22px;
+ text-decoration: none;
+}
+#padpage #toptoolbar a#passwordlock:hover {
+ background-color: #ffffee;
+}
+#padpage #toptoolbar .passwordhidden { display: none; }
+#padpage #toptoolbar .passwordlocked {
+ display: block;
+ background: url(/static/img/may09/passwordlocked.gif) no-repeat center center;
+}
+#padpage #toptoolbar .passwordnone {
+ display: block;
+ background: url(/static/img/may09/passwordnone.gif) no-repeat center center;
+}
+#padpage #sidebarcell {}
+#padpage #sidebar {
+ width: 300px;
+ border-top: 1px solid #666;
+ border-right: 1px solid #666;
+ border-bottom: 1px solid #666;
+ background: #fff;
+ overflow: auto;
+}
+#padpage div.sidebar_loading {
+ border-left: 1px solid #666;
+}
+#padpage #editorcontainer iframe {
+ width: 100%;
+ padding:0;
+ margin:0;
+}
+#padpage #appjetfooter {
+ padding: 3px 3px;
+ font-family: Verdana, Helvetica, sans-serif;
+ font-size: 1em;
+ text-align: right;
+}
+div#djs {
+ font-family: monospace;
+ font-size: 10pt;
+ height: 300px;
+ overflow: scroll;
+ border: 1px solid #ccc;
+ background: #fee;
+ margin: 5px 0;
+ padding: 6px;
+}
+div#djs p { margin: 0; padding: 0; display: block; }
+#padpage a.small_link {
+ font-style: normal;
+ color: #66f;
+ text-decoration: none;
+ font-size: 1.1em;
+}
+#padpage a.small_link:hover { text-decoration: underline; }
+#padpage .editorcell_loading #editorcellinner {
+ height: 400px; /* make #sizedcontent stretch the outer table for height calc */
+}
+#padpage #editorcellinner {
+ position: relative;
+ zoom: 1;
+}
+#padpage #loadingbox {
+ padding-top: 100px;
+ padding-bottom: 100px;
+ font-size: 2.5em;
+ color: #aaa;
+ text-align: center;
+ position: absolute;
+ width: 100%;
+ height: 30px;
+ z-index: 100;
+}
+/*----------------------------------------------------------------*/
+/* userlist */
+/*----------------------------------------------------------------*/
+#sidebar div.sideheadwrap {
+ font-weight: normal;
+ font-size: 1.2em;
+ text-align: center;
+ padding: 3px 6px;
+ background: #eee url(/static/img/pad/sidehead-grad.gif) repeat-x bottom left;
+ border-bottom: 1px solid #666;
+ cursor: pointer;
+ zoom: 1;
+}
+#sidebar div.sh_hilited {
+ background-image: url(/static/img/oct/sidehead-gradhilite.gif);
+}
+#sidebar div.sideheadwrap p.sidehead {
+ display: block;
+ text-align: left;
+ padding: 0 0 0 18px;
+ margin: 0;
+}
+#sidebar div.sh_uncollapsed p.sidehead {
+ background: url(/static/img/pad/expandy-arrow6-down.gif) no-repeat center left;
+}
+#sidebar div.sh_collapsed p.sidehead {
+ background: url(/static/img/pad/expandy-arrow6-right.gif) no-repeat center left;
+}
+#sidebar div.sideheadwrap:hover {
+ cursor: pointer;
+ background: #bbb;
+}
+#sidebar div.sh_uncollapsed:hover p.sidehead {
+ background: url(/static/img/pad/expandy-arrow6-down-active.gif) no-repeat center left;
+}
+#sidebar div.sh_collapsed:hover p.sidehead {
+ background: url(/static/img/pad/expandy-arrow6-right-active.gif) no-repeat center left;
+}
+#sidebar div.sidebox { margin-bottom: 10px; }
+#sidebar div#chatbox { margin-bottom: 2px; }
+#sidebar div.sidebox_last { margin-bottom: 0; }
+#sidebar #userlist div.userbox {
+ border-bottom: 1px solid #ccc;
+}
+#sidebar #userlist div.lastuser {
+ border-bottom: 0;
+}
+#sidebar #userlist div.userbox div.userinfo {
+ font-style: italic;
+ margin-top: 3px;
+}
+#sidebar #userlist div.userbox div.userinfo span.username {
+ padding-bottom: 4px;
+ font-size: 1.2em;
+}
+#sidebar #userlist div.userbox div.userinfo div.ip {
+ color: #999;
+ font-size: 1em;
+ margin-bottom: 3px;
+}
+#sidebar #userlist div.userbox div.usercolor {
+ border: 1px solid black;
+ width: 12px;
+ height: 12px;
+ float: left;
+ margin: 6px;
+ margin-top: 3px;
+ margin-left: 0;
+}
+#sidebar #userlist div.userbox div#rightuserlink {
+ float: right;
+ text-align: right;
+ width: 120px;
+}
+#sidebar #userlist a#changenamelink {
+ padding-right: 18px;
+ background: url(/static/img/pad/pencil-icon-small-blue.gif) no-repeat top right;
+}
+#sidebar #userlist div.userinfowrap {
+ padding: 6px 0 6px 6px;
+}
+#sidebar #userlist div.myuserwrap:hover {
+ cursor: pointer;
+ background: #eee;
+}
+/*----------------------------------------------------------------*/
+/* editing my user info */
+/*----------------------------------------------------------------*/
+#userlist div.edituserinfo {
+ color: black;
+ padding: 6px 0 12px 12px;
+}
+#userlist div.edituserinfo p {
+ font-size: 1.2em;
+ margin: 8px 4px;
+}
+#userlist div.edituserinfo h4 {
+ margin-top: 1em;
+ margin-left: 4px;
+ font-size: 1.3em;
+ color: black;
+ font-weight: bold;
+}
+#userlist div.edituserinfo input {
+ width: 260px;
+}
+#userlist div.edituserinfo button {
+ margin: 8px 4px;
+}
+#colorpicker a {
+ border: 3px solid #ccc;
+ text-decoration: none;
+ display: block;
+ width: 12px;
+ height: 12px;
+ float: left;
+ margin: 4px;
+ cursor: pointer;
+}
+#colorpicker a.selectedcolor {
+ border: 3px solid black;
+}
+#colorpicker a:hover {
+ border: 3px solid black;
+}
+/*----------------------------------------------------------------*/
+/* invitemore */
+/*----------------------------------------------------------------*/
+#sidebar #invitemore {
+ display: none;
+ text-align: center;
+ margin-top: 10px;
+ font-size: 1.1em;
+}
+#sidebar #invitemore input {
+ font-size: 1.1em;
+}
+#sidebar #invitemore a {}
+#sidebar #invitemore #inviteinstructions {
+ background-color: #efe;
+ border: 1px solid #ccc;
+}
+#sidebar #invitemore #inviteinstructions p {
+ text-align: justify;
+ padding: .4em 1em;
+}
+#sidebar #invitemore #inviteinstructions p#hideinstructions {
+ text-align: center;
+}
+#sidebar #invitemore #inviteinstructions p#emailinviteleadin {
+ margin-top: .6em;
+}
+#sidebar #invite_email { width: 160px; }
+#sidebar #invitemore #inviteinstructions #invite_email_submit {}
+#sidebar #invitemore #invite_email_status { color: #642; }
+/*----------------------------------------------------------------*/
+/* prefs */
+/*----------------------------------------------------------------*/
+#sidebar div#headprefs { border-top: 1px solid #666; }
+#sidebar div#headfeedback { border-top: 1px solid #666; }
+#sidebar div#headrevisions { border-top: 1px solid #666; }
+#sidebar div#headchatbox { border-top: 1px solid #666; }
+#sidebar div#headimportexport { border-top: 1px solid #666; }
+#sidebar #prefs div.prefcheckbox {
+ margin: 0;
+ cursor: pointer;
+ border: 1px solid #fff;
+ font-size: 1em;
+}
+#sidebar #prefs div.prefcheckbox td.checkboxcell {
+ padding: 0 4px;
+}
+#sidebar #prefs div.prefcheckbox td.labelcell {
+ padding: 3px 4px;
+}
+#sidebar #prefs div.prefcheckbox:hover {
+ cursor: pointer;
+ background-color: #def;
+ border: 1px solid #aaa;
+}
+/*----------------------------------------------------------------*/
+/* revisions */
+/*----------------------------------------------------------------*/
+#sidebar #revisions {
+ text-align: center;
+}
+#sidebar #revisionlist {}
+#sidebar #revisions input#savenow {
+ width: 260px;
+ margin-left: auto; margin-right: auto;
+ margin-top: 6px; margin-bottom: 6px;
+}
+#sidebar #revisions .revisioninfo {
+ text-align: left;
+ font-size: 1.1em;
+ border-top: 1px solid #ccc;
+ padding: 3px 2px 3px 6px;
+}
+#sidebar #revisions .revisioninfo .ractions {
+ color: #aaa;
+ font-size: 1em;
+}
+#sidebar #revisions .revisioninfo .rleft {
+ width: 96px;
+ float: left;
+}
+#sidebar #revisions .revisioninfo .editrlabel {
+ padding-left: 16px;
+ background: url(/static/img/pad/pencil-icon-small-blue.gif) no-repeat center left;
+}
+#sidebar #revisions .revisioninfo .rright {
+ margin-left: 18px;
+ color: #777;
+ font-style: italic;
+}
+#sidebar #revisions .revisionbottomlinks {
+ border-top: 1px solid #eee;
+ padding-top: 5px;
+ color: #888;
+ font-size: 1.1em;
+}
+#sidebar #revisions #nosaveprivs {
+ display: none;
+ color: #282;
+ font-size: 1.2em;
+ padding: 1em;
+}
+#sidebar #revisions p.revlabelprompt {
+ color: #444;
+ padding: 2px;
+}
+#sidebar #revisions input.inputrevlabel {
+ display: block;
+ width: 260px;
+ margin-left: auto; margin-right: auto;
+ margin-top: 6px; margin-bottom: 6px;
+ border: 1px solid #ccf;
+}
+
+/*----------------------------------------------------------------*/
+/* feedback */
+/*----------------------------------------------------------------*/
+#sidebar #feedback {
+ background: #eee;
+ padding: 1px 8px; /* non-zero padding so that background extends */
+ border-bottom: 1px solid #bbb;
+ text-align: center;
+ padding-bottom: 12px;
+}
+#sidebar #feedback p {
+ font-size: 1.1em;
+ margin: 10px 0;
+ color: #333;
+ text-align: justify;
+}
+#sidebar #feedback p em {
+ font-weight: bold;
+ font-style: italic;
+}
+#sidebar #feedback #formbox {
+ width: 260px;
+ margin-left: auto;
+ margin-right: auto;
+ zoom: 1;
+ positive: relative;
+}
+#sidebar #feedback textarea {
+ width: 100%;
+ margin-left: auto;
+ margin-right: auto;
+ height: 100px;
+}
+#sidebar #feedbacksubmit {
+ width: 100%;
+ text-align: center;
+ margin-left: auto;
+ margin-right: auto;
+}
+#sidebar #feedbackresult {
+ display: none;
+}
+/*----------------------------------------------------------------*/
+/* other */
+/*----------------------------------------------------------------*/
+a#newbutton {
+ font-family:"Lucida Grande", Tahoma, Arial, Verdana, sans-serif;
+ font-size:1.2em;
+ line-height:1.0;
+ margin:0 0 0 0;
+ text-decoration:none;
+
+ background-color:#eee;
+ border:1px solid #999;
+ border-top: 1px solid #bbb;
+ border-bottom: 1px solid #666;
+
+ padding:5px 5px 3px 5px;
+
+ cursor:pointer;
+ font-weight:bold;
+ color:#555;
+ display: block;
+}
+a#newbutton:hover {
+ background-color: #ddd;
+ border: 1px solid #999;
+ color: #333;
+}
+a#newbutton img {
+ padding:0;
+ padding-bottom: 2px;
+ border:none;
+ width: 16px;
+ height: 16px;
+ vertical-align: middle;
+}
+
+#framedpage #notice {
+ padding: 40px 20px;
+}
+#framedpage #notice p {
+ margin: 16px 0;
+}
+
+img#plane { border: 0; vertical-align: middle; }
+
+/*----------------------------------------------------------------*/
+/* top msgs */
+/*----------------------------------------------------------------*/
+
+div.topmsg {
+ zoom: 1;
+ margin: 5px 0;
+ position: relative;
+}
+div.topmsg a#hidetopmsg {
+ position: absolute;
+ right: 5px;
+ bottom: 5px;
+}
+
+div#bigtoperror_wrap {
+ border: 1px solid #a66;
+ background: #fdd;
+ font-size: 1.2em;
+ padding: 1em;
+ padding-bottom: .5em;
+}
+div#bigtoperror_wrap p {
+ margin-bottom: 6px;
+ color: #222;
+}
+div#bigtoperror_wrap p.whynote {
+ color: #444;
+}
+div#bigtoperror_wrap p.whynote a {
+ color: #33f;
+}
+div#bigtoperror_wrap button.forcereconnect {
+ margin-top: 6px;
+}
+div#bigtoperror_wrap a { color: #00a; }
+div#bigtoperror_wrap a:visited { color: #00a; }
+
+div#servermsg {
+ position: relative;
+ border: 1px solid #992;
+ background: #ffc;
+ padding: 1em;
+}
+
+/*----------------------------------------------------------------*/
+/* chat */
+/*----------------------------------------------------------------*/
+#chatbox {}
+#chatbox #chatmessages {
+ margin: 0;
+ margin-bottom: 2px;
+ height: 160px;
+ border: 1px solid #ccc;
+ overflow: auto;
+}
+#chatbox div.chatmessage { padding: 2px 0; }
+#chatbox div.chatusermessage0 { background-color: #eee; }
+#chatbox span.chatname { font-style: italic; }
+#chatbox span.chattime { font-style: italic; color: #444; }
+#chatbox span.chatline { color: #222; }
+#chatbox input#chatinput { width: 100%; }
+#chatbox #chatsaytable {
+ width: 270px;
+ margin-left: auto;
+ margin-right: auto;
+ padding: 0;
+ border-spacing: 0;
+}
+#chatbox #chatsaytable td { padding: 0 2px; }
+
+/*----------------------------------------------------------------*/
+/* import/export */
+/*----------------------------------------------------------------*/
+
+#importexport td.exportpic a img {
+ border: 0;
+}
+
+#importexport .exportspinner {
+ display: none;
+}
+
+#importexport .exportspinner img {
+ margin-left: 7px;
+}
+
+#importexport a.disabledexport {
+ color: gray;
+}
+
+#importexport {
+ font-size: 1em;
+ font-family: verdana, helvetica, sans-serif;
+}
+
+#importexport .exportlink {
+ margin: 2px 0;
+}
+
+#importexport td.labelcell {
+ padding-left: 4px;
+}
+
+#importexport td.firsttd {
+ padding-left: 10px;
+}
+
+#importexport td.secondtd {
+ padding-left: 50px;
+}
+
+#importexport #headexport {
+ font-size: 1.1em;
+ margin: 7px;
+ margin-top: 10px;
+}
+
+#importexport #importsection {
+ border-top: 1px solid #ccc;
+ margin-top: 5px;
+ padding-top: 3px;
+}
+
+#importexport .importformdiv {
+ padding: 5px 15px;
+}
+
+#importexport #importformsubmitdiv {
+ margin-top: 5px;
+}
+
+.importformenabled {
+ background: #cfc;
+ border-top: 1px solid #292;
+ border-bottom: 1px solid #292;
+}
+
+#importexport #headimport {
+ font-size: 1.1em;
+ margin: 7px;
+}
+
+#importexport .importmessage {
+ display: none;
+ border: 1px solid #992;
+ background: #ffc;
+ padding: 5px;
+}
+
+#importexport #exportmessage {
+ display: none;
+ border: 1px solid #992;
+ background: #ffc;
+ padding: 5px;
+ margin: 10px 15px;
+}
+
+#importexport #importmessagefail {
+ margin-top: 10px;
+ margin-bottom: 5px;
+}
+
+#importexport #importmessagesuccess {
+ margin: 0 20px;
+}
+
+#importexport #importstatusball {
+ display: none;
+ padding-bottom: 3px;
+}
+
+#importexport #importarrow {
+ display: none;
+ margin-left: 5px;
+}
+
+span.nowrap {
+ white-space: nowrap;
+}
+
+#sidebar #prefs div.prefcheckbox {
+ margin: 0;
+ cursor: pointer;
+ border: 1px solid #fff;
+ font-size: 1em;
+}
+#sidebar #prefs div.prefcheckbox td.checkboxcell {
+ padding: 0 4px;
+}
+#sidebar #prefs div.prefcheckbox td.labelcell {
+ padding: 3px 4px;
+}
+#sidebar #prefs div.prefcheckbox:hover {
+ cursor: pointer;
+ background-color: #def;
+ border: 1px solid #aaa;
+}
+
+/*----------------------------------------------------------------*/
+/* modal dialogs */
+/*----------------------------------------------------------------*/
+
+#modaloverlay {
+ position: absolute;
+ z-index: 100;
+ background-image: url(/static/img/apr09/black35.png);
+ zoom: 1;
+ display: none;
+ left: 0; top: 0;
+ width: 100%;
+}
+
+* html #modaloverlay { /* for IE 6+ */
+ background-color: transparent;
+ background-image: url(/static/img/apr09/blank.gif);
+ filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="/static/img/apr09/black35.png", sizingMethod="scale");
+}
+
+#modaldialog {
+ border: 1px solid #333;
+ background: #ddd;
+ width: 400px;
+ margin-left: auto;
+ margin-right: auto;
+ position: relative;
+}
+
+#dialogtopbar {
+ height: 18px;
+ border-bottom: 1px solid #333;
+ background: #2e609e url(/static/img/apr09/modalbar.gif) repeat-x left top;
+ color: #cbd7e7;
+ font-size: 1.0em;
+ font-weight: bold;
+ line-height: 18px;
+ padding-left: 10px;
+ cursor: default;
+}
+
+td#dialogcontent {
+ padding: 10px;
+ height: 100px;
+ vertical-align: top;
+}
+
+table#dialogcontenttable { width: 100%; }
diff --git a/trunk/etherpad/src/static/css/pad2_ejs.css b/trunk/etherpad/src/static/css/pad2_ejs.css
new file mode 100644
index 0000000..253b8e2
--- /dev/null
+++ b/trunk/etherpad/src/static/css/pad2_ejs.css
@@ -0,0 +1,889 @@
+
+*,html.body { margin: 0; padding: 0; }
+
+h1, h2, h3, h4, h5, h6 { display: inline; line-height: 2em; }
+
+.clear { clear: both; }
+
+html { font-size: 62.5%; }
+
+body { background: #ebebeb url(/static/img/jun09/pad/backgrad.gif) repeat-x left top; }
+body, textarea { font-family: Arial, sans-serif; }
+
+#padpage { margin-left: auto; margin-right: auto; width: 900px; }
+
+body.fullwidth #padpage { width: auto; margin-left: 6px; margin-right: 6px; min-width: 800px; }
+body.squish1width #padpage { width: 900px; }
+body.squish2width #padpage { width: 800px; }
+
+#topbar { height: 25px; background: #326cbd url(/static/img/jun09/pad/padtopback2.gif) repeat-x left top;
+ position: relative; }
+
+#topbarleft { float: left; height: 100%; overflow: hidden;
+ background: url(/static/img/jun09/pad/padtop5.png) no-repeat left top; width: 5px; }
+#topbarright { float: right; height: 100%; overflow: hidden;
+ background: url(/static/img/jun09/pad/padtop5.png) no-repeat right top; width: 5px; }
+
+.propad #topbar { background: #2c2c2c url(/static/img/jun09/pad/protop.png) repeat-x 0 -25px; }
+.propad #topbarleft { background: url(/static/img/jun09/pad/protop.png) no-repeat left top; }
+.propad #topbarright { background: url(/static/img/jun09/pad/protop.png) no-repeat right top; }
+
+/*
+a#topbarnewpad { display: block; float: left; position: relative; top: 4px; width: 94px;
+ height: 0; padding-top: 26px; overflow: hidden;
+ background: url(/static/img/jun09/pad/padtop5.png) no-repeat -5px -4px; }
+a#topbarnewpad:focus { outline: 0; }
+
+a#topbarfullwidth { display: block; float: right; position: relative; top: 2px; width: 107px;
+ height: 0; padding-top: 27px; overflow: hidden;
+ background: url(/static/img/jun09/pad/padtop5.png) no-repeat -788px -2px; }
+a#topbarfullwidth:focus { outline: 0; }
+*/
+
+a#backtoprosite, #accountnav {
+ display: block; position: absolute; height: 15px; line-height: 15px;
+ width: auto; top: 5px; font-size: 1.2em;
+}
+a#backtoprosite, #accountnav a { color: #cde7ff; text-decoration: underline; }
+
+a#backtoprosite { padding-left: 20px; left: 6px;
+ background: url(/static/img/jun09/pad/protop.png) no-repeat -5px -6px; }
+#accountnav { right: 10px; color: #fff; }
+
+#topbarcenter { margin-left: 150px; margin-right: 150px; }
+a#topbaretherpad { margin-left: auto; margin-right: auto; display: block; width: 127px;
+ position: relative; top: 0px; height: 0; padding-top: 25px;
+ background: url(/static/img/jun09/pad/padtop5.png) no-repeat -397px 0px; overflow: hidden; }
+
+.propad a#topbaretherpad { background: url(/static/img/jun09/pad/protop.png) no-repeat -397px 0px; }
+
+#specialkeyarea { top: 5px; left: 250px; color: yellow; font-weight: bold;
+ font-size: 1.5em; position: absolute; }
+
+#alertbar { margin-top: 6px;
+opacity: 0; filter: alpha(opacity = 0); /* IE */
+display: none;
+}
+
+#servermsg { position: relative; zoom: 1; border: 1px solid #992;
+ background: #ffc; padding: 0.8em; font-size: 1.2em; }
+#servermsg h3 { font-weight: bold; margin-right: 10px;
+ margin-bottom: 1em; float: left; width: auto; }
+#servermsg #servermsgdate { font-style: italic; font-weight: normal; color: #666; }
+a#hidetopmsg { position: absolute; right: 5px; bottom: 5px; }
+
+#shuttingdown { position: relative; zoom: 1; border: 1px solid #992;
+ background: #ffc; padding: 0.6em; font-size: 1.2em; margin-top: 6px; }
+
+#docbar { margin-top: 6px; height: 30px; position: relative; zoom: 1;
+ background: #fbfbfb url(/static/img/jun09/pad/padtopback2.gif) repeat-x 0 -31px; }
+
+#docbarleft { position: absolute; left: 0; top: 0; height: 100%;
+ overflow: hidden;
+ background: url(/static/img/jun09/pad/padtop5.png) no-repeat left -31px; width: 7px; }
+
+<% function docbarButton(name, width, imgleft, posright, hoverimgleft, openimgleft) {
+ return ("#docbar$name$-outer { width: "+width+"px; position: absolute; height: 30px; top: 0; right: "+posright+"px; "+
+ "background: url(/static/img/jun09/pad/padtop5.png) no-repeat "+(-imgleft)+"px -31px; "+
+ "/* avoid IE flicker using double background */ }"+
+ "a#docbar$name$ { display: block; height: 0; padding-top: 30px; position: absolute; width: 100%; "+
+ "overflow: hidden; background: url(/static/img/jun09/pad/padtop5.png) no-repeat "+(-imgleft)+"px -31px; "+
+ "z-index: 53; /* > .dbpanel-wrapper */} "+
+ "a#docbar$name$:focus { outline: 0; } "+
+ "a#docbar$name$:hover { background: url(/static/img/jun09/pad/docbarstates3.png) no-repeat "+(-hoverimgleft)+"px 0; } "+
+ ".docbar$name$-opening a#docbar$name$, .docbar$name$-opening a#docbar$name$:hover, .docbar$name$-closing a#docbar$name$, .docbar$name$-closing a#docbar$name$:hover, .docbar$name$-open a#docbar$name$, .docbar$name$-open a#docbar$name$:hover { "+
+ "background: url(/static/img/jun09/pad/docbarstates3.png) no-repeat "+(-openimgleft)+"px 0; } "+
+ "a#docbar$name$:hover, .docbar$name$-closing a#docbar$name$ { padding-top: 29px; } "+
+ ".docbar$name$-opening a#docbar$name$, .docbar$name$-opening a#docbar$name$:hover { "+
+ "/* opening or closing: link covers gray line below it */ "+
+ "padding-top: 30px; }"+
+ ".docbar$name$-open a#docbar$name$, .docbar$name$-open a#docbar$name$:hover { "+
+ "/* link covers gray line below it, and also top highlight of panel */ "+
+ "padding-top: 30px; }").replace(/\$name\$/g, name);
+} %>
+
+<% // include left border, not right %>
+<%= docbarButton("savedrevs", 128, 669, 103, 123, 123) %>
+<%= docbarButton("impexp", 122, 547, 231, 0, 0) %>
+<%= docbarButton("options", 109, 438, 353, 379, 379) %>
+<%= docbarButton("security", 85, 353, 462, 489, 489) %>
+
+#docbarslider-outer { width: 104px; position: absolute; height: 30px; top: 0; right: 0;
+ background: url(/static/img/jun09/pad/padtop5.png) no-repeat -796px -31px;
+ /* avoid IE flicker using double background */ }
+a#docbarslider { display: block; height: 0; padding-top: 30px; position: absolute; width: 100%;
+ overflow: hidden; background: url(/static/img/jun09/pad/padtop5.png) no-repeat -796px -31px; z-index: 53; /* > .dbpanel-wrapper */}
+
+<% /* changing the size of the title / rename area means adjusting
+ the #docbarpadtitle.width, #padtitlebuttons.left,
+ and #padtitleedit.width */ %>
+
+#docbarpadtitle { position: absolute; height: auto; left: 9px;
+ width: 280px; font-size: 1.6em; color: #444; font-weight: normal;
+ line-height: 22px; margin-left: 2px; height: 22px; top: 4px;
+ overflow: hidden; text-overflow: ellipsis /*not supported in FF*/;
+ white-space:nowrap; }
+.docbar-public #docbarpadtitle { padding-left: 22px;
+ background: url(/static/img/jun09/pad/public.gif) no-repeat left center; }
+
+#docbarrenamelink { position: absolute; top: 9px;
+ font-size: 1.1em; display: none; }
+#docbarrenamelink a { color: #999; }
+#docbarrenamelink a:hover { color: #48d; }
+#padtitlebuttons { position: absolute; width: 120px; zoom: 1;
+ height: 22px; top: 4px; left: 223px; display: none;
+ background: url(/static/img/jun09/pad/padtop5.png) -19px -35px; }
+#padtitlesave { position: absolute; display: block;
+ height: 0; padding-top: 22px; overflow: hidden;
+ width: 49px; left: 0; top: 0; }
+#padtitlecancel { position: absolute; display: block;
+ height: 0; padding-top: 22px; overflow: hidden;
+ width: 49px; right: 0; top: 0; }
+#padtitleedit { position: absolute; top: 4px; left: 5px;
+ height: 17px; padding: 2px; font-size: 1.4em;
+ background: white; border-left: 1px solid #c3c3c3;
+ border-top: 1px solid #c3c3c3;
+ border-right: 1px solid #e6e6e6; border-bottom: 1px solid #e6e6e6;
+ width: 207px; display: none;
+}
+
+#padmain { margin-top: 6px; position: relative; zoom: 1; }
+
+#padeditor { margin-right: 300px; zoom: 1; }
+.hidesidebar #padeditor { margin-right: 0; }
+
+#editbar { height: 36px;
+ background: #a5bfe2 url(/static/img/jun09/pad/editbar3.png) repeat-x left -36px; position: relative; }
+
+#editbarleft { float: left; height: 100%; overflow: hidden;
+ background: url(/static/img/jun09/pad/editbar3.png) no-repeat left top; width: 3px; }
+#editbarright { float: right; height: 100%; overflow: hidden;
+ background: url(/static/img/jun09/pad/editbar3.png) no-repeat right top; width: 3px; }
+
+#editbar a.editbarbutton {
+ display: block;
+ position: absolute;
+ height: 26px;
+ width: 26px;
+ background-image: url(/static/img/jun09/pad/editbar3.png);
+ background-color: transparent;
+ background-repeat: no-repeat;
+ text-decoration: none;
+ top: 5px;
+}
+#editbar.disabledtoolbar a.editbarbutton {
+ opacity: 0.5;
+ filter: alpha(opacity = 50); /* IE */
+ zoom: 1;
+ cursor: auto;
+}
+/*#editbar .divider { position: absolute; width: 4px; height: 15px;
+ background-image: url(/static/img/jun09/pad/editbar3.png);
+ background-color: transparent; background-repeat: no-repeat; }
+#editbar .divider1 { left: 137px; top: 11px; background-position: -137px -11px; }
+#editbar .divider2 { left: 188px; top: 11px; background-position: -188px -11px; }*/
+#editbar a:focus { outline: 0; }
+
+<% function editbarButton(name, pos, width, fromRight) {
+ width = width || 26;
+ var bposX = - (fromRight ? 600-width-pos : pos);
+ return "div#editbar a."+name+" { "+
+ (fromRight?'right':'left')+": "+pos+"px; background-position: "+
+ bposX+"px -5px; width: "+width+"px; }\n"+
+ "div#padeditor div.enabledtoolbar a."+name+":active { background-position: "+
+ bposX+"px -77px; }";
+} %>
+<%= editbarButton('bold', 7, 25) %>
+<%= editbarButton('italic', 32, 23) %>
+<%= editbarButton('underline', 55, 23) %>
+<%= editbarButton('strikethrough', 78, 24) %>
+<%= editbarButton('h1', 108, 25) %>
+<%= editbarButton('h2', 133, 23) %>
+<%= editbarButton('h3', 156, 23) %>
+<%= editbarButton('h4', 179, 23) %>
+<%= editbarButton('h5', 202, 23) %>
+<%= editbarButton('h6', 225, 24) %>
+<%= editbarButton('clearauthorship', 342) %>
+<%= editbarButton('undo', 374, 25) %>
+<%= editbarButton('redo', 399, 24) %>
+<%= editbarButton('insertunorderedlist', 255) %>
+<%= editbarButton('indent', 287, 25) %>
+<%= editbarButton('outdent', 312, 24) %>
+<%= editbarButton('save', 6, null, true) %>
+
+#editbar #syncstatussyncing { position: absolute; height: 26px; width: 26px;
+ background: url(/static/img/jun09/pad/syncing2.gif) no-repeat center center;
+ right: 38px; top: 5px; display: none; }
+#editbar #syncstatusdone { position: absolute; height: 26px; width: 26px;
+ background: url(/static/img/jun09/pad/syncdone.gif) no-repeat center center;
+ right: 38px; top: 5px; display: none; }
+
+#editorcontainerbox {
+ border-left: 1px solid #c4c4c4; border-right: 1px solid #c4c4c4;
+ border-bottom: 1px solid #c4c4c4;
+ background: #fff; overflow: hidden; position: relative;
+ zoom: 1; height: 397px; /*...initially*/ }
+
+#editorcontainer { height: 100%; }
+
+#editorcontainer iframe { width: 100%; padding: 0; margin: 0; }
+
+#editorloadingbox { padding-top: 100px; padding-bottom: 100px; font-size: 2.5em; color: #aaa;
+ text-align: center; position: absolute; width: 100%; height: 30px; z-index: 100; }
+
+#padsidebar { float: right; width: 290px; }
+.hidesidebar #padsidebar { width: 0; overflow: hidden; }
+
+#padusers { border: 1px solid #c4c4c4; background: #fafafa; position: relative; zoom: 1; }
+
+#myuser { background: #d9e7f9; padding: 5px; height: 53px; position: relative; }
+#myswatchbox { position: absolute; left: 5px; top: 5px; width: 22px; height: 22px;
+ /*border-top: 1px solid #c3cfe0; border-left: 1px solid #c3cfe0;
+ border-right: 1px solid #ecf3fc; border-bottom: 1px solid #ecf3fc;*/
+ border: 1px solid #bbb;
+ padding: 1px; background: transparent; cursor: pointer; }
+#myuser .myswatchboxhoverable, #myuser .myswatchboxunhoverable {
+ background: white;
+}
+#myuser .myswatchboxhoverable:hover {
+ background: #bbb;
+}
+#myswatch { width: 100%; height: 100%; background: transparent;/*...initially*/ }
+#mycolorpicker {
+ background: url(/static/img/jun09/pad/colorpicker.gif) no-repeat left top;
+ width: 232px; height: 76px;
+ position: absolute;
+ left: 13px; top: 13px; z-index: 101;
+ display: none;/*...initially*/
+}
+#mycolorpicker .n1 { left: 13px; }
+#mycolorpicker .n2 { left: 40px; }
+#mycolorpicker .n3 { left: 67px; }
+#mycolorpicker .n4 { left: 94px; }
+#mycolorpicker .n5 { left: 121px; }
+#mycolorpicker .n6 { left: 148px; }
+#mycolorpicker .n7 { left: 175px; }
+#mycolorpicker .n8 { left: 202px; }
+#mycolorpicker .pickerswatchouter {
+ border: 1px solid white;
+ width: 15px; height: 15px; position: absolute;
+ top: 12px;
+}
+#mycolorpicker .pickerswatch {
+ border: 1px solid #999;
+ width: 13px;
+ height: 13px;
+ position: absolute;
+ left: 0; top: 0;
+}
+#mycolorpicker .picked { border: 1px solid #666 !important; }
+#mycolorpicker .picked .pickerswatch { border: 1px solid #666; }
+#mycolorpickersave { position: absolute; left: 14px; top: 42px;
+ width: 47px; height: 0; padding-top: 20px; overflow: hidden;
+ cursor: pointer; }
+#mycolorpickercancel { position: absolute; left: 87px; top: 42px;
+ width: 44px; height: 0; padding-top: 20px; overflow: hidden;
+ cursor: pointer; }
+#myusernameform { margin-left: 35px; }
+#myusernameedit { font-size: 1.6em; color: #444;
+ padding: 3px; height: 18px; margin: 0; border: 0;
+ width: 197px; background: transparent; }
+#myusernameform input.editable { border: 1px solid #bbb; }
+#myuser .myusernameedithoverable:hover { background: white; }
+#mystatusform { margin-left: 35px; margin-top: 5px; }
+#mystatusedit { font-size: 1.2em; color: #777;
+ font-style: italic; display: none;
+ padding: 2px; height: 14px; margin: 0; border: 1px solid #bbb;
+ width: 199px; background: transparent; }
+#myusernameform .editactive, #myusernameform .editempty {
+ background: white; border-left: 1px solid #c3c3c3;
+ border-top: 1px solid #c3c3c3;
+ border-right: 1px solid #e6e6e6; border-bottom: 1px solid #e6e6e6;
+}
+#myusernameform .editempty { color: #ef641e; }
+
+#otherusers {
+ height: 100px;/*...initially*/
+ overflow: auto;
+}
+
+table#otheruserstable { display: none; }
+#nootherusers { padding: 10px; font-size: 1.2em; color: #999; font-weight: bold;}
+#nootherusers a { color: #48d; }
+
+#otheruserstable td {
+ border-bottom: 1px solid #e1e1e1;
+ height: 26px;
+ vertical-align: middle;
+ padding: 0 2px;
+}
+
+#otheruserstable .swatch {
+ border: 1px solid #999; width: 13px; height: 13px; overflow: hidden;
+ margin: 0 4px;
+}
+
+.usertdswatch { width: 1%; }
+.usertdname { font-size: 1.3em; color: #444; }
+.usertdstatus { font-size: 1.1em; font-style: italic; color: #999; }
+.usertdactivity { font-size: 1.1em; color: #777; }
+
+.usertdname input { border: 1px solid #bbb; width: 80px; padding: 2px; }
+.usertdname input.editactive, .usertdname input.editempty {
+ background: white; border-left: 1px solid #c3c3c3;
+ border-top: 1px solid #c3c3c3;
+ border-right: 1px solid #e6e6e6; border-bottom: 1px solid #e6e6e6;
+}
+.usertdname input.editempty { color: #888; font-style: italic;}
+
+#userlistbuttonarea { height: 28px; position: relative;
+ background: url(/static/img/jun09/pad/inviteshare2.gif) repeat-x 0 0; }
+#sharebutton {
+ background: url(/static/img/jun09/pad/inviteshare2.gif) no-repeat 0 -31px;
+ position: absolute; display: block; top: 3px; padding-top: 23px;
+ height: 0; overflow: hidden; width: 96px; left: 96px; }
+
+ /*#guestslabel { font-size: 1.2em; position: absolute; width: auto;
+ height: 22px; line-height: 22px; top: 4px; left: 8px; }
+#guestsmenu { font-size: 1.2em; position: absolute; left: 100px;
+ top: 5px; width: 95px; }
+.guestpolicystuff { display: none; }*/
+
+.guestprompt { border: 1px solid #ccc; font-size: 1.2em;
+ padding: 5px; color: #222; background: #ffc; }
+.guestprompt .choices { float: right; }
+.guestprompt a { margin: 0 0.5em; }
+
+#hdraggie {
+ background: url(/static/img/jun09/pad/hdraggie.gif) repeat-x center top;
+ height: 10px; cursor: S-resize; }
+
+#padchat { border: 1px solid #c4c4c4; }
+
+#chattop { background: #ecf2fa; padding: 5px; font-size: 1.2em; border-bottom: 1px solid #ddd; }
+#chattop a { color: #36b; }
+#chatlines { height: 198px;/*...initially*/ overflow: auto; background: #fafafa; position: relative; }
+#chatlines .chatline { color: #444; padding-left: 5px; padding-top: 2px; padding-bottom: 2px;
+ background: #ddd; overflow: hidden; }
+#chatlines .chatlinetime { display: block; font-size: 1em; color: #666; float: right; width: auto;
+ padding-right: 5px; }
+#chatlines .chatlinename, #chatlines .chatlinetext { font-size: 1.2em; }
+#chatlines h2 { margin: 0; padding-left: 5px; padding-top: 2px; padding-bottom: 2px; color: #999; font-style: italic; font-weight: bold; font-size: 1.2em; }
+#chatbottom { background: #ecf2fa; padding: 4px; }
+#chatprompt { font-size: 1.2em; color: #444; float: left; line-height: 22px; width: 35px; text-align: right; }
+#chatentryform { margin-left: 40px; }
+#chatentrybox { font-size: 1.2em; color: #444;
+ padding: 2px; height: 16px; margin: 0; border-left: 1px solid #c3c3c3;
+ border-top: 1px solid #c3c3c3;
+ border-right: 1px solid #e6e6e6; border-bottom: 1px solid #e6e6e6;
+ width: 230px; }
+#padchat a#chatloadmore { display: none; font-size: 1.2em; padding: 2px 5px; font-style: italic; }
+#padchat #chatloadingmore { display: none; font-size: 1.2em; padding: 2px 5px; font-style: italic;
+ color: #999; }
+#padchat a#chatloadmore:focus { outline: 0; }
+
+#djs { font-family: monospace; font-size: 10pt;
+ height: 200px; overflow: auto; border: 1px solid #ccc;
+ background: #fee; margin: 0; padding: 6px;
+}
+#djs p { margin: 0; padding: 0; display: block; }
+
+#connectionbox {
+ position: absolute; left: 0; top: 0; width: 100%;
+ height: 191px;/*...initially; #padusers height */
+ z-index: 10; zoom: 1; overflow: hidden;
+}
+#connectionboxinner {
+ position: relative; width: 100%; height: 100%; overflow: hidden;
+}
+.cboxconnecting #connectionboxinner {
+ background: #ffd url(/static/img/jun09/pad/connectingbar.gif) no-repeat center 60px;
+}
+.cboxreconnecting #connectionboxinner {
+ background: #fed url(/static/img/jun09/pad/connectingbar.gif) no-repeat center 60px;
+}
+.cboxdisconnected #connectionboxinner {
+ background: #fdd;
+}
+.cboxdisconnected #connectionboxinner div { display: none; }
+.cboxdisconnected_userdup #connectionboxinner #disconnected_userdup { display: block; }
+.cboxdisconnected_initsocketfail #connectionboxinner #disconnected_initsocketfail { display: block; }
+.cboxdisconnected_looping #connectionboxinner #disconnected_looping { display: block; }
+.cboxdisconnected_slowcommit #connectionboxinner #disconnected_slowcommit { display: block; }
+.cboxdisconnected_unauth #connectionboxinner #disconnected_unauth { display: block; }
+.cboxdisconnected_unknown #connectionboxinner #disconnected_unknown { display: block; }
+.cboxdisconnected_initsocketfail #connectionboxinner #reconnect_advise,
+.cboxdisconnected_looping #connectionboxinner #reconnect_advise,
+.cboxdisconnected_slowcommit #connectionboxinner #reconnect_advise,
+.cboxdisconnected_unknown #connectionboxinner #reconnect_advise { display: block; }
+.cboxdisconnected div#reconnect_form { display: block; }
+.cboxdisconnected .disconnected h2 { display: none; }
+.cboxdisconnected .disconnected .h2_disconnect { display: block; }
+.cboxdisconnected_userdup .disconnected h2.h2_disconnect { display: none; }
+.cboxdisconnected_userdup .disconnected h2.h2_userdup { display: block; }
+.cboxdisconnected_unauth .disconnected h2.h2_disconnect { display: none; }
+.cboxdisconnected_unauth .disconnected h2.h2_unauth { display: block; }
+
+#connectionstatus {
+ position: absolute; width: 37px; height: 32px; overflow: hidden;
+ right: 0;
+ z-index: 11;
+}
+#connectionboxinner .connecting {
+ margin-top: 20px;
+ font-size: 2.0em; color: #555;
+ text-align: center; display: none;
+}
+.cboxconnecting #connectionboxinner .connecting { display: block; }
+
+#connectionboxinner .disconnected h2 {
+ font-size: 1.8em; color: #333;
+ text-align: left;
+ margin-top: 10px; margin-left: 10px; margin-right: 10px;
+ margin-bottom: 10px;
+}
+#connectionboxinner .disconnected p {
+ margin: 10px 10px;
+ font-size: 1.2em;
+ line-height: 1.1;
+ color: #333;
+}
+#connectionboxinner .disconnected { display: none; }
+.cboxdisconnected #connectionboxinner .disconnected { display: block; }
+
+#connectionboxinner .reconnecting {
+ margin-top: 20px;
+ font-size: 1.6em; color: #555;
+ text-align: center; display: none;
+}
+.cboxreconnecting #connectionboxinner .reconnecting { display: block; }
+
+#reconnect_form button {
+ position: relative; width: 268px; height: 28px; left: 10px;
+ font-size: 12pt;
+}
+
+/* We give docbar a higher z-index than its descendant impexp-wrapper in
+ order to allow the Import/Export panel to be on top of stuff lower
+ down on the page in IE. Strange but it works! */
+#docbar { z-index: 52; }
+
+#impexp-wrapper { width: 500px; right: 10px; }
+#impexp-panel { height: 160px; }
+.docbarimpexp-closing #impexp-wrapper { z-index: 50; }
+
+#savedrevs-wrapper { width: 100%; left: 0; }
+#savedrevs-panel { height: 79px; }
+.docbarsavedrevs-closing #savedrevs-wrapper { z-index: 50; }
+#savedrevs-wrapper .dbpanel-rightedge { background-position: 0 -10px; }
+
+#options-wrapper { width: 340px; right: 200px; }
+#options-panel { height: 114px; }
+.docbaroptions-closing #options-wrapper { z-index: 50; }
+
+#security-wrapper { width: 320px; right: 300px; }
+#security-panel { height: 130px; }
+.docbarsecurity-closing #security-wrapper { z-index: 50; }
+
+#revision-notifier { position: absolute; right: 8px; top: 25px;
+ width: auto; height: auto; font-size: 1.2em; background: #ffc;
+ border: 1px solid #aaa; color: #444; padding: 3px 5px;
+ display: none; z-index: 55; }
+#revision-notifier .label { color: #777; font-weight: bold; }
+
+/* We don't ever actually hide the wrapper, even when the panel is
+ cloased, so that its contents can always be manipulated accurately. */
+.dbpanel-wrapper { position: absolute;
+ overflow: hidden; /* animated: */ height: 0; top: 30px; /* /animated */
+ z-index: 51; zoom: 1; }
+.dbpanel-panel { position: absolute; bottom: 0; width: 100%; }
+
+.dbpanel-middle { margin-left: 7px; margin-right: 7px;
+ position: relative; height: 100%; overflow: hidden; zoom: 1; }
+.dbpanel-inner { background: #f7f7f7 /* covered up by images */;
+ width: 100%; height: 100%; position: absolute; overflow: hidden; top: -10px; }
+
+.dbpanel-top { position: absolute; top: 0; width: 100%;
+ height: 400px; background-image: url(/static/img/jun09/pad/docpanelmiddle2.png);
+ background-position: left top; }
+
+.dbpanel-bottom { position: absolute; height: 400px;
+ bottom: -390px; width: 100%;
+ background-image: url(/static/img/jun09/pad/docpanelmiddle2.png);
+ background-position: left top;
+}
+
+* html .dbpanel-top, * html .dbpanel-bottom { /* for IE 6+ */
+ background-color: transparent;
+ background-image: url(/static/img/apr09/blank.gif);
+ /* scale the image instead of repeating, but it amounts to the same */
+ filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="/static/img/jun09/pad/docpanelmiddle2.png", sizingMethod="scale");
+}
+
+.dbpanel-leftedge, .dbpanel-rightedge, .dbpanel-botleftcorner, .dbpanel-botrightcorner {
+ position: absolute;
+ background-repeat: no-repeat;
+ background-color: transparent;
+ background-image: url(/static/img/jun09/pad/docpaneledge2.png);
+}
+
+.dbpanel-leftedge, .dbpanel-rightedge { height: 100%; width: 7px; bottom: 11px; }
+.dbpanel-botleftcorner, .dbpanel-botrightcorner { height: 11px; width: 7px; bottom: 0; }
+
+.dbpanel-leftedge, .dbpanel-botleftcorner { left: 0; background-position: -7px 0; }
+.dbpanel-rightedge, .dbpanel-botrightcorner { right: 0; background-position: 0 0; }
+
+#importexport { position: absolute; top: 5px; left: 0; font-size: 1.2em; color: #444;
+ height: 100%; width: 100%; }
+
+* html .dbpanel-leftedge, * html .dbpanel-rightedge, * html .dbpanel-botleftcorner, * html .dbpanel-botrightcorner {
+ background-color: transparent;
+ background-image: url(/static/img/apr09/blank.gif);
+ /* crop the image */
+ filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="/static/img/jun09/pad/docpaneledge2.png", sizingMethod="crop");
+}
+* html .dbpanel-leftedge, * html .dbpanel-botleftcorner { left: -7px; width: 14px; }
+
+#impexp-importlabel { position: absolute; top: 5px; left: 10px; width: 300px; }
+
+#importform { position: absolute; top: 24px; left: 5px; width: 300px; height: 60px; }
+#importformsubmitdiv, #importformfilediv { padding: 5px 5px; }
+#importexport .importformenabled {
+ background: #cfc;
+ border: 1px solid #292;
+}
+#importexport span.nowrap { white-space: nowrap; }
+#importexport #importstatusball { margin-left: 3px; padding-top: 1px; display: none; }
+#importexport #importarrow { margin-left: 5px; padding-top: 1px; display: none; }
+#importexport .importmessage { border: 1px solid #992;
+ background: #ffc; padding: 5px; font-size: 85%; display: none; }
+#importexport #importmessagefail { margin-top: 5px; }
+#importexport #importmessagesuccess { margin: 0 20px; }
+#importexport a.disabledexport {
+ color: #333; text-decoration: none;
+ opacity: 0.5; filter: alpha(opacity = 50) /*IE*/;
+}
+#importexport #importfileinput { padding: 2px 0; }
+#importexport #importsubmitinput { padding: 2px; }
+
+#impexp-divider { position: absolute; left: 320px; top: 5px; height: 135px; width: 2px;
+ background: #ddd; }
+#impexp-close { display: block; position: absolute; right: 2px; bottom: 15px;
+ width: auto; height: auto; font-size: 85%; color: #444;
+ z-index: 61 /* > clickcatcher */}
+#impexp-disabled-clickcatcher {
+ display: none;
+ position: absolute; width: 100%; height: 100%;
+ z-index: 60;
+}
+
+#impexp-exportlabel { position: absolute; top: 5px; left: 350px;
+ width: 300px; }
+#exportlinks .exportlink {
+ display: block; position: absolute; height: 22px; width: auto;
+ background-repeat: no-repeat;
+ background-image: url(/static/img/jun09/pad/fileicons.gif);
+ line-height: 22px; padding-left: 22px; padding-right: 2px;
+}
+#exportlinks .n1 { left: 350px; top: 30px; }
+#exportlinks .n2 { left: 350px; top: 57px; }
+#exportlinks .n3 { left: 350px; top: 84px; }
+#exportlinks .n4 { left: 485px; top: 30px; }
+#exportlinks .n5 { left: 485px; top: 57px; }
+#exportlinks .n6 { left: 485px; top: 84px; }
+#exportlinks .exporthrefdoc { background-position: 2px -1px; }
+#exportlinks .exporthrefhtml { background-position: 2px -25px; }
+#exportlinks .exporthreflink { background-position: 2px -49px; }
+#exportlinks .exporthrefodt { background-position: 2px -73px; }
+#exportlinks .exporthrefpdf { background-position: 2px -97px; }
+#exportlinks .exporthreftxt { background-position: 2px -121px; }
+
+#savedrevisions { position: absolute; top: 0; left: 0; font-size: 1.2em;
+ color: #444; height: 100%; width: 100%; }
+#savedrevs-scrolly { height: 75px; width: auto; margin-right: 136px;
+ overflow: hidden; position: relative; top: 1px;
+}
+#savedrevs-scrollleft { height: 100%; width: 14px; position: absolute;
+ left: 0; top: 0; cursor: pointer;
+ background: url(/static/img/jun09/pad/savedrevarrows.gif) no-repeat right top;
+}
+#savedrevs-scrollright { height: 100%; width: 14px; position: absolute;
+ right: 0; top: 0; cursor: pointer;
+ background: url(/static/img/jun09/pad/savedrevarrows.gif) no-repeat left top;
+}
+#savedrevs-scrolly .disabledscrollleft { background-position: right bottom; }
+#savedrevs-scrolly .disabledscrollright { background-position: left bottom; }
+#savedrevs-scrollouter { margin-left: 14px; margin-right: 14px;
+ width: auto; height: 100%; overflow: hidden; position: relative;
+}
+#savedrevs-scrollinner { position: absolute; width: 1px; height: 100%;
+ overflow: visible; right: 0/*...initially*/; top: 0; }
+#savedrevisions .srouterbox { width: 120px; height: 100%;
+ position: absolute; top: 0;
+}
+#savedrevisions .srinnerbox { position: relative; top: 8px;
+ height: 59px; width: auto; border-left: 1px solid #ddd;
+ padding: 0 8px 0 8px; }
+#savedrevisions a.srname { display: block; white-space: nowrap;
+ text-overflow: ellipsis /*no FF support*/; overflow: hidden;
+ text-decoration: none; color: #444; cursor: text;
+ padding: 1px; height: 14px; position: relative; left: -1px;
+ width: 100px /*specify for proper overflow in IE*/;
+}
+#savedrevisions a.srname:hover { text-decoration: none; color: #444;
+ border: 1px solid #ccc; padding: 0; }
+#savedrevisions .sractions { font-size: 85%; color: #ccc;
+ margin-top: 1px; height: 12px; }
+#savedrevisions .sractions a { text-decoration: none;
+ color: #06c; }
+#savedrevisions .sractions a:hover { text-decoration: underline; }
+#savedrevisions .srtime { color: #666; font-size: 90%;
+ white-space: nowrap; margin-top: 3px; }
+#savedrevisions .srauthor { color: #666; font-size: 90%;
+ white-space: nowrap; overflow: hidden; text-overflow: ellipsis /*no FF*/;
+}
+#savedrevisions .srtwirly { position: absolute; display: block;
+ bottom: 0; right: 10px; display: none; }
+#savedrevisions .srnameedit {
+ position: absolute;
+}
+#savedrevs-savenow { display: block; position: absolute;
+ overflow: hidden; height: 0; padding-top: 24px; width: 81px;
+ top: 22px; right: 27px;
+ background: url(/static/img/jun09/pad/savedrevsgfx2.gif) no-repeat 0 0;
+}
+#savedrevs-savenow:active { background-position: 0 -24px; }
+#savedrevs-close { display: block; position: absolute; right: 7px; bottom: 8px;
+ width: auto; height: auto; font-size: 85%; color: #444; }
+form#reconnectform { display: none; }
+
+#padoptions { position: absolute; top: 0; left: 0; font-size: 1.2em;
+ color: #444; height: 100%; width: 100%; line-height: 15px; }
+#options-viewhead { font-weight: bold; position: absolute; top: 10px; left: 15px;
+ width: auto; height: auto; }
+#padoptions label { display: block; }
+#padoptions input { padding: 0; margin: 0; }
+#options-colorscheck { position: absolute; left: 15px; top: 34px; width: 15px; height: 15px; }
+#options-colorslabel { position: absolute; left: 35px; top: 34px; }
+#options-linenoscheck { position: absolute; left: 15px; top: 57px; width: 15px; height: 15px; }
+#options-linenoslabel { position: absolute; left: 35px; top: 57px; }
+#options-fontlabel { position: absolute; left: 15px; top: 82px; }
+#viewfontmenu { position: absolute; top: 80px; left: 90px; width: 110px; }
+#options-viewexplain { position: absolute; left: 215px; top: 15px; width: 100px; height: 70px;
+ padding-left: 10px; padding-top: 10px; border-left: 1px solid #ccc;
+ line-height: 20px; font-weight: bold; color: #999; }
+#options-close { display: block; position: absolute; right: 7px; bottom: 8px;
+ width: auto; height: auto; font-size: 85%; color: #444; }
+
+#padsecurity { position: absolute; top: 0; left: 0; font-size: 1.2em;
+ color: #444; height: 100%; width: 100%; line-height: 15px; }
+#security-close { display: block; position: absolute; right: 7px; bottom: 8px;
+ width: auto; height: auto; font-size: 85%; color: #444; }
+#security-passhead { font-weight: bold; position: absolute; top: 90px; left: 15px;
+ width: auto; height: auto; }
+#security-passbody { position: absolute; left: 75px; top: 90px; }
+#security-passwordedit { height: 15px; border: 1px solid #bbb;
+ position: absolute; top: 0; left: 15px; width: 120px; }
+#security-password a { text-decoration: none; display: block;
+ width: auto; height: auto; }
+#password-savelink, #password-cancellink {position: absolute; top: 0; }
+#security-password a:hover { text-decoration: underline; }
+#password-savelink { left: 144px; color: #06c; }
+#password-cancellink { left: 180px; color: #666; }
+#password-nonedit { left: 15px; position: absolute;
+ width: 220px; top: 0; }
+#password-setlink { color: #06c; }
+#password-clearlink { color: #06c; }
+#password-display { height: 15px; width: auto; }
+#password-inedit { display: none; }
+#password-display, #password-setlink, #password-clearlink {
+ float: left; margin-right: 10px;
+}
+#password-display { font-size: 18px; }
+#security-password .nopassword #password-display { font-size: 100%; }
+#security-password .nopassword #password-clearlink { display: none; }
+#security-password .nopassword #password-setlink { left: 60px; }
+
+#security-access { position: absolute; left: 15px; width: 200px; }
+#security-accesshead { font-weight: bold; position: absolute; top: 10px;
+ left: 0; width: auto; height: auto; }
+#security-access input, #security-access label { position: absolute; }
+#security-access input { left: 10px; }
+#security-access label { left: 30px; width: 250px; }
+#access-private, #access-private-label { top: 35px; }
+#access-public, #access-public-label { top: 60px; }
+#security-access label { color: #999; }
+#security-access label strong { font-weight: normal; padding-right: 10px;
+ color: #444; }
+
+#mainmodals { z-index: 600; /* higher than the modals themselves
+ so that modals are on top in IE */ }
+
+.modalfield { font-size: 1.2em; padding: 1px; border: 1px solid #bbb;
+ position: absolute;}
+#mainmodals .editempty { color: #aaa; }
+
+<% feedbackbox = {width:400, height:270}; %>
+#feedbackbox {
+ position: absolute; display: none;
+ width: <%=feedbackbox.width%>px; height: <%=feedbackbox.height%>px;
+ left: 100px/*set in code*/; bottom: 50px;
+ z-index: 501; zoom: 1;
+}
+#feedbackbox-tl, #feedbackbox-tr, #feedbackbox-bl, #feedbackbox-br,
+#feedbackbox-hide, #feedbackbox-send, #feedbackbox-back {
+ position: absolute; display: block;
+ background-repeat: no-repeat;
+ background-image: url(/static/img/jun09/pad/feedbackbox2.gif);
+}
+#feedbackbox-tl { width: <%=feedbackbox.width-8%>px;
+ height: <%=feedbackbox.height-8%>px; left: 0; top: 0;
+ background-position: left top; }
+#feedbackbox-tr { width: 8px; height: <%=feedbackbox.height-8%>px;
+ right: 0; top: 0; background-position: right top; }
+#feedbackbox-bl { width: <%=feedbackbox.width-8%>px;
+ height: 8px; left: 0; bottom: 0;
+ background-position: left bottom; }
+#feedbackbox-br { width: 8px; height: 8px; bottom: 0; right: 0;
+ background-position: right bottom; }
+#feedbackbox-hide { width: 22px; height: 22px; right: 9px; top: 7px;
+ background-position: -569px -6px;
+}
+#feedbackbox-back { width: <%=feedbackbox.width-16%>px;
+ height: <%=feedbackbox.height-16%>px; left: 8px; top: 8px;
+ background-position: -8px -8px;
+ background-color: white; }
+#feedbackbox-contents { width: <%=feedbackbox.width-16%>px;
+ height: <%=feedbackbox.height-16%>px; left: 8px; top: 8px;
+ position: absolute; font-size: 1.4em; color: #444; }
+#feedbackbox-contentsinner { padding: 10px; }
+#feedbackbox-send { width: 50px; height: 22px; right: 15px; bottom: 15px;
+ background-position: -535px -363px;
+}
+#feedbackbox-email { left: 90px; top: 48px; width: 356px; height: auto; }
+#feedbackbox-message { left: 90px; top: 84px; width: 358px; height: 100px; }
+#feedbackbox-response { position: absolute; bottom: 15px; left: 15px;
+ width: 390px; height: auto; font-size: 1.2em; display: none; }
+#feedbackbox .goodresponse { font-weight: bold; color: green; }
+#feedbackbox .badresponse { font-weight: bold; color: red; }
+#feedbackbox p { margin-bottom: 1em; }
+#feedbackbox ul { margin: 1em 0 1em 2em }
+#feedbackbox li { padding: 0.3em 0; }
+#feedbackbox li a { display: block; font-weight: bold; }
+#feedbackbox li a:hover { background: #ffe; }
+#feedbackbox a, #feedbackbox li a:visited { color: #47b; }
+#feedbackbox tt { font-size: 110%; }
+
+<% var shareboxfull = {width:485, height:326}; %>
+#sharebox {
+ position: absolute;
+ width: 485px;
+ left: 300px/*set in code*/; top: 100px; display: none;
+ z-index: 501; zoom: 1;
+ overflow: hidden;
+ background: white; border: 1px solid #999;
+}
+#sharebox { height: 160px/*set in code*/; }
+.nonprouser #sharebox { height: 110px/*set in code*/; }
+#sharebox-inner { width: 100%; }
+#sharebox-forms { position: absolute; top: 50px; width: 100%; }
+#sharebox-hide, #sharebox-send {
+ position: absolute; background-repeat: no-repeat;
+ background-image: url(/static/img/jun09/pad/sharebox4.gif);
+}
+#sharebox-hide, #sharebox-send { display: block; }
+#sharebox-hide { width: 22px; height: 22px; right: 9px; top: 7px;
+ background-position: <%= -(shareboxfull.width-31) %>px -6px;
+}
+#sharebox-send { width: 87px; height: 22px; right: 15px; top: <%= shareboxfull.height-22-15 %>px;
+ background-position: <%= -(shareboxfull.width-87-15) %>px <%= -(shareboxfull.height-22-15) %>px;
+}
+#sharebox-url { position: absolute; left: 20px; top: 42px; width: 440px; height: 18px;
+ text-align: left; font-size: 1.3em; line-height: 18px; padding: 2px; }
+
+#sharebox-to { left: 90px; top: 117px; height: auto; width: 378px; background: #ffe; }
+#sharebox-subject { left: 90px; top: 150px; height: auto; width: 378px; font-weight: bold; }
+#sharebox-message { left: 90px; top: 182px; width: 380px; height: 90px; }
+#sharebox-response { position: absolute; bottom: 15px; left: 15px;
+ width: 350px; height: auto; font-size: 1.2em; display: none; }
+#sharebox .goodresponse { font-weight: bold; color: green; }
+#sharebox .badresponse { font-weight: bold; color: red; }
+#sharebox-dislink { position: absolute; left: 12px; top: 78px;
+ height: 22px; width: 220px; cursor: pointer;
+ background-image: url(/static/img/jun09/pad/sharedistri.gif);
+ background-repeat: no-repeat;
+ background-position: 0 5px;
+}
+.sharebox-open #sharebox-dislink { background-position: 0 -28px; }
+#sharebox-shownwhenexpanded { display: none; }
+.sharebox-open #sharebox-shownwhenexpanded { display: block; }
+
+#sharebox-pastelink { font-size: 155%; font-weight: bold;
+ top: 13px; left: 17px; position: absolute; color: #444; }
+#sharebox-orsend { font-size: 145%; font-weight: bold;
+ top: 80px; left: 31px; position: absolute; color: #444; }
+#sharebox-fieldname-to, #sharebox-fieldname-subject, #sharebox-fieldname-message {
+ position: absolute; font-weight: bold; font-size: 125%;
+ left: 15px; color: #222;
+}
+#sharebox-fieldname-to { top: 119px; }
+#sharebox-fieldname-subject { top: 152px; }
+#sharebox-fieldname-message { top: 183px; }
+
+#sharebox-stripe { position: absolute; left: 10px;
+ width: 436px; top: 8px; height: 45px; line-height: 1.2; }
+#sharebox-stripe div { padding: 5px; font-size: 130%; }
+#sharebox-stripe strong { font-weight: bold; }
+.sharebox-stripe-public { background: #cfc; }
+.sharebox-stripe-private { background: #fec; }
+.sharebox-stripe-public .private { display: none; }
+.sharebox-stripe-private .public { display: none; }
+#sharebox-stripe a { color: #06c; }
+
+.nonprouser #sharebox-stripe { display: none; }
+.nonprouser #sharebox-forms { top: 0; }
+
+#viewbarcontents { display: none; }
+#viewzoomtitle {
+ position: absolute; left: 10px; top: 4px; height: 20px; line-height: 20px;
+ width: auto;
+}
+#viewzoommenu {
+ position: absolute; top: 3px; left: 50px;
+ width: 65px;
+}
+#bottomarea { height: 28px; overflow: hidden; position: relative;
+ font-size: 1.2em; color: #444; }
+#widthprefcheck { position: absolute;
+ background-image: url(/static/img/jun09/pad/layoutbuttons.gif);
+ background-repeat: no-repeat; cursor: pointer;
+ width: 86px; height: 20px; top: 4px; right: 2px; }
+.widthprefunchecked { background-position: -1px -1px; }
+.widthprefchecked { background-position: -1px -23px; }
+#sidebarcheck { position: absolute;
+ background-image: url(/static/img/jun09/pad/layoutbuttons.gif);
+ background-repeat: no-repeat; cursor: pointer;
+ width: 86px; height: 20px; top: 4px; right: 90px; }
+.sidebarunchecked { background-position: -1px -45px; }
+.sidebarchecked { background-position: -1px -67px; }
+#feedbackbutton { display: block; position: absolute; width: 68px;
+ height: 0; padding-top: 17px; overflow: hidden;
+ background: url(/static/img/jun09/pad/bottomareagfx.gif);
+ top: 5px; right: 220px;
+}
+
+#modaloverlay {
+ z-index: 500; display: none;
+ background-image: url(/static/img/jun09/pad/overlay2.png);
+ background-repeat: repeat-both;
+ width: 100%; position: absolute;
+ height: 400px; left: 0; top: 0;
+}
+
+* html #modaloverlay { /* for IE 6+ */
+ opacity: 1; /* in case this is looked at */
+ background-image: none;
+ background-repeat: no-repeat;
+ /* scale the image */
+ filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="/static/img/jun09/pad/overlay2.png", sizingMethod="scale");
+}
diff --git a/trunk/etherpad/src/static/css/pne-manual.css b/trunk/etherpad/src/static/css/pne-manual.css
new file mode 100644
index 0000000..19f1ba0
--- /dev/null
+++ b/trunk/etherpad/src/static/css/pne-manual.css
@@ -0,0 +1,143 @@
+/* global */
+
+div.pne-manpage {
+ font-size: 1.1em;
+}
+
+.pne-manual-topnav {
+ border-bottom: 1px solid #ccc;
+ margin-bottom: 1em;
+ padding-bottom: 4px;
+}
+
+div.pne-manpage h2 {
+ color: #111;
+ border-bottom: 1px solid #111;
+}
+
+div.pne-manpage ul,ol {
+ padding-left: 2em;
+}
+div.pne-manpage li {
+ margin-top: .4em;
+}
+div.pne-manpage ol li {
+ list-style: decimal;
+ margin-top: 1em;
+}
+
+
+div.pne-manpage tt {
+ font-family: monospace;
+ font-size: 1.1em;
+ color: #040;
+ font-style: italic;
+}
+
+div.pne-manpage div.code {
+ font-family: monospace;
+ font-size: 1.0em;
+ padding: 0 1em;
+ border: 1px solid #ccc;
+ background: #eee;
+ margin: 0;
+}
+
+div.pne-manpage div.code span.prompt {
+ color: #609;
+}
+
+div.pne-manpage div.code tt {
+ font-style: normal;
+ font-size: 1.0em;
+ color: #00f;
+}
+
+div.pne-manpage div.code p {
+ line-height: 125%;
+ margin: 1em 0;
+ padding: 0;
+}
+
+/* main */
+
+div#pne-main h2 {
+ font-size: 1.4em;
+ color: black;
+ font-weight: bold;
+ border: 0;
+ margin: 0;
+ padding: 0;
+}
+div#pne-main h3 {
+ font-size: 1.1em;
+ color: #555;
+ font-style: italic;
+}
+div#pne-main h4 {
+ color: #888;
+ margin-top: 2em;
+ font-weight: bold;
+}
+div#pne-main ul {
+ padding-left: 2em;
+}
+div#pne-main ul li {
+ list-style: square;
+}
+div#pne-main p#version-notice {
+ font-size: 88%;
+ color: #333;
+ margin-top: 2em;
+ border-top: 1px solid #ccc;
+}
+
+/* configuration-guide */
+
+table#opts {
+ width: 100%;
+ border-top: 1px solid #ccc;
+ border-left: 1px solid #ccc;
+ border-right: 0;
+ border-bottom: 0;
+ margin: 2em 0 1em 0;
+}
+table#opts td, table#opts th {
+ padding: 4px 0;
+ border-right: 1px solid #ccc;
+ border-bottom: 1px solid #ccc;
+}
+table#opts td {
+ font-family: monospace;
+}
+table#opts td.desc {
+ font-family: Verdana, sans-serif;
+}
+table#opts th {
+ text-align: left;
+ font-weight: bold;
+}
+table#opts td.rowhead {
+ padding-top: 1em;
+ font-style: italic;
+ color: #222;
+ border-bottom: 1px solid #222;
+}
+
+/* changelog */
+
+div#pne-changelog h2 {
+ margin-top: 2em;
+}
+
+div#pne-changelog h3 {
+ font-weight: bold;
+ padding-left: 1em;
+ margin: 1em 0;
+}
+
+div#pne-changelog ul {
+ padding-left: 3em;
+}
+
+
diff --git a/trunk/etherpad/src/static/css/pricing.css b/trunk/etherpad/src/static/css/pricing.css
new file mode 100644
index 0000000..0b7c9d5
--- /dev/null
+++ b/trunk/etherpad/src/static/css/pricing.css
@@ -0,0 +1,153 @@
+/*----------------------------------------------------------------*/
+/* pricing */
+/*----------------------------------------------------------------*/
+
+div.pricingpage {
+}
+
+.pricingpage form#pricingcontact {
+ display: block;
+ margin: 1em 0;
+ background: #eee;
+ border: 1px solid #ccc;
+ padding: 1em;
+}
+.pricingpage form#pricingcontact p { margin: .75em 0; font-weight: bold; }
+.pricingpage form#pricingcontact ul li { list-style: none; margin: 0;}
+
+.pricingpage .eepnet-inquiry label {
+ display: block;
+ float: left;
+ width: 140px;
+ text-align: right;
+ font-weight: normal;
+ color: #444;
+}
+.pricingpage .eepnet-inquiry input.ti,
+.pricingpage .eepnet-inquiry select {
+ width: 240px;
+ margin-left: 12px;
+}
+.pricingpage .eepnet-inquiry button {
+ width: 100px;
+ margin-left: 160px;
+}
+.pricingpage div.inquiryhead {
+ font-weight: bold;
+ color: #000;
+ margin-bottom: 1em;
+ border-bottom: 2px solid #aaa;
+}
+
+.pricingpage div#errorbox, .pricingpage div#confirmbox {
+ color: #222;
+ font-weight: bold;
+ padding: 1em .5em;
+}
+.pricingpage div#errorbox {
+ background: #fee;
+ border: 1px solid #f66;
+}
+.pricingpage div#confirmbox {
+ background: #efe;
+ border: 1px solid #ccc;
+}
+
+a.pricingbox {
+ display: block;
+ height: 280px;
+ border: 1px solid #777;
+ background: #fcfcfc;
+ cursor: pointer;
+ text-align: center;
+ text-decoration: none;
+}
+
+a.pricingbox span {
+ display: block;
+}
+
+a.pricingbox span#buylink {
+ display: inline;
+ color: #004ca8;
+}
+
+a.pricingbox span#buylink:hover {
+ text-decoration: underline;
+}
+
+a.pricingbox:hover {
+ background: #e2f2ff;
+ border: 1px solid #000;
+ text-decoration: none;
+}
+
+a.pricingbox img {
+ margin: 10px 0;
+ border: 0;
+}
+
+a.pricingbox span.pricingtitle {
+ display: block;
+ margin-top: 5px;
+ margin-left: 10px;
+ font-size: 1.2em;
+ color: #119;
+ text-decoration: underline;
+}
+
+a.pricingbox span.pricingdesc {
+ display: block;
+ color: #555;
+ margin: 10px;
+ height: 4.25em;
+}
+
+a.pricingbox span.pricingcost {
+ top: 10px;
+ display: block;
+ color: #000;
+ margin: 10px;
+ font-weight: bold;
+}
+
+a.pricingbox span.pricingcost p {
+ font-weight: normal;
+}
+
+#freetrialwrap a.freetrialbox {
+ padding-top: 4px;
+ display: block;
+ border: 1px solid #ccc;
+ background: #eee;
+ cursor: pointer;
+ color: #000;
+ text-decoration: underline;
+}
+#freetrialwrap a.freetrialbox:hover {
+ background: #def;
+}
+#freetrialwrap a.freetrialbox span.freetrialtext {
+ margin-top: 7px;
+ float: left;
+}
+a.freetrialbox img {
+ border: 0;
+ float: left;
+ margin: 5px 10px;
+}
+
+a.pro-signup-button {
+ display: block
+ border: 0;
+ cursor: pointer;
+ color: #fff;
+ font-weight: bold;
+ overflow: visible;
+ padding: 0;
+ background: #70a4ec;
+ border: 1px solid #3773c6;
+ padding: 4px 6px;
+ margin-top: 4px;
+}
+
diff --git a/trunk/etherpad/src/static/css/pro-signup.css b/trunk/etherpad/src/static/css/pro-signup.css
new file mode 100644
index 0000000..b58d86d
--- /dev/null
+++ b/trunk/etherpad/src/static/css/pro-signup.css
@@ -0,0 +1,69 @@
+.pro-signup {
+}
+
+.pro-signup #about {
+ width: 400px;
+ font-size: 86%;
+ color: #333;
+}
+
+.pro-signup h1 {
+ border: 0;
+}
+
+.pro-signup h3 {
+ font-size: 1.2em;
+ font-weight: bold;
+ margin: 0 0 .75em 0;
+ color: #888;
+}
+
+form#pro-act-form {
+}
+
+div.inputdiv {
+ width: 400px;
+ float: left;
+ background: #efe;
+ padding: .75em;
+ border-right: 1px solid #999;
+}
+
+div.inputdiv p {
+ margin: .2em 0 .6em 0;
+}
+
+div.inputhelp {
+ width: 300px;
+ font-size: 86%;
+ color: #555;
+ float: left;
+ padding-left: 1em;
+ padding-top: .5em;
+}
+
+form input {
+ border: 1px solid #377ec6;
+}
+
+form button {
+ border: 0;
+ cursor: pointer;
+ color: #fff;
+ font-weight: bold;
+ overflow: visible;
+ padding: 0;
+ background: #70a4ec;
+ border: 1px solid #3773c6;
+ padding: 4px 6px;
+ margin-top: 4px;
+}
+
+div.err {
+ margin: 1em 0;
+ padding: 1em;
+ font-weight: bold;
+ border: 1px solid #500;
+ background: #fdd;
+}
+
diff --git a/trunk/etherpad/src/static/css/pro/account.css b/trunk/etherpad/src/static/css/pro/account.css
new file mode 100644
index 0000000..212a847
--- /dev/null
+++ b/trunk/etherpad/src/static/css/pro/account.css
@@ -0,0 +1,254 @@
+.account-container {
+ width: 434px;
+ margin: 0 auto;
+}
+
+#account-error {
+ margin: 1em 0;
+ padding: 1em;
+ background: #fee;
+ border: 1px solid #f66;
+ font-weight: bold;
+}
+
+#account-message {
+ margin: 1em 0;
+ padding: 1em;
+ background: #efe;
+ border: 1px solid #ccc;
+ font-weight: bold;
+}
+
+#signin-notice {
+ margin: 1em 0;
+ padding: 1em;
+ background: #fff6cc;
+ border: 1px solid #ccc;
+}
+
+/*---- blue box (general) ----*/
+/* TODO: move to different file, bluebox.css? */
+
+div.bb {
+ background: #f7f7f7;
+}
+
+div.bb div.bb-top {
+ position: relative;
+ width: 100%;
+ height: 30px;
+ background: url(/static/img/pro/box/blue-boxtop.gif) repeat-x 0 -30px;
+}
+
+div.bb div.bb-topleft {
+ position: absolute;
+ top: 0;
+ left: 0;
+ height: 30px;
+ width: 9px;
+ background: url(/static/img/pro/box/blue-boxtop.gif) no-repeat 0 0;
+}
+
+div.bb div.bb-topright {
+ position: absolute;
+ top: 0;
+ right: 0;
+ height: 30px;
+ width: 9px;
+ background: url(/static/img/pro/box/blue-boxtop.gif) no-repeat -9px 0;
+}
+
+div.bb div.bb-title {
+ color: #fff;
+ font-weight: bold;
+ line-height: 30px;
+ padding-left: 10px;
+}
+
+div.bb div.bb-in {
+ border-left: 1px solid #ccc;
+ border-right: 1px solid #ccc;
+ border-bottom: 1px solid #ccc;
+}
+
+button.bluebutton {
+ border: 0;
+ cursor: pointer;
+ color: #fff;
+ font-weight: bold;
+ overflow: visible;
+ padding: 0;
+ background: #70a4ec;
+ border: 1px solid #3773c6;
+ padding: 4px 6px;
+}
+
+button.bluebutton120 {
+ background: url(/static/img/pro/buttons/bluebutton120.gif) no-repeat;
+ width: 120px;
+ height: 26px;
+ padding: 0;
+ border: 0;
+}
+
+
+/*---- sign-in box ----*/
+
+div.bb-signin div.bb-in {
+ padding: 10px 12px 20px 12px;
+}
+
+div.bb-signin label#email-label,
+div.bb-signin label#password-label {
+ display: block;
+ float: left;
+ width: 92px;
+ margin-top: 10px;
+ font-size: 1.1em;
+ color: #333;
+ padding-top: 5px;
+}
+
+div.bb-signin input.textin,
+div.bb-signin input.passin {
+ border: 1px solid #c2c2c2;
+ background: #ffffff;
+ margin-top: 10px;
+ width: 300px;
+ font-size: 1.1em;
+ float: right;
+}
+
+div.bb-signin input#rememberMe,
+div.bb-signin label#rememberMe-label {
+ float: left;
+}
+div.bb-signin input#rememberMe {
+ margin-top: 32px;
+}
+div.bb-signin label#rememberMe-label {
+ margin-left: 10px;
+ margin-top: 32px;
+ color: #555;
+ font-size: .9em;
+ display: block;
+}
+
+div.bb-signin button.bluebutton {
+ float: right;
+ margin-top: 24px;
+}
+
+
+div.account-container div#bottom-text {
+ padding-top: 20px;
+ padding-left: 4px;
+ font-size: .9em;
+}
+div.account-container div#bottom-text a {
+ text-decoration: none;
+}
+
+#guest-signin-choice {
+ display: block;
+ border: 1px solid green;
+ background: #efe;
+ padding: 1em;
+ margin: 1em 0;
+}
+
+#account-signin-choice {
+ display: block;
+ border: 1px solid blue;
+ background: #eef;
+ padding: 1em;
+ margin: 1em 0;
+}
+
+div#guest-knock-box {
+ width: 500px;
+ margin: 0 auto;
+ border: 1px solid green;
+ background: #efe;
+ font-weight: bold;
+ padding: 1em;
+ font-size: 1.5em;
+}
+
+div#guest-knock-denied {
+ border: 1px solid red;
+ background: #fee;
+ font-weight: bold;
+ font-size: 1.5em;
+ padding: 1em;
+ margin: 0 auto;
+ width: 500px;
+ display: none;
+}
+
+/*---- recover lost password ----*/
+
+div.bb-forgotpass div.bb-in {
+ padding: 10px 12px 12px 12px;
+}
+
+div.bb-forgotpass div#instructions {
+ font-size: .8em;
+ color: #222;
+}
+
+div.bb-forgotpass label {
+ float: left;
+ width: 92px;
+ margin-top: 10px;
+ font-size: 1.1em;
+ color: #333;
+ padding-top: 5px;
+}
+
+div.bb-forgotpass input.textin {
+ border: 1px solid #c2c2c2;
+ background: #fff;
+ margin-top: 14px;
+ width: 300px;
+ float: right;
+}
+
+div.bb-forgotpass button {
+ float: right;
+ margin-top: 16px;
+}
+
+
+/*---- my account ----*/
+/* TODO: re-style this and move to different file */
+
+div.my-account {
+ width: 600px;
+}
+
+div.my-account h2 {
+ font-size: 1.2em;
+ border-bottom: 1px solid #444;
+ color: #444;
+ margin: 1em 0;
+}
+
+div.my-account table {
+ width: 500px;
+}
+
+div.my-account table .ti input {
+ width: 100%;
+}
+
+div.my-account table th {
+ width: 160px;
+}
+
+div.my-account table th,
+div.my-account table td {
+ padding: 4px 8px;
+}
+
+
diff --git a/trunk/etherpad/src/static/css/pro/framedpage-pro.css b/trunk/etherpad/src/static/css/pro/framedpage-pro.css
new file mode 100644
index 0000000..cffa58b
--- /dev/null
+++ b/trunk/etherpad/src/static/css/pro/framedpage-pro.css
@@ -0,0 +1,125 @@
+/*--- farmed page styles ---*/
+
+/*------
+ Global Container
+------*/
+
+body#framedpagebody {
+ background: #fff;
+}
+
+#container {
+ font-family: Arial, Helvetica, Calibri, sans-serif;
+ width: 920px; margin: 0 auto;
+}
+
+/*------
+ Layout
+------*/
+
+/* framed page general */
+div.fpcontent {
+ width: 888px;
+ margin: 0 auto;
+ font-size: 1.3em;
+ background-color: #fff;
+ padding-top: 1em;
+}
+
+div.fpcontent p {
+ margin: 1em 0;
+ line-height: 150%;
+}
+div.fpcontent ul {
+ list-style: disc;
+ padding-left: 2em;
+}
+div.fpcontent ul li {
+ margin: 1em 0;
+}
+
+/* top header */
+
+body.pro-withtopbar {
+ background: url(/static/img/pro/header/pro-header-plustopnav-back.gif) repeat-x top !important;
+}
+
+#pro-topbar {
+ height: 48px;
+}
+
+#pro-topbar-inner {
+ width: 888px;
+ margin: 0 auto;
+ height: 48px;
+ line-height: 48px;
+ background: url(/static/img/pro/header/pro-header-logo.png) no-repeat top center;
+}
+
+#pro-topbar div#org-name a {
+ font-size: 1.4em;
+ color: #fff;
+ vertical-align: center;
+}
+
+#pro-topbar #accountnav {
+ float: right;
+ vertical-align: center;
+ color: #fff;
+}
+
+#pro-topbar #accountnav a {
+ color: #cde7ff;
+ text-decoration: underline;
+}
+
+
+/* navigation */
+
+#pro-topnav {
+ background: url(/static/img/pro/topnav/pro-topnav-back.gif) repeat-x top;
+ height: 36px;
+}
+
+#pro-topnav-inner {
+ margin: 0 auto;
+ height: 36px;
+ width: 888px;
+}
+
+#pro-topnav ul {
+ float: left;
+}
+#pro-topnav ul li {
+ display: block;
+ height: 36px;
+ float: left;
+}
+#pro-topnav ul li a {
+ display: block;
+ line-height: 36px;
+ margin: 0 20px;
+}
+#pro-topnav ul li.topnav_home a {
+ margin-left: 0;
+}
+#pro-topnav ul li a:hover { }
+#pro-topnav ul li.selected a {
+ color: #000;
+ background: url(/static/img/pro/topnav/pro-topnav-notch.gif) no-repeat center 28px;
+}
+
+#shuttingdown { position: relative; zoom: 1; border: 1px solid #992;
+ background: #ffc; padding: 0.6em; font-size: 1.2em; margin-top: 6px; }
+
+
+/*--- framed page styles ---*/
+
+div.global-pro-notice {
+ margin: .5em 1em;
+ border: 1px solid #f84;
+ background: #ffc;
+ font-weight: bold;
+ padding: 1em;
+}
+
diff --git a/trunk/etherpad/src/static/css/pro/padlist.css b/trunk/etherpad/src/static/css/pro/padlist.css
new file mode 100644
index 0000000..13d3171
--- /dev/null
+++ b/trunk/etherpad/src/static/css/pro/padlist.css
@@ -0,0 +1,115 @@
+
+/*---- nav ----*/
+
+#padlist-nav {
+ border-top: 1px solid #ddd;
+ border-bottom: 1px solid #ddd;
+}
+
+#padlist-nav ul {
+ margin: 0;
+ padding: 0;
+ float: left;
+}
+
+#padlist-nav form {
+ float: right;
+ padding-top: 2px;
+}
+
+#padlist-nav ul li {
+ list-style: none;
+ float: left;
+ padding: 0;
+ margin: 0;
+}
+
+#padlist-nav ul li a {
+ display: block;
+ padding: 8px 12px;
+ font-size: .8em;
+}
+
+#padlist-nav ul li a.selected {
+ color: black;
+}
+
+#padlist-nav ul li a#nav-all-pads {
+ padding-left: 0;
+}
+
+/*---- showing sentence ----*/
+
+#showing-desc {
+ margin-top: 12px;
+ color: #464;
+ font-size: .8em;
+ font-style: italic;
+}
+
+/*---- table ----*/
+
+#padtable {
+ margin-top: 1em;
+ border-top: 1px solid #ccc;
+ border-left: 1px solid #ccc;
+}
+
+#padtable th {
+ font-weight: bold;
+}
+
+#padtable th,
+#padtable td {
+ border-right: 1px solid #ccc;
+ border-bottom: 1px solid #ccc;
+ padding: 4px 8px;
+}
+
+#padtable td.actions {
+ padding: 0;
+}
+
+#padtable tr:hover {
+ background: #ffffaa;
+}
+#padtable tr.toprow:hover {
+ background: inherit;
+}
+
+#padtable div.gear-drop {
+ width: 36px;
+ height: 20px;
+ background: url(/static/img/pro/padlist/gear-drop.gif) no-repeat center 4px;
+ cursor: pointer;
+ padding: 4px 8px;
+}
+
+#padtable tr.selected {
+/* background: #6670ff; */
+ background: #ffff88;
+}
+#padtable tr.selected td {
+ border-top: 1px solid black;
+ border-bottom: 1px solid black;
+ border-right: 0;
+}
+#padtable tr.selected td.first {
+ border-left: 1px solid black;
+}
+#padtable tr.selected td.last {
+ border-right: 1px solid black;
+}
+#padtable tr.selected td a {
+ color: #000;
+}
+
+div.padlist-notice {
+ border: 1px solid #ccc;
+ font-weight: bold;
+ background: #fff6cc;
+ padding: 1em;
+ margin-bottom: 1em;
+ font-size: 82.5%;
+}
+
diff --git a/trunk/etherpad/src/static/css/pro/payment-required.css b/trunk/etherpad/src/static/css/pro/payment-required.css
new file mode 100644
index 0000000..44d55b2
--- /dev/null
+++ b/trunk/etherpad/src/static/css/pro/payment-required.css
@@ -0,0 +1,39 @@
+
+#outside{
+ padding: 0 0 0 266px;
+ background: url(/static/img/pro/billing/cards-button.gif) 50px 25px no-repeat;
+}
+
+#inside {
+ margin: 0;
+ padding: 1em;
+ border-left: 1px solid #ccc;
+ background: #fff;
+}
+
+h1 {
+ font-weight: bold;
+ font-size: 1.33em;
+ border-bottom: 1px solid #ccc;
+}
+
+#message {
+ border: 1px solid #b97;
+ background: #ffe;
+ padding: 1em;
+ margin: 1em 0;
+}
+
+a.manage-billing-button {
+ display: block
+ border: 0;
+ cursor: pointer;
+ color: #fff;
+ font-weight: bold;
+ overflow: visible;
+ background: #70a4ec;
+ border: 1px solid #3773c6;
+ padding: 8px 12px;
+ margin-top: 4px;
+}
+
diff --git a/trunk/etherpad/src/static/css/pro/pro-admin.css b/trunk/etherpad/src/static/css/pro/pro-admin.css
new file mode 100644
index 0000000..e7462c9
--- /dev/null
+++ b/trunk/etherpad/src/static/css/pro/pro-admin.css
@@ -0,0 +1,343 @@
+/*----------------------------------------------------------------*/
+/* admin leftnav */
+/*----------------------------------------------------------------*/
+
+#admin-layout-table {
+ width: 100%;
+}
+
+#admin-layout-table td {
+}
+
+#admin-leftnav {
+ font-size: .81em;
+ border: 1px solid #ccc;
+ white-space: nowrap;
+ background: #eee;
+ padding: 0;
+}
+
+#admin-leftnav .leftnav-title {
+ padding: .75em .25em .25em .25em;
+ border-bottom: 1px solid #ccc;
+}
+#admin-leftnav ul {
+ padding: 0;
+ list-style: none;
+}
+
+#admin-leftnav ul ul {
+ list-style: disc;
+}
+
+#admin-leftnav li {
+ display: block;
+ width: 100%;
+ margin: 0;
+}
+
+#admin-leftnav li a {
+ display: block;
+ margin: 0;
+ padding: .5em;
+}
+
+#admin-leftnav li a:hover {
+ text-decoration: none;
+ background: #ffc;
+}
+
+#admin-leftnav li.selected a {
+ color: #000;
+ font-weight: bold;
+ background: #fff;
+}
+
+/*----------------------------------------------------------------*/
+/* admin content area */
+/*----------------------------------------------------------------*/
+
+#admin-right {
+ padding-left: 1em;
+}
+
+#admin-right h3 {
+ font-weight: bold;
+ font-size: 1.1em;
+ color: #666;
+ border-bottom: 1px solid #666;
+ margin: 1.25em 0;
+}
+
+#admin-right h3.top {
+ margin-top: 0;
+}
+
+/*----------------------------------------------------------------*/
+/* server dashboard */
+/*----------------------------------------------------------------*/
+
+#responsecodes-table {
+ border 1px solid #ccc;
+}
+#responsecodes-table td,
+#responsecodes-table th {
+ padding: .4em;
+}
+#responsecodes-table th {
+ font-weight: bold;
+ border-bottom: 1px solid #ccc;
+ padding-right: 2em;
+}
+
+/*----------------------------------------------------------------*/
+/* license manager */
+/*----------------------------------------------------------------*/
+
+div.lm-error-msg {
+ border: 1px solid #f99;
+ font-weight: bold;
+ background: #fdd;
+ padding: 0 1em;
+ margin-bottom: 1em;
+}
+
+div.lm-notice-msg {
+ border: 1px solid #ccc;
+ font-weight: bold;
+ background: #fff6cc;
+ padding: 0 1em;
+ margin-bottom: 1em;
+}
+
+#lm-status {
+ border: 1px solid #ccc;
+ padding: 1em;
+ background: #dfd;
+}
+
+#lm-status table td {
+ padding: .5em 1.5em .5em 0;
+ border-bottom: 1px solid #ccc;
+ white-space: nowrap;
+}
+
+#lm-edit-button-wrap { margin: 1em 0; }
+
+#lm-edit {
+ background: #eef;
+ border: 1px solid #ccc;
+ padding: 0 1em 1em 1em;
+}
+#lm-edit p {
+ margin: 1em 0 0 0;
+}
+#lm-edit-submit-wrap { margin: 1em 0; }
+
+#lm h3 {
+/* margin-left: 1em; */
+}
+
+/*----------------------------------------------------------------*/
+/* accountmanager */
+/*----------------------------------------------------------------*/
+
+.manage-accounts {
+ font-size: .76em;
+}
+
+.manage-accounts #message {
+ border: 1px solid #ccc;
+ background: #efe;
+ color: #666;
+ font-weight: bold;
+ padding: 1em;
+}
+
+.manage-accounts #warning {
+ border: 1px solid #ccc;
+ background: #ffd;
+ color: #333;
+ font-weight: bold;
+ padding: 1em;
+ margin-top: 1em;
+}
+
+.manage-accounts form#new-account-button {
+ margin: 1em 0;
+}
+
+table#accountlist {
+ border: 1px solid #ccc;
+ border-bottom: 0;
+}
+
+table#accountlist tr:hover {
+ background: #ffc;
+}
+
+table#accountlist th,
+table#accountlist td {
+ white-space: nowrap;
+ padding: .5em 1em .5em .5em;
+ border-bottom: 1px solid #ccc;
+}
+
+table#accountlist th {
+ font-weight: bold;
+ background-color: #eef;
+}
+
+.manage-accounts p.free-notice {
+ font-style: italic;
+ color: #162;
+}
+
+.manage-accounts p.account-tally {
+ font-style: italic;
+}
+
+/* new account form */
+
+.new-account-form {
+ border: 1px solid #ccc;
+ background: #eef;
+ padding: 0;
+ margin: 0;
+}
+
+.new-account-form .forminner {
+ padding: 1em;
+}
+
+.new-account-form div.formfield {
+ margin-top: .5em;
+ padding: 0 1em;
+}
+
+.new-account-form div.formfield label { display: block; margin-top: 1em; }
+.new-account-form div.formfield input.checkboxinput { float: left; width: 20px; }
+.new-account-form div.formfield input.textinput { display: block; width: 240px; }
+.new-account-form div.formfield input.temppassinput { display: block; width: 240px; }
+.new-account-form div.formfield label.checkboxlabel { float: left; margin-top: .333em; padding-left: .25em; }
+.newaccount .buttons-wrap { margin-left: 2em; }
+
+.newaccount #bottom-note {
+ color: #555;
+ margin-left: 2em;
+ width: 50%;
+}
+
+#error-message {
+ border: 1px solid red;
+ background: #fee;
+ padding: 1em;
+ font-weight: bold;
+ margin-bottom: 1em;
+}
+
+/* manage account page */
+
+table#manage-account {
+ border-left: 1px solid #ccc;
+ border-top: 1px solid #ccc;
+ background: #eef;
+}
+table#manage-account td,
+table#manage-account th {
+ border-right: 1px solid #ccc;
+ border-bottom: 1px solid #ccc;
+ padding: 4px 8px;
+}
+table#manage-account th {
+ text-align: right;
+}
+
+#delete-account-page div.confirm {
+ font-weight: bold;
+}
+
+#delete-account-page div.account-info {
+ border: 1px solid #555;
+ background: #fcc;
+ padding: 1em;
+ margin: 1em 0;
+ font-family: monospace;
+}
+
+#delete-account-page div.note {
+ margin-top: 1em;
+ margin-right: 222px;
+ font-size: .9em;
+ color: #555;
+}
+
+
+/*----------------------------------------------------------------*/
+/* PNE server config */
+/*----------------------------------------------------------------*/
+
+table#pne-config {
+ font-family: monospace;
+ font-size: 12px;
+ border-top: 1px solid #ccc;
+ border-left: 1px solid #ccc;
+ white-space: nowrap;
+ background: #fefefe;
+}
+
+table#pne-config th {
+ border-bottom: 2px solid #666;
+ font-weight: bold;
+}
+table#pne-config td {
+ padding: 2px;
+ border-bottom: 1px solid #ccc;
+ border-right: 1px solid #ccc;
+}
+
+table#pne-config td.key {
+ color: #009;
+ padding-right: 4px;
+}
+table#pne-config td.val { color: #420; }
+
+/*----------------------------------------------------------------*/
+/* Pro config */
+/*----------------------------------------------------------------*/
+
+div#pro-config-message {
+ border: 1px solid #ccc;
+ padding: 1em;
+ font-weight: bold;
+ margin: 1em 0;
+ background: #cfc;
+}
+
+table#t-pro-config {
+ display: block;
+ border-left: 1px solid #aaa;
+ border-right: 1px solid #aaa;
+ border-bottom: 1px solid #aaa;
+}
+
+table#t-pro-config th,
+table#t-pro-config td {
+ border-top: 1px solid #aaa;
+ padding: 1em;
+ text-align: top;
+ vertical-align: top;
+}
+
+table#t-pro-config td textarea {
+ width: 100%;
+ height: 260px;
+}
+
+table#t-pro-config th {
+ text-align: right;
+ color: #963;
+ font-weight: bold;
+}
+
+
diff --git a/trunk/etherpad/src/static/css/pro/pro-home.css b/trunk/etherpad/src/static/css/pro/pro-home.css
new file mode 100644
index 0000000..03f163a
--- /dev/null
+++ b/trunk/etherpad/src/static/css/pro/pro-home.css
@@ -0,0 +1,65 @@
+
+#welcome-msg {
+ font-size: 1.2em;
+ color: #333;
+}
+
+#homeright {
+ width: 320px;
+ float: right;
+}
+
+#homeleft {
+ width: 548px;
+ float: left;
+}
+
+#homeleft-title {
+ font-weight: bold;
+ font-size: 1.0em;
+ margin-top: 1em;
+}
+
+.news-time-sep {
+ margin-top: 2em;
+}
+
+.news-time-sep .date {
+ float: left;
+ background: #fff;
+ padding-right: 1em;
+ color: #666;
+ font-size: .9em;
+}
+
+.news-time-sep .line {
+ height: .5em;
+ border-bottom: 1px solid #ccc;
+}
+
+.news-item {
+ padding: 0 2em 0 1em;
+ font-size: .86em;
+}
+
+/*--------------------------------------------------------------------------------
+ * recent pads
+ *--------------------------------------------------------------------------------*/
+
+#recent-pads #viewall {
+ display: block;
+ float: left;
+ margin: 0.8em 0;
+ font-size: 0.8em;
+}
+
+#homeright #padtable {
+ width: 100%;
+}
+
+#homeright h3 {
+ font-size: 1.0em;
+ font-weight: bold;
+ margin-top: 1em;
+}
+
diff --git a/trunk/etherpad/src/static/css/stats.css b/trunk/etherpad/src/static/css/stats.css
new file mode 100644
index 0000000..25dd074
--- /dev/null
+++ b/trunk/etherpad/src/static/css/stats.css
@@ -0,0 +1,71 @@
+div.statentry {
+ width: 600px;
+ border: 1px solid #060;
+ background: #afa;
+ margin: 1em;
+}
+
+body {
+ margin: 0;
+}
+
+div.warning {
+ background: #ffa;
+ border: 1px solid #630;
+}
+
+div.error {
+ background: #faa;
+ border: 1px solid #600;
+}
+
+.statentry h2 {
+ font-size: 13pt;
+ font-family: sans-serif;
+ background: #0a0;
+ color: white;
+ padding: 5px;
+ margin: 0;
+ cursor: pointer;
+}
+
+.statentry h2:hover {
+ text-decoration: underline;
+}
+
+.warning h2 {
+ background: #ea0;
+}
+
+.error h2 {
+ background: #a00;
+}
+
+.statentry th {
+ padding: 3px;
+ font-weight: normal;
+}
+
+.statentry td {
+ text-align: left;
+ padding: 3px;
+ width: 400px;
+ font-size: 24px;
+}
+
+.statentry table {
+ width: 100%;
+}
+
+.statbody {
+ display: none;
+}
+
+/*div.categorywrapper {
+ -moz-column-width: 500px;
+ -moz-column-gap: 20px;
+ -webkit-column-width: 500px;
+ -webkit-column-gap: 20px;
+ column-width: 500px;
+ column-gap: 20px;
+}*/ \ No newline at end of file
diff --git a/trunk/etherpad/src/static/css/store/eepnet-checkout.css b/trunk/etherpad/src/static/css/store/eepnet-checkout.css
new file mode 100644
index 0000000..20254af
--- /dev/null
+++ b/trunk/etherpad/src/static/css/store/eepnet-checkout.css
@@ -0,0 +1,284 @@
+#shoppingmain dt {
+ margin: 0.5em;
+ margin-left: 0.5em;
+ font-weight: bold;
+ line-height: 120%;
+}
+
+#shoppingmain dd {
+ margin: 0.5em;
+ margin-top: 1em;
+ margin-bottom: 2em;
+ line-height: 120%;
+}
+
+#shoppingmain dd:first-letter,
+#shoppingmain dt:first-letter {
+ font-weight: bold;
+}
+
+#shoppingmain table {
+ background: #eef;
+ width: 100%;
+}
+
+#shoppingmain table tr {}
+#shoppingmain > table td,
+#shoppingmain > table th {
+ padding: 6px 8px;
+}
+
+#shoppingmain {
+ width: 100%;
+ border: 1px solid #ccc;
+}
+
+#shoppingmain p {
+ margin: 1em;
+}
+
+#shoppingmain a {
+ text-decoration: underline;
+}
+
+#shoppingmain ul,
+#shoppingmain ol {
+ margin-right: 1em;
+}
+
+#shoppingmain h3 {
+ background: #eee;
+ padding: 0.5em 1em;
+ border-bottom: 1px solid #ccc;
+ font-weight: bold;
+ font-size: 110%;
+}
+
+#shoppingwrapper {
+ width: 580px;
+}
+
+.shoppingcart table td.pcell, table th.pcell {
+ text-align: right;
+ white-space: nowrap;
+}
+
+#shoppingcart {
+ border: 1px solid #ccc;
+ background: white;
+}
+
+.shoppingcart table {
+ width: 100%;
+}
+
+.shoppingcart table th {
+ font-weight: bold;
+ background: #eee;
+ border-bottom: 1px solid #ccc;
+}
+
+.shoppingcart table td {
+ padding: 0 3px;
+}
+
+.shoppingcart table th {
+ padding: 5px;
+}
+
+div.checkoutnav {
+ padding-top: 10px;
+ clear: both;
+ display: block;
+ text-align: center;
+}
+
+.center,
+.center td {
+ text-align: center;
+}
+
+div.errormsg {
+ border: 1px solid #caa;
+ padding: 1em;
+ margin: 1em 0;
+}
+
+div.errormsg,
+table tr.error,
+p.error,
+div.error {
+ background: #fdd;
+}
+
+div.innererrormsg {
+ margin: 1em;
+ border: 1px solid #aca;
+ padding: 1em;
+ background: #dfd;
+}
+
+a#cschelp {
+ font-size: 7pt;
+ color: blue;
+ border-bottom: 1px dashed blue;
+}
+
+table tr.total td {
+ font-weight: bold;
+}
+
+#shoppingcart {
+ width: 250px;
+ float: right;
+ font-size: 85%;
+}
+
+.shoppingcart span.desc {
+ display: block;
+ padding-top: 4px;
+ padding-left: 10px;
+ font-style: italic;
+}
+
+table tr.withoutsubtotal td,
+table tr.subtotal td {
+ border-top: 1px solid #ccc;
+}
+
+#backbutton {
+ float: right;
+ padding: 5px;
+}
+
+#continuebutton {
+ float: right;
+ padding: 5px;
+}
+
+.shoppingcart tr.base td {
+ padding-top: 5px;
+}
+
+.shoppingcart tr.refer td {
+ color: green;
+ font-weight: bold;
+}
+
+.shoppingcart tr.support td {
+ padding-top: 10px;
+}
+
+.shoppingcart tr.referralbonus td {
+ padding-top: 10px;
+}
+
+.shoppingcart tr.spacer td {
+ padding-top: 10px;
+}
+
+.shoppingcart tr.subtotal td {
+ padding-top: 5px;
+}
+
+.shoppingcart tr.referraldiscount td {
+ padding-top: 5px;
+}
+
+.shoppingcart tr.total td {
+ padding-top: 5px;
+ padding-bottom: 5px;
+}
+
+.shoppingcart td.noitems {
+ text-align: center;
+ padding-top: 15px;
+}
+
+.paymentbutton label {
+ cursor: pointer;
+}
+
+.billingtable td.pcell {
+ width: 300px;
+}
+
+.billingtable tr.intonly {
+ display: none;
+}
+
+.billingtable #ccimages img.ccimageselected {
+ border: 3px solid #0f0;
+ opacity: 1;
+}
+
+.billingtable #ccimages img {
+ border: 3px solid #eef;
+ opacity: 0.5;
+}
+
+input[type="text"].greenborder {
+ border: 1px solid #0f0;
+}
+
+input[type="text"].redborder {
+ border: 1px solid #f00;
+}
+
+input[type="text"] {
+ border: 1px solid black;
+ padding: 2px;
+}
+
+td.tcell {
+ width: 200px;
+}
+
+#shoppingmain td {
+ font-weight: bold;
+}
+
+#shoppingmain input,
+#shoppingmain p,
+#shoppingmain td,
+#shoppingmain li {
+ font-size: 85%;
+}
+
+h4 {
+ font-weight: bold;
+ font-size: 100%;
+ margin: 10px;
+ margin-top: 20px;
+}
+
+h4 .editlink {
+ font-size: 75%;
+}
+
+span.item .editlink {
+ font-size: 75%;
+}
+
+span.desc .editlink {
+ font-size: 75%;
+ font-style: normal;
+}
+
+.paymentbutton {
+ padding-left: 1.5em;
+}
+
+div.position {
+ font-size: 80%;
+ color: #999;
+ padding-bottom: 10px;
+}
+
+div.position .current {
+ color: #f93;
+}
+
+span.poslabel {
+ padding: 0 1em;
+}
diff --git a/trunk/etherpad/src/static/css/store/ondemand-billing.css b/trunk/etherpad/src/static/css/store/ondemand-billing.css
new file mode 100644
index 0000000..7c4afe3
--- /dev/null
+++ b/trunk/etherpad/src/static/css/store/ondemand-billing.css
@@ -0,0 +1,170 @@
+input[type="text"].greenborder {
+ border: 1px solid #0f0;
+}
+
+input[type="text"].redborder {
+ border: 1px solid #f00;
+}
+
+input[type="text"] {
+ border: 1px solid black;
+ padding: 2px;
+}
+
+h4 {
+ font-weight: bold;
+ margin-bottom: 0.75em;
+ margin-top: 1em;
+ margin-left: 15px;
+}
+
+div.billinginfo {
+ margin-left: 65px;
+ border-left: 1px solid gray;
+ border-right: 1px solid gray;
+ border-top: 1px solid gray;
+ border-bottom: 1px solid gray;
+ width: 600px;
+}
+
+.billinginfo table.billingtable {
+ width: 100%;
+ margin: 0;
+ background: #eef;
+}
+
+.billinginfo table.billingtable td,
+.billinginfo table.billingtable th {
+ padding: 5px;
+}
+
+.billinginfo table.billingtable td.pcell,
+.billinginfo table.billingtable td.firstcell {
+ padding-left: 35px;
+}
+
+.billinginfo table.billingtable td.pcell {
+ width: 200px;
+}
+
+.billinginfo div {
+ padding-left: 35px;
+}
+
+.billinginfo div#billingselect {
+ padding-left: 0;
+}
+
+.billinginfo div#billingselect p {
+ padding: 3px;
+ padding-left: 35px;
+}
+
+.billingtable #ccimages img.ccimageselected {
+ border: 3px solid #0f0;
+ opacity: 1;
+}
+
+.billingtable #ccimages img {
+ border: 3px solid #eef;
+ opacity: 0.5;
+}
+
+a#cschelp {
+ font-size: 7pt;
+ color: blue;
+ border-bottom: 1px dashed blue;
+}
+
+div#contbutton {
+ margin-top: 1em;
+ margin-right: 45px;
+ float: right;
+}
+
+div#backbutton {
+ margin-top: 1em;
+ float: right;
+}
+
+div.errormsg {
+ border: 1px solid #caa;
+ padding: 1em;
+ margin: 1em 0;
+}
+
+div.errormsg,
+table tr.error,
+p.error,
+div.error {
+ background: #fdd;
+}
+
+table.billingsummary {
+ width: 100%;
+ border-top: 1px solid gray;
+ border-left: 1px solid gray;
+}
+
+table.billingsummary th,
+table.billingsummary td {
+ padding: 5px;
+ border-bottom: 1px solid gray;
+ border-right: 1px solid gray;
+}
+
+table.billingsummary th {
+ font-weight: bold;
+}
+
+span#editpaymentslink {
+ text-align: right;
+ font-size: 80%;
+ margin-left: 0.5em;
+}
+
+#editpaymentslink a {
+ text-decoration: underline;
+}
+
+
+.paymentbutton label {
+ cursor: pointer;
+}
+.paymentbutton {
+ padding-left: 1.5em;
+}
+
+/* invoice list */
+
+.informational {
+ font-style: italic;
+}
+
+table.invoicelist {
+ border-left: 1px solid #ccc;
+ border-top: 1px solid #ccc;
+ border-right: 1px solid #ccc;
+ width: 100%;
+ font-size: 10pt;
+}
+
+table.invoicelist tr:hover {
+ background: #ffc;
+}
+
+table.invoicelist td,
+table.invoicelist th {
+ border-bottom: 1px solid #ccc;
+ padding: 5px;
+}
+
+table.invoicelist th {
+ font-weight: bold;
+ background: #eef;
+}
+
+.returnlink {
+ font-size: 10pt;
+ font-style: italic;
+} \ No newline at end of file
diff --git a/trunk/etherpad/src/static/css/store/store.css b/trunk/etherpad/src/static/css/store/store.css
new file mode 100644
index 0000000..f228698
--- /dev/null
+++ b/trunk/etherpad/src/static/css/store/store.css
@@ -0,0 +1,90 @@
+div.storepage a.downloadbutton_disabled {
+ text-decoration: none;
+ display: block;
+ padding: .4em;
+ margin: 1em;
+ font-size: 1.4em;
+ width: 270px;
+ margin-left: auto;
+ margin-right: auto;
+ border: 2px solid #ccc;
+ background: #eee;
+ color: #888;
+}
+
+div.storepage a.downloadbutton {
+ text-decoration: none;
+ display: block;
+ padding: .4em;
+ margin: 1em;
+ font-size: 1.4em;
+ width: 270px;
+ margin-left: auto;
+ margin-right: auto;
+ border: 2px solid #333;
+ background: #ccc;
+}
+div.storepage a.downloadbutton:hover {
+ background: #ddf;
+ cursor: pointer;
+}
+
+div.storepage label:hover {
+ cursor: pointer;
+}
+
+div.storepage div#topmsg {
+ border: 1px solid #999;
+ background: #dfd;
+ font-weight: bold;
+ padding: 1em;
+}
+
+div.storepage div#errormsg {
+ border: 1px solid #f00;
+ background: #fee;
+ font-weight: bold;
+ padding: 1em 2em;
+ magin-top: 1em;
+ margin-bottom: 1em;
+}
+
+#dlsignup {
+ border: 1px solid #ccc;
+/* background: #ffefdf; */
+ background: #efe;
+ padding: 1em 2em;
+}
+
+#dlsignup, div#errormsg {
+ display: block;
+}
+
+#dlsignup p {
+ margin: 0;
+}
+
+#dlsignup p label {
+ display: block;
+ margin-top: .7em;
+ color: #444;
+}
+
+#dlsignup input,
+#dlsignup select {
+ width: 400px;
+}
+
+#dlsignup button {
+ margin-top: 1em;
+ width: 200px;
+ margin-left: 100px;
+}
+
+div#processingmsg {
+ border: 1px solid #ccc;
+ background: #efe;
+ padding: 1em 2em;
+ font-weight: bold;
+ font-size: 1.4em;
+}
diff --git a/trunk/etherpad/src/static/favicon.ico b/trunk/etherpad/src/static/favicon.ico
new file mode 100644
index 0000000..a833c3a
--- /dev/null
+++ b/trunk/etherpad/src/static/favicon.ico
Binary files differ
diff --git a/trunk/etherpad/src/static/img/about/appjet-logo-large.gif b/trunk/etherpad/src/static/img/about/appjet-logo-large.gif
new file mode 100644
index 0000000..11351b2
--- /dev/null
+++ b/trunk/etherpad/src/static/img/about/appjet-logo-large.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/about/appjet-logo-medium.png b/trunk/etherpad/src/static/img/about/appjet-logo-medium.png
new file mode 100644
index 0000000..f6297e1
--- /dev/null
+++ b/trunk/etherpad/src/static/img/about/appjet-logo-medium.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/about/investors/mitchkapor.jpg b/trunk/etherpad/src/static/img/about/investors/mitchkapor.jpg
new file mode 100644
index 0000000..5a65938
--- /dev/null
+++ b/trunk/etherpad/src/static/img/about/investors/mitchkapor.jpg
Binary files differ
diff --git a/trunk/etherpad/src/static/img/about/investors/pb.jpg b/trunk/etherpad/src/static/img/about/investors/pb.jpg
new file mode 100644
index 0000000..bd59c5c
--- /dev/null
+++ b/trunk/etherpad/src/static/img/about/investors/pb.jpg
Binary files differ
diff --git a/trunk/etherpad/src/static/img/about/investors/pg.jpg b/trunk/etherpad/src/static/img/about/investors/pg.jpg
new file mode 100644
index 0000000..184155d
--- /dev/null
+++ b/trunk/etherpad/src/static/img/about/investors/pg.jpg
Binary files differ
diff --git a/trunk/etherpad/src/static/img/about/investors/sanjeev.jpg b/trunk/etherpad/src/static/img/about/investors/sanjeev.jpg
new file mode 100644
index 0000000..9073b50
--- /dev/null
+++ b/trunk/etherpad/src/static/img/about/investors/sanjeev.jpg
Binary files differ
diff --git a/trunk/etherpad/src/static/img/about/investors/seth.jpg b/trunk/etherpad/src/static/img/about/investors/seth.jpg
new file mode 100644
index 0000000..00f2aa9
--- /dev/null
+++ b/trunk/etherpad/src/static/img/about/investors/seth.jpg
Binary files differ
diff --git a/trunk/etherpad/src/static/img/about/people/aaron-david-iphones-thumb.jpg b/trunk/etherpad/src/static/img/about/people/aaron-david-iphones-thumb.jpg
new file mode 100644
index 0000000..70c17db
--- /dev/null
+++ b/trunk/etherpad/src/static/img/about/people/aaron-david-iphones-thumb.jpg
Binary files differ
diff --git a/trunk/etherpad/src/static/img/about/people/aaron-david-iphones.jpg b/trunk/etherpad/src/static/img/about/people/aaron-david-iphones.jpg
new file mode 100644
index 0000000..70c17db
--- /dev/null
+++ b/trunk/etherpad/src/static/img/about/people/aaron-david-iphones.jpg
Binary files differ
diff --git a/trunk/etherpad/src/static/img/about/people/aaron-google-air.jpg b/trunk/etherpad/src/static/img/about/people/aaron-google-air.jpg
new file mode 100644
index 0000000..5948454
--- /dev/null
+++ b/trunk/etherpad/src/static/img/about/people/aaron-google-air.jpg
Binary files differ
diff --git a/trunk/etherpad/src/static/img/about/people/aaron-headshot-thumb.jpg b/trunk/etherpad/src/static/img/about/people/aaron-headshot-thumb.jpg
new file mode 100644
index 0000000..b0cffcd
--- /dev/null
+++ b/trunk/etherpad/src/static/img/about/people/aaron-headshot-thumb.jpg
Binary files differ
diff --git a/trunk/etherpad/src/static/img/about/people/aaron-headshot.jpg b/trunk/etherpad/src/static/img/about/people/aaron-headshot.jpg
new file mode 100644
index 0000000..2b88437
--- /dev/null
+++ b/trunk/etherpad/src/static/img/about/people/aaron-headshot.jpg
Binary files differ
diff --git a/trunk/etherpad/src/static/img/about/people/aaron-headshot2-thumb.jpg b/trunk/etherpad/src/static/img/about/people/aaron-headshot2-thumb.jpg
new file mode 100644
index 0000000..d6c1a97
--- /dev/null
+++ b/trunk/etherpad/src/static/img/about/people/aaron-headshot2-thumb.jpg
Binary files differ
diff --git a/trunk/etherpad/src/static/img/about/people/aaron-headshot2.jpg b/trunk/etherpad/src/static/img/about/people/aaron-headshot2.jpg
new file mode 100644
index 0000000..e4b2a77
--- /dev/null
+++ b/trunk/etherpad/src/static/img/about/people/aaron-headshot2.jpg
Binary files differ
diff --git a/trunk/etherpad/src/static/img/about/people/aaron-headshot3-thumb.jpg b/trunk/etherpad/src/static/img/about/people/aaron-headshot3-thumb.jpg
new file mode 100644
index 0000000..cca1b68
--- /dev/null
+++ b/trunk/etherpad/src/static/img/about/people/aaron-headshot3-thumb.jpg
Binary files differ
diff --git a/trunk/etherpad/src/static/img/about/people/aaron-headshot3.jpg b/trunk/etherpad/src/static/img/about/people/aaron-headshot3.jpg
new file mode 100644
index 0000000..13c433d
--- /dev/null
+++ b/trunk/etherpad/src/static/img/about/people/aaron-headshot3.jpg
Binary files differ
diff --git a/trunk/etherpad/src/static/img/about/people/daniel-headshot-thumb.jpg b/trunk/etherpad/src/static/img/about/people/daniel-headshot-thumb.jpg
new file mode 100644
index 0000000..567316c
--- /dev/null
+++ b/trunk/etherpad/src/static/img/about/people/daniel-headshot-thumb.jpg
Binary files differ
diff --git a/trunk/etherpad/src/static/img/about/people/david-headshot-thumb.jpg b/trunk/etherpad/src/static/img/about/people/david-headshot-thumb.jpg
new file mode 100644
index 0000000..5f9da44
--- /dev/null
+++ b/trunk/etherpad/src/static/img/about/people/david-headshot-thumb.jpg
Binary files differ
diff --git a/trunk/etherpad/src/static/img/about/people/david-headshot.jpg b/trunk/etherpad/src/static/img/about/people/david-headshot.jpg
new file mode 100644
index 0000000..89ab3ea
--- /dev/null
+++ b/trunk/etherpad/src/static/img/about/people/david-headshot.jpg
Binary files differ
diff --git a/trunk/etherpad/src/static/img/about/people/davy-headshot.jpg b/trunk/etherpad/src/static/img/about/people/davy-headshot.jpg
new file mode 100644
index 0000000..9430186
--- /dev/null
+++ b/trunk/etherpad/src/static/img/about/people/davy-headshot.jpg
Binary files differ
diff --git a/trunk/etherpad/src/static/img/about/people/jd-headshot-thumb.jpg b/trunk/etherpad/src/static/img/about/people/jd-headshot-thumb.jpg
new file mode 100644
index 0000000..b399a57
--- /dev/null
+++ b/trunk/etherpad/src/static/img/about/people/jd-headshot-thumb.jpg
Binary files differ
diff --git a/trunk/etherpad/src/static/img/about/people/jd-headshot.jpg b/trunk/etherpad/src/static/img/about/people/jd-headshot.jpg
new file mode 100644
index 0000000..182d534
--- /dev/null
+++ b/trunk/etherpad/src/static/img/about/people/jd-headshot.jpg
Binary files differ
diff --git a/trunk/etherpad/src/static/img/about/people/rhonda-headshot-thumb.jpg b/trunk/etherpad/src/static/img/about/people/rhonda-headshot-thumb.jpg
new file mode 100644
index 0000000..8d9358b
--- /dev/null
+++ b/trunk/etherpad/src/static/img/about/people/rhonda-headshot-thumb.jpg
Binary files differ
diff --git a/trunk/etherpad/src/static/img/about/people/rhonda-headshot.jpg b/trunk/etherpad/src/static/img/about/people/rhonda-headshot.jpg
new file mode 100644
index 0000000..b4c4ec8
--- /dev/null
+++ b/trunk/etherpad/src/static/img/about/people/rhonda-headshot.jpg
Binary files differ
diff --git a/trunk/etherpad/src/static/img/about/pier38.png b/trunk/etherpad/src/static/img/about/pier38.png
new file mode 100644
index 0000000..d15b3a8
--- /dev/null
+++ b/trunk/etherpad/src/static/img/about/pier38.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/about/quote-close.png b/trunk/etherpad/src/static/img/about/quote-close.png
new file mode 100644
index 0000000..de4b18b
--- /dev/null
+++ b/trunk/etherpad/src/static/img/about/quote-close.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/about/quote-open.png b/trunk/etherpad/src/static/img/about/quote-open.png
new file mode 100644
index 0000000..e637705
--- /dev/null
+++ b/trunk/etherpad/src/static/img/about/quote-open.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/about/screencastpreview800x600.jpg b/trunk/etherpad/src/static/img/about/screencastpreview800x600.jpg
new file mode 100644
index 0000000..2a4ed39
--- /dev/null
+++ b/trunk/etherpad/src/static/img/about/screencastpreview800x600.jpg
Binary files differ
diff --git a/trunk/etherpad/src/static/img/account/betawarn.jpg b/trunk/etherpad/src/static/img/account/betawarn.jpg
new file mode 100644
index 0000000..c3cec1b
--- /dev/null
+++ b/trunk/etherpad/src/static/img/account/betawarn.jpg
Binary files differ
diff --git a/trunk/etherpad/src/static/img/acecarets/000000.gif b/trunk/etherpad/src/static/img/acecarets/000000.gif
new file mode 100644
index 0000000..f67bd3d
--- /dev/null
+++ b/trunk/etherpad/src/static/img/acecarets/000000.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/acecarets/666666.gif b/trunk/etherpad/src/static/img/acecarets/666666.gif
new file mode 100644
index 0000000..cd8e264
--- /dev/null
+++ b/trunk/etherpad/src/static/img/acecarets/666666.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/acecarets/999999.gif b/trunk/etherpad/src/static/img/acecarets/999999.gif
new file mode 100644
index 0000000..fa75d25
--- /dev/null
+++ b/trunk/etherpad/src/static/img/acecarets/999999.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/acecarets/default.gif b/trunk/etherpad/src/static/img/acecarets/default.gif
new file mode 100644
index 0000000..196d9ff
--- /dev/null
+++ b/trunk/etherpad/src/static/img/acecarets/default.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/apr09/backgrad.png b/trunk/etherpad/src/static/img/apr09/backgrad.png
new file mode 100644
index 0000000..c61d830
--- /dev/null
+++ b/trunk/etherpad/src/static/img/apr09/backgrad.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/apr09/black35.png b/trunk/etherpad/src/static/img/apr09/black35.png
new file mode 100644
index 0000000..9d82846
--- /dev/null
+++ b/trunk/etherpad/src/static/img/apr09/black35.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/apr09/blank.gif b/trunk/etherpad/src/static/img/apr09/blank.gif
new file mode 100644
index 0000000..8fb6fb0
--- /dev/null
+++ b/trunk/etherpad/src/static/img/apr09/blank.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/apr09/modalbar.gif b/trunk/etherpad/src/static/img/apr09/modalbar.gif
new file mode 100644
index 0000000..3e86759
--- /dev/null
+++ b/trunk/etherpad/src/static/img/apr09/modalbar.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/apr09/newpadicon.gif b/trunk/etherpad/src/static/img/apr09/newpadicon.gif
new file mode 100644
index 0000000..a282728
--- /dev/null
+++ b/trunk/etherpad/src/static/img/apr09/newpadicon.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/apr09/shadbot.png b/trunk/etherpad/src/static/img/apr09/shadbot.png
new file mode 100644
index 0000000..9506058
--- /dev/null
+++ b/trunk/etherpad/src/static/img/apr09/shadbot.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/apr09/shadleft.png b/trunk/etherpad/src/static/img/apr09/shadleft.png
new file mode 100644
index 0000000..72049e0
--- /dev/null
+++ b/trunk/etherpad/src/static/img/apr09/shadleft.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/apr09/shadleftbot.png b/trunk/etherpad/src/static/img/apr09/shadleftbot.png
new file mode 100644
index 0000000..7d3fb5b
--- /dev/null
+++ b/trunk/etherpad/src/static/img/apr09/shadleftbot.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/apr09/shadlefttop.png b/trunk/etherpad/src/static/img/apr09/shadlefttop.png
new file mode 100644
index 0000000..9af4e90
--- /dev/null
+++ b/trunk/etherpad/src/static/img/apr09/shadlefttop.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/apr09/shadright.png b/trunk/etherpad/src/static/img/apr09/shadright.png
new file mode 100644
index 0000000..41d099c
--- /dev/null
+++ b/trunk/etherpad/src/static/img/apr09/shadright.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/apr09/shadrightbot.png b/trunk/etherpad/src/static/img/apr09/shadrightbot.png
new file mode 100644
index 0000000..6770ec5
--- /dev/null
+++ b/trunk/etherpad/src/static/img/apr09/shadrightbot.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/apr09/shadrighttop.png b/trunk/etherpad/src/static/img/apr09/shadrighttop.png
new file mode 100644
index 0000000..0f7a0ba
--- /dev/null
+++ b/trunk/etherpad/src/static/img/apr09/shadrighttop.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/apr09/topbar.gif b/trunk/etherpad/src/static/img/apr09/topbar.gif
new file mode 100644
index 0000000..65ed7c8
--- /dev/null
+++ b/trunk/etherpad/src/static/img/apr09/topbar.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/apr09/topbarlogo.gif b/trunk/etherpad/src/static/img/apr09/topbarlogo.gif
new file mode 100644
index 0000000..9a3aa77
--- /dev/null
+++ b/trunk/etherpad/src/static/img/apr09/topbarlogo.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/apr09/widthfull.gif b/trunk/etherpad/src/static/img/apr09/widthfull.gif
new file mode 100644
index 0000000..8850ec2
--- /dev/null
+++ b/trunk/etherpad/src/static/img/apr09/widthfull.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/apr09/widthfullactive.gif b/trunk/etherpad/src/static/img/apr09/widthfullactive.gif
new file mode 100644
index 0000000..36b566f
--- /dev/null
+++ b/trunk/etherpad/src/static/img/apr09/widthfullactive.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/apr09/widthlim.gif b/trunk/etherpad/src/static/img/apr09/widthlim.gif
new file mode 100644
index 0000000..551e741
--- /dev/null
+++ b/trunk/etherpad/src/static/img/apr09/widthlim.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/apr09/widthlimactive.gif b/trunk/etherpad/src/static/img/apr09/widthlimactive.gif
new file mode 100644
index 0000000..8cf0194
--- /dev/null
+++ b/trunk/etherpad/src/static/img/apr09/widthlimactive.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/billing/amex.gif b/trunk/etherpad/src/static/img/billing/amex.gif
new file mode 100644
index 0000000..96e2cbb
--- /dev/null
+++ b/trunk/etherpad/src/static/img/billing/amex.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/billing/creditcard.gif b/trunk/etherpad/src/static/img/billing/creditcard.gif
new file mode 100644
index 0000000..ac2353e
--- /dev/null
+++ b/trunk/etherpad/src/static/img/billing/creditcard.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/billing/csc-help.gif b/trunk/etherpad/src/static/img/billing/csc-help.gif
new file mode 100644
index 0000000..1afb6b7
--- /dev/null
+++ b/trunk/etherpad/src/static/img/billing/csc-help.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/billing/disc.gif b/trunk/etherpad/src/static/img/billing/disc.gif
new file mode 100644
index 0000000..a5e3a90
--- /dev/null
+++ b/trunk/etherpad/src/static/img/billing/disc.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/billing/invoice.gif b/trunk/etherpad/src/static/img/billing/invoice.gif
new file mode 100644
index 0000000..cee681a
--- /dev/null
+++ b/trunk/etherpad/src/static/img/billing/invoice.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/billing/mc.gif b/trunk/etherpad/src/static/img/billing/mc.gif
new file mode 100644
index 0000000..2331849
--- /dev/null
+++ b/trunk/etherpad/src/static/img/billing/mc.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/billing/paypal.gif b/trunk/etherpad/src/static/img/billing/paypal.gif
new file mode 100644
index 0000000..25333b1
--- /dev/null
+++ b/trunk/etherpad/src/static/img/billing/paypal.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/billing/visa.gif b/trunk/etherpad/src/static/img/billing/visa.gif
new file mode 100644
index 0000000..4769f0c
--- /dev/null
+++ b/trunk/etherpad/src/static/img/billing/visa.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/blog/posts/new-features/fullwidth.gif b/trunk/etherpad/src/static/img/blog/posts/new-features/fullwidth.gif
new file mode 100644
index 0000000..73f427d
--- /dev/null
+++ b/trunk/etherpad/src/static/img/blog/posts/new-features/fullwidth.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/blog/posts/new-features/importexport.gif b/trunk/etherpad/src/static/img/blog/posts/new-features/importexport.gif
new file mode 100644
index 0000000..f0d2bac
--- /dev/null
+++ b/trunk/etherpad/src/static/img/blog/posts/new-features/importexport.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/blog/posts/new-features/richtext.gif b/trunk/etherpad/src/static/img/blog/posts/new-features/richtext.gif
new file mode 100644
index 0000000..5d03bdc
--- /dev/null
+++ b/trunk/etherpad/src/static/img/blog/posts/new-features/richtext.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/blog/posts/new-features/viewzoom.gif b/trunk/etherpad/src/static/img/blog/posts/new-features/viewzoom.gif
new file mode 100644
index 0000000..47e1caa
--- /dev/null
+++ b/trunk/etherpad/src/static/img/blog/posts/new-features/viewzoom.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/blog/posts/pricing-survey-results.png b/trunk/etherpad/src/static/img/blog/posts/pricing-survey-results.png
new file mode 100644
index 0000000..668dd72
--- /dev/null
+++ b/trunk/etherpad/src/static/img/blog/posts/pricing-survey-results.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/blog/posts/pricing-survey.png b/trunk/etherpad/src/static/img/blog/posts/pricing-survey.png
new file mode 100644
index 0000000..7289aa8
--- /dev/null
+++ b/trunk/etherpad/src/static/img/blog/posts/pricing-survey.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/blog/posts/time-slider-screenshot.gif b/trunk/etherpad/src/static/img/blog/posts/time-slider-screenshot.gif
new file mode 100644
index 0000000..782ac15
--- /dev/null
+++ b/trunk/etherpad/src/static/img/blog/posts/time-slider-screenshot.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/davy/bg/home-createpad.png b/trunk/etherpad/src/static/img/davy/bg/home-createpad.png
new file mode 100644
index 0000000..e34e643
--- /dev/null
+++ b/trunk/etherpad/src/static/img/davy/bg/home-createpad.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/davy/bg/home-features-bottom.gif b/trunk/etherpad/src/static/img/davy/bg/home-features-bottom.gif
new file mode 100644
index 0000000..7dc7644
--- /dev/null
+++ b/trunk/etherpad/src/static/img/davy/bg/home-features-bottom.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/davy/bg/home-features-free-bottom.gif b/trunk/etherpad/src/static/img/davy/bg/home-features-free-bottom.gif
new file mode 100644
index 0000000..3bd7ba8
--- /dev/null
+++ b/trunk/etherpad/src/static/img/davy/bg/home-features-free-bottom.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/davy/bg/home-features-paid-top.gif b/trunk/etherpad/src/static/img/davy/bg/home-features-paid-top.gif
new file mode 100644
index 0000000..faed96c
--- /dev/null
+++ b/trunk/etherpad/src/static/img/davy/bg/home-features-paid-top.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/davy/bg/home-features-top.gif b/trunk/etherpad/src/static/img/davy/bg/home-features-top.gif
new file mode 100644
index 0000000..2db70a6
--- /dev/null
+++ b/trunk/etherpad/src/static/img/davy/bg/home-features-top.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/davy/bg/home-nav-selected.png b/trunk/etherpad/src/static/img/davy/bg/home-nav-selected.png
new file mode 100644
index 0000000..01c0a99
--- /dev/null
+++ b/trunk/etherpad/src/static/img/davy/bg/home-nav-selected.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/davy/bg/home-screencast.png b/trunk/etherpad/src/static/img/davy/bg/home-screencast.png
new file mode 100644
index 0000000..e03b83d
--- /dev/null
+++ b/trunk/etherpad/src/static/img/davy/bg/home-screencast.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/davy/bg/home2.png b/trunk/etherpad/src/static/img/davy/bg/home2.png
new file mode 100644
index 0000000..814c339
--- /dev/null
+++ b/trunk/etherpad/src/static/img/davy/bg/home2.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/davy/bg/product-nav-selected-white.png b/trunk/etherpad/src/static/img/davy/bg/product-nav-selected-white.png
new file mode 100644
index 0000000..4365d4a
--- /dev/null
+++ b/trunk/etherpad/src/static/img/davy/bg/product-nav-selected-white.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/davy/bg/product-nav-selected.png b/trunk/etherpad/src/static/img/davy/bg/product-nav-selected.png
new file mode 100644
index 0000000..a3094d0
--- /dev/null
+++ b/trunk/etherpad/src/static/img/davy/bg/product-nav-selected.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/davy/bg/product.png b/trunk/etherpad/src/static/img/davy/bg/product.png
new file mode 100644
index 0000000..5a6f6f2
--- /dev/null
+++ b/trunk/etherpad/src/static/img/davy/bg/product.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/davy/btn/createpad-home.gif b/trunk/etherpad/src/static/img/davy/btn/createpad-home.gif
new file mode 100644
index 0000000..5a46f02
--- /dev/null
+++ b/trunk/etherpad/src/static/img/davy/btn/createpad-home.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/davy/btn/createpad-large.gif b/trunk/etherpad/src/static/img/davy/btn/createpad-large.gif
new file mode 100644
index 0000000..9e37808
--- /dev/null
+++ b/trunk/etherpad/src/static/img/davy/btn/createpad-large.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/davy/btn/createpad-small.gif b/trunk/etherpad/src/static/img/davy/btn/createpad-small.gif
new file mode 100644
index 0000000..5df6502
--- /dev/null
+++ b/trunk/etherpad/src/static/img/davy/btn/createpad-small.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/davy/btn/intro-screencast.png b/trunk/etherpad/src/static/img/davy/btn/intro-screencast.png
new file mode 100644
index 0000000..8343ddf
--- /dev/null
+++ b/trunk/etherpad/src/static/img/davy/btn/intro-screencast.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/davy/btn/intro-testimonials.png b/trunk/etherpad/src/static/img/davy/btn/intro-testimonials.png
new file mode 100644
index 0000000..b1df4f2
--- /dev/null
+++ b/trunk/etherpad/src/static/img/davy/btn/intro-testimonials.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/davy/btn/learnmore.gif b/trunk/etherpad/src/static/img/davy/btn/learnmore.gif
new file mode 100644
index 0000000..9f0f612
--- /dev/null
+++ b/trunk/etherpad/src/static/img/davy/btn/learnmore.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/davy/btn/signup-home-2.gif b/trunk/etherpad/src/static/img/davy/btn/signup-home-2.gif
new file mode 100644
index 0000000..1aea6ff
--- /dev/null
+++ b/trunk/etherpad/src/static/img/davy/btn/signup-home-2.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/davy/btn/signup-home-3.gif b/trunk/etherpad/src/static/img/davy/btn/signup-home-3.gif
new file mode 100644
index 0000000..a237242
--- /dev/null
+++ b/trunk/etherpad/src/static/img/davy/btn/signup-home-3.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/davy/btn/signup-home-4.gif b/trunk/etherpad/src/static/img/davy/btn/signup-home-4.gif
new file mode 100644
index 0000000..966371e
--- /dev/null
+++ b/trunk/etherpad/src/static/img/davy/btn/signup-home-4.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/davy/btn/signup-home.gif b/trunk/etherpad/src/static/img/davy/btn/signup-home.gif
new file mode 100644
index 0000000..0a83858
--- /dev/null
+++ b/trunk/etherpad/src/static/img/davy/btn/signup-home.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/davy/btn/uses-more.gif b/trunk/etherpad/src/static/img/davy/btn/uses-more.gif
new file mode 100644
index 0000000..6a73bbc
--- /dev/null
+++ b/trunk/etherpad/src/static/img/davy/btn/uses-more.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/davy/gfx/32/114.png b/trunk/etherpad/src/static/img/davy/gfx/32/114.png
new file mode 100644
index 0000000..cbab795
--- /dev/null
+++ b/trunk/etherpad/src/static/img/davy/gfx/32/114.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/davy/gfx/32/15.png b/trunk/etherpad/src/static/img/davy/gfx/32/15.png
new file mode 100644
index 0000000..596fd0f
--- /dev/null
+++ b/trunk/etherpad/src/static/img/davy/gfx/32/15.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/davy/gfx/32/65.png b/trunk/etherpad/src/static/img/davy/gfx/32/65.png
new file mode 100644
index 0000000..c331ee1
--- /dev/null
+++ b/trunk/etherpad/src/static/img/davy/gfx/32/65.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/davy/gfx/32/78.png b/trunk/etherpad/src/static/img/davy/gfx/32/78.png
new file mode 100644
index 0000000..fae3f29
--- /dev/null
+++ b/trunk/etherpad/src/static/img/davy/gfx/32/78.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/davy/gfx/bullet.gif b/trunk/etherpad/src/static/img/davy/gfx/bullet.gif
new file mode 100644
index 0000000..cb2e123
--- /dev/null
+++ b/trunk/etherpad/src/static/img/davy/gfx/bullet.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/davy/gfx/home-logo2.gif b/trunk/etherpad/src/static/img/davy/gfx/home-logo2.gif
new file mode 100644
index 0000000..24b67bd
--- /dev/null
+++ b/trunk/etherpad/src/static/img/davy/gfx/home-logo2.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/davy/gfx/home-screencast.png b/trunk/etherpad/src/static/img/davy/gfx/home-screencast.png
new file mode 100644
index 0000000..b5516d3
--- /dev/null
+++ b/trunk/etherpad/src/static/img/davy/gfx/home-screencast.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/davy/gfx/plane.gif b/trunk/etherpad/src/static/img/davy/gfx/plane.gif
new file mode 100644
index 0000000..cf3226e
--- /dev/null
+++ b/trunk/etherpad/src/static/img/davy/gfx/plane.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/davy/gfx/product-logo.gif b/trunk/etherpad/src/static/img/davy/gfx/product-logo.gif
new file mode 100644
index 0000000..72975bb
--- /dev/null
+++ b/trunk/etherpad/src/static/img/davy/gfx/product-logo.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/davy/gfx/screenshot.gif b/trunk/etherpad/src/static/img/davy/gfx/screenshot.gif
new file mode 100644
index 0000000..ba87f99
--- /dev/null
+++ b/trunk/etherpad/src/static/img/davy/gfx/screenshot.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/davy/gfx/use-meetings.gif b/trunk/etherpad/src/static/img/davy/gfx/use-meetings.gif
new file mode 100644
index 0000000..3451969
--- /dev/null
+++ b/trunk/etherpad/src/static/img/davy/gfx/use-meetings.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/davy/gfx/use-meetings.png b/trunk/etherpad/src/static/img/davy/gfx/use-meetings.png
new file mode 100644
index 0000000..4e41581
--- /dev/null
+++ b/trunk/etherpad/src/static/img/davy/gfx/use-meetings.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/davy/gfx/use-programming.gif b/trunk/etherpad/src/static/img/davy/gfx/use-programming.gif
new file mode 100644
index 0000000..795e7fd
--- /dev/null
+++ b/trunk/etherpad/src/static/img/davy/gfx/use-programming.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/davy/gfx/use-programming.png b/trunk/etherpad/src/static/img/davy/gfx/use-programming.png
new file mode 100644
index 0000000..825095d
--- /dev/null
+++ b/trunk/etherpad/src/static/img/davy/gfx/use-programming.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/davy/gfx/use-writing.gif b/trunk/etherpad/src/static/img/davy/gfx/use-writing.gif
new file mode 100644
index 0000000..0e2d11b
--- /dev/null
+++ b/trunk/etherpad/src/static/img/davy/gfx/use-writing.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/davy/gfx/use-writing.png b/trunk/etherpad/src/static/img/davy/gfx/use-writing.png
new file mode 100644
index 0000000..c6e267c
--- /dev/null
+++ b/trunk/etherpad/src/static/img/davy/gfx/use-writing.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/davy/txt/home-button.gif b/trunk/etherpad/src/static/img/davy/txt/home-button.gif
new file mode 100644
index 0000000..bdf4945
--- /dev/null
+++ b/trunk/etherpad/src/static/img/davy/txt/home-button.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/featuretour/code.gif b/trunk/etherpad/src/static/img/featuretour/code.gif
new file mode 100644
index 0000000..abff862
--- /dev/null
+++ b/trunk/etherpad/src/static/img/featuretour/code.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/featuretour/edits.gif b/trunk/etherpad/src/static/img/featuretour/edits.gif
new file mode 100644
index 0000000..d38f83a
--- /dev/null
+++ b/trunk/etherpad/src/static/img/featuretour/edits.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/featuretour/editsandusers.gif b/trunk/etherpad/src/static/img/featuretour/editsandusers.gif
new file mode 100644
index 0000000..90c36f8
--- /dev/null
+++ b/trunk/etherpad/src/static/img/featuretour/editsandusers.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/featuretour/padlock.png b/trunk/etherpad/src/static/img/featuretour/padlock.png
new file mode 100644
index 0000000..f6d6c05
--- /dev/null
+++ b/trunk/etherpad/src/static/img/featuretour/padlock.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/featuretour/revisions.gif b/trunk/etherpad/src/static/img/featuretour/revisions.gif
new file mode 100644
index 0000000..a0d8220
--- /dev/null
+++ b/trunk/etherpad/src/static/img/featuretour/revisions.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/featuretour/users.gif b/trunk/etherpad/src/static/img/featuretour/users.gif
new file mode 100644
index 0000000..48b4470
--- /dev/null
+++ b/trunk/etherpad/src/static/img/featuretour/users.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/feb09/framedheaderback.gif b/trunk/etherpad/src/static/img/feb09/framedheaderback.gif
new file mode 100644
index 0000000..5b17b9f
--- /dev/null
+++ b/trunk/etherpad/src/static/img/feb09/framedheaderback.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/feb09/framedheaderlogo.gif b/trunk/etherpad/src/static/img/feb09/framedheaderlogo.gif
new file mode 100644
index 0000000..01de8cd
--- /dev/null
+++ b/trunk/etherpad/src/static/img/feb09/framedheaderlogo.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/feb09/home_firstp.gif b/trunk/etherpad/src/static/img/feb09/home_firstp.gif
new file mode 100644
index 0000000..af0a2cd
--- /dev/null
+++ b/trunk/etherpad/src/static/img/feb09/home_firstp.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/feb09/home_firstp.png b/trunk/etherpad/src/static/img/feb09/home_firstp.png
new file mode 100644
index 0000000..cb7fc8c
--- /dev/null
+++ b/trunk/etherpad/src/static/img/feb09/home_firstp.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/feb09/home_firstp2.gif b/trunk/etherpad/src/static/img/feb09/home_firstp2.gif
new file mode 100644
index 0000000..9e0ed1e
--- /dev/null
+++ b/trunk/etherpad/src/static/img/feb09/home_firstp2.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/feb09/home_h1.gif b/trunk/etherpad/src/static/img/feb09/home_h1.gif
new file mode 100644
index 0000000..1fe08cb
--- /dev/null
+++ b/trunk/etherpad/src/static/img/feb09/home_h1.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/feb09/home_h1.png b/trunk/etherpad/src/static/img/feb09/home_h1.png
new file mode 100644
index 0000000..1b5784d
--- /dev/null
+++ b/trunk/etherpad/src/static/img/feb09/home_h1.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/feb09/home_newpadbutton.gif b/trunk/etherpad/src/static/img/feb09/home_newpadbutton.gif
new file mode 100644
index 0000000..8706407
--- /dev/null
+++ b/trunk/etherpad/src/static/img/feb09/home_newpadbutton.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/feb09/home_newpadbutton.png b/trunk/etherpad/src/static/img/feb09/home_newpadbutton.png
new file mode 100644
index 0000000..ead253c
--- /dev/null
+++ b/trunk/etherpad/src/static/img/feb09/home_newpadbutton.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/feb09/home_newpadbutton2.gif b/trunk/etherpad/src/static/img/feb09/home_newpadbutton2.gif
new file mode 100644
index 0000000..ba5d478
--- /dev/null
+++ b/trunk/etherpad/src/static/img/feb09/home_newpadbutton2.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/feb09/home_newpadbutton_eepnet.gif b/trunk/etherpad/src/static/img/feb09/home_newpadbutton_eepnet.gif
new file mode 100644
index 0000000..a365351
--- /dev/null
+++ b/trunk/etherpad/src/static/img/feb09/home_newpadbutton_eepnet.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/feb09/hometop_back.gif b/trunk/etherpad/src/static/img/feb09/hometop_back.gif
new file mode 100644
index 0000000..7cdd406
--- /dev/null
+++ b/trunk/etherpad/src/static/img/feb09/hometop_back.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/feb09/nav1.gif b/trunk/etherpad/src/static/img/feb09/nav1.gif
new file mode 100644
index 0000000..4af4869
--- /dev/null
+++ b/trunk/etherpad/src/static/img/feb09/nav1.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/feb09/nav1_back.gif b/trunk/etherpad/src/static/img/feb09/nav1_back.gif
new file mode 100644
index 0000000..d2db7ee
--- /dev/null
+++ b/trunk/etherpad/src/static/img/feb09/nav1_back.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/feb09/nav2.gif b/trunk/etherpad/src/static/img/feb09/nav2.gif
new file mode 100644
index 0000000..f43a40e
--- /dev/null
+++ b/trunk/etherpad/src/static/img/feb09/nav2.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/feb09/screencast.gif b/trunk/etherpad/src/static/img/feb09/screencast.gif
new file mode 100644
index 0000000..3819082
--- /dev/null
+++ b/trunk/etherpad/src/static/img/feb09/screencast.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/home/etherpad-mainheader1.jpg b/trunk/etherpad/src/static/img/home/etherpad-mainheader1.jpg
new file mode 100644
index 0000000..ed7e830
--- /dev/null
+++ b/trunk/etherpad/src/static/img/home/etherpad-mainheader1.jpg
Binary files differ
diff --git a/trunk/etherpad/src/static/img/home/headergradient.gif b/trunk/etherpad/src/static/img/home/headergradient.gif
new file mode 100644
index 0000000..d6526dc
--- /dev/null
+++ b/trunk/etherpad/src/static/img/home/headergradient.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/home/homeheader1.jpg b/trunk/etherpad/src/static/img/home/homeheader1.jpg
new file mode 100644
index 0000000..3436158
--- /dev/null
+++ b/trunk/etherpad/src/static/img/home/homeheader1.jpg
Binary files differ
diff --git a/trunk/etherpad/src/static/img/home/homeheader2.jpg b/trunk/etherpad/src/static/img/home/homeheader2.jpg
new file mode 100644
index 0000000..e19ba41
--- /dev/null
+++ b/trunk/etherpad/src/static/img/home/homeheader2.jpg
Binary files differ
diff --git a/trunk/etherpad/src/static/img/home/leftgrad.gif b/trunk/etherpad/src/static/img/home/leftgrad.gif
new file mode 100644
index 0000000..ff3931a
--- /dev/null
+++ b/trunk/etherpad/src/static/img/home/leftgrad.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/home/pencilpaperback.png b/trunk/etherpad/src/static/img/home/pencilpaperback.png
new file mode 100644
index 0000000..e0d2f9d
--- /dev/null
+++ b/trunk/etherpad/src/static/img/home/pencilpaperback.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/home/screencapture1.gif b/trunk/etherpad/src/static/img/home/screencapture1.gif
new file mode 100644
index 0000000..7c3ce5a
--- /dev/null
+++ b/trunk/etherpad/src/static/img/home/screencapture1.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/home/underdevicon.gif b/trunk/etherpad/src/static/img/home/underdevicon.gif
new file mode 100644
index 0000000..5b75fd1
--- /dev/null
+++ b/trunk/etherpad/src/static/img/home/underdevicon.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/icon/downarrow.gif b/trunk/etherpad/src/static/img/icon/downarrow.gif
new file mode 100644
index 0000000..6389439
--- /dev/null
+++ b/trunk/etherpad/src/static/img/icon/downarrow.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/icon/feed.gif b/trunk/etherpad/src/static/img/icon/feed.gif
new file mode 100644
index 0000000..fb9b66c
--- /dev/null
+++ b/trunk/etherpad/src/static/img/icon/feed.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/backgrad.gif b/trunk/etherpad/src/static/img/jun09/pad/backgrad.gif
new file mode 100644
index 0000000..8fee1a5
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/backgrad.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/bottomareagfx.gif b/trunk/etherpad/src/static/img/jun09/pad/bottomareagfx.gif
new file mode 100644
index 0000000..c499a62
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/bottomareagfx.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/colorpicker.gif b/trunk/etherpad/src/static/img/jun09/pad/colorpicker.gif
new file mode 100644
index 0000000..602e7e6
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/colorpicker.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/connectingbar.gif b/trunk/etherpad/src/static/img/jun09/pad/connectingbar.gif
new file mode 100644
index 0000000..34f54e9
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/connectingbar.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/connectionindicator.gif b/trunk/etherpad/src/static/img/jun09/pad/connectionindicator.gif
new file mode 100644
index 0000000..ecc270d
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/connectionindicator.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/docbarstates.png b/trunk/etherpad/src/static/img/jun09/pad/docbarstates.png
new file mode 100644
index 0000000..13a5913
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/docbarstates.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/docbarstates2.png b/trunk/etherpad/src/static/img/jun09/pad/docbarstates2.png
new file mode 100644
index 0000000..9e26b1d
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/docbarstates2.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/docbarstates3.png b/trunk/etherpad/src/static/img/jun09/pad/docbarstates3.png
new file mode 100644
index 0000000..cdc6a45
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/docbarstates3.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/docpaneledge.png b/trunk/etherpad/src/static/img/jun09/pad/docpaneledge.png
new file mode 100644
index 0000000..de22d6a
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/docpaneledge.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/docpaneledge2.png b/trunk/etherpad/src/static/img/jun09/pad/docpaneledge2.png
new file mode 100644
index 0000000..c119c74
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/docpaneledge2.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/docpanelmiddle.png b/trunk/etherpad/src/static/img/jun09/pad/docpanelmiddle.png
new file mode 100644
index 0000000..9290e86
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/docpanelmiddle.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/docpanelmiddle2.png b/trunk/etherpad/src/static/img/jun09/pad/docpanelmiddle2.png
new file mode 100644
index 0000000..d251c23
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/docpanelmiddle2.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/editbar.gif b/trunk/etherpad/src/static/img/jun09/pad/editbar.gif
new file mode 100644
index 0000000..eb7100c
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/editbar.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/editbar2.gif b/trunk/etherpad/src/static/img/jun09/pad/editbar2.gif
new file mode 100644
index 0000000..d222fb8
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/editbar2.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/editbar3.png b/trunk/etherpad/src/static/img/jun09/pad/editbar3.png
new file mode 100644
index 0000000..ff65146
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/editbar3.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/editbar3.xcf b/trunk/etherpad/src/static/img/jun09/pad/editbar3.xcf
new file mode 100644
index 0000000..91cae20
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/editbar3.xcf
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/editbarback.gif b/trunk/etherpad/src/static/img/jun09/pad/editbarback.gif
new file mode 100644
index 0000000..ab51802
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/editbarback.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/feedbackbox2.gif b/trunk/etherpad/src/static/img/jun09/pad/feedbackbox2.gif
new file mode 100644
index 0000000..f1b8f5b
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/feedbackbox2.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/fileicons.gif b/trunk/etherpad/src/static/img/jun09/pad/fileicons.gif
new file mode 100644
index 0000000..26f6388
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/fileicons.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/hdraggie.gif b/trunk/etherpad/src/static/img/jun09/pad/hdraggie.gif
new file mode 100644
index 0000000..0a6fe3e
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/hdraggie.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/inviteshare.gif b/trunk/etherpad/src/static/img/jun09/pad/inviteshare.gif
new file mode 100644
index 0000000..55345e5
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/inviteshare.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/inviteshare2.gif b/trunk/etherpad/src/static/img/jun09/pad/inviteshare2.gif
new file mode 100644
index 0000000..98d4c85
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/inviteshare2.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/layoutbuttons.gif b/trunk/etherpad/src/static/img/jun09/pad/layoutbuttons.gif
new file mode 100644
index 0000000..ea43432
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/layoutbuttons.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/overlay.png b/trunk/etherpad/src/static/img/jun09/pad/overlay.png
new file mode 100644
index 0000000..46abff3
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/overlay.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/overlay2.png b/trunk/etherpad/src/static/img/jun09/pad/overlay2.png
new file mode 100644
index 0000000..c3d3f1c
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/overlay2.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/padtop.gif b/trunk/etherpad/src/static/img/jun09/pad/padtop.gif
new file mode 100644
index 0000000..9e77b07
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/padtop.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/padtop2.gif b/trunk/etherpad/src/static/img/jun09/pad/padtop2.gif
new file mode 100644
index 0000000..1e3d8c2
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/padtop2.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/padtop3.gif b/trunk/etherpad/src/static/img/jun09/pad/padtop3.gif
new file mode 100644
index 0000000..b6bc589
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/padtop3.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/padtop4.gif b/trunk/etherpad/src/static/img/jun09/pad/padtop4.gif
new file mode 100644
index 0000000..d896250
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/padtop4.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/padtop4.png b/trunk/etherpad/src/static/img/jun09/pad/padtop4.png
new file mode 100644
index 0000000..11c4595
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/padtop4.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/padtop4.xcf b/trunk/etherpad/src/static/img/jun09/pad/padtop4.xcf
new file mode 100644
index 0000000..1e735aa
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/padtop4.xcf
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/padtop5.png b/trunk/etherpad/src/static/img/jun09/pad/padtop5.png
new file mode 100644
index 0000000..6546509
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/padtop5.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/padtop5.xcf b/trunk/etherpad/src/static/img/jun09/pad/padtop5.xcf
new file mode 100644
index 0000000..4abbd02
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/padtop5.xcf
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/padtopback.gif b/trunk/etherpad/src/static/img/jun09/pad/padtopback.gif
new file mode 100644
index 0000000..335979b
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/padtopback.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/padtopback2.gif b/trunk/etherpad/src/static/img/jun09/pad/padtopback2.gif
new file mode 100644
index 0000000..eb92358
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/padtopback2.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/protop.png b/trunk/etherpad/src/static/img/jun09/pad/protop.png
new file mode 100644
index 0000000..740d903
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/protop.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/protop.xcf b/trunk/etherpad/src/static/img/jun09/pad/protop.xcf
new file mode 100644
index 0000000..4f3abfd
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/protop.xcf
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/public.gif b/trunk/etherpad/src/static/img/jun09/pad/public.gif
new file mode 100644
index 0000000..e6f09c7
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/public.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/savedrevarrows.gif b/trunk/etherpad/src/static/img/jun09/pad/savedrevarrows.gif
new file mode 100644
index 0000000..2aa2e41
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/savedrevarrows.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/savedrevsgfx2.gif b/trunk/etherpad/src/static/img/jun09/pad/savedrevsgfx2.gif
new file mode 100644
index 0000000..45c3459
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/savedrevsgfx2.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/sharebox2.gif b/trunk/etherpad/src/static/img/jun09/pad/sharebox2.gif
new file mode 100644
index 0000000..8e89925
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/sharebox2.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/sharebox3.gif b/trunk/etherpad/src/static/img/jun09/pad/sharebox3.gif
new file mode 100644
index 0000000..6f8f03c
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/sharebox3.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/sharebox4.gif b/trunk/etherpad/src/static/img/jun09/pad/sharebox4.gif
new file mode 100644
index 0000000..eccaa7e
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/sharebox4.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/sharedistri.gif b/trunk/etherpad/src/static/img/jun09/pad/sharedistri.gif
new file mode 100644
index 0000000..8eb5891
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/sharedistri.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/syncdone.gif b/trunk/etherpad/src/static/img/jun09/pad/syncdone.gif
new file mode 100644
index 0000000..e4d971b
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/syncdone.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/syncing.gif b/trunk/etherpad/src/static/img/jun09/pad/syncing.gif
new file mode 100644
index 0000000..bbc731f
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/syncing.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/syncing2.gif b/trunk/etherpad/src/static/img/jun09/pad/syncing2.gif
new file mode 100644
index 0000000..29470e3
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/syncing2.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/jun09/pad/viewbargfx.gif b/trunk/etherpad/src/static/img/jun09/pad/viewbargfx.gif
new file mode 100644
index 0000000..396483a
--- /dev/null
+++ b/trunk/etherpad/src/static/img/jun09/pad/viewbargfx.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-gloss-cyan-menu-item-hover.gif b/trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-gloss-cyan-menu-item-hover.gif
new file mode 100644
index 0000000..d0e428e
--- /dev/null
+++ b/trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-gloss-cyan-menu-item-hover.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-gloss-menu-item-hover.gif b/trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-gloss-menu-item-hover.gif
new file mode 100644
index 0000000..8240ba3
--- /dev/null
+++ b/trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-gloss-menu-item-hover.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-gloss-semitransparent-menu-item-hover.png b/trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-gloss-semitransparent-menu-item-hover.png
new file mode 100644
index 0000000..6314d53
--- /dev/null
+++ b/trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-gloss-semitransparent-menu-item-hover.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-human-menu-item-hover.gif b/trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-human-menu-item-hover.gif
new file mode 100644
index 0000000..7e70aae
--- /dev/null
+++ b/trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-human-menu-item-hover.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-osx-menu-item-hover.gif b/trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-osx-menu-item-hover.gif
new file mode 100644
index 0000000..aa802e0
--- /dev/null
+++ b/trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-osx-menu-item-hover.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-vista-bg.gif b/trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-vista-bg.gif
new file mode 100644
index 0000000..565e771
--- /dev/null
+++ b/trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-vista-bg.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-vista-menu-item-hover.gif b/trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-vista-menu-item-hover.gif
new file mode 100644
index 0000000..2825eb1
--- /dev/null
+++ b/trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-vista-menu-item-hover.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-xp-bg.gif b/trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-xp-bg.gif
new file mode 100644
index 0000000..11b238c
--- /dev/null
+++ b/trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-xp-bg.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/may09/bold.gif b/trunk/etherpad/src/static/img/may09/bold.gif
new file mode 100644
index 0000000..49e0d17
--- /dev/null
+++ b/trunk/etherpad/src/static/img/may09/bold.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/may09/doc.gif b/trunk/etherpad/src/static/img/may09/doc.gif
new file mode 100644
index 0000000..2b62080
--- /dev/null
+++ b/trunk/etherpad/src/static/img/may09/doc.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/may09/doc.png b/trunk/etherpad/src/static/img/may09/doc.png
new file mode 100644
index 0000000..b374d0d
--- /dev/null
+++ b/trunk/etherpad/src/static/img/may09/doc.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/may09/html.gif b/trunk/etherpad/src/static/img/may09/html.gif
new file mode 100644
index 0000000..f7837e5
--- /dev/null
+++ b/trunk/etherpad/src/static/img/may09/html.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/may09/html.png b/trunk/etherpad/src/static/img/may09/html.png
new file mode 100644
index 0000000..917f1a4
--- /dev/null
+++ b/trunk/etherpad/src/static/img/may09/html.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/may09/italic.gif b/trunk/etherpad/src/static/img/may09/italic.gif
new file mode 100644
index 0000000..2876ed9
--- /dev/null
+++ b/trunk/etherpad/src/static/img/may09/italic.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/may09/leftarrow.gif b/trunk/etherpad/src/static/img/may09/leftarrow.gif
new file mode 100644
index 0000000..c26475f
--- /dev/null
+++ b/trunk/etherpad/src/static/img/may09/leftarrow.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/may09/leftarrow2.gif b/trunk/etherpad/src/static/img/may09/leftarrow2.gif
new file mode 100644
index 0000000..63bbddc
--- /dev/null
+++ b/trunk/etherpad/src/static/img/may09/leftarrow2.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/may09/link.gif b/trunk/etherpad/src/static/img/may09/link.gif
new file mode 100644
index 0000000..44ffdd2
--- /dev/null
+++ b/trunk/etherpad/src/static/img/may09/link.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/may09/link.png b/trunk/etherpad/src/static/img/may09/link.png
new file mode 100644
index 0000000..4e46fcb
--- /dev/null
+++ b/trunk/etherpad/src/static/img/may09/link.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/may09/odt.gif b/trunk/etherpad/src/static/img/may09/odt.gif
new file mode 100644
index 0000000..be42352
--- /dev/null
+++ b/trunk/etherpad/src/static/img/may09/odt.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/may09/odt.png b/trunk/etherpad/src/static/img/may09/odt.png
new file mode 100644
index 0000000..5d630db
--- /dev/null
+++ b/trunk/etherpad/src/static/img/may09/odt.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/may09/padlock.gif b/trunk/etherpad/src/static/img/may09/padlock.gif
new file mode 100644
index 0000000..167bf75
--- /dev/null
+++ b/trunk/etherpad/src/static/img/may09/padlock.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/may09/padlockopen.gif b/trunk/etherpad/src/static/img/may09/padlockopen.gif
new file mode 100644
index 0000000..0aaf413
--- /dev/null
+++ b/trunk/etherpad/src/static/img/may09/padlockopen.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/may09/passwordlocked.gif b/trunk/etherpad/src/static/img/may09/passwordlocked.gif
new file mode 100644
index 0000000..167bf75
--- /dev/null
+++ b/trunk/etherpad/src/static/img/may09/passwordlocked.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/may09/passwordlocked_cropped.gif b/trunk/etherpad/src/static/img/may09/passwordlocked_cropped.gif
new file mode 100644
index 0000000..969a8bd
--- /dev/null
+++ b/trunk/etherpad/src/static/img/may09/passwordlocked_cropped.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/may09/passwordnone.gif b/trunk/etherpad/src/static/img/may09/passwordnone.gif
new file mode 100644
index 0000000..61e38d9
--- /dev/null
+++ b/trunk/etherpad/src/static/img/may09/passwordnone.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/may09/paypal.gif b/trunk/etherpad/src/static/img/may09/paypal.gif
new file mode 100644
index 0000000..add5454
--- /dev/null
+++ b/trunk/etherpad/src/static/img/may09/paypal.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/may09/pdf.gif b/trunk/etherpad/src/static/img/may09/pdf.gif
new file mode 100644
index 0000000..1614d2c
--- /dev/null
+++ b/trunk/etherpad/src/static/img/may09/pdf.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/may09/pdf.png b/trunk/etherpad/src/static/img/may09/pdf.png
new file mode 100644
index 0000000..ac4fa75
--- /dev/null
+++ b/trunk/etherpad/src/static/img/may09/pdf.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/may09/redo.gif b/trunk/etherpad/src/static/img/may09/redo.gif
new file mode 100644
index 0000000..bb07b8e
--- /dev/null
+++ b/trunk/etherpad/src/static/img/may09/redo.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/may09/txt.gif b/trunk/etherpad/src/static/img/may09/txt.gif
new file mode 100644
index 0000000..c3f026e
--- /dev/null
+++ b/trunk/etherpad/src/static/img/may09/txt.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/may09/txt.png b/trunk/etherpad/src/static/img/may09/txt.png
new file mode 100644
index 0000000..f830cc6
--- /dev/null
+++ b/trunk/etherpad/src/static/img/may09/txt.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/may09/underline.gif b/trunk/etherpad/src/static/img/may09/underline.gif
new file mode 100644
index 0000000..1d1f573
--- /dev/null
+++ b/trunk/etherpad/src/static/img/may09/underline.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/may09/undo.gif b/trunk/etherpad/src/static/img/may09/undo.gif
new file mode 100644
index 0000000..273e9e6
--- /dev/null
+++ b/trunk/etherpad/src/static/img/may09/undo.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/miniplane.gif b/trunk/etherpad/src/static/img/miniplane.gif
new file mode 100644
index 0000000..aeaacb0
--- /dev/null
+++ b/trunk/etherpad/src/static/img/miniplane.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/misc/diagnostic-links.gif b/trunk/etherpad/src/static/img/misc/diagnostic-links.gif
new file mode 100644
index 0000000..f76669a
--- /dev/null
+++ b/trunk/etherpad/src/static/img/misc/diagnostic-links.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/misc/status-ball.gif b/trunk/etherpad/src/static/img/misc/status-ball.gif
new file mode 100644
index 0000000..085ccae
--- /dev/null
+++ b/trunk/etherpad/src/static/img/misc/status-ball.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/misc/traclogo.gif b/trunk/etherpad/src/static/img/misc/traclogo.gif
new file mode 100644
index 0000000..7ee31d1
--- /dev/null
+++ b/trunk/etherpad/src/static/img/misc/traclogo.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/atlonglast.gif b/trunk/etherpad/src/static/img/oct/atlonglast.gif
new file mode 100644
index 0000000..88e1c98
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/atlonglast.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/banner1.jpg b/trunk/etherpad/src/static/img/oct/banner1.jpg
new file mode 100644
index 0000000..431d2ba
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/banner1.jpg
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/banner2.jpg b/trunk/etherpad/src/static/img/oct/banner2.jpg
new file mode 100644
index 0000000..50570a8
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/banner2.jpg
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/banner3.jpg b/trunk/etherpad/src/static/img/oct/banner3.jpg
new file mode 100644
index 0000000..c0260a3
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/banner3.jpg
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/banner4.jpg b/trunk/etherpad/src/static/img/oct/banner4.jpg
new file mode 100644
index 0000000..e1593b7
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/banner4.jpg
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/banner5.gif b/trunk/etherpad/src/static/img/oct/banner5.gif
new file mode 100644
index 0000000..82c8eee
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/banner5.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/banner6.gif b/trunk/etherpad/src/static/img/oct/banner6.gif
new file mode 100644
index 0000000..9016ed8
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/banner6.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/banner7.gif b/trunk/etherpad/src/static/img/oct/banner7.gif
new file mode 100644
index 0000000..a999f93
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/banner7.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/banner8.gif b/trunk/etherpad/src/static/img/oct/banner8.gif
new file mode 100644
index 0000000..cc2c436
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/banner8.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/banner9.gif b/trunk/etherpad/src/static/img/oct/banner9.gif
new file mode 100644
index 0000000..e7d9043
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/banner9.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/bannerback5.gif b/trunk/etherpad/src/static/img/oct/bannerback5.gif
new file mode 100644
index 0000000..d748357
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/bannerback5.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/bannerback6.gif b/trunk/etherpad/src/static/img/oct/bannerback6.gif
new file mode 100644
index 0000000..416314f
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/bannerback6.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/bodyback1.gif b/trunk/etherpad/src/static/img/oct/bodyback1.gif
new file mode 100644
index 0000000..39d3fd7
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/bodyback1.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/bodyback2.gif b/trunk/etherpad/src/static/img/oct/bodyback2.gif
new file mode 100644
index 0000000..5bbdad8
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/bodyback2.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/bodyback3.gif b/trunk/etherpad/src/static/img/oct/bodyback3.gif
new file mode 100644
index 0000000..a04faf4
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/bodyback3.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/bodyback4.gif b/trunk/etherpad/src/static/img/oct/bodyback4.gif
new file mode 100644
index 0000000..a875cd6
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/bodyback4.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/bodyback5.gif b/trunk/etherpad/src/static/img/oct/bodyback5.gif
new file mode 100644
index 0000000..66f67ce
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/bodyback5.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/bodybacktop1.gif b/trunk/etherpad/src/static/img/oct/bodybacktop1.gif
new file mode 100644
index 0000000..20a9261
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/bodybacktop1.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/computers.gif b/trunk/etherpad/src/static/img/oct/computers.gif
new file mode 100644
index 0000000..c8f2aff
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/computers.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/computers2.gif b/trunk/etherpad/src/static/img/oct/computers2.gif
new file mode 100644
index 0000000..bd8d133
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/computers2.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/glossyblue.gif b/trunk/etherpad/src/static/img/oct/glossyblue.gif
new file mode 100644
index 0000000..eed77e7
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/glossyblue.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/glossyblue2.gif b/trunk/etherpad/src/static/img/oct/glossyblue2.gif
new file mode 100644
index 0000000..c323ebb
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/glossyblue2.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/glossyblueh.gif b/trunk/etherpad/src/static/img/oct/glossyblueh.gif
new file mode 100644
index 0000000..e56ceeb
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/glossyblueh.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/insetrect.gif b/trunk/etherpad/src/static/img/oct/insetrect.gif
new file mode 100644
index 0000000..1d8124d
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/insetrect.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/minilogo1-05e.gif b/trunk/etherpad/src/static/img/oct/minilogo1-05e.gif
new file mode 100644
index 0000000..a09ef16
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/minilogo1-05e.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/minilogo1-07f.gif b/trunk/etherpad/src/static/img/oct/minilogo1-07f.gif
new file mode 100644
index 0000000..8565272
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/minilogo1-07f.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/minilogo3.jpg b/trunk/etherpad/src/static/img/oct/minilogo3.jpg
new file mode 100644
index 0000000..d0cd89b
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/minilogo3.jpg
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/minitopback1.gif b/trunk/etherpad/src/static/img/oct/minitopback1.gif
new file mode 100644
index 0000000..da8f575
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/minitopback1.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/minitopback2.gif b/trunk/etherpad/src/static/img/oct/minitopback2.gif
new file mode 100644
index 0000000..a1f43ab
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/minitopback2.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/minitopbar1-05e.gif b/trunk/etherpad/src/static/img/oct/minitopbar1-05e.gif
new file mode 100644
index 0000000..1115749
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/minitopbar1-05e.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/minitopbar2-05e.gif b/trunk/etherpad/src/static/img/oct/minitopbar2-05e.gif
new file mode 100644
index 0000000..2c5d10f
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/minitopbar2-05e.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/minitopbar2-07f.gif b/trunk/etherpad/src/static/img/oct/minitopbar2-07f.gif
new file mode 100644
index 0000000..5687aed
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/minitopbar2-07f.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/minitopbar3.jpg b/trunk/etherpad/src/static/img/oct/minitopbar3.jpg
new file mode 100644
index 0000000..d0cd89b
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/minitopbar3.jpg
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/minitopbar4.gif b/trunk/etherpad/src/static/img/oct/minitopbar4.gif
new file mode 100644
index 0000000..bf7aec9
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/minitopbar4.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/minitoplogo1.gif b/trunk/etherpad/src/static/img/oct/minitoplogo1.gif
new file mode 100644
index 0000000..6317c0f
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/minitoplogo1.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/minitoplogo2.gif b/trunk/etherpad/src/static/img/oct/minitoplogo2.gif
new file mode 100644
index 0000000..bbb5e21
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/minitoplogo2.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/newpadmain.gif b/trunk/etherpad/src/static/img/oct/newpadmain.gif
new file mode 100644
index 0000000..6427037
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/newpadmain.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/newpadmainback.gif b/trunk/etherpad/src/static/img/oct/newpadmainback.gif
new file mode 100644
index 0000000..2016864
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/newpadmainback.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/newpadmainbackh.gif b/trunk/etherpad/src/static/img/oct/newpadmainbackh.gif
new file mode 100644
index 0000000..3060634
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/newpadmainbackh.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/pageshot.png b/trunk/etherpad/src/static/img/oct/pageshot.png
new file mode 100644
index 0000000..cb86428
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/pageshot.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/pageshotmini.png b/trunk/etherpad/src/static/img/oct/pageshotmini.png
new file mode 100644
index 0000000..0f8a9d0
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/pageshotmini.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/sidehead-gradhilite.gif b/trunk/etherpad/src/static/img/oct/sidehead-gradhilite.gif
new file mode 100644
index 0000000..5469d87
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/sidehead-gradhilite.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/tinytriangle.gif b/trunk/etherpad/src/static/img/oct/tinytriangle.gif
new file mode 100644
index 0000000..1821e3b
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/tinytriangle.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/topnav1.gif b/trunk/etherpad/src/static/img/oct/topnav1.gif
new file mode 100644
index 0000000..d801c59
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/topnav1.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/topnav2.gif b/trunk/etherpad/src/static/img/oct/topnav2.gif
new file mode 100644
index 0000000..c1ab5c5
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/topnav2.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/topnav3.gif b/trunk/etherpad/src/static/img/oct/topnav3.gif
new file mode 100644
index 0000000..fa25e75
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/topnav3.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/topnav4.gif b/trunk/etherpad/src/static/img/oct/topnav4.gif
new file mode 100644
index 0000000..1f4c714
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/topnav4.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/topnav5.gif b/trunk/etherpad/src/static/img/oct/topnav5.gif
new file mode 100644
index 0000000..fa8b737
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/topnav5.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/topnav6.gif b/trunk/etherpad/src/static/img/oct/topnav6.gif
new file mode 100644
index 0000000..e0e6815
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/topnav6.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/topnavback1.gif b/trunk/etherpad/src/static/img/oct/topnavback1.gif
new file mode 100644
index 0000000..55103a3
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/topnavback1.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/topnavback2.gif b/trunk/etherpad/src/static/img/oct/topnavback2.gif
new file mode 100644
index 0000000..9b4bdca
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/topnavback2.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/topnavback3.gif b/trunk/etherpad/src/static/img/oct/topnavback3.gif
new file mode 100644
index 0000000..327cba1
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/topnavback3.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/usecasesnavdown.gif b/trunk/etherpad/src/static/img/oct/usecasesnavdown.gif
new file mode 100644
index 0000000..c8fc7df
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/usecasesnavdown.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/usecasesnavdownh.gif b/trunk/etherpad/src/static/img/oct/usecasesnavdownh.gif
new file mode 100644
index 0000000..e1fa3a5
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/usecasesnavdownh.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/usecasesnavup.gif b/trunk/etherpad/src/static/img/oct/usecasesnavup.gif
new file mode 100644
index 0000000..470dcbe
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/usecasesnavup.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/usecasesnavuph.gif b/trunk/etherpad/src/static/img/oct/usecasesnavuph.gif
new file mode 100644
index 0000000..4207386
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/usecasesnavuph.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/oct/watchscreencast.gif b/trunk/etherpad/src/static/img/oct/watchscreencast.gif
new file mode 100644
index 0000000..f52ed17
--- /dev/null
+++ b/trunk/etherpad/src/static/img/oct/watchscreencast.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/animated-orb-orange-12.gif b/trunk/etherpad/src/static/img/pad/animated-orb-orange-12.gif
new file mode 100644
index 0000000..9db02c6
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/animated-orb-orange-12.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/backgrad.png b/trunk/etherpad/src/static/img/pad/backgrad.png
new file mode 100644
index 0000000..d85f73c
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/backgrad.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/backshadow/backshadow-940-20-eee-20.gif b/trunk/etherpad/src/static/img/pad/backshadow/backshadow-940-20-eee-20.gif
new file mode 100644
index 0000000..bc3088b
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/backshadow/backshadow-940-20-eee-20.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/backshadow/backshadow-940-20-fff-20.gif b/trunk/etherpad/src/static/img/pad/backshadow/backshadow-940-20-fff-20.gif
new file mode 100644
index 0000000..8a87283
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/backshadow/backshadow-940-20-fff-20.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/backshadow/backshadow-940-20-fff-40.gif b/trunk/etherpad/src/static/img/pad/backshadow/backshadow-940-20-fff-40.gif
new file mode 100644
index 0000000..695ecbe
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/backshadow/backshadow-940-20-fff-40.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/backshadow/backshadow-940-20-fff-60.gif b/trunk/etherpad/src/static/img/pad/backshadow/backshadow-940-20-fff-60.gif
new file mode 100644
index 0000000..0005405
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/backshadow/backshadow-940-20-fff-60.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/backshadow/botshadow-940-20-eee-20.gif b/trunk/etherpad/src/static/img/pad/backshadow/botshadow-940-20-eee-20.gif
new file mode 100644
index 0000000..92fb5dc
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/backshadow/botshadow-940-20-eee-20.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/etherpad-logo-small-grad.gif b/trunk/etherpad/src/static/img/pad/etherpad-logo-small-grad.gif
new file mode 100644
index 0000000..a65aa15
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/etherpad-logo-small-grad.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/etherpad-logo-small.gif b/trunk/etherpad/src/static/img/pad/etherpad-logo-small.gif
new file mode 100644
index 0000000..6669f39
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/etherpad-logo-small.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/etherpad-logo-small2.gif b/trunk/etherpad/src/static/img/pad/etherpad-logo-small2.gif
new file mode 100644
index 0000000..6e33392
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/etherpad-logo-small2.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/expandy-arrow-down.gif b/trunk/etherpad/src/static/img/pad/expandy-arrow-down.gif
new file mode 100644
index 0000000..4b67c17
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/expandy-arrow-down.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/expandy-arrow-right.gif b/trunk/etherpad/src/static/img/pad/expandy-arrow-right.gif
new file mode 100644
index 0000000..61303e6
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/expandy-arrow-right.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/expandy-arrow6-down-active.gif b/trunk/etherpad/src/static/img/pad/expandy-arrow6-down-active.gif
new file mode 100644
index 0000000..5f530b7
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/expandy-arrow6-down-active.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/expandy-arrow6-down.gif b/trunk/etherpad/src/static/img/pad/expandy-arrow6-down.gif
new file mode 100644
index 0000000..42fa9af
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/expandy-arrow6-down.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/expandy-arrow6-right-active.gif b/trunk/etherpad/src/static/img/pad/expandy-arrow6-right-active.gif
new file mode 100644
index 0000000..4496fad
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/expandy-arrow6-right-active.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/expandy-arrow6-right.gif b/trunk/etherpad/src/static/img/pad/expandy-arrow6-right.gif
new file mode 100644
index 0000000..9a8274c
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/expandy-arrow6-right.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/header-revgrad.gif b/trunk/etherpad/src/static/img/pad/header-revgrad.gif
new file mode 100644
index 0000000..e803e2a
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/header-revgrad.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/newpad.gif b/trunk/etherpad/src/static/img/pad/newpad.gif
new file mode 100644
index 0000000..c631cc4
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/newpad.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/orb-greenred-12.gif b/trunk/etherpad/src/static/img/pad/orb-greenred-12.gif
new file mode 100644
index 0000000..d722168
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/orb-greenred-12.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/padbg1.jpg b/trunk/etherpad/src/static/img/pad/padbg1.jpg
new file mode 100644
index 0000000..8e640fd
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/padbg1.jpg
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/padbg2.jpg b/trunk/etherpad/src/static/img/pad/padbg2.jpg
new file mode 100644
index 0000000..1248bc0
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/padbg2.jpg
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/padbg3.jpg b/trunk/etherpad/src/static/img/pad/padbg3.jpg
new file mode 100644
index 0000000..99bba32
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/padbg3.jpg
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/padbg4.jpg b/trunk/etherpad/src/static/img/pad/padbg4.jpg
new file mode 100644
index 0000000..2497360
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/padbg4.jpg
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/padbg5.jpg b/trunk/etherpad/src/static/img/pad/padbg5.jpg
new file mode 100644
index 0000000..bc3953c
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/padbg5.jpg
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/padhead1.jpg b/trunk/etherpad/src/static/img/pad/padhead1.jpg
new file mode 100644
index 0000000..e263cf4
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/padhead1.jpg
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/padhead2.jpg b/trunk/etherpad/src/static/img/pad/padhead2.jpg
new file mode 100644
index 0000000..a2f247d
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/padhead2.jpg
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/padhead3.jpg b/trunk/etherpad/src/static/img/pad/padhead3.jpg
new file mode 100644
index 0000000..101432f
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/padhead3.jpg
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/pencil-icon-small-blue.gif b/trunk/etherpad/src/static/img/pad/pencil-icon-small-blue.gif
new file mode 100644
index 0000000..f60b0f2
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/pencil-icon-small-blue.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/sidehead-grad.gif b/trunk/etherpad/src/static/img/pad/sidehead-grad.gif
new file mode 100644
index 0000000..32bac92
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/sidehead-grad.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/timeslider/button_depressed.png b/trunk/etherpad/src/static/img/pad/timeslider/button_depressed.png
new file mode 100644
index 0000000..1e96692
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/timeslider/button_depressed.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/timeslider/button_undepressed.png b/trunk/etherpad/src/static/img/pad/timeslider/button_undepressed.png
new file mode 100644
index 0000000..cd2d7a8
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/timeslider/button_undepressed.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/timeslider/crushed_button_depressed.png b/trunk/etherpad/src/static/img/pad/timeslider/crushed_button_depressed.png
new file mode 100644
index 0000000..d75dcce
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/timeslider/crushed_button_depressed.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/timeslider/crushed_button_undepressed.png b/trunk/etherpad/src/static/img/pad/timeslider/crushed_button_undepressed.png
new file mode 100644
index 0000000..d86e3f3
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/timeslider/crushed_button_undepressed.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/timeslider/crushed_current_location.png b/trunk/etherpad/src/static/img/pad/timeslider/crushed_current_location.png
new file mode 100644
index 0000000..76e0835
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/timeslider/crushed_current_location.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/timeslider/crushed_pause.png b/trunk/etherpad/src/static/img/pad/timeslider/crushed_pause.png
new file mode 100644
index 0000000..437b384
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/timeslider/crushed_pause.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/timeslider/crushed_play.png b/trunk/etherpad/src/static/img/pad/timeslider/crushed_play.png
new file mode 100644
index 0000000..c5b754b
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/timeslider/crushed_play.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/timeslider/crushed_play_button.png b/trunk/etherpad/src/static/img/pad/timeslider/crushed_play_button.png
new file mode 100644
index 0000000..3b112f6
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/timeslider/crushed_play_button.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/timeslider/crushed_timeslider_mockup.png b/trunk/etherpad/src/static/img/pad/timeslider/crushed_timeslider_mockup.png
new file mode 100644
index 0000000..f4ccbf1
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/timeslider/crushed_timeslider_mockup.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/timeslider/current_location.gif b/trunk/etherpad/src/static/img/pad/timeslider/current_location.gif
new file mode 100644
index 0000000..5d5062f
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/timeslider/current_location.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/timeslider/current_location.png b/trunk/etherpad/src/static/img/pad/timeslider/current_location.png
new file mode 100644
index 0000000..ab02792
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/timeslider/current_location.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/timeslider/pause.gif b/trunk/etherpad/src/static/img/pad/timeslider/pause.gif
new file mode 100644
index 0000000..0fa105b
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/timeslider/pause.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/timeslider/pause.png b/trunk/etherpad/src/static/img/pad/timeslider/pause.png
new file mode 100644
index 0000000..657782c
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/timeslider/pause.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/timeslider/play.gif b/trunk/etherpad/src/static/img/pad/timeslider/play.gif
new file mode 100644
index 0000000..ae308c2
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/timeslider/play.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/timeslider/play.png b/trunk/etherpad/src/static/img/pad/timeslider/play.png
new file mode 100644
index 0000000..19afe03
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/timeslider/play.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/timeslider/play_button.png b/trunk/etherpad/src/static/img/pad/timeslider/play_button.png
new file mode 100644
index 0000000..bc1736d
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/timeslider/play_button.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/timeslider/star.gif b/trunk/etherpad/src/static/img/pad/timeslider/star.gif
new file mode 100644
index 0000000..f6a2c70
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/timeslider/star.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/timeslider/star.png b/trunk/etherpad/src/static/img/pad/timeslider/star.png
new file mode 100644
index 0000000..e0c7099
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/timeslider/star.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/timeslider/star_selected.png b/trunk/etherpad/src/static/img/pad/timeslider/star_selected.png
new file mode 100644
index 0000000..c336589
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/timeslider/star_selected.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/timeslider/stepper_buttons.png b/trunk/etherpad/src/static/img/pad/timeslider/stepper_buttons.png
new file mode 100644
index 0000000..e011a45
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/timeslider/stepper_buttons.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/timeslider/timeslider_background.png b/trunk/etherpad/src/static/img/pad/timeslider/timeslider_background.png
new file mode 100644
index 0000000..faa45c6
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/timeslider/timeslider_background.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/timeslider/timeslider_left.png b/trunk/etherpad/src/static/img/pad/timeslider/timeslider_left.png
new file mode 100644
index 0000000..594d86b
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/timeslider/timeslider_left.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/timeslider/timeslider_mockup.png b/trunk/etherpad/src/static/img/pad/timeslider/timeslider_mockup.png
new file mode 100644
index 0000000..bc93914
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/timeslider/timeslider_mockup.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pad/timeslider/timeslider_right.png b/trunk/etherpad/src/static/img/pad/timeslider/timeslider_right.png
new file mode 100644
index 0000000..3bf10a2
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pad/timeslider/timeslider_right.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pricing/free.gif b/trunk/etherpad/src/static/img/pricing/free.gif
new file mode 100644
index 0000000..bce2a0b
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pricing/free.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pricing/group.gif b/trunk/etherpad/src/static/img/pricing/group.gif
new file mode 100644
index 0000000..b839d3e
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pricing/group.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pricing/on-demand.gif b/trunk/etherpad/src/static/img/pricing/on-demand.gif
new file mode 100644
index 0000000..19d2e57
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pricing/on-demand.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pricing/private-network.gif b/trunk/etherpad/src/static/img/pricing/private-network.gif
new file mode 100644
index 0000000..70d197f
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pricing/private-network.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pricing/support.gif b/trunk/etherpad/src/static/img/pricing/support.gif
new file mode 100644
index 0000000..7f42f76
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pricing/support.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pro/billing/cards-button.gif b/trunk/etherpad/src/static/img/pro/billing/cards-button.gif
new file mode 100644
index 0000000..2f9c8cf
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pro/billing/cards-button.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pro/box/blue-boxtop.gif b/trunk/etherpad/src/static/img/pro/box/blue-boxtop.gif
new file mode 100644
index 0000000..38e3538
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pro/box/blue-boxtop.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pro/buttons/bluebutton120.gif b/trunk/etherpad/src/static/img/pro/buttons/bluebutton120.gif
new file mode 100644
index 0000000..2f22003
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pro/buttons/bluebutton120.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pro/header/pro-header-back.gif b/trunk/etherpad/src/static/img/pro/header/pro-header-back.gif
new file mode 100644
index 0000000..9514dbf
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pro/header/pro-header-back.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pro/header/pro-header-logo.png b/trunk/etherpad/src/static/img/pro/header/pro-header-logo.png
new file mode 100644
index 0000000..b36daa8
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pro/header/pro-header-logo.png
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pro/header/pro-header-logo.xcf b/trunk/etherpad/src/static/img/pro/header/pro-header-logo.xcf
new file mode 100644
index 0000000..524c00f
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pro/header/pro-header-logo.xcf
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pro/header/pro-header-plustopnav-back.gif b/trunk/etherpad/src/static/img/pro/header/pro-header-plustopnav-back.gif
new file mode 100644
index 0000000..f7398fe
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pro/header/pro-header-plustopnav-back.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pro/padlist/gear-drop.gif b/trunk/etherpad/src/static/img/pro/padlist/gear-drop.gif
new file mode 100644
index 0000000..ded0f24
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pro/padlist/gear-drop.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pro/padlist/paper-icon.gif b/trunk/etherpad/src/static/img/pro/padlist/paper-icon.gif
new file mode 100644
index 0000000..161b66e
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pro/padlist/paper-icon.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pro/padlist/trash-icon.gif b/trunk/etherpad/src/static/img/pro/padlist/trash-icon.gif
new file mode 100644
index 0000000..74b5ede
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pro/padlist/trash-icon.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pro/topnav/pro-topnav-back.gif b/trunk/etherpad/src/static/img/pro/topnav/pro-topnav-back.gif
new file mode 100644
index 0000000..336fd05
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pro/topnav/pro-topnav-back.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/pro/topnav/pro-topnav-notch.gif b/trunk/etherpad/src/static/img/pro/topnav/pro-topnav-notch.gif
new file mode 100644
index 0000000..5dbe57b
--- /dev/null
+++ b/trunk/etherpad/src/static/img/pro/topnav/pro-topnav-notch.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/tinyplane.gif b/trunk/etherpad/src/static/img/tinyplane.gif
new file mode 100644
index 0000000..5aaa223
--- /dev/null
+++ b/trunk/etherpad/src/static/img/tinyplane.gif
Binary files differ
diff --git a/trunk/etherpad/src/static/img/wavejet.jpg b/trunk/etherpad/src/static/img/wavejet.jpg
new file mode 100644
index 0000000..8d10fd0
--- /dev/null
+++ b/trunk/etherpad/src/static/img/wavejet.jpg
Binary files differ
diff --git a/trunk/etherpad/src/static/js/ace.js b/trunk/etherpad/src/static/js/ace.js
new file mode 100644
index 0000000..6766cee
--- /dev/null
+++ b/trunk/etherpad/src/static/js/ace.js
@@ -0,0 +1,29 @@
+Ace2Editor.registry={nextId:1};function Ace2Editor(){var K="Ace2Editor";var F=Ace2Editor;var B={};var A={editor:B,id:(F.registry.nextId++)};
+var D=false;var E=[];function C(R,Q){return function(){var T=this;var S=arguments;function U(){R.apply(T,S);
+}if(Q){Q.apply(T,S);}if(D){U();}else{E.push(U);}};}function I(){for(var Q=0;Q<E.length;Q++){E[Q]();}E=[];
+}F.registry[A.id]=A;B.importText=C(function(R,Q){A.ace_importText(R,Q);});B.importAText=C(function(R,S,Q){A.ace_importAText(R,S,Q);
+});B.exportText=function(){if(!D){return"(awaiting init)\n";}return A.ace_exportText();};B.getFrame=function(){return A.frame||null;
+};B.focus=C(function(){A.ace_focus();});B.adjustSize=C(function(){var R=A.frame.parentNode;var Q=R.clientHeight;
+A.frame.style.height=(Q?Q+"px":R.style.height);A.ace_editorChangedSize();});B.setEditable=C(function(Q){A.ace_setEditable(Q);
+});B.getFormattedCode=function(){return A.ace_getFormattedCode();};B.setOnKeyPress=C(function(Q){A.ace_setOnKeyPress(Q);
+});B.setOnKeyDown=C(function(Q){A.ace_setOnKeyDown(Q);});B.setNotifyDirty=C(function(Q){A.ace_setNotifyDirty(Q);
+});B.setProperty=C(function(Q,R){A.ace_setProperty(Q,R);});B.getDebugProperty=function(Q){return A.ace_getDebugProperty(Q);
+};B.setBaseText=C(function(Q){A.ace_setBaseText(Q);});B.setBaseAttributedText=C(function(R,Q){A.ace_setBaseAttributedText(R,Q);
+});B.applyChangesToBase=C(function(R,Q,S){A.ace_applyChangesToBase(R,Q,S);});B.prepareUserChangeset=function(){if(!D){return null;
+}return A.ace_prepareUserChangeset();};B.applyPreparedChangesetToBase=C(function(){A.ace_applyPreparedChangesetToBase();
+});B.setUserChangeNotificationCallback=C(function(Q){A.ace_setUserChangeNotificationCallback(Q);});B.setAuthorInfo=C(function(R,Q){A.ace_setAuthorInfo(R,Q);
+});B.setAuthorSelectionRange=C(function(R,S,Q){A.ace_setAuthorSelectionRange(R,S,Q);});B.getUnhandledErrors=function(){if(!D){return[];
+}return A.ace_getUnhandledErrors();};B.execCommand=C(function(R,Q){A.ace_execCommand(R,Q);});var H=function(Q){return'<link rel="stylesheet" type="text/css" href="'+Q+'"/>';
+};var J=function(Q){return'\x3cscript type="text/javascript" src="'+Q+'">\x3c/script>';};var M=J;var N=H;
+var L=function(Q){return'\'<link rel="stylesheet" type="text/css" href="'+Q+"\"/>'";};var G=function(Q){return'\'\\x3cscript type="text/javascript" src="'+Q+"\">\\x3c/script>'";
+};var P=G;var O=L;B.destroy=C(function(){A.ace_dispose();A.frame.parentNode.removeChild(A.frame);delete F.registry[A.id];
+A=null;});B.init=function(Q,S,R){B.importText(S);A.onEditorReady=function(){D=true;I();R();};(function(){var W='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">';
+var T=["'"+W+"<html><head>'"];T.push(("('<style type=\"text/css\">'+'body{margin:0;white-space:nowrap;}h1,h2,h3,h4,h5,h6{display:inline;line-height:2em;}#outerdocbody{background-color:#fff;}body.grayedout{background-color:#eee!important;}#innerdocbody{font-size:12px;font-family:monospace;line-height:16px;}body.doesWrap{white-space:normal;}#innerdocbody{padding-top:1px;padding-right:10px;padding-bottom:8px;padding-left:1px;overflow:hidden;background-image:url(data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==);}#sidediv{font-size:11px;font-family:monospace;line-height:16px;padding-top:8px;padding-right:3px;position:absolute;width:20px;top:0;left:0;cursor:default;color:white;}#sidedivinner{text-align:right;}.sidedivdelayed{background-color:#eee;color:#888!important;border-right:1px solid #999;}.sidedivhidden{display:none;}#outerdocbody iframe{display:block;position:relative;left:32px;top:7px;border:0;width:1px;height:1px;}#outerdocbody .hotrect{border:1px solid #999;position:absolute;}body.mozilla,body.safari{display:table-cell;}body.doesWrap{display:block!important;}.safari div{padding-right:1px;}p{margin:0;}#linemetricsdiv{position:absolute;left:-1000px;top:-1000px;color:white;z-index:-1;font-size:12px;font-family:monospace;}#overlaysdiv{position:absolute;left:-1000px;top:-1000px;}.syntax .c{color:#bd3f00;font-style:italic;}.syntax .o{font-weight:bold;}.syntax .p{font-weight:bold;}.syntax .k{color:blue;}.syntax .kc{color:purple;}.syntax .mf{color:purple;}.syntax .mh{color:purple;}.syntax .mi{color:purple;}.syntax .sr{color:purple;}.syntax .s2{color:purple;}.syntax .s1{color:purple;}.syntax .sd{color:purple;}.syntax .cs{color:#0a3;font-weight:bold;font-style:italic;}.syntax .err{color:#c00;font-weight:bold;text-decoration:underline;}.syntax .nt{font-weight:bold;}.syntax .nc{color:#336;}.syntax .nf{color:#336;}.syntax .nd{color:#999;}.syntax .m{color:purple;}.syntax .nb{color:purple;}.syntax .cp{color:#bd3f00;}.syntax .flash{background-color:#adf!important;}.syntax .flashbad{background-color:#f55!important;}html{cursor:text;}span{cursor:auto;}a{cursor:pointer!important;}ul,ol,li{padding:0;margin:0;}ul{margin-left:1.5em;}ul ul{margin-left:0!important;}ul.list-bullet1{margin-left:1.5em;}ul.list-bullet2{margin-left:3em;}ul.list-bullet3{margin-left:4.5em;}ul.list-bullet4{margin-left:6em;}ul.list-bullet5{margin-left:7.5em;}ul.list-bullet6{margin-left:9em;}ul.list-bullet7{margin-left:10.5em;}ul.list-bullet8{margin-left:12em;}ul{list-style-type:disc;}ul.list-bullet1{list-style-type:disc;}ul.list-bullet2{list-style-type:circle;}ul.list-bullet3{list-style-type:square;}ul.list-bullet4{list-style-type:disc;}ul.list-bullet5{list-style-type:circle;}ul.list-bullet6{list-style-type:square;}ul.list-bullet7{list-style-type:disc;}ul.list-bullet8{list-style-type:circle;}'+'\\x3c/style>')"));
+T.push(('(\'\\x3cscript type="text/javascript">//<!--\\n\'+\'function isNodeText(A){return(A.nodeType==3);}function object(B){var A=function(){};A.prototype=B;return new A();\\n}function extend(C,A){for(var B in A){C[B]=A[B];}return C;}function forEach(B,D){for(var A=0;A\\x3cB.length;\\nA++){var C=D(B[A],A);if(C){break;}}}function map(C,D){var B=[];for(var A=0;A\\x3cC.length;A++){if(D){B.push(D(C[A],A));\\n}else{B.push(C[A]);}}return B;}function filter(B,D){var C=[];for(var A=0;A\\x3cB.length;A++){if(D(B[A],A)){C.push(B[A]);\\n}}return C;}function isArray(A){return A&&typeof A==="object"&&!(A.propertyIsEnumerable("length"))&&typeof A.length==="number";\\n}var userAgent=navigator.userAgent.toLowerCase();var browser={version:(userAgent.match(/.+(?:rv|it|ra|ie)[\\\\\\x2f: ]([\\\\d.]+)/)||[])[1],safari:/webkit/.test(userAgent),opera:/opera/.test(userAgent),msie:/msie/.test(userAgent)&&!/opera/.test(userAgent),mozilla:/mozilla/.test(userAgent)&&!/(compatible|webkit)/.test(userAgent),windows:/windows/.test(userAgent)};\\nfunction getAssoc(B,A){return B["_magicdom_"+A];}function setAssoc(C,A,B){C["_magicdom_"+A]=B;}function binarySearch(A,E){if(A\\x3c1){return 0;\\n}if(E(0)){return 0;}if(!E(A-1)){return A;}var D=0;var B=A-1;while((B-D)>1){var C=Math.floor((D+B)/2);\\nif(E(C)){B=C;}else{D=C;}}return B;}function binarySearchInfinite(C,B){var A=0;while(!B(A)){A+=C;}return binarySearch(A,B);\\n}function htmlPrettyEscape(A){return A.replace(/&/g,"&amp;").replace(/\\x3c/g,"&lt;").replace(/>/g,"&gt;").replace(/\\\\r?\\\\n/g,"\\\\\\\\n");\\n}function newSkipList(){var H=window.PROFILER;if(!H){H=function(){return{start:G,mark:G,literal:G,end:G,cancel:G};\\n};}function G(){}var B={key:null,levels:1,upPtrs:[null],downPtrs:[null],downSkips:[1],downSkipWidths:[0]};\\nvar E={key:null,levels:1,upPtrs:[null],downPtrs:[null],downSkips:[null],downSkipWidths:[null]};var A=0;\\nvar D=0;var I={};B.downPtrs[0]=E;E.upPtrs[0]=B;function J(Z){var W=B.levels;var T=W-1;var X=-1,b=0;var V=new Array(W);\\nvar Y=new Array(W);var a=new Array(W);V[T]=B;Y[T]=-1;a[T]=0;while(T>=0){var U=V[T];while(U.downPtrs[T]&&(X+U.downSkips[T]\\x3cZ)){X+=U.downSkips[T];\\nb+=U.downSkipWidths[T];U=U.downPtrs[T];}V[T]=U;Y[T]=X;a[T]=b;T--;if(T>=0){V[T]=U;}}return{nodes:V,idxs:Y,loc:Z,widthSkips:a,toString:function(){return"getPoint("+Z+")";\\n}};}function P(W){var V=0;var T=B;var U=B.levels-1;while(U>=0&&T.downPtrs[U]){while(T.downPtrs[U]&&(V+T.downSkipWidths[U]\\x3c=W)){V+=T.downSkipWidths[U];\\nT=T.downPtrs[U];}U--;}if(T===B){return(B.downPtrs[0]||null);}else{if(T===E){return(W==D?(E.upPtrs[0]||null):null);\\n}}return T;}function K(T){return(T&&T.width)||0;}function N(V,f,h){var X=H("insertKey",false);var W={key:f,levels:0,upPtrs:[],downPtrs:[],downSkips:[],downSkipWidths:[]};\\nX.mark("donealloc");var Y=V.nodes;var e=V.idxs;var k=V.loc;var j=V.widthSkips[0]+V.nodes[0].downSkipWidths[0];\\nvar a=K(h);X.mark("loop1");while(W.levels==0||Math.random()\\x3c0.01){var T=W.levels;W.levels++;if(T==Y.length){Y[T]=B;\\ne[T]=-1;B.levels++;E.levels++;B.downPtrs[T]=E;E.upPtrs[T]=B;B.downSkips[T]=A+1;B.downSkipWidths[T]=D;\\nV.widthSkips[T]=0;}var Z=W;var U=Y[T];var d=U.downPtrs[T];var b=k-e[T];var g=U.downSkips[T]+1-b;U.downSkips[T]=b;\\nU.downPtrs[T]=Z;Z.downSkips[T]=g;Z.upPtrs[T]=U;Z.downPtrs[T]=d;d.upPtrs[T]=Z;var c=j-V.widthSkips[T];\\nvar i=U.downSkipWidths[T]+a-c;U.downSkipWidths[T]=c;Z.downSkipWidths[T]=i;}X.mark("loop2");X.literal(Y.length,"PNL");\\nfor(var T=W.levels;T\\x3cY.length;T++){var U=Y[T];U.downSkips[T]++;U.downSkipWidths[T]+=a;}X.mark("map");\\nI["$KEY$"+f]=W;A++;D+=a;X.end();}function O(T){return T.nodes[0].downPtrs[0];}function S(U){U.loc++;for(var T=0;\\nT\\x3cU.nodes.length;T++){if(U.idxs[T]+U.nodes[T].downSkips[T]\\x3cU.loc){U.idxs[T]+=U.nodes[T].downSkips[T];\\nU.widthSkips[T]+=U.nodes[T].downSkipWidths[T];U.nodes[T]=U.nodes[T].downPtrs[T];}}}function M(W){var V=W.nodes[0].downPtrs[0];\\nvar X=K(V.entry);for(var T=0;T\\x3cW.nodes.length;T++){if(T\\x3cV.levels){var U=V.upPtrs[T];var Y=V.downPtrs[T];\\nvar a=U.downSkips[T]+V.downSkips[T]-1;U.downPtrs[T]=Y;Y.upPtrs[T]=U;U.downSkips[T]=a;var Z=U.downSkipWidths[T]+V.downSkipWidths[T]-X;\\nU.downSkipWidths[T]=Z;}else{var U=W.nodes[T];var Y=U.downPtrs[T];U.downSkips[T]--;U.downSkipWidths[T]-=X;\\n}}delete I["$KEY$"+V.key];A--;D-=X;}function R(V){var X=V.downSkipWidths[0];var Y=K(V.entry);var W=Y-X;\\nvar U=V;var T=0;while(T\\x3cU.levels){U.downSkipWidths[T]+=W;T++;while(T>=U.levels&&U.upPtrs[T-1]){U=U.upPtrs[T-1];\\n}}D+=W;}function L(X,W){var V=(W?0:-1);var T=X;while(T!==B){var U=T.levels-1;T=T.upPtrs[U];if(W){V+=T.downSkipWidths[U];\\n}else{V+=T.downSkips[U];}}return V;}function F(T){return I["$KEY$"+T];}function Q(Y){var U=B;var T=B.levels-1;\\nvar W=-1;function X(Z){if(Z===B){return false;}else{if(Z===E){return true;}else{return Y(Z.entry);}}}while(T>=0){var V=U.downPtrs[T];\\nwhile(!X(V)){W+=U.downSkips[T];U=V;V=U.downPtrs[T];}T--;}return W+1;}var C={length:function(){return A;\\n},atIndex:function(T){if(T\\x3c0){console.warn("atIndex("+T+")");}if(T>=A){console.warn("atIndex("+T+">="+A+")");\\n}return O(J(T)).entry;},splice:function(T,W,V){if(T\\x3c0){console.warn("splice("+T+", ...)");}if(T+W>A){console.warn("splice("+T+", "+W+", ...), N="+A);\\nconsole.warn("%s %s %s",typeof T,typeof W,typeof A);console.trace();}if(!V){V=[];}var Y=J(T);for(var U=0;\\nU\\x3cW;U++){M(Y);}for(var U=(V.length-1);U>=0;U--){var X=V[U];N(Y,X.key,X);var Z=F(X.key);Z.entry=X;}},next:function(T){return F(T.key).downPtrs[0].entry||null;\\n},prev:function(T){return F(T.key).upPtrs[0].entry||null;},push:function(T){C.splice(A,0,[T]);},slice:function(T,U){if(T===undefined){T=0;\\n}else{if(T\\x3c0){T+=A;}}if(U===undefined){U=A;}else{if(U\\x3c0){U+=A;}}if(T\\x3c0){T=0;}if(T>A){T=A;}if(U\\x3c0){U=0;\\n}if(U>A){U=A;}dmesg(String([T,U,A]));if(U\\x3c=T){return[];}var V=C.atIndex(T);var X=[V];for(var W=1;W\\x3c(U-T);\\nW++){V=C.next(V);X.push(V);}return X;},atKey:function(T){return F(T).entry;},indexOfKey:function(T){return L(F(T));\\n},indexOfEntry:function(T){return C.indexOfKey(T.key);},containsKey:function(T){return !!(F(T));},atOffset:function(T){return P(T).entry;\\n},keyAtOffset:function(T){return C.atOffset(T).key;},offsetOfKey:function(T){return L(F(T),true);},offsetOfEntry:function(T){return C.offsetOfKey(T.key);\\n},setEntryWidth:function(T,U){T.width=U;R(F(T.key));},totalWidth:function(){return D;},offsetOfIndex:function(T){if(T\\x3c0){return 0;\\n}if(T>=A){return D;}return C.offsetOfEntry(C.atIndex(T));},indexOfOffset:function(T){if(T\\x3c=0){return 0;\\n}if(T>=D){return A;}return C.indexOfEntry(C.atOffset(T));},search:function(T){return Q(T);},debugGetPoint:J,debugDepth:function(){return B.levels;\\n}};return C;}function makeVirtualLineView(B){var E=20;var D=null;function G(){return B.textContent.length;\\n}function F(){if(!D){var J=C();J.forwardByWhile(E);D=J;}return D.getVirtualLine()+1;}function H(L){var J=C();\\nJ.forwardByWhile(E,null,L);var K=J.getVirtualLine();J.backwardByWhile(8,function(){return J.getVirtualLine()==K;\\n});J.forwardByWhile(1,function(){return J.getVirtualLine()!=K;});var M=J.getOffset();return{vline:K,offset:(L-M)};\\n}function I(Q,P){var J=C();J.binarySearch(function(){return J.getVirtualLine()>=Q;});var M=J.getOffset();\\nvar L=J.getVirtualLine();J.forwardByWhile(E,null,M+P);J.backwardByWhile(1,function(){return J.getVirtualLine()!=L;\\n},M);var K=J.getOffset();var N=K-M;if(N\\x3cP&&L==(F()-1)){var O=G();N+=O-K;K=O;}return{vline:L,offset:N,lineChar:K};\\n}return{getNumVirtualLines:F,getVLineAndOffsetForChar:H,getCharForVLineAndOffset:I,makeCharSeeker:function(){return C();\\n}};function A(J){J=J.firstChild;while(J&&J.firstChild){J=J.firstChild;}if(J.data){return J;}return null;\\n}function C(){function T(a,b){var Z=a.parentNode;var f=(a.nodeValue.charAt(b)===" ");if(f){if(b==0){if(Z.previousSibling&&A(Z.previousSibling)){a=A(Z.previousSibling);\\nb=a.length-1;Z=a.parentNode;}else{return{top:Z.offsetTop,left:Z.offsetLeft};}}else{b--;}}var c=document.createElement("SPAN");\\nvar e=a.nodeValue;var d=document.createDocumentFragment();d.appendChild(document.createTextNode(e.substring(0,b)));\\nc.appendChild(document.createTextNode(e.substr(b,1)));d.appendChild(c);d.appendChild(document.createTextNode(e.substring(b+1)));\\nZ.replaceChild(d,a);var g={top:c.offsetTop,left:c.offsetLeft+(f?c.offsetWidth:0),height:c.offsetHeight};\\nwhile(Z.firstChild){Z.removeChild(Z.firstChild);}Z.appendChild(a);return g;}var Y=B.textContent;var O=Y.length;\\nvar M=null;var J=0;var L=0;var P;var N;var V;var X=0;function Q(){var Z=M;if(!Z){Z=B.firstChild;}else{Z=Z.nextSibling;\\n}while(Z&&!A(Z)){Z=Z.nextSibling;}return Z;}function S(){var Z=M;if(!Z){Z=B.lastChild;}else{Z=Z.previousSibling;\\n}while(Z&&!A(Z)){Z=Z.previousSibling;}return Z;}var K;if(O>0){M=Q();var R=T(A(M),0);V=R.height;P=R.top;\\nN=R.left;function W(b,a){var Z=T(b,a);X+=Math.round((Z.top-P)/V);P=Z.top;N=Z.left;}K={forward:function(e){var c=J;\\nvar Z=J+e;if(Z>(O-1)){Z=O-1;}while(J\\x3cZ){var d=A(M).length;var a=d-L;if(J+a>Z||!Q()){var b=Z-J;if(b>=a){b=a-1;\\n}J+=b;L+=b;break;}else{J+=a;L=0;M=Q();}}W(A(M),L);return J-c;},backward:function(c){var b=J;var Z=J-c;\\nif(Z\\x3c0){Z=0;}while(J>Z){if(J-L\\x3c=Z||!S()){var a=J-Z;if(a>L){a=L;}J-=a;L-=a;break;}else{J-=L+1;M=S();L=A(M).length-1;\\n}}W(A(M),L);return b-J;},getVirtualLine:function(){return X;},getLeftCoord:function(){return N;}};}else{N=B.offsetLeft;\\nK={forward:function(Z){return 0;},backward:function(Z){return 0;},getVirtualLine:function(){return 0;\\n},getLeftCoord:function(){return N;}};}K.getOffset=function(){return J;};K.getLineLength=function(){return O;\\n};K.toString=function(){return"seeker[curChar: "+J+"("+Y.charAt(J)+"), left: "+K.getLeftCoord()+", vline: "+K.getVirtualLine()+"]";\\n};function U(c,j,e,a){var b=null;var i=((typeof e)=="function");var h=e;var g=((typeof a)=="number");\\nvar d=a;while(b!==0&&((!i)||h())){var Z=j;if(g){var f=(c?J-d:d-J);if(f\\x3cZ){Z=f;}}if(Z\\x3c0){break;}b=(c?K.backward(Z):K.forward(Z));\\n}}K.forwardByWhile=function(Z,b,a){U(false,Z,b,a);};K.backwardByWhile=function(Z,b,a){U(true,Z,b,a);};\\nK.binarySearch=function(Z){var b=Z;var a=function(){return !Z();};K.forwardByWhile(20,a);K.backwardByWhile(20,b);\\nK.forwardByWhile(10,a);K.backwardByWhile(5,b);K.forwardByWhile(1,a);return K.getOffset()+(Z()?0:1);};\\nreturn K;}}var _opt=null;function AttribPool(){var A={};A.numToAttrib={};A.attribToNum={};A.nextNum=0;\\nA.putAttrib=function(C,E){var D=String(C);if(D in A.attribToNum){return A.attribToNum[D];}if(E){return -1;\\n}var B=A.nextNum++;A.attribToNum[D]=B;A.numToAttrib[B]=[String(C[0]||""),String(C[1]||"")];return B;};\\nA.getAttrib=function(C){var B=A.numToAttrib[C];if(!B){return B;}return[B[0],B[1]];};A.getAttribKey=function(C){var B=A.numToAttrib[C];\\nif(!B){return"";}return B[0];};A.getAttribValue=function(C){var B=A.numToAttrib[C];if(!B){return"";}return B[1];\\n};A.eachAttrib=function(C){for(var D in A.numToAttrib){var B=A.numToAttrib[D];C(B[0],B[1]);}};A.toJsonable=function(){return{numToAttrib:A.numToAttrib,nextNum:A.nextNum};\\n};A.fromJsonable=function(B){A.numToAttrib=B.numToAttrib;A.nextNum=B.nextNum;A.attribToNum={};for(var C in A.numToAttrib){A.attribToNum[String(A.numToAttrib[C])]=Number(C);\\n}return A;};return A;}var Changeset={};Changeset.error=function error(B){var A=new Error(B);A.easysync=true;\\nthrow A;};Changeset.assert=function assert(A,C){if(!A){var B=Array.prototype.slice.call(arguments,1).join("");\\nChangeset.error("Changeset: "+B);}};Changeset.parseNum=function(A){return parseInt(A,36);};Changeset.numToString=function(A){return A.toString(36).toLowerCase();\\n};Changeset.toBaseTen=function(A){var B=A.indexOf("$");var D=A.substring(0,B);var C=A.substring(B);return D.replace(/[0-9a-z]+/g,function(E){return String(Changeset.parseNum(E));\\n})+C;};Changeset.oldLen=function(A){return Changeset.unpack(A).oldLen;};Changeset.newLen=function(A){return Changeset.unpack(A).newLen;\\n};Changeset.opIterator=function(E,K){var D=/((?:\\\\*[0-9a-z]+)*)(?:\\\\|([0-9a-z]+))?([-+=])([0-9a-z]+)|\\\\?|/g;\\nvar L=(K||0);var B=L;var F=B;function C(){F=B;var M;if(_opt){M=_opt.nextOpInString(E,B);if(M){if(M.opcode()=="?"){Changeset.error("Hit error opcode in op stream");\\n}B=M.lastIndex();}}else{D.lastIndex=B;M=D.exec(E);B=D.lastIndex;if(M[0]=="?"){Changeset.error("Hit error opcode in op stream");\\n}}return M;}var A=C();var H=Changeset.newOp();function I(N){var M=(N||H);if(_opt&&A){M.attribs=A.attribs();\\nM.lines=A.lines();M.chars=A.chars();M.opcode=A.opcode();A=C();}else{if((!_opt)&&A[0]){M.attribs=A[1];\\nM.lines=Changeset.parseNum(A[2]||0);M.opcode=A[3];M.chars=Changeset.parseNum(A[4]);A=C();}else{Changeset.clearOp(M);\\n}}return M;}function G(){return !!(_opt?A:A[0]);}function J(){return F;}return{next:I,hasNext:G,lastIndex:J};\\n};Changeset.clearOp=function(A){A.opcode="";A.chars=0;A.lines=0;A.attribs="";};Changeset.newOp=function(A){return{opcode:(A||""),chars:0,lines:0,attribs:""};\\n};Changeset.cloneOp=function(A){return{opcode:A.opcode,chars:A.chars,lines:A.lines,attribs:A.attribs};\\n};Changeset.copyOp=function(B,A){A.opcode=B.opcode;A.chars=B.chars;A.lines=B.lines;A.attribs=B.attribs;\\n};Changeset.opString=function(B){if(!B.opcode){return"null";}var A=Changeset.opAssembler();A.append(B);\\nreturn A.toString();};Changeset.stringOp=function(A){return Changeset.opIterator(A).next();};Changeset.checkRep=function(C){var G=Changeset.unpack(C);\\nvar F=G.oldLen;var J=G.newLen;var M=G.ops;var E=G.charBank;var I=Changeset.smartOpAssembler();var D=0;\\nvar B=0;var H=0;var K=Changeset.opIterator(M);while(K.hasNext()){var A=K.next();switch(A.opcode){case"=":D+=A.chars;\\nB+=A.chars;break;case"-":D+=A.chars;Changeset.assert(D\\x3cF,D," >= ",F," in ",C);break;case"+":B+=A.chars;\\nH+=A.chars;Changeset.assert(B\\x3cJ,B," >= ",J," in ",C);break;}I.append(A);}B+=F-D;E=E.substring(0,H);while(E.length\\x3cH){E+="?";\\n}I.endDocument();var L=Changeset.pack(F,B,I.toString(),E);Changeset.assert(L==C,L," != ",C);return C;\\n};Changeset.smartOpAssembler=function(){var G=Changeset.mergingOpAssembler();var E=Changeset.mergingOpAssembler();\\nvar B=Changeset.mergingOpAssembler();var A=Changeset.stringAssembler();var C="";var F=0;function H(){A.append(B.toString());\\nB.clear();}function I(){A.append(G.toString());G.clear();A.append(E.toString());E.clear();}function D(O){if(!O.opcode){return;\\n}if(!O.chars){return;}if(O.opcode=="-"){if(C=="="){H();}G.append(O);F-=O.chars;}else{if(O.opcode=="+"){if(C=="="){H();\\n}E.append(O);F+=O.chars;}else{if(O.opcode=="="){if(C!="="){I();}B.append(O);}}}C=O.opcode;}function M(R,P,T,S){var O=Changeset.newOp(R);\\nO.attribs=Changeset.makeAttribsString(R,T,S);var Q=P.lastIndexOf("\\\\n");if(Q\\x3c0){O.chars=P.length;O.lines=0;\\nD(O);}else{O.chars=Q+1;O.lines=P.match(/\\\\n/g).length;D(O);O.chars=P.length-(Q+1);O.lines=0;D(O);}}function K(){I();\\nH();return A.toString();}function N(){G.clear();E.clear();B.clear();A.clear();F=0;}function L(){B.endDocument();\\n}function J(){return F;}return{append:D,toString:K,clear:N,endDocument:L,appendOpWithText:M,getLengthChange:J};\\n};if(_opt){Changeset.mergingOpAssembler=function(){var A=_opt.mergingOpAssembler();function C(F){A.append(F.opcode,F.chars,F.lines,F.attribs);\\n}function E(){return A.toString();}function B(){A.clear();}function D(){A.endDocument();}return{append:C,toString:E,clear:B,endDocument:D};\\n};}else{Changeset.mergingOpAssembler=function(){var C=Changeset.opAssembler();var A=Changeset.newOp();\\nvar B=0;function D(I){if(A.opcode){if(I&&A.opcode=="="&&!A.attribs){}else{C.append(A);if(B){A.chars=B;\\nA.lines=0;C.append(A);B=0;}}A.opcode="";}}function F(I){if(I.chars>0){if(A.opcode==I.opcode&&A.attribs==I.attribs){if(I.lines>0){A.chars+=B+I.chars;\\nA.lines+=I.lines;B=0;}else{if(A.lines==0){A.chars+=I.chars;}else{B+=I.chars;}}}else{D();Changeset.copyOp(I,A);\\n}}}function G(){D(true);}function H(){D();return C.toString();}function E(){C.clear();Changeset.clearOp(A);\\n}return{append:F,toString:H,clear:E,endDocument:G};};}if(_opt){Changeset.opAssembler=function(){var A=_opt.opAssembler();\\nfunction C(E){A.append(E.opcode,E.chars,E.lines,E.attribs);}function D(){return A.toString();}function B(){A.clear();\\n}return{append:C,toString:D,clear:B};};}else{Changeset.opAssembler=function(){var A=[];function C(E){A.push(E.attribs);\\nif(E.lines){A.push("|",Changeset.numToString(E.lines));}A.push(E.opcode);A.push(Changeset.numToString(E.chars));\\n}function D(){return A.join("");}function B(){A.length=0;}return{append:C,toString:D,clear:B};};}Changeset.stringIterator=function(D){var A=0;\\nfunction B(H){Changeset.assert(H\\x3c=C(),"!(",H," \\x3c= ",C(),")");}function F(H){B(H);var I=D.substr(A,H);\\nA+=H;return I;}function E(H){B(H);var I=D.substr(A,H);return I;}function G(H){B(H);A+=H;}function C(){return D.length-A;\\n}return{take:F,skip:G,remaining:C,peek:E};};Changeset.stringAssembler=function(){var A=[];function B(D){A.push(String(D));\\n}function C(){return A.join("");}return{append:B,toString:C};};Changeset.textLinesMutator=function(C){var A=[0,0];\\nvar D=false;var E=0,B=0;function O(X){C.splice.apply(C,X);}function S(){return C.toSource();}function J(X){if(C.get){return C.get(X);\\n}else{return C[X];}}function U(Y,X){if(C.slice){return C.slice(Y,X);}else{return[];}}function T(){if((typeof C.length)=="number"){return C.length;\\n}else{return C.length();}}function G(){A[0]=E;A[1]=0;if(B>0){F();}D=true;}function K(){O(A);A.length=2;\\nA[0]=A[1]=0;D=false;}function H(){return(E-A[0]\\x3c(A.length-2));}function W(X){print(X+": "+A.toSource()+" / "+E+","+B+" / "+S());\\n}function F(){if(!H()){A.push(J(A[0]+A[1]));A[1]++;}return 2+E-A[0];}function L(X,Z){if(X){if(Z){if(!D){G();\\n}for(var Y=0;Y\\x3cX;Y++){B=0;F();E++;}}else{if(D){if(X>1){K();}else{F();}}E+=X;B=0;}}}function R(Z,Y,X){if(Z){if(Y){L(Y,X);\\n}else{if(X&&!D){G();}if(D){F();}B+=Z;}}}function I(X){var Y="";if(X){if(!D){G();}function a(c){var b=A[0]+A[1];\\nreturn U(b,b+c).join("");}if(H()){if(B==0){Y=A[A.length-1];A.length--;Y+=a(X-1);A[1]+=X-1;}else{Y=a(X-1);\\nA[1]+=X-1;var Z=A.length-1;Y=A[Z].substring(B)+Y;A[Z]=A[Z].substring(0,B)+J(A[0]+A[1]);A[1]+=1;}}else{Y=a(X);\\nA[1]+=X;}}return Y;}function V(Y,Z){var a="";if(Y){if(Z){return I(Z);}else{if(!D){G();}var X=F();a=A[X].substring(B,B+Y);\\nA[X]=A[X].substring(0,B)+A[X].substring(B+Y);}}return a;}function M(Z,c){if(Z){if(!D){G();}if(c){var X=Changeset.splitTextLines(Z);\\nif(H()){var Y=A.length-1;var a=A[Y];var b=B;A[Y]=a.substring(0,b)+X[0];E++;X.splice(0,1);Array.prototype.push.apply(A,X);\\nE+=X.length;A.push(a.substring(b));B=0;}else{Array.prototype.push.apply(A,X);E+=X.length;}}else{var Y=F();\\nA[Y]=A[Y].substring(0,B)+Z+A[Y].substring(B);B+=Z.length;}}}function N(){var X=T();if(D){X+=A.length-2-A[1];\\n}return E\\x3cX;}function P(){if(D){K();}}var Q={skip:R,remove:V,insert:M,close:P,hasMore:N,removeLines:I,skipLines:L};\\nreturn Q;};Changeset.applyZip=function(I,K,H,J,G){var F=Changeset.opIterator(I,K);var E=Changeset.opIterator(H,J);\\nvar D=Changeset.smartOpAssembler();var A=Changeset.newOp();var C=Changeset.newOp();var B=Changeset.newOp();\\nwhile(A.opcode||F.hasNext()||C.opcode||E.hasNext()){if((!A.opcode)&&F.hasNext()){F.next(A);}if((!C.opcode)&&E.hasNext()){E.next(C);\\n}G(A,C,B);if(B.opcode){D.append(B);B.opcode="";}}D.endDocument();return D.toString();};Changeset.unpack=function(B){var G=/Z:([0-9a-z]+)([>\\x3c])([0-9a-z]+)|/;\\nvar A=G.exec(B);if((!A)||(!A[0])){Changeset.error("Not a changeset: "+B);}var D=Changeset.parseNum(A[1]);\\nvar E=(A[2]==">")?1:-1;var H=Changeset.parseNum(A[3]);var F=D+E*H;var I=A[0].length;var C=B.indexOf("$");\\nif(C\\x3c0){C=B.length;}return{oldLen:D,newLen:F,ops:B.substring(I,C),charBank:B.substring(C+1)};};Changeset.pack=function(C,G,F,D){var A=G-C;\\nvar E=(A>=0?">"+Changeset.numToString(A):"\\x3c"+Changeset.numToString(-A));var B=[];B.push("Z:",Changeset.numToString(C),E,F,"$",D);\\nreturn B.join("");};Changeset.applyToText=function(H,E){var C=Changeset.unpack(H);Changeset.assert(E.length==C.oldLen,"mismatched apply: ",E.length," / ",C.oldLen);\\nvar F=Changeset.opIterator(C.ops);var G=Changeset.stringIterator(C.charBank);var B=Changeset.stringIterator(E);\\nvar A=Changeset.stringAssembler();while(F.hasNext()){var D=F.next();switch(D.opcode){case"+":A.append(G.take(D.chars));\\nbreak;case"-":B.skip(D.chars);break;case"=":A.append(B.take(D.chars));break;}}A.append(B.take(B.remaining()));\\nreturn A.toString();};Changeset.mutateTextLines=function(G,F){var C=Changeset.unpack(G);var D=Changeset.opIterator(C.ops);\\nvar E=Changeset.stringIterator(C.charBank);var B=Changeset.textLinesMutator(F);while(D.hasNext()){var A=D.next();\\nswitch(A.opcode){case"+":B.insert(E.take(A.chars),A.lines);break;case"-":B.remove(A.chars,A.lines);break;\\ncase"=":B.skip(A.chars,A.lines,(!!A.attribs));break;}}B.close();};Changeset.composeAttributes=function(G,F,E,D){if((!G)&&E){return F;\\n}if(!F){return G;}var A=[];G.replace(/\\\\*([0-9a-z]+)/g,function(I,H){A.push(D.getAttrib(Changeset.parseNum(H)));\\nreturn"";});F.replace(/\\\\*([0-9a-z]+)/g,function(M,L){var H=D.getAttrib(Changeset.parseNum(L));var K=false;\\nfor(var I=0;I\\x3cA.length;I++){var J=A[I];if(J[0]==H[0]){if(H[1]||E){J[1]=H[1];}else{A.splice(I,1);}K=true;\\nbreak;}}if((!K)&&(H[1]||E)){A.push(H);}return"";});A.sort();var B=Changeset.stringAssembler();for(var C=0;\\nC\\x3cA.length;C++){B.append("*");B.append(Changeset.numToString(D.putAttrib(A[C])));}return B.toString();\\n};Changeset._slicerZipperFunc=function(A,B,C,D){if(A.opcode=="-"){Changeset.copyOp(A,C);A.opcode="";}else{if(!A.opcode){Changeset.copyOp(B,C);\\nB.opcode="";}else{switch(B.opcode){case"-":if(B.chars\\x3c=A.chars){if(A.opcode=="="){C.opcode="-";C.chars=B.chars;\\nC.lines=B.lines;C.attribs="";}A.chars-=B.chars;A.lines-=B.lines;B.opcode="";if(!A.chars){A.opcode="";\\n}}else{if(A.opcode=="="){C.opcode="-";C.chars=A.chars;C.lines=A.lines;C.attribs="";}B.chars-=A.chars;\\nB.lines-=A.lines;A.opcode="";}break;case"+":Changeset.copyOp(B,C);B.opcode="";break;case"=":if(B.chars\\x3c=A.chars){C.opcode=A.opcode;\\nC.chars=B.chars;C.lines=B.lines;C.attribs=Changeset.composeAttributes(A.attribs,B.attribs,A.opcode=="=",D);\\nB.opcode="";A.chars-=B.chars;A.lines-=B.lines;if(!A.chars){A.opcode="";}}else{C.opcode=A.opcode;C.chars=A.chars;\\nC.lines=A.lines;C.attribs=Changeset.composeAttributes(A.attribs,B.attribs,A.opcode=="=",D);A.opcode="";\\nB.chars-=A.chars;B.lines-=A.lines;}break;case"":Changeset.copyOp(A,C);A.opcode="";break;}}}};Changeset.applyToAttribution=function(D,C,B){var A=Changeset.unpack(D);\\nreturn Changeset.applyZip(C,0,A.ops,0,function(F,E,G){return Changeset._slicerZipperFunc(F,E,G,B);});\\n};Changeset.mutateAttributionLines=function(N,Q,O){var I=Changeset.unpack(N);var H=Changeset.opIterator(I.ops);\\nvar M=I.charBank;var G=0;var F=Changeset.textLinesMutator(Q);var B=null;function L(){return(B&&B.hasNext())||F.hasMore();\\n}function P(R){if((!(B&&B.hasNext()))&&F.hasMore()){var S=F.removeLines(1);B=Changeset.opIterator(S);\\n}if(B&&B.hasNext()){B.next(R);}else{R.opcode="";}}var D=null;function J(R){if(!D){D=Changeset.mergingOpAssembler();\\n}D.append(R);if(R.lines>0){Changeset.assert(R.lines==1,"Can\\\'t have op.lines of ",R.lines," in attribution lines");\\nF.insert(D.toString(),1);D=null;}}var A=Changeset.newOp();var E=Changeset.newOp();var C=Changeset.newOp();\\nwhile(A.opcode||H.hasNext()||E.opcode||L()){if((!A.opcode)&&H.hasNext()){H.next(A);}if((!A.opcode)&&(!E.opcode)&&(!D)&&(!(B&&B.hasNext()))){break;\\n}else{if(A.opcode=="="&&A.lines>0&&(!A.attribs)&&(!E.opcode)&&(!D)&&(!(B&&B.hasNext()))){F.skipLines(A.lines);\\nA.opcode="";}else{if(A.opcode=="+"){if(A.lines>1){var K=M.indexOf("\\\\n",G)+1-G;Changeset.copyOp(A,C);A.chars-=K;\\nA.lines--;C.lines=1;C.chars=K;}else{Changeset.copyOp(A,C);A.opcode="";}J(C);G+=C.chars;C.opcode="";}else{if((!E.opcode)&&L()){P(E);\\n}Changeset._slicerZipperFunc(E,A,C,O);if(C.opcode){J(C);C.opcode="";}}}}}Changeset.assert(!D,"line assembler not finished");\\nF.close();};Changeset.joinAttributionLines=function(D){var C=Changeset.mergingOpAssembler();for(var A=0;\\nA\\x3cD.length;A++){var E=D[A];var B=Changeset.opIterator(E);while(B.hasNext()){C.append(B.next());}}return C.toString();\\n};Changeset.splitAttributionLines=function(J,K){var G=Changeset.opIterator(J);var C=Changeset.mergingOpAssembler();\\nvar I=[];var D=0;function E(L){C.append(L);if(L.lines>0){I.push(C.toString());C.clear();}D+=L.chars;}while(G.hasNext()){var A=G.next();\\nvar F=A.chars;var B=A.lines;while(B>1){var H=K.indexOf("\\\\n",D)+1;Changeset.assert(H>0,"newlineEnd \\x3c= 0 in splitAttributionLines");\\nA.chars=H-D;A.lines=1;E(A);F-=A.chars;B-=A.lines;}if(B==1){A.chars=F;A.lines=1;}E(A);}return I;};Changeset.splitTextLines=function(A){return A.match(/[^\\\\n]*(?:\\\\n|[^\\\\n]$)/g);\\n};Changeset.compose=function(J,H,L){var B=Changeset.unpack(J);var A=Changeset.unpack(H);var K=B.oldLen;\\nvar I=B.newLen;Changeset.assert(I==A.oldLen,"mismatched composition");var G=A.newLen;var D=Changeset.stringIterator(B.charBank);\\nvar E=Changeset.stringIterator(A.charBank);var C=Changeset.stringAssembler();var F=Changeset.applyZip(B.ops,0,A.ops,0,function(O,N,M){var Q=O.opcode;\\nvar P=N.opcode;if(Q=="+"&&P=="-"){D.skip(Math.min(O.chars,N.chars));}Changeset._slicerZipperFunc(O,N,M,L);\\nif(M.opcode=="+"){if(P=="+"){C.append(E.take(M.chars));}else{C.append(D.take(M.chars));}}});return Changeset.pack(K,G,F,C.toString());\\n};Changeset.attributeTester=function(D,B){if(!B){return C;}var A=B.putAttrib(D,true);if(A\\x3c0){return C;\\n}else{var E=new RegExp("\\\\\\\\*"+Changeset.numToString(A)+"(?!\\\\\\\\w)");return function(F){return E.test(F);\\n};}function C(F){return false;}};Changeset.identity=function(A){return Changeset.pack(A,A,"","");};Changeset.makeSplice=function(B,A,F,E,H,J){var D=B.length;\\nif(A>=D){A=D-1;}if(F>B.length-A-1){F=B.length-A-1;}var G=B.substring(A,A+F);var I=D+E.length-G.length;\\nvar C=Changeset.smartOpAssembler();C.appendOpWithText("=",B.substring(0,A));C.appendOpWithText("-",G);\\nC.appendOpWithText("+",E,H,J);C.endDocument();return Changeset.pack(D,I,C.toString(),E);};Changeset.toSplices=function(G){var F=Changeset.unpack(G);\\nvar B=[];var C=0;var E=Changeset.opIterator(F.ops);var H=Changeset.stringIterator(F.charBank);var D=false;\\nwhile(E.hasNext()){var A=E.next();if(A.opcode=="="){C+=A.chars;D=false;}else{if(!D){B.push([C,C,""]);\\nD=true;}if(A.opcode=="-"){C+=A.chars;B[B.length-1][1]+=A.chars;}else{if(A.opcode=="+"){B[B.length-1][2]+=H.take(A.chars);\\n}}}}return B;};Changeset.characterRangeFollow=function(M,N,L,K){var C=N;var A=L;var J=Changeset.toSplices(M);\\nvar H=0;for(var G=0;G\\x3cJ.length;G++){var F=J[G];var B=F[0]+H;var E=F[1]+H;var I=F[2].length;var D=I-(E-B);\\nif(B\\x3c=C&&E>=A){if(K){C=A=B;}else{C=A=B+I;}}else{if(E\\x3c=C){C+=D;A+=D;}else{if(B>=A){}else{if(B>=C&&E\\x3c=A){A+=D;\\n}else{if(E\\x3cA){C=B+I;A+=D;}else{A=B;}}}}}H+=D;}return[C,A];};Changeset.moveOpsToNewPool=function(A,F,C){var B=A.indexOf("$");\\nif(B\\x3c0){B=A.length;}var D=A.substring(0,B);var E=A.substring(B);return D.replace(/\\\\*([0-9a-z]+)/g,function(K,G){var H=Changeset.parseNum(G);\\nvar J=F.getAttrib(H);var I=C.putAttrib(J);return"*"+Changeset.numToString(I);})+E;};Changeset.makeAttribution=function(B){var A=Changeset.smartOpAssembler();\\nA.appendOpWithText("+",B);return A.toString();};Changeset.eachAttribNumber=function(A,D){var B=A.indexOf("$");\\nif(B\\x3c0){B=A.length;}var C=A.substring(0,B);C.replace(/\\\\*([0-9a-z]+)/g,function(F,E){D(Changeset.parseNum(E));\\nreturn"";});};Changeset.filterAttribNumbers=function(B,A){return Changeset.mapAttribNumbers(B,A);};Changeset.mapAttribNumbers=function(A,E){var B=A.indexOf("$");\\nif(B\\x3c0){B=A.length;}var D=A.substring(0,B);var C=D.replace(/\\\\*([0-9a-z]+)/g,function(H,G){var F=E(Changeset.parseNum(G));\\nif(F===true){return H;}else{if((typeof F)==="number"){return"*"+Changeset.numToString(F);}else{return"";\\n}}});return C+A.substring(B);};Changeset.makeAText=function(A,B){return{text:A,attribs:(B||Changeset.makeAttribution(A))};\\n};Changeset.applyToAText=function(A,B,C){return{text:Changeset.applyToText(A,B.text),attribs:Changeset.applyToAttribution(A,B.attribs,C)};\\n};Changeset.cloneAText=function(A){return{text:A.text,attribs:A.attribs};};Changeset.copyAText=function(A,B){B.text=A.text;\\nB.attribs=A.attribs;};Changeset.appendATextToAssembler=function(C,B){var D=Changeset.opIterator(C.attribs);\\nvar A=Changeset.newOp();while(D.hasNext()){D.next(A);if(!D.hasNext()){if(A.lines\\x3c=1){A.lines=0;A.chars--;\\nif(A.chars){B.append(A);}}else{var F=C.text.lastIndexOf("\\\\n",C.text.length-2)+1;var E=C.text.length-F-1;\\nA.lines--;A.chars-=(E+1);B.append(A);A.lines=0;A.chars=E;if(A.chars){B.append(A);}}}else{B.append(A);\\n}}};Changeset.prepareForWire=function(C,B){var A=new AttribPool();var D=Changeset.moveOpsToNewPool(C,B,A);\\nreturn{translated:D,pool:A};};Changeset.isIdentity=function(B){var A=Changeset.unpack(B);return A.ops==""&&A.oldLen==A.newLen;\\n};Changeset.opAttributeValue=function(C,B,A){return Changeset.attribsAttributeValue(C.attribs,B,A);};\\nChangeset.attribsAttributeValue=function(C,D,A){var B="";if(C){Changeset.eachAttribNumber(C,function(E){if(A.getAttribKey(E)==D){B=A.getAttribValue(E);\\n}});}return B;};Changeset.builder=function(D){var B=Changeset.smartOpAssembler();var A=Changeset.newOp();\\nvar E=Changeset.stringAssembler();var C={keep:function(I,G,F,H){A.opcode="=";A.attribs=(F&&Changeset.makeAttribsString("=",F,H))||"";\\nA.chars=I;A.lines=(G||0);B.append(A);return C;},keepText:function(H,G,F){B.appendOpWithText("=",H,G,F);\\nreturn C;},insert:function(F,H,G){B.appendOpWithText("+",F,H,G);E.append(F);return C;},remove:function(G,F){A.opcode="-";\\nA.attribs="";A.chars=G;A.lines=(F||0);B.append(A);return C;},toString:function(){B.endDocument();var F=D+B.getLengthChange();\\nreturn Changeset.pack(D,F,B.toString(),E.toString());}};return C;};Changeset.makeAttribsString=function(E,A,D){if(!A){return"";\\n}else{if((typeof A)=="string"){return A;}else{if(D&&A&&A.length){if(A.length>1){A=A.slice();A.sort();\\n}var C=[];for(var B=0;B\\x3cA.length;B++){var F=A[B];if(E=="="||(E=="+"&&F[1])){C.push("*"+Changeset.numToString(D.putAttrib(F)));\\n}}return C.join("");}}}};Changeset.subattribution=function(I,F,H){var E=Changeset.opIterator(I,0);var D=Changeset.smartOpAssembler();\\nvar A=Changeset.newOp();var B=Changeset.newOp();var C=Changeset.newOp();function G(){if(B.chars){while(B.opcode&&(A.opcode||E.hasNext())){if(!A.opcode){E.next(A);\\n}if(B.opcode&&A.opcode&&B.chars>=A.chars&&A.lines>0&&B.lines\\x3c=0){B.lines++;}Changeset._slicerZipperFunc(A,B,C,null);\\nif(C.opcode){D.append(C);C.opcode="";}}}}B.opcode="-";B.chars=F;G();if(H===undefined){if(A.opcode){D.append(A);\\n}while(E.hasNext()){E.next(A);D.append(A);}}else{B.opcode="=";B.chars=H-F;G();}return D.toString();};\\nChangeset.inverse=function(U,F,G,J){function P(a){if(F.get){return F.get(a);}else{return F[a];}}function Z(){if((typeof F.length)=="number"){return F.length;\\n}else{return F.length();}}function Q(a){if(G.get){return G.get(a);}else{return G[a];}}function Y(){if((typeof G.length)=="number"){return G.length;\\n}else{return G.length();}}var C=0;var E=0;var D=null;var I;var A=Changeset.newOp("+");var R=Changeset.unpack(U);\\nvar N=Changeset.opIterator(R.ops);var H=Changeset.builder(R.newLen);function M(c,e){if((!D)||(I!=C)){D=Changeset.opIterator(Q(C));\\nI=C;var b=0;var d=false;while(!d){D.next(A);if(b+A.chars>=E){A.chars-=(E-b);d=true;}else{b+=A.chars;}}}while(c>0){if((!A.chars)&&(!D.hasNext())){C++;\\nE=0;I=C;A.chars=0;D=Changeset.opIterator(Q(C));}if(!A.chars){D.next(A);}var a=Math.min(c,A.chars);e(a,A.attribs,a==A.chars&&A.lines>0);\\nc-=a;A.chars-=a;E+=a;}if((!A.chars)&&(!D.hasNext())){C++;E=0;}}function X(b,a){if(a){C+=a;E=0;}else{if(D&&I==C){M(b,function(){});\\n}else{E+=b;}}}function S(e){var a=0;var b=Changeset.stringAssembler();var c=P(C).substring(E);a+=c.length;\\nb.append(c);var f=C+1;while(a\\x3ce){var d=P(f);a+=d.length;b.append(d);f++;}return b.toString().substring(0,e);\\n}function W(b){var a={};return function(c){if(!a[c]){a[c]=b(c);}return a[c];};}var K=[];var L=[];while(N.hasNext()){var B=N.next();\\nif(B.opcode=="="){if(B.attribs){K.length=0;L.length=0;Changeset.eachAttribNumber(B.attribs,function(a){K.push(J.getAttribKey(a));\\nL.push(J.getAttribValue(a));});var T=W(function(f){var d=[];for(var a=0;a\\x3cK.length;a++){var c=K[a];var e=L[a];\\nvar b=Changeset.attribsAttributeValue(f,c,J);if(e!=b){d.push([c,b]);}}return Changeset.makeAttribsString("=",d,J);\\n});M(B.chars,function(a,c,b){H.keep(a,b?1:0,T(c));});}else{X(B.chars,B.lines);H.keep(B.chars,B.lines);\\n}}else{if(B.opcode=="+"){H.remove(B.chars,B.lines);}else{if(B.opcode=="-"){var V=S(B.chars);var O=0;M(B.chars,function(a,b,c){H.insert(V.substr(O,a),b);\\nO+=a;});}}}}return Changeset.checkRep(H.toString());};Changeset.follow=function(N,L,K,G){var C=Changeset.unpack(N);\\nvar B=Changeset.unpack(L);var O=C.oldLen;var M=B.oldLen;Changeset.assert(O==M,"mismatched follow");var I=Changeset.stringIterator(C.charBank);\\nvar H=Changeset.stringIterator(B.charBank);var F=C.newLen;var D=0;var A=0;var E=Changeset.attributeTester(["insertorder","first"],G);\\nvar J=Changeset.applyZip(C.ops,0,B.ops,0,function(Q,P,R){if(Q.opcode=="+"||P.opcode=="+"){var S;if(P.opcode!="+"){S=1;\\n}else{if(Q.opcode!="+"){S=2;}else{var V=I.peek(1);var T=H.peek(1);var W=E(Q.attribs);var U=E(P.attribs);\\nif(W&&!U){S=1;}else{if(U&&!W){S=2;}else{if(V=="\\\\n"&&T!="\\\\n"){S=2;}else{if(V!="\\\\n"&&T=="\\\\n"){S=1;}else{if(K){S=2;\\n}else{S=1;}}}}}}}if(S==1){I.skip(Q.chars);R.opcode="=";R.lines=Q.lines;R.chars=Q.chars;R.attribs="";Q.opcode="";\\n}else{H.skip(P.chars);Changeset.copyOp(P,R);P.opcode="";}}else{if(Q.opcode=="-"){if(!P.opcode){Q.opcode="";\\n}else{if(Q.chars\\x3c=P.chars){P.chars-=Q.chars;P.lines-=Q.lines;Q.opcode="";if(!P.chars){P.opcode="";}}else{Q.chars-=P.chars;\\nQ.lines-=P.lines;P.opcode="";}}}else{if(P.opcode=="-"){Changeset.copyOp(P,R);if(!Q.opcode){P.opcode="";\\n}else{if(P.chars\\x3c=Q.chars){Q.chars-=P.chars;Q.lines-=P.lines;P.opcode="";if(!Q.chars){Q.opcode="";}}else{R.lines=Q.lines;\\nR.chars=Q.chars;P.lines-=Q.lines;P.chars-=Q.chars;Q.opcode="";}}}else{if(!Q.opcode){Changeset.copyOp(P,R);\\nP.opcode="";}else{if(!P.opcode){Changeset.copyOp(Q,R);Q.opcode="";}else{R.opcode="=";R.attribs=Changeset.followAttributes(Q.attribs,P.attribs,G);\\nif(Q.chars\\x3c=P.chars){R.chars=Q.chars;R.lines=Q.lines;P.chars-=Q.chars;P.lines-=Q.lines;Q.opcode="";if(!P.chars){P.opcode="";\\n}}else{R.chars=P.chars;R.lines=P.lines;Q.chars-=P.chars;Q.lines-=P.lines;P.opcode="";}}}}}}switch(R.opcode){case"=":D+=R.chars;\\nA+=R.chars;break;case"-":D+=R.chars;break;case"+":A+=R.chars;break;}});A+=F-D;return Changeset.pack(F,A,J,B.charBank);\\n};Changeset.followAttributes=function(F,E,B){if((!E)||(!B)){return"";}if(!F){return E;}var A=[];E.replace(/\\\\*([0-9a-z]+)/g,function(H,G){A.push(B.getAttrib(Changeset.parseNum(G)));\\nreturn"";});F.replace(/\\\\*([0-9a-z]+)/g,function(K,J){var I=B.getAttrib(Changeset.parseNum(J));for(var G=0;\\nG\\x3cA.length;G++){var H=A[G];if(I[0]==H[0]){if(I[1]\\x3c=H[1]){A.splice(G,1);}break;}}return"";});var C=Changeset.stringAssembler();\\nfor(var D=0;D\\x3cA.length;D++){C.append("*");C.append(Changeset.numToString(B.putAttrib(A[D])));}return C.toString();\\n};function makeCSSManager(I){function E(N){var M=document.styleSheets;for(var K=0;K\\x3cM.length;K++){var L=M[K];\\nif(L.title==N){return L;}}return null;}var A=E(I);function C(){return(A.cssRules||A.rules);}function H(K){if(A.deleteRule){A.deleteRule(K);\\n}else{A.removeRule(K);}}function F(L,K){if(A.insertRule){A.insertRule(K+" {}",L);}else{A.addRule(K,null,L);\\n}}var B=[];function D(L){for(var K=0;K\\x3cB.length;K++){if(B[K]==L){return K;}}return -1;}function G(K){var L=D(K);\\nif(L\\x3c0){F(0,K);B.splice(0,0,K);L=0;}return C().item(L).style;}function J(L){var K=D(L);if(K>=0){H(K);\\nB.splice(K,1);}}return{selectorStyle:G,removeSelectorStyle:J,info:function(){return B.length+":"+C().length;\\n}};}var colorutils={};colorutils.css2triple=function(C){var B=colorutils.css2sixhex(C);function A(D){return Number("0x"+D)/255;\\n}return[A(B.substr(0,2)),A(B.substr(2,2)),A(B.substr(4,2))];};colorutils.css2sixhex=function(E){var A=/[0-9a-fA-F]+/.exec(E)[0];\\nif(A.length!=6){var C=A.charAt(0);var B=A.charAt(1);var D=A.charAt(2);A=C+C+B+B+D+D;}return A;};colorutils.triple2css=function(B){function A(D){var C=colorutils.clamp(Math.round(D*255),0,255);\\nreturn("0"+C.toString(16)).slice(-2);}return"#"+A(B[0])+A(B[1])+A(B[2]);};colorutils.clamp=function(A,C,B){return A\\x3cC?C:(A>B?B:A);\\n};colorutils.min3=function(C,B,A){return(C\\x3cB)?(C\\x3cA?C:A):(B\\x3cA?B:A);};colorutils.max3=function(C,B,A){return(C>B)?(C>A?C:A):(B>A?B:A);\\n};colorutils.colorMin=function(A){return colorutils.min3(A[0],A[1],A[2]);};colorutils.colorMax=function(A){return colorutils.max3(A[0],A[1],A[2]);\\n};colorutils.scale=function(B,A,C){return colorutils.clamp(A+B*(C-A),0,1);};colorutils.unscale=function(B,A,C){return colorutils.clamp((B-A)/(C-A),0,1);\\n};colorutils.scaleColor=function(C,B,A){return[colorutils.scale(C[0],B,A),colorutils.scale(C[1],B,A),colorutils.scale(C[2],B,A)];\\n};colorutils.unscaleColor=function(C,B,A){return[colorutils.unscale(C[0],B,A),colorutils.unscale(C[1],B,A),colorutils.unscale(C[2],B,A)];\\n};colorutils.luminosity=function(A){return A[0]*0.3+A[1]*0.59+A[2]*0.11;};colorutils.saturate=function(A){var C=colorutils.colorMin(A);\\nvar B=colorutils.colorMax(A);if(B-C\\x3c=0){return[1,1,1];}return colorutils.unscaleColor(A,C,B);};colorutils.blend=function(C,B,A){return[colorutils.scale(A,C[0],B[0]),colorutils.scale(A,C[1],B[1]),colorutils.scale(A,C[2],B[2])];\\n};undoModule=(function(){var A=(function(){var M=[];var O=0;var Q="undoableEvent";var N="externalChange";\\nfunction P(){M.length=0;M.push({elementType:Q,eventType:"bottom"});O=1;}P();function T(Y){var X=extend({},Y);\\nX.elementType=Q;M.push(X);O++;}function U(Y){var X=M.length-1;if(M[X].elementType==N){M[X].changeset=Changeset.compose(M[X].changeset,Y,D());\\n}else{M.push({elementType:N,changeset:Y});}}function R(e){var c=M.length-1-e;var X=M.length-1;while(X>c||M[X].elementType==N){if(M[X].elementType==N){var Z=M[X];\\nvar Y=M[X-1];if(Y.backset){var b=Z.changeset;var d=Y.backset;Y.backset=Changeset.follow(b,Y.backset,false,D());\\nZ.changeset=Changeset.follow(d,Z.changeset,true,D());if((typeof Y.selStart)=="number"){var a=Changeset.characterRangeFollow(b,Y.selStart,Y.selEnd);\\nY.selStart=a[0];Y.selEnd=a[1];if(Y.selStart==Y.selEnd){Y.selFocusAtStart=false;}}}M[X-1]=Z;M[X]=Y;if(X>=2&&M[X-2].elementType==N){Z.changeset=Changeset.compose(M[X-2].changeset,Z.changeset,D());\\nM.splice(X-2,1);X--;}}else{X--;}}}function W(X){R(X);return M[M.length-1-X];}function V(){return O;}function S(){R(0);\\nO--;return M.pop();}return{numEvents:V,popEvent:S,pushEvent:T,pushExternalChange:U,clearStack:P,getNthFromTop:W};\\n})();var B=0;function J(){A.clearStack();B=0;}function H(O,P){var M=0;var N=0;while(M>=0&&M\\x3cO.length){M=O.indexOf(P,M);\\nif(M>=0){N++;M++;}}return N;}function C(M,N){return H(Changeset.unpack(M).ops,N);}function K(N,M){if(!N){return M;\\n}if(!M){return N;}var U=C(N,"+");var T=C(M,"+");var R=C(N,"-");var Q=C(M,"-");if(U==1&&T==1&&R==0&&Q==0){var O=Changeset.compose(N,M,D());\\nvar S=C(O,"+");var P=C(O,"-");if(S==1&&P==0){return O;}}else{if(U==0&&T==0&&R==1&&Q==1){var O=Changeset.compose(N,M,D());\\nvar S=C(O,"+");var P=C(O,"-");if(S==0&&P==1){return O;}}}return null;}function I(M){var N=A.getNthFromTop(0);\\nfunction P(){if((typeof M.selStart)=="number"){N.selStart=M.selStart;N.selEnd=M.selEnd;N.selFocusAtStart=M.selFocusAtStart;\\n}}if((!M.backset)||Changeset.isIdentity(M.backset)){P();}else{var O=false;if(N.eventType==M.eventType){var Q=K(M.backset,N.backset);\\nif(Q){N.backset=Q;P();O=true;}}if(!O){A.pushEvent(M);}B=0;}}function G(M){if(M&&!Changeset.isIdentity(M)){A.pushExternalChange(M);\\n}}function E(M){if((typeof M.selStart)!="number"){return null;}else{return{selStart:M.selStart,selEnd:M.selEnd,selFocusAtStart:M.selFocusAtStart};\\n}}function F(M){if(B\\x3cA.numEvents()-1){var O=A.getNthFromTop(B);var N=A.getNthFromTop(B+1);var P=M(O.backset,E(N));\\nA.pushEvent(P);B+=2;}else{M();}}function L(M){if(B>=2){var O=A.getNthFromTop(0);var N=A.getNthFromTop(1);\\nM(O.backset,E(N));A.popEvent();B-=2;}else{M();}}function D(){return undoModule.apool;}return{clearHistory:J,reportEvent:I,reportExternalChange:G,performUndo:F,performRedo:L,enabled:true,apool:null};\\n})();var _MAX_LIST_LEVEL=8;function sanitizeUnicode(A){return A.replace(/[\\\\uffff\\\\ufffe\\\\ufeff\\\\ufdd0-\\\\ufdef\\\\ud800-\\\\udfff]/g,"?");\\n}function makeContentCollector(h,H,M,Y,U){H=H||{};var B=Y||{isNodeText:function(k){return(k.nodeType==3);\\n},nodeTagName:function(k){return k.tagName;},nodeValue:function(k){return k.nodeValue;},nodeNumChildren:function(k){return k.childNodes.length;\\n},nodeChild:function(l,k){return l.childNodes.item(k);},nodeProp:function(l,k){return l[k];},nodeAttr:function(l,k){return l.getAttribute(k);\\n},optNodeInnerHTML:function(k){return k.innerHTML;}};var i={div:1,p:1,pre:1,li:1};function R(k){return !!i[(B.nodeTagName(k)||"").toLowerCase()];\\n}function e(k){return sanitizeUnicode(k.replace(/[\\\\n\\\\r ]/g," ").replace(/\\\\xa0/g," ").replace(/\\\\t/g," "));\\n}function Q(l,k){return B.nodeProp(l,"_magicdom_"+k);}var A=(function(){var k=[];var o=[];var l=null;\\nvar n=Changeset.newOp("+");var m={length:function(){return k.length;},atColumnZero:function(){return k[k.length-1]==="";\\n},startNew:function(){k.push("");m.flush(true);l=Changeset.smartOpAssembler();},textOfLine:function(p){return k[p];\\n},appendText:function(p,q){k[k.length-1]+=p;n.attribs=q;n.chars=p.length;l.append(n);},textLines:function(){return k.slice();\\n},attribLines:function(){return o;},flush:function(p){if(l){o.push(l.toString());l=null;}}};m.startNew();\\nreturn m;}());var E={};function O(k){if(!A.atColumnZero()){K(k);}}var N,D,C;var J=[-1,-1],I=[-1,-1];var j={div:1,p:1,pre:1};\\nfunction S(k,l){if(B.nodeNumChildren(k)==0){return true;}if(B.nodeNumChildren(k)==1&&Q(k,"shouldBeEmpty")&&B.optNodeInnerHTML(k)=="&nbsp;"&&!Q(k,"unpasted")){if(l){var m=B.nodeChild(k,0);\\nL(m,0,l);L(m,1,l);}return true;}return false;}function F(n,m){var l=A.length()-1;var k=A.textOfLine(l).length;\\nif(k==0&&m.listType&&m.listType!="none"){k+=1;}k+=n;return[l,k];}function P(k,l,m){if(!B.isNodeText(k)){L(k,l,m);\\n}}function L(m,k,l){if(D&&m==D.node&&D.index==k){J=F(0,l);}if(C&&m==C.node&&C.index==k){I=F(0,l);}}function b(k,l){k.flags[l]=(k.flags[l]||0)+1;\\n}function W(k,l){k.flags[l]--;}function X(k,l){if(!k.attribs[l]){k.attribs[l]=1;}else{k.attribs[l]++;\\n}G(k);}function f(k,l){k.attribs[l]--;G(k);}function T(k,l){var m=k.listType;k.listLevel=(k.listLevel||0)+1;\\nif(l!="none"){k.listNesting=(k.listNesting||0)+1;}k.listType=l;G(k);return m;}function d(k,l){k.listLevel--;\\nif(k.listType!="none"){k.listNesting--;}k.listType=l;G(k);}function c(k,m){var l=k.author;k.authorLevel=(k.authorLevel||0)+1;\\nk.author=m;G(k);return l;}function g(k,l){k.authorLevel--;k.author=l;G(k);}function G(k){var l=[];for(var m in k.attribs){if(k.attribs[m]){l.push([m,"true"]);\\n}}if(k.authorLevel>0){var n=["author",k.author];if(M.putAttrib(n,true)>=0){l.push(n);}}k.attribString=Changeset.makeAttribsString("+",l,M);\\n}function V(k){A.appendText("*",Changeset.makeAttribsString("+",[["list",k.listType],["insertorder","first"]],M));\\n}function K(k){if(k){var l=A.textOfLine(A.length()-1).length==0;if(l&&k.listType&&k.listType!="none"){V(k);\\n}}A.startNew();}E.notifySelection=function(k){if(k){N=k;D=N.startPoint;C=N.endPoint;}};E.collectContent=function(m,k){if(!k){k={flags:{},attribs:{},attribString:""};\\n}var AF=R(m);var AK=S(m,k);if(AF){O(k);}var AL=A.length()-1;P(m,0,k);if(B.isNodeText(m)){var n=B.nodeValue(m);\\nvar AG="";var t=0;if(n.length==0){if(D&&m==D.node){J=F(0,k);}if(C&&m==C.node){I=F(0,k);}}while(n.length>0){var AA=0;\\nif(k.flags.preMode){var AE=n.split("\\\\n",1)[0];AA=AE.length+1;AG=n.substring(AA);n=AE;}else{}if(D&&m==D.node&&D.index-t\\x3c=n.length){J=F(D.index-t,k);\\n}if(C&&m==C.node&&C.index-t\\x3c=n.length){I=F(C.index-t,k);}var u=n;if((!k.flags.preMode)&&/^[\\\\r\\\\n]*$/.exec(n)){u="";\\n}var AC=A.textOfLine(A.length()-1).length==0;if(AC){u=u.replace(/^\\\\n*/,"");}if(AC&&k.listType&&k.listType!="none"){V(k);\\n}A.appendText(e(u),k.attribString);t+=AA;n=AG;if(n.length>0){K(k);}}}else{var l=(B.nodeTagName(m)||"").toLowerCase();\\nif(l=="br"){K(k);}else{if(l=="script"||l=="style"){}else{if(!AK){var p=B.nodeAttr(m,"style");var r=B.nodeProp(m,"className");\\nvar w=(l=="pre");if((!w)&&H.safari){w=(p&&/\\\\bwhite-space:\\\\s*pre\\\\b/i.exec(p));}if(w){b(k,"preMode");}var s=null;\\nvar v=null;var AB=null;if(h){function o(x){s=(s||[]);s.push(x);X(k,x);}if(l=="b"||(p&&/\\\\bfont-weight:\\\\s*bold\\\\b/i.exec(p))||l=="strong"){o("bold");\\n}if(l=="i"||(p&&/\\\\bfont-style:\\\\s*italic\\\\b/i.exec(p))||l=="em"){o("italic");}if(l=="u"||(p&&/\\\\btext-decoration:\\\\s*underline\\\\b/i.exec(p))||l=="ins"){o("underline");\\n}if(l=="s"||(p&&/\\\\btext-decoration:\\\\s*line-through\\\\b/i.exec(p))||l=="del"){o("strikethrough");}if(l=="h1"){o("h1");\\n}if(l=="h2"){o("h2");}if(l=="h3"){o("h3");}if(l=="h4"){o("h4");}if(l=="h5"){o("h5");}if(l=="h6"){o("h6");\\n}if(l=="ul"){var z;var AD=r&&/(?:^| )list-(bullet[12345678])\\\\b/.exec(r);z=AD&&AD[1]||"bullet"+String(Math.min(_MAX_LIST_LEVEL,(k.listNesting||0)+1));\\nv=(T(k,z)||"none");}else{if((l=="div"||l=="p")&&r&&r.match(/(?:^| )ace-line\\\\b/)){v=(T(k,z)||"none");}}if(U&&r){var y=r.match(/\\\\S+/g);\\nif(y&&y.length>0){for(var q=0;q\\x3cy.length;q++){var AH=y[q];var AI=U(AH);if(AI){AB=(c(k,AI)||"none");break;\\n}}}}}var AJ=B.nodeNumChildren(m);for(var q=0;q\\x3cAJ;q++){var AH=B.nodeChild(m,q);E.collectContent(AH,k);\\n}if(w){W(k,"preMode");}if(s){for(var q=0;q\\x3cs.length;q++){f(k,s[q]);}}if(v){d(k,v);}if(AB){g(k,AB);}}}}}if(!H.msie){P(m,1,k);\\n}if(AF){if(A.length()-1==AL){K(k);}else{O(k);}}if(H.msie){P(m,1,k);}};E.notifyNextNode=function(k){if((!k)||(R(k)&&!S(k))){O(null);\\n}};var a=function(){return J;};var Z=function(){return I;};E.getLines=function(){return A.textLines();\\n};E.finish=function(){A.flush();var l=A.attribLines();var k=E.getLines();k.length--;l.length--;var n=a();\\nvar o=Z();function p(){var v=2000;var z=10;var x=0;var w=0;for(var q=k.length-1;q>=0;q--){var s=k[q];\\nvar u=l[q];if(s.length>v+z){var r=[];var t=[];while(s.length>v){lengthToTake=v;r.push(s.substring(0,lengthToTake));\\ns=s.substring(lengthToTake);t.push(Changeset.subattribution(u,0,lengthToTake));u=Changeset.subattribution(u,lengthToTake);\\n}if(s.length>0){r.push(s);t.push(u);}function y(AA){if(AA[0]\\x3c0){return;}var AB=AA[0];var AD=AA[1];if(AB>q){AB+=(r.length-1);\\n}else{if(AB==q){var AC=0;while(AD>r[AC].length){AD-=r[AC].length;AC++;}AB+=AC;}}AA[0]=AB;AA[1]=AD;}y(n);\\ny(o);x++;w+=r.length;r.unshift(q,1);k.splice.apply(k,r);t.unshift(q,1);l.splice.apply(l,t);}}return{linesWrapped:x,numLinesAfter:w};\\n}var m=p();return{selStart:n,selEnd:o,linesWrapped:m.linesWrapped,numLinesAfter:m.numLinesAfter,lines:k,lineAttribs:l};\\n};return E;}function makeChangesetTracker(K,A,J){var D=Changeset.makeAText("\\\\n");var B=null;var C=Changeset.identity(1);\\nvar F=false;var E=false;var G=null;var H=null;function L(){if(G&&H===null){H=K.setTimeout(function(){try{G();\\n}finally{H=null;}},0);}}var I;return I={isTracking:function(){return F;},setBaseText:function(M){I.setBaseAttributedText(Changeset.makeAText(M),null);\\n},setBaseAttributedText:function(M,N){J.withCallbacks("setBaseText",function(P){F=true;D=Changeset.cloneAText(M);\\nif(N){var O=(new AttribPool()).fromJsonable(N);D.attribs=Changeset.moveOpsToNewPool(D.attribs,O,A);}B=null;\\nC=Changeset.identity(M.text.length);E=true;try{P.setDocumentAttributedText(M);}finally{E=false;}});},composeUserChangeset:function(M){if(!F){return;\\n}if(E){return;}if(Changeset.isIdentity(M)){return;}C=Changeset.compose(C,M,A);L();},applyChangesToBase:function(M,O,N){if(!F){return;\\n}J.withCallbacks("applyChangesToBase",function(V){if(N){var U=(new AttribPool()).fromJsonable(N);M=Changeset.moveOpsToNewPool(M,U,A);\\n}D=Changeset.applyToAText(M,D,A);var P=M;if(B){var Q=B;B=Changeset.follow(M,Q,false,A);P=Changeset.follow(Q,M,true,A);\\n}var R=true;var S=C;C=Changeset.follow(P,S,R,A);var T=Changeset.follow(S,P,!R,A);var W=true;E=true;try{V.applyChangesetToDocument(T,W);\\n}finally{E=false;}});},prepareUserChangeset:function(){var M;if(B){M=Changeset.compose(B,C,A);}else{if(Changeset.isIdentity(C)){M=null;\\n}else{M=C;}}var N=null;if(M){B=M;C=Changeset.identity(Changeset.newLen(M));N=M;}var P=null;if(N){var O=Changeset.prepareForWire(N,A);\\nP=O.pool.toJsonable();N=O.translated;}var Q={changeset:N,apool:P};return Q;},applyPreparedChangesetToBase:function(){if(!B){throw new Error("applySubmittedChangesToBase: no submitted changes to apply");\\n}D=Changeset.applyToAText(B,D,A);B=null;},setUserChangeNotificationCallback:function(M){G=M;},hasUncommittedChanges:function(){return !!(B||(!Changeset.isIdentity(C)));\\n}};}var linestylefilter={};linestylefilter.ATTRIB_CLASSES={bold:"tag:b",italic:"tag:i",underline:"tag:u",strikethrough:"tag:s",h1:"tag:h1",h2:"tag:h2",h3:"tag:h3",h4:"tag:h4",h5:"tag:h5",h6:"tag:h6"};\\nlinestylefilter.getAuthorClassName=function(A){return"author-"+A.replace(/[^a-y0-9]/g,function(B){if(B=="."){return"-";\\n}return"z"+B.charCodeAt(0)+"z";});};linestylefilter.getLineStyleFilter=function(B,E,A,D){if(B==0){return A;\\n}var C=A;var F=(function(){var N=B;var L=0;var J;var G;function P(R){var Q="";Changeset.eachAttribNumber(R,function(U){var S=D.getAttribKey(U);\\nif(S){var T=D.getAttribValue(U);if(T){if(S=="author"){Q+=" "+linestylefilter.getAuthorClassName(T);}else{if(S=="list"){Q+=" list:"+T;\\n}else{if(linestylefilter.ATTRIB_CLASSES[S]){Q+=" "+linestylefilter.ATTRIB_CLASSES[S];}}}}}});return Q.substring(1);\\n}var O=Changeset.opIterator(E);var H,I;function K(){H=O.next();I=(H.opcode&&P(H.attribs));}K();function M(){if(L\\x3cN){J=I;\\nG=H.chars;K();while(H.opcode&&I==J){G+=H.chars;K();}}}M();return function(Q,S){while(Q.length>0){if(G\\x3c=0){return C(Q,S);\\n}var R=Q.length;if(R>G){R=G;}var T=Q.substring(0,R);Q=Q.substring(R);C(T,(S&&S+" ")+J);L+=R;G-=R;if(G==0){M();\\n}}};})();return F;};linestylefilter.getAtSignSplitterFilter=function(E,C){var B=/@/g;B.lastIndex=0;var A=null;\\nvar D;while((D=B.exec(E))){if(!A){A=[];}A.push(D.index);}if(!A){return C;}return linestylefilter.textAndClassFuncSplitter(C,A);\\n};linestylefilter.REGEX_WORDCHAR=/[\\\\u0030-\\\\u0039\\\\u0041-\\\\u005A\\\\u0061-\\\\u007A\\\\u00C0-\\\\u00D6\\\\u00D8-\\\\u00F6\\\\u00F8-\\\\u00FF\\\\u0100-\\\\u1FFF\\\\u3040-\\\\u9FFF\\\\uF900-\\\\uFDFF\\\\uFE70-\\\\uFEFE\\\\uFF10-\\\\uFF19\\\\uFF21-\\\\uFF3A\\\\uFF41-\\\\uFF5A\\\\uFF66-\\\\uFFDC]/;\\nlinestylefilter.REGEX_URLCHAR=new RegExp("("+/[-:@a-zA-Z0-9_.,~%+\\\\\\x2f\\\\\\\\?=&#;()$]/.source+"|"+linestylefilter.REGEX_WORDCHAR.source+")");\\nlinestylefilter.REGEX_URL=new RegExp(/(?:(?:https?|s?ftp|ftps|file|smb|afp|nfs|(x-)?man|gopher|txmt):\\\\\\x2f\\\\\\x2f|mailto:)/.source+linestylefilter.REGEX_URLCHAR.source+"*(?![:.,;])"+linestylefilter.REGEX_URLCHAR.source,"g");\\nlinestylefilter.getURLFilter=function(I,F){linestylefilter.REGEX_URL.lastIndex=0;var A=null;var C=null;\\nvar B;while((B=linestylefilter.REGEX_URL.exec(I))){if(!A){A=[];C=[];}var D=B.index;var E=B[0];A.push([D,E]);\\nC.push(D,D+E.length);}if(!A){return F;}function G(L){for(var K=0;K\\x3cA.length;K++){var J=A[K];if(L>=J[0]&&L\\x3cJ[0]+J[1].length){return J[1];\\n}}return false;}var H=(function(){var J=0;return function(K,N){var O=K.length;var M=N;var L=G(J);if(L){M+=" url:"+L;\\n}F(K,M);J+=O;};})();return linestylefilter.textAndClassFuncSplitter(H,C);};linestylefilter.textAndClassFuncSplitter=function(D,B){var A=0;\\nvar C=0;while(B&&A\\x3cB.length&&B[A]==0){A++;}function E(F,H){if((!B)||A>=B.length){D(F,H);C+=F.length;}else{var J=B;\\nvar G=J[A]-C;var I=F.length;if(G>=I){D(F,H);C+=F.length;if(G==I){A++;}}else{if(G>0){D(F.substring(0,G),H);\\nC+=G;}A++;E(F.substring(G),H);}}}return E;};linestylefilter.populateDomLine=function(F,D,E,G){var A=F;\\nif(A.slice(-1)=="\\\\n"){A=A.substring(0,A.length-1);}function C(H,I){G.appendSpan(H,I);}var B=C;B=linestylefilter.getURLFilter(A,B);\\nB=linestylefilter.getLineStyleFilter(A.length,D,B,E);B(A,"");};var domline={};domline.noop=function(){};\\ndomline.identity=function(A){return A;};domline.addToLineClass=function(A,B){B.replace(/\\\\S+/g,function(C){if(C.indexOf("line:")==0){A=(A?A+" ":"")+C.substring(5);\\n}});return A;};domline.createDomLine=function(N,H,J,O){var A={node:null,appendSpan:domline.noop,prepareForAdd:domline.noop,notifyAdded:domline.noop,clearSpans:domline.noop,finishUpdate:domline.noop,lineMarker:0};\\nvar M=(J||{});var G=O;if(G){A.node=G.createElement("div");}else{A.node={innerHTML:"",className:""};}var D=[];\\nvar F,E;var C=null;function K(R){return domline.processSpaces(R,H);}var L=domline.identity;var Q=(H?L:K);\\nvar P=(H?K:L);var B="ace-line";A.appendSpan=function(W,R){if(R.indexOf("list")>=0){var U=/(?:^| )list:(\\\\S+)/.exec(R);\\nif(U){U=U[1];if(U){F=\\\'\\x3cul class="list-\\\'+U+\\\'">\\x3cli>\\\';E="\\x3c/li>\\x3c/ul>";}A.lineMarker+=W.length;return;}}var X=null;\\nvar S=null;if(R.indexOf("url")>=0){R=R.replace(/(^| )url:(\\\\S+)/g,function(a,Z,Y){X=Y;return Z+"url";});\\n}if(R.indexOf("tag")>=0){R=R.replace(/(^| )tag:(\\\\S+)/g,function(a,Z,Y){if(!S){S=[];}S.push(Y.toLowerCase());\\nreturn Z+Y;});}if((!W)&&R){B=domline.addToLineClass(B,R);}else{if(W){var T="";var V="";if(X){T=T+\\\'\\x3ca href="\\\'+X.replace(/\\\\"/g,"&quot;")+\\\'">\\\';\\nV="\\x3c/a>"+V;}if(S){S.sort();T=T+"\\x3c"+S.join(">\\x3c")+">";S.reverse();V="\\x3c/"+S.join(">\\x3c/")+">"+V;}D.push(\\\'\\x3cspan class="\\\',R||"",\\\'">\\\',T,Q(domline.escapeHTML(W)),V,"\\x3c/span>");\\n}}};A.clearSpans=function(){D=[];B="";A.lineMarker=0;};function I(){var R=P(D.join(""));if(!R){if((!G)||(!J)){R+="&nbsp;";\\n}else{if(!M.msie){R+="\\x3cbr/>";}}}if(N){R=(F||"")+R+(E||"");}D=F=E=null;if(R!==C){C=R;A.node.innerHTML=C;\\n}if(B!==null){A.node.className=B;}}A.prepareForAdd=I;A.finishUpdate=I;A.getInnerHTML=function(){return C||"";\\n};return A;};domline.escapeHTML=function(B){var A=/[&\\x3c>\\\'"]/g;/\\\']/;if(!A.MAP){A.MAP={"&":"&amp;","\\x3c":"&lt;",">":"&gt;",\\\'"\\\':"&#34;","\\\'":"&#39;"};\\n}return B.replace(A,function(C){return A.MAP[C];});};domline.processSpaces=function(F,G){if(F.indexOf("\\x3c")\\x3c0&&!G){return F.replace(/ /g,"&nbsp;");\\n}var B=[];F.replace(/\\x3c[^>]*>?| |[^ \\x3c]+/g,function(H){B.push(H);});if(G){var D=true;var E=false;for(var A=B.length-1;\\nA>=0;A--){var C=B[A];if(C==" "){if(D||E){B[A]="&nbsp;";}D=false;E=true;}else{if(C.charAt(0)!="\\x3c"){D=false;\\nE=false;}}}for(var A=0;A\\x3cB.length;A++){var C=B[A];if(C==" "){B[A]="&nbsp;";break;}else{if(C.charAt(0)!="\\x3c"){break;\\n}}}}else{for(var A=0;A\\x3cB.length;A++){var C=B[A];if(C==" "){B[A]="&nbsp;";}}}return B.join("");};\'+\'//-->\\n\\x3c/script>\')'));
+T.push(('(\'\\x3cscript type="text/javascript">//<!--\\n\'+\'function OUTER(Cj){var AL=false;var B4=false;var Ae=" ";var BW=8;var BO=4;var CC=4;var Ai=20;var Bj=8;\\nvar BS=8;var BU=[];var e="";var BL=false;var E=parent.editorInfo;var g=window.frameElement;var F=g.ace_outerWin;\\ng.ace_outerWin=null;var U=g.nextSibling;var x=U.nextSibling;var Cb=x.nextSibling;BX();var Au=function(i){};\\nvar Ar=function(i){return true;};var An=function(){};var A={lines:newSkipList(),selStart:null,selEnd:null,selFocusAtStart:false,alltext:"",alines:[],apool:new AttribPool()};\\nif(undoModule.enabled){undoModule.apool=A.apool;}var B,D;var l=true;var L=true;var AQ=true;var AY=true;\\nvar q=Ai+BO+BS;var Aj=Bj;var Bo=0,CQ=0;var T=(AL&&top.console);if(!T){var Al=["log","debug","info","warn","error","assert","dir","dirxml","group","groupEnd","time","timeEnd","count","trace","profile","profileEnd"];\\nT={};for(var Ah=0;Ah\\x3cAl.length;++Ah){T[Al[Ah]]=function(){};}}var P=window.PROFILER;if(!P){P=function(){return{start:I,mark:I,literal:I,end:I,cancel:I};\\n};}function I(){}function Ce(i){return i;}var w=I;window.dmesg=I;var O=parent;var AR="monospace";var v=12;\\nfunction AW(){return Math.round(v*4/3);}var X=null;function Bh(){X=makeCSSManager("dynamicsyntax");}var N=makeChangesetTracker(O,A.apool,{withCallbacks:function(i,Cp){Z(i,function(){G(1);\\nCp({setDocumentAttributedText:function(Cq){Ab(Cq);},applyChangesetToDocument:function(Cs,Cr){var Cq=C.editEvent.eventType;\\nC.startNewEvent("nonundoable");c(Cs,Cr);C.startNewEvent(Cq);}});});}});var Ao={};function CV(i,Cp){if((typeof i)!="string"){throw new Error("setAuthorInfo: author ("+i+") is not a string");\\n}if(!Cp){delete Ao[i];if(X){X.removeSelectorStyle(Av(BP(i)));}}else{Ao[i]=Cp;if(Cp.bgcolor){if(X){var Cq=Cp.bgcolor;\\nif((typeof Cp.fade)=="number"){Cq=BZ(Cq,Cp.fade);}X.selectorStyle(Av(BP(i))).backgroundColor=Cq;}}}}function BP(i){return"author-"+i.replace(/[^a-y0-9]/g,function(Cp){if(Cp=="."){return"-";\\n}return"z"+Cp.charCodeAt(0)+"z";});}function Bd(i){if(i.substring(0,7)=="author-"){return i.substring(7).replace(/[a-y0-9]+|-|z.+?z/g,function(Cp){if(Cp=="-"){return".";\\n}else{if(Cp.charAt(0)=="z"){return String.fromCharCode(Number(Cp.slice(1,-1)));}else{return Cp;}}});}return null;\\n}function Av(i){return".authorColors ."+i;}function AT(){if(X){var Cp=x.offsetHeight;var Cq=AW();var i=0;\\nvar Cs=0;if(Cp\\x3cCq){i=Math.ceil((Cq-Cp)/2);Cs=Cq-Cp-i;}var Cr=X.selectorStyle("#innerdocbody span");Cr.paddingTop=Cs+"px";\\nCr.paddingBottom=i+"px";}}function Ci(Cp){var i=colorutils.css2triple(Cp);i=colorutils.saturate(i);i=colorutils.scaleColor(i,0,0.5/colorutils.luminosity(i));\\nreturn colorutils.triple2css(i);}function BZ(Cp,Cq){var i=colorutils.css2triple(Cp);i=colorutils.blend(i,[1,1,1],Cq);\\nreturn colorutils.triple2css(i);}function By(i){O.setTimeout(function(){alert(i);},0);}var C=null;function b(Cq,Cx){if(BL){return;\\n}if(C){T.error("Can\\\'t enter callstack "+Cq+", already in "+C.type);}var Ct=false;function Cw(){Ct=true;\\nT.profile();}function Cr(Cz){return{eventType:Cz,backset:null};}function Cs(Cz){if(A.selStart&&A.selEnd){var C2=A.lines.offsetOfIndex(A.selStart[0])+A.selStart[1];\\nvar C1=A.lines.offsetOfIndex(A.selEnd[0])+A.selEnd[1];Cz.selStart=C2;Cz.selEnd=C1;Cz.selFocusAtStart=A.selFocusAtStart;\\n}if(undoModule.enabled){var C0=false;try{if(Cz.eventType=="setup"||Cz.eventType=="importText"||Cz.eventType=="setBaseText"){undoModule.clearHistory();\\n}else{if(Cz.eventType=="nonundoable"){if(Cz.changeset){undoModule.reportExternalChange(Cz.changeset);\\n}}else{undoModule.reportEvent(Cz);}}C0=true;}finally{if(!C0){undoModule.enabled=false;}}}}function Cy(C1,C0){var Cz=C.editEvent;\\nif(!C0){Cs(Cz);}C.editEvent=Cr(C1);return Cz;}C={type:Cq,docTextChanged:false,selectionAffected:false,userChangedSelection:false,domClean:false,profileRest:Cw,isUserChange:false,repChanged:false,editEvent:Cr(Cq),startNewEvent:Cy};\\nvar Cu=false;var Cv;try{Cv=Cx();Cu=true;}catch(Cp){BU.push({error:Cp,time:+new Date()});w(Cp.toString());\\nthrow Cp;}finally{var i=C;if(Cu){Cs(i.editEvent);if(i.domClean&&i.type!="setup"){if(i.isUserChange){if(i.repChanged){AH.notifyChange();\\n}else{AH.notifyTick();}}Be.recolorLines();if(i.selectionAffected){Ba();}if((i.docTextChanged||i.userChangedSelection)&&i.type!="applyChangesToBase"){CP();\\n}if(i.docTextChanged&&i.type.indexOf("importText")\\x3c0){An();}}}else{if(C.type=="idleWorkTimer"){H.atLeast(1000);\\n}}C=null;if(Ct){T.profileEnd();}}return Cv;}function Z(Cp,i){if(!C){b(Cp,i);}else{i();}}function Ag(i){if(A.lines.containsKey(i)){var Cp=A.lines.offsetOfKey(i);\\nvar Cq=A.lines.atKey(i).width;AV(Cp,Cp+Cq);}}function AM(i){return A.lines.atOffset(i).key;}var Be=(function(){var i={};\\nvar Cp={};Cp.setCharNeedsRecoloring=function(Cq){if(Cq>=A.alltext.length){Cq=A.alltext.length-1;}i[AM(Cq)]=true;\\n};Cp.setCharRangeNeedsRecoloring=function(Cs,Cr){if(Cs>=A.alltext.length){Cs=A.alltext.length-1;}if(Cr>=A.alltext.length){Cr=A.alltext.length-1;\\n}var Cu=A.lines.atOffset(Cs);var Ct=A.lines.atOffset(Cr).key;i[Ct]=true;var Cq=Cu;while(Cq&&Cq.key!=Ct){i[Cq.key]=true;\\nCq=A.lines.next(Cq);}};Cp.recolorLines=function(){for(var Cq in i){Ag(Cq);}i={};};return Cp;})();var AH=(function(){var i={};\\ni.notifyTick=function(){Cr(false);};i.notifyChange=function(){Cr(true);};i.shouldNormalizeOnChar=function(Ct){if(Cq.active){return true;\\n}Ct=String.fromCharCode(Ct);return !!(Cp[Ct]);};var Cq={active:false,whichChars:null,whichLineKeys:null,expireTime:null};\\nvar Cp={"(":1,")":-1,"[":2,"]":-2,"{":3,"}":-3};var Cs=/[{}\\\\[\\\\]()]/g;function Cr(C0){function C5(C8){var C7=BI();\\nvar DA=100;var C9=3000;if(C7[0]>C8-DA){C7[0]=C8-DA;}if(C7[0]\\x3cC8-C9){C7[0]=C8-C9;}if(C7[0]\\x3c0){C7[0]=0;\\n}if(C7[1]\\x3cC8+DA){C7[1]=C8+DA;}if(C7[1]>C8+C9){C7[1]=C8+C9;}if(C7[1]>A.lines.totalWidth()){C7[1]=A.lines.totalWidth();\\n}return C7;}function C6(DH,DC){var C8=C5(DH);var DF=A.alltext.substring(C8[0],C8[1]);var DG=DF.replace(Cs,"(");\\nvar C7=DH-C8[0];var DE=[];var DA=false;var DB=false;function DI(){if(C7\\x3c0){return;}if(DC){C7++;}else{C7--;\\n}if(C7\\x3c0||C7>=DF.length){C7=-1;}if(C7>=0){if(DC){C7=DG.indexOf("(",C7);}else{C7=DG.lastIndexOf("(",C7);\\n}}}while((!DA)&&(C7>=0)){if(BH(C7+C8[0])=="p"){var DJ=Cp[DF.charAt(C7)];var DD=DC;var C9=DJ;if(C9\\x3c0){DD=!DD;\\nC9=-C9;}if(DD){DE.push(C9);}else{var DK=DE.pop();if(DK!=C9){DA=true;DB=false;}else{if(DE.length==0){DA=true;\\nDB=true;}}}}if((!DA)&&(C7>=0)){DI();}}if(!DA){return null;}return{chr:(C7+C8[0]),good:DB};}var Ct=Cq;\\nvar Cu=null;var Cw=null;if(Ct.active&&(C0||(S()>Ct.expireTime))){Cw=Ct.whichLineKeys;Ct.active=false;\\n}if((!Ct.active)&&C0&&V()&&u()>0){var Cv=A7();if(Cv>0&&BH(Cv-1)=="p"){var Cz=A.alltext.charAt(Cv-1);if(Cp[Cz]){var C2=(Cp[Cz]>0);\\nvar Cx=C6(Cv-1,C2);if(Cx){var Cy=Cx.chr;var C4=Cx.good;Ct.active=true;Cu={};Cu[Cv-1]="flash";Cu[Cy]=(C4?"flash":"flashbad");\\nCt.whichLineKeys=[];Ct.whichLineKeys.push(AM(Cv-1));Ct.whichLineKeys.push(AM(Cy));Ct.expireTime=S()+4000;\\nnewlyActive=true;}}}}if(Cw){Ag(Cw[0]);Ag(Cw[1]);}if(Ct.active&&Cu){function C3(C8,C7,C9,DB){var DA=Cu[DB];\\nif(C7){C9(C8,C7+" "+DA);}else{C9(C8,C7);}}for(var C1 in Cu){AV((+C1),(+C1)+1,null,C3);}}}return i;})();\\nfunction B8(){BL=true;if(H){H.never();}BF();}function BN(){return;function Cp(){throw new Error("checkALines");\\n}if(A.alines.length!=A.lines.length()){Cp();}for(var Cq=0;Cq\\x3cA.alines.length;Cq++){var Cu=A.alines[Cq];\\nvar Cv=A.lines.atIndex(Cq).text+"\\\\n";var Cw=Cv.length;var Cr=Changeset.opIterator(Cu);var Ct=0;while(Cr.hasNext()){var Cs=Cr.next();\\nCt+=Cs.chars;if(Cr.hasNext()){if(Cs.lines!=0){Cp();}}else{if(Cs.lines!=1){Cp();}}}if(Ct!=Cw){Cp();}}}function CD(i){L=i;\\nvar Cp="doesWrap";R(B,"doesWrap",L);O.setTimeout(function(){Z("setWraps",function(){G(7);BY();h();});\\n},0);}function CK(i){var Cp=AY;AY=!!i;if(i!=Cp){if(!i){Z("setStyled",function(){G(12);var Cq=[];for(var Cr in Ad){Cq.push([Cr,""]);\\n}A2(0,A.alltext.length,Cq);});}}}function Bv(i){AR=i;B.style.fontFamily=AR;x.style.fontFamily=AR;O.setTimeout(function(){AT();\\n},0);}function Bl(i){v=i;B.style.fontSize=v+"px";B.style.lineHeight=AW()+"px";U.style.lineHeight=AW()+"px";\\nx.style.fontSize=v+"px";O.setTimeout(function(){AT();},0);}function BY(){AV(0,A.alltext.length);}function BG(i){l=i;\\nif(!l){Az(false);}else{Az(true);}R(B,"static",!l);}function Ax(){BG(l);}function CA(i,Cs,Cq){var Cp;if(Cq){if(i.charAt(i.length-1)!="\\\\n"){throw new Error("new raw text must end with newline");\\n}if(/[\\\\r\\\\t\\\\xa0]/.exec(i)){throw new Error("new raw text must not contain CR, tab, or nbsp");}Cp=i.substring(0,i.length-1).split("\\\\n");\\n}else{Cp=map(i.split("\\\\n"),CH);}var Cr="\\\\n";if(Cp.length>0){Cr=Cp.join("\\\\n")+"\\\\n";}Z("importText"+(Cs?"Undoable":""),function(){Bb(Cr);\\n});if(Cq&&A.alltext!=i){throw new Error("mismatch error setting raw text in importText");}}function B9(i,Cp,Cr){i=Changeset.cloneAText(i);\\nif(Cp){var Cq=(new AttribPool()).fromJsonable(Cp);i.attribs=Changeset.moveOpsToNewPool(i.attribs,Cq,A.apool);\\n}Z("importText"+(Cr?"Undoable":""),function(){Ab(i);});}function Ab(Cq){G(8);var Cs=A.lines.totalWidth();\\nvar Cr=A.lines.length();var Cu=A.lines.offsetOfIndex(Cr-1);var Ct=A.lines.atIndex(Cr-1).text.length;var Cp=Changeset.smartOpAssembler();\\nvar i=Changeset.newOp("-");i.chars=Cu;i.lines=Cr-1;Cp.append(i);i.chars=Ct;i.lines=0;Cp.append(i);Changeset.appendATextToAssembler(Cq,Cp);\\nvar Cv=Cs+Cp.getLengthChange();var Cw=Changeset.checkRep(Changeset.pack(Cs,Cv,Cp.toString(),Cq.text.slice(0,-1)));\\nc(Cw);d([0,A.lines.atIndex(0).lineMarker],[0,A.lines.atIndex(0).lineMarker]);H.atMost(100);if(A.alltext!=Cq.text){w(htmlPrettyEscape(A.alltext));\\nw(htmlPrettyEscape(Cq.text));throw new Error("mismatch error setting raw text in setDocAText");}}function Bb(i){Ab(Changeset.makeAText(i));\\n}function CW(){var Cp=A.alltext;var i=Cp.length;if(i>0){i--;}return Cp.substring(0,i);}function CL(){if(C&&!C.domClean){Z("exportText",function(){G(2);\\n});}return CW();}function B7(){h();}function Bn(i){Ar=i;}function Bq(i){Au=i;}function Bp(i){An=i;}function Bm(){if(C&&!C.domClean){Z("getFormattedCode",r);\\n}var Cp=[];if(A.lines.length()>0){var i=A.lines.atIndex(0);while(i){var Cq=i.domInfo;Cp.push((Cq&&Cq.getInnerHTML())||domline.processSpaces(domline.escapeHTML(i.text),L)||"&nbsp;");\\ni=A.lines.next(i);}}return\\\'\\x3cdiv class="syntax">\\x3cdiv>\\\'+Cp.join("\\x3c/div>\\\\n\\x3cdiv>")+"\\x3c/div>\\x3c/div>";}var Aa={bold:function(){J("bold");\\n},italic:function(){J("italic");},underline:function(){J("underline");},strikethrough:function(){J("strikethrough");\\n},h1:function(){J("h1");},h2:function(){J("h2");},h3:function(){J("h3");},h4:function(){J("h4");},h5:function(){J("h5");\\n},h6:function(){J("h6");},undo:function(){t("undo");},redo:function(){t("redo");},clearauthorship:function(i){if((!(A.selStart&&A.selEnd))||V()){if(i){i();\\n}else{A2(0,A.alltext.length,[["author",""]]);}}else{Bi("author","");}},insertunorderedlist:As,indent:function(){if(!AU(false)){As();\\n}},outdent:function(){AU(true);}};function CR(i){i=i.toLowerCase();var Cp=Array.prototype.slice.call(arguments,1);\\nif(Aa[i]){b(i,function(){G(9);Aa[i].apply(Aa,Cp);});}}E.ace_focus=Bc;E.ace_importText=CA;E.ace_importAText=B9;\\nE.ace_exportText=CL;E.ace_editorChangedSize=B7;E.ace_setOnKeyPress=Bn;E.ace_setOnKeyDown=Bq;E.ace_setNotifyDirty=Bp;\\nE.ace_dispose=B8;E.ace_getFormattedCode=Bm;E.ace_setEditable=BG;E.ace_execCommand=CR;E.ace_setProperty=function(Cq,i){var Cp=Cq.toLowerCase();\\nif(Cp=="wraps"){CD(i);}else{if(Cp=="showsauthorcolors"){R(B,"authorColors",!!i);}else{if(Cp=="showsuserselections"){R(B,"userSelections",!!i);\\n}else{if(Cp=="showslinenumbers"){AQ=!!i;R(U,"sidedivhidden",!AQ);h();}else{if(Cp=="grayedout"){R(F.document.body,"grayedout",!!i);\\n}else{if(Cp=="dmesg"){w=i;window.dmesg=i;}else{if(Cp=="userauthor"){e=String(i);}else{if(Cp=="styled"){CK(i);\\n}else{if(Cp=="textface"){Bv(i);}else{if(Cp=="textsize"){Bl(i);}}}}}}}}}}};E.ace_setBaseText=function(i){N.setBaseText(i);\\n};E.ace_setBaseAttributedText=function(Cp,i){AT();N.setBaseAttributedText(Cp,i);};E.ace_applyChangesToBase=function(Cq,i,Cp){N.applyChangesToBase(Cq,i,Cp);\\n};E.ace_prepareUserChangeset=function(){return N.prepareUserChangeset();};E.ace_applyPreparedChangesetToBase=function(){N.applyPreparedChangesetToBase();\\n};E.ace_setUserChangeNotificationCallback=function(i){N.setUserChangeNotificationCallback(i);};E.ace_setAuthorInfo=function(i,Cp){CV(i,Cp);\\n};E.ace_setAuthorSelectionRange=function(Cp,Cq,i){N.setAuthorSelectionRange(Cp,Cq,i);};E.ace_getUnhandledErrors=function(){return BU.slice();\\n};E.ace_getDebugProperty=function(i){if(i=="debugger"){window["eval"]("debugger");}else{if(i=="rep"){return A;\\n}else{if(i=="window"){return window;}else{if(i=="document"){return document;}}}}return undefined;};function S(){return(new Date()).getTime();\\n}function AD(Cs){var Cq=S();var Ct=0;var i=false;var Cr=false;var Cp=function(){if(i){if((!Cr)){Cr=true;\\n}return true;}var Cu=S()-Cq;if(Cu>Cs){i=true;return true;}else{Ct=Cu;return false;}};Cp.elapsed=function(){return S()-Cq;\\n};return Cp;}function CZ(Cs){var i=null;var Cp=0;function Cr(){if(i){O.clearTimeout(i);i=null;}}function Cq(Cv){Cr();\\nCp=Cv;var Cu=Cv-S();if(Cu\\x3c0){Cu=0;}i=O.setTimeout(Ct,Cu);}function Ct(){i=null;Cs();}return{atMost:function(Cv){var Cu=S()+Cv;\\nif((!i)||Cp>Cu){Cq(Cu);}},atLeast:function(Cv){var Cu=S()+Cv;if((!i)||Cp\\x3cCu){Cq(Cu);}},never:function(){Cr();\\n}};}function G(i){r(AD(0));}function A8(){var i=A8;var Cr=(i.failures||0);if(Cr\\x3c5){var Cq=AD(40);var Cs=r(Cq);\\nif(Cq()){i.failures=Cr+1;}return true;}else{var Cp=(i.skipCount||0);Cp++;if(Cp==20){Cp=0;i.failures=0;\\n}i.skipCount=Cp;}return false;}var H=CZ(function(){if(!l){return;}if(s){H.atLeast(500);return;}b("idleWorkTimer",function(){var i=AD(250);\\nvar Cq=false;var Cr=false;try{r(i);if(i()){return;}CN();if(i()){return;}var Ct=BI();var Cs=[0,A.lines.totalWidth()];\\nCq=true;Cr=true;}finally{if(Cr){H.atMost(1000);}else{if(Cq){H.atMost(500);}else{var Cp=Math.round(i.elapsed()/2);\\nif(Cp\\x3c100){Cp=100;}H.atMost(Cp);}}}});});var B1=1;function M(Cp){var i=Cp.id;if(i){return i;}return(Cp.id="magicdomid"+(B1++));\\n}function AV(Cr,Cx,Cs,Cv){if(Cx\\x3c=Cr){return;}if(Cr\\x3c0||Cr>=A.lines.totalWidth()){return;}var i=A.lines.atOffset(Cr);\\nvar Cp=A.lines.offsetOfEntry(i);var Cq=A.lines.indexOfEntry(i);var Cw=false;var Cy=null;var C1=null;Cs=(Cs||I);\\nvar Cu;var Ct=function(C2,C3){i.domInfo.appendSpan(C2,C3);};if(Cv){var C0=Ct;Ct=function(C2,C3){Cv(C2,C3,C0,Cu);\\nCu+=C2.length;};}while(i&&Cp\\x3cCx&&!Cs()){var Cz=Cp+i.width;Cu=Cp;i.domInfo.clearSpans();BJ(i,Ct,Cp);i.domInfo.finishUpdate();\\nAA(i.lineNode);if(A.selStart&&A.selStart[0]==Cq||A.selEnd&&A.selEnd[0]==Cq){Cw=true;}if(Cy===null){Cy=Cq;\\n}C1=Cq;Cp=Cz;i=A.lines.next(i);Cq++;}if(Cw){C.selectionAffected=true;}}function BJ(Cq,Cs,Cw){var Cr=Cw;\\nif((typeof Cr)!="number"){Cr=A.lines.offsetOfEntry(Cq);}var Cp=Cq.text;var Cx=Cq.width;if(Cp.length==0){var Ct=linestylefilter.getLineStyleFilter(0,"",Cs,A.apool);\\nCt("","");}else{var Cy=0;var i=Cs;i=linestylefilter.getURLFilter(Cp,i);if(browser.msie){i=linestylefilter.getAtSignSplitterFilter(Cp,i);\\n}var Cu=A.lines.indexOfEntry(Cq);var Cv=A.alines[Cu];i=linestylefilter.getLineStyleFilter(Cp.length,Cv,i,A.apool);\\ni(Cp,"");}}function BH(i){return"";}var AF;function A4(){AF={cleanNodesNearChanges:{}};}A4();function m(Cq){var i=P("getCleanNodeByKey",false);\\ni.extra=0;var Cp=D.getElementById(Cq);while(Cp&&W(Cp)){i.extra++;Cp.id="";Cp=D.getElementById(Cq);}i.literal(i.extra,"extra");\\ni.end();return Cp;}function z(Cs){var i;var Cu;if(!W(Cs)){i=Cs;var Ct=i.previousSibling;var Cr=i.nextSibling;\\nCu=((Ct&&W(Ct))||(Cr&&W(Cr)));}else{var Cp=Cs.previousSibling;while(Cp&&W(Cp)){Cp=Cp.previousSibling;\\n}if(Cp){i=Cp;}else{var Cq=Cs.nextSibling;while(Cq&&W(Cq)){Cq=Cq.nextSibling;}if(Cq){i=Cq;}}if(!i){return;\\n}Cu=true;}if(Cu){AF.cleanNodesNearChanges["$"+M(i)]=true;}else{var Cx=M(i);var Ct=i.previousSibling;var Cr=i.nextSibling;\\nvar C1=((Ct&&M(Ct))||null);var C0=((Cr&&M(Cr))||null);var Cv=A.lines.prev(A.lines.atKey(Cx));var Cw=A.lines.next(A.lines.atKey(Cx));\\nvar Cz=((Cv&&Cv.key)||null);var Cy=((Cw&&Cw.key)||null);if(C1!=Cz||C0!=Cy){AF.cleanNodesNearChanges["$"+M(i)]=true;\\n}}}function Aw(){if(C.observedSelection){return;}C.observedSelection=true;var Cs=P("getSelection",false);\\nvar Cq=Af();Cs.end();if(Cq){function Cr(Ct){if((!Ct)||Ct==B){return null;}while(Ct.parentNode!=B){Ct=Ct.parentNode;\\n}return Ct;}var Cp=Cr(Cq.startPoint.node);var i=Cr(Cq.endPoint.node);if(Cp){z(Cp);}if(i&&Cp!=i){z(i);\\n}}}function BV(){if(B.getElementsByTagName){var Cr=B.getElementsByTagName("style");for(var Cq=0;Cq\\x3cCr.length;\\nCq++){var Cp=Cr[Cq];while(Cp.parentNode&&Cp.parentNode!=B){Cp=Cp.parentNode;}if(Cp.parentNode==B){z(Cp);\\n}}}}function r(C9){if(C.domClean){return false;}s=false;C.isUserChange=true;C9=(C9||function(){return false;\\n});if(AL&&top.DONT_INCORP||window.DEBUG_DONT_INCORP){return false;}var Cq=P("incorp",false);if(!B.firstChild){B.innerHTML="\\x3cdiv>\\x3c!-- -->\\x3c/div>";\\n}Cq.mark("obs");Aw();BV();Cq.mark("dirty");var Cy=BK();var DN=true;var C5=0;var Cp,Ct;while(C5\\x3cCy.length){Cp=Cy[C5][0];\\nCt=Cy[C5][1];if(!((Cp==0||m(A.lines.atIndex(Cp-1).key))&&(Ct==A.lines.length()||m(A.lines.atIndex(Ct).key)))){DN=false;\\nbreak;}C5++;}if(!DN){var DS=B.childNodes.length;for(var C0=0;C0\\x3cDS;C0++){var C7=B.childNodes.item(C0);\\nif((C7.tagName)&&((!C7.id)||(!A.lines.containsKey(C7.id)))){z(C7);}}Cy=BK();}A4();Cq.mark("getsel");var Cv=Af();\\nvar Cs,Cr;var DE=0;var C6=[];var C3=0;var C8=[];Cq.mark("ranges");Cq.literal(Cy.length,"numdirt");var DI=[];\\nwhile(DE\\x3cCy.length){var DG=Cy[DE];Cp=DG[0];Ct=DG[1];var C1=(((Cp==0)&&B.firstChild)||m(A.lines.atIndex(Cp-1).key).nextSibling);\\nC1=(C1&&W(C1)&&C1);var Cu=(((Ct==A.lines.length())&&B.lastChild)||m(A.lines.atIndex(Ct).key).previousSibling);\\nCu=(Cu&&W(Cu)&&Cu);if(C1&&Cu){var Cz=makeContentCollector(AY,browser,A.apool,null,Bd);Cz.notifySelection(Cv);\\nvar DJ=[];for(var Cx=C1;Cx&&!(Cx.previousSibling&&Cx.previousSibling==Cu);Cx=Cx.nextSibling){if(browser.msie){try{Cx.createTextRange().execCommand("unlink",false,null);\\n}catch(DR){}}Cz.collectContent(Cx);DJ.push(Cx);}Cz.notifyNextNode(Cu.nextSibling);var Cw=Cz.getLines();\\nif((Cw.length\\x3c=1||Cw[Cw.length-1]!=="")&&Cu.nextSibling){Ct++;var DF=Cu.nextSibling;Cz.collectContent(DF);\\nC8.push(DF);Cz.notifyNextNode(DF.nextSibling);}var C2=Cz.finish();var DA=C2.selStart;var DB=C2.selEnd;\\nCw=C2.lines;var DP=C2.lineAttribs;var DC=C2.linesWrapped;if(DC>0){By("Editor warning: "+DC+" long line"+(DC==1?" was":"s were")+" hard-wrapped into "+C2.numLinesAfter+" lines.");\\n}if(DA[0]>=0){Cs=[DA[0]+Cp+C3,DA[1]];}if(DB[0]>=0){Cr=[DB[0]+Cp+C3,DB[1]];}var DL=[];var DO=Cu;var DH=new Array(Cw.length);\\nfor(var C0=0;C0\\x3cCw.length;C0++){var DQ=Cw[C0];var DK=AO(DQ);DL.push(DK);DH[C0]=DK.domInfo;}DI.push([DO,DH]);\\nforEach(DJ,function(i){C8.push(i);});var DD={};if(Cs){DD.selStart=Cs;}if(Cr){DD.selEnd=Cr;}C6.push([Cp+C3,Ct-Cp,DL,DP,DD]);\\nC3+=(Cw.length-(Ct-Cp));}else{if(Ct>Cp){C6.push([Cp+C3,Ct-Cp,[],[]]);}}DE++;}var DM=(C6.length>0);Cq.mark("splice");\\nforEach(C6,function(i){CI(i[0],i[1],i[2],i[3],i[4]);});Cq.mark("insert");forEach(DI,function(i){AI(i[0],i[1],C9);\\n});Cq.mark("del");forEach(C8,function(i){i.parentNode.removeChild(i);});Cq.mark("findsel");if(Cv&&!Cs){Cs=Aq(Cv.startPoint);\\n}if(Cv&&!Cr){Cr=Aq(Cv.endPoint);}var C4=A.lines.length();if(Cs&&Cs[0]>=C4){Cs[0]=C4-1;Cs[1]=A.lines.atIndex(Cs[0]).text.length;\\n}if(Cr&&Cr[0]>=C4){Cr[0]=C4-1;Cr[1]=A.lines.atIndex(Cr[0]).text.length;}Cq.mark("repsel");BR(Cs,Cr,Cv&&Cv.focusAtStart);\\nCq.mark("browsel");if(Cv&&(DM||V())){C.selectionAffected=true;}C.domClean=true;Cq.mark("fixview");h();\\nCq.end("END");return DM;}function Cc(Cp){var i=D.createElement("DIV");i.appendChild(Cp);return i.innerHTML;\\n}var Ad={bold:true,italic:true,underline:true,strikethrough:true,h1:true,h2:true,h3:true,h4:true,h5:true,h6:true,list:true};\\nvar CF={insertorder:true,author:true};function A0(i){return !!Ad[i];}function Ck(i){return(!!Ad[i])||(!!CF[i]);\\n}function AI(Cq,i,Cr){Cr=(Cr||function(){return false;});var Cp;var Cs;if(i.length\\x3c1){return;}var Cu=A.lines.atKey(M(i[0].node));\\nvar Ct=A.lines.atKey(M(i[i.length-1].node));var Cv=A.lines.offsetOfEntry(Cu);var Cw=A.lines.offsetOfEntry(Ct)+Ct.width;\\nforEach(i,function(C0){var Cx=P("insertLine",false);var Cz=C0.node;var C1=M(Cz);var Cy;Cx.mark("findEntry");\\nif(Cp){var C2=A.lines.next(Cp);if(C2&&C2.key==C1){Cy=C2;Cs+=Cp.width;}}if(!Cy){Cx.literal(1,"nonopt");\\nCy=A.lines.atKey(C1);Cs=A.lines.offsetOfKey(C1);}else{Cx.literal(0,"nonopt");}Cp=Cy;Cx.mark("spans");\\nBJ(Cy,function(C3,C4){C0.appendSpan(C3,C4);},Cs,Cr());Cx.mark("addLine");C0.prepareForAdd();Cy.lineMarker=C0.lineMarker;\\nif(!Cq){B.insertBefore(Cz,B.firstChild);}else{B.insertBefore(Cz,Cq.nextSibling);}Cq=Cz;C0.notifyAdded();\\nCx.mark("markClean");AA(Cz);Cx.end();});}function V(){return(A.selStart&&A.selEnd&&A.selStart[0]==A.selEnd[0]&&A.selStart[1]==A.selEnd[1]);\\n}function n(){return A.selStart[0];}function u(){return A.selStart[1];}function A7(){return A.lines.offsetOfIndex(n())+u();\\n}function CE(){if(V()&&u()==0&&n()>0){var i=n();var Ct=A.lines.atIndex(i);var Cs=A.lines.prev(Ct);var Cq=Cs.text;\\nvar Cp=/^ *(?:)/.exec(Cq)[0];if(/[\\\\[\\\\(\\\\{]\\\\s*$/.exec(Cq)){Cp+=Ae;}var Cr=Changeset.builder(A.lines.totalWidth()).keep(A.lines.offsetOfIndex(i),i).insert(Cp,[["author",e]],A.apool).toString();\\nc(Cr);d([i,Cp.length],[i,Cp.length]);}}function Bk(Cr){var Cq=A.lines.atIndex(Cr).lineNode;var i=Cq.firstChild;\\nwhile(AG(i)&&i.firstChild){i=i.firstChild;}var Cp;if(isNodeText(i)){i.nodeValue=" "+i.nodeValue;Cp=i;\\n}else{Cp=D.createTextNode(" ");i.parentNode.insertBefore(Cp,i);}AA(Cq);return{unhack:function(){if(Cp.nodeValue==" "){Cp.parentNode.removeChild(Cp);\\n}else{Cp.nodeValue=Cp.nodeValue.substring(1);}AA(Cq);}};}function Am(Cv){var Cw=Cv[0];var Cp=Cv[1];var Ct=A.lines.atIndex(Cw);\\nCp-=Ct.lineMarker;if(Cp\\x3c0){Cp=0;}var Cr=Ct.lineNode;var i=Cr;var Cq=false;if(Cp==0){var Cu=0;if(browser.msie&&Cw==(A.lines.length()-1)&&Cr.childNodes.length==0){Cu=1;\\n}return{node:Cr,index:Cu,maxIndex:1};}while(!(i==Cr&&Cq)){if(Cq){if(i.nextSibling){i=i.nextSibling;Cq=false;\\n}else{i=i.parentNode;}}else{if(isNodeText(i)){var Cs=i.nodeValue.length;if(Cp\\x3c=Cs){return{node:i,index:Cp,maxIndex:Cs};\\n}Cp-=Cs;Cq=true;}else{if(i.firstChild){i=i.firstChild;}else{Cq=true;}}}}return{node:Cr,index:1,maxIndex:1};\\n}function BD(i){return i.innerText||i.textContent||i.nodeValue||"";}function Aq(Cq){if(Cq.node==B){if(Cq.index==0){return[0,0];\\n}else{var Ct=A.lines.length();var Cw=A.lines.atIndex(Ct-1);return[Ct-1,Cw.text.length];}}else{var i=Cq.node;\\nvar Cp=0;if(isNodeText(i)){Cp=Cq.index;}else{if(Cq.index>0){Cp=BD(i).length;}}var Cr,Cs;while((Cr=i.parentNode)!=B){if((Cs=i.previousSibling)){i=Cs;\\nCp+=BD(i).length;}else{i=Cr;}}if(i.id==""){T.debug("BAD");}if(i.firstChild&&AG(i.firstChild)){Cp+=1;}var Cu=A.lines.atKey(i.id);\\nvar Cv=A.lines.indexOfEntry(Cu);return[Cv,Cp];}}function AO(Cq){var Cp=CS(Cq.length>0);var i=Cp.node;\\nreturn{key:M(i),text:Cq,lineNode:i,domInfo:Cp,lineMarker:0};}function Co(i){return Changeset.oldLen(i)==A.alltext.length;\\n}function c(Cp,Cr){Ap(Cp,Cr);var i=null;if(A.selStart&&A.selEnd){var Cv=A.lines.offsetOfIndex(A.selStart[0])+A.selStart[1];\\nvar Cs=A.lines.offsetOfIndex(A.selEnd[0])+A.selEnd[1];var Cq=Changeset.characterRangeFollow(Cp,Cv,Cs,Cr);\\ni=[Cq[0],Cq[1],A.selFocusAtStart];}var Cu={splice:function(Cx,Cw,Cy){Ct(Cx,Cw,map(Array.prototype.slice.call(arguments,2),function(Cz){return Cz.slice(0,-1);\\n}),null);},get:function(Cw){return A.lines.atIndex(Cw).text+"\\\\n";},length:function(){return A.lines.length();\\n},slice_notused:function(Cx,Cw){return map(A.lines.slice(Cx,Cw),function(Cy){return Cy.text+"\\\\n";});}};\\nChangeset.mutateTextLines(Cp,Cu);BN();if(i){d(Q(i[0]),Q(i[1]),i[2]);}function Ct(Cw,Cx,C4,Cz){Cz=(Cz||AD(50));\\nvar C1=[];if(Cx>0){var C0=A.lines.atIndex(Cw);for(var C3=0;C3\\x3cCx;C3++){C1.push(C0.key);C0=A.lines.next(C0);\\n}}var C2=map(C4,AO);AK(Cw,Cx,C2);var Cy;if(Cw>0){Cy=m(A.lines.atIndex(Cw-1).key);}else{Cy=null;}AI(Cy,map(C2,function(C5){return C5.domInfo;\\n}),Cz);forEach(C1,function(C6){var C5=D.getElementById(C6);C5.parentNode.removeChild(C5);});if((A.selStart&&A.selStart[0]>=Cw&&A.selStart[0]\\x3c=Cw+Cx)||(A.selEnd&&A.selEnd[0]>=Cw&&A.selEnd[0]\\x3c=Cw+Cx)){C.selectionAffected=true;\\n}}}function Bz(Cw){return true;var Cs=Changeset.opIterator(Changeset.unpack(Cw).ops);var Cp=0;var Cr=0;\\nvar Cq=0;while(Cs.hasNext()){var i=Cs.next();if(i.opcode=="-"||i.opcode=="="){Cp+=i.chars;if(i.lines){Cr+=i.lines;\\nCq=0;}else{Cq+=i.chars;}}var Ct=A.lines.indexOfOffset(Cp);var Cv=A.lines.offsetOfIndex(Ct);var Cu=Cp-Cv;\\nif(Cu!=Cq||Ct!=Cr){return false;}}return true;}function Ap(i,Cq){Changeset.checkRep(i);if(Changeset.oldLen(i)!=A.alltext.length){throw new Error("doRepApplyChangeset length mismatch: "+Changeset.oldLen(i)+"/"+A.alltext.length);\\n}if(!Bz(i)){throw new Error("doRepApplyChangeset line break mismatch");}(function Cp(Cs){var Cr=C.editEvent;\\nif(Cr.eventType=="nonundoable"){if(!Cr.changeset){Cr.changeset=Cs;}else{Cr.changeset=Changeset.compose(Cr.changeset,Cs,A.apool);\\n}}else{var Ct=Changeset.inverse(Cs,{get:function(Cu){return A.lines.atIndex(Cu).text+"\\\\n";},length:function(){return A.lines.length();\\n}},A.alines,A.apool);if(!Cr.backset){Cr.backset=Ct;}else{Cr.backset=Changeset.compose(Ct,Cr.backset,A.apool);\\n}}})(i);Changeset.mutateAttributionLines(i,A.alines,A.apool);if(N.isTracking()){N.composeUserChangeset(i);\\n}}function Q(i){var Cp=A.lines.atOffset(i);var Cq=A.lines.offsetOfEntry(Cp);var Cr=A.lines.indexOfEntry(Cp);\\nreturn[Cr,i-Cq];}function BA(Cq,Cp,i){if(Cq==Cp&&i.length==0){return;}if(Cp==A.alltext.length){if(Cq==Cp){Cq--;\\nCp--;i="\\\\n"+i.substring(0,i.length-1);}else{if(i.length==0){Cq--;Cp--;}else{Cp--;i=i.substring(0,i.length-1);\\n}}}f(Q(Cq),Q(Cp),i);}function f(Cp,Cq,Cs){var i=Changeset.builder(A.lines.totalWidth());At(i,Cp);BQ(i,Cp,Cq);\\ni.insert(Cs,[["author",e]],A.apool);var Cr=i.toString();c(Cr);}function A2(Cq,i,Cp){if(i>=A.alltext.length){i=A.alltext.length-1;\\n}AC(Q(Cq),Q(i),Cp);}function AC(Cp,Cq,Cs){var i=Changeset.builder(A.lines.totalWidth());At(i,Cp);AS(i,Cp,Cq,Cs,A.apool);\\nvar Cr=i.toString();c(Cr);}function At(Cp,i){var Cq=A.lines.offsetOfIndex(i[0]);Cp.keep(Cq,i[0]);Cp.keep(i[1]);\\n}function BQ(Cq,Cp,i){var Cs=A.lines.offsetOfIndex(Cp[0]);var Cr=A.lines.offsetOfIndex(i[0]);if(i[0]>Cp[0]){Cq.remove(Cr-Cs-Cp[1],i[0]-Cp[0]);\\nCq.remove(i[1]);}else{Cq.remove(i[1]-Cp[1]);}}function AS(Cq,Cp,i,Cs,Cr){var Cu=A.lines.offsetOfIndex(Cp[0]);\\nvar Ct=A.lines.offsetOfIndex(i[0]);if(i[0]>Cp[0]){Cq.keep(Ct-Cu-Cp[1],i[0]-Cp[0],Cs,Cr);Cq.keep(i[1],0,Cs,Cr);\\n}else{Cq.keep(i[1]-Cp[1],0,Cs,Cr);}}function Bi(i,Cp){if(!(A.selStart&&A.selEnd)){return;}AC(A.selStart,A.selEnd,[[i,Cp]]);\\n}function J(i){if(!(A.selStart&&A.selEnd)){return;}var Cr=true;var C2=Changeset.makeAttribsString("+",[[i,"true"]],A.apool);\\nvar C3=new RegExp(C2.replace(/\\\\*/g,"\\\\\\\\*")+"(\\\\\\\\*|$)");function C1(C4){return C3.test(C4);}var Cy=A.selStart[0];\\nvar Cv=A.selEnd[0];for(var Cq=Cy;Cq\\x3c=Cv;Cq++){var Ct=Changeset.opIterator(A.alines[Cq]);var C0=0;var Cx=0;\\nvar Cs=A.lines.atIndex(Cq).text.length;if(Cq==Cy){Cx=A.selStart[1];}if(Cq==Cv){Cs=A.selEnd[1];}while(Ct.hasNext()){var Cw=Ct.next();\\nvar Cz=C0;var Cu=Cz+Cw.chars;if(!C1(Cw.attribs)){if(!(Cu\\x3c=Cx||Cz>=Cs)){Cr=false;break;}}C0=Cu;}if(!Cr){break;\\n}}if(Cr){AC(A.selStart,A.selEnd,[[i,""]]);}else{var Cp=[[i,"true"]];if(i=="h1"||i=="h2"||i=="h3"||i=="h4"||i=="h5"||i=="h6"){Cp=[["h1",""],["h2",""],["h3",""],["h4",""],["h5",""],["h6",""]];\\nif(i=="h1"){Cp[0]=[i,"true"];}else{if(i=="h2"){Cp[1]=[i,"true"];}else{if(i=="h3"){Cp[2]=[i,"true"];}else{if(i=="h4"){Cp[3]=[i,"true"];\\n}else{if(i=="h5"){Cp[4]=[i,"true"];}else{if(i=="h6"){Cp[5]=[i,"true"];}}}}}}}AC(A.selStart,A.selEnd,Cp);\\n}}function AZ(i){if(!(A.selStart&&A.selEnd)){return;}f(A.selStart,A.selEnd,i);}function AK(i,Cq,Cp){forEach(Cp,function(Cx){Cx.width=Cx.text.length+1;\\n});var Cs=A.lines.offsetOfIndex(i);var Ct=A.lines.offsetOfIndex(i+Cq);var Cu=A.lines.offsetOfIndex(i);\\nvar Cw=A.lines.offsetOfIndex(i+Cq);A.lines.splice(i,Cq,Cp);C.docTextChanged=true;C.repChanged=true;var Cv=A.lines.offsetOfIndex(i+Cp.length);\\nvar Cr=map(Cp,function(Cx){return Cx.text+"\\\\n";}).join("");A.alltext=A.alltext.substring(0,Cs)+Cr+A.alltext.substring(Ct,A.alltext.length);\\n}function CI(Cv,C6,DE,DO,Cr){var DF=A.lines.offsetOfIndex(Cv);var DK=A.lines.offsetOfIndex(Cv+C6);var DJ=A.lines.offsetOfIndex(Cv);\\nvar DI,DM;if(Cr&&Cr.selStart){DI=A.lines.offsetOfIndex(Cr.selStart[0])+Cr.selStart[1]-DJ;}if(Cr&&Cr.selEnd){DM=A.lines.offsetOfIndex(Cr.selEnd[0])+Cr.selEnd[1]-DJ;\\n}var Cs=map(DE,function(DU){return DU.text+"\\\\n";}).join("");var C1=A.alltext.substring(DF,DK);var DG=A.alines.slice(Cv,Cv+C6).join("");\\nvar C0=DO.join("|1+1")+"|1+1";var DC=B6(C1,Cs,DG,C0,DI,DM);var Cz=DC[0];var Cx=DC[1];var Cq=C1.substring(Cz,C1.length-Cx);\\nvar i=Cs.substring(Cz,Cs.length-Cx);var Cp=DF+Cz;var Ct=DK-Cx;var Cw=false;if(Cq.charAt(Cq.length-1)=="\\\\n"&&i.charAt(i.length-1)=="\\\\n"){Cq=Cq.slice(0,-1);\\ni=i.slice(0,-1);Ct--;Cx++;}if(Cq.length==0&&Cp==A.alltext.length&&i.length>0){Cp--;Ct--;i="\\\\n"+i.slice(0,-1);\\nCw=true;}if(Ct==A.alltext.length&&Cq.length>0&&i.length==0){if(A.alltext.charAt(Cp-1)=="\\\\n"){Cp--;Ct--;\\n}}if(!(Cq.length==0&&i.length==0)){var DR=A.alltext;var DT=DR.length;var C4=A.lines.indexOfOffset(Cp);\\nvar DA=A.lines.offsetOfIndex(C4);function C8(){var DU=Changeset.builder(DT);DU.keep(DA,C4);DU.keep(Cp-DA);\\nreturn DU;}function Cy(Db,Da){var DW=Changeset.opIterator(Db);var DU=0;var DX=Cz;var DZ=Cs.length-Cx-(Cw?1:0);\\nwhile(DW.hasNext()){var DY=DW.next();var DV=DU+DY.chars;if(!(DV\\x3c=DX||DU>=DZ)){Da(Math.max(DX,DU),Math.min(DZ,DV),DY.attribs);\\n}DU=DV;}}var DP=(i==Cq);var C5;if(DP){var DN=BT(function(DU){return Changeset.mapAttribNumbers(DU,function(DW){var DV=A.apool.getAttribKey(DW);\\nif(A0(DV)){return A.apool.putAttrib([DV,""]);}return false;});});var C3=C8();if(Cw){C3.keep(1,1);}Cy(DG,function(DW,DU,DV){C3.keepText(Cs.substring(DW,DU),DN(DV));\\n});var DS=C3.toString();var C2=C8();if(Cw){C2.keep(1,1);}Cy(C0,function(DW,DU,DV){C2.keepText(Cs.substring(DW,DU),DV);\\n});var DQ=C2.toString();C5=Changeset.compose(DS,DQ,A.apool);}else{var Cu=C8();var DH=A.lines.indexOfOffset(Ct);\\nvar C9=A.lines.offsetOfIndex(DH);if(C9>Cp){Cu.remove(C9-Cp,DH-C4);Cu.remove(Ct-C9);}else{Cu.remove(Ct-Cp);\\n}var DL=false;var DD=Changeset.makeAttribsString("+",(e?[["author",e]]:[]),A.apool);var DB=BT(function(DU){if(DL){return Changeset.composeAttributes(DD,DU,true,A.apool);\\n}else{return Changeset.composeAttributes(DU,DD,true,A.apool);}});var C7="";Cy(C0,function(DX,DW,DV){var DU=Changeset.attribsAttributeValue(DV,"author",A.apool);\\nif(DU&&DU!=C7){if(!C7){C7=DU;}else{DL=true;}}});if(Cw){Cu.insert("\\\\n",DB(""));}Cy(C0,function(DW,DU,DV){Cu.insert(Cs.substring(DW,DU),DB(DV));\\n});C5=Cu.toString();}Ap(C5);}AK(Cv,C6,DE);BN();}function BT(Cp){var i={};return function(Cq){if(!i[Cq]){i[Cq]=Cp(Cq);\\n}return i[Cq];};}function B6(Cv,Cw,C6,C7,DA,Cx){function Cy(DB){return A0(A.apool.getAttribKey(DB));}function C5(DF){var DC=[];\\nvar DE=[];var DB=Changeset.opIterator(DF);while(DB.hasNext()){var DD=DB.next();DC.push(DD.chars);DE.push(DD.attribs);\\n}return[DC,DE];}function Ct(DF,DE){var DD=DF[0];var DG=DF[1];var DB=(DE?DD.length-1:0);var DC=0;return function DH(){while(DC>=DD[DB]){if(DE){DB--;\\n}else{DB++;}DC=0;}var DI=DG[DB];DC++;return DI;};}var Cs=Cv.length;var Cr=Cw.length;var C2=Math.min(Cs,Cr);\\nvar C3=C5(Changeset.filterAttribNumbers(C6,Cy));var C1=C5(Changeset.filterAttribNumbers(C7,Cy));var Cp=0;\\nvar C9=Ct(C3,false);var C8=Ct(C1,false);while(Cp\\x3cC2){if(Cv.charAt(Cp)==Cw.charAt(Cp)&&C9()==C8()){Cp++;\\n}else{break;}}var i=0;var C0=Ct(C3,true);var C4=Ct(C1,true);while(i\\x3cC2){if(i==0){C0();C4();i++;}else{if(Cv.charAt(Cs-1-i)==Cw.charAt(Cr-1-i)&&C0()==C4()){i++;\\n}else{break;}}}var Cq=-1;if((typeof Cx)=="number"){Cq=Cr-Cx;}if(Cp+i>Cs){var Cu=Cs-Cp;var Cz=i;if(Cq>=Cu&&Cq\\x3c=Cz){i=Cq;\\n}else{i=Cu;}Cp=Cs-i;}if(Cp+i>Cr){var Cu=Cr-Cp;var Cz=i;if(Cq>=Cu&&Cq\\x3c=Cz){i=Cq;}else{i=Cu;}Cp=Cr-i;}return[Cp,i];\\n}function A3(Cp,i){if(!Cp){return !i;}if(!i){return !Cp;}return(Cp[0]==i[0]&&Cp[1]==i[1]);}function d(Cq,i,Cp){if(BR(Cq,i,Cp)){C.selectionAffected=true;\\n}}function BR(Cp,i,Cq){Cq=!!Cq;var Cr=(Cq&&((!Cp)||(!i)||(Cp[0]!=i[0])||(Cp[1]!=i[1])));if((!A3(A.selStart,Cp))||(!A3(A.selEnd,i))||(A.selFocusAtStart!=Cr)){A.selStart=Cp;\\nA.selEnd=i;A.selFocusAtStart=Cr;if(AB){AB.notifySelectionChanged();}C.repChanged=true;return true;}return false;\\n}function CS(Cs){if(browser.msie&&(!Cs)){var i={node:null,appendSpan:I,prepareForAdd:I,notifyAdded:I,clearSpans:I,finishUpdate:I,lineMarker:0};\\nvar Cq=D.createElement("div");i.node=Cq;i.notifyAdded=function(){Cq.innerHTML="";setAssoc(Cq,"shouldBeEmpty",true);\\nsetAssoc(Cq,"unpasted",{});};var Cp="ace-line";i.appendSpan=function(Cu,Ct){if((!Cu)&&Ct){Cp=domline.addToLineClass(Cp,Ct);\\n}};i.clearSpans=function(){Cp="";};function Cr(){if(Cp!==null){Cq.className=Cp;}}i.prepareForAdd=Cr;i.finishUpdate=Cr;\\ni.getInnerHTML=function(){return"";};return i;}else{return domline.createDomLine(Cs,L,browser,D);}}function CH(i){return i.replace(/[\\\\n\\\\r ]/g," ").replace(/\\\\xa0/g," ").replace(/\\\\t/g," ");\\n}var Br={div:1,p:1,pre:1,li:1,ol:1,ul:1};function AG(i){return !!Br[(i.tagName||"").toLowerCase()];}function BK(){var i=P("getDirtyRanges",false);\\ni.forIndices=0;i.consecutives=0;i.corrections=0;var Cs={};var Cq=A.lines.length();function Cu(C7){if(Cs[C7]===undefined){i.forIndices++;\\nvar C8;if(C7\\x3c0||C7>=Cq){C8=true;}else{var C9=A.lines.atIndex(C7).key;C8=(m(C9)||false);}Cs[C7]=C8;}return Cs[C7];\\n}var Cv={};function Cw(C7){if(Cv[C7]===undefined){i.consecutives++;Cv[C7]=(function(){var C9=Cu(C7-1);\\nvar C8=Cu(C7);if((!C9)||(!C8)){return false;}if((C9===true)&&(C8===true)){return !B.firstChild;}if((C9===true)&&C8.previousSibling){return false;\\n}if((C8===true)&&C9.nextSibling){return false;}if((C9===true)||(C8===true)){return true;}return C9.nextSibling==C8;\\n})();}return Cv[C7];}function Cx(C7){return !!Cu(C7);}var Cp=[[-1,Cq+1]];function C4(C7){var C8=-1;forEach(Cp,function(C9,DA){if(C7>=C9[1]){return false;\\n}if(C7\\x3cC9[0]){return true;}C8=DA;return true;});return C8;}function C3(C7,C8){var DA=Cp[C7][0];var C9=Cp[C7][1];\\nif((DA+1)==C9){Cp.splice(C7,1);}else{if(C8==DA){Cp[C7][0]++;}else{if(C8==(C9-1)){Cp[C7][1]--;}else{Cp.splice(C7,1,[DA,C8],[C8+1,C9]);\\n}}}}function Ct(C7,C8){var DA=Cp[C7][0];var C9=Cp[C7][1];Cp.splice(C7,1,[DA,C8],[C8,C9]);}var Cz={};function C1(C7){if(Cz[C7]){return true;\\n}i.corrections++;Cz[C7]=true;var C8=C4(C7);var DA=Cx(C7);if(C8\\x3c0){if(DA){T.debug("somehow lost clean line");\\n}return true;}if(!DA){C3(C8,C7);return false;}else{var DC=Cp[C8][0];var DB=Cp[C8][1];var C9=false;if(DC\\x3cC7&&Cx(C7-1)&&!Cw(C7)){Ct(C8,C7);\\nC9=true;}if(DB>(C7+1)&&Cx(C7+1)&&!Cw(C7+1)){Ct(C8,C7+1);C9=true;}return !C9;}}function Cy(C9,DA){var C8=0;\\nvar C7=C9;while(C8\\x3cDA&&C7>=0){if(C1(C7)){C8++;}else{C8=0;}C7--;}C8=0;C7=C9;while(C8\\x3cDA&&C7\\x3cCq){if(C1(C7)){C8++;\\n}else{C8=0;}C7++;}}if(Cq==0){i.cancel();if(!Cw(0)){Ct(0,0);}}else{i.mark("topbot");Cy(0,1);Cy(Cq-1,1);\\ni.mark("obs");for(var C6 in AF.cleanNodesNearChanges){var C2=C6.substring(1);if(A.lines.containsKey(C2)){var C5=A.lines.indexOfKey(C2);\\nCy(C5,2);}}i.mark("stats&calc");i.literal(i.forIndices,"byidx");i.literal(i.consecutives,"cons");i.literal(i.corrections,"corr");\\n}var C0=[];for(var Cr=0;Cr\\x3cCp.length-1;Cr++){C0.push([Cp[Cr][1],Cp[Cr+1][0]]);}i.end();return C0;}function AA(Cp){var i={};\\ni.nodeId=M(Cp);i.knownHTML=Cp.innerHTML;if(browser.msie){i.knownText=Cp.innerText;}setAssoc(Cp,"dirtiness",i);\\n}function W(i){var Cq=P("cleanCheck",false);if(i.parentNode!=B){return true;}var Cp=getAssoc(i,"dirtiness");\\nif(!Cp){return true;}if(i.id!==Cp.nodeId){return true;}if(browser.msie){if(i.innerText!==Cp.knownText){return true;\\n}}if(i.innerHTML!==Cp.knownHTML){return true;}Cq.end();return false;}function A1(Ct,Cs){var Cq=Ct.lineNode;\\nvar Cp=Cq.offsetTop;var Cr=Cq.offsetHeight;var i=(Cs||{});i.top=Cp;i.bottom=(Cp+Cr);return i;}function B5(){var i=Ac();\\nvar Cq=F.document;var Cp=Cq.documentElement.clientHeight;return{top:i,bottom:(i+Cp)};}function Bs(){var Cq=B5();\\nvar Cr={};var Cp=A.lines.search(function(Cs){return A1(Cs,Cr).bottom>Cq.top;});var i=A.lines.search(function(Cs){return A1(Cs,Cr).top>=Cq.bottom;\\n});if(i\\x3cCp){i=Cp;}return[Cp,i];}function BI(){var i=Bs();return[A.lines.offsetOfIndex(i[0]),A.lines.offsetOfIndex(i[1])];\\n}function CY(Cp){b("handleClick",function(){H.atMost(200);});if((!Cp.ctrlKey)&&(Cp.button!=2)&&(Cp.button!=3)){function Cq(Ct){return(Ct.tagName||"").toLowerCase()=="a"&&Ct.href;\\n}var i=Cp.target;while(i&&i.parentNode&&!Cq(i)){i=i.parentNode;}if(i&&Cq(i)){try{var Cr=window.open(i.href,"_blank");\\nCr.focus();}catch(Cs){}Cp.preventDefault();}}}function Bu(){if(!(A.selStart&&A.selEnd)){return;}var i=A.selStart[0];\\nvar Cp=Y(i);AZ("\\\\n");if(Cp){if(i+1\\x3cA.lines.length()){Bg(i+1,Cp);}}else{CE();}}function AU(Cw){if(!(A.selStart&&A.selEnd)){return false;\\n}var Cq,Cv;Cq=A.selStart[0];Cv=Math.max(Cq,A.selEnd[0]-((A.selEnd[1]==0)?1:0));var Cr=[];var Cs=false;\\nfor(var Cp=Cq;Cp\\x3c=Cv;Cp++){var i=Y(Cp);if(i){i=/([a-z]+)([12345678])/.exec(i);if(i){Cs=true;var Cx=i[1];\\nvar Ct=Number(i[2]);var Cu=Math.max(1,Math.min(BW,Ct+(Cw?-1:1)));if(Ct!=Cu){Cr.push([Cp,Cx+Cu]);}}}}if(Cr.length>0){AJ(Cr);\\n}return Cs;}function Bw(i){if(!AU(i)){AZ(Ae);}}function A5(C4){var Cu=C4||{};var Cw=false;if(A.selStart){if(V()){var Cv=n();\\nvar Ct=u();var Cp=A.lines.atIndex(Cv);var C0=Cp.text;var Cx=Cp.lineMarker;if(/^ +$/.exec(C0.substring(Cx,Ct))){var C3=Ct-Cx;\\nvar C1=Ae.length;var Cz=((C3-1)%C1)+1;f([Cv,Ct-Cz],[Cv,Ct],"");Cw=true;}}if(!Cw){if(V()){var i=n();var Cp=A.lines.atIndex(i);\\nif(u()\\x3c=Cp.lineMarker){var C6="delete_newline";var C2=(i>0?Y(i-1):"");var C5=Y(i);var Cq=(i>0&&A.lines.atIndex(i-1));\\nvar Cy=(Cq&&Cq.text.length==Cq.lineMarker);if(C5){if(Cy&&!C2){f([i-1,Cq.text.length],[i,0],"");}else{f([i,0],[i,Cp.lineMarker],"");\\n}}else{if(i>0){f([i-1,Cq.text.length],[i,0],"");}}}else{var Cr=A7();if(Cr>0){if(Cu.metaKey||Cu.ctrlKey||Cu.altKey){var Cs=Cr-1;\\nwhile(Cs>Cp.lineMarker&&o(A.alltext.charAt(Cs-1))){Cs--;}BA(Cs,Cr,"");}else{BA(Cr-1,Cr,"");}}}}else{AZ("");\\n}}}}var Bf=/[\\\\u0030-\\\\u0039\\\\u0041-\\\\u005A\\\\u0061-\\\\u007A\\\\u00C0-\\\\u00D6\\\\u00D8-\\\\u00F6\\\\u00F8-\\\\u00FF\\\\u0100-\\\\u1FFF\\\\u3040-\\\\u9FFF\\\\uF900-\\\\uFDFF\\\\uFE70-\\\\uFEFE\\\\uFF10-\\\\uFF19\\\\uFF21-\\\\uFF3A\\\\uFF41-\\\\uFF5A\\\\uFF66-\\\\uFFDC]/;\\nvar CU=/\\\\s/;function o(i){return !!Bf.exec(i);}function Cf(i){return !!CU.exec(i);}function y(Cu,Cv,Cr){var Cp=Cv;\\nfunction Ct(){if(Cr){return Cu.charAt(Cp);}else{return Cu.charAt(Cp-1);}}function Cs(){if(Cr){Cp++;}else{Cp--;\\n}}function Cq(){if(Cr){return Cp>=Cu.length;}else{return Cp\\x3c=0;}}if(browser.windows&&Cr){while((!Cq())&&o(Ct())){Cs();\\n}while((!Cq())&&!o(Ct())){Cs();}}else{while((!Cq())&&!o(Ct())){Cs();}while((!Cq())&&o(Ct())){Cs();}}return Cp;\\n}function AN(i){if(AL&&top.DONT_INCORP){return;}if(!l){return;}var Cq=i.type;var Cw=i.charCode;var Cr=i.keyCode;\\nvar Cs="";if(i.altKey){Cs=Cs+"A";}if(i.ctrlKey){Cs=Cs+"C";}if(i.shiftKey){Cs=Cs+"S";}if(i.metaKey){Cs=Cs+"M";\\n}var Cz="";if(Cs){Cz=Cs+"-";}var Ct=i.which;var Cy=((!Cw)&&((Cq=="keyup")||(Cq=="keydown"))&&(Cr==16||Cr==17||Cr==18||Cr==20||Cr==224||Cr==91));\\nif(Cy){return;}var Cp=false;var Cv=((browser.msie||browser.safari)?(Cq=="keydown"):(Cq=="keypress"));\\nvar Cu=((browser.msie||browser.safari)?(Cq=="keydown"):(Cq=="keypress"));var Cx=false;b("handleKeyEvent",function(){if(Cq=="keypress"||(Cv&&Cr==13)){if(!Ar(i)){i.preventDefault();\\nCx=true;}}else{if(Cq=="keydown"){Au(i);}}if(!Cx){if(Cv&&Cr==8){G(3);i.preventDefault();A5(i);Cp=true;\\n}if((!Cp)&&Cv&&Cr==13){G(4);i.preventDefault();Bu();O.setTimeout(function(){F.scrollBy(-100,0);},0);Cp=true;\\n}if((!Cp)&&Cv&&Cr==9&&!(i.metaKey||i.ctrlKey)){G(5);i.preventDefault();Bw(i.shiftKey);Cp=true;}if((!Cp)&&Cu&&String.fromCharCode(Ct).toLowerCase()=="z"&&(i.metaKey||i.ctrlKey)){G(6);\\ni.preventDefault();if(i.shiftKey){t("redo");}else{t("undo");}Cp=true;}if((!Cp)&&Cu&&String.fromCharCode(Ct).toLowerCase()=="y"&&(i.metaKey||i.ctrlKey)){G(10);\\ni.preventDefault();t("redo");Cp=true;}if((!Cp)&&Cu&&String.fromCharCode(Ct).toLowerCase()=="b"&&(i.metaKey||i.ctrlKey)){G(13);\\ni.preventDefault();J("bold");Cp=true;}if((!Cp)&&Cu&&String.fromCharCode(Ct).toLowerCase()=="i"&&(i.metaKey||i.ctrlKey)){G(14);\\ni.preventDefault();J("italic");Cp=true;}if((!Cp)&&Cu&&String.fromCharCode(Ct).toLowerCase()=="u"&&(i.metaKey||i.ctrlKey)){G(15);\\ni.preventDefault();J("underline");Cp=true;}if((!Cp)&&Cu&&String.fromCharCode(Ct).toLowerCase()=="h"&&(i.ctrlKey)){G(20);\\ni.preventDefault();A5();Cp=true;}if(AB&&AB.handleKeyEvent(i)){i.preventDefault();Cp=true;}}if(Cq=="keydown"){H.atLeast(500);\\n}else{if(Cq=="keypress"){if((!Cp)&&AH.shouldNormalizeOnChar(Cw)){H.atMost(0);}else{H.atLeast(500);}}else{if(Cq=="keyup"){var C0=200;\\nH.atLeast(C0);H.atMost(C0);}}}var C2=(browser.mozilla&&i.altKey&&Cw==0&&Cr==0);var C1=(browser.safari&&i.altKey&&Cr==229);\\nif(AE||C2||C1){H.atLeast(3000);AE=true;}if((!Cp)&&(!AE)&&(!s)){if(Cq!="keyup"||!A8()){Aw();}}if(Cq=="keyup"){AE=false;\\n}});}var AE=false;function t(Cp){if(undoModule.enabled){var i;if(Cp=="undo"){i="performUndo";}if(Cp=="redo"){i="performRedo";\\n}if(i){var Cq=C.editEvent.eventType;C.startNewEvent(Cp);undoModule[i](function(Cs,Cr){if(Cs){c(Cs);}if(Cr){d(Q(Cr.selStart),Q(Cr.selEnd),Cr.selFocusAtStart);\\n}var Ct=C.startNewEvent(Cq,true);return Ct;});}}}function Ba(){var i=A.selStart,Cp=A.selEnd;if(!(i&&Cp)){AX(null);\\nreturn;}var Cr=(false&&browser.mozilla&&i&&Cp&&i[0]==Cp[0]&&i[1]==A.lines.atIndex(i[0]).lineMarker&&Cp[1]==A.lines.atIndex(Cp[0]).lineMarker&&Bk(i[0]));\\nvar Cq={};var Cs=[i[0],i[1]];if(Cr){Cs[1]+=1;}Cq.startPoint=Am(Cs);var Ct=[Cp[0],Cp[1]];if(Cr){Ct[1]+=1;\\n}Cq.endPoint=Am(Ct);Cq.focusAtStart=!!A.selFocusAtStart;AX(Cq);if(Cr){Cr.unhack();}}function Ca(){return map(A.lines.slice(),function(Cq){var Cp=Cq.text;\\nvar i;if(Cp.length==0){i=\\\'\\x3cspan style="color: #aaa">--\\x3c/span>\\\';}else{i=htmlPrettyEscape(Cp);}return"\\x3cdiv>\\x3ccode>"+i+"\\x3c/div>\\x3c/code>";\\n}).join("");}function k(i){if(isNodeText(i)){return i.nodeValue.length;}else{return 1;}}function CX(){var i;\\ntry{i=D.selection;}catch(Cq){}if(!i){return false;}var Cp;try{Cp=i.createRange();}catch(Cq){}if(!Cp){return false;\\n}var Cr=Cp.parentElement();if(Cr.ownerDocument!=D){return false;}return true;}function Af(){if(browser.msie){var i;\\ntry{i=D.selection;}catch(Cx){}if(!i){return null;}var Cr;try{Cr=i.createRange();}catch(Cx){}if(!Cr){return null;\\n}var C0=Cr.parentElement();if(C0.ownerDocument!=D){return null;}function C1(){return D.body.createTextRange();\\n}function Cv(C3){var C2=C1();C2.moveToElementText(C3);return C2;}function Ct(C6){var C2=C6.parentElement();\\nvar C5=-1;var C8=C2.childNodes.length;var C7=Cv(C2);if(C6.compareEndPoints("StartToStart",C7)==0){return{node:C2,index:0,maxIndex:1};\\n}else{if(C6.compareEndPoints("EndToEnd",C7)==0){if(AG(C2)&&C2.nextSibling){return{node:C2.nextSibling,index:0,maxIndex:1};\\n}return{node:C2,index:1,maxIndex:1};}else{if(C2.childNodes.length==0){return{node:C2,index:0,maxIndex:1};\\n}}}for(var C3=0;C3\\x3cC2.childNodes.length;C3++){var DD=C2.childNodes.item(C3);if(!isNodeText(DD)){var DA=Cv(DD);\\nvar DE=C6.compareEndPoints("StartToStart",DA);var DH=C6.compareEndPoints("EndToEnd",DA);if(DE>=0&&DH\\x3c=0){var DG=0;\\nif(DE>0){DG=1;}return{node:DD,index:DG,maxIndex:1};}else{if(DH>0){if(C3>C5){C5=C3;C7.setEndPoint("StartToEnd",DA);\\n}}else{if(DE\\x3c0){if(C3\\x3cC8){C8=C3;C7.setEndPoint("EndToStart",DA);}}}}}}if((C8-C5)==1){if(C5>=0){return{node:C2.childNodes.item(C5),index:1,maxIndex:1};\\n}else{return{node:C2.childNodes.item(C8),index:0,maxIndex:1};}}var DF=0;var C9=C6.duplicate();var DI=binarySearchInfinite(40,function(DL){var DK=DL-DF;\\nvar DJ=Math.abs(C9.move("character",-DK));if(C9.move("character",-1)){C9.move("character",1);}if(DK\\x3c0){DF-=DJ;\\n}else{DF+=DJ;}return(C9.compareEndPoints("StartToStart",C7)\\x3c=0);});var DB=C5+1;var DC=DI;while(DB\\x3cC8){var C4=C2.childNodes.item(DB);\\nif(DC\\x3c=C4.nodeValue.length){return{node:C4,index:DC,maxIndex:C4.nodeValue.length};}DC-=C4.nodeValue.length;\\nDB++;}var C4=C2.childNodes.item(DB-1);return{node:C4,index:C4.nodeValue.length,maxIndex:C4.nodeValue.length};\\n}var Cq={};if(Cr.compareEndPoints("StartToEnd",Cr)==0){var Cs=Ct(Cr);Cq.startPoint=Cs;Cq.endPoint={node:Cs.node,index:Cs.index,maxIndex:Cs.maxIndex};\\n}else{var Cu=Cr.duplicate();Cu.collapse(true);var Cw=Cr.duplicate();Cw.collapse(false);Cq.startPoint=Ct(Cu);\\nCq.endPoint=Ct(Cw);}return Cq;}else{var i=window.getSelection();if(i&&i.type!="None"&&i.rangeCount!==0){var Cp=i.getRangeAt(0);\\nfunction Cz(C2){while(C2&&!(C2.tagName&&C2.tagName.toLowerCase()=="body")){C2=C2.parentNode;}return !!C2;\\n}function Cy(C7,C6){if(!Cz(C7)){return{node:B,index:0,maxIndex:1};}var C2=C7;var C5=C2.childNodes.length;\\nif(isNodeText(C2)){return{node:C2,index:C6,maxIndex:C2.nodeValue.length};}else{if(C5==0){return{node:C2,index:0,maxIndex:1};\\n}else{if(C6==C5){var C3=C2.childNodes.item(C5-1);var C4=k(C3);return{node:C3,index:C4,maxIndex:C4};}else{var C3=C2.childNodes.item(C6);\\nvar C4=k(C3);return{node:C3,index:0,maxIndex:C4};}}}}var Cq={};Cq.startPoint=Cy(Cp.startContainer,Cp.startOffset);\\nCq.endPoint=Cy(Cp.endContainer,Cp.endOffset);Cq.focusAtStart=(((Cp.startContainer!=Cp.endContainer)||(Cp.startOffset!=Cp.endOffset))&&i.anchorNode&&(i.anchorNode==Cp.endContainer)&&(i.anchorOffset==Cp.endOffset));\\nreturn Cq;}else{return null;}}}function AX(i){function Cv(C5){return{node:C5.node,index:C5.index,maxIndex:C5.maxIndex};\\n}if(browser.msie){D.body.scrollHeight;function Cz(C6,C5){while(C5.firstChild&&!isNodeText(C5.firstChild)){C5=C5.firstChild;\\n}C6.moveToElementText(C5);}function C4(){return D.body.createTextRange();}function Cs(C5,C6){if(isNodeText(C6)){if(C6.previousSibling&&!isNodeText(C6.previousSibling)){Cx(C5,C6.previousSibling);\\n}else{Cs(C5,C6.parentNode);}}else{Cz(C5,C6);if(C5.move("character",1)){C5.move("character",-1);}C5.collapse(true);\\n}}function Cx(C5,C6){if(isNodeText(C6)){Cs(C5,C6);C5.move("character",C6.nodeValue.length);}else{Cz(C5,C6);\\nC5.collapse(false);}}function Cy(C7){var C5=C4();var C6=C7.node;if(isNodeText(C6)){Cs(C5,C6);C5.move("character",C7.index);\\n}else{if(C7.index==0){Cs(C5,C6);}else{Cx(C5,C6);}}return C5;}if(i){if(!CX()){return;}var C1=Cv(i.startPoint);\\nvar Cp=Cv(i.endPoint);if(Cp.index==Cp.maxIndex&&Cp.node.nextSibling){Cp.node=Cp.node.nextSibling;Cp.index=0;\\nCp.maxIndex=k(Cp.node);}var Cr=Cy(C1);Cr.setEndPoint("EndToEnd",Cy(Cp));function C3(C7){var C6;try{C6=D.selection;\\n}catch(C8){}if(!C6){return false;}var C5=C6.createRange();if(C5.parentElement().ownerDocument!=D){return false;\\n}if(C7.compareEndPoints("StartToStart",C5)!==0){return false;}if(C7.compareEndPoints("EndToEnd",C5)!==0){return false;\\n}return true;}if(!C3(Cr)){Cr.select();}}else{try{D.selection.empty();}catch(C2){}}}else{var Cw;function C0(C8){var C5=Cv(C8);\\nif(Cw){function C7(){while(C5.node.childNodes.length>0){if(C5.index==0){C5.node=C5.node.firstChild;C5.maxIndex=k(C5.node);\\n}else{if(C5.index==C5.maxIndex){C5.node=C5.node.lastChild;C5.maxIndex=k(C5.node);C5.index=C5.maxIndex;\\n}else{break;}}}}if(isNodeText(C5.node)&&C5.index==C5.maxIndex){var C6=C5.node;while((!C6.nextSibling)&&(C6!=B)&&(C6.parentNode!=B)){C6=C6.parentNode;\\n}if(C6.nextSibling&&(!((typeof C6.nextSibling.tagName)=="string"&&C6.nextSibling.tagName.toLowerCase()=="br"))&&(C6!=C5.node)&&(C6!=B)&&(C6.parentNode!=B)){C5.node=C6.nextSibling;\\nC5.maxIndex=k(C5.node);C5.index=0;C7();}}if(!isNodeText(C5.node)){C7();}}if(isNodeText(C5.node)){return{container:C5.node,offset:C5.index};\\n}else{return{container:C5.node.parentNode,offset:Bx(C5.node)+C5.index};}}var Cq=window.getSelection();\\nif(Cq){Cq.removeAllRanges();if(i){Cw=(i.startPoint.node===i.endPoint.node&&i.startPoint.index===i.endPoint.index);\\nvar Ct=C0(i.startPoint);var Cu=C0(i.endPoint);if((!Cw)&&i.focusAtStart&&Cq.collapse&&Cq.extend){Cq.collapse(Cu.container,Cu.offset);\\nCq.extend(Ct.container,Ct.offset);}else{var Cr=D.createRange();Cr.setStart(Ct.container,Ct.offset);Cr.setEnd(Cu.container,Cu.offset);\\nCq.removeAllRanges();Cq.addRange(Cr);}}}}}function Bx(i){var Cp=0;while(i.previousSibling){Cp++;i=i.previousSibling;\\n}return Cp;}function h(){if(AP()==0||Ak()==0){return;}function Cp(C3,C4,i){if(C3[C4]!=i){C3[C4]=i;}}var Cx=U.firstChild.offsetWidth;\\nvar Cs=Cx+CC;if(Cs\\x3cAi){Cs=Ai;}q=BS;if(AQ){q+=Cs+BO;}Cp(g.style,"left",q+"px");Cp(U.style,"width",Cs+"px");\\nfor(var Cv=0;Cv\\x3c2;Cv++){var Cq=B.clientHeight;var Cr=(browser.msie?B.createTextRange().boundingWidth:B.clientWidth);\\nvar Ct=Ak()-Bo-Aj;var Cu=AP()-q-CQ;if(Cq\\x3cCt){Cq=Ct;if(browser.msie){Cp(F.document.documentElement.style,"overflowY","auto");\\n}}else{if(browser.msie){Cp(F.document.documentElement.style,"overflowY","scroll");}}if(L){Cr=Cu;}else{if(Cr\\x3cCu){Cr=Cu;\\n}}if(Cq>32000){Cq=32000;}if(Cr>32000){Cr=32000;}Cp(g.style,"height",Cq+"px");Cp(g.style,"width",Cr+"px");\\nCp(U.style,"height",Cq+"px");}if(browser.mozilla){if(!L){var Cy=B;var Cw=Ct+"px";Cp(Cy.style,"height",Cw);\\n}else{Cp(B.style,"height","");}}var C2=BB();var C1=Ac();var C0=F;var Cz=20;Ax();p(U,"sidedivdelayed");\\n}function BE(){var Cp=F;var Cq=F.document;if(typeof(Cp.pageYOffset)=="number"){return{x:Cp.pageXOffset,y:Cp.pageYOffset};\\n}var i=Cq.documentElement;if(i&&typeof(i.scrollTop)=="number"){return{x:i.scrollLeft,y:i.scrollTop};}}function BB(){return BE().x;\\n}function Ac(){return BE().y;}function Cm(i){F.scrollTo(i,Ac());}function Cl(i){F.scrollTo(BB(),i);}function Ch(i,Cp){F.scrollTo(i,Cp);\\n}var BM=[];function BF(){forEach(BM,function(i){i();});}K(window,"load",CB);function Az(i){try{function Cp(Cs,Cu,Ct){if(String(Cs[Cu]).toLowerCase()!=Ct){Cs[Cu]=Ct;\\nreturn true;}return false;}if(browser.msie||browser.safari){Cp(B,"contentEditable",(i?"true":"false"));\\n}else{var Cq=Cp(D,"designMode",(i?"on":"off"));if(Cq&&i&&browser.opera){A9();}}return true;}catch(Cr){return false;\\n}}var Cg=null;function B3(Cp){var i=window.clipboardData&&window.clipboardData.getData("Text");if(i&&D.selection){}}var s=false;\\nfunction A6(i){if(i.type=="compositionstart"){s=true;}else{if(i.type=="compositionend"){s=false;}}}function A9(){K(window,"unload",BF);\\nK(document,"keydown",AN);K(document,"keypress",AN);K(document,"keyup",AN);K(document,"click",CY);K(B,"blur",CJ);\\nif(browser.msie){K(document,"click",CM);}if(browser.msie){K(B,"paste",B3);}if((!browser.msie)&&document.documentElement){K(document.documentElement,"compositionstart",A6);\\nK(document.documentElement,"compositionend",A6);}}function CM(i){if((i.target.tagName||"").toLowerCase()!="html"){return;\\n}if(!(i.pageY>B.clientHeight)){return;}b("handleOuterClick",function(){G(11);if(V()){var Cp=A.lines.length()-1;\\nvar Cq=A.lines.atIndex(Cp).text.length;d([Cp,Cq],[Cp,Cq]);}});}function BC(Cq,i){var Cp=[];(Cq.className||"").replace(/\\\\S+/g,function(Cr){if((!i)||(i(Cr))){Cp.push(Cr);\\n}});return Cp;}function Ay(i,Cp){i.className=Cp.join(" ");}function p(Cq,Cp){var i=false;var Cr=BC(Cq,function(Cs){if(Cs==Cp){i=true;\\n}return true;});if(!i){Cr.push(Cp);Ay(Cq,Cr);}}function CG(Cp,Cq){var i=false;var Cr=BC(Cp,function(Cs){if(Cs==Cq){i=true;\\nreturn false;}return true;});if(i){Ay(Cp,Cr);}}function R(Cp,i,Cq){if(Cq){p(Cp,i);}else{CG(Cp,i);}}function CB(){D=document;\\nb("setup",function(){var Cp=D.getElementById("innerdocbody");B=Cp;if(browser.mozilla){p(B,"mozilla");\\n}if(browser.safari){p(B,"safari");}if(browser.msie){p(B,"msie");}if(browser.msie){try{D.execCommand("BackgroundImageCache",false,true);\\n}catch(Cq){}}R(B,"authorColors",true);R(B,"doesWrap",L);Bh();Ax();while(B.firstChild){B.removeChild(B.firstChild);\\n}var i=AO("");AK(0,A.lines.length(),[i]);AI(null,[i.domInfo],null);A.alines=Changeset.splitAttributionLines(Changeset.makeAttribution("\\\\n"),"\\\\n");\\nA9();});O.setTimeout(function(){parent.readyFunc();},0);B4=true;}function Bc(){window.focus();}function CJ(i){if(browser.msie){AX(null);\\n}}function K(Cp,Cq,i){var Cr;if((typeof i._wrapper)!="function"){i._wrapper=function(Cs){i(CO(Cs||window.event||{}));\\n};}var Cr=i._wrapper;if(Cp.addEventListener){Cp.addEventListener(Cq,Cr,false);}else{Cp.attachEvent("on"+Cq,Cr);\\n}BM.push(function(){B2(Cp,Cq,i);});}function B2(i,Cq,Cr){var Cp=Cr._wrapper;if(i.removeEventListener){i.removeEventListener(Cq,Cp,false);\\n}else{i.detachEvent("on"+Cq,Cp);}}function B0(Cw){var Cp=Cw.node;var Ct=Cw.index;function Cq(C0){return C0.offsetLeft;\\n}function Cr(C0){return C0.offsetLeft+C0.offsetWidth;}if(!isNodeText(Cp)){if(Ct==0){return Cq(Cp);}else{return Cr(Cp);\\n}}else{var Cs=Ct;var Cv=Cp.nodeValue.length-Ct;var i;for(i=Cp.previousSibling;i&&isNodeText(i);i=i.previousSibling){Cs+=i.nodeValue;\\n}var Cu=(i?Cr(i):Cq(Cp.parentNode));for(i=Cp.nextSibling;i&&isNodeText(i);i=i.nextSibling){Cv+=i.nodeValue;\\n}var Cz=(i?Cq(i):Cr(Cp.parentNode));var Cx=(Cs/(Cs+Cv));var Cy=Cu+Cx*(Cz-Cu);return Math.round(Cy);}}function Cd(){var i=F;\\nvar Cp=i.document;if(i.innerHeight&&i.scrollMaxY){return i.innerHeight+i.scrollMaxY;}else{if(Cp.body.scrollHeight>Cp.body.offsetHeight){return Cp.body.scrollHeight;\\n}else{return Cp.body.offsetHeight;}}}function Cn(){var i=F;var Cp=i.document;if(i.innerWidth&&i.scrollMaxX){return i.innerWidth+i.scrollMaxX;\\n}else{if(Cp.body.scrollWidth>Cp.body.offsetWidth){return Cp.body.scrollWidth;}else{return Cp.body.offsetWidth;\\n}}}function Ak(){var Cp=F;var Cq=Cp.document;var i;if(browser.opera){i=Cp.innerHeight;}else{i=Cq.documentElement.clientHeight;\\n}if(i){return i;}return Number(E.frame.parentNode.style.height.replace(/[^0-9]/g,"")||0);}function AP(){var Cp=F;\\nvar i=Cp.document;return i.documentElement.clientWidth;}function Bt(Cp){var i=F;var Cs=F.document;var Cq=Cp.offsetTop+Aj-i.scrollY;\\nvar Cr=i.scrollY+Ak()-(Cp.offsetTop+Aj+Cp.offsetHeight);if(Cq\\x3c0){i.scrollBy(0,Cq);}else{if(Cr\\x3c0){i.scrollBy(0,-Cr);\\n}}}function CT(Cp){var i=F;var Cs=F.document;Cp+=q;var Cq=Cp-i.scrollX;var Cr=i.scrollX+AP()-Cp;if(Cq\\x3c0){i.scrollBy(Cq,0);\\n}else{if(Cr\\x3c0){i.scrollBy(-Cr+1,0);}}}function CP(){if(!A.selStart){return;}h();var Cq=(A.selFocusAtStart?A.selStart[0]:A.selEnd[0]);\\nBt(A.lines.atIndex(Cq).lineNode);if(!L){var i=Af();if(i){var Cr=(i.focusAtStart?i.startPoint:i.endPoint);\\nvar Cp=B0(Cr);CT(Cp);h();}}}function Y(Cq){var Cp=A.alines[Cq];if(Cp){var i=Changeset.opIterator(Cp);\\nif(i.hasNext()){return Changeset.opAttributeValue(i.next(),"list",A.apool)||"";}}return"";}function Bg(Cp,i){AJ([[Cp,i]]);\\n}function AJ(Cw){var Cp=[0,0];var Cq=Changeset.builder(A.lines.totalWidth());for(var Ct=0;Ct\\x3cCw.length;\\nCt++){var Cv=Cw[Ct];var Cs=Cv[0];var Cr=Cv[1];AS(Cq,Cp,(Cp=[Cs,0]));if(Y(Cs)){if(Cr){AS(Cq,Cp,(Cp=[Cs,1]),[["list",Cr]],A.apool);\\n}else{BQ(Cq,Cp,(Cp=[Cs,1]));}}else{if(Cr){Cq.insert("*",[["author",e],["insertorder","first"],["list",Cr]],A.apool);\\n}}}var Cu=Cq.toString();if(!Changeset.isIdentity(Cu)){c(Cu);}}function As(){if(!(A.selStart&&A.selEnd)){return;\\n}var Cp,Cq;Cp=A.selStart[0];Cq=Math.max(Cp,A.selEnd[0]-((A.selEnd[1]==0)?1:0));var Ct=true;for(var i=Cp;\\ni\\x3c=Cq;i++){if(!Y(i)){Ct=false;break;}}var Cs=[];for(var i=Cp;i\\x3c=Cq;i++){var Cr=Y(i);Cs.push([i,Ct?"":(Cr?Cr:"bullet1")]);\\n}AJ(Cs);}var AB=(browser.mozilla&&(function(){var i=0;var Cp=false;function Cr(Cu){var Ct=A.lines.atIndex(Cu).lineNode;\\nwhile(Ct.firstChild&&AG(Ct.firstChild)){Ct=Ct.firstChild;}return makeVirtualLineView(Ct);}function Cs(Ct,Cu){return[Ct,Cu-A.lines.atIndex(Ct).lineMarker];\\n}function Cq(Ct,Cu){return[Ct,Cu+A.lines.atIndex(Ct).lineMarker];}return{notifySelectionChanged:function(){if(!Cp){i=0;\\n}},handleKeyEvent:function(C0){if(C0.type!="keypress"){return false;}var Cw=C0.keyCode;if(Cw\\x3c37||Cw>40){return false;\\n}r();if(!(A.selStart&&A.selEnd)){return true;}var Cx=(C0.altKey?"byWord":(C0.ctrlKey?"byWord":(C0.metaKey?"toEnd":"normal")));\\nvar Cu=Cs(A.selStart[0],A.selStart[1]);var Ct=Cs(A.selEnd[0],A.selEnd[1]);var DB=V();if(A.selFocusAtStart){var DK=Cu;\\nCu=Ct;Ct=DK;}var DH=38,DJ=40,C7=37,DE=39;var C8=false;if(DB&&!C0.shiftKey){Cu=Ct;}else{if((!DB)&&(!C0.shiftKey)){if(Cw==C7){if(A.selFocusAtStart){Cu=Ct;\\n}else{Ct=Cu;}if(Cx=="normal"){C8=true;}}else{if(Cw==DE){if(A.selFocusAtStart){Ct=Cu;}else{Cu=Ct;}if(Cx=="normal"){C8=true;\\n}}else{Cu=Ct;}}}}if(!C8){function C1(DN){var DM=A.lines.atIndex(DN);return DM.text.length-DM.lineMarker;\\n}function C4(DN){var DM=A.lines.atIndex(DN);return DM.text.substring(DM.lineMarker);}if(Cw==DH||Cw==DJ){var Cy=(Cw==DH);\\nvar C3=((Cy&&Ct[0])||((!Cy)&&Ct[0]\\x3cA.lines.length()-1));var C6,C2,DA=false;if(L){C6=Cr(Ct[0]);C2=C6.getVLineAndOffsetForChar(Ct[1]);\\nDA=((Cy&&C2.vline>0)||((!Cy)&&C2.vline\\x3c(C6.getNumVirtualLines()-1)));}var DL;if(Cx=="toEnd"){if(Cy){Ct[0]=0;\\nCt[1]=0;}else{Ct[0]=A.lines.length()-1;Ct[1]=C1(Ct[0]);}}else{if(Cx=="byWord"){if(Cy){if(Ct[1]==0&&C3){Ct[0]--;\\nCt[1]=0;}else{Ct[1]=0;}}else{var Cz=C1(Ct[0]);if(browser.windows){if(C3){Ct[0]++;Ct[1]=0;}else{Ct[1]=Cz;\\n}}else{if(Ct[1]==Cz&&C3){Ct[0]++;Ct[1]=C1(Ct[0]);}else{Ct[1]=Cz;}}}i=0;}else{if(DA){var C5=C2.vline;var Cv=C2.offset;\\nif(Cy){C5--;}else{C5++;}if(i>Cv){Cv=i;}else{i=Cv;}var DF=C6.getCharForVLineAndOffset(C5,Cv);Ct[1]=DF.lineChar;\\n}else{if(C3){if(Cy){Ct[0]--;}else{Ct[0]++;}var Cv=Ct[1];if(L){Cv=C2.offset;}if(i>Cv){Cv=i;}else{i=Cv;\\n}if(L){var DG=Cr(Ct[0]);var C5=(Cy?DG.getNumVirtualLines()-1:0);var DF=DG.getCharForVLineAndOffset(C5,Cv);\\nCt[1]=DF.lineChar;}else{var Cz=C1(Ct[0]);if(Cv>Cz){Cv=Cz;}Ct[1]=Cv;}}else{if(Cy){Ct[1]=0;}else{Ct[1]=C1(Ct[0]);\\n}i=0;}}}}}else{if(Cw==C7||Cw==DE){var DI=(Cw==C7);if(DI){if(Cx=="toEnd"){Ct[1]=0;}else{if(Ct[1]>0){if(Cx=="byWord"){Ct[1]=y(C4(Ct[0]),Ct[1],false);\\n}else{Ct[1]--;}}else{if(Ct[0]>0){Ct[0]--;Ct[1]=C1(Ct[0]);if(Cx=="byWord"){Ct[1]=y(C4(Ct[0]),Ct[1],false);\\n}}}}}else{var Cz=C1(Ct[0]);if(Cx=="toEnd"){Ct[1]=Cz;}else{if(Ct[1]\\x3cCz){if(Cx=="byWord"){Ct[1]=y(C4(Ct[0]),Ct[1],true);\\n}else{Ct[1]++;}}else{if(Ct[0]\\x3cA.lines.length()-1){Ct[0]++;Ct[1]=0;if(Cx=="byWord"){Ct[1]=y(C4(Ct[0]),Ct[1],true);\\n}}}}}i=0;}}}var C9=((Ct[0]\\x3cCu[0])||(Ct[0]==Cu[0]&&Ct[1]\\x3cCu[1]));var DD=(C9?Ct:Cu);var DC=(C9?Cu:Ct);Cp=true;\\nd(Cq(DD[0],DD[1]),Cq(DC[0],DC[1]),C9);Cp=false;C.userChangedSelection=true;return true;}};})());function CO(i){var Cp=i;\\ni=extend({},Cp);i.preventDefault=function(){if(Cp.preventDefault){Cp.preventDefault();}Cp.returnValue=false;\\n};i.stopPropagation=function(){if(Cp.stopPropagation){Cp.stopPropagation();}Cp.cancelBubble=true;};if(!i.target&&i.srcElement){i.target=i.srcElement;\\n}if(browser.safari&&i.target.nodeType==3){i.target=Cp.target.parentNode;}if(!i.relatedTarget&&i.fromElement){i.relatedTarget=i.fromElement==i.target?i.toElement:i.fromElement;\\n}if(i.pageX==null&&i.clientX!=null){var Cq=document.documentElement,Cr=document.body;i.pageX=i.clientX+(Cq&&Cq.scrollLeft||Cr.scrollLeft||0);\\ni.pageY=i.clientY+(Cq&&Cq.scrollTop||Cr.scrollTop||0);}if(!i.which&&(i.charCode||i.keyCode)){i.which=i.charCode||i.keyCode;\\n}if(!i.metaKey&&i.ctrlKey){i.metaKey=i.ctrlKey;}if(!i.which&&i.button){i.which=(i.button&1?1:(i.button&2?3:(i.button&4?2:0)));\\n}return i;}var a;var j;function BX(){a=1;U.innerHTML=\\\'\\x3ctable border="0" cellpadding="0" cellspacing="0" align="right">\\x3ctr>\\x3ctd id="sidedivinner">\\x3cdiv>1\\x3c/div>\\x3c/td>\\x3c/tr>\\x3c/table>\\\';\\nj=F.document.getElementById("sidedivinner");}function CN(){var Cq=A.lines.length();if(Cq\\x3c1){Cq=1;}if(Cq!=a){var Cr=j;\\nvar Cv=F.document;while(a\\x3cCq){a++;var Cw=a;var Ct=Cv.createElement("DIV");Ct.appendChild(Cv.createTextNode(String(Cw)));\\nCr.appendChild(Ct);}while(a>Cq){Cr.removeChild(Cr.lastChild);a--;}}if(C&&C.domClean){var Cp=j.firstChild;\\nvar i=D.body.firstChild;while(Cp&&i){var Cs=(i.clientHeight||i.offsetHeight);if(i.nextSibling){Cs=i.nextSibling.offsetTop-i.offsetTop;\\n}if(Cs){var Cu=Cs+"px";if(Cp.style.height!=Cu){Cp.style.height=Cu;}}Cp=Cp.nextSibling;i=i.nextSibling;\\n}j.firstChild.style.marginTop=(D.body.firstChild.offsetTop-j.firstChild.offsetTop+parseInt(j.firstChild.style.marginTop+"0"))+"px";\\n}}}OUTER(this);\'+\'//-->\\n\\x3c/script>\')'));
+T.push('\'\\n<style type="text/css" title="dynamicsyntax"></style>\\n\'');T.push('\'</head><body id="innerdocbody" class="syntax" spellcheck="false">&nbsp;</body></html>\'');
+var X='editorId = "'+A.id+'"; editorInfo = parent.'+K+'.registry[editorId]; window.onload = function() { window.onload = null; setTimeout(function() { var iframe = document.createElement("IFRAME"); iframe.scrolling = "no"; var outerdocbody = document.getElementById("outerdocbody"); iframe.frameBorder = 0; iframe.allowTransparency = true; outerdocbody.insertBefore(iframe, outerdocbody.firstChild); iframe.ace_outerWin = window; readyFunc = function() { editorInfo.onEditorReady(); readyFunc = null; editorInfo = null; }; var doc = iframe.contentWindow.document; doc.open(); doc.write('+T.join("+")+"); doc.close(); }, 0); }";
+var Y=[W,"<html><head>",('<style type="text/css">body{margin:0;white-space:nowrap;}h1,h2,h3,h4,h5,h6{display:inline;line-height:2em;}#outerdocbody{background-color:#fff;}body.grayedout{background-color:#eee!important;}#innerdocbody{font-size:12px;font-family:monospace;line-height:16px;}body.doesWrap{white-space:normal;}#innerdocbody{padding-top:1px;padding-right:10px;padding-bottom:8px;padding-left:1px;overflow:hidden;background-image:url(data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==);}#sidediv{font-size:11px;font-family:monospace;line-height:16px;padding-top:8px;padding-right:3px;position:absolute;width:20px;top:0;left:0;cursor:default;color:white;}#sidedivinner{text-align:right;}.sidedivdelayed{background-color:#eee;color:#888!important;border-right:1px solid #999;}.sidedivhidden{display:none;}#outerdocbody iframe{display:block;position:relative;left:32px;top:7px;border:0;width:1px;height:1px;}#outerdocbody .hotrect{border:1px solid #999;position:absolute;}body.mozilla,body.safari{display:table-cell;}body.doesWrap{display:block!important;}.safari div{padding-right:1px;}p{margin:0;}#linemetricsdiv{position:absolute;left:-1000px;top:-1000px;color:white;z-index:-1;font-size:12px;font-family:monospace;}#overlaysdiv{position:absolute;left:-1000px;top:-1000px;}</style>'),'<link rel="stylesheet" type="text/css" href="data:text/css,"/>',"\x3cscript>",X,"\x3c/script>",'</head><body id="outerdocbody"><div id="sidediv"><!-- --></div><div id="linemetricsdiv">x</div><div id="overlaysdiv"><!-- --></div></body></html>'];
+var U=document.createElement("IFRAME");U.frameBorder=0;A.frame=U;document.getElementById(Q).appendChild(U);
+var V=U.contentWindow.document;V.open();V.write(Y.join(""));V.close();B.adjustSize();})();};return B;
+} \ No newline at end of file
diff --git a/trunk/etherpad/src/static/js/billing.js b/trunk/etherpad/src/static/js/billing.js
new file mode 100644
index 0000000..c9fa30e
--- /dev/null
+++ b/trunk/etherpad/src/static/js/billing.js
@@ -0,0 +1,111 @@
+/**
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS-IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+$(function() {
+ billing.initFieldDisplay();
+ billing.initCcValidation();
+});
+
+billing.initFieldDisplay = function() {
+ var id = $('#billingselect input:checked').attr("value");
+ $('.billingfield').not('.billingfield.'+id+'req').hide();
+ $('.paymentbutton').click(billing.selectPaymentType);
+
+ $('#billingCountry').click(billing.selectCountry);
+ billing.selectCountry();
+}
+
+billing.selectCountry = function() {
+ var countryCode = $('#billingCountry').attr("value");
+ var id = $('#billingselect input:checked').attr("value");
+ if (countryCode != 'US') {
+ $('.billingfield.intonly.'+id+'req').show();
+ $('.billingfield.usonly').hide();
+ } else {
+ $('.billingfield.intonly').hide();
+ $('.billingfield.usonly.'+id+'req').show();
+ }
+}
+
+billing.countryAntiSelector = function() {
+ var countryCode = $('#billingCountry').attr("value");
+ if (countryCode != 'US') {
+ return '.usonly';
+ } else {
+ return '.intonly';
+ }
+}
+
+billing.selectPaymentType = function() {
+ var radio = $(this).children('input');
+ var id = radio.attr("value");
+ radio.attr("checked", "checked");
+
+ var selector = billing.countryAntiSelector();
+ var toShow = $('.billingfield.'+id+'req:hidden').not('.billingfield'+selector);
+ var toHide = $('.billingfield:visible').not('.billingfield.'+id+'req');
+
+ if (toShow.size() > 0 && toHide.size() > 0) {
+ toHide.fadeOut(200);
+ setTimeout(function() {
+ toShow.fadeIn(200);
+ }, 200);
+ } else if (toShow.size() > 0 || toHide.size() > 0){
+ toShow.fadeIn(200);
+ toHide.fadeOut(200);
+ }
+}
+
+billing.extractCcType = function(numsrc) {
+ var number = $(numsrc).val();
+ var newType = billing.getCcType(number);
+ $('.ccimage').removeClass('ccimageselected');
+ if (newType) {
+ $('#img'+newType).addClass('ccimageselected');
+ }
+ if (billing.validateCcNumber(number)) {
+ $('input[name=billingCCNumber]').css('border', '1px solid #0f0');
+ } else if (billing.validateCcLength(number) ||
+ ! (/^\d*$/.test(number))) {
+ $('input[name=billingCCNumber]').css('border', '1px solid #f00');
+ } else {
+ $('input[name=billingCCNumber]').css('border', '1px solid black');
+ }
+}
+
+billing.handleCcFieldChange = function(target, event) {
+ if (event &&
+ ! (event.keyCode == 8 ||
+ (event.keyCode >= 32 && event.keyCode <= 126))) {
+ return;
+ }
+ var ccValue = $(target).val();
+ if (ccValue == billing.lastCcValue) {
+ return;
+ }
+ billing.lastCcValue = ccValue;
+ setTimeout(function() {
+ billing.extractCcType(target);
+ }, 0);
+}
+
+billing.initCcValidation = function() {
+ $('input[name=billingCCNumber]').keydown(
+ function(event) { billing.handleCcFieldChange(this, event); });
+ $('input[name=billingCCNumber]').blur(
+ function() { billing.handleCcFieldChange(this) });
+ billing.lastCcValue = $('input[name=billingCCNumber]').val();
+} \ No newline at end of file
diff --git a/trunk/etherpad/src/static/js/billing_shared.js b/trunk/etherpad/src/static/js/billing_shared.js
new file mode 100644
index 0000000..dc3a00c
--- /dev/null
+++ b/trunk/etherpad/src/static/js/billing_shared.js
@@ -0,0 +1,94 @@
+/**
+ * Copyright 2009 Google 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.
+ */
+
+var billing = {};
+
+billing.CC = function(shortName, prefixes, length) {
+ this.type = shortName;
+ this.prefixes = prefixes;
+ this.length = length;
+ function validateLuhn(number) {
+ var digits = [];
+ var sum = 0;
+ for (var i = 0; i < number.length; ++i) {
+ var c = Number(number.charAt(number.length-1-i));
+ sum += c;
+ if (i % 2 == 1) { // every second digit
+ sum += c;
+ if (2*c >= 10) {
+ sum -= 9;
+ }
+ }
+ }
+ return (sum % 10 == 0);
+ }
+ this.validatePrefix = function(number) {
+ for (var i = 0; i < this.prefixes.length; ++i) {
+ if (number.indexOf(String(this.prefixes[i])) == 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+ this.validateLength = function(number) {
+ return number.length == this.length;
+ }
+
+ this.validateNumber = function(number) {
+ return this.validateLength(number) &&
+ this.validatePrefix(number) &&
+ validateLuhn(number);
+ }
+}
+
+billing.ccTypes = [
+ new billing.CC('amex', [34, 37], 15),
+ new billing.CC('disc', [6011, 644, 645, 646, 647, 648, 649, 65], 16),
+ new billing.CC('mc', [51, 52, 53, 54, 55], 16),
+ new billing.CC('visa', [4], 16)];
+
+billing.validateCcNumber = function(number) {
+ if (! (/^\d+$/.test(number))) {
+ return false;
+ }
+ for (var i = 0; i < billing.ccTypes.length; ++i) {
+ var ccType = billing.ccTypes[i];
+ if (ccType.validatePrefix(number)) {
+ return ccType.validateNumber(number);
+ }
+ }
+ return false;
+}
+
+billing.validateCcLength = function(number) {
+ for (var i = 0; i < billing.ccTypes.length; ++i) {
+ var ccType = billing.ccTypes[i];
+ if (ccType.validatePrefix(number)) {
+ return ccType.validateLength(number);
+ }
+ }
+ return false;
+}
+
+billing.getCcType = function(number) {
+ for (var i = 0; i < billing.ccTypes.length; ++i) {
+ var ccType = billing.ccTypes[i];
+ if (ccType.validatePrefix(number)) {
+ return ccType.type;
+ }
+ }
+ return false;
+}
diff --git a/trunk/etherpad/src/static/js/broadcast.js b/trunk/etherpad/src/static/js/broadcast.js
new file mode 100644
index 0000000..9fa8141
--- /dev/null
+++ b/trunk/etherpad/src/static/js/broadcast.js
@@ -0,0 +1,607 @@
+/**
+ * Copyright 2009 Google 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.
+ */
+
+// just in case... (todo: this must be somewhere else in the client code.)
+if (!Array.prototype.map)
+{
+ Array.prototype.map = function(fun /*, thisp*/)
+ {
+ var len = this.length >>> 0;
+ if (typeof fun != "function")
+ throw new TypeError();
+
+ var res = new Array(len);
+ var thisp = arguments[1];
+ for (var i = 0; i < len; i++)
+ {
+ if (i in this)
+ res[i] = fun.call(thisp, this[i], i, this);
+ }
+
+ return res;
+ };
+}
+
+if (!Array.prototype.forEach)
+{
+ Array.prototype.forEach = function(fun /*, thisp*/)
+ {
+ var len = this.length >>> 0;
+ if (typeof fun != "function")
+ throw new TypeError();
+
+ var thisp = arguments[1];
+ for (var i = 0; i < len; i++)
+ {
+ if (i in this)
+ fun.call(thisp, this[i], i, this);
+ }
+ };
+}
+
+if (!Array.prototype.indexOf)
+{
+ Array.prototype.indexOf = function(elt /*, from*/)
+ {
+ var len = this.length >>> 0;
+
+ var from = Number(arguments[1]) || 0;
+ from = (from < 0)
+ ? Math.ceil(from)
+ : Math.floor(from);
+ if (from < 0)
+ from += len;
+
+ for (; from < len; from++)
+ {
+ if (from in this &&
+ this[from] === elt)
+ return from;
+ }
+ return -1;
+ };
+}
+
+function debugLog() {
+ try {
+ // console.log.apply(console, arguments);
+ } catch (e) {console.log("error printing: ",e);}
+}
+
+function randomString() {
+ return "_"+Math.floor(Math.random() * 1000000);
+}
+
+// for IE
+if ($.browser.msie) {
+ try {
+ document.execCommand("BackgroundImageCache", false, true);
+ } catch (e) {}
+}
+
+var userId = "hiddenUser" + randomString();
+var socketId;
+var socket;
+
+var channelState = "DISCONNECTED";
+
+var appLevelDisconnectReason = null;
+
+var padContents = {
+ currentRevision: clientVars.revNum,
+ currentTime : clientVars.currentTime,
+ currentLines: Changeset.splitTextLines(clientVars.initialStyledContents.atext.text),
+ currentDivs : null, // to be filled in once the dom loads
+ apool: (new AttribPool()).fromJsonable(clientVars.initialStyledContents.apool),
+ alines: Changeset.splitAttributionLines(
+ clientVars.initialStyledContents.atext.attribs,
+ clientVars.initialStyledContents.atext.text),
+
+ // generates a jquery element containing HTML for a line
+ lineToElement: function(line, aline) {
+ var element = document.createElement("div");
+ var emptyLine = (line == '\n');
+ var domInfo = domline.createDomLine(! emptyLine, true);
+ linestylefilter.populateDomLine(line, aline, this.apool,
+ domInfo);
+ domInfo.prepareForAdd();
+ element.className = domInfo.node.className;
+ element.innerHTML = domInfo.node.innerHTML;
+ element.id = Math.random();
+ return $(element);
+ },
+
+ applySpliceToDivs: function(start, numRemoved, newLines) {
+ // remove spliced-out lines from DOM
+ for(var i=start; i<start+numRemoved && i<this.currentDivs.length; i++) {
+ debugLog("removing", this.currentDivs[i].attr('id'));
+ this.currentDivs[i].remove();
+ }
+
+ // remove spliced-out line divs from currentDivs array
+ this.currentDivs.splice(start, numRemoved);
+
+ var newDivs = [];
+ for(var i=0;i<newLines.length;i++) {
+ newDivs.push(this.lineToElement(newLines[i],
+ this.alines[start+i]));
+ }
+
+ // grab the div just before the first one
+ var startDiv = this.currentDivs[start-1] || null;
+
+ // insert the div elements into the correct place, in the correct order
+ for(var i=0; i<newDivs.length; i++) {
+ if (startDiv) {
+ startDiv.after(newDivs[i]);
+ }
+ else {
+ $("#padcontent").prepend(newDivs[i]);
+ }
+ startDiv = newDivs[i];
+ }
+
+ // insert new divs into currentDivs array
+ newDivs.unshift(0); // remove 0 elements
+ newDivs.unshift(start);
+ this.currentDivs.splice.apply(this.currentDivs, newDivs);
+ return this;
+ },
+
+ // splice the lines
+ splice: function(start, numRemoved, newLinesVA) {
+ var newLines = Array.prototype.slice.call(arguments, 2).map(
+ function(s) { return s; });
+
+ // apply this splice to the divs
+ this.applySpliceToDivs(start, numRemoved, newLines);
+
+ // call currentLines.splice, to keep the currentLines array up to date
+ newLines.unshift(numRemoved);
+ newLines.unshift(start);
+ this.currentLines.splice.apply(this.currentLines, arguments);
+ },
+ // returns the contents of the specified line I
+ get: function(i) {
+ return this.currentLines[i];
+ },
+ // returns the number of lines in the document
+ length: function() {
+ return this.currentLines.length;
+ },
+
+ getActiveAuthors: function() {
+ var self = this;
+ var authors = [];
+ var seenNums = {};
+ var alines = self.alines;
+ for(var i=0;i<alines.length;i++) {
+ Changeset.eachAttribNumber(alines[i], function(n) {
+ if (! seenNums[n]) {
+ seenNums[n] = true;
+ if (self.apool.getAttribKey(n) == 'author') {
+ var a = self.apool.getAttribValue(n);
+ if (a) {
+ authors.push(a);
+ }
+ }
+ }
+ });
+ }
+ authors.sort();
+ return authors;
+ }
+};
+
+function callCatchingErrors(catcher, func) {
+ try {
+ wrapRecordingErrors(catcher, func)();
+ }
+ catch (e) { /*absorb*/ }
+}
+
+function wrapRecordingErrors(catcher, func) {
+ return function() {
+ try {
+ return func.apply(this, Array.prototype.slice.call(arguments));
+ }
+ catch (e) {
+ // caughtErrors.push(e);
+ // caughtErrorCatchers.push(catcher);
+ // caughtErrorTimes.push(+new Date());
+ // console.dir({catcher: catcher, e: e});
+ debugLog(e); // TODO(kroo): added temporary, to catch errors
+ throw e;
+ }
+ };
+}
+
+function loadedNewChangeset(changesetForward, changesetBackward, revision, timeDelta) {
+ var broadcasting = (BroadcastSlider.getSliderPosition() == revisionInfo.latest);
+ debugLog("broadcasting:", broadcasting, BroadcastSlider.getSliderPosition(), revisionInfo.latest, revision);
+ revisionInfo.addChangeset(revision, revision+1, changesetForward, changesetBackward, timeDelta);
+ BroadcastSlider.setSliderLength(revisionInfo.latest);
+ if(broadcasting)
+ applyChangeset(changesetForward, revision+1, false, timeDelta);
+}
+
+/*
+ At this point, we must be certain that the changeset really does map from
+ the current revision to the specified revision. Any mistakes here will
+ cause the whole slider to get out of sync.
+ */
+function applyChangeset(changeset, revision, preventSliderMovement, timeDelta) {
+ // disable the next 'gotorevision' call handled by a timeslider update
+ if(!preventSliderMovement) {
+ goToRevisionIfEnabledCount ++;
+ BroadcastSlider.setSliderPosition(revision);
+ }
+
+ try {
+ // must mutate attribution lines before text lines
+ Changeset.mutateAttributionLines(changeset, padContents.alines, padContents.apool);
+ }catch(e) { debugLog(e); }
+
+ Changeset.mutateTextLines(changeset, padContents);
+ padContents.currentRevision = revision;
+ padContents.currentTime += timeDelta * 1000;
+ debugLog('Time Delta: ',timeDelta)
+ updateTimer();
+ BroadcastSlider.setAuthors(padContents.getActiveAuthors().map(function(name) {return authorData[name];}));
+}
+
+function updateTimer() {
+ var zpad = function(str, length) {
+ str = str+"";
+ while(str.length < length)
+ str = '0'+str;
+ return str;
+ }
+ var date = new Date(padContents.currentTime);
+ var dateFormat = function() {
+ var month = zpad(date.getMonth()+1,2);
+ var day = zpad(date.getDate(),2);
+ var year = (date.getFullYear());
+ var hours = zpad(date.getHours(),2);
+ var minutes = zpad(date.getMinutes(),2);
+ var seconds = zpad(date.getSeconds(),2);
+ return ([month,'/',day,'/',year,' ',hours,':',minutes,':',seconds].join(""));
+ }
+
+ $('#timer').html(dateFormat());
+
+ var revisionDate = [
+ "Saved",
+ [
+ "Jan", "Feb", "March", "April", "May", "June",
+ "July", "Aug", "Sept", "Oct", "Nov", "Dec"
+ ][date.getMonth()],
+ date.getDate()+",",
+ date.getFullYear()
+ ].join(" ")
+ $('#revision_date').html(revisionDate)
+
+}
+
+function goToRevision(newRevision) {
+ padContents.targetRevision = newRevision;
+ var self = this;
+ var path = revisionInfo.getPath(padContents.currentRevision, newRevision);
+ debugLog('newRev: ', padContents.currentRevision, path);
+ if( path.status == 'complete') {
+ var cs = path.changesets;
+ debugLog("status: complete, changesets: ",cs, "path:", path);
+ var changeset = cs[0];
+ var timeDelta = path.times[0];
+ for(var i=1; i<cs.length; i++) {
+ changeset = Changeset.compose(changeset, cs[i], padContents.apool);
+ timeDelta += path.times[i];
+ }
+ if(changeset)
+ applyChangeset(changeset, path.rev, true, timeDelta);
+ } else if(path.status == "partial") {
+ debugLog('partial');
+ var sliderLocation = padContents.currentRevision;
+ // callback is called after changeset information is pulled from server
+ // this may never get called, if the changeset has already been loaded
+ var update = function(start, end) {
+ // if we've called goToRevision in the time since, don't goToRevision
+ goToRevision(padContents.targetRevision);
+ };
+
+ // do our best with what we have...
+ var cs = path.changesets;
+
+ var changeset = cs[0];
+ var timeDelta = path.times[0];
+ for(var i=1; i<cs.length; i++) {
+ changeset = Changeset.compose(changeset, cs[i], padContents.apool);
+ timeDelta += path.times[i];
+ }
+ if(changeset)
+ applyChangeset(changeset, path.rev, true, timeDelta);
+
+
+ if(BroadcastSlider.getSliderLength() > 10000) {
+ var start = (Math.floor((newRevision) / 10000) * 10000); // revision 0 to 10
+ changesetLoader.queueUp(start, 100);
+ }
+
+ if(BroadcastSlider.getSliderLength() > 1000) {
+ var start = (Math.floor((newRevision) / 1000) * 1000); // (start from -1, go to 19) + 1
+ changesetLoader.queueUp(start, 10);
+ }
+
+ start = (Math.floor((newRevision) / 100) * 100);
+
+ changesetLoader.queueUp(start, 1, update);
+ }
+ BroadcastSlider.setAuthors(padContents.getActiveAuthors().map(function(name) {return authorData[name];}));
+}
+
+var changesetLoader = {
+ running: false,
+ resolved: [],
+ requestQueue1: [],
+ requestQueue2: [],
+ requestQueue3: [],
+ queueUp: function(revision, width, callback) {
+ if(revision < 0) revision = 0;
+ // if(changesetLoader.requestQueue.indexOf(revision) != -1)
+ // return; // already in the queue.
+ if(changesetLoader.resolved.indexOf(revision+"_"+width) != -1)
+ return; // already loaded from the server
+ changesetLoader.resolved.push(revision+"_"+width);
+
+ var requestQueue = width == 1 ? changesetLoader.requestQueue3 :
+ width == 10 ? changesetLoader.requestQueue2 :
+ changesetLoader.requestQueue1;
+ requestQueue.push({'rev': revision, 'res': width, 'callback': callback});
+ if(!changesetLoader.running) {
+ changesetLoader.running = true;
+ setTimeout(changesetLoader.loadFromQueue, 10);
+ }
+ },
+ loadFromQueue: function() {
+ var self = changesetLoader;
+ var requestQueue = self.requestQueue1.length > 0 ? self.requestQueue1 :
+ self.requestQueue2.length > 0 ? self.requestQueue2 :
+ self.requestQueue3.length > 0 ? self.requestQueue3 : null;
+
+ if(!requestQueue) {
+ self.running = false;
+ return;
+ }
+
+ var request = requestQueue.pop();
+ var granularity = request.res;
+ var callback = request.callback;
+ var start = request.rev;
+ debugLog("loadinging revision", start, "through ajax");
+ $.getJSON(
+ "/ep/pad/changes/"+clientVars.padIdForUrl+"?s="+start + "&g="+granularity,
+ function(data, textStatus) {
+ if(textStatus !== "success") {
+ console.log(textStatus);
+ BroadcastSlider.showReconnectUI();
+ }
+ self.handleResponse(data, start, granularity, callback);
+
+ setTimeout(self.loadFromQueue, 10); // load the next ajax function
+ }
+ );
+ },
+ handleResponse: function(data, start, granularity, callback) {
+ debugLog("response: ", data);
+ var pool = (new AttribPool()).fromJsonable(data.apool);
+ for(var i=0; i<data.forwardsChangesets.length; i++) {
+ var astart = start + i * granularity - 1; // rev -1 is a blank single line
+ var aend = start + (i+1) * granularity - 1;// totalRevs is the most recent revision
+ if(aend > data.actualEndNum - 1) aend = data.actualEndNum - 1;
+ debugLog("adding changeset:", astart, aend);
+ var forwardcs = Changeset.moveOpsToNewPool(data.forwardsChangesets[i], pool, padContents.apool);
+ var backwardcs = Changeset.moveOpsToNewPool(data.backwardsChangesets[i], pool, padContents.apool);
+ revisionInfo.addChangeset(astart, aend, forwardcs, backwardcs, data.timeDeltas[i]);
+ }
+ if(callback)callback(start - 1, start + data.forwardsChangesets.length * granularity - 1);
+ }
+};
+
+function handleMessageFromServer() {
+ debugLog("handleMessage:", arguments);
+ var obj = arguments[0]['data'];
+ var expectedType = "COLLABROOM";
+
+ obj = JSON.parse(obj);
+ if (obj['type'] == expectedType) {
+ obj = obj['data'];
+
+ if (obj['type'] == "NEW_CHANGES") {
+ debugLog(obj);
+ var changeset = Changeset.moveOpsToNewPool(
+ obj.changeset, (new AttribPool()).fromJsonable(obj.apool),
+ padContents.apool);
+
+ var changesetBack = Changeset.moveOpsToNewPool(
+ obj.changesetBack, (new AttribPool()).fromJsonable(obj.apool),
+ padContents.apool);
+
+ loadedNewChangeset(changeset, changesetBack, obj.newRev-1, obj.timeDelta);
+ }
+ else if (obj['type'] == "NEW_AUTHORDATA") {
+ var authorMap = {};
+ authorMap[obj.author] = obj.data;
+ receiveAuthorData(authorMap);
+ BroadcastSlider.setAuthors(padContents.getActiveAuthors().map(function(name) {return authorData[name];}));
+ } else if (obj['type'] == "NEW_SAVEDREV") {
+ var savedRev = obj.savedRev;
+ BroadcastSlider.addSavedRevision(savedRev.revNum, savedRev);
+ }
+ } else {
+ debugLog("incorrect message type: " + obj['type'] + ", expected " + expectedType);
+ }
+}
+
+function handleSocketClosed(params) {
+ debugLog("socket closed!", params);
+ socket = null;
+
+ BroadcastSlider.showReconnectUI();
+ // var reason = appLevelDisconnectReason || params.reason;
+ // var shouldReconnect = params.reconnect;
+ // if (shouldReconnect) {
+ // // determine if this is a tight reconnect loop due to weird connectivity problems
+ // // reconnectTimes.push(+new Date());
+ // var TOO_MANY_RECONNECTS = 8;
+ // var TOO_SHORT_A_TIME_MS = 10000;
+ // if (reconnectTimes.length >= TOO_MANY_RECONNECTS &&
+ // ((+new Date()) - reconnectTimes[reconnectTimes.length-TOO_MANY_RECONNECTS]) <
+ // TOO_SHORT_A_TIME_MS) {
+ // setChannelState("DISCONNECTED", "looping");
+ // }
+ // else {
+ // setChannelState("RECONNECTING", reason);
+ // setUpSocket();
+ // }
+ // }
+ // else {
+ // BroadcastSlider.showReconnectUI();
+ // setChannelState("DISCONNECTED", reason);
+ // }
+}
+
+function sendMessage(msg) {
+ socket.postMessage(JSON.stringify({type: "COLLABROOM", data: msg}));
+}
+
+function setUpSocket() {
+ // required for Comet
+ if ((! $.browser.msie) &&
+ (! ($.browser.mozilla && $.browser.version.indexOf("1.8.") == 0))) {
+ document.domain = document.domain; // for comet
+ }
+
+ var success = false;
+ callCatchingErrors("setUpSocket", function() {
+ appLevelDisconnectReason = null;
+
+ socketId = String(Math.floor(Math.random()*1e12));
+ socket = new WebSocket(socketId);
+ socket.onmessage = wrapRecordingErrors("socket.onmessage", handleMessageFromServer);
+ socket.onclosed = wrapRecordingErrors("socket.onclosed", handleSocketClosed);
+ socket.onopen = wrapRecordingErrors("socket.onopen", function() {
+ setChannelState("CONNECTED");
+ var msg = { type:"CLIENT_READY", roomType:'padview',
+ roomName:'padview/'+clientVars.viewId,
+ data: { lastRev:clientVars.revNum,
+ userInfo:{userId: userId} } };
+ sendMessage(msg);
+ });
+ // socket.onhiccup = wrapRecordingErrors("socket.onhiccup", handleCometHiccup);
+ // socket.onlogmessage = function(x) {debugLog(x); };
+ socket.connect();
+ success = true;
+ });
+ if (success) {
+ //initialStartConnectTime = +new Date();
+ }
+ else {
+ abandonConnection("initsocketfail");
+ }
+}
+
+function setChannelState(newChannelState, moreInfo) {
+ if (newChannelState != channelState) {
+ channelState = newChannelState;
+ // callbacks.onChannelStateChange(channelState, moreInfo);
+ }
+}
+
+function abandonConnection(reason) {
+ if (socket) {
+ socket.onclosed = function() {};
+ socket.onhiccup = function() {};
+ socket.disconnect();
+ }
+ socket = null;
+ setChannelState("DISCONNECTED", reason);
+}
+
+window['onloadFuncts'] = [];
+window.onload = function() {
+ window['isloaded'] = true;
+ window['onloadFuncts'].forEach(function(funct) {
+ funct();
+ });
+};
+
+// to start upon window load, just push a function onto this array
+window['onloadFuncts'].push(setUpSocket);
+window['onloadFuncts'].push(function() {
+ // set up the currentDivs and DOM
+ padContents.currentDivs = [];
+ $("#padcontent").html("");
+ for(var i=0; i<padContents.currentLines.length; i++) {
+ var div = padContents.lineToElement(padContents.currentLines[i],
+ padContents.alines[i]);
+ padContents.currentDivs.push(div);
+ $("#padcontent").append(div);
+ }
+ debugLog(padContents.currentDivs);
+});
+
+// this is necessary to keep infinite loops of events firing,
+// since goToRevision changes the slider position
+var goToRevisionIfEnabledCount = 0;
+var goToRevisionIfEnabled = function() {
+ if(goToRevisionIfEnabledCount > 0) {
+ goToRevisionIfEnabledCount --;
+ } else {
+ goToRevision.apply(goToRevision, arguments);
+ }
+}
+
+BroadcastSlider.onSlider(goToRevisionIfEnabled);
+
+(function() {
+ for(var i=0; i<clientVars.initialChangesets.length; i++) {
+ var csgroup = clientVars.initialChangesets[i];
+ var start = clientVars.initialChangesets[i].start;
+ var granularity = clientVars.initialChangesets[i].granularity;
+ debugLog("loading changest on startup: ", start, granularity, csgroup);
+ changesetLoader.handleResponse(csgroup, start, granularity, null);
+ }
+})();
+
+var dynamicCSS = makeCSSManager('dynamicsyntax');
+var authorData = {};
+
+function receiveAuthorData(newAuthorData) {
+ for(var author in newAuthorData) {
+ var data = newAuthorData[author];
+ if ((typeof data.colorId) == 'number') {
+ var bgcolor = clientVars.colorPalette[data.colorId];
+ if (bgcolor && dynamicCSS) {
+ dynamicCSS.selectorStyle(
+ '.'+linestylefilter.getAuthorClassName(author)).backgroundColor =
+ bgcolor;
+ }
+ }
+ authorData[author] = data;
+ }
+}
+
+receiveAuthorData(clientVars.historicalAuthorData);
diff --git a/trunk/etherpad/src/static/js/broadcast_revisions.js b/trunk/etherpad/src/static/js/broadcast_revisions.js
new file mode 100644
index 0000000..7e99003
--- /dev/null
+++ b/trunk/etherpad/src/static/js/broadcast_revisions.js
@@ -0,0 +1,119 @@
+/**
+ * Copyright 2009 Google 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.
+ */
+
+// revision info is a skip list whos entries represent a particular revision
+// of the document. These revisions are connected together by various
+// changesets, or deltas, between any two revisions.
+function Revision(revNum) {
+ this.rev = revNum;
+ this.changesets = [];
+}
+
+Revision.prototype.addChangeset = function(destIndex, changeset, timeDelta) {
+ var changesetWrapper = {
+ deltaRev: destIndex-this.rev,
+ deltaTime: timeDelta,
+ getValue: function() {
+ return changeset;
+ }
+ };
+ this.changesets.push(changesetWrapper);
+ this.changesets.sort(function(a, b) {
+ return (b.deltaRev - a.deltaRev)
+ });
+}
+
+revisionInfo = {};
+revisionInfo.addChangeset = function(fromIndex, toIndex, changeset, backChangeset, timeDelta) {
+ var startRevision = revisionInfo[fromIndex] || revisionInfo.createNew(fromIndex);
+ var endRevision = revisionInfo[toIndex] || revisionInfo.createNew(toIndex);
+ startRevision.addChangeset(toIndex, changeset, timeDelta);
+ endRevision.addChangeset(fromIndex, backChangeset, -1 * timeDelta);
+}
+
+revisionInfo.latest = clientVars.totalRevs || -1;
+
+revisionInfo.createNew = function(index) {
+ revisionInfo[index] = new Revision(index);
+ if(index > revisionInfo.latest) {
+ revisionInfo.latest = index;
+ }
+
+ return revisionInfo[index];
+}
+
+// assuming that there is a path from fromIndex to toIndex, and that the links
+// are laid out in a skip-list format
+revisionInfo.getPath = function(fromIndex, toIndex) {
+ var changesets = [];
+ var spans = [];
+ var times = [];
+ var elem = revisionInfo[fromIndex] || revisionInfo.createNew(fromIndex);
+ if(elem.changesets.length != 0 && fromIndex != toIndex) {
+ var reverse = !(fromIndex < toIndex)
+ while(((elem.rev < toIndex) && !reverse) ||
+ ((elem.rev > toIndex) && reverse)) {
+ var couldNotContinue = false;
+ var oldRev = elem.rev;
+
+ for(var i = reverse ? elem.changesets.length - 1 : 0;
+ reverse?i>=0:i<elem.changesets.length;
+ i += reverse ? -1 : 1) {
+ if(((elem.changesets[i].deltaRev < 0) && !reverse) ||
+ ((elem.changesets[i].deltaRev > 0) && reverse)) {
+ couldNotContinue = true;
+ break;
+ }
+
+ if(((elem.rev + elem.changesets[i].deltaRev <= toIndex) && !reverse) ||
+ ((elem.rev + elem.changesets[i].deltaRev >= toIndex) && reverse)) {
+ var topush = elem.changesets[i];
+ changesets.push(topush.getValue());
+ spans.push(elem.changesets[i].deltaRev);
+ times.push(topush.deltaTime);
+ elem = revisionInfo[elem.rev + elem.changesets[i].deltaRev];
+ break;
+ }
+ }
+
+ if(couldNotContinue || oldRev == elem.rev) break;
+ }
+ }
+
+ var status = 'partial';
+ if(elem.rev == toIndex)
+ status = 'complete';
+
+ return {
+ 'fromRev':fromIndex,
+ 'rev': elem.rev,
+ 'status': status,
+ 'changesets': changesets,
+ 'spans' : spans,
+ 'times' : times
+ };
+}
+
+// revisionInfo.addChangeset(0, 5, "abcde")
+// revisionInfo.addChangeset(5, 10, "fghij")
+// revisionInfo.addChangeset(10, 11, "k")
+// revisionInfo.addChangeset(11, 12, "l")
+// revisionInfo.addChangeset(12, 13, "m")
+// revisionInfo.addChangeset(13, 14, "n")
+// revisionInfo.addChangeset(14, 15, "o")
+// revisionInfo.addChangeset(15, 20, "pqrst")
+//
+// print (revisionInfo.getPath(15, 0))
diff --git a/trunk/etherpad/src/static/js/broadcast_slider.js b/trunk/etherpad/src/static/js/broadcast_slider.js
new file mode 100644
index 0000000..371663e
--- /dev/null
+++ b/trunk/etherpad/src/static/js/broadcast_slider.js
@@ -0,0 +1,401 @@
+/**
+ * Copyright 2009 Google 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.
+ */
+
+var global = this;
+
+(function() { // wrap this code in its own namespace
+ var sliderLength = 1000;
+ var sliderPos = 0;
+ var sliderActive = false;
+ var slidercallbacks = [];
+ var savedRevisions = [];
+ var sliderPlaying = false;
+
+ function disableSelection(element) {
+ element.onselectstart = function() {
+ return false;
+ };
+ element.unselectable = "on";
+ element.style.MozUserSelect = "none";
+ element.style.cursor = "default";
+ }
+ var _callSliderCallbacks = function(newval) {
+ sliderPos = newval;
+ for(var i=0; i<slidercallbacks.length; i++) {
+ slidercallbacks[i](newval);
+ }
+ }
+
+ var updateSliderElements = function() {
+ for(var i=0; i<savedRevisions.length; i++) {
+ var position = parseInt(savedRevisions[i].attr('pos'));
+ savedRevisions[i].css('left', (position * ($("#ui-slider-bar").width()-2) / (sliderLength * 1.0)) - 1);
+ }
+ $("#ui-slider-handle").css('left', sliderPos * ($("#ui-slider-bar").width()-2) / (sliderLength * 1.0));
+ }
+
+ var addSavedRevision = function(position, info) {
+ var newSavedRevision = $('<div></div>');
+ newSavedRevision.addClass("star");
+
+ newSavedRevision.attr('pos', position);
+ newSavedRevision.css('position', 'absolute');
+ newSavedRevision.css('left', (position * ($("#ui-slider-bar").width()-2) / (sliderLength * 1.0)) - 1);
+ $("#timeslider-slider").append(newSavedRevision);
+ newSavedRevision.mouseup(function(evt) {
+ BroadcastSlider.setSliderPosition(position);
+ });
+ savedRevisions.push(newSavedRevision);
+ };
+
+ var removeSavedRevision = function (position) {
+ var element = $("div.star [pos="+position+"]");
+ savedRevisions.remove(element);
+ element.remove();
+ return element;
+ };
+
+ /* Begin small 'API' */
+ function onSlider(callback) {
+ slidercallbacks.push(callback);
+ }
+
+ function getSliderPosition() {
+ return sliderPos;
+ }
+
+ function setSliderPosition(newpos) {
+ newpos = Number(newpos);
+ if(newpos < 0 || newpos > sliderLength) return;
+ $("#ui-slider-handle").css('left', newpos * ($("#ui-slider-bar").width()-2) / (sliderLength * 1.0));
+ $("a.tlink").map(function() {
+ $(this).attr('href', $(this).attr('thref').replace("%revision%", newpos));
+ });
+ $("#revision_label").html("Version " + newpos);
+
+ if(newpos == 0) {
+ $("#leftstar").css('opacity', .5);
+ $("#leftstep").css('opacity', .5);
+ } else {
+ $("#leftstar").css('opacity', 1);
+ $("#leftstep").css('opacity', 1);
+ }
+
+ if(newpos == sliderLength) {
+ $("#rightstar").css('opacity', .5);
+ $("#rightstep").css('opacity', .5);
+ } else {
+ $("#rightstar").css('opacity', 1);
+ $("#rightstep").css('opacity', 1);
+ }
+
+ sliderPos = newpos;
+ _callSliderCallbacks(newpos);
+ }
+
+ function getSliderLength() {
+ return sliderLength;
+ }
+
+ function setSliderLength(newlength) {
+ sliderLength = newlength;
+ updateSliderElements();
+ }
+
+ // just take over the whole slider screen with a reconnect message
+ function showReconnectUI() {
+ if(!clientVars.sliderEnabled || !clientVars.supportsSlider) {
+ $("#padmain, #rightbars").css('top', "95px");
+ $("#timeslider").show();
+ }
+ $('#error').show();
+ }
+
+ function setAuthors(authors) {
+ $("#authorstable").empty();
+ var numAnonymous = 0;
+ var numNamed = 0;
+ authors.forEach(function(author) {
+ if(author.name) {
+ numNamed ++;
+ var tr = $('<tr></tr>');
+ var swatchtd = $('<td></td>');
+ var swatch = $('<div class="swatch"></div>');
+ swatch.css('background-color', clientVars.colorPalette[author.colorId]);
+ swatchtd.append(swatch);
+ tr.append(swatchtd);
+ var nametd = $('<td></td>');
+ nametd.text(author.name || "unnamed");
+ tr.append(nametd);
+ $("#authorstable").append(tr);
+ } else {
+ numAnonymous ++;
+ }
+ });
+ if(numAnonymous > 0) {
+ var html = "<tr><td colspan=\"2\" style=\"color:#999; padding-left: 10px\">"+(numNamed>0?"...and ":"")+numAnonymous+" unnamed author"+(numAnonymous>1?"s":"")+"</td></tr>";
+ $("#authorstable").append($(html));
+ } if(authors.length == 0) {
+ $("#authorstable").append($("<tr><td colspan=\"2\" style=\"color:#999; padding-left: 10px\">No Authors</td></tr>"))
+ }
+ }
+
+ global.BroadcastSlider = {
+ onSlider: onSlider,
+ getSliderPosition: getSliderPosition,
+ setSliderPosition: setSliderPosition,
+ getSliderLength: getSliderLength,
+ setSliderLength: setSliderLength,
+ isSliderActive: function() {return sliderActive;},
+ playpause: playpause,
+ addSavedRevision: addSavedRevision,
+ showReconnectUI : showReconnectUI,
+ setAuthors: setAuthors
+ }
+
+ function playButtonUpdater() {
+ if(sliderPlaying) {
+ if(getSliderPosition()+1 > sliderLength) {
+ $("#playpause_button_icon").toggleClass('pause');
+ sliderPlaying = false;
+ return;
+ }
+ setSliderPosition(getSliderPosition()+1);
+
+ setTimeout(playButtonUpdater, 100);
+ }
+ }
+
+ function playpause() {
+ $("#playpause_button_icon").toggleClass('pause');
+
+ if(!sliderPlaying) {
+ if(getSliderPosition() == sliderLength)
+ setSliderPosition(0);
+ sliderPlaying = true;
+ playButtonUpdater();
+ } else {
+ sliderPlaying = false;
+ }
+ }
+
+ // assign event handlers to html UI elements after page load
+ $(window).load(function() {
+ disableSelection($("#playpause_button")[0]);
+ disableSelection($("#timeslider")[0]);
+
+ if(clientVars.sliderEnabled && clientVars.supportsSlider) {
+ $(document).keyup(function(e) {
+ var code = -1;
+ if (!e) var e = window.event;
+ if (e.keyCode) code = e.keyCode;
+ else if (e.which) code = e.which;
+
+ if(code == 37) { // left
+ if(!e.shiftKey) {
+ setSliderPosition(getSliderPosition() - 1);
+ } else {
+ var nextStar = 0; // default to first revision in document
+ for(var i=0; i<savedRevisions.length; i++) {
+ var pos = parseInt(savedRevisions[i].attr('pos'));
+ if(pos < getSliderPosition() && nextStar < pos)
+ nextStar = pos;
+ }
+ setSliderPosition(nextStar);
+ }
+ } else if(code == 39) {
+ if(!e.shiftKey) {
+ setSliderPosition(getSliderPosition() + 1);
+ } else {
+ var nextStar = sliderLength; // default to last revision in document
+ for(var i=0; i<savedRevisions.length; i++) {
+ var pos = parseInt(savedRevisions[i].attr('pos'));
+ if(pos > getSliderPosition() && nextStar > pos)
+ nextStar = pos;
+ }
+ setSliderPosition(nextStar);
+ }
+ } else if(code == 32)
+ playpause();
+
+ });
+ }
+
+ $(window).resize(function() {
+ updateSliderElements();
+ });
+
+ $("#ui-slider-bar").mousedown(function(evt) {
+ setSliderPosition(Math.floor((evt.clientX-$("#ui-slider-bar").offset().left) * sliderLength / 742));
+ $("#ui-slider-handle").css('left', (evt.clientX-$("#ui-slider-bar").offset().left));
+ $("#ui-slider-handle").trigger(evt);
+ });
+
+ // Slider dragging
+ $("#ui-slider-handle").mousedown(function(evt) {
+ this.startLoc = evt.clientX;
+ this.currentLoc = parseInt($(this).css('left'));
+ var self = this;
+ sliderActive = true;
+ $(document).mousemove(function(evt2) {
+ $(self).css('pointer', 'move')
+ var newloc = self.currentLoc + (evt2.clientX - self.startLoc);
+ if(newloc < 0) newloc = 0;
+ if(newloc > ($("#ui-slider-bar").width()-2)) newloc = ($("#ui-slider-bar").width()-2);
+ $("#revision_label").html("Version " + Math.floor(newloc * sliderLength / ($("#ui-slider-bar").width()-2)));
+ $(self).css('left', newloc);
+ if(getSliderPosition() != Math.floor(newloc * sliderLength / ($("#ui-slider-bar").width()-2)))
+ _callSliderCallbacks(Math.floor(newloc * sliderLength / ($("#ui-slider-bar").width()-2)))
+ });
+ $(document).mouseup(function(evt2) {
+ $(document).unbind('mousemove');
+ $(document).unbind('mouseup');
+ sliderActive = false;
+ var newloc = self.currentLoc + (evt2.clientX - self.startLoc);
+ if(newloc < 0) newloc = 0;
+ if(newloc > ($("#ui-slider-bar").width()-2)) newloc = ($("#ui-slider-bar").width()-2);
+ $(self).css('left', newloc);
+ // if(getSliderPosition() != Math.floor(newloc * sliderLength / ($("#ui-slider-bar").width()-2)))
+ setSliderPosition(Math.floor(newloc * sliderLength / ($("#ui-slider-bar").width()-2)))
+ self.currentLoc = parseInt($(self).css('left'));
+ });
+ })
+
+ // play/pause toggling
+ $("#playpause_button").mousedown(function(evt) {
+ var self = this;
+
+ $(self).css('background-image', 'url(/static/img/pad/timeslider/crushed_button_depressed.png)');
+ $(self).mouseup(function(evt2) {
+ $(self).css('background-image', 'url(/static/img/pad/timeslider/crushed_button_undepressed.png)');
+ $(self).unbind('mouseup');
+ BroadcastSlider.playpause();
+ });
+ $(document).mouseup(function(evt2) {
+ $(self).css('background-image', 'url(/static/img/pad/timeslider/crushed_button_undepressed.png)');
+ $(document).unbind('mouseup');
+ });
+ });
+
+ // next/prev saved revision and changeset
+ $('.stepper').mousedown(function(evt) {
+ var self = this;
+ var origcss = $(self).css('background-position');
+ if (! origcss) {
+ origcss = $(self).css('background-position-x')+" "+$(self).css('background-position-y');
+ }
+ var origpos = parseInt(origcss.split(" ")[1]);
+ var newpos = (origpos - 43);
+ if(newpos < 0) newpos += 87;
+
+ var newcss = (origcss.split(" ")[0] + " " + newpos + "px");
+ if($(self).css('opacity') != 1.0)
+ newcss = origcss;
+
+ $(self).css('background-position', newcss)
+
+ $(self).mouseup(function(evt2) {
+ $(self).css('background-position',origcss);
+ $(self).unbind('mouseup');
+ $(document).unbind('mouseup');
+ if($(self).attr("id") == ("leftstep")) {
+ setSliderPosition(getSliderPosition() - 1);
+ }
+ else if($(self).attr("id") == ("rightstep")) {
+ setSliderPosition(getSliderPosition() + 1);
+ }
+ else if($(self).attr("id") == ("leftstar")) {
+ var nextStar = 0; // default to first revision in document
+ for(var i=0; i<savedRevisions.length; i++) {
+ var pos = parseInt(savedRevisions[i].attr('pos'));
+ if(pos < getSliderPosition() && nextStar < pos)
+ nextStar = pos;
+ }
+ setSliderPosition(nextStar);
+ }
+ else if($(self).attr("id") == ("rightstar")) {
+ var nextStar = sliderLength; // default to last revision in document
+ for(var i=0; i<savedRevisions.length; i++) {
+ var pos = parseInt(savedRevisions[i].attr('pos'));
+ if(pos > getSliderPosition() && nextStar > pos)
+ nextStar = pos;
+ }
+ setSliderPosition(nextStar);
+ }
+ });
+ $(document).mouseup(function(evt2) {
+ $(self).css('background-position',origcss);
+ $(self).unbind('mouseup');
+ $(document).unbind('mouseup');
+ });
+ })
+
+ if(clientVars) {
+ if(clientVars.fullWidth) {
+ $("#padpage").css('width', '100%');
+ $("#revision").css('position', "absolute")
+ $("#revision").css('right', "20px")
+ $("#revision").css('top', "20px")
+ $("#padmain").css('left', '0px');
+ $("#padmain").css('right', '197px');
+ $("#padmain").css('width', 'auto');
+ $("#rightbars").css('right', '7px');
+ $("#rightbars").css('margin-right', '0px');
+ $("#timeslider").css('width', 'auto');
+ }
+
+ if(clientVars.disableRightBar) {
+ $("#rightbars").css('display', 'none');
+ $('#padmain').css('width', 'auto');
+ if(clientVars.fullWidth)
+ $("#padmain").css('right', '7px');
+ else
+ $("#padmain").css('width', '860px');
+ $("#revision").css('position', "absolute");
+ $("#revision").css('right', "20px");
+ $("#revision").css('top', "20px");
+ }
+
+
+ if(clientVars.sliderEnabled) {
+ if(clientVars.supportsSlider) {
+ $("#padmain, #rightbars").css('top', "95px");
+ $("#timeslider").show();
+ setSliderLength(clientVars.totalRevs);
+ setSliderPosition(clientVars.revNum);
+ clientVars.savedRevisions.forEach(function(revision) {
+ addSavedRevision(revision.revNum, revision);
+ })
+ } else {
+ // slider is not supported
+ $("#padmain, #rightbars").css('top', "95px");
+ $("#timeslider").show();
+ $("#error").html("The timeslider feature is not supported on this pad. <a href=\"/ep/about/faq#disabledslider\">Why not?</a>");
+ $("#error").show();
+ }
+ } else {
+ if(clientVars.supportsSlider) {
+ setSliderLength(clientVars.totalRevs);
+ setSliderPosition(clientVars.revNum);
+ }
+ }
+ }
+ });
+})();
+
+BroadcastSlider.onSlider(function(loc) {
+ $("#viewlatest").html(loc==BroadcastSlider.getSliderLength()?"Viewing latest content":"View latest content");
+})
diff --git a/trunk/etherpad/src/static/js/collab_client.js b/trunk/etherpad/src/static/js/collab_client.js
new file mode 100644
index 0000000..d8834d7
--- /dev/null
+++ b/trunk/etherpad/src/static/js/collab_client.js
@@ -0,0 +1,628 @@
+/**
+ * Copyright 2009 Google 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.
+ */
+
+$(window).bind("load", function() {
+ getCollabClient.windowLoaded = true;
+});
+
+/** Call this when the document is ready, and a new Ace2Editor() has been created and inited.
+ ACE's ready callback does not need to have fired yet.
+ "serverVars" are from calling doc.getCollabClientVars() on the server. */
+function getCollabClient(ace2editor, serverVars, initialUserInfo, options) {
+ var editor = ace2editor;
+
+ var rev = serverVars.rev;
+ var padId = serverVars.padId;
+ var globalPadId = serverVars.globalPadId;
+
+ var state = "IDLE";
+ var stateMessage;
+ var stateMessageSocketId;
+ var channelState = "CONNECTING";
+ var appLevelDisconnectReason = null;
+
+ var lastCommitTime = 0;
+ var initialStartConnectTime = 0;
+
+ var userId = initialUserInfo.userId;
+ var socketId;
+ var socket;
+ var userSet = {}; // userId -> userInfo
+ userSet[userId] = initialUserInfo;
+
+ var reconnectTimes = [];
+ var caughtErrors = [];
+ var caughtErrorCatchers = [];
+ var caughtErrorTimes = [];
+ var debugMessages = [];
+
+ tellAceAboutHistoricalAuthors(serverVars.historicalAuthorData);
+ tellAceActiveAuthorInfo(initialUserInfo);
+
+ var callbacks = {
+ onUserJoin: function() {},
+ onUserLeave: function() {},
+ onUpdateUserInfo: function() {},
+ onChannelStateChange: function() {},
+ onClientMessage: function() {},
+ onInternalAction: function() {},
+ onConnectionTrouble: function() {},
+ onServerMessage: function() {}
+ };
+
+ $(window).bind("unload", function() {
+ if (socket) {
+ socket.onclosed = function() {};
+ socket.onhiccup = function() {};
+ socket.disconnect(true);
+ }
+ });
+ if ($.browser.mozilla) {
+ // Prevent "escape" from taking effect and canceling a comet connection;
+ // doesn't work if focus is on an iframe.
+ $(window).bind("keydown", function(evt) { if (evt.which == 27) { evt.preventDefault() } });
+ }
+
+ editor.setProperty("userAuthor", userId);
+ editor.setBaseAttributedText(serverVars.initialAttributedText, serverVars.apool);
+ editor.setUserChangeNotificationCallback(wrapRecordingErrors("handleUserChanges", handleUserChanges));
+
+ function abandonConnection(reason) {
+ if (socket) {
+ socket.onclosed = function() {};
+ socket.onhiccup = function() {};
+ socket.disconnect();
+ }
+ socket = null;
+ setChannelState("DISCONNECTED", reason);
+ }
+
+ function dmesg(str) {
+ if (typeof window.ajlog == "string") window.ajlog += str+'\n';
+ debugMessages.push(str);
+ }
+
+ function handleUserChanges() {
+ if ((! socket) || channelState == "CONNECTING") {
+ if (channelState == "CONNECTING" && (((+new Date()) - initialStartConnectTime) > 20000)) {
+ abandonConnection("initsocketfail"); // give up
+ }
+ else {
+ // check again in a bit
+ setTimeout(wrapRecordingErrors("setTimeout(handleUserChanges)", handleUserChanges),
+ 1000);
+ }
+ return;
+ }
+
+ var t = (+new Date());
+
+ if (state != "IDLE") {
+ if (state == "COMMITTING" && (t - lastCommitTime) > 20000) {
+ // a commit is taking too long
+ appLevelDisconnectReason = "slowcommit";
+ socket.disconnect();
+ }
+ else if (state == "COMMITTING" && (t - lastCommitTime) > 5000) {
+ callbacks.onConnectionTrouble("SLOW");
+ }
+ else {
+ // run again in a few seconds, to detect a disconnect
+ setTimeout(wrapRecordingErrors("setTimeout(handleUserChanges)", handleUserChanges),
+ 3000);
+ }
+ return;
+ }
+
+ var earliestCommit = lastCommitTime + 500;
+ if (t < earliestCommit) {
+ setTimeout(wrapRecordingErrors("setTimeout(handleUserChanges)", handleUserChanges),
+ earliestCommit - t);
+ return;
+ }
+
+ var sentMessage = false;
+ var userChangesData = editor.prepareUserChangeset();
+ if (userChangesData.changeset) {
+ lastCommitTime = t;
+ state = "COMMITTING";
+ stateMessage = {type:"USER_CHANGES", baseRev:rev,
+ changeset:userChangesData.changeset,
+ apool: userChangesData.apool };
+ stateMessageSocketId = socketId;
+ sendMessage(stateMessage);
+ sentMessage = true;
+ callbacks.onInternalAction("commitPerformed");
+ }
+
+ if (sentMessage) {
+ // run again in a few seconds, to detect a disconnect
+ setTimeout(wrapRecordingErrors("setTimeout(handleUserChanges)", handleUserChanges),
+ 3000);
+ }
+ }
+
+ function getStats() {
+ var stats = {};
+
+ stats.screen = [$(window).width(), $(window).height(),
+ window.screen.availWidth, window.screen.availHeight,
+ window.screen.width, window.screen.height].join(',');
+ stats.ip = serverVars.clientIp;
+ stats.useragent = serverVars.clientAgent;
+
+ return stats;
+ }
+
+ function setUpSocket() {
+ var success = false;
+ callCatchingErrors("setUpSocket", function() {
+ appLevelDisconnectReason = null;
+
+ var oldSocketId = socketId;
+ socketId = String(Math.floor(Math.random()*1e12));
+ socket = new WebSocket(socketId);
+ socket.onmessage = wrapRecordingErrors("socket.onmessage", handleMessageFromServer);
+ socket.onclosed = wrapRecordingErrors("socket.onclosed", handleSocketClosed);
+ socket.onopen = wrapRecordingErrors("socket.onopen", function() {
+ hiccupCount = 0;
+ setChannelState("CONNECTED");
+ var msg = { type:"CLIENT_READY", roomType:'padpage',
+ roomName:'padpage/'+globalPadId,
+ data: {
+ lastRev:rev,
+ userInfo:userSet[userId],
+ stats: getStats() } };
+ if (oldSocketId) {
+ msg.data.isReconnectOf = oldSocketId;
+ msg.data.isCommitPending = (state == "COMMITTING");
+ }
+ sendMessage(msg);
+ doDeferredActions();
+ });
+ socket.onhiccup = wrapRecordingErrors("socket.onhiccup", handleCometHiccup);
+ socket.onlogmessage = dmesg;
+ socket.connect();
+ success = true;
+ });
+ if (success) {
+ initialStartConnectTime = +new Date();
+ }
+ else {
+ abandonConnection("initsocketfail");
+ }
+ }
+ function setUpSocketWhenWindowLoaded() {
+ if (getCollabClient.windowLoaded) {
+ setUpSocket();
+ }
+ else {
+ setTimeout(setUpSocketWhenWindowLoaded, 200);
+ }
+ }
+ setTimeout(setUpSocketWhenWindowLoaded, 0);
+
+ var hiccupCount = 0;
+ function handleCometHiccup(params) {
+ dmesg("HICCUP (connected:"+(!!params.connected)+")");
+ var connectedNow = params.connected;
+ if (! connectedNow) {
+ hiccupCount++;
+ // skip first "cut off from server" notification
+ if (hiccupCount > 1) {
+ setChannelState("RECONNECTING");
+ }
+ }
+ else {
+ hiccupCount = 0;
+ setChannelState("CONNECTED");
+ }
+ }
+
+ function sendMessage(msg) {
+ socket.postMessage(JSON.stringify({type: "COLLABROOM", data: msg}));
+ }
+
+ function wrapRecordingErrors(catcher, func) {
+ return function() {
+ try {
+ return func.apply(this, Array.prototype.slice.call(arguments));
+ }
+ catch (e) {
+ caughtErrors.push(e);
+ caughtErrorCatchers.push(catcher);
+ caughtErrorTimes.push(+new Date());
+ //console.dir({catcher: catcher, e: e});
+ throw e;
+ }
+ };
+ }
+
+ function callCatchingErrors(catcher, func) {
+ try {
+ wrapRecordingErrors(catcher, func)();
+ }
+ catch (e) { /*absorb*/ }
+ }
+
+ function handleMessageFromServer(evt) {
+ if (! socket) return;
+ if (! evt.data) return;
+ var wrapper = JSON.parse(evt.data);
+ if(wrapper.type != "COLLABROOM") return;
+ var msg = wrapper.data;
+ if (msg.type == "NEW_CHANGES") {
+ var newRev = msg.newRev;
+ var changeset = msg.changeset;
+ var author = (msg.author || '');
+ var apool = msg.apool;
+ if (newRev != (rev+1)) {
+ dmesg("bad message revision on NEW_CHANGES: "+newRev+" not "+(rev+1));
+ socket.disconnect();
+ return;
+ }
+ rev = newRev;
+ editor.applyChangesToBase(changeset, author, apool);
+ }
+ else if (msg.type == "ACCEPT_COMMIT") {
+ var newRev = msg.newRev;
+ if (newRev != (rev+1)) {
+ dmesg("bad message revision on ACCEPT_COMMIT: "+newRev+" not "+(rev+1));
+ socket.disconnect();
+ return;
+ }
+ rev = newRev;
+ editor.applyPreparedChangesetToBase();
+ setStateIdle();
+ callCatchingErrors("onInternalAction", function() {
+ callbacks.onInternalAction("commitAcceptedByServer");
+ });
+ callCatchingErrors("onConnectionTrouble", function() {
+ callbacks.onConnectionTrouble("OK");
+ });
+ handleUserChanges();
+ }
+ else if (msg.type == "NO_COMMIT_PENDING") {
+ if (state == "COMMITTING") {
+ // server missed our commit message; abort that commit
+ setStateIdle();
+ handleUserChanges();
+ }
+ }
+ else if (msg.type == "USER_NEWINFO") {
+ var userInfo = msg.userInfo;
+ var id = userInfo.userId;
+ if (userSet[id]) {
+ userSet[id] = userInfo;
+ callbacks.onUpdateUserInfo(userInfo);
+ dmesgUsers();
+ }
+ else {
+ userSet[id] = userInfo;
+ callbacks.onUserJoin(userInfo);
+ dmesgUsers();
+ }
+ tellAceActiveAuthorInfo(userInfo);
+ }
+ else if (msg.type == "USER_LEAVE") {
+ var userInfo = msg.userInfo;
+ var id = userInfo.userId;
+ if (userSet[id]) {
+ delete userSet[userInfo.userId];
+ fadeAceAuthorInfo(userInfo);
+ callbacks.onUserLeave(userInfo);
+ dmesgUsers();
+ }
+ }
+ else if (msg.type == "DISCONNECT_REASON") {
+ appLevelDisconnectReason = msg.reason;
+ }
+ else if (msg.type == "CLIENT_MESSAGE") {
+ callbacks.onClientMessage(msg.payload);
+ }
+ else if (msg.type == "SERVER_MESSAGE") {
+ callbacks.onServerMessage(msg.payload);
+ }
+ }
+ function updateUserInfo(userInfo) {
+ userInfo.userId = userId;
+ userSet[userId] = userInfo;
+ tellAceActiveAuthorInfo(userInfo);
+ if (! socket) return;
+ sendMessage({type: "USERINFO_UPDATE", userInfo:userInfo});
+ }
+
+ function tellAceActiveAuthorInfo(userInfo) {
+ tellAceAuthorInfo(userInfo.userId, userInfo.colorId);
+ }
+ function tellAceAuthorInfo(userId, colorId, inactive) {
+ if (colorId || (typeof colorId) == "number") {
+ colorId = Number(colorId);
+ if (options && options.colorPalette && options.colorPalette[colorId]) {
+ var cssColor = options.colorPalette[colorId];
+ if (inactive) {
+ editor.setAuthorInfo(userId, {bgcolor: cssColor, fade: 0.5});
+ }
+ else {
+ editor.setAuthorInfo(userId, {bgcolor: cssColor});
+ }
+ }
+ }
+ }
+ function fadeAceAuthorInfo(userInfo) {
+ tellAceAuthorInfo(userInfo.userId, userInfo.colorId, true);
+ }
+
+ function getConnectedUsers() {
+ return valuesArray(userSet);
+ }
+
+ function tellAceAboutHistoricalAuthors(hadata) {
+ for(var author in hadata) {
+ var data = hadata[author];
+ if (! userSet[author]) {
+ tellAceAuthorInfo(author, data.colorId, true);
+ }
+ }
+ }
+
+ function dmesgUsers() {
+ //pad.dmesg($.map(getConnectedUsers(), function(u) { return u.userId.slice(-2); }).join(','));
+ }
+
+ function handleSocketClosed(params) {
+ socket = null;
+
+ $.each(keys(userSet), function() {
+ var uid = String(this);
+ if (uid != userId) {
+ var userInfo = userSet[uid];
+ delete userSet[uid];
+ callbacks.onUserLeave(userInfo);
+ dmesgUsers();
+ }
+ });
+
+ var reason = appLevelDisconnectReason || params.reason;
+ var shouldReconnect = params.reconnect;
+ if (shouldReconnect) {
+
+ // determine if this is a tight reconnect loop due to weird connectivity problems
+ reconnectTimes.push(+new Date());
+ var TOO_MANY_RECONNECTS = 8;
+ var TOO_SHORT_A_TIME_MS = 10000;
+ if (reconnectTimes.length >= TOO_MANY_RECONNECTS &&
+ ((+new Date()) - reconnectTimes[reconnectTimes.length-TOO_MANY_RECONNECTS]) <
+ TOO_SHORT_A_TIME_MS) {
+ setChannelState("DISCONNECTED", "looping");
+ }
+ else {
+ setChannelState("RECONNECTING", reason);
+ setUpSocket();
+ }
+
+ }
+ else {
+ setChannelState("DISCONNECTED", reason);
+ }
+ }
+
+ function setChannelState(newChannelState, moreInfo) {
+ if (newChannelState != channelState) {
+ channelState = newChannelState;
+ callbacks.onChannelStateChange(channelState, moreInfo);
+ }
+ }
+
+ function keys(obj) {
+ var array = [];
+ $.each(obj, function (k, v) { array.push(k); });
+ return array;
+ }
+ function valuesArray(obj) {
+ var array = [];
+ $.each(obj, function (k, v) { array.push(v); });
+ return array;
+ }
+
+ // We need to present a working interface even before the socket
+ // is connected for the first time.
+ var deferredActions = [];
+ function defer(func, tag) {
+ return function() {
+ var that = this;
+ var args = arguments;
+ function action() {
+ func.apply(that, args);
+ }
+ action.tag = tag;
+ if (channelState == "CONNECTING") {
+ deferredActions.push(action);
+ }
+ else {
+ action();
+ }
+ }
+ }
+ function doDeferredActions(tag) {
+ var newArray = [];
+ for(var i=0;i<deferredActions.length;i++) {
+ var a = deferredActions[i];
+ if ((!tag) || (tag == a.tag)) {
+ a();
+ }
+ else {
+ newArray.push(a);
+ }
+ }
+ deferredActions = newArray;
+ }
+
+ function sendClientMessage(msg) {
+ sendMessage({ type: "CLIENT_MESSAGE", payload: msg });
+ }
+
+ function getCurrentRevisionNumber() {
+ return rev;
+ }
+
+ function getDiagnosticInfo() {
+ var maxCaughtErrors = 3;
+ var maxAceErrors = 3;
+ var maxDebugMessages = 50;
+ var longStringCutoff = 500;
+
+ function trunc(str) {
+ return String(str).substring(0, longStringCutoff);
+ }
+
+ var info = { errors: {length: 0} };
+ function addError(e, catcher, time) {
+ var error = {catcher:catcher};
+ if (time) error.time = time;
+
+ // a little over-cautious?
+ try { if (e.description) error.description = e.description; } catch (x) {}
+ try { if (e.fileName) error.fileName = e.fileName; } catch (x) {}
+ try { if (e.lineNumber) error.lineNumber = e.lineNumber; } catch (x) {}
+ try { if (e.message) error.message = e.message; } catch (x) {}
+ try { if (e.name) error.name = e.name; } catch (x) {}
+ try { if (e.number) error.number = e.number; } catch (x) {}
+ try { if (e.stack) error.stack = trunc(e.stack); } catch (x) {}
+
+ info.errors[info.errors.length] = error;
+ info.errors.length++;
+ }
+ for(var i=0; ((i<caughtErrors.length) && (i<maxCaughtErrors)); i++) {
+ addError(caughtErrors[i], caughtErrorCatchers[i], caughtErrorTimes[i]);
+ }
+ if (editor) {
+ var aceErrors = editor.getUnhandledErrors();
+ for(var i=0; ((i<aceErrors.length) && (i<maxAceErrors)) ;i++) {
+ var errorRecord = aceErrors[i];
+ addError(errorRecord.error, "ACE", errorRecord.time);
+ }
+ }
+
+ info.time = +new Date();
+ info.collabState = state;
+ info.channelState = channelState;
+ info.lastCommitTime = lastCommitTime;
+ info.numSocketReconnects = reconnectTimes.length;
+ info.userId = userId;
+ info.currentRev = rev;
+ info.participants = (function() {
+ var pp = [];
+ for(var u in userSet) {
+ pp.push(u);
+ }
+ return pp.join(',');
+ })();
+
+ if (debugMessages.length > maxDebugMessages) {
+ debugMessages = debugMessages.slice(debugMessages.length-maxDebugMessages,
+ debugMessages.length);
+ }
+
+ info.debugMessages = {length: 0};
+ for(var i=0;i<debugMessages.length;i++) {
+ info.debugMessages[i] = trunc(debugMessages[i]);
+ info.debugMessages.length++;
+ }
+
+ return info;
+ }
+
+ function getMissedChanges() {
+ var obj = {};
+ obj.userInfo = userSet[userId];
+ obj.baseRev = rev;
+ if (state == "COMMITTING" && stateMessage) {
+ obj.committedChangeset = stateMessage.changeset;
+ obj.committedChangesetAPool = stateMessage.apool;
+ obj.committedChangesetSocketId = stateMessageSocketId;
+ editor.applyPreparedChangesetToBase();
+ }
+ var userChangesData = editor.prepareUserChangeset();
+ if (userChangesData.changeset) {
+ obj.furtherChangeset = userChangesData.changeset;
+ obj.furtherChangesetAPool = userChangesData.apool;
+ }
+ return obj;
+ }
+
+ function setStateIdle() {
+ state = "IDLE";
+ callbacks.onInternalAction("newlyIdle");
+ schedulePerhapsCallIdleFuncs();
+ }
+
+ function callWhenNotCommitting(func) {
+ idleFuncs.push(func);
+ schedulePerhapsCallIdleFuncs();
+ }
+
+ var idleFuncs = [];
+ function schedulePerhapsCallIdleFuncs() {
+ setTimeout(function() {
+ if (state == "IDLE") {
+ while (idleFuncs.length > 0) {
+ var f = idleFuncs.shift();
+ f();
+ }
+ }
+ }, 0);
+ }
+
+ var self;
+ return (self = {
+ setOnUserJoin: function(cb) { callbacks.onUserJoin = cb; },
+ setOnUserLeave: function(cb) { callbacks.onUserLeave = cb; },
+ setOnUpdateUserInfo: function(cb) { callbacks.onUpdateUserInfo = cb; },
+ setOnChannelStateChange: function(cb) { callbacks.onChannelStateChange = cb; },
+ setOnClientMessage: function(cb) { callbacks.onClientMessage = cb; },
+ setOnInternalAction: function(cb) { callbacks.onInternalAction = cb; },
+ setOnConnectionTrouble: function(cb) { callbacks.onConnectionTrouble = cb; },
+ setOnServerMessage: function(cb) { callbacks.onServerMessage = cb; },
+ updateUserInfo: defer(updateUserInfo),
+ getConnectedUsers: getConnectedUsers,
+ sendClientMessage: sendClientMessage,
+ getCurrentRevisionNumber: getCurrentRevisionNumber,
+ getDiagnosticInfo: getDiagnosticInfo,
+ getMissedChanges: getMissedChanges,
+ callWhenNotCommitting: callWhenNotCommitting,
+ addHistoricalAuthors: tellAceAboutHistoricalAuthors
+ });
+}
+
+function selectElementContents(elem) {
+ if ($.browser.msie) {
+ var range = document.body.createTextRange();
+ range.moveToElementText(elem);
+ range.select();
+ }
+ else {
+ if (window.getSelection) {
+ var browserSelection = window.getSelection();
+ if (browserSelection) {
+ var range = document.createRange();
+ range.selectNodeContents(elem);
+ browserSelection.removeAllRanges();
+ browserSelection.addRange(range);
+ }
+ }
+ }
+}
diff --git a/trunk/etherpad/src/static/js/colorutils.js b/trunk/etherpad/src/static/js/colorutils.js
new file mode 100644
index 0000000..e745f8e
--- /dev/null
+++ b/trunk/etherpad/src/static/js/colorutils.js
@@ -0,0 +1,91 @@
+// DO NOT EDIT THIS FILE, edit infrastructure/ace/www/colorutils.js
+
+/**
+ * Copyright 2009 Google 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.
+ */
+
+var colorutils = {};
+
+// "#ffffff" or "#fff" or "ffffff" or "fff" to [1.0, 1.0, 1.0]
+colorutils.css2triple = function(cssColor) {
+ var sixHex = colorutils.css2sixhex(cssColor);
+ function hexToFloat(hh) {
+ return Number("0x"+hh)/255;
+ }
+ return [hexToFloat(sixHex.substr(0,2)),
+ hexToFloat(sixHex.substr(2,2)),
+ hexToFloat(sixHex.substr(4,2))];
+}
+
+// "#ffffff" or "#fff" or "ffffff" or "fff" to "ffffff"
+colorutils.css2sixhex = function(cssColor) {
+ var h = /[0-9a-fA-F]+/.exec(cssColor)[0];
+ if (h.length != 6) {
+ var a = h.charAt(0);
+ var b = h.charAt(1);
+ var c = h.charAt(2);
+ h = a+a+b+b+c+c;
+ }
+ return h;
+}
+
+// [1.0, 1.0, 1.0] -> "#ffffff"
+colorutils.triple2css = function(triple) {
+ function floatToHex(n) {
+ var n2 = colorutils.clamp(Math.round(n*255), 0, 255);
+ return ("0"+n2.toString(16)).slice(-2);
+ }
+ return "#" + floatToHex(triple[0]) +
+ floatToHex(triple[1]) + floatToHex(triple[2]);
+}
+
+
+colorutils.clamp = function(v,bot,top) { return v < bot ? bot : (v > top ? top : v); };
+colorutils.min3 = function(a,b,c) { return (a < b) ? (a < c ? a : c) : (b < c ? b : c); };
+colorutils.max3 = function(a,b,c) { return (a > b) ? (a > c ? a : c) : (b > c ? b : c); };
+colorutils.colorMin = function(c) { return colorutils.min3(c[0], c[1], c[2]); };
+colorutils.colorMax = function(c) { return colorutils.max3(c[0], c[1], c[2]); };
+colorutils.scale = function(v, bot, top) { return colorutils.clamp(bot + v*(top-bot), 0, 1); };
+colorutils.unscale = function(v, bot, top) { return colorutils.clamp((v-bot)/(top-bot), 0, 1); };
+
+colorutils.scaleColor = function(c, bot, top) {
+ return [colorutils.scale(c[0], bot, top),
+ colorutils.scale(c[1], bot, top),
+ colorutils.scale(c[2], bot, top)];
+}
+
+colorutils.unscaleColor = function(c, bot, top) {
+ return [colorutils.unscale(c[0], bot, top),
+ colorutils.unscale(c[1], bot, top),
+ colorutils.unscale(c[2], bot, top)];
+}
+
+colorutils.luminosity = function(c) {
+ // rule of thumb for RGB brightness; 1.0 is white
+ return c[0]*0.30 + c[1]*0.59 + c[2]*0.11;
+}
+
+colorutils.saturate = function(c) {
+ var min = colorutils.colorMin(c);
+ var max = colorutils.colorMax(c);
+ if (max - min <= 0) return [1.0, 1.0, 1.0];
+ return colorutils.unscaleColor(c, min, max);
+}
+
+colorutils.blend = function(c1, c2, t) {
+ return [colorutils.scale(t, c1[0], c2[0]),
+ colorutils.scale(t, c1[1], c2[1]),
+ colorutils.scale(t, c1[2], c2[2])];
+}
diff --git a/trunk/etherpad/src/static/js/confirmation.js b/trunk/etherpad/src/static/js/confirmation.js
new file mode 100644
index 0000000..a0f725c
--- /dev/null
+++ b/trunk/etherpad/src/static/js/confirmation.js
@@ -0,0 +1,21 @@
+/**
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS-IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+$(function() {
+ $('#shoppingform').submit(function() {
+ $('#contbutton').attr("disabled", true).attr("value", "Purchasing...");
+ });
+}) \ No newline at end of file
diff --git a/trunk/etherpad/src/static/js/connection_diagnostics.js b/trunk/etherpad/src/static/js/connection_diagnostics.js
new file mode 100644
index 0000000..cc43d46
--- /dev/null
+++ b/trunk/etherpad/src/static/js/connection_diagnostics.js
@@ -0,0 +1,126 @@
+/**
+ * Copyright 2009 Google 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.
+ */
+
+diagnostics = {};
+
+diagnostics.data = {};
+
+diagnostics.steps = [
+ ['init', "Initializing"],
+ ['examineBrowser', "Examining web browser"],
+ ['testStreaming', "Testing primary transport (streaming)"],
+ ['testPolling', "Testing secondary transport (polling)"],
+ ['testHiccups', "Testing connection hiccups"],
+ ['sendInfo', "Sending information"],
+ ['showResult', ""]
+];
+
+diagnostics.processNext = function(i) {
+ if (i < diagnostics.steps.length) {
+ var msg = "Step "+(i+1)+": "+diagnostics.steps[i][1]+"...";
+ $('#statusmsg').html(msg);
+ diagnostics[diagnostics.steps[i][0]](function() {
+ diagnostics.processNext(i+1);
+ });
+ }
+};
+
+$(document).ready(function() {
+ diagnostics.processNext(0);
+
+ var emailClicked = false;
+ $('#email').click(function() {
+ if (!emailClicked) {
+ $('#email').select();
+ emailClicked = true;
+ }
+ });
+
+ $('#emailsubmit').click(function() {
+ function err(m) {
+ $('#emailerrormsg').hide().html(m).fadeIn('fast');
+ }
+ var email = $('#email').val();
+ if (!etherpad.validEmail(email)) {
+ err("That doesn't look like a valid email address.");
+ return;
+ }
+ $.ajax({
+ type: 'post',
+ url: '/ep/connection-diagnostics/submitemail',
+ data: {email: email, diagnosticStorableId: clientVars.diagnosticStorableId},
+ success: success,
+ error: error
+ });
+ function success(responseText) {
+ if (responseText == "OK") {
+ $('#emailform').html("<p>Thanks! We will look at your case shortly.</p>");
+ } else {
+ err(responseText);
+ }
+ }
+ function error() {
+ err("There was an error processing your request.");
+ }
+ });
+});
+
+diagnostics.init = function(done) {
+ setTimeout(done, 1000);
+};
+
+diagnostics.examineBrowser = function(done) {
+ setTimeout(done, 1000);
+};
+
+diagnostics.testStreaming = function(done) {
+ setTimeout(done, 1000);
+};
+
+diagnostics.testPolling = function(done) {
+ setTimeout(done, 1000);
+};
+
+diagnostics.testHiccups = function(done) {
+ setTimeout(done, 1000);
+};
+
+diagnostics.sendInfo = function(done) {
+
+ // TODO(jd): remove these test data when you submit actual data.
+ diagnostics.data.test1 = "foo";
+ diagnostics.data.test2 = "bar";
+ diagnostics.data.testNested = {a: 1, b: 2, c: 3};
+
+ // send data object back to server.
+ $.ajax({
+ type: 'post',
+ url: '/ep/connection-diagnostics/submitdata',
+ data: {dataJson: JSON.stringify(diagnostics.data),
+ diagnosticStorableId: clientVars.diagnosticStorableId},
+ success: done,
+ error: function() { alert("There was an error submitting the diagnostic information to the server."); done(); }
+ });
+};
+
+diagnostics.showResult = function(done) {
+ $('#linkanimation').hide();
+ $('#statusmsg').html("<br/>Result: your browser and internet"
+ + " connection appear to be incompatibile with EtherPad.");
+ $('#statusmsg').css('color', '#520');
+ $('#emailform').show();
+};
+
diff --git a/trunk/etherpad/src/static/js/cssmanager_client.js b/trunk/etherpad/src/static/js/cssmanager_client.js
new file mode 100644
index 0000000..04ed641
--- /dev/null
+++ b/trunk/etherpad/src/static/js/cssmanager_client.js
@@ -0,0 +1,88 @@
+// DO NOT EDIT THIS FILE, edit infrastructure/ace/www/cssmanager.js
+
+/**
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS-IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+function makeCSSManager(emptyStylesheetTitle) {
+
+ function getSheetByTitle(title) {
+ var allSheets = document.styleSheets;
+ for(var i=0;i<allSheets.length;i++) {
+ var s = allSheets[i];
+ if (s.title == title) {
+ return s;
+ }
+ }
+ return null;
+ }
+
+ /*function getSheetTagByTitle(title) {
+ var allStyleTags = document.getElementsByTagName("style");
+ for(var i=0;i<allStyleTags.length;i++) {
+ var t = allStyleTags[i];
+ if (t.title == title) {
+ return t;
+ }
+ }
+ return null;
+ }*/
+
+ var browserSheet = getSheetByTitle(emptyStylesheetTitle);
+ //var browserTag = getSheetTagByTitle(emptyStylesheetTitle);
+ function browserRules() { return (browserSheet.cssRules || browserSheet.rules); }
+ function browserDeleteRule(i) {
+ if (browserSheet.deleteRule) browserSheet.deleteRule(i);
+ else browserSheet.removeRule(i);
+ }
+ function browserInsertRule(i, selector) {
+ if (browserSheet.insertRule) browserSheet.insertRule(selector+' {}', i);
+ else browserSheet.addRule(selector, null, i);
+ }
+ var selectorList = [];
+
+ function indexOfSelector(selector) {
+ for(var i=0;i<selectorList.length;i++) {
+ if (selectorList[i] == selector) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ function selectorStyle(selector) {
+ var i = indexOfSelector(selector);
+ if (i < 0) {
+ // add selector
+ browserInsertRule(0, selector);
+ selectorList.splice(0, 0, selector);
+ i = 0;
+ }
+ return browserRules().item(i).style;
+ }
+
+ function removeSelectorStyle(selector) {
+ var i = indexOfSelector(selector);
+ if (i >= 0) {
+ browserDeleteRule(i);
+ selectorList.splice(i, 1);
+ }
+ }
+
+ return {selectorStyle:selectorStyle, removeSelectorStyle:removeSelectorStyle,
+ info: function() {
+ return selectorList.length+":"+browserRules().length;
+ }};
+}
diff --git a/trunk/etherpad/src/static/js/domline_client.js b/trunk/etherpad/src/static/js/domline_client.js
new file mode 100644
index 0000000..de2e7d3
--- /dev/null
+++ b/trunk/etherpad/src/static/js/domline_client.js
@@ -0,0 +1,210 @@
+// DO NOT EDIT THIS FILE, edit infrastructure/ace/www/domline.js
+
+/**
+ * Copyright 2009 Google 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.
+ */
+
+var domline = {};
+domline.noop = function() {};
+domline.identity = function(x) { return x; };
+
+domline.addToLineClass = function(lineClass, cls) {
+ // an "empty span" at any point can be used to add classes to
+ // the line, using line:className. otherwise, we ignore
+ // the span.
+ cls.replace(/\S+/g, function (c) {
+ if (c.indexOf("line:") == 0) {
+ // add class to line
+ lineClass = (lineClass ? lineClass+' ' : '')+c.substring(5);
+ }
+ });
+ return lineClass;
+}
+
+// if "document" is falsy we don't create a DOM node, just
+// an object with innerHTML and className
+domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument) {
+ var result = { node: null,
+ appendSpan: domline.noop,
+ prepareForAdd: domline.noop,
+ notifyAdded: domline.noop,
+ clearSpans: domline.noop,
+ finishUpdate: domline.noop,
+ lineMarker: 0 };
+
+ var browser = (optBrowser || {});
+ var document = optDocument;
+
+ if (document) {
+ result.node = document.createElement("div");
+ }
+ else {
+ result.node = {innerHTML: '', className: ''};
+ }
+
+ var html = [];
+ var preHtml, postHtml;
+ var curHTML = null;
+ function processSpaces(s) {
+ return domline.processSpaces(s, doesWrap);
+ }
+ var identity = domline.identity;
+ var perTextNodeProcess = (doesWrap ? identity : processSpaces);
+ var perHtmlLineProcess = (doesWrap ? processSpaces : identity);
+ var lineClass = 'ace-line';
+ result.appendSpan = function(txt, cls) {
+ if (cls.indexOf('list') >= 0) {
+ var listType = /(?:^| )list:(\S+)/.exec(cls);
+ if (listType) {
+ listType = listType[1];
+ if (listType) {
+ preHtml = '<ul class="list-'+listType+'"><li>';
+ postHtml = '</li></ul>';
+ }
+ result.lineMarker += txt.length;
+ return; // don't append any text
+ }
+ }
+ var href = null;
+ var simpleTags = null;
+ if (cls.indexOf('url') >= 0) {
+ cls = cls.replace(/(^| )url:(\S+)/g, function(x0, space, url) {
+ href = url;
+ return space+"url";
+ });
+ }
+ if (cls.indexOf('tag') >= 0) {
+ cls = cls.replace(/(^| )tag:(\S+)/g, function(x0, space, tag) {
+ if (! simpleTags) simpleTags = [];
+ simpleTags.push(tag.toLowerCase());
+ return space+tag;
+ });
+ }
+ if ((! txt) && cls) {
+ lineClass = domline.addToLineClass(lineClass, cls);
+ }
+ else if (txt) {
+ var extraOpenTags = "";
+ var extraCloseTags = "";
+ if (href) {
+ extraOpenTags = extraOpenTags+'<a href="'+
+ href.replace(/\"/g, '&quot;')+'">';
+ extraCloseTags = '</a>'+extraCloseTags;
+ }
+ if (simpleTags) {
+ simpleTags.sort();
+ extraOpenTags = extraOpenTags+'<'+simpleTags.join('><')+'>';
+ simpleTags.reverse();
+ extraCloseTags = '</'+simpleTags.join('></')+'>'+extraCloseTags;
+ }
+ html.push('<span class="',cls||'','">',extraOpenTags,
+ perTextNodeProcess(domline.escapeHTML(txt)),
+ extraCloseTags,'</span>');
+ }
+ };
+ result.clearSpans = function() {
+ html = [];
+ lineClass = ''; // non-null to cause update
+ result.lineMarker = 0;
+ };
+ function writeHTML() {
+ var newHTML = perHtmlLineProcess(html.join(''));
+ if (! newHTML) {
+ if ((! document) || (! optBrowser)) {
+ newHTML += '&nbsp;';
+ }
+ else if (! browser.msie) {
+ newHTML += '<br/>';
+ }
+ }
+ if (nonEmpty) {
+ newHTML = (preHtml||'')+newHTML+(postHtml||'');
+ }
+ html = preHtml = postHtml = null; // free memory
+ if (newHTML !== curHTML) {
+ curHTML = newHTML;
+ result.node.innerHTML = curHTML;
+ }
+ if (lineClass !== null) result.node.className = lineClass;
+ }
+ result.prepareForAdd = writeHTML;
+ result.finishUpdate = writeHTML;
+ result.getInnerHTML = function() { return curHTML || ''; };
+
+ return result;
+};
+
+domline.escapeHTML = function(s) {
+ var re = /[&<>'"]/g; /']/; // stupid indentation thing
+ if (! re.MAP) {
+ // persisted across function calls!
+ re.MAP = {
+ '&': '&amp;',
+ '<': '&lt;',
+ '>': '&gt;',
+ '"': '&#34;',
+ "'": '&#39;'
+ };
+ }
+ return s.replace(re, function(c) { return re.MAP[c]; });
+};
+
+domline.processSpaces = function(s, doesWrap) {
+ if (s.indexOf("<") < 0 && ! doesWrap) {
+ // short-cut
+ return s.replace(/ /g, '&nbsp;');
+ }
+ var parts = [];
+ s.replace(/<[^>]*>?| |[^ <]+/g, function(m) { parts.push(m); });
+ if (doesWrap) {
+ var endOfLine = true;
+ var beforeSpace = false;
+ // last space in a run is normal, others are nbsp,
+ // end of line is nbsp
+ for(var i=parts.length-1;i>=0;i--) {
+ var p = parts[i];
+ if (p == " ") {
+ if (endOfLine || beforeSpace)
+ parts[i] = '&nbsp;';
+ endOfLine = false;
+ beforeSpace = true;
+ }
+ else if (p.charAt(0) != "<") {
+ endOfLine = false;
+ beforeSpace = false;
+ }
+ }
+ // beginning of line is nbsp
+ for(var i=0;i<parts.length;i++) {
+ var p = parts[i];
+ if (p == " ") {
+ parts[i] = '&nbsp;';
+ break;
+ }
+ else if (p.charAt(0) != "<") {
+ break;
+ }
+ }
+ }
+ else {
+ for(var i=0;i<parts.length;i++) {
+ var p = parts[i];
+ if (p == " ") {
+ parts[i] = '&nbsp;';
+ }
+ }
+ }
+ return parts.join('');
+};
diff --git a/trunk/etherpad/src/static/js/draggable.js b/trunk/etherpad/src/static/js/draggable.js
new file mode 100644
index 0000000..97a1a3d
--- /dev/null
+++ b/trunk/etherpad/src/static/js/draggable.js
@@ -0,0 +1,60 @@
+/**
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS-IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+function makeDraggable(jqueryNodes, eventHandler) {
+ jqueryNodes.each(function() {
+ var node = $(this);
+ var state = {};
+ var inDrag = false;
+ function dragStart(evt) {
+ if (inDrag) {
+ return;
+ }
+ inDrag = true;
+ if (eventHandler('dragstart', evt, state) !== false) {
+ $(document).bind('mousemove', dragUpdate);
+ $(document).bind('mouseup', dragEnd);
+ }
+ evt.preventDefault();
+ return false;
+ }
+ function dragUpdate(evt) {
+ if (! inDrag) {
+ return;
+ }
+ eventHandler('dragupdate', evt, state);
+ evt.preventDefault();
+ return false;
+ }
+ function dragEnd(evt) {
+ if (! inDrag) {
+ return;
+ }
+ inDrag = false;
+ try {
+ eventHandler('dragend', evt, state);
+ }
+ finally {
+ $(document).unbind('mousemove', dragUpdate);
+ $(document).unbind('mouseup', dragEnd);
+ evt.preventDefault();
+ }
+ return false;
+ }
+ node.bind('mousedown', dragStart);
+ });
+} \ No newline at end of file
diff --git a/trunk/etherpad/src/static/js/easysync2_client.js b/trunk/etherpad/src/static/js/easysync2_client.js
new file mode 100644
index 0000000..043099c
--- /dev/null
+++ b/trunk/etherpad/src/static/js/easysync2_client.js
@@ -0,0 +1,1777 @@
+// DO NOT EDIT THIS FILE, edit infrastructure/ace/www/easysync2.js
+
+/**
+ * Copyright 2009 Google 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.
+ */
+
+//var _opt = (this.Easysync2Support || null);
+var _opt = null; // disable optimization for now
+
+function AttribPool() {
+ var p = {};
+ p.numToAttrib = {}; // e.g. {0: ['foo','bar']}
+ p.attribToNum = {}; // e.g. {'foo,bar': 0}
+ p.nextNum = 0;
+
+ p.putAttrib = function(attrib, dontAddIfAbsent) {
+ var str = String(attrib);
+ if (str in p.attribToNum) {
+ return p.attribToNum[str];
+ }
+ if (dontAddIfAbsent) {
+ return -1;
+ }
+ var num = p.nextNum++;
+ p.attribToNum[str] = num;
+ p.numToAttrib[num] = [String(attrib[0]||''),
+ String(attrib[1]||'')];
+ return num;
+ };
+
+ p.getAttrib = function(num) {
+ var pair = p.numToAttrib[num];
+ if (! pair) return pair;
+ return [pair[0], pair[1]]; // return a mutable copy
+ };
+
+ p.getAttribKey = function(num) {
+ var pair = p.numToAttrib[num];
+ if (! pair) return '';
+ return pair[0];
+ };
+
+ p.getAttribValue = function(num) {
+ var pair = p.numToAttrib[num];
+ if (! pair) return '';
+ return pair[1];
+ };
+
+ p.eachAttrib = function(func) {
+ for(var n in p.numToAttrib) {
+ var pair = p.numToAttrib[n];
+ func(pair[0], pair[1]);
+ }
+ };
+
+ p.toJsonable = function() {
+ return {numToAttrib: p.numToAttrib, nextNum: p.nextNum};
+ };
+
+ p.fromJsonable = function(obj) {
+ p.numToAttrib = obj.numToAttrib;
+ p.nextNum = obj.nextNum;
+ p.attribToNum = {};
+ for(var n in p.numToAttrib) {
+ p.attribToNum[String(p.numToAttrib[n])] = Number(n);
+ }
+ return p;
+ };
+
+ return p;
+}
+
+var Changeset = {};
+
+Changeset.error = function error(msg) { var e = new Error(msg); e.easysync = true; throw e; };
+Changeset.assert = function assert(b, msgParts) {
+ if (! b) {
+ var msg = Array.prototype.slice.call(arguments, 1).join('');
+ Changeset.error("Changeset: "+msg);
+ }
+};
+
+Changeset.parseNum = function(str) { return parseInt(str, 36); };
+Changeset.numToString = function(num) { return num.toString(36).toLowerCase(); };
+Changeset.toBaseTen = function(cs) {
+ var dollarIndex = cs.indexOf('$');
+ var beforeDollar = cs.substring(0, dollarIndex);
+ var fromDollar = cs.substring(dollarIndex);
+ return beforeDollar.replace(/[0-9a-z]+/g, function(s) {
+ return String(Changeset.parseNum(s)); }) + fromDollar;
+};
+
+Changeset.oldLen = function(cs) {
+ return Changeset.unpack(cs).oldLen;
+};
+Changeset.newLen = function(cs) {
+ return Changeset.unpack(cs).newLen;
+};
+
+Changeset.opIterator = function(opsStr, optStartIndex) {
+ //print(opsStr);
+ var regex = /((?:\*[0-9a-z]+)*)(?:\|([0-9a-z]+))?([-+=])([0-9a-z]+)|\?|/g;
+ var startIndex = (optStartIndex || 0);
+ var curIndex = startIndex;
+ var prevIndex = curIndex;
+ function nextRegexMatch() {
+ prevIndex = curIndex;
+ var result;
+ if (_opt) {
+ result = _opt.nextOpInString(opsStr, curIndex);
+ if (result) {
+ if (result.opcode() == '?') {
+ Changeset.error("Hit error opcode in op stream");
+ }
+ curIndex = result.lastIndex();
+ }
+ }
+ else {
+ regex.lastIndex = curIndex;
+ result = regex.exec(opsStr);
+ curIndex = regex.lastIndex;
+ if (result[0] == '?') {
+ Changeset.error("Hit error opcode in op stream");
+ }
+ }
+ return result;
+ }
+ var regexResult = nextRegexMatch();
+ var obj = Changeset.newOp();
+ function next(optObj) {
+ var op = (optObj || obj);
+ if (_opt && regexResult) {
+ op.attribs = regexResult.attribs();
+ op.lines = regexResult.lines();
+ op.chars = regexResult.chars();
+ op.opcode = regexResult.opcode();
+ regexResult = nextRegexMatch();
+ }
+ else if ((! _opt) && regexResult[0]) {
+ op.attribs = regexResult[1];
+ op.lines = Changeset.parseNum(regexResult[2] || 0);
+ op.opcode = regexResult[3];
+ op.chars = Changeset.parseNum(regexResult[4]);
+ regexResult = nextRegexMatch();
+ }
+ else {
+ Changeset.clearOp(op);
+ }
+ return op;
+ }
+ function hasNext() { return !! (_opt ? regexResult : regexResult[0]); }
+ function lastIndex() { return prevIndex; }
+ return {next: next, hasNext: hasNext, lastIndex: lastIndex};
+};
+
+Changeset.clearOp = function(op) {
+ op.opcode = '';
+ op.chars = 0;
+ op.lines = 0;
+ op.attribs = '';
+};
+Changeset.newOp = function(optOpcode) {
+ return {opcode:(optOpcode || ''), chars:0, lines:0, attribs:''};
+};
+Changeset.cloneOp = function(op) {
+ return {opcode: op.opcode, chars: op.chars, lines: op.lines, attribs: op.attribs};
+};
+Changeset.copyOp = function(op1, op2) {
+ op2.opcode = op1.opcode;
+ op2.chars = op1.chars;
+ op2.lines = op1.lines;
+ op2.attribs = op1.attribs;
+};
+Changeset.opString = function(op) {
+ // just for debugging
+ if (! op.opcode) return 'null';
+ var assem = Changeset.opAssembler();
+ assem.append(op);
+ return assem.toString();
+};
+Changeset.stringOp = function(str) {
+ // just for debugging
+ return Changeset.opIterator(str).next();
+};
+
+Changeset.checkRep = function(cs) {
+ // doesn't check things that require access to attrib pool (e.g. attribute order)
+ // or original string (e.g. newline positions)
+ var unpacked = Changeset.unpack(cs);
+ var oldLen = unpacked.oldLen;
+ var newLen = unpacked.newLen;
+ var ops = unpacked.ops;
+ var charBank = unpacked.charBank;
+
+ var assem = Changeset.smartOpAssembler();
+ var oldPos = 0;
+ var calcNewLen = 0;
+ var numInserted = 0;
+ var iter = Changeset.opIterator(ops);
+ while (iter.hasNext()) {
+ var o = iter.next();
+ switch (o.opcode) {
+ case '=': oldPos += o.chars; calcNewLen += o.chars; break;
+ case '-': oldPos += o.chars; Changeset.assert(oldPos < oldLen, oldPos," >= ",oldLen," in ",cs); break;
+ case '+': {
+ calcNewLen += o.chars; numInserted += o.chars;
+ Changeset.assert(calcNewLen < newLen, calcNewLen," >= ",newLen," in ",cs);
+ break;
+ }
+ }
+ assem.append(o);
+ }
+
+ calcNewLen += oldLen - oldPos;
+ charBank = charBank.substring(0, numInserted);
+ while (charBank.length < numInserted) {
+ charBank += "?";
+ }
+
+ assem.endDocument();
+ var normalized = Changeset.pack(oldLen, calcNewLen, assem.toString(), charBank);
+ Changeset.assert(normalized == cs, normalized,' != ',cs);
+
+ return cs;
+}
+
+Changeset.smartOpAssembler = function() {
+ // Like opAssembler but able to produce conforming changesets
+ // from slightly looser input, at the cost of speed.
+ // Specifically:
+ // - merges consecutive operations that can be merged
+ // - strips final "="
+ // - ignores 0-length changes
+ // - reorders consecutive + and - (which margingOpAssembler doesn't do)
+
+ var minusAssem = Changeset.mergingOpAssembler();
+ var plusAssem = Changeset.mergingOpAssembler();
+ var keepAssem = Changeset.mergingOpAssembler();
+ var assem = Changeset.stringAssembler();
+ var lastOpcode = '';
+ var lengthChange = 0;
+
+ function flushKeeps() {
+ assem.append(keepAssem.toString());
+ keepAssem.clear();
+ }
+
+ function flushPlusMinus() {
+ assem.append(minusAssem.toString());
+ minusAssem.clear();
+ assem.append(plusAssem.toString());
+ plusAssem.clear();
+ }
+
+ function append(op) {
+ if (! op.opcode) return;
+ if (! op.chars) return;
+
+ if (op.opcode == '-') {
+ if (lastOpcode == '=') {
+ flushKeeps();
+ }
+ minusAssem.append(op);
+ lengthChange -= op.chars;
+ }
+ else if (op.opcode == '+') {
+ if (lastOpcode == '=') {
+ flushKeeps();
+ }
+ plusAssem.append(op);
+ lengthChange += op.chars;
+ }
+ else if (op.opcode == '=') {
+ if (lastOpcode != '=') {
+ flushPlusMinus();
+ }
+ keepAssem.append(op);
+ }
+ lastOpcode = op.opcode;
+ }
+
+ function appendOpWithText(opcode, text, attribs, pool) {
+ var op = Changeset.newOp(opcode);
+ op.attribs = Changeset.makeAttribsString(opcode, attribs, pool);
+ var lastNewlinePos = text.lastIndexOf('\n');
+ if (lastNewlinePos < 0) {
+ op.chars = text.length;
+ op.lines = 0;
+ append(op);
+ }
+ else {
+ op.chars = lastNewlinePos+1;
+ op.lines = text.match(/\n/g).length;
+ append(op);
+ op.chars = text.length - (lastNewlinePos+1);
+ op.lines = 0;
+ append(op);
+ }
+ }
+
+ function toString() {
+ flushPlusMinus();
+ flushKeeps();
+ return assem.toString();
+ }
+
+ function clear() {
+ minusAssem.clear();
+ plusAssem.clear();
+ keepAssem.clear();
+ assem.clear();
+ lengthChange = 0;
+ }
+
+ function endDocument() {
+ keepAssem.endDocument();
+ }
+
+ function getLengthChange() {
+ return lengthChange;
+ }
+
+ return {append: append, toString: toString, clear: clear, endDocument: endDocument,
+ appendOpWithText: appendOpWithText, getLengthChange: getLengthChange };
+};
+
+if (_opt) {
+ Changeset.mergingOpAssembler = function() {
+ var assem = _opt.mergingOpAssembler();
+
+ function append(op) {
+ assem.append(op.opcode, op.chars, op.lines, op.attribs);
+ }
+ function toString() {
+ return assem.toString();
+ }
+ function clear() {
+ assem.clear();
+ }
+ function endDocument() {
+ assem.endDocument();
+ }
+
+ return {append: append, toString: toString, clear: clear, endDocument: endDocument};
+ };
+}
+else {
+ Changeset.mergingOpAssembler = function() {
+ // This assembler can be used in production; it efficiently
+ // merges consecutive operations that are mergeable, ignores
+ // no-ops, and drops final pure "keeps". It does not re-order
+ // operations.
+ var assem = Changeset.opAssembler();
+ var bufOp = Changeset.newOp();
+
+ // If we get, for example, insertions [xxx\n,yyy], those don't merge,
+ // but if we get [xxx\n,yyy,zzz\n], that merges to [xxx\nyyyzzz\n].
+ // This variable stores the length of yyy and any other newline-less
+ // ops immediately after it.
+ var bufOpAdditionalCharsAfterNewline = 0;
+
+ function flush(isEndDocument) {
+ if (bufOp.opcode) {
+ if (isEndDocument && bufOp.opcode == '=' && ! bufOp.attribs) {
+ // final merged keep, leave it implicit
+ }
+ else {
+ assem.append(bufOp);
+ if (bufOpAdditionalCharsAfterNewline) {
+ bufOp.chars = bufOpAdditionalCharsAfterNewline;
+ bufOp.lines = 0;
+ assem.append(bufOp);
+ bufOpAdditionalCharsAfterNewline = 0;
+ }
+ }
+ bufOp.opcode = '';
+ }
+ }
+ function append(op) {
+ if (op.chars > 0) {
+ if (bufOp.opcode == op.opcode && bufOp.attribs == op.attribs) {
+ if (op.lines > 0) {
+ // bufOp and additional chars are all mergeable into a multi-line op
+ bufOp.chars += bufOpAdditionalCharsAfterNewline + op.chars;
+ bufOp.lines += op.lines;
+ bufOpAdditionalCharsAfterNewline = 0;
+ }
+ else if (bufOp.lines == 0) {
+ // both bufOp and op are in-line
+ bufOp.chars += op.chars;
+ }
+ else {
+ // append in-line text to multi-line bufOp
+ bufOpAdditionalCharsAfterNewline += op.chars;
+ }
+ }
+ else {
+ flush();
+ Changeset.copyOp(op, bufOp);
+ }
+ }
+ }
+ function endDocument() {
+ flush(true);
+ }
+ function toString() {
+ flush();
+ return assem.toString();
+ }
+ function clear() {
+ assem.clear();
+ Changeset.clearOp(bufOp);
+ }
+ return {append: append, toString: toString, clear: clear, endDocument: endDocument};
+ };
+}
+
+if (_opt) {
+ Changeset.opAssembler = function() {
+ var assem = _opt.opAssembler();
+ // this function allows op to be mutated later (doesn't keep a ref)
+ function append(op) {
+ assem.append(op.opcode, op.chars, op.lines, op.attribs);
+ }
+ function toString() {
+ return assem.toString();
+ }
+ function clear() {
+ assem.clear();
+ }
+ return {append: append, toString: toString, clear: clear};
+ };
+}
+else {
+ Changeset.opAssembler = function() {
+ var pieces = [];
+ // this function allows op to be mutated later (doesn't keep a ref)
+ function append(op) {
+ pieces.push(op.attribs);
+ if (op.lines) {
+ pieces.push('|', Changeset.numToString(op.lines));
+ }
+ pieces.push(op.opcode);
+ pieces.push(Changeset.numToString(op.chars));
+ }
+ function toString() {
+ return pieces.join('');
+ }
+ function clear() {
+ pieces.length = 0;
+ }
+ return {append: append, toString: toString, clear: clear};
+ };
+}
+
+Changeset.stringIterator = function(str) {
+ var curIndex = 0;
+ function assertRemaining(n) {
+ Changeset.assert(n <= remaining(), "!(",n," <= ",remaining(),")");
+ }
+ function take(n) {
+ assertRemaining(n);
+ var s = str.substr(curIndex, n);
+ curIndex += n;
+ return s;
+ }
+ function peek(n) {
+ assertRemaining(n);
+ var s = str.substr(curIndex, n);
+ return s;
+ }
+ function skip(n) {
+ assertRemaining(n);
+ curIndex += n;
+ }
+ function remaining() {
+ return str.length - curIndex;
+ }
+ return {take:take, skip:skip, remaining:remaining, peek:peek};
+};
+
+Changeset.stringAssembler = function() {
+ var pieces = [];
+ function append(x) {
+ pieces.push(String(x));
+ }
+ function toString() {
+ return pieces.join('');
+ }
+ return {append: append, toString: toString};
+};
+
+// "lines" need not be an array as long as it supports certain calls (lines_foo inside).
+Changeset.textLinesMutator = function(lines) {
+ // Mutates lines, an array of strings, in place.
+ // Mutation operations have the same constraints as changeset operations
+ // with respect to newlines, but not the other additional constraints
+ // (i.e. ins/del ordering, forbidden no-ops, non-mergeability, final newline).
+ // Can be used to mutate lists of strings where the last char of each string
+ // is not actually a newline, but for the purposes of N and L values,
+ // the caller should pretend it is, and for things to work right in that case, the input
+ // to insert() should be a single line with no newlines.
+
+ var curSplice = [0,0];
+ var inSplice = false;
+ // position in document after curSplice is applied:
+ var curLine = 0, curCol = 0;
+ // invariant: if (inSplice) then (curLine is in curSplice[0] + curSplice.length - {2,3}) &&
+ // curLine >= curSplice[0]
+ // invariant: if (inSplice && (curLine >= curSplice[0] + curSplice.length - 2)) then
+ // curCol == 0
+
+ function lines_applySplice(s) {
+ lines.splice.apply(lines, s);
+ }
+ function lines_toSource() {
+ return lines.toSource();
+ }
+ function lines_get(idx) {
+ if (lines.get) {
+ return lines.get(idx);
+ }
+ else {
+ return lines[idx];
+ }
+ }
+ // can be unimplemented if removeLines's return value not needed
+ function lines_slice(start, end) {
+ if (lines.slice) {
+ return lines.slice(start, end);
+ }
+ else {
+ return [];
+ }
+ }
+ function lines_length() {
+ if ((typeof lines.length) == "number") {
+ return lines.length;
+ }
+ else {
+ return lines.length();
+ }
+ }
+
+ function enterSplice() {
+ curSplice[0] = curLine;
+ curSplice[1] = 0;
+ if (curCol > 0) {
+ putCurLineInSplice();
+ }
+ inSplice = true;
+ }
+ function leaveSplice() {
+ lines_applySplice(curSplice);
+ curSplice.length = 2;
+ curSplice[0] = curSplice[1] = 0;
+ inSplice = false;
+ }
+ function isCurLineInSplice() {
+ return (curLine - curSplice[0] < (curSplice.length - 2));
+ }
+ function debugPrint(typ) {
+ print(typ+": "+curSplice.toSource()+" / "+curLine+","+curCol+" / "+lines_toSource());
+ }
+ function putCurLineInSplice() {
+ if (! isCurLineInSplice()) {
+ curSplice.push(lines_get(curSplice[0] + curSplice[1]));
+ curSplice[1]++;
+ }
+ return 2 + curLine - curSplice[0];
+ }
+
+ function skipLines(L, includeInSplice) {
+ if (L) {
+ if (includeInSplice) {
+ if (! inSplice) {
+ enterSplice();
+ }
+ for(var i=0;i<L;i++) {
+ curCol = 0;
+ putCurLineInSplice();
+ curLine++;
+ }
+ }
+ else {
+ if (inSplice) {
+ if (L > 1) {
+ leaveSplice();
+ }
+ else {
+ putCurLineInSplice();
+ }
+ }
+ curLine += L;
+ curCol = 0;
+ }
+ //print(inSplice+" / "+isCurLineInSplice()+" / "+curSplice[0]+" / "+curSplice[1]+" / "+lines.length);
+ /*if (inSplice && (! isCurLineInSplice()) && (curSplice[0] + curSplice[1] < lines.length)) {
+ print("BLAH");
+ putCurLineInSplice();
+ }*/ // tests case foo in remove(), which isn't otherwise covered in current impl
+ }
+ //debugPrint("skip");
+ }
+
+ function skip(N, L, includeInSplice) {
+ if (N) {
+ if (L) {
+ skipLines(L, includeInSplice);
+ }
+ else {
+ if (includeInSplice && ! inSplice) {
+ enterSplice();
+ }
+ if (inSplice) {
+ putCurLineInSplice();
+ }
+ curCol += N;
+ //debugPrint("skip");
+ }
+ }
+ }
+
+ function removeLines(L) {
+ var removed = '';
+ if (L) {
+ if (! inSplice) {
+ enterSplice();
+ }
+ function nextKLinesText(k) {
+ var m = curSplice[0] + curSplice[1];
+ return lines_slice(m, m+k).join('');
+ }
+ if (isCurLineInSplice()) {
+ //print(curCol);
+ if (curCol == 0) {
+ removed = curSplice[curSplice.length-1];
+ // print("FOO"); // case foo
+ curSplice.length--;
+ removed += nextKLinesText(L-1);
+ curSplice[1] += L-1;
+ }
+ else {
+ removed = nextKLinesText(L-1);
+ curSplice[1] += L-1;
+ var sline = curSplice.length - 1;
+ removed = curSplice[sline].substring(curCol) + removed;
+ curSplice[sline] = curSplice[sline].substring(0, curCol) +
+ lines_get(curSplice[0] + curSplice[1]);
+ curSplice[1] += 1;
+ }
+ }
+ else {
+ removed = nextKLinesText(L);
+ curSplice[1] += L;
+ }
+ //debugPrint("remove");
+ }
+ return removed;
+ }
+
+ function remove(N, L) {
+ var removed = '';
+ if (N) {
+ if (L) {
+ return removeLines(L);
+ }
+ else {
+ if (! inSplice) {
+ enterSplice();
+ }
+ var sline = putCurLineInSplice();
+ removed = curSplice[sline].substring(curCol, curCol+N);
+ curSplice[sline] = curSplice[sline].substring(0, curCol) +
+ curSplice[sline].substring(curCol+N);
+ //debugPrint("remove");
+ }
+ }
+ return removed;
+ }
+
+ function insert(text, L) {
+ if (text) {
+ if (! inSplice) {
+ enterSplice();
+ }
+ if (L) {
+ var newLines = Changeset.splitTextLines(text);
+ if (isCurLineInSplice()) {
+ //if (curCol == 0) {
+ //curSplice.length--;
+ //curSplice[1]--;
+ //Array.prototype.push.apply(curSplice, newLines);
+ //curLine += newLines.length;
+ //}
+ //else {
+ var sline = curSplice.length - 1;
+ var theLine = curSplice[sline];
+ var lineCol = curCol;
+ curSplice[sline] = theLine.substring(0, lineCol) + newLines[0];
+ curLine++;
+ newLines.splice(0, 1);
+ Array.prototype.push.apply(curSplice, newLines);
+ curLine += newLines.length;
+ curSplice.push(theLine.substring(lineCol));
+ curCol = 0;
+ //}
+ }
+ else {
+ Array.prototype.push.apply(curSplice, newLines);
+ curLine += newLines.length;
+ }
+ }
+ else {
+ var sline = putCurLineInSplice();
+ curSplice[sline] = curSplice[sline].substring(0, curCol) +
+ text + curSplice[sline].substring(curCol);
+ curCol += text.length;
+ }
+ //debugPrint("insert");
+ }
+ }
+
+ function hasMore() {
+ //print(lines.length+" / "+inSplice+" / "+(curSplice.length - 2)+" / "+curSplice[1]);
+ var docLines = lines_length();
+ if (inSplice) {
+ docLines += curSplice.length - 2 - curSplice[1];
+ }
+ return curLine < docLines;
+ }
+
+ function close() {
+ if (inSplice) {
+ leaveSplice();
+ }
+ //debugPrint("close");
+ }
+
+ var self = {skip:skip, remove:remove, insert:insert, close:close, hasMore:hasMore,
+ removeLines:removeLines, skipLines: skipLines};
+ return self;
+};
+
+Changeset.applyZip = function(in1, idx1, in2, idx2, func) {
+ var iter1 = Changeset.opIterator(in1, idx1);
+ var iter2 = Changeset.opIterator(in2, idx2);
+ var assem = Changeset.smartOpAssembler();
+ var op1 = Changeset.newOp();
+ var op2 = Changeset.newOp();
+ var opOut = Changeset.newOp();
+ while (op1.opcode || iter1.hasNext() || op2.opcode || iter2.hasNext()) {
+ if ((! op1.opcode) && iter1.hasNext()) iter1.next(op1);
+ if ((! op2.opcode) && iter2.hasNext()) iter2.next(op2);
+ func(op1, op2, opOut);
+ if (opOut.opcode) {
+ //print(opOut.toSource());
+ assem.append(opOut);
+ opOut.opcode = '';
+ }
+ }
+ assem.endDocument();
+ return assem.toString();
+};
+
+Changeset.unpack = function(cs) {
+ var headerRegex = /Z:([0-9a-z]+)([><])([0-9a-z]+)|/;
+ var headerMatch = headerRegex.exec(cs);
+ if ((! headerMatch) || (! headerMatch[0])) {
+ Changeset.error("Not a changeset: "+cs);
+ }
+ var oldLen = Changeset.parseNum(headerMatch[1]);
+ var changeSign = (headerMatch[2] == '>') ? 1 : -1;
+ var changeMag = Changeset.parseNum(headerMatch[3]);
+ var newLen = oldLen + changeSign*changeMag;
+ var opsStart = headerMatch[0].length;
+ var opsEnd = cs.indexOf("$");
+ if (opsEnd < 0) opsEnd = cs.length;
+ return {oldLen: oldLen, newLen: newLen, ops: cs.substring(opsStart, opsEnd),
+ charBank: cs.substring(opsEnd+1)};
+};
+
+Changeset.pack = function(oldLen, newLen, opsStr, bank) {
+ var lenDiff = newLen - oldLen;
+ var lenDiffStr = (lenDiff >= 0 ?
+ '>'+Changeset.numToString(lenDiff) :
+ '<'+Changeset.numToString(-lenDiff));
+ var a = [];
+ a.push('Z:', Changeset.numToString(oldLen), lenDiffStr, opsStr, '$', bank);
+ return a.join('');
+};
+
+Changeset.applyToText = function(cs, str) {
+ var unpacked = Changeset.unpack(cs);
+ Changeset.assert(str.length == unpacked.oldLen,
+ "mismatched apply: ",str.length," / ",unpacked.oldLen);
+ var csIter = Changeset.opIterator(unpacked.ops);
+ var bankIter = Changeset.stringIterator(unpacked.charBank);
+ var strIter = Changeset.stringIterator(str);
+ var assem = Changeset.stringAssembler();
+ while (csIter.hasNext()) {
+ var op = csIter.next();
+ switch(op.opcode) {
+ case '+': assem.append(bankIter.take(op.chars)); break;
+ case '-': strIter.skip(op.chars); break;
+ case '=': assem.append(strIter.take(op.chars)); break;
+ }
+ }
+ assem.append(strIter.take(strIter.remaining()));
+ return assem.toString();
+};
+
+Changeset.mutateTextLines = function(cs, lines) {
+ var unpacked = Changeset.unpack(cs);
+ var csIter = Changeset.opIterator(unpacked.ops);
+ var bankIter = Changeset.stringIterator(unpacked.charBank);
+ var mut = Changeset.textLinesMutator(lines);
+ while (csIter.hasNext()) {
+ var op = csIter.next();
+ switch(op.opcode) {
+ case '+': mut.insert(bankIter.take(op.chars), op.lines); break;
+ case '-': mut.remove(op.chars, op.lines); break;
+ case '=': mut.skip(op.chars, op.lines, (!! op.attribs)); break;
+ }
+ }
+ mut.close();
+};
+
+Changeset.composeAttributes = function(att1, att2, resultIsMutation, pool) {
+ // att1 and att2 are strings like "*3*f*1c", asMutation is a boolean.
+
+ // Sometimes attribute (key,value) pairs are treated as attribute presence
+ // information, while other times they are treated as operations that
+ // mutate a set of attributes, and this affects whether an empty value
+ // is a deletion or a change.
+ // Examples, of the form (att1Items, att2Items, resultIsMutation) -> result
+ // ([], [(bold, )], true) -> [(bold, )]
+ // ([], [(bold, )], false) -> []
+ // ([], [(bold, true)], true) -> [(bold, true)]
+ // ([], [(bold, true)], false) -> [(bold, true)]
+ // ([(bold, true)], [(bold, )], true) -> [(bold, )]
+ // ([(bold, true)], [(bold, )], false) -> []
+
+ // pool can be null if att2 has no attributes.
+
+ if ((! att1) && resultIsMutation) {
+ // In the case of a mutation (i.e. composing two changesets),
+ // an att2 composed with an empy att1 is just att2. If att1
+ // is part of an attribution string, then att2 may remove
+ // attributes that are already gone, so don't do this optimization.
+ return att2;
+ }
+ if (! att2) return att1;
+ var atts = [];
+ att1.replace(/\*([0-9a-z]+)/g, function(_, a) {
+ atts.push(pool.getAttrib(Changeset.parseNum(a)));
+ return '';
+ });
+ att2.replace(/\*([0-9a-z]+)/g, function(_, a) {
+ var pair = pool.getAttrib(Changeset.parseNum(a));
+ var found = false;
+ for(var i=0;i<atts.length;i++) {
+ var oldPair = atts[i];
+ if (oldPair[0] == pair[0]) {
+ if (pair[1] || resultIsMutation) {
+ oldPair[1] = pair[1];
+ }
+ else {
+ atts.splice(i, 1);
+ }
+ found = true;
+ break;
+ }
+ }
+ if ((! found) && (pair[1] || resultIsMutation)) {
+ atts.push(pair);
+ }
+ return '';
+ });
+ atts.sort();
+ var buf = Changeset.stringAssembler();
+ for(var i=0;i<atts.length;i++) {
+ buf.append('*');
+ buf.append(Changeset.numToString(pool.putAttrib(atts[i])));
+ }
+ //print(att1+" / "+att2+" / "+buf.toString());
+ return buf.toString();
+};
+
+Changeset._slicerZipperFunc = function(attOp, csOp, opOut, pool) {
+ // attOp is the op from the sequence that is being operated on, either an
+ // attribution string or the earlier of two changesets being composed.
+ // pool can be null if definitely not needed.
+
+ //print(csOp.toSource()+" "+attOp.toSource()+" "+opOut.toSource());
+ if (attOp.opcode == '-') {
+ Changeset.copyOp(attOp, opOut);
+ attOp.opcode = '';
+ }
+ else if (! attOp.opcode) {
+ Changeset.copyOp(csOp, opOut);
+ csOp.opcode = '';
+ }
+ else {
+ switch (csOp.opcode) {
+ case '-': {
+ if (csOp.chars <= attOp.chars) {
+ // delete or delete part
+ if (attOp.opcode == '=') {
+ opOut.opcode = '-';
+ opOut.chars = csOp.chars;
+ opOut.lines = csOp.lines;
+ opOut.attribs = '';
+ }
+ attOp.chars -= csOp.chars;
+ attOp.lines -= csOp.lines;
+ csOp.opcode = '';
+ if (! attOp.chars) {
+ attOp.opcode = '';
+ }
+ }
+ else {
+ // delete and keep going
+ if (attOp.opcode == '=') {
+ opOut.opcode = '-';
+ opOut.chars = attOp.chars;
+ opOut.lines = attOp.lines;
+ opOut.attribs = '';
+ }
+ csOp.chars -= attOp.chars;
+ csOp.lines -= attOp.lines;
+ attOp.opcode = '';
+ }
+ break;
+ }
+ case '+': {
+ // insert
+ Changeset.copyOp(csOp, opOut);
+ csOp.opcode = '';
+ break;
+ }
+ case '=': {
+ if (csOp.chars <= attOp.chars) {
+ // keep or keep part
+ opOut.opcode = attOp.opcode;
+ opOut.chars = csOp.chars;
+ opOut.lines = csOp.lines;
+ opOut.attribs = Changeset.composeAttributes(attOp.attribs, csOp.attribs,
+ attOp.opcode == '=', pool);
+ csOp.opcode = '';
+ attOp.chars -= csOp.chars;
+ attOp.lines -= csOp.lines;
+ if (! attOp.chars) {
+ attOp.opcode = '';
+ }
+ }
+ else {
+ // keep and keep going
+ opOut.opcode = attOp.opcode;
+ opOut.chars = attOp.chars;
+ opOut.lines = attOp.lines;
+ opOut.attribs = Changeset.composeAttributes(attOp.attribs, csOp.attribs,
+ attOp.opcode == '=', pool);
+ attOp.opcode = '';
+ csOp.chars -= attOp.chars;
+ csOp.lines -= attOp.lines;
+ }
+ break;
+ }
+ case '': {
+ Changeset.copyOp(attOp, opOut);
+ attOp.opcode = '';
+ break;
+ }
+ }
+ }
+};
+
+Changeset.applyToAttribution = function(cs, astr, pool) {
+ var unpacked = Changeset.unpack(cs);
+
+ return Changeset.applyZip(astr, 0, unpacked.ops, 0, function(op1, op2, opOut) {
+ return Changeset._slicerZipperFunc(op1, op2, opOut, pool);
+ });
+};
+
+/*Changeset.oneInsertedLineAtATimeOpIterator = function(opsStr, optStartIndex, charBank) {
+ var iter = Changeset.opIterator(opsStr, optStartIndex);
+ var bankIndex = 0;
+
+};*/
+
+Changeset.mutateAttributionLines = function(cs, lines, pool) {
+ //dmesg(cs);
+ //dmesg(lines.toSource()+" ->");
+
+ var unpacked = Changeset.unpack(cs);
+ var csIter = Changeset.opIterator(unpacked.ops);
+ var csBank = unpacked.charBank;
+ var csBankIndex = 0;
+ // treat the attribution lines as text lines, mutating a line at a time
+ var mut = Changeset.textLinesMutator(lines);
+
+ var lineIter = null;
+ function isNextMutOp() {
+ return (lineIter && lineIter.hasNext()) || mut.hasMore();
+ }
+ function nextMutOp(destOp) {
+ if ((!(lineIter && lineIter.hasNext())) && mut.hasMore()) {
+ var line = mut.removeLines(1);
+ lineIter = Changeset.opIterator(line);
+ }
+ if (lineIter && lineIter.hasNext()) {
+ lineIter.next(destOp);
+ }
+ else {
+ destOp.opcode = '';
+ }
+ }
+ var lineAssem = null;
+ function outputMutOp(op) {
+ //print("outputMutOp: "+op.toSource());
+ if (! lineAssem) {
+ lineAssem = Changeset.mergingOpAssembler();
+ }
+ lineAssem.append(op);
+ if (op.lines > 0) {
+ Changeset.assert(op.lines == 1, "Can't have op.lines of ",op.lines," in attribution lines");
+ // ship it to the mut
+ mut.insert(lineAssem.toString(), 1);
+ lineAssem = null;
+ }
+ }
+
+ var csOp = Changeset.newOp();
+ var attOp = Changeset.newOp();
+ var opOut = Changeset.newOp();
+ while (csOp.opcode || csIter.hasNext() || attOp.opcode || isNextMutOp()) {
+ if ((! csOp.opcode) && csIter.hasNext()) {
+ csIter.next(csOp);
+ }
+ //print(csOp.toSource()+" "+attOp.toSource()+" "+opOut.toSource());
+ //print(csOp.opcode+"/"+csOp.lines+"/"+csOp.attribs+"/"+lineAssem+"/"+lineIter+"/"+(lineIter?lineIter.hasNext():null));
+ //print("csOp: "+csOp.toSource());
+ if ((! csOp.opcode) && (! attOp.opcode) &&
+ (! lineAssem) && (! (lineIter && lineIter.hasNext()))) {
+ break; // done
+ }
+ else if (csOp.opcode == '=' && csOp.lines > 0 && (! csOp.attribs) && (! attOp.opcode) &&
+ (! lineAssem) && (! (lineIter && lineIter.hasNext()))) {
+ // skip multiple lines; this is what makes small changes not order of the document size
+ mut.skipLines(csOp.lines);
+ //print("skipped: "+csOp.lines);
+ csOp.opcode = '';
+ }
+ else if (csOp.opcode == '+') {
+ if (csOp.lines > 1) {
+ var firstLineLen = csBank.indexOf('\n', csBankIndex) + 1 - csBankIndex;
+ Changeset.copyOp(csOp, opOut);
+ csOp.chars -= firstLineLen;
+ csOp.lines--;
+ opOut.lines = 1;
+ opOut.chars = firstLineLen;
+ }
+ else {
+ Changeset.copyOp(csOp, opOut);
+ csOp.opcode = '';
+ }
+ outputMutOp(opOut);
+ csBankIndex += opOut.chars;
+ opOut.opcode = '';
+ }
+ else {
+ if ((! attOp.opcode) && isNextMutOp()) {
+ nextMutOp(attOp);
+ }
+ //print("attOp: "+attOp.toSource());
+ Changeset._slicerZipperFunc(attOp, csOp, opOut, pool);
+ if (opOut.opcode) {
+ outputMutOp(opOut);
+ opOut.opcode = '';
+ }
+ }
+ }
+
+ Changeset.assert(! lineAssem, "line assembler not finished");
+ mut.close();
+
+ //dmesg("-> "+lines.toSource());
+};
+
+Changeset.joinAttributionLines = function(theAlines) {
+ var assem = Changeset.mergingOpAssembler();
+ for(var i=0;i<theAlines.length;i++) {
+ var aline = theAlines[i];
+ var iter = Changeset.opIterator(aline);
+ while (iter.hasNext()) {
+ assem.append(iter.next());
+ }
+ }
+ return assem.toString();
+};
+
+Changeset.splitAttributionLines = function(attrOps, text) {
+ var iter = Changeset.opIterator(attrOps);
+ var assem = Changeset.mergingOpAssembler();
+ var lines = [];
+ var pos = 0;
+
+ function appendOp(op) {
+ assem.append(op);
+ if (op.lines > 0) {
+ lines.push(assem.toString());
+ assem.clear();
+ }
+ pos += op.chars;
+ }
+
+ while (iter.hasNext()) {
+ var op = iter.next();
+ var numChars = op.chars;
+ var numLines = op.lines;
+ while (numLines > 1) {
+ var newlineEnd = text.indexOf('\n', pos)+1;
+ Changeset.assert(newlineEnd > 0, "newlineEnd <= 0 in splitAttributionLines");
+ op.chars = newlineEnd - pos;
+ op.lines = 1;
+ appendOp(op);
+ numChars -= op.chars;
+ numLines -= op.lines;
+ }
+ if (numLines == 1) {
+ op.chars = numChars;
+ op.lines = 1;
+ }
+ appendOp(op);
+ }
+
+ return lines;
+};
+
+Changeset.splitTextLines = function(text) {
+ return text.match(/[^\n]*(?:\n|[^\n]$)/g);
+};
+
+Changeset.compose = function(cs1, cs2, pool) {
+ var unpacked1 = Changeset.unpack(cs1);
+ var unpacked2 = Changeset.unpack(cs2);
+ var len1 = unpacked1.oldLen;
+ var len2 = unpacked1.newLen;
+ Changeset.assert(len2 == unpacked2.oldLen, "mismatched composition");
+ var len3 = unpacked2.newLen;
+ var bankIter1 = Changeset.stringIterator(unpacked1.charBank);
+ var bankIter2 = Changeset.stringIterator(unpacked2.charBank);
+ var bankAssem = Changeset.stringAssembler();
+
+ var newOps = Changeset.applyZip(unpacked1.ops, 0, unpacked2.ops, 0, function(op1, op2, opOut) {
+ //var debugBuilder = Changeset.stringAssembler();
+ //debugBuilder.append(Changeset.opString(op1));
+ //debugBuilder.append(',');
+ //debugBuilder.append(Changeset.opString(op2));
+ //debugBuilder.append(' / ');
+
+ var op1code = op1.opcode;
+ var op2code = op2.opcode;
+ if (op1code == '+' && op2code == '-') {
+ bankIter1.skip(Math.min(op1.chars, op2.chars));
+ }
+ Changeset._slicerZipperFunc(op1, op2, opOut, pool);
+ if (opOut.opcode == '+') {
+ if (op2code == '+') {
+ bankAssem.append(bankIter2.take(opOut.chars));
+ }
+ else {
+ bankAssem.append(bankIter1.take(opOut.chars));
+ }
+ }
+
+ //debugBuilder.append(Changeset.opString(op1));
+ //debugBuilder.append(',');
+ //debugBuilder.append(Changeset.opString(op2));
+ //debugBuilder.append(' -> ');
+ //debugBuilder.append(Changeset.opString(opOut));
+ //print(debugBuilder.toString());
+ });
+
+ return Changeset.pack(len1, len3, newOps, bankAssem.toString());
+};
+
+Changeset.attributeTester = function(attribPair, pool) {
+ // returns a function that tests if a string of attributes
+ // (e.g. *3*4) contains a given attribute key,value that
+ // is already present in the pool.
+ if (! pool) {
+ return never;
+ }
+ var attribNum = pool.putAttrib(attribPair, true);
+ if (attribNum < 0) {
+ return never;
+ }
+ else {
+ var re = new RegExp('\\*'+Changeset.numToString(attribNum)+
+ '(?!\\w)');
+ return function(attribs) {
+ return re.test(attribs);
+ };
+ }
+ function never(attribs) { return false; }
+};
+
+Changeset.identity = function(N) {
+ return Changeset.pack(N, N, "", "");
+};
+
+Changeset.makeSplice = function(oldFullText, spliceStart, numRemoved, newText, optNewTextAPairs, pool) {
+ var oldLen = oldFullText.length;
+
+ if (spliceStart >= oldLen) {
+ spliceStart = oldLen - 1;
+ }
+ if (numRemoved > oldFullText.length - spliceStart - 1) {
+ numRemoved = oldFullText.length - spliceStart - 1;
+ }
+ var oldText = oldFullText.substring(spliceStart, spliceStart+numRemoved);
+ var newLen = oldLen + newText.length - oldText.length;
+
+ var assem = Changeset.smartOpAssembler();
+ assem.appendOpWithText('=', oldFullText.substring(0, spliceStart));
+ assem.appendOpWithText('-', oldText);
+ assem.appendOpWithText('+', newText, optNewTextAPairs, pool);
+ assem.endDocument();
+ return Changeset.pack(oldLen, newLen, assem.toString(), newText);
+};
+
+Changeset.toSplices = function(cs) {
+ // get a list of splices, [startChar, endChar, newText]
+
+ var unpacked = Changeset.unpack(cs);
+ var splices = [];
+
+ var oldPos = 0;
+ var iter = Changeset.opIterator(unpacked.ops);
+ var charIter = Changeset.stringIterator(unpacked.charBank);
+ var inSplice = false;
+ while (iter.hasNext()) {
+ var op = iter.next();
+ if (op.opcode == '=') {
+ oldPos += op.chars;
+ inSplice = false;
+ }
+ else {
+ if (! inSplice) {
+ splices.push([oldPos, oldPos, ""]);
+ inSplice = true;
+ }
+ if (op.opcode == '-') {
+ oldPos += op.chars;
+ splices[splices.length-1][1] += op.chars;
+ }
+ else if (op.opcode == '+') {
+ splices[splices.length-1][2] += charIter.take(op.chars);
+ }
+ }
+ }
+
+ return splices;
+};
+
+Changeset.characterRangeFollow = function(cs, startChar, endChar, insertionsAfter) {
+ var newStartChar = startChar;
+ var newEndChar = endChar;
+ var splices = Changeset.toSplices(cs);
+ var lengthChangeSoFar = 0;
+ for(var i=0;i<splices.length;i++) {
+ var splice = splices[i];
+ var spliceStart = splice[0] + lengthChangeSoFar;
+ var spliceEnd = splice[1] + lengthChangeSoFar;
+ var newTextLength = splice[2].length;
+ var thisLengthChange = newTextLength - (spliceEnd - spliceStart);
+
+ if (spliceStart <= newStartChar && spliceEnd >= newEndChar) {
+ // splice fully replaces/deletes range
+ // (also case that handles insertion at a collapsed selection)
+ if (insertionsAfter) {
+ newStartChar = newEndChar = spliceStart;
+ }
+ else {
+ newStartChar = newEndChar = spliceStart + newTextLength;
+ }
+ }
+ else if (spliceEnd <= newStartChar) {
+ // splice is before range
+ newStartChar += thisLengthChange;
+ newEndChar += thisLengthChange;
+ }
+ else if (spliceStart >= newEndChar) {
+ // splice is after range
+ }
+ else if (spliceStart >= newStartChar && spliceEnd <= newEndChar) {
+ // splice is inside range
+ newEndChar += thisLengthChange;
+ }
+ else if (spliceEnd < newEndChar) {
+ // splice overlaps beginning of range
+ newStartChar = spliceStart + newTextLength;
+ newEndChar += thisLengthChange;
+ }
+ else {
+ // splice overlaps end of range
+ newEndChar = spliceStart;
+ }
+
+ lengthChangeSoFar += thisLengthChange;
+ }
+
+ return [newStartChar, newEndChar];
+};
+
+Changeset.moveOpsToNewPool = function(cs, oldPool, newPool) {
+ // works on changeset or attribution string
+ var dollarPos = cs.indexOf('$');
+ if (dollarPos < 0) {
+ dollarPos = cs.length;
+ }
+ var upToDollar = cs.substring(0, dollarPos);
+ var fromDollar = cs.substring(dollarPos);
+ // order of attribs stays the same
+ return upToDollar.replace(/\*([0-9a-z]+)/g, function(_, a) {
+ var oldNum = Changeset.parseNum(a);
+ var pair = oldPool.getAttrib(oldNum);
+ var newNum = newPool.putAttrib(pair);
+ return '*'+Changeset.numToString(newNum);
+ }) + fromDollar;
+};
+
+Changeset.makeAttribution = function(text) {
+ var assem = Changeset.smartOpAssembler();
+ assem.appendOpWithText('+', text);
+ return assem.toString();
+};
+
+// callable on a changeset, attribution string, or attribs property of an op
+Changeset.eachAttribNumber = function(cs, func) {
+ var dollarPos = cs.indexOf('$');
+ if (dollarPos < 0) {
+ dollarPos = cs.length;
+ }
+ var upToDollar = cs.substring(0, dollarPos);
+
+ upToDollar.replace(/\*([0-9a-z]+)/g, function(_, a) {
+ func(Changeset.parseNum(a));
+ return '';
+ });
+};
+
+// callable on a changeset, attribution string, or attribs property of an op,
+// though it may easily create adjacent ops that can be merged.
+Changeset.filterAttribNumbers = function(cs, filter) {
+ return Changeset.mapAttribNumbers(cs, filter);
+};
+
+Changeset.mapAttribNumbers = function(cs, func) {
+ var dollarPos = cs.indexOf('$');
+ if (dollarPos < 0) {
+ dollarPos = cs.length;
+ }
+ var upToDollar = cs.substring(0, dollarPos);
+
+ var newUpToDollar = upToDollar.replace(/\*([0-9a-z]+)/g, function(s, a) {
+ var n = func(Changeset.parseNum(a));
+ if (n === true) {
+ return s;
+ }
+ else if ((typeof n) === "number") {
+ return '*'+Changeset.numToString(n);
+ }
+ else {
+ return '';
+ }
+ });
+
+ return newUpToDollar + cs.substring(dollarPos);
+};
+
+Changeset.makeAText = function(text, attribs) {
+ return { text: text, attribs: (attribs || Changeset.makeAttribution(text)) };
+};
+
+Changeset.applyToAText = function(cs, atext, pool) {
+ return { text: Changeset.applyToText(cs, atext.text),
+ attribs: Changeset.applyToAttribution(cs, atext.attribs, pool) };
+};
+
+Changeset.cloneAText = function(atext) {
+ return { text: atext.text, attribs: atext.attribs };
+};
+
+Changeset.copyAText = function(atext1, atext2) {
+ atext2.text = atext1.text;
+ atext2.attribs = atext1.attribs;
+};
+
+Changeset.appendATextToAssembler = function(atext, assem) {
+ // intentionally skips last newline char of atext
+ var iter = Changeset.opIterator(atext.attribs);
+ var op = Changeset.newOp();
+ while (iter.hasNext()) {
+ iter.next(op);
+ if (! iter.hasNext()) {
+ // last op, exclude final newline
+ if (op.lines <= 1) {
+ op.lines = 0;
+ op.chars--;
+ if (op.chars) {
+ assem.append(op);
+ }
+ }
+ else {
+ var nextToLastNewlineEnd =
+ atext.text.lastIndexOf('\n', atext.text.length-2) + 1;
+ var lastLineLength = atext.text.length - nextToLastNewlineEnd - 1;
+ op.lines--;
+ op.chars -= (lastLineLength + 1);
+ assem.append(op);
+ op.lines = 0;
+ op.chars = lastLineLength;
+ if (op.chars) {
+ assem.append(op);
+ }
+ }
+ }
+ else {
+ assem.append(op);
+ }
+ }
+};
+
+Changeset.prepareForWire = function(cs, pool) {
+ var newPool = new AttribPool();
+ var newCs = Changeset.moveOpsToNewPool(cs, pool, newPool);
+ return {translated: newCs, pool: newPool};
+};
+
+Changeset.isIdentity = function(cs) {
+ var unpacked = Changeset.unpack(cs);
+ return unpacked.ops == "" && unpacked.oldLen == unpacked.newLen;
+};
+
+Changeset.opAttributeValue = function(op, key, pool) {
+ return Changeset.attribsAttributeValue(op.attribs, key, pool);
+};
+
+Changeset.attribsAttributeValue = function(attribs, key, pool) {
+ var value = '';
+ if (attribs) {
+ Changeset.eachAttribNumber(attribs, function(n) {
+ if (pool.getAttribKey(n) == key) {
+ value = pool.getAttribValue(n);
+ }
+ });
+ }
+ return value;
+};
+
+Changeset.builder = function(oldLen) {
+ var assem = Changeset.smartOpAssembler();
+ var o = Changeset.newOp();
+ var charBank = Changeset.stringAssembler();
+
+ var self = {
+ // attribs are [[key1,value1],[key2,value2],...] or '*0*1...' (no pool needed in latter case)
+ keep: function(N, L, attribs, pool) {
+ o.opcode = '=';
+ o.attribs = (attribs &&
+ Changeset.makeAttribsString('=', attribs, pool)) || '';
+ o.chars = N;
+ o.lines = (L || 0);
+ assem.append(o);
+ return self;
+ },
+ keepText: function(text, attribs, pool) {
+ assem.appendOpWithText('=', text, attribs, pool);
+ return self;
+ },
+ insert: function(text, attribs, pool) {
+ assem.appendOpWithText('+', text, attribs, pool);
+ charBank.append(text);
+ return self;
+ },
+ remove: function(N, L) {
+ o.opcode = '-';
+ o.attribs = '';
+ o.chars = N;
+ o.lines = (L || 0);
+ assem.append(o);
+ return self;
+ },
+ toString: function() {
+ assem.endDocument();
+ var newLen = oldLen + assem.getLengthChange();
+ return Changeset.pack(oldLen, newLen, assem.toString(),
+ charBank.toString());
+ }
+ };
+
+ return self;
+};
+
+Changeset.makeAttribsString = function(opcode, attribs, pool) {
+ // makeAttribsString(opcode, '*3') or makeAttribsString(opcode, [['foo','bar']], myPool) work
+ if (! attribs) {
+ return '';
+ }
+ else if ((typeof attribs) == "string") {
+ return attribs;
+ }
+ else if (pool && attribs && attribs.length) {
+ if (attribs.length > 1) {
+ attribs = attribs.slice();
+ attribs.sort();
+ }
+ var result = [];
+ for(var i=0;i<attribs.length;i++) {
+ var pair = attribs[i];
+ if (opcode == '=' || (opcode == '+' && pair[1])) {
+ result.push('*'+Changeset.numToString(pool.putAttrib(pair)));
+ }
+ }
+ return result.join('');
+ }
+};
+
+// like "substring" but on a single-line attribution string
+Changeset.subattribution = function(astr, start, optEnd) {
+ var iter = Changeset.opIterator(astr, 0);
+ var assem = Changeset.smartOpAssembler();
+ var attOp = Changeset.newOp();
+ var csOp = Changeset.newOp();
+ var opOut = Changeset.newOp();
+
+ function doCsOp() {
+ if (csOp.chars) {
+ while (csOp.opcode && (attOp.opcode || iter.hasNext())) {
+ if (! attOp.opcode) iter.next(attOp);
+
+ if (csOp.opcode && attOp.opcode && csOp.chars >= attOp.chars &&
+ attOp.lines > 0 && csOp.lines <= 0) {
+ csOp.lines++;
+ }
+
+ Changeset._slicerZipperFunc(attOp, csOp, opOut, null);
+ if (opOut.opcode) {
+ assem.append(opOut);
+ opOut.opcode = '';
+ }
+ }
+ }
+ }
+
+ csOp.opcode = '-';
+ csOp.chars = start;
+
+ doCsOp();
+
+ if (optEnd === undefined) {
+ if (attOp.opcode) {
+ assem.append(attOp);
+ }
+ while (iter.hasNext()) {
+ iter.next(attOp);
+ assem.append(attOp);
+ }
+ }
+ else {
+ csOp.opcode = '=';
+ csOp.chars = optEnd - start;
+ doCsOp();
+ }
+
+ return assem.toString();
+};
+
+Changeset.inverse = function(cs, lines, alines, pool) {
+ // lines and alines are what the changeset is meant to apply to.
+ // They may be arrays or objects with .get(i) and .length methods.
+ // They include final newlines on lines.
+ function lines_get(idx) {
+ if (lines.get) {
+ return lines.get(idx);
+ }
+ else {
+ return lines[idx];
+ }
+ }
+ function lines_length() {
+ if ((typeof lines.length) == "number") {
+ return lines.length;
+ }
+ else {
+ return lines.length();
+ }
+ }
+ function alines_get(idx) {
+ if (alines.get) {
+ return alines.get(idx);
+ }
+ else {
+ return alines[idx];
+ }
+ }
+ function alines_length() {
+ if ((typeof alines.length) == "number") {
+ return alines.length;
+ }
+ else {
+ return alines.length();
+ }
+ }
+
+ var curLine = 0;
+ var curChar = 0;
+ var curLineOpIter = null;
+ var curLineOpIterLine;
+ var curLineNextOp = Changeset.newOp('+');
+
+ var unpacked = Changeset.unpack(cs);
+ var csIter = Changeset.opIterator(unpacked.ops);
+ var builder = Changeset.builder(unpacked.newLen);
+
+ function consumeAttribRuns(numChars, func/*(len, attribs, endsLine)*/) {
+
+ if ((! curLineOpIter) || (curLineOpIterLine != curLine)) {
+ // create curLineOpIter and advance it to curChar
+ curLineOpIter = Changeset.opIterator(alines_get(curLine));
+ curLineOpIterLine = curLine;
+ var indexIntoLine = 0;
+ var done = false;
+ while (! done) {
+ curLineOpIter.next(curLineNextOp);
+ if (indexIntoLine + curLineNextOp.chars >= curChar) {
+ curLineNextOp.chars -= (curChar - indexIntoLine);
+ done = true;
+ }
+ else {
+ indexIntoLine += curLineNextOp.chars;
+ }
+ }
+ }
+
+ while (numChars > 0) {
+ if ((! curLineNextOp.chars) && (! curLineOpIter.hasNext())) {
+ curLine++;
+ curChar = 0;
+ curLineOpIterLine = curLine;
+ curLineNextOp.chars = 0;
+ curLineOpIter = Changeset.opIterator(alines_get(curLine));
+ }
+ if (! curLineNextOp.chars) {
+ curLineOpIter.next(curLineNextOp);
+ }
+ var charsToUse = Math.min(numChars, curLineNextOp.chars);
+ func(charsToUse, curLineNextOp.attribs,
+ charsToUse == curLineNextOp.chars && curLineNextOp.lines > 0);
+ numChars -= charsToUse;
+ curLineNextOp.chars -= charsToUse;
+ curChar += charsToUse;
+ }
+
+ if ((! curLineNextOp.chars) && (! curLineOpIter.hasNext())) {
+ curLine++;
+ curChar = 0;
+ }
+ }
+
+ function skip(N, L) {
+ if (L) {
+ curLine += L;
+ curChar = 0;
+ }
+ else {
+ if (curLineOpIter && curLineOpIterLine == curLine) {
+ consumeAttribRuns(N, function() {});
+ }
+ else {
+ curChar += N;
+ }
+ }
+ }
+
+ function nextText(numChars) {
+ var len = 0;
+ var assem = Changeset.stringAssembler();
+ var firstString = lines_get(curLine).substring(curChar);
+ len += firstString.length;
+ assem.append(firstString);
+
+ var lineNum = curLine+1;
+ while (len < numChars) {
+ var nextString = lines_get(lineNum);
+ len += nextString.length;
+ assem.append(nextString);
+ lineNum++;
+ }
+
+ return assem.toString().substring(0, numChars);
+ }
+
+ function cachedStrFunc(func) {
+ var cache = {};
+ return function(s) {
+ if (! cache[s]) {
+ cache[s] = func(s);
+ }
+ return cache[s];
+ };
+ }
+
+ var attribKeys = [];
+ var attribValues = [];
+ while (csIter.hasNext()) {
+ var csOp = csIter.next();
+ if (csOp.opcode == '=') {
+ if (csOp.attribs) {
+ attribKeys.length = 0;
+ attribValues.length = 0;
+ Changeset.eachAttribNumber(csOp.attribs, function(n) {
+ attribKeys.push(pool.getAttribKey(n));
+ attribValues.push(pool.getAttribValue(n));
+ });
+ var undoBackToAttribs = cachedStrFunc(function(attribs) {
+ var backAttribs = [];
+ for(var i=0;i<attribKeys.length;i++) {
+ var appliedKey = attribKeys[i];
+ var appliedValue = attribValues[i];
+ var oldValue = Changeset.attribsAttributeValue(attribs, appliedKey, pool);
+ if (appliedValue != oldValue) {
+ backAttribs.push([appliedKey, oldValue]);
+ }
+ }
+ return Changeset.makeAttribsString('=', backAttribs, pool);
+ });
+ consumeAttribRuns(csOp.chars, function(len, attribs, endsLine) {
+ builder.keep(len, endsLine ? 1 : 0, undoBackToAttribs(attribs));
+ });
+ }
+ else {
+ skip(csOp.chars, csOp.lines);
+ builder.keep(csOp.chars, csOp.lines);
+ }
+ }
+ else if (csOp.opcode == '+') {
+ builder.remove(csOp.chars, csOp.lines);
+ }
+ else if (csOp.opcode == '-') {
+ var textBank = nextText(csOp.chars);
+ var textBankIndex = 0;
+ consumeAttribRuns(csOp.chars, function(len, attribs, endsLine) {
+ builder.insert(textBank.substr(textBankIndex, len), attribs);
+ textBankIndex += len;
+ });
+ }
+ }
+
+ return Changeset.checkRep(builder.toString());
+};
diff --git a/trunk/etherpad/src/static/js/etherpad.js b/trunk/etherpad/src/static/js/etherpad.js
new file mode 100644
index 0000000..4e51dbf
--- /dev/null
+++ b/trunk/etherpad/src/static/js/etherpad.js
@@ -0,0 +1,217 @@
+/**
+ * Copyright 2009 Google 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.
+ */
+
+$(document).ready(function() {
+ etherpad.deobfuscateEmails();
+
+ if ($('#betasignuppage').size() > 0) {
+ etherpad.betaSignupPageInit();
+ }
+
+ if ($('#productpage').size() > 0) {
+ etherpad.productPageInit();
+ }
+
+ if ($('.pricingpage').size() > 0) {
+ etherpad.pricingPageInit();
+ }
+});
+
+etherpad = {};
+
+//----------------------------------------------------------------
+// general utils
+//----------------------------------------------------------------
+
+etherpad.validEmail = function(x) {
+ return (x.length > 0 &&
+ x.match(/^[\w\.\_\+\-]+\@[\w\_\-]+\.[\w\_\-\.]+$/));
+};
+
+//----------------------------------------------------------------
+// obfuscating emails
+//----------------------------------------------------------------
+
+etherpad.deobfuscateEmails = function() {
+ $("a.obfuscemail").each(function() {
+ $(this).html($(this).html().replace('p*d.sp***e','pad.spline'));
+ this.href = this.href.replace('p*d.sp***e','pad.spline');
+ });
+};
+
+//----------------------------------------------------------------
+// Signing up for pricing info
+//----------------------------------------------------------------
+
+etherpad.pricingPageInit = function() {
+ $('#submitbutton').click(etherpad.pricingSubmit);
+};
+
+etherpad.pricingSubmit = function(edition) {
+ var allData = {};
+ $('#pricingcontact input.ti').each(function() {
+ allData[$(this).attr('id')] = $(this).val();
+ });
+ allData.industry = $('#industry').val();
+
+ $('form button').hide();
+ $('#spinner').show();
+ $('form input').attr('disabled', true);
+
+ $.ajax({
+ type: 'post',
+ url: $('#pricingcontact').attr('action'),
+ data: allData,
+ success: success,
+ error: error
+ });
+
+ function success(responseText) {
+ $('#spinner').hide();
+ if (responseText == "OK") {
+ $('#errorbox').hide();
+ $('#confirmbox').fadeIn('fast');
+ } else {
+ $('#confirmbox').hide();
+ $('#errorbox').hide().html(responseText).fadeIn('fast');
+ $('form button').show();
+ $('form input').removeAttr('disabled');
+ }
+ }
+ function error() {
+ $('#spinner').hide();
+ $('#errorbox').hide().html("Server error.").fadeIn('fast');
+ $('form button').show();
+ $('form input').removeAttr('disabled');
+ }
+
+ return false;
+}
+
+
+//----------------------------------------------------------------
+// Product page (client-side nagivation with JS)
+//----------------------------------------------------------------
+
+etherpad.productPageInit = function() {
+ $("#productpage #tour").addClass("javascripton");
+ etherpad.productPageNavigateTo(window.location.hash.substring(1));
+
+ $("#productpage a.tournav").click(etherpad.tourNavClick);
+}
+
+etherpad.tourNavClick = function() { // to be called as a click event handler
+ var href = $(this).attr('href');
+ var thorpLoc = href.indexOf('#');
+ if (thorpLoc >= 0) {
+ etherpad.productPageNavigateTo(href.substring(thorpLoc+1), true);
+ }
+}
+
+etherpad.productPageNavigateTo = function(hash, shouldAnimate) {
+ function setNavLink(rightOrLeft, text, linkhash) {
+ var navcells = $('#productpage .tourbar .'+rightOrLeft);
+ if (! text) {
+ navcells.html('&nbsp;');
+ }
+ else {
+ navcells.
+ html('<a class="tournav" href="'+clientVars.pageURL+'#'+(linkhash||'')+'">'+text+'</a>').
+ find('a.tournav').click(etherpad.tourNavClick);
+ }
+ }
+ function switchCardsIfNecessary(fromCard, toCard, andThen/*(didAnimate)*/) {
+ if (! $('#productpage #tour').hasClass("show"+toCard)) {
+ var afterAnimate = function() {
+ $("#productpage #"+fromCard).get(0).style.display = "";
+ $('#productpage #tour').removeClass("show"+fromCard).addClass("show"+toCard);
+ if (andThen) andThen(shouldAnimate);
+ }
+ if (shouldAnimate) {
+ $("#productpage #"+fromCard).fadeOut("fast", afterAnimate);
+ }
+ else {
+ afterAnimate();
+ }
+ }
+ else {
+ andThen(false);
+ }
+ }
+ function switchProseIfNecessary(toNum, useAnimation, andThen) {
+ var visibleProse = $("#productpage .tourprose:visible");
+ var alreadyVisible = ($("#productpage #tour"+toNum+"prose:visible").size() > 0);
+ function assignVisibilities() {
+ $("#productpage .tourprose").each(function() {
+ if (this.id == "tour"+toNum+"prose") {
+ this.style.display = 'block';
+ }
+ else {
+ this.style.display = 'none';
+ }
+ });
+ }
+
+ if ((! useAnimation) || visibleProse.size() == 0 || alreadyVisible) {
+ assignVisibilities();
+ andThen();
+ }
+ else {
+ function afterAnimate() {
+ assignVisibilities();
+ andThen();
+ }
+ if (visibleProse.size() > 0 && visibleProse.get(0).id != "tour"+toNum+"prose") {
+ visibleProse.fadeOut("fast", afterAnimate);
+ }
+ else {
+ afterAnimate();
+ }
+ }
+ }
+ function getProseTitle(n) {
+ if (n == 0) return clientVars.screenshotTitle;
+ var atag = $("#productpage #tourleftnav .tour"+n+" a");
+ if (atag.size() > 0) return atag.text();
+ return '';
+ }
+
+ var regexResult;
+ if ((regexResult = /^uses([1-9][0-9]*)$/.exec(hash))) {
+ var tourNum = +regexResult[1];
+ switchCardsIfNecessary("pageshot", "usecases", function(didAnimate) {
+ switchProseIfNecessary(tourNum, shouldAnimate && !didAnimate, function() {
+ /*var n = tourNum;
+ setNavLink("left", "&laquo; "+getProseTitle(n-1), (n == 1 ? "" : "uses"+(n-1)));
+ var nextTitle = getProseTitle(n+1);
+ if (! nextTitle) setNavLink("right", "");
+ else setNavLink("right", nextTitle+" &raquo;", "uses"+(n+1));*/
+ /*setNavLink("left", "&laquo; "+getProseTitle(0), "");
+ setNavLink("right", "");*/
+ setNavLink("right", "&laquo; "+getProseTitle(0), "");
+ $('#tourtop td.left').html("Use Cases");
+ $("#productpage #tourleftnav li").removeClass("selected");
+ $("#productpage #tourleftnav li.tour"+tourNum).addClass("selected");
+ });
+ });
+ }
+ else {
+ switchCardsIfNecessary("usecases", "pageshot", function() {
+ $('#tourtop td.left').html(getProseTitle(0));
+ setNavLink("right", clientVars.screenshotNextLink, "uses1");
+ });
+ }
+}
diff --git a/trunk/etherpad/src/static/js/jquery-1.2.6.js b/trunk/etherpad/src/static/js/jquery-1.2.6.js
new file mode 100755
index 0000000..88e661e
--- /dev/null
+++ b/trunk/etherpad/src/static/js/jquery-1.2.6.js
@@ -0,0 +1,3549 @@
+(function(){
+/*
+ * jQuery 1.2.6 - New Wave Javascript
+ *
+ * Copyright (c) 2008 John Resig (jquery.com)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * $Date: 2008-05-24 14:22:17 -0400 (Sat, 24 May 2008) $
+ * $Rev: 5685 $
+ */
+
+// Map over jQuery in case of overwrite
+var _jQuery = window.jQuery,
+// Map over the $ in case of overwrite
+ _$ = window.$;
+
+var jQuery = window.jQuery = window.$ = function( selector, context ) {
+ // The jQuery object is actually just the init constructor 'enhanced'
+ return new jQuery.fn.init( selector, context );
+};
+
+// A simple way to check for HTML strings or ID strings
+// (both of which we optimize for)
+var quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/,
+
+// Is it a simple selector
+ isSimple = /^.[^:#\[\.]*$/,
+
+// Will speed up references to undefined, and allows munging its name.
+ undefined;
+
+jQuery.fn = jQuery.prototype = {
+ init: function( selector, context ) {
+ // Make sure that a selection was provided
+ selector = selector || document;
+
+ // Handle $(DOMElement)
+ if ( selector.nodeType ) {
+ this[0] = selector;
+ this.length = 1;
+ return this;
+ }
+ // Handle HTML strings
+ if ( typeof selector == "string" ) {
+ // Are we dealing with HTML string or an ID?
+ var match = quickExpr.exec( selector );
+
+ // Verify a match, and that no context was specified for #id
+ if ( match && (match[1] || !context) ) {
+
+ // HANDLE: $(html) -> $(array)
+ if ( match[1] )
+ selector = jQuery.clean( [ match[1] ], context );
+
+ // HANDLE: $("#id")
+ else {
+ var elem = document.getElementById( match[3] );
+
+ // Make sure an element was located
+ if ( elem ){
+ // Handle the case where IE and Opera return items
+ // by name instead of ID
+ if ( elem.id != match[3] )
+ return jQuery().find( selector );
+
+ // Otherwise, we inject the element directly into the jQuery object
+ return jQuery( elem );
+ }
+ selector = [];
+ }
+
+ // HANDLE: $(expr, [context])
+ // (which is just equivalent to: $(content).find(expr)
+ } else
+ return jQuery( context ).find( selector );
+
+ // HANDLE: $(function)
+ // Shortcut for document ready
+ } else if ( jQuery.isFunction( selector ) )
+ return jQuery( document )[ jQuery.fn.ready ? "ready" : "load" ]( selector );
+
+ return this.setArray(jQuery.makeArray(selector));
+ },
+
+ // The current version of jQuery being used
+ jquery: "1.2.6",
+
+ // The number of elements contained in the matched element set
+ size: function() {
+ return this.length;
+ },
+
+ // The number of elements contained in the matched element set
+ length: 0,
+
+ // Get the Nth element in the matched element set OR
+ // Get the whole matched element set as a clean array
+ get: function( num ) {
+ return num == undefined ?
+
+ // Return a 'clean' array
+ jQuery.makeArray( this ) :
+
+ // Return just the object
+ this[ num ];
+ },
+
+ // Take an array of elements and push it onto the stack
+ // (returning the new matched element set)
+ pushStack: function( elems ) {
+ // Build a new jQuery matched element set
+ var ret = jQuery( elems );
+
+ // Add the old object onto the stack (as a reference)
+ ret.prevObject = this;
+
+ // Return the newly-formed element set
+ return ret;
+ },
+
+ // Force the current matched set of elements to become
+ // the specified array of elements (destroying the stack in the process)
+ // You should use pushStack() in order to do this, but maintain the stack
+ setArray: function( elems ) {
+ // Resetting the length to 0, then using the native Array push
+ // is a super-fast way to populate an object with array-like properties
+ this.length = 0;
+ Array.prototype.push.apply( this, elems );
+
+ return this;
+ },
+
+ // Execute a callback for every element in the matched set.
+ // (You can seed the arguments with an array of args, but this is
+ // only used internally.)
+ each: function( callback, args ) {
+ return jQuery.each( this, callback, args );
+ },
+
+ // Determine the position of an element within
+ // the matched set of elements
+ index: function( elem ) {
+ var ret = -1;
+
+ // Locate the position of the desired element
+ return jQuery.inArray(
+ // If it receives a jQuery object, the first element is used
+ elem && elem.jquery ? elem[0] : elem
+ , this );
+ },
+
+ attr: function( name, value, type ) {
+ var options = name;
+
+ // Look for the case where we're accessing a style value
+ if ( name.constructor == String )
+ if ( value === undefined )
+ return this[0] && jQuery[ type || "attr" ]( this[0], name );
+
+ else {
+ options = {};
+ options[ name ] = value;
+ }
+
+ // Check to see if we're setting style values
+ return this.each(function(i){
+ // Set all the styles
+ for ( name in options )
+ jQuery.attr(
+ type ?
+ this.style :
+ this,
+ name, jQuery.prop( this, options[ name ], type, i, name )
+ );
+ });
+ },
+
+ css: function( key, value ) {
+ // ignore negative width and height values
+ if ( (key == 'width' || key == 'height') && parseFloat(value) < 0 )
+ value = undefined;
+ return this.attr( key, value, "curCSS" );
+ },
+
+ text: function( text ) {
+ if ( typeof text != "object" && text != null )
+ return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );
+
+ var ret = "";
+
+ jQuery.each( text || this, function(){
+ jQuery.each( this.childNodes, function(){
+ if ( this.nodeType != 8 )
+ ret += this.nodeType != 1 ?
+ this.nodeValue :
+ jQuery.fn.text( [ this ] );
+ });
+ });
+
+ return ret;
+ },
+
+ wrapAll: function( html ) {
+ if ( this[0] )
+ // The elements to wrap the target around
+ jQuery( html, this[0].ownerDocument )
+ .clone()
+ .insertBefore( this[0] )
+ .map(function(){
+ var elem = this;
+
+ while ( elem.firstChild )
+ elem = elem.firstChild;
+
+ return elem;
+ })
+ .append(this);
+
+ return this;
+ },
+
+ wrapInner: function( html ) {
+ return this.each(function(){
+ jQuery( this ).contents().wrapAll( html );
+ });
+ },
+
+ wrap: function( html ) {
+ return this.each(function(){
+ jQuery( this ).wrapAll( html );
+ });
+ },
+
+ append: function() {
+ return this.domManip(arguments, true, false, function(elem){
+ if (this.nodeType == 1)
+ this.appendChild( elem );
+ });
+ },
+
+ prepend: function() {
+ return this.domManip(arguments, true, true, function(elem){
+ if (this.nodeType == 1)
+ this.insertBefore( elem, this.firstChild );
+ });
+ },
+
+ before: function() {
+ return this.domManip(arguments, false, false, function(elem){
+ this.parentNode.insertBefore( elem, this );
+ });
+ },
+
+ after: function() {
+ return this.domManip(arguments, false, true, function(elem){
+ this.parentNode.insertBefore( elem, this.nextSibling );
+ });
+ },
+
+ end: function() {
+ return this.prevObject || jQuery( [] );
+ },
+
+ find: function( selector ) {
+ var elems = jQuery.map(this, function(elem){
+ return jQuery.find( selector, elem );
+ });
+
+ return this.pushStack( /[^+>] [^+>]/.test( selector ) || selector.indexOf("..") > -1 ?
+ jQuery.unique( elems ) :
+ elems );
+ },
+
+ clone: function( events ) {
+ // Do the clone
+ var ret = this.map(function(){
+ if ( jQuery.browser.msie && !jQuery.isXMLDoc(this) ) {
+ // IE copies events bound via attachEvent when
+ // using cloneNode. Calling detachEvent on the
+ // clone will also remove the events from the orignal
+ // In order to get around this, we use innerHTML.
+ // Unfortunately, this means some modifications to
+ // attributes in IE that are actually only stored
+ // as properties will not be copied (such as the
+ // the name attribute on an input).
+ var clone = this.cloneNode(true),
+ container = document.createElement("div");
+ container.appendChild(clone);
+ return jQuery.clean([container.innerHTML])[0];
+ } else
+ return this.cloneNode(true);
+ });
+
+ // Need to set the expando to null on the cloned set if it exists
+ // removeData doesn't work here, IE removes it from the original as well
+ // this is primarily for IE but the data expando shouldn't be copied over in any browser
+ var clone = ret.find("*").andSelf().each(function(){
+ if ( this[ expando ] != undefined )
+ this[ expando ] = null;
+ });
+
+ // Copy the events from the original to the clone
+ if ( events === true )
+ this.find("*").andSelf().each(function(i){
+ if (this.nodeType == 3)
+ return;
+ var events = jQuery.data( this, "events" );
+
+ for ( var type in events )
+ for ( var handler in events[ type ] )
+ jQuery.event.add( clone[ i ], type, events[ type ][ handler ], events[ type ][ handler ].data );
+ });
+
+ // Return the cloned set
+ return ret;
+ },
+
+ filter: function( selector ) {
+ return this.pushStack(
+ jQuery.isFunction( selector ) &&
+ jQuery.grep(this, function(elem, i){
+ return selector.call( elem, i );
+ }) ||
+
+ jQuery.multiFilter( selector, this ) );
+ },
+
+ not: function( selector ) {
+ if ( selector.constructor == String )
+ // test special case where just one selector is passed in
+ if ( isSimple.test( selector ) )
+ return this.pushStack( jQuery.multiFilter( selector, this, true ) );
+ else
+ selector = jQuery.multiFilter( selector, this );
+
+ var isArrayLike = selector.length && selector[selector.length - 1] !== undefined && !selector.nodeType;
+ return this.filter(function() {
+ return isArrayLike ? jQuery.inArray( this, selector ) < 0 : this != selector;
+ });
+ },
+
+ add: function( selector ) {
+ return this.pushStack( jQuery.unique( jQuery.merge(
+ this.get(),
+ typeof selector == 'string' ?
+ jQuery( selector ) :
+ jQuery.makeArray( selector )
+ )));
+ },
+
+ is: function( selector ) {
+ return !!selector && jQuery.multiFilter( selector, this ).length > 0;
+ },
+
+ hasClass: function( selector ) {
+ return this.is( "." + selector );
+ },
+
+ val: function( value ) {
+ if ( value == undefined ) {
+
+ if ( this.length ) {
+ var elem = this[0];
+
+ // We need to handle select boxes special
+ if ( jQuery.nodeName( elem, "select" ) ) {
+ var index = elem.selectedIndex,
+ values = [],
+ options = elem.options,
+ one = elem.type == "select-one";
+
+ // Nothing was selected
+ if ( index < 0 )
+ return null;
+
+ // Loop through all the selected options
+ for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) {
+ var option = options[ i ];
+
+ if ( option.selected ) {
+ // Get the specifc value for the option
+ value = jQuery.browser.msie && !option.attributes.value.specified ? option.text : option.value;
+
+ // We don't need an array for one selects
+ if ( one )
+ return value;
+
+ // Multi-Selects return an array
+ values.push( value );
+ }
+ }
+
+ return values;
+
+ // Everything else, we just grab the value
+ } else
+ return (this[0].value || "").replace(/\r/g, "");
+
+ }
+
+ return undefined;
+ }
+
+ if( value.constructor == Number )
+ value += '';
+
+ return this.each(function(){
+ if ( this.nodeType != 1 )
+ return;
+
+ if ( value.constructor == Array && /radio|checkbox/.test( this.type ) )
+ this.checked = (jQuery.inArray(this.value, value) >= 0 ||
+ jQuery.inArray(this.name, value) >= 0);
+
+ else if ( jQuery.nodeName( this, "select" ) ) {
+ var values = jQuery.makeArray(value);
+
+ jQuery( "option", this ).each(function(){
+ this.selected = (jQuery.inArray( this.value, values ) >= 0 ||
+ jQuery.inArray( this.text, values ) >= 0);
+ });
+
+ if ( !values.length )
+ this.selectedIndex = -1;
+
+ } else
+ this.value = value;
+ });
+ },
+
+ html: function( value ) {
+ return value == undefined ?
+ (this[0] ?
+ this[0].innerHTML :
+ null) :
+ this.empty().append( value );
+ },
+
+ replaceWith: function( value ) {
+ return this.after( value ).remove();
+ },
+
+ eq: function( i ) {
+ return this.slice( i, i + 1 );
+ },
+
+ slice: function() {
+ return this.pushStack( Array.prototype.slice.apply( this, arguments ) );
+ },
+
+ map: function( callback ) {
+ return this.pushStack( jQuery.map(this, function(elem, i){
+ return callback.call( elem, i, elem );
+ }));
+ },
+
+ andSelf: function() {
+ return this.add( this.prevObject );
+ },
+
+ data: function( key, value ){
+ var parts = key.split(".");
+ parts[1] = parts[1] ? "." + parts[1] : "";
+
+ if ( value === undefined ) {
+ var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);
+
+ if ( data === undefined && this.length )
+ data = jQuery.data( this[0], key );
+
+ return data === undefined && parts[1] ?
+ this.data( parts[0] ) :
+ data;
+ } else
+ return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function(){
+ jQuery.data( this, key, value );
+ });
+ },
+
+ removeData: function( key ){
+ return this.each(function(){
+ jQuery.removeData( this, key );
+ });
+ },
+
+ domManip: function( args, table, reverse, callback ) {
+ var clone = this.length > 1, elems;
+
+ return this.each(function(){
+ if ( !elems ) {
+ elems = jQuery.clean( args, this.ownerDocument );
+
+ if ( reverse )
+ elems.reverse();
+ }
+
+ var obj = this;
+
+ if ( table && jQuery.nodeName( this, "table" ) && jQuery.nodeName( elems[0], "tr" ) )
+ obj = this.getElementsByTagName("tbody")[0] || this.appendChild( this.ownerDocument.createElement("tbody") );
+
+ var scripts = jQuery( [] );
+
+ jQuery.each(elems, function(){
+ var elem = clone ?
+ jQuery( this ).clone( true )[0] :
+ this;
+
+ // execute all scripts after the elements have been injected
+ if ( jQuery.nodeName( elem, "script" ) )
+ scripts = scripts.add( elem );
+ else {
+ // Remove any inner scripts for later evaluation
+ if ( elem.nodeType == 1 )
+ scripts = scripts.add( jQuery( "script", elem ).remove() );
+
+ // Inject the elements into the document
+ callback.call( obj, elem );
+ }
+ });
+
+ scripts.each( evalScript );
+ });
+ }
+};
+
+// Give the init function the jQuery prototype for later instantiation
+jQuery.fn.init.prototype = jQuery.fn;
+
+function evalScript( i, elem ) {
+ if ( elem.src )
+ jQuery.ajax({
+ url: elem.src,
+ async: false,
+ dataType: "script"
+ });
+
+ else
+ jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" );
+
+ if ( elem.parentNode )
+ elem.parentNode.removeChild( elem );
+}
+
+function now(){
+ return +new Date;
+}
+
+jQuery.extend = jQuery.fn.extend = function() {
+ // copy reference to target object
+ var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options;
+
+ // Handle a deep copy situation
+ if ( target.constructor == Boolean ) {
+ deep = target;
+ target = arguments[1] || {};
+ // skip the boolean and the target
+ i = 2;
+ }
+
+ // Handle case when target is a string or something (possible in deep copy)
+ if ( typeof target != "object" && typeof target != "function" )
+ target = {};
+
+ // extend jQuery itself if only one argument is passed
+ if ( length == i ) {
+ target = this;
+ --i;
+ }
+
+ for ( ; i < length; i++ )
+ // Only deal with non-null/undefined values
+ if ( (options = arguments[ i ]) != null )
+ // Extend the base object
+ for ( var name in options ) {
+ var src = target[ name ], copy = options[ name ];
+
+ // Prevent never-ending loop
+ if ( target === copy )
+ continue;
+
+ // Recurse if we're merging object values
+ if ( deep && copy && typeof copy == "object" && !copy.nodeType )
+ target[ name ] = jQuery.extend( deep,
+ // Never move original objects, clone them
+ src || ( copy.length != null ? [ ] : { } )
+ , copy );
+
+ // Don't bring in undefined values
+ else if ( copy !== undefined )
+ target[ name ] = copy;
+
+ }
+
+ // Return the modified object
+ return target;
+};
+
+var expando = "jQuery" + now(), uuid = 0, windowData = {},
+ // exclude the following css properties to add px
+ exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i,
+ // cache defaultView
+ defaultView = document.defaultView || {};
+
+jQuery.extend({
+ noConflict: function( deep ) {
+ window.$ = _$;
+
+ if ( deep )
+ window.jQuery = _jQuery;
+
+ return jQuery;
+ },
+
+ // See test/unit/core.js for details concerning this function.
+ isFunction: function( fn ) {
+ return !!fn && typeof fn != "string" && !fn.nodeName &&
+ fn.constructor != Array && /^[\s[]?function/.test( fn + "" );
+ },
+
+ // check if an element is in a (or is an) XML document
+ isXMLDoc: function( elem ) {
+ return elem.documentElement && !elem.body ||
+ elem.tagName && elem.ownerDocument && !elem.ownerDocument.body;
+ },
+
+ // Evalulates a script in a global context
+ globalEval: function( data ) {
+ data = jQuery.trim( data );
+
+ if ( data ) {
+ // Inspired by code by Andrea Giammarchi
+ // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html
+ var head = document.getElementsByTagName("head")[0] || document.documentElement,
+ script = document.createElement("script");
+
+ script.type = "text/javascript";
+ if ( jQuery.browser.msie )
+ script.text = data;
+ else
+ script.appendChild( document.createTextNode( data ) );
+
+ // Use insertBefore instead of appendChild to circumvent an IE6 bug.
+ // This arises when a base node is used (#2709).
+ head.insertBefore( script, head.firstChild );
+ head.removeChild( script );
+ }
+ },
+
+ nodeName: function( elem, name ) {
+ return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase();
+ },
+
+ cache: {},
+
+ data: function( elem, name, data ) {
+ elem = elem == window ?
+ windowData :
+ elem;
+
+ var id = elem[ expando ];
+
+ // Compute a unique ID for the element
+ if ( !id )
+ id = elem[ expando ] = ++uuid;
+
+ // Only generate the data cache if we're
+ // trying to access or manipulate it
+ if ( name && !jQuery.cache[ id ] )
+ jQuery.cache[ id ] = {};
+
+ // Prevent overriding the named cache with undefined values
+ if ( data !== undefined )
+ jQuery.cache[ id ][ name ] = data;
+
+ // Return the named cache data, or the ID for the element
+ return name ?
+ jQuery.cache[ id ][ name ] :
+ id;
+ },
+
+ removeData: function( elem, name ) {
+ elem = elem == window ?
+ windowData :
+ elem;
+
+ var id = elem[ expando ];
+
+ // If we want to remove a specific section of the element's data
+ if ( name ) {
+ if ( jQuery.cache[ id ] ) {
+ // Remove the section of cache data
+ delete jQuery.cache[ id ][ name ];
+
+ // If we've removed all the data, remove the element's cache
+ name = "";
+
+ for ( name in jQuery.cache[ id ] )
+ break;
+
+ if ( !name )
+ jQuery.removeData( elem );
+ }
+
+ // Otherwise, we want to remove all of the element's data
+ } else {
+ // Clean up the element expando
+ try {
+ delete elem[ expando ];
+ } catch(e){
+ // IE has trouble directly removing the expando
+ // but it's ok with using removeAttribute
+ if ( elem.removeAttribute )
+ elem.removeAttribute( expando );
+ }
+
+ // Completely remove the data cache
+ delete jQuery.cache[ id ];
+ }
+ },
+
+ // args is for internal usage only
+ each: function( object, callback, args ) {
+ var name, i = 0, length = object.length;
+
+ if ( args ) {
+ if ( length == undefined ) {
+ for ( name in object )
+ if ( callback.apply( object[ name ], args ) === false )
+ break;
+ } else
+ for ( ; i < length; )
+ if ( callback.apply( object[ i++ ], args ) === false )
+ break;
+
+ // A special, fast, case for the most common use of each
+ } else {
+ if ( length == undefined ) {
+ for ( name in object )
+ if ( callback.call( object[ name ], name, object[ name ] ) === false )
+ break;
+ } else
+ for ( var value = object[0];
+ i < length && callback.call( value, i, value ) !== false; value = object[++i] ){}
+ }
+
+ return object;
+ },
+
+ prop: function( elem, value, type, i, name ) {
+ // Handle executable functions
+ if ( jQuery.isFunction( value ) )
+ value = value.call( elem, i );
+
+ // Handle passing in a number to a CSS property
+ return value && value.constructor == Number && type == "curCSS" && !exclude.test( name ) ?
+ value + "px" :
+ value;
+ },
+
+ className: {
+ // internal only, use addClass("class")
+ add: function( elem, classNames ) {
+ jQuery.each((classNames || "").split(/\s+/), function(i, className){
+ if ( elem.nodeType == 1 && !jQuery.className.has( elem.className, className ) )
+ elem.className += (elem.className ? " " : "") + className;
+ });
+ },
+
+ // internal only, use removeClass("class")
+ remove: function( elem, classNames ) {
+ if (elem.nodeType == 1)
+ elem.className = classNames != undefined ?
+ jQuery.grep(elem.className.split(/\s+/), function(className){
+ return !jQuery.className.has( classNames, className );
+ }).join(" ") :
+ "";
+ },
+
+ // internal only, use hasClass("class")
+ has: function( elem, className ) {
+ return jQuery.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1;
+ }
+ },
+
+ // A method for quickly swapping in/out CSS properties to get correct calculations
+ swap: function( elem, options, callback ) {
+ var old = {};
+ // Remember the old values, and insert the new ones
+ for ( var name in options ) {
+ old[ name ] = elem.style[ name ];
+ elem.style[ name ] = options[ name ];
+ }
+
+ callback.call( elem );
+
+ // Revert the old values
+ for ( var name in options )
+ elem.style[ name ] = old[ name ];
+ },
+
+ css: function( elem, name, force ) {
+ if ( name == "width" || name == "height" ) {
+ var val, props = { position: "absolute", visibility: "hidden", display:"block" }, which = name == "width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ];
+
+ function getWH() {
+ val = name == "width" ? elem.offsetWidth : elem.offsetHeight;
+ var padding = 0, border = 0;
+ jQuery.each( which, function() {
+ padding += parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0;
+ border += parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0;
+ });
+ val -= Math.round(padding + border);
+ }
+
+ if ( jQuery(elem).is(":visible") )
+ getWH();
+ else
+ jQuery.swap( elem, props, getWH );
+
+ return Math.max(0, val);
+ }
+
+ return jQuery.curCSS( elem, name, force );
+ },
+
+ curCSS: function( elem, name, force ) {
+ var ret, style = elem.style;
+
+ // A helper method for determining if an element's values are broken
+ function color( elem ) {
+ if ( !jQuery.browser.safari )
+ return false;
+
+ // defaultView is cached
+ var ret = defaultView.getComputedStyle( elem, null );
+ return !ret || ret.getPropertyValue("color") == "";
+ }
+
+ // We need to handle opacity special in IE
+ if ( name == "opacity" && jQuery.browser.msie ) {
+ ret = jQuery.attr( style, "opacity" );
+
+ return ret == "" ?
+ "1" :
+ ret;
+ }
+ // Opera sometimes will give the wrong display answer, this fixes it, see #2037
+ if ( jQuery.browser.opera && name == "display" ) {
+ var save = style.outline;
+ style.outline = "0 solid black";
+ style.outline = save;
+ }
+
+ // Make sure we're using the right name for getting the float value
+ if ( name.match( /float/i ) )
+ name = styleFloat;
+
+ if ( !force && style && style[ name ] )
+ ret = style[ name ];
+
+ else if ( defaultView.getComputedStyle ) {
+
+ // Only "float" is needed here
+ if ( name.match( /float/i ) )
+ name = "float";
+
+ name = name.replace( /([A-Z])/g, "-$1" ).toLowerCase();
+
+ var computedStyle = defaultView.getComputedStyle( elem, null );
+
+ if ( computedStyle && !color( elem ) )
+ ret = computedStyle.getPropertyValue( name );
+
+ // If the element isn't reporting its values properly in Safari
+ // then some display: none elements are involved
+ else {
+ var swap = [], stack = [], a = elem, i = 0;
+
+ // Locate all of the parent display: none elements
+ for ( ; a && color(a); a = a.parentNode )
+ stack.unshift(a);
+
+ // Go through and make them visible, but in reverse
+ // (It would be better if we knew the exact display type that they had)
+ for ( ; i < stack.length; i++ )
+ if ( color( stack[ i ] ) ) {
+ swap[ i ] = stack[ i ].style.display;
+ stack[ i ].style.display = "block";
+ }
+
+ // Since we flip the display style, we have to handle that
+ // one special, otherwise get the value
+ ret = name == "display" && swap[ stack.length - 1 ] != null ?
+ "none" :
+ ( computedStyle && computedStyle.getPropertyValue( name ) ) || "";
+
+ // Finally, revert the display styles back
+ for ( i = 0; i < swap.length; i++ )
+ if ( swap[ i ] != null )
+ stack[ i ].style.display = swap[ i ];
+ }
+
+ // We should always get a number back from opacity
+ if ( name == "opacity" && ret == "" )
+ ret = "1";
+
+ } else if ( elem.currentStyle ) {
+ var camelCase = name.replace(/\-(\w)/g, function(all, letter){
+ return letter.toUpperCase();
+ });
+
+ ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ];
+
+ // From the awesome hack by Dean Edwards
+ // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
+
+ // If we're not dealing with a regular pixel number
+ // but a number that has a weird ending, we need to convert it to pixels
+ if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) {
+ // Remember the original values
+ var left = style.left, rsLeft = elem.runtimeStyle.left;
+
+ // Put in the new values to get a computed value out
+ elem.runtimeStyle.left = elem.currentStyle.left;
+ style.left = ret || 0;
+ ret = style.pixelLeft + "px";
+
+ // Revert the changed values
+ style.left = left;
+ elem.runtimeStyle.left = rsLeft;
+ }
+ }
+
+ return ret;
+ },
+
+ clean: function( elems, context ) {
+ var ret = [];
+ context = context || document;
+ // !context.createElement fails in IE with an error but returns typeof 'object'
+ if (typeof context.createElement == 'undefined')
+ context = context.ownerDocument || context[0] && context[0].ownerDocument || document;
+
+ jQuery.each(elems, function(i, elem){
+ if ( !elem )
+ return;
+
+ if ( elem.constructor == Number )
+ elem += '';
+
+ // Convert html string into DOM nodes
+ if ( typeof elem == "string" ) {
+ // Fix "XHTML"-style tags in all browsers
+ elem = elem.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag){
+ return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ?
+ all :
+ front + "></" + tag + ">";
+ });
+
+ // Trim whitespace, otherwise indexOf won't work as expected
+ var tags = jQuery.trim( elem ).toLowerCase(), div = context.createElement("div");
+
+ var wrap =
+ // option or optgroup
+ !tags.indexOf("<opt") &&
+ [ 1, "<select multiple='multiple'>", "</select>" ] ||
+
+ !tags.indexOf("<leg") &&
+ [ 1, "<fieldset>", "</fieldset>" ] ||
+
+ tags.match(/^<(thead|tbody|tfoot|colg|cap)/) &&
+ [ 1, "<table>", "</table>" ] ||
+
+ !tags.indexOf("<tr") &&
+ [ 2, "<table><tbody>", "</tbody></table>" ] ||
+
+ // <thead> matched above
+ (!tags.indexOf("<td") || !tags.indexOf("<th")) &&
+ [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ] ||
+
+ !tags.indexOf("<col") &&
+ [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ] ||
+
+ // IE can't serialize <link> and <script> tags normally
+ jQuery.browser.msie &&
+ [ 1, "div<div>", "</div>" ] ||
+
+ [ 0, "", "" ];
+
+ // Go to html and back, then peel off extra wrappers
+ div.innerHTML = wrap[1] + elem + wrap[2];
+
+ // Move to the right depth
+ while ( wrap[0]-- )
+ div = div.lastChild;
+
+ // Remove IE's autoinserted <tbody> from table fragments
+ if ( jQuery.browser.msie ) {
+
+ // String was a <table>, *may* have spurious <tbody>
+ var tbody = !tags.indexOf("<table") && tags.indexOf("<tbody") < 0 ?
+ div.firstChild && div.firstChild.childNodes :
+
+ // String was a bare <thead> or <tfoot>
+ wrap[1] == "<table>" && tags.indexOf("<tbody") < 0 ?
+ div.childNodes :
+ [];
+
+ for ( var j = tbody.length - 1; j >= 0 ; --j )
+ if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length )
+ tbody[ j ].parentNode.removeChild( tbody[ j ] );
+
+ // IE completely kills leading whitespace when innerHTML is used
+ if ( /^\s/.test( elem ) )
+ div.insertBefore( context.createTextNode( elem.match(/^\s*/)[0] ), div.firstChild );
+
+ }
+
+ elem = jQuery.makeArray( div.childNodes );
+ }
+
+ if ( elem.length === 0 && (!jQuery.nodeName( elem, "form" ) && !jQuery.nodeName( elem, "select" )) )
+ return;
+
+ if ( elem[0] == undefined || jQuery.nodeName( elem, "form" ) || elem.options )
+ ret.push( elem );
+
+ else
+ ret = jQuery.merge( ret, elem );
+
+ });
+
+ return ret;
+ },
+
+ attr: function( elem, name, value ) {
+ // don't set attributes on text and comment nodes
+ if (!elem || elem.nodeType == 3 || elem.nodeType == 8)
+ return undefined;
+
+ var notxml = !jQuery.isXMLDoc( elem ),
+ // Whether we are setting (or getting)
+ set = value !== undefined,
+ msie = jQuery.browser.msie;
+
+ // Try to normalize/fix the name
+ name = notxml && jQuery.props[ name ] || name;
+
+ // Only do all the following if this is a node (faster for style)
+ // IE elem.getAttribute passes even for style
+ if ( elem.tagName ) {
+
+ // These attributes require special treatment
+ var special = /href|src|style/.test( name );
+
+ // Safari mis-reports the default selected property of a hidden option
+ // Accessing the parent's selectedIndex property fixes it
+ if ( name == "selected" && jQuery.browser.safari )
+ elem.parentNode.selectedIndex;
+
+ // If applicable, access the attribute via the DOM 0 way
+ if ( name in elem && notxml && !special ) {
+ if ( set ){
+ // We can't allow the type property to be changed (since it causes problems in IE)
+ if ( name == "type" && jQuery.nodeName( elem, "input" ) && elem.parentNode )
+ throw "type property can't be changed";
+
+ elem[ name ] = value;
+ }
+
+ // browsers index elements by id/name on forms, give priority to attributes.
+ if( jQuery.nodeName( elem, "form" ) && elem.getAttributeNode(name) )
+ return elem.getAttributeNode( name ).nodeValue;
+
+ return elem[ name ];
+ }
+
+ if ( msie && notxml && name == "style" )
+ return jQuery.attr( elem.style, "cssText", value );
+
+ if ( set )
+ // convert the value to a string (all browsers do this but IE) see #1070
+ elem.setAttribute( name, "" + value );
+
+ var attr = msie && notxml && special
+ // Some attributes require a special call on IE
+ ? elem.getAttribute( name, 2 )
+ : elem.getAttribute( name );
+
+ // Non-existent attributes return null, we normalize to undefined
+ return attr === null ? undefined : attr;
+ }
+
+ // elem is actually elem.style ... set the style
+
+ // IE uses filters for opacity
+ if ( msie && name == "opacity" ) {
+ if ( set ) {
+ // IE has trouble with opacity if it does not have layout
+ // Force it by setting the zoom level
+ elem.zoom = 1;
+
+ // Set the alpha filter to set the opacity
+ elem.filter = (elem.filter || "").replace( /alpha\([^)]*\)/, "" ) +
+ (parseInt( value ) + '' == "NaN" ? "" : "alpha(opacity=" + value * 100 + ")");
+ }
+
+ return elem.filter && elem.filter.indexOf("opacity=") >= 0 ?
+ (parseFloat( elem.filter.match(/opacity=([^)]*)/)[1] ) / 100) + '':
+ "";
+ }
+
+ name = name.replace(/-([a-z])/ig, function(all, letter){
+ return letter.toUpperCase();
+ });
+
+ if ( set )
+ elem[ name ] = value;
+
+ return elem[ name ];
+ },
+
+ trim: function( text ) {
+ return (text || "").replace( /^\s+|\s+$/g, "" );
+ },
+
+ makeArray: function( array ) {
+ var ret = [];
+
+ if( array != null ){
+ var i = array.length;
+ //the window, strings and functions also have 'length'
+ if( i == null || array.split || array.setInterval || array.call )
+ ret[0] = array;
+ else
+ while( i )
+ ret[--i] = array[i];
+ }
+
+ return ret;
+ },
+
+ inArray: function( elem, array ) {
+ for ( var i = 0, length = array.length; i < length; i++ )
+ // Use === because on IE, window == document
+ if ( array[ i ] === elem )
+ return i;
+
+ return -1;
+ },
+
+ merge: function( first, second ) {
+ // We have to loop this way because IE & Opera overwrite the length
+ // expando of getElementsByTagName
+ var i = 0, elem, pos = first.length;
+ // Also, we need to make sure that the correct elements are being returned
+ // (IE returns comment nodes in a '*' query)
+ if ( jQuery.browser.msie ) {
+ while ( elem = second[ i++ ] )
+ if ( elem.nodeType != 8 )
+ first[ pos++ ] = elem;
+
+ } else
+ while ( elem = second[ i++ ] )
+ first[ pos++ ] = elem;
+
+ return first;
+ },
+
+ unique: function( array ) {
+ var ret = [], done = {};
+
+ try {
+
+ for ( var i = 0, length = array.length; i < length; i++ ) {
+ var id = jQuery.data( array[ i ] );
+
+ if ( !done[ id ] ) {
+ done[ id ] = true;
+ ret.push( array[ i ] );
+ }
+ }
+
+ } catch( e ) {
+ ret = array;
+ }
+
+ return ret;
+ },
+
+ grep: function( elems, callback, inv ) {
+ var ret = [];
+
+ // Go through the array, only saving the items
+ // that pass the validator function
+ for ( var i = 0, length = elems.length; i < length; i++ )
+ if ( !inv != !callback( elems[ i ], i ) )
+ ret.push( elems[ i ] );
+
+ return ret;
+ },
+
+ map: function( elems, callback ) {
+ var ret = [];
+
+ // Go through the array, translating each of the items to their
+ // new value (or values).
+ for ( var i = 0, length = elems.length; i < length; i++ ) {
+ var value = callback( elems[ i ], i );
+
+ if ( value != null )
+ ret[ ret.length ] = value;
+ }
+
+ return ret.concat.apply( [], ret );
+ }
+});
+
+var userAgent = navigator.userAgent.toLowerCase();
+
+// Figure out what browser is being used
+jQuery.browser = {
+ version: (userAgent.match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [])[1],
+ safari: /webkit/.test( userAgent ),
+ opera: /opera/.test( userAgent ),
+ msie: /msie/.test( userAgent ) && !/opera/.test( userAgent ),
+ mozilla: /mozilla/.test( userAgent ) && !/(compatible|webkit)/.test( userAgent )
+};
+
+var styleFloat = jQuery.browser.msie ?
+ "styleFloat" :
+ "cssFloat";
+
+jQuery.extend({
+ // Check to see if the W3C box model is being used
+ boxModel: !jQuery.browser.msie || document.compatMode == "CSS1Compat",
+
+ props: {
+ "for": "htmlFor",
+ "class": "className",
+ "float": styleFloat,
+ cssFloat: styleFloat,
+ styleFloat: styleFloat,
+ readonly: "readOnly",
+ maxlength: "maxLength",
+ cellspacing: "cellSpacing"
+ }
+});
+
+jQuery.each({
+ parent: function(elem){return elem.parentNode;},
+ parents: function(elem){return jQuery.dir(elem,"parentNode");},
+ next: function(elem){return jQuery.nth(elem,2,"nextSibling");},
+ prev: function(elem){return jQuery.nth(elem,2,"previousSibling");},
+ nextAll: function(elem){return jQuery.dir(elem,"nextSibling");},
+ prevAll: function(elem){return jQuery.dir(elem,"previousSibling");},
+ siblings: function(elem){return jQuery.sibling(elem.parentNode.firstChild,elem);},
+ children: function(elem){return jQuery.sibling(elem.firstChild);},
+ contents: function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.makeArray(elem.childNodes);}
+}, function(name, fn){
+ jQuery.fn[ name ] = function( selector ) {
+ var ret = jQuery.map( this, fn );
+
+ if ( selector && typeof selector == "string" )
+ ret = jQuery.multiFilter( selector, ret );
+
+ return this.pushStack( jQuery.unique( ret ) );
+ };
+});
+
+jQuery.each({
+ appendTo: "append",
+ prependTo: "prepend",
+ insertBefore: "before",
+ insertAfter: "after",
+ replaceAll: "replaceWith"
+}, function(name, original){
+ jQuery.fn[ name ] = function() {
+ var args = arguments;
+
+ return this.each(function(){
+ for ( var i = 0, length = args.length; i < length; i++ )
+ jQuery( args[ i ] )[ original ]( this );
+ });
+ };
+});
+
+jQuery.each({
+ removeAttr: function( name ) {
+ jQuery.attr( this, name, "" );
+ if (this.nodeType == 1)
+ this.removeAttribute( name );
+ },
+
+ addClass: function( classNames ) {
+ jQuery.className.add( this, classNames );
+ },
+
+ removeClass: function( classNames ) {
+ jQuery.className.remove( this, classNames );
+ },
+
+ toggleClass: function( classNames ) {
+ jQuery.className[ jQuery.className.has( this, classNames ) ? "remove" : "add" ]( this, classNames );
+ },
+
+ remove: function( selector ) {
+ if ( !selector || jQuery.filter( selector, [ this ] ).r.length ) {
+ // Prevent memory leaks
+ jQuery( "*", this ).add(this).each(function(){
+ jQuery.event.remove(this);
+ jQuery.removeData(this);
+ });
+ if (this.parentNode)
+ this.parentNode.removeChild( this );
+ }
+ },
+
+ empty: function() {
+ // Remove element nodes and prevent memory leaks
+ jQuery( ">*", this ).remove();
+
+ // Remove any remaining nodes
+ while ( this.firstChild )
+ this.removeChild( this.firstChild );
+ }
+}, function(name, fn){
+ jQuery.fn[ name ] = function(){
+ return this.each( fn, arguments );
+ };
+});
+
+jQuery.each([ "Height", "Width" ], function(i, name){
+ var type = name.toLowerCase();
+
+ jQuery.fn[ type ] = function( size ) {
+ // Get window width or height
+ return this[0] == window ?
+ // Opera reports document.body.client[Width/Height] properly in both quirks and standards
+ jQuery.browser.opera && document.body[ "client" + name ] ||
+
+ // Safari reports inner[Width/Height] just fine (Mozilla and Opera include scroll bar widths)
+ jQuery.browser.safari && window[ "inner" + name ] ||
+
+ // Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode
+ document.compatMode == "CSS1Compat" && document.documentElement[ "client" + name ] || document.body[ "client" + name ] :
+
+ // Get document width or height
+ this[0] == document ?
+ // Either scroll[Width/Height] or offset[Width/Height], whichever is greater
+ Math.max(
+ Math.max(document.body["scroll" + name], document.documentElement["scroll" + name]),
+ Math.max(document.body["offset" + name], document.documentElement["offset" + name])
+ ) :
+
+ // Get or set width or height on the element
+ size == undefined ?
+ // Get width or height on the element
+ (this.length ? jQuery.css( this[0], type ) : null) :
+
+ // Set the width or height on the element (default to pixels if value is unitless)
+ this.css( type, size.constructor == String ? size : size + "px" );
+ };
+});
+
+// Helper function used by the dimensions and offset modules
+function num(elem, prop) {
+ return elem[0] && parseInt( jQuery.curCSS(elem[0], prop, true), 10 ) || 0;
+}var chars = jQuery.browser.safari && parseInt(jQuery.browser.version) < 417 ?
+ "(?:[\\w*_-]|\\\\.)" :
+ "(?:[\\w\u0128-\uFFFF*_-]|\\\\.)",
+ quickChild = new RegExp("^>\\s*(" + chars + "+)"),
+ quickID = new RegExp("^(" + chars + "+)(#)(" + chars + "+)"),
+ quickClass = new RegExp("^([#.]?)(" + chars + "*)");
+
+jQuery.extend({
+ expr: {
+ "": function(a,i,m){return m[2]=="*"||jQuery.nodeName(a,m[2]);},
+ "#": function(a,i,m){return a.getAttribute("id")==m[2];},
+ ":": {
+ // Position Checks
+ lt: function(a,i,m){return i<m[3]-0;},
+ gt: function(a,i,m){return i>m[3]-0;},
+ nth: function(a,i,m){return m[3]-0==i;},
+ eq: function(a,i,m){return m[3]-0==i;},
+ first: function(a,i){return i==0;},
+ last: function(a,i,m,r){return i==r.length-1;},
+ even: function(a,i){return i%2==0;},
+ odd: function(a,i){return i%2;},
+
+ // Child Checks
+ "first-child": function(a){return a.parentNode.getElementsByTagName("*")[0]==a;},
+ "last-child": function(a){return jQuery.nth(a.parentNode.lastChild,1,"previousSibling")==a;},
+ "only-child": function(a){return !jQuery.nth(a.parentNode.lastChild,2,"previousSibling");},
+
+ // Parent Checks
+ parent: function(a){return a.firstChild;},
+ empty: function(a){return !a.firstChild;},
+
+ // Text Check
+ contains: function(a,i,m){return (a.textContent||a.innerText||jQuery(a).text()||"").indexOf(m[3])>=0;},
+
+ // Visibility
+ visible: function(a){return "hidden"!=a.type&&jQuery.css(a,"display")!="none"&&jQuery.css(a,"visibility")!="hidden";},
+ hidden: function(a){return "hidden"==a.type||jQuery.css(a,"display")=="none"||jQuery.css(a,"visibility")=="hidden";},
+
+ // Form attributes
+ enabled: function(a){return !a.disabled;},
+ disabled: function(a){return a.disabled;},
+ checked: function(a){return a.checked;},
+ selected: function(a){return a.selected||jQuery.attr(a,"selected");},
+
+ // Form elements
+ text: function(a){return "text"==a.type;},
+ radio: function(a){return "radio"==a.type;},
+ checkbox: function(a){return "checkbox"==a.type;},
+ file: function(a){return "file"==a.type;},
+ password: function(a){return "password"==a.type;},
+ submit: function(a){return "submit"==a.type;},
+ image: function(a){return "image"==a.type;},
+ reset: function(a){return "reset"==a.type;},
+ button: function(a){return "button"==a.type||jQuery.nodeName(a,"button");},
+ input: function(a){return /input|select|textarea|button/i.test(a.nodeName);},
+
+ // :has()
+ has: function(a,i,m){return jQuery.find(m[3],a).length;},
+
+ // :header
+ header: function(a){return /h\d/i.test(a.nodeName);},
+
+ // :animated
+ animated: function(a){return jQuery.grep(jQuery.timers,function(fn){return a==fn.elem;}).length;}
+ }
+ },
+
+ // The regular expressions that power the parsing engine
+ parse: [
+ // Match: [@value='test'], [@foo]
+ /^(\[) *@?([\w-]+) *([!*$^~=]*) *('?"?)(.*?)\4 *\]/,
+
+ // Match: :contains('foo')
+ /^(:)([\w-]+)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/,
+
+ // Match: :even, :last-child, #id, .class
+ new RegExp("^([:.#]*)(" + chars + "+)")
+ ],
+
+ multiFilter: function( expr, elems, not ) {
+ var old, cur = [];
+
+ while ( expr && expr != old ) {
+ old = expr;
+ var f = jQuery.filter( expr, elems, not );
+ expr = f.t.replace(/^\s*,\s*/, "" );
+ cur = not ? elems = f.r : jQuery.merge( cur, f.r );
+ }
+
+ return cur;
+ },
+
+ find: function( t, context ) {
+ // Quickly handle non-string expressions
+ if ( typeof t != "string" )
+ return [ t ];
+
+ // check to make sure context is a DOM element or a document
+ if ( context && context.nodeType != 1 && context.nodeType != 9)
+ return [ ];
+
+ // Set the correct context (if none is provided)
+ context = context || document;
+
+ // Initialize the search
+ var ret = [context], done = [], last, nodeName;
+
+ // Continue while a selector expression exists, and while
+ // we're no longer looping upon ourselves
+ while ( t && last != t ) {
+ var r = [];
+ last = t;
+
+ t = jQuery.trim(t);
+
+ var foundToken = false,
+
+ // An attempt at speeding up child selectors that
+ // point to a specific element tag
+ re = quickChild,
+
+ m = re.exec(t);
+
+ if ( m ) {
+ nodeName = m[1].toUpperCase();
+
+ // Perform our own iteration and filter
+ for ( var i = 0; ret[i]; i++ )
+ for ( var c = ret[i].firstChild; c; c = c.nextSibling )
+ if ( c.nodeType == 1 && (nodeName == "*" || c.nodeName.toUpperCase() == nodeName) )
+ r.push( c );
+
+ ret = r;
+ t = t.replace( re, "" );
+ if ( t.indexOf(" ") == 0 ) continue;
+ foundToken = true;
+ } else {
+ re = /^([>+~])\s*(\w*)/i;
+
+ if ( (m = re.exec(t)) != null ) {
+ r = [];
+
+ var merge = {};
+ nodeName = m[2].toUpperCase();
+ m = m[1];
+
+ for ( var j = 0, rl = ret.length; j < rl; j++ ) {
+ var n = m == "~" || m == "+" ? ret[j].nextSibling : ret[j].firstChild;
+ for ( ; n; n = n.nextSibling )
+ if ( n.nodeType == 1 ) {
+ var id = jQuery.data(n);
+
+ if ( m == "~" && merge[id] ) break;
+
+ if (!nodeName || n.nodeName.toUpperCase() == nodeName ) {
+ if ( m == "~" ) merge[id] = true;
+ r.push( n );
+ }
+
+ if ( m == "+" ) break;
+ }
+ }
+
+ ret = r;
+
+ // And remove the token
+ t = jQuery.trim( t.replace( re, "" ) );
+ foundToken = true;
+ }
+ }
+
+ // See if there's still an expression, and that we haven't already
+ // matched a token
+ if ( t && !foundToken ) {
+ // Handle multiple expressions
+ if ( !t.indexOf(",") ) {
+ // Clean the result set
+ if ( context == ret[0] ) ret.shift();
+
+ // Merge the result sets
+ done = jQuery.merge( done, ret );
+
+ // Reset the context
+ r = ret = [context];
+
+ // Touch up the selector string
+ t = " " + t.substr(1,t.length);
+
+ } else {
+ // Optimize for the case nodeName#idName
+ var re2 = quickID;
+ var m = re2.exec(t);
+
+ // Re-organize the results, so that they're consistent
+ if ( m ) {
+ m = [ 0, m[2], m[3], m[1] ];
+
+ } else {
+ // Otherwise, do a traditional filter check for
+ // ID, class, and element selectors
+ re2 = quickClass;
+ m = re2.exec(t);
+ }
+
+ m[2] = m[2].replace(/\\/g, "");
+
+ var elem = ret[ret.length-1];
+
+ // Try to do a global search by ID, where we can
+ if ( m[1] == "#" && elem && elem.getElementById && !jQuery.isXMLDoc(elem) ) {
+ // Optimization for HTML document case
+ var oid = elem.getElementById(m[2]);
+
+ // Do a quick check for the existence of the actual ID attribute
+ // to avoid selecting by the name attribute in IE
+ // also check to insure id is a string to avoid selecting an element with the name of 'id' inside a form
+ if ( (jQuery.browser.msie||jQuery.browser.opera) && oid && typeof oid.id == "string" && oid.id != m[2] )
+ oid = jQuery('[@id="'+m[2]+'"]', elem)[0];
+
+ // Do a quick check for node name (where applicable) so
+ // that div#foo searches will be really fast
+ ret = r = oid && (!m[3] || jQuery.nodeName(oid, m[3])) ? [oid] : [];
+ } else {
+ // We need to find all descendant elements
+ for ( var i = 0; ret[i]; i++ ) {
+ // Grab the tag name being searched for
+ var tag = m[1] == "#" && m[3] ? m[3] : m[1] != "" || m[0] == "" ? "*" : m[2];
+
+ // Handle IE7 being really dumb about <object>s
+ if ( tag == "*" && ret[i].nodeName.toLowerCase() == "object" )
+ tag = "param";
+
+ r = jQuery.merge( r, ret[i].getElementsByTagName( tag ));
+ }
+
+ // It's faster to filter by class and be done with it
+ if ( m[1] == "." )
+ r = jQuery.classFilter( r, m[2] );
+
+ // Same with ID filtering
+ if ( m[1] == "#" ) {
+ var tmp = [];
+
+ // Try to find the element with the ID
+ for ( var i = 0; r[i]; i++ )
+ if ( r[i].getAttribute("id") == m[2] ) {
+ tmp = [ r[i] ];
+ break;
+ }
+
+ r = tmp;
+ }
+
+ ret = r;
+ }
+
+ t = t.replace( re2, "" );
+ }
+
+ }
+
+ // If a selector string still exists
+ if ( t ) {
+ // Attempt to filter it
+ var val = jQuery.filter(t,r);
+ ret = r = val.r;
+ t = jQuery.trim(val.t);
+ }
+ }
+
+ // An error occurred with the selector;
+ // just return an empty set instead
+ if ( t )
+ ret = [];
+
+ // Remove the root context
+ if ( ret && context == ret[0] )
+ ret.shift();
+
+ // And combine the results
+ done = jQuery.merge( done, ret );
+
+ return done;
+ },
+
+ classFilter: function(r,m,not){
+ m = " " + m + " ";
+ var tmp = [];
+ for ( var i = 0; r[i]; i++ ) {
+ var pass = (" " + r[i].className + " ").indexOf( m ) >= 0;
+ if ( !not && pass || not && !pass )
+ tmp.push( r[i] );
+ }
+ return tmp;
+ },
+
+ filter: function(t,r,not) {
+ var last;
+
+ // Look for common filter expressions
+ while ( t && t != last ) {
+ last = t;
+
+ var p = jQuery.parse, m;
+
+ for ( var i = 0; p[i]; i++ ) {
+ m = p[i].exec( t );
+
+ if ( m ) {
+ // Remove what we just matched
+ t = t.substring( m[0].length );
+
+ m[2] = m[2].replace(/\\/g, "");
+ break;
+ }
+ }
+
+ if ( !m )
+ break;
+
+ // :not() is a special case that can be optimized by
+ // keeping it out of the expression list
+ if ( m[1] == ":" && m[2] == "not" )
+ // optimize if only one selector found (most common case)
+ r = isSimple.test( m[3] ) ?
+ jQuery.filter(m[3], r, true).r :
+ jQuery( r ).not( m[3] );
+
+ // We can get a big speed boost by filtering by class here
+ else if ( m[1] == "." )
+ r = jQuery.classFilter(r, m[2], not);
+
+ else if ( m[1] == "[" ) {
+ var tmp = [], type = m[3];
+
+ for ( var i = 0, rl = r.length; i < rl; i++ ) {
+ var a = r[i], z = a[ jQuery.props[m[2]] || m[2] ];
+
+ if ( z == null || /href|src|selected/.test(m[2]) )
+ z = jQuery.attr(a,m[2]) || '';
+
+ if ( (type == "" && !!z ||
+ type == "=" && z == m[5] ||
+ type == "!=" && z != m[5] ||
+ type == "^=" && z && !z.indexOf(m[5]) ||
+ type == "$=" && z.substr(z.length - m[5].length) == m[5] ||
+ (type == "*=" || type == "~=") && z.indexOf(m[5]) >= 0) ^ not )
+ tmp.push( a );
+ }
+
+ r = tmp;
+
+ // We can get a speed boost by handling nth-child here
+ } else if ( m[1] == ":" && m[2] == "nth-child" ) {
+ var merge = {}, tmp = [],
+ // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
+ test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
+ m[3] == "even" && "2n" || m[3] == "odd" && "2n+1" ||
+ !/\D/.test(m[3]) && "0n+" + m[3] || m[3]),
+ // calculate the numbers (first)n+(last) including if they are negative
+ first = (test[1] + (test[2] || 1)) - 0, last = test[3] - 0;
+
+ // loop through all the elements left in the jQuery object
+ for ( var i = 0, rl = r.length; i < rl; i++ ) {
+ var node = r[i], parentNode = node.parentNode, id = jQuery.data(parentNode);
+
+ if ( !merge[id] ) {
+ var c = 1;
+
+ for ( var n = parentNode.firstChild; n; n = n.nextSibling )
+ if ( n.nodeType == 1 )
+ n.nodeIndex = c++;
+
+ merge[id] = true;
+ }
+
+ var add = false;
+
+ if ( first == 0 ) {
+ if ( node.nodeIndex == last )
+ add = true;
+ } else if ( (node.nodeIndex - last) % first == 0 && (node.nodeIndex - last) / first >= 0 )
+ add = true;
+
+ if ( add ^ not )
+ tmp.push( node );
+ }
+
+ r = tmp;
+
+ // Otherwise, find the expression to execute
+ } else {
+ var fn = jQuery.expr[ m[1] ];
+ if ( typeof fn == "object" )
+ fn = fn[ m[2] ];
+
+ if ( typeof fn == "string" )
+ fn = eval("false||function(a,i){return " + fn + ";}");
+
+ // Execute it against the current filter
+ r = jQuery.grep( r, function(elem, i){
+ return fn(elem, i, m, r);
+ }, not );
+ }
+ }
+
+ // Return an array of filtered elements (r)
+ // and the modified expression string (t)
+ return { r: r, t: t };
+ },
+
+ dir: function( elem, dir ){
+ var matched = [],
+ cur = elem[dir];
+ while ( cur && cur != document ) {
+ if ( cur.nodeType == 1 )
+ matched.push( cur );
+ cur = cur[dir];
+ }
+ return matched;
+ },
+
+ nth: function(cur,result,dir,elem){
+ result = result || 1;
+ var num = 0;
+
+ for ( ; cur; cur = cur[dir] )
+ if ( cur.nodeType == 1 && ++num == result )
+ break;
+
+ return cur;
+ },
+
+ sibling: function( n, elem ) {
+ var r = [];
+
+ for ( ; n; n = n.nextSibling ) {
+ if ( n.nodeType == 1 && n != elem )
+ r.push( n );
+ }
+
+ return r;
+ }
+});
+/*
+ * A number of helper functions used for managing events.
+ * Many of the ideas behind this code orignated from
+ * Dean Edwards' addEvent library.
+ */
+jQuery.event = {
+
+ // Bind an event to an element
+ // Original by Dean Edwards
+ add: function(elem, types, handler, data) {
+ if ( elem.nodeType == 3 || elem.nodeType == 8 )
+ return;
+
+ // For whatever reason, IE has trouble passing the window object
+ // around, causing it to be cloned in the process
+ if ( jQuery.browser.msie && elem.setInterval )
+ elem = window;
+
+ // Make sure that the function being executed has a unique ID
+ if ( !handler.guid )
+ handler.guid = this.guid++;
+
+ // if data is passed, bind to handler
+ if( data != undefined ) {
+ // Create temporary function pointer to original handler
+ var fn = handler;
+
+ // Create unique handler function, wrapped around original handler
+ handler = this.proxy( fn, function() {
+ // Pass arguments and context to original handler
+ return fn.apply(this, arguments);
+ });
+
+ // Store data in unique handler
+ handler.data = data;
+ }
+
+ // Init the element's event structure
+ var events = jQuery.data(elem, "events") || jQuery.data(elem, "events", {}),
+ handle = jQuery.data(elem, "handle") || jQuery.data(elem, "handle", function(){
+ // Handle the second event of a trigger and when
+ // an event is called after a page has unloaded
+ if ( typeof jQuery != "undefined" && !jQuery.event.triggered )
+ return jQuery.event.handle.apply(arguments.callee.elem, arguments);
+ });
+ // Add elem as a property of the handle function
+ // This is to prevent a memory leak with non-native
+ // event in IE.
+ handle.elem = elem;
+
+ // Handle multiple events separated by a space
+ // jQuery(...).bind("mouseover mouseout", fn);
+ jQuery.each(types.split(/\s+/), function(index, type) {
+ // Namespaced event handlers
+ var parts = type.split(".");
+ type = parts[0];
+ handler.type = parts[1];
+
+ // Get the current list of functions bound to this event
+ var handlers = events[type];
+
+ // Init the event handler queue
+ if (!handlers) {
+ handlers = events[type] = {};
+
+ // Check for a special event handler
+ // Only use addEventListener/attachEvent if the special
+ // events handler returns false
+ if ( !jQuery.event.special[type] || jQuery.event.special[type].setup.call(elem) === false ) {
+ // Bind the global event handler to the element
+ if (elem.addEventListener)
+ elem.addEventListener(type, handle, false);
+ else if (elem.attachEvent)
+ elem.attachEvent("on" + type, handle);
+ }
+ }
+
+ // Add the function to the element's handler list
+ handlers[handler.guid] = handler;
+
+ // Keep track of which events have been used, for global triggering
+ jQuery.event.global[type] = true;
+ });
+
+ // Nullify elem to prevent memory leaks in IE
+ elem = null;
+ },
+
+ guid: 1,
+ global: {},
+
+ // Detach an event or set of events from an element
+ remove: function(elem, types, handler) {
+ // don't do events on text and comment nodes
+ if ( elem.nodeType == 3 || elem.nodeType == 8 )
+ return;
+
+ var events = jQuery.data(elem, "events"), ret, index;
+
+ if ( events ) {
+ // Unbind all events for the element
+ if ( types == undefined || (typeof types == "string" && types.charAt(0) == ".") )
+ for ( var type in events )
+ this.remove( elem, type + (types || "") );
+ else {
+ // types is actually an event object here
+ if ( types.type ) {
+ handler = types.handler;
+ types = types.type;
+ }
+
+ // Handle multiple events seperated by a space
+ // jQuery(...).unbind("mouseover mouseout", fn);
+ jQuery.each(types.split(/\s+/), function(index, type){
+ // Namespaced event handlers
+ var parts = type.split(".");
+ type = parts[0];
+
+ if ( events[type] ) {
+ // remove the given handler for the given type
+ if ( handler )
+ delete events[type][handler.guid];
+
+ // remove all handlers for the given type
+ else
+ for ( handler in events[type] )
+ // Handle the removal of namespaced events
+ if ( !parts[1] || events[type][handler].type == parts[1] )
+ delete events[type][handler];
+
+ // remove generic event handler if no more handlers exist
+ for ( ret in events[type] ) break;
+ if ( !ret ) {
+ if ( !jQuery.event.special[type] || jQuery.event.special[type].teardown.call(elem) === false ) {
+ if (elem.removeEventListener)
+ elem.removeEventListener(type, jQuery.data(elem, "handle"), false);
+ else if (elem.detachEvent)
+ elem.detachEvent("on" + type, jQuery.data(elem, "handle"));
+ }
+ ret = null;
+ delete events[type];
+ }
+ }
+ });
+ }
+
+ // Remove the expando if it's no longer used
+ for ( ret in events ) break;
+ if ( !ret ) {
+ var handle = jQuery.data( elem, "handle" );
+ if ( handle ) handle.elem = null;
+ jQuery.removeData( elem, "events" );
+ jQuery.removeData( elem, "handle" );
+ }
+ }
+ },
+
+ trigger: function(type, data, elem, donative, extra) {
+ // Clone the incoming data, if any
+ data = jQuery.makeArray(data);
+
+ if ( type.indexOf("!") >= 0 ) {
+ type = type.slice(0, -1);
+ var exclusive = true;
+ }
+
+ // Handle a global trigger
+ if ( !elem ) {
+ // Only trigger if we've ever bound an event for it
+ if ( this.global[type] )
+ jQuery("*").add([window, document]).trigger(type, data);
+
+ // Handle triggering a single element
+ } else {
+ // don't do events on text and comment nodes
+ if ( elem.nodeType == 3 || elem.nodeType == 8 )
+ return undefined;
+
+ var val, ret, fn = jQuery.isFunction( elem[ type ] || null ),
+ // Check to see if we need to provide a fake event, or not
+ event = !data[0] || !data[0].preventDefault;
+
+ // Pass along a fake event
+ if ( event ) {
+ data.unshift({
+ type: type,
+ target: elem,
+ preventDefault: function(){},
+ stopPropagation: function(){},
+ timeStamp: now()
+ });
+ data[0][expando] = true; // no need to fix fake event
+ }
+
+ // Enforce the right trigger type
+ data[0].type = type;
+ if ( exclusive )
+ data[0].exclusive = true;
+
+ // Trigger the event, it is assumed that "handle" is a function
+ var handle = jQuery.data(elem, "handle");
+ if ( handle )
+ val = handle.apply( elem, data );
+
+ // Handle triggering native .onfoo handlers (and on links since we don't call .click() for links)
+ if ( (!fn || (jQuery.nodeName(elem, 'a') && type == "click")) && elem["on"+type] && elem["on"+type].apply( elem, data ) === false )
+ val = false;
+
+ // Extra functions don't get the custom event object
+ if ( event )
+ data.shift();
+
+ // Handle triggering of extra function
+ if ( extra && jQuery.isFunction( extra ) ) {
+ // call the extra function and tack the current return value on the end for possible inspection
+ ret = extra.apply( elem, val == null ? data : data.concat( val ) );
+ // if anything is returned, give it precedence and have it overwrite the previous value
+ if (ret !== undefined)
+ val = ret;
+ }
+
+ // Trigger the native events (except for clicks on links)
+ if ( fn && donative !== false && val !== false && !(jQuery.nodeName(elem, 'a') && type == "click") ) {
+ this.triggered = true;
+ try {
+ elem[ type ]();
+ // prevent IE from throwing an error for some hidden elements
+ } catch (e) {}
+ }
+
+ this.triggered = false;
+ }
+
+ return val;
+ },
+
+ handle: function(event) {
+ // returned undefined or false
+ var val, ret, namespace, all, handlers;
+
+ event = arguments[0] = jQuery.event.fix( event || window.event );
+
+ // Namespaced event handlers
+ namespace = event.type.split(".");
+ event.type = namespace[0];
+ namespace = namespace[1];
+ // Cache this now, all = true means, any handler
+ all = !namespace && !event.exclusive;
+
+ handlers = ( jQuery.data(this, "events") || {} )[event.type];
+
+ for ( var j in handlers ) {
+ var handler = handlers[j];
+
+ // Filter the functions by class
+ if ( all || handler.type == namespace ) {
+ // Pass in a reference to the handler function itself
+ // So that we can later remove it
+ event.handler = handler;
+ event.data = handler.data;
+
+ ret = handler.apply( this, arguments );
+
+ if ( val !== false )
+ val = ret;
+
+ if ( ret === false ) {
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ }
+ }
+
+ return val;
+ },
+
+ fix: function(event) {
+ if ( event[expando] == true )
+ return event;
+
+ // store a copy of the original event object
+ // and "clone" to set read-only properties
+ var originalEvent = event;
+ event = { originalEvent: originalEvent };
+ var props = "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target timeStamp toElement type view wheelDelta which".split(" ");
+ for ( var i=props.length; i; i-- )
+ event[ props[i] ] = originalEvent[ props[i] ];
+
+ // Mark it as fixed
+ event[expando] = true;
+
+ // add preventDefault and stopPropagation since
+ // they will not work on the clone
+ event.preventDefault = function() {
+ // if preventDefault exists run it on the original event
+ if (originalEvent.preventDefault)
+ originalEvent.preventDefault();
+ // otherwise set the returnValue property of the original event to false (IE)
+ originalEvent.returnValue = false;
+ };
+ event.stopPropagation = function() {
+ // if stopPropagation exists run it on the original event
+ if (originalEvent.stopPropagation)
+ originalEvent.stopPropagation();
+ // otherwise set the cancelBubble property of the original event to true (IE)
+ originalEvent.cancelBubble = true;
+ };
+
+ // Fix timeStamp
+ event.timeStamp = event.timeStamp || now();
+
+ // Fix target property, if necessary
+ if ( !event.target )
+ event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either
+
+ // check if target is a textnode (safari)
+ if ( event.target.nodeType == 3 )
+ event.target = event.target.parentNode;
+
+ // Add relatedTarget, if necessary
+ if ( !event.relatedTarget && event.fromElement )
+ event.relatedTarget = event.fromElement == event.target ? event.toElement : event.fromElement;
+
+ // Calculate pageX/Y if missing and clientX/Y available
+ if ( event.pageX == null && event.clientX != null ) {
+ var doc = document.documentElement, body = document.body;
+ event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc.clientLeft || 0);
+ event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc.clientTop || 0);
+ }
+
+ // Add which for key events
+ if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) )
+ event.which = event.charCode || event.keyCode;
+
+ // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
+ if ( !event.metaKey && event.ctrlKey )
+ event.metaKey = event.ctrlKey;
+
+ // Add which for click: 1 == left; 2 == middle; 3 == right
+ // Note: button is not normalized, so don't use it
+ if ( !event.which && event.button )
+ event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
+
+ return event;
+ },
+
+ proxy: function( fn, proxy ){
+ // Set the guid of unique handler to the same of original handler, so it can be removed
+ proxy.guid = fn.guid = fn.guid || proxy.guid || this.guid++;
+ // So proxy can be declared as an argument
+ return proxy;
+ },
+
+ special: {
+ ready: {
+ setup: function() {
+ // Make sure the ready event is setup
+ bindReady();
+ return;
+ },
+
+ teardown: function() { return; }
+ },
+
+ mouseenter: {
+ setup: function() {
+ if ( jQuery.browser.msie ) return false;
+ jQuery(this).bind("mouseover", jQuery.event.special.mouseenter.handler);
+ return true;
+ },
+
+ teardown: function() {
+ if ( jQuery.browser.msie ) return false;
+ jQuery(this).unbind("mouseover", jQuery.event.special.mouseenter.handler);
+ return true;
+ },
+
+ handler: function(event) {
+ // If we actually just moused on to a sub-element, ignore it
+ if ( withinElement(event, this) ) return true;
+ // Execute the right handlers by setting the event type to mouseenter
+ event.type = "mouseenter";
+ return jQuery.event.handle.apply(this, arguments);
+ }
+ },
+
+ mouseleave: {
+ setup: function() {
+ if ( jQuery.browser.msie ) return false;
+ jQuery(this).bind("mouseout", jQuery.event.special.mouseleave.handler);
+ return true;
+ },
+
+ teardown: function() {
+ if ( jQuery.browser.msie ) return false;
+ jQuery(this).unbind("mouseout", jQuery.event.special.mouseleave.handler);
+ return true;
+ },
+
+ handler: function(event) {
+ // If we actually just moused on to a sub-element, ignore it
+ if ( withinElement(event, this) ) return true;
+ // Execute the right handlers by setting the event type to mouseleave
+ event.type = "mouseleave";
+ return jQuery.event.handle.apply(this, arguments);
+ }
+ }
+ }
+};
+
+jQuery.fn.extend({
+ bind: function( type, data, fn ) {
+ return type == "unload" ? this.one(type, data, fn) : this.each(function(){
+ jQuery.event.add( this, type, fn || data, fn && data );
+ });
+ },
+
+ one: function( type, data, fn ) {
+ var one = jQuery.event.proxy( fn || data, function(event) {
+ jQuery(this).unbind(event, one);
+ return (fn || data).apply( this, arguments );
+ });
+ return this.each(function(){
+ jQuery.event.add( this, type, one, fn && data);
+ });
+ },
+
+ unbind: function( type, fn ) {
+ return this.each(function(){
+ jQuery.event.remove( this, type, fn );
+ });
+ },
+
+ trigger: function( type, data, fn ) {
+ return this.each(function(){
+ jQuery.event.trigger( type, data, this, true, fn );
+ });
+ },
+
+ triggerHandler: function( type, data, fn ) {
+ return this[0] && jQuery.event.trigger( type, data, this[0], false, fn );
+ },
+
+ toggle: function( fn ) {
+ // Save reference to arguments for access in closure
+ var args = arguments, i = 1;
+
+ // link all the functions, so any of them can unbind this click handler
+ while( i < args.length )
+ jQuery.event.proxy( fn, args[i++] );
+
+ return this.click( jQuery.event.proxy( fn, function(event) {
+ // Figure out which function to execute
+ this.lastToggle = ( this.lastToggle || 0 ) % i;
+
+ // Make sure that clicks stop
+ event.preventDefault();
+
+ // and execute the function
+ return args[ this.lastToggle++ ].apply( this, arguments ) || false;
+ }));
+ },
+
+ hover: function(fnOver, fnOut) {
+ return this.bind('mouseenter', fnOver).bind('mouseleave', fnOut);
+ },
+
+ ready: function(fn) {
+ // Attach the listeners
+ bindReady();
+
+ // If the DOM is already ready
+ if ( jQuery.isReady )
+ // Execute the function immediately
+ fn.call( document, jQuery );
+
+ // Otherwise, remember the function for later
+ else
+ // Add the function to the wait list
+ jQuery.readyList.push( function() { return fn.call(this, jQuery); } );
+
+ return this;
+ }
+});
+
+jQuery.extend({
+ isReady: false,
+ readyList: [],
+ // Handle when the DOM is ready
+ ready: function() {
+ // Make sure that the DOM is not already loaded
+ if ( !jQuery.isReady ) {
+ // Remember that the DOM is ready
+ jQuery.isReady = true;
+
+ // If there are functions bound, to execute
+ if ( jQuery.readyList ) {
+ // Execute all of them
+ jQuery.each( jQuery.readyList, function(){
+ this.call( document );
+ });
+
+ // Reset the list of functions
+ jQuery.readyList = null;
+ }
+
+ // Trigger any bound ready events
+ jQuery(document).triggerHandler("ready");
+ }
+ }
+});
+
+var readyBound = false;
+
+function bindReady(){
+ if ( readyBound ) return;
+ readyBound = true;
+
+ // Mozilla, Opera (see further below for it) and webkit nightlies currently support this event
+ if ( document.addEventListener && !jQuery.browser.opera)
+ // Use the handy event callback
+ document.addEventListener( "DOMContentLoaded", jQuery.ready, false );
+
+ // If IE is used and is not in a frame
+ // Continually check to see if the document is ready
+ if ( jQuery.browser.msie && window == top ) (function(){
+ if (jQuery.isReady) return;
+ try {
+ // If IE is used, use the trick by Diego Perini
+ // http://javascript.nwbox.com/IEContentLoaded/
+ document.documentElement.doScroll("left");
+ } catch( error ) {
+ setTimeout( arguments.callee, 0 );
+ return;
+ }
+ // and execute any waiting functions
+ jQuery.ready();
+ })();
+
+ if ( jQuery.browser.opera )
+ document.addEventListener( "DOMContentLoaded", function () {
+ if (jQuery.isReady) return;
+ for (var i = 0; i < document.styleSheets.length; i++)
+ if (document.styleSheets[i].disabled) {
+ setTimeout( arguments.callee, 0 );
+ return;
+ }
+ // and execute any waiting functions
+ jQuery.ready();
+ }, false);
+
+ if ( jQuery.browser.safari ) {
+ var numStyles;
+ (function(){
+ if (jQuery.isReady) return;
+ if ( document.readyState != "loaded" && document.readyState != "complete" ) {
+ setTimeout( arguments.callee, 0 );
+ return;
+ }
+ if ( numStyles === undefined )
+ numStyles = jQuery("style, link[rel=stylesheet]").length;
+ if ( document.styleSheets.length != numStyles ) {
+ setTimeout( arguments.callee, 0 );
+ return;
+ }
+ // and execute any waiting functions
+ jQuery.ready();
+ })();
+ }
+
+ // A fallback to window.onload, that will always work
+ jQuery.event.add( window, "load", jQuery.ready );
+}
+
+jQuery.each( ("blur,focus,load,resize,scroll,unload,click,dblclick," +
+ "mousedown,mouseup,mousemove,mouseover,mouseout,change,select," +
+ "submit,keydown,keypress,keyup,error").split(","), function(i, name){
+
+ // Handle event binding
+ jQuery.fn[name] = function(fn){
+ return fn ? this.bind(name, fn) : this.trigger(name);
+ };
+});
+
+// Checks if an event happened on an element within another element
+// Used in jQuery.event.special.mouseenter and mouseleave handlers
+var withinElement = function(event, elem) {
+ // Check if mouse(over|out) are still within the same parent element
+ var parent = event.relatedTarget;
+ // Traverse up the tree
+ while ( parent && parent != elem ) try { parent = parent.parentNode; } catch(error) { parent = elem; }
+ // Return true if we actually just moused on to a sub-element
+ return parent == elem;
+};
+
+// Prevent memory leaks in IE
+// And prevent errors on refresh with events like mouseover in other browsers
+// Window isn't included so as not to unbind existing unload events
+jQuery(window).bind("unload", function() {
+ jQuery("*").add(document).unbind();
+});
+jQuery.fn.extend({
+ // Keep a copy of the old load
+ _load: jQuery.fn.load,
+
+ load: function( url, params, callback ) {
+ if ( typeof url != 'string' )
+ return this._load( url );
+
+ var off = url.indexOf(" ");
+ if ( off >= 0 ) {
+ var selector = url.slice(off, url.length);
+ url = url.slice(0, off);
+ }
+
+ callback = callback || function(){};
+
+ // Default to a GET request
+ var type = "GET";
+
+ // If the second parameter was provided
+ if ( params )
+ // If it's a function
+ if ( jQuery.isFunction( params ) ) {
+ // We assume that it's the callback
+ callback = params;
+ params = null;
+
+ // Otherwise, build a param string
+ } else {
+ params = jQuery.param( params );
+ type = "POST";
+ }
+
+ var self = this;
+
+ // Request the remote document
+ jQuery.ajax({
+ url: url,
+ type: type,
+ dataType: "html",
+ data: params,
+ complete: function(res, status){
+ // If successful, inject the HTML into all the matched elements
+ if ( status == "success" || status == "notmodified" )
+ // See if a selector was specified
+ self.html( selector ?
+ // Create a dummy div to hold the results
+ jQuery("<div/>")
+ // inject the contents of the document in, removing the scripts
+ // to avoid any 'Permission Denied' errors in IE
+ .append(res.responseText.replace(/<script(.|\s)*?\/script>/g, ""))
+
+ // Locate the specified elements
+ .find(selector) :
+
+ // If not, just inject the full result
+ res.responseText );
+
+ self.each( callback, [res.responseText, status, res] );
+ }
+ });
+ return this;
+ },
+
+ serialize: function() {
+ return jQuery.param(this.serializeArray());
+ },
+ serializeArray: function() {
+ return this.map(function(){
+ return jQuery.nodeName(this, "form") ?
+ jQuery.makeArray(this.elements) : this;
+ })
+ .filter(function(){
+ return this.name && !this.disabled &&
+ (this.checked || /select|textarea/i.test(this.nodeName) ||
+ /text|hidden|password/i.test(this.type));
+ })
+ .map(function(i, elem){
+ var val = jQuery(this).val();
+ return val == null ? null :
+ val.constructor == Array ?
+ jQuery.map( val, function(val, i){
+ return {name: elem.name, value: val};
+ }) :
+ {name: elem.name, value: val};
+ }).get();
+ }
+});
+
+// Attach a bunch of functions for handling common AJAX events
+jQuery.each( "ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","), function(i,o){
+ jQuery.fn[o] = function(f){
+ return this.bind(o, f);
+ };
+});
+
+var jsc = now();
+
+jQuery.extend({
+ get: function( url, data, callback, type ) {
+ // shift arguments if data argument was ommited
+ if ( jQuery.isFunction( data ) ) {
+ callback = data;
+ data = null;
+ }
+
+ return jQuery.ajax({
+ type: "GET",
+ url: url,
+ data: data,
+ success: callback,
+ dataType: type
+ });
+ },
+
+ getScript: function( url, callback ) {
+ return jQuery.get(url, null, callback, "script");
+ },
+
+ getJSON: function( url, data, callback ) {
+ return jQuery.get(url, data, callback, "json");
+ },
+
+ post: function( url, data, callback, type ) {
+ if ( jQuery.isFunction( data ) ) {
+ callback = data;
+ data = {};
+ }
+
+ return jQuery.ajax({
+ type: "POST",
+ url: url,
+ data: data,
+ success: callback,
+ dataType: type
+ });
+ },
+
+ ajaxSetup: function( settings ) {
+ jQuery.extend( jQuery.ajaxSettings, settings );
+ },
+
+ ajaxSettings: {
+ url: location.href,
+ global: true,
+ type: "GET",
+ timeout: 0,
+ contentType: "application/x-www-form-urlencoded",
+ processData: true,
+ async: true,
+ data: null,
+ username: null,
+ password: null,
+ accepts: {
+ xml: "application/xml, text/xml",
+ html: "text/html",
+ script: "text/javascript, application/javascript",
+ json: "application/json, text/javascript",
+ text: "text/plain",
+ _default: "*/*"
+ }
+ },
+
+ // Last-Modified header cache for next request
+ lastModified: {},
+
+ ajax: function( s ) {
+ // Extend the settings, but re-extend 's' so that it can be
+ // checked again later (in the test suite, specifically)
+ s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s));
+
+ var jsonp, jsre = /=\?(&|$)/g, status, data,
+ type = s.type.toUpperCase();
+
+ // convert data if not already a string
+ if ( s.data && s.processData && typeof s.data != "string" )
+ s.data = jQuery.param(s.data);
+
+ // Handle JSONP Parameter Callbacks
+ if ( s.dataType == "jsonp" ) {
+ if ( type == "GET" ) {
+ if ( !s.url.match(jsre) )
+ s.url += (s.url.match(/\?/) ? "&" : "?") + (s.jsonp || "callback") + "=?";
+ } else if ( !s.data || !s.data.match(jsre) )
+ s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?";
+ s.dataType = "json";
+ }
+
+ // Build temporary JSONP function
+ if ( s.dataType == "json" && (s.data && s.data.match(jsre) || s.url.match(jsre)) ) {
+ jsonp = "jsonp" + jsc++;
+
+ // Replace the =? sequence both in the query string and the data
+ if ( s.data )
+ s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");
+ s.url = s.url.replace(jsre, "=" + jsonp + "$1");
+
+ // We need to make sure
+ // that a JSONP style response is executed properly
+ s.dataType = "script";
+
+ // Handle JSONP-style loading
+ window[ jsonp ] = function(tmp){
+ data = tmp;
+ success();
+ complete();
+ // Garbage collect
+ window[ jsonp ] = undefined;
+ try{ delete window[ jsonp ]; } catch(e){}
+ if ( head )
+ head.removeChild( script );
+ };
+ }
+
+ if ( s.dataType == "script" && s.cache == null )
+ s.cache = false;
+
+ if ( s.cache === false && type == "GET" ) {
+ var ts = now();
+ // try replacing _= if it is there
+ var ret = s.url.replace(/(\?|&)_=.*?(&|$)/, "$1_=" + ts + "$2");
+ // if nothing was replaced, add timestamp to the end
+ s.url = ret + ((ret == s.url) ? (s.url.match(/\?/) ? "&" : "?") + "_=" + ts : "");
+ }
+
+ // If data is available, append data to url for get requests
+ if ( s.data && type == "GET" ) {
+ s.url += (s.url.match(/\?/) ? "&" : "?") + s.data;
+
+ // IE likes to send both get and post data, prevent this
+ s.data = null;
+ }
+
+ // Watch for a new set of requests
+ if ( s.global && ! jQuery.active++ )
+ jQuery.event.trigger( "ajaxStart" );
+
+ // Matches an absolute URL, and saves the domain
+ var remote = /^(?:\w+:)?\/\/([^\/?#]+)/;
+
+ // If we're requesting a remote document
+ // and trying to load JSON or Script with a GET
+ if ( s.dataType == "script" && type == "GET"
+ && remote.test(s.url) && remote.exec(s.url)[1] != location.host ){
+ var head = document.getElementsByTagName("head")[0];
+ var script = document.createElement("script");
+ script.src = s.url;
+ if (s.scriptCharset)
+ script.charset = s.scriptCharset;
+
+ // Handle Script loading
+ if ( !jsonp ) {
+ var done = false;
+
+ // Attach handlers for all browsers
+ script.onload = script.onreadystatechange = function(){
+ if ( !done && (!this.readyState ||
+ this.readyState == "loaded" || this.readyState == "complete") ) {
+ done = true;
+ success();
+ complete();
+ head.removeChild( script );
+ }
+ };
+ }
+
+ head.appendChild(script);
+
+ // We handle everything using the script element injection
+ return undefined;
+ }
+
+ var requestDone = false;
+
+ // Create the request object; Microsoft failed to properly
+ // implement the XMLHttpRequest in IE7, so we use the ActiveXObject when it is available
+ var xhr = window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest();
+
+ // Open the socket
+ // Passing null username, generates a login popup on Opera (#2865)
+ if( s.username )
+ xhr.open(type, s.url, s.async, s.username, s.password);
+ else
+ xhr.open(type, s.url, s.async);
+
+ // Need an extra try/catch for cross domain requests in Firefox 3
+ try {
+ // Set the correct header, if data is being sent
+ if ( s.data )
+ xhr.setRequestHeader("Content-Type", s.contentType);
+
+ // Set the If-Modified-Since header, if ifModified mode.
+ if ( s.ifModified )
+ xhr.setRequestHeader("If-Modified-Since",
+ jQuery.lastModified[s.url] || "Thu, 01 Jan 1970 00:00:00 GMT" );
+
+ // Set header so the called script knows that it's an XMLHttpRequest
+ xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
+
+ // Set the Accepts header for the server, depending on the dataType
+ xhr.setRequestHeader("Accept", s.dataType && s.accepts[ s.dataType ] ?
+ s.accepts[ s.dataType ] + ", */*" :
+ s.accepts._default );
+ } catch(e){}
+
+ // Allow custom headers/mimetypes
+ if ( s.beforeSend && s.beforeSend(xhr, s) === false ) {
+ // cleanup active request counter
+ s.global && jQuery.active--;
+ // close opended socket
+ xhr.abort();
+ return false;
+ }
+
+ if ( s.global )
+ jQuery.event.trigger("ajaxSend", [xhr, s]);
+
+ // Wait for a response to come back
+ var onreadystatechange = function(isTimeout){
+ // The transfer is complete and the data is available, or the request timed out
+ if ( !requestDone && xhr && (xhr.readyState == 4 || isTimeout == "timeout") ) {
+ requestDone = true;
+
+ // clear poll interval
+ if (ival) {
+ clearInterval(ival);
+ ival = null;
+ }
+
+ status = isTimeout == "timeout" && "timeout" ||
+ !jQuery.httpSuccess( xhr ) && "error" ||
+ s.ifModified && jQuery.httpNotModified( xhr, s.url ) && "notmodified" ||
+ "success";
+
+ if ( status == "success" ) {
+ // Watch for, and catch, XML document parse errors
+ try {
+ // process the data (runs the xml through httpData regardless of callback)
+ data = jQuery.httpData( xhr, s.dataType, s.dataFilter );
+ } catch(e) {
+ status = "parsererror";
+ }
+ }
+
+ // Make sure that the request was successful or notmodified
+ if ( status == "success" ) {
+ // Cache Last-Modified header, if ifModified mode.
+ var modRes;
+ try {
+ modRes = xhr.getResponseHeader("Last-Modified");
+ } catch(e) {} // swallow exception thrown by FF if header is not available
+
+ if ( s.ifModified && modRes )
+ jQuery.lastModified[s.url] = modRes;
+
+ // JSONP handles its own success callback
+ if ( !jsonp )
+ success();
+ } else
+ jQuery.handleError(s, xhr, status);
+
+ // Fire the complete handlers
+ complete();
+
+ // Stop memory leaks
+ if ( s.async )
+ xhr = null;
+ }
+ };
+
+ if ( s.async ) {
+ // don't attach the handler to the request, just poll it instead
+ var ival = setInterval(onreadystatechange, 13);
+
+ // Timeout checker
+ if ( s.timeout > 0 )
+ setTimeout(function(){
+ // Check to see if the request is still happening
+ if ( xhr ) {
+ // Cancel the request
+ xhr.abort();
+
+ if( !requestDone )
+ onreadystatechange( "timeout" );
+ }
+ }, s.timeout);
+ }
+
+ // Send the data
+ try {
+ xhr.send(s.data);
+ } catch(e) {
+ jQuery.handleError(s, xhr, null, e);
+ }
+
+ // firefox 1.5 doesn't fire statechange for sync requests
+ if ( !s.async )
+ onreadystatechange();
+
+ function success(){
+ // If a local callback was specified, fire it and pass it the data
+ if ( s.success )
+ s.success( data, status );
+
+ // Fire the global callback
+ if ( s.global )
+ jQuery.event.trigger( "ajaxSuccess", [xhr, s] );
+ }
+
+ function complete(){
+ // Process result
+ if ( s.complete )
+ s.complete(xhr, status);
+
+ // The request was completed
+ if ( s.global )
+ jQuery.event.trigger( "ajaxComplete", [xhr, s] );
+
+ // Handle the global AJAX counter
+ if ( s.global && ! --jQuery.active )
+ jQuery.event.trigger( "ajaxStop" );
+ }
+
+ // return XMLHttpRequest to allow aborting the request etc.
+ return xhr;
+ },
+
+ handleError: function( s, xhr, status, e ) {
+ // If a local callback was specified, fire it
+ if ( s.error ) s.error( xhr, status, e );
+
+ // Fire the global callback
+ if ( s.global )
+ jQuery.event.trigger( "ajaxError", [xhr, s, e] );
+ },
+
+ // Counter for holding the number of active queries
+ active: 0,
+
+ // Determines if an XMLHttpRequest was successful or not
+ httpSuccess: function( xhr ) {
+ try {
+ // IE error sometimes returns 1223 when it should be 204 so treat it as success, see #1450
+ return !xhr.status && location.protocol == "file:" ||
+ ( xhr.status >= 200 && xhr.status < 300 ) || xhr.status == 304 || xhr.status == 1223 ||
+ jQuery.browser.safari && xhr.status == undefined;
+ } catch(e){}
+ return false;
+ },
+
+ // Determines if an XMLHttpRequest returns NotModified
+ httpNotModified: function( xhr, url ) {
+ try {
+ var xhrRes = xhr.getResponseHeader("Last-Modified");
+
+ // Firefox always returns 200. check Last-Modified date
+ return xhr.status == 304 || xhrRes == jQuery.lastModified[url] ||
+ jQuery.browser.safari && xhr.status == undefined;
+ } catch(e){}
+ return false;
+ },
+
+ httpData: function( xhr, type, filter ) {
+ var ct = xhr.getResponseHeader("content-type"),
+ xml = type == "xml" || !type && ct && ct.indexOf("xml") >= 0,
+ data = xml ? xhr.responseXML : xhr.responseText;
+
+ if ( xml && data.documentElement.tagName == "parsererror" )
+ throw "parsererror";
+
+ // Allow a pre-filtering function to sanitize the response
+ if( filter )
+ data = filter( data, type );
+
+ // If the type is "script", eval it in global context
+ if ( type == "script" )
+ jQuery.globalEval( data );
+
+ // Get the JavaScript object, if JSON is used.
+ if ( type == "json" )
+ data = eval("(" + data + ")");
+
+ return data;
+ },
+
+ // Serialize an array of form elements or a set of
+ // key/values into a query string
+ param: function( a ) {
+ var s = [];
+
+ // If an array was passed in, assume that it is an array
+ // of form elements
+ if ( a.constructor == Array || a.jquery )
+ // Serialize the form elements
+ jQuery.each( a, function(){
+ s.push( encodeURIComponent(this.name) + "=" + encodeURIComponent( this.value ) );
+ });
+
+ // Otherwise, assume that it's an object of key/value pairs
+ else
+ // Serialize the key/values
+ for ( var j in a )
+ // If the value is an array then the key names need to be repeated
+ if ( a[j] && a[j].constructor == Array )
+ jQuery.each( a[j], function(){
+ s.push( encodeURIComponent(j) + "=" + encodeURIComponent( this ) );
+ });
+ else
+ s.push( encodeURIComponent(j) + "=" + encodeURIComponent( jQuery.isFunction(a[j]) ? a[j]() : a[j] ) );
+
+ // Return the resulting serialization
+ return s.join("&").replace(/%20/g, "+");
+ }
+
+});
+jQuery.fn.extend({
+ show: function(speed,callback){
+ return speed ?
+ this.animate({
+ height: "show", width: "show", opacity: "show"
+ }, speed, callback) :
+
+ this.filter(":hidden").each(function(){
+ this.style.display = this.oldblock || "";
+ if ( jQuery.css(this,"display") == "none" ) {
+ var elem = jQuery("<" + this.tagName + " />").appendTo("body");
+ this.style.display = elem.css("display");
+ // handle an edge condition where css is - div { display:none; } or similar
+ if (this.style.display == "none")
+ this.style.display = "block";
+ elem.remove();
+ }
+ }).end();
+ },
+
+ hide: function(speed,callback){
+ return speed ?
+ this.animate({
+ height: "hide", width: "hide", opacity: "hide"
+ }, speed, callback) :
+
+ this.filter(":visible").each(function(){
+ this.oldblock = this.oldblock || jQuery.css(this,"display");
+ this.style.display = "none";
+ }).end();
+ },
+
+ // Save the old toggle function
+ _toggle: jQuery.fn.toggle,
+
+ toggle: function( fn, fn2 ){
+ return jQuery.isFunction(fn) && jQuery.isFunction(fn2) ?
+ this._toggle.apply( this, arguments ) :
+ fn ?
+ this.animate({
+ height: "toggle", width: "toggle", opacity: "toggle"
+ }, fn, fn2) :
+ this.each(function(){
+ jQuery(this)[ jQuery(this).is(":hidden") ? "show" : "hide" ]();
+ });
+ },
+
+ slideDown: function(speed,callback){
+ return this.animate({height: "show"}, speed, callback);
+ },
+
+ slideUp: function(speed,callback){
+ return this.animate({height: "hide"}, speed, callback);
+ },
+
+ slideToggle: function(speed, callback){
+ return this.animate({height: "toggle"}, speed, callback);
+ },
+
+ fadeIn: function(speed, callback){
+ return this.animate({opacity: "show"}, speed, callback);
+ },
+
+ fadeOut: function(speed, callback){
+ return this.animate({opacity: "hide"}, speed, callback);
+ },
+
+ fadeTo: function(speed,to,callback){
+ return this.animate({opacity: to}, speed, callback);
+ },
+
+ animate: function( prop, speed, easing, callback ) {
+ var optall = jQuery.speed(speed, easing, callback);
+
+ return this[ optall.queue === false ? "each" : "queue" ](function(){
+ if ( this.nodeType != 1)
+ return false;
+
+ var opt = jQuery.extend({}, optall), p,
+ hidden = jQuery(this).is(":hidden"), self = this;
+
+ for ( p in prop ) {
+ if ( prop[p] == "hide" && hidden || prop[p] == "show" && !hidden )
+ return opt.complete.call(this);
+
+ if ( p == "height" || p == "width" ) {
+ // Store display property
+ opt.display = jQuery.css(this, "display");
+
+ // Make sure that nothing sneaks out
+ opt.overflow = this.style.overflow;
+ }
+ }
+
+ if ( opt.overflow != null )
+ this.style.overflow = "hidden";
+
+ opt.curAnim = jQuery.extend({}, prop);
+
+ jQuery.each( prop, function(name, val){
+ var e = new jQuery.fx( self, opt, name );
+
+ if ( /toggle|show|hide/.test(val) )
+ e[ val == "toggle" ? hidden ? "show" : "hide" : val ]( prop );
+ else {
+ var parts = val.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),
+ start = e.cur(true) || 0;
+
+ if ( parts ) {
+ var end = parseFloat(parts[2]),
+ unit = parts[3] || "px";
+
+ // We need to compute starting value
+ if ( unit != "px" ) {
+ self.style[ name ] = (end || 1) + unit;
+ start = ((end || 1) / e.cur(true)) * start;
+ self.style[ name ] = start + unit;
+ }
+
+ // If a +=/-= token was provided, we're doing a relative animation
+ if ( parts[1] )
+ end = ((parts[1] == "-=" ? -1 : 1) * end) + start;
+
+ e.custom( start, end, unit );
+ } else
+ e.custom( start, val, "" );
+ }
+ });
+
+ // For JS strict compliance
+ return true;
+ });
+ },
+
+ queue: function(type, fn){
+ if ( jQuery.isFunction(type) || ( type && type.constructor == Array )) {
+ fn = type;
+ type = "fx";
+ }
+
+ if ( !type || (typeof type == "string" && !fn) )
+ return queue( this[0], type );
+
+ return this.each(function(){
+ if ( fn.constructor == Array )
+ queue(this, type, fn);
+ else {
+ queue(this, type).push( fn );
+
+ if ( queue(this, type).length == 1 )
+ fn.call(this);
+ }
+ });
+ },
+
+ stop: function(clearQueue, gotoEnd){
+ var timers = jQuery.timers;
+
+ if (clearQueue)
+ this.queue([]);
+
+ this.each(function(){
+ // go in reverse order so anything added to the queue during the loop is ignored
+ for ( var i = timers.length - 1; i >= 0; i-- )
+ if ( timers[i].elem == this ) {
+ if (gotoEnd)
+ // force the next step to be the last
+ timers[i](true);
+ timers.splice(i, 1);
+ }
+ });
+
+ // start the next in the queue if the last step wasn't forced
+ if (!gotoEnd)
+ this.dequeue();
+
+ return this;
+ }
+
+});
+
+var queue = function( elem, type, array ) {
+ if ( elem ){
+
+ type = type || "fx";
+
+ var q = jQuery.data( elem, type + "queue" );
+
+ if ( !q || array )
+ q = jQuery.data( elem, type + "queue", jQuery.makeArray(array) );
+
+ }
+ return q;
+};
+
+jQuery.fn.dequeue = function(type){
+ type = type || "fx";
+
+ return this.each(function(){
+ var q = queue(this, type);
+
+ q.shift();
+
+ if ( q.length )
+ q[0].call( this );
+ });
+};
+
+jQuery.extend({
+
+ speed: function(speed, easing, fn) {
+ var opt = speed && speed.constructor == Object ? speed : {
+ complete: fn || !fn && easing ||
+ jQuery.isFunction( speed ) && speed,
+ duration: speed,
+ easing: fn && easing || easing && easing.constructor != Function && easing
+ };
+
+ opt.duration = (opt.duration && opt.duration.constructor == Number ?
+ opt.duration :
+ jQuery.fx.speeds[opt.duration]) || jQuery.fx.speeds.def;
+
+ // Queueing
+ opt.old = opt.complete;
+ opt.complete = function(){
+ if ( opt.queue !== false )
+ jQuery(this).dequeue();
+ if ( jQuery.isFunction( opt.old ) )
+ opt.old.call( this );
+ };
+
+ return opt;
+ },
+
+ easing: {
+ linear: function( p, n, firstNum, diff ) {
+ return firstNum + diff * p;
+ },
+ swing: function( p, n, firstNum, diff ) {
+ return ((-Math.cos(p*Math.PI)/2) + 0.5) * diff + firstNum;
+ }
+ },
+
+ timers: [],
+ timerId: null,
+
+ fx: function( elem, options, prop ){
+ this.options = options;
+ this.elem = elem;
+ this.prop = prop;
+
+ if ( !options.orig )
+ options.orig = {};
+ }
+
+});
+
+jQuery.fx.prototype = {
+
+ // Simple function for setting a style value
+ update: function(){
+ if ( this.options.step )
+ this.options.step.call( this.elem, this.now, this );
+
+ (jQuery.fx.step[this.prop] || jQuery.fx.step._default)( this );
+
+ // Set display property to block for height/width animations
+ if ( this.prop == "height" || this.prop == "width" )
+ this.elem.style.display = "block";
+ },
+
+ // Get the current size
+ cur: function(force){
+ if ( this.elem[this.prop] != null && this.elem.style[this.prop] == null )
+ return this.elem[ this.prop ];
+
+ var r = parseFloat(jQuery.css(this.elem, this.prop, force));
+ return r && r > -10000 ? r : parseFloat(jQuery.curCSS(this.elem, this.prop)) || 0;
+ },
+
+ // Start an animation from one number to another
+ custom: function(from, to, unit){
+ this.startTime = now();
+ this.start = from;
+ this.end = to;
+ this.unit = unit || this.unit || "px";
+ this.now = this.start;
+ this.pos = this.state = 0;
+ this.update();
+
+ var self = this;
+ function t(gotoEnd){
+ return self.step(gotoEnd);
+ }
+
+ t.elem = this.elem;
+
+ jQuery.timers.push(t);
+
+ if ( jQuery.timerId == null ) {
+ jQuery.timerId = setInterval(function(){
+ var timers = jQuery.timers;
+
+ for ( var i = 0; i < timers.length; i++ )
+ if ( !timers[i]() )
+ timers.splice(i--, 1);
+
+ if ( !timers.length ) {
+ clearInterval( jQuery.timerId );
+ jQuery.timerId = null;
+ }
+ }, 13);
+ }
+ },
+
+ // Simple 'show' function
+ show: function(){
+ // Remember where we started, so that we can go back to it later
+ this.options.orig[this.prop] = jQuery.attr( this.elem.style, this.prop );
+ this.options.show = true;
+
+ // Begin the animation
+ this.custom(0, this.cur());
+
+ // Make sure that we start at a small width/height to avoid any
+ // flash of content
+ if ( this.prop == "width" || this.prop == "height" )
+ this.elem.style[this.prop] = "1px";
+
+ // Start by showing the element
+ jQuery(this.elem).show();
+ },
+
+ // Simple 'hide' function
+ hide: function(){
+ // Remember where we started, so that we can go back to it later
+ this.options.orig[this.prop] = jQuery.attr( this.elem.style, this.prop );
+ this.options.hide = true;
+
+ // Begin the animation
+ this.custom(this.cur(), 0);
+ },
+
+ // Each step of an animation
+ step: function(gotoEnd){
+ var t = now();
+
+ if ( gotoEnd || t > this.options.duration + this.startTime ) {
+ this.now = this.end;
+ this.pos = this.state = 1;
+ this.update();
+
+ this.options.curAnim[ this.prop ] = true;
+
+ var done = true;
+ for ( var i in this.options.curAnim )
+ if ( this.options.curAnim[i] !== true )
+ done = false;
+
+ if ( done ) {
+ if ( this.options.display != null ) {
+ // Reset the overflow
+ this.elem.style.overflow = this.options.overflow;
+
+ // Reset the display
+ this.elem.style.display = this.options.display;
+ if ( jQuery.css(this.elem, "display") == "none" )
+ this.elem.style.display = "block";
+ }
+
+ // Hide the element if the "hide" operation was done
+ if ( this.options.hide )
+ this.elem.style.display = "none";
+
+ // Reset the properties, if the item has been hidden or shown
+ if ( this.options.hide || this.options.show )
+ for ( var p in this.options.curAnim )
+ jQuery.attr(this.elem.style, p, this.options.orig[p]);
+ }
+
+ if ( done )
+ // Execute the complete function
+ this.options.complete.call( this.elem );
+
+ return false;
+ } else {
+ var n = t - this.startTime;
+ this.state = n / this.options.duration;
+
+ // Perform the easing function, defaults to swing
+ this.pos = jQuery.easing[this.options.easing || (jQuery.easing.swing ? "swing" : "linear")](this.state, n, 0, 1, this.options.duration);
+ this.now = this.start + ((this.end - this.start) * this.pos);
+
+ // Perform the next step of the animation
+ this.update();
+ }
+
+ return true;
+ }
+
+};
+
+jQuery.extend( jQuery.fx, {
+ speeds:{
+ slow: 600,
+ fast: 200,
+ // Default speed
+ def: 400
+ },
+ step: {
+ scrollLeft: function(fx){
+ fx.elem.scrollLeft = fx.now;
+ },
+
+ scrollTop: function(fx){
+ fx.elem.scrollTop = fx.now;
+ },
+
+ opacity: function(fx){
+ jQuery.attr(fx.elem.style, "opacity", fx.now);
+ },
+
+ _default: function(fx){
+ fx.elem.style[ fx.prop ] = fx.now + fx.unit;
+ }
+ }
+});
+// The Offset Method
+// Originally By Brandon Aaron, part of the Dimension Plugin
+// http://jquery.com/plugins/project/dimensions
+jQuery.fn.offset = function() {
+ var left = 0, top = 0, elem = this[0], results;
+
+ if ( elem ) with ( jQuery.browser ) {
+ var parent = elem.parentNode,
+ offsetChild = elem,
+ offsetParent = elem.offsetParent,
+ doc = elem.ownerDocument,
+ safari2 = safari && parseInt(version) < 522 && !/adobeair/i.test(userAgent),
+ css = jQuery.curCSS,
+ fixed = css(elem, "position") == "fixed";
+
+ // Use getBoundingClientRect if available
+ if ( elem.getBoundingClientRect ) {
+ var box = elem.getBoundingClientRect();
+
+ // Add the document scroll offsets
+ add(box.left + Math.max(doc.documentElement.scrollLeft, doc.body.scrollLeft),
+ box.top + Math.max(doc.documentElement.scrollTop, doc.body.scrollTop));
+
+ // IE adds the HTML element's border, by default it is medium which is 2px
+ // IE 6 and 7 quirks mode the border width is overwritable by the following css html { border: 0; }
+ // IE 7 standards mode, the border is always 2px
+ // This border/offset is typically represented by the clientLeft and clientTop properties
+ // However, in IE6 and 7 quirks mode the clientLeft and clientTop properties are not updated when overwriting it via CSS
+ // Therefore this method will be off by 2px in IE while in quirksmode
+ add( -doc.documentElement.clientLeft, -doc.documentElement.clientTop );
+
+ // Otherwise loop through the offsetParents and parentNodes
+ } else {
+
+ // Initial element offsets
+ add( elem.offsetLeft, elem.offsetTop );
+
+ // Get parent offsets
+ while ( offsetParent ) {
+ // Add offsetParent offsets
+ add( offsetParent.offsetLeft, offsetParent.offsetTop );
+
+ // Mozilla and Safari > 2 does not include the border on offset parents
+ // However Mozilla adds the border for table or table cells
+ if ( mozilla && !/^t(able|d|h)$/i.test(offsetParent.tagName) || safari && !safari2 )
+ border( offsetParent );
+
+ // Add the document scroll offsets if position is fixed on any offsetParent
+ if ( !fixed && css(offsetParent, "position") == "fixed" )
+ fixed = true;
+
+ // Set offsetChild to previous offsetParent unless it is the body element
+ offsetChild = /^body$/i.test(offsetParent.tagName) ? offsetChild : offsetParent;
+ // Get next offsetParent
+ offsetParent = offsetParent.offsetParent;
+ }
+
+ // Get parent scroll offsets
+ while ( parent && parent.tagName && !/^body|html$/i.test(parent.tagName) ) {
+ // Remove parent scroll UNLESS that parent is inline or a table to work around Opera inline/table scrollLeft/Top bug
+ if ( !/^inline|table.*$/i.test(css(parent, "display")) )
+ // Subtract parent scroll offsets
+ add( -parent.scrollLeft, -parent.scrollTop );
+
+ // Mozilla does not add the border for a parent that has overflow != visible
+ if ( mozilla && css(parent, "overflow") != "visible" )
+ border( parent );
+
+ // Get next parent
+ parent = parent.parentNode;
+ }
+
+ // Safari <= 2 doubles body offsets with a fixed position element/offsetParent or absolutely positioned offsetChild
+ // Mozilla doubles body offsets with a non-absolutely positioned offsetChild
+ if ( (safari2 && (fixed || css(offsetChild, "position") == "absolute")) ||
+ (mozilla && css(offsetChild, "position") != "absolute") )
+ add( -doc.body.offsetLeft, -doc.body.offsetTop );
+
+ // Add the document scroll offsets if position is fixed
+ if ( fixed )
+ add(Math.max(doc.documentElement.scrollLeft, doc.body.scrollLeft),
+ Math.max(doc.documentElement.scrollTop, doc.body.scrollTop));
+ }
+
+ // Return an object with top and left properties
+ results = { top: top, left: left };
+ }
+
+ function border(elem) {
+ add( jQuery.curCSS(elem, "borderLeftWidth", true), jQuery.curCSS(elem, "borderTopWidth", true) );
+ }
+
+ function add(l, t) {
+ left += parseInt(l, 10) || 0;
+ top += parseInt(t, 10) || 0;
+ }
+
+ return results;
+};
+
+
+jQuery.fn.extend({
+ position: function() {
+ var left = 0, top = 0, results;
+
+ if ( this[0] ) {
+ // Get *real* offsetParent
+ var offsetParent = this.offsetParent(),
+
+ // Get correct offsets
+ offset = this.offset(),
+ parentOffset = /^body|html$/i.test(offsetParent[0].tagName) ? { top: 0, left: 0 } : offsetParent.offset();
+
+ // Subtract element margins
+ // note: when an element has margin: auto the offsetLeft and marginLeft
+ // are the same in Safari causing offset.left to incorrectly be 0
+ offset.top -= num( this, 'marginTop' );
+ offset.left -= num( this, 'marginLeft' );
+
+ // Add offsetParent borders
+ parentOffset.top += num( offsetParent, 'borderTopWidth' );
+ parentOffset.left += num( offsetParent, 'borderLeftWidth' );
+
+ // Subtract the two offsets
+ results = {
+ top: offset.top - parentOffset.top,
+ left: offset.left - parentOffset.left
+ };
+ }
+
+ return results;
+ },
+
+ offsetParent: function() {
+ var offsetParent = this[0].offsetParent;
+ while ( offsetParent && (!/^body|html$/i.test(offsetParent.tagName) && jQuery.css(offsetParent, 'position') == 'static') )
+ offsetParent = offsetParent.offsetParent;
+ return jQuery(offsetParent);
+ }
+});
+
+
+// Create scrollLeft and scrollTop methods
+jQuery.each( ['Left', 'Top'], function(i, name) {
+ var method = 'scroll' + name;
+
+ jQuery.fn[ method ] = function(val) {
+ if (!this[0]) return;
+
+ return val != undefined ?
+
+ // Set the scroll offset
+ this.each(function() {
+ this == window || this == document ?
+ window.scrollTo(
+ !i ? val : jQuery(window).scrollLeft(),
+ i ? val : jQuery(window).scrollTop()
+ ) :
+ this[ method ] = val;
+ }) :
+
+ // Return the scroll offset
+ this[0] == window || this[0] == document ?
+ self[ i ? 'pageYOffset' : 'pageXOffset' ] ||
+ jQuery.boxModel && document.documentElement[ method ] ||
+ document.body[ method ] :
+ this[0][ method ];
+ };
+});
+// Create innerHeight, innerWidth, outerHeight and outerWidth methods
+jQuery.each([ "Height", "Width" ], function(i, name){
+
+ var tl = i ? "Left" : "Top", // top or left
+ br = i ? "Right" : "Bottom"; // bottom or right
+
+ // innerHeight and innerWidth
+ jQuery.fn["inner" + name] = function(){
+ return this[ name.toLowerCase() ]() +
+ num(this, "padding" + tl) +
+ num(this, "padding" + br);
+ };
+
+ // outerHeight and outerWidth
+ jQuery.fn["outer" + name] = function(margin) {
+ return this["inner" + name]() +
+ num(this, "border" + tl + "Width") +
+ num(this, "border" + br + "Width") +
+ (margin ?
+ num(this, "margin" + tl) + num(this, "margin" + br) : 0);
+ };
+
+});})();
diff --git a/trunk/etherpad/src/static/js/jquery-1.3.2.js b/trunk/etherpad/src/static/js/jquery-1.3.2.js
new file mode 100644
index 0000000..9263574
--- /dev/null
+++ b/trunk/etherpad/src/static/js/jquery-1.3.2.js
@@ -0,0 +1,4376 @@
+/*!
+ * jQuery JavaScript Library v1.3.2
+ * http://jquery.com/
+ *
+ * Copyright (c) 2009 John Resig
+ * Dual licensed under the MIT and GPL licenses.
+ * http://docs.jquery.com/License
+ *
+ * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009)
+ * Revision: 6246
+ */
+(function(){
+
+var
+ // Will speed up references to window, and allows munging its name.
+ window = this,
+ // Will speed up references to undefined, and allows munging its name.
+ undefined,
+ // Map over jQuery in case of overwrite
+ _jQuery = window.jQuery,
+ // Map over the $ in case of overwrite
+ _$ = window.$,
+
+ jQuery = window.jQuery = window.$ = function( selector, context ) {
+ // The jQuery object is actually just the init constructor 'enhanced'
+ return new jQuery.fn.init( selector, context );
+ },
+
+ // A simple way to check for HTML strings or ID strings
+ // (both of which we optimize for)
+ quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,
+ // Is it a simple selector
+ isSimple = /^.[^:#\[\.,]*$/;
+
+jQuery.fn = jQuery.prototype = {
+ init: function( selector, context ) {
+ // Make sure that a selection was provided
+ selector = selector || document;
+
+ // Handle $(DOMElement)
+ if ( selector.nodeType ) {
+ this[0] = selector;
+ this.length = 1;
+ this.context = selector;
+ return this;
+ }
+ // Handle HTML strings
+ if ( typeof selector === "string" ) {
+ // Are we dealing with HTML string or an ID?
+ var match = quickExpr.exec( selector );
+
+ // Verify a match, and that no context was specified for #id
+ if ( match && (match[1] || !context) ) {
+
+ // HANDLE: $(html) -> $(array)
+ if ( match[1] )
+ selector = jQuery.clean( [ match[1] ], context );
+
+ // HANDLE: $("#id")
+ else {
+ var elem = document.getElementById( match[3] );
+
+ // Handle the case where IE and Opera return items
+ // by name instead of ID
+ if ( elem && elem.id != match[3] )
+ return jQuery().find( selector );
+
+ // Otherwise, we inject the element directly into the jQuery object
+ var ret = jQuery( elem || [] );
+ ret.context = document;
+ ret.selector = selector;
+ return ret;
+ }
+
+ // HANDLE: $(expr, [context])
+ // (which is just equivalent to: $(content).find(expr)
+ } else
+ return jQuery( context ).find( selector );
+
+ // HANDLE: $(function)
+ // Shortcut for document ready
+ } else if ( jQuery.isFunction( selector ) )
+ return jQuery( document ).ready( selector );
+
+ // Make sure that old selector state is passed along
+ if ( selector.selector && selector.context ) {
+ this.selector = selector.selector;
+ this.context = selector.context;
+ }
+
+ return this.setArray(jQuery.isArray( selector ) ?
+ selector :
+ jQuery.makeArray(selector));
+ },
+
+ // Start with an empty selector
+ selector: "",
+
+ // The current version of jQuery being used
+ jquery: "1.3.2",
+
+ // The number of elements contained in the matched element set
+ size: function() {
+ return this.length;
+ },
+
+ // Get the Nth element in the matched element set OR
+ // Get the whole matched element set as a clean array
+ get: function( num ) {
+ return num === undefined ?
+
+ // Return a 'clean' array
+ Array.prototype.slice.call( this ) :
+
+ // Return just the object
+ this[ num ];
+ },
+
+ // Take an array of elements and push it onto the stack
+ // (returning the new matched element set)
+ pushStack: function( elems, name, selector ) {
+ // Build a new jQuery matched element set
+ var ret = jQuery( elems );
+
+ // Add the old object onto the stack (as a reference)
+ ret.prevObject = this;
+
+ ret.context = this.context;
+
+ if ( name === "find" )
+ ret.selector = this.selector + (this.selector ? " " : "") + selector;
+ else if ( name )
+ ret.selector = this.selector + "." + name + "(" + selector + ")";
+
+ // Return the newly-formed element set
+ return ret;
+ },
+
+ // Force the current matched set of elements to become
+ // the specified array of elements (destroying the stack in the process)
+ // You should use pushStack() in order to do this, but maintain the stack
+ setArray: function( elems ) {
+ // Resetting the length to 0, then using the native Array push
+ // is a super-fast way to populate an object with array-like properties
+ this.length = 0;
+ Array.prototype.push.apply( this, elems );
+
+ return this;
+ },
+
+ // Execute a callback for every element in the matched set.
+ // (You can seed the arguments with an array of args, but this is
+ // only used internally.)
+ each: function( callback, args ) {
+ return jQuery.each( this, callback, args );
+ },
+
+ // Determine the position of an element within
+ // the matched set of elements
+ index: function( elem ) {
+ // Locate the position of the desired element
+ return jQuery.inArray(
+ // If it receives a jQuery object, the first element is used
+ elem && elem.jquery ? elem[0] : elem
+ , this );
+ },
+
+ attr: function( name, value, type ) {
+ var options = name;
+
+ // Look for the case where we're accessing a style value
+ if ( typeof name === "string" )
+ if ( value === undefined )
+ return this[0] && jQuery[ type || "attr" ]( this[0], name );
+
+ else {
+ options = {};
+ options[ name ] = value;
+ }
+
+ // Check to see if we're setting style values
+ return this.each(function(i){
+ // Set all the styles
+ for ( name in options )
+ jQuery.attr(
+ type ?
+ this.style :
+ this,
+ name, jQuery.prop( this, options[ name ], type, i, name )
+ );
+ });
+ },
+
+ css: function( key, value ) {
+ // ignore negative width and height values
+ if ( (key == 'width' || key == 'height') && parseFloat(value) < 0 )
+ value = undefined;
+ return this.attr( key, value, "curCSS" );
+ },
+
+ text: function( text ) {
+ if ( typeof text !== "object" && text != null )
+ return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );
+
+ var ret = "";
+
+ jQuery.each( text || this, function(){
+ jQuery.each( this.childNodes, function(){
+ if ( this.nodeType != 8 )
+ ret += this.nodeType != 1 ?
+ this.nodeValue :
+ jQuery.fn.text( [ this ] );
+ });
+ });
+
+ return ret;
+ },
+
+ wrapAll: function( html ) {
+ if ( this[0] ) {
+ // The elements to wrap the target around
+ var wrap = jQuery( html, this[0].ownerDocument ).clone();
+
+ if ( this[0].parentNode )
+ wrap.insertBefore( this[0] );
+
+ wrap.map(function(){
+ var elem = this;
+
+ while ( elem.firstChild )
+ elem = elem.firstChild;
+
+ return elem;
+ }).append(this);
+ }
+
+ return this;
+ },
+
+ wrapInner: function( html ) {
+ return this.each(function(){
+ jQuery( this ).contents().wrapAll( html );
+ });
+ },
+
+ wrap: function( html ) {
+ return this.each(function(){
+ jQuery( this ).wrapAll( html );
+ });
+ },
+
+ append: function() {
+ return this.domManip(arguments, true, function(elem){
+ if (this.nodeType == 1)
+ this.appendChild( elem );
+ });
+ },
+
+ prepend: function() {
+ return this.domManip(arguments, true, function(elem){
+ if (this.nodeType == 1)
+ this.insertBefore( elem, this.firstChild );
+ });
+ },
+
+ before: function() {
+ return this.domManip(arguments, false, function(elem){
+ this.parentNode.insertBefore( elem, this );
+ });
+ },
+
+ after: function() {
+ return this.domManip(arguments, false, function(elem){
+ this.parentNode.insertBefore( elem, this.nextSibling );
+ });
+ },
+
+ end: function() {
+ return this.prevObject || jQuery( [] );
+ },
+
+ // For internal use only.
+ // Behaves like an Array's method, not like a jQuery method.
+ push: [].push,
+ sort: [].sort,
+ splice: [].splice,
+
+ find: function( selector ) {
+ if ( this.length === 1 ) {
+ var ret = this.pushStack( [], "find", selector );
+ ret.length = 0;
+ jQuery.find( selector, this[0], ret );
+ return ret;
+ } else {
+ return this.pushStack( jQuery.unique(jQuery.map(this, function(elem){
+ return jQuery.find( selector, elem );
+ })), "find", selector );
+ }
+ },
+
+ clone: function( events ) {
+ // Do the clone
+ var ret = this.map(function(){
+ if ( !jQuery.support.noCloneEvent && !jQuery.isXMLDoc(this) ) {
+ // IE copies events bound via attachEvent when
+ // using cloneNode. Calling detachEvent on the
+ // clone will also remove the events from the orignal
+ // In order to get around this, we use innerHTML.
+ // Unfortunately, this means some modifications to
+ // attributes in IE that are actually only stored
+ // as properties will not be copied (such as the
+ // the name attribute on an input).
+ var html = this.outerHTML;
+ if ( !html ) {
+ var div = this.ownerDocument.createElement("div");
+ div.appendChild( this.cloneNode(true) );
+ html = div.innerHTML;
+ }
+
+ return jQuery.clean([html.replace(/ jQuery\d+="(?:\d+|null)"/g, "").replace(/^\s*/, "")])[0];
+ } else
+ return this.cloneNode(true);
+ });
+
+ // Copy the events from the original to the clone
+ if ( events === true ) {
+ var orig = this.find("*").andSelf(), i = 0;
+
+ ret.find("*").andSelf().each(function(){
+ if ( this.nodeName !== orig[i].nodeName )
+ return;
+
+ var events = jQuery.data( orig[i], "events" );
+
+ for ( var type in events ) {
+ for ( var handler in events[ type ] ) {
+ jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data );
+ }
+ }
+
+ i++;
+ });
+ }
+
+ // Return the cloned set
+ return ret;
+ },
+
+ filter: function( selector ) {
+ return this.pushStack(
+ jQuery.isFunction( selector ) &&
+ jQuery.grep(this, function(elem, i){
+ return selector.call( elem, i );
+ }) ||
+
+ jQuery.multiFilter( selector, jQuery.grep(this, function(elem){
+ return elem.nodeType === 1;
+ }) ), "filter", selector );
+ },
+
+ closest: function( selector ) {
+ var pos = jQuery.expr.match.POS.test( selector ) ? jQuery(selector) : null,
+ closer = 0;
+
+ return this.map(function(){
+ var cur = this;
+ while ( cur && cur.ownerDocument ) {
+ if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selector) ) {
+ jQuery.data(cur, "closest", closer);
+ return cur;
+ }
+ cur = cur.parentNode;
+ closer++;
+ }
+ });
+ },
+
+ not: function( selector ) {
+ if ( typeof selector === "string" )
+ // test special case where just one selector is passed in
+ if ( isSimple.test( selector ) )
+ return this.pushStack( jQuery.multiFilter( selector, this, true ), "not", selector );
+ else
+ selector = jQuery.multiFilter( selector, this );
+
+ var isArrayLike = selector.length && selector[selector.length - 1] !== undefined && !selector.nodeType;
+ return this.filter(function() {
+ return isArrayLike ? jQuery.inArray( this, selector ) < 0 : this != selector;
+ });
+ },
+
+ add: function( selector ) {
+ return this.pushStack( jQuery.unique( jQuery.merge(
+ this.get(),
+ typeof selector === "string" ?
+ jQuery( selector ) :
+ jQuery.makeArray( selector )
+ )));
+ },
+
+ is: function( selector ) {
+ return !!selector && jQuery.multiFilter( selector, this ).length > 0;
+ },
+
+ hasClass: function( selector ) {
+ return !!selector && this.is( "." + selector );
+ },
+
+ val: function( value ) {
+ if ( value === undefined ) {
+ var elem = this[0];
+
+ if ( elem ) {
+ if( jQuery.nodeName( elem, 'option' ) )
+ return (elem.attributes.value || {}).specified ? elem.value : elem.text;
+
+ // We need to handle select boxes special
+ if ( jQuery.nodeName( elem, "select" ) ) {
+ var index = elem.selectedIndex,
+ values = [],
+ options = elem.options,
+ one = elem.type == "select-one";
+
+ // Nothing was selected
+ if ( index < 0 )
+ return null;
+
+ // Loop through all the selected options
+ for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) {
+ var option = options[ i ];
+
+ if ( option.selected ) {
+ // Get the specifc value for the option
+ value = jQuery(option).val();
+
+ // We don't need an array for one selects
+ if ( one )
+ return value;
+
+ // Multi-Selects return an array
+ values.push( value );
+ }
+ }
+
+ return values;
+ }
+
+ // Everything else, we just grab the value
+ return (elem.value || "").replace(/\r/g, "");
+
+ }
+
+ return undefined;
+ }
+
+ if ( typeof value === "number" )
+ value += '';
+
+ return this.each(function(){
+ if ( this.nodeType != 1 )
+ return;
+
+ if ( jQuery.isArray(value) && /radio|checkbox/.test( this.type ) )
+ this.checked = (jQuery.inArray(this.value, value) >= 0 ||
+ jQuery.inArray(this.name, value) >= 0);
+
+ else if ( jQuery.nodeName( this, "select" ) ) {
+ var values = jQuery.makeArray(value);
+
+ jQuery( "option", this ).each(function(){
+ this.selected = (jQuery.inArray( this.value, values ) >= 0 ||
+ jQuery.inArray( this.text, values ) >= 0);
+ });
+
+ if ( !values.length )
+ this.selectedIndex = -1;
+
+ } else
+ this.value = value;
+ });
+ },
+
+ html: function( value ) {
+ return value === undefined ?
+ (this[0] ?
+ this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g, "") :
+ null) :
+ this.empty().append( value );
+ },
+
+ replaceWith: function( value ) {
+ return this.after( value ).remove();
+ },
+
+ eq: function( i ) {
+ return this.slice( i, +i + 1 );
+ },
+
+ slice: function() {
+ return this.pushStack( Array.prototype.slice.apply( this, arguments ),
+ "slice", Array.prototype.slice.call(arguments).join(",") );
+ },
+
+ map: function( callback ) {
+ return this.pushStack( jQuery.map(this, function(elem, i){
+ return callback.call( elem, i, elem );
+ }));
+ },
+
+ andSelf: function() {
+ return this.add( this.prevObject );
+ },
+
+ domManip: function( args, table, callback ) {
+ if ( this[0] ) {
+ var fragment = (this[0].ownerDocument || this[0]).createDocumentFragment(),
+ scripts = jQuery.clean( args, (this[0].ownerDocument || this[0]), fragment ),
+ first = fragment.firstChild;
+
+ if ( first )
+ for ( var i = 0, l = this.length; i < l; i++ )
+ callback.call( root(this[i], first), this.length > 1 || i > 0 ?
+ fragment.cloneNode(true) : fragment );
+
+ if ( scripts )
+ jQuery.each( scripts, evalScript );
+ }
+
+ return this;
+
+ function root( elem, cur ) {
+ return table && jQuery.nodeName(elem, "table") && jQuery.nodeName(cur, "tr") ?
+ (elem.getElementsByTagName("tbody")[0] ||
+ elem.appendChild(elem.ownerDocument.createElement("tbody"))) :
+ elem;
+ }
+ }
+};
+
+// Give the init function the jQuery prototype for later instantiation
+jQuery.fn.init.prototype = jQuery.fn;
+
+function evalScript( i, elem ) {
+ if ( elem.src )
+ jQuery.ajax({
+ url: elem.src,
+ async: false,
+ dataType: "script"
+ });
+
+ else
+ jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" );
+
+ if ( elem.parentNode )
+ elem.parentNode.removeChild( elem );
+}
+
+function now(){
+ return +new Date;
+}
+
+jQuery.extend = jQuery.fn.extend = function() {
+ // copy reference to target object
+ var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options;
+
+ // Handle a deep copy situation
+ if ( typeof target === "boolean" ) {
+ deep = target;
+ target = arguments[1] || {};
+ // skip the boolean and the target
+ i = 2;
+ }
+
+ // Handle case when target is a string or something (possible in deep copy)
+ if ( typeof target !== "object" && !jQuery.isFunction(target) )
+ target = {};
+
+ // extend jQuery itself if only one argument is passed
+ if ( length == i ) {
+ target = this;
+ --i;
+ }
+
+ for ( ; i < length; i++ )
+ // Only deal with non-null/undefined values
+ if ( (options = arguments[ i ]) != null )
+ // Extend the base object
+ for ( var name in options ) {
+ var src = target[ name ], copy = options[ name ];
+
+ // Prevent never-ending loop
+ if ( target === copy )
+ continue;
+
+ // Recurse if we're merging object values
+ if ( deep && copy && typeof copy === "object" && !copy.nodeType )
+ target[ name ] = jQuery.extend( deep,
+ // Never move original objects, clone them
+ src || ( copy.length != null ? [ ] : { } )
+ , copy );
+
+ // Don't bring in undefined values
+ else if ( copy !== undefined )
+ target[ name ] = copy;
+
+ }
+
+ // Return the modified object
+ return target;
+};
+
+// exclude the following css properties to add px
+var exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i,
+ // cache defaultView
+ defaultView = document.defaultView || {},
+ toString = Object.prototype.toString;
+
+jQuery.extend({
+ noConflict: function( deep ) {
+ window.$ = _$;
+
+ if ( deep )
+ window.jQuery = _jQuery;
+
+ return jQuery;
+ },
+
+ // See test/unit/core.js for details concerning isFunction.
+ // Since version 1.3, DOM methods and functions like alert
+ // aren't supported. They return false on IE (#2968).
+ isFunction: function( obj ) {
+ return toString.call(obj) === "[object Function]";
+ },
+
+ isArray: function( obj ) {
+ return toString.call(obj) === "[object Array]";
+ },
+
+ // check if an element is in a (or is an) XML document
+ isXMLDoc: function( elem ) {
+ return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
+ !!elem.ownerDocument && jQuery.isXMLDoc( elem.ownerDocument );
+ },
+
+ // Evalulates a script in a global context
+ globalEval: function( data ) {
+ if ( data && /\S/.test(data) ) {
+ // Inspired by code by Andrea Giammarchi
+ // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html
+ var head = document.getElementsByTagName("head")[0] || document.documentElement,
+ script = document.createElement("script");
+
+ script.type = "text/javascript";
+ if ( jQuery.support.scriptEval )
+ script.appendChild( document.createTextNode( data ) );
+ else
+ script.text = data;
+
+ // Use insertBefore instead of appendChild to circumvent an IE6 bug.
+ // This arises when a base node is used (#2709).
+ head.insertBefore( script, head.firstChild );
+ head.removeChild( script );
+ }
+ },
+
+ nodeName: function( elem, name ) {
+ return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase();
+ },
+
+ // args is for internal usage only
+ each: function( object, callback, args ) {
+ var name, i = 0, length = object.length;
+
+ if ( args ) {
+ if ( length === undefined ) {
+ for ( name in object )
+ if ( callback.apply( object[ name ], args ) === false )
+ break;
+ } else
+ for ( ; i < length; )
+ if ( callback.apply( object[ i++ ], args ) === false )
+ break;
+
+ // A special, fast, case for the most common use of each
+ } else {
+ if ( length === undefined ) {
+ for ( name in object )
+ if ( callback.call( object[ name ], name, object[ name ] ) === false )
+ break;
+ } else
+ for ( var value = object[0];
+ i < length && callback.call( value, i, value ) !== false; value = object[++i] ){}
+ }
+
+ return object;
+ },
+
+ prop: function( elem, value, type, i, name ) {
+ // Handle executable functions
+ if ( jQuery.isFunction( value ) )
+ value = value.call( elem, i );
+
+ // Handle passing in a number to a CSS property
+ return typeof value === "number" && type == "curCSS" && !exclude.test( name ) ?
+ value + "px" :
+ value;
+ },
+
+ className: {
+ // internal only, use addClass("class")
+ add: function( elem, classNames ) {
+ jQuery.each((classNames || "").split(/\s+/), function(i, className){
+ if ( elem.nodeType == 1 && !jQuery.className.has( elem.className, className ) )
+ elem.className += (elem.className ? " " : "") + className;
+ });
+ },
+
+ // internal only, use removeClass("class")
+ remove: function( elem, classNames ) {
+ if (elem.nodeType == 1)
+ elem.className = classNames !== undefined ?
+ jQuery.grep(elem.className.split(/\s+/), function(className){
+ return !jQuery.className.has( classNames, className );
+ }).join(" ") :
+ "";
+ },
+
+ // internal only, use hasClass("class")
+ has: function( elem, className ) {
+ return elem && jQuery.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1;
+ }
+ },
+
+ // A method for quickly swapping in/out CSS properties to get correct calculations
+ swap: function( elem, options, callback ) {
+ var old = {};
+ // Remember the old values, and insert the new ones
+ for ( var name in options ) {
+ old[ name ] = elem.style[ name ];
+ elem.style[ name ] = options[ name ];
+ }
+
+ callback.call( elem );
+
+ // Revert the old values
+ for ( var name in options )
+ elem.style[ name ] = old[ name ];
+ },
+
+ css: function( elem, name, force, extra ) {
+ if ( name == "width" || name == "height" ) {
+ var val, props = { position: "absolute", visibility: "hidden", display:"block" }, which = name == "width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ];
+
+ function getWH() {
+ val = name == "width" ? elem.offsetWidth : elem.offsetHeight;
+
+ if ( extra === "border" )
+ return;
+
+ jQuery.each( which, function() {
+ if ( !extra )
+ val -= parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0;
+ if ( extra === "margin" )
+ val += parseFloat(jQuery.curCSS( elem, "margin" + this, true)) || 0;
+ else
+ val -= parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0;
+ });
+ }
+
+ if ( elem.offsetWidth !== 0 )
+ getWH();
+ else
+ jQuery.swap( elem, props, getWH );
+
+ return Math.max(0, Math.round(val));
+ }
+
+ return jQuery.curCSS( elem, name, force );
+ },
+
+ curCSS: function( elem, name, force ) {
+ var ret, style = elem.style;
+
+ // We need to handle opacity special in IE
+ if ( name == "opacity" && !jQuery.support.opacity ) {
+ ret = jQuery.attr( style, "opacity" );
+
+ return ret == "" ?
+ "1" :
+ ret;
+ }
+
+ // Make sure we're using the right name for getting the float value
+ if ( name.match( /float/i ) )
+ name = styleFloat;
+
+ if ( !force && style && style[ name ] )
+ ret = style[ name ];
+
+ else if ( defaultView.getComputedStyle ) {
+
+ // Only "float" is needed here
+ if ( name.match( /float/i ) )
+ name = "float";
+
+ name = name.replace( /([A-Z])/g, "-$1" ).toLowerCase();
+
+ var computedStyle = defaultView.getComputedStyle( elem, null );
+
+ if ( computedStyle )
+ ret = computedStyle.getPropertyValue( name );
+
+ // We should always get a number back from opacity
+ if ( name == "opacity" && ret == "" )
+ ret = "1";
+
+ } else if ( elem.currentStyle ) {
+ var camelCase = name.replace(/\-(\w)/g, function(all, letter){
+ return letter.toUpperCase();
+ });
+
+ ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ];
+
+ // From the awesome hack by Dean Edwards
+ // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
+
+ // If we're not dealing with a regular pixel number
+ // but a number that has a weird ending, we need to convert it to pixels
+ if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) {
+ // Remember the original values
+ var left = style.left, rsLeft = elem.runtimeStyle.left;
+
+ // Put in the new values to get a computed value out
+ elem.runtimeStyle.left = elem.currentStyle.left;
+ style.left = ret || 0;
+ ret = style.pixelLeft + "px";
+
+ // Revert the changed values
+ style.left = left;
+ elem.runtimeStyle.left = rsLeft;
+ }
+ }
+
+ return ret;
+ },
+
+ clean: function( elems, context, fragment ) {
+ context = context || document;
+
+ // !context.createElement fails in IE with an error but returns typeof 'object'
+ if ( typeof context.createElement === "undefined" )
+ context = context.ownerDocument || context[0] && context[0].ownerDocument || document;
+
+ // If a single string is passed in and it's a single tag
+ // just do a createElement and skip the rest
+ if ( !fragment && elems.length === 1 && typeof elems[0] === "string" ) {
+ var match = /^<(\w+)\s*\/?>$/.exec(elems[0]);
+ if ( match )
+ return [ context.createElement( match[1] ) ];
+ }
+
+ var ret = [], scripts = [], div = context.createElement("div");
+
+ jQuery.each(elems, function(i, elem){
+ if ( typeof elem === "number" )
+ elem += '';
+
+ if ( !elem )
+ return;
+
+ // Convert html string into DOM nodes
+ if ( typeof elem === "string" ) {
+ // Fix "XHTML"-style tags in all browsers
+ elem = elem.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag){
+ return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ?
+ all :
+ front + "></" + tag + ">";
+ });
+
+ // Trim whitespace, otherwise indexOf won't work as expected
+ var tags = elem.replace(/^\s+/, "").substring(0, 10).toLowerCase();
+
+ var wrap =
+ // option or optgroup
+ !tags.indexOf("<opt") &&
+ [ 1, "<select multiple='multiple'>", "</select>" ] ||
+
+ !tags.indexOf("<leg") &&
+ [ 1, "<fieldset>", "</fieldset>" ] ||
+
+ tags.match(/^<(thead|tbody|tfoot|colg|cap)/) &&
+ [ 1, "<table>", "</table>" ] ||
+
+ !tags.indexOf("<tr") &&
+ [ 2, "<table><tbody>", "</tbody></table>" ] ||
+
+ // <thead> matched above
+ (!tags.indexOf("<td") || !tags.indexOf("<th")) &&
+ [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ] ||
+
+ !tags.indexOf("<col") &&
+ [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ] ||
+
+ // IE can't serialize <link> and <script> tags normally
+ !jQuery.support.htmlSerialize &&
+ [ 1, "div<div>", "</div>" ] ||
+
+ [ 0, "", "" ];
+
+ // Go to html and back, then peel off extra wrappers
+ div.innerHTML = wrap[1] + elem + wrap[2];
+
+ // Move to the right depth
+ while ( wrap[0]-- )
+ div = div.lastChild;
+
+ // Remove IE's autoinserted <tbody> from table fragments
+ if ( !jQuery.support.tbody ) {
+
+ // String was a <table>, *may* have spurious <tbody>
+ var hasBody = /<tbody/i.test(elem),
+ tbody = !tags.indexOf("<table") && !hasBody ?
+ div.firstChild && div.firstChild.childNodes :
+
+ // String was a bare <thead> or <tfoot>
+ wrap[1] == "<table>" && !hasBody ?
+ div.childNodes :
+ [];
+
+ for ( var j = tbody.length - 1; j >= 0 ; --j )
+ if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length )
+ tbody[ j ].parentNode.removeChild( tbody[ j ] );
+
+ }
+
+ // IE completely kills leading whitespace when innerHTML is used
+ if ( !jQuery.support.leadingWhitespace && /^\s/.test( elem ) )
+ div.insertBefore( context.createTextNode( elem.match(/^\s*/)[0] ), div.firstChild );
+
+ elem = jQuery.makeArray( div.childNodes );
+ }
+
+ if ( elem.nodeType )
+ ret.push( elem );
+ else
+ ret = jQuery.merge( ret, elem );
+
+ });
+
+ if ( fragment ) {
+ for ( var i = 0; ret[i]; i++ ) {
+ if ( jQuery.nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) {
+ scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] );
+ } else {
+ if ( ret[i].nodeType === 1 )
+ ret.splice.apply( ret, [i + 1, 0].concat(jQuery.makeArray(ret[i].getElementsByTagName("script"))) );
+ fragment.appendChild( ret[i] );
+ }
+ }
+
+ return scripts;
+ }
+
+ return ret;
+ },
+
+ attr: function( elem, name, value ) {
+ // don't set attributes on text and comment nodes
+ if (!elem || elem.nodeType == 3 || elem.nodeType == 8)
+ return undefined;
+
+ var notxml = !jQuery.isXMLDoc( elem ),
+ // Whether we are setting (or getting)
+ set = value !== undefined;
+
+ // Try to normalize/fix the name
+ name = notxml && jQuery.props[ name ] || name;
+
+ // Only do all the following if this is a node (faster for style)
+ // IE elem.getAttribute passes even for style
+ if ( elem.tagName ) {
+
+ // These attributes require special treatment
+ var special = /href|src|style/.test( name );
+
+ // Safari mis-reports the default selected property of a hidden option
+ // Accessing the parent's selectedIndex property fixes it
+ if ( name == "selected" && elem.parentNode )
+ elem.parentNode.selectedIndex;
+
+ // If applicable, access the attribute via the DOM 0 way
+ if ( name in elem && notxml && !special ) {
+ if ( set ){
+ // We can't allow the type property to be changed (since it causes problems in IE)
+ if ( name == "type" && jQuery.nodeName( elem, "input" ) && elem.parentNode )
+ throw "type property can't be changed";
+
+ elem[ name ] = value;
+ }
+
+ // browsers index elements by id/name on forms, give priority to attributes.
+ if( jQuery.nodeName( elem, "form" ) && elem.getAttributeNode(name) )
+ return elem.getAttributeNode( name ).nodeValue;
+
+ // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
+ // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
+ if ( name == "tabIndex" ) {
+ var attributeNode = elem.getAttributeNode( "tabIndex" );
+ return attributeNode && attributeNode.specified
+ ? attributeNode.value
+ : elem.nodeName.match(/(button|input|object|select|textarea)/i)
+ ? 0
+ : elem.nodeName.match(/^(a|area)$/i) && elem.href
+ ? 0
+ : undefined;
+ }
+
+ return elem[ name ];
+ }
+
+ if ( !jQuery.support.style && notxml && name == "style" )
+ return jQuery.attr( elem.style, "cssText", value );
+
+ if ( set )
+ // convert the value to a string (all browsers do this but IE) see #1070
+ elem.setAttribute( name, "" + value );
+
+ var attr = !jQuery.support.hrefNormalized && notxml && special
+ // Some attributes require a special call on IE
+ ? elem.getAttribute( name, 2 )
+ : elem.getAttribute( name );
+
+ // Non-existent attributes return null, we normalize to undefined
+ return attr === null ? undefined : attr;
+ }
+
+ // elem is actually elem.style ... set the style
+
+ // IE uses filters for opacity
+ if ( !jQuery.support.opacity && name == "opacity" ) {
+ if ( set ) {
+ // IE has trouble with opacity if it does not have layout
+ // Force it by setting the zoom level
+ elem.zoom = 1;
+
+ // Set the alpha filter to set the opacity
+ elem.filter = (elem.filter || "").replace( /alpha\([^)]*\)/, "" ) +
+ (parseInt( value ) + '' == "NaN" ? "" : "alpha(opacity=" + value * 100 + ")");
+ }
+
+ return elem.filter && elem.filter.indexOf("opacity=") >= 0 ?
+ (parseFloat( elem.filter.match(/opacity=([^)]*)/)[1] ) / 100) + '':
+ "";
+ }
+
+ name = name.replace(/-([a-z])/ig, function(all, letter){
+ return letter.toUpperCase();
+ });
+
+ if ( set )
+ elem[ name ] = value;
+
+ return elem[ name ];
+ },
+
+ trim: function( text ) {
+ return (text || "").replace( /^\s+|\s+$/g, "" );
+ },
+
+ makeArray: function( array ) {
+ var ret = [];
+
+ if( array != null ){
+ var i = array.length;
+ // The window, strings (and functions) also have 'length'
+ if( i == null || typeof array === "string" || jQuery.isFunction(array) || array.setInterval )
+ ret[0] = array;
+ else
+ while( i )
+ ret[--i] = array[i];
+ }
+
+ return ret;
+ },
+
+ inArray: function( elem, array ) {
+ for ( var i = 0, length = array.length; i < length; i++ )
+ // Use === because on IE, window == document
+ if ( array[ i ] === elem )
+ return i;
+
+ return -1;
+ },
+
+ merge: function( first, second ) {
+ // We have to loop this way because IE & Opera overwrite the length
+ // expando of getElementsByTagName
+ var i = 0, elem, pos = first.length;
+ // Also, we need to make sure that the correct elements are being returned
+ // (IE returns comment nodes in a '*' query)
+ if ( !jQuery.support.getAll ) {
+ while ( (elem = second[ i++ ]) != null )
+ if ( elem.nodeType != 8 )
+ first[ pos++ ] = elem;
+
+ } else
+ while ( (elem = second[ i++ ]) != null )
+ first[ pos++ ] = elem;
+
+ return first;
+ },
+
+ unique: function( array ) {
+ var ret = [], done = {};
+
+ try {
+
+ for ( var i = 0, length = array.length; i < length; i++ ) {
+ var id = jQuery.data( array[ i ] );
+
+ if ( !done[ id ] ) {
+ done[ id ] = true;
+ ret.push( array[ i ] );
+ }
+ }
+
+ } catch( e ) {
+ ret = array;
+ }
+
+ return ret;
+ },
+
+ grep: function( elems, callback, inv ) {
+ var ret = [];
+
+ // Go through the array, only saving the items
+ // that pass the validator function
+ for ( var i = 0, length = elems.length; i < length; i++ )
+ if ( !inv != !callback( elems[ i ], i ) )
+ ret.push( elems[ i ] );
+
+ return ret;
+ },
+
+ map: function( elems, callback ) {
+ var ret = [];
+
+ // Go through the array, translating each of the items to their
+ // new value (or values).
+ for ( var i = 0, length = elems.length; i < length; i++ ) {
+ var value = callback( elems[ i ], i );
+
+ if ( value != null )
+ ret[ ret.length ] = value;
+ }
+
+ return ret.concat.apply( [], ret );
+ }
+});
+
+// Use of jQuery.browser is deprecated.
+// It's included for backwards compatibility and plugins,
+// although they should work to migrate away.
+
+var userAgent = navigator.userAgent.toLowerCase();
+
+// Figure out what browser is being used
+jQuery.browser = {
+ version: (userAgent.match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [0,'0'])[1],
+ safari: /webkit/.test( userAgent ),
+ opera: /opera/.test( userAgent ),
+ msie: /msie/.test( userAgent ) && !/opera/.test( userAgent ),
+ mozilla: /mozilla/.test( userAgent ) && !/(compatible|webkit)/.test( userAgent )
+};
+
+jQuery.each({
+ parent: function(elem){return elem.parentNode;},
+ parents: function(elem){return jQuery.dir(elem,"parentNode");},
+ next: function(elem){return jQuery.nth(elem,2,"nextSibling");},
+ prev: function(elem){return jQuery.nth(elem,2,"previousSibling");},
+ nextAll: function(elem){return jQuery.dir(elem,"nextSibling");},
+ prevAll: function(elem){return jQuery.dir(elem,"previousSibling");},
+ siblings: function(elem){return jQuery.sibling(elem.parentNode.firstChild,elem);},
+ children: function(elem){return jQuery.sibling(elem.firstChild);},
+ contents: function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.makeArray(elem.childNodes);}
+}, function(name, fn){
+ jQuery.fn[ name ] = function( selector ) {
+ var ret = jQuery.map( this, fn );
+
+ if ( selector && typeof selector == "string" )
+ ret = jQuery.multiFilter( selector, ret );
+
+ return this.pushStack( jQuery.unique( ret ), name, selector );
+ };
+});
+
+jQuery.each({
+ appendTo: "append",
+ prependTo: "prepend",
+ insertBefore: "before",
+ insertAfter: "after",
+ replaceAll: "replaceWith"
+}, function(name, original){
+ jQuery.fn[ name ] = function( selector ) {
+ var ret = [], insert = jQuery( selector );
+
+ for ( var i = 0, l = insert.length; i < l; i++ ) {
+ var elems = (i > 0 ? this.clone(true) : this).get();
+ jQuery.fn[ original ].apply( jQuery(insert[i]), elems );
+ ret = ret.concat( elems );
+ }
+
+ return this.pushStack( ret, name, selector );
+ };
+});
+
+jQuery.each({
+ removeAttr: function( name ) {
+ jQuery.attr( this, name, "" );
+ if (this.nodeType == 1)
+ this.removeAttribute( name );
+ },
+
+ addClass: function( classNames ) {
+ jQuery.className.add( this, classNames );
+ },
+
+ removeClass: function( classNames ) {
+ jQuery.className.remove( this, classNames );
+ },
+
+ toggleClass: function( classNames, state ) {
+ if( typeof state !== "boolean" )
+ state = !jQuery.className.has( this, classNames );
+ jQuery.className[ state ? "add" : "remove" ]( this, classNames );
+ },
+
+ remove: function( selector ) {
+ if ( !selector || jQuery.filter( selector, [ this ] ).length ) {
+ // Prevent memory leaks
+ jQuery( "*", this ).add([this]).each(function(){
+ jQuery.event.remove(this);
+ jQuery.removeData(this);
+ });
+ if (this.parentNode)
+ this.parentNode.removeChild( this );
+ }
+ },
+
+ empty: function() {
+ // Remove element nodes and prevent memory leaks
+ jQuery(this).children().remove();
+
+ // Remove any remaining nodes
+ while ( this.firstChild )
+ this.removeChild( this.firstChild );
+ }
+}, function(name, fn){
+ jQuery.fn[ name ] = function(){
+ return this.each( fn, arguments );
+ };
+});
+
+// Helper function used by the dimensions and offset modules
+function num(elem, prop) {
+ return elem[0] && parseInt( jQuery.curCSS(elem[0], prop, true), 10 ) || 0;
+}
+var expando = "jQuery" + now(), uuid = 0, windowData = {};
+
+jQuery.extend({
+ cache: {},
+
+ data: function( elem, name, data ) {
+ elem = elem == window ?
+ windowData :
+ elem;
+
+ var id = elem[ expando ];
+
+ // Compute a unique ID for the element
+ if ( !id )
+ id = elem[ expando ] = ++uuid;
+
+ // Only generate the data cache if we're
+ // trying to access or manipulate it
+ if ( name && !jQuery.cache[ id ] )
+ jQuery.cache[ id ] = {};
+
+ // Prevent overriding the named cache with undefined values
+ if ( data !== undefined )
+ jQuery.cache[ id ][ name ] = data;
+
+ // Return the named cache data, or the ID for the element
+ return name ?
+ jQuery.cache[ id ][ name ] :
+ id;
+ },
+
+ removeData: function( elem, name ) {
+ elem = elem == window ?
+ windowData :
+ elem;
+
+ var id = elem[ expando ];
+
+ // If we want to remove a specific section of the element's data
+ if ( name ) {
+ if ( jQuery.cache[ id ] ) {
+ // Remove the section of cache data
+ delete jQuery.cache[ id ][ name ];
+
+ // If we've removed all the data, remove the element's cache
+ name = "";
+
+ for ( name in jQuery.cache[ id ] )
+ break;
+
+ if ( !name )
+ jQuery.removeData( elem );
+ }
+
+ // Otherwise, we want to remove all of the element's data
+ } else {
+ // Clean up the element expando
+ try {
+ delete elem[ expando ];
+ } catch(e){
+ // IE has trouble directly removing the expando
+ // but it's ok with using removeAttribute
+ if ( elem.removeAttribute )
+ elem.removeAttribute( expando );
+ }
+
+ // Completely remove the data cache
+ delete jQuery.cache[ id ];
+ }
+ },
+ queue: function( elem, type, data ) {
+ if ( elem ){
+
+ type = (type || "fx") + "queue";
+
+ var q = jQuery.data( elem, type );
+
+ if ( !q || jQuery.isArray(data) )
+ q = jQuery.data( elem, type, jQuery.makeArray(data) );
+ else if( data )
+ q.push( data );
+
+ }
+ return q;
+ },
+
+ dequeue: function( elem, type ){
+ var queue = jQuery.queue( elem, type ),
+ fn = queue.shift();
+
+ if( !type || type === "fx" )
+ fn = queue[0];
+
+ if( fn !== undefined )
+ fn.call(elem);
+ }
+});
+
+jQuery.fn.extend({
+ data: function( key, value ){
+ var parts = key.split(".");
+ parts[1] = parts[1] ? "." + parts[1] : "";
+
+ if ( value === undefined ) {
+ var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);
+
+ if ( data === undefined && this.length )
+ data = jQuery.data( this[0], key );
+
+ return data === undefined && parts[1] ?
+ this.data( parts[0] ) :
+ data;
+ } else
+ return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function(){
+ jQuery.data( this, key, value );
+ });
+ },
+
+ removeData: function( key ){
+ return this.each(function(){
+ jQuery.removeData( this, key );
+ });
+ },
+ queue: function(type, data){
+ if ( typeof type !== "string" ) {
+ data = type;
+ type = "fx";
+ }
+
+ if ( data === undefined )
+ return jQuery.queue( this[0], type );
+
+ return this.each(function(){
+ var queue = jQuery.queue( this, type, data );
+
+ if( type == "fx" && queue.length == 1 )
+ queue[0].call(this);
+ });
+ },
+ dequeue: function(type){
+ return this.each(function(){
+ jQuery.dequeue( this, type );
+ });
+ }
+});/*!
+ * Sizzle CSS Selector Engine - v0.9.3
+ * Copyright 2009, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ * More information: http://sizzlejs.com/
+ */
+(function(){
+
+var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,
+ done = 0,
+ toString = Object.prototype.toString;
+
+var Sizzle = function(selector, context, results, seed) {
+ results = results || [];
+ context = context || document;
+
+ if ( context.nodeType !== 1 && context.nodeType !== 9 )
+ return [];
+
+ if ( !selector || typeof selector !== "string" ) {
+ return results;
+ }
+
+ var parts = [], m, set, checkSet, check, mode, extra, prune = true;
+
+ // Reset the position of the chunker regexp (start from head)
+ chunker.lastIndex = 0;
+
+ while ( (m = chunker.exec(selector)) !== null ) {
+ parts.push( m[1] );
+
+ if ( m[2] ) {
+ extra = RegExp.rightContext;
+ break;
+ }
+ }
+
+ if ( parts.length > 1 && origPOS.exec( selector ) ) {
+ if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
+ set = posProcess( parts[0] + parts[1], context );
+ } else {
+ set = Expr.relative[ parts[0] ] ?
+ [ context ] :
+ Sizzle( parts.shift(), context );
+
+ while ( parts.length ) {
+ selector = parts.shift();
+
+ if ( Expr.relative[ selector ] )
+ selector += parts.shift();
+
+ set = posProcess( selector, set );
+ }
+ }
+ } else {
+ var ret = seed ?
+ { expr: parts.pop(), set: makeArray(seed) } :
+ Sizzle.find( parts.pop(), parts.length === 1 && context.parentNode ? context.parentNode : context, isXML(context) );
+ set = Sizzle.filter( ret.expr, ret.set );
+
+ if ( parts.length > 0 ) {
+ checkSet = makeArray(set);
+ } else {
+ prune = false;
+ }
+
+ while ( parts.length ) {
+ var cur = parts.pop(), pop = cur;
+
+ if ( !Expr.relative[ cur ] ) {
+ cur = "";
+ } else {
+ pop = parts.pop();
+ }
+
+ if ( pop == null ) {
+ pop = context;
+ }
+
+ Expr.relative[ cur ]( checkSet, pop, isXML(context) );
+ }
+ }
+
+ if ( !checkSet ) {
+ checkSet = set;
+ }
+
+ if ( !checkSet ) {
+ throw "Syntax error, unrecognized expression: " + (cur || selector);
+ }
+
+ if ( toString.call(checkSet) === "[object Array]" ) {
+ if ( !prune ) {
+ results.push.apply( results, checkSet );
+ } else if ( context.nodeType === 1 ) {
+ for ( var i = 0; checkSet[i] != null; i++ ) {
+ if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {
+ results.push( set[i] );
+ }
+ }
+ } else {
+ for ( var i = 0; checkSet[i] != null; i++ ) {
+ if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
+ results.push( set[i] );
+ }
+ }
+ }
+ } else {
+ makeArray( checkSet, results );
+ }
+
+ if ( extra ) {
+ Sizzle( extra, context, results, seed );
+
+ if ( sortOrder ) {
+ hasDuplicate = false;
+ results.sort(sortOrder);
+
+ if ( hasDuplicate ) {
+ for ( var i = 1; i < results.length; i++ ) {
+ if ( results[i] === results[i-1] ) {
+ results.splice(i--, 1);
+ }
+ }
+ }
+ }
+ }
+
+ return results;
+};
+
+Sizzle.matches = function(expr, set){
+ return Sizzle(expr, null, null, set);
+};
+
+Sizzle.find = function(expr, context, isXML){
+ var set, match;
+
+ if ( !expr ) {
+ return [];
+ }
+
+ for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
+ var type = Expr.order[i], match;
+
+ if ( (match = Expr.match[ type ].exec( expr )) ) {
+ var left = RegExp.leftContext;
+
+ if ( left.substr( left.length - 1 ) !== "\\" ) {
+ match[1] = (match[1] || "").replace(/\\/g, "");
+ set = Expr.find[ type ]( match, context, isXML );
+ if ( set != null ) {
+ expr = expr.replace( Expr.match[ type ], "" );
+ break;
+ }
+ }
+ }
+ }
+
+ if ( !set ) {
+ set = context.getElementsByTagName("*");
+ }
+
+ return {set: set, expr: expr};
+};
+
+Sizzle.filter = function(expr, set, inplace, not){
+ var old = expr, result = [], curLoop = set, match, anyFound,
+ isXMLFilter = set && set[0] && isXML(set[0]);
+
+ while ( expr && set.length ) {
+ for ( var type in Expr.filter ) {
+ if ( (match = Expr.match[ type ].exec( expr )) != null ) {
+ var filter = Expr.filter[ type ], found, item;
+ anyFound = false;
+
+ if ( curLoop == result ) {
+ result = [];
+ }
+
+ if ( Expr.preFilter[ type ] ) {
+ match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
+
+ if ( !match ) {
+ anyFound = found = true;
+ } else if ( match === true ) {
+ continue;
+ }
+ }
+
+ if ( match ) {
+ for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
+ if ( item ) {
+ found = filter( item, match, i, curLoop );
+ var pass = not ^ !!found;
+
+ if ( inplace && found != null ) {
+ if ( pass ) {
+ anyFound = true;
+ } else {
+ curLoop[i] = false;
+ }
+ } else if ( pass ) {
+ result.push( item );
+ anyFound = true;
+ }
+ }
+ }
+ }
+
+ if ( found !== undefined ) {
+ if ( !inplace ) {
+ curLoop = result;
+ }
+
+ expr = expr.replace( Expr.match[ type ], "" );
+
+ if ( !anyFound ) {
+ return [];
+ }
+
+ break;
+ }
+ }
+ }
+
+ // Improper expression
+ if ( expr == old ) {
+ if ( anyFound == null ) {
+ throw "Syntax error, unrecognized expression: " + expr;
+ } else {
+ break;
+ }
+ }
+
+ old = expr;
+ }
+
+ return curLoop;
+};
+
+var Expr = Sizzle.selectors = {
+ order: [ "ID", "NAME", "TAG" ],
+ match: {
+ ID: /#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
+ CLASS: /\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
+ NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)['"]*\]/,
+ ATTR: /\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
+ TAG: /^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/,
+ CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
+ POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
+ PSEUDO: /:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/
+ },
+ attrMap: {
+ "class": "className",
+ "for": "htmlFor"
+ },
+ attrHandle: {
+ href: function(elem){
+ return elem.getAttribute("href");
+ }
+ },
+ relative: {
+ "+": function(checkSet, part, isXML){
+ var isPartStr = typeof part === "string",
+ isTag = isPartStr && !/\W/.test(part),
+ isPartStrNotTag = isPartStr && !isTag;
+
+ if ( isTag && !isXML ) {
+ part = part.toUpperCase();
+ }
+
+ for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
+ if ( (elem = checkSet[i]) ) {
+ while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
+
+ checkSet[i] = isPartStrNotTag || elem && elem.nodeName === part ?
+ elem || false :
+ elem === part;
+ }
+ }
+
+ if ( isPartStrNotTag ) {
+ Sizzle.filter( part, checkSet, true );
+ }
+ },
+ ">": function(checkSet, part, isXML){
+ var isPartStr = typeof part === "string";
+
+ if ( isPartStr && !/\W/.test(part) ) {
+ part = isXML ? part : part.toUpperCase();
+
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+ var elem = checkSet[i];
+ if ( elem ) {
+ var parent = elem.parentNode;
+ checkSet[i] = parent.nodeName === part ? parent : false;
+ }
+ }
+ } else {
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+ var elem = checkSet[i];
+ if ( elem ) {
+ checkSet[i] = isPartStr ?
+ elem.parentNode :
+ elem.parentNode === part;
+ }
+ }
+
+ if ( isPartStr ) {
+ Sizzle.filter( part, checkSet, true );
+ }
+ }
+ },
+ "": function(checkSet, part, isXML){
+ var doneName = done++, checkFn = dirCheck;
+
+ if ( !part.match(/\W/) ) {
+ var nodeCheck = part = isXML ? part : part.toUpperCase();
+ checkFn = dirNodeCheck;
+ }
+
+ checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
+ },
+ "~": function(checkSet, part, isXML){
+ var doneName = done++, checkFn = dirCheck;
+
+ if ( typeof part === "string" && !part.match(/\W/) ) {
+ var nodeCheck = part = isXML ? part : part.toUpperCase();
+ checkFn = dirNodeCheck;
+ }
+
+ checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
+ }
+ },
+ find: {
+ ID: function(match, context, isXML){
+ if ( typeof context.getElementById !== "undefined" && !isXML ) {
+ var m = context.getElementById(match[1]);
+ return m ? [m] : [];
+ }
+ },
+ NAME: function(match, context, isXML){
+ if ( typeof context.getElementsByName !== "undefined" ) {
+ var ret = [], results = context.getElementsByName(match[1]);
+
+ for ( var i = 0, l = results.length; i < l; i++ ) {
+ if ( results[i].getAttribute("name") === match[1] ) {
+ ret.push( results[i] );
+ }
+ }
+
+ return ret.length === 0 ? null : ret;
+ }
+ },
+ TAG: function(match, context){
+ return context.getElementsByTagName(match[1]);
+ }
+ },
+ preFilter: {
+ CLASS: function(match, curLoop, inplace, result, not, isXML){
+ match = " " + match[1].replace(/\\/g, "") + " ";
+
+ if ( isXML ) {
+ return match;
+ }
+
+ for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
+ if ( elem ) {
+ if ( not ^ (elem.className && (" " + elem.className + " ").indexOf(match) >= 0) ) {
+ if ( !inplace )
+ result.push( elem );
+ } else if ( inplace ) {
+ curLoop[i] = false;
+ }
+ }
+ }
+
+ return false;
+ },
+ ID: function(match){
+ return match[1].replace(/\\/g, "");
+ },
+ TAG: function(match, curLoop){
+ for ( var i = 0; curLoop[i] === false; i++ ){}
+ return curLoop[i] && isXML(curLoop[i]) ? match[1] : match[1].toUpperCase();
+ },
+ CHILD: function(match){
+ if ( match[1] == "nth" ) {
+ // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
+ var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
+ match[2] == "even" && "2n" || match[2] == "odd" && "2n+1" ||
+ !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
+
+ // calculate the numbers (first)n+(last) including if they are negative
+ match[2] = (test[1] + (test[2] || 1)) - 0;
+ match[3] = test[3] - 0;
+ }
+
+ // TODO: Move to normal caching system
+ match[0] = done++;
+
+ return match;
+ },
+ ATTR: function(match, curLoop, inplace, result, not, isXML){
+ var name = match[1].replace(/\\/g, "");
+
+ if ( !isXML && Expr.attrMap[name] ) {
+ match[1] = Expr.attrMap[name];
+ }
+
+ if ( match[2] === "~=" ) {
+ match[4] = " " + match[4] + " ";
+ }
+
+ return match;
+ },
+ PSEUDO: function(match, curLoop, inplace, result, not){
+ if ( match[1] === "not" ) {
+ // If we're dealing with a complex expression, or a simple one
+ if ( match[3].match(chunker).length > 1 || /^\w/.test(match[3]) ) {
+ match[3] = Sizzle(match[3], null, null, curLoop);
+ } else {
+ var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
+ if ( !inplace ) {
+ result.push.apply( result, ret );
+ }
+ return false;
+ }
+ } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
+ return true;
+ }
+
+ return match;
+ },
+ POS: function(match){
+ match.unshift( true );
+ return match;
+ }
+ },
+ filters: {
+ enabled: function(elem){
+ return elem.disabled === false && elem.type !== "hidden";
+ },
+ disabled: function(elem){
+ return elem.disabled === true;
+ },
+ checked: function(elem){
+ return elem.checked === true;
+ },
+ selected: function(elem){
+ // Accessing this property makes selected-by-default
+ // options in Safari work properly
+ elem.parentNode.selectedIndex;
+ return elem.selected === true;
+ },
+ parent: function(elem){
+ return !!elem.firstChild;
+ },
+ empty: function(elem){
+ return !elem.firstChild;
+ },
+ has: function(elem, i, match){
+ return !!Sizzle( match[3], elem ).length;
+ },
+ header: function(elem){
+ return /h\d/i.test( elem.nodeName );
+ },
+ text: function(elem){
+ return "text" === elem.type;
+ },
+ radio: function(elem){
+ return "radio" === elem.type;
+ },
+ checkbox: function(elem){
+ return "checkbox" === elem.type;
+ },
+ file: function(elem){
+ return "file" === elem.type;
+ },
+ password: function(elem){
+ return "password" === elem.type;
+ },
+ submit: function(elem){
+ return "submit" === elem.type;
+ },
+ image: function(elem){
+ return "image" === elem.type;
+ },
+ reset: function(elem){
+ return "reset" === elem.type;
+ },
+ button: function(elem){
+ return "button" === elem.type || elem.nodeName.toUpperCase() === "BUTTON";
+ },
+ input: function(elem){
+ return /input|select|textarea|button/i.test(elem.nodeName);
+ }
+ },
+ setFilters: {
+ first: function(elem, i){
+ return i === 0;
+ },
+ last: function(elem, i, match, array){
+ return i === array.length - 1;
+ },
+ even: function(elem, i){
+ return i % 2 === 0;
+ },
+ odd: function(elem, i){
+ return i % 2 === 1;
+ },
+ lt: function(elem, i, match){
+ return i < match[3] - 0;
+ },
+ gt: function(elem, i, match){
+ return i > match[3] - 0;
+ },
+ nth: function(elem, i, match){
+ return match[3] - 0 == i;
+ },
+ eq: function(elem, i, match){
+ return match[3] - 0 == i;
+ }
+ },
+ filter: {
+ PSEUDO: function(elem, match, i, array){
+ var name = match[1], filter = Expr.filters[ name ];
+
+ if ( filter ) {
+ return filter( elem, i, match, array );
+ } else if ( name === "contains" ) {
+ return (elem.textContent || elem.innerText || "").indexOf(match[3]) >= 0;
+ } else if ( name === "not" ) {
+ var not = match[3];
+
+ for ( var i = 0, l = not.length; i < l; i++ ) {
+ if ( not[i] === elem ) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ },
+ CHILD: function(elem, match){
+ var type = match[1], node = elem;
+ switch (type) {
+ case 'only':
+ case 'first':
+ while (node = node.previousSibling) {
+ if ( node.nodeType === 1 ) return false;
+ }
+ if ( type == 'first') return true;
+ node = elem;
+ case 'last':
+ while (node = node.nextSibling) {
+ if ( node.nodeType === 1 ) return false;
+ }
+ return true;
+ case 'nth':
+ var first = match[2], last = match[3];
+
+ if ( first == 1 && last == 0 ) {
+ return true;
+ }
+
+ var doneName = match[0],
+ parent = elem.parentNode;
+
+ if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
+ var count = 0;
+ for ( node = parent.firstChild; node; node = node.nextSibling ) {
+ if ( node.nodeType === 1 ) {
+ node.nodeIndex = ++count;
+ }
+ }
+ parent.sizcache = doneName;
+ }
+
+ var diff = elem.nodeIndex - last;
+ if ( first == 0 ) {
+ return diff == 0;
+ } else {
+ return ( diff % first == 0 && diff / first >= 0 );
+ }
+ }
+ },
+ ID: function(elem, match){
+ return elem.nodeType === 1 && elem.getAttribute("id") === match;
+ },
+ TAG: function(elem, match){
+ return (match === "*" && elem.nodeType === 1) || elem.nodeName === match;
+ },
+ CLASS: function(elem, match){
+ return (" " + (elem.className || elem.getAttribute("class")) + " ")
+ .indexOf( match ) > -1;
+ },
+ ATTR: function(elem, match){
+ var name = match[1],
+ result = Expr.attrHandle[ name ] ?
+ Expr.attrHandle[ name ]( elem ) :
+ elem[ name ] != null ?
+ elem[ name ] :
+ elem.getAttribute( name ),
+ value = result + "",
+ type = match[2],
+ check = match[4];
+
+ return result == null ?
+ type === "!=" :
+ type === "=" ?
+ value === check :
+ type === "*=" ?
+ value.indexOf(check) >= 0 :
+ type === "~=" ?
+ (" " + value + " ").indexOf(check) >= 0 :
+ !check ?
+ value && result !== false :
+ type === "!=" ?
+ value != check :
+ type === "^=" ?
+ value.indexOf(check) === 0 :
+ type === "$=" ?
+ value.substr(value.length - check.length) === check :
+ type === "|=" ?
+ value === check || value.substr(0, check.length + 1) === check + "-" :
+ false;
+ },
+ POS: function(elem, match, i, array){
+ var name = match[2], filter = Expr.setFilters[ name ];
+
+ if ( filter ) {
+ return filter( elem, i, match, array );
+ }
+ }
+ }
+};
+
+var origPOS = Expr.match.POS;
+
+for ( var type in Expr.match ) {
+ Expr.match[ type ] = RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
+}
+
+var makeArray = function(array, results) {
+ array = Array.prototype.slice.call( array );
+
+ if ( results ) {
+ results.push.apply( results, array );
+ return results;
+ }
+
+ return array;
+};
+
+// Perform a simple check to determine if the browser is capable of
+// converting a NodeList to an array using builtin methods.
+try {
+ Array.prototype.slice.call( document.documentElement.childNodes );
+
+// Provide a fallback method if it does not work
+} catch(e){
+ makeArray = function(array, results) {
+ var ret = results || [];
+
+ if ( toString.call(array) === "[object Array]" ) {
+ Array.prototype.push.apply( ret, array );
+ } else {
+ if ( typeof array.length === "number" ) {
+ for ( var i = 0, l = array.length; i < l; i++ ) {
+ ret.push( array[i] );
+ }
+ } else {
+ for ( var i = 0; array[i]; i++ ) {
+ ret.push( array[i] );
+ }
+ }
+ }
+
+ return ret;
+ };
+}
+
+var sortOrder;
+
+if ( document.documentElement.compareDocumentPosition ) {
+ sortOrder = function( a, b ) {
+ var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
+ if ( ret === 0 ) {
+ hasDuplicate = true;
+ }
+ return ret;
+ };
+} else if ( "sourceIndex" in document.documentElement ) {
+ sortOrder = function( a, b ) {
+ var ret = a.sourceIndex - b.sourceIndex;
+ if ( ret === 0 ) {
+ hasDuplicate = true;
+ }
+ return ret;
+ };
+} else if ( document.createRange ) {
+ sortOrder = function( a, b ) {
+ var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
+ aRange.selectNode(a);
+ aRange.collapse(true);
+ bRange.selectNode(b);
+ bRange.collapse(true);
+ var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
+ if ( ret === 0 ) {
+ hasDuplicate = true;
+ }
+ return ret;
+ };
+}
+
+// Check to see if the browser returns elements by name when
+// querying by getElementById (and provide a workaround)
+(function(){
+ // We're going to inject a fake input element with a specified name
+ var form = document.createElement("form"),
+ id = "script" + (new Date).getTime();
+ form.innerHTML = "<input name='" + id + "'/>";
+
+ // Inject it into the root element, check its status, and remove it quickly
+ var root = document.documentElement;
+ root.insertBefore( form, root.firstChild );
+
+ // The workaround has to do additional checks after a getElementById
+ // Which slows things down for other browsers (hence the branching)
+ if ( !!document.getElementById( id ) ) {
+ Expr.find.ID = function(match, context, isXML){
+ if ( typeof context.getElementById !== "undefined" && !isXML ) {
+ var m = context.getElementById(match[1]);
+ return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
+ }
+ };
+
+ Expr.filter.ID = function(elem, match){
+ var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
+ return elem.nodeType === 1 && node && node.nodeValue === match;
+ };
+ }
+
+ root.removeChild( form );
+})();
+
+(function(){
+ // Check to see if the browser returns only elements
+ // when doing getElementsByTagName("*")
+
+ // Create a fake element
+ var div = document.createElement("div");
+ div.appendChild( document.createComment("") );
+
+ // Make sure no comments are found
+ if ( div.getElementsByTagName("*").length > 0 ) {
+ Expr.find.TAG = function(match, context){
+ var results = context.getElementsByTagName(match[1]);
+
+ // Filter out possible comments
+ if ( match[1] === "*" ) {
+ var tmp = [];
+
+ for ( var i = 0; results[i]; i++ ) {
+ if ( results[i].nodeType === 1 ) {
+ tmp.push( results[i] );
+ }
+ }
+
+ results = tmp;
+ }
+
+ return results;
+ };
+ }
+
+ // Check to see if an attribute returns normalized href attributes
+ div.innerHTML = "<a href='#'></a>";
+ if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
+ div.firstChild.getAttribute("href") !== "#" ) {
+ Expr.attrHandle.href = function(elem){
+ return elem.getAttribute("href", 2);
+ };
+ }
+})();
+
+if ( document.querySelectorAll ) (function(){
+ var oldSizzle = Sizzle, div = document.createElement("div");
+ div.innerHTML = "<p class='TEST'></p>";
+
+ // Safari can't handle uppercase or unicode characters when
+ // in quirks mode.
+ if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
+ return;
+ }
+
+ Sizzle = function(query, context, extra, seed){
+ context = context || document;
+
+ // Only use querySelectorAll on non-XML documents
+ // (ID selectors don't work in non-HTML documents)
+ if ( !seed && context.nodeType === 9 && !isXML(context) ) {
+ try {
+ return makeArray( context.querySelectorAll(query), extra );
+ } catch(e){}
+ }
+
+ return oldSizzle(query, context, extra, seed);
+ };
+
+ Sizzle.find = oldSizzle.find;
+ Sizzle.filter = oldSizzle.filter;
+ Sizzle.selectors = oldSizzle.selectors;
+ Sizzle.matches = oldSizzle.matches;
+})();
+
+if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){
+ var div = document.createElement("div");
+ div.innerHTML = "<div class='test e'></div><div class='test'></div>";
+
+ // Opera can't find a second classname (in 9.6)
+ if ( div.getElementsByClassName("e").length === 0 )
+ return;
+
+ // Safari caches class attributes, doesn't catch changes (in 3.2)
+ div.lastChild.className = "e";
+
+ if ( div.getElementsByClassName("e").length === 1 )
+ return;
+
+ Expr.order.splice(1, 0, "CLASS");
+ Expr.find.CLASS = function(match, context, isXML) {
+ if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
+ return context.getElementsByClassName(match[1]);
+ }
+ };
+})();
+
+function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+ var sibDir = dir == "previousSibling" && !isXML;
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+ var elem = checkSet[i];
+ if ( elem ) {
+ if ( sibDir && elem.nodeType === 1 ){
+ elem.sizcache = doneName;
+ elem.sizset = i;
+ }
+ elem = elem[dir];
+ var match = false;
+
+ while ( elem ) {
+ if ( elem.sizcache === doneName ) {
+ match = checkSet[elem.sizset];
+ break;
+ }
+
+ if ( elem.nodeType === 1 && !isXML ){
+ elem.sizcache = doneName;
+ elem.sizset = i;
+ }
+
+ if ( elem.nodeName === cur ) {
+ match = elem;
+ break;
+ }
+
+ elem = elem[dir];
+ }
+
+ checkSet[i] = match;
+ }
+ }
+}
+
+function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+ var sibDir = dir == "previousSibling" && !isXML;
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+ var elem = checkSet[i];
+ if ( elem ) {
+ if ( sibDir && elem.nodeType === 1 ) {
+ elem.sizcache = doneName;
+ elem.sizset = i;
+ }
+ elem = elem[dir];
+ var match = false;
+
+ while ( elem ) {
+ if ( elem.sizcache === doneName ) {
+ match = checkSet[elem.sizset];
+ break;
+ }
+
+ if ( elem.nodeType === 1 ) {
+ if ( !isXML ) {
+ elem.sizcache = doneName;
+ elem.sizset = i;
+ }
+ if ( typeof cur !== "string" ) {
+ if ( elem === cur ) {
+ match = true;
+ break;
+ }
+
+ } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
+ match = elem;
+ break;
+ }
+ }
+
+ elem = elem[dir];
+ }
+
+ checkSet[i] = match;
+ }
+ }
+}
+
+var contains = document.compareDocumentPosition ? function(a, b){
+ return a.compareDocumentPosition(b) & 16;
+} : function(a, b){
+ return a !== b && (a.contains ? a.contains(b) : true);
+};
+
+var isXML = function(elem){
+ return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
+ !!elem.ownerDocument && isXML( elem.ownerDocument );
+};
+
+var posProcess = function(selector, context){
+ var tmpSet = [], later = "", match,
+ root = context.nodeType ? [context] : context;
+
+ // Position selectors must be done after the filter
+ // And so must :not(positional) so we move all PSEUDOs to the end
+ while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
+ later += match[0];
+ selector = selector.replace( Expr.match.PSEUDO, "" );
+ }
+
+ selector = Expr.relative[selector] ? selector + "*" : selector;
+
+ for ( var i = 0, l = root.length; i < l; i++ ) {
+ Sizzle( selector, root[i], tmpSet );
+ }
+
+ return Sizzle.filter( later, tmpSet );
+};
+
+// EXPOSE
+jQuery.find = Sizzle;
+jQuery.filter = Sizzle.filter;
+jQuery.expr = Sizzle.selectors;
+jQuery.expr[":"] = jQuery.expr.filters;
+
+Sizzle.selectors.filters.hidden = function(elem){
+ return elem.offsetWidth === 0 || elem.offsetHeight === 0;
+};
+
+Sizzle.selectors.filters.visible = function(elem){
+ return elem.offsetWidth > 0 || elem.offsetHeight > 0;
+};
+
+Sizzle.selectors.filters.animated = function(elem){
+ return jQuery.grep(jQuery.timers, function(fn){
+ return elem === fn.elem;
+ }).length;
+};
+
+jQuery.multiFilter = function( expr, elems, not ) {
+ if ( not ) {
+ expr = ":not(" + expr + ")";
+ }
+
+ return Sizzle.matches(expr, elems);
+};
+
+jQuery.dir = function( elem, dir ){
+ var matched = [], cur = elem[dir];
+ while ( cur && cur != document ) {
+ if ( cur.nodeType == 1 )
+ matched.push( cur );
+ cur = cur[dir];
+ }
+ return matched;
+};
+
+jQuery.nth = function(cur, result, dir, elem){
+ result = result || 1;
+ var num = 0;
+
+ for ( ; cur; cur = cur[dir] )
+ if ( cur.nodeType == 1 && ++num == result )
+ break;
+
+ return cur;
+};
+
+jQuery.sibling = function(n, elem){
+ var r = [];
+
+ for ( ; n; n = n.nextSibling ) {
+ if ( n.nodeType == 1 && n != elem )
+ r.push( n );
+ }
+
+ return r;
+};
+
+return;
+
+window.Sizzle = Sizzle;
+
+})();
+/*
+ * A number of helper functions used for managing events.
+ * Many of the ideas behind this code originated from
+ * Dean Edwards' addEvent library.
+ */
+jQuery.event = {
+
+ // Bind an event to an element
+ // Original by Dean Edwards
+ add: function(elem, types, handler, data) {
+ if ( elem.nodeType == 3 || elem.nodeType == 8 )
+ return;
+
+ // For whatever reason, IE has trouble passing the window object
+ // around, causing it to be cloned in the process
+ if ( elem.setInterval && elem != window )
+ elem = window;
+
+ // Make sure that the function being executed has a unique ID
+ if ( !handler.guid )
+ handler.guid = this.guid++;
+
+ // if data is passed, bind to handler
+ if ( data !== undefined ) {
+ // Create temporary function pointer to original handler
+ var fn = handler;
+
+ // Create unique handler function, wrapped around original handler
+ handler = this.proxy( fn );
+
+ // Store data in unique handler
+ handler.data = data;
+ }
+
+ // Init the element's event structure
+ var events = jQuery.data(elem, "events") || jQuery.data(elem, "events", {}),
+ handle = jQuery.data(elem, "handle") || jQuery.data(elem, "handle", function(){
+ // Handle the second event of a trigger and when
+ // an event is called after a page has unloaded
+ return typeof jQuery !== "undefined" && !jQuery.event.triggered ?
+ jQuery.event.handle.apply(arguments.callee.elem, arguments) :
+ undefined;
+ });
+ // Add elem as a property of the handle function
+ // This is to prevent a memory leak with non-native
+ // event in IE.
+ handle.elem = elem;
+
+ // Handle multiple events separated by a space
+ // jQuery(...).bind("mouseover mouseout", fn);
+ jQuery.each(types.split(/\s+/), function(index, type) {
+ // Namespaced event handlers
+ var namespaces = type.split(".");
+ type = namespaces.shift();
+ handler.type = namespaces.slice().sort().join(".");
+
+ // Get the current list of functions bound to this event
+ var handlers = events[type];
+
+ if ( jQuery.event.specialAll[type] )
+ jQuery.event.specialAll[type].setup.call(elem, data, namespaces);
+
+ // Init the event handler queue
+ if (!handlers) {
+ handlers = events[type] = {};
+
+ // Check for a special event handler
+ // Only use addEventListener/attachEvent if the special
+ // events handler returns false
+ if ( !jQuery.event.special[type] || jQuery.event.special[type].setup.call(elem, data, namespaces) === false ) {
+ // Bind the global event handler to the element
+ if (elem.addEventListener)
+ elem.addEventListener(type, handle, false);
+ else if (elem.attachEvent)
+ elem.attachEvent("on" + type, handle);
+ }
+ }
+
+ // Add the function to the element's handler list
+ handlers[handler.guid] = handler;
+
+ // Keep track of which events have been used, for global triggering
+ jQuery.event.global[type] = true;
+ });
+
+ // Nullify elem to prevent memory leaks in IE
+ elem = null;
+ },
+
+ guid: 1,
+ global: {},
+
+ // Detach an event or set of events from an element
+ remove: function(elem, types, handler) {
+ // don't do events on text and comment nodes
+ if ( elem.nodeType == 3 || elem.nodeType == 8 )
+ return;
+
+ var events = jQuery.data(elem, "events"), ret, index;
+
+ if ( events ) {
+ // Unbind all events for the element
+ if ( types === undefined || (typeof types === "string" && types.charAt(0) == ".") )
+ for ( var type in events )
+ this.remove( elem, type + (types || "") );
+ else {
+ // types is actually an event object here
+ if ( types.type ) {
+ handler = types.handler;
+ types = types.type;
+ }
+
+ // Handle multiple events seperated by a space
+ // jQuery(...).unbind("mouseover mouseout", fn);
+ jQuery.each(types.split(/\s+/), function(index, type){
+ // Namespaced event handlers
+ var namespaces = type.split(".");
+ type = namespaces.shift();
+ var namespace = RegExp("(^|\\.)" + namespaces.slice().sort().join(".*\\.") + "(\\.|$)");
+
+ if ( events[type] ) {
+ // remove the given handler for the given type
+ if ( handler )
+ delete events[type][handler.guid];
+
+ // remove all handlers for the given type
+ else
+ for ( var handle in events[type] )
+ // Handle the removal of namespaced events
+ if ( namespace.test(events[type][handle].type) )
+ delete events[type][handle];
+
+ if ( jQuery.event.specialAll[type] )
+ jQuery.event.specialAll[type].teardown.call(elem, namespaces);
+
+ // remove generic event handler if no more handlers exist
+ for ( ret in events[type] ) break;
+ if ( !ret ) {
+ if ( !jQuery.event.special[type] || jQuery.event.special[type].teardown.call(elem, namespaces) === false ) {
+ if (elem.removeEventListener)
+ elem.removeEventListener(type, jQuery.data(elem, "handle"), false);
+ else if (elem.detachEvent)
+ elem.detachEvent("on" + type, jQuery.data(elem, "handle"));
+ }
+ ret = null;
+ delete events[type];
+ }
+ }
+ });
+ }
+
+ // Remove the expando if it's no longer used
+ for ( ret in events ) break;
+ if ( !ret ) {
+ var handle = jQuery.data( elem, "handle" );
+ if ( handle ) handle.elem = null;
+ jQuery.removeData( elem, "events" );
+ jQuery.removeData( elem, "handle" );
+ }
+ }
+ },
+
+ // bubbling is internal
+ trigger: function( event, data, elem, bubbling ) {
+ // Event object or event type
+ var type = event.type || event;
+
+ if( !bubbling ){
+ event = typeof event === "object" ?
+ // jQuery.Event object
+ event[expando] ? event :
+ // Object literal
+ jQuery.extend( jQuery.Event(type), event ) :
+ // Just the event type (string)
+ jQuery.Event(type);
+
+ if ( type.indexOf("!") >= 0 ) {
+ event.type = type = type.slice(0, -1);
+ event.exclusive = true;
+ }
+
+ // Handle a global trigger
+ if ( !elem ) {
+ // Don't bubble custom events when global (to avoid too much overhead)
+ event.stopPropagation();
+ // Only trigger if we've ever bound an event for it
+ if ( this.global[type] )
+ jQuery.each( jQuery.cache, function(){
+ if ( this.events && this.events[type] )
+ jQuery.event.trigger( event, data, this.handle.elem );
+ });
+ }
+
+ // Handle triggering a single element
+
+ // don't do events on text and comment nodes
+ if ( !elem || elem.nodeType == 3 || elem.nodeType == 8 )
+ return undefined;
+
+ // Clean up in case it is reused
+ event.result = undefined;
+ event.target = elem;
+
+ // Clone the incoming data, if any
+ data = jQuery.makeArray(data);
+ data.unshift( event );
+ }
+
+ event.currentTarget = elem;
+
+ // Trigger the event, it is assumed that "handle" is a function
+ var handle = jQuery.data(elem, "handle");
+ if ( handle )
+ handle.apply( elem, data );
+
+ // Handle triggering native .onfoo handlers (and on links since we don't call .click() for links)
+ if ( (!elem[type] || (jQuery.nodeName(elem, 'a') && type == "click")) && elem["on"+type] && elem["on"+type].apply( elem, data ) === false )
+ event.result = false;
+
+ // Trigger the native events (except for clicks on links)
+ if ( !bubbling && elem[type] && !event.isDefaultPrevented() && !(jQuery.nodeName(elem, 'a') && type == "click") ) {
+ this.triggered = true;
+ try {
+ elem[ type ]();
+ // prevent IE from throwing an error for some hidden elements
+ } catch (e) {}
+ }
+
+ this.triggered = false;
+
+ if ( !event.isPropagationStopped() ) {
+ var parent = elem.parentNode || elem.ownerDocument;
+ if ( parent )
+ jQuery.event.trigger(event, data, parent, true);
+ }
+ },
+
+ handle: function(event) {
+ // returned undefined or false
+ var all, handlers;
+
+ event = arguments[0] = jQuery.event.fix( event || window.event );
+ event.currentTarget = this;
+
+ // Namespaced event handlers
+ var namespaces = event.type.split(".");
+ event.type = namespaces.shift();
+
+ // Cache this now, all = true means, any handler
+ all = !namespaces.length && !event.exclusive;
+
+ var namespace = RegExp("(^|\\.)" + namespaces.slice().sort().join(".*\\.") + "(\\.|$)");
+
+ handlers = ( jQuery.data(this, "events") || {} )[event.type];
+
+ for ( var j in handlers ) {
+ var handler = handlers[j];
+
+ // Filter the functions by class
+ if ( all || namespace.test(handler.type) ) {
+ // Pass in a reference to the handler function itself
+ // So that we can later remove it
+ event.handler = handler;
+ event.data = handler.data;
+
+ var ret = handler.apply(this, arguments);
+
+ if( ret !== undefined ){
+ event.result = ret;
+ if ( ret === false ) {
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ }
+
+ if( event.isImmediatePropagationStopped() )
+ break;
+
+ }
+ }
+ },
+
+ props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
+
+ fix: function(event) {
+ if ( event[expando] )
+ return event;
+
+ // store a copy of the original event object
+ // and "clone" to set read-only properties
+ var originalEvent = event;
+ event = jQuery.Event( originalEvent );
+
+ for ( var i = this.props.length, prop; i; ){
+ prop = this.props[ --i ];
+ event[ prop ] = originalEvent[ prop ];
+ }
+
+ // Fix target property, if necessary
+ if ( !event.target )
+ event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either
+
+ // check if target is a textnode (safari)
+ if ( event.target.nodeType == 3 )
+ event.target = event.target.parentNode;
+
+ // Add relatedTarget, if necessary
+ if ( !event.relatedTarget && event.fromElement )
+ event.relatedTarget = event.fromElement == event.target ? event.toElement : event.fromElement;
+
+ // Calculate pageX/Y if missing and clientX/Y available
+ if ( event.pageX == null && event.clientX != null ) {
+ var doc = document.documentElement, body = document.body;
+ event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc.clientLeft || 0);
+ event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc.clientTop || 0);
+ }
+
+ // Add which for key events
+ if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) )
+ event.which = event.charCode || event.keyCode;
+
+ // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
+ if ( !event.metaKey && event.ctrlKey )
+ event.metaKey = event.ctrlKey;
+
+ // Add which for click: 1 == left; 2 == middle; 3 == right
+ // Note: button is not normalized, so don't use it
+ if ( !event.which && event.button )
+ event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
+
+ return event;
+ },
+
+ proxy: function( fn, proxy ){
+ proxy = proxy || function(){ return fn.apply(this, arguments); };
+ // Set the guid of unique handler to the same of original handler, so it can be removed
+ proxy.guid = fn.guid = fn.guid || proxy.guid || this.guid++;
+ // So proxy can be declared as an argument
+ return proxy;
+ },
+
+ special: {
+ ready: {
+ // Make sure the ready event is setup
+ setup: bindReady,
+ teardown: function() {}
+ }
+ },
+
+ specialAll: {
+ live: {
+ setup: function( selector, namespaces ){
+ jQuery.event.add( this, namespaces[0], liveHandler );
+ },
+ teardown: function( namespaces ){
+ if ( namespaces.length ) {
+ var remove = 0, name = RegExp("(^|\\.)" + namespaces[0] + "(\\.|$)");
+
+ jQuery.each( (jQuery.data(this, "events").live || {}), function(){
+ if ( name.test(this.type) )
+ remove++;
+ });
+
+ if ( remove < 1 )
+ jQuery.event.remove( this, namespaces[0], liveHandler );
+ }
+ }
+ }
+ }
+};
+
+jQuery.Event = function( src ){
+ // Allow instantiation without the 'new' keyword
+ if( !this.preventDefault )
+ return new jQuery.Event(src);
+
+ // Event object
+ if( src && src.type ){
+ this.originalEvent = src;
+ this.type = src.type;
+ // Event type
+ }else
+ this.type = src;
+
+ // timeStamp is buggy for some events on Firefox(#3843)
+ // So we won't rely on the native value
+ this.timeStamp = now();
+
+ // Mark it as fixed
+ this[expando] = true;
+};
+
+function returnFalse(){
+ return false;
+}
+function returnTrue(){
+ return true;
+}
+
+// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
+// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
+jQuery.Event.prototype = {
+ preventDefault: function() {
+ this.isDefaultPrevented = returnTrue;
+
+ var e = this.originalEvent;
+ if( !e )
+ return;
+ // if preventDefault exists run it on the original event
+ if (e.preventDefault)
+ e.preventDefault();
+ // otherwise set the returnValue property of the original event to false (IE)
+ e.returnValue = false;
+ },
+ stopPropagation: function() {
+ this.isPropagationStopped = returnTrue;
+
+ var e = this.originalEvent;
+ if( !e )
+ return;
+ // if stopPropagation exists run it on the original event
+ if (e.stopPropagation)
+ e.stopPropagation();
+ // otherwise set the cancelBubble property of the original event to true (IE)
+ e.cancelBubble = true;
+ },
+ stopImmediatePropagation:function(){
+ this.isImmediatePropagationStopped = returnTrue;
+ this.stopPropagation();
+ },
+ isDefaultPrevented: returnFalse,
+ isPropagationStopped: returnFalse,
+ isImmediatePropagationStopped: returnFalse
+};
+// Checks if an event happened on an element within another element
+// Used in jQuery.event.special.mouseenter and mouseleave handlers
+var withinElement = function(event) {
+ // Check if mouse(over|out) are still within the same parent element
+ var parent = event.relatedTarget;
+ // Traverse up the tree
+ while ( parent && parent != this )
+ try { parent = parent.parentNode; }
+ catch(e) { parent = this; }
+
+ if( parent != this ){
+ // set the correct event type
+ event.type = event.data;
+ // handle event if we actually just moused on to a non sub-element
+ jQuery.event.handle.apply( this, arguments );
+ }
+};
+
+jQuery.each({
+ mouseover: 'mouseenter',
+ mouseout: 'mouseleave'
+}, function( orig, fix ){
+ jQuery.event.special[ fix ] = {
+ setup: function(){
+ jQuery.event.add( this, orig, withinElement, fix );
+ },
+ teardown: function(){
+ jQuery.event.remove( this, orig, withinElement );
+ }
+ };
+});
+
+jQuery.fn.extend({
+ bind: function( type, data, fn ) {
+ return type == "unload" ? this.one(type, data, fn) : this.each(function(){
+ jQuery.event.add( this, type, fn || data, fn && data );
+ });
+ },
+
+ one: function( type, data, fn ) {
+ var one = jQuery.event.proxy( fn || data, function(event) {
+ jQuery(this).unbind(event, one);
+ return (fn || data).apply( this, arguments );
+ });
+ return this.each(function(){
+ jQuery.event.add( this, type, one, fn && data);
+ });
+ },
+
+ unbind: function( type, fn ) {
+ return this.each(function(){
+ jQuery.event.remove( this, type, fn );
+ });
+ },
+
+ trigger: function( type, data ) {
+ return this.each(function(){
+ jQuery.event.trigger( type, data, this );
+ });
+ },
+
+ triggerHandler: function( type, data ) {
+ if( this[0] ){
+ var event = jQuery.Event(type);
+ event.preventDefault();
+ event.stopPropagation();
+ jQuery.event.trigger( event, data, this[0] );
+ return event.result;
+ }
+ },
+
+ toggle: function( fn ) {
+ // Save reference to arguments for access in closure
+ var args = arguments, i = 1;
+
+ // link all the functions, so any of them can unbind this click handler
+ while( i < args.length )
+ jQuery.event.proxy( fn, args[i++] );
+
+ return this.click( jQuery.event.proxy( fn, function(event) {
+ // Figure out which function to execute
+ this.lastToggle = ( this.lastToggle || 0 ) % i;
+
+ // Make sure that clicks stop
+ event.preventDefault();
+
+ // and execute the function
+ return args[ this.lastToggle++ ].apply( this, arguments ) || false;
+ }));
+ },
+
+ hover: function(fnOver, fnOut) {
+ return this.mouseenter(fnOver).mouseleave(fnOut);
+ },
+
+ ready: function(fn) {
+ // Attach the listeners
+ bindReady();
+
+ // If the DOM is already ready
+ if ( jQuery.isReady )
+ // Execute the function immediately
+ fn.call( document, jQuery );
+
+ // Otherwise, remember the function for later
+ else
+ // Add the function to the wait list
+ jQuery.readyList.push( fn );
+
+ return this;
+ },
+
+ live: function( type, fn ){
+ var proxy = jQuery.event.proxy( fn );
+ proxy.guid += this.selector + type;
+
+ jQuery(document).bind( liveConvert(type, this.selector), this.selector, proxy );
+
+ return this;
+ },
+
+ die: function( type, fn ){
+ jQuery(document).unbind( liveConvert(type, this.selector), fn ? { guid: fn.guid + this.selector + type } : null );
+ return this;
+ }
+});
+
+function liveHandler( event ){
+ var check = RegExp("(^|\\.)" + event.type + "(\\.|$)"),
+ stop = true,
+ elems = [];
+
+ jQuery.each(jQuery.data(this, "events").live || [], function(i, fn){
+ if ( check.test(fn.type) ) {
+ var elem = jQuery(event.target).closest(fn.data)[0];
+ if ( elem )
+ elems.push({ elem: elem, fn: fn });
+ }
+ });
+
+ elems.sort(function(a,b) {
+ return jQuery.data(a.elem, "closest") - jQuery.data(b.elem, "closest");
+ });
+
+ jQuery.each(elems, function(){
+ if ( this.fn.call(this.elem, event, this.fn.data) === false )
+ return (stop = false);
+ });
+
+ return stop;
+}
+
+function liveConvert(type, selector){
+ return ["live", type, selector.replace(/\./g, "`").replace(/ /g, "|")].join(".");
+}
+
+jQuery.extend({
+ isReady: false,
+ readyList: [],
+ // Handle when the DOM is ready
+ ready: function() {
+ // Make sure that the DOM is not already loaded
+ if ( !jQuery.isReady ) {
+ // Remember that the DOM is ready
+ jQuery.isReady = true;
+
+ // If there are functions bound, to execute
+ if ( jQuery.readyList ) {
+ // Execute all of them
+ jQuery.each( jQuery.readyList, function(){
+ this.call( document, jQuery );
+ });
+
+ // Reset the list of functions
+ jQuery.readyList = null;
+ }
+
+ // Trigger any bound ready events
+ jQuery(document).triggerHandler("ready");
+ }
+ }
+});
+
+var readyBound = false;
+
+function bindReady(){
+ if ( readyBound ) return;
+ readyBound = true;
+
+ // Mozilla, Opera and webkit nightlies currently support this event
+ if ( document.addEventListener ) {
+ // Use the handy event callback
+ document.addEventListener( "DOMContentLoaded", function(){
+ document.removeEventListener( "DOMContentLoaded", arguments.callee, false );
+ jQuery.ready();
+ }, false );
+
+ // If IE event model is used
+ } else if ( document.attachEvent ) {
+ // ensure firing before onload,
+ // maybe late but safe also for iframes
+ document.attachEvent("onreadystatechange", function(){
+ if ( document.readyState === "complete" ) {
+ document.detachEvent( "onreadystatechange", arguments.callee );
+ jQuery.ready();
+ }
+ });
+
+ // If IE and not an iframe
+ // continually check to see if the document is ready
+ if ( document.documentElement.doScroll && window == window.top ) (function(){
+ if ( jQuery.isReady ) return;
+
+ try {
+ // If IE is used, use the trick by Diego Perini
+ // http://javascript.nwbox.com/IEContentLoaded/
+ document.documentElement.doScroll("left");
+ } catch( error ) {
+ setTimeout( arguments.callee, 0 );
+ return;
+ }
+
+ // and execute any waiting functions
+ jQuery.ready();
+ })();
+ }
+
+ // A fallback to window.onload, that will always work
+ jQuery.event.add( window, "load", jQuery.ready );
+}
+
+jQuery.each( ("blur,focus,load,resize,scroll,unload,click,dblclick," +
+ "mousedown,mouseup,mousemove,mouseover,mouseout,mouseenter,mouseleave," +
+ "change,select,submit,keydown,keypress,keyup,error").split(","), function(i, name){
+
+ // Handle event binding
+ jQuery.fn[name] = function(fn){
+ return fn ? this.bind(name, fn) : this.trigger(name);
+ };
+});
+
+// Prevent memory leaks in IE
+// And prevent errors on refresh with events like mouseover in other browsers
+// Window isn't included so as not to unbind existing unload events
+jQuery( window ).bind( 'unload', function(){
+ for ( var id in jQuery.cache )
+ // Skip the window
+ if ( id != 1 && jQuery.cache[ id ].handle )
+ jQuery.event.remove( jQuery.cache[ id ].handle.elem );
+});
+(function(){
+
+ jQuery.support = {};
+
+ var root = document.documentElement,
+ script = document.createElement("script"),
+ div = document.createElement("div"),
+ id = "script" + (new Date).getTime();
+
+ div.style.display = "none";
+ div.innerHTML = ' <link/><table></table><a href="/a" style="color:red;float:left;opacity:.5;">a</a><select><option>text</option></select><object><param/></object>';
+
+ var all = div.getElementsByTagName("*"),
+ a = div.getElementsByTagName("a")[0];
+
+ // Can't get basic test support
+ if ( !all || !all.length || !a ) {
+ return;
+ }
+
+ jQuery.support = {
+ // IE strips leading whitespace when .innerHTML is used
+ leadingWhitespace: div.firstChild.nodeType == 3,
+
+ // Make sure that tbody elements aren't automatically inserted
+ // IE will insert them into empty tables
+ tbody: !div.getElementsByTagName("tbody").length,
+
+ // Make sure that you can get all elements in an <object> element
+ // IE 7 always returns no results
+ objectAll: !!div.getElementsByTagName("object")[0]
+ .getElementsByTagName("*").length,
+
+ // Make sure that link elements get serialized correctly by innerHTML
+ // This requires a wrapper element in IE
+ htmlSerialize: !!div.getElementsByTagName("link").length,
+
+ // Get the style information from getAttribute
+ // (IE uses .cssText insted)
+ style: /red/.test( a.getAttribute("style") ),
+
+ // Make sure that URLs aren't manipulated
+ // (IE normalizes it by default)
+ hrefNormalized: a.getAttribute("href") === "/a",
+
+ // Make sure that element opacity exists
+ // (IE uses filter instead)
+ opacity: a.style.opacity === "0.5",
+
+ // Verify style float existence
+ // (IE uses styleFloat instead of cssFloat)
+ cssFloat: !!a.style.cssFloat,
+
+ // Will be defined later
+ scriptEval: false,
+ noCloneEvent: true,
+ boxModel: null
+ };
+
+ script.type = "text/javascript";
+ try {
+ script.appendChild( document.createTextNode( "window." + id + "=1;" ) );
+ } catch(e){}
+
+ root.insertBefore( script, root.firstChild );
+
+ // Make sure that the execution of code works by injecting a script
+ // tag with appendChild/createTextNode
+ // (IE doesn't support this, fails, and uses .text instead)
+ if ( window[ id ] ) {
+ jQuery.support.scriptEval = true;
+ delete window[ id ];
+ }
+
+ root.removeChild( script );
+
+ if ( div.attachEvent && div.fireEvent ) {
+ div.attachEvent("onclick", function(){
+ // Cloning a node shouldn't copy over any
+ // bound event handlers (IE does this)
+ jQuery.support.noCloneEvent = false;
+ div.detachEvent("onclick", arguments.callee);
+ });
+ div.cloneNode(true).fireEvent("onclick");
+ }
+
+ // Figure out if the W3C box model works as expected
+ // document.body must exist before we can do this
+ jQuery(function(){
+ var div = document.createElement("div");
+ div.style.width = div.style.paddingLeft = "1px";
+
+ document.body.appendChild( div );
+ jQuery.boxModel = jQuery.support.boxModel = div.offsetWidth === 2;
+ document.body.removeChild( div ).style.display = 'none';
+ });
+})();
+
+var styleFloat = jQuery.support.cssFloat ? "cssFloat" : "styleFloat";
+
+jQuery.props = {
+ "for": "htmlFor",
+ "class": "className",
+ "float": styleFloat,
+ cssFloat: styleFloat,
+ styleFloat: styleFloat,
+ readonly: "readOnly",
+ maxlength: "maxLength",
+ cellspacing: "cellSpacing",
+ rowspan: "rowSpan",
+ tabindex: "tabIndex"
+};
+jQuery.fn.extend({
+ // Keep a copy of the old load
+ _load: jQuery.fn.load,
+
+ load: function( url, params, callback ) {
+ if ( typeof url !== "string" )
+ return this._load( url );
+
+ var off = url.indexOf(" ");
+ if ( off >= 0 ) {
+ var selector = url.slice(off, url.length);
+ url = url.slice(0, off);
+ }
+
+ // Default to a GET request
+ var type = "GET";
+
+ // If the second parameter was provided
+ if ( params )
+ // If it's a function
+ if ( jQuery.isFunction( params ) ) {
+ // We assume that it's the callback
+ callback = params;
+ params = null;
+
+ // Otherwise, build a param string
+ } else if( typeof params === "object" ) {
+ params = jQuery.param( params );
+ type = "POST";
+ }
+
+ var self = this;
+
+ // Request the remote document
+ jQuery.ajax({
+ url: url,
+ type: type,
+ dataType: "html",
+ data: params,
+ complete: function(res, status){
+ // If successful, inject the HTML into all the matched elements
+ if ( status == "success" || status == "notmodified" )
+ // See if a selector was specified
+ self.html( selector ?
+ // Create a dummy div to hold the results
+ jQuery("<div/>")
+ // inject the contents of the document in, removing the scripts
+ // to avoid any 'Permission Denied' errors in IE
+ .append(res.responseText.replace(/<script(.|\s)*?\/script>/g, ""))
+
+ // Locate the specified elements
+ .find(selector) :
+
+ // If not, just inject the full result
+ res.responseText );
+
+ if( callback )
+ self.each( callback, [res.responseText, status, res] );
+ }
+ });
+ return this;
+ },
+
+ serialize: function() {
+ return jQuery.param(this.serializeArray());
+ },
+ serializeArray: function() {
+ return this.map(function(){
+ return this.elements ? jQuery.makeArray(this.elements) : this;
+ })
+ .filter(function(){
+ return this.name && !this.disabled &&
+ (this.checked || /select|textarea/i.test(this.nodeName) ||
+ /text|hidden|password|search/i.test(this.type));
+ })
+ .map(function(i, elem){
+ var val = jQuery(this).val();
+ return val == null ? null :
+ jQuery.isArray(val) ?
+ jQuery.map( val, function(val, i){
+ return {name: elem.name, value: val};
+ }) :
+ {name: elem.name, value: val};
+ }).get();
+ }
+});
+
+// Attach a bunch of functions for handling common AJAX events
+jQuery.each( "ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","), function(i,o){
+ jQuery.fn[o] = function(f){
+ return this.bind(o, f);
+ };
+});
+
+var jsc = now();
+
+jQuery.extend({
+
+ get: function( url, data, callback, type ) {
+ // shift arguments if data argument was ommited
+ if ( jQuery.isFunction( data ) ) {
+ callback = data;
+ data = null;
+ }
+
+ return jQuery.ajax({
+ type: "GET",
+ url: url,
+ data: data,
+ success: callback,
+ dataType: type
+ });
+ },
+
+ getScript: function( url, callback ) {
+ return jQuery.get(url, null, callback, "script");
+ },
+
+ getJSON: function( url, data, callback ) {
+ return jQuery.get(url, data, callback, "json");
+ },
+
+ post: function( url, data, callback, type ) {
+ if ( jQuery.isFunction( data ) ) {
+ callback = data;
+ data = {};
+ }
+
+ return jQuery.ajax({
+ type: "POST",
+ url: url,
+ data: data,
+ success: callback,
+ dataType: type
+ });
+ },
+
+ ajaxSetup: function( settings ) {
+ jQuery.extend( jQuery.ajaxSettings, settings );
+ },
+
+ ajaxSettings: {
+ url: location.href,
+ global: true,
+ type: "GET",
+ contentType: "application/x-www-form-urlencoded",
+ processData: true,
+ async: true,
+ /*
+ timeout: 0,
+ data: null,
+ username: null,
+ password: null,
+ */
+ // Create the request object; Microsoft failed to properly
+ // implement the XMLHttpRequest in IE7, so we use the ActiveXObject when it is available
+ // This function can be overriden by calling jQuery.ajaxSetup
+ xhr:function(){
+ return window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest();
+ },
+ accepts: {
+ xml: "application/xml, text/xml",
+ html: "text/html",
+ script: "text/javascript, application/javascript",
+ json: "application/json, text/javascript",
+ text: "text/plain",
+ _default: "*/*"
+ }
+ },
+
+ // Last-Modified header cache for next request
+ lastModified: {},
+
+ ajax: function( s ) {
+ // Extend the settings, but re-extend 's' so that it can be
+ // checked again later (in the test suite, specifically)
+ s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s));
+
+ var jsonp, jsre = /=\?(&|$)/g, status, data,
+ type = s.type.toUpperCase();
+
+ // convert data if not already a string
+ if ( s.data && s.processData && typeof s.data !== "string" )
+ s.data = jQuery.param(s.data);
+
+ // Handle JSONP Parameter Callbacks
+ if ( s.dataType == "jsonp" ) {
+ if ( type == "GET" ) {
+ if ( !s.url.match(jsre) )
+ s.url += (s.url.match(/\?/) ? "&" : "?") + (s.jsonp || "callback") + "=?";
+ } else if ( !s.data || !s.data.match(jsre) )
+ s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?";
+ s.dataType = "json";
+ }
+
+ // Build temporary JSONP function
+ if ( s.dataType == "json" && (s.data && s.data.match(jsre) || s.url.match(jsre)) ) {
+ jsonp = "jsonp" + jsc++;
+
+ // Replace the =? sequence both in the query string and the data
+ if ( s.data )
+ s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");
+ s.url = s.url.replace(jsre, "=" + jsonp + "$1");
+
+ // We need to make sure
+ // that a JSONP style response is executed properly
+ s.dataType = "script";
+
+ // Handle JSONP-style loading
+ window[ jsonp ] = function(tmp){
+ data = tmp;
+ success();
+ complete();
+ // Garbage collect
+ window[ jsonp ] = undefined;
+ try{ delete window[ jsonp ]; } catch(e){}
+ if ( head )
+ head.removeChild( script );
+ };
+ }
+
+ if ( s.dataType == "script" && s.cache == null )
+ s.cache = false;
+
+ if ( s.cache === false && type == "GET" ) {
+ var ts = now();
+ // try replacing _= if it is there
+ var ret = s.url.replace(/(\?|&)_=.*?(&|$)/, "$1_=" + ts + "$2");
+ // if nothing was replaced, add timestamp to the end
+ s.url = ret + ((ret == s.url) ? (s.url.match(/\?/) ? "&" : "?") + "_=" + ts : "");
+ }
+
+ // If data is available, append data to url for get requests
+ if ( s.data && type == "GET" ) {
+ s.url += (s.url.match(/\?/) ? "&" : "?") + s.data;
+
+ // IE likes to send both get and post data, prevent this
+ s.data = null;
+ }
+
+ // Watch for a new set of requests
+ if ( s.global && ! jQuery.active++ )
+ jQuery.event.trigger( "ajaxStart" );
+
+ // Matches an absolute URL, and saves the domain
+ var parts = /^(\w+:)?\/\/([^\/?#]+)/.exec( s.url );
+
+ // If we're requesting a remote document
+ // and trying to load JSON or Script with a GET
+ if ( s.dataType == "script" && type == "GET" && parts
+ && ( parts[1] && parts[1] != location.protocol || parts[2] != location.host )){
+
+ var head = document.getElementsByTagName("head")[0];
+ var script = document.createElement("script");
+ script.src = s.url;
+ if (s.scriptCharset)
+ script.charset = s.scriptCharset;
+
+ // Handle Script loading
+ if ( !jsonp ) {
+ var done = false;
+
+ // Attach handlers for all browsers
+ script.onload = script.onreadystatechange = function(){
+ if ( !done && (!this.readyState ||
+ this.readyState == "loaded" || this.readyState == "complete") ) {
+ done = true;
+ success();
+ complete();
+
+ // Handle memory leak in IE
+ script.onload = script.onreadystatechange = null;
+ head.removeChild( script );
+ }
+ };
+ }
+
+ head.appendChild(script);
+
+ // We handle everything using the script element injection
+ return undefined;
+ }
+
+ var requestDone = false;
+
+ // Create the request object
+ var xhr = s.xhr();
+
+ // Open the socket
+ // Passing null username, generates a login popup on Opera (#2865)
+ if( s.username )
+ xhr.open(type, s.url, s.async, s.username, s.password);
+ else
+ xhr.open(type, s.url, s.async);
+
+ // Need an extra try/catch for cross domain requests in Firefox 3
+ try {
+ // Set the correct header, if data is being sent
+ if ( s.data )
+ xhr.setRequestHeader("Content-Type", s.contentType);
+
+ // Set the If-Modified-Since header, if ifModified mode.
+ if ( s.ifModified )
+ xhr.setRequestHeader("If-Modified-Since",
+ jQuery.lastModified[s.url] || "Thu, 01 Jan 1970 00:00:00 GMT" );
+
+ // Set header so the called script knows that it's an XMLHttpRequest
+ xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
+
+ // Set the Accepts header for the server, depending on the dataType
+ xhr.setRequestHeader("Accept", s.dataType && s.accepts[ s.dataType ] ?
+ s.accepts[ s.dataType ] + ", */*" :
+ s.accepts._default );
+ } catch(e){}
+
+ // Allow custom headers/mimetypes and early abort
+ if ( s.beforeSend && s.beforeSend(xhr, s) === false ) {
+ // Handle the global AJAX counter
+ if ( s.global && ! --jQuery.active )
+ jQuery.event.trigger( "ajaxStop" );
+ // close opended socket
+ xhr.abort();
+ return false;
+ }
+
+ if ( s.global )
+ jQuery.event.trigger("ajaxSend", [xhr, s]);
+
+ // Wait for a response to come back
+ var onreadystatechange = function(isTimeout){
+ // The request was aborted, clear the interval and decrement jQuery.active
+ if (xhr.readyState == 0) {
+ if (ival) {
+ // clear poll interval
+ clearInterval(ival);
+ ival = null;
+ // Handle the global AJAX counter
+ if ( s.global && ! --jQuery.active )
+ jQuery.event.trigger( "ajaxStop" );
+ }
+ // The transfer is complete and the data is available, or the request timed out
+ } else if ( !requestDone && xhr && (xhr.readyState == 4 || isTimeout == "timeout") ) {
+ requestDone = true;
+
+ // clear poll interval
+ if (ival) {
+ clearInterval(ival);
+ ival = null;
+ }
+
+ status = isTimeout == "timeout" ? "timeout" :
+ !jQuery.httpSuccess( xhr ) ? "error" :
+ s.ifModified && jQuery.httpNotModified( xhr, s.url ) ? "notmodified" :
+ "success";
+
+ if ( status == "success" ) {
+ // Watch for, and catch, XML document parse errors
+ try {
+ // process the data (runs the xml through httpData regardless of callback)
+ data = jQuery.httpData( xhr, s.dataType, s );
+ } catch(e) {
+ status = "parsererror";
+ }
+ }
+
+ // Make sure that the request was successful or notmodified
+ if ( status == "success" ) {
+ // Cache Last-Modified header, if ifModified mode.
+ var modRes;
+ try {
+ modRes = xhr.getResponseHeader("Last-Modified");
+ } catch(e) {} // swallow exception thrown by FF if header is not available
+
+ if ( s.ifModified && modRes )
+ jQuery.lastModified[s.url] = modRes;
+
+ // JSONP handles its own success callback
+ if ( !jsonp )
+ success();
+ } else
+ jQuery.handleError(s, xhr, status);
+
+ // Fire the complete handlers
+ complete();
+
+ if ( isTimeout )
+ xhr.abort();
+
+ // Stop memory leaks
+ if ( s.async )
+ xhr = null;
+ }
+ };
+
+ if ( s.async ) {
+ // don't attach the handler to the request, just poll it instead
+ var ival = setInterval(onreadystatechange, 13);
+
+ // Timeout checker
+ if ( s.timeout > 0 )
+ setTimeout(function(){
+ // Check to see if the request is still happening
+ if ( xhr && !requestDone )
+ onreadystatechange( "timeout" );
+ }, s.timeout);
+ }
+
+ // Send the data
+ try {
+ xhr.send(s.data);
+ } catch(e) {
+ jQuery.handleError(s, xhr, null, e);
+ }
+
+ // firefox 1.5 doesn't fire statechange for sync requests
+ if ( !s.async )
+ onreadystatechange();
+
+ function success(){
+ // If a local callback was specified, fire it and pass it the data
+ if ( s.success )
+ s.success( data, status );
+
+ // Fire the global callback
+ if ( s.global )
+ jQuery.event.trigger( "ajaxSuccess", [xhr, s] );
+ }
+
+ function complete(){
+ // Process result
+ if ( s.complete )
+ s.complete(xhr, status);
+
+ // The request was completed
+ if ( s.global )
+ jQuery.event.trigger( "ajaxComplete", [xhr, s] );
+
+ // Handle the global AJAX counter
+ if ( s.global && ! --jQuery.active )
+ jQuery.event.trigger( "ajaxStop" );
+ }
+
+ // return XMLHttpRequest to allow aborting the request etc.
+ return xhr;
+ },
+
+ handleError: function( s, xhr, status, e ) {
+ // If a local callback was specified, fire it
+ if ( s.error ) s.error( xhr, status, e );
+
+ // Fire the global callback
+ if ( s.global )
+ jQuery.event.trigger( "ajaxError", [xhr, s, e] );
+ },
+
+ // Counter for holding the number of active queries
+ active: 0,
+
+ // Determines if an XMLHttpRequest was successful or not
+ httpSuccess: function( xhr ) {
+ try {
+ // IE error sometimes returns 1223 when it should be 204 so treat it as success, see #1450
+ return !xhr.status && location.protocol == "file:" ||
+ ( xhr.status >= 200 && xhr.status < 300 ) || xhr.status == 304 || xhr.status == 1223;
+ } catch(e){}
+ return false;
+ },
+
+ // Determines if an XMLHttpRequest returns NotModified
+ httpNotModified: function( xhr, url ) {
+ try {
+ var xhrRes = xhr.getResponseHeader("Last-Modified");
+
+ // Firefox always returns 200. check Last-Modified date
+ return xhr.status == 304 || xhrRes == jQuery.lastModified[url];
+ } catch(e){}
+ return false;
+ },
+
+ httpData: function( xhr, type, s ) {
+ var ct = xhr.getResponseHeader("content-type"),
+ xml = type == "xml" || !type && ct && ct.indexOf("xml") >= 0,
+ data = xml ? xhr.responseXML : xhr.responseText;
+
+ if ( xml && data.documentElement.tagName == "parsererror" )
+ throw "parsererror";
+
+ // Allow a pre-filtering function to sanitize the response
+ // s != null is checked to keep backwards compatibility
+ if( s && s.dataFilter )
+ data = s.dataFilter( data, type );
+
+ // The filter can actually parse the response
+ if( typeof data === "string" ){
+
+ // If the type is "script", eval it in global context
+ if ( type == "script" )
+ jQuery.globalEval( data );
+
+ // Get the JavaScript object, if JSON is used.
+ if ( type == "json" )
+ data = window["eval"]("(" + data + ")");
+ }
+
+ return data;
+ },
+
+ // Serialize an array of form elements or a set of
+ // key/values into a query string
+ param: function( a ) {
+ var s = [ ];
+
+ function add( key, value ){
+ s[ s.length ] = encodeURIComponent(key) + '=' + encodeURIComponent(value);
+ };
+
+ // If an array was passed in, assume that it is an array
+ // of form elements
+ if ( jQuery.isArray(a) || a.jquery )
+ // Serialize the form elements
+ jQuery.each( a, function(){
+ add( this.name, this.value );
+ });
+
+ // Otherwise, assume that it's an object of key/value pairs
+ else
+ // Serialize the key/values
+ for ( var j in a )
+ // If the value is an array then the key names need to be repeated
+ if ( jQuery.isArray(a[j]) )
+ jQuery.each( a[j], function(){
+ add( j, this );
+ });
+ else
+ add( j, jQuery.isFunction(a[j]) ? a[j]() : a[j] );
+
+ // Return the resulting serialization
+ return s.join("&").replace(/%20/g, "+");
+ }
+
+});
+var elemdisplay = {},
+ timerId,
+ fxAttrs = [
+ // height animations
+ [ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ],
+ // width animations
+ [ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ],
+ // opacity animations
+ [ "opacity" ]
+ ];
+
+function genFx( type, num ){
+ var obj = {};
+ jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice(0,num)), function(){
+ obj[ this ] = type;
+ });
+ return obj;
+}
+
+jQuery.fn.extend({
+ show: function(speed,callback){
+ if ( speed ) {
+ return this.animate( genFx("show", 3), speed, callback);
+ } else {
+ for ( var i = 0, l = this.length; i < l; i++ ){
+ var old = jQuery.data(this[i], "olddisplay");
+
+ this[i].style.display = old || "";
+
+ if ( jQuery.css(this[i], "display") === "none" ) {
+ var tagName = this[i].tagName, display;
+
+ if ( elemdisplay[ tagName ] ) {
+ display = elemdisplay[ tagName ];
+ } else {
+ var elem = jQuery("<" + tagName + " />").appendTo("body");
+
+ display = elem.css("display");
+ if ( display === "none" )
+ display = "block";
+
+ elem.remove();
+
+ elemdisplay[ tagName ] = display;
+ }
+
+ jQuery.data(this[i], "olddisplay", display);
+ }
+ }
+
+ // Set the display of the elements in a second loop
+ // to avoid the constant reflow
+ for ( var i = 0, l = this.length; i < l; i++ ){
+ this[i].style.display = jQuery.data(this[i], "olddisplay") || "";
+ }
+
+ return this;
+ }
+ },
+
+ hide: function(speed,callback){
+ if ( speed ) {
+ return this.animate( genFx("hide", 3), speed, callback);
+ } else {
+ for ( var i = 0, l = this.length; i < l; i++ ){
+ var old = jQuery.data(this[i], "olddisplay");
+ if ( !old && old !== "none" )
+ jQuery.data(this[i], "olddisplay", jQuery.css(this[i], "display"));
+ }
+
+ // Set the display of the elements in a second loop
+ // to avoid the constant reflow
+ for ( var i = 0, l = this.length; i < l; i++ ){
+ this[i].style.display = "none";
+ }
+
+ return this;
+ }
+ },
+
+ // Save the old toggle function
+ _toggle: jQuery.fn.toggle,
+
+ toggle: function( fn, fn2 ){
+ var bool = typeof fn === "boolean";
+
+ return jQuery.isFunction(fn) && jQuery.isFunction(fn2) ?
+ this._toggle.apply( this, arguments ) :
+ fn == null || bool ?
+ this.each(function(){
+ var state = bool ? fn : jQuery(this).is(":hidden");
+ jQuery(this)[ state ? "show" : "hide" ]();
+ }) :
+ this.animate(genFx("toggle", 3), fn, fn2);
+ },
+
+ fadeTo: function(speed,to,callback){
+ return this.animate({opacity: to}, speed, callback);
+ },
+
+ animate: function( prop, speed, easing, callback ) {
+ var optall = jQuery.speed(speed, easing, callback);
+
+ return this[ optall.queue === false ? "each" : "queue" ](function(){
+
+ var opt = jQuery.extend({}, optall), p,
+ hidden = this.nodeType == 1 && jQuery(this).is(":hidden"),
+ self = this;
+
+ for ( p in prop ) {
+ if ( prop[p] == "hide" && hidden || prop[p] == "show" && !hidden )
+ return opt.complete.call(this);
+
+ if ( ( p == "height" || p == "width" ) && this.style ) {
+ // Store display property
+ opt.display = jQuery.css(this, "display");
+
+ // Make sure that nothing sneaks out
+ opt.overflow = this.style.overflow;
+ }
+ }
+
+ if ( opt.overflow != null )
+ this.style.overflow = "hidden";
+
+ opt.curAnim = jQuery.extend({}, prop);
+
+ jQuery.each( prop, function(name, val){
+ var e = new jQuery.fx( self, opt, name );
+
+ if ( /toggle|show|hide/.test(val) )
+ e[ val == "toggle" ? hidden ? "show" : "hide" : val ]( prop );
+ else {
+ var parts = val.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),
+ start = e.cur(true) || 0;
+
+ if ( parts ) {
+ var end = parseFloat(parts[2]),
+ unit = parts[3] || "px";
+
+ // We need to compute starting value
+ if ( unit != "px" ) {
+ self.style[ name ] = (end || 1) + unit;
+ start = ((end || 1) / e.cur(true)) * start;
+ self.style[ name ] = start + unit;
+ }
+
+ // If a +=/-= token was provided, we're doing a relative animation
+ if ( parts[1] )
+ end = ((parts[1] == "-=" ? -1 : 1) * end) + start;
+
+ e.custom( start, end, unit );
+ } else
+ e.custom( start, val, "" );
+ }
+ });
+
+ // For JS strict compliance
+ return true;
+ });
+ },
+
+ stop: function(clearQueue, gotoEnd){
+ var timers = jQuery.timers;
+
+ if (clearQueue)
+ this.queue([]);
+
+ this.each(function(){
+ // go in reverse order so anything added to the queue during the loop is ignored
+ for ( var i = timers.length - 1; i >= 0; i-- )
+ if ( timers[i].elem == this ) {
+ if (gotoEnd)
+ // force the next step to be the last
+ timers[i](true);
+ timers.splice(i, 1);
+ }
+ });
+
+ // start the next in the queue if the last step wasn't forced
+ if (!gotoEnd)
+ this.dequeue();
+
+ return this;
+ }
+
+});
+
+// Generate shortcuts for custom animations
+jQuery.each({
+ slideDown: genFx("show", 1),
+ slideUp: genFx("hide", 1),
+ slideToggle: genFx("toggle", 1),
+ fadeIn: { opacity: "show" },
+ fadeOut: { opacity: "hide" }
+}, function( name, props ){
+ jQuery.fn[ name ] = function( speed, callback ){
+ return this.animate( props, speed, callback );
+ };
+});
+
+jQuery.extend({
+
+ speed: function(speed, easing, fn) {
+ var opt = typeof speed === "object" ? speed : {
+ complete: fn || !fn && easing ||
+ jQuery.isFunction( speed ) && speed,
+ duration: speed,
+ easing: fn && easing || easing && !jQuery.isFunction(easing) && easing
+ };
+
+ opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
+ jQuery.fx.speeds[opt.duration] || jQuery.fx.speeds._default;
+
+ // Queueing
+ opt.old = opt.complete;
+ opt.complete = function(){
+ if ( opt.queue !== false )
+ jQuery(this).dequeue();
+ if ( jQuery.isFunction( opt.old ) )
+ opt.old.call( this );
+ };
+
+ return opt;
+ },
+
+ easing: {
+ linear: function( p, n, firstNum, diff ) {
+ return firstNum + diff * p;
+ },
+ swing: function( p, n, firstNum, diff ) {
+ return ((-Math.cos(p*Math.PI)/2) + 0.5) * diff + firstNum;
+ }
+ },
+
+ timers: [],
+
+ fx: function( elem, options, prop ){
+ this.options = options;
+ this.elem = elem;
+ this.prop = prop;
+
+ if ( !options.orig )
+ options.orig = {};
+ }
+
+});
+
+jQuery.fx.prototype = {
+
+ // Simple function for setting a style value
+ update: function(){
+ if ( this.options.step )
+ this.options.step.call( this.elem, this.now, this );
+
+ (jQuery.fx.step[this.prop] || jQuery.fx.step._default)( this );
+
+ // Set display property to block for height/width animations
+ if ( ( this.prop == "height" || this.prop == "width" ) && this.elem.style )
+ this.elem.style.display = "block";
+ },
+
+ // Get the current size
+ cur: function(force){
+ if ( this.elem[this.prop] != null && (!this.elem.style || this.elem.style[this.prop] == null) )
+ return this.elem[ this.prop ];
+
+ var r = parseFloat(jQuery.css(this.elem, this.prop, force));
+ return r && r > -10000 ? r : parseFloat(jQuery.curCSS(this.elem, this.prop)) || 0;
+ },
+
+ // Start an animation from one number to another
+ custom: function(from, to, unit){
+ this.startTime = now();
+ this.start = from;
+ this.end = to;
+ this.unit = unit || this.unit || "px";
+ this.now = this.start;
+ this.pos = this.state = 0;
+
+ var self = this;
+ function t(gotoEnd){
+ return self.step(gotoEnd);
+ }
+
+ t.elem = this.elem;
+
+ if ( t() && jQuery.timers.push(t) && !timerId ) {
+ timerId = setInterval(function(){
+ var timers = jQuery.timers;
+
+ for ( var i = 0; i < timers.length; i++ )
+ if ( !timers[i]() )
+ timers.splice(i--, 1);
+
+ if ( !timers.length ) {
+ clearInterval( timerId );
+ timerId = undefined;
+ }
+ }, 13);
+ }
+ },
+
+ // Simple 'show' function
+ show: function(){
+ // Remember where we started, so that we can go back to it later
+ this.options.orig[this.prop] = jQuery.attr( this.elem.style, this.prop );
+ this.options.show = true;
+
+ // Begin the animation
+ // Make sure that we start at a small width/height to avoid any
+ // flash of content
+ this.custom(this.prop == "width" || this.prop == "height" ? 1 : 0, this.cur());
+
+ // Start by showing the element
+ jQuery(this.elem).show();
+ },
+
+ // Simple 'hide' function
+ hide: function(){
+ // Remember where we started, so that we can go back to it later
+ this.options.orig[this.prop] = jQuery.attr( this.elem.style, this.prop );
+ this.options.hide = true;
+
+ // Begin the animation
+ this.custom(this.cur(), 0);
+ },
+
+ // Each step of an animation
+ step: function(gotoEnd){
+ var t = now();
+
+ if ( gotoEnd || t >= this.options.duration + this.startTime ) {
+ this.now = this.end;
+ this.pos = this.state = 1;
+ this.update();
+
+ this.options.curAnim[ this.prop ] = true;
+
+ var done = true;
+ for ( var i in this.options.curAnim )
+ if ( this.options.curAnim[i] !== true )
+ done = false;
+
+ if ( done ) {
+ if ( this.options.display != null ) {
+ // Reset the overflow
+ this.elem.style.overflow = this.options.overflow;
+
+ // Reset the display
+ this.elem.style.display = this.options.display;
+ if ( jQuery.css(this.elem, "display") == "none" )
+ this.elem.style.display = "block";
+ }
+
+ // Hide the element if the "hide" operation was done
+ if ( this.options.hide )
+ jQuery(this.elem).hide();
+
+ // Reset the properties, if the item has been hidden or shown
+ if ( this.options.hide || this.options.show )
+ for ( var p in this.options.curAnim )
+ jQuery.attr(this.elem.style, p, this.options.orig[p]);
+
+ // Execute the complete function
+ this.options.complete.call( this.elem );
+ }
+
+ return false;
+ } else {
+ var n = t - this.startTime;
+ this.state = n / this.options.duration;
+
+ // Perform the easing function, defaults to swing
+ this.pos = jQuery.easing[this.options.easing || (jQuery.easing.swing ? "swing" : "linear")](this.state, n, 0, 1, this.options.duration);
+ this.now = this.start + ((this.end - this.start) * this.pos);
+
+ // Perform the next step of the animation
+ this.update();
+ }
+
+ return true;
+ }
+
+};
+
+jQuery.extend( jQuery.fx, {
+ speeds:{
+ slow: 600,
+ fast: 200,
+ // Default speed
+ _default: 400
+ },
+ step: {
+
+ opacity: function(fx){
+ jQuery.attr(fx.elem.style, "opacity", fx.now);
+ },
+
+ _default: function(fx){
+ if ( fx.elem.style && fx.elem.style[ fx.prop ] != null )
+ fx.elem.style[ fx.prop ] = fx.now + fx.unit;
+ else
+ fx.elem[ fx.prop ] = fx.now;
+ }
+ }
+});
+if ( document.documentElement["getBoundingClientRect"] )
+ jQuery.fn.offset = function() {
+ if ( !this[0] ) return { top: 0, left: 0 };
+ if ( this[0] === this[0].ownerDocument.body ) return jQuery.offset.bodyOffset( this[0] );
+ var box = this[0].getBoundingClientRect(), doc = this[0].ownerDocument, body = doc.body, docElem = doc.documentElement,
+ clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0,
+ top = box.top + (self.pageYOffset || jQuery.boxModel && docElem.scrollTop || body.scrollTop ) - clientTop,
+ left = box.left + (self.pageXOffset || jQuery.boxModel && docElem.scrollLeft || body.scrollLeft) - clientLeft;
+ return { top: top, left: left };
+ };
+else
+ jQuery.fn.offset = function() {
+ if ( !this[0] ) return { top: 0, left: 0 };
+ if ( this[0] === this[0].ownerDocument.body ) return jQuery.offset.bodyOffset( this[0] );
+ jQuery.offset.initialized || jQuery.offset.initialize();
+
+ var elem = this[0], offsetParent = elem.offsetParent, prevOffsetParent = elem,
+ doc = elem.ownerDocument, computedStyle, docElem = doc.documentElement,
+ body = doc.body, defaultView = doc.defaultView,
+ prevComputedStyle = defaultView.getComputedStyle(elem, null),
+ top = elem.offsetTop, left = elem.offsetLeft;
+
+ while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) {
+ computedStyle = defaultView.getComputedStyle(elem, null);
+ top -= elem.scrollTop, left -= elem.scrollLeft;
+ if ( elem === offsetParent ) {
+ top += elem.offsetTop, left += elem.offsetLeft;
+ if ( jQuery.offset.doesNotAddBorder && !(jQuery.offset.doesAddBorderForTableAndCells && /^t(able|d|h)$/i.test(elem.tagName)) )
+ top += parseInt( computedStyle.borderTopWidth, 10) || 0,
+ left += parseInt( computedStyle.borderLeftWidth, 10) || 0;
+ prevOffsetParent = offsetParent, offsetParent = elem.offsetParent;
+ }
+ if ( jQuery.offset.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" )
+ top += parseInt( computedStyle.borderTopWidth, 10) || 0,
+ left += parseInt( computedStyle.borderLeftWidth, 10) || 0;
+ prevComputedStyle = computedStyle;
+ }
+
+ if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" )
+ top += body.offsetTop,
+ left += body.offsetLeft;
+
+ if ( prevComputedStyle.position === "fixed" )
+ top += Math.max(docElem.scrollTop, body.scrollTop),
+ left += Math.max(docElem.scrollLeft, body.scrollLeft);
+
+ return { top: top, left: left };
+ };
+
+jQuery.offset = {
+ initialize: function() {
+ if ( this.initialized ) return;
+ var body = document.body, container = document.createElement('div'), innerDiv, checkDiv, table, td, rules, prop, bodyMarginTop = body.style.marginTop,
+ html = '<div style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;"><div></div></div><table style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;" cellpadding="0" cellspacing="0"><tr><td></td></tr></table>';
+
+ rules = { position: 'absolute', top: 0, left: 0, margin: 0, border: 0, width: '1px', height: '1px', visibility: 'hidden' };
+ for ( prop in rules ) container.style[prop] = rules[prop];
+
+ container.innerHTML = html;
+ body.insertBefore(container, body.firstChild);
+ innerDiv = container.firstChild, checkDiv = innerDiv.firstChild, td = innerDiv.nextSibling.firstChild.firstChild;
+
+ this.doesNotAddBorder = (checkDiv.offsetTop !== 5);
+ this.doesAddBorderForTableAndCells = (td.offsetTop === 5);
+
+ innerDiv.style.overflow = 'hidden', innerDiv.style.position = 'relative';
+ this.subtractsBorderForOverflowNotVisible = (checkDiv.offsetTop === -5);
+
+ body.style.marginTop = '1px';
+ this.doesNotIncludeMarginInBodyOffset = (body.offsetTop === 0);
+ body.style.marginTop = bodyMarginTop;
+
+ body.removeChild(container);
+ this.initialized = true;
+ },
+
+ bodyOffset: function(body) {
+ jQuery.offset.initialized || jQuery.offset.initialize();
+ var top = body.offsetTop, left = body.offsetLeft;
+ if ( jQuery.offset.doesNotIncludeMarginInBodyOffset )
+ top += parseInt( jQuery.curCSS(body, 'marginTop', true), 10 ) || 0,
+ left += parseInt( jQuery.curCSS(body, 'marginLeft', true), 10 ) || 0;
+ return { top: top, left: left };
+ }
+};
+
+
+jQuery.fn.extend({
+ position: function() {
+ var left = 0, top = 0, results;
+
+ if ( this[0] ) {
+ // Get *real* offsetParent
+ var offsetParent = this.offsetParent(),
+
+ // Get correct offsets
+ offset = this.offset(),
+ parentOffset = /^body|html$/i.test(offsetParent[0].tagName) ? { top: 0, left: 0 } : offsetParent.offset();
+
+ // Subtract element margins
+ // note: when an element has margin: auto the offsetLeft and marginLeft
+ // are the same in Safari causing offset.left to incorrectly be 0
+ offset.top -= num( this, 'marginTop' );
+ offset.left -= num( this, 'marginLeft' );
+
+ // Add offsetParent borders
+ parentOffset.top += num( offsetParent, 'borderTopWidth' );
+ parentOffset.left += num( offsetParent, 'borderLeftWidth' );
+
+ // Subtract the two offsets
+ results = {
+ top: offset.top - parentOffset.top,
+ left: offset.left - parentOffset.left
+ };
+ }
+
+ return results;
+ },
+
+ offsetParent: function() {
+ var offsetParent = this[0].offsetParent || document.body;
+ while ( offsetParent && (!/^body|html$/i.test(offsetParent.tagName) && jQuery.css(offsetParent, 'position') == 'static') )
+ offsetParent = offsetParent.offsetParent;
+ return jQuery(offsetParent);
+ }
+});
+
+
+// Create scrollLeft and scrollTop methods
+jQuery.each( ['Left', 'Top'], function(i, name) {
+ var method = 'scroll' + name;
+
+ jQuery.fn[ method ] = function(val) {
+ if (!this[0]) return null;
+
+ return val !== undefined ?
+
+ // Set the scroll offset
+ this.each(function() {
+ this == window || this == document ?
+ window.scrollTo(
+ !i ? val : jQuery(window).scrollLeft(),
+ i ? val : jQuery(window).scrollTop()
+ ) :
+ this[ method ] = val;
+ }) :
+
+ // Return the scroll offset
+ this[0] == window || this[0] == document ?
+ self[ i ? 'pageYOffset' : 'pageXOffset' ] ||
+ jQuery.boxModel && document.documentElement[ method ] ||
+ document.body[ method ] :
+ this[0][ method ];
+ };
+});
+// Create innerHeight, innerWidth, outerHeight and outerWidth methods
+jQuery.each([ "Height", "Width" ], function(i, name){
+
+ var tl = i ? "Left" : "Top", // top or left
+ br = i ? "Right" : "Bottom", // bottom or right
+ lower = name.toLowerCase();
+
+ // innerHeight and innerWidth
+ jQuery.fn["inner" + name] = function(){
+ return this[0] ?
+ jQuery.css( this[0], lower, false, "padding" ) :
+ null;
+ };
+
+ // outerHeight and outerWidth
+ jQuery.fn["outer" + name] = function(margin) {
+ return this[0] ?
+ jQuery.css( this[0], lower, false, margin ? "margin" : "border" ) :
+ null;
+ };
+
+ var type = name.toLowerCase();
+
+ jQuery.fn[ type ] = function( size ) {
+ // Get window width or height
+ return this[0] == window ?
+ // Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode
+ document.compatMode == "CSS1Compat" && document.documentElement[ "client" + name ] ||
+ document.body[ "client" + name ] :
+
+ // Get document width or height
+ this[0] == document ?
+ // Either scroll[Width/Height] or offset[Width/Height], whichever is greater
+ Math.max(
+ document.documentElement["client" + name],
+ document.body["scroll" + name], document.documentElement["scroll" + name],
+ document.body["offset" + name], document.documentElement["offset" + name]
+ ) :
+
+ // Get or set width or height on the element
+ size === undefined ?
+ // Get width or height on the element
+ (this.length ? jQuery.css( this[0], type ) : null) :
+
+ // Set the width or height on the element (default to pixels if value is unitless)
+ this.css( type, typeof size === "string" ? size : size + "px" );
+ };
+
+});
+})();
diff --git a/trunk/etherpad/src/static/js/json2.js b/trunk/etherpad/src/static/js/json2.js
new file mode 100644
index 0000000..32988c2
--- /dev/null
+++ b/trunk/etherpad/src/static/js/json2.js
@@ -0,0 +1,498 @@
+/**
+ * Copyright 2009 Google 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.
+ */
+
+/*
+ http://www.JSON.org/json2.js
+ 2008-09-01
+
+ Public Domain.
+
+ NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
+
+ See http://www.JSON.org/js.html
+
+ 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.
+
+ 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 object holding the key.
+
+ 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, 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.
+
+ 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.
+*/
+
+/*jslint evil: true */
+
+/*global JSON */
+
+/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", call,
+ charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, getUTCMinutes,
+ getUTCMonth, getUTCSeconds, hasOwnProperty, join, lastIndex, length,
+ parse, propertyIsEnumerable, 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) {
+ 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 this.getUTCFullYear() + '-' +
+ f(this.getUTCMonth() + 1) + '-' +
+ f(this.getUTCDate()) + 'T' +
+ f(this.getUTCHours()) + ':' +
+ f(this.getUTCMinutes()) + ':' +
+ f(this.getUTCSeconds()) + 'Z';
+ };
+
+ String.prototype.toJSON =
+ Number.prototype.toJSON =
+ Boolean.prototype.toJSON = function (key) {
+ return this.valueOf();
+ };
+ }
+
+ // APPJET: escape all characters except non-control 7-bit ASCII (changed cx and escapeable)
+ var cx = /[\u0000-\u001f\u007f-\uffff]/g,
+ escapeable = /[\\\"\u0000-\u001f\u007f-\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.
+
+ escapeable.lastIndex = 0;
+ return escapeable.test(string) ?
+ '"' + string.replace(escapeable, function (a) {
+ var c = meta[a];
+ if (typeof c === 'string') {
+ return c;
+ }
+ return '\\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 = [];
+
+// If the object has a dontEnum length property, we'll treat it as an array.
+
+ if (typeof value.length === 'number' &&
+ !value.propertyIsEnumerable('length')) {
+
+// The object 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.
+
+ 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 = window['ev'+'al']('(' + 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');
+ };
+ }
+})();
diff --git a/trunk/etherpad/src/static/js/lib/jquery.contextmenu.js b/trunk/etherpad/src/static/js/lib/jquery.contextmenu.js
new file mode 100644
index 0000000..42cc17e
--- /dev/null
+++ b/trunk/etherpad/src/static/js/lib/jquery.contextmenu.js
@@ -0,0 +1,284 @@
+/**
+ * Copyright (c)2005-2009 Matt Kruse (javascripttoolbox.com)
+ *
+ * Dual licensed under the MIT and GPL licenses.
+ * This basically means you can use this code however you want for
+ * free, but don't claim to have written it yourself!
+ * Donations always accepted: http://www.JavascriptToolbox.com/donate/
+ *
+ * Please do not link to the .js files on javascripttoolbox.com from
+ * your site. Copy the files locally to your server instead.
+ *
+ */
+/**
+ * jquery.contextmenu.js
+ * jQuery Plugin for Context Menus
+ * http://www.JavascriptToolbox.com/lib/contextmenu/
+ *
+ * Copyright (c) 2008 Matt Kruse (javascripttoolbox.com)
+ * Dual licensed under the MIT and GPL licenses.
+ *
+ * @version 1.0
+ * @history 1.0 2008-10-20 Initial Release
+ * @todo slideUp doesn't work in IE - because of iframe?
+ * @todo Hide all other menus when contextmenu is shown?
+ * @todo More themes
+ * @todo Nested context menus
+ */
+;(function($){
+ $.contextMenu = {
+ shadow:true,
+ shadowOffset:0,
+ shadowOffsetX:5,
+ shadowOffsetY:5,
+ shadowWidthAdjust:-3,
+ shadowHeightAdjust:-3,
+ shadowOpacity:.2,
+ shadowClass:'context-menu-shadow',
+ shadowColor:'black',
+
+ offsetX:0,
+ offsetY:0,
+ appendTo:'body',
+ direction:'down',
+ constrainToScreen:true,
+
+ showTransition:'show',
+ hideTransition:'hide',
+ showSpeed:'',
+ hideSpeed:'',
+ showCallback:null,
+ hideCallback:null,
+
+ className:'context-menu',
+ itemClassName:'context-menu-item',
+ itemHoverClassName:'context-menu-item-hover',
+ disabledItemClassName:'context-menu-item-disabled',
+ disabledItemHoverClassName:'context-menu-item-disabled-hover',
+ separatorClassName:'context-menu-separator',
+ innerDivClassName:'context-menu-item-inner',
+ themePrefix:'context-menu-theme-',
+ theme:'default',
+
+ separator:'context-menu-separator', // A specific key to identify a separator
+ target:null, // The target of the context click, to be populated when triggered
+ menu:null, // The jQuery object containing the HTML object that is the menu itself
+ shadowObj:null, // Shadow object
+ bgiframe:null, // The iframe object for IE6
+ shown:false, // Currently being shown?
+ useIframe:/*@cc_on @*//*@if (@_win32) true, @else @*/false,/*@end @*/ // This is a better check than looking at userAgent!
+
+ bindTarget: 'contextmenu',
+
+ // Create the menu instance
+ create: function(menu,opts) {
+ var cmenu = $.extend({},this,opts); // Clone all default properties to created object
+
+ // If a selector has been passed in, then use that as the menu
+ if (typeof menu=="string") {
+ cmenu.menu = $(menu);
+ }
+ // If a function has been passed in, call it each time the menu is shown to create the menu
+ else if (typeof menu=="function") {
+ cmenu.menuFunction = menu;
+ }
+ // Otherwise parse the Array passed in
+ else {
+ cmenu.menu = cmenu.createMenu(menu,cmenu);
+ }
+ if (cmenu.menu) {
+ cmenu.menu.css({display:'none'});
+ $(cmenu.appendTo).append(cmenu.menu);
+ }
+
+ // Create the shadow object if shadow is enabled
+ if (cmenu.shadow) {
+ cmenu.createShadow(cmenu); // Extracted to method for extensibility
+ if (cmenu.shadowOffset) { cmenu.shadowOffsetX = cmenu.shadowOffsetY = cmenu.shadowOffset; }
+ }
+
+ // when to hide the menu:
+ // click anywhere in the body:
+ $('body').bind('click',function(){cmenu.hide();});
+ // right-click anywhere in the body:
+ $('body').bind('contextmenu',function(){cmenu.hide();});
+ // escape key:
+ $(document).keyup(function(e) { if (e.keyCode == 27) { cmenu.hide(); } });
+
+ return cmenu;
+ },
+
+ // Create an iframe object to go behind the menu
+ createIframe: function() {
+ return $('<iframe frameborder="0" tabindex="-1" src="javascript:false" style="display:block;position:absolute;z-index:-1;filter:Alpha(Opacity=0);"/>');
+ },
+
+ // Accept an Array representing a menu structure and turn it into HTML
+ createMenu: function(menu,cmenu) {
+ var className = cmenu.className;
+ $.each(cmenu.theme.split(","),function(i,n){className+=' '+cmenu.themePrefix+n});
+ var $t = $('<table cellspacing=0 cellpadding=0></table>').click(function(){cmenu.hide(); return false;}); // We wrap a table around it so width can be flexible
+ var $tr = $('<tr></tr>');
+ var $td = $('<td></td>');
+ var $div = $('<div class="'+className+'"></div>');
+
+ // Each menu item is specified as either:
+ // title:function
+ // or title: { property:value ... }
+ for (var i=0; i<menu.length; i++) {
+ var m = menu[i];
+ if (m==$.contextMenu.separator) {
+ $div.append(cmenu.createSeparator());
+ }
+ else {
+ for (var opt in menu[i]) {
+ $div.append(cmenu.createMenuItem(opt,menu[i][opt])); // Extracted to method for extensibility
+ }
+ }
+ }
+ if ( cmenu.useIframe ) {
+ $td.append(cmenu.createIframe());
+ }
+ $t.append($tr.append($td.append($div)))
+ return $t;
+ },
+
+ // Create an individual menu item
+ createMenuItem: function(label,obj) {
+ var cmenu = this;
+ if (typeof obj=="function") { obj={onclick:obj}; } // If passed a simple function, turn it into a property of an object
+ // Default properties, extended in case properties are passed
+ var o = $.extend({
+ onclick:function() { },
+ className:'',
+ hoverClassName:cmenu.itemHoverClassName,
+ icon:'',
+ disabled:false,
+ title:'',
+ hoverItem:cmenu.hoverItem,
+ hoverItemOut:cmenu.hoverItemOut
+ },obj);
+ // If an icon is specified, hard-code the background-image style. Themes that don't show images should take this into account in their CSS
+ var iconStyle = (o.icon)?'background-image:url('+o.icon+');':'';
+ var $div = $('<div class="'+cmenu.itemClassName+' '+o.className+((o.disabled)?' '+cmenu.disabledItemClassName:'')+'" title="'+o.title+'"></div>')
+ // If the item is disabled, don't do anything when it is clicked
+ .click(function(e){if(cmenu.isItemDisabled(this)){return false;}else{return o.onclick.call(cmenu.target,this,cmenu,e)}})
+ // Change the class of the item when hovered over
+ .hover( function(){ o.hoverItem.call(this,(cmenu.isItemDisabled(this))?cmenu.disabledItemHoverClassName:o.hoverClassName); }
+ ,function(){ o.hoverItemOut.call(this,(cmenu.isItemDisabled(this))?cmenu.disabledItemHoverClassName:o.hoverClassName); }
+ );
+ var $idiv = $('<div class="'+cmenu.innerDivClassName+'" style="'+iconStyle+'">'+label+'</div>');
+ $div.append($idiv);
+ return $div;
+ },
+
+ // Create a separator row
+ createSeparator: function() {
+ return $('<div class="'+this.separatorClassName+'"></div>');
+ },
+
+ // Determine if an individual item is currently disabled. This is called each time the item is hovered or clicked because the disabled status may change at any time
+ isItemDisabled: function(item) { return $(item).is('.'+this.disabledItemClassName); },
+
+ // Functions to fire on hover. Extracted to methods for extensibility
+ hoverItem: function(c) { $(this).addClass(c); },
+ hoverItemOut: function(c) { $(this).removeClass(c); },
+
+ // Create the shadow object
+ createShadow: function(cmenu) {
+ cmenu.shadowObj = $('<div class="'+cmenu.shadowClass+'"></div>').css( {display:'none',position:"absolute", zIndex:9998, opacity:cmenu.shadowOpacity, backgroundColor:cmenu.shadowColor } );
+ $(cmenu.appendTo).append(cmenu.shadowObj);
+ },
+
+ // Display the shadow object, given the position of the menu itself
+ showShadow: function(x,y,e) {
+ var cmenu = this;
+ if (cmenu.shadow) {
+ cmenu.shadowObj.css( {
+ width:(cmenu.menu.width()+cmenu.shadowWidthAdjust)+"px",
+ height:(cmenu.menu.height()+cmenu.shadowHeightAdjust)+"px",
+ top:(y+cmenu.shadowOffsetY)+"px",
+ left:(x+cmenu.shadowOffsetX)+"px"
+ }).addClass(cmenu.shadowClass)[cmenu.showTransition](cmenu.showSpeed);
+ }
+ },
+
+ // A hook to call before the menu is shown, in case special processing needs to be done.
+ // Return false to cancel the default show operation
+ beforeShow: function() { return true; },
+
+ // Show the context menu
+ show: function(t,e) {
+ var cmenu=this, x=e.pageX, y=e.pageY;
+ cmenu.target = t; // Preserve the object that triggered this context menu so menu item click methods can see it
+ if (cmenu.beforeShow()!==false) {
+ // If the menu content is a function, call it to populate the menu each time it is displayed
+ if (cmenu.menuFunction) {
+ if (cmenu.menu) { $(cmenu.menu).remove(); }
+ cmenu.menu = cmenu.createMenu(cmenu.menuFunction(cmenu,t),cmenu);
+ cmenu.menu.css({display:'none'});
+ $(cmenu.appendTo).append(cmenu.menu);
+ }
+ var $c = cmenu.menu;
+ x+=cmenu.offsetX; y+=cmenu.offsetY;
+ var pos = cmenu.getPosition(x,y,cmenu,e); // Extracted to method for extensibility
+ cmenu.showShadow(pos.x,pos.y,e);
+ // Resize the iframe if needed
+ if (cmenu.useIframe) {
+ $c.find('iframe').css({width:$c.width()+cmenu.shadowOffsetX+cmenu.shadowWidthAdjust,height:$c.height()+cmenu.shadowOffsetY+cmenu.shadowHeightAdjust});
+ }
+ $c.css( {top:pos.y+"px", left:pos.x+"px", position:"absolute",zIndex:9999} )[cmenu.showTransition](cmenu.showSpeed,((cmenu.showCallback)?function(){cmenu.showCallback.call(cmenu);}:null));
+ cmenu.shown=true;
+ $(document).one('click',null,function(){cmenu.hide()}); // Handle a single click to the document to hide the menu
+ }
+ },
+
+ // Find the position where the menu should appear, given an x,y of the click event
+ getPosition: function(clickX,clickY,cmenu,e) {
+ var x = clickX+cmenu.offsetX;
+ var y = clickY+cmenu.offsetY
+ var h = $(cmenu.menu).height();
+ var w = $(cmenu.menu).width();
+ var dir = cmenu.direction;
+ if (cmenu.constrainToScreen) {
+ var $w = $(window);
+ var wh = $w.height();
+ var ww = $w.width();
+ if (dir=="down" && (y+h-$w.scrollTop() > wh)) { dir = "up"; }
+ var maxRight = x+w-$w.scrollLeft();
+ if (maxRight > ww) { x -= (maxRight-ww); }
+ }
+ if (dir=="up") { y -= h; }
+ return {'x':x,'y':y};
+ },
+
+ // Hide the menu, of course
+ hide: function() {
+ var cmenu=this;
+ if (cmenu.shown) {
+ if (cmenu.iframe) {
+ $(cmenu.iframe).hide();
+ }
+ if (cmenu.menu) {
+ cmenu.menu[cmenu.hideTransition](cmenu.hideSpeed,null);
+ }
+ if (cmenu.shadow) {
+ cmenu.shadowObj[cmenu.hideTransition](cmenu.hideSpeed);
+ }
+ if (cmenu.hideCallback) {
+ cmenu.hideCallback.call(cmenu);
+ }
+ }
+ cmenu.shown = false;
+ }
+ };
+
+ // This actually adds the .contextMenu() function to the jQuery namespace
+ $.fn.contextMenu = function(menu,options) {
+ var cmenu = $.contextMenu.create(menu,options);
+ return this.each(function(){
+ $(this).bind(cmenu.bindTarget,function(e){cmenu.show(this,e);return false;});
+ });
+ };
+})(jQuery);
+
diff --git a/trunk/etherpad/src/static/js/linestylefilter_client.js b/trunk/etherpad/src/static/js/linestylefilter_client.js
new file mode 100644
index 0000000..ca60296
--- /dev/null
+++ b/trunk/etherpad/src/static/js/linestylefilter_client.js
@@ -0,0 +1,252 @@
+// DO NOT EDIT THIS FILE, edit infrastructure/ace/www/linestylefilter.js
+
+/**
+ * Copyright 2009 Google 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.
+ */
+
+// requires: easysync2.Changeset
+
+var linestylefilter = {};
+
+linestylefilter.ATTRIB_CLASSES = {
+ 'bold':'tag:b',
+ 'italic':'tag:i',
+ 'underline':'tag:u',
+ 'strikethrough':'tag:s',
+ 'h1':'tag:h1',
+ 'h2':'tag:h2',
+ 'h3':'tag:h3',
+ 'h4':'tag:h4',
+ 'h5':'tag:h5',
+ 'h6':'tag:h6'
+};
+
+linestylefilter.getAuthorClassName = function(author) {
+ return "author-"+author.replace(/[^a-y0-9]/g, function(c) {
+ if (c == ".") return "-";
+ return 'z'+c.charCodeAt(0)+'z';
+ });
+};
+
+// lineLength is without newline; aline includes newline,
+// but may be falsy if lineLength == 0
+linestylefilter.getLineStyleFilter = function(lineLength, aline,
+ textAndClassFunc, apool) {
+
+ if (lineLength == 0) return textAndClassFunc;
+
+ var nextAfterAuthorColors = textAndClassFunc;
+
+ var authorColorFunc = (function() {
+ var lineEnd = lineLength;
+ var curIndex = 0;
+ var extraClasses;
+ var leftInAuthor;
+
+ function attribsToClasses(attribs) {
+ var classes = '';
+ Changeset.eachAttribNumber(attribs, function(n) {
+ var key = apool.getAttribKey(n);
+ if (key) {
+ var value = apool.getAttribValue(n);
+ if (value) {
+ if (key == 'author') {
+ classes += ' '+linestylefilter.getAuthorClassName(value);
+ }
+ else if (key == 'list') {
+ classes += ' list:'+value;
+ }
+ else if (linestylefilter.ATTRIB_CLASSES[key]) {
+ classes += ' '+linestylefilter.ATTRIB_CLASSES[key];
+ }
+ }
+ }
+ });
+ return classes.substring(1);
+ }
+
+ var attributionIter = Changeset.opIterator(aline);
+ var nextOp, nextOpClasses;
+ function goNextOp() {
+ nextOp = attributionIter.next();
+ nextOpClasses = (nextOp.opcode && attribsToClasses(nextOp.attribs));
+ }
+ goNextOp();
+ function nextClasses() {
+ if (curIndex < lineEnd) {
+ extraClasses = nextOpClasses;
+ leftInAuthor = nextOp.chars;
+ goNextOp();
+ while (nextOp.opcode && nextOpClasses == extraClasses) {
+ leftInAuthor += nextOp.chars;
+ goNextOp();
+ }
+ }
+ }
+ nextClasses();
+
+ return function(txt, cls) {
+ while (txt.length > 0) {
+ if (leftInAuthor <= 0) {
+ // prevent infinite loop if something funny's going on
+ return nextAfterAuthorColors(txt, cls);
+ }
+ var spanSize = txt.length;
+ if (spanSize > leftInAuthor) {
+ spanSize = leftInAuthor;
+ }
+ var curTxt = txt.substring(0, spanSize);
+ txt = txt.substring(spanSize);
+ nextAfterAuthorColors(curTxt, (cls&&cls+" ")+extraClasses);
+ curIndex += spanSize;
+ leftInAuthor -= spanSize;
+ if (leftInAuthor == 0) {
+ nextClasses();
+ }
+ }
+ };
+ })();
+ return authorColorFunc;
+};
+
+linestylefilter.getAtSignSplitterFilter = function(lineText,
+ textAndClassFunc) {
+ var at = /@/g;
+ at.lastIndex = 0;
+ var splitPoints = null;
+ var execResult;
+ while ((execResult = at.exec(lineText))) {
+ if (! splitPoints) {
+ splitPoints = [];
+ }
+ splitPoints.push(execResult.index);
+ }
+
+ if (! splitPoints) return textAndClassFunc;
+
+ return linestylefilter.textAndClassFuncSplitter(textAndClassFunc,
+ splitPoints);
+};
+
+linestylefilter.REGEX_WORDCHAR = /[\u0030-\u0039\u0041-\u005A\u0061-\u007A\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\u1FFF\u3040-\u9FFF\uF900-\uFDFF\uFE70-\uFEFE\uFF10-\uFF19\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFDC]/;
+linestylefilter.REGEX_URLCHAR = new RegExp('('+/[-:@a-zA-Z0-9_.,~%+\/\\?=&#;()$]/.source+'|'+linestylefilter.REGEX_WORDCHAR.source+')');
+linestylefilter.REGEX_URL = new RegExp(/(?:(?:https?|s?ftp|ftps|file|smb|afp|nfs|(x-)?man|gopher|txmt):\/\/|mailto:)/.source+linestylefilter.REGEX_URLCHAR.source+'*(?![:.,;])'+linestylefilter.REGEX_URLCHAR.source, 'g');
+
+linestylefilter.getURLFilter = function(lineText, textAndClassFunc) {
+ linestylefilter.REGEX_URL.lastIndex = 0;
+ var urls = null;
+ var splitPoints = null;
+ var execResult;
+ while ((execResult = linestylefilter.REGEX_URL.exec(lineText))) {
+ if (! urls) {
+ urls = [];
+ splitPoints = [];
+ }
+ var startIndex = execResult.index;
+ var url = execResult[0];
+ urls.push([startIndex, url]);
+ splitPoints.push(startIndex, startIndex + url.length);
+ }
+
+ if (! urls) return textAndClassFunc;
+
+ function urlForIndex(idx) {
+ for(var k=0; k<urls.length; k++) {
+ var u = urls[k];
+ if (idx >= u[0] && idx < u[0]+u[1].length) {
+ return u[1];
+ }
+ }
+ return false;
+ }
+
+ var handleUrlsAfterSplit = (function() {
+ var curIndex = 0;
+ return function(txt, cls) {
+ var txtlen = txt.length;
+ var newCls = cls;
+ var url = urlForIndex(curIndex);
+ if (url) {
+ newCls += " url:"+url;
+ }
+ textAndClassFunc(txt, newCls);
+ curIndex += txtlen;
+ };
+ })();
+
+ return linestylefilter.textAndClassFuncSplitter(handleUrlsAfterSplit,
+ splitPoints);
+};
+
+linestylefilter.textAndClassFuncSplitter = function(func, splitPointsOpt) {
+ var nextPointIndex = 0;
+ var idx = 0;
+
+ // don't split at 0
+ while (splitPointsOpt &&
+ nextPointIndex < splitPointsOpt.length &&
+ splitPointsOpt[nextPointIndex] == 0) {
+ nextPointIndex++;
+ }
+
+ function spanHandler(txt, cls) {
+ if ((! splitPointsOpt) || nextPointIndex >= splitPointsOpt.length) {
+ func(txt, cls);
+ idx += txt.length;
+ }
+ else {
+ var splitPoints = splitPointsOpt;
+ var pointLocInSpan = splitPoints[nextPointIndex] - idx;
+ var txtlen = txt.length;
+ if (pointLocInSpan >= txtlen) {
+ func(txt, cls);
+ idx += txt.length;
+ if (pointLocInSpan == txtlen) {
+ nextPointIndex++;
+ }
+ }
+ else {
+ if (pointLocInSpan > 0) {
+ func(txt.substring(0, pointLocInSpan), cls);
+ idx += pointLocInSpan;
+ }
+ nextPointIndex++;
+ // recurse
+ spanHandler(txt.substring(pointLocInSpan), cls);
+ }
+ }
+ }
+ return spanHandler;
+};
+
+// domLineObj is like that returned by domline.createDomLine
+linestylefilter.populateDomLine = function(textLine, aline, apool,
+ domLineObj) {
+ // remove final newline from text if any
+ var text = textLine;
+ if (text.slice(-1) == '\n') {
+ text = text.substring(0, text.length-1);
+ }
+
+ function textAndClassFunc(tokenText, tokenClass) {
+ domLineObj.appendSpan(tokenText, tokenClass);
+ }
+
+ var func = textAndClassFunc;
+ func = linestylefilter.getURLFilter(text, func);
+ func = linestylefilter.getLineStyleFilter(text.length, aline,
+ func, apool);
+ func(text, '');
+};
diff --git a/trunk/etherpad/src/static/js/pad.js.old b/trunk/etherpad/src/static/js/pad.js.old
new file mode 100644
index 0000000..6191b31
--- /dev/null
+++ b/trunk/etherpad/src/static/js/pad.js.old
@@ -0,0 +1,1984 @@
+//----------------------------------------------------------------
+// initialization
+//----------------------------------------------------------------
+
+$(document).ready(function() {
+ if (!pad.initialized) {
+ pad.init();
+ }
+});
+
+$(window).unload(function() {
+ if (pad.ace) {
+ pad.ace.destroy();
+ }
+});
+
+pad = {
+ initTime: 0,
+ initialized: false,
+ myUserInfo: null, /* userInfo for this client's user */
+ debugEnabled: false,
+ userBoxIds: {},
+ lastBoxId: 0,
+ connectionState: 'DISCONNECTED',
+ notifyQueue: [],
+ lastNotify: 0,
+ persistLastNotify: false,
+ collabClient: null,
+ users: {},
+ ace: null,
+ padId: null,
+ wrapLines: true,
+ initialCookiePrefs: {},
+ revisionList: [],
+ clientTimeOffset: (+new Date()) - clientVars.serverTimestamp,
+ REVISIONCOUNT_STEP: 5,
+ shownRevisions: 5,
+ sideBarVisible: true,
+ sideBoxVisibility: {},
+ diagnosticInfo: {},
+ experiencingConnectionTrouble: false,
+ padHasFocus: true,
+ isFreshCookie: false,
+ viewZoom: 100,
+ title: null,
+ password: null,
+ isEditingTitle: false
+};
+
+pad.initCookie = function() {
+ pad.initialCookiePrefs = pad.getInitialCookiePrefs();
+};
+
+pad.initPrefsPane = function() {
+ // Setup checkbox prefs.
+ function scbp(n, v) {
+ var t = pad.initialCookiePrefs[v];
+ pad.markPref(n, t);
+ }
+ scbp('wrap', 'wrapLines');
+ scbp('showcolors', 'showAuthorColors');
+ scbp('linenums', 'showLineNumbers');
+ scbp('fullwidth', 'fullWidth');
+
+ pad.setFullWidth(pad.initialCookiePrefs.fullWidth);
+
+ // events
+ function prefclick(elt) {
+ var d = $(elt);
+ var name = d.attr('id').substr("checkpref".length);
+ var t = !pad.isPrefMarked(name);
+ pad.markPref(name, t);
+ pad.updatePref(name, t);
+ pad.saveCookie();
+ }
+ $('div.prefcheckbox').mousedown(function(e) {
+ prefclick(this);
+ e.preventDefault();
+ return false;
+ });
+ $('div.prefcheckbox').mouseup(pad.handlerPreventDefault);
+ $('div.prefcheckbox').click(pad.handlerPreventDefault);
+};
+
+pad.initAce = function(donefunc) {
+
+ // ACE functions.
+ function editorReady() {
+ $('#editorcell').removeClass('editorcell_loading').addClass('editorcell_loaded');
+ $('#sidebar').removeClass('sidebar_loading');
+ $('#loadingbox').remove();
+ $(window).bind("resize", pad.resizePage);
+ setTimeout(function() { ace.focus(); pad.resizePage(); }, 0);
+ donefunc();
+ }
+
+ // Setup ace.
+ pad.dmesg("pad.init(): loading Ace2Editor");
+ var ace = new Ace2Editor();
+ ace.init("editorcontainer", "", editorReady);
+ ace.setProperty("wraps", pad.initialCookiePrefs.wrapLines);
+ ace.setProperty("showsAuthorColors", pad.initialCookiePrefs.showAuthorColors);
+ ace.setProperty("lineNumbers", pad.initialCookiePrefs.showLineNumbers);
+ if (pad.initialCookiePrefs.highlightJs === true) {
+ ace.setProperty("lexer", "js");
+ pad.markPref('jshighlight', true);
+ } else {
+ ace.setProperty("lexer", "txt");
+ pad.markPref('jshighlight', false);
+ }
+ // star window title on dirty
+ ace.setNotifyDirty(function() {
+ if (!pad.padHasFocus) {
+ var currentTitle = $(document).attr('title');
+ if (currentTitle.charAt(0) != '*') {
+ $(document).attr('title', '* '+currentTitle);
+ }
+ }
+ });
+ if (pad.debugEnabled) {
+ ace.setProperty("dmesg", pad.dmesg);
+ }
+
+ pad.ace = ace;
+};
+
+pad.initMyUserInfo = function() {
+ // assigned colorId
+ var colorId = clientVars.assignedColorId;
+ if (!clientVars.colorPalette[colorId]) {
+ colorId = Math.floor((Math.random() * clientVars.colorPalette.length));
+ }
+
+ // Populate user info.
+ pad.dmesg("clientVars: "+JSON.stringify(clientVars));
+ pad.myUserInfo = {
+ userId: (pad.initialCookiePrefs.userId || pad.uniqueId()),
+ name: pad.getDefaultUsername(),
+ ip: clientVars.clientIp,
+ colorId: colorId,
+ userAgent: pad.uaDisplay(clientVars.userAgent)
+ };
+
+ pad.userJoin(pad.myUserInfo);
+ pad.dmesg("myUserInfo: "+JSON.stringify(pad.myUserInfo));
+};
+
+pad.initSideBoxes = function() {
+ // expand side-bars if cookie indicates
+ for (paneName in pad.initialCookiePrefs.sideBoxVisibility) {
+ if (pad.initialCookiePrefs.sideBoxVisibility[paneName]) {
+ pad.showSideBox(paneName);
+ }
+ }
+};
+
+pad.initCollab = function() {
+ var collabOptions = {
+ colorPalette: clientVars.colorPalette
+ };
+ pad.collabClient = getCollabClient(pad.ace,
+ clientVars.collab_client_vars,
+ pad.myUserInfo,
+ collabOptions);
+
+ pad.collabClient.setOnClientMessage(pad.onClientMessage);
+ pad.collabClient.setOnServerMessage(pad.onServerMessage);
+ pad.collabClient.setOnUserJoin(pad.userJoin);
+ pad.collabClient.setOnUserLeave(pad.userLeave);
+ pad.collabClient.setOnUpdateUserInfo(pad.updateUserInfo);
+ pad.collabClient.setOnChannelStateChange(pad.channelStateChange);
+ pad.collabClient.setOnConnectionTrouble(pad.onConnectionTrouble);
+};
+
+pad.initRevisions = function() {
+ // Render revision list.
+ pad.revisionList = clientVars.initialRevisionList;
+ pad.renderRevisionList();
+
+ // Events
+ $('#savenow').click(function(e) {
+ pad.saveRevision();
+ e.preventDefault();
+ return false;
+ });
+ setInterval(function() {
+ pad.renderRevisionList(true);
+ }, (2*60*1000)); // to update times correctly
+};
+
+pad.initEvents = function() {
+ // shareurl click
+ $("#headurl").click(function() { selectElementContents($("#shareurl").get(0)); });
+
+ // feedback form
+ $('#feedbacksubmit').click(function(e) {
+ pad.submitfeedback();
+ e.preventDefault();
+ return false;
+ });
+
+ // sidepane expand/collapse events
+ $('#sidebar div.sideheadwrap').mousedown(function(e) {
+ var paneName = $(this).attr('id').substr("head".length);
+ if (pad.isSideBoxVisible(paneName)) {
+ pad.hideSideBox(paneName);
+ } else {
+ pad.showSideBox(paneName);
+ }
+ e.preventDefault();
+ return false;
+ });
+ $('#sidebar div.sideheadwrap').mouseup(pad.handlerPreventDefault);
+ $('#sidebar div.sideheadwrap').click(pad.handlerPreventDefault);
+
+ // ---- share link ----
+ $('#invitemore').show();
+ $('#invite_email_submit').click(pad.invitemoreSendEmail);
+ pad.setOnEnterKey('#invite_email, #invite_email_submit', pad.invitemoreSendEmail);
+
+ // ---- force reconnect ----
+ $('button.forcereconnect').click(function() {
+ pad.forceReconnect();
+ });
+};
+
+pad.initFocusTracker = function() {
+ var myFrames = [];
+ function walkframes(frame, depth) {
+ try {
+ if (depth > 4) { return; }
+ myFrames.push(frame);
+ $(frame).contents().find('iframe').each(function() {
+ walkframes(this, depth+1);
+ });
+ } catch (ex) {/*swallow*/}
+ }
+ myFrames.push($(window));
+ $('iframe').each(function() { walkframes(this, 1); });
+
+ function onFocus() { pad.padHasFocus = true; }
+ function onBlur() { pad.padHasFocus = false; }
+
+ for (var i = 0; i < myFrames.length; i++) {
+ var f = myFrames[i];
+ $(f).focus(onFocus).blur(onBlur);
+ /*
+ if (f.contentDocument) {
+ $(f.contentDocument).click(onFocus);
+ }*/
+ }
+ /* $(document).click(onFocus); */
+
+ // Reclaim the title periodically
+ var focusedLastCheck = true;
+ setInterval(function() {
+ if (!focusedLastCheck && pad.padHasFocus) {
+ $(document).attr('title', 'EtherPad: '+pad.padId);
+ }
+ focusedLastCheck = pad.padHasFocus;
+ }, 1000);
+};
+
+pad.initWelcomeMessage = function() {
+ if (pad.isFreshCookie && (clientVars.numConnectedUsers == 0)) {
+ pad.invitemoreShow();
+ }
+};
+
+pad.initResizeTimer = function() {
+ pad.resizePage(true);
+ setInterval(function() { pad.resizePage(true); }, 2000);
+}
+
+pad.initModalDialogs = function() {
+ pad.swallowAllMouseEvents($("#dialogtopbar"));
+}
+
+pad.initImportExport = function() {
+ // pad.addExportFrames();
+ pad.addImportFrames();
+ $('#importfileinput').change(pad.fileInputUpdated);
+ $('#importform').submit(pad.fileInputSubmit);
+ $('.disabledexport').click(pad.cantExport)
+ // $('.requiresoffice').click(pad.exportDoLongClick);
+ // $('.exportlink').click(pad.exportHideMessages);
+}
+
+pad.initRichText = function() {
+ $(".toptoolbarbutton").attr("unselectable", "on"); // for IE
+ $("#toptoolbar").removeClass("disabledtoolbar");
+}
+
+pad.initViewBar = function() {
+
+ pad.setViewFont(pad.initialCookiePrefs.viewFont || 'normal'); // sets pad.viewFont
+ pad.setViewZoom(pad.initialCookiePrefs.viewZoom || 100); // sets pad.viewZoom
+
+ $("#bottoolbar").css('display', 'block'); // show it
+ $("#bottoolbar").removeClass("disabledtoolbar");
+
+ $("#viewzoommenu").change(function(evt) {
+ pad.setViewZoom(Number($("#viewzoommenu").val().substring(1))); // remove 'z' from val
+ });
+ $("#viewfontmenu").change(function(evt) {
+ pad.setViewFont($("#viewfontmenu").val());
+ });
+}
+
+pad.initPadTitle = function() {
+ pad.title = clientVars.initialTitle;
+ pad.password = clientVars.initialPassword;
+
+ $("#padtitle").submit(function(evt) {
+ pad.submitTitle(true);
+ evt.preventDefault();
+ return false;
+ });
+ $("#padtitleedit").keyup(function(evt) {
+ if (evt.which == 27) {
+ // "escape" key
+ pad.submitTitle(false);
+ }
+ });
+ pad.renderPadTitle();
+}
+
+pad.init = function() {
+ // note: order of init calls is important.
+
+ pad.debugEnabled = clientVars.debugEnabled;
+ pad.padId = clientVars.padId;
+ pad.diagnosticInfo.uniqueId = pad.uniqueId();
+
+ pad.dmesg("pad.init()");
+
+ pad.initCookie();
+ pad.initResizeTimer();
+ pad.initAce(function() {
+ document.domain = document.domain; // for comet, set after ACE creates its frames
+ pad.initMyUserInfo();
+ pad.initSideBoxes();
+ pad.initCollab();
+ pad.initRichText();
+ pad.initViewBar();
+ pad.initRevisions();
+ pad.initPrefsPane();
+ pad.initChat();
+ pad.initEvents();
+ pad.initFocusTracker();
+ pad.initPadTitle();
+ $('a#hidesidebar').show();
+ pad.initTime = +(new Date());
+ pad.initialized = true;
+ pad.saveCookie(); // must happen after pad.initialized = true.
+ pad.initModalDialogs();
+ pad.initImportExport();
+ pad.initWelcomeMessage();
+ pad.dmesg("pad.init(): done!");
+ });
+};
+
+//----------------------------------------------------------------
+
+pad.getDefaultUsername = function() {
+ var cookieName = pad.initialCookiePrefs.name;
+ if (cookieName) {
+ return cookieName;
+ }
+ var nameGuess = clientVars.nameGuess;
+ if (nameGuess) { return nameGuess; }
+ return null;
+};
+
+pad.getCurrentPageHeight = function() {
+ return $("#padcontent").offset().top + $("#padcontent").height() + 1;
+}
+
+pad.resizePage = function(dontReschedule) {
+ // this method should not require any init to have happened
+
+ var visibleHeight = document.documentElement.clientHeight;
+ var sizedContentHeight = $("#sizedcontent").height() - 2; // subtract 2 for border
+ var pageHeight = pad.getCurrentPageHeight();
+
+ var newHeight = visibleHeight - (pageHeight - sizedContentHeight);
+ var totalToolbarHeight = $("#toptoolbar").outerHeight() + $("#bottoolbar").outerHeight();
+
+ // editor (left column, 1)
+ var oldHeight1 = $("#editorcontainer").height() - totalToolbarHeight;
+ var newHeight1 = newHeight - totalToolbarHeight;
+
+ // sidebar (right column, 2)
+ var oldHeight2 = $("#sidebar").height();
+ var newHeight2 = newHeight;
+
+ if (newHeight1 != oldHeight1) {
+ $("#editorcontainer").css('height', newHeight1+"px");
+ if (pad.ace) {
+ pad.ace.adjustSize();
+ }
+ }
+ if (newHeight2 != oldHeight2) {
+ $("#sidebar").css('height', newHeight2+"px");
+ }
+
+ pad.repositionModals();
+ if (pad.initialized) {
+ pad.renderPadTitle();
+ }
+
+ if (! dontReschedule) {
+ // try again in a bit in case we missed the correct size,
+ // but don't let that call cause another setTimeout
+ setTimeout(function() { pad.resizePage(true); }, 100);
+ }
+};
+
+pad.setOnEnterKey = function(eltpath, f) {
+ $(eltpath).keydown(function(e) {
+ if (e.keyCode == 13) {
+ f(e);
+ e.preventDefault();
+ return false;
+ }
+ return true; //continue event
+ });
+};
+
+//----------------------------------------------------------------
+// New pad button.
+//----------------------------------------------------------------
+
+pad.newPad = function() {
+ $('#newpad').submit();
+};
+
+//----------------------------------------------------------------
+// UI Preferences
+//----------------------------------------------------------------
+
+pad.isPrefMarked = function(n) {
+ var v = !!$('#checkpref'+n+' input').attr('checked');
+ pad.dmesg("isPrefMarked("+n+") = "+v);
+ return v;
+};
+
+pad.markPref = function(n, t) {
+ var i = $('#checkpref'+n+' input');
+ if (t) {
+ i.attr('checked', 'checked');
+ } else {
+ i.removeAttr('checked');
+ }
+};
+
+pad.updatePref = function(name, val) {
+ var ace = pad.ace;
+ if (name == "showcolors") {
+ ace.setProperty("showsAuthorColors", val);
+ }
+ if (name == "wrap") {
+ ace.setProperty("wraps", val);
+ }
+ if (name == "linenums") {
+ ace.setProperty("lineNumbers", val);
+ }
+ if (name == "jshighlight") {
+ if (val) {
+ ace.setProperty("lexer", "js");
+ } else {
+ ace.setProperty("lexer", "txt");
+ }
+ }
+ if (name == "fullwidth") {
+ pad.setFullWidth(val);
+ }
+};
+
+//----------------------------------------------------------------
+
+pad.uniqueId = function() {
+ return [
+ clientVars.clientIp,
+ String(+(new Date())),
+ String(Math.floor(Math.random()*100000))
+ ].join('.');
+};
+
+pad.getUserDisplayName = function(userInfo) {
+ // strip HTML from newName.
+ // TODO: better HTML escaping?
+ var name = userInfo.name;
+ if (!(typeof(name) == "string")) {
+ name = "unnamed";
+ if (userInfo.userId == pad.myUserInfo.userId) {
+ name += ' (me)';
+ }
+ }
+ return name;
+};
+
+//----------------------------------------------------------------
+// userJoin
+//----------------------------------------------------------------
+
+pad.userJoin = function(userInfo) {
+ pad.dmesg("pad.userJoin: "+userInfo.name+"/"+userInfo.userId);
+ pad.users[userInfo.userId] = userInfo;
+
+ var name = pad.escapeHtml(pad.getUserDisplayName(userInfo));
+ if (userInfo.userId != pad.myUserInfo.userId) {
+ var currentTime = +(new Date());
+ if ((currentTime - pad.initTime) > 1000*2) {
+ pad.notify('<i>'+name+'</i> has joined the pad.');
+ }
+ }
+
+ // assign an ID for the userbox div
+ var boxId = pad.userBoxIds[userInfo.userId];
+ if (!boxId) {
+ pad.lastBoxId++;
+ boxId = "userbox"+pad.lastBoxId;
+ pad.userBoxIds[userInfo.userId] = boxId;
+ var classNames = "userbox";
+ if (userInfo.userId == pad.myUserInfo.userId) { classNames += " me"; }
+ var userbox = '<div class="'+classNames+'" id="'+boxId+'"><!-- --></div>';
+ $('#userlist').append(userbox);
+ }
+
+ $('#userlist div.lastuser').removeClass('lastuser');
+ $('#'+boxId).addClass('lastuser').hide();
+
+ pad.updateUserBox(userInfo);
+ pad.updateChatHistoryForUser(userInfo);
+
+ // show
+ $('#'+boxId).fadeIn('slow');
+};
+
+pad.updateUserBox = function(userInfo) {
+ var nameHtml = '<span class="username">' + pad.escapeHtml(pad.getUserDisplayName(userInfo)) + '</span>';
+ var changeLink = '';
+ var isMe = (userInfo.userId == pad.myUserInfo.userId);
+ if (isMe) {
+ changeLink = '<div id="rightuserlink">'+
+ '<a id="changenamelink" href="javascript: void;" class="small_link">edit name/color</a>'+
+ '</div>';
+ }
+ var displayIp = userInfo.ip + '/' + userInfo.userAgent;
+ var boxId = pad.userBoxIds[userInfo.userId];
+ var box = $('#'+boxId);
+ if (box.size() != 1) {
+ pad.notify("Warning: spurious user update for user &lt;"+userInfo.userId+"&gt;");
+ return;
+ }
+ var bgcolor = clientVars.colorPalette[userInfo.colorId];
+ box.empty().html([
+ '<div class="userinfowrap' + (isMe ? ' myuserwrap' : '') + '">',
+ '<div class="usercolor" style="background-color: ',bgcolor, '">',
+ '&nbsp;</div>',
+ '<div class="userinfo">',
+ changeLink,
+ ' <div>',
+ nameHtml,
+ ' </div>',
+ ' <div class="ip">',
+ displayIp,
+ ' </div>',
+ '</div>',
+ '<div style="clear: both;"><!-- --></div>',
+ '</div>'
+ ].join(''));
+ if (isMe) {
+ $('#userlist div.myuserwrap').click(pad.myUserInfoClick);
+ }
+};
+
+pad.myUserInfoClick = function(e) {
+ if (pad.isEditingUserInfo) {
+ return true;
+ }
+ pad.oldColorId = pad.myUserInfo.colorId;
+ pad.isEditingUserInfo = true;
+ $('#userlist div.me').hide().html([
+ '<div class="edituserinfo">',
+ '<h4>Name:</h4>',
+ '<p><input type="text" id="mynewname" /></p>',
+ '<h4>Color:</h4>',
+ pad.renderColorPicker(),
+ '<button id="savemyuserinfo">Save</button>',
+ '<button id="canceleditmyuserinfo">Cancel</button>',
+ '</div>'
+ ].join('')).fadeIn('fast', function() {});
+
+ $('input#mynewname').val(pad.getUserDisplayName(pad.myUserInfo)).focus().select();
+ pad.setOnEnterKey('input#mynewname', pad.saveMyNewUserInfo);
+
+ $('#canceleditmyuserinfo').click(pad.cancelEditMyUserInfo);
+ $('#savemyuserinfo').click(pad.saveMyNewUserInfo);
+
+ e.preventDefault();
+ return false;
+};
+
+pad.cancelEditMyUserInfo = function(e) {
+ if (pad.oldColorId != pad.myUserInfo.colorId) {
+ pad.myUserInfo.colorId = pad.oldColorId;
+ pad.collabClient.updateUserInfo(pad.myUserInfo);
+ }
+ $('#userlist div.me').fadeOut('fast', function() {
+ pad.updateUserBox(pad.myUserInfo);
+ $('#userlist div.me').fadeIn('fast');
+ pad.isEditingUserInfo = false;
+ });
+ e.preventDefault();
+ return false;
+};
+
+pad.saveMyNewUserInfo = function(e) {
+ var newName = $('#mynewname').val();
+ if (newName != "unnamed (me)") {
+ pad.myUserInfo.name = newName;
+ }
+ $('#userlist div.me').fadeOut('fast', function() {
+ $('#userlist div.me').html('Saving...');
+ pad.collabClient.updateUserInfo(pad.myUserInfo);
+ pad.saveCookie();
+ pad.updateUserBox(pad.myUserInfo);
+ $('#userlist div.me').fadeIn('fast');
+ pad.isEditingUserInfo = false;
+ pad.updateChatHistoryForUser(pad.myUserInfo);
+ });
+ e.preventDefault();
+ return false;
+};
+
+pad.renderColorPicker = function() {
+ function renderColorSquare(i) {
+ var classNames = "";
+ if (i == pad.myUserInfo.colorId) {
+ classNames += " selectedcolor";
+ }
+ var html = [
+ '<a class="'+classNames+'"',
+ ' id="colorchoice'+i+'"',
+ ' style="background: '+clientVars.colorPalette[i], ';"',
+ ' href="javascript:void pad.chooseNewColor('+i+');">',
+ '&nbsp;</a>'
+ ].join('');
+ return html;
+ }
+ var r = '<div id="colorpicker">';
+ for (var i = 0; i < clientVars.colorPalette.length; i++) {
+ r += renderColorSquare(i);
+ }
+ r += '<div style="clear: both;"><!-- --></div></div>';
+ return r;
+};
+
+pad.chooseNewColor = function(colorId) {
+ pad.myUserInfo.colorId = colorId;
+ pad.collabClient.updateUserInfo(pad.myUserInfo);
+ $('#colorpicker a').removeClass('selectedcolor');
+ $('#colorpicker a#colorchoice'+colorId).addClass('selectedcolor');
+};
+
+//----------------------------------------------------------------
+// userLeave
+//----------------------------------------------------------------
+
+pad.userLeave = function(userInfo) {
+ pad.dmesg("pad.userLeave: "+userInfo.name+"/"+userInfo.userId);
+
+ var box = $('#'+pad.userBoxIds[userInfo.userId]);
+ box.attr('id', ('oldbox'+(+(new Date()))));
+
+ delete pad.users[userInfo.userId];
+ delete pad.userBoxIds[userInfo.userId];
+
+ box.fadeOut('slow', function() {
+ box.remove();
+ var boxes = $('#userlist').children();
+ if (boxes.size() > 0) {
+ $(boxes.get(boxes.size()-1)).addClass('lastuser');
+ }
+ });
+
+ pad.updateChatHistoryForUser(userInfo);
+};
+
+//----------------------------------------------------------------
+// updateUserInfo
+//----------------------------------------------------------------
+
+pad.updateUserInfo = function(userInfo) {
+ pad.updateUserBox(userInfo);
+ pad.updateChatHistoryForUser(userInfo);
+ // TODO: draw attention to the updated userbox somehow? flash it?
+ var oldInfo = pad.users[userInfo.userId];
+ if (oldInfo.name != userInfo.name) {
+ var oldName = pad.escapeHtml(pad.getUserDisplayName(oldInfo));
+ var newName = pad.escapeHtml(pad.getUserDisplayName(userInfo));
+ pad.notify("<i>"+oldName+"</i> is now known as <i>"+newName+"</i>.");
+ }
+ if (oldInfo.colorId != userInfo.colorId) {
+ pad.notify("<i>"+pad.escapeHtml(pad.getUserDisplayName(userInfo))+"</i> has a new background color.");
+ }
+ pad.users[userInfo.userId] = userInfo;
+};
+
+//----------------------------------------------------------------
+// channelStateChange
+//----------------------------------------------------------------
+
+pad.channelStateChange = function(newState, message) {
+ pad.connectionState = newState;
+ if (newState == "CONNECTED") {
+ $('#savenow').removeAttr('disabled');
+ pad.displayConnectionStatus('connected');
+ pad.notify("Connected to EtherPad synchronization server.");
+ }
+ if (newState == "RECONNECTING") {
+ $('#savenow').attr('disabled', true);
+ pad.displayConnectionStatus('connecting');
+ pad.notify("Lost connection with server. Attempting to reconnect...", true);
+ }
+ if (newState == "DISCONNECTED") {
+ pad.diagnosticInfo.disconnectedMessage = message;
+ pad.diagnosticInfo.padInitTime = pad.initTime;
+ pad.asyncSendDiagnosticInfo();
+
+ $('#savenow').attr('disabled', true);
+
+ pad.displayConnectionStatus('disconnected');
+ if (typeof top.ajlog == "string") { top.ajlog += ("Disconnected: "+message+'\n'); }
+
+ $('div#bigtoperror_wrap').addClass('hidden');
+ $('div.bigtoperror').hide();
+ pad.clearNotificationQueue();
+ if (pad.ace) {
+ pad.ace.setProperty("grayedOut", true);
+ pad.ace.setEditable(false);
+ }
+ if (message == "userdup") {
+ $('div#disconnected_userdup').show();
+ } else if (message == "looping") {
+ $('div#disconnected_looping').show();
+ $('div#reconnect_advise').show();
+ } else if (message == "slowcommit") {
+ $('div#disconnected_slowcommit').show();
+ $('div#reconnect_advise').show();
+ } else if (message == "initsocketfail") {
+ $('div#disconnected_initsocketfail').show();
+ $('div#reconnect_advise').show();
+ } else {
+ $('div#disconnected_unknown').show();
+ $('div#reconnect_advise').show();
+ }
+ pad.notify("Lost connection with server.");
+ $('div#bigtoperror_wrap').html();
+ $('div#bigtoperror_wrap').removeClass('hidden').fadeIn('fast', function() {
+ pad.resizePage();
+ });
+ $('div#userlist div.userbox').remove();
+ }
+};
+
+//----------------------------------------------------------------
+// Notification system
+//----------------------------------------------------------------
+
+pad.notify = function(message, persist) {
+ pad.notifyQueue.push([message, persist]);
+ setTimeout(pad.processNotifyQueue, 50);
+};
+
+pad.clearNotificationQueue = function() {
+ delete pad.notifyQueue;
+ pad.notifyQueue = [];
+};
+
+pad.processNotifyQueue = function() {
+ if (pad.notifyQueue.length == 0) {
+ return;
+ }
+ var msSinceLast = (+(new Date()) - pad.lastNotify);
+ if (msSinceLast < 1000) {
+ setTimeout(pad.processNotifyQueue, 1050-msSinceLast);
+ return;
+ }
+ var msg = pad.notifyQueue.shift();
+ $('#topbarmsg').fadeOut('fast', function() {
+ $('#topbarmsg').html(msg[0]).fadeIn('slow');
+ });
+ pad.lastNotify = +(new Date());
+
+ // set window title if blurred
+ function striptags(x) {
+ return x.replace(/<[^\>]+>/g, '');
+ }
+ if (!pad.padHasFocus) {
+ $(document).attr('title', '* '+striptags(msg[0]));
+ }
+
+ // clear it after 5 seconds unless "persist" was specified
+ pad.persistLastNotify = msg[1];
+ setTimeout(function() {
+ if (!pad.persistLastNotify) {
+ $('#topbarmsg').fadeOut('slow');
+ }
+ }, 5000);
+};
+
+//----------------------------------------------------------------
+// Cookies
+//----------------------------------------------------------------
+
+pad.saveCookie = function() {
+ if (!pad.initialized) { return; }
+ var prefs = {
+ userId: pad.myUserInfo.userId,
+ name: pad.myUserInfo.name,
+ colorId: pad.myUserInfo.colorId,
+ wrapLines: pad.isPrefMarked('wrap'),
+ showAuthorColors: pad.isPrefMarked('showcolors'),
+ showLineNumbers: pad.isPrefMarked('linenums'),
+ highlightJs: pad.isPrefMarked('jshighlight'),
+ fullWidth: pad.isPrefMarked('fullwidth'),
+ sideBoxVisibility: pad.sideBoxVisibility,
+ viewZoom: pad.viewZoom,
+ viewFont: pad.viewFont
+ };
+ var expiresDate = new Date();
+ expiresDate.setFullYear(3000);
+ document.cookie = ('prefs='+escape(JSON.stringify(prefs))+
+ ';expires='+expiresDate.toGMTString());
+
+ // If the browser rejects the cookie, then show an error message.
+ if (clientVars.isProPad &&
+ !(document.cookie &&
+ (document.cookie.length > 0) &&
+ (document.cookie.indexOf('prefs') >= 0))) {
+
+ if (!pad.alreadyWarnedAboutNoCookies) {
+ alert("Warning: it appears that your browser does not have cookies enabled."+
+ " EtherPad uses cookies to keep track of unique users for the purpose"+
+ " of putting a quota on the number of active users. Using EtherPad without "+
+ " cookies may fill up your server's user quota faster than expected.");
+ pad.alreadyWarnedAboutNoCookies = true;
+ }
+ }
+};
+
+pad.getInitialCookiePrefs = function() {
+
+ function _getExistingCookie() {
+ if (document.cookie.length < 1) {
+ pad.isFreshCookie = true;
+ return {};
+ }
+ var name = "prefs";
+ var start = document.cookie.indexOf(name + "=");
+ if (start == -1) {
+ pad.isFreshCookie = true;
+ return {};
+ }
+ start = start + name.length + 1;
+ var end = document.cookie.indexOf(";", start);
+ if (end == -1) {
+ end = document.cookie.length;
+ }
+ try {
+ return JSON.parse(unescape(document.cookie.substring(start, end)));
+ } catch (ex) {
+ pad.isFreshCookie = true;
+ return {};
+ }
+ }
+
+ var cp = _getExistingCookie();
+
+ // required cookie fields & defaults
+ function _setDefault(name, val) {
+ if (cp[name] === undefined) {
+ cp[name] = val;
+ }
+ }
+ _setDefault('sideBoxVisibility', {});
+ _setDefault('wrapLines', true);
+ _setDefault('showAuthorColors', true);
+ _setDefault('showLineNumbers', false);
+ _setDefault('fullWidth', false);
+
+ return cp;
+};
+
+//----------------------------------------------------------------
+
+pad.forceReconnect = function(diagnosticInfo) {
+ $('form#reconnect input#padId').val(pad.padId);
+ pad.diagnosticInfo.collabDiagnosticInfo = pad.collabClient.getDiagnosticInfo();
+ $('form#reconnect input#diagnosticInfo').val(JSON.stringify(pad.diagnosticInfo));
+ $('form#reconnect input#missedChanges').val(JSON.stringify(pad.collabClient.getMissedChanges()));
+ $('form#reconnect').submit();
+};
+
+pad.dmesg = function(m) {
+ if (pad.debugEnabled) {
+ $('#djs').append('<p>'+m+'</p>');
+ var djs = $('#djs').get(0);
+ djs.scrollTop = djs.scrollHeight;
+ }
+};
+
+pad.showSideBar = function() {
+ $('#sidebarcell').show().addClass('visible');
+ $('a#showsidebar').hide();
+ $('a#hidesidebar').show();
+ pad.sideBarVisible = true;
+ pad.saveCookie();
+ return false;
+};
+
+pad.hideSideBar = function() {
+ $('#sidebarcell').hide().removeClass('visible');
+ $('a#showsidebar').show();
+ $('a#hidesidebar').hide();
+ pad.sideBarVisible = false;
+ pad.saveCookie();
+ return false;
+};
+
+pad.setFullWidth = function(fullWidth) {
+ if (fullWidth) {
+ //$('#padhead, #appjetfooter').fadeOut('fast').addClass('hidden');
+ $('body').removeClass('limwidth').addClass('fullwidth');
+ $('#widthlink').attr('title', "Use fixed pad width");
+ } else {
+ // hack:
+ $("#padtitle").css('width', '400px');
+
+ //$('#padhead, #appjetfooter').removeClass('hidden').fadeIn('fast');
+ $('body').removeClass('fullwidth').addClass('limwidth');
+ $('#widthlink').attr('title', "Use full window width");
+ }
+ if (pad.initialized) {
+ pad.resizePage();
+ }
+};
+
+pad.toggleFullWidth = function() {
+ // fires when user clicks icon in corner; updates preferences
+ var oldValue = pad.isPrefMarked("fullwidth");
+ var newValue = ! oldValue;
+
+ pad.markPref("fullwidth", newValue);
+ pad.updatePref("fullwidth", newValue);
+
+ pad.saveCookie();
+}
+
+pad.handlerPreventDefault = function(e) {
+ e.preventDefault();
+ return false;
+}
+
+pad.swallowAllMouseEvents = function(jqueryNodes) {
+ jqueryNodes.click(pad.handlerPreventDefault);
+ jqueryNodes.mousedown(pad.handlerPreventDefault);
+ jqueryNodes.mouseup(pad.handlerPreventDefault);
+}
+
+//----------------------------------------------------------------
+// Saved Revisions
+//----------------------------------------------------------------
+
+pad.renderRevisionList = function(fast) {
+ var rlis = pad.revisionList;
+ var content = "";
+ for (var i = 0; (i < rlis.length) && (i < pad.shownRevisions); i++) {
+ var rnum = rlis.length - i;
+ var name = pad.escapeHtml(rlis[i].savedBy);
+ name += " ("+rlis[i].ip+")";
+ var when = pad.timediff(new Date(rlis[i].timestamp));
+ var viewLink = '/ep/pad/view/'+pad.padId+'/'+rlis[i].id;
+ var restoreLink = 'javascript:void pad.restoreRevision('+rnum+');';
+ var label = pad.escapeHtml(rlis[i].label || ('Revision '+rnum));
+ if (rlis[i].savedById == pad.myUserInfo.userId &&
+ ((+(new Date()) - rlis[i].timestamp) < (24*60*60*1000))) {
+ label = ('<a class="editrlabel small_link" href="javascript: void pad.editRevisionLabel('+rnum+');">'+
+ label + '</a>');
+ }
+ content += ('<div class="revisioninfo" id="revisioninfo_'+rnum+'">'+
+ ' <div class="rleft">'+
+ ' <div id="rlabel'+rnum+'">'+label+'</div>'+
+ ' <div class="ractions">'+
+ ' <a class="small_link" target="_blank" href="'+viewLink+'">view</a>&nbsp;|&nbsp;<a class="small_link" href="'+restoreLink+'">restore</a>'+
+ ' </div>'+
+ ' </div>'+
+ ' <div class="rright">saved '+when+' by '+name+'</div>'+
+ ' <div style="clear: both;"><!-- --></div>'+
+ '</div>');
+ }
+ var areMore = (pad.shownRevisions < pad.revisionList.length);
+ var areFewer = (pad.shownRevisions > pad.REVISIONCOUNT_STEP);
+ content += '<div class="revisionbottomlinks">';
+ if (areMore || areFewer) {
+ content += 'show:&nbsp;';
+ }
+ if (areFewer) {
+ content += '<a class="small_link" href="javascript:void pad.fewerRevisions();">fewer</a>';
+ }
+ if (areMore && areFewer) {
+ content += '&nbsp;|&nbsp;';
+ }
+ if (areMore) {
+ content += '<a class="small_link" href="javascript:void pad.moreRevisions();">more</a>';
+ }
+ content += '</div>';
+
+ if (fast) {
+ $('#revisionlist').html(content);
+ } else {
+ $('#revisionlist').hide().html(content).fadeIn('fast');
+ }
+};
+
+pad.fewerRevisions = function() {
+ pad.shownRevisions -= pad.REVISIONCOUNT_STEP;
+ pad.renderRevisionList();
+};
+
+pad.moreRevisions = function() {
+ pad.shownRevisions += pad.REVISIONCOUNT_STEP;
+ pad.renderRevisionList();
+};
+
+pad.saveRevision = function() {
+ var mv = clientVars.accountPrivs.maxRevisions;
+ if ((mv != -1) && (pad.revisionList.length >= mv)) {
+ $('#savenow').hide();
+ $('#nosaveprivs').fadeIn('fast');
+ return;
+ }
+ $('#savenow').attr('disabled', true).val("Saving...");
+ var savedBy = pad.myUserInfo.name || "unnamed";
+ pad.collabClient.callWhenNotCommitting(doAjax);
+ function doAjax() {
+ $.ajax({
+ type: 'post',
+ url: '/ep/pad/saverevision',
+ data: {
+ padId: pad.padId,
+ savedBy: savedBy,
+ savedById: pad.myUserInfo.userId,
+ revNum: pad.collabClient.getCurrentRevisionNumber()
+ },
+ success: success,
+ error: error
+ });
+ }
+ function success(text) {
+ pad.revisionList = JSON.parse(text);
+ if (pad.revisionList.length > 0) {
+ pad.clientTimeOffset = +(new Date) - pad.revisionList[0].timestamp;
+ }
+ pad.renderRevisionList();
+ pad.collabClient.sendClientMessage({
+ type: 'newRevisionList',
+ revisionList: pad.revisionList,
+ savedBy: savedBy
+ });
+ $('#savenow').val("Save Now").removeAttr('disabled');
+ }
+ function error(e) {
+ alert("Oops! The server failed to save the revision. Please try again later.");
+ $('#savenow').val("Save Now").removeAttr('disabled');
+ }
+};
+
+pad.restoreRevision = function(rnum) {
+ var i = pad.revisionList.length - rnum;
+ var rev = pad.revisionList[i];
+ var warning = ("Restoring this revision will replace the current"
+ + "pad with the text saved in revision \""
+ + ""+rev.label+"\". Are you sure you want to continue?");
+
+ if (confirm(warning)) {
+ $.ajax({
+ type: 'get',
+ url: '/ep/pad/getrevisionatext',
+ data: {padId: pad.padId, revId: rev.id},
+ success: success,
+ error: error
+ });
+ }
+ function success(resultJson) {
+ var result = JSON.parse(resultJson);
+ pad.collabClient.addHistoricalAuthors(result.historicalAuthorData);
+ pad.ace.importAText(result.atext, result.apool, true);
+ }
+ function error(e) {
+ alert("Oops! There was an error retreiving the text (revNum= "+
+ rev.revNum+"; padId="+pad.padId);
+ }
+};
+
+pad.editRevisionLabel = function(rnum) {
+ var i = pad.revisionList.length - rnum;
+ var rev = pad.revisionList[i];
+ $('#revisioninfo_'+rnum).html(
+ '<p class="revlabelprompt">Enter a label for revision #'+rnum+' and press return.</p>'+
+ '<input class="inputrevlabel"'+
+ ' id="inputrevlabel'+rnum+'" type="text" />'
+ );
+ $('input#inputrevlabel'+rnum).val(rev.label).focus().select();
+ pad.setOnEnterKey('#inputrevlabel'+rnum, function() {
+ pad.saveRevisionLabel(rnum);
+ });
+};
+
+pad.saveRevisionLabel = function(rnum) {
+ var i = pad.revisionList.length - rnum;
+ var rev = pad.revisionList[i];
+ var newLabel = $('#inputrevlabel'+rnum).val();
+ $.ajax({
+ type: 'post',
+ url: '/ep/pad/saverevisionlabel',
+ data: {userId: pad.myUserInfo.userId,
+ padId: pad.padId,
+ revId: rev.id,
+ newLabel: newLabel},
+ success: success,
+ error: error
+ });
+ function success(text) {
+ pad.revisionList = JSON.parse(text);
+ pad.renderRevisionList();
+ pad.collabClient.sendClientMessage({
+ type: 'revisionLabel',
+ revisionList: pad.revisionList,
+ savedBy: pad.myUserInfo.name,
+ newLabel: newLabel
+ });
+ }
+ function error(e) {
+ alert("Oops! There was an error saving that revision label. Please try again later.");
+ }
+};
+
+//----------------------------------------------------------------
+// Feedback form.
+//----------------------------------------------------------------
+pad.submitfeedback = function() {
+ var feedback = $('#feedbackarea').val();
+ $('#feedbackarea').val('');
+ $.ajax({
+ type: 'post',
+ url: '/ep/pad/feedback',
+ data: {feedback: feedback, padId: pad.padId, username: pad.myUserInfo.name},
+ success: success,
+ error: error
+ });
+
+ $('#feedbackresult #feedbackemail').html('feedback'+'@'+'etherpad'+'.com');
+ $('#feedbackbox').fadeOut('fast', function() {
+ $('#feedbackresult').fadeIn('slow', function() {
+ setTimeout(function() {
+ $('#feedbackresult').fadeOut('slow', function() {
+ $('#feedbackbox').fadeIn('fast');
+ });
+ }, 10000);
+ });
+ });
+
+ function success(msg) {};
+ function error(e) {
+ alert("Oops! There was an error submitting the feedback. Please try again later.");
+ }
+};
+
+//----------------------------------------------------------------
+// UA Display
+//----------------------------------------------------------------
+
+pad.uaDisplay = function(ua) {
+ var m;
+
+ function clean(a) {
+ var maxlen = 16;
+ a = a.replace(/[^a-zA-Z0-9\.]/g, '');
+ if (a.length > maxlen) {
+ a = a.substr(0,maxlen);
+ }
+ return a;
+ }
+
+ function checkver(name) {
+ var m = ua.match(RegExp(name + '\\/([\\d\\.]+)'));
+ if (m && m.length > 1) {
+ return clean(name+m[1]);
+ }
+ return null;
+ }
+
+ // firefox
+ if (checkver('Firefox')) { return checkver('Firefox'); }
+
+ // misc browsers, including IE
+ m = ua.match(/compatible; ([^;]+);/);
+ if (m && m.length > 1) {
+ return clean(m[1]);
+ }
+
+ // iphone
+ if (ua.match(/\(iPhone;/)) {
+ return 'iPhone';
+ }
+
+ // chrome
+ if (checkver('Chrome')) { return checkver('Chrome'); }
+
+ // safari
+ m = ua.match(/Safari\/[\d\.]+/);
+ if (m) {
+ var v = '?';
+ m = ua.match(/Version\/([\d\.]+)/);
+ if (m && m.length > 1) {
+ v = m[1];
+ }
+ return clean('Safari'+v);
+ }
+
+ // everything else
+ var x = ua.split(' ')[0];
+ return clean(x);
+};
+
+//----------------------------------------------------------------
+// invitemoreClick
+//----------------------------------------------------------------
+
+pad.invitemoreShow = function() {
+ $('#invitemorelink').hide();
+ $('#inviteinstructions').fadeIn('fast');
+ $('#invite_email').focus();
+};
+
+pad.invitemoreHide = function() {
+ $('#inviteinstructions').fadeOut('fast');
+ $('#invitemorelink').show();
+};
+
+pad.invitemoreSendEmail = function() {
+ function msg(m) {
+ $('#invite_email_status').hide().html(m).fadeIn('fast');
+ }
+ var email = $('#invite_email').val();
+ if (!pad.validEmail(email)) {
+ msg("That doesn't look like a valid email address.");
+ return;
+ }
+ $('#invite_email_submit').attr('disabled', true).val('Sending...');
+ $.ajax({
+ type: 'post',
+ url: '/ep/pad/emailinvite',
+ data: {email: email,
+ padId: pad.padId,
+ username: (pad.myUserInfo.name || "someone")
+ },
+ success: function() {
+ $('#invite_email').val('');
+ msg("Email sent to "+email+".");
+ $('#invite_email_submit').val('Send').removeAttr('disabled');
+ },
+ error: function() {
+ msg("Oops! There was an error sending the invite. Please try again later.");
+ $('#invite_email_submit').val('Send').removeAttr('disabled');
+ }
+ });
+};
+
+pad.validEmail = function(x) {
+ return (x.length > 0 &&
+ x.match(/^[\w\.\_\+\-]+\@[\w\_\-]+\.[\w\_\-\.]+$/));
+};
+
+/*
+pad.getQueryParam = function(name) {
+ var params = {};
+ var query = window.location.search.substr(1);
+ var parts = query.split('&');
+ for (var i = 0; i < parts.length; i++) {
+ var subparts = parts[i].split('=');
+ if (subparts.length == 2) {
+ params[subparts[0]] = subparts[1];
+ }
+ }
+ return params[name];
+};
+*/
+
+pad.timediff = function(d) {
+ function format(n, word) {
+ n = Math.round(n);
+ return ('' + n + ' ' + word + (n != 1 ? 's' : '') + ' ago');
+ }
+ d = (+(new Date) - (+d) - pad.clientTimeOffset) / 1000;
+ if (d < 60) { return format(d, 'second'); }
+ d /= 60;
+ if (d < 60) { return format(d, 'minute'); }
+ d /= 60;
+ if (d < 24) { return format(d, 'hour'); }
+ d /= 24;
+ return format(d, 'day');
+};
+
+//----------------------------------------------------------------
+
+pad.onClientMessage = function(m) {
+ if (m.type == 'newRevisionList') {
+ pad.revisionList = m.revisionList;
+ pad.notify(pad.escapeHtml(m.savedBy) + " saved a new revision.");
+ pad.renderRevisionList();
+ }
+ if (m.type == 'revisionLabel') {
+ pad.revisionList = m.revisionList;
+ pad.notify(pad.escapeHtml(m.savedBy) + ' labeled a revision, "'+pad.escapeHtml(m.newLabel)+'".');
+ pad.renderRevisionList();
+ }
+ if (m.type == 'chat') {
+ pad.receiveChat(m);
+ }
+ if (m.type == 'padtitle') {
+ pad.title = m.title;
+ pad.renderPadTitle();
+ if (m.changedBy) {
+ pad.notify(pad.escapeHtml(m.changedBy) + ' changed the pad title.');
+ }
+ }
+ if (m.type == 'padpassword') {
+ pad.password = m.password;
+ pad.renderPadTitle();
+ if (m.changedBy) {
+ pad.notify(pad.escapeHtml(m.changedBy) + (
+ m.password ? ' changed the pad password.' : ' removed password protection from this pad.'));
+ }
+ }
+};
+
+pad.onServerMessage = function(m) {
+ if (m.type == 'NOTICE') {
+ if (m.text) {
+ $('#servermsg').hide().addClass('hidden');
+ $('#servermsgdate').html((new Date()).toString());
+ $('#servermsgtext').html(m.text);
+ $('#servermsg').removeClass('hidden').fadeIn('fast', function() { pad.resizePage(); });
+ pad.notify("Server Notice");
+ }
+ if (m.js) {
+ eval(m.js);
+ }
+ }
+};
+
+pad.hideTopMsg = function(mn) {
+ var x = $('#'+mn+'msg');
+ x.fadeOut('fast', function() {
+ x.addClass('hidden');
+ pad.resizePage();
+ });
+};
+
+//----------------------------------------------------------------
+
+pad.isSideBoxVisible = function(paneName) {
+ var head = $('#head'+paneName);
+ var box = $('#'+paneName);
+ return head.hasClass('sh_uncollapsed');
+};
+
+pad.showSideBox = function(paneName) {
+ var head = $('#head'+paneName);
+ var box = $('#'+paneName);
+ head.removeClass('sh_collapsed').addClass('sh_uncollapsed');
+ box.fadeIn('fast', function() {});
+ pad.sideBoxVisibility[paneName] = true;
+ pad.saveCookie();
+ if (paneName == "chatbox") {
+ pad.onShowChatBox();
+ }
+};
+
+pad.hideSideBox = function(paneName) {
+ var head = $('#head'+paneName);
+ var box = $('#'+paneName);
+ box.fadeOut('fast', function() {
+ head.removeClass('sh_uncollapsed').addClass('sh_collapsed');
+ pad.sideBoxVisibility[paneName] = false;
+ pad.saveCookie();
+ });
+};
+
+//----------------------------------------------------------------
+
+pad.escapeHtml = function(x) {
+ return x.replace(/\</g, '&lt;').replace(/\>/g, '&gt;');
+};
+
+//----------------------------------------------------------------
+// Chat
+//----------------------------------------------------------------
+
+pad.initChat = function() {
+ pad.setOnEnterKey('#chatinput', function() {
+ pad.sendChat();
+ });
+ pad.chatUserNums = {};
+ pad.lastChatUserNum = 0;
+ pad.numUnreadChatMessages = 0;
+ pad.initChatHistory();
+};
+
+pad.sendChat = function() {
+ var lineText = $('#chatinput').val();
+ $('#chatinput').val('').focus();
+ var msg = {
+ type: 'chat',
+ userId: pad.myUserInfo.userId,
+ lineText: lineText
+ };
+ pad.collabClient.sendClientMessage(msg);
+ pad.receiveChat(msg);
+};
+
+pad.updateChatHistoryForUser = function(userInfo) {
+ if (!(pad.chatUserNums && pad.users)) {
+ return;
+ }
+ var userNum = pad.chatUserNums[userInfo.userId];
+ var bgcolor = '#fff';
+ if (pad.users[userInfo.userId]) {
+ // only color the chat if user is on the pad.
+ bgcolor = clientVars.colorPalette[userInfo.colorId];
+ }
+ var name = pad.escapeHtml(pad.getUserDisplayName(userInfo));
+ $('.chatusermessage'+userNum).css('background-color', bgcolor);
+ $('.chatusername'+userNum).html(name);
+};
+
+pad.receiveChat = function(msg, quick) {
+ var userInfo = pad.users[msg.userId];
+ if (!userInfo) { return; }
+
+ if (!pad.chatUserNums[userInfo.userId]) {
+ pad.lastChatUserNum++;
+ pad.chatUserNums[userInfo.userId] = pad.lastChatUserNum;
+ userNum = pad.lastChatUserNum;
+ }
+ var userNum = pad.chatUserNums[userInfo.userId];
+
+ pad.renderChatLine(userNum, '.', +(new Date), msg.lineText);
+ pad.updateChatHistoryForUser(userInfo);
+ pad.notifyNewChatMessage(userInfo, msg);
+};
+
+pad.renderChatLine = function(userNum, name, t, lineText) {
+ function chatTime() {
+ var x = '';
+ var d = new Date(t);
+ x += (d.getHours() < 10) ? ('0'+d.getHours()) : d.getHours();
+ x += ':';
+ x += (d.getMinutes() < 10) ? ('0'+d.getMinutes()) : d.getMinutes();
+ return x;
+ }
+ var displayName;
+ if (!name) {
+ name = "unnamed";
+ }
+ $('#chatmessages').append([
+ '<div class="chatmessage chatusermessage'+userNum+'">',
+ '<span class="chatname chatusername'+userNum+'">'+pad.escapeHtml(name)+'</span>',
+ '<span class="chattime">&nbsp;(', chatTime(t), ')</span>:&nbsp;',
+ '<span class="chatline">', pad.escapeHtml(lineText), '</span>',
+ '</div>'
+ ].join(''));
+};
+
+pad.initChatHistory = function() {
+ var chatHistory = clientVars.chatHistory;
+ if (!chatHistory) { return; }
+ for (var i = 0; i < chatHistory.length; i++) {
+ var l = chatHistory[i];
+ pad.renderChatLine(0, l.name, l.time, l.lineText);
+ }
+ pad.scrollChatBox();
+};
+
+pad.onShowChatBox = function() {
+ $('#chatheadname').html('Chat');
+ $('#headchatbox').removeClass('sh_hilited');
+ pad.numUnreadChatMessages = 0;
+ pad.scrollChatBox();
+};
+
+pad.scrollChatBox = function() {
+ var messageBox = $('#chatmessages').get(0);
+ messageBox.scrollTop = messageBox.scrollHeight;
+};
+
+pad.notifyNewChatMessage = function(userInfo, msg) {
+ if (userInfo.userId != pad.myUserInfo.userId) {
+ var name = pad.escapeHtml(pad.getUserDisplayName(userInfo));
+ var text;
+ if (msg.lineText.length <= 40) {
+ text = msg.lineText;
+ } else {
+ text = (msg.lineText.substr(0, 37) + '...');
+ }
+ text = pad.escapeHtml(text);
+ pad.notify("<i>"+name+" says</i>: "+text);
+ }
+ if ($('#headchatbox').hasClass('sh_collapsed')) {
+ pad.numUnreadChatMessages++;
+ var pluralS = (pad.numUnreadChatMessages > 1 ? 's' : '');
+ $('#chatheadname').html('Chat <b>('+pad.numUnreadChatMessages+' unread message'+pluralS+')</b>');
+ if (!$('#headchatbox').hasClass('sh_hilited')) {
+ $('#headchatbox').addClass('sh_hilited');
+ }
+ }
+ pad.scrollChatBox();
+};
+
+//----------------------------------------------------------------
+// Connection trouble
+//----------------------------------------------------------------
+
+pad.onConnectionTrouble = function(troubleStatus) {
+ if (troubleStatus == "OK") {
+ $('#savenow').removeAttr('disabled');
+ pad.displayConnectionStatus('connected');
+ if (pad.experiencingConnectionTrouble) {
+ pad.notify("Connection re-established!");
+ pad.experiencingConnectionTrouble = false;
+ }
+ } else if (troubleStatus == "SLOW") {
+ $('#savenow').attr('disabled', true);
+ pad.displayConnectionStatus('connecting');
+ if (!pad.experiencingConnectionTrouble) {
+ pad.experiencingConnectionTrouble = true;
+ pad.notify("The network seems slow. Waiting to hear back from the server...", true);
+ }
+ }
+};
+
+//----------------------------------------------------------------
+// Connection Status Display
+//----------------------------------------------------------------
+
+pad.displayConnectionStatus = function(status) {
+ var cs = $('#connectionstatus');
+ cs.attr('class', status);
+ if (status == "connecting") { cs.html("Connecting..."); }
+ else if (status == "connected") { cs.html("Connected"); }
+ else if (status == "disconnected") { cs.html("Disconnected"); }
+ else { cs.html(status+'?'); }
+};
+
+//----------------------------------------------------------------
+
+pad.asyncSendDiagnosticInfo = function() {
+ pad.diagnosticInfo.collabDiagnosticInfo = pad.collabClient.getDiagnosticInfo();
+ setTimeout(function() {
+ $.ajax({
+ type: 'post',
+ url: '/ep/pad/connection-diagnostic-info',
+ data: {padId: pad.padId, diagnosticInfo: JSON.stringify(pad.diagnosticInfo)},
+ success: function() {},
+ error: function() {}
+ });
+ });
+};
+
+//----------------------------------------------------------------
+// Modal Dialogs
+//----------------------------------------------------------------
+
+pad.repositionModals = function() {
+ var overlayNode = $("#modaloverlay");
+ var pageHeight = pad.getCurrentPageHeight();
+ overlayNode.height(pageHeight);
+
+ var dialogNode = $("#modaldialog");
+ var dialogHeight = dialogNode.outerHeight();
+ dialogNode.css('top', Math.round((pageHeight - dialogHeight)/2)+"px");
+}
+
+//----------------------------------------------------------------
+// Import/Export
+//----------------------------------------------------------------
+
+pad.importClearTimeout = function() {
+ if (pad.currentImport) {
+ clearTimeout(pad.currentImport);
+ delete pad.currentImport;
+ }
+}
+
+pad.importApplicationSuccessful = function(data, textStatus) {
+ if (data.substr(0, 2) == "ok") {
+ if ($('.importmessage').is(':visible')) {
+ $('.importmessage').hide();
+ }
+ $('#importmessagesuccess').html('<strong style="color: green">Import successful!</strong>').show();
+ $('#importformfilediv').hide();
+ setTimeout(function() {
+ $('#importmessagesuccess').fadeOut("slow", function() { $('#importformfilediv').show() });
+ }, 4000);
+ } else if (data.substr(0, 4) == "fail") {
+ pad.importErrorMessage("Couldn't update pad contents. Do you have \"cookies\" enabled in your web browser?");
+ } else if (data.substr(0, 4) == "msg:") {
+ pad.importErrorMessage(data.substr(4));
+ }
+ pad.importDone();
+}
+
+pad.importSuccessful = function(token) {
+ $.ajax({
+ type: 'post',
+ url: '/ep/pad/impexp/import2',
+ data: {token: token, padId: pad.padId},
+ success: pad.importApplicationSuccessful,
+ error: pad.importApplicationFailed,
+ timeout: 25000 // 20 second timeout.
+ });
+ pad.addImportFrames();
+}
+
+pad.importApplicationFailed = function(xhr, textStatus, errorThrown) {
+ pad.importErrorMessage("Error during conversion.");
+ pad.importDone();
+}
+
+pad.importFailed = function(msg) {
+ pad.importErrorMessage(msg);
+ pad.importDone();
+ pad.addImportFrames();
+}
+
+pad.importErrorMessage = function(msg) {
+ function showError(fade) {
+ $('#importmessagefail').html('<strong style="color: red">Import failed:</strong> '+
+ (msg || 'Please try a different file.'))[(fade?"fadeIn":"show")]();
+ }
+
+ if ($('.importmessage').is(':visible')) {
+ $('#importmessagesuccess').fadeOut("fast");
+ $('#importmessagefail').fadeOut("fast", function() { showError(true) });
+ } else {
+ showError();
+ }
+}
+
+pad.fileInputUpdated = function() {
+ $('#importformfilediv').addClass('importformenabled');
+ $('#importsubmitinput').removeAttr('disabled');
+ $('#importmessagefail').fadeOut("fast");
+ $('#importarrow').show();
+ $('#importarrow').animate({paddingLeft:"0px"}, 500)
+ .animate({paddingLeft:"10px"}, 150, 'swing')
+ .animate({paddingLeft:"0px"}, 150, 'swing')
+ .animate({paddingLeft:"10px"}, 150, 'swing')
+ .animate({paddingLeft:"0px"}, 150, 'swing')
+ .animate({paddingLeft:"10px"}, 150, 'swing')
+ .animate({paddingLeft:"0px"}, 150, 'swing');
+}
+
+pad.importDone = function() {
+ $('#importsubmitinput').removeAttr('disabled').val("Import Now");
+ setTimeout(function() { $('#importfileinput').removeAttr('disabled'); }, 0);
+ $('#importstatusball').hide();
+ pad.importClearTimeout();
+}
+
+pad.fileInputSubmit = function() {
+ $('#importmessagefail').fadeOut("fast");
+ var ret = confirm("Importing a file will overwrite your existing document,"+
+ " and cannot be undone. If you want to keep your current"+
+ " document, download it or save a revision.\n\n"+
+ "Are you sure you want to proceed?");
+ if (ret) {
+ pad.currentImport = setTimeout(function() {
+ if (! pad.currentImport) {
+ return;
+ }
+ delete pad.currentImport;
+ pad.importFailed("Request timed out.");
+ }, 25000); // 20 second timeout.
+ $('#importsubmitinput').attr({disabled: true}).val("Importing...");
+ setTimeout(function() { $('#importfileinput').attr({disabled: true}) }, 0);
+ $('#importarrow').stop(true, true).hide();
+ $('#importstatusball').show();
+ }
+ return ret;
+}
+
+pad.cantExport = function() {
+ var type = $(this);
+ if (type.hasClass("exporthrefpdf")) {
+ type = "PDF";
+ } else if (type.hasClass("exporthrefdoc")) {
+ type = "Microsoft Word";
+ } else if (type.hasClass("exporthrefodt")) {
+ type = "OpenDocument";
+ } else {
+ type = "this file";
+ }
+ alert("Exporting as "+type+" format is disabled. Please contact your"+
+ " system administrator for details.");
+ return false;
+}
+
+// pad.exportDoNothingClickHandler = function() { return false; }
+// pad.exportDoLongClick = function() {
+// if ($(this).hasClass('exporthrefdoc')) {
+// pad.exportStartLong('doc');
+// } else if ($(this).hasClass('exporthrefpdf')) {
+// pad.exportStartLong('pdf');
+// } else {
+// return false;
+// }
+// setTimeout(pad.exportDisableOffice, 0);
+// return true;
+// }
+//
+// pad.exportDisableOffice = function() {
+// $('.requiresoffice').unbind('click', pad.exportDoLongClick);
+// $('.requiresoffice').click(pad.exportDoNothingClickHandler);
+// $('.requiresoffice').addClass('disabledexport');
+// }
+//
+// pad.exportEnableOffice = function() {
+// $('.exportspinner').hide();
+// $('.requiresoffice').removeClass('disabledexport');
+// $('.requiresoffice').unbind('click', pad.exportDoNothingClickHandler);
+// $('.requiresoffice').click(pad.exportDoLongClick);
+// }
+//
+// pad.exportMessage = function(msg, head) {
+// function showMessage(fade) {
+// head = head || '<strong style="color: red">Export failed:</strong> ';
+// msg = msg || 'Please try a different file type or try again later.';
+// $('#exportmessage').html(head+msg)[(fade?"fadeIn":"show")]();
+// }
+// if ($('#exportmessage').is(":visible")) {
+// $('#exportmessage').fadeOut("fast", function() { showMessage(true) });
+// } else {
+// showMessage();
+// }
+// }
+//
+// pad.exportDone = function(message) {
+// if (message == "ok") {
+// pad.exportMessage("downloading...", '<strong style="color: green">Conversion successful;</strong> ');
+// setTimeout(function() {
+// $('#exportmessage').fadeOut();
+// }, 4000);
+// } else if (message == "timeout") {
+// pad.exportMessage("Request timed out; please try a different file type or try again later.");
+// } else if (message == "nocookie") {
+// pad.exportMessage("Conversion in progress, please wait...", " ");
+// setTimeout(function() {
+// pad.exportDone("nocookie-delay");
+// }, 25000);
+// } else if (message == "nocookie-delay") {
+// pad.exportMessage("If your download hasn't started yet, then an error occurred; please try a different file type or try again later.", " ");
+// setTimeout(function() {
+// $('#exportmessage').fadeOut();
+// }, 10000);
+// } else {
+// pad.exportMessage("Error converting; please try a different file type or try again later.");
+// }
+// pad.exportEnableOffice();
+// if (! $.browser.opera) {
+// pad.addExportFrames();
+// }
+// }
+//
+// pad.exportHideMessages = function() {
+// $('#exportmessage').fadeOut();
+// }
+//
+// pad.exportStartLong = function(format) {
+// $('#exportspinner'+format).show();
+// $.ajax({
+// type: 'post',
+// url: '/ep/pad/impexp/exportsync',
+// data: { waiter: pad.exportSetCookie() },
+// success: function(data) { pad.exportDone(data); },
+// error: function(xhr, textStatus) { pad.exportDone(textStatus); },
+// timeout: 25000
+// });
+// }
+//
+// pad.exportSetCookie = function() {
+// var cookieValue = Math.round(Math.random()*1e20);
+// document.cookie = "EPexport="+cookieValue;
+//
+// if (!(document.cookie &&
+// (document.cookie.length > 0) &&
+// (document.cookie.indexOf('EPexport') >= 0))) {
+// return false;
+// }
+// return cookieValue;
+// }
+//
+// pad.addExportFrames = function() {
+// $('.exportframe').remove();
+// $('#importexport').append(
+// $('<iframe style="display: none;" name="exportofficeiframe" class="exportframe"></iframe>'));
+// $('#importexport').append(
+// $('<iframe style="display: none;" name="exportiframe" class="exportframe"></iframe>'));
+// }
+
+pad.addImportFrames = function() {
+ $('.importframe').remove();
+ $('#importexport').append(
+ $('<iframe style="display: none;" name="importiframe" class="importframe"></iframe>'));
+}
+
+//----------------------------------------------------------------
+// Rich text
+//----------------------------------------------------------------
+
+pad.isTopToolbarEnabled = function() {
+ return ! $("#toptoolbar").hasClass('disabledtoolbar');
+}
+
+pad.isBotToolbarEnabled = function() {
+ return ! $("#bottoolbar").hasClass('disabledtoolbar');
+}
+
+pad.toolbarClick = function(which) {
+ if (pad.isTopToolbarEnabled()) {
+ pad.ace.execCommand(which);
+ }
+ pad.ace.focus();
+}
+
+//----------------------------------------------------------------
+// View bar
+//----------------------------------------------------------------
+
+pad.setViewZoom = function(percent) {
+ if (! (percent >= 50 && percent <= 1000)) {
+ // percent is out of sane range or NaN (which fails comparisons)
+ return;
+ }
+
+ pad.viewZoom = percent;
+ $("#viewzoommenu").val('z'+percent);
+ pad.updateAceFontAndSize();
+ pad.saveCookie(); // does nothing if we were called from init > initViewBar
+}
+
+pad.setViewFont = function(font) {
+ pad.viewFont = font;
+ $("#viewfontmenu").val(font);
+ pad.updateAceFontAndSize();
+ pad.saveCookie();
+}
+
+pad.updateAceFontAndSize = function() {
+ var baseSize = 12;
+ pad.ace.setProperty('textsize', Math.round(baseSize * pad.viewZoom / 100));
+
+ var face = "sans-serif";
+ if (pad.viewFont == 'code') {
+ face = "monospace";
+ }
+ pad.ace.setProperty('textface', face);
+}
+
+//----------------------------------------------------------------
+// Pad title
+//----------------------------------------------------------------
+
+pad.renderPadTitle = function() {
+ if (pad.title) {
+ var theTitle = (pad.isEditingTitle ? "-" : pad.title);
+
+ $("#padtitle").css('display', 'block');
+
+ var padtitleWidth = $("#toptoolbar").width() - $("#toptoolbar .toptoolbarbutton:last").position().left - 80;
+ $("#padtitle").css('width', padtitleWidth+"px");
+
+ var shownLength = theTitle.length;
+ $("#padtitletitle").html(pad.escapeHtml(theTitle));
+
+ // while it wraps, shorten the displayed title
+ while ($("#padtitle .editlink").offset().top > $("#padtitle .padtitlepad").offset().top +
+ $("#padtitle .padtitlepad").height()) {
+ shownLength--;
+ $("#padtitletitle").html(pad.escapeHtml(theTitle.substring(0, shownLength)+"..."));
+ }
+
+ var titlePos = $("#padtitletitle").position();
+ var titleHeight = $("#padtitletitle").height();
+ var inputBox = $("#padtitleedit");
+ var inputWidth = $("#toptoolbar").width() - titlePos.left - 70;
+ inputBox.css({left: titlePos.left, top: titlePos.top, height: titleHeight,
+ width: inputWidth});
+ $("#toptoolbar .oklink").css({ top: 5, left: titlePos.left + inputWidth + 15 });
+ }
+ else {
+ $("#padtitle").css('display', 'none');
+ }
+
+ if (clientVars.isProPad) {
+ if (pad.password) {
+ $("#passwordlock").attr('title','Change password...').get(0).className = 'passwordlocked';
+ }
+ else {
+ $("#passwordlock").attr('title','Password protect...').get(0).className = 'passwordnone';
+ }
+ }
+};
+
+pad.editTitle = function() {
+ $("#padtitleedit").val(pad.title).show().focus().select();
+ $("#padtitle .oklink").show();
+ $("#padtitle .editlink").hide();
+
+ pad.isEditingTitle = true;
+ pad.renderPadTitle();
+}
+
+pad.submitTitle = function(acceptChanges) {
+ if (acceptChanges) {
+ var newTitle = $("#padtitleedit").val();
+ if (newTitle) {
+ newTitle = newTitle.substring(0, 80);
+ pad.title = newTitle;
+
+ pad.collabClient.sendClientMessage({
+ type: 'padtitle',
+ title: newTitle,
+ changedBy: pad.myUserInfo.name || "unnamed"
+ });
+ }
+ }
+
+ $("#padtitleedit").hide();
+ $("#padtitle .oklink").hide();
+ $("#padtitle .editlink").show();
+
+ pad.isEditingTitle = false;
+ pad.renderPadTitle();
+};
+
+pad.passwordClick = function() {
+ var oldPassword = pad.password;
+
+ var msg = (oldPassword ? "Enter a new password for this pad, or "+
+ "make blank to remove password protection:" :
+ "Choose a password that will be required to view this pad:");
+
+ var result = window.prompt(msg, oldPassword || '');
+ if ((typeof result) == "string") {
+ pad.password = (result || null); // empty string --> null
+ if (pad.password !== oldPassword) {
+ pad.collabClient.sendClientMessage({
+ type: 'padpassword',
+ password: pad.password,
+ changedBy: pad.myUserInfo.name || "unnamed"
+ });
+ pad.renderPadTitle();
+ }
+ }
+ else {
+ // user canceled
+ }
+}; \ No newline at end of file
diff --git a/trunk/etherpad/src/static/js/pad2.js b/trunk/etherpad/src/static/js/pad2.js
new file mode 100644
index 0000000..14ac762
--- /dev/null
+++ b/trunk/etherpad/src/static/js/pad2.js
@@ -0,0 +1,591 @@
+/**
+ * Copyright 2009 Google 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.
+ */
+
+/* global $, window */
+
+$(document).ready(function() {
+ pad.init();
+});
+
+$(window).unload(function() {
+ pad.dispose();
+});
+
+var pad = {
+ // don't access these directly from outside this file, except
+ // for debugging
+ collabClient: null,
+ myUserInfo: null,
+ diagnosticInfo: {},
+ initTime: 0,
+ clientTimeOffset: (+new Date()) - clientVars.serverTimestamp,
+ preloadedImages: false,
+ padOptions: {},
+ resizeInited: false,
+
+ // these don't require init; clientVars should all go through here
+ getPadId: function() { return clientVars.padId; },
+ getClientIp: function() { return clientVars.clientIp; },
+ getIsProPad: function() { return clientVars.isProPad; },
+ getColorPalette: function() { return clientVars.colorPalette; },
+ getDisplayUserAgent: function() {
+ return padutils.uaDisplay(clientVars.userAgent);
+ },
+ getIsDebugEnabled: function() { return clientVars.debugEnabled; },
+ getPrivilege: function(name) { return clientVars.accountPrivs[name]; },
+ getUserIsGuest: function() { return clientVars.userIsGuest; },
+ //
+
+ getUserId: function() { return pad.myUserInfo.userId; },
+ getUserName: function() { return pad.myUserInfo.name; },
+ sendClientMessage: function(msg) {
+ pad.collabClient.sendClientMessage(msg);
+ },
+
+ initResize: function() {
+ $(window).bind("resize", pad.resizePage);
+ pad.resizeInited = true;
+ pad.resizePage();
+ // just in case, periodically check size:
+ setInterval(function() { pad.resizePage(); }, 2000);
+ },
+ init: function() {
+ pad.diagnosticInfo.uniqueId = padutils.uniqueId();
+ pad.initTime = +(new Date());
+ pad.padOptions = clientVars.initialOptions;
+
+ if ((! $.browser.msie) &&
+ (! ($.browser.mozilla && $.browser.version.indexOf("1.8.") == 0))) {
+ document.domain = document.domain; // for comet
+ }
+
+ // for IE
+ if ($.browser.msie) {
+ try {
+ doc.execCommand("BackgroundImageCache", false, true);
+ } catch (e) {}
+ }
+
+ // order of inits is important here:
+
+ padcookie.init(clientVars.cookiePrefsToSet);
+
+ $("#widthprefcheck").click(pad.toggleWidthPref);
+ $("#sidebarcheck").click(pad.toggleSidebar);
+
+ pad.myUserInfo = {
+ userId: clientVars.userId,
+ name: clientVars.userName,
+ ip: pad.getClientIp(),
+ colorId: clientVars.userColor,
+ userAgent: pad.getDisplayUserAgent()
+ };
+ if (clientVars.specialKey) {
+ pad.myUserInfo.specialKey = clientVars.specialKey;
+ if (clientVars.specialKeyTranslation) {
+ $("#specialkeyarea").html("mode: "+
+ String(clientVars.specialKeyTranslation).toUpperCase());
+ }
+ }
+ paddocbar.init({isTitleEditable: pad.getIsProPad(),
+ initialTitle:clientVars.initialTitle,
+ initialPassword:clientVars.initialPassword,
+ guestPolicy: pad.padOptions.guestPolicy
+ });
+ padimpexp.init();
+ padsavedrevs.init(clientVars.initialRevisionList);
+
+ padeditor.init(postAceInit, pad.padOptions.view || {});
+ sidebarSplit.init();
+ pad.initResize();
+
+ paduserlist.init(pad.myUserInfo);
+ padchat.init(clientVars.chatHistory, pad.myUserInfo);
+ padconnectionstatus.init();
+ padmodals.init();
+
+ pad.collabClient =
+ getCollabClient(padeditor.ace,
+ clientVars.collab_client_vars,
+ pad.myUserInfo,
+ { colorPalette: pad.getColorPalette() });
+ pad.collabClient.setOnUserJoin(pad.handleUserJoin);
+ pad.collabClient.setOnUpdateUserInfo(pad.handleUserUpdate);
+ pad.collabClient.setOnUserLeave(pad.handleUserLeave);
+ pad.collabClient.setOnClientMessage(pad.handleClientMessage);
+ pad.collabClient.setOnServerMessage(pad.handleServerMessage);
+ pad.collabClient.setOnChannelStateChange(pad.handleChannelStateChange);
+ pad.collabClient.setOnInternalAction(pad.handleCollabAction);
+
+ function postAceInit() {
+ padeditbar.init();
+ setTimeout(function() { padeditor.ace.focus(); }, 0);
+ }
+
+ pad.resizePage();
+ },
+ dispose: function() {
+ padeditor.dispose();
+ },
+ resizePage: function() {
+ if (! pad.resizeInited) {
+ return;
+ }
+ // requires padeditor and sidebarSplit
+ var pageHeight = $(window).height();
+ if ($("#djs").length > 0) {
+ pageHeight -= $("#djs").outerHeight();
+ }
+ var MIN_PAGE_HEIGHT = 400;
+ if (pageHeight < MIN_PAGE_HEIGHT) {
+ pageHeight = MIN_PAGE_HEIGHT;
+ }
+ var bottomAreaHeight = 28;
+ padeditor.setBottom(pageHeight - bottomAreaHeight);
+ sidebarSplit.setBottom(pageHeight - bottomAreaHeight);
+ paddocbar.handleResizePage();
+ padmodals.relayoutWithBottom(pageHeight);
+ pad.handleWidthChange();
+ },
+ notifyChangeName: function(newName) {
+ pad.myUserInfo.name = newName;
+ pad.collabClient.updateUserInfo(pad.myUserInfo);
+ padchat.handleUserJoinOrUpdate(pad.myUserInfo);
+ },
+ notifyChangeColor: function(newColorId) {
+ pad.myUserInfo.colorId = newColorId;
+ pad.collabClient.updateUserInfo(pad.myUserInfo);
+ padchat.handleUserJoinOrUpdate(pad.myUserInfo);
+ },
+ notifyChangeTitle: function(newTitle) {
+ pad.collabClient.sendClientMessage({
+ type: 'padtitle',
+ title: newTitle,
+ changedBy: pad.myUserInfo.name || "unnamed"
+ });
+ },
+ notifyChangePassword: function(newPass) {
+ pad.collabClient.sendClientMessage({
+ type: 'padpassword',
+ password: newPass,
+ changedBy: pad.myUserInfo.name || "unnamed"
+ });
+ },
+ changePadOption: function(key, value) {
+ var options = {};
+ options[key] = value;
+ pad.handleOptionsChange(options);
+ pad.collabClient.sendClientMessage({
+ type: 'padoptions',
+ options: options,
+ changedBy: pad.myUserInfo.name || "unnamed"
+ });
+ },
+ changeViewOption: function(key, value) {
+ var options = {view: {}};
+ options.view[key] = value;
+ pad.handleOptionsChange(options);
+ pad.collabClient.sendClientMessage({
+ type: 'padoptions',
+ options: options,
+ changedBy: pad.myUserInfo.name || "unnamed"
+ });
+ },
+ handleOptionsChange: function(opts) {
+ // opts object is a full set of options or just
+ // some options to change
+ if (opts.view) {
+ if (! pad.padOptions.view) {
+ pad.padOptions.view = {};
+ }
+ for(var k in opts.view) {
+ pad.padOptions.view[k] = opts.view[k];
+ }
+ padeditor.setViewOptions(pad.padOptions.view);
+ }
+ if (opts.guestPolicy) {
+ // order important here
+ pad.padOptions.guestPolicy = opts.guestPolicy;
+ paddocbar.setGuestPolicy(opts.guestPolicy);
+ }
+ },
+ getPadOptions: function() {
+ // caller shouldn't mutate the object
+ return pad.padOptions;
+ },
+ isPadPublic: function() {
+ return (! pad.getIsProPad()) || (pad.getPadOptions().guestPolicy == 'allow');
+ },
+ suggestUserName: function(userId, name) {
+ pad.collabClient.sendClientMessage({
+ type: 'suggestUserName',
+ unnamedId: userId,
+ newName: name
+ });
+ },
+ handleUserJoin: function(userInfo) {
+ paduserlist.userJoinOrUpdate(userInfo);
+ padchat.handleUserJoinOrUpdate(userInfo);
+ },
+ handleUserUpdate: function(userInfo) {
+ paduserlist.userJoinOrUpdate(userInfo);
+ padchat.handleUserJoinOrUpdate(userInfo);
+ },
+ handleUserLeave: function(userInfo) {
+ paduserlist.userLeave(userInfo);
+ padchat.handleUserLeave(userInfo);
+ },
+ handleClientMessage: function(msg) {
+ if (msg.type == 'suggestUserName') {
+ if (msg.unnamedId == pad.myUserInfo.userId && msg.newName &&
+ ! pad.myUserInfo.name) {
+ pad.notifyChangeName(msg.newName);
+ paduserlist.setMyUserInfo(pad.myUserInfo);
+ }
+ }
+ else if (msg.type == 'chat') {
+ padchat.receiveChat(msg);
+ }
+ else if (msg.type == 'padtitle') {
+ paddocbar.changeTitle(msg.title);
+ }
+ else if (msg.type == 'padpassword') {
+ paddocbar.changePassword(msg.password);
+ }
+ else if (msg.type == 'newRevisionList') {
+ padsavedrevs.newRevisionList(msg.revisionList);
+ }
+ else if (msg.type == 'revisionLabel') {
+ padsavedrevs.newRevisionList(msg.revisionList);
+ }
+ else if (msg.type == 'padoptions') {
+ var opts = msg.options;
+ pad.handleOptionsChange(opts);
+ }
+ else if (msg.type == 'guestanswer') {
+ // someone answered a prompt, remove it
+ paduserlist.removeGuestPrompt(msg.guestId);
+ }
+ },
+ editbarClick: function(cmd) {
+ if (padeditbar) {
+ padeditbar.toolbarClick(cmd);
+ }
+ },
+ dmesg: function(m) {
+ if (pad.getIsDebugEnabled()) {
+ var djs = $('#djs').get(0);
+ var wasAtBottom = (djs.scrollTop - (djs.scrollHeight - $(djs).height())
+ >= -20);
+ $('#djs').append('<p>'+m+'</p>');
+ if (wasAtBottom) {
+ djs.scrollTop = djs.scrollHeight;
+ }
+ }
+ },
+ handleServerMessage: function(m) {
+ if (m.type == 'NOTICE') {
+ if (m.text) {
+ alertBar.displayMessage(function (abar) {
+ abar.find("#servermsgdate").html(" ("+padutils.simpleDateTime(new Date)+")");
+ abar.find("#servermsgtext").html(m.text);
+ });
+ }
+ if (m.js) {
+ window['ev'+'al'](m.js);
+ }
+ }
+ else if (m.type == 'GUEST_PROMPT') {
+ paduserlist.showGuestPrompt(m.userId, m.displayName);
+ }
+ },
+ handleChannelStateChange: function(newState, message) {
+ var oldFullyConnected = !! padconnectionstatus.isFullyConnected();
+ var wasConnecting = (padconnectionstatus.getStatus().what == 'connecting');
+ if (newState == "CONNECTED") {
+ padconnectionstatus.connected();
+ }
+ else if (newState == "RECONNECTING") {
+ padconnectionstatus.reconnecting();
+ }
+ else if (newState == "DISCONNECTED") {
+ pad.diagnosticInfo.disconnectedMessage = message;
+ pad.diagnosticInfo.padInitTime = pad.initTime;
+ pad.asyncSendDiagnosticInfo();
+ if (typeof window.ajlog == "string") { window.ajlog += ("Disconnected: "+message+'\n'); }
+ padeditor.disable();
+ padeditbar.disable();
+ paddocbar.disable();
+ padimpexp.disable();
+
+ padconnectionstatus.disconnected(message);
+ }
+ var newFullyConnected = !! padconnectionstatus.isFullyConnected();
+ if (newFullyConnected != oldFullyConnected) {
+ pad.handleIsFullyConnected(newFullyConnected, wasConnecting);
+ }
+ },
+ handleIsFullyConnected: function(isConnected, isInitialConnect) {
+ // load all images referenced from CSS, one at a time,
+ // starting one second after connection is first established.
+ if (isConnected && ! pad.preloadedImages) {
+ window.setTimeout(function() {
+ if (! pad.preloadedImages) {
+ pad.preloadImages();
+ pad.preloadedImages = true;
+ }
+ }, 1000);
+ }
+
+ padsavedrevs.handleIsFullyConnected(isConnected);
+
+ pad.determineSidebarVisibility(isConnected && ! isInitialConnect);
+ },
+ determineSidebarVisibility: function(asNowConnectedFeedback) {
+ if (pad.isFullyConnected()) {
+ var setSidebarVisibility =
+ padutils.getCancellableAction(
+ "set-sidebar-visibility",
+ function() {
+ $("body").toggleClass('hidesidebar',
+ !! padcookie.getPref('hideSidebar'));
+ });
+ window.setTimeout(setSidebarVisibility,
+ asNowConnectedFeedback ? 3000 : 0);
+ }
+ else {
+ padutils.cancelActions("set-sidebar-visibility");
+ $("body").removeClass('hidesidebar');
+ }
+ pad.resizePage();
+ },
+ handleCollabAction: function(action) {
+ if (action == "commitPerformed") {
+ padeditbar.setSyncStatus("syncing");
+ }
+ else if (action == "newlyIdle") {
+ padeditbar.setSyncStatus("done");
+ }
+ },
+ hideServerMessage: function() {
+ alertBar.hideMessage();
+ },
+ asyncSendDiagnosticInfo: function() {
+ pad.diagnosticInfo.collabDiagnosticInfo = pad.collabClient.getDiagnosticInfo();
+ window.setTimeout(function() {
+ $.ajax({
+ type: 'post',
+ url: '/ep/pad/connection-diagnostic-info',
+ data: {padId: pad.getPadId(), diagnosticInfo: JSON.stringify(pad.diagnosticInfo)},
+ success: function() {},
+ error: function() {}
+ });
+ }, 0);
+ },
+ forceReconnect: function() {
+ $('form#reconnectform input.padId').val(pad.getPadId());
+ pad.diagnosticInfo.collabDiagnosticInfo = pad.collabClient.getDiagnosticInfo();
+ $('form#reconnectform input.diagnosticInfo').val(JSON.stringify(pad.diagnosticInfo));
+ $('form#reconnectform input.missedChanges').val(JSON.stringify(pad.collabClient.getMissedChanges()));
+ $('form#reconnectform').submit();
+ },
+ toggleWidthPref: function() {
+ var newValue = ! padcookie.getPref('fullWidth');
+ padcookie.setPref('fullWidth', newValue);
+ $("#widthprefcheck").toggleClass('widthprefchecked', !!newValue).toggleClass(
+ 'widthprefunchecked', !newValue);
+ pad.handleWidthChange();
+ },
+ toggleSidebar: function() {
+ var newValue = ! padcookie.getPref('hideSidebar');
+ padcookie.setPref('hideSidebar', newValue);
+ $("#sidebarcheck").toggleClass('sidebarchecked', !newValue).toggleClass(
+ 'sidebarunchecked', !!newValue);
+ pad.determineSidebarVisibility();
+ },
+ handleWidthChange: function() {
+ var isFullWidth = padcookie.getPref('fullWidth');
+ if (isFullWidth) {
+ $("body").addClass('fullwidth').removeClass('limwidth').removeClass(
+ 'squish1width').removeClass('squish2width');
+ }
+ else {
+ $("body").addClass('limwidth').removeClass('fullwidth');
+
+ var pageWidth = $(window).width();
+ $("body").toggleClass('squish1width', (pageWidth < 912 && pageWidth > 812)).toggleClass(
+ 'squish2width', (pageWidth <= 812));
+ }
+ },
+ // this is called from code put into a frame from the server:
+ handleImportExportFrameCall: function(callName, varargs) {
+ padimpexp.handleFrameCall.call(padimpexp, callName,
+ Array.prototype.slice.call(arguments, 1));
+ },
+ callWhenNotCommitting: function(f) {
+ pad.collabClient.callWhenNotCommitting(f);
+ },
+ getCollabRevisionNumber: function() {
+ return pad.collabClient.getCurrentRevisionNumber();
+ },
+ isFullyConnected: function() {
+ return padconnectionstatus.isFullyConnected();
+ },
+ addHistoricalAuthors: function(data) {
+ if (! pad.collabClient) {
+ window.setTimeout(function() { pad.addHistoricalAuthors(data); },
+ 1000);
+ }
+ else {
+ pad.collabClient.addHistoricalAuthors(data);
+ }
+ },
+ preloadImages: function() {
+ var images = [
+ '/static/img/jun09/pad/feedbackbox2.gif',
+ '/static/img/jun09/pad/sharebox4.gif',
+ '/static/img/jun09/pad/sharedistri.gif',
+ '/static/img/jun09/pad/colorpicker.gif',
+ '/static/img/jun09/pad/docbarstates.png',
+ '/static/img/jun09/pad/overlay.png'
+ ];
+ function loadNextImage() {
+ if (images.length == 0) {
+ return;
+ }
+ var img = new Image();
+ img.src = images.shift();
+ if (img.complete) {
+ scheduleLoadNextImage();
+ }
+ else {
+ $(img).bind('error load onreadystatechange', scheduleLoadNextImage);
+ }
+ }
+ function scheduleLoadNextImage() {
+ window.setTimeout(loadNextImage, 0);
+ }
+ scheduleLoadNextImage();
+ }
+};
+
+var sidebarSplit = (function(){
+ var MIN_SIZED_BOX_HEIGHT = 75;
+
+ function relayout(heightDelta) {
+ heightDelta = (heightDelta || 0);
+
+ var sizedBox1 = $("#otherusers");
+ var sizedBox2 = $("#chatlines");
+ var height1 = sizedBox1.height();
+ var height2 = sizedBox2.height();
+ var newTotalHeight = height1 + height2 + heightDelta;
+ var newHeight1 = height1;
+ var newHeight2 = height2;
+
+ if (newTotalHeight >= MIN_SIZED_BOX_HEIGHT*2) {
+ // room for both panes to be at least min height
+ if (newTotalHeight >= self.desiredUsersBoxHeight + MIN_SIZED_BOX_HEIGHT) {
+ // room for users pane to be desiredUsersBoxHeight
+ newHeight1 = self.desiredUsersBoxHeight;
+ newHeight2 = newTotalHeight - newHeight1;
+ }
+ else {
+ newHeight2 = MIN_SIZED_BOX_HEIGHT;
+ newHeight1 = newTotalHeight - newHeight2;
+ }
+ }
+ else {
+ newHeight1 = Math.round(newTotalHeight/2);
+ newHeight2 = newTotalHeight - newHeight1;
+ }
+
+ sizedBox1.height(newHeight1);
+ sizedBox2.height(newHeight2);
+
+ $("#connectionbox").height(
+ $("#myuser").outerHeight() + $("#userlistbuttonarea").outerHeight() +
+ height1
+ );
+ }
+
+ var self = {
+ desiredUsersBoxHeight: MIN_SIZED_BOX_HEIGHT,
+ init: function() {
+ self.desiredUsersBoxHeight = Math.max(
+ $("#otherusers").height(), MIN_SIZED_BOX_HEIGHT);
+ makeDraggable($("#hdraggie"), function(eType, evt, state) {
+ if (eType == 'dragstart') {
+ state.startY = evt.pageY;
+ state.startHeight = $("#otherusers").height();
+ }
+ else if (eType == 'dragupdate') {
+ var newHeight = state.startHeight + (evt.pageY - state.startY);
+ if (newHeight < MIN_SIZED_BOX_HEIGHT) {
+ newHeight = MIN_SIZED_BOX_HEIGHT;
+ }
+ self.desiredUsersBoxHeight = newHeight;
+ relayout();
+ }
+ });
+ },
+ setBottom: function(bottomPx) {
+ var curBottom = $("#padsidebar").offset().top + $("#padsidebar").height();
+ var deltaBottom = bottomPx - curBottom;
+
+ if (deltaBottom != 0) {
+ relayout(deltaBottom);
+ }
+ }
+ };
+ return self;
+}());
+
+var alertBar = (function() {
+
+ var animator = padutils.makeShowHideAnimator(arriveAtAnimationState, false, 25, 400);
+
+ function arriveAtAnimationState(state) {
+ if (state == -1) {
+ $("#alertbar").css('opacity', 0).css('display', 'block');
+ pad.resizePage();
+ }
+ else if (state == 0) {
+ $("#alertbar").css('opacity', 1);
+ }
+ else if (state == 1) {
+ $("#alertbar").css('opacity', 0).css('display', 'none');
+ pad.resizePage();
+ }
+ else if (state < 0) {
+ $("#alertbar").css('opacity', state+1);
+ }
+ else if (state > 0) {
+ $("#alertbar").css('opacity', 1 - state);
+ }
+ }
+
+ var self = {
+ displayMessage: function(setupFunc) {
+ animator.show();
+ setupFunc($("#alertbar"));
+ },
+ hideMessage: function() {
+ animator.hide();
+ }
+ };
+ return self;
+}());
diff --git a/trunk/etherpad/src/static/js/pad_chat.js b/trunk/etherpad/src/static/js/pad_chat.js
new file mode 100644
index 0000000..35903c2
--- /dev/null
+++ b/trunk/etherpad/src/static/js/pad_chat.js
@@ -0,0 +1,295 @@
+/**
+ * Copyright 2009 Google 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.
+ */
+
+
+var padchat = (function(){
+
+ var numToAuthorMap = [''];
+ var authorColorArray = [null];
+ var authorToNumMap = {};
+ var chatLinesByDay = []; // {day:'2009-06-17', lines: [...]}
+ var oldestHistoricalLine = 0;
+
+ var loadingMoreHistory = false;
+ var HISTORY_LINES_TO_LOAD_AT_A_TIME = 50;
+
+ function authorToNum(author, dontAddIfAbsent) {
+ if ((typeof authorToNumMap[author]) == "number") {
+ return authorToNumMap[author];
+ }
+ else if (dontAddIfAbsent) {
+ return -1;
+ }
+ else {
+ var n = numToAuthorMap.length;
+ numToAuthorMap.push(author);
+ authorToNumMap[author] = n;
+ return n;
+ }
+ }
+ function getDateNumCSSDayString(dateNum) {
+ var d = new Date(+dateNum);
+ var year = String(d.getFullYear());
+ var month = ("0"+String(d.getMonth()+1)).slice(-2);
+ var day = ("0"+String(d.getDate())).slice(-2);
+ return year+"-"+month+"-"+day;
+ }
+ function getDateNumHumanDayString(dateNum) {
+ var d = new Date(+dateNum);
+ var monthName = (["January", "February", "March",
+ "April", "May", "June", "July", "August", "September",
+ "October", "November", "December"])[d.getMonth()];
+ var dayOfMonth = d.getDate();
+ var year = d.getFullYear();
+ return monthName+" "+dayOfMonth+", "+year;
+ }
+ function ensureChatDay(time) {
+ var day = getDateNumCSSDayString(time);
+ var dayIndex = padutils.binarySearch(chatLinesByDay.length, function(n) {
+ return chatLinesByDay[n].day >= day;
+ });
+ if (dayIndex >= chatLinesByDay.length ||
+ chatLinesByDay[dayIndex].day != day) {
+ // add new day to chat display!
+
+ chatLinesByDay.splice(dayIndex, 0, {day: day, lines: []});
+ var dayHtml = '<div class="chatday" id="chatday'+day+'">'+
+ '<h2 class="dayheader">'+getDateNumHumanDayString(time)+
+ '</h2></div>';
+ var dayDivs = $("#chatlines .chatday");
+ if (dayIndex == dayDivs.length) {
+ $("#chatlines").append(dayHtml);
+ }
+ else {
+ dayDivs.eq(dayIndex).before(dayHtml);
+ }
+ }
+
+ return dayIndex;
+ }
+ function addChatLine(userId, time, name, lineText, addBefore) {
+ var dayIndex = ensureChatDay(time);
+ var dayDiv = $("#chatday"+getDateNumCSSDayString(time));
+ var d = new Date(+time);
+ var hourmin = d.getHours()+":"+("0"+d.getMinutes()).slice(-2);
+ var nameHtml;
+ if (name) {
+ nameHtml = padutils.escapeHtml(name);
+ }
+ else {
+ nameHtml = "<i>unnamed</i>";
+ }
+ var chatlineClass = "chatline";
+ if (userId) {
+ var authorNum = authorToNum(userId);
+ chatlineClass += " chatauthor"+authorNum;
+ }
+ var textHtml = padutils.escapeHtmlWithClickableLinks(lineText, '_blank');
+ var lineNode = $('<div class="'+chatlineClass+'">'+
+ '<span class="chatlinetime">'+hourmin+' </span>'+
+ '<span class="chatlinename">'+nameHtml+': </span>'+
+ '<span class="chatlinetext">'+textHtml+'</span></div>');
+ var linesArray = chatLinesByDay[dayIndex].lines;
+ var lineObj = {userId:userId, time:time, name:name, lineText:lineText};
+ if (addBefore) {
+ dayDiv.find("h2").after(lineNode);
+ linesArray.splice(0, 0, lineObj);
+ }
+ else {
+ dayDiv.append(lineNode);
+ linesArray.push(lineObj);
+ }
+ if (userId) {
+ var color = getAuthorCSSColor(userId);
+ if (color) {
+ lineNode.css('background', color);
+ }
+ }
+
+ return {lineNode:lineNode};
+ }
+ function receiveChatHistoryBlock(block) {
+ for(var a in block.historicalAuthorData) {
+ var data = block.historicalAuthorData[a];
+ var n = authorToNum(a);
+ if (! authorColorArray[n]) {
+ // no data about this author, use historical info
+ authorColorArray[n] = { colorId: data.colorId, faded: true };
+ }
+ }
+
+ oldestHistoricalLine = block.start;
+
+ var lines = block.lines;
+ for(var i=lines.length-1; i>=0; i--) {
+ var line = lines[i];
+ addChatLine(line.userId, line.time, line.name, line.lineText, true);
+ }
+
+ if (oldestHistoricalLine > 0) {
+ $("a#chatloadmore").css('display', 'block');
+ }
+ else {
+ $("a#chatloadmore").css('display', 'none');
+ }
+ }
+ function fadeColor(colorCSS) {
+ var color = colorutils.css2triple(colorCSS);
+ color = colorutils.blend(color, [1,1,1], 0.5);
+ return colorutils.triple2css(color);
+ }
+ function getAuthorCSSColor(author) {
+ var n = authorToNum(author, true);
+ if (n < 0) {
+ return '';
+ }
+ else {
+ var cdata = authorColorArray[n];
+ if (! cdata) {
+ return '';
+ }
+ else {
+ var c = pad.getColorPalette()[cdata.colorId];
+ if (cdata.faded) {
+ c = fadeColor(c);
+ }
+ return c;
+ }
+ }
+ }
+ function changeAuthorColorData(author, cdata) {
+ var n = authorToNum(author);
+ authorColorArray[n] = cdata;
+ var cssColor = getAuthorCSSColor(author);
+ if (cssColor) {
+ $("#chatlines .chatauthor"+n).css('background',cssColor);
+ }
+ }
+
+ function sendChat() {
+ var lineText = $("#chatentrybox").val();
+ if (lineText) {
+ $("#chatentrybox").val('').focus();
+ var msg = {
+ type: 'chat',
+ userId: pad.getUserId(),
+ lineText: lineText,
+ senderName: pad.getUserName(),
+ authId: pad.getUserId()
+ };
+ pad.sendClientMessage(msg);
+ self.receiveChat(msg);
+ self.scrollToBottom();
+ }
+ }
+
+ var self = {
+ init: function(chatHistoryBlock, initialUserInfo) {
+ ensureChatDay(+new Date); // so that current date shows up right away
+
+ $("a#chatloadmore").click(self.loadMoreHistory);
+
+ self.handleUserJoinOrUpdate(initialUserInfo);
+ receiveChatHistoryBlock(chatHistoryBlock);
+
+ padutils.bindEnterAndEscape($("#chatentrybox"), function(evt) {
+ // return/enter
+ sendChat();
+ }, null);
+
+ self.scrollToBottom();
+ },
+ receiveChat: function(msg) {
+ var box = $("#chatlines").get(0);
+ var wasAtBottom = (box.scrollTop -
+ (box.scrollHeight - $(box).height()) >= -5);
+ addChatLine(msg.userId, +new Date, msg.senderName, msg.lineText, false);
+ if (wasAtBottom) {
+ window.setTimeout(function() {
+ self.scrollToBottom();
+ }, 0);
+ }
+ },
+ handleUserJoinOrUpdate: function(userInfo) {
+ changeAuthorColorData(userInfo.userId,
+ { colorId: userInfo.colorId, faded: false });
+ },
+ handleUserLeave: function(userInfo) {
+ changeAuthorColorData(userInfo.userId,
+ { colorId: userInfo.colorId, faded: true });
+ },
+ scrollToBottom: function() {
+ var box = $("#chatlines").get(0);
+ box.scrollTop = box.scrollHeight;
+ },
+ scrollToTop: function() {
+ var box = $("#chatlines").get(0);
+ box.scrollTop = 0;
+ },
+ loadMoreHistory: function() {
+ if (loadingMoreHistory) {
+ return;
+ }
+
+ var end = oldestHistoricalLine;
+ var start = Math.max(0, end - HISTORY_LINES_TO_LOAD_AT_A_TIME);
+ var padId = pad.getPadId();
+
+ loadingMoreHistory = true;
+ $("#padchat #chatloadmore").css('display', 'none');
+ $("#padchat #chatloadingmore").css('display', 'block');
+
+ $.ajax({
+ type: 'get',
+ url: '/ep/pad/chathistory',
+ data: { padId: padId, start: start, end: end },
+ success: success,
+ error: error
+ });
+
+ function success(text) {
+ notLoading();
+
+ var result = JSON.parse(text);
+
+ // try to keep scrolled to the same place...
+ var scrollBox = $("#chatlines").get(0);
+ var scrollDeterminer = function() { return 0; };
+ var topLine = $("#chatlines .chatday:first .chatline:first").children().eq(0);
+ if (topLine.length > 0) {
+ var posTop = topLine.position().top;
+ var scrollTop = scrollBox.scrollTop;
+ scrollDeterminer = function() {
+ var newPosTop = topLine.position().top;
+ return newPosTop + (scrollTop - posTop);
+ };
+ }
+ receiveChatHistoryBlock(result);
+
+ scrollBox.scrollTop = Math.max(0, Math.min(scrollBox.scrollHeight, scrollDeterminer()));
+ }
+ function error() {
+ notLoading();
+ }
+ function notLoading() {
+ loadingMoreHistory = false;
+ $("#padchat #chatloadmore").css('display', 'block');
+ $("#padchat #chatloadingmore").css('display', 'none');
+ }
+ }
+ };
+ return self;
+}()); \ No newline at end of file
diff --git a/trunk/etherpad/src/static/js/pad_connectionstatus.js b/trunk/etherpad/src/static/js/pad_connectionstatus.js
new file mode 100644
index 0000000..cc06728
--- /dev/null
+++ b/trunk/etherpad/src/static/js/pad_connectionstatus.js
@@ -0,0 +1,63 @@
+/**
+ * Copyright 2009 Google 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.
+ */
+
+var padconnectionstatus = (function() {
+
+ var status = {what: 'connecting'};
+
+ var showHideAnimator = padutils.makeShowHideAnimator(function(state) {
+ $("#connectionbox").css('opacity', 1 - Math.abs(state));
+ if (state == -1) {
+ $("#connectionbox").css('display', 'block');
+ }
+ else if (state == 1) {
+ $("#connectionbox").css('display', 'none');
+ }
+ }, true, 25, 200);
+
+ var self = {
+ init: function() {
+ $('button#forcereconnect').click(function() {
+ pad.forceReconnect();
+ });
+ },
+ connected: function() {
+ status = {what: 'connected'};
+ showHideAnimator.hide();
+ },
+ reconnecting: function() {
+ status = {what: 'reconnecting'};
+ $("#connectionbox").get(0).className = 'cboxreconnecting';
+ showHideAnimator.show();
+ },
+ disconnected: function(msg) {
+ status = {what: 'disconnected', why: msg};
+ var k = String(msg).toLowerCase(); // known reason why
+ if (!(k == 'userdup' || k == 'looping' || k == 'slowcommit' ||
+ k == 'initsocketfail' || k == 'unauth')) {
+ k = 'unknown';
+ }
+ var cls = 'cboxdisconnected cboxdisconnected_'+k;
+ $("#connectionbox").get(0).className = cls;
+ showHideAnimator.show();
+ },
+ isFullyConnected: function() {
+ return status.what == 'connected';
+ },
+ getStatus: function() { return status; }
+ };
+ return self;
+}()); \ No newline at end of file
diff --git a/trunk/etherpad/src/static/js/pad_cookie.js b/trunk/etherpad/src/static/js/pad_cookie.js
new file mode 100644
index 0000000..3cc31ed
--- /dev/null
+++ b/trunk/etherpad/src/static/js/pad_cookie.js
@@ -0,0 +1,101 @@
+/**
+ * Copyright 2009 Google 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.
+ */
+
+
+var padcookie = (function(){
+ function getRawCookie() {
+ // returns null if can't get cookie text
+ if (! document.cookie) {
+ return null;
+ }
+ // look for (start of string OR semicolon) followed by whitespace followed by prefs=(something);
+ var regexResult = document.cookie.match(/(?:^|;)\s*prefs=([^;]*)(?:;|$)/);
+ if ((! regexResult) || (! regexResult[1])) {
+ return null;
+ }
+ return regexResult[1];
+ }
+ function setRawCookie(safeText) {
+ var expiresDate = new Date();
+ expiresDate.setFullYear(3000);
+ document.cookie = ('prefs='+safeText+';expires='+expiresDate.toGMTString());
+ }
+ function parseCookie(text) {
+ // returns null if can't parse cookie.
+
+ try {
+ var cookieData = JSON.parse(unescape(text));
+ return cookieData;
+ }
+ catch (e) {
+ return null;
+ }
+ }
+ function stringifyCookie(data) {
+ return escape(JSON.stringify(data));
+ }
+ function saveCookie() {
+ if (! inited) {
+ return;
+ }
+ setRawCookie(stringifyCookie(cookieData));
+
+ if (pad.getIsProPad() && (! getRawCookie()) && (! alreadyWarnedAboutNoCookies)) {
+ alert("Warning: it appears that your browser does not have cookies enabled."+
+ " EtherPad uses cookies to keep track of unique users for the purpose"+
+ " of putting a quota on the number of active users. Using EtherPad without "+
+ " cookies may fill up your server's user quota faster than expected.");
+ alreadyWarnedAboutNoCookies = true;
+ }
+ }
+
+ var wasNoCookie = true;
+ var cookieData = {};
+ var alreadyWarnedAboutNoCookies = false;
+ var inited = false;
+
+ var self = {
+ init: function(prefsToSet) {
+ var rawCookie = getRawCookie();
+ if (rawCookie) {
+ var cookieObj = parseCookie(rawCookie);
+ if (cookieObj) {
+ wasNoCookie = false; // there was a cookie
+ delete cookieObj.userId;
+ delete cookieObj.name;
+ delete cookieObj.colorId;
+ cookieData = cookieObj;
+ }
+ }
+
+ for(var k in prefsToSet) {
+ cookieData[k] = prefsToSet[k];
+ }
+
+ inited = true;
+ saveCookie();
+ },
+ wasNoCookie: function() { return wasNoCookie; },
+ getPref: function(prefName) {
+ return cookieData[prefName];
+ },
+ setPref: function(prefName, value) {
+ cookieData[prefName] = value;
+ saveCookie();
+ }
+ };
+ return self;
+}()); \ No newline at end of file
diff --git a/trunk/etherpad/src/static/js/pad_docbar.js b/trunk/etherpad/src/static/js/pad_docbar.js
new file mode 100644
index 0000000..586b20f
--- /dev/null
+++ b/trunk/etherpad/src/static/js/pad_docbar.js
@@ -0,0 +1,347 @@
+/**
+ * Copyright 2009 Google 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.
+ */
+
+
+var paddocbar = (function() {
+ var isTitleEditable = false;
+ var isEditingTitle = false;
+ var isEditingPassword = false;
+ var enabled = false;
+
+ function getPanelOpenCloseAnimator(panelName, panelHeight) {
+ var wrapper = $("#"+panelName+"-wrapper");
+ var openingClass = "docbar"+panelName+"-opening";
+ var openClass = "docbar"+panelName+"-open";
+ var closingClass = "docbar"+panelName+"-closing";
+ function setPanelState(action) {
+ $("#docbar").removeClass(openingClass).removeClass(openClass).
+ removeClass(closingClass);
+ if (action != "closed") {
+ $("#docbar").addClass("docbar"+panelName+"-"+action);
+ }
+ }
+
+ function openCloseAnimate(state) {
+ function pow(x) { x = 1-x; x *= x*x; return 1-x; }
+
+ if (state == -1) {
+ // startng to open
+ setPanelState("opening");
+ wrapper.css('height', '0');
+ }
+ else if (state < 0) {
+ // opening
+ var height = Math.round(pow(state+1)*(panelHeight-1))+'px';
+ wrapper.css('height', height);
+ }
+ else if (state == 0) {
+ // open
+ setPanelState("open");
+ wrapper.css('height', panelHeight-1);
+ }
+ else if (state < 1) {
+ // closing
+ setPanelState("closing");
+ var height = Math.round((1-pow(state))*(panelHeight-1))+'px';
+ wrapper.css('height', height);
+ }
+ else if (state == 1) {
+ // closed
+ setPanelState("closed");
+ wrapper.css('height', '0');
+ }
+ }
+
+ return padutils.makeShowHideAnimator(openCloseAnimate, false, 25, 500);
+ }
+
+
+ var currentPanel = null;
+ function setCurrentPanel(newCurrentPanel) {
+ if (currentPanel != newCurrentPanel) {
+ currentPanel = newCurrentPanel;
+ padutils.cancelActions("hide-docbar-panel");
+ }
+ }
+ var panels;
+
+ function changePassword(newPass) {
+ if ((newPass || null) != (self.password || null)) {
+ self.password = (newPass || null);
+ pad.notifyChangePassword(newPass);
+ }
+ self.renderPassword();
+ }
+
+ var self = {
+ title: null,
+ password: null,
+ init: function(opts) {
+ panels = {
+ impexp: { animator: getPanelOpenCloseAnimator("impexp", 160) },
+ savedrevs: { animator: getPanelOpenCloseAnimator("savedrevs", 79) },
+ options: { animator: getPanelOpenCloseAnimator(
+ "options", 114) },
+ security: { animator: getPanelOpenCloseAnimator("security", 130) }
+ };
+
+ isTitleEditable = opts.isTitleEditable;
+ self.title = opts.initialTitle;
+ self.password = opts.initialPassword;
+
+ $("#docbarimpexp").click(function() {self.togglePanel("impexp");});
+ $("#docbarsavedrevs").click(function() {self.togglePanel("savedrevs");});
+ $("#docbaroptions").click(function() {self.togglePanel("options");});
+ $("#docbarsecurity").click(function() {self.togglePanel("security");});
+
+ $("#docbarrenamelink").click(self.editTitle);
+ $("#padtitlesave").click(function() { self.closeTitleEdit(true); });
+ $("#padtitlecancel").click(function() { self.closeTitleEdit(false); });
+ padutils.bindEnterAndEscape($("#padtitleedit"),
+ function() {
+ $("#padtitlesave").trigger('click'); },
+ function() {
+ $("#padtitlecancel").trigger('click'); });
+
+ $("#options-close").click(function() {self.setShownPanel(null);});
+ $("#security-close").click(function() {self.setShownPanel(null);});
+
+ if (pad.getIsProPad()) {
+ self.initPassword();
+ }
+
+ enabled = true;
+ self.render();
+
+ // public/private
+ $("#security-access input").bind("change click", function(evt) {
+ pad.changePadOption('guestPolicy',
+ $("#security-access input[name='padaccess']:checked").val());
+ });
+ self.setGuestPolicy(opts.guestPolicy);
+ },
+ setGuestPolicy: function(newPolicy) {
+ $("#security-access input[value='"+newPolicy+"']").attr("checked",
+ "checked");
+ self.render();
+ },
+ initPassword: function() {
+ self.renderPassword();
+ $("#password-clearlink").click(function() {
+ changePassword(null);
+ });
+ $("#password-setlink, #password-display").click(function() {
+ self.enterPassword();
+ });
+ $("#password-cancellink").click(function() {
+ self.exitPassword(false);
+ });
+ $("#password-savelink").click(function() {
+ self.exitPassword(true);
+ });
+ padutils.bindEnterAndEscape($("#security-passwordedit"),
+ function() {
+ self.exitPassword(true);
+ },
+ function() {
+ self.exitPassword(false);
+ });
+ },
+ enterPassword: function() {
+ isEditingPassword = true;
+ $("#security-passwordedit").val(self.password || '');
+ self.renderPassword();
+ $("#security-passwordedit").focus().select();
+ },
+ exitPassword: function(accept) {
+ isEditingPassword = false;
+ if (accept) {
+ changePassword($("#security-passwordedit").val());
+ }
+ else {
+ self.renderPassword();
+ }
+ },
+ renderPassword: function() {
+ if (isEditingPassword) {
+ $("#password-nonedit").hide();
+ $("#password-inedit").show();
+ }
+ else {
+ $("#password-nonedit").toggleClass('nopassword', ! self.password);
+ $("#password-setlink").html(self.password ? "Change..." : "Set...");
+ if (self.password) {
+ $("#password-display").html(self.password.replace(/./g, '&#8226;'));
+ }
+ else {
+ $("#password-display").html("None");
+ }
+ $("#password-inedit").hide();
+ $("#password-nonedit").show();
+ }
+ },
+ togglePanel: function(panelName) {
+ if (panelName in panels) {
+ if (currentPanel == panelName) {
+ self.setShownPanel(null);
+ }
+ else {
+ self.setShownPanel(panelName);
+ }
+ }
+ },
+ setShownPanel: function(panelName) {
+ function animateHidePanel(panelName, next) {
+ var delay = 0;
+ if (panelName == 'options' && isEditingPassword) {
+ // give user feedback that the password they've
+ // typed in won't actually take effect
+ self.exitPassword(false);
+ delay = 500;
+ }
+
+ window.setTimeout(function() {
+ panels[panelName].animator.hide();
+ if (next) {
+ next();
+ }
+ }, delay);
+ }
+
+ if (! panelName) {
+ if (currentPanel) {
+ animateHidePanel(currentPanel);
+ setCurrentPanel(null);
+ }
+ }
+ else if (panelName in panels) {
+ if (currentPanel != panelName) {
+ if (currentPanel) {
+ animateHidePanel(currentPanel, function() {
+ panels[panelName].animator.show();
+ setCurrentPanel(panelName);
+ });
+ }
+ else {
+ panels[panelName].animator.show();
+ setCurrentPanel(panelName);
+ }
+ }
+ }
+ },
+ isPanelShown: function(panelName) {
+ if (! panelName) {
+ return ! currentPanel;
+ }
+ else {
+ return (panelName == currentPanel);
+ }
+ },
+ changeTitle: function(newTitle) {
+ self.title = newTitle;
+ self.render();
+ },
+ editTitle: function() {
+ if (! enabled) {
+ return;
+ }
+ $("#padtitleedit").val(self.title);
+ isEditingTitle = true;
+ self.render();
+ $("#padtitleedit").focus().select();
+ },
+ closeTitleEdit: function(accept) {
+ if (! enabled) {
+ return;
+ }
+ if (accept) {
+ var newTitle = $("#padtitleedit").val();
+ if (newTitle) {
+ newTitle = newTitle.substring(0, 80);
+ self.title = newTitle;
+
+ pad.notifyChangeTitle(newTitle);
+ }
+ }
+
+ isEditingTitle = false;
+ self.render();
+ },
+ changePassword: function(newPass) {
+ if (newPass) {
+ self.password = newPass;
+ }
+ else {
+ self.password = null;
+ }
+ self.renderPassword();
+ },
+ render: function() {
+ if (isEditingTitle) {
+ $("#docbarpadtitle").hide();
+ $("#docbarrenamelink").hide();
+ $("#padtitleedit").show();
+ $("#padtitlebuttons").show();
+ if (! enabled) {
+ $("#padtitleedit").attr('disabled', 'disabled');
+ }
+ else {
+ $("#padtitleedit").removeAttr('disabled');
+ }
+ }
+ else {
+ $("#padtitleedit").hide();
+ $("#padtitlebuttons").hide();
+
+ var titleSpan = $("#docbarpadtitle span");
+ titleSpan.html(padutils.escapeHtml(self.title));
+ $("#docbarpadtitle").attr('title',
+ (pad.isPadPublic() ? "Public Pad: " : "")+
+ self.title);
+ $("#docbarpadtitle").show();
+
+ if (isTitleEditable) {
+ var titleRight = $("#docbarpadtitle").position().left +
+ $("#docbarpadtitle span").position().left +
+ Math.min($("#docbarpadtitle").width(),
+ $("#docbarpadtitle span").width());
+ $("#docbarrenamelink").css('left', titleRight + 10).show();
+ }
+
+ if (pad.isPadPublic()) {
+ $("#docbar").addClass("docbar-public");
+ }
+ else {
+ $("#docbar").removeClass("docbar-public");
+ }
+ }
+ },
+ disable: function() {
+ enabled = false;
+ self.render();
+ },
+ handleResizePage: function() {
+ padsavedrevs.handleResizePage();
+ },
+ hideLaterIfNoOtherInteraction: function() {
+ return padutils.getCancellableAction('hide-docbar-panel',
+ function() {
+ self.setShownPanel(null);
+ });
+ }
+ };
+ return self;
+}());
diff --git a/trunk/etherpad/src/static/js/pad_editbar.js b/trunk/etherpad/src/static/js/pad_editbar.js
new file mode 100644
index 0000000..34b774a
--- /dev/null
+++ b/trunk/etherpad/src/static/js/pad_editbar.js
@@ -0,0 +1,107 @@
+/**
+ * Copyright 2009 Google 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.
+ */
+
+
+var padeditbar = (function(){
+
+ var syncAnimation = (function() {
+ var SYNCING = -100;
+ var DONE = 100;
+ var state = DONE;
+ var fps = 25;
+ var step = 1/fps;
+ var T_START = -0.5;
+ var T_FADE = 1.0;
+ var T_GONE = 1.5;
+ var animator = padutils.makeAnimationScheduler(function() {
+ if (state == SYNCING || state == DONE) {
+ return false;
+ }
+ else if (state >= T_GONE) {
+ state = DONE;
+ $("#syncstatussyncing").css('display', 'none');
+ $("#syncstatusdone").css('display', 'none');
+ return false;
+ }
+ else if (state < 0) {
+ state += step;
+ if (state >= 0) {
+ $("#syncstatussyncing").css('display', 'none');
+ $("#syncstatusdone").css('display', 'block').css('opacity', 1);
+ }
+ return true;
+ }
+ else {
+ state += step;
+ if (state >= T_FADE) {
+ $("#syncstatusdone").css('opacity', (T_GONE - state) / (T_GONE - T_FADE));
+ }
+ return true;
+ }
+ }, step*1000);
+ return {
+ syncing: function() {
+ state = SYNCING;
+ $("#syncstatussyncing").css('display', 'block');
+ $("#syncstatusdone").css('display', 'none');
+ },
+ done: function() {
+ state = T_START;
+ animator.scheduleAnimation();
+ }
+ };
+ }());
+
+ var self = {
+ init: function() {
+ $("#editbar .editbarbutton").attr("unselectable", "on"); // for IE
+ $("#editbar").removeClass("disabledtoolbar").addClass("enabledtoolbar");
+ },
+ isEnabled: function() {
+ return ! $("#editbar").hasClass('disabledtoolbar');
+ },
+ disable: function() {
+ $("#editbar").addClass('disabledtoolbar').removeClass("enabledtoolbar");
+ },
+ toolbarClick: function(cmd) {
+ if (self.isEnabled()) {
+ if (cmd == 'save') {
+ padsavedrevs.saveNow();
+ }
+ else if (cmd == 'clearauthorship') {
+ padeditor.ace.execCommand('clearauthorship', function() {
+ if (window.confirm("Clear authorship colors on entire document?")) {
+ padeditor.ace.execCommand('clearauthorship');
+ }
+ });
+ }
+ else {
+ padeditor.ace.execCommand(cmd);
+ }
+ }
+ padeditor.ace.focus();
+ },
+ setSyncStatus: function(status) {
+ if (status == "syncing") {
+ syncAnimation.syncing();
+ }
+ else if (status == "done") {
+ syncAnimation.done();
+ }
+ }
+ };
+ return self;
+}()); \ No newline at end of file
diff --git a/trunk/etherpad/src/static/js/pad_editor.js b/trunk/etherpad/src/static/js/pad_editor.js
new file mode 100644
index 0000000..f2fab26
--- /dev/null
+++ b/trunk/etherpad/src/static/js/pad_editor.js
@@ -0,0 +1,136 @@
+/**
+ * Copyright 2009 Google 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.
+ */
+
+
+var padeditor = (function(){
+ var self = {
+ ace: null, // this is accessed directly from other files
+ viewZoom: 100,
+ init: function(readyFunc, initialViewOptions) {
+
+ function aceReady() {
+ $("#editorloadingbox").hide();
+ if (readyFunc) {
+ readyFunc();
+ }
+ }
+
+ self.ace = new Ace2Editor();
+ self.ace.init("editorcontainer", "", aceReady);
+ self.ace.setProperty("wraps", true);
+ if (pad.getIsDebugEnabled()) {
+ self.ace.setProperty("dmesg", pad.dmesg);
+ }
+ self.initViewOptions();
+ self.setViewOptions(initialViewOptions);
+
+ // view bar
+ self.initViewZoom();
+ $("#viewbarcontents").show();
+ },
+ initViewOptions: function() {
+ padutils.bindCheckboxChange($("#options-linenoscheck"), function() {
+ pad.changeViewOption('showLineNumbers',
+ padutils.getCheckbox($("#options-linenoscheck")));
+ });
+ padutils.bindCheckboxChange($("#options-colorscheck"), function() {
+ pad.changeViewOption('showAuthorColors',
+ padutils.getCheckbox("#options-colorscheck"));
+ });
+ $("#viewfontmenu").change(function() {
+ pad.changeViewOption('useMonospaceFont',
+ $("#viewfontmenu").val() == 'monospace');
+ });
+ },
+ setViewOptions: function(newOptions) {
+ function getOption(key, defaultValue) {
+ var value = String(newOptions[key]);
+ if (value == "true") return true;
+ if (value == "false") return false;
+ return defaultValue;
+ }
+ var v;
+
+ v = getOption('showLineNumbers', true);
+ self.ace.setProperty("showslinenumbers", v);
+ padutils.setCheckbox($("#options-linenoscheck"), v);
+
+ v = getOption('showAuthorColors', true);
+ self.ace.setProperty("showsauthorcolors", v);
+ padutils.setCheckbox($("#options-colorscheck"), v);
+
+ v = getOption('useMonospaceFont', false);
+ self.ace.setProperty("textface",
+ (v ? "monospace" : "Arial, sans-serif"));
+ $("#viewfontmenu").val(v ? "monospace" : "normal");
+ },
+ initViewZoom: function() {
+ var viewZoom = Number(padcookie.getPref('viewZoom'));
+ if ((! viewZoom) || isNaN(viewZoom)) {
+ viewZoom = 100;
+ }
+ self.setViewZoom(viewZoom);
+ $("#viewzoommenu").change(function(evt) {
+ // strip initial 'z' from val
+ self.setViewZoom(Number($("#viewzoommenu").val().substring(1)));
+ });
+ },
+ setViewZoom: function(percent) {
+ if (! (percent >= 50 && percent <= 1000)) {
+ // percent is out of sane range or NaN (which fails comparisons)
+ return;
+ }
+
+ self.viewZoom = percent;
+ $("#viewzoommenu").val('z'+percent);
+
+ var baseSize = 13;
+ self.ace.setProperty('textsize',
+ Math.round(baseSize * self.viewZoom / 100));
+
+ padcookie.setPref('viewZoom', percent);
+ },
+ dispose: function() {
+ if (self.ace) {
+ self.ace.destroy();
+ }
+ },
+ setBottom: function(bottomPx) {
+ var myTop = $("#padeditor").offset().top;
+ var myHeight = $("#padeditor").height();
+ var myBottom = myTop + myHeight;
+ var sizedBoxHeight = $("#editorcontainerbox").height();
+
+ var deltaBottom = bottomPx - myBottom;
+ if (deltaBottom != 0) {
+ $("#editorcontainerbox").height(sizedBoxHeight + deltaBottom);
+ }
+ self.ace.adjustSize();
+ },
+ disable: function() {
+ if (self.ace) {
+ self.ace.setProperty("grayedOut", true);
+ self.ace.setEditable(false);
+ }
+ },
+ restoreRevisionText: function(dataFromServer) {
+ pad.addHistoricalAuthors(dataFromServer.historicalAuthorData);
+ self.ace.importAText(dataFromServer.atext, dataFromServer.apool, true);
+ }
+ };
+ return self;
+}());
+
diff --git a/trunk/etherpad/src/static/js/pad_impexp.js b/trunk/etherpad/src/static/js/pad_impexp.js
new file mode 100644
index 0000000..e928332
--- /dev/null
+++ b/trunk/etherpad/src/static/js/pad_impexp.js
@@ -0,0 +1,187 @@
+/**
+ * Copyright 2009 Google 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.
+ */
+
+
+var padimpexp = (function() {
+
+ ///// import
+
+ var currentImportTimer = null;
+ var hidePanelCall = null;
+
+ function addImportFrames() {
+ $("#impexp-import .importframe").remove();
+ $('#impexp-import').append(
+ $('<iframe style="display: none;" name="importiframe" class="importframe"></iframe>'));
+ }
+ function fileInputUpdated() {
+ $('#importformfilediv').addClass('importformenabled');
+ $('#importsubmitinput').removeAttr('disabled');
+ $('#importmessagefail').fadeOut("fast");
+ $('#importarrow').show();
+ $('#importarrow').animate({paddingLeft:"0px"}, 500)
+ .animate({paddingLeft:"10px"}, 150, 'swing')
+ .animate({paddingLeft:"0px"}, 150, 'swing')
+ .animate({paddingLeft:"10px"}, 150, 'swing')
+ .animate({paddingLeft:"0px"}, 150, 'swing')
+ .animate({paddingLeft:"10px"}, 150, 'swing')
+ .animate({paddingLeft:"0px"}, 150, 'swing');
+ }
+ function fileInputSubmit() {
+ $('#importmessagefail').fadeOut("fast");
+ var ret = window.confirm(
+ "Importing a file will overwrite the current text of the pad."+
+ " Are you sure you want to proceed?");
+ if (ret) {
+ hidePanelCall = paddocbar.hideLaterIfNoOtherInteraction();
+ currentImportTimer = window.setTimeout(function() {
+ if (! currentImportTimer) {
+ return;
+ }
+ currentImportTimer = null;
+ importFailed("Request timed out.");
+ }, 25000); // time out after some number of seconds
+ $('#importsubmitinput').attr({disabled: true}).val("Importing...");
+ window.setTimeout(function() {
+ $('#importfileinput').attr({disabled: true}); }, 0);
+ $('#importarrow').stop(true, true).hide();
+ $('#importstatusball').show();
+ }
+ return ret;
+ }
+ function importFailed(msg) {
+ importErrorMessage(msg);
+ importDone();
+ addImportFrames();
+ }
+ function importDone() {
+ $('#importsubmitinput').removeAttr('disabled').val("Import Now");
+ window.setTimeout(function() {
+ $('#importfileinput').removeAttr('disabled'); }, 0);
+ $('#importstatusball').hide();
+ importClearTimeout();
+ }
+ function importClearTimeout() {
+ if (currentImportTimer) {
+ window.clearTimeout(currentImportTimer);
+ currentImportTimer = null;
+ }
+ }
+ function importErrorMessage(msg) {
+ function showError(fade) {
+ $('#importmessagefail').html(
+ '<strong style="color: red">Import failed:</strong> '+
+ (msg || 'Please try a different file.'))[(fade?"fadeIn":"show")]();
+ }
+
+ if ($('#importexport .importmessage').is(':visible')) {
+ $('#importmessagesuccess').fadeOut("fast");
+ $('#importmessagefail').fadeOut("fast", function() {
+ showError(true); });
+ } else {
+ showError();
+ }
+ }
+ function importSuccessful(token) {
+ $.ajax({
+ type: 'post',
+ url: '/ep/pad/impexp/import2',
+ data: {token: token, padId: pad.getPadId()},
+ success: importApplicationSuccessful,
+ error: importApplicationFailed,
+ timeout: 25000
+ });
+ addImportFrames();
+ }
+ function importApplicationFailed(xhr, textStatus, errorThrown) {
+ importErrorMessage("Error during conversion.");
+ importDone();
+ }
+ function importApplicationSuccessful(data, textStatus) {
+ if (data.substr(0, 2) == "ok") {
+ if ($('#importexport .importmessage').is(':visible')) {
+ $('#importexport .importmessage').hide();
+ }
+ $('#importmessagesuccess').html(
+ '<strong style="color: green">Import successful!</strong>').show();
+ $('#importformfilediv').hide();
+ window.setTimeout(function() {
+ $('#importmessagesuccess').fadeOut("slow", function() {
+ $('#importformfilediv').show();
+ });
+ if (hidePanelCall) {
+ hidePanelCall();
+ }
+ }, 3000);
+ } else if (data.substr(0, 4) == "fail") {
+ importErrorMessage(
+ "Couldn't update pad contents. This can happen if your web browser has \"cookies\" disabled.");
+ } else if (data.substr(0, 4) == "msg:") {
+ importErrorMessage(data.substr(4));
+ }
+ importDone();
+ }
+
+ ///// export
+
+ function cantExport() {
+ var type = $(this);
+ if (type.hasClass("exporthrefpdf")) {
+ type = "PDF";
+ } else if (type.hasClass("exporthrefdoc")) {
+ type = "Microsoft Word";
+ } else if (type.hasClass("exporthrefodt")) {
+ type = "OpenDocument";
+ } else {
+ type = "this file";
+ }
+ alert("Exporting as "+type+" format is disabled. Please contact your"+
+ " system administrator for details.");
+ return false;
+ }
+
+ /////
+
+ var self = {
+ init: function() {
+ $("#impexp-close").click(function() {paddocbar.setShownPanel(null);});
+
+ addImportFrames();
+ $("#importfileinput").change(fileInputUpdated);
+ $('#importform').submit(fileInputSubmit);
+ $('.disabledexport').click(cantExport);
+ },
+ handleFrameCall: function(callName, argsArray) {
+ if (callName == 'importFailed') {
+ importFailed(argsArray[0]);
+ }
+ else if (callName == 'importSuccessful') {
+ importSuccessful(argsArray[0]);
+ }
+ },
+ disable: function() {
+ $("#impexp-disabled-clickcatcher").show();
+ $("#impexp-import").css('opacity', 0.5);
+ $("#impexp-export").css('opacity', 0.5);
+ },
+ enable: function() {
+ $("#impexp-disabled-clickcatcher").hide();
+ $("#impexp-import").css('opacity', 1);
+ $("#impexp-export").css('opacity', 1);
+ }
+ };
+ return self;
+}());
diff --git a/trunk/etherpad/src/static/js/pad_modals.js b/trunk/etherpad/src/static/js/pad_modals.js
new file mode 100644
index 0000000..c9f48b5
--- /dev/null
+++ b/trunk/etherpad/src/static/js/pad_modals.js
@@ -0,0 +1,364 @@
+/**
+ * Copyright 2009 Google 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.
+ */
+
+var padmodals = (function() {
+
+ /*var clearFeedbackEmail = function() {};
+ function clearFeedback() {
+ clearFeedbackEmail();
+ $("#feedbackbox-message").val('');
+ }
+
+ var sendingFeedback = false;
+ function setSendingFeedback(v) {
+ v = !! v;
+ if (sendingFeedback != v) {
+ sendingFeedback = v;
+ if (v) {
+ $("#feedbackbox-send").css('opacity', 0.75);
+ }
+ else {
+ $("#feedbackbox-send").css('opacity', 1);
+ }
+ }
+ }*/
+
+ var sendingInvite = false;
+ function setSendingInvite(v) {
+ v = !! v;
+ if (sendingInvite != v) {
+ sendingInvite = v;
+ if (v) {
+ $("#sharebox-send").css('opacity', 0.75);
+ }
+ else {
+ $("#sharebox-send").css('opacity', 1);
+ }
+ }
+ }
+
+ var clearShareBoxTo = function() {};
+ function clearShareBox() {
+ clearShareBoxTo();
+ }
+
+ var allModals = $("#feedbackbox, #sharebox");
+
+ var shareboxExpanded = false;
+ var shareExpander = padutils.makeShowHideAnimator(function(state) {
+ function pow(x) { x = 1-x; x *= x*x; return 1-x; }
+ function ease(x) { return (x < 0) ? pow(x+1) : 1-pow(x); }
+ var smallHeight = 110 + (pad.getUserIsGuest() ? 0 : 50);
+ var bigHeight = 326 + (pad.getUserIsGuest() ? 0 : 50);
+ var boxHeight = (Math.abs(ease(state)))*(bigHeight-smallHeight) + smallHeight;
+ $("#sharebox").height(boxHeight);
+ // sharebox-open controls the disclosure triangle
+ // and also the visibility of any response (error/success)
+ // messages, which must be hidden during animation as well
+ // as when closed.
+ if (state == 0) {
+ $("#sharebox").addClass('sharebox-open');
+ }
+ else if (state > 0) {
+ $("#sharebox").removeClass('sharebox-open');
+ $("#sharebox-response").hide();
+ }
+ }, false, 20, 500);
+
+ //var overlayNode = null;
+
+ /*function adjustOverlay() {
+ if (overlayNode) {
+ var nodeWidth = overlayNode.width();
+ var nodeHeight = overlayNode.height();
+ var nodePos = overlayNode.position();
+ var outset = 10;
+ $("#modaloverlay").css({width: nodeWidth+outset*2,
+ height: nodeHeight+outset*2,
+ left: nodePos.left-outset,
+ top: nodePos.top-outset});
+ }
+ }*/
+
+ function showOverlay(forNode) {
+ //overlayNode = forNode;
+ //adjustOverlay();
+ $("#modaloverlay").show();
+ }
+ function hideOverlay() {
+ $("#modaloverlay").hide();
+ }
+
+ var self = {
+ shareExpander: shareExpander,
+ init: function() {
+ self.initFeedback();
+ self.initShareBox();
+ },
+ initFeedback: function() {
+ /*var emailField = $("#feedbackbox-email");
+ clearFeedbackEmail =
+ padutils.makeFieldLabeledWhenEmpty(emailField, '(your email address)').clear;
+ clearFeedback();*/
+
+ $("#feedbackbox-hide").click(function() {
+ self.hideModal();
+ });
+ /*$("#feedbackbox-send").click(function() {
+ self.sendFeedbackEmail();
+ });*/
+
+ $("#feedbackbutton").click(function() {
+ self.showFeedback();
+ });
+
+ $("#uservoicelinks a").click(function() {
+ self.hideModal();
+ return true;
+ });
+ $("#feedbackemails a").each(function() {
+ var node = $(this);
+ node.attr('href', "mailto:"+node.attr('href')+"@pad.spline.inf.fu-berlin.de");
+ });
+ },
+ initShareBox: function() {
+ $("#sharebutton, #nootherusers a").click(function() {
+ self.showShareBox();
+ });
+ $("#sharebox-hide").click(function() {
+ self.hideModal();
+ });
+ $("#sharebox-send").click(function() {
+ self.sendInvite();
+ });
+
+ $("#sharebox-url").click(function() {
+ $("#sharebox-url").focus().select();
+ });
+
+ clearShareBoxTo =
+ padutils.makeFieldLabeledWhenEmpty($("#sharebox-to"),
+ "(email addresses)").clear;
+ clearShareBox();
+
+ $("#sharebox-subject").val(self.getDefaultShareBoxSubjectForName(pad.getUserName()));
+ $("#sharebox-message").val(self.getDefaultShareBoxMessageForName(pad.getUserName()));
+
+ $("#sharebox-dislink").click(function() {
+ if (shareboxExpanded) {
+ shareboxExpanded = false;
+ shareExpander.hide();
+ }
+ else {
+ shareboxExpanded = true;
+ shareExpander.show();
+ }
+ });
+
+ $("#sharebox-stripe .setsecurity").click(function() {
+ self.hideModal();
+ paddocbar.setShownPanel('security');
+ });
+ },
+ getDefaultShareBoxMessageForName: function(name) {
+ return (name || "Somebody")+" has shared an EtherPad document with you."+
+ "\n\n"+"View it here:\n\n"+
+ padutils.escapeHtml($("#sharebox-url").val()+"\n");
+ },
+ getDefaultShareBoxSubjectForName: function(name) {
+ return (name || "Somebody")+" invited you to an EtherPad document";
+ },
+ relayoutWithBottom: function(px) {
+ $("#modaloverlay").height(px);
+ $("#sharebox").css('left',
+ Math.floor(($(window).width() -
+ $("#sharebox").outerWidth())/2));
+ $("#feedbackbox").css('left',
+ Math.floor(($(window).width() -
+ $("#feedbackbox").outerWidth())/2));
+ },
+ showFeedback: function() {
+ allModals.hide();
+ $("#feedbackbox").show();
+ showOverlay($("#feedbackbox"));
+ },
+ showShareBox: function() {
+ // when showing the dialog, if it still says "Somebody" invited you
+ // then we fill in the updated username if there is one;
+ // otherwise, we don't touch it, perhaps the user is happy with it
+ var msgbox = $("#sharebox-message");
+ if (msgbox.val() == self.getDefaultShareBoxMessageForName(null)) {
+ msgbox.val(self.getDefaultShareBoxMessageForName(pad.getUserName()));
+ }
+ var subjBox = $("#sharebox-subject");
+ if (subjBox.val() == self.getDefaultShareBoxSubjectForName(null)) {
+ subjBox.val(self.getDefaultShareBoxSubjectForName(pad.getUserName()));
+ }
+
+ if (pad.isPadPublic()) {
+ $("#sharebox-stripe").get(0).className = 'sharebox-stripe-public';
+ }
+ else {
+ $("#sharebox-stripe").get(0).className = 'sharebox-stripe-private';
+ }
+
+ allModals.hide();
+ $("#sharebox").show();
+ showOverlay($("#sharebox"));
+ $("#sharebox-url").focus().select();
+ },
+ hideModal: function() {
+ padutils.cancelActions('hide-feedbackbox');
+ padutils.cancelActions('hide-sharebox');
+ $("#sharebox-response").hide();
+ allModals.hide();
+ hideOverlay();
+ },
+ hideFeedbackLaterIfNoOtherInteraction: function() {
+ return padutils.getCancellableAction('hide-feedbackbox',
+ function() {
+ self.hideModal();
+ });
+ },
+ hideShareboxLaterIfNoOtherInteraction: function() {
+ return padutils.getCancellableAction('hide-sharebox',
+ function() {
+ self.hideModal();
+ });
+ },
+/* sendFeedbackEmail: function() {
+ if (sendingFeedback) {
+ return;
+ }
+ var message = $("#feedbackbox-message").val();
+ if (! message) {
+ return;
+ }
+ var email = ($("#feedbackbox-email").hasClass('editempty') ? '' :
+ $("#feedbackbox-email").val());
+ var padId = pad.getPadId();
+ var username = pad.getUserName();
+ setSendingFeedback(true);
+ $("#feedbackbox-response").html("Sending...").get(0).className = '';
+ $("#feedbackbox-response").show();
+ $.ajax({
+ type: 'post',
+ url: '/ep/pad/feedback',
+ data: {
+ feedback: message,
+ padId: padId,
+ username: username,
+ email: email
+ },
+ success: success,
+ error: error
+ });
+ var hideCall = self.hideFeedbackLaterIfNoOtherInteraction();
+ function success(msg) {
+ setSendingFeedback(false);
+ clearFeedback();
+ $("#feedbackbox-response").html("Thanks for your feedback").get(0).className = 'goodresponse';
+ $("#feedbackbox-response").show();
+ window.setTimeout(function() {
+ $("#feedbackbox-response").fadeOut('slow', function() {
+ hideCall();
+ });
+ }, 1500);
+ }
+ function error(e) {
+ setSendingFeedback(false);
+ $("#feedbackbox-response").html("Could not send feedback. Please email us at feedback"+"@"+"pad.spline.inf.fu-berlin.de instead.").get(0).className = 'badresponse';
+ $("#feedbackbox-response").show();
+ }
+ },*/
+ sendInvite: function() {
+ if (sendingInvite) {
+ return;
+ }
+ if (! pad.isFullyConnected()) {
+ displayErrorMessage("Error: Connection to the server is down or flaky.");
+ return;
+ }
+ var message = $("#sharebox-message").val();
+ if (! message) {
+ displayErrorMessage("Please enter a message body before sending.");
+ return;
+ }
+ var emails = ($("#sharebox-to").hasClass('editempty') ? '' :
+ $("#sharebox-to").val()) || '';
+ // find runs of characters that aren't obviously non-email punctuation
+ var emailArray = emails.match(/[^\s,:;<>\"\'\/\(\)\[\]{}]+/g) || [];
+ if (emailArray.length == 0) {
+ displayErrorMessage('Please enter at least one "To:" address.');
+ $("#sharebox-to").focus().select();
+ return;
+ }
+ for(var i=0;i<emailArray.length;i++) {
+ var addr = emailArray[i];
+ if (! addr.match(/^[\w\.\_\+\-]+\@[\w\_\-]+\.[\w\_\-\.]+$/)) {
+ displayErrorMessage('"'+padutils.escapeHtml(addr) +
+ '" does not appear to be a valid email address.');
+ return;
+ }
+ }
+ var subject = $("#sharebox-subject").val();
+ if (! subject) {
+ subject = self.getDefaultShareBoxSubjectForName(pad.getUserName());
+ $("#sharebox-subject").val(subject); // force the default subject
+ }
+
+ var padId = pad.getPadId();
+ var username = pad.getUserName();
+ setSendingInvite(true);
+ $("#sharebox-response").html("Sending...").get(0).className = '';
+ $("#sharebox-response").show();
+ $.ajax({
+ type: 'post',
+ url: '/ep/pad/emailinvite',
+ data: {
+ message: message,
+ toEmails: emailArray.join(','),
+ subject: subject,
+ username: username,
+ padId: padId
+ },
+ success: success,
+ error: error
+ });
+ var hideCall = self.hideShareboxLaterIfNoOtherInteraction();
+ function success(msg) {
+ setSendingInvite(false);
+ $("#sharebox-response").html("Email invitation sent!").get(0).className = 'goodresponse';
+ $("#sharebox-response").show();
+ window.setTimeout(function() {
+ $("#sharebox-response").fadeOut('slow', function() {
+ hideCall();
+ });
+ }, 1500);
+ }
+ function error(e) {
+ setSendingFeedback(false);
+ $("#sharebox-response").html("An error occurred; no email was sent.").get(0).className = 'badresponse';
+ $("#sharebox-response").show();
+ }
+ function displayErrorMessage(msgHtml) {
+ $("#sharebox-response").html(msgHtml).get(0).className = 'badresponse';
+ $("#sharebox-response").show();
+ }
+ }
+ };
+ return self;
+}()); \ No newline at end of file
diff --git a/trunk/etherpad/src/static/js/pad_savedrevs.js b/trunk/etherpad/src/static/js/pad_savedrevs.js
new file mode 100644
index 0000000..ec06a1f
--- /dev/null
+++ b/trunk/etherpad/src/static/js/pad_savedrevs.js
@@ -0,0 +1,408 @@
+/**
+ * Copyright 2009 Google 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.
+ */
+
+
+var padsavedrevs = (function() {
+
+ function reversedCopy(L) {
+ var L2 = L.slice();
+ L2.reverse();
+ return L2;
+ }
+
+ function makeRevisionBox(revisionInfo, rnum) {
+ var box = $('<div class="srouterbox">'+
+ '<div class="srinnerbox">'+
+ '<a href="javascript:void(0)" class="srname"><!-- --></a>'+
+ '<div class="sractions"><a class="srview" href="javascript:void(0)" target="_blank">view</a> | <a class="srrestore" href="javascript:void(0)">restore</a></div>'+
+ '<div class="srtime"><!-- --></div>'+
+ '<div class="srauthor"><!-- --></div>'+
+ '<img class="srtwirly" src="/static/img/misc/status-ball.gif">'+
+ '</div></div>');
+ setBoxLabel(box, revisionInfo.label);
+ setBoxTimestamp(box, revisionInfo.timestamp);
+ box.find(".srauthor").html("by "+padutils.escapeHtml(revisionInfo.savedBy));
+ var viewLink = '/ep/pad/view/'+pad.getPadId()+'/'+revisionInfo.id;
+ box.find(".srview").attr('href', viewLink);
+ var restoreLink = 'javascript:void padsavedrevs.restoreRevision('+rnum+');';
+ box.find(".srrestore").attr('href', restoreLink);
+ box.find(".srname").click(function(evt) {
+ editRevisionLabel(rnum, box);
+ });
+ return box;
+ }
+ function setBoxLabel(box, label) {
+ box.find(".srname").html(padutils.escapeHtml(label)).attr('title', label);
+ }
+ function setBoxTimestamp(box, timestamp) {
+ box.find(".srtime").html(padutils.escapeHtml(
+ padutils.timediff(new Date(timestamp))));
+ }
+ function getNthBox(n) {
+ return $("#savedrevisions .srouterbox").eq(n);
+ }
+ function editRevisionLabel(rnum, box) {
+ var input = $('<input type="text" class="srnameedit"/>');
+ box.find(".srnameedit").remove(); // just in case
+ var label = box.find(".srname");
+ input.width(label.width());
+ input.height(label.height());
+ input.css('top', label.position().top);
+ input.css('left', label.position().left);
+ label.after(input);
+ label.css('opacity', 0);
+ function endEdit() {
+ input.remove();
+ label.css('opacity', 1);
+ }
+ var rev = currentRevisionList[rnum];
+ var oldLabel = rev.label;
+ input.blur(function() {
+ var newLabel = input.val();
+ if (newLabel && newLabel != oldLabel) {
+ relabelRevision(rnum, newLabel);
+ }
+ endEdit();
+ });
+ input.val(rev.label).focus().select();
+ padutils.bindEnterAndEscape(input, function onEnter() {
+ input.blur();
+ }, function onEscape() {
+ input.val('').blur();
+ });
+ }
+ function relabelRevision(rnum, newLabel) {
+ var rev = currentRevisionList[rnum];
+ $.ajax({
+ type: 'post',
+ url: '/ep/pad/saverevisionlabel',
+ data: {userId: pad.getUserId(),
+ padId: pad.getPadId(),
+ revId: rev.id,
+ newLabel: newLabel},
+ success: success,
+ error: error
+ });
+ function success(text) {
+ var newRevisionList = JSON.parse(text);
+ self.newRevisionList(newRevisionList);
+ pad.sendClientMessage({
+ type: 'revisionLabel',
+ revisionList: reversedCopy(currentRevisionList),
+ savedBy: pad.getUserName(),
+ newLabel: newLabel
+ });
+ }
+ function error(e) {
+ alert("Oops! There was an error saving that revision label. Please try again later.");
+ }
+ }
+
+ var currentRevisionList = [];
+ function setRevisionList(newRevisionList, noAnimation) {
+ // deals with changed labels and new added revisions
+ for(var i=0; i<currentRevisionList.length; i++) {
+ var a = currentRevisionList[i];
+ var b = newRevisionList[i];
+ if (b.label != a.label) {
+ setBoxLabel(getNthBox(i), b.label);
+ }
+ }
+ for(var j=currentRevisionList.length; j<newRevisionList.length; j++) {
+ var newBox = makeRevisionBox(newRevisionList[j], j);
+ $("#savedrevs-scrollinner").append(newBox);
+ newBox.css('left', j * REVISION_BOX_WIDTH);
+ }
+ var newOnes = (newRevisionList.length > currentRevisionList.length);
+ currentRevisionList = newRevisionList;
+ if (newOnes) {
+ setDesiredScroll(getMaxScroll());
+ if (noAnimation) {
+ setScroll(desiredScroll);
+ }
+
+ if (! noAnimation) {
+ var nameOfLast = currentRevisionList[currentRevisionList.length-1].label;
+ displaySavedTip(nameOfLast);
+ }
+ }
+ }
+ function refreshRevisionList() {
+ for(var i=0;i<currentRevisionList.length; i++) {
+ var r = currentRevisionList[i];
+ var box = getNthBox(i);
+ setBoxTimestamp(box, r.timestamp);
+ }
+ }
+
+ var savedTipAnimator = padutils.makeShowHideAnimator(function(state) {
+ if (state == -1) {
+ $("#revision-notifier").css('opacity', 0).css('display', 'block');
+ }
+ else if (state == 0) {
+ $("#revision-notifier").css('opacity', 1);
+ }
+ else if (state == 1) {
+ $("#revision-notifier").css('opacity', 0).css('display', 'none');
+ }
+ else if (state < 0) {
+ $("#revision-notifier").css('opacity', 1);
+ }
+ else if (state > 0) {
+ $("#revision-notifier").css('opacity', 1 - state);
+ }
+ }, false, 25, 300);
+
+ function displaySavedTip(text) {
+ $("#revision-notifier .name").html(padutils.escapeHtml(text));
+ savedTipAnimator.show();
+ padutils.cancelActions("hide-revision-notifier");
+ var hideLater = padutils.getCancellableAction("hide-revision-notifier",
+ function() {
+ savedTipAnimator.hide();
+ });
+ window.setTimeout(hideLater, 3000);
+ }
+
+ var REVISION_BOX_WIDTH = 120;
+ var curScroll = 0; // distance between left of revisions and right of view
+ var desiredScroll = 0;
+ function getScrollWidth() {
+ return REVISION_BOX_WIDTH * currentRevisionList.length;
+ }
+ function getViewportWidth() {
+ return $("#savedrevs-scrollouter").width();
+ }
+ function getMinScroll() {
+ return Math.min(getViewportWidth(), getScrollWidth());
+ }
+ function getMaxScroll() {
+ return getScrollWidth();
+ }
+ function setScroll(newScroll) {
+ curScroll = newScroll;
+ $("#savedrevs-scrollinner").css('right', newScroll);
+ updateScrollArrows();
+ }
+ function setDesiredScroll(newDesiredScroll, dontUpdate) {
+ desiredScroll = Math.min(getMaxScroll(), Math.max(getMinScroll(),
+ newDesiredScroll));
+ if (! dontUpdate) {
+ updateScroll();
+ }
+ }
+ function updateScroll() {
+ updateScrollArrows();
+ scrollAnimator.scheduleAnimation();
+ }
+ function updateScrollArrows() {
+ $("#savedrevs-scrollleft").toggleClass("disabledscrollleft",
+ desiredScroll <= getMinScroll());
+ $("#savedrevs-scrollright").toggleClass("disabledscrollright",
+ desiredScroll >= getMaxScroll());
+ }
+ var scrollAnimator = padutils.makeAnimationScheduler(function() {
+ setDesiredScroll(desiredScroll, true); // re-clamp
+ if (Math.abs(desiredScroll - curScroll) < 1) {
+ setScroll(desiredScroll);
+ return false;
+ }
+ else {
+ setScroll(curScroll + (desiredScroll - curScroll)*0.5);
+ return true;
+ }
+ }, 50, 2);
+
+ var isSaving = false;
+ function setIsSaving(v) {
+ isSaving = v;
+ rerenderButton();
+ }
+
+ function haveReachedRevLimit() {
+ var mv = pad.getPrivilege('maxRevisions');
+ return (!(mv < 0 || mv > currentRevisionList.length));
+ }
+ function rerenderButton() {
+ if (isSaving || (! pad.isFullyConnected()) ||
+ haveReachedRevLimit()) {
+ $("#savedrevs-savenow").css('opacity', 0.75);
+ }
+ else {
+ $("#savedrevs-savenow").css('opacity', 1);
+ }
+ }
+
+ var scrollRepeatTimer = null;
+ var scrollStartTime = 0;
+ function setScrollRepeatTimer(dir) {
+ clearScrollRepeatTimer();
+ scrollStartTime = +new Date;
+ scrollRepeatTimer = window.setTimeout(function f() {
+ if (! scrollRepeatTimer) {
+ return;
+ }
+ self.scroll(dir);
+ var scrollTime = (+new Date) - scrollStartTime;
+ var delay = (scrollTime > 2000 ? 50 : 300);
+ scrollRepeatTimer = window.setTimeout(f, delay);
+ }, 300);
+ $(document).bind('mouseup', clearScrollRepeatTimer);
+ }
+ function clearScrollRepeatTimer() {
+ if (scrollRepeatTimer) {
+ window.clearTimeout(scrollRepeatTimer);
+ scrollRepeatTimer = null;
+ }
+ $(document).unbind('mouseup', clearScrollRepeatTimer);
+ }
+
+ var self = {
+ init: function(initialRevisions) {
+ self.newRevisionList(initialRevisions, true);
+
+ $("#savedrevs-savenow").click(function() { self.saveNow(); });
+ $("#savedrevs-scrollleft").mousedown(function() {
+ self.scroll('left');
+ setScrollRepeatTimer('left');
+ });
+ $("#savedrevs-scrollright").mousedown(function() {
+ self.scroll('right');
+ setScrollRepeatTimer('right');
+ });
+ $("#savedrevs-close").click(function() {paddocbar.setShownPanel(null);});
+
+ // update "saved n minutes ago" times
+ window.setInterval(function() {
+ refreshRevisionList();
+ }, 60*1000);
+ },
+ restoreRevision: function(rnum) {
+ var rev = currentRevisionList[rnum];
+ var warning = ("Restoring this revision will overwrite the current"
+ + " text of the pad. "+
+ "Are you sure you want to continue?");
+ var hidePanel = paddocbar.hideLaterIfNoOtherInteraction();
+ var box = getNthBox(rnum);
+ if (confirm(warning)) {
+ box.find(".srtwirly").show();
+ $.ajax({
+ type: 'get',
+ url: '/ep/pad/getrevisionatext',
+ data: {padId: pad.getPadId(), revId: rev.id},
+ success: success,
+ error: error
+ });
+ }
+ function success(resultJson) {
+ untwirl();
+ var result = JSON.parse(resultJson);
+ padeditor.restoreRevisionText(result);
+ window.setTimeout(function() {
+ hidePanel();
+ }, 0);
+ }
+ function error(e) {
+ untwirl();
+ alert("Oops! There was an error retreiving the text (revNum= "+
+ rev.revNum+"; padId="+pad.getPadId());
+ }
+ function untwirl() {
+ box.find(".srtwirly").hide();
+ }
+ },
+ showReachedLimit: function() {
+ alert("Sorry, you do not have privileges to save more than "+
+ pad.getPrivilege('maxRevisions')+" revisions.");
+ },
+ newRevisionList: function(lst, noAnimation) {
+ // server gives us list with newest first;
+ // we want chronological order
+ var L = reversedCopy(lst);
+ setRevisionList(L, noAnimation);
+ rerenderButton();
+ },
+ saveNow: function() {
+ if (isSaving) {
+ return;
+ }
+ if (! pad.isFullyConnected()) {
+ return;
+ }
+ if (haveReachedRevLimit()) {
+ self.showReachedLimit();
+ return;
+ }
+ setIsSaving(true);
+ var savedBy = pad.getUserName() || "unnamed";
+ pad.callWhenNotCommitting(submitSave);
+
+ function submitSave() {
+ $.ajax({
+ type: 'post',
+ url: '/ep/pad/saverevision',
+ data: {
+ padId: pad.getPadId(),
+ savedBy: savedBy,
+ savedById: pad.getUserId(),
+ revNum: pad.getCollabRevisionNumber()
+ },
+ success: success,
+ error: error
+ });
+ }
+ function success(text) {
+ setIsSaving(false);
+ var newRevisionList = JSON.parse(text);
+ self.newRevisionList(newRevisionList);
+ pad.sendClientMessage({
+ type: 'newRevisionList',
+ revisionList: newRevisionList,
+ savedBy: savedBy
+ });
+ }
+ function error(e) {
+ setIsSaving(false);
+ alert("Oops! The server failed to save the revision. Please try again later.");
+ }
+ },
+ handleResizePage: function() {
+ updateScrollArrows();
+ },
+ handleIsFullyConnected: function(isConnected) {
+ rerenderButton();
+ },
+ scroll: function(dir) {
+ var minScroll = getMinScroll();
+ var maxScroll = getMaxScroll();
+ if (dir == 'left') {
+ if (desiredScroll > minScroll) {
+ var n = Math.floor((desiredScroll - 1 - minScroll) /
+ REVISION_BOX_WIDTH);
+ setDesiredScroll(Math.max(0, n)*REVISION_BOX_WIDTH + minScroll);
+ }
+ }
+ else if (dir == 'right') {
+ if (desiredScroll < maxScroll) {
+ var n = Math.floor((maxScroll - desiredScroll - 1) /
+ REVISION_BOX_WIDTH);
+ setDesiredScroll(maxScroll - Math.max(0, n)*REVISION_BOX_WIDTH);
+ }
+ }
+ }
+ };
+ return self;
+}()); \ No newline at end of file
diff --git a/trunk/etherpad/src/static/js/pad_userlist.js b/trunk/etherpad/src/static/js/pad_userlist.js
new file mode 100644
index 0000000..13bfd6a
--- /dev/null
+++ b/trunk/etherpad/src/static/js/pad_userlist.js
@@ -0,0 +1,604 @@
+/**
+ * Copyright 2009 Google 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.
+ */
+
+
+var paduserlist = (function() {
+
+ var rowManager = (function() {
+ // The row manager handles rendering rows of the user list and animating
+ // their insertion, removal, and reordering. It manipulates TD height
+ // and TD opacity.
+
+ function nextRowId() {
+ return "usertr"+(nextRowId.counter++);
+ }
+ nextRowId.counter = 1;
+ // objects are shared; fields are "domId","data","animationStep"
+ var rowsFadingOut = []; // unordered set
+ var rowsFadingIn = []; // unordered set
+ var rowsPresent = []; // in order
+
+ var ANIMATION_START = -12; // just starting to fade in
+ var ANIMATION_END = 12; // just finishing fading out
+ function getAnimationHeight(step, power) {
+ var a = Math.abs(step/12);
+ if (power == 2) a = a*a;
+ else if (power == 3) a = a*a*a;
+ else if (power == 4) a = a*a*a*a;
+ else if (power >= 5) a = a*a*a*a*a;
+ return Math.round(26*(1-a));
+ }
+ var OPACITY_STEPS = 6;
+
+ var ANIMATION_STEP_TIME = 20;
+ var LOWER_FRAMERATE_FACTOR = 2;
+ var scheduleAnimation = padutils.makeAnimationScheduler(animateStep, ANIMATION_STEP_TIME,
+ LOWER_FRAMERATE_FACTOR).scheduleAnimation;
+
+ var NUMCOLS = 4;
+
+ // we do lots of manipulation of table rows and stuff that JQuery makes ok, despite
+ // IE's poor handling when manipulating the DOM directly.
+
+ function getEmptyRowHtml(height) {
+ return '<td colspan="'+NUMCOLS+'" style="border:0;height:'+height+'px"><!-- --></td>';
+ }
+ function isNameEditable(data) {
+ return (! data.name) && (data.status != 'Disconnected');
+ }
+ function replaceUserRowContents(tr, height, data) {
+ var tds = getUserRowHtml(height, data).match(/<td.*?<\/td>/gi);
+ if (isNameEditable(data) && tr.find("td.usertdname input:enabled").length > 0) {
+ // preserve input field node
+ for(var i=0; i<tds.length; i++) {
+ var oldTd = $(tr.find("td").get(i));
+ if (! oldTd.hasClass('usertdname')) {
+ oldTd.replaceWith(tds[i]);
+ }
+ }
+ }
+ else {
+ tr.html(tds.join(''));
+ }
+ return tr;
+ }
+ function getUserRowHtml(height, data) {
+ var nameHtml;
+ var isGuest = (data.id.charAt(0) != 'p');
+ if (data.name) {
+ nameHtml = padutils.escapeHtml(data.name);
+ if (isGuest && pad.getIsProPad()) {
+ nameHtml += ' (Guest)';
+ }
+ }
+ else {
+ nameHtml = '<input type="text" class="editempty newinput" value="unnamed" '+
+ (isNameEditable(data) ? '' : 'disabled="disabled" ')+
+ '/>';
+ }
+
+ return ['<td style="height:',height,'px" class="usertdswatch"><div class="swatch" style="background:'+data.color+'">&nbsp;</div></td>',
+ '<td style="height:',height,'px" class="usertdname">',nameHtml,'</td>',
+ '<td style="height:',height,'px" class="usertdstatus">',padutils.escapeHtml(data.status),'</td>',
+ '<td style="height:',height,'px" class="activity">',padutils.escapeHtml(data.activity),'</td>'].join('');
+ }
+ function getRowHtml(id, innerHtml) {
+ return '<tr id="'+id+'">'+innerHtml+'</tr>';
+ }
+ function rowNode(row) {
+ return $("#"+row.domId);
+ }
+ function handleRowData(row) {
+ if (row.data && row.data.status == 'Disconnected') {
+ row.opacity = 0.5;
+ }
+ else {
+ delete row.opacity;
+ }
+ }
+ function handleRowNode(tr, data) {
+ if (data.titleText) {
+ tr.attr('title', data.titleText);
+ }
+ else {
+ tr.removeAttr('title');
+ }
+ }
+ function handleOtherUserInputs() {
+ // handle 'INPUT' elements for naming other unnamed users
+ $("#otheruserstable input.newinput").each(function() {
+ var input = $(this);
+ var tr = input.closest("tr");
+ if (tr.length > 0) {
+ var index = tr.parent().children().index(tr);
+ if (index >= 0) {
+ var userId = rowsPresent[index].data.id;
+ rowManagerMakeNameEditor($(this), userId);
+ }
+ }
+ }).removeClass('newinput');
+ }
+
+ // animationPower is 0 to skip animation, 1 for linear, 2 for quadratic, etc.
+ function insertRow(position, data, animationPower) {
+ position = Math.max(0, Math.min(rowsPresent.length, position));
+ animationPower = (animationPower === undefined ? 4 : animationPower);
+
+ var domId = nextRowId();
+ var row = {data: data, animationStep: ANIMATION_START, domId: domId,
+ animationPower: animationPower};
+ handleRowData(row);
+ rowsPresent.splice(position, 0, row);
+ var tr;
+ if (animationPower == 0) {
+ tr = $(getRowHtml(domId, getUserRowHtml(getAnimationHeight(0), data)));
+ row.animationStep = 0;
+ }
+ else {
+ rowsFadingIn.push(row);
+ tr = $(getRowHtml(domId, getEmptyRowHtml(getAnimationHeight(ANIMATION_START))));
+ }
+ handleRowNode(tr, data);
+ if (position == 0) {
+ $("table#otheruserstable").prepend(tr);
+ }
+ else {
+ rowNode(rowsPresent[position-1]).after(tr);
+ }
+
+ if (animationPower != 0) {
+ scheduleAnimation();
+ }
+
+ handleOtherUserInputs();
+
+ return row;
+ }
+
+ function updateRow(position, data) {
+ var row = rowsPresent[position];
+ if (row) {
+ row.data = data;
+ handleRowData(row);
+ if (row.animationStep == 0) {
+ // not currently animating
+ var tr = rowNode(row);
+ replaceUserRowContents(tr, getAnimationHeight(0), row.data).find(
+ "td").css('opacity', (row.opacity === undefined ? 1 : row.opacity));
+ handleRowNode(tr, data);
+ handleOtherUserInputs();
+ }
+ }
+ }
+
+ function removeRow(position, animationPower) {
+ animationPower = (animationPower === undefined ? 4 : animationPower);
+ var row = rowsPresent[position];
+ if (row) {
+ rowsPresent.splice(position, 1); // remove
+ if (animationPower == 0) {
+ rowNode(row).remove();
+ }
+ else {
+ row.animationStep = - row.animationStep; // use symmetry
+ row.animationPower = animationPower;
+ rowsFadingOut.push(row);
+ scheduleAnimation();
+ }
+ }
+ }
+
+ // newPosition is position after the row has been removed
+ function moveRow(oldPosition, newPosition, animationPower) {
+ animationPower = (animationPower === undefined ? 1 : animationPower); // linear is best
+ var row = rowsPresent[oldPosition];
+ if (row && oldPosition != newPosition) {
+ var rowData = row.data;
+ removeRow(oldPosition, animationPower);
+ insertRow(newPosition, rowData, animationPower);
+ }
+ }
+
+ function animateStep() {
+ // animation must be symmetrical
+ for(var i=rowsFadingIn.length-1;i>=0;i--) { // backwards to allow removal
+ var row = rowsFadingIn[i];
+ var step = ++row.animationStep;
+ var animHeight = getAnimationHeight(step, row.animationPower);
+ var node = rowNode(row);
+ var baseOpacity = (row.opacity === undefined ? 1 : row.opacity);
+ if (step <= -OPACITY_STEPS) {
+ node.find("td").height(animHeight);
+ }
+ else if (step == -OPACITY_STEPS+1) {
+ node.html(getUserRowHtml(animHeight, row.data)).find("td").css(
+ 'opacity', baseOpacity*1/OPACITY_STEPS);
+ handleRowNode(node, row.data);
+ }
+ else if (step < 0) {
+ node.find("td").css('opacity', baseOpacity*(OPACITY_STEPS-(-step))/OPACITY_STEPS).height(animHeight);
+ }
+ else if (step == 0) {
+ // set HTML in case modified during animation
+ node.html(getUserRowHtml(animHeight, row.data)).find("td").css(
+ 'opacity', baseOpacity*1).height(animHeight);
+ handleRowNode(node, row.data);
+ rowsFadingIn.splice(i, 1); // remove from set
+ }
+ }
+ for(var i=rowsFadingOut.length-1;i>=0;i--) { // backwards to allow removal
+ var row = rowsFadingOut[i];
+ var step = ++row.animationStep;
+ var node = rowNode(row);
+ var animHeight = getAnimationHeight(step, row.animationPower);
+ var baseOpacity = (row.opacity === undefined ? 1 : row.opacity);
+ if (step < OPACITY_STEPS) {
+ node.find("td").css('opacity', baseOpacity*(OPACITY_STEPS - step)/OPACITY_STEPS).height(animHeight);
+ }
+ else if (step == OPACITY_STEPS) {
+ node.html(getEmptyRowHtml(animHeight));
+ }
+ else if (step <= ANIMATION_END) {
+ node.find("td").height(animHeight);
+ }
+ else {
+ rowsFadingOut.splice(i, 1); // remove from set
+ node.remove();
+ }
+ }
+
+ handleOtherUserInputs();
+
+ return (rowsFadingIn.length > 0) || (rowsFadingOut.length > 0); // is more to do
+ }
+
+ var self = {
+ insertRow: insertRow,
+ removeRow: removeRow,
+ moveRow: moveRow,
+ updateRow: updateRow
+ };
+ return self;
+ }()); ////////// rowManager
+
+ var myUserInfo = {};
+ var otherUsersInfo = [];
+ var otherUsersData = [];
+ var colorPickerOpen = false;
+
+ function rowManagerMakeNameEditor(jnode, userId) {
+ setUpEditable(jnode, function() {
+ var existingIndex = findExistingIndex(userId);
+ if (existingIndex >= 0) {
+ return otherUsersInfo[existingIndex].name || '';
+ }
+ else {
+ return '';
+ }
+ }, function(newName) {
+ if (! newName) {
+ jnode.addClass("editempty");
+ jnode.val("unnamed");
+ }
+ else {
+ jnode.attr('disabled', 'disabled');
+ pad.suggestUserName(userId, newName);
+ }
+ });
+ }
+
+ function renderMyUserInfo() {
+ if (myUserInfo.name) {
+ $("#myusernameedit").removeClass("editempty").val(
+ myUserInfo.name);
+ }
+ else {
+ $("#myusernameedit").addClass("editempty").val(
+ "< enter your name >");
+ }
+ if (colorPickerOpen) {
+ $("#myswatchbox").addClass('myswatchboxunhoverable').removeClass(
+ 'myswatchboxhoverable');
+ }
+ else {
+ $("#myswatchbox").addClass('myswatchboxhoverable').removeClass(
+ 'myswatchboxunhoverable');
+ }
+ $("#myswatch").css('background', pad.getColorPalette()[myUserInfo.colorId]);
+ }
+
+ function findExistingIndex(userId) {
+ var existingIndex = -1;
+ for(var i=0;i<otherUsersInfo.length;i++) {
+ if (otherUsersInfo[i].userId == userId) {
+ existingIndex = i;
+ break;
+ }
+ }
+ return existingIndex;
+ }
+
+ function setUpEditable(jqueryNode, valueGetter, valueSetter) {
+ jqueryNode.bind('focus', function(evt) {
+ var oldValue = valueGetter();
+ if (jqueryNode.val() !== oldValue) {
+ jqueryNode.val(oldValue);
+ }
+ jqueryNode.addClass("editactive").removeClass("editempty");
+ });
+ jqueryNode.bind('blur', function(evt) {
+ var newValue = jqueryNode.removeClass("editactive").val();
+ valueSetter(newValue);
+ });
+ padutils.bindEnterAndEscape(jqueryNode, function onEnter() {
+ jqueryNode.blur();
+ }, function onEscape() {
+ jqueryNode.val(valueGetter()).blur();
+ });
+ jqueryNode.removeAttr('disabled').addClass('editable');
+ }
+
+ function showColorPicker() {
+ if (! colorPickerOpen) {
+ var palette = pad.getColorPalette();
+ for(var i=0;i<palette.length;i++) {
+ $("#mycolorpicker .n"+(i+1)+" .pickerswatch").css(
+ 'background', palette[i]);
+ }
+ $("#mycolorpicker").css('display', 'block');
+ colorPickerOpen = true;
+ renderMyUserInfo();
+ }
+ // this part happens even if color picker is already open
+ $("#mycolorpicker .pickerswatchouter").removeClass('picked');
+ $("#mycolorpicker .pickerswatchouter:eq("+(myUserInfo.colorId||0)+")").
+ addClass('picked');
+ }
+ function getColorPickerSwatchIndex(jnode) {
+ return Number(jnode.get(0).className.match(/\bn([0-9]+)\b/)[1])-1;
+ }
+ function closeColorPicker(accept) {
+ if (accept) {
+ var newColorId = getColorPickerSwatchIndex($("#mycolorpicker .picked"));
+ if (newColorId >= 0) { // fails on NaN
+ myUserInfo.colorId = newColorId;
+ pad.notifyChangeColor(newColorId);
+ }
+ }
+ colorPickerOpen = false;
+ $("#mycolorpicker").css('display', 'none');
+ renderMyUserInfo();
+ }
+
+ function updateInviteNotice() {
+ if (otherUsersInfo.length == 0) {
+ $("#otheruserstable").hide();
+ $("#nootherusers").show();
+ }
+ else {
+ $("#nootherusers").hide();
+ $("#otheruserstable").show();
+ }
+ }
+
+ var knocksToIgnore = {};
+ var guestPromptFlashState = 0;
+ var guestPromptFlash = padutils.makeAnimationScheduler(
+ function () {
+ var prompts = $("#guestprompts .guestprompt");
+ if (prompts.length == 0) {
+ return false; // no more to do
+ }
+
+ guestPromptFlashState = 1 - guestPromptFlashState;
+ if (guestPromptFlashState) {
+ prompts.css('background', '#ffa');
+ }
+ else {
+ prompts.css('background', '#ffe');
+ }
+
+ return true;
+ }, 1000);
+
+ var self = {
+ init: function(myInitialUserInfo) {
+ self.setMyUserInfo(myInitialUserInfo);
+
+ $("#otheruserstable tr").remove();
+
+ if (pad.getUserIsGuest()) {
+ $("#myusernameedit").addClass('myusernameedithoverable');
+ setUpEditable($("#myusernameedit"),
+ function() {
+ return myUserInfo.name || '';
+ },
+ function(newValue) {
+ myUserInfo.name = newValue;
+ pad.notifyChangeName(newValue);
+ // wrap with setTimeout to do later because we get
+ // a double "blur" fire in IE...
+ window.setTimeout(function() {
+ renderMyUserInfo();
+ }, 0);
+ });
+ }
+
+ // color picker
+ $("#myswatchbox").click(showColorPicker);
+ $("#mycolorpicker .pickerswatchouter").click(function() {
+ $("#mycolorpicker .pickerswatchouter").removeClass('picked');
+ $(this).addClass('picked');
+ });
+ $("#mycolorpickersave").click(function() {
+ closeColorPicker(true);
+ });
+ $("#mycolorpickercancel").click(function() {
+ closeColorPicker(false);
+ });
+ //
+
+ },
+ setMyUserInfo: function(info) {
+ myUserInfo = $.extend({}, info);
+
+ renderMyUserInfo();
+ },
+ userJoinOrUpdate: function(info) {
+ if ((! info.userId) || (info.userId == myUserInfo.userId)) {
+ // not sure how this would happen
+ return;
+ }
+
+ var userData = {};
+ userData.color = pad.getColorPalette()[info.colorId];
+ userData.name = info.name;
+ userData.status = '';
+ userData.activity = '';
+ userData.id = info.userId;
+ // Firefox ignores \n in title text; Safari does a linebreak
+ userData.titleText = [info.userAgent||'', info.ip||''].join(' \n');
+
+ var existingIndex = findExistingIndex(info.userId);
+
+ var numUsersBesides = otherUsersInfo.length;
+ if (existingIndex >= 0) {
+ numUsersBesides--;
+ }
+ var newIndex = padutils.binarySearch(numUsersBesides, function(n) {
+ if (existingIndex >= 0 && n >= existingIndex) {
+ // pretend existingIndex isn't there
+ n++;
+ }
+ var infoN = otherUsersInfo[n];
+ var nameN = (infoN.name||'').toLowerCase();
+ var nameThis = (info.name||'').toLowerCase();
+ var idN = infoN.userId;
+ var idThis = info.userId;
+ return (nameN > nameThis) || (nameN == nameThis &&
+ idN > idThis);
+ });
+
+ if (existingIndex >= 0) {
+ // update
+ if (existingIndex == newIndex) {
+ otherUsersInfo[existingIndex] = info;
+ otherUsersData[existingIndex] = userData;
+ rowManager.updateRow(existingIndex, userData);
+ }
+ else {
+ otherUsersInfo.splice(existingIndex, 1);
+ otherUsersData.splice(existingIndex, 1);
+ otherUsersInfo.splice(newIndex, 0, info);
+ otherUsersData.splice(newIndex, 0, userData);
+ rowManager.updateRow(existingIndex, userData);
+ rowManager.moveRow(existingIndex, newIndex);
+ }
+ }
+ else {
+ otherUsersInfo.splice(newIndex, 0, info);
+ otherUsersData.splice(newIndex, 0, userData);
+ rowManager.insertRow(newIndex, userData);
+ }
+
+ updateInviteNotice();
+ },
+ userLeave: function(info) {
+ var existingIndex = findExistingIndex(info.userId);
+ if (existingIndex >= 0) {
+ var userData = otherUsersData[existingIndex];
+ userData.status = 'Disconnected';
+ rowManager.updateRow(existingIndex, userData);
+ if (userData.leaveTimer) {
+ window.clearTimeout(userData.leaveTimer);
+ }
+ // set up a timer that will only fire if no leaves,
+ // joins, or updates happen for this user in the
+ // next N seconds, to remove the user from the list.
+ var thisUserId = info.userId;
+ var thisLeaveTimer = window.setTimeout(function() {
+ var newExistingIndex = findExistingIndex(thisUserId);
+ if (newExistingIndex >= 0) {
+ var newUserData = otherUsersData[newExistingIndex];
+ if (newUserData.status == 'Disconnected' &&
+ newUserData.leaveTimer == thisLeaveTimer) {
+ otherUsersInfo.splice(newExistingIndex, 1);
+ otherUsersData.splice(newExistingIndex, 1);
+ rowManager.removeRow(newExistingIndex);
+ updateInviteNotice();
+ }
+ }
+ }, 8000); // how long to wait
+ userData.leaveTimer = thisLeaveTimer;
+ }
+ updateInviteNotice();
+ },
+ showGuestPrompt: function(userId, displayName) {
+ if (knocksToIgnore[userId]) {
+ return;
+ }
+
+ var encodedUserId = padutils.encodeUserId(userId);
+
+ var actionName = 'hide-guest-prompt-'+encodedUserId;
+ padutils.cancelActions(actionName);
+
+ var box = $("#guestprompt-"+encodedUserId);
+ if (box.length == 0) {
+ // make guest prompt box
+ box = $('<div id="guestprompt-'+encodedUserId+'" class="guestprompt"><div class="choices"><a href="javascript:void(paduserlist.answerGuestPrompt(\''+encodedUserId+'\',false))">Deny</a> <a href="javascript:void(paduserlist.answerGuestPrompt(\''+encodedUserId+'\',true))">Approve</a></div><div class="guestname"><strong>Guest:</strong> '+padutils.escapeHtml(displayName)+'</div></div>');
+ $("#guestprompts").append(box);
+ }
+ else {
+ // update display name
+ box.find(".guestname").html('<strong>Guest:</strong> '+padutils.escapeHtml(displayName));
+ }
+ var hideLater = padutils.getCancellableAction(actionName, function() {
+ self.removeGuestPrompt(userId);
+ });
+ window.setTimeout(hideLater, 15000); // time-out with no knock
+
+ guestPromptFlash.scheduleAnimation();
+ },
+ removeGuestPrompt: function(userId) {
+ var box = $("#guestprompt-"+padutils.encodeUserId(userId));
+ // remove ID now so a new knock by same user gets new, unfaded box
+ box.removeAttr('id').fadeOut("fast", function() {
+ box.remove();
+ });
+
+ knocksToIgnore[userId] = true;
+ window.setTimeout(function() {
+ delete knocksToIgnore[userId];
+ }, 5000);
+ },
+ answerGuestPrompt: function(encodedUserId, approve) {
+ var guestId = padutils.decodeUserId(encodedUserId);
+
+ var msg = {
+ type: 'guestanswer',
+ authId: pad.getUserId(),
+ guestId: guestId,
+ answer: (approve ? "approved" : "denied")
+ };
+ pad.sendClientMessage(msg);
+
+ self.removeGuestPrompt(guestId);
+ }
+ };
+ return self;
+}());
+
diff --git a/trunk/etherpad/src/static/js/pad_utils.js b/trunk/etherpad/src/static/js/pad_utils.js
new file mode 100644
index 0000000..de606ad
--- /dev/null
+++ b/trunk/etherpad/src/static/js/pad_utils.js
@@ -0,0 +1,359 @@
+/**
+ * Copyright 2009 Google 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.
+ */
+
+var padutils = {
+ escapeHtml: function(x) {
+ return String(x).replace(/\</g, '&lt;').replace(/\>/g, '&gt;');
+ },
+ uniqueId: function() {
+ function encodeNum(n, width) {
+ // returns string that is exactly 'width' chars, padding with zeros
+ // and taking rightmost digits
+ return (Array(width+1).join('0') + Number(n).toString(35)).slice(-width);
+ }
+ return [pad.getClientIp(),
+ encodeNum(+new Date, 7),
+ encodeNum(Math.floor(Math.random()*1e9), 4)].join('.');
+ },
+ uaDisplay: function(ua) {
+ var m;
+
+ function clean(a) {
+ var maxlen = 16;
+ a = a.replace(/[^a-zA-Z0-9\.]/g, '');
+ if (a.length > maxlen) {
+ a = a.substr(0,maxlen);
+ }
+ return a;
+ }
+
+ function checkver(name) {
+ var m = ua.match(RegExp(name + '\\/([\\d\\.]+)'));
+ if (m && m.length > 1) {
+ return clean(name+m[1]);
+ }
+ return null;
+ }
+
+ // firefox
+ if (checkver('Firefox')) { return checkver('Firefox'); }
+
+ // misc browsers, including IE
+ m = ua.match(/compatible; ([^;]+);/);
+ if (m && m.length > 1) {
+ return clean(m[1]);
+ }
+
+ // iphone
+ if (ua.match(/\(iPhone;/)) {
+ return 'iPhone';
+ }
+
+ // chrome
+ if (checkver('Chrome')) { return checkver('Chrome'); }
+
+ // safari
+ m = ua.match(/Safari\/[\d\.]+/);
+ if (m) {
+ var v = '?';
+ m = ua.match(/Version\/([\d\.]+)/);
+ if (m && m.length > 1) {
+ v = m[1];
+ }
+ return clean('Safari'+v);
+ }
+
+ // everything else
+ var x = ua.split(' ')[0];
+ return clean(x);
+ },
+ // "func" is a function over 0..(numItems-1) that is monotonically
+ // "increasing" with index (false, then true). Finds the boundary
+ // between false and true, a number between 0 and numItems inclusive.
+ binarySearch: function (numItems, func) {
+ if (numItems < 1) return 0;
+ if (func(0)) return 0;
+ if (! func(numItems-1)) return numItems;
+ var low = 0; // func(low) is always false
+ var high = numItems-1; // func(high) is always true
+ while ((high - low) > 1) {
+ var x = Math.floor((low+high)/2); // x != low, x != high
+ if (func(x)) high = x;
+ else low = x;
+ }
+ return high;
+ },
+ // e.g. "Thu Jun 18 2009 13:09"
+ simpleDateTime: function(date) {
+ var d = new Date(+date); // accept either number or date
+ var dayOfWeek = (['Sun','Mon','Tue','Wed','Thu','Fri','Sat'])[d.getDay()];
+ var month = (['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'])[d.getMonth()];
+ var dayOfMonth = d.getDate();
+ var year = d.getFullYear();
+ var hourmin = d.getHours()+":"+("0"+d.getMinutes()).slice(-2);
+ return dayOfWeek+' '+month+' '+dayOfMonth+' '+year+' '+hourmin;
+ },
+ findURLs: function(text) {
+ // copied from ACE
+ var _REGEX_WORDCHAR = /[\u0030-\u0039\u0041-\u005A\u0061-\u007A\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\u1FFF\u3040-\u9FFF\uF900-\uFDFF\uFE70-\uFEFE\uFF10-\uFF19\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFDC]/;
+ var _REGEX_URLCHAR = new RegExp('('+/[-:@a-zA-Z0-9_.,~%+\/?=&#;()$]/.source+'|'+_REGEX_WORDCHAR.source+')');
+ var _REGEX_URL = new RegExp(/(?:(?:https?|s?ftp|ftps|file|smb|afp|nfs|(x-)?man|gopher|txmt):\/\/|mailto:)/.source+_REGEX_URLCHAR.source+'*(?![:.,;])'+_REGEX_URLCHAR.source, 'g');
+
+ // returns null if no URLs, or [[startIndex1, url1], [startIndex2, url2], ...]
+ function _findURLs(text) {
+ _REGEX_URL.lastIndex = 0;
+ var urls = null;
+ var execResult;
+ while ((execResult = _REGEX_URL.exec(text))) {
+ urls = (urls || []);
+ var startIndex = execResult.index;
+ var url = execResult[0];
+ urls.push([startIndex, url]);
+ }
+
+ return urls;
+ }
+
+ return _findURLs(text);
+ },
+ escapeHtmlWithClickableLinks: function(text, target) {
+ var idx = 0;
+ var pieces = [];
+ var urls = padutils.findURLs(text);
+ function advanceTo(i) {
+ if (i > idx) {
+ pieces.push(padutils.escapeHtml(text.substring(idx, i)));
+ idx = i;
+ }
+ }
+ if (urls) {
+ for(var j=0;j<urls.length;j++) {
+ var startIndex = urls[j][0];
+ var href = urls[j][1];
+ advanceTo(startIndex);
+ pieces.push('<a ', (target?'target="'+target+'" ':''),
+ 'href="', href.replace(/\"/g, '&quot;'), '">');
+ advanceTo(startIndex + href.length);
+ pieces.push('</a>');
+ }
+ }
+ advanceTo(text.length);
+ return pieces.join('');
+ },
+ bindEnterAndEscape: function(node, onEnter, onEscape) {
+ function handleKey(evt) {
+ if (evt.which == 27 && onEscape) {
+ // "escape" key
+ if (evt.type == 'keydown') {
+ onEscape(evt);
+ }
+ evt.preventDefault();
+ }
+ else if (evt.which == 13 && onEnter) {
+ // return/enter
+ if (evt.type == 'keyup') {
+ onEnter(evt);
+ }
+ evt.preventDefault();
+ }
+ }
+ $(node).bind('keyup keypress keydown', handleKey);
+ },
+ timediff: function(d) {
+ function format(n, word) {
+ n = Math.round(n);
+ return ('' + n + ' ' + word + (n != 1 ? 's' : '') + ' ago');
+ }
+ d = Math.max(0, (+(new Date) - (+d) - pad.clientTimeOffset) / 1000);
+ if (d < 60) { return format(d, 'second'); }
+ d /= 60;
+ if (d < 60) { return format(d, 'minute'); }
+ d /= 60;
+ if (d < 24) { return format(d, 'hour'); }
+ d /= 24;
+ return format(d, 'day');
+ },
+ makeAnimationScheduler: function(funcToAnimateOneStep, stepTime, stepsAtOnce) {
+ if (stepsAtOnce === undefined) {
+ stepsAtOnce = 1;
+ }
+
+ var animationTimer = null;
+
+ function scheduleAnimation() {
+ if (! animationTimer) {
+ animationTimer = window.setTimeout(function() {
+ animationTimer = null;
+ var n = stepsAtOnce;
+ var moreToDo = true;
+ while (moreToDo && n > 0) {
+ moreToDo = funcToAnimateOneStep();
+ n--;
+ }
+ if (moreToDo) {
+ // more to do
+ scheduleAnimation();
+ }
+ }, stepTime*stepsAtOnce);
+ }
+ }
+ return { scheduleAnimation: scheduleAnimation };
+ },
+ makeShowHideAnimator: function(funcToArriveAtState, initiallyShown, fps, totalMs) {
+ var animationState = (initiallyShown ? 0 : -2); // -2 hidden, -1 to 0 fade in, 0 to 1 fade out
+ var animationFrameDelay = 1000 / fps;
+ var animationStep = animationFrameDelay / totalMs;
+
+ var scheduleAnimation =
+ padutils.makeAnimationScheduler(animateOneStep, animationFrameDelay).scheduleAnimation;
+
+ function doShow() {
+ animationState = -1;
+ funcToArriveAtState(animationState);
+ scheduleAnimation();
+ }
+
+ function doQuickShow() { // start showing without losing any fade-in progress
+ if (animationState < -1) {
+ animationState = -1;
+ }
+ else if (animationState <= 0) {
+ animationState = animationState;
+ }
+ else {
+ animationState = Math.max(-1, Math.min(0, - animationState));
+ }
+ funcToArriveAtState(animationState);
+ scheduleAnimation();
+ }
+
+ function doHide() {
+ if (animationState >= -1 && animationState <= 0) {
+ animationState = 1e-6;
+ scheduleAnimation();
+ }
+ }
+
+ function animateOneStep() {
+ if (animationState < -1 || animationState == 0) {
+ return false;
+ }
+ else if (animationState < 0) {
+ // animate show
+ animationState += animationStep;
+ if (animationState >= 0) {
+ animationState = 0;
+ funcToArriveAtState(animationState);
+ return false;
+ }
+ else {
+ funcToArriveAtState(animationState);
+ return true;
+ }
+ }
+ else if (animationState > 0) {
+ // animate hide
+ animationState += animationStep;
+ if (animationState >= 1) {
+ animationState = 1;
+ funcToArriveAtState(animationState);
+ animationState = -2;
+ return false;
+ }
+ else {
+ funcToArriveAtState(animationState);
+ return true;
+ }
+ }
+ }
+
+ return {show: doShow, hide: doHide, quickShow: doQuickShow};
+ },
+ _nextActionId: 1,
+ uncanceledActions: {},
+ getCancellableAction: function(actionType, actionFunc) {
+ var o = padutils.uncanceledActions[actionType];
+ if (! o) {
+ o = {};
+ padutils.uncanceledActions[actionType] = o;
+ }
+ var actionId = (padutils._nextActionId++);
+ o[actionId] = true;
+ return function() {
+ var p = padutils.uncanceledActions[actionType];
+ if (p && p[actionId]) {
+ actionFunc();
+ }
+ };
+ },
+ cancelActions: function(actionType) {
+ var o = padutils.uncanceledActions[actionType];
+ if (o) {
+ // clear it
+ delete padutils.uncanceledActions[actionType];
+ }
+ },
+ makeFieldLabeledWhenEmpty: function(field, labelText) {
+ field = $(field);
+ function clear() {
+ field.addClass('editempty');
+ field.val(labelText);
+ }
+ field.focus(function() {
+ if (field.hasClass('editempty')) {
+ field.val('');
+ }
+ field.removeClass('editempty');
+ });
+ field.blur(function() {
+ if (! field.val()) {
+ clear();
+ }
+ });
+ return {clear:clear};
+ },
+ getCheckbox: function(node) {
+ return $(node).is(':checked');
+ },
+ setCheckbox: function(node, value) {
+ if (value) {
+ $(node).attr('checked', 'checked');
+ }
+ else {
+ $(node).removeAttr('checked');
+ }
+ },
+ bindCheckboxChange: function(node, func) {
+ $(node).bind("click change", func);
+ },
+ encodeUserId: function(userId) {
+ return userId.replace(/[^a-y0-9]/g, function(c) {
+ if (c == ".") return "-";
+ return 'z'+c.charCodeAt(0)+'z';
+ });
+ },
+ decodeUserId: function(encodedUserId) {
+ return encodedUserId.replace(/[a-y0-9]+|-|z.+?z/g, function(cc) {
+ if (cc == '-') return '.';
+ else if (cc.charAt(0) == 'z') {
+ return String.fromCharCode(Number(cc.slice(1,-1)));
+ }
+ else {
+ return cc;
+ }
+ });
+ }
+};
diff --git a/trunk/etherpad/src/static/js/pricing.js b/trunk/etherpad/src/static/js/pricing.js
new file mode 100644
index 0000000..913d5ce
--- /dev/null
+++ b/trunk/etherpad/src/static/js/pricing.js
@@ -0,0 +1,19 @@
+/**
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS-IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+$(function() {
+ $('#buylink').click(function() { window.location = "/ep/store/eepnet-checkout"; return false; });
+}); \ No newline at end of file
diff --git a/trunk/etherpad/src/static/js/pro/guest-knock-client.js b/trunk/etherpad/src/static/js/pro/guest-knock-client.js
new file mode 100644
index 0000000..bace225
--- /dev/null
+++ b/trunk/etherpad/src/static/js/pro/guest-knock-client.js
@@ -0,0 +1,53 @@
+/**
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS-IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+function knock() {
+ $.ajax({
+ type: "POST",
+ url: "/ep/account/guest-knock",
+ cache: false,
+ data: {
+ padId: clientVars.localPadId,
+ guestDisplayName: clientVars.guestDisplayName
+ },
+ success: knockReply,
+ error: knockError
+ });
+}
+
+function knockReply(responseText) {
+ //console.log("knockReply: "+responseText);
+ if (responseText == "approved") {
+ window.location.href = clientVars.padUrl;
+ }
+ if (responseText == "denied") {
+ $("#guest-knock-box").hide();
+ $("#guest-knock-denied").show();
+ }
+ if (responseText == "wait") {
+ setTimeout(knock, 1000);
+ }
+}
+
+function knockError() {
+ alert("There was an error requesting access to the pad. Kindly report this by sending email to bugs@pad.spline.inf.fu-berlin.de.");
+}
+
+$(document).ready(function() {
+ knock();
+});
+
diff --git a/trunk/etherpad/src/static/js/pro/pro-padlist-client.js b/trunk/etherpad/src/static/js/pro/pro-padlist-client.js
new file mode 100644
index 0000000..ba50d95
--- /dev/null
+++ b/trunk/etherpad/src/static/js/pro/pro-padlist-client.js
@@ -0,0 +1,104 @@
+/**
+ * Copyright 2009 Google 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.
+ */
+
+if (!window.etherpad) {
+ etherpad = {};
+}
+if (!window.etherpad.pro) {
+ etherpad.pro = {};
+}
+
+etherpad.pro.padlist = {};
+
+$(document).ready(function() {
+
+ function getTargetPadId(target) {
+ var padmetaId = $(target).attr('id').split('-')[2];
+ //console.log("padmetaId = "+padmetaId);
+ return clientVars.localPadIds[padmetaId];
+ }
+
+ var padActionsMenu = [
+ {"View Read-Only": {
+ onclick: function(menuItem, menu) {
+ var localPadId = getTargetPadId(menu.target);
+ window.location.href = ("/ep/pad/view/"+localPadId+"/latest");
+ },
+ icon: '/static/img/pro/padlist/paper-icon.gif'
+ }
+ },
+ $.contextMenu.separator,
+ {"Archive": {
+ onclick: function(menuItem, menu) {
+ var localPadId = getTargetPadId(menu.target);
+ etherpad.pro.padlist.toggleArchivePad(localPadId);
+ }
+ }
+ },
+ {"Delete": {
+ onclick: function(menuItem, menu) {
+ var localPadId = getTargetPadId(menu.target);
+ etherpad.pro.padlist.deletePad(localPadId);
+ },
+ icon: '/static/img/pro/padlist/trash-icon.gif'
+ }
+ }
+ ];
+
+ if (clientVars.showingArchivedPads) {
+ padActionsMenu[2]["Un-archive"] = padActionsMenu[2]["Archive"];
+ delete padActionsMenu[2]["Archive"];
+ }
+
+ $('.gear-drop').contextMenu(padActionsMenu, {
+ theme: 'gloss,gloss-cyan',
+ bindTarget: 'click',
+ beforeShow: function() {
+ var localPadId = getTargetPadId(this.target);
+ $('tr.selected').removeClass('selected');
+ $('tr#pad-row-'+localPadId).addClass('selected');
+ return true;
+ },
+ hideCallback: function() {
+ var localPadId = getTargetPadId(this.target);
+ $('tr#pad-row-'+localPadId).removeClass('selected');
+ }
+ });
+});
+
+etherpad.pro.padlist.deletePad = function(localPadId) {
+ if (!confirm("Are you sure you want to delete the pad \""+clientVars.padTitles[localPadId]+"\"?")) {
+ return;
+ }
+
+ var inp = $("#padIdToDelete");
+ inp.val(localPadId);
+
+ // sanity check
+ if (! (inp.val() == localPadId)) {
+ alert("Error: "+inp.val());
+ return;
+ }
+
+ $("#delete-pad").submit();
+};
+
+etherpad.pro.padlist.toggleArchivePad = function(localPadId) {
+ var inp = $("#padIdToToggleArchive");
+ inp.val(localPadId);
+ $("#toggle-archive-pad").submit();
+};
+
diff --git a/trunk/etherpad/src/static/js/pro/signin-client.js b/trunk/etherpad/src/static/js/pro/signin-client.js
new file mode 100644
index 0000000..62847e5
--- /dev/null
+++ b/trunk/etherpad/src/static/js/pro/signin-client.js
@@ -0,0 +1,27 @@
+/**
+ * Copyright 2009 Google 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.
+ */
+
+
+$(document).ready(function() {
+ if ($("#signin-form").length > 0) {
+ $("#email").focus();
+ }
+ if ($("#guest-signin-form").length > 0) {
+ $("#guestDisplayName").focus();
+ }
+});
+
+
diff --git a/trunk/etherpad/src/static/js/pulse.jquery.js b/trunk/etherpad/src/static/js/pulse.jquery.js
new file mode 100644
index 0000000..b23aede
--- /dev/null
+++ b/trunk/etherpad/src/static/js/pulse.jquery.js
@@ -0,0 +1,105 @@
+/**
+ * jQuery.pulse
+ * Copyright (c) 2008 James Padolsey - jp(at)qd9(dot)co.uk | http://james.padolsey.com / http://enhance.qd-creative.co.uk
+ * Dual licensed under MIT and GPL.
+ * Date: 05/11/08
+ *
+ * @projectDescription Applies a continual pulse to any element specified
+ * http://enhance.qd-creative.co.uk/demos/pulse/
+ * Tested successfully with jQuery 1.2.6. On FF 2/3, IE 6/7, Opera 9.5 and Safari 3. on Windows XP.
+ *
+ * @author James Padolsey
+ * @version 1.11
+ *
+ * @id jQuery.pulse
+ * @id jQuery.recover
+ * @id jQuery.fn.pulse
+ * @id jQuery.fn.recover
+ */
+(function($){
+ $.fn.recover = function() {
+ /* Empty inline styles - i.e. set element back to previous state */
+ /* Note, the recovery might not work properly if you had inline styles set before pulse initiation */
+ return this.each(function(){$(this).stop().css({backgroundColor:'',color:'',borderLeftColor:'',borderRightColor:'',borderTopColor:'',borderBottomColor:'',opacity:1});});
+ }
+ $.fn.pulse = function(options){
+ var defaultOptions = {
+ textColors: [],
+ backgroundColors: [],
+ borderColors: [],
+ opacityPulse: true,
+ opacityRange: [],
+ speed: 1000,
+ duration: false,
+ runLength: false
+ }, o = $.extend(defaultOptions,options);
+ /* Validate custom options */
+ if(o.textColors.length===1||o.backgroundColors.length===1||o.borderColors.length===1) {return false;}
+ /* Begin: */
+ return this.each(function(){
+ var $t = $(this), pulseCount=1, pulseLimit = (o.runLength&&o.runLength>0) ? o.runLength*largestArrayLength([o.textColors.length,o.backgroundColors.length,o.borderColors.length,o.opacityRange.length]) : false;
+ clearTimeout(recover);
+ if(o.duration) {
+ setTimeout(recover,o.duration);
+ }
+ function nudgePulse(textColorIndex,bgColorIndex,borderColorIndex,opacityIndex) {
+ if(pulseLimit&&pulseCount===pulseLimit) {
+ return $t.recover();
+ }
+ pulseCount++;
+ /* Initiate color change - on callback continue */
+ return $t.animate(getColorsAtIndex(textColorIndex,bgColorIndex,borderColorIndex,opacityIndex),o.speed,function(){
+ /* Callback of each step */
+ nudgePulse(
+ getNextIndex(o.textColors,textColorIndex),
+ getNextIndex(o.backgroundColors,bgColorIndex),
+ getNextIndex(o.borderColors,borderColorIndex),
+ getNextIndex(o.opacityRange,opacityIndex)
+ );
+ });
+ }
+ /* Set CSS to first step (no animation) */
+ $t.css(getColorsAtIndex(0,0,0,0));
+ /* Then animate to second step */
+ nudgePulse(1,1,1,1);
+ function getColorsAtIndex(textColorIndex,bgColorIndex,borderColorIndex,opacityIndex) {
+ /* Prepare animation object - get's all property names/values from passed indexes */
+ var params = {};
+ if(o.backgroundColors.length) {
+ params['backgroundColor'] = o.backgroundColors[bgColorIndex];
+ }
+ if(o.textColors.length) {
+ params['color'] = o.textColors[textColorIndex];
+ }
+ if(o.borderColors.length) {
+ params['borderLeftColor'] = o.borderColors[borderColorIndex];
+ params['borderRightColor'] = o.borderColors[borderColorIndex];
+ params['borderTopColor'] = o.borderColors[borderColorIndex];
+ params['borderBottomColor'] = o.borderColors[borderColorIndex];
+ }
+ if(o.opacityPulse&&o.opacityRange.length) {
+ params['opacity'] = o.opacityRange[opacityIndex];
+ }
+ return params;
+ }
+ function getNextIndex(property,currentIndex) {
+ if (property.length>currentIndex+1) {return currentIndex+1;}
+ else {return 0;}
+ }
+ function largestArrayLength(arrayOfArrays) {
+ return Math.max.apply( Math, arrayOfArrays );
+ }
+ function recover() {
+ $t.recover();
+ }
+ });
+ }
+})(jQuery);
+/* The below code extends the animate function so that it works with color animations */
+/* By John Resig */
+(function(jQuery){
+jQuery.each(['backgroundColor','borderBottomColor','borderLeftColor','borderRightColor','borderTopColor','color','outlineColor'],function(i,attr){jQuery.fx.step[attr]=function(fx){if(fx.state==0){fx.start=getColor(fx.elem,attr);fx.end=getRGB(fx.end)}fx.elem.style[attr]="rgb("+[Math.max(Math.min(parseInt((fx.pos*(fx.end[0]-fx.start[0]))+fx.start[0]),255),0),Math.max(Math.min(parseInt((fx.pos*(fx.end[1]-fx.start[1]))+fx.start[1]),255),0),Math.max(Math.min(parseInt((fx.pos*(fx.end[2]-fx.start[2]))+fx.start[2]),255),0)].join(",")+")"}});
+function getRGB(color){var result;if(color&&color.constructor==Array&&color.length==3)return color;if(result=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color)){return[parseInt(result[1]),parseInt(result[2]),parseInt(result[3])]}if(result=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(color)){return[parseFloat(result[1])*2.55,parseFloat(result[2])*2.55,parseFloat(result[3])*2.55]}if(result=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(color)){return[parseInt(result[1],16),parseInt(result[2],16),parseInt(result[3],16)]}if(result=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(color)){return[parseInt(result[1]+result[1],16),parseInt(result[2]+result[2],16),parseInt(result[3]+result[3],16)]}return colors[jQuery.trim(color).toLowerCase()]}
+function getColor(elem,attr){var color;do{color=jQuery.curCSS(elem,attr);if(color!=''&&color!='transparent'||jQuery.nodeName(elem,"body")){break}attr="backgroundColor"}while(elem=elem.parentNode);return getRGB(color)};
+var colors={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0]};
+})(jQuery); \ No newline at end of file
diff --git a/trunk/etherpad/src/static/js/statpage.js b/trunk/etherpad/src/static/js/statpage.js
new file mode 100644
index 0000000..be2948c
--- /dev/null
+++ b/trunk/etherpad/src/static/js/statpage.js
@@ -0,0 +1,143 @@
+/**
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS-IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+$(function() {
+ $('.title').click(function() {
+ var id = $(this).parent().attr("id")
+ toggleId(id);
+ // $(this).parent().children('.statbody').toggle();
+ });
+ $('.statbody').each(function() {
+ if (isVisible($(this).parent().attr("id"))) {
+ $(this).show();
+ }
+ });
+ var cat = window.location.hash.slice(1);
+ if (! cat) {
+ cat = "health";
+ }
+ $('.navlink').click(function() {
+ var cat = $(this).attr("id").slice(4);
+ showCategory(cat);
+ });
+ showCategory(cat);
+});
+
+function showCategory(cat) {
+ $('#fragment').val(cat);
+ $('.categorywrapper').each(function() {
+ var localCat = $(this).attr("id").slice(3);
+ if (localCat == cat) {
+ $('#link'+localCat).parent().addClass("selected");
+ $(this).show();
+ } else {
+ $('#link'+localCat).parent().removeClass("selected");
+ $(this).hide();
+ }
+ })
+}
+
+function formChanged() {
+ document.forms[0].submit();
+}
+
+if (! String.prototype.startsWith) {
+ String.prototype.startsWith = function(s) {
+ if (this.length < s.length) { return false; }
+ return this.substr(0, s.length) == s;
+ }
+}
+
+if (! String.prototype.trim) {
+ String.prototype.trim = function() {
+ var firstNonSpace;
+ for (var i = 0; i < this.length; ++i) {
+ if (this[i] != ' ') {
+ firstNonSpace = i;
+ break;
+ }
+ }
+ var s = this;
+ if (firstNonSpace) {
+ s = this.substr(firstNonSpace);
+ }
+ var lastNonSpace;
+ for (var i = this.length-1; i >= 0; --i) {
+ if (this[i] != ' ') {
+ lastNonSpace = i;
+ break;
+ }
+ }
+ if (lastNonSpace !== undefined) {
+ s = s.substr(0, lastNonSpace+1);
+ }
+ return s;
+ }
+}
+
+if (! Array.prototype.contains) {
+ Array.prototype.contains = function(obj) {
+ for (var i = 0; i < this.length; ++i) {
+ if (this[i] == obj) return true;
+ }
+ return false;
+ }
+}
+
+if (! Array.prototype.first) {
+ Array.prototype.first = function(f) {
+ for (var i = 0; i < this.length; ++i) {
+ if (f(this[i])) {
+ return this[i];
+ }
+ }
+ }
+}
+
+var cookieprefix = "visiblestats="
+
+function statsCookieValue() {
+ return (document.cookie.split(";").map(function(s) { return s.trim() }).first(function(str) {
+ return str.startsWith(cookieprefix);
+ }) || cookieprefix).split("=")[1];
+}
+
+function isVisible(id) {
+ var cookieValue = statsCookieValue();
+ return ! (cookieValue.split("-").contains(id));
+}
+
+function rememberHidden(id) {
+ if (! isVisible(id)) { return; }
+ document.cookie = cookieprefix+
+ statsCookieValue().split("-").concat([id]).join("-");
+}
+
+function rememberVisible(id) {
+ if (isVisible(id)) { return; }
+ document.cookie = cookieprefix+
+ statsCookieValue().split("-").filter(function(obj) { return obj != id }).join("-");
+}
+
+function toggleId(id) {
+ var body = $('#'+id).children('.statbody');
+ body.toggle();
+ if (body.is(":visible")) {
+ rememberVisible(id);
+ } else {
+ rememberHidden(id);
+ }
+} \ No newline at end of file
diff --git a/trunk/etherpad/src/static/js/store.js b/trunk/etherpad/src/static/js/store.js
new file mode 100644
index 0000000..549e52d
--- /dev/null
+++ b/trunk/etherpad/src/static/js/store.js
@@ -0,0 +1,116 @@
+/**
+ * Copyright 2009 Google 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.
+ */
+
+
+store = {};
+
+$(document).ready(function() {
+ if ($('#downloadpage').size() > 0) {
+ $("#license_agree, #license_agree_label").click(function() {
+ if ($("#license_agree").attr("checked")) {
+ $("a.downloadbutton_disabled").removeClass("downloadbutton_disabled")
+ .addClass("downloadbutton")
+ .attr('href', '/ep/store/eepnet-download-nextsteps');
+ } else {
+ $("a.downloadbutton").removeClass("downloadbutton")
+ .addClass("downloadbutton_disabled")
+ .attr('href', 'javascript:void store.mustAgree()');
+ }
+ });
+ }
+
+ if ($('#eepnet_trial_signup_page').size() > 0) {
+ store.eepnetTrial.init();
+ }
+
+});
+
+store.mustAgree = function() {
+ alert("You must first click 'Accept License' before downloading this software.");
+};
+
+//----------------------------------------------------------------
+// trial download page
+//----------------------------------------------------------------
+
+store.eepnetTrial = {};
+
+store.eepnetTrial.init = function() {
+ $("#submit").attr("disabled", false);
+ $("input.signupData").keydown(function() {
+ $("#submit").attr("disabled", false);
+ });
+ $("input.signupData").change(function() {
+ $("#submit").attr("disabled", false);
+ });
+};
+
+store.eepnetTrial.handleError = function(msg) {
+ $('#processingmsg').hide();
+ $('#dlsignup').show();
+ $("#errormsg").hide().html(msg).fadeIn("fast");
+ var href = window.location.href;
+ href = href.split("#")[0];
+ window.location.href = (href + "#toph2");
+ $('#submit').attr('disabled', false);
+};
+
+store.eepnetTrial.submit = function() {
+
+ $("#errormsg").hide();
+ $('#dlsignup').hide();
+ $('#processingmsg').fadeIn('fast');
+
+ // first stubmit to pad.spline.inf.fu-berlin.de...
+ var data = {};
+ $(".signupData").each(function() {
+ data[$(this).attr("id")] = $(this).val();
+ });
+ data.industry = $('#industry').val();
+
+ $('#submit').attr('disabled', true);
+
+ $.ajax({
+ type: 'post',
+ url: '/ep/store/eepnet-eval-signup',
+ data: data,
+ success: success,
+ error: error
+ });
+
+ function success(text) {
+ var responseData = eval("("+text+")");
+ if (responseData.error) {
+ store.eepnetTrial.handleError(responseData.error);
+ return;
+ }
+
+ store.eepnetTrial.submitWebToLead(responseData);
+ }
+
+ function error(e) {
+ store.eepnetTrial.handleError("Oops! There was an error processing your request.");
+ }
+};
+
+store.eepnetTrial.submitWebToLead = function(data) {
+ for (k in data) {
+ $('#wl_'+k).val(data[k]);
+ }
+ setTimeout(function() { $('#wlform').submit(); }, 50);
+};
+
+
diff --git a/trunk/etherpad/src/static/js/swfobject.js b/trunk/etherpad/src/static/js/swfobject.js
new file mode 100644
index 0000000..b741304
--- /dev/null
+++ b/trunk/etherpad/src/static/js/swfobject.js
@@ -0,0 +1,24 @@
+/**
+ * Copyright 2009 Google 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.
+ */
+
+/**
+ * SWFObject v1.5: Flash Player detection and embed - http://blog.deconcept.com/swfobject/
+ *
+ * SWFObject is (c) 2007 Geoff Stearns and is released under the MIT License:
+ * http://www.opensource.org/licenses/mit-license.php
+ *
+ */
+if(typeof deconcept=="undefined"){var deconcept=new Object();}if(typeof deconcept.util=="undefined"){deconcept.util=new Object();}if(typeof deconcept.SWFObjectUtil=="undefined"){deconcept.SWFObjectUtil=new Object();}deconcept.SWFObject=function(_1,id,w,h,_5,c,_7,_8,_9,_a){if(!document.getElementById){return;}this.DETECT_KEY=_a?_a:"detectflash";this.skipDetect=deconcept.util.getRequestParameter(this.DETECT_KEY);this.params=new Object();this.variables=new Object();this.attributes=new Array();if(_1){this.setAttribute("swf",_1);}if(id){this.setAttribute("id",id);}if(w){this.setAttribute("width",w);}if(h){this.setAttribute("height",h);}if(_5){this.setAttribute("version",new deconcept.PlayerVersion(_5.toString().split(".")));}this.installedVer=deconcept.SWFObjectUtil.getPlayerVersion();if(!window.opera&&document.all&&this.installedVer.major>7){deconcept.SWFObject.doPrepUnload=true;}if(c){this.addParam("bgcolor",c);}var q=_7?_7:"high";this.addParam("quality",q);this.setAttribute("useExpressInstall",false);this.setAttribute("doExpressInstall",false);var _c=(_8)?_8:window.location;this.setAttribute("xiRedirectUrl",_c);this.setAttribute("redirectUrl","");if(_9){this.setAttribute("redirectUrl",_9);}};deconcept.SWFObject.prototype={useExpressInstall:function(_d){this.xiSWFPath=!_d?"expressinstall.swf":_d;this.setAttribute("useExpressInstall",true);},setAttribute:function(_e,_f){this.attributes[_e]=_f;},getAttribute:function(_10){return this.attributes[_10];},addParam:function(_11,_12){this.params[_11]=_12;},getParams:function(){return this.params;},addVariable:function(_13,_14){this.variables[_13]=_14;},getVariable:function(_15){return this.variables[_15];},getVariables:function(){return this.variables;},getVariablePairs:function(){var _16=new Array();var key;var _18=this.getVariables();for(key in _18){_16[_16.length]=key+"="+_18[key];}return _16;},getSWFHTML:function(){var _19="";if(navigator.plugins&&navigator.mimeTypes&&navigator.mimeTypes.length){if(this.getAttribute("doExpressInstall")){this.addVariable("MMplayerType","PlugIn");this.setAttribute("swf",this.xiSWFPath);}_19="<embed type=\"application/x-shockwave-flash\" src=\""+this.getAttribute("swf")+"\" width=\""+this.getAttribute("width")+"\" height=\""+this.getAttribute("height")+"\" style=\""+this.getAttribute("style")+"\"";_19+=" id=\""+this.getAttribute("id")+"\" name=\""+this.getAttribute("id")+"\" ";var _1a=this.getParams();for(var key in _1a){_19+=[key]+"=\""+_1a[key]+"\" ";}var _1c=this.getVariablePairs().join("&");if(_1c.length>0){_19+="flashvars=\""+_1c+"\"";}_19+="/>";}else{if(this.getAttribute("doExpressInstall")){this.addVariable("MMplayerType","ActiveX");this.setAttribute("swf",this.xiSWFPath);}_19="<object id=\""+this.getAttribute("id")+"\" classid=\"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\" width=\""+this.getAttribute("width")+"\" height=\""+this.getAttribute("height")+"\" style=\""+this.getAttribute("style")+"\">";_19+="<param name=\"movie\" value=\""+this.getAttribute("swf")+"\" />";var _1d=this.getParams();for(var key in _1d){_19+="<param name=\""+key+"\" value=\""+_1d[key]+"\" />";}var _1f=this.getVariablePairs().join("&");if(_1f.length>0){_19+="<param name=\"flashvars\" value=\""+_1f+"\" />";}_19+="</object>";}return _19;},write:function(_20){if(this.getAttribute("useExpressInstall")){var _21=new deconcept.PlayerVersion([6,0,65]);if(this.installedVer.versionIsValid(_21)&&!this.installedVer.versionIsValid(this.getAttribute("version"))){this.setAttribute("doExpressInstall",true);this.addVariable("MMredirectURL",escape(this.getAttribute("xiRedirectUrl")));document.title=document.title.slice(0,47)+" - Flash Player Installation";this.addVariable("MMdoctitle",document.title);}}if(this.skipDetect||this.getAttribute("doExpressInstall")||this.installedVer.versionIsValid(this.getAttribute("version"))){var n=(typeof _20=="string")?document.getElementById(_20):_20;n.innerHTML=this.getSWFHTML();return true;}else{if(this.getAttribute("redirectUrl")!=""){document.location.replace(this.getAttribute("redirectUrl"));}}return false;}};deconcept.SWFObjectUtil.getPlayerVersion=function(){var _23=new deconcept.PlayerVersion([0,0,0]);if(navigator.plugins&&navigator.mimeTypes.length){var x=navigator.plugins["Shockwave Flash"];if(x&&x.description){_23=new deconcept.PlayerVersion(x.description.replace(/([a-zA-Z]|\s)+/,"").replace(/(\s+r|\s+b[0-9]+)/,".").split("."));}}else{if(navigator.userAgent&&navigator.userAgent.indexOf("Windows CE")>=0){var axo=1;var _26=3;while(axo){try{_26++;axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash."+_26);_23=new deconcept.PlayerVersion([_26,0,0]);}catch(e){axo=null;}}}else{try{var axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");}catch(e){try{var axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");_23=new deconcept.PlayerVersion([6,0,21]);axo.AllowScriptAccess="always";}catch(e){if(_23.major==6){return _23;}}try{axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash");}catch(e){}}if(axo!=null){_23=new deconcept.PlayerVersion(axo.GetVariable("$version").split(" ")[1].split(","));}}}return _23;};deconcept.PlayerVersion=function(_29){this.major=_29[0]!=null?parseInt(_29[0]):0;this.minor=_29[1]!=null?parseInt(_29[1]):0;this.rev=_29[2]!=null?parseInt(_29[2]):0;};deconcept.PlayerVersion.prototype.versionIsValid=function(fv){if(this.major<fv.major){return false;}if(this.major>fv.major){return true;}if(this.minor<fv.minor){return false;}if(this.minor>fv.minor){return true;}if(this.rev<fv.rev){return false;}return true;};deconcept.util={getRequestParameter:function(_2b){var q=document.location.search||document.location.hash;if(_2b==null){return q;}if(q){var _2d=q.substring(1).split("&");for(var i=0;i<_2d.length;i++){if(_2d[i].substring(0,_2d[i].indexOf("="))==_2b){return _2d[i].substring((_2d[i].indexOf("=")+1));}}}return "";}};deconcept.SWFObjectUtil.cleanupSWFs=function(){var _2f=document.getElementsByTagName("OBJECT");for(var i=_2f.length-1;i>=0;i--){_2f[i].style.display="none";for(var x in _2f[i]){if(typeof _2f[i][x]=="function"){_2f[i][x]=function(){};}}}};if(deconcept.SWFObject.doPrepUnload){if(!deconcept.unloadSet){deconcept.SWFObjectUtil.prepUnload=function(){__flash_unloadHandler=function(){};__flash_savedUnloadHandler=function(){};window.attachEvent("onunload",deconcept.SWFObjectUtil.cleanupSWFs);};window.attachEvent("onbeforeunload",deconcept.SWFObjectUtil.prepUnload);deconcept.unloadSet=true;}}if(!document.getElementById&&document.all){document.getElementById=function(id){return document.all[id];};}var getQueryParamValue=deconcept.util.getRequestParameter;var FlashObject=deconcept.SWFObject;var SWFObject=deconcept.SWFObject; \ No newline at end of file
diff --git a/trunk/etherpad/src/static/js/timeslider.js b/trunk/etherpad/src/static/js/timeslider.js
new file mode 100644
index 0000000..552a971
--- /dev/null
+++ b/trunk/etherpad/src/static/js/timeslider.js
@@ -0,0 +1,663 @@
+/**
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS-IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+
+function repeatString(str, times) {
+ if (times <= 0) return "";
+ var s = repeatString(str, times >> 1);
+ s += s;
+ if (times & 1) s += str;
+ return s;
+}
+function chr(n) { return String.fromCharCode(n+48); }
+function ord(c) { return c.charCodeAt(0)-48; }
+
+function map(array, func) {
+ var result = [];
+ // must remain compatible with "arguments" pseudo-array
+ for(var i=0;i<array.length;i++) {
+ if (func) result.push(func(array[i], i));
+ else result.push(array[i]);
+ }
+ return result;
+}
+
+function forEach(array, func) {
+ for(var i=0;i<array.length;i++) {
+ var result = func(array[i], i);
+ if (result) break;
+ }
+}
+
+function getText(padOpaqueRef, r, func/*(text, optErrorData)*/) {
+ doAjaxGet('/ep/pad/history/'+padOpaqueRef+'/text/'+Number(r),
+ function(data, optErrorData) {
+ if (optErrorData) {
+ func(null, optErrorData);
+ }
+ else {
+ var text = data.text;
+ func({text: text});
+ }
+ });
+}
+
+function getChanges(padOpaqueRef, first, last, func/*(data, optErrorData)*/) {
+ doAjaxGet('/ep/pad/history/'+padOpaqueRef+'/changes/'+Number(first)+'-'+Number(last),
+ function(data, optErrorData) {
+ if (optErrorData) {
+ func(null, optErrorData);
+ }
+ else {
+ func(uncompressChangesBlock({charPool: data.charPool,
+ changes: data.changes,
+ firstRev: first}));
+ }
+ });
+}
+
+function statPad(padOpaqueRef, func/*(atext, optErrorData)*/) {
+ doAjaxGet('/ep/pad/history/'+padOpaqueRef+'/stat',
+ function(data, optErrorData) {
+ if (optErrorData) {
+ func(null, optErrorData);
+ }
+ else {
+ var obj = {exists: data.exists};
+ if (obj.exists) {
+ obj.latestRev = data.latestRev;
+ }
+
+ func(obj);
+ }
+ });
+}
+
+function doAjaxGet(url, func/*(data, optErrorData)*/) {
+ $.ajax({
+ type: 'get',
+ dataType: 'json',
+ url: url,
+ success: function(data) {
+ if (data.error) {
+ func(null, {serverError: data});
+ }
+ else {
+ func(data);
+ }
+ },
+ error: function(xhr, textStatus, errorThrown) {
+ func(null, {clientError: { textStatus:textStatus, errorThrown: errorThrown }});
+ }
+ });
+}
+
+function uncompressChangesBlock(data) {
+ var charPool = data.charPool;
+ var changesArray = data.changes.split(',');
+ var firstRev = data.firstRev;
+
+ var changesBlock = {};
+ var changeStructs = [];
+ var charPoolIndex = 0;
+ var lastTimestamp = 0;
+ for(var i=0;i<changesArray.length;i++) {
+ var receiver = [null, 0];
+ var curString = changesArray[i];
+ function nextChar() {
+ return curString.charAt(receiver[1]);
+ }
+ function readChar() {
+ var c = nextChar();
+ receiver[1]++;
+ return c;
+ }
+ function readNum() {
+ return decodeVarInt(curString, receiver[1], receiver);
+ }
+ function readString() {
+ var len = readNum();
+ var str = charPool.substr(charPoolIndex, len);
+ charPoolIndex += len;
+ return str;
+ }
+ function readTimestamp() {
+ var absolute = false;
+ if (nextChar() == "+") {
+ readChar();
+ absolute = true;
+ }
+ var t = readNum()*1000;
+ if (! absolute) {
+ t += lastTimestamp;
+ }
+ lastTimestamp = t;
+ return t;
+ }
+ function atEnd() {
+ return receiver[1] >= curString.length;
+ }
+ var timestamp = readTimestamp();
+ var authorNum = readNum();
+ var splices = [];
+ while (! atEnd()) {
+ var spliceType = readChar();
+ var startChar = readNum();
+ var oldText = "";
+ var newText = "";
+ if (spliceType != '+') {
+ oldText = readString();
+ }
+ if (spliceType != '-') {
+ newText = readString();
+ }
+ splices.push([startChar,oldText,newText]);
+ }
+ changeStructs.push({t:timestamp, a:authorNum, splices:splices});
+ }
+
+ changesBlock.firstRev = firstRev;
+ changesBlock.changeStructs = changeStructs;
+
+ return changesBlock;
+}
+
+var BASE64_DIGITS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._";
+var BASE64_DIGIT_TO_NUM = (function() {
+ var map = {};
+ for(var i=0;i<BASE64_DIGITS.length;i++) {
+ map[BASE64_DIGITS.charAt(i)] = i;
+ }
+ return map;
+})();
+
+function decodeVarInt(stringIn, indexIn, numAndIndexOut) {
+ var n = 0;
+ var done = false;
+ var i = indexIn;
+ while (! done) {
+ var d = + BASE64_DIGIT_TO_NUM[stringIn.charAt(i++)];
+ if (isNaN(d)) return -1;
+ if ((d & 32) == 0) {
+ done = true;
+ }
+ n = n*32 + (d & 31);
+ }
+ if (numAndIndexOut) {
+ numAndIndexOut[0] = n;
+ numAndIndexOut[1] = i;
+ }
+ return n;
+}
+
+function escapeHTML(s) {
+ var re = /[&<>\n]/g;
+ if (! re.MAP) {
+ // persisted across function calls!
+ re.MAP = {
+ '&': '&amp;',
+ '<': '&lt;',
+ '>': '&gt;',
+ '\n': '<br/>'
+ };
+ }
+ return s.replace(re, function(c) { return re.MAP[c]; });
+}
+
+var padOpaqueRef = clientVars.padOpaqueRef;
+var keyframes = []; // [rev, atext] pairs
+var changesBlocks = []; // [first, last, changesBlock]
+var lastRev;
+var lastRevLoaded = -1;
+var problemData = null;
+var curRev = -1;
+var curText = { lines: [/*string, length+1*/] };
+
+function setLastRevLoaded(r) {
+ lastRevLoaded = r;
+ //$("#sliderui").slider('option', 'max', lastRevLoaded);
+ $("#currevdisplay .max").html(String(lastRevLoaded));
+}
+
+function initialStat(continuation) {
+ statPad(padOpaqueRef, function(data, errorData) {
+ if (errorData) {
+ reportProblem(errorData);
+ continuation(false);
+ }
+ else {
+ if (! data.exists) {
+ reportProblem({msg: "Pad not found."});
+ continuation(false);
+ }
+ else {
+ lastRev = data.latestRev;
+ continuation(true);
+ return;
+ }
+ }
+ });
+}
+
+function loadKeyframe(r, continuation) {
+ getText(padOpaqueRef, r, function(data, errorData) {
+ if (errorData) {
+ reportProblem(errorData);
+ continuation(false);
+ }
+ else {
+ keyframes.push([r, data]);
+ keyframes.sort(function(a, b) {
+ return a[0] - b[0];
+ });
+ continuation(true);
+ }
+ });
+}
+
+function loadChangesBlock(first, last, continuation) {
+ getChanges(padOpaqueRef, first, last, function(data, errorData) {
+ if (errorData) {
+ reportProblem(errorData);
+ continuation(false);
+ }
+ else {
+ changesBlocks.push([first, last, data]);
+ continuation(true);
+ }
+ });
+}
+
+function loadThroughZero(continuation) {
+ initialStat(function(success) {
+ if (success) {
+ loadKeyframe(0, function(success) {
+ if (success) {
+ setLastRevLoaded(0);
+ continuation(true);
+ }
+ else continuation(false);
+ });
+ }
+ else continuation(false);
+ });
+}
+
+function loadMoreRevs(continuation) {
+ if (lastRevLoaded >= lastRev) {
+ continuation(true);
+ }
+ else {
+ var first = lastRevLoaded+1;
+ var last = first + 499;
+ if (last > lastRev) {
+ last = lastRev;
+ }
+ loadChangesBlock(first, last, function(success) {
+ if (success) {
+ loadKeyframe(last, function(success) {
+ if (success) {
+ setLastRevLoaded(last);
+ continuation(true);
+ }
+ else continuation(false);
+ });
+ }
+ else continuation(false);
+ });
+ }
+}
+
+function getDocTextForText(text) {
+ var lines = map(text.split('\n').slice(0, -1), function(s) {
+ return [s, s.length+1];
+ });
+ return { lines: lines };
+}
+
+function getLineAndChar(docText, charIndex) {
+ // returns [lineIndex, charIndexIntoLine];
+ // if the charIndex is after the final newline of the document,
+ // lineIndex may be == docText.lines.length.
+ // Otherwise, lneIndex is an actual line and charIndex
+ // is between 0 and the line's length inclusive.
+ var startLine = 0;
+ var startLineStartChar = 0;
+ var lines = docText.lines;
+ var done = false;
+ while (!done) {
+ if (startLine >= lines.length) {
+ done = true;
+ }
+ else {
+ var lineLength = lines[startLine][1];
+ var nextLineStart = startLineStartChar + lineLength;
+ if (nextLineStart <= charIndex) {
+ startLine++;
+ startLineStartChar = nextLineStart;
+ }
+ else {
+ done = true;
+ }
+ }
+ }
+ return [startLine, charIndex - startLineStartChar];
+}
+
+function applySplice(docText, splice, forward) {
+ var startChar = splice[0];
+ var oldText = splice[1];
+ var newText = splice[2];
+ if (! forward) {
+ var tmp = oldText;
+ oldText = newText;
+ newText = tmp;
+ }
+
+ //var OLD_FULL_TEXT = map(docText.lines, function(L) { return L[0]; }).join('\n')+'\n';
+ //var OLD_NUM_LINES = docText.lines.length;
+
+ var lines = docText.lines;
+ var startLineAndChar = getLineAndChar(docText, startChar);
+ var endLineAndChar = getLineAndChar(docText, startChar+oldText.length);
+
+ var lineSpliceStart = startLineAndChar[0];
+ var lineSpliceEnd = endLineAndChar[0];
+ var newLines = newText.split('\n');
+ // we want to splice in entire lines, so adjust start to include beginning of line
+ // we're starting to insert into
+ if (startLineAndChar[1] > 0) {
+ newLines[0] = lines[startLineAndChar[0]][0].substring(0, startLineAndChar[1]) + newLines[0];
+ }
+ // adjust end to include entire last line that will be changed
+ if (endLineAndChar[1] > 0 || newLines[newLines.length-1].length > 0) {
+ newLines[newLines.length-1] += lines[endLineAndChar[0]][0].substring(endLineAndChar[1]);
+ lineSpliceEnd += 1;
+ }
+ else {
+ // the splice is ok as is, except for an extra newline
+ newLines.pop();
+ }
+
+ var newLineEntries = map(newLines, function(s) {
+ return [s, s.length+1];
+ });
+
+ Array.prototype.splice.apply(lines,
+ [lineSpliceStart, lineSpliceEnd-lineSpliceStart].concat(newLineEntries));
+
+ // check it
+ //var EXPECTED_FULL_TEXT = OLD_FULL_TEXT.substring(0, startChar) + newText +
+ //OLD_FULL_TEXT.substring(startChar + oldText.length, OLD_FULL_TEXT.length);
+ //var ACTUAL_FULL_TEXT = map(docText.lines, function(L) { return L[0]; }).join('\n')+'\n';
+
+ //console.log("%o %o %o %d %d %d %d %d",
+ //docText.lines, startLineAndChar, endLineAndChar, OLD_NUM_LINES,
+ //lines.length, lineSpliceStart, lineSpliceEnd-lineSpliceStart, newLineEntries.length);
+
+ //if (EXPECTED_FULL_TEXT != ACTUAL_FULL_TEXT) {
+ //console.log(escapeHTML("mismatch: "+EXPECTED_FULL_TEXT+" / "+ACTUAL_FULL_TEXT));
+ //}
+
+ return [lineSpliceStart, lineSpliceEnd-lineSpliceStart, newLines];
+}
+
+function lineHTML(line) {
+ return (escapeHTML(line) || '&nbsp;');
+}
+
+function setCurText(docText, dontSetDom) {
+ curText = docText;
+ if (! dontSetDom) {
+ var docNode = $("#stuff");
+ var html = map(docText.lines, function(line) {
+ return '<div>'+lineHTML(line[0])+'</div>';
+ });
+ docNode.html(html.join(''));
+ }
+}
+
+function spliceDom(splice) {
+ var index = splice[0];
+ var numRemoved = splice[1];
+ var newLines = splice[2];
+
+ var overlap = Math.min(numRemoved, newLines.length);
+ var container = $("#stuff").get(0);
+ var oldNumNodes = container.childNodes.length;
+ var i = 0;
+ for(;i<overlap;i++) {
+ var n = container.childNodes.item(index+i);
+ $(n).html(lineHTML(newLines[i]));
+ }
+ for(;i<newLines.length;i++) {
+ var insertIndex = index+i;
+ var content = '<div>'+lineHTML(newLines[i])+'</div>';
+ if (insertIndex >= container.childNodes.length) {
+ $(container).append(content);
+ }
+ else {
+ $(container.childNodes.item(insertIndex)).before(content);
+ }
+ }
+ for(;i<numRemoved;i++) {
+ var deleteIndex = index+overlap;
+ $(container.childNodes.item(deleteIndex)).remove();
+ }
+
+ //console.log("%d %d %d %d %d", splice[0], splice[1], splice[2].length,
+ //oldNumNodes + newLines.length - numRemoved,
+ //container.childNodes.length);
+}
+
+function seekToRev(r) {
+ // precond: r is reachable
+
+ var isStep = false;
+
+ var bestKeyFrameIndex = -1;
+ var bestKeyFrameDistance = -1;
+ function considerKeyframe(index, kr) {
+ var dist = Math.abs(r - kr);
+ if (bestKeyFrameDistance < 0 || dist < bestKeyFrameDistance) {
+ bestKeyFrameDistance = dist;
+ bestKeyFrameIndex = index;
+ }
+ }
+ for(var i=0;i<keyframes.length;i++) {
+ considerKeyframe(i, keyframes[i][0]);
+ }
+ if (curRev >= 0) {
+ if (Math.abs(r - curRev) == 1) {
+ isStep = true;
+ bestKeyFrameIndex = -2; // -2 to mean "current revision"
+ }
+ else {
+ considerKeyframe(-2, curRev);
+ }
+ }
+
+ var docText = curText;
+ var docRev = curRev;
+ if (bestKeyFrameIndex >= 0) {
+ // some keyframe is better than moving from the current location;
+ // move to that keyframe
+ var keyframe = keyframes[bestKeyFrameIndex];
+ docRev = keyframe[0];
+ docText = getDocTextForText(keyframe[1].text);
+ }
+
+ var startRev = docRev;
+ var destRev = r;
+
+ var curChangesBlock = null;
+ function findChangesBlockFor(n) {
+ function changesBlockWorks(arr) {
+ return n >= arr[0] && n <= arr[1];
+ }
+ if (curChangesBlock == null || ! changesBlockWorks(curChangesBlock)) {
+ curChangesBlock = null;
+ for(var i=0;i<changesBlocks.length;i++) {
+ var cba = changesBlocks[i];
+ if (changesBlockWorks(cba)) {
+ curChangesBlock = cba;
+ break;
+ }
+ }
+ }
+ }
+
+ //var DEBUG_REVS_APPLIED = [];
+
+ function applyRev(n, forward) {
+ findChangesBlockFor(n);
+ var cb = curChangesBlock[2];
+ var idx = n - curChangesBlock[0];
+ var chng = cb.changeStructs[idx];
+
+ var splices = chng.splices;
+ if (forward) {
+ for(var i=0;i<splices.length;i++) {
+ var splice = applySplice(docText, splices[i], true);
+ if (isStep) spliceDom(splice);
+ }
+ }
+ else {
+ for(var i=splices.length-1;i>=0;i--) {
+ var splice = applySplice(docText, splices[i], false);
+ if (isStep) spliceDom(splice);
+ }
+ }
+
+ //DEBUG_REVS_APPLIED.push(n);
+ }
+
+ if (destRev > startRev) {
+ for (var j=startRev+1; j<=destRev; j++) {
+ applyRev(j, true);
+ }
+ }
+ else if (destRev < startRev) {
+ for(var j=startRev; j >= destRev+1; j--) {
+ applyRev(j, false);
+ }
+ }
+
+ docRev = destRev;
+
+ setCurText(docText, isStep);
+ curRev = docRev;
+ $("#currevdisplay .cur").html(String(curRev));
+}
+
+function reportProblem(probData) {
+ problemData = probData;
+ if (probData.msg) {
+ $("#stuff").html(escapeHTML(probData.msg));
+ }
+}
+
+var playTimer = null;
+
+$(function() {
+ /*$("#sliderui").slider({min: 0, max: 0, value: 0, step: 1, change: slidechange});
+ function slidechange(event, ui) {
+ alert("HELLO");
+ var value = ui.value;
+ console.log(value);
+ }*/
+
+ $("#controls .next").click(function() {
+ if (curRev < lastRevLoaded) {
+ seekToRev(curRev+1);
+ }
+ return false;
+ });
+
+ $("#controls .prev").click(function() {
+ if (curRev > 0) {
+ seekToRev(curRev-1);
+ }
+ return false;
+ });
+
+ function stop() {
+ if (playTimer) {
+ clearInterval(playTimer);
+ playTimer = null;
+ }
+ }
+
+ function play() {
+ stop();
+ playTimer = setInterval(function() {
+ if (curRev < lastRevLoaded) {
+ seekToRev(curRev+1);
+ }
+ else {
+ stop();
+ }
+ }, 60);
+ return false;
+ }
+
+ $("#controls .play").click(play);
+
+ $("#controls .stop").click(function() {
+ stop();
+ return false;
+ });
+
+ $("#controls .entry").change(function() {
+ var value = $("#controls .entry").val();
+ value = Number(value || 0);
+ if (isNaN(value)) value = 0;
+ if (value < 0) value = 0;
+ if (value > lastRevLoaded) {
+ value = lastRevLoaded;
+ }
+ $("#controls .entry").val('');
+ seekToRev(value);
+ });
+ $("#controls .entry").val('');
+
+ var useAutoplay = true;
+ var hasAutoplayed = false;
+
+ loadThroughZero(function(success) {
+ if (success) {
+ seekToRev(0);
+
+ function loadMoreRevsIfNecessary(continuation) {
+ if (lastRevLoaded < lastRev) {
+ loadMoreRevs(continuation);
+ }
+ }
+ loadMoreRevsIfNecessary(function cont(success) {
+ if (success) {
+ if (lastRevLoaded > 0 && useAutoplay && ! hasAutoplayed) {
+ hasAutoplayed = true;
+ play();
+ }
+ loadMoreRevsIfNecessary(cont);
+ }
+ });
+ }
+ });
+});
+
diff --git a/trunk/etherpad/src/static/js/undo-xpopup.js b/trunk/etherpad/src/static/js/undo-xpopup.js
new file mode 100644
index 0000000..89cfb4d
--- /dev/null
+++ b/trunk/etherpad/src/static/js/undo-xpopup.js
@@ -0,0 +1,25 @@
+/**
+ * Copyright 2009 Google 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.
+ */
+
+if (window._orig_windowOpen) {
+ window.open = _orig_windowOpen;
+}
+if (window._orig_windowSetTimeout) {
+ window.setTimeout = _orig_windowSetTimeout;
+}
+if (window._orig_windowSetInterval) {
+ window.setInterval = _orig_windowSetInterval;
+}
diff --git a/trunk/etherpad/src/static/swf/vidplayer.swf b/trunk/etherpad/src/static/swf/vidplayer.swf
new file mode 100755
index 0000000..a38a94e
--- /dev/null
+++ b/trunk/etherpad/src/static/swf/vidplayer.swf
Binary files differ