From d7c5ad7d6263fd1baf9bfdbaa4c50b70ef2fbdb2 Mon Sep 17 00:00:00 2001 From: Alexander Sulfrian Date: Tue, 8 Jun 2010 08:22:05 +0200 Subject: reverted folder structure change for better mergeing with upstream --- trunk/COPYING | 202 + trunk/README | 290 ++ trunk/etherpad/.gitignore | 9 + trunk/etherpad/bin/.gitignore | 1 + trunk/etherpad/bin/rebuildjar.sh | 75 + trunk/etherpad/bin/run-local.sh | 66 + trunk/etherpad/bin/setup-mysql-db.sh | 30 + trunk/etherpad/etc/etherpad.changeme.properties | 16 + .../etc/etherpad.localdev-default.properties | 16 + trunk/etherpad/lib/dnsjava-2.0.6.jar | Bin 0 -> 268823 bytes trunk/etherpad/lib/jbcrypt-0.2.jar | Bin 0 -> 26222 bytes trunk/etherpad/lib/jcommon-1.0.15.jar | Bin 0 -> 309294 bytes trunk/etherpad/lib/jfreechart-1.0.12.jar | Bin 0 -> 1368681 bytes trunk/etherpad/src/etherpad/admin/shell.js | 127 + trunk/etherpad/src/etherpad/billing/billing.js | 800 ++++ trunk/etherpad/src/etherpad/billing/fields.js | 219 + .../etherpad/src/etherpad/billing/team_billing.js | 422 ++ .../src/etherpad/collab/ace/contentcollector.js | 527 ++ trunk/etherpad/src/etherpad/collab/ace/domline.js | 210 + .../etherpad/src/etherpad/collab/ace/easysync1.js | 923 ++++ .../etherpad/src/etherpad/collab/ace/easysync2.js | 1968 ++++++++ .../src/etherpad/collab/ace/easysync2_tests.js | 877 ++++ .../src/etherpad/collab/ace/linestylefilter.js | 253 + .../etherpad/src/etherpad/collab/collab_server.js | 778 +++ .../src/etherpad/collab/collabroom_server.js | 359 ++ trunk/etherpad/src/etherpad/collab/genimg.js | 55 + .../etherpad/src/etherpad/collab/json_sans_eval.js | 178 + .../src/etherpad/collab/readonly_server.js | 174 + trunk/etherpad/src/etherpad/collab/server_utils.js | 204 + .../etherpad/src/etherpad/control/aboutcontrol.js | 263 + .../etherpad/src/etherpad/control/admincontrol.js | 1471 ++++++ trunk/etherpad/src/etherpad/control/blogcontrol.js | 199 + .../control/connection_diagnostics_control.js | 87 + .../etherpad/control/global_pro_account_control.js | 143 + .../src/etherpad/control/historycontrol.js | 226 + .../src/etherpad/control/loadtestcontrol.js | 93 + trunk/etherpad/src/etherpad/control/maincontrol.js | 54 + .../etherpad/control/pad/pad_changeset_control.js | 280 ++ .../src/etherpad/control/pad/pad_control.js | 780 +++ .../control/pad/pad_importexport_control.js | 319 ++ .../src/etherpad/control/pad/pad_view_control.js | 287 ++ .../src/etherpad/control/pne_manual_control.js | 75 + .../src/etherpad/control/pne_tracker_control.js | 48 + .../src/etherpad/control/pro/account_control.js | 369 ++ .../control/pro/admin/account_manager_control.js | 260 + .../control/pro/admin/license_manager_control.js | 128 + .../control/pro/admin/pro_admin_control.js | 283 ++ .../control/pro/admin/pro_config_control.js | 54 + .../control/pro/admin/team_billing_control.js | 447 ++ .../src/etherpad/control/pro/pro_main_control.js | 150 + .../etherpad/control/pro/pro_padlist_control.js | 200 + .../src/etherpad/control/pro_beta_control.js | 136 + .../src/etherpad/control/pro_signup_control.js | 173 + .../etherpad/src/etherpad/control/scriptcontrol.js | 75 + .../src/etherpad/control/static_control.js | 65 + .../etherpad/src/etherpad/control/statscontrol.js | 1214 +++++ .../control/store/eepnet_checkout_control.js | 757 +++ .../src/etherpad/control/store/storecontrol.js | 201 + trunk/etherpad/src/etherpad/control/testcontrol.js | 74 + .../src/etherpad/db_migrations/m0000_test.js | 23 + .../db_migrations/m0001_eepnet_signups_init.js | 38 + .../db_migrations/m0002_eepnet_signups_2.js | 47 + .../db_migrations/m0003_create_tests_table_v2.js | 29 + .../m0004_convert_all_tables_to_innodb.js | 38 + .../db_migrations/m0005_create_billing_tables.js | 73 + .../db_migrations/m0006_eepnet_signups_3.js | 29 + .../db_migrations/m0007_create_pro_tables_v4.js | 67 + .../db_migrations/m0008_persistent_vars.js | 31 + .../src/etherpad/db_migrations/m0009_pad_tables.js | 31 + .../etherpad/db_migrations/m0010_pad_sqlmeta.js | 71 + .../db_migrations/m0011_pro_users_temppass.js | 33 + .../db_migrations/m0012_pro_users_auto_signin.js | 30 + .../db_migrations/m0013_pne_padv2_upgrade.js | 54 + .../db_migrations/m0014_pne_globalpadids.js | 102 + .../db_migrations/m0015_padmeta_passwords.js | 25 + .../db_migrations/m0016_pne_tracking_data.js | 35 + .../db_migrations/m0017_pne_tracking_data_v2.js | 30 + .../db_migrations/m0018_eepnet_checkout_tables.js | 82 + .../db_migrations/m0019_padmeta_deleted.js | 24 + .../db_migrations/m0020_padmeta_archived.js | 25 + .../db_migrations/m0021_pro_padmeta_json.js | 57 + .../db_migrations/m0022_create_userids_table.js | 30 + .../db_migrations/m0023_create_usagestats_table.js | 32 + .../db_migrations/m0024_statistics_table.js | 42 + .../db_migrations/m0025_rename_pro_users_table.js | 26 + .../db_migrations/m0026_create_guests_table.js | 37 + .../src/etherpad/db_migrations/m0027_pro_config.js | 27 + .../db_migrations/m0028_ondemand_beta_emails.js | 29 + .../db_migrations/m0029_lowercase_subdomains.js | 31 + .../db_migrations/m0030_fix_statistics_values.js | 26 + .../db_migrations/m0031_deleted_pro_users.js | 24 + .../db_migrations/m0032_reduce_topvalues_counts.js | 39 + .../db_migrations/m0033_pro_account_usage.js | 30 + .../m0034_create_recurring_billing_table.js | 42 + .../m0035_add_email_to_paymentinfo.js | 28 + .../m0036_create_missing_subscription_records.js | 45 + .../m0037_create_pro_referral_table.js | 32 + .../db_migrations/m0038_pad_coarse_revs.js | 26 + .../src/etherpad/db_migrations/migration_runner.js | 147 + trunk/etherpad/src/etherpad/debug.js | 26 + trunk/etherpad/src/etherpad/globals.js | 41 + trunk/etherpad/src/etherpad/helpers.js | 276 ++ .../src/etherpad/importexport/importexport.js | 241 + trunk/etherpad/src/etherpad/legacy_urls.js | 37 + trunk/etherpad/src/etherpad/licensing.js | 163 + trunk/etherpad/src/etherpad/log.js | 255 + trunk/etherpad/src/etherpad/metrics/metrics.js | 438 ++ trunk/etherpad/src/etherpad/pad/activepads.js | 52 + trunk/etherpad/src/etherpad/pad/chatarchive.js | 67 + trunk/etherpad/src/etherpad/pad/dbwriter.js | 338 ++ .../src/etherpad/pad/easysync2migration.js | 675 +++ trunk/etherpad/src/etherpad/pad/exporthtml.js | 383 ++ trunk/etherpad/src/etherpad/pad/importhtml.js | 230 + trunk/etherpad/src/etherpad/pad/model.js | 651 +++ trunk/etherpad/src/etherpad/pad/noprowatcher.js | 110 + trunk/etherpad/src/etherpad/pad/pad_migrations.js | 206 + trunk/etherpad/src/etherpad/pad/pad_security.js | 237 + trunk/etherpad/src/etherpad/pad/padevents.js | 170 + trunk/etherpad/src/etherpad/pad/padusers.js | 397 ++ trunk/etherpad/src/etherpad/pad/padutils.js | 154 + trunk/etherpad/src/etherpad/pad/revisions.js | 103 + trunk/etherpad/src/etherpad/pne/pne_utils.js | 187 + trunk/etherpad/src/etherpad/pro/domains.js | 141 + .../src/etherpad/pro/pro_account_auto_signin.js | 101 + trunk/etherpad/src/etherpad/pro/pro_accounts.js | 496 ++ trunk/etherpad/src/etherpad/pro/pro_config.js | 92 + trunk/etherpad/src/etherpad/pro/pro_pad_db.js | 232 + trunk/etherpad/src/etherpad/pro/pro_pad_editors.js | 104 + trunk/etherpad/src/etherpad/pro/pro_padlist.js | 289 ++ trunk/etherpad/src/etherpad/pro/pro_padmeta.js | 111 + trunk/etherpad/src/etherpad/pro/pro_quotas.js | 141 + trunk/etherpad/src/etherpad/pro/pro_utils.js | 165 + trunk/etherpad/src/etherpad/quotas.js | 50 + trunk/etherpad/src/etherpad/sessions.js | 203 + .../etherpad/src/etherpad/statistics/exceptions.js | 231 + .../etherpad/src/etherpad/statistics/statistics.js | 1248 +++++ trunk/etherpad/src/etherpad/store/checkout.js | 300 ++ .../etherpad/src/etherpad/store/eepnet_checkout.js | 101 + trunk/etherpad/src/etherpad/store/eepnet_trial.js | 241 + trunk/etherpad/src/etherpad/testing/testutils.js | 23 + .../src/etherpad/testing/unit_tests/t0000_test.js | 22 + .../t0001_sqlbase_transaction_rollback.js | 48 + .../testing/unit_tests/t0002_license_generation.js | 89 + .../testing/unit_tests/t0003_persistent_vars.js | 42 + .../etherpad/testing/unit_tests/t0004_sqlobj.js | 214 + .../etherpad/testing/unit_tests/t0005_easysync.js | 22 + .../src/etherpad/usage_stats/usage_stats.js | 162 + trunk/etherpad/src/etherpad/utils.js | 396 ++ trunk/etherpad/src/main.js | 418 ++ trunk/etherpad/src/static/crossdomain.xml | 12 + .../etherpad/src/static/css/admin/admin-stats.css | 183 + trunk/etherpad/src/static/css/beta.css | 49 + trunk/etherpad/src/static/css/broadcast.css | 386 ++ .../src/static/css/connection_diagnostics.css | 13 + trunk/etherpad/src/static/css/etherpad.css | 770 +++ trunk/etherpad/src/static/css/fluxbb.css | 55 + trunk/etherpad/src/static/css/framedpage.css | 175 + .../etherpad/src/static/css/global-pro-account.css | 52 + trunk/etherpad/src/static/css/home-opensource.css | 44 + trunk/etherpad/src/static/css/home.css | 264 + .../src/static/css/lib/jquery.contextmenu.css | 244 + trunk/etherpad/src/static/css/pad.css | 1000 ++++ trunk/etherpad/src/static/css/pad2_ejs.css | 889 ++++ trunk/etherpad/src/static/css/pne-manual.css | 143 + trunk/etherpad/src/static/css/pricing.css | 153 + trunk/etherpad/src/static/css/pro-signup.css | 69 + trunk/etherpad/src/static/css/pro/account.css | 254 + .../etherpad/src/static/css/pro/framedpage-pro.css | 125 + trunk/etherpad/src/static/css/pro/padlist.css | 115 + .../src/static/css/pro/payment-required.css | 39 + trunk/etherpad/src/static/css/pro/pro-admin.css | 343 ++ trunk/etherpad/src/static/css/pro/pro-home.css | 65 + trunk/etherpad/src/static/css/stats.css | 71 + .../src/static/css/store/eepnet-checkout.css | 284 ++ .../src/static/css/store/ondemand-billing.css | 170 + trunk/etherpad/src/static/css/store/store.css | 90 + trunk/etherpad/src/static/favicon.ico | Bin 0 -> 1354 bytes .../src/static/img/about/appjet-logo-large.gif | Bin 0 -> 11045 bytes .../src/static/img/about/appjet-logo-medium.png | Bin 0 -> 4127 bytes .../src/static/img/about/investors/mitchkapor.jpg | Bin 0 -> 30223 bytes .../etherpad/src/static/img/about/investors/pb.jpg | Bin 0 -> 23929 bytes .../etherpad/src/static/img/about/investors/pg.jpg | Bin 0 -> 28915 bytes .../src/static/img/about/investors/sanjeev.jpg | Bin 0 -> 23342 bytes .../src/static/img/about/investors/seth.jpg | Bin 0 -> 27346 bytes .../img/about/people/aaron-david-iphones-thumb.jpg | Bin 0 -> 145654 bytes .../img/about/people/aaron-david-iphones.jpg | Bin 0 -> 145654 bytes .../static/img/about/people/aaron-google-air.jpg | Bin 0 -> 163592 bytes .../img/about/people/aaron-headshot-thumb.jpg | Bin 0 -> 11310 bytes .../src/static/img/about/people/aaron-headshot.jpg | Bin 0 -> 83517 bytes .../img/about/people/aaron-headshot2-thumb.jpg | Bin 0 -> 29884 bytes .../static/img/about/people/aaron-headshot2.jpg | Bin 0 -> 99738 bytes .../img/about/people/aaron-headshot3-thumb.jpg | Bin 0 -> 27864 bytes .../static/img/about/people/aaron-headshot3.jpg | Bin 0 -> 195114 bytes .../img/about/people/daniel-headshot-thumb.jpg | Bin 0 -> 21991 bytes .../img/about/people/david-headshot-thumb.jpg | Bin 0 -> 18650 bytes .../src/static/img/about/people/david-headshot.jpg | Bin 0 -> 63296 bytes .../src/static/img/about/people/davy-headshot.jpg | Bin 0 -> 20175 bytes .../static/img/about/people/jd-headshot-thumb.jpg | Bin 0 -> 13008 bytes .../src/static/img/about/people/jd-headshot.jpg | Bin 0 -> 37777 bytes .../img/about/people/rhonda-headshot-thumb.jpg | Bin 0 -> 17360 bytes .../static/img/about/people/rhonda-headshot.jpg | Bin 0 -> 133259 bytes trunk/etherpad/src/static/img/about/pier38.png | Bin 0 -> 66877 bytes .../etherpad/src/static/img/about/quote-close.png | Bin 0 -> 1361 bytes trunk/etherpad/src/static/img/about/quote-open.png | Bin 0 -> 1341 bytes .../static/img/about/screencastpreview800x600.jpg | Bin 0 -> 248364 bytes trunk/etherpad/src/static/img/account/betawarn.jpg | Bin 0 -> 13535 bytes trunk/etherpad/src/static/img/acecarets/000000.gif | Bin 0 -> 41 bytes trunk/etherpad/src/static/img/acecarets/666666.gif | Bin 0 -> 41 bytes trunk/etherpad/src/static/img/acecarets/999999.gif | Bin 0 -> 41 bytes .../etherpad/src/static/img/acecarets/default.gif | Bin 0 -> 43 bytes trunk/etherpad/src/static/img/apr09/backgrad.png | Bin 0 -> 2276 bytes trunk/etherpad/src/static/img/apr09/black35.png | Bin 0 -> 221 bytes trunk/etherpad/src/static/img/apr09/blank.gif | Bin 0 -> 129 bytes trunk/etherpad/src/static/img/apr09/modalbar.gif | Bin 0 -> 145 bytes trunk/etherpad/src/static/img/apr09/newpadicon.gif | Bin 0 -> 89 bytes trunk/etherpad/src/static/img/apr09/shadbot.png | Bin 0 -> 149 bytes trunk/etherpad/src/static/img/apr09/shadleft.png | Bin 0 -> 142 bytes .../etherpad/src/static/img/apr09/shadleftbot.png | Bin 0 -> 172 bytes .../etherpad/src/static/img/apr09/shadlefttop.png | Bin 0 -> 929 bytes trunk/etherpad/src/static/img/apr09/shadright.png | Bin 0 -> 136 bytes .../etherpad/src/static/img/apr09/shadrightbot.png | Bin 0 -> 174 bytes .../etherpad/src/static/img/apr09/shadrighttop.png | Bin 0 -> 954 bytes trunk/etherpad/src/static/img/apr09/topbar.gif | Bin 0 -> 180 bytes trunk/etherpad/src/static/img/apr09/topbarlogo.gif | Bin 0 -> 1784 bytes trunk/etherpad/src/static/img/apr09/widthfull.gif | Bin 0 -> 104 bytes .../src/static/img/apr09/widthfullactive.gif | Bin 0 -> 104 bytes trunk/etherpad/src/static/img/apr09/widthlim.gif | Bin 0 -> 102 bytes .../src/static/img/apr09/widthlimactive.gif | Bin 0 -> 102 bytes trunk/etherpad/src/static/img/billing/amex.gif | Bin 0 -> 995 bytes .../etherpad/src/static/img/billing/creditcard.gif | Bin 0 -> 1229 bytes trunk/etherpad/src/static/img/billing/csc-help.gif | Bin 0 -> 9430 bytes trunk/etherpad/src/static/img/billing/disc.gif | Bin 0 -> 370 bytes trunk/etherpad/src/static/img/billing/invoice.gif | Bin 0 -> 424 bytes trunk/etherpad/src/static/img/billing/mc.gif | Bin 0 -> 1370 bytes trunk/etherpad/src/static/img/billing/paypal.gif | Bin 0 -> 812 bytes trunk/etherpad/src/static/img/billing/visa.gif | Bin 0 -> 724 bytes .../img/blog/posts/new-features/fullwidth.gif | Bin 0 -> 7328 bytes .../img/blog/posts/new-features/importexport.gif | Bin 0 -> 9758 bytes .../img/blog/posts/new-features/richtext.gif | Bin 0 -> 2146 bytes .../img/blog/posts/new-features/viewzoom.gif | Bin 0 -> 8257 bytes .../img/blog/posts/pricing-survey-results.png | Bin 0 -> 12994 bytes .../src/static/img/blog/posts/pricing-survey.png | Bin 0 -> 10589 bytes .../img/blog/posts/time-slider-screenshot.gif | Bin 0 -> 6544 bytes .../src/static/img/davy/bg/home-createpad.png | Bin 0 -> 4327 bytes .../static/img/davy/bg/home-features-bottom.gif | Bin 0 -> 308 bytes .../img/davy/bg/home-features-free-bottom.gif | Bin 0 -> 298 bytes .../static/img/davy/bg/home-features-paid-top.gif | Bin 0 -> 459 bytes .../src/static/img/davy/bg/home-features-top.gif | Bin 0 -> 489 bytes .../src/static/img/davy/bg/home-nav-selected.png | Bin 0 -> 195 bytes .../src/static/img/davy/bg/home-screencast.png | Bin 0 -> 5738 bytes trunk/etherpad/src/static/img/davy/bg/home2.png | Bin 0 -> 367 bytes .../img/davy/bg/product-nav-selected-white.png | Bin 0 -> 196 bytes .../static/img/davy/bg/product-nav-selected.png | Bin 0 -> 198 bytes trunk/etherpad/src/static/img/davy/bg/product.png | Bin 0 -> 161 bytes .../src/static/img/davy/btn/createpad-home.gif | Bin 0 -> 5839 bytes .../src/static/img/davy/btn/createpad-large.gif | Bin 0 -> 6614 bytes .../src/static/img/davy/btn/createpad-small.gif | Bin 0 -> 3148 bytes .../src/static/img/davy/btn/intro-screencast.png | Bin 0 -> 815 bytes .../src/static/img/davy/btn/intro-testimonials.png | Bin 0 -> 816 bytes .../etherpad/src/static/img/davy/btn/learnmore.gif | Bin 0 -> 3103 bytes .../src/static/img/davy/btn/signup-home-2.gif | Bin 0 -> 5827 bytes .../src/static/img/davy/btn/signup-home-3.gif | Bin 0 -> 5735 bytes .../src/static/img/davy/btn/signup-home-4.gif | Bin 0 -> 5818 bytes .../src/static/img/davy/btn/signup-home.gif | Bin 0 -> 5815 bytes .../etherpad/src/static/img/davy/btn/uses-more.gif | Bin 0 -> 1485 bytes trunk/etherpad/src/static/img/davy/gfx/32/114.png | Bin 0 -> 2424 bytes trunk/etherpad/src/static/img/davy/gfx/32/15.png | Bin 0 -> 2904 bytes trunk/etherpad/src/static/img/davy/gfx/32/65.png | Bin 0 -> 3028 bytes trunk/etherpad/src/static/img/davy/gfx/32/78.png | Bin 0 -> 2208 bytes trunk/etherpad/src/static/img/davy/gfx/bullet.gif | Bin 0 -> 55 bytes .../src/static/img/davy/gfx/home-logo2.gif | Bin 0 -> 7136 bytes .../src/static/img/davy/gfx/home-screencast.png | Bin 0 -> 65832 bytes trunk/etherpad/src/static/img/davy/gfx/plane.gif | Bin 0 -> 56 bytes .../src/static/img/davy/gfx/product-logo.gif | Bin 0 -> 2222 bytes .../src/static/img/davy/gfx/screenshot.gif | Bin 0 -> 23708 bytes .../src/static/img/davy/gfx/use-meetings.gif | Bin 0 -> 10521 bytes .../src/static/img/davy/gfx/use-meetings.png | Bin 0 -> 25506 bytes .../src/static/img/davy/gfx/use-programming.gif | Bin 0 -> 13655 bytes .../src/static/img/davy/gfx/use-programming.png | Bin 0 -> 56154 bytes .../src/static/img/davy/gfx/use-writing.gif | Bin 0 -> 22275 bytes .../src/static/img/davy/gfx/use-writing.png | Bin 0 -> 38264 bytes .../src/static/img/davy/txt/home-button.gif | Bin 0 -> 4749 bytes trunk/etherpad/src/static/img/featuretour/code.gif | Bin 0 -> 27794 bytes .../etherpad/src/static/img/featuretour/edits.gif | Bin 0 -> 24788 bytes .../src/static/img/featuretour/editsandusers.gif | Bin 0 -> 21815 bytes .../src/static/img/featuretour/padlock.png | Bin 0 -> 13061 bytes .../src/static/img/featuretour/revisions.gif | Bin 0 -> 20820 bytes .../etherpad/src/static/img/featuretour/users.gif | Bin 0 -> 8843 bytes .../src/static/img/feb09/framedheaderback.gif | Bin 0 -> 606 bytes .../src/static/img/feb09/framedheaderlogo.gif | Bin 0 -> 8177 bytes .../etherpad/src/static/img/feb09/home_firstp.gif | Bin 0 -> 7754 bytes .../etherpad/src/static/img/feb09/home_firstp.png | Bin 0 -> 15320 bytes .../etherpad/src/static/img/feb09/home_firstp2.gif | Bin 0 -> 7919 bytes trunk/etherpad/src/static/img/feb09/home_h1.gif | Bin 0 -> 11332 bytes trunk/etherpad/src/static/img/feb09/home_h1.png | Bin 0 -> 28857 bytes .../src/static/img/feb09/home_newpadbutton.gif | Bin 0 -> 6828 bytes .../src/static/img/feb09/home_newpadbutton.png | Bin 0 -> 10554 bytes .../src/static/img/feb09/home_newpadbutton2.gif | Bin 0 -> 7112 bytes .../static/img/feb09/home_newpadbutton_eepnet.gif | Bin 0 -> 5442 bytes .../etherpad/src/static/img/feb09/hometop_back.gif | Bin 0 -> 2743 bytes trunk/etherpad/src/static/img/feb09/nav1.gif | Bin 0 -> 10901 bytes trunk/etherpad/src/static/img/feb09/nav1_back.gif | Bin 0 -> 150 bytes trunk/etherpad/src/static/img/feb09/nav2.gif | Bin 0 -> 17028 bytes trunk/etherpad/src/static/img/feb09/screencast.gif | Bin 0 -> 20091 bytes .../src/static/img/home/etherpad-mainheader1.jpg | Bin 0 -> 48871 bytes .../src/static/img/home/headergradient.gif | Bin 0 -> 246 bytes trunk/etherpad/src/static/img/home/homeheader1.jpg | Bin 0 -> 33227 bytes trunk/etherpad/src/static/img/home/homeheader2.jpg | Bin 0 -> 33259 bytes trunk/etherpad/src/static/img/home/leftgrad.gif | Bin 0 -> 113 bytes .../src/static/img/home/pencilpaperback.png | Bin 0 -> 83487 bytes .../src/static/img/home/screencapture1.gif | Bin 0 -> 106694 bytes .../etherpad/src/static/img/home/underdevicon.gif | Bin 0 -> 98 bytes trunk/etherpad/src/static/img/icon/downarrow.gif | Bin 0 -> 376 bytes trunk/etherpad/src/static/img/icon/feed.gif | Bin 0 -> 1135 bytes .../etherpad/src/static/img/jun09/pad/backgrad.gif | Bin 0 -> 697 bytes .../src/static/img/jun09/pad/bottomareagfx.gif | Bin 0 -> 1045 bytes .../src/static/img/jun09/pad/colorpicker.gif | Bin 0 -> 1806 bytes .../src/static/img/jun09/pad/connectingbar.gif | Bin 0 -> 10819 bytes .../static/img/jun09/pad/connectionindicator.gif | Bin 0 -> 1185 bytes .../src/static/img/jun09/pad/docbarstates.png | Bin 0 -> 3314 bytes .../src/static/img/jun09/pad/docbarstates2.png | Bin 0 -> 4902 bytes .../src/static/img/jun09/pad/docbarstates3.png | Bin 0 -> 4990 bytes .../src/static/img/jun09/pad/docpaneledge.png | Bin 0 -> 589 bytes .../src/static/img/jun09/pad/docpaneledge2.png | Bin 0 -> 635 bytes .../src/static/img/jun09/pad/docpanelmiddle.png | Bin 0 -> 240 bytes .../src/static/img/jun09/pad/docpanelmiddle2.png | Bin 0 -> 295 bytes .../etherpad/src/static/img/jun09/pad/editbar.gif | Bin 0 -> 4667 bytes .../etherpad/src/static/img/jun09/pad/editbar2.gif | Bin 0 -> 9156 bytes .../etherpad/src/static/img/jun09/pad/editbar3.png | Bin 0 -> 16869 bytes .../etherpad/src/static/img/jun09/pad/editbar3.xcf | Bin 0 -> 79212 bytes .../src/static/img/jun09/pad/editbarback.gif | Bin 0 -> 368 bytes .../src/static/img/jun09/pad/feedbackbox2.gif | Bin 0 -> 6262 bytes .../src/static/img/jun09/pad/fileicons.gif | Bin 0 -> 1397 bytes .../etherpad/src/static/img/jun09/pad/hdraggie.gif | Bin 0 -> 453 bytes .../src/static/img/jun09/pad/inviteshare.gif | Bin 0 -> 511 bytes .../src/static/img/jun09/pad/inviteshare2.gif | Bin 0 -> 1836 bytes .../src/static/img/jun09/pad/layoutbuttons.gif | Bin 0 -> 3750 bytes .../etherpad/src/static/img/jun09/pad/overlay.png | Bin 0 -> 141 bytes .../etherpad/src/static/img/jun09/pad/overlay2.png | Bin 0 -> 149 bytes trunk/etherpad/src/static/img/jun09/pad/padtop.gif | Bin 0 -> 8055 bytes .../etherpad/src/static/img/jun09/pad/padtop2.gif | Bin 0 -> 6168 bytes .../etherpad/src/static/img/jun09/pad/padtop3.gif | Bin 0 -> 7511 bytes .../etherpad/src/static/img/jun09/pad/padtop4.gif | Bin 0 -> 8192 bytes .../etherpad/src/static/img/jun09/pad/padtop4.png | Bin 0 -> 17161 bytes .../etherpad/src/static/img/jun09/pad/padtop4.xcf | Bin 0 -> 41184 bytes .../etherpad/src/static/img/jun09/pad/padtop5.png | Bin 0 -> 18850 bytes .../etherpad/src/static/img/jun09/pad/padtop5.xcf | Bin 0 -> 66525 bytes .../src/static/img/jun09/pad/padtopback.gif | Bin 0 -> 553 bytes .../src/static/img/jun09/pad/padtopback2.gif | Bin 0 -> 384 bytes trunk/etherpad/src/static/img/jun09/pad/protop.png | Bin 0 -> 6768 bytes trunk/etherpad/src/static/img/jun09/pad/protop.xcf | Bin 0 -> 16565 bytes trunk/etherpad/src/static/img/jun09/pad/public.gif | Bin 0 -> 1141 bytes .../src/static/img/jun09/pad/savedrevarrows.gif | Bin 0 -> 866 bytes .../src/static/img/jun09/pad/savedrevsgfx2.gif | Bin 0 -> 1904 bytes .../src/static/img/jun09/pad/sharebox2.gif | Bin 0 -> 8836 bytes .../src/static/img/jun09/pad/sharebox3.gif | Bin 0 -> 6056 bytes .../src/static/img/jun09/pad/sharebox4.gif | Bin 0 -> 5788 bytes .../src/static/img/jun09/pad/sharedistri.gif | Bin 0 -> 85 bytes .../etherpad/src/static/img/jun09/pad/syncdone.gif | Bin 0 -> 211 bytes .../etherpad/src/static/img/jun09/pad/syncing.gif | Bin 0 -> 673 bytes .../etherpad/src/static/img/jun09/pad/syncing2.gif | Bin 0 -> 172 bytes .../src/static/img/jun09/pad/viewbargfx.gif | Bin 0 -> 155 bytes .../cmenu-gloss-cyan-menu-item-hover.gif | Bin 0 -> 52 bytes .../cmenu-gloss-menu-item-hover.gif | Bin 0 -> 52 bytes ...cmenu-gloss-semitransparent-menu-item-hover.png | Bin 0 -> 2837 bytes .../cmenu-human-menu-item-hover.gif | Bin 0 -> 195 bytes .../cmenu-osx-menu-item-hover.gif | Bin 0 -> 87 bytes .../jquery.contextmenu.images/cmenu-vista-bg.gif | Bin 0 -> 64 bytes .../cmenu-vista-menu-item-hover.gif | Bin 0 -> 347 bytes .../lib/jquery.contextmenu.images/cmenu-xp-bg.gif | Bin 0 -> 223 bytes trunk/etherpad/src/static/img/may09/bold.gif | Bin 0 -> 70 bytes trunk/etherpad/src/static/img/may09/doc.gif | Bin 0 -> 632 bytes trunk/etherpad/src/static/img/may09/doc.png | Bin 0 -> 3317 bytes trunk/etherpad/src/static/img/may09/html.gif | Bin 0 -> 1040 bytes trunk/etherpad/src/static/img/may09/html.png | Bin 0 -> 3468 bytes trunk/etherpad/src/static/img/may09/italic.gif | Bin 0 -> 73 bytes trunk/etherpad/src/static/img/may09/leftarrow.gif | Bin 0 -> 1016 bytes trunk/etherpad/src/static/img/may09/leftarrow2.gif | Bin 0 -> 950 bytes trunk/etherpad/src/static/img/may09/link.gif | Bin 0 -> 622 bytes trunk/etherpad/src/static/img/may09/link.png | Bin 0 -> 3323 bytes trunk/etherpad/src/static/img/may09/odt.gif | Bin 0 -> 405 bytes trunk/etherpad/src/static/img/may09/odt.png | Bin 0 -> 3341 bytes trunk/etherpad/src/static/img/may09/padlock.gif | Bin 0 -> 1053 bytes .../etherpad/src/static/img/may09/padlockopen.gif | Bin 0 -> 109 bytes .../src/static/img/may09/passwordlocked.gif | Bin 0 -> 1053 bytes .../static/img/may09/passwordlocked_cropped.gif | Bin 0 -> 114 bytes .../etherpad/src/static/img/may09/passwordnone.gif | Bin 0 -> 636 bytes trunk/etherpad/src/static/img/may09/paypal.gif | Bin 0 -> 3794 bytes trunk/etherpad/src/static/img/may09/pdf.gif | Bin 0 -> 398 bytes trunk/etherpad/src/static/img/may09/pdf.png | Bin 0 -> 3320 bytes trunk/etherpad/src/static/img/may09/redo.gif | Bin 0 -> 78 bytes trunk/etherpad/src/static/img/may09/txt.gif | Bin 0 -> 381 bytes trunk/etherpad/src/static/img/may09/txt.png | Bin 0 -> 3139 bytes trunk/etherpad/src/static/img/may09/underline.gif | Bin 0 -> 81 bytes trunk/etherpad/src/static/img/may09/undo.gif | Bin 0 -> 79 bytes trunk/etherpad/src/static/img/miniplane.gif | Bin 0 -> 70 bytes .../src/static/img/misc/diagnostic-links.gif | Bin 0 -> 10132 bytes trunk/etherpad/src/static/img/misc/status-ball.gif | Bin 0 -> 1553 bytes trunk/etherpad/src/static/img/misc/traclogo.gif | Bin 0 -> 5684 bytes trunk/etherpad/src/static/img/oct/atlonglast.gif | Bin 0 -> 4901 bytes trunk/etherpad/src/static/img/oct/banner1.jpg | Bin 0 -> 19897 bytes trunk/etherpad/src/static/img/oct/banner2.jpg | Bin 0 -> 45052 bytes trunk/etherpad/src/static/img/oct/banner3.jpg | Bin 0 -> 38726 bytes trunk/etherpad/src/static/img/oct/banner4.jpg | Bin 0 -> 39563 bytes trunk/etherpad/src/static/img/oct/banner5.gif | Bin 0 -> 24046 bytes trunk/etherpad/src/static/img/oct/banner6.gif | Bin 0 -> 23655 bytes trunk/etherpad/src/static/img/oct/banner7.gif | Bin 0 -> 24352 bytes trunk/etherpad/src/static/img/oct/banner8.gif | Bin 0 -> 24724 bytes trunk/etherpad/src/static/img/oct/banner9.gif | Bin 0 -> 24363 bytes trunk/etherpad/src/static/img/oct/bannerback5.gif | Bin 0 -> 2957 bytes trunk/etherpad/src/static/img/oct/bannerback6.gif | Bin 0 -> 2140 bytes trunk/etherpad/src/static/img/oct/bodyback1.gif | Bin 0 -> 488 bytes trunk/etherpad/src/static/img/oct/bodyback2.gif | Bin 0 -> 560 bytes trunk/etherpad/src/static/img/oct/bodyback3.gif | Bin 0 -> 608 bytes trunk/etherpad/src/static/img/oct/bodyback4.gif | Bin 0 -> 964 bytes trunk/etherpad/src/static/img/oct/bodyback5.gif | Bin 0 -> 579 bytes trunk/etherpad/src/static/img/oct/bodybacktop1.gif | Bin 0 -> 2991 bytes trunk/etherpad/src/static/img/oct/computers.gif | Bin 0 -> 27542 bytes trunk/etherpad/src/static/img/oct/computers2.gif | Bin 0 -> 27434 bytes trunk/etherpad/src/static/img/oct/glossyblue.gif | Bin 0 -> 1521 bytes trunk/etherpad/src/static/img/oct/glossyblue2.gif | Bin 0 -> 994 bytes trunk/etherpad/src/static/img/oct/glossyblueh.gif | Bin 0 -> 920 bytes trunk/etherpad/src/static/img/oct/insetrect.gif | Bin 0 -> 7056 bytes .../etherpad/src/static/img/oct/minilogo1-05e.gif | Bin 0 -> 2201 bytes .../etherpad/src/static/img/oct/minilogo1-07f.gif | Bin 0 -> 2252 bytes trunk/etherpad/src/static/img/oct/minilogo3.jpg | Bin 0 -> 12805 bytes trunk/etherpad/src/static/img/oct/minitopback1.gif | Bin 0 -> 954 bytes trunk/etherpad/src/static/img/oct/minitopback2.gif | Bin 0 -> 1598 bytes .../src/static/img/oct/minitopbar1-05e.gif | Bin 0 -> 284 bytes .../src/static/img/oct/minitopbar2-05e.gif | Bin 0 -> 330 bytes .../src/static/img/oct/minitopbar2-07f.gif | Bin 0 -> 330 bytes trunk/etherpad/src/static/img/oct/minitopbar3.jpg | Bin 0 -> 12805 bytes trunk/etherpad/src/static/img/oct/minitopbar4.gif | Bin 0 -> 2818 bytes trunk/etherpad/src/static/img/oct/minitoplogo1.gif | Bin 0 -> 4184 bytes trunk/etherpad/src/static/img/oct/minitoplogo2.gif | Bin 0 -> 3255 bytes trunk/etherpad/src/static/img/oct/newpadmain.gif | Bin 0 -> 1172 bytes .../etherpad/src/static/img/oct/newpadmainback.gif | Bin 0 -> 801 bytes .../src/static/img/oct/newpadmainbackh.gif | Bin 0 -> 801 bytes trunk/etherpad/src/static/img/oct/pageshot.png | Bin 0 -> 151570 bytes trunk/etherpad/src/static/img/oct/pageshotmini.png | Bin 0 -> 80505 bytes .../src/static/img/oct/sidehead-gradhilite.gif | Bin 0 -> 288 bytes trunk/etherpad/src/static/img/oct/tinytriangle.gif | Bin 0 -> 62 bytes trunk/etherpad/src/static/img/oct/topnav1.gif | Bin 0 -> 12521 bytes trunk/etherpad/src/static/img/oct/topnav2.gif | Bin 0 -> 11286 bytes trunk/etherpad/src/static/img/oct/topnav3.gif | Bin 0 -> 12363 bytes trunk/etherpad/src/static/img/oct/topnav4.gif | Bin 0 -> 11803 bytes trunk/etherpad/src/static/img/oct/topnav5.gif | Bin 0 -> 11650 bytes trunk/etherpad/src/static/img/oct/topnav6.gif | Bin 0 -> 11295 bytes trunk/etherpad/src/static/img/oct/topnavback1.gif | Bin 0 -> 1594 bytes trunk/etherpad/src/static/img/oct/topnavback2.gif | Bin 0 -> 1299 bytes trunk/etherpad/src/static/img/oct/topnavback3.gif | Bin 0 -> 380 bytes .../src/static/img/oct/usecasesnavdown.gif | Bin 0 -> 1388 bytes .../src/static/img/oct/usecasesnavdownh.gif | Bin 0 -> 1337 bytes .../etherpad/src/static/img/oct/usecasesnavup.gif | Bin 0 -> 1119 bytes .../etherpad/src/static/img/oct/usecasesnavuph.gif | Bin 0 -> 720 bytes .../src/static/img/oct/watchscreencast.gif | Bin 0 -> 25840 bytes .../src/static/img/pad/animated-orb-orange-12.gif | Bin 0 -> 2614 bytes trunk/etherpad/src/static/img/pad/backgrad.png | Bin 0 -> 1290 bytes .../pad/backshadow/backshadow-940-20-eee-20.gif | Bin 0 -> 1052 bytes .../pad/backshadow/backshadow-940-20-fff-20.gif | Bin 0 -> 1052 bytes .../pad/backshadow/backshadow-940-20-fff-40.gif | Bin 0 -> 1009 bytes .../pad/backshadow/backshadow-940-20-fff-60.gif | Bin 0 -> 1123 bytes .../img/pad/backshadow/botshadow-940-20-eee-20.gif | Bin 0 -> 1746 bytes .../static/img/pad/etherpad-logo-small-grad.gif | Bin 0 -> 1537 bytes .../src/static/img/pad/etherpad-logo-small.gif | Bin 0 -> 6664 bytes .../src/static/img/pad/etherpad-logo-small2.gif | Bin 0 -> 6646 bytes .../src/static/img/pad/expandy-arrow-down.gif | Bin 0 -> 500 bytes .../src/static/img/pad/expandy-arrow-right.gif | Bin 0 -> 296 bytes .../static/img/pad/expandy-arrow6-down-active.gif | Bin 0 -> 57 bytes .../src/static/img/pad/expandy-arrow6-down.gif | Bin 0 -> 57 bytes .../static/img/pad/expandy-arrow6-right-active.gif | Bin 0 -> 61 bytes .../src/static/img/pad/expandy-arrow6-right.gif | Bin 0 -> 61 bytes .../etherpad/src/static/img/pad/header-revgrad.gif | Bin 0 -> 598 bytes trunk/etherpad/src/static/img/pad/newpad.gif | Bin 0 -> 251 bytes .../src/static/img/pad/orb-greenred-12.gif | Bin 0 -> 1105 bytes trunk/etherpad/src/static/img/pad/padbg1.jpg | Bin 0 -> 120888 bytes trunk/etherpad/src/static/img/pad/padbg2.jpg | Bin 0 -> 44119 bytes trunk/etherpad/src/static/img/pad/padbg3.jpg | Bin 0 -> 12577 bytes trunk/etherpad/src/static/img/pad/padbg4.jpg | Bin 0 -> 12696 bytes trunk/etherpad/src/static/img/pad/padbg5.jpg | Bin 0 -> 8158 bytes trunk/etherpad/src/static/img/pad/padhead1.jpg | Bin 0 -> 13413 bytes trunk/etherpad/src/static/img/pad/padhead2.jpg | Bin 0 -> 14104 bytes trunk/etherpad/src/static/img/pad/padhead3.jpg | Bin 0 -> 6750 bytes .../src/static/img/pad/pencil-icon-small-blue.gif | Bin 0 -> 84 bytes .../etherpad/src/static/img/pad/sidehead-grad.gif | Bin 0 -> 292 bytes .../static/img/pad/timeslider/button_depressed.png | Bin 0 -> 4610 bytes .../img/pad/timeslider/button_undepressed.png | Bin 0 -> 4625 bytes .../pad/timeslider/crushed_button_depressed.png | Bin 0 -> 4134 bytes .../pad/timeslider/crushed_button_undepressed.png | Bin 0 -> 4166 bytes .../pad/timeslider/crushed_current_location.png | Bin 0 -> 1009 bytes .../static/img/pad/timeslider/crushed_pause.png | Bin 0 -> 2876 bytes .../src/static/img/pad/timeslider/crushed_play.png | Bin 0 -> 2946 bytes .../img/pad/timeslider/crushed_play_button.png | Bin 0 -> 4305 bytes .../pad/timeslider/crushed_timeslider_mockup.png | Bin 0 -> 8164 bytes .../static/img/pad/timeslider/current_location.gif | Bin 0 -> 1502 bytes .../static/img/pad/timeslider/current_location.png | Bin 0 -> 1100 bytes .../src/static/img/pad/timeslider/pause.gif | Bin 0 -> 3320 bytes .../src/static/img/pad/timeslider/pause.png | Bin 0 -> 2883 bytes .../src/static/img/pad/timeslider/play.gif | Bin 0 -> 3297 bytes .../src/static/img/pad/timeslider/play.png | Bin 0 -> 3017 bytes .../src/static/img/pad/timeslider/play_button.png | Bin 0 -> 4867 bytes .../src/static/img/pad/timeslider/star.gif | Bin 0 -> 3511 bytes .../src/static/img/pad/timeslider/star.png | Bin 0 -> 3241 bytes .../static/img/pad/timeslider/star_selected.png | Bin 0 -> 3242 bytes .../static/img/pad/timeslider/stepper_buttons.png | Bin 0 -> 4858 bytes .../img/pad/timeslider/timeslider_background.png | Bin 0 -> 915 bytes .../static/img/pad/timeslider/timeslider_left.png | Bin 0 -> 1653 bytes .../img/pad/timeslider/timeslider_mockup.png | Bin 0 -> 4860 bytes .../static/img/pad/timeslider/timeslider_right.png | Bin 0 -> 1581 bytes trunk/etherpad/src/static/img/pricing/free.gif | Bin 0 -> 7419 bytes trunk/etherpad/src/static/img/pricing/group.gif | Bin 0 -> 6783 bytes .../etherpad/src/static/img/pricing/on-demand.gif | Bin 0 -> 5791 bytes .../src/static/img/pricing/private-network.gif | Bin 0 -> 4677 bytes trunk/etherpad/src/static/img/pricing/support.gif | Bin 0 -> 2028 bytes .../src/static/img/pro/billing/cards-button.gif | Bin 0 -> 9524 bytes .../src/static/img/pro/box/blue-boxtop.gif | Bin 0 -> 523 bytes .../src/static/img/pro/buttons/bluebutton120.gif | Bin 0 -> 951 bytes .../src/static/img/pro/header/pro-header-back.gif | Bin 0 -> 213 bytes .../src/static/img/pro/header/pro-header-logo.png | Bin 0 -> 5527 bytes .../src/static/img/pro/header/pro-header-logo.xcf | Bin 0 -> 13274 bytes .../img/pro/header/pro-header-plustopnav-back.gif | Bin 0 -> 474 bytes .../src/static/img/pro/padlist/gear-drop.gif | Bin 0 -> 300 bytes .../src/static/img/pro/padlist/paper-icon.gif | Bin 0 -> 619 bytes .../src/static/img/pro/padlist/trash-icon.gif | Bin 0 -> 1080 bytes .../src/static/img/pro/topnav/pro-topnav-back.gif | Bin 0 -> 137 bytes .../src/static/img/pro/topnav/pro-topnav-notch.gif | Bin 0 -> 92 bytes trunk/etherpad/src/static/img/tinyplane.gif | Bin 0 -> 59 bytes trunk/etherpad/src/static/img/wavejet.jpg | Bin 0 -> 55379 bytes trunk/etherpad/src/static/js/ace.js | 29 + trunk/etherpad/src/static/js/billing.js | 111 + trunk/etherpad/src/static/js/billing_shared.js | 94 + trunk/etherpad/src/static/js/broadcast.js | 607 +++ .../etherpad/src/static/js/broadcast_revisions.js | 119 + trunk/etherpad/src/static/js/broadcast_slider.js | 401 ++ trunk/etherpad/src/static/js/collab_client.js | 628 +++ trunk/etherpad/src/static/js/colorutils.js | 91 + trunk/etherpad/src/static/js/confirmation.js | 21 + .../src/static/js/connection_diagnostics.js | 126 + trunk/etherpad/src/static/js/cssmanager_client.js | 88 + trunk/etherpad/src/static/js/domline_client.js | 210 + trunk/etherpad/src/static/js/draggable.js | 60 + trunk/etherpad/src/static/js/easysync2_client.js | 1777 +++++++ trunk/etherpad/src/static/js/etherpad.js | 217 + trunk/etherpad/src/static/js/jquery-1.2.6.js | 3549 ++++++++++++++ trunk/etherpad/src/static/js/jquery-1.3.2.js | 4376 +++++++++++++++++ trunk/etherpad/src/static/js/json2.js | 498 ++ .../src/static/js/lib/jquery.contextmenu.js | 284 ++ .../src/static/js/linestylefilter_client.js | 252 + trunk/etherpad/src/static/js/pad.js.old | 1984 ++++++++ trunk/etherpad/src/static/js/pad2.js | 591 +++ trunk/etherpad/src/static/js/pad_chat.js | 295 ++ .../etherpad/src/static/js/pad_connectionstatus.js | 63 + trunk/etherpad/src/static/js/pad_cookie.js | 101 + trunk/etherpad/src/static/js/pad_docbar.js | 347 ++ trunk/etherpad/src/static/js/pad_editbar.js | 107 + trunk/etherpad/src/static/js/pad_editor.js | 136 + trunk/etherpad/src/static/js/pad_impexp.js | 187 + trunk/etherpad/src/static/js/pad_modals.js | 364 ++ trunk/etherpad/src/static/js/pad_savedrevs.js | 408 ++ trunk/etherpad/src/static/js/pad_userlist.js | 604 +++ trunk/etherpad/src/static/js/pad_utils.js | 359 ++ trunk/etherpad/src/static/js/pricing.js | 19 + .../src/static/js/pro/guest-knock-client.js | 53 + .../src/static/js/pro/pro-padlist-client.js | 104 + trunk/etherpad/src/static/js/pro/signin-client.js | 27 + trunk/etherpad/src/static/js/pulse.jquery.js | 105 + trunk/etherpad/src/static/js/statpage.js | 143 + trunk/etherpad/src/static/js/store.js | 116 + trunk/etherpad/src/static/js/swfobject.js | 24 + trunk/etherpad/src/static/js/timeslider.js | 663 +++ trunk/etherpad/src/static/js/undo-xpopup.js | 25 + trunk/etherpad/src/static/swf/vidplayer.swf | Bin 0 -> 41390 bytes trunk/etherpad/src/templates/500_body.ejs | 26 + trunk/etherpad/src/templates/beta/signup.ejs | 63 + .../src/templates/email/eepnet_license_info.ejs | 72 + .../templates/email/eepnet_purchase_receipt.ejs | 93 + trunk/etherpad/src/templates/email/padinvite.ejs | 18 + .../src/templates/email/pro_beta_invite.ejs | 23 + .../src/templates/email/pro_payment_failure.ejs | 26 + .../src/templates/email/pro_payment_receipt.ejs | 55 + .../etherpad/src/templates/framed/framedfooter.ejs | 13 + .../src/templates/framed/framedheader-pro.ejs | 76 + .../etherpad/src/templates/framed/framedheader.ejs | 13 + .../src/templates/framed/framedpage-pro.ejs | 31 + trunk/etherpad/src/templates/framed/framedpage.ejs | 37 + trunk/etherpad/src/templates/html.ejs | 43 + trunk/etherpad/src/templates/main/home.ejs | 58 + .../src/templates/main/pro_signup_body.ejs | 71 + trunk/etherpad/src/templates/misc/pad_default.ejs | 16 + trunk/etherpad/src/templates/notice.ejs | 16 + trunk/etherpad/src/templates/pad/create_body.ejs | 26 + .../src/templates/pad/create_body_rafter.ejs | 23 + trunk/etherpad/src/templates/pad/exporthtml.ejs | 28 + trunk/etherpad/src/templates/pad/pad_body.ejs | 69 + trunk/etherpad/src/templates/pad/pad_body2.ejs | 472 ++ trunk/etherpad/src/templates/pad/pad_content.ejs | 297 ++ .../src/templates/pad/pad_download_link.ejs | 27 + .../etherpad/src/templates/pad/pad_iphone_body.ejs | 29 + trunk/etherpad/src/templates/pad/padfull_body.ejs | 32 + .../etherpad/src/templates/pad/padslider_body.ejs | 41 + trunk/etherpad/src/templates/pad/padview_body.ejs | 141 + .../src/templates/pad/total_users_exceeded.ejs | 29 + .../etherpad/src/templates/pro-account/recover.ejs | 48 + .../etherpad/src/templates/pro-account/sign-in.ejs | 57 + .../pro/account/account-welcome-email.ejs | 32 + .../templates/pro/account/create-admin-account.ejs | 37 + .../pro/account/forgot-password-email.ejs | 22 + .../src/templates/pro/account/forgot-password.ejs | 66 + .../account/global-multi-domain-recover-email.ejs | 27 + .../src/templates/pro/account/guest-knock.ejs | 27 + .../src/templates/pro/account/my-account.ejs | 67 + .../src/templates/pro/account/signin-guest.ejs | 51 + .../etherpad/src/templates/pro/account/signin.ejs | 81 + .../src/templates/pro/admin/account-manager.ejs | 59 + .../src/templates/pro/admin/admin-template.ejs | 31 + trunk/etherpad/src/templates/pro/admin/admin.ejs | 15 + .../src/templates/pro/admin/billing-invoices.ejs | 45 + .../src/templates/pro/admin/delete-account.ejs | 35 + .../src/templates/pro/admin/manage-account.ejs | 64 + .../src/templates/pro/admin/manage-billing.ejs | 35 + .../src/templates/pro/admin/new-account.ejs | 86 + .../src/templates/pro/admin/pne-config.ejs | 33 + .../src/templates/pro/admin/pne-dashboard.ejs | 40 + .../templates/pro/admin/pne-license-manager.ejs | 132 + .../etherpad/src/templates/pro/admin/pne-shell.ejs | 33 + .../src/templates/pro/admin/pro-config.ejs | 55 + .../src/templates/pro/admin/single-invoice.ejs | 47 + .../src/templates/pro/padlist/pro-padlist.ejs | 49 + .../src/templates/pro/pro-payment-required.ejs | 51 + trunk/etherpad/src/templates/pro/pro_home.ejs | 103 + .../src/templates/statistics/stat_page.ejs | 89 + trunk/etherpad/src/templates/store/csc-help.ejs | 23 + .../store/eepnet-checkout/billing-info.ejs | 183 + .../src/templates/store/eepnet-checkout/cart.ejs | 119 + .../store/eepnet-checkout/checkout-template.ejs | 38 + .../store/eepnet-checkout/confirmation.ejs | 33 + .../store/eepnet-checkout/license-info.ejs | 40 + .../templates/store/eepnet-checkout/purchase.ejs | 33 + .../templates/store/eepnet-checkout/receipt.ejs | 43 + .../templates/store/eepnet-checkout/summary.ejs | 91 + .../store/eepnet-checkout/support-contract.ejs | 41 + .../src/templates/store/eepnet_download.ejs | 43 + .../src/templates/store/eepnet_eval_nextsteps.ejs | 40 + .../src/templates/store/eepnet_eval_signup.ejs | 125 + trunk/infrastructure/.gitignore | 5 + trunk/infrastructure/ace/.gitignore | 1 + trunk/infrastructure/ace/README | 69 + trunk/infrastructure/ace/bin/make | 337 ++ trunk/infrastructure/ace/bin/serve | 45 + trunk/infrastructure/ace/blog.txt | 390 ++ trunk/infrastructure/ace/build/.gitignore | 2 + trunk/infrastructure/ace/build/index.html | 47 + trunk/infrastructure/ace/build/jquery-1.2.1.js | 2992 ++++++++++++ trunk/infrastructure/ace/build/testcode.js | 36 + trunk/infrastructure/ace/easysync-notes.txt | 129 + trunk/infrastructure/ace/lib/rhino-js-1.7r1.jar | 1 + .../ace/lib/yuicompressor-2.4-appjet.jar | 1 + trunk/infrastructure/ace/notes.txt | 195 + trunk/infrastructure/ace/www/ace2_common.js | 115 + trunk/infrastructure/ace/www/ace2_common_dev.js | 296 ++ trunk/infrastructure/ace/www/ace2_inner.js | 4817 +++++++++++++++++++ trunk/infrastructure/ace/www/ace2_outer.js | 214 + trunk/infrastructure/ace/www/ace2_wrapper.js | 226 + trunk/infrastructure/ace/www/bbtree.js | 372 ++ trunk/infrastructure/ace/www/changesettracker.js | 170 + trunk/infrastructure/ace/www/colorutils.js | 91 + trunk/infrastructure/ace/www/contentcollector.js | 527 ++ trunk/infrastructure/ace/www/cssmanager.js | 88 + trunk/infrastructure/ace/www/dev.html | 39 + trunk/infrastructure/ace/www/domline.js | 210 + trunk/infrastructure/ace/www/easy_sync.js | 923 ++++ trunk/infrastructure/ace/www/easysync2.js | 1968 ++++++++ trunk/infrastructure/ace/www/easysync2_tests.js | 877 ++++ trunk/infrastructure/ace/www/editor.css | 114 + trunk/infrastructure/ace/www/firebug/errorIcon.png | Bin 0 -> 457 bytes trunk/infrastructure/ace/www/firebug/firebug.css | 209 + trunk/infrastructure/ace/www/firebug/firebug.html | 23 + trunk/infrastructure/ace/www/firebug/firebug.js | 688 +++ trunk/infrastructure/ace/www/firebug/firebugx.js | 26 + trunk/infrastructure/ace/www/firebug/infoIcon.png | Bin 0 -> 524 bytes .../infrastructure/ace/www/firebug/warningIcon.png | Bin 0 -> 516 bytes trunk/infrastructure/ace/www/index.html | 50 + trunk/infrastructure/ace/www/inner.css | 48 + trunk/infrastructure/ace/www/jquery-1.2.1.js | 2992 ++++++++++++ trunk/infrastructure/ace/www/lang_html.js | 1685 +++++++ trunk/infrastructure/ace/www/lang_js.js | 102 + trunk/infrastructure/ace/www/lexer_support.js | 1068 +++++ trunk/infrastructure/ace/www/linestylefilter.js | 253 + trunk/infrastructure/ace/www/magicdom.js | 293 ++ trunk/infrastructure/ace/www/multilang_lexer.js | 349 ++ trunk/infrastructure/ace/www/processing.js | 1714 +++++++ trunk/infrastructure/ace/www/profiler.js | 117 + trunk/infrastructure/ace/www/skiplist.js | 347 ++ trunk/infrastructure/ace/www/spanlist.js | 279 ++ trunk/infrastructure/ace/www/syntax-new.css | 35 + trunk/infrastructure/ace/www/syntax.css | 32 + trunk/infrastructure/ace/www/test.html | 11 + trunk/infrastructure/ace/www/testcode.js | 36 + trunk/infrastructure/ace/www/toSource.js | 274 ++ trunk/infrastructure/ace/www/undomodule.js | 258 + trunk/infrastructure/ace/www/virtual_lines.js | 287 ++ trunk/infrastructure/bin/classpath.sh | 21 + trunk/infrastructure/bin/comp.sh | 199 + trunk/infrastructure/bin/compilecache.sh | 64 + trunk/infrastructure/bin/jscomp.sh | 23 + trunk/infrastructure/bin/makejar.sh | 74 + trunk/infrastructure/bin/makesars.sh | 23 + trunk/infrastructure/bin/run.sh | 20 + .../importexport.scala | 189 + .../com.etherpad/easysync2support.scala | 167 + trunk/infrastructure/com.etherpad/licensing.scala | 169 + trunk/infrastructure/com.etherpad/main.scala | 23 + .../framework-src/modules/atomfeed.js | 72 + trunk/infrastructure/framework-src/modules/blob.js | 50 + .../framework-src/modules/cache_utils.js | 36 + .../infrastructure/framework-src/modules/comet.js | 91 + .../framework-src/modules/dateutils.js | 48 + .../framework-src/modules/dispatch.js | 149 + trunk/infrastructure/framework-src/modules/ejs.js | 471 ++ .../infrastructure/framework-src/modules/email.js | 53 + .../framework-src/modules/exceptionutils.js | 210 + .../framework-src/modules/execution.js | 58 + .../framework-src/modules/fastJSON.js | 27 + .../framework-src/modules/faststatic.js | 318 ++ .../framework-src/modules/fileutils.js | 108 + .../framework-src/modules/funhtml.js | 158 + .../framework-src/modules/global/appjet.js | 107 + .../framework-src/modules/global/request.js | 312 ++ .../framework-src/modules/global/response.js | 294 ++ .../infrastructure/framework-src/modules/image.js | 110 + .../framework-src/modules/jsutils.js | 195 + .../framework-src/modules/netutils.js | 88 + .../framework-src/modules/profiler.js | 48 + .../framework-src/modules/sessions.js | 156 + .../modules/sqlbase/persistent_vars.js | 57 + .../framework-src/modules/sqlbase/sqlbase.js | 205 + .../framework-src/modules/sqlbase/sqlcommon.js | 99 + .../framework-src/modules/sqlbase/sqlobj.js | 505 ++ .../framework-src/modules/stringutils.js | 399 ++ trunk/infrastructure/framework-src/modules/sync.js | 81 + .../infrastructure/framework-src/modules/timer.js | 29 + trunk/infrastructure/framework-src/modules/varz.js | 52 + .../framework-src/modules/yuicompressor.js | 85 + trunk/infrastructure/framework-src/oncomet.js | 38 + trunk/infrastructure/framework-src/onerror.js | 24 + trunk/infrastructure/framework-src/onprint.js | 19 + trunk/infrastructure/framework-src/onrequest.js | 24 + trunk/infrastructure/framework-src/onreset.js | 19 + trunk/infrastructure/framework-src/onsars.js | 27 + .../framework-src/onscheduledtask.js | 33 + trunk/infrastructure/framework-src/onshutdown.js | 19 + trunk/infrastructure/framework-src/onstartup.js | 19 + .../infrastructure/framework-src/onsyntaxerror.js | 17 + trunk/infrastructure/framework-src/postamble.js | 19 + trunk/infrastructure/framework-src/preamble.js | 325 ++ trunk/infrastructure/framework-src/syntaxerror.js | 32 + trunk/infrastructure/lib/activation.jar | Bin 0 -> 56290 bytes trunk/infrastructure/lib/c3p0-0.9.1.2.jar | Bin 0 -> 610790 bytes trunk/infrastructure/lib/commons-lang-2.4.jar | Bin 0 -> 261809 bytes trunk/infrastructure/lib/derby-10.5.1.1.jar | Bin 0 -> 2513361 bytes trunk/infrastructure/lib/derbytools.jar | Bin 0 -> 155796 bytes trunk/infrastructure/lib/dnsjava-2.0.6.jar | Bin 0 -> 268823 bytes trunk/infrastructure/lib/jetty-6.1.20.jar | Bin 0 -> 533605 bytes .../infrastructure/lib/jetty-sslengine-6.1.20.jar | Bin 0 -> 18285 bytes trunk/infrastructure/lib/jetty-util-6.1.20.jar | Bin 0 -> 176016 bytes trunk/infrastructure/lib/json.jar | Bin 0 -> 42335 bytes trunk/infrastructure/lib/mail.jar | Bin 0 -> 356519 bytes trunk/infrastructure/lib/manifest | 2 + trunk/infrastructure/lib/rhino-js-1.7r1.jar | Bin 0 -> 769000 bytes trunk/infrastructure/lib/sanselan-0.94aj.jar | Bin 0 -> 390426 bytes .../lib/servlet-api-2.5-20081211.jar | Bin 0 -> 134190 bytes trunk/infrastructure/lib/tagsoup-1.2.jar | Bin 0 -> 90023 bytes .../lib/yuicompressor-2.4-appjet.jar | Bin 0 -> 574932 bytes .../net.appjet.ajstdlib/ajstdlib.scala | 253 + .../net.appjet.ajstdlib/sqlbase.scala | 563 +++ .../net.appjet.ajstdlib/streaming-client.js | 920 ++++ .../net.appjet.ajstdlib/streaming-iframe.html | 76 + .../net.appjet.ajstdlib/streaming.scala | 892 ++++ .../infrastructure/net.appjet.ajstdlib/timer.scala | 85 + .../net.appjet.bodylock/bodylock.scala | 291 ++ .../net.appjet.bodylock/compressor.scala | 269 ++ .../infrastructure/net.appjet.common.cli/cli.scala | 56 + .../net.appjet.common.sars/sars.scala | 348 ++ .../net.appjet.common.sars/sha1.scala | 40 + .../net.appjet.common/rhino/rhinospect.scala | 58 + .../net.appjet.common/util/BCrypt.java | 752 +++ .../net.appjet.common/util/BetterFile.java | 280 ++ .../net.appjet.common/util/ClassReload.java | 263 + .../net.appjet.common/util/ExpiringMapping.java | 163 + .../util/HttpServletRequestFactory.java | 306 ++ .../net.appjet.common/util/LenientFormatter.java | 2809 +++++++++++ .../net.appjet.common/util/LimitedSizeMapping.java | 28 + .../infrastructure/net.appjet.oui/ConfigParam.java | 25 + trunk/infrastructure/net.appjet.oui/FastJSON.scala | 171 + .../net.appjet.oui/GeneratedConfigParam.java | 23 + trunk/infrastructure/net.appjet.oui/config.scala | 240 + .../infrastructure/net.appjet.oui/dynamicvar.scala | 49 + .../infrastructure/net.appjet.oui/encryption.scala | 267 ++ .../infrastructure/net.appjet.oui/execution.scala | 654 +++ trunk/infrastructure/net.appjet.oui/files.scala | 355 ++ trunk/infrastructure/net.appjet.oui/logging.scala | 530 +++ trunk/infrastructure/net.appjet.oui/main.scala | 386 ++ .../infrastructure/net.appjet.oui/monitoring.scala | 125 + trunk/infrastructure/net.appjet.oui/network.scala | 50 + .../net.appjet.oui/servermodel.scala | 209 + trunk/infrastructure/net.appjet.oui/stats.scala | 220 + .../net.appjet.oui/synchronizer.scala | 69 + trunk/infrastructure/net.appjet.oui/util.scala | 153 + .../rhino1_7R1/apiClasses.properties | 65 + trunk/infrastructure/rhino1_7R1/build-date | 1 + trunk/infrastructure/rhino1_7R1/build.properties | 65 + trunk/infrastructure/rhino1_7R1/build.xml | 310 ++ .../rhino1_7R1/deprecatedsrc/build.xml | 61 + .../javascript/ClassDefinitionException.java | 53 + .../mozilla/javascript/NotAFunctionException.java | 52 + .../org/mozilla/javascript/PropertyException.java | 54 + .../xml/impl/xmlbeans/LogicalEquality.java | 367 ++ .../javascript/xml/impl/xmlbeans/Namespace.java | 335 ++ .../xml/impl/xmlbeans/NamespaceHelper.java | 350 ++ .../javascript/xml/impl/xmlbeans/QName.java | 321 ++ .../mozilla/javascript/xml/impl/xmlbeans/XML.java | 3092 ++++++++++++ .../javascript/xml/impl/xmlbeans/XMLCtor.java | 269 ++ .../javascript/xml/impl/xmlbeans/XMLLibImpl.java | 754 +++ .../javascript/xml/impl/xmlbeans/XMLList.java | 1617 +++++++ .../javascript/xml/impl/xmlbeans/XMLName.java | 171 + .../xml/impl/xmlbeans/XMLObjectImpl.java | 724 +++ .../javascript/xml/impl/xmlbeans/XMLWithScope.java | 125 + .../rhino1_7R1/examples/Control.java | 100 + .../rhino1_7R1/examples/Counter.java | 59 + .../rhino1_7R1/examples/CounterTest.java | 82 + .../rhino1_7R1/examples/DynamicScopes.java | 204 + .../rhino1_7R1/examples/E4X/e4x_example.js | 223 + trunk/infrastructure/rhino1_7R1/examples/File.java | 348 ++ trunk/infrastructure/rhino1_7R1/examples/Foo.java | 169 + .../infrastructure/rhino1_7R1/examples/Matrix.java | 279 ++ .../rhino1_7R1/examples/NervousText.html | 53 + .../rhino1_7R1/examples/NervousText.js | 109 + .../rhino1_7R1/examples/PrimitiveWrapFactory.java | 72 + .../rhino1_7R1/examples/RunScript.java | 78 + .../rhino1_7R1/examples/RunScript2.java | 69 + .../rhino1_7R1/examples/RunScript3.java | 88 + .../rhino1_7R1/examples/RunScript4.java | 78 + .../infrastructure/rhino1_7R1/examples/Shell.java | 345 ++ .../rhino1_7R1/examples/SwingApplication.js | 111 + .../rhino1_7R1/examples/checkParam.js | 137 + trunk/infrastructure/rhino1_7R1/examples/enum.js | 69 + trunk/infrastructure/rhino1_7R1/examples/jsdoc.js | 556 +++ .../rhino1_7R1/examples/liveConnect.js | 57 + trunk/infrastructure/rhino1_7R1/examples/unique.js | 56 + .../rhino1_7R1/javadoc/allclasses-frame.html | 89 + .../rhino1_7R1/javadoc/allclasses-noframe.html | 89 + .../rhino1_7R1/javadoc/constant-values.html | 424 ++ .../rhino1_7R1/javadoc/deprecated-list.html | 386 ++ .../rhino1_7R1/javadoc/help-doc.html | 217 + .../rhino1_7R1/javadoc/index-all.html | 1566 ++++++ trunk/infrastructure/rhino1_7R1/javadoc/index.html | 37 + .../javadoc/org/mozilla/javascript/Callable.html | 224 + .../javadoc/org/mozilla/javascript/ClassCache.html | 445 ++ .../org/mozilla/javascript/ClassShutter.html | 247 + .../org/mozilla/javascript/CompilerEnvirons.html | 669 +++ .../javadoc/org/mozilla/javascript/Context.html | 3581 ++++++++++++++ .../org/mozilla/javascript/ContextAction.html | 214 + .../javascript/ContextFactory.Listener.html | 236 + .../org/mozilla/javascript/ContextFactory.html | 935 ++++ .../javadoc/org/mozilla/javascript/EcmaError.html | 455 ++ .../org/mozilla/javascript/ErrorReporter.html | 299 ++ .../org/mozilla/javascript/EvaluatorException.html | 405 ++ .../javadoc/org/mozilla/javascript/Function.html | 297 ++ .../org/mozilla/javascript/FunctionObject.html | 774 +++ .../mozilla/javascript/GeneratedClassLoader.html | 236 + .../org/mozilla/javascript/ImporterTopLevel.html | 583 +++ .../mozilla/javascript/JavaScriptException.html | 375 ++ .../org/mozilla/javascript/RefCallable.html | 233 + .../org/mozilla/javascript/RhinoException.html | 544 +++ .../javadoc/org/mozilla/javascript/Script.html | 231 + .../javadoc/org/mozilla/javascript/Scriptable.html | 756 +++ .../org/mozilla/javascript/ScriptableObject.html | 2604 ++++++++++ .../org/mozilla/javascript/SecurityController.html | 507 ++ .../org/mozilla/javascript/Synchronizer.html | 331 ++ .../org/mozilla/javascript/WrapFactory.html | 400 ++ .../org/mozilla/javascript/WrappedException.html | 323 ++ .../javadoc/org/mozilla/javascript/Wrapper.html | 214 + .../mozilla/javascript/debug/DebuggableScript.html | 455 ++ .../mozilla/javascript/debug/package-frame.html | 32 + .../mozilla/javascript/debug/package-summary.html | 156 + .../org/mozilla/javascript/debug/package-tree.html | 149 + .../javascript/optimizer/ClassCompiler.html | 461 ++ .../javascript/optimizer/package-frame.html | 32 + .../javascript/optimizer/package-summary.html | 155 + .../mozilla/javascript/optimizer/package-tree.html | 151 + .../org/mozilla/javascript/package-frame.html | 100 + .../org/mozilla/javascript/package-summary.html | 294 ++ .../org/mozilla/javascript/package-tree.html | 185 + .../serialize/ScriptableInputStream.html | 373 ++ .../serialize/ScriptableOutputStream.html | 458 ++ .../javascript/serialize/package-frame.html | 34 + .../javascript/serialize/package-summary.html | 161 + .../mozilla/javascript/serialize/package-tree.html | 163 + .../rhino1_7R1/javadoc/overview-frame.html | 48 + .../rhino1_7R1/javadoc/overview-summary.html | 161 + .../rhino1_7R1/javadoc/overview-tree.html | 196 + .../infrastructure/rhino1_7R1/javadoc/package-list | 4 + .../rhino1_7R1/javadoc/resources/inherit.gif | Bin 0 -> 57 bytes .../rhino1_7R1/javadoc/serialized-form.html | 1652 +++++++ .../rhino1_7R1/javadoc/stylesheet.css | 29 + .../rhino1_7R1/lib/jsr173_1.0_api.jar | Bin 0 -> 23630 bytes trunk/infrastructure/rhino1_7R1/lib/xbean.jar | Bin 0 -> 2664574 bytes trunk/infrastructure/rhino1_7R1/src/build.xml | 98 + trunk/infrastructure/rhino1_7R1/src/manifest | 3 + .../src/org/mozilla/classfile/ByteCode.java | 274 ++ .../src/org/mozilla/classfile/ClassFileWriter.java | 3038 ++++++++++++ .../src/org/mozilla/javascript/Arguments.java | 311 ++ .../src/org/mozilla/javascript/BaseFunction.java | 553 +++ .../src/org/mozilla/javascript/Callable.java | 59 + .../src/org/mozilla/javascript/ClassCache.java | 220 + .../src/org/mozilla/javascript/ClassShutter.java | 89 + .../org/mozilla/javascript/CompilerEnvirons.java | 233 + .../org/mozilla/javascript/ConstProperties.java | 109 + .../src/org/mozilla/javascript/Context.java | 2526 ++++++++++ .../src/org/mozilla/javascript/ContextAction.java | 59 + .../src/org/mozilla/javascript/ContextFactory.java | 594 +++ .../org/mozilla/javascript/ContextListener.java | 60 + .../src/org/mozilla/javascript/DToA.java | 1271 +++++ .../src/org/mozilla/javascript/Decompiler.java | 918 ++++ .../mozilla/javascript/DefaultErrorReporter.java | 113 + .../mozilla/javascript/DefiningClassLoader.java | 88 + .../src/org/mozilla/javascript/Delegator.java | 266 ++ .../src/org/mozilla/javascript/EcmaError.java | 160 + .../src/org/mozilla/javascript/ErrorReporter.java | 106 + .../src/org/mozilla/javascript/Evaluator.java | 118 + .../org/mozilla/javascript/EvaluatorException.java | 123 + .../src/org/mozilla/javascript/Function.java | 84 + .../src/org/mozilla/javascript/FunctionNode.java | 117 + .../src/org/mozilla/javascript/FunctionObject.java | 569 +++ .../mozilla/javascript/GeneratedClassLoader.java | 66 + .../src/org/mozilla/javascript/IRFactory.java | 1607 +++++++ .../src/org/mozilla/javascript/IdFunctionCall.java | 55 + .../org/mozilla/javascript/IdFunctionObject.java | 189 + .../org/mozilla/javascript/IdScriptableObject.java | 734 +++ .../org/mozilla/javascript/ImporterTopLevel.java | 318 ++ .../org/mozilla/javascript/InformativeParser.java | 225 + .../org/mozilla/javascript/InterfaceAdapter.java | 156 + .../mozilla/javascript/InterpretedFunction.java | 221 + .../src/org/mozilla/javascript/Interpreter.java | 4643 ++++++++++++++++++ .../org/mozilla/javascript/InterpreterData.java | 192 + .../src/org/mozilla/javascript/JavaAdapter.java | 1129 +++++ .../src/org/mozilla/javascript/JavaMembers.java | 935 ++++ .../mozilla/javascript/JavaScriptException.java | 117 + .../rhino1_7R1/src/org/mozilla/javascript/Kit.java | 486 ++ .../org/mozilla/javascript/LazilyLoadedCtor.java | 136 + .../src/org/mozilla/javascript/MemberBox.java | 362 ++ .../src/org/mozilla/javascript/NativeArray.java | 1727 +++++++ .../src/org/mozilla/javascript/NativeBoolean.java | 170 + .../src/org/mozilla/javascript/NativeCall.java | 154 + .../src/org/mozilla/javascript/NativeDate.java | 1604 +++++++ .../src/org/mozilla/javascript/NativeError.java | 227 + .../src/org/mozilla/javascript/NativeFunction.java | 169 + .../org/mozilla/javascript/NativeGenerator.java | 281 ++ .../src/org/mozilla/javascript/NativeGlobal.java | 790 +++ .../src/org/mozilla/javascript/NativeIterator.java | 260 + .../org/mozilla/javascript/NativeJavaArray.java | 168 + .../org/mozilla/javascript/NativeJavaClass.java | 320 ++ .../mozilla/javascript/NativeJavaConstructor.java | 85 + .../org/mozilla/javascript/NativeJavaMethod.java | 576 +++ .../org/mozilla/javascript/NativeJavaObject.java | 1002 ++++ .../org/mozilla/javascript/NativeJavaPackage.java | 199 + .../mozilla/javascript/NativeJavaTopPackage.java | 187 + .../src/org/mozilla/javascript/NativeMath.java | 399 ++ .../src/org/mozilla/javascript/NativeNumber.java | 244 + .../src/org/mozilla/javascript/NativeObject.java | 316 ++ .../src/org/mozilla/javascript/NativeScript.java | 221 + .../src/org/mozilla/javascript/NativeString.java | 983 ++++ .../src/org/mozilla/javascript/NativeWith.java | 207 + .../src/org/mozilla/javascript/Node.java | 1394 ++++++ .../org/mozilla/javascript/NodeTransformer.java | 565 +++ .../src/org/mozilla/javascript/ObjArray.java | 388 ++ .../src/org/mozilla/javascript/ObjToIntMap.java | 697 +++ .../src/org/mozilla/javascript/Parser.java | 2554 ++++++++++ .../javascript/PolicySecurityController.java | 223 + .../rhino1_7R1/src/org/mozilla/javascript/Ref.java | 64 + .../src/org/mozilla/javascript/RefCallable.java | 59 + .../src/org/mozilla/javascript/RegExpProxy.java | 71 + .../src/org/mozilla/javascript/RhinoException.java | 306 ++ .../src/org/mozilla/javascript/Script.java | 73 + .../src/org/mozilla/javascript/ScriptOrFnNode.java | 241 + .../src/org/mozilla/javascript/ScriptRuntime.java | 3830 +++++++++++++++ .../src/org/mozilla/javascript/Scriptable.java | 342 ++ .../org/mozilla/javascript/ScriptableObject.java | 2428 ++++++++++ .../src/org/mozilla/javascript/SecureCaller.java | 198 + .../org/mozilla/javascript/SecurityController.java | 211 + .../org/mozilla/javascript/SecurityUtilities.java | 80 + .../src/org/mozilla/javascript/SpecialRef.java | 151 + .../src/org/mozilla/javascript/Synchronizer.java | 81 + .../src/org/mozilla/javascript/Token.java | 436 ++ .../src/org/mozilla/javascript/TokenStream.java | 1500 ++++++ .../src/org/mozilla/javascript/UintMap.java | 659 +++ .../src/org/mozilla/javascript/Undefined.java | 60 + .../src/org/mozilla/javascript/UniqueTag.java | 120 + .../src/org/mozilla/javascript/VMBridge.java | 183 + .../src/org/mozilla/javascript/WrapFactory.java | 183 + .../org/mozilla/javascript/WrappedException.java | 93 + .../src/org/mozilla/javascript/Wrapper.java | 58 + .../javascript/continuations/Continuation.java | 136 + .../org/mozilla/javascript/debug/DebugFrame.java | 91 + .../mozilla/javascript/debug/DebuggableObject.java | 61 + .../mozilla/javascript/debug/DebuggableScript.java | 119 + .../src/org/mozilla/javascript/debug/Debugger.java | 69 + .../mozilla/javascript/jdk11/VMBridge_jdk11.java | 84 + .../mozilla/javascript/jdk13/VMBridge_jdk13.java | 157 + .../mozilla/javascript/jdk15/VMBridge_jdk15.java | 87 + .../org/mozilla/javascript/optimizer/Block.java | 615 +++ .../javascript/optimizer/ClassCompiler.java | 214 + .../org/mozilla/javascript/optimizer/Codegen.java | 5031 ++++++++++++++++++++ .../javascript/optimizer/DataFlowBitSet.java | 134 + .../javascript/optimizer/OptFunctionNode.java | 149 + .../mozilla/javascript/optimizer/OptRuntime.java | 311 ++ .../javascript/optimizer/OptTransformer.java | 133 + .../mozilla/javascript/optimizer/Optimizer.java | 510 ++ .../mozilla/javascript/regexp/NativeRegExp.java | 2782 +++++++++++ .../javascript/regexp/NativeRegExpCtor.java | 289 ++ .../org/mozilla/javascript/regexp/RegExpImpl.java | 541 +++ .../org/mozilla/javascript/regexp/SubString.java | 75 + .../javascript/resources/Messages.properties | 778 +++ .../javascript/resources/Messages_fr.properties | 329 ++ .../serialize/ScriptableInputStream.java | 112 + .../serialize/ScriptableOutputStream.java | 207 + .../src/org/mozilla/javascript/xml/XMLLib.java | 132 + .../src/org/mozilla/javascript/xml/XMLObject.java | 128 + trunk/infrastructure/rhino1_7R1/testsrc/base.skip | 685 +++ trunk/infrastructure/rhino1_7R1/testsrc/build.xml | 183 + trunk/infrastructure/rhino1_7R1/testsrc/opt1.skip | 26 + .../org/mozilla/javascript/drivers/JsDriver.java | 838 ++++ .../org/mozilla/javascript/drivers/ShellTest.java | 346 ++ .../mozilla/javascript/drivers/StandardTests.java | 212 + .../org/mozilla/javascript/drivers/results.html | 105 + .../mozilla/javascript/tests/Bug409702Test.java | 49 + .../javascript/tests/JavaAcessibilityTest.java | 99 + .../javascript/tests/PrivateAccessClass.java | 89 + trunk/infrastructure/rhino1_7R1/toolsrc/build.xml | 103 + .../javascript/tools/ToolErrorReporter.java | 225 + .../org/mozilla/javascript/tools/debugger/Dim.java | 1560 ++++++ .../javascript/tools/debugger/GuiCallback.java | 71 + .../mozilla/javascript/tools/debugger/Main.java | 431 ++ .../javascript/tools/debugger/ScopeProvider.java | 52 + .../javascript/tools/debugger/SwingGui.java | 3547 ++++++++++++++ .../mozilla/javascript/tools/debugger/build.xml | 126 + .../javascript/tools/idswitch/CodePrinter.java | 212 + .../javascript/tools/idswitch/FileBody.java | 191 + .../javascript/tools/idswitch/IdValuePair.java | 58 + .../mozilla/javascript/tools/idswitch/Main.java | 612 +++ .../javascript/tools/idswitch/SwitchGenerator.java | 491 ++ .../org/mozilla/javascript/tools/jsc/Main.java | 395 ++ .../javascript/tools/resources/Messages.properties | 268 ++ .../javascript/tools/shell/ConsoleTextArea.java | 300 ++ .../javascript/tools/shell/Environment.java | 141 + .../org/mozilla/javascript/tools/shell/Global.java | 1038 ++++ .../mozilla/javascript/tools/shell/JSConsole.java | 225 + .../javascript/tools/shell/JavaPolicySecurity.java | 240 + .../org/mozilla/javascript/tools/shell/Main.java | 638 +++ .../mozilla/javascript/tools/shell/QuitAction.java | 50 + .../javascript/tools/shell/SecurityProxy.java | 48 + .../tools/shell/ShellContextFactory.java | 114 + .../infrastructure/rhino1_7R1/xmlimplsrc/build.xml | 165 + .../org/mozilla/javascript/xmlimpl/Namespace.java | 367 ++ .../org/mozilla/javascript/xmlimpl/QName.java | 381 ++ .../org/mozilla/javascript/xmlimpl/XML.java | 734 +++ .../org/mozilla/javascript/xmlimpl/XMLCtor.java | 280 ++ .../org/mozilla/javascript/xmlimpl/XMLLibImpl.java | 606 +++ .../org/mozilla/javascript/xmlimpl/XMLList.java | 765 +++ .../org/mozilla/javascript/xmlimpl/XMLName.java | 469 ++ .../mozilla/javascript/xmlimpl/XMLObjectImpl.java | 812 ++++ .../mozilla/javascript/xmlimpl/XMLWithScope.java | 125 + .../org/mozilla/javascript/xmlimpl/XmlNode.java | 869 ++++ .../mozilla/javascript/xmlimpl/XmlProcessor.java | 445 ++ .../infrastructure/yuicompressor/lib/jargs-1.0.jar | Bin 0 -> 11406 bytes .../yuicompressor/lib/rhino-yuicompressor.jar | Bin 0 -> 538064 bytes trunk/infrastructure/yuicompressor/make.sh | 27 + .../yahoo/platform/yui/compressor/Bootstrap.java | 22 + .../platform/yui/compressor/CssCompressor.java | 188 + .../platform/yui/compressor/JarClassLoader.java | 158 + .../yui/compressor/JavaScriptCompressor.java | 1307 +++++ .../yui/compressor/JavaScriptIdentifier.java | 55 + .../platform/yui/compressor/JavaScriptToken.java | 28 + .../platform/yui/compressor/ScriptOrFnScope.java | 169 + .../platform/yui/compressor/YUICompressor.java | 232 + .../org/mozilla/javascript/Decompiler.java | 916 ++++ .../org/mozilla/javascript/Decompiler.java.orig | 910 ++++ .../org/mozilla/javascript/Parser.java | 2170 +++++++++ .../org/mozilla/javascript/Parser.java.orig | 2159 +++++++++ .../org/mozilla/javascript/Token.java | 420 ++ .../org/mozilla/javascript/Token.java.orig | 417 ++ .../org/mozilla/javascript/TokenStream.java | 1381 ++++++ .../org/mozilla/javascript/TokenStream.java.orig | 1398 ++++++ 1093 files changed, 238727 insertions(+) create mode 100644 trunk/COPYING create mode 100644 trunk/README create mode 100644 trunk/etherpad/.gitignore create mode 100644 trunk/etherpad/bin/.gitignore create mode 100755 trunk/etherpad/bin/rebuildjar.sh create mode 100755 trunk/etherpad/bin/run-local.sh create mode 100755 trunk/etherpad/bin/setup-mysql-db.sh create mode 100644 trunk/etherpad/etc/etherpad.changeme.properties create mode 100644 trunk/etherpad/etc/etherpad.localdev-default.properties create mode 100644 trunk/etherpad/lib/dnsjava-2.0.6.jar create mode 100644 trunk/etherpad/lib/jbcrypt-0.2.jar create mode 100644 trunk/etherpad/lib/jcommon-1.0.15.jar create mode 100644 trunk/etherpad/lib/jfreechart-1.0.12.jar create mode 100644 trunk/etherpad/src/etherpad/admin/shell.js create mode 100644 trunk/etherpad/src/etherpad/billing/billing.js create mode 100644 trunk/etherpad/src/etherpad/billing/fields.js create mode 100644 trunk/etherpad/src/etherpad/billing/team_billing.js create mode 100644 trunk/etherpad/src/etherpad/collab/ace/contentcollector.js create mode 100644 trunk/etherpad/src/etherpad/collab/ace/domline.js create mode 100644 trunk/etherpad/src/etherpad/collab/ace/easysync1.js create mode 100644 trunk/etherpad/src/etherpad/collab/ace/easysync2.js create mode 100644 trunk/etherpad/src/etherpad/collab/ace/easysync2_tests.js create mode 100644 trunk/etherpad/src/etherpad/collab/ace/linestylefilter.js create mode 100644 trunk/etherpad/src/etherpad/collab/collab_server.js create mode 100644 trunk/etherpad/src/etherpad/collab/collabroom_server.js create mode 100644 trunk/etherpad/src/etherpad/collab/genimg.js create mode 100644 trunk/etherpad/src/etherpad/collab/json_sans_eval.js create mode 100644 trunk/etherpad/src/etherpad/collab/readonly_server.js create mode 100644 trunk/etherpad/src/etherpad/collab/server_utils.js create mode 100644 trunk/etherpad/src/etherpad/control/aboutcontrol.js create mode 100644 trunk/etherpad/src/etherpad/control/admincontrol.js create mode 100644 trunk/etherpad/src/etherpad/control/blogcontrol.js create mode 100644 trunk/etherpad/src/etherpad/control/connection_diagnostics_control.js create mode 100644 trunk/etherpad/src/etherpad/control/global_pro_account_control.js create mode 100644 trunk/etherpad/src/etherpad/control/historycontrol.js create mode 100644 trunk/etherpad/src/etherpad/control/loadtestcontrol.js create mode 100644 trunk/etherpad/src/etherpad/control/maincontrol.js create mode 100644 trunk/etherpad/src/etherpad/control/pad/pad_changeset_control.js create mode 100644 trunk/etherpad/src/etherpad/control/pad/pad_control.js create mode 100644 trunk/etherpad/src/etherpad/control/pad/pad_importexport_control.js create mode 100644 trunk/etherpad/src/etherpad/control/pad/pad_view_control.js create mode 100644 trunk/etherpad/src/etherpad/control/pne_manual_control.js create mode 100644 trunk/etherpad/src/etherpad/control/pne_tracker_control.js create mode 100644 trunk/etherpad/src/etherpad/control/pro/account_control.js create mode 100644 trunk/etherpad/src/etherpad/control/pro/admin/account_manager_control.js create mode 100644 trunk/etherpad/src/etherpad/control/pro/admin/license_manager_control.js create mode 100644 trunk/etherpad/src/etherpad/control/pro/admin/pro_admin_control.js create mode 100644 trunk/etherpad/src/etherpad/control/pro/admin/pro_config_control.js create mode 100644 trunk/etherpad/src/etherpad/control/pro/admin/team_billing_control.js create mode 100644 trunk/etherpad/src/etherpad/control/pro/pro_main_control.js create mode 100644 trunk/etherpad/src/etherpad/control/pro/pro_padlist_control.js create mode 100644 trunk/etherpad/src/etherpad/control/pro_beta_control.js create mode 100644 trunk/etherpad/src/etherpad/control/pro_signup_control.js create mode 100644 trunk/etherpad/src/etherpad/control/scriptcontrol.js create mode 100644 trunk/etherpad/src/etherpad/control/static_control.js create mode 100644 trunk/etherpad/src/etherpad/control/statscontrol.js create mode 100644 trunk/etherpad/src/etherpad/control/store/eepnet_checkout_control.js create mode 100644 trunk/etherpad/src/etherpad/control/store/storecontrol.js create mode 100644 trunk/etherpad/src/etherpad/control/testcontrol.js create mode 100644 trunk/etherpad/src/etherpad/db_migrations/m0000_test.js create mode 100644 trunk/etherpad/src/etherpad/db_migrations/m0001_eepnet_signups_init.js create mode 100644 trunk/etherpad/src/etherpad/db_migrations/m0002_eepnet_signups_2.js create mode 100644 trunk/etherpad/src/etherpad/db_migrations/m0003_create_tests_table_v2.js create mode 100644 trunk/etherpad/src/etherpad/db_migrations/m0004_convert_all_tables_to_innodb.js create mode 100644 trunk/etherpad/src/etherpad/db_migrations/m0005_create_billing_tables.js create mode 100644 trunk/etherpad/src/etherpad/db_migrations/m0006_eepnet_signups_3.js create mode 100644 trunk/etherpad/src/etherpad/db_migrations/m0007_create_pro_tables_v4.js create mode 100644 trunk/etherpad/src/etherpad/db_migrations/m0008_persistent_vars.js create mode 100644 trunk/etherpad/src/etherpad/db_migrations/m0009_pad_tables.js create mode 100644 trunk/etherpad/src/etherpad/db_migrations/m0010_pad_sqlmeta.js create mode 100644 trunk/etherpad/src/etherpad/db_migrations/m0011_pro_users_temppass.js create mode 100644 trunk/etherpad/src/etherpad/db_migrations/m0012_pro_users_auto_signin.js create mode 100644 trunk/etherpad/src/etherpad/db_migrations/m0013_pne_padv2_upgrade.js create mode 100644 trunk/etherpad/src/etherpad/db_migrations/m0014_pne_globalpadids.js create mode 100644 trunk/etherpad/src/etherpad/db_migrations/m0015_padmeta_passwords.js create mode 100644 trunk/etherpad/src/etherpad/db_migrations/m0016_pne_tracking_data.js create mode 100644 trunk/etherpad/src/etherpad/db_migrations/m0017_pne_tracking_data_v2.js create mode 100644 trunk/etherpad/src/etherpad/db_migrations/m0018_eepnet_checkout_tables.js create mode 100644 trunk/etherpad/src/etherpad/db_migrations/m0019_padmeta_deleted.js create mode 100644 trunk/etherpad/src/etherpad/db_migrations/m0020_padmeta_archived.js create mode 100644 trunk/etherpad/src/etherpad/db_migrations/m0021_pro_padmeta_json.js create mode 100644 trunk/etherpad/src/etherpad/db_migrations/m0022_create_userids_table.js create mode 100644 trunk/etherpad/src/etherpad/db_migrations/m0023_create_usagestats_table.js create mode 100644 trunk/etherpad/src/etherpad/db_migrations/m0024_statistics_table.js create mode 100644 trunk/etherpad/src/etherpad/db_migrations/m0025_rename_pro_users_table.js create mode 100644 trunk/etherpad/src/etherpad/db_migrations/m0026_create_guests_table.js create mode 100644 trunk/etherpad/src/etherpad/db_migrations/m0027_pro_config.js create mode 100644 trunk/etherpad/src/etherpad/db_migrations/m0028_ondemand_beta_emails.js create mode 100644 trunk/etherpad/src/etherpad/db_migrations/m0029_lowercase_subdomains.js create mode 100644 trunk/etherpad/src/etherpad/db_migrations/m0030_fix_statistics_values.js create mode 100644 trunk/etherpad/src/etherpad/db_migrations/m0031_deleted_pro_users.js create mode 100644 trunk/etherpad/src/etherpad/db_migrations/m0032_reduce_topvalues_counts.js create mode 100644 trunk/etherpad/src/etherpad/db_migrations/m0033_pro_account_usage.js create mode 100644 trunk/etherpad/src/etherpad/db_migrations/m0034_create_recurring_billing_table.js create mode 100644 trunk/etherpad/src/etherpad/db_migrations/m0035_add_email_to_paymentinfo.js create mode 100644 trunk/etherpad/src/etherpad/db_migrations/m0036_create_missing_subscription_records.js create mode 100644 trunk/etherpad/src/etherpad/db_migrations/m0037_create_pro_referral_table.js create mode 100644 trunk/etherpad/src/etherpad/db_migrations/m0038_pad_coarse_revs.js create mode 100644 trunk/etherpad/src/etherpad/db_migrations/migration_runner.js create mode 100644 trunk/etherpad/src/etherpad/debug.js create mode 100644 trunk/etherpad/src/etherpad/globals.js create mode 100644 trunk/etherpad/src/etherpad/helpers.js create mode 100644 trunk/etherpad/src/etherpad/importexport/importexport.js create mode 100644 trunk/etherpad/src/etherpad/legacy_urls.js create mode 100644 trunk/etherpad/src/etherpad/licensing.js create mode 100644 trunk/etherpad/src/etherpad/log.js create mode 100644 trunk/etherpad/src/etherpad/metrics/metrics.js create mode 100644 trunk/etherpad/src/etherpad/pad/activepads.js create mode 100644 trunk/etherpad/src/etherpad/pad/chatarchive.js create mode 100644 trunk/etherpad/src/etherpad/pad/dbwriter.js create mode 100644 trunk/etherpad/src/etherpad/pad/easysync2migration.js create mode 100644 trunk/etherpad/src/etherpad/pad/exporthtml.js create mode 100644 trunk/etherpad/src/etherpad/pad/importhtml.js create mode 100644 trunk/etherpad/src/etherpad/pad/model.js create mode 100644 trunk/etherpad/src/etherpad/pad/noprowatcher.js create mode 100644 trunk/etherpad/src/etherpad/pad/pad_migrations.js create mode 100644 trunk/etherpad/src/etherpad/pad/pad_security.js create mode 100644 trunk/etherpad/src/etherpad/pad/padevents.js create mode 100644 trunk/etherpad/src/etherpad/pad/padusers.js create mode 100644 trunk/etherpad/src/etherpad/pad/padutils.js create mode 100644 trunk/etherpad/src/etherpad/pad/revisions.js create mode 100644 trunk/etherpad/src/etherpad/pne/pne_utils.js create mode 100644 trunk/etherpad/src/etherpad/pro/domains.js create mode 100644 trunk/etherpad/src/etherpad/pro/pro_account_auto_signin.js create mode 100644 trunk/etherpad/src/etherpad/pro/pro_accounts.js create mode 100644 trunk/etherpad/src/etherpad/pro/pro_config.js create mode 100644 trunk/etherpad/src/etherpad/pro/pro_pad_db.js create mode 100644 trunk/etherpad/src/etherpad/pro/pro_pad_editors.js create mode 100644 trunk/etherpad/src/etherpad/pro/pro_padlist.js create mode 100644 trunk/etherpad/src/etherpad/pro/pro_padmeta.js create mode 100644 trunk/etherpad/src/etherpad/pro/pro_quotas.js create mode 100644 trunk/etherpad/src/etherpad/pro/pro_utils.js create mode 100644 trunk/etherpad/src/etherpad/quotas.js create mode 100644 trunk/etherpad/src/etherpad/sessions.js create mode 100644 trunk/etherpad/src/etherpad/statistics/exceptions.js create mode 100644 trunk/etherpad/src/etherpad/statistics/statistics.js create mode 100644 trunk/etherpad/src/etherpad/store/checkout.js create mode 100644 trunk/etherpad/src/etherpad/store/eepnet_checkout.js create mode 100644 trunk/etherpad/src/etherpad/store/eepnet_trial.js create mode 100644 trunk/etherpad/src/etherpad/testing/testutils.js create mode 100644 trunk/etherpad/src/etherpad/testing/unit_tests/t0000_test.js create mode 100644 trunk/etherpad/src/etherpad/testing/unit_tests/t0001_sqlbase_transaction_rollback.js create mode 100644 trunk/etherpad/src/etherpad/testing/unit_tests/t0002_license_generation.js create mode 100644 trunk/etherpad/src/etherpad/testing/unit_tests/t0003_persistent_vars.js create mode 100644 trunk/etherpad/src/etherpad/testing/unit_tests/t0004_sqlobj.js create mode 100644 trunk/etherpad/src/etherpad/testing/unit_tests/t0005_easysync.js create mode 100644 trunk/etherpad/src/etherpad/usage_stats/usage_stats.js create mode 100644 trunk/etherpad/src/etherpad/utils.js create mode 100644 trunk/etherpad/src/main.js create mode 100644 trunk/etherpad/src/static/crossdomain.xml create mode 100644 trunk/etherpad/src/static/css/admin/admin-stats.css create mode 100644 trunk/etherpad/src/static/css/beta.css create mode 100644 trunk/etherpad/src/static/css/broadcast.css create mode 100644 trunk/etherpad/src/static/css/connection_diagnostics.css create mode 100644 trunk/etherpad/src/static/css/etherpad.css create mode 100644 trunk/etherpad/src/static/css/fluxbb.css create mode 100644 trunk/etherpad/src/static/css/framedpage.css create mode 100644 trunk/etherpad/src/static/css/global-pro-account.css create mode 100644 trunk/etherpad/src/static/css/home-opensource.css create mode 100644 trunk/etherpad/src/static/css/home.css create mode 100644 trunk/etherpad/src/static/css/lib/jquery.contextmenu.css create mode 100644 trunk/etherpad/src/static/css/pad.css create mode 100644 trunk/etherpad/src/static/css/pad2_ejs.css create mode 100644 trunk/etherpad/src/static/css/pne-manual.css create mode 100644 trunk/etherpad/src/static/css/pricing.css create mode 100644 trunk/etherpad/src/static/css/pro-signup.css create mode 100644 trunk/etherpad/src/static/css/pro/account.css create mode 100644 trunk/etherpad/src/static/css/pro/framedpage-pro.css create mode 100644 trunk/etherpad/src/static/css/pro/padlist.css create mode 100644 trunk/etherpad/src/static/css/pro/payment-required.css create mode 100644 trunk/etherpad/src/static/css/pro/pro-admin.css create mode 100644 trunk/etherpad/src/static/css/pro/pro-home.css create mode 100644 trunk/etherpad/src/static/css/stats.css create mode 100644 trunk/etherpad/src/static/css/store/eepnet-checkout.css create mode 100644 trunk/etherpad/src/static/css/store/ondemand-billing.css create mode 100644 trunk/etherpad/src/static/css/store/store.css create mode 100644 trunk/etherpad/src/static/favicon.ico create mode 100644 trunk/etherpad/src/static/img/about/appjet-logo-large.gif create mode 100644 trunk/etherpad/src/static/img/about/appjet-logo-medium.png create mode 100644 trunk/etherpad/src/static/img/about/investors/mitchkapor.jpg create mode 100644 trunk/etherpad/src/static/img/about/investors/pb.jpg create mode 100644 trunk/etherpad/src/static/img/about/investors/pg.jpg create mode 100644 trunk/etherpad/src/static/img/about/investors/sanjeev.jpg create mode 100644 trunk/etherpad/src/static/img/about/investors/seth.jpg create mode 100644 trunk/etherpad/src/static/img/about/people/aaron-david-iphones-thumb.jpg create mode 100644 trunk/etherpad/src/static/img/about/people/aaron-david-iphones.jpg create mode 100644 trunk/etherpad/src/static/img/about/people/aaron-google-air.jpg create mode 100644 trunk/etherpad/src/static/img/about/people/aaron-headshot-thumb.jpg create mode 100644 trunk/etherpad/src/static/img/about/people/aaron-headshot.jpg create mode 100644 trunk/etherpad/src/static/img/about/people/aaron-headshot2-thumb.jpg create mode 100644 trunk/etherpad/src/static/img/about/people/aaron-headshot2.jpg create mode 100644 trunk/etherpad/src/static/img/about/people/aaron-headshot3-thumb.jpg create mode 100644 trunk/etherpad/src/static/img/about/people/aaron-headshot3.jpg create mode 100644 trunk/etherpad/src/static/img/about/people/daniel-headshot-thumb.jpg create mode 100644 trunk/etherpad/src/static/img/about/people/david-headshot-thumb.jpg create mode 100644 trunk/etherpad/src/static/img/about/people/david-headshot.jpg create mode 100644 trunk/etherpad/src/static/img/about/people/davy-headshot.jpg create mode 100644 trunk/etherpad/src/static/img/about/people/jd-headshot-thumb.jpg create mode 100644 trunk/etherpad/src/static/img/about/people/jd-headshot.jpg create mode 100644 trunk/etherpad/src/static/img/about/people/rhonda-headshot-thumb.jpg create mode 100644 trunk/etherpad/src/static/img/about/people/rhonda-headshot.jpg create mode 100644 trunk/etherpad/src/static/img/about/pier38.png create mode 100644 trunk/etherpad/src/static/img/about/quote-close.png create mode 100644 trunk/etherpad/src/static/img/about/quote-open.png create mode 100644 trunk/etherpad/src/static/img/about/screencastpreview800x600.jpg create mode 100644 trunk/etherpad/src/static/img/account/betawarn.jpg create mode 100644 trunk/etherpad/src/static/img/acecarets/000000.gif create mode 100644 trunk/etherpad/src/static/img/acecarets/666666.gif create mode 100644 trunk/etherpad/src/static/img/acecarets/999999.gif create mode 100644 trunk/etherpad/src/static/img/acecarets/default.gif create mode 100644 trunk/etherpad/src/static/img/apr09/backgrad.png create mode 100644 trunk/etherpad/src/static/img/apr09/black35.png create mode 100644 trunk/etherpad/src/static/img/apr09/blank.gif create mode 100644 trunk/etherpad/src/static/img/apr09/modalbar.gif create mode 100644 trunk/etherpad/src/static/img/apr09/newpadicon.gif create mode 100644 trunk/etherpad/src/static/img/apr09/shadbot.png create mode 100644 trunk/etherpad/src/static/img/apr09/shadleft.png create mode 100644 trunk/etherpad/src/static/img/apr09/shadleftbot.png create mode 100644 trunk/etherpad/src/static/img/apr09/shadlefttop.png create mode 100644 trunk/etherpad/src/static/img/apr09/shadright.png create mode 100644 trunk/etherpad/src/static/img/apr09/shadrightbot.png create mode 100644 trunk/etherpad/src/static/img/apr09/shadrighttop.png create mode 100644 trunk/etherpad/src/static/img/apr09/topbar.gif create mode 100644 trunk/etherpad/src/static/img/apr09/topbarlogo.gif create mode 100644 trunk/etherpad/src/static/img/apr09/widthfull.gif create mode 100644 trunk/etherpad/src/static/img/apr09/widthfullactive.gif create mode 100644 trunk/etherpad/src/static/img/apr09/widthlim.gif create mode 100644 trunk/etherpad/src/static/img/apr09/widthlimactive.gif create mode 100644 trunk/etherpad/src/static/img/billing/amex.gif create mode 100644 trunk/etherpad/src/static/img/billing/creditcard.gif create mode 100644 trunk/etherpad/src/static/img/billing/csc-help.gif create mode 100644 trunk/etherpad/src/static/img/billing/disc.gif create mode 100644 trunk/etherpad/src/static/img/billing/invoice.gif create mode 100644 trunk/etherpad/src/static/img/billing/mc.gif create mode 100644 trunk/etherpad/src/static/img/billing/paypal.gif create mode 100644 trunk/etherpad/src/static/img/billing/visa.gif create mode 100644 trunk/etherpad/src/static/img/blog/posts/new-features/fullwidth.gif create mode 100644 trunk/etherpad/src/static/img/blog/posts/new-features/importexport.gif create mode 100644 trunk/etherpad/src/static/img/blog/posts/new-features/richtext.gif create mode 100644 trunk/etherpad/src/static/img/blog/posts/new-features/viewzoom.gif create mode 100644 trunk/etherpad/src/static/img/blog/posts/pricing-survey-results.png create mode 100644 trunk/etherpad/src/static/img/blog/posts/pricing-survey.png create mode 100644 trunk/etherpad/src/static/img/blog/posts/time-slider-screenshot.gif create mode 100644 trunk/etherpad/src/static/img/davy/bg/home-createpad.png create mode 100644 trunk/etherpad/src/static/img/davy/bg/home-features-bottom.gif create mode 100644 trunk/etherpad/src/static/img/davy/bg/home-features-free-bottom.gif create mode 100644 trunk/etherpad/src/static/img/davy/bg/home-features-paid-top.gif create mode 100644 trunk/etherpad/src/static/img/davy/bg/home-features-top.gif create mode 100644 trunk/etherpad/src/static/img/davy/bg/home-nav-selected.png create mode 100644 trunk/etherpad/src/static/img/davy/bg/home-screencast.png create mode 100644 trunk/etherpad/src/static/img/davy/bg/home2.png create mode 100644 trunk/etherpad/src/static/img/davy/bg/product-nav-selected-white.png create mode 100644 trunk/etherpad/src/static/img/davy/bg/product-nav-selected.png create mode 100644 trunk/etherpad/src/static/img/davy/bg/product.png create mode 100644 trunk/etherpad/src/static/img/davy/btn/createpad-home.gif create mode 100644 trunk/etherpad/src/static/img/davy/btn/createpad-large.gif create mode 100644 trunk/etherpad/src/static/img/davy/btn/createpad-small.gif create mode 100644 trunk/etherpad/src/static/img/davy/btn/intro-screencast.png create mode 100644 trunk/etherpad/src/static/img/davy/btn/intro-testimonials.png create mode 100644 trunk/etherpad/src/static/img/davy/btn/learnmore.gif create mode 100644 trunk/etherpad/src/static/img/davy/btn/signup-home-2.gif create mode 100644 trunk/etherpad/src/static/img/davy/btn/signup-home-3.gif create mode 100644 trunk/etherpad/src/static/img/davy/btn/signup-home-4.gif create mode 100644 trunk/etherpad/src/static/img/davy/btn/signup-home.gif create mode 100644 trunk/etherpad/src/static/img/davy/btn/uses-more.gif create mode 100644 trunk/etherpad/src/static/img/davy/gfx/32/114.png create mode 100644 trunk/etherpad/src/static/img/davy/gfx/32/15.png create mode 100644 trunk/etherpad/src/static/img/davy/gfx/32/65.png create mode 100644 trunk/etherpad/src/static/img/davy/gfx/32/78.png create mode 100644 trunk/etherpad/src/static/img/davy/gfx/bullet.gif create mode 100644 trunk/etherpad/src/static/img/davy/gfx/home-logo2.gif create mode 100644 trunk/etherpad/src/static/img/davy/gfx/home-screencast.png create mode 100644 trunk/etherpad/src/static/img/davy/gfx/plane.gif create mode 100644 trunk/etherpad/src/static/img/davy/gfx/product-logo.gif create mode 100644 trunk/etherpad/src/static/img/davy/gfx/screenshot.gif create mode 100644 trunk/etherpad/src/static/img/davy/gfx/use-meetings.gif create mode 100644 trunk/etherpad/src/static/img/davy/gfx/use-meetings.png create mode 100644 trunk/etherpad/src/static/img/davy/gfx/use-programming.gif create mode 100644 trunk/etherpad/src/static/img/davy/gfx/use-programming.png create mode 100644 trunk/etherpad/src/static/img/davy/gfx/use-writing.gif create mode 100644 trunk/etherpad/src/static/img/davy/gfx/use-writing.png create mode 100644 trunk/etherpad/src/static/img/davy/txt/home-button.gif create mode 100644 trunk/etherpad/src/static/img/featuretour/code.gif create mode 100644 trunk/etherpad/src/static/img/featuretour/edits.gif create mode 100644 trunk/etherpad/src/static/img/featuretour/editsandusers.gif create mode 100644 trunk/etherpad/src/static/img/featuretour/padlock.png create mode 100644 trunk/etherpad/src/static/img/featuretour/revisions.gif create mode 100644 trunk/etherpad/src/static/img/featuretour/users.gif create mode 100644 trunk/etherpad/src/static/img/feb09/framedheaderback.gif create mode 100644 trunk/etherpad/src/static/img/feb09/framedheaderlogo.gif create mode 100644 trunk/etherpad/src/static/img/feb09/home_firstp.gif create mode 100644 trunk/etherpad/src/static/img/feb09/home_firstp.png create mode 100644 trunk/etherpad/src/static/img/feb09/home_firstp2.gif create mode 100644 trunk/etherpad/src/static/img/feb09/home_h1.gif create mode 100644 trunk/etherpad/src/static/img/feb09/home_h1.png create mode 100644 trunk/etherpad/src/static/img/feb09/home_newpadbutton.gif create mode 100644 trunk/etherpad/src/static/img/feb09/home_newpadbutton.png create mode 100644 trunk/etherpad/src/static/img/feb09/home_newpadbutton2.gif create mode 100644 trunk/etherpad/src/static/img/feb09/home_newpadbutton_eepnet.gif create mode 100644 trunk/etherpad/src/static/img/feb09/hometop_back.gif create mode 100644 trunk/etherpad/src/static/img/feb09/nav1.gif create mode 100644 trunk/etherpad/src/static/img/feb09/nav1_back.gif create mode 100644 trunk/etherpad/src/static/img/feb09/nav2.gif create mode 100644 trunk/etherpad/src/static/img/feb09/screencast.gif create mode 100644 trunk/etherpad/src/static/img/home/etherpad-mainheader1.jpg create mode 100644 trunk/etherpad/src/static/img/home/headergradient.gif create mode 100644 trunk/etherpad/src/static/img/home/homeheader1.jpg create mode 100644 trunk/etherpad/src/static/img/home/homeheader2.jpg create mode 100644 trunk/etherpad/src/static/img/home/leftgrad.gif create mode 100644 trunk/etherpad/src/static/img/home/pencilpaperback.png create mode 100644 trunk/etherpad/src/static/img/home/screencapture1.gif create mode 100644 trunk/etherpad/src/static/img/home/underdevicon.gif create mode 100644 trunk/etherpad/src/static/img/icon/downarrow.gif create mode 100644 trunk/etherpad/src/static/img/icon/feed.gif create mode 100644 trunk/etherpad/src/static/img/jun09/pad/backgrad.gif create mode 100644 trunk/etherpad/src/static/img/jun09/pad/bottomareagfx.gif create mode 100644 trunk/etherpad/src/static/img/jun09/pad/colorpicker.gif create mode 100644 trunk/etherpad/src/static/img/jun09/pad/connectingbar.gif create mode 100644 trunk/etherpad/src/static/img/jun09/pad/connectionindicator.gif create mode 100644 trunk/etherpad/src/static/img/jun09/pad/docbarstates.png create mode 100644 trunk/etherpad/src/static/img/jun09/pad/docbarstates2.png create mode 100644 trunk/etherpad/src/static/img/jun09/pad/docbarstates3.png create mode 100644 trunk/etherpad/src/static/img/jun09/pad/docpaneledge.png create mode 100644 trunk/etherpad/src/static/img/jun09/pad/docpaneledge2.png create mode 100644 trunk/etherpad/src/static/img/jun09/pad/docpanelmiddle.png create mode 100644 trunk/etherpad/src/static/img/jun09/pad/docpanelmiddle2.png create mode 100644 trunk/etherpad/src/static/img/jun09/pad/editbar.gif create mode 100644 trunk/etherpad/src/static/img/jun09/pad/editbar2.gif create mode 100644 trunk/etherpad/src/static/img/jun09/pad/editbar3.png create mode 100644 trunk/etherpad/src/static/img/jun09/pad/editbar3.xcf create mode 100644 trunk/etherpad/src/static/img/jun09/pad/editbarback.gif create mode 100644 trunk/etherpad/src/static/img/jun09/pad/feedbackbox2.gif create mode 100644 trunk/etherpad/src/static/img/jun09/pad/fileicons.gif create mode 100644 trunk/etherpad/src/static/img/jun09/pad/hdraggie.gif create mode 100644 trunk/etherpad/src/static/img/jun09/pad/inviteshare.gif create mode 100644 trunk/etherpad/src/static/img/jun09/pad/inviteshare2.gif create mode 100644 trunk/etherpad/src/static/img/jun09/pad/layoutbuttons.gif create mode 100644 trunk/etherpad/src/static/img/jun09/pad/overlay.png create mode 100644 trunk/etherpad/src/static/img/jun09/pad/overlay2.png create mode 100644 trunk/etherpad/src/static/img/jun09/pad/padtop.gif create mode 100644 trunk/etherpad/src/static/img/jun09/pad/padtop2.gif create mode 100644 trunk/etherpad/src/static/img/jun09/pad/padtop3.gif create mode 100644 trunk/etherpad/src/static/img/jun09/pad/padtop4.gif create mode 100644 trunk/etherpad/src/static/img/jun09/pad/padtop4.png create mode 100644 trunk/etherpad/src/static/img/jun09/pad/padtop4.xcf create mode 100644 trunk/etherpad/src/static/img/jun09/pad/padtop5.png create mode 100644 trunk/etherpad/src/static/img/jun09/pad/padtop5.xcf create mode 100644 trunk/etherpad/src/static/img/jun09/pad/padtopback.gif create mode 100644 trunk/etherpad/src/static/img/jun09/pad/padtopback2.gif create mode 100644 trunk/etherpad/src/static/img/jun09/pad/protop.png create mode 100644 trunk/etherpad/src/static/img/jun09/pad/protop.xcf create mode 100644 trunk/etherpad/src/static/img/jun09/pad/public.gif create mode 100644 trunk/etherpad/src/static/img/jun09/pad/savedrevarrows.gif create mode 100644 trunk/etherpad/src/static/img/jun09/pad/savedrevsgfx2.gif create mode 100644 trunk/etherpad/src/static/img/jun09/pad/sharebox2.gif create mode 100644 trunk/etherpad/src/static/img/jun09/pad/sharebox3.gif create mode 100644 trunk/etherpad/src/static/img/jun09/pad/sharebox4.gif create mode 100644 trunk/etherpad/src/static/img/jun09/pad/sharedistri.gif create mode 100644 trunk/etherpad/src/static/img/jun09/pad/syncdone.gif create mode 100644 trunk/etherpad/src/static/img/jun09/pad/syncing.gif create mode 100644 trunk/etherpad/src/static/img/jun09/pad/syncing2.gif create mode 100644 trunk/etherpad/src/static/img/jun09/pad/viewbargfx.gif create mode 100644 trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-gloss-cyan-menu-item-hover.gif create mode 100644 trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-gloss-menu-item-hover.gif create mode 100644 trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-gloss-semitransparent-menu-item-hover.png create mode 100644 trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-human-menu-item-hover.gif create mode 100644 trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-osx-menu-item-hover.gif create mode 100644 trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-vista-bg.gif create mode 100644 trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-vista-menu-item-hover.gif create mode 100644 trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-xp-bg.gif create mode 100644 trunk/etherpad/src/static/img/may09/bold.gif create mode 100644 trunk/etherpad/src/static/img/may09/doc.gif create mode 100644 trunk/etherpad/src/static/img/may09/doc.png create mode 100644 trunk/etherpad/src/static/img/may09/html.gif create mode 100644 trunk/etherpad/src/static/img/may09/html.png create mode 100644 trunk/etherpad/src/static/img/may09/italic.gif create mode 100644 trunk/etherpad/src/static/img/may09/leftarrow.gif create mode 100644 trunk/etherpad/src/static/img/may09/leftarrow2.gif create mode 100644 trunk/etherpad/src/static/img/may09/link.gif create mode 100644 trunk/etherpad/src/static/img/may09/link.png create mode 100644 trunk/etherpad/src/static/img/may09/odt.gif create mode 100644 trunk/etherpad/src/static/img/may09/odt.png create mode 100644 trunk/etherpad/src/static/img/may09/padlock.gif create mode 100644 trunk/etherpad/src/static/img/may09/padlockopen.gif create mode 100644 trunk/etherpad/src/static/img/may09/passwordlocked.gif create mode 100644 trunk/etherpad/src/static/img/may09/passwordlocked_cropped.gif create mode 100644 trunk/etherpad/src/static/img/may09/passwordnone.gif create mode 100644 trunk/etherpad/src/static/img/may09/paypal.gif create mode 100644 trunk/etherpad/src/static/img/may09/pdf.gif create mode 100644 trunk/etherpad/src/static/img/may09/pdf.png create mode 100644 trunk/etherpad/src/static/img/may09/redo.gif create mode 100644 trunk/etherpad/src/static/img/may09/txt.gif create mode 100644 trunk/etherpad/src/static/img/may09/txt.png create mode 100644 trunk/etherpad/src/static/img/may09/underline.gif create mode 100644 trunk/etherpad/src/static/img/may09/undo.gif create mode 100644 trunk/etherpad/src/static/img/miniplane.gif create mode 100644 trunk/etherpad/src/static/img/misc/diagnostic-links.gif create mode 100644 trunk/etherpad/src/static/img/misc/status-ball.gif create mode 100644 trunk/etherpad/src/static/img/misc/traclogo.gif create mode 100644 trunk/etherpad/src/static/img/oct/atlonglast.gif create mode 100644 trunk/etherpad/src/static/img/oct/banner1.jpg create mode 100644 trunk/etherpad/src/static/img/oct/banner2.jpg create mode 100644 trunk/etherpad/src/static/img/oct/banner3.jpg create mode 100644 trunk/etherpad/src/static/img/oct/banner4.jpg create mode 100644 trunk/etherpad/src/static/img/oct/banner5.gif create mode 100644 trunk/etherpad/src/static/img/oct/banner6.gif create mode 100644 trunk/etherpad/src/static/img/oct/banner7.gif create mode 100644 trunk/etherpad/src/static/img/oct/banner8.gif create mode 100644 trunk/etherpad/src/static/img/oct/banner9.gif create mode 100644 trunk/etherpad/src/static/img/oct/bannerback5.gif create mode 100644 trunk/etherpad/src/static/img/oct/bannerback6.gif create mode 100644 trunk/etherpad/src/static/img/oct/bodyback1.gif create mode 100644 trunk/etherpad/src/static/img/oct/bodyback2.gif create mode 100644 trunk/etherpad/src/static/img/oct/bodyback3.gif create mode 100644 trunk/etherpad/src/static/img/oct/bodyback4.gif create mode 100644 trunk/etherpad/src/static/img/oct/bodyback5.gif create mode 100644 trunk/etherpad/src/static/img/oct/bodybacktop1.gif create mode 100644 trunk/etherpad/src/static/img/oct/computers.gif create mode 100644 trunk/etherpad/src/static/img/oct/computers2.gif create mode 100644 trunk/etherpad/src/static/img/oct/glossyblue.gif create mode 100644 trunk/etherpad/src/static/img/oct/glossyblue2.gif create mode 100644 trunk/etherpad/src/static/img/oct/glossyblueh.gif create mode 100644 trunk/etherpad/src/static/img/oct/insetrect.gif create mode 100644 trunk/etherpad/src/static/img/oct/minilogo1-05e.gif create mode 100644 trunk/etherpad/src/static/img/oct/minilogo1-07f.gif create mode 100644 trunk/etherpad/src/static/img/oct/minilogo3.jpg create mode 100644 trunk/etherpad/src/static/img/oct/minitopback1.gif create mode 100644 trunk/etherpad/src/static/img/oct/minitopback2.gif create mode 100644 trunk/etherpad/src/static/img/oct/minitopbar1-05e.gif create mode 100644 trunk/etherpad/src/static/img/oct/minitopbar2-05e.gif create mode 100644 trunk/etherpad/src/static/img/oct/minitopbar2-07f.gif create mode 100644 trunk/etherpad/src/static/img/oct/minitopbar3.jpg create mode 100644 trunk/etherpad/src/static/img/oct/minitopbar4.gif create mode 100644 trunk/etherpad/src/static/img/oct/minitoplogo1.gif create mode 100644 trunk/etherpad/src/static/img/oct/minitoplogo2.gif create mode 100644 trunk/etherpad/src/static/img/oct/newpadmain.gif create mode 100644 trunk/etherpad/src/static/img/oct/newpadmainback.gif create mode 100644 trunk/etherpad/src/static/img/oct/newpadmainbackh.gif create mode 100644 trunk/etherpad/src/static/img/oct/pageshot.png create mode 100644 trunk/etherpad/src/static/img/oct/pageshotmini.png create mode 100644 trunk/etherpad/src/static/img/oct/sidehead-gradhilite.gif create mode 100644 trunk/etherpad/src/static/img/oct/tinytriangle.gif create mode 100644 trunk/etherpad/src/static/img/oct/topnav1.gif create mode 100644 trunk/etherpad/src/static/img/oct/topnav2.gif create mode 100644 trunk/etherpad/src/static/img/oct/topnav3.gif create mode 100644 trunk/etherpad/src/static/img/oct/topnav4.gif create mode 100644 trunk/etherpad/src/static/img/oct/topnav5.gif create mode 100644 trunk/etherpad/src/static/img/oct/topnav6.gif create mode 100644 trunk/etherpad/src/static/img/oct/topnavback1.gif create mode 100644 trunk/etherpad/src/static/img/oct/topnavback2.gif create mode 100644 trunk/etherpad/src/static/img/oct/topnavback3.gif create mode 100644 trunk/etherpad/src/static/img/oct/usecasesnavdown.gif create mode 100644 trunk/etherpad/src/static/img/oct/usecasesnavdownh.gif create mode 100644 trunk/etherpad/src/static/img/oct/usecasesnavup.gif create mode 100644 trunk/etherpad/src/static/img/oct/usecasesnavuph.gif create mode 100644 trunk/etherpad/src/static/img/oct/watchscreencast.gif create mode 100644 trunk/etherpad/src/static/img/pad/animated-orb-orange-12.gif create mode 100644 trunk/etherpad/src/static/img/pad/backgrad.png create mode 100644 trunk/etherpad/src/static/img/pad/backshadow/backshadow-940-20-eee-20.gif create mode 100644 trunk/etherpad/src/static/img/pad/backshadow/backshadow-940-20-fff-20.gif create mode 100644 trunk/etherpad/src/static/img/pad/backshadow/backshadow-940-20-fff-40.gif create mode 100644 trunk/etherpad/src/static/img/pad/backshadow/backshadow-940-20-fff-60.gif create mode 100644 trunk/etherpad/src/static/img/pad/backshadow/botshadow-940-20-eee-20.gif create mode 100644 trunk/etherpad/src/static/img/pad/etherpad-logo-small-grad.gif create mode 100644 trunk/etherpad/src/static/img/pad/etherpad-logo-small.gif create mode 100644 trunk/etherpad/src/static/img/pad/etherpad-logo-small2.gif create mode 100644 trunk/etherpad/src/static/img/pad/expandy-arrow-down.gif create mode 100644 trunk/etherpad/src/static/img/pad/expandy-arrow-right.gif create mode 100644 trunk/etherpad/src/static/img/pad/expandy-arrow6-down-active.gif create mode 100644 trunk/etherpad/src/static/img/pad/expandy-arrow6-down.gif create mode 100644 trunk/etherpad/src/static/img/pad/expandy-arrow6-right-active.gif create mode 100644 trunk/etherpad/src/static/img/pad/expandy-arrow6-right.gif create mode 100644 trunk/etherpad/src/static/img/pad/header-revgrad.gif create mode 100644 trunk/etherpad/src/static/img/pad/newpad.gif create mode 100644 trunk/etherpad/src/static/img/pad/orb-greenred-12.gif create mode 100644 trunk/etherpad/src/static/img/pad/padbg1.jpg create mode 100644 trunk/etherpad/src/static/img/pad/padbg2.jpg create mode 100644 trunk/etherpad/src/static/img/pad/padbg3.jpg create mode 100644 trunk/etherpad/src/static/img/pad/padbg4.jpg create mode 100644 trunk/etherpad/src/static/img/pad/padbg5.jpg create mode 100644 trunk/etherpad/src/static/img/pad/padhead1.jpg create mode 100644 trunk/etherpad/src/static/img/pad/padhead2.jpg create mode 100644 trunk/etherpad/src/static/img/pad/padhead3.jpg create mode 100644 trunk/etherpad/src/static/img/pad/pencil-icon-small-blue.gif create mode 100644 trunk/etherpad/src/static/img/pad/sidehead-grad.gif create mode 100644 trunk/etherpad/src/static/img/pad/timeslider/button_depressed.png create mode 100644 trunk/etherpad/src/static/img/pad/timeslider/button_undepressed.png create mode 100644 trunk/etherpad/src/static/img/pad/timeslider/crushed_button_depressed.png create mode 100644 trunk/etherpad/src/static/img/pad/timeslider/crushed_button_undepressed.png create mode 100644 trunk/etherpad/src/static/img/pad/timeslider/crushed_current_location.png create mode 100644 trunk/etherpad/src/static/img/pad/timeslider/crushed_pause.png create mode 100644 trunk/etherpad/src/static/img/pad/timeslider/crushed_play.png create mode 100644 trunk/etherpad/src/static/img/pad/timeslider/crushed_play_button.png create mode 100644 trunk/etherpad/src/static/img/pad/timeslider/crushed_timeslider_mockup.png create mode 100644 trunk/etherpad/src/static/img/pad/timeslider/current_location.gif create mode 100644 trunk/etherpad/src/static/img/pad/timeslider/current_location.png create mode 100644 trunk/etherpad/src/static/img/pad/timeslider/pause.gif create mode 100644 trunk/etherpad/src/static/img/pad/timeslider/pause.png create mode 100644 trunk/etherpad/src/static/img/pad/timeslider/play.gif create mode 100644 trunk/etherpad/src/static/img/pad/timeslider/play.png create mode 100644 trunk/etherpad/src/static/img/pad/timeslider/play_button.png create mode 100644 trunk/etherpad/src/static/img/pad/timeslider/star.gif create mode 100644 trunk/etherpad/src/static/img/pad/timeslider/star.png create mode 100644 trunk/etherpad/src/static/img/pad/timeslider/star_selected.png create mode 100644 trunk/etherpad/src/static/img/pad/timeslider/stepper_buttons.png create mode 100644 trunk/etherpad/src/static/img/pad/timeslider/timeslider_background.png create mode 100644 trunk/etherpad/src/static/img/pad/timeslider/timeslider_left.png create mode 100644 trunk/etherpad/src/static/img/pad/timeslider/timeslider_mockup.png create mode 100644 trunk/etherpad/src/static/img/pad/timeslider/timeslider_right.png create mode 100644 trunk/etherpad/src/static/img/pricing/free.gif create mode 100644 trunk/etherpad/src/static/img/pricing/group.gif create mode 100644 trunk/etherpad/src/static/img/pricing/on-demand.gif create mode 100644 trunk/etherpad/src/static/img/pricing/private-network.gif create mode 100644 trunk/etherpad/src/static/img/pricing/support.gif create mode 100644 trunk/etherpad/src/static/img/pro/billing/cards-button.gif create mode 100644 trunk/etherpad/src/static/img/pro/box/blue-boxtop.gif create mode 100644 trunk/etherpad/src/static/img/pro/buttons/bluebutton120.gif create mode 100644 trunk/etherpad/src/static/img/pro/header/pro-header-back.gif create mode 100644 trunk/etherpad/src/static/img/pro/header/pro-header-logo.png create mode 100644 trunk/etherpad/src/static/img/pro/header/pro-header-logo.xcf create mode 100644 trunk/etherpad/src/static/img/pro/header/pro-header-plustopnav-back.gif create mode 100644 trunk/etherpad/src/static/img/pro/padlist/gear-drop.gif create mode 100644 trunk/etherpad/src/static/img/pro/padlist/paper-icon.gif create mode 100644 trunk/etherpad/src/static/img/pro/padlist/trash-icon.gif create mode 100644 trunk/etherpad/src/static/img/pro/topnav/pro-topnav-back.gif create mode 100644 trunk/etherpad/src/static/img/pro/topnav/pro-topnav-notch.gif create mode 100644 trunk/etherpad/src/static/img/tinyplane.gif create mode 100644 trunk/etherpad/src/static/img/wavejet.jpg create mode 100644 trunk/etherpad/src/static/js/ace.js create mode 100644 trunk/etherpad/src/static/js/billing.js create mode 100644 trunk/etherpad/src/static/js/billing_shared.js create mode 100644 trunk/etherpad/src/static/js/broadcast.js create mode 100644 trunk/etherpad/src/static/js/broadcast_revisions.js create mode 100644 trunk/etherpad/src/static/js/broadcast_slider.js create mode 100644 trunk/etherpad/src/static/js/collab_client.js create mode 100644 trunk/etherpad/src/static/js/colorutils.js create mode 100644 trunk/etherpad/src/static/js/confirmation.js create mode 100644 trunk/etherpad/src/static/js/connection_diagnostics.js create mode 100644 trunk/etherpad/src/static/js/cssmanager_client.js create mode 100644 trunk/etherpad/src/static/js/domline_client.js create mode 100644 trunk/etherpad/src/static/js/draggable.js create mode 100644 trunk/etherpad/src/static/js/easysync2_client.js create mode 100644 trunk/etherpad/src/static/js/etherpad.js create mode 100755 trunk/etherpad/src/static/js/jquery-1.2.6.js create mode 100644 trunk/etherpad/src/static/js/jquery-1.3.2.js create mode 100644 trunk/etherpad/src/static/js/json2.js create mode 100644 trunk/etherpad/src/static/js/lib/jquery.contextmenu.js create mode 100644 trunk/etherpad/src/static/js/linestylefilter_client.js create mode 100644 trunk/etherpad/src/static/js/pad.js.old create mode 100644 trunk/etherpad/src/static/js/pad2.js create mode 100644 trunk/etherpad/src/static/js/pad_chat.js create mode 100644 trunk/etherpad/src/static/js/pad_connectionstatus.js create mode 100644 trunk/etherpad/src/static/js/pad_cookie.js create mode 100644 trunk/etherpad/src/static/js/pad_docbar.js create mode 100644 trunk/etherpad/src/static/js/pad_editbar.js create mode 100644 trunk/etherpad/src/static/js/pad_editor.js create mode 100644 trunk/etherpad/src/static/js/pad_impexp.js create mode 100644 trunk/etherpad/src/static/js/pad_modals.js create mode 100644 trunk/etherpad/src/static/js/pad_savedrevs.js create mode 100644 trunk/etherpad/src/static/js/pad_userlist.js create mode 100644 trunk/etherpad/src/static/js/pad_utils.js create mode 100644 trunk/etherpad/src/static/js/pricing.js create mode 100644 trunk/etherpad/src/static/js/pro/guest-knock-client.js create mode 100644 trunk/etherpad/src/static/js/pro/pro-padlist-client.js create mode 100644 trunk/etherpad/src/static/js/pro/signin-client.js create mode 100644 trunk/etherpad/src/static/js/pulse.jquery.js create mode 100644 trunk/etherpad/src/static/js/statpage.js create mode 100644 trunk/etherpad/src/static/js/store.js create mode 100644 trunk/etherpad/src/static/js/swfobject.js create mode 100644 trunk/etherpad/src/static/js/timeslider.js create mode 100644 trunk/etherpad/src/static/js/undo-xpopup.js create mode 100755 trunk/etherpad/src/static/swf/vidplayer.swf create mode 100644 trunk/etherpad/src/templates/500_body.ejs create mode 100644 trunk/etherpad/src/templates/beta/signup.ejs create mode 100644 trunk/etherpad/src/templates/email/eepnet_license_info.ejs create mode 100644 trunk/etherpad/src/templates/email/eepnet_purchase_receipt.ejs create mode 100644 trunk/etherpad/src/templates/email/padinvite.ejs create mode 100644 trunk/etherpad/src/templates/email/pro_beta_invite.ejs create mode 100644 trunk/etherpad/src/templates/email/pro_payment_failure.ejs create mode 100644 trunk/etherpad/src/templates/email/pro_payment_receipt.ejs create mode 100644 trunk/etherpad/src/templates/framed/framedfooter.ejs create mode 100644 trunk/etherpad/src/templates/framed/framedheader-pro.ejs create mode 100644 trunk/etherpad/src/templates/framed/framedheader.ejs create mode 100644 trunk/etherpad/src/templates/framed/framedpage-pro.ejs create mode 100644 trunk/etherpad/src/templates/framed/framedpage.ejs create mode 100644 trunk/etherpad/src/templates/html.ejs create mode 100644 trunk/etherpad/src/templates/main/home.ejs create mode 100644 trunk/etherpad/src/templates/main/pro_signup_body.ejs create mode 100644 trunk/etherpad/src/templates/misc/pad_default.ejs create mode 100644 trunk/etherpad/src/templates/notice.ejs create mode 100644 trunk/etherpad/src/templates/pad/create_body.ejs create mode 100644 trunk/etherpad/src/templates/pad/create_body_rafter.ejs create mode 100644 trunk/etherpad/src/templates/pad/exporthtml.ejs create mode 100644 trunk/etherpad/src/templates/pad/pad_body.ejs create mode 100644 trunk/etherpad/src/templates/pad/pad_body2.ejs create mode 100644 trunk/etherpad/src/templates/pad/pad_content.ejs create mode 100644 trunk/etherpad/src/templates/pad/pad_download_link.ejs create mode 100644 trunk/etherpad/src/templates/pad/pad_iphone_body.ejs create mode 100644 trunk/etherpad/src/templates/pad/padfull_body.ejs create mode 100644 trunk/etherpad/src/templates/pad/padslider_body.ejs create mode 100644 trunk/etherpad/src/templates/pad/padview_body.ejs create mode 100644 trunk/etherpad/src/templates/pad/total_users_exceeded.ejs create mode 100644 trunk/etherpad/src/templates/pro-account/recover.ejs create mode 100644 trunk/etherpad/src/templates/pro-account/sign-in.ejs create mode 100644 trunk/etherpad/src/templates/pro/account/account-welcome-email.ejs create mode 100644 trunk/etherpad/src/templates/pro/account/create-admin-account.ejs create mode 100644 trunk/etherpad/src/templates/pro/account/forgot-password-email.ejs create mode 100644 trunk/etherpad/src/templates/pro/account/forgot-password.ejs create mode 100644 trunk/etherpad/src/templates/pro/account/global-multi-domain-recover-email.ejs create mode 100644 trunk/etherpad/src/templates/pro/account/guest-knock.ejs create mode 100644 trunk/etherpad/src/templates/pro/account/my-account.ejs create mode 100644 trunk/etherpad/src/templates/pro/account/signin-guest.ejs create mode 100644 trunk/etherpad/src/templates/pro/account/signin.ejs create mode 100644 trunk/etherpad/src/templates/pro/admin/account-manager.ejs create mode 100644 trunk/etherpad/src/templates/pro/admin/admin-template.ejs create mode 100644 trunk/etherpad/src/templates/pro/admin/admin.ejs create mode 100644 trunk/etherpad/src/templates/pro/admin/billing-invoices.ejs create mode 100644 trunk/etherpad/src/templates/pro/admin/delete-account.ejs create mode 100644 trunk/etherpad/src/templates/pro/admin/manage-account.ejs create mode 100644 trunk/etherpad/src/templates/pro/admin/manage-billing.ejs create mode 100644 trunk/etherpad/src/templates/pro/admin/new-account.ejs create mode 100644 trunk/etherpad/src/templates/pro/admin/pne-config.ejs create mode 100644 trunk/etherpad/src/templates/pro/admin/pne-dashboard.ejs create mode 100644 trunk/etherpad/src/templates/pro/admin/pne-license-manager.ejs create mode 100644 trunk/etherpad/src/templates/pro/admin/pne-shell.ejs create mode 100644 trunk/etherpad/src/templates/pro/admin/pro-config.ejs create mode 100644 trunk/etherpad/src/templates/pro/admin/single-invoice.ejs create mode 100644 trunk/etherpad/src/templates/pro/padlist/pro-padlist.ejs create mode 100644 trunk/etherpad/src/templates/pro/pro-payment-required.ejs create mode 100644 trunk/etherpad/src/templates/pro/pro_home.ejs create mode 100644 trunk/etherpad/src/templates/statistics/stat_page.ejs create mode 100644 trunk/etherpad/src/templates/store/csc-help.ejs create mode 100644 trunk/etherpad/src/templates/store/eepnet-checkout/billing-info.ejs create mode 100644 trunk/etherpad/src/templates/store/eepnet-checkout/cart.ejs create mode 100644 trunk/etherpad/src/templates/store/eepnet-checkout/checkout-template.ejs create mode 100644 trunk/etherpad/src/templates/store/eepnet-checkout/confirmation.ejs create mode 100644 trunk/etherpad/src/templates/store/eepnet-checkout/license-info.ejs create mode 100644 trunk/etherpad/src/templates/store/eepnet-checkout/purchase.ejs create mode 100644 trunk/etherpad/src/templates/store/eepnet-checkout/receipt.ejs create mode 100644 trunk/etherpad/src/templates/store/eepnet-checkout/summary.ejs create mode 100644 trunk/etherpad/src/templates/store/eepnet-checkout/support-contract.ejs create mode 100644 trunk/etherpad/src/templates/store/eepnet_download.ejs create mode 100644 trunk/etherpad/src/templates/store/eepnet_eval_nextsteps.ejs create mode 100644 trunk/etherpad/src/templates/store/eepnet_eval_signup.ejs create mode 100644 trunk/infrastructure/.gitignore create mode 100644 trunk/infrastructure/ace/.gitignore create mode 100644 trunk/infrastructure/ace/README create mode 100755 trunk/infrastructure/ace/bin/make create mode 100755 trunk/infrastructure/ace/bin/serve create mode 100644 trunk/infrastructure/ace/blog.txt create mode 100644 trunk/infrastructure/ace/build/.gitignore create mode 100644 trunk/infrastructure/ace/build/index.html create mode 100644 trunk/infrastructure/ace/build/jquery-1.2.1.js create mode 100644 trunk/infrastructure/ace/build/testcode.js create mode 100644 trunk/infrastructure/ace/easysync-notes.txt create mode 120000 trunk/infrastructure/ace/lib/rhino-js-1.7r1.jar create mode 120000 trunk/infrastructure/ace/lib/yuicompressor-2.4-appjet.jar create mode 100644 trunk/infrastructure/ace/notes.txt create mode 100644 trunk/infrastructure/ace/www/ace2_common.js create mode 100644 trunk/infrastructure/ace/www/ace2_common_dev.js create mode 100644 trunk/infrastructure/ace/www/ace2_inner.js create mode 100644 trunk/infrastructure/ace/www/ace2_outer.js create mode 100644 trunk/infrastructure/ace/www/ace2_wrapper.js create mode 100644 trunk/infrastructure/ace/www/bbtree.js create mode 100644 trunk/infrastructure/ace/www/changesettracker.js create mode 100644 trunk/infrastructure/ace/www/colorutils.js create mode 100644 trunk/infrastructure/ace/www/contentcollector.js create mode 100644 trunk/infrastructure/ace/www/cssmanager.js create mode 100644 trunk/infrastructure/ace/www/dev.html create mode 100644 trunk/infrastructure/ace/www/domline.js create mode 100644 trunk/infrastructure/ace/www/easy_sync.js create mode 100644 trunk/infrastructure/ace/www/easysync2.js create mode 100644 trunk/infrastructure/ace/www/easysync2_tests.js create mode 100644 trunk/infrastructure/ace/www/editor.css create mode 100644 trunk/infrastructure/ace/www/firebug/errorIcon.png create mode 100644 trunk/infrastructure/ace/www/firebug/firebug.css create mode 100644 trunk/infrastructure/ace/www/firebug/firebug.html create mode 100644 trunk/infrastructure/ace/www/firebug/firebug.js create mode 100644 trunk/infrastructure/ace/www/firebug/firebugx.js create mode 100644 trunk/infrastructure/ace/www/firebug/infoIcon.png create mode 100644 trunk/infrastructure/ace/www/firebug/warningIcon.png create mode 100644 trunk/infrastructure/ace/www/index.html create mode 100644 trunk/infrastructure/ace/www/inner.css create mode 100644 trunk/infrastructure/ace/www/jquery-1.2.1.js create mode 100644 trunk/infrastructure/ace/www/lang_html.js create mode 100644 trunk/infrastructure/ace/www/lang_js.js create mode 100644 trunk/infrastructure/ace/www/lexer_support.js create mode 100644 trunk/infrastructure/ace/www/linestylefilter.js create mode 100644 trunk/infrastructure/ace/www/magicdom.js create mode 100644 trunk/infrastructure/ace/www/multilang_lexer.js create mode 100644 trunk/infrastructure/ace/www/processing.js create mode 100644 trunk/infrastructure/ace/www/profiler.js create mode 100644 trunk/infrastructure/ace/www/skiplist.js create mode 100644 trunk/infrastructure/ace/www/spanlist.js create mode 100644 trunk/infrastructure/ace/www/syntax-new.css create mode 100644 trunk/infrastructure/ace/www/syntax.css create mode 100644 trunk/infrastructure/ace/www/test.html create mode 100644 trunk/infrastructure/ace/www/testcode.js create mode 100644 trunk/infrastructure/ace/www/toSource.js create mode 100644 trunk/infrastructure/ace/www/undomodule.js create mode 100644 trunk/infrastructure/ace/www/virtual_lines.js create mode 100755 trunk/infrastructure/bin/classpath.sh create mode 100755 trunk/infrastructure/bin/comp.sh create mode 100644 trunk/infrastructure/bin/compilecache.sh create mode 100755 trunk/infrastructure/bin/jscomp.sh create mode 100755 trunk/infrastructure/bin/makejar.sh create mode 100755 trunk/infrastructure/bin/makesars.sh create mode 100755 trunk/infrastructure/bin/run.sh create mode 100644 trunk/infrastructure/com.etherpad.openofficeservice/importexport.scala create mode 100644 trunk/infrastructure/com.etherpad/easysync2support.scala create mode 100644 trunk/infrastructure/com.etherpad/licensing.scala create mode 100644 trunk/infrastructure/com.etherpad/main.scala create mode 100644 trunk/infrastructure/framework-src/modules/atomfeed.js create mode 100644 trunk/infrastructure/framework-src/modules/blob.js create mode 100644 trunk/infrastructure/framework-src/modules/cache_utils.js create mode 100644 trunk/infrastructure/framework-src/modules/comet.js create mode 100644 trunk/infrastructure/framework-src/modules/dateutils.js create mode 100644 trunk/infrastructure/framework-src/modules/dispatch.js create mode 100644 trunk/infrastructure/framework-src/modules/ejs.js create mode 100644 trunk/infrastructure/framework-src/modules/email.js create mode 100644 trunk/infrastructure/framework-src/modules/exceptionutils.js create mode 100644 trunk/infrastructure/framework-src/modules/execution.js create mode 100644 trunk/infrastructure/framework-src/modules/fastJSON.js create mode 100644 trunk/infrastructure/framework-src/modules/faststatic.js create mode 100644 trunk/infrastructure/framework-src/modules/fileutils.js create mode 100644 trunk/infrastructure/framework-src/modules/funhtml.js create mode 100644 trunk/infrastructure/framework-src/modules/global/appjet.js create mode 100644 trunk/infrastructure/framework-src/modules/global/request.js create mode 100644 trunk/infrastructure/framework-src/modules/global/response.js create mode 100644 trunk/infrastructure/framework-src/modules/image.js create mode 100644 trunk/infrastructure/framework-src/modules/jsutils.js create mode 100644 trunk/infrastructure/framework-src/modules/netutils.js create mode 100644 trunk/infrastructure/framework-src/modules/profiler.js create mode 100644 trunk/infrastructure/framework-src/modules/sessions.js create mode 100644 trunk/infrastructure/framework-src/modules/sqlbase/persistent_vars.js create mode 100644 trunk/infrastructure/framework-src/modules/sqlbase/sqlbase.js create mode 100644 trunk/infrastructure/framework-src/modules/sqlbase/sqlcommon.js create mode 100644 trunk/infrastructure/framework-src/modules/sqlbase/sqlobj.js create mode 100644 trunk/infrastructure/framework-src/modules/stringutils.js create mode 100644 trunk/infrastructure/framework-src/modules/sync.js create mode 100644 trunk/infrastructure/framework-src/modules/timer.js create mode 100644 trunk/infrastructure/framework-src/modules/varz.js create mode 100644 trunk/infrastructure/framework-src/modules/yuicompressor.js create mode 100644 trunk/infrastructure/framework-src/oncomet.js create mode 100644 trunk/infrastructure/framework-src/onerror.js create mode 100644 trunk/infrastructure/framework-src/onprint.js create mode 100644 trunk/infrastructure/framework-src/onrequest.js create mode 100644 trunk/infrastructure/framework-src/onreset.js create mode 100644 trunk/infrastructure/framework-src/onsars.js create mode 100644 trunk/infrastructure/framework-src/onscheduledtask.js create mode 100644 trunk/infrastructure/framework-src/onshutdown.js create mode 100644 trunk/infrastructure/framework-src/onstartup.js create mode 100644 trunk/infrastructure/framework-src/onsyntaxerror.js create mode 100644 trunk/infrastructure/framework-src/postamble.js create mode 100644 trunk/infrastructure/framework-src/preamble.js create mode 100644 trunk/infrastructure/framework-src/syntaxerror.js create mode 100644 trunk/infrastructure/lib/activation.jar create mode 100644 trunk/infrastructure/lib/c3p0-0.9.1.2.jar create mode 100644 trunk/infrastructure/lib/commons-lang-2.4.jar create mode 100644 trunk/infrastructure/lib/derby-10.5.1.1.jar create mode 100644 trunk/infrastructure/lib/derbytools.jar create mode 100644 trunk/infrastructure/lib/dnsjava-2.0.6.jar create mode 100644 trunk/infrastructure/lib/jetty-6.1.20.jar create mode 100644 trunk/infrastructure/lib/jetty-sslengine-6.1.20.jar create mode 100644 trunk/infrastructure/lib/jetty-util-6.1.20.jar create mode 100644 trunk/infrastructure/lib/json.jar create mode 100644 trunk/infrastructure/lib/mail.jar create mode 100644 trunk/infrastructure/lib/manifest create mode 100644 trunk/infrastructure/lib/rhino-js-1.7r1.jar create mode 100644 trunk/infrastructure/lib/sanselan-0.94aj.jar create mode 100644 trunk/infrastructure/lib/servlet-api-2.5-20081211.jar create mode 100644 trunk/infrastructure/lib/tagsoup-1.2.jar create mode 100644 trunk/infrastructure/lib/yuicompressor-2.4-appjet.jar create mode 100644 trunk/infrastructure/net.appjet.ajstdlib/ajstdlib.scala create mode 100644 trunk/infrastructure/net.appjet.ajstdlib/sqlbase.scala create mode 100644 trunk/infrastructure/net.appjet.ajstdlib/streaming-client.js create mode 100644 trunk/infrastructure/net.appjet.ajstdlib/streaming-iframe.html create mode 100644 trunk/infrastructure/net.appjet.ajstdlib/streaming.scala create mode 100644 trunk/infrastructure/net.appjet.ajstdlib/timer.scala create mode 100644 trunk/infrastructure/net.appjet.bodylock/bodylock.scala create mode 100644 trunk/infrastructure/net.appjet.bodylock/compressor.scala create mode 100644 trunk/infrastructure/net.appjet.common.cli/cli.scala create mode 100644 trunk/infrastructure/net.appjet.common.sars/sars.scala create mode 100644 trunk/infrastructure/net.appjet.common.sars/sha1.scala create mode 100644 trunk/infrastructure/net.appjet.common/rhino/rhinospect.scala create mode 100644 trunk/infrastructure/net.appjet.common/util/BCrypt.java create mode 100644 trunk/infrastructure/net.appjet.common/util/BetterFile.java create mode 100644 trunk/infrastructure/net.appjet.common/util/ClassReload.java create mode 100644 trunk/infrastructure/net.appjet.common/util/ExpiringMapping.java create mode 100644 trunk/infrastructure/net.appjet.common/util/HttpServletRequestFactory.java create mode 100644 trunk/infrastructure/net.appjet.common/util/LenientFormatter.java create mode 100644 trunk/infrastructure/net.appjet.common/util/LimitedSizeMapping.java create mode 100644 trunk/infrastructure/net.appjet.oui/ConfigParam.java create mode 100644 trunk/infrastructure/net.appjet.oui/FastJSON.scala create mode 100644 trunk/infrastructure/net.appjet.oui/GeneratedConfigParam.java create mode 100644 trunk/infrastructure/net.appjet.oui/config.scala create mode 100644 trunk/infrastructure/net.appjet.oui/dynamicvar.scala create mode 100644 trunk/infrastructure/net.appjet.oui/encryption.scala create mode 100644 trunk/infrastructure/net.appjet.oui/execution.scala create mode 100644 trunk/infrastructure/net.appjet.oui/files.scala create mode 100644 trunk/infrastructure/net.appjet.oui/logging.scala create mode 100644 trunk/infrastructure/net.appjet.oui/main.scala create mode 100644 trunk/infrastructure/net.appjet.oui/monitoring.scala create mode 100644 trunk/infrastructure/net.appjet.oui/network.scala create mode 100644 trunk/infrastructure/net.appjet.oui/servermodel.scala create mode 100644 trunk/infrastructure/net.appjet.oui/stats.scala create mode 100644 trunk/infrastructure/net.appjet.oui/synchronizer.scala create mode 100644 trunk/infrastructure/net.appjet.oui/util.scala create mode 100644 trunk/infrastructure/rhino1_7R1/apiClasses.properties create mode 100644 trunk/infrastructure/rhino1_7R1/build-date create mode 100644 trunk/infrastructure/rhino1_7R1/build.properties create mode 100644 trunk/infrastructure/rhino1_7R1/build.xml create mode 100644 trunk/infrastructure/rhino1_7R1/deprecatedsrc/build.xml create mode 100644 trunk/infrastructure/rhino1_7R1/deprecatedsrc/org/mozilla/javascript/ClassDefinitionException.java create mode 100644 trunk/infrastructure/rhino1_7R1/deprecatedsrc/org/mozilla/javascript/NotAFunctionException.java create mode 100644 trunk/infrastructure/rhino1_7R1/deprecatedsrc/org/mozilla/javascript/PropertyException.java create mode 100644 trunk/infrastructure/rhino1_7R1/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/LogicalEquality.java create mode 100644 trunk/infrastructure/rhino1_7R1/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/Namespace.java create mode 100644 trunk/infrastructure/rhino1_7R1/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/NamespaceHelper.java create mode 100644 trunk/infrastructure/rhino1_7R1/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/QName.java create mode 100644 trunk/infrastructure/rhino1_7R1/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XML.java create mode 100644 trunk/infrastructure/rhino1_7R1/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLCtor.java create mode 100644 trunk/infrastructure/rhino1_7R1/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLLibImpl.java create mode 100644 trunk/infrastructure/rhino1_7R1/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLList.java create mode 100644 trunk/infrastructure/rhino1_7R1/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLName.java create mode 100644 trunk/infrastructure/rhino1_7R1/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLObjectImpl.java create mode 100644 trunk/infrastructure/rhino1_7R1/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLWithScope.java create mode 100644 trunk/infrastructure/rhino1_7R1/examples/Control.java create mode 100644 trunk/infrastructure/rhino1_7R1/examples/Counter.java create mode 100644 trunk/infrastructure/rhino1_7R1/examples/CounterTest.java create mode 100644 trunk/infrastructure/rhino1_7R1/examples/DynamicScopes.java create mode 100644 trunk/infrastructure/rhino1_7R1/examples/E4X/e4x_example.js create mode 100644 trunk/infrastructure/rhino1_7R1/examples/File.java create mode 100644 trunk/infrastructure/rhino1_7R1/examples/Foo.java create mode 100644 trunk/infrastructure/rhino1_7R1/examples/Matrix.java create mode 100644 trunk/infrastructure/rhino1_7R1/examples/NervousText.html create mode 100644 trunk/infrastructure/rhino1_7R1/examples/NervousText.js create mode 100644 trunk/infrastructure/rhino1_7R1/examples/PrimitiveWrapFactory.java create mode 100644 trunk/infrastructure/rhino1_7R1/examples/RunScript.java create mode 100644 trunk/infrastructure/rhino1_7R1/examples/RunScript2.java create mode 100644 trunk/infrastructure/rhino1_7R1/examples/RunScript3.java create mode 100644 trunk/infrastructure/rhino1_7R1/examples/RunScript4.java create mode 100644 trunk/infrastructure/rhino1_7R1/examples/Shell.java create mode 100644 trunk/infrastructure/rhino1_7R1/examples/SwingApplication.js create mode 100644 trunk/infrastructure/rhino1_7R1/examples/checkParam.js create mode 100644 trunk/infrastructure/rhino1_7R1/examples/enum.js create mode 100644 trunk/infrastructure/rhino1_7R1/examples/jsdoc.js create mode 100644 trunk/infrastructure/rhino1_7R1/examples/liveConnect.js create mode 100644 trunk/infrastructure/rhino1_7R1/examples/unique.js create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/allclasses-frame.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/allclasses-noframe.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/constant-values.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/deprecated-list.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/help-doc.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/index-all.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/index.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/Callable.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/ClassCache.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/ClassShutter.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/CompilerEnvirons.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/Context.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/ContextAction.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/ContextFactory.Listener.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/ContextFactory.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/EcmaError.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/ErrorReporter.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/EvaluatorException.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/Function.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/FunctionObject.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/GeneratedClassLoader.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/ImporterTopLevel.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/JavaScriptException.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/RefCallable.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/RhinoException.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/Script.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/Scriptable.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/ScriptableObject.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/SecurityController.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/Synchronizer.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/WrapFactory.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/WrappedException.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/Wrapper.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/debug/DebuggableScript.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/debug/package-frame.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/debug/package-summary.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/debug/package-tree.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/optimizer/ClassCompiler.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/optimizer/package-frame.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/optimizer/package-summary.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/optimizer/package-tree.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/package-frame.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/package-summary.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/package-tree.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/serialize/ScriptableInputStream.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/serialize/ScriptableOutputStream.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/serialize/package-frame.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/serialize/package-summary.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/serialize/package-tree.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/overview-frame.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/overview-summary.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/overview-tree.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/package-list create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/resources/inherit.gif create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/serialized-form.html create mode 100644 trunk/infrastructure/rhino1_7R1/javadoc/stylesheet.css create mode 100644 trunk/infrastructure/rhino1_7R1/lib/jsr173_1.0_api.jar create mode 100644 trunk/infrastructure/rhino1_7R1/lib/xbean.jar create mode 100644 trunk/infrastructure/rhino1_7R1/src/build.xml create mode 100644 trunk/infrastructure/rhino1_7R1/src/manifest create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/classfile/ByteCode.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/classfile/ClassFileWriter.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Arguments.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/BaseFunction.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Callable.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ClassCache.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ClassShutter.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/CompilerEnvirons.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ConstProperties.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Context.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ContextAction.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ContextFactory.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ContextListener.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/DToA.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Decompiler.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/DefaultErrorReporter.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/DefiningClassLoader.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Delegator.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/EcmaError.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ErrorReporter.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Evaluator.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/EvaluatorException.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Function.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/FunctionNode.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/FunctionObject.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/GeneratedClassLoader.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/IRFactory.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/IdFunctionCall.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/IdFunctionObject.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/IdScriptableObject.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ImporterTopLevel.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/InformativeParser.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/InterfaceAdapter.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/InterpretedFunction.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Interpreter.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/InterpreterData.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/JavaAdapter.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/JavaMembers.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/JavaScriptException.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Kit.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/LazilyLoadedCtor.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/MemberBox.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeArray.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeBoolean.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeCall.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeDate.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeError.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeFunction.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeGenerator.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeGlobal.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeIterator.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaArray.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaClass.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaConstructor.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaMethod.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaObject.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaPackage.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaTopPackage.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeMath.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeNumber.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeObject.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeScript.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeString.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeWith.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Node.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NodeTransformer.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ObjArray.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ObjToIntMap.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Parser.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/PolicySecurityController.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Ref.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/RefCallable.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/RegExpProxy.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/RhinoException.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Script.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ScriptOrFnNode.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ScriptRuntime.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Scriptable.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ScriptableObject.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/SecureCaller.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/SecurityController.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/SecurityUtilities.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/SpecialRef.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Synchronizer.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Token.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/TokenStream.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/UintMap.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Undefined.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/UniqueTag.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/VMBridge.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/WrapFactory.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/WrappedException.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Wrapper.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/continuations/Continuation.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/debug/DebugFrame.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/debug/DebuggableObject.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/debug/DebuggableScript.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/debug/Debugger.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/jdk11/VMBridge_jdk11.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/jdk13/VMBridge_jdk13.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/jdk15/VMBridge_jdk15.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/Block.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/ClassCompiler.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/Codegen.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/DataFlowBitSet.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/OptFunctionNode.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/OptRuntime.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/OptTransformer.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/Optimizer.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/regexp/NativeRegExp.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/regexp/NativeRegExpCtor.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/regexp/RegExpImpl.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/regexp/SubString.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/resources/Messages.properties create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/resources/Messages_fr.properties create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/serialize/ScriptableInputStream.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/serialize/ScriptableOutputStream.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/xml/XMLLib.java create mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/xml/XMLObject.java create mode 100644 trunk/infrastructure/rhino1_7R1/testsrc/base.skip create mode 100644 trunk/infrastructure/rhino1_7R1/testsrc/build.xml create mode 100644 trunk/infrastructure/rhino1_7R1/testsrc/opt1.skip create mode 100644 trunk/infrastructure/rhino1_7R1/testsrc/org/mozilla/javascript/drivers/JsDriver.java create mode 100644 trunk/infrastructure/rhino1_7R1/testsrc/org/mozilla/javascript/drivers/ShellTest.java create mode 100644 trunk/infrastructure/rhino1_7R1/testsrc/org/mozilla/javascript/drivers/StandardTests.java create mode 100644 trunk/infrastructure/rhino1_7R1/testsrc/org/mozilla/javascript/drivers/results.html create mode 100644 trunk/infrastructure/rhino1_7R1/testsrc/org/mozilla/javascript/tests/Bug409702Test.java create mode 100644 trunk/infrastructure/rhino1_7R1/testsrc/org/mozilla/javascript/tests/JavaAcessibilityTest.java create mode 100644 trunk/infrastructure/rhino1_7R1/testsrc/org/mozilla/javascript/tests/PrivateAccessClass.java create mode 100644 trunk/infrastructure/rhino1_7R1/toolsrc/build.xml create mode 100644 trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/ToolErrorReporter.java create mode 100644 trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/debugger/Dim.java create mode 100644 trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/debugger/GuiCallback.java create mode 100644 trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/debugger/Main.java create mode 100644 trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/debugger/ScopeProvider.java create mode 100644 trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/debugger/SwingGui.java create mode 100644 trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/debugger/build.xml create mode 100644 trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/idswitch/CodePrinter.java create mode 100644 trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/idswitch/FileBody.java create mode 100644 trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/idswitch/IdValuePair.java create mode 100644 trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/idswitch/Main.java create mode 100644 trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/idswitch/SwitchGenerator.java create mode 100644 trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/jsc/Main.java create mode 100644 trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/resources/Messages.properties create mode 100644 trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/ConsoleTextArea.java create mode 100644 trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/Environment.java create mode 100644 trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/Global.java create mode 100644 trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/JSConsole.java create mode 100644 trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/JavaPolicySecurity.java create mode 100644 trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/Main.java create mode 100644 trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/QuitAction.java create mode 100644 trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/SecurityProxy.java create mode 100644 trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/ShellContextFactory.java create mode 100644 trunk/infrastructure/rhino1_7R1/xmlimplsrc/build.xml create mode 100644 trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/Namespace.java create mode 100644 trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/QName.java create mode 100644 trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XML.java create mode 100644 trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLCtor.java create mode 100644 trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLLibImpl.java create mode 100644 trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLList.java create mode 100644 trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLName.java create mode 100644 trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLObjectImpl.java create mode 100644 trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLWithScope.java create mode 100644 trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XmlNode.java create mode 100644 trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XmlProcessor.java create mode 100644 trunk/infrastructure/yuicompressor/lib/jargs-1.0.jar create mode 100644 trunk/infrastructure/yuicompressor/lib/rhino-yuicompressor.jar create mode 100755 trunk/infrastructure/yuicompressor/make.sh create mode 100644 trunk/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/Bootstrap.java create mode 100644 trunk/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/CssCompressor.java create mode 100644 trunk/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/JarClassLoader.java create mode 100644 trunk/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/JavaScriptCompressor.java create mode 100644 trunk/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/JavaScriptIdentifier.java create mode 100644 trunk/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/JavaScriptToken.java create mode 100644 trunk/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/ScriptOrFnScope.java create mode 100644 trunk/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/YUICompressor.java create mode 100644 trunk/infrastructure/yuicompressor/src/yuicompressor/org/mozilla/javascript/Decompiler.java create mode 100644 trunk/infrastructure/yuicompressor/src/yuicompressor/org/mozilla/javascript/Decompiler.java.orig create mode 100644 trunk/infrastructure/yuicompressor/src/yuicompressor/org/mozilla/javascript/Parser.java create mode 100644 trunk/infrastructure/yuicompressor/src/yuicompressor/org/mozilla/javascript/Parser.java.orig create mode 100644 trunk/infrastructure/yuicompressor/src/yuicompressor/org/mozilla/javascript/Token.java create mode 100644 trunk/infrastructure/yuicompressor/src/yuicompressor/org/mozilla/javascript/Token.java.orig create mode 100644 trunk/infrastructure/yuicompressor/src/yuicompressor/org/mozilla/javascript/TokenStream.java create mode 100644 trunk/infrastructure/yuicompressor/src/yuicompressor/org/mozilla/javascript/TokenStream.java.orig (limited to 'trunk') diff --git a/trunk/COPYING b/trunk/COPYING new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/trunk/COPYING @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/trunk/README b/trunk/README new file mode 100644 index 0000000..a214853 --- /dev/null +++ b/trunk/README @@ -0,0 +1,290 @@ +EtherPad is a web-based realtime collaborative document editor. + +EtherPad currently lives at http://code.google.com/p/etherpad + +For instructions to build and run EtherPad, see: http://code.google.com/p/etherpad/wiki/Instructions + +This distribution includes some code written by other organizations. +The rest is Copyright 2007-2009 Google Inc. and licensed under the Apache License 2.0. + +>>>>>>>>>>>>>> +Apache Commons Lang +Copyright 2001-2008 The Apache Software Foundation + +This product includes software developed by +The Apache Software Foundation (http://www.apache.org/). + +========================================================================= +== NOTICE file corresponding to section 4(d) of the Apache License, == +== Version 2.0, in this case for the Apache Derby distribution. == +========================================================================= + +>>>>>>>>>>>>>> +Apache Derby +Copyright 2004-2008 The Apache Software Foundation + +This product includes software developed by +The Apache Software Foundation (http://www.apache.org/). + +Portions of Derby were originally developed by +International Business Machines Corporation and are +licensed to the Apache Software Foundation under the +"Software Grant and Corporate Contribution License Agreement", +informally known as the "Derby CLA". +The following copyright notice(s) were affixed to portions of the code +with which this file is now or was at one time distributed +and are placed here unaltered. + +(C) Copyright 1997,2004 International Business Machines Corporation. All rights reserved. + +(C) Copyright IBM Corp. 2003. + +The portion of the functionTests under 'nist' was originally +developed by the National Institute of Standards and Technology (NIST), +an agency of the United States Department of Commerce, and adapted by +International Business Machines Corporation in accordance with the NIST +Software Acknowledgment and Redistribution document at +http://www.itl.nist.gov/div897/ctg/sql_form.htm + +>>>>>>>>>>>>>> +Apache Harmony +Copyright 2006, 2009 The Apache Software Foundation. + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). + +Portions of Apache Harmony were originally developed by +Intel Corporation and are licensed to the Apache Software +Foundation under the "Software Grant and Corporate Contribution +License Agreement" and for which the following copyright notices +apply + (C) Copyright 2005 Intel Corporation + (C) Copyright 2005-2006 Intel Corporation + (C) Copyright 2006 Intel Corporation + +>>>>>>>>>>>>>> +============================================================== + Jetty Web Container + Copyright 1995-2006 Mort Bay Consulting Pty Ltd +============================================================== + +The Jetty Web Container is Copyright Mort Bay Consulting Pty Ltd +unless otherwise noted. It is licensed under the apache 2.0 +license. + +The javax.servlet package used by Jetty is copyright +Sun Microsystems, Inc and Apache Software Foundation. It is +distributed under the Common Development and Distribution License. +You can obtain a copy of the license at +https://glassfish.dev.java.net/public/CDDLv1.0.html. + +The UnixCrypt.java code ~Implements the one way cryptography used by +Unix systems for simple password protection. Copyright 1996 Aki Yoshida, +modified April 2001 by Iris Van den Broeke, Daniel Deville. +Permission to use, copy, modify and distribute UnixCrypt +for non-commercial or commercial purposes and without fee is +granted provided that the copyright notice appears in all copies. + +The default JSP implementation is provided by the Glassfish JSP engine +from project Glassfish http://glassfish.dev.java.net. Copyright 2005 +Sun Microsystems, Inc. and portions Copyright Apache Software Foundation. + +Some portions of the code are Copyright: + 2006 Tim Vernum + 1999 Jason Gilbert. + +The jboss integration module contains some LGPL code. + +The win32 Java Service Wrapper (v3.2.3) is Copyright (c) 1999, 2006 +Tanuki Software, Inc. and 2001 Silver Egg Technology. It is +covered by an open license which is viewable at +http://svn.codehaus.org/jetty/jetty/branches/jetty-6.1/extras/win32service/LICENSE.txt + +>>>>>>>>>>>>>> +Apache Sanselan +Copyright 2007-2009 The Apache Software Foundation. + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). + +>>>>>>>>>>>>>> +TagSoup +Copyright 2002-2008 by John Cowan + +>>>>>>>>>>>>>> +dnsjava: + +Copyright (c) 1999-2005, Brian Wellington +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of the dnsjava project nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +>>>>>>>>>>>>>> +jBCrypt: + +Copyright (c) 2006 Damien Miller + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +>>>>>>>>>>>>>> +Scala: + +Copyright (c) 2002-2009 EPFL, Lausanne, unless otherwise specified. +All rights reserved. + +This software was developed by the Programming Methods Laboratory of the +Swiss Federal Institute of Technology (EPFL), Lausanne, Switzerland. + +Permission to use, copy, modify, and distribute this software in source +or binary form for any purpose with or without fee is hereby granted, +provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the EPFL nor the names of its contributors + may be used to endorse or promote products derived from this + software without specific prior written permission. + + +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +>>>>>>>>>>>>>> +YUI Compressor: + +Copyright (c) 2009, Yahoo! Inc. +All rights reserved. + +Redistribution and use of this software in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of Yahoo! Inc. nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission of Yahoo! Inc. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +>>>>>>>>>>>>>> +jQuery +Copyright (c) 2009 John Resig, http://jquery.com/ + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +>>>>>>>>>>>>>> +jQuery Pulse: + +Copyright (c) 2008 James Padolsey - jp(at)qd9(dot)co.uk | http://james.padolsey.com / http://enhance.qd-creative.co.uk + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +>>>>>>>>>>>>>> +SWFObject v1.5 +Copyright (c) 2007 Geoff Stearns + +Licensed under the MIT License + +>>>>>>>>>>>>>> +jQuery Context Menu Plugin: + +Original version copyright 2008 A Beautiful Site, LLC. Modifications by AppJet, Inc. released under the same license. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +>>>>>>>>>>>>>> +c3p0 +Copyright (c) 2006 Machinery for Change, Inc. + +Licensed under the LGPL 2.1 + +>>>>>>>>>>>>>> +JCommon, JFreeChart +Copyright (c) 2007-2009 Object Refinery Limited + +Licensed under the LGPL 2.1 + +>>>>>>>>>>>>>> +Mozilla Rhino + +Licensed under the MPL 1.1, GPL 2.0 or later + + +>>>>>>>>>>>>>> +MySQL Connector + +Licensed uder the GPL 2.0 or later with the FOSS exception diff --git a/trunk/etherpad/.gitignore b/trunk/etherpad/.gitignore new file mode 100644 index 0000000..5f41ae6 --- /dev/null +++ b/trunk/etherpad/.gitignore @@ -0,0 +1,9 @@ +data/ +build +derby.log +local +*.swp +appjet-eth*.jar +etherpad-pne-*.jar + + diff --git a/trunk/etherpad/bin/.gitignore b/trunk/etherpad/bin/.gitignore new file mode 100644 index 0000000..00fd678 --- /dev/null +++ b/trunk/etherpad/bin/.gitignore @@ -0,0 +1 @@ +run-david.sh diff --git a/trunk/etherpad/bin/rebuildjar.sh b/trunk/etherpad/bin/rebuildjar.sh new file mode 100755 index 0000000..9e802c2 --- /dev/null +++ b/trunk/etherpad/bin/rebuildjar.sh @@ -0,0 +1,75 @@ +#!/bin/bash -e + +# 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 [ -z "$JAR" ]; then + if [ ! -z `which fastjar` ]; then + JAR=fastjar + else + JAR=jar + fi +fi + +function notify { + if [ ! -z `which growlnotify` ]; then + echo $0 finished | growlnotify + fi +} +trap notify EXIT + +source ../infrastructure/bin/compilecache.sh + +suffix="-dev"; +if [ "$1" == "prod" ]; then + suffix=""; + shift; +fi + +OWD=`pwd` +cd ../infrastructure +JAR=$JAR bin/makejar.sh $@ + +rm -rf build/etherpad-jars +mkdir -p build/etherpad-jars + +echo "including etherpad JARs..." + +JARFILES="echo ../etherpad/lib/*.jar" +function genjar { + echo "unzipping JARs..." + pushd $1 >> /dev/null + + for a in ../../../etherpad/lib/*.jar; do + $JAR xf $a + rm -rf META-INF/{MANIFEST.MF,NOTICE{,.txt},LICENSE{,.txt},INDEX.LIST,SUN_MICR.{RSA,SF},maven} + done + + popd >> /dev/null +} +cacheonfiles JAR-etherpad "$JARFILES" genjar 1 + +echo "updating..." + +pushd buildcache/JAR-etherpad >> /dev/null +$JAR uf ../../build/appjet.jar `ls . | grep -v "^t$"` + +echo "done." + +popd >> /dev/null + +dst="$OWD/appjet-eth$suffix.jar" +cp -f build/appjet.jar $dst +cd $OWD +echo "wrote $dst" diff --git a/trunk/etherpad/bin/run-local.sh b/trunk/etherpad/bin/run-local.sh new file mode 100755 index 0000000..72b0cc1 --- /dev/null +++ b/trunk/etherpad/bin/run-local.sh @@ -0,0 +1,66 @@ +#!/bin/bash -e + +# 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. + +mkdir -p data/appjet + +MXRAM="1G" +if [ ! -z $1 ]; then + if [ ! '-' = `echo $1 | head -c 1` ]; then + MXRAM="$1"; + shift; + fi +fi + +CP="appjet-eth-dev.jar:data" +for f in lib/*.jar; do + CP="$CP:$f" +done + +if [ -z "$JAVA" ]; then + JAVA=java +fi + +# etherpad properties file +cfg_file=./data/etherpad.local.properties +if [ ! -f $cfg_file ]; then + cfg_file=./etc/etherpad.localdev-default.properties +fi +if [[ $1 == "--cfg" ]]; then + cfg_file=${2} + shift; + shift; +fi + +echo "Using config file: ${cfg_file}" + +$JAVA -classpath $CP \ + -server \ + -Xmx${MXRAM} \ + -Xms${MXRAM} \ + -Djava.awt.headless=true \ + -XX:MaxGCPauseMillis=500 \ + -XX:+UseConcMarkSweepGC \ + -XX:+CMSIncrementalMode \ + -XX:CMSIncrementalSafetyFactor=50 \ + -XX:+PrintGCDetails \ + -XX:+PrintGCTimeStamps \ + -Xloggc:./data/logs/backend/jvm-gc.log \ + -Dappjet.jmxremote=true \ + $JAVA_OPTS \ + net.appjet.oui.main \ + --configFile=${cfg_file} \ + "$@" + diff --git a/trunk/etherpad/bin/setup-mysql-db.sh b/trunk/etherpad/bin/setup-mysql-db.sh new file mode 100755 index 0000000..d823a9e --- /dev/null +++ b/trunk/etherpad/bin/setup-mysql-db.sh @@ -0,0 +1,30 @@ +#!/bin/bash -e + +# 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 [ `whoami` != "root" ]; then + echo "Must run as root, i.e., sudo $0" + exit 1 +fi + +db="etherpad" + +echo "Creating etherpad ${db}..." +echo "create database ${db};" | ${mysql} -u root + +echo "Granting priviliges..." +echo "grant all privileges on ${db}.* to 'etherpad'@'localhost' identified by 'password';" | ${mysql} -u root + +echo "Success" diff --git a/trunk/etherpad/etc/etherpad.changeme.properties b/trunk/etherpad/etc/etherpad.changeme.properties new file mode 100644 index 0000000..ddc0bd8 --- /dev/null +++ b/trunk/etherpad/etc/etherpad.changeme.properties @@ -0,0 +1,16 @@ +ajstdlibHome = ../infrastructure/framework-src/modules +appjetHome = ./data/appjet +devMode = false +etherpad.adminPass = password +etherpad.fakeProduction = false +etherpad.isProduction = true +etherpad.SQL_JDBC_DRIVER = com.mysql.jdbc.Driver +etherpad.SQL_JDBC_URL = jdbc:mysql://mysql:3306/etherpad +etherpad.SQL_PASSWORD = password +etherpad.SQL_USERNAME = etherpad +listen = 0.0.0.0:80 +logDir = ./data/logs +modulePath = ./src +transportPrefix = /comet +transportUseWildcardSubdomains = true +useVirtualFileRoot = ./src diff --git a/trunk/etherpad/etc/etherpad.localdev-default.properties b/trunk/etherpad/etc/etherpad.localdev-default.properties new file mode 100644 index 0000000..c2a2122 --- /dev/null +++ b/trunk/etherpad/etc/etherpad.localdev-default.properties @@ -0,0 +1,16 @@ +ajstdlibHome = ../infrastructure/framework-src/modules +appjetHome = ./data/appjet +devMode = true +etherpad.adminPass = password +etherpad.fakeProduction = false +etherpad.isProduction = false +etherpad.SQL_JDBC_DRIVER = com.mysql.jdbc.Driver +etherpad.SQL_JDBC_URL = jdbc:mysql://localhost:3306/etherpad +etherpad.SQL_PASSWORD = password +etherpad.SQL_USERNAME = etherpad +listen = 9000 +logDir = ./data/logs +modulePath = ./src +transportPrefix = /comet +transportUseWildcardSubdomains = true +useVirtualFileRoot = ./src diff --git a/trunk/etherpad/lib/dnsjava-2.0.6.jar b/trunk/etherpad/lib/dnsjava-2.0.6.jar new file mode 100644 index 0000000..e41f9b0 Binary files /dev/null and b/trunk/etherpad/lib/dnsjava-2.0.6.jar differ diff --git a/trunk/etherpad/lib/jbcrypt-0.2.jar b/trunk/etherpad/lib/jbcrypt-0.2.jar new file mode 100644 index 0000000..109033c Binary files /dev/null and b/trunk/etherpad/lib/jbcrypt-0.2.jar differ diff --git a/trunk/etherpad/lib/jcommon-1.0.15.jar b/trunk/etherpad/lib/jcommon-1.0.15.jar new file mode 100644 index 0000000..d0dc26d Binary files /dev/null and b/trunk/etherpad/lib/jcommon-1.0.15.jar differ diff --git a/trunk/etherpad/lib/jfreechart-1.0.12.jar b/trunk/etherpad/lib/jfreechart-1.0.12.jar new file mode 100644 index 0000000..73be90f Binary files /dev/null and b/trunk/etherpad/lib/jfreechart-1.0.12.jar differ diff --git a/trunk/etherpad/src/etherpad/admin/shell.js b/trunk/etherpad/src/etherpad/admin/shell.js new file mode 100644 index 0000000..391d524 --- /dev/null +++ b/trunk/etherpad/src/etherpad/admin/shell.js @@ -0,0 +1,127 @@ +/** + * 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. + */ + +import("funhtml.*"); +import("jsutils.cmp"); +import("jsutils.eachProperty"); +import("exceptionutils"); +import("execution"); +import("stringutils.trim"); + +import("etherpad.sessions.getSession"); +import("etherpad.utils.*"); + +function _splitCommand(cmd) { + var parts = [[], []]; + var importing = true; + cmd.split("\n").forEach(function(l) { + if ((trim(l).length > 0) && + (trim(l).indexOf("import") != 0)) { + importing = false; + } + + if (importing) { + parts[0].push(l); + } else { + parts[1].push(l); + } + }); + + parts[0] = parts[0].join("\n"); + parts[1] = parts[1].join("\n"); + return parts; +} + +function getResult(cmd) { + var resultString = (function() { + try { + var parts = _splitCommand(cmd); + result = execution.fancyAssEval(parts[0], parts[1]); + } catch (e) { + // if (e instanceof JavaException) { + // e = new net.appjet.bodylock.JSRuntimeException(e.getMessage(), e.javaException); + // } + if (appjet.config.devMode) { + (e.javaException || e.rhinoException || e).printStackTrace(); + } + result = exceptionutils.getStackTracePlain(e); + } + var resultString; + try { + resultString = ((result && result.toString) ? result.toString() : String(result)); + } catch (ex) { + resultString = "Error converting result to string: "+ex.toString(); + } + return resultString; + })(); + return resultString; +} + +function _renderCommandShell() { + // run command if necessary + if (request.params.cmd) { + var cmd = request.params.cmd; + var resultString = getResult(cmd); + + getSession().shellCommand = cmd; + getSession().shellResult = resultString; + response.redirect(request.path+(request.query?'?'+request.query:'')); + } + + var div = DIV({style: "padding: 4px; margin: 4px; background: #eee; " + + "border: 1px solid #338"}); + // command div + var oldCmd = getSession().shellCommand || ""; + var commandDiv = DIV({style: "width: 100%; margin: 4px 0;"}); + commandDiv.push(FORM({style: "width: 100%;", + method: "POST", action: request.path + (request.query?'?'+request.query:'')}, + TEXTAREA({name: "cmd", + style: "border: 1px solid #555;" + + "width: 100%; height: 160px; font-family: monospace;"}, + html(oldCmd)), + INPUT({type: "submit"}))); + + // result div + var resultDiv = DIV({style: ""}); + var isResult = getSession().shellResult != null; + if (isResult) { + resultDiv.push(DIV( + PRE({style: 'border: 1px solid #555; font-family: monospace; margin: 4px 0; padding: 4px;'}, + getSession().shellResult))); + delete getSession().shellResult; + resultDiv.push(DIV({style: "text-align: right;"}, + A({href: qpath({})}, "clear"))); + } else { + resultDiv.push(P("result will go here")); + } + + var t = TABLE({border: 0, cellspacing: 0, cellpadding: 0, width: "100%", + style: "width: 100%;"}); + t.push(TR(TH({width: "49%", align: "left"}, " Command:"), + TH({width: "49%", align: "left"}, " "+(isResult ? "Result:" : ""))), + TR(TD({valign: "top", style: 'padding: 4px;'}, commandDiv), + TD({valign: "top", style: 'padding: 4px;'}, resultDiv))); + div.push(t); + return div; +} + +function handleRequest() { + var body = BODY(); + body.push(A({href: '/ep/admin/'}, html("« Admin"))); + body.push(BR(), BR()); + body.push(_renderCommandShell()); + response.write(HTML(body)); +} diff --git a/trunk/etherpad/src/etherpad/billing/billing.js b/trunk/etherpad/src/etherpad/billing/billing.js new file mode 100644 index 0000000..444c233 --- /dev/null +++ b/trunk/etherpad/src/etherpad/billing/billing.js @@ -0,0 +1,800 @@ +/** + * 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. + */ + +import("dateutils.*"); +import("fastJSON"); +import("jsutils.eachProperty"); +import("netutils.urlPost"); +import("sqlbase.sqlbase"); +import("sqlbase.sqlcommon"); +import("sqlbase.sqlobj"); +import("stringutils.{md5,repeat}"); + +import("etherpad.log.{custom=>eplog}"); + + +jimport("java.lang.System.out.println"); + +function clearKeys(obj, keys) { + var newObj = {}; + eachProperty(obj, function(k, v) { + var isCopied = false; + keys.forEach(function(key) { + if (k == key.name && + key.valueTest(v)) { + newObj[k] = key.valueReplace(v); + isCopied = true; + } + }); + if (! isCopied) { + if (typeof(obj[k]) == 'object') { + newObj[k] = clearKeys(v, keys); + } else { + newObj[k] = v; + } + } + }); + return newObj; +} + +function replaceWithX(s) { + return repeat("X", s.length); +} + +function log(obj) { + eplog('billing', clearKeys(obj, [ + {name: "ACCT", + valueTest: function(s) { return /^\d{15,16}$/.test(s) }, + valueReplace: replaceWithX}, + {name: "CVV2", + valueTest: function(s) { return /^\d{3,4}$/.test(s) }, + valueReplace: replaceWithX}])); +} + +var _USER = function() { return appjet.config['etherpad.paypal.user'] || "zamfir_1239051855_biz_api1.gmail.com"; } +var _PWD = function() { return appjet.config['etherpad.paypal.pwd'] || "1239051867"; } +var _SIGNATURE = function() { return appjet.config['etherpad.paypal.signature'] || "AQU0e5vuZCvSg-XJploSa.sGUDlpAwAy5fz.FhtfOQ25Qa9sFLDt7Bmp"; } +var _RECEIVER = function() { return appjet.config['etherpad.paypal.receiver'] || "zamfir_1239051855_biz@gmail.com"; } +var _paypalApiUrl = function() { return appjet.config['etherpad.paypal.apiUrl'] || "https://api-3t.sandbox.paypal.com/nvp"; } +var _paypalWebUrl = function() { return appjet.config['etherpad.paypal.webUrl'] || "https://www.sandbox.paypal.com/cgi-bin/webscr"; } +function paypalPurchaseUrl(token) { + return (appjet.config['etherpad.paypal.purchaseUrl'] || "https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token=")+token; +} + +function getPurchase(id) { + return sqlobj.selectSingle('billing_purchase', {id: id}); +} + +function getPurchaseForCustomer(customerId) { + return sqlobj.selectSingle('billing_purchase', {customer: customerId}); +} + +function updatePurchase(id, fields) { + sqlobj.updateSingle('billing_purchase', {id: id}, fields); +} + +function getInvoicesForPurchase(purchaseId) { + return sqlobj.selectMulti('billing_invoice', {purchase: purchaseId}); +} + +function getInvoice(id) { + return sqlobj.selectSingle('billing_invoice', {id: id}); +} + +function createInvoice() { + return _newInvoice(); +} + +function updateInvoice(id, fields) { + sqlobj.updateSingle('billing_invoice', {id: id}, fields) +} + +function getTransaction(id) { + return sqlobj.selectSingle('billing_transaction', {id: id}); +} +function getTransactionByExternalId(txnId) { + return sqlobj.selectSingle('billing_transaction', {txnId: txnId}); +} + +function getTransactionsForCustomer(customerId) { + return sqlobj.selectMulti('billing_transaction', {customer: customerId}); +} + +function getPendingTransactionsForCustomer(customerId) { + return sqlobj.selectMulti('billing_transaction', {customer: customerId, status: 'pending'}); +} + +function _updateTransaction(id, fields) { + return sqlobj.updateSingle('billing_transaction', {id: id}, fields); +} + +function getAdjustments(invoiceId) { + return sqlobj.selectMulti('billing_adjustment', {invoice: invoiceId}); +} + +function createSubscription(customer, product, dollars, couponCode) { + var purchaseId = _newPurchase(customer, product, dollarsToCents(dollars), couponCode); + _purchaseActive(purchaseId); + updatePurchase(purchaseId, {type: 'subscription', paidThrough: nextMonth(noon(new Date))}); + return purchaseId; +} + +function _newPurchase(customer, product, cents, couponCode) { + var purchaseId = sqlobj.insert('billing_purchase', { + customer: customer, + product: product, + cost: cents, + coupon: couponCode, + status: 'inactive' + }); + return purchaseId; +} + +function _newInvoice() { + var invoiceId = sqlobj.insert('billing_invoice', { + time: new Date(), + purchase: -1, + amt: 0, + status: 'pending' + }); + return invoiceId; +} + +function _newTransaction(customer, cents) { + var transactionId = sqlobj.insert('billing_transaction', { + customer: customer, + time: new Date(), + amt: cents, + status: 'new' + }); + return transactionId; +} + +function _newAdjustment(transaction, invoice, cents) { + sqlobj.insert('billing_adjustment', { + transaction: transaction, + invoice: invoice, + time: new Date(), + amt: cents + }); +} + +function _transactionSuccess(transaction, txnId, payInfo) { + _updateTransaction(transaction, { + status: 'success', txnId: txnId, time: new Date(), payInfo: payInfo + }); +} + +function _transactionFailure(transaction, txnId) { + _updateTransaction(transaction, { + status: 'failure', txnId: txnId, time: new Date() + }); +} + +function _transactionPending(transaction, txnId) { + _updateTransaction(transaction, { + status: 'pending', txnId: txnId, time: new Date() + }); +} + +function _invoicePaid(invoice) { + updateInvoice(invoice, {status: 'paid'}); +} + +function _purchaseActive(purchase) { + updatePurchase(purchase, {status: 'active'}); +} + +function _purchaseExtend(purchase, monthCount) { + var expiration = getPurchase(purchase).paidThrough; + for (var i = monthCount; i > 0; i--) { + expiration = nextMonth(expiration); + } + // paying your invoice always makes you current. + if (expiration < new Date) { + expiration = nextMonth(new Date); + } + updatePurchase(purchase, {paidThrough: expiration}); +} + +function _doPost(url, body) { + try { + var ret = urlPost(url, body); + } catch (e) { + if (e.javaException) { + net.appjet.oui.exceptionlog.apply(e.javaException); + } + return { error: e }; + } + return { value: ret }; +} + +function _doPaypalNvpPost(properties0) { + return { + status: 'failure', + errorMessage: "Billing has been discontinued. No new services may be purchased." + } + // var properties = { + // USER: _USER(), + // PWD: _PWD(), + // SIGNATURE: _SIGNATURE(), + // VERSION: "56.0" + // } + // eachProperty(properties0, function(k, v) { + // if (v !== undefined) { + // properties[k] = v; + // } + // }) + // log({'type': 'api call', 'value': properties}); + // var ret = _doPost(_paypalApiUrl(), properties); + // if (ret.error) { + // return { + // status: 'failure', + // exception: ret.error.javaException || ret.error, + // errorMessage: ret.error.message + // } + // } + // ret = ret.value; + // var paypalResponse = {}; + // ret.content.split("&").forEach(function(x) { + // var parts = x.split("="); + // paypalResponse[decodeURIComponent(parts[0])] = + // decodeURIComponent(parts[1]); + // }) + // + // var res = paypalResponse; + // log(res) + // if (res.ACK == "Success" || res.ACK == "SuccessWithWarning") { + // return { + // status: 'success', + // response: res + // } + // } else { + // errors = []; + // for (var i = 0; res['L_LONGMESSAGE'+i]; ++i) { + // errors.push(res['L_LONGMESSAGE'+i]); + // } + // return { + // status: 'failure', + // errorMessage: errors.join(", "), + // errorMessages: errors, + // response: res + // } + // } +} + +// status -> 'completion', 'bad', 'redundant', 'possible_fraud' +function handlePaypalNotification() { + var content = (typeof(request.content) == 'string' ? request.content : undefined); + if (! content) { + return new BillingResult('bad', "no content"); + } + log({'type': 'paypal-notification', 'content': content}); + var params = {}; + content.split("&").forEach(function(x) { + var parts = x.split("="); + params[decodeURIComponent(parts[0])] = decodeURIComponent(parts[1]); + }); + var txnId = params.txn_id; + var properties = []; + for(var i in params) { + properties.push(i+" -> "+params[i]); + } + var debugString = properties.join(", "); + log({'type': 'parsed-paypal-notification', 'value': debugString}); + var transaction = getTransactionByExternalId(txnId); + log({'type': 'notification-transaction', 'value': (transaction || {})}); + if (_RECEIVER() != params.receiver_email) { + return new BillingResult('possible_fraud', debugString); + } + if (params.payment_status == "Completed" && transaction && + (transaction.status == 'pending' || transaction.status == 'new')) { + var ret = _doPost(_paypalWebUrl(), "cmd=_notify-validate&"+content); + if (ret.error || ret.value.content != "VERIFIED") { + return new BillingResult('possible_fraud', debugString); + } + var invoice = getInvoice(params.invoice); + if (invoice.amt != dollarsToCents(params.mc_gross)) { + return new BillingResult('possible_fraud', debugString); + } + + sqlcommon.inTransaction(function () { + _transactionSuccess(transaction.id, txnId, "via eCheck"); + _invoicePaid(invoice.id); + _purchaseActive(invoice.purchase); + }); + var purchase = getPurchase(invoice.purchase); + return new BillingResult('completion', debugString, null, + new PurchaseInfo(params.custom, + invoice.id, + transaction.id, + params.txn_id, + purchase.id, + centsToDollars(invoice.amt), + purchase.couponCode, + purchase.time, + undefined)); + } else { + return new BillingResult('redundant', debugString); + } +} + +function _expressCheckoutCustom(invoiceId, transactionId) { + return md5("zimki_sucks"+invoiceId+transactionId); +} + +function PurchaseInfo(custom, invoiceId, transactionId, paypalId, purchaseId, dollars, couponCode, time, token, description) { + this.__defineGetter__("custom", function() { return custom }); + this.__defineGetter__("invoiceId", function() { return invoiceId }); + this.__defineGetter__("transactionId", function() { return transactionId }); + this.__defineGetter__("paypalId", function() { return paypalId }); + this.__defineGetter__("purchaseId", function() { return purchaseId }); + this.__defineGetter__("cost", function() { return dollars }); + this.__defineGetter__("couponCode", function() { return couponCode }); + this.__defineGetter__("time", function() { return time }); + this.__defineGetter__("token", function() { return token }); + this.__defineGetter__("description", function() { return description }); +} + +function PayerInfo(paypalResult) { + this.__defineGetter__("payerId", function() { return paypalResult.response.PAYERID }); + this.__defineGetter__("email", function() { return paypalResult.response.EMAIL }); + this.__defineGetter__("businessName", function() { return paypalResult.response.BUSINESS }); + this.__defineGetter__("nameSalutation", function() { return paypalResult.response.SALUTATION }); + this.__defineGetter__("nameFirst", function() { return paypalResult.response.FIRSTNAME }); + this.__defineGetter__("nameMiddle", function() { return paypalResult.response.MIDDLENAME }); + this.__defineGetter__("nameLast", function() { return paypalResult.response.LASTNAME }); +} + +function BillingResult(status, debug, errorField, purchaseInfo, payerInfo) { + this.__defineGetter__("status", function() { return status }); + this.__defineGetter__("debug", function() { return debug }); + this.__defineGetter__("errorField", function() { return errorField }); + this.__defineGetter__("purchaseInfo", function() { return purchaseInfo }); + this.__defineGetter__("payerInfo", function() { return payerInfo }); +} + +function dollarsToCents(dollars) { + return Math.round(Number(dollars)*100); +} + +function centsToDollars(cents) { + return Math.round(Number(cents)) / 100; +} + +function verifyDollars(dollars) { + return Math.round(Number(dollars)*100)/100; +} + +function beginExpressPurchase(invoiceId, customerId, productId, dollars, couponCode, successUrl, failureUrl, notifyUrl, authorizeOnly) { + var cents = dollarsToCents(dollars); + var time = new Date(); + var purchaseId; + var transactionid; + if (! authorizeOnly) { + try { + sqlcommon.inTransaction(function() { + purchaseId = _newPurchase(customerId, productId, cents, couponCode); + updateInvoice(invoiceId, {purchase: purchaseId, amt: cents}); + transactionId = _newTransaction(customerId, cents); + _newAdjustment(transactionId, invoiceId, cents); + }); + } catch (e) { + if (e instanceof BillingResult) { return e; } + throw e; + } + } + + var paypalResult = + _setExpressCheckout(invoiceId, transactionId, cents, + successUrl, failureUrl, notifyUrl, authorizeOnly); + + if (paypalResult.status == 'success') { + var token = paypalResult.response.TOKEN; + return new BillingResult('success', paypalResult, null, new PurchaseInfo( + _expressCheckoutCustom(invoiceId, transactionId), + invoiceId, + transactionId, + undefined, + purchaseId, + verifyDollars(dollars), + couponCode, + time, + token)); + } else { + return new BillingResult('failure', paypalResult); + } +} + +function _setExpressCheckout(invoiceId, transactionId, cents, successUrl, failureUrl, notifyUrl, authorizeOnly) { + var properties = { + INVNUM: invoiceId, + + METHOD: 'SetExpressCheckout', + CUSTOM: + _expressCheckoutCustom(invoiceId, transactionId), + MAXAMT: centsToDollars(cents), + RETURNURL: successUrl, + CANCELURL: failureUrl, + NOTIFYURL: notifyUrl, + NOSHIPPING: 1, + PAYMENTACTION: (authorizeOnly ? 'Authorization' : 'Sale'), + + AMT: centsToDollars(cents) + } + + return _doPaypalNvpPost(properties); +} + +function continueExpressPurchase(purchaseInfo, authorizeOnly) { + var paypalResult = _getExpressCheckoutDetails(purchaseInfo.token, authorizeOnly) + if (paypalResult.status == 'success') { + if (! authorizeOnly) { + if (paypalResult.response.INVNUM != purchaseInfo.invoiceId) { + return new BillingResult('failure', "invoice id mismatch"); + } + } + if (paypalResult.response.CUSTOM != + _expressCheckoutCustom(purchaseInfo.invoiceId, purchaseInfo.transactionId)) { + return new BillingResult('failure', "custom mismatch"); + } + return new BillingResult('success', paypalResult, null, null, new PayerInfo(paypalResult)); + } else { + return new BillingResult('failure', paypalResult); + } +} + +function _getExpressCheckoutDetails(token, authorizeOnly) { + var properties = { + METHOD: 'GetExpresscheckoutDetails', + TOKEN: token, + } + + return _doPaypalNvpPost(properties); +} + +function completeExpressPurchase(purchaseInfo, payerInfo, notifyUrl, authorizeOnly) { + var paypalResult = _doExpressCheckoutPayment(purchaseInfo, payerInfo, notifyUrl, authorizeOnly); + + if (paypalResult.status == 'success') { + if (paypalResult.response.PAYMENTSTATUS == 'Completed') { + if (! authorizeOnly) { + sqlcommon.inTransaction(function() { + _transactionSuccess(purchaseInfo.transactionId, + paypalResult.response.TRANSACTIONID, "via PayPal"); + _invoicePaid(purchaseInfo.invoiceId); + _purchaseActive(purchaseInfo.purchaseId); + }); + } + return new BillingResult('success', paypalResult); + } else if (paypalResult.response.PAYMENTSTATUS == 'Pending') { + if (! authorizeOnly) { + sqlcommon.inTransaction(function() { + _transactionPending(purchaseInfo.transactionId, + paypalResult.response.TRANSACTIONID); + }); + } + return new BillingResult('pending', paypalResult); + } + } else { + if (! authorizeOnly) { + sqlcommon.inTransaction(function() { + _transactionFailure(purchaseInfo.transactionId, + (paypalResult.response ? + paypalResult.response.TRANSACTIONID || "" : + "")); + }); + } + return new BillingResult('failure', paypalResult); + } +} + +function _doExpressCheckoutPayment(purchaseInfo, payerInfo, notifyUrl, authorizeOnly) { + var properties = { + METHOD: 'DoExpressCheckoutPayment', + TOKEN: purchaseInfo.token, + PAYMENTACTION: (authorizeOnly ? 'Authorization' : 'Sale'), + + NOTIFYURL: notifyUrl, + + PAYERID: payerInfo.payerId, + + AMT: verifyDollars(purchaseInfo.cost), // dollars + INVNUM: purchaseInfo.invoiceId, + CUSTOM: + _expressCheckoutCustom(purchaseInfo.invoiceId, purchaseInfo.transactionId) + } + + return _doPaypalNvpPost(properties); +} + +// which field has error? and, is it not user-correctable? +var _directErrorCodes = { + '10502': ['cardExpiration'], + '10504': ['cardCvv'], + '10505': ['addressStreet', true], + '10508': ['cardExpiration'], + '10510': ['cardType'], + '10512': ['nameFirst'], + '10513': ['nameLast'], + '10519': ['cardNumber'], + '10521': ['cardNumber'], + '10527': ['cardNumber'], + '10534': ['cardNumber', true], + '10535': ['cardNumber'], + '10536': ['invoiceId', true], + '10537': ['addressCountry', true], + '10540': ['addressStreet', true], + '10541': ['cardNumber', true], + '10554': ['address', true], + '10555': ['address', true], + '10556': ['address', true], + '10561': ['address'], + '10562': ['cardExpiration'], + '10563': ['cardExpiration'], + '10565': ['addressCountry'], + '10566': ['cardType'], + '10571': ['cardCvv'], + '10701': ['address'], + '10702': ['addressStreet'], + '10703': ['addressStreet2'], + '10704': ['addressCity'], + '10705': ['addressState'], + '10706': ['addressZip'], + '10707': ['addressCountry'], + '10708': ['address'], + '10709': ['addressStreet'], + '10710': ['addressCity'], + '10711': ['addressState'], + '10712': ['addressZip'], + '10713': ['addressCountry'], + '10714': ['address'], + '10715': ['addressState'], + '10716': ['addressZip'], + '10717': ['addressZip'], + '10718': ['addressCity,addressState'], + '10748': ['cardCvv'], + '10752': ['card'], + '10756': ['address,card'], + '10759': ['cardNumber'], + '10762': ['cardCvv'], + '11611': function(response) { + var avsCode = response.AVSCODE; + var cvv2Match = response.CVV2MATCH; + var errorFields = []; + switch (avsCode) { + case 'N': case 'C': case 'A': case 'B': + case 'R': case 'S': case 'U': case 'G': + case 'I': case 'E': + errorFields.push('address'); + } + switch (cvv2Match) { + case 'N': + errorFields.push('cardCvv'); + } + return [errorFields.join(",")]; + }, + '15004': ['cardCvv'], + '15005': ['cardNumber'], + '15006': ['cardNumber'], + '15007': ['cardNumber'] +} + +function authorizePurchase(payinfo, notifyUrl) { + return directPurchase(undefined, undefined, undefined, 1, undefined, payinfo, notifyUrl, true); +} + +function directPurchase(invoiceId, customerId, productId, dollars, couponCode, payinfo, notifyUrl, authorizeOnly) { + var time = new Date(); + var cents = dollarsToCents(dollars); + + var purchaseId, transactionId; + + if (! authorizeOnly) { + try { + sqlcommon.inTransaction(function() { + purchaseId = _newPurchase(customerId, productId, cents, couponCode); + updateInvoice(invoiceId, {purchase: purchaseId, amt: cents}); + transactionId = _newTransaction(customerId, cents); + _newAdjustment(transactionId, invoiceId, cents); + }); + } catch (e) { + if (e instanceof BillingResult) { return e; } + if (e.javaException || e.rhinoException) { + throw e.javaException || e.rhinoException; + } + throw e; + } + } + + var paypalResult = _doDirectPurchase(invoiceId, cents, payinfo, notifyUrl, authorizeOnly); + + if (paypalResult.status == 'success') { + if (! authorizeOnly) { + sqlcommon.inTransaction(function() { + _transactionSuccess(transactionId, + paypalResult.response.TRANSACTIONID, + payinfo.cardType+" ending in "+payinfo.cardNumber.substr(-4)); + _invoicePaid(invoiceId); + _purchaseActive(purchaseId); + }); + } + return new BillingResult('success', paypalResult, null, new PurchaseInfo( + undefined, + invoiceId, + transactionId, + paypalResult.response.TRANSACTIONID, + purchaseId, + verifyDollars(dollars), + couponCode, + time, + undefined)); + } else { + if (! authorizeOnly) { + sqlcommon.inTransaction(function() { + _transactionFailure(transactionId, + (paypalResult.response ? + paypalResult.response.TRANSACTIONID || "": + "")); + }); + } + return new BillingResult('failure', paypalResult, _getErrorCodes(paypalResult.response)); + } +} + +function _getErrorCodes(paypalResponse) { + var errorCodes = {userErrors: [], permanentErrors: []}; + if (! paypalResponse) { + return undefined; + } + for (var i = 0; paypalResponse['L_ERRORCODE'+i]; ++i) { + var code = paypalResponse['L_ERRORCODE'+i]; + var errorField = _directErrorCodes[code]; + if (typeof(errorField) == 'function') { + errorField = errorField(paypalResponse); + } + if (errorField && errorField[1]) { + Array.prototype.push.apply(errorCodes.permanentErrors, errorField[0].split(",")); + } else if (errorField) { + Array.prototype.push.apply(errorCodes.userErrors, errorField[0].split(",")); + } + } + return errorCodes; +} + +function _doDirectPurchase(invoiceId, cents, payinfo, notifyUrl, authorizeOnly) { + var properties = { + INVNUM: invoiceId, + + METHOD: 'DoDirectPayment', + PAYMENTACTION: (authorizeOnly ? 'Authorization' : 'Sale'), + IPADDRESS: request.clientAddr, + NOTIFYURL: notifyUrl, + + CREDITCARDTYPE: payinfo.cardType, + ACCT: payinfo.cardNumber, + EXPDATE: payinfo.cardExpiration, + CVV2: payinfo.cardCvv, + + SALUTATION: payinfo.nameSalutation, + FIRSTNAME: payinfo.nameFirst, + MIDDLENAME: payinfo.nameMiddle, + LASTNAME: payinfo.nameLast, + SUFFIX: payinfo.nameSuffix, + + STREET: payinfo.addressStreet, + STREET2: payinfo.addressStreet2, + CITY: payinfo.addressCity, + STATE: payinfo.addressState, + COUNTRYCODE: payinfo.addressCountry, + ZIP: payinfo.addressZip, + + AMT: centsToDollars(cents) + } + + return _doPaypalNvpPost(properties); +} + +// function directAuthorization(payInfo, dollars, notifyUrl) { +// var paypalResult = _doDirectPurchase(undefined, dollarsToCents(dollars), payInfo, notifyUrl, true); +// if (paypalResult.status == 'success') { +// return new BillingResult('success', paypalResult, null, new PurchaseInfo( +// undefined, +// undefined, +// paypalResult.response.TRANSACTIONID, +// undefined, +// verifyDollars(dollars), +// undefined, +// undefined, +// undefined)); +// } else { +// return new BillingResult('failure', paypalResult, _getErrorCodes(paypalResult.response)); +// } +// } + +function asyncRecurringPurchase(invoiceId, purchaseId, oldTransactionId, paymentInfo, dollars, monthCount, notifyUrl) { + var time = new Date(); + var cents = dollarsToCents(dollars); + + var purchase, transactionId; + + try { + sqlcommon.inTransaction(function() { + // purchaseId = _newPurchase(customerId, productId, cents, couponCode); + purchase = getPurchase(purchaseId); + updateInvoice(invoiceId, {purchase: purchaseId, amt: cents}); + transactionId = _newTransaction(purchase.customer, cents); + _newAdjustment(transactionId, invoiceId, cents); + }); + } catch (e) { + if (e instanceof BillingResult) { return e; } + if (e.rhinoException) { + throw e.rhinoException; + } + throw e; + } + + // do transaction using previous transaction as template + var paypalResult; + if (cents == 0) { + // can't actually charge nothing, so fake it. + paypalResult = { status: 'success', response: { TRANSACTIONID: null }} + } else { + paypalResult = _doReferenceTransaction(invoiceId, cents, oldTransactionId, notifyUrl); + } + + if (paypalResult.status == 'success') { + sqlcommon.inTransaction(function() { + _transactionSuccess(transactionId, + paypalResult.response.TRANSACTIONID, + paymentInfo); + _invoicePaid(invoiceId); + _purchaseActive(purchaseId); + _purchaseExtend(purchaseId, monthCount); + }); + return new BillingResult('success', paypalResult, null, new PurchaseInfo( + undefined, + invoiceId, + transactionId, + paypalResult.response.TRANSACTIONID, + purchaseId, + verifyDollars(dollars), + undefined, + time, + undefined)); + } else { + sqlcommon.inTransaction(function() { + _transactionFailure(transactionId, + (paypalResult.response ? + paypalResult.response.TRANSACTIONID || "": + "")); + }); + return new BillingResult('failure', paypalResult, _getErrorCodes(paypalResult.response)); + } +} + +function _doReferenceTransaction(invoiceId, cents, transactionId, notifyUrl) { + var properties = { + METHOD: 'DoReferenceTransaction', + PAYMENTACTION: 'Sale', + + REFERENCEID: transactionId, + AMT: centsToDollars(cents), + INVNUM: invoiceId + } + + return _doPaypalNvpPost(properties); +} \ No newline at end of file diff --git a/trunk/etherpad/src/etherpad/billing/fields.js b/trunk/etherpad/src/etherpad/billing/fields.js new file mode 100644 index 0000000..4a307ac --- /dev/null +++ b/trunk/etherpad/src/etherpad/billing/fields.js @@ -0,0 +1,219 @@ +/** + * 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. + */ + + +// Taken from paypal's form +var countryList = [ + ["US", "United States"], + ["AL", "Albania"], + ["DZ", "Algeria"], + ["AD", "Andorra"], + ["AO", "Angola"], + ["AI", "Anguilla"], + ["AG", "Antigua and Barbuda"], + ["AR", "Argentina"], + ["AM", "Armenia"], + ["AW", "Aruba"], + ["AU", "Australia"], + ["AT", "Austria"], + ["AZ", "Azerbaijan Republic"], + ["BS", "Bahamas"], + ["BH", "Bahrain"], + ["BB", "Barbados"], + ["BE", "Belgium"], + ["BZ", "Belize"], + ["BJ", "Benin"], + ["BM", "Bermuda"], + ["BT", "Bhutan"], + ["BO", "Bolivia"], + ["BA", "Bosnia and Herzegovina"], + ["BW", "Botswana"], + ["BR", "Brazil"], + ["VG", "British Virgin Islands"], + ["BN", "Brunei"], + ["BG", "Bulgaria"], + ["BF", "Burkina Faso"], + ["BI", "Burundi"], + ["KH", "Cambodia"], + ["CA", "Canada"], + ["CV", "Cape Verde"], + ["KY", "Cayman Islands"], + ["TD", "Chad"], + ["CL", "Chile"], + ["C2", "China"], + ["CO", "Colombia"], + ["KM", "Comoros"], + ["CK", "Cook Islands"], + ["CR", "Costa Rica"], + ["HR", "Croatia"], + ["CY", "Cyprus"], + ["CZ", "Czech Republic"], + ["CD", "Democratic Republic of the Congo"], + ["DK", "Denmark"], + ["DJ", "Djibouti"], + ["DM", "Dominica"], + ["DO", "Dominican Republic"], + ["EC", "Ecuador"], + ["SV", "El Salvador"], + ["ER", "Eritrea"], + ["EE", "Estonia"], + ["ET", "Ethiopia"], + ["FK", "Falkland Islands"], + ["FO", "Faroe Islands"], + ["FM", "Federated States of Micronesia"], + ["FJ", "Fiji"], + ["FI", "Finland"], + ["FR", "France"], + ["GF", "French Guiana"], + ["PF", "French Polynesia"], + ["GA", "Gabon Republic"], + ["GM", "Gambia"], + ["DE", "Germany"], + ["GI", "Gibraltar"], + ["GR", "Greece"], + ["GL", "Greenland"], + ["GD", "Grenada"], + ["GP", "Guadeloupe"], + ["GT", "Guatemala"], + ["GN", "Guinea"], + ["GW", "Guinea Bissau"], + ["GY", "Guyana"], + ["HN", "Honduras"], + ["HK", "Hong Kong"], + ["HU", "Hungary"], + ["IS", "Iceland"], + ["IN", "India"], + ["ID", "Indonesia"], + ["IE", "Ireland"], + ["IL", "Israel"], + ["IT", "Italy"], + ["JM", "Jamaica"], + ["JP", "Japan"], + ["JO", "Jordan"], + ["KZ", "Kazakhstan"], + ["KE", "Kenya"], + ["KI", "Kiribati"], + ["KW", "Kuwait"], + ["KG", "Kyrgyzstan"], + ["LA", "Laos"], + ["LV", "Latvia"], + ["LS", "Lesotho"], + ["LI", "Liechtenstein"], + ["LT", "Lithuania"], + ["LU", "Luxembourg"], + ["MG", "Madagascar"], + ["MW", "Malawi"], + ["MY", "Malaysia"], + ["MV", "Maldives"], + ["ML", "Mali"], + ["MT", "Malta"], + ["MH", "Marshall Islands"], + ["MQ", "Martinique"], + ["MR", "Mauritania"], + ["MU", "Mauritius"], + ["YT", "Mayotte"], + ["MX", "Mexico"], + ["MN", "Mongolia"], + ["MS", "Montserrat"], + ["MA", "Morocco"], + ["MZ", "Mozambique"], + ["NA", "Namibia"], + ["NR", "Nauru"], + ["NP", "Nepal"], + ["NL", "Netherlands"], + ["AN", "Netherlands Antilles"], + ["NC", "New Caledonia"], + ["NZ", "New Zealand"], + ["NI", "Nicaragua"], + ["NE", "Niger"], + ["NU", "Niue"], + ["NF", "Norfolk Island"], + ["NO", "Norway"], + ["OM", "Oman"], + ["PW", "Palau"], + ["PA", "Panama"], + ["PG", "Papua New Guinea"], + ["PE", "Peru"], + ["PN", "Pitcairn Islands"], + ["PL", "Poland"], + ["PT", "Portugal"], + ["QA", "Qatar"], + ["CG", "Republic of the Congo"], + ["RE", "Reunion"], + ["RO", "Romania"], + ["RU", "Russia"], + ["VC", "Saint Vincent and the Grenadines"], + ["WS", "Samoa"], + ["SM", "San Marino"], + ["ST", "São Tomé and Príncipe"], + ["SA", "Saudi Arabia"], + ["SN", "Senegal"], + ["SC", "Seychelles"], + ["SL", "Sierra Leone"], + ["SG", "Singapore"], + ["SK", "Slovakia"], + ["SI", "Slovenia"], + ["SB", "Solomon Islands"], + ["SO", "Somalia"], + ["ZA", "South Africa"], + ["KR", "South Korea"], + ["ES", "Spain"], + ["LK", "Sri Lanka"], + ["SH", "St. Helena"], + ["KN", "St. Kitts and Nevis"], + ["LC", "St. Lucia"], + ["PM", "St. Pierre and Miquelon"], + ["SR", "Suriname"], + ["SJ", "Svalbard and Jan Mayen Islands"], + ["SZ", "Swaziland"], + ["SE", "Sweden"], + ["CH", "Switzerland"], + ["TW", "Taiwan"], + ["TJ", "Tajikistan"], + ["TZ", "Tanzania"], + ["TH", "Thailand"], + ["TG", "Togo"], + ["TO", "Tonga"], + ["TT", "Trinidad and Tobago"], + ["TN", "Tunisia"], + ["TR", "Turkey"], + ["TM", "Turkmenistan"], + ["TC", "Turks and Caicos Islands"], + ["TV", "Tuvalu"], + ["UG", "Uganda"], + ["UA", "Ukraine"], + ["AE", "United Arab Emirates"], + ["GB", "United Kingdom"], + ["UY", "Uruguay"], + ["VU", "Vanuatu"], + ["VA", "Vatican City State"], + ["VE", "Venezuela"], + ["VN", "Vietnam"], + ["WF", "Wallis and Futuna Islands"], + ["YE", "Yemen"], + ["ZM", "Zambia"], +]; + +var usaStateList = [ + "", "AK", "AL", "AR", "AZ", "CA", "CO", "CT", "DC", "DE", + "FL", "GA", "HI", "IA", "ID", "IL", "IN", "KS", "KY", "LA", + "MA", "MD", "ME", "MI", "MN", "MO", "MS", "MT", "NC", "ND", + "NE", "NH", "NJ", "NM", "NV", "NY", "OH", "OK", "OR", "PA", + "RI", "SC", "SD", "TN", "TX", "UT", "VA", "VT", "WA", "WI", + "WV", "WY", "AA", "AE", "AP", "AS", "FM", "GU", "MH", "MP", + "PR", "PW", "VI" +]; + diff --git a/trunk/etherpad/src/etherpad/billing/team_billing.js b/trunk/etherpad/src/etherpad/billing/team_billing.js new file mode 100644 index 0000000..ae8ae8a --- /dev/null +++ b/trunk/etherpad/src/etherpad/billing/team_billing.js @@ -0,0 +1,422 @@ +/** + * 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. + */ + +import("execution"); +import("exceptionutils"); +import("sqlbase.sqlobj"); +import("sqlbase.sqlcommon.inTransaction"); + +import("etherpad.billing.billing"); +import("etherpad.globals"); +import("etherpad.log"); +import("etherpad.pro.domains"); +import("etherpad.pro.pro_quotas"); +import("etherpad.store.checkout"); +import("etherpad.utils.renderTemplateAsString"); + +jimport("java.lang.System.out.println"); + +function recurringBillingNotifyUrl() { + return ""; +} + +function _billing() { + if (! appjet.cache.billing) { + appjet.cache.billing = {}; + } + return appjet.cache.billing; +} + +function _lpad(str, width, padDigit) { + str = String(str); + padDigit = (padDigit === undefined ? ' ' : padDigit); + var count = width - str.length; + var prepend = [] + for (var i = 0; i < count; ++i) { + prepend.push(padDigit); + } + return prepend.join("")+str; +} + +// utility functions + +function _dayToDateTime(date) { + return [date.getFullYear(), _lpad(date.getMonth()+1, 2, '0'), _lpad(date.getDate(), 2, '0')].join("-"); +} + +function _createInvoice(subscription) { + var maxUsers = getMaxUsers(subscription.customer); + var invoice = inTransaction(function() { + var invoiceId = billing.createInvoice(); + billing.updateInvoice( + invoiceId, + {purchase: subscription.id, + amt: billing.dollarsToCents(calculateSubscriptionCost(maxUsers, subscription.coupon)), + users: maxUsers}); + return billing.getInvoice(invoiceId); + }); + if (invoice) { + resetMaxUsers(subscription.customer) + } + return invoice; +} + +function getExpiredSubscriptions(date) { + return sqlobj.selectMulti('billing_purchase', + {type: 'subscription', + status: 'active', + paidThrough: ['<', _dayToDateTime(date)]}); +} + +function getAllSubscriptions() { + return sqlobj.selectMulti('billing_purchase', {type: 'subscription', status: 'active'}); +} + +function getSubscriptionForCustomer(customerId) { + return sqlobj.selectSingle('billing_purchase', + {type: 'subscription', + customer: customerId}); +} + +function getOrCreateInvoice(subscription) { + return inTransaction(function() { + var existingInvoice = + sqlobj.selectSingle('billing_invoice', + {purchase: subscription.id, status: 'pending'}); + if (existingInvoice) { + return existingInvoice; + } else { + return _createInvoice(subscription); + } + }); +} + +function getLatestPendingInvoice(subscriptionId) { + return sqlobj.selectMulti('billing_invoice', + {purchase: subscriptionId, status: 'pending'}, + {orderBy: '-time', limit: 1})[0]; +} + +function getLatestPaidInvoice(subscriptionId) { + return sqlobj.selectMulti('billing_invoice', + {purchase: subscriptionId, status: 'paid'}, + {orderBy: '-time', limit: 1})[0]; +} + +function pendingTransactions(customer) { + return billing.getPendingTransactionsForCustomer(customer); +} + +function checkPendingTransactions(transactions) { + // XXX: do nothing for now. + return transactions.length > 0; +} + +function getRecurringBillingTransactionId(customerId) { + return sqlobj.selectSingle('billing_payment_info', {customer: customerId}).transaction; +} + +function getRecurringBillingInfo(customerId) { + return sqlobj.selectSingle('billing_payment_info', {customer: customerId}); +} + +function clearRecurringBillingInfo(customerId) { + return sqlobj.deleteRows('billing_payment_info', {customer: customerId}); +} + +function setRecurringBillingInfo(customerId, fullName, email, paymentSummary, expiration, transactionId) { + var info = { + fullname: fullName, + email: email, + paymentsummary: paymentSummary, + expiration: expiration, + transaction: transactionId + } + inTransaction(function() { + if (sqlobj.selectSingle('billing_payment_info', {customer: customerId})) { + sqlobj.update('billing_payment_info', {customer: customerId}, info); + } else { + info.customer = customerId; + sqlobj.insert('billing_payment_info', info); + } + }); +} + +function createSubscription(customerId, couponCode) { + domainCacheClear(customerId); + return inTransaction(function() { + return billing.createSubscription(customerId, 'ONDEMAND', 0, couponCode); + }); +} + +function updateSubscriptionCouponCode(subscriptionId, couponCode) { + billing.updatePurchase(subscriptionId, {coupon: couponCode || ""}); +} + +function subscriptionChargeFailure(subscription, invoice, failureMessage) { + billing.updatePurchase(subscription.id, + {error: failureMessage, status: 'inactive'}); + sendFailureEmail(subscription, invoice); +} + +function subscriptionChargeSuccess(subscription, invoice) { + sendReceiptEmail(subscription, invoice); +} + +function errorFieldsToMessage(errorCodes) { + var prefix = "Your payment information was rejected. Please verify your "; + var errorList = (errorCodes.permanentErrors ? errorCodes.permanentErrors : errorCodes.userErrors); + + return prefix + + errorList.map(function(field) { + return checkout.billingCartFieldMap[field].d; + }).join(", ")+ + "." +} + +function getAllInvoices(customer) { + var purchase = getSubscriptionForCustomer(customer); + if (! purchase) { + return []; + } + return billing.getInvoicesForPurchase(purchase.id); +} + +// scheduled charges + +function attemptCharge(invoice, subscription) { + var billingInfo = getRecurringBillingInfo(subscription.customer); + if (! billingInfo) { + subscriptionChargeFailure(subscription, invoice, "No billing information on file."); + return false; + } + + var result = + billing.asyncRecurringPurchase( + invoice.id, + subscription.id, + billingInfo.transaction, + billingInfo.paymentsummary, + billing.centsToDollars(invoice.amt), + 1, // 1 month only for now + recurringBillingNotifyUrl); + if (result.status == 'success') { + subscriptionChargeSuccess(subscription, invoice); + return true; + } else { + subscriptionChargeFailure(subscription, invoice, errorFieldsToMessage(result.errorField)); + return false; + } +} + +function processSubscription(subscription) { + try { + var hasPendingTransactions = inTransaction(function() { + var transactions = pendingTransactions(subscription.customer); + if (checkPendingTransactions(transactions)) { + billing.log({type: 'pending-transactions-delay', subscription: subscription, transactions: transactions}); + // there are actual pending transactions. wait until tomorrow. + return true; + } else { + return false; + } + }); + if (hasPendingTransactions) { + return; + } + var invoice = getOrCreateInvoice(subscription); + + return attemptCharge(invoice, subscription); + } catch (e) { + log.logException(e); + billing.log({message: "Thrown error", + exception: exceptionutils.getStackTracePlain(e), + subscription: subscription}); + subscriptionChargeFailure(subscription, "Permanent failure. Please confirm your billing information."); + } finally { + domainCacheClear(subscription.customer); + } +} + +function processAllSubscriptions() { + var subs = getExpiredSubscriptions(new Date); + println("processing "+subs.length+" subscriptions."); + subs.forEach(processSubscription); +} + +function _scheduleNextDailyUpdate() { + // Run at 2:22am every day + var now = +(new Date); + var tomorrow = new Date(now + 1000*60*60*24); + tomorrow.setHours(2); + tomorrow.setMinutes(22); + tomorrow.setMilliseconds(222); + log.info("Scheduling next daily billing update for: "+tomorrow.toString()); + var delay = +tomorrow - (+(new Date)); + execution.scheduleTask('billing', "billingDailyUpdate", delay, []); +} + +serverhandlers.tasks.billingDailyUpdate = function() { + return; // do nothing, there's no more billing. + // if (! globals.isProduction()) { return; } + // try { + // processAllSubscriptions(); + // } finally { + // _scheduleNextDailyUpdate(); + // } +} + +function onStartup() { + execution.initTaskThreadPool("billing", 1); + _scheduleNextDailyUpdate(); +} + +// pricing + +function getMaxUsers(customer) { + return pro_quotas.getAccountUsageCount(customer); +} + +function resetMaxUsers(customer) { + pro_quotas.resetAccountUsageCount(customer); +} + +var COST_PER_USER = 8; + +function getCouponValue(couponCode) { + if (couponCode && couponCode.length == 8) { + return sqlobj.selectSingle('checkout_pro_referral', {id: couponCode}); + } +} + +function calculateSubscriptionCost(users, couponId) { + if (users <= globals.PRO_FREE_ACCOUNTS) { + return 0; + } + var coupon = getCouponValue(couponId); + var pctDiscount = (coupon ? coupon.pctDiscount : 0); + var freeUsers = (coupon ? coupon.freeUsers : 0); + + var cost = (users - freeUsers) * COST_PER_USER; + cost = cost * (100-pctDiscount)/100; + + return Math.max(0, cost); +} + +// currentDomainsCache + +function _cache() { + if (! appjet.cache.currentDomainsCache) { + appjet.cache.currentDomainsCache = {}; + } + return appjet.cache.currentDomainsCache; +} + +function domainCacheClear(domain) { + delete _cache()[domain]; +} + +function _domainCacheGetOrUpdate(domain, f) { + if (domain in _cache()) { + return _cache()[domain]; + } + + _cache()[domain] = f(); + return _cache()[domain]; +} + +// external API helpers + +function _getPaidThroughDate(domainId) { + return _domainCacheGetOrUpdate(domainId, function() { + var subscription = getSubscriptionForCustomer(domainId); + if (! subscription) { + return null; + } else { + return subscription.paidThrough; + } + }); +} + +// external API + +var GRACE_PERIOD_DAYS = 10; + +var CURRENT = 0; +var PAST_DUE = 1; +var SUSPENDED = 2; +var NO_BILLING_INFO = 3; + +function getDomainStatus(domainId) { + var paidThrough = _getPaidThroughDate(domainId); + + if (paidThrough == null) { + return NO_BILLING_INFO; + } + if (paidThrough.getTime() > new Date(Date.now()-86400*1000)) { + return CURRENT; + } + // less than GRACE_PERIOD_DAYS have passed since paidThrough date + if (paidThrough.getTime() > Date.now() - GRACE_PERIOD_DAYS*86400*1000) { + return PAST_DUE; + } + return SUSPENDED; +} + +function getDomainDueDate(domainId) { + return _getPaidThroughDate(domainId); +} + +function getDomainSuspensionDate(domainId) { + return new Date(_getPaidThroughDate(domainId).getTime() + GRACE_PERIOD_DAYS*86400*1000); +} + +// emails + +function sendReceiptEmail(subscription, invoice) { + var paymentInfo = getRecurringBillingInfo(subscription.customer); + var coupon = getCouponValue(subscription.coupon); + var emailText = renderTemplateAsString('email/pro_payment_receipt.ejs', { + fullName: paymentInfo.fullname, + paymentSummary: paymentInfo.paymentsummary, + expiration: checkout.formatExpiration(paymentInfo.expiration), + invoiceNumber: invoice.id, + numUsers: invoice.users, + cost: billing.centsToDollars(invoice.amt), + dollars: checkout.dollars, + coupon: coupon, + globals: globals + }); + var address = paymentInfo.email; + checkout.salesEmail(address, "sales@pad.spline.inf.fu-berlin.de", "EtherPad: Receipt for "+paymentInfo.fullname, + {}, emailText); +} + +function sendFailureEmail(subscription, invoice, failureMessage) { + var domain = subscription.customer; + var subDomain = domains.getDomainRecord(domain).subDomain; + var paymentInfo = getRecurringBillingInfo(subscription.customer); + var emailText = renderTemplateAsString('email/pro_payment_failure.ejs', { + fullName: paymentInfo.fullname, + billingError: failureMessage, + balance: "US $"+checkout.dollars(billing.centsToDollars(invoice.amt)), + suspensionDate: checkout.formatDate(new Date(subscription.paidThrough.getTime()+GRACE_PERIOD_DAYS*86400*1000)), + billingAdminLink: "https://"+subDomain+".pad.spline.inf.fu-berlin.de/ep/admin/billing/" + }); + var address = paymentInfo.email; + checkout.salesEmail(address, "sales@pad.spline.inf.fu-berlin.de", "EtherPad: Payment Failure for "+paymentInfo.fullname, + {}, emailText); +} \ No newline at end of file diff --git a/trunk/etherpad/src/etherpad/collab/ace/contentcollector.js b/trunk/etherpad/src/etherpad/collab/ace/contentcollector.js new file mode 100644 index 0000000..5dd4f9c --- /dev/null +++ b/trunk/etherpad/src/etherpad/collab/ace/contentcollector.js @@ -0,0 +1,527 @@ +// DO NOT EDIT THIS FILE, edit infrastructure/ace/www/contentcollector.js +import("etherpad.collab.ace.easysync2.Changeset") + +/** + * 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 _MAX_LIST_LEVEL = 8; + +function sanitizeUnicode(s) { + return s.replace(/[\uffff\ufffe\ufeff\ufdd0-\ufdef\ud800-\udfff]/g, '?'); +} + +function makeContentCollector(collectStyles, browser, apool, domInterface, + className2Author) { + browser = browser || {}; + + var dom = domInterface || { + isNodeText: function(n) { + return (n.nodeType == 3); + }, + nodeTagName: function(n) { + return n.tagName; + }, + nodeValue: function(n) { + return n.nodeValue; + }, + nodeNumChildren: function(n) { + return n.childNodes.length; + }, + nodeChild: function(n, i) { + return n.childNodes.item(i); + }, + nodeProp: function(n, p) { + return n[p]; + }, + nodeAttr: function(n, a) { + return n.getAttribute(a); + }, + optNodeInnerHTML: function(n) { + return n.innerHTML; + } + }; + + var _blockElems = { "div":1, "p":1, "pre":1, "li":1 }; + function isBlockElement(n) { + return !!_blockElems[(dom.nodeTagName(n) || "").toLowerCase()]; + } + function textify(str) { + return sanitizeUnicode( + str.replace(/[\n\r ]/g, ' ').replace(/\xa0/g, ' ').replace(/\t/g, ' ')); + } + function getAssoc(node, name) { + return dom.nodeProp(node, "_magicdom_"+name); + } + + var lines = (function() { + var textArray = []; + var attribsArray = []; + var attribsBuilder = null; + var op = Changeset.newOp('+'); + var self = { + length: function() { return textArray.length; }, + atColumnZero: function() { + return textArray[textArray.length-1] === ""; + }, + startNew: function() { + textArray.push(""); + self.flush(true); + attribsBuilder = Changeset.smartOpAssembler(); + }, + textOfLine: function(i) { return textArray[i]; }, + appendText: function(txt, attrString) { + textArray[textArray.length-1] += txt; + //dmesg(txt+" / "+attrString); + op.attribs = attrString; + op.chars = txt.length; + attribsBuilder.append(op); + }, + textLines: function() { return textArray.slice(); }, + attribLines: function() { return attribsArray; }, + // call flush only when you're done + flush: function(withNewline) { + if (attribsBuilder) { + attribsArray.push(attribsBuilder.toString()); + attribsBuilder = null; + } + } + }; + self.startNew(); + return self; + }()); + var cc = {}; + function _ensureColumnZero(state) { + if (! lines.atColumnZero()) { + _startNewLine(state); + } + } + var selection, startPoint, endPoint; + var selStart = [-1,-1], selEnd = [-1,-1]; + var blockElems = { "div":1, "p":1, "pre":1 }; + function _isEmpty(node, state) { + // consider clean blank lines pasted in IE to be empty + if (dom.nodeNumChildren(node) == 0) return true; + if (dom.nodeNumChildren(node) == 1 && + getAssoc(node, "shouldBeEmpty") && dom.optNodeInnerHTML(node) == " " + && ! getAssoc(node, "unpasted")) { + if (state) { + var child = dom.nodeChild(node, 0); + _reachPoint(child, 0, state); + _reachPoint(child, 1, state); + } + return true; + } + return false; + } + function _pointHere(charsAfter, state) { + var ln = lines.length()-1; + var chr = lines.textOfLine(ln).length; + if (chr == 0 && state.listType && state.listType != 'none') { + chr += 1; // listMarker + } + chr += charsAfter; + return [ln, chr]; + } + function _reachBlockPoint(nd, idx, state) { + if (! dom.isNodeText(nd)) _reachPoint(nd, idx, state); + } + function _reachPoint(nd, idx, state) { + if (startPoint && nd == startPoint.node && startPoint.index == idx) { + selStart = _pointHere(0, state); + } + if (endPoint && nd == endPoint.node && endPoint.index == idx) { + selEnd = _pointHere(0, state); + } + } + function _incrementFlag(state, flagName) { + state.flags[flagName] = (state.flags[flagName] || 0)+1; + } + function _decrementFlag(state, flagName) { + state.flags[flagName]--; + } + function _incrementAttrib(state, attribName) { + if (! state.attribs[attribName]) { + state.attribs[attribName] = 1; + } + else { + state.attribs[attribName]++; + } + _recalcAttribString(state); + } + function _decrementAttrib(state, attribName) { + state.attribs[attribName]--; + _recalcAttribString(state); + } + function _enterList(state, listType) { + var oldListType = state.listType; + state.listLevel = (state.listLevel || 0)+1; + if (listType != 'none') { + state.listNesting = (state.listNesting || 0)+1; + } + state.listType = listType; + _recalcAttribString(state); + return oldListType; + } + function _exitList(state, oldListType) { + state.listLevel--; + if (state.listType != 'none') { + state.listNesting--; + } + state.listType = oldListType; + _recalcAttribString(state); + } + function _enterAuthor(state, author) { + var oldAuthor = state.author; + state.authorLevel = (state.authorLevel || 0)+1; + state.author = author; + _recalcAttribString(state); + return oldAuthor; + } + function _exitAuthor(state, oldAuthor) { + state.authorLevel--; + state.author = oldAuthor; + _recalcAttribString(state); + } + function _recalcAttribString(state) { + var lst = []; + for(var a in state.attribs) { + if (state.attribs[a]) { + lst.push([a,'true']); + } + } + if (state.authorLevel > 0) { + var authorAttrib = ['author', state.author]; + if (apool.putAttrib(authorAttrib, true) >= 0) { + // require that author already be in pool + // (don't add authors from other documents, etc.) + lst.push(authorAttrib); + } + } + state.attribString = Changeset.makeAttribsString('+', lst, apool); + } + function _produceListMarker(state) { + lines.appendText('*', Changeset.makeAttribsString( + '+', [['list', state.listType], + ['insertorder', 'first']], + apool)); + } + function _startNewLine(state) { + if (state) { + var atBeginningOfLine = lines.textOfLine(lines.length()-1).length == 0; + if (atBeginningOfLine && state.listType && state.listType != 'none') { + _produceListMarker(state); + } + } + lines.startNew(); + } + cc.notifySelection = function (sel) { + if (sel) { + selection = sel; + startPoint = selection.startPoint; + endPoint = selection.endPoint; + } + }; + cc.collectContent = function (node, state) { + if (! state) { + state = {flags: {/*name -> nesting counter*/}, + attribs: {/*name -> nesting counter*/}, + attribString: ''}; + } + var isBlock = isBlockElement(node); + var isEmpty = _isEmpty(node, state); + if (isBlock) _ensureColumnZero(state); + var startLine = lines.length()-1; + _reachBlockPoint(node, 0, state); + if (dom.isNodeText(node)) { + var txt = dom.nodeValue(node); + var rest = ''; + var x = 0; // offset into original text + if (txt.length == 0) { + if (startPoint && node == startPoint.node) { + selStart = _pointHere(0, state); + } + if (endPoint && node == endPoint.node) { + selEnd = _pointHere(0, state); + } + } + while (txt.length > 0) { + var consumed = 0; + if (state.flags.preMode) { + var firstLine = txt.split('\n',1)[0]; + consumed = firstLine.length+1; + rest = txt.substring(consumed); + txt = firstLine; + } + else { /* will only run this loop body once */ } + if (startPoint && node == startPoint.node && + startPoint.index-x <= txt.length) { + selStart = _pointHere(startPoint.index-x, state); + } + if (endPoint && node == endPoint.node && + endPoint.index-x <= txt.length) { + selEnd = _pointHere(endPoint.index-x, state); + } + var txt2 = txt; + if ((! state.flags.preMode) && /^[\r\n]*$/.exec(txt)) { + // prevents textnodes containing just "\n" from being significant + // in safari when pasting text, now that we convert them to + // spaces instead of removing them, because in other cases + // removing "\n" from pasted HTML will collapse words together. + txt2 = ""; + } + var atBeginningOfLine = lines.textOfLine(lines.length()-1).length == 0; + if (atBeginningOfLine) { + // newlines in the source mustn't become spaces at beginning of line box + txt2 = txt2.replace(/^\n*/, ''); + } + if (atBeginningOfLine && state.listType && state.listType != 'none') { + _produceListMarker(state); + } + lines.appendText(textify(txt2), state.attribString); + x += consumed; + txt = rest; + if (txt.length > 0) { + _startNewLine(state); + } + } + } + else { + var tname = (dom.nodeTagName(node) || "").toLowerCase(); + if (tname == "br") { + _startNewLine(state); + } + else if (tname == "script" || tname == "style") { + // ignore + } + else if (! isEmpty) { + var styl = dom.nodeAttr(node, "style"); + var cls = dom.nodeProp(node, "className"); + + var isPre = (tname == "pre"); + if ((! isPre) && browser.safari) { + isPre = (styl && /\bwhite-space:\s*pre\b/i.exec(styl)); + } + if (isPre) _incrementFlag(state, 'preMode'); + var attribs = null; + var oldListTypeOrNull = null; + var oldAuthorOrNull = null; + if (collectStyles) { + function doAttrib(na) { + attribs = (attribs || []); + attribs.push(na); + _incrementAttrib(state, na); + } + if (tname == "b" || (styl && /\bfont-weight:\s*bold\b/i.exec(styl)) || + tname == "strong") { + doAttrib("bold"); + } + if (tname == "i" || (styl && /\bfont-style:\s*italic\b/i.exec(styl)) || + tname == "em") { + doAttrib("italic"); + } + if (tname == "u" || (styl && /\btext-decoration:\s*underline\b/i.exec(styl)) || + tname == "ins") { + doAttrib("underline"); + } + if (tname == "s" || (styl && /\btext-decoration:\s*line-through\b/i.exec(styl)) || + tname == "del") { + doAttrib("strikethrough"); + } + if (tname == "h1") { + doAttrib("h1"); + } + if (tname == "h2") { + doAttrib("h2"); + } + if (tname == "h3") { + doAttrib("h3"); + } + if (tname == "h4") { + doAttrib("h4"); + } + if (tname == "h5") { + doAttrib("h5"); + } + if (tname == "h6") { + doAttrib("h6"); + } + if (tname == "ul") { + var type; + var rr = cls && /(?:^| )list-(bullet[12345678])\b/.exec(cls); + type = rr && rr[1] || "bullet"+ + String(Math.min(_MAX_LIST_LEVEL, (state.listNesting||0)+1)); + oldListTypeOrNull = (_enterList(state, type) || 'none'); + } + else if ((tname == "div" || tname == "p") && cls && + cls.match(/(?:^| )ace-line\b/)) { + oldListTypeOrNull = (_enterList(state, type) || 'none'); + } + if (className2Author && cls) { + var classes = cls.match(/\S+/g); + if (classes && classes.length > 0) { + for(var i=0;i=0; i--) { + var oldString = lineStrings[i]; + var oldAttribString = lineAttribs[i]; + if (oldString.length > lineLimit+buffer) { + var newStrings = []; + var newAttribStrings = []; + while (oldString.length > lineLimit) { + //var semiloc = oldString.lastIndexOf(';', lineLimit-1); + //var lengthToTake = (semiloc >= 0 ? (semiloc+1) : lineLimit); + lengthToTake = lineLimit; + newStrings.push(oldString.substring(0, lengthToTake)); + oldString = oldString.substring(lengthToTake); + newAttribStrings.push(Changeset.subattribution(oldAttribString, + 0, lengthToTake)); + oldAttribString = Changeset.subattribution(oldAttribString, + lengthToTake); + } + if (oldString.length > 0) { + newStrings.push(oldString); + newAttribStrings.push(oldAttribString); + } + function fixLineNumber(lineChar) { + if (lineChar[0] < 0) return; + var n = lineChar[0]; + var c = lineChar[1]; + if (n > i) { + n += (newStrings.length-1); + } + else if (n == i) { + var a = 0; + while (c > newStrings[a].length) { + c -= newStrings[a].length; + a++; + } + n += a; + } + lineChar[0] = n; + lineChar[1] = c; + } + fixLineNumber(ss); + fixLineNumber(se); + linesWrapped++; + numLinesAfter += newStrings.length; + + newStrings.unshift(i, 1); + lineStrings.splice.apply(lineStrings, newStrings); + newAttribStrings.unshift(i, 1); + lineAttribs.splice.apply(lineAttribs, newAttribStrings); + } + } + return {linesWrapped:linesWrapped, numLinesAfter:numLinesAfter}; + } + var wrapData = fixLongLines(); + + return { selStart: ss, selEnd: se, linesWrapped: wrapData.linesWrapped, + numLinesAfter: wrapData.numLinesAfter, + lines: lineStrings, lineAttribs: lineAttribs }; + } + + return cc; +} diff --git a/trunk/etherpad/src/etherpad/collab/ace/domline.js b/trunk/etherpad/src/etherpad/collab/ace/domline.js new file mode 100644 index 0000000..de2e7d3 --- /dev/null +++ b/trunk/etherpad/src/etherpad/collab/ace/domline.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 = ''; + } + 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+''; + extraCloseTags = ''+extraCloseTags; + } + if (simpleTags) { + simpleTags.sort(); + extraOpenTags = extraOpenTags+'<'+simpleTags.join('><')+'>'; + simpleTags.reverse(); + extraCloseTags = ''+extraCloseTags; + } + html.push('',extraOpenTags, + perTextNodeProcess(domline.escapeHTML(txt)), + extraCloseTags,''); + } + }; + 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 += ' '; + } + else if (! browser.msie) { + newHTML += '
'; + } + } + 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 = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''' + }; + } + 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, ' '); + } + 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] = ' '; + endOfLine = false; + beforeSpace = true; + } + else if (p.charAt(0) != "<") { + endOfLine = false; + beforeSpace = false; + } + } + // beginning of line is nbsp + for(var i=0;i= 0, "bad old text length"); + assert(this[2] >= 0, "bad new text length"); + assert((this.length % 3) == 0, "bad array length"); + assert(this.length >= 6, "must be at least one strip"); + var numStrips = this.numStrips(); + var oldLen = this[1]; + var newLen = this[2]; + // iterate over the "text strips" + var actualNewLen = 0; + this.eachStrip(function(startIndex, numTaken, newText, i) { + var s = startIndex, t = numTaken, n = newText; + var isFirst = (i == 0); + var isLast = (i == numStrips-1); + assert(t >= 0, "can't take negative number of chars"); + assert(isFirst || t > 0, "all strips but first must take"); + assert((t > 0) || (s == 0), "if first strip doesn't take, must have 0 startIndex"); + assert(s >= 0 && s + t <= oldLen, "bad index: "+this.toString()); + assert(t > 0 || n.length > 0 || (isFirst && isLast), "empty strip must be first and only"); + if (! isLast) { + var s2 = this[3 + i*3 + 3]; // startIndex of following strip + var gap = s2 - (s + t); + assert(gap >= 0, "overlapping or out-of-order strips: "+this.toString()); + assert(gap > 0 || n.length > 0, "touching strips with no added text"); + } + actualNewLen += t + n.length; + }); + assert(newLen == actualNewLen, "calculated new text length doesn't match"); + } + + array.applyToText = function(text) { + assert(text.length == this.oldLen(), "mismatched apply: "+text.length+" / "+this.oldLen()); + var buf = []; + this.eachStrip(function (s, t, n) { + buf.push(text.substr(s, t), n); + }); + return buf.join(''); + } + + function _makeBuilder(oldLen, supportAuthors) { + var C = Changeset(oldLen); + if (supportAuthors) { + _ensureAuthors(C); + } + return C.builder(); + } + + function _getNumInserted(C) { + var numChars = 0; + C.eachStrip(function(s,t,n) { + numChars += n.length; + }); + return numChars; + } + + function _ensureAuthors(C) { + if (! C.authors) { + C.setAuthor(); + } + return C; + } + + array.setAuthor = function(author) { + var C = this; + // authors array has even length >= 2; + // alternates [numChars1, author1, numChars2, author2]; + // all numChars > 0 unless there is exactly one, in which + // case it can be == 0. + C.authors = [_getNumInserted(C), author || '']; + return C; + } + + array.builder = function() { + // normal pattern is Changeset(oldLength).builder().appendOldText(...). ... + // builder methods mutate this! + var C = this; + // OOP style: state in environment + var self; + return self = { + appendNewText: function(str, author) { + C[C.length-1] += str; + C[2] += str.length; + + if (C.authors) { + var a = (author || ''); + var lastAuthorPtr = C.authors.length-1; + var lastAuthorLengthPtr = C.authors.length-2; + if ((!a) || a == C.authors[lastAuthorPtr]) { + C.authors[lastAuthorLengthPtr] += str.length; + } + else if (0 == C.authors[lastAuthorLengthPtr]) { + C.authors[lastAuthorLengthPtr] = str.length; + C.authors[lastAuthorPtr] = (a || C.authors[lastAuthorPtr]); + } + else { + C.authors.push(str.length, a); + } + } + + return self; + }, + appendOldText: function(startIndex, numTaken) { + if (numTaken == 0) return self; + // properties of last strip... + var s = C[C.length-3], t = C[C.length-2], n = C[C.length-1]; + if (t == 0 && n == "") { + // must be empty changeset, one strip that doesn't take old chars or add new ones + C[C.length-3] = startIndex; + C[C.length-2] = numTaken; + } + else if (n == "" && (s+t == startIndex)) { + C[C.length-2] += numTaken; // take more + } + else C.push(startIndex, numTaken, ""); // add a strip + C[2] += numTaken; + C.checkRep(); + return self; + }, + toChangeset: function() { return C; } + }; + } + + array.authorSlicer = function(outputBuilder) { + return _makeAuthorSlicer(this, outputBuilder); + } + + function _makeAuthorSlicer(changesetOrAuthorsIn, builderOut) { + // "builderOut" only needs to support appendNewText + var authors; // considered immutable + if (changesetOrAuthorsIn.isChangeset) { + authors = changesetOrAuthorsIn.authors; + } + else { + authors = changesetOrAuthorsIn; + } + + // OOP style: state in environment + var authorPtr = 0; + var charIndex = 0; + var charWithinAuthor = 0; // 0 <= charWithinAuthor <= authors[authorPtr]; max value iff atEnd + var atEnd = false; + function curAuthor() { return authors[authorPtr+1]; } + function curAuthorWidth() { return authors[authorPtr]; } + function assertNotAtEnd() { assert(! atEnd, "_authorSlicer: can't move past end"); } + function forwardInAuthor(numChars) { + charWithinAuthor += numChars; + charIndex += numChars; + } + function nextAuthor() { + assertNotAtEnd(); + assert(charWithinAuthor == curAuthorWidth(), "_authorSlicer: not at author end"); + charWithinAuthor = 0; + authorPtr += 2; + if (authorPtr == authors.length) { + atEnd = true; + } + } + + var self; + return self = { + skipChars: function(n) { + assert(n >= 0, "_authorSlicer: can't skip negative n"); + if (n == 0) return; + assertNotAtEnd(); + + var leftToSkip = n; + while (leftToSkip > 0) { + var leftInAuthor = curAuthorWidth() - charWithinAuthor; + if (leftToSkip >= leftInAuthor) { + forwardInAuthor(leftInAuthor); + leftToSkip -= leftInAuthor; + nextAuthor(); + } + else { + forwardInAuthor(leftToSkip); + leftToSkip = 0; + } + } + }, + takeChars: function(n, text) { + assert(n >= 0, "_authorSlicer: can't take negative n"); + if (n == 0) return; + assertNotAtEnd(); + assert(n == text.length, "_authorSlicer: bad text length"); + + var textLeft = text; + var leftToTake = n; + while (leftToTake > 0) { + if (curAuthorWidth() > 0 && charWithinAuthor < curAuthorWidth()) { + // at least one char to take from current author + var leftInAuthor = (curAuthorWidth() - charWithinAuthor); + assert(leftInAuthor > 0, "_authorSlicer: should have leftInAuthor > 0"); + var toTake = min(leftInAuthor, leftToTake); + assert(toTake > 0, "_authorSlicer: should have toTake > 0"); + builderOut.appendNewText(textLeft.substring(0, toTake), curAuthor()); + forwardInAuthor(toTake); + leftToTake -= toTake; + textLeft = textLeft.substring(toTake); + } + assert(charWithinAuthor <= curAuthorWidth(), "_authorSlicer: past end of author"); + if (charWithinAuthor == curAuthorWidth()) { + nextAuthor(); + } + } + }, + setBuilder: function(builder) { + builderOut = builder; + } + }; + } + + function _makeSlicer(C, output) { + // C: Changeset, output: builder from _makeBuilder + // C is considered immutable, won't change or be changed + + // OOP style: state in environment + var charIndex = 0; // 0 <= charIndex <= C.newLen(); maximum value iff atEnd + var stripIndex = 0; // 0 <= stripIndex <= C.numStrips(); maximum value iff atEnd + var charWithinStrip = 0; // 0 <= charWithinStrip < curStripWidth() + var atEnd = false; + + var authorSlicer; + if (C.authors) { + authorSlicer = _makeAuthorSlicer(C.authors, output); + } + + var ptr = 3; + function curStartIndex() { return C[ptr]; } + function curNumTaken() { return C[ptr+1]; } + function curNewText() { return C[ptr+2]; } + function curStripWidth() { return curNumTaken() + curNewText().length; } + function assertNotAtEnd() { assert(! atEnd, "_slicer: can't move past changeset end"); } + function forwardInStrip(numChars) { + charWithinStrip += numChars; + charIndex += numChars; + } + function nextStrip() { + assertNotAtEnd(); + assert(charWithinStrip == curStripWidth(), "_slicer: not at strip end"); + charWithinStrip = 0; + stripIndex++; + ptr += 3; + if (stripIndex == C.numStrips()) { + atEnd = true; + } + } + function curNumNewCharsInRange(start, end) { + // takes two indices into the current strip's combined "taken" and "new" + // chars, and returns how many "new" chars are included in the range + assert(start <= end, "_slicer: curNumNewCharsInRange given out-of-order indices"); + var nt = curNumTaken(); + var nn = curNewText().length; + var s = nt; + var e = nt+nn; + if (s < start) s = start; + if (e > end) e = end; + if (e < s) return 0; + return e-s; + } + + var self; + return self = { + skipChars: function (n) { + assert(n >= 0, "_slicer: can't skip negative n"); + if (n == 0) return; + assertNotAtEnd(); + + var leftToSkip = n; + while (leftToSkip > 0) { + var leftInStrip = curStripWidth() - charWithinStrip; + if (leftToSkip >= leftInStrip) { + forwardInStrip(leftInStrip); + + if (authorSlicer) + authorSlicer.skipChars(curNumNewCharsInRange(charWithinStrip, + charWithinStrip + leftInStrip)); + + leftToSkip -= leftInStrip; + nextStrip(); + } + else { + if (authorSlicer) + authorSlicer.skipChars(curNumNewCharsInRange(charWithinStrip, + charWithinStrip + leftToSkip)); + + forwardInStrip(leftToSkip); + leftToSkip = 0; + } + } + }, + takeChars: function (n) { + assert(n >= 0, "_slicer: can't take negative n"); + if (n == 0) return; + assertNotAtEnd(); + + var leftToTake = n; + while (leftToTake > 0) { + if (curNumTaken() > 0 && charWithinStrip < curNumTaken()) { + // at least one char to take from current strip's numTaken + var leftInTaken = (curNumTaken() - charWithinStrip); + assert(leftInTaken > 0, "_slicer: should have leftInTaken > 0"); + var toTake = min(leftInTaken, leftToTake); + assert(toTake > 0, "_slicer: should have toTake > 0"); + output.appendOldText(curStartIndex() + charWithinStrip, toTake); + forwardInStrip(toTake); + leftToTake -= toTake; + } + if (leftToTake > 0 && curNewText().length > 0 && charWithinStrip >= curNumTaken() && + charWithinStrip < curStripWidth()) { + // at least one char to take from current strip's newText + var leftInNewText = (curStripWidth() - charWithinStrip); + assert(leftInNewText > 0, "_slicer: should have leftInNewText > 0"); + var toTake = min(leftInNewText, leftToTake); + assert(toTake > 0, "_slicer: should have toTake > 0"); + var newText = curNewText().substr(charWithinStrip - curNumTaken(), toTake); + if (authorSlicer) { + authorSlicer.takeChars(newText.length, newText); + } + else { + output.appendNewText(newText); + } + forwardInStrip(toTake); + leftToTake -= toTake; + } + assert(charWithinStrip <= curStripWidth(), "_slicer: past end of strip"); + if (charWithinStrip == curStripWidth()) { + nextStrip(); + } + } + }, + skipTo: function(n) { + self.skipChars(n - charIndex); + } + }; + } + + array.slicer = function(outputBuilder) { + return _makeSlicer(this, outputBuilder); + } + + array.compose = function(next) { + assert(next.oldLen() == this.newLen(), "mismatched composition"); + + var builder = _makeBuilder(this.oldLen(), !!(this.authors || next.authors)); + var slicer = _makeSlicer(this, builder); + + var authorSlicer; + if (next.authors) { + authorSlicer = _makeAuthorSlicer(next.authors, builder); + } + + next.eachStrip(function(s, t, n) { + slicer.skipTo(s); + slicer.takeChars(t); + if (authorSlicer) { + authorSlicer.takeChars(n.length, n); + } + else { + builder.appendNewText(n); + } + }, this); + + return builder.toChangeset(); + }; + + array.traverser = function() { + return _makeTraverser(this); + } + + function _makeTraverser(C) { + var s = C[3], t = C[4], n = C[5]; + var nextIndex = 6; + var indexIntoNewText = 0; + + var authorSlicer; + if (C.authors) { + authorSlicer = _makeAuthorSlicer(C.authors, null); + } + + function advanceIfPossible() { + if (t == 0 && n == "" && nextIndex < C.length) { + s = C[nextIndex]; + t = C[nextIndex+1]; + n = C[nextIndex+2]; + nextIndex += 3; + } + } + + var self; + return self = { + numTakenChars: function() { + // if starts with taken characters, then how many, else 0 + return (t > 0) ? t : 0; + }, + numNewChars: function() { + // if starts with new characters, then how many, else 0 + return (t == 0 && n.length > 0) ? n.length : 0; + }, + takenCharsStart: function() { + return (self.numTakenChars() > 0) ? s : 0; + }, + hasMore: function() { + return self.numTakenChars() > 0 || self.numNewChars() > 0; + }, + curIndex: function() { + return indexIntoNewText; + }, + consumeTakenChars: function (x) { + assert(self.numTakenChars() > 0, "_traverser: no taken chars"); + assert(x >= 0 && x <= self.numTakenChars(), "_traverser: bad number of taken chars"); + if (x == 0) return; + if (t == x) { s = 0; t = 0; } + else { s += x; t -= x; } + indexIntoNewText += x; + advanceIfPossible(); + }, + consumeNewChars: function(x) { + return self.appendNewChars(x, null); + }, + appendNewChars: function(x, builder) { + assert(self.numNewChars() > 0, "_traverser: no new chars"); + assert(x >= 0 && x <= self.numNewChars(), "_traverser: bad number of new chars"); + if (x == 0) return ""; + var str = n.substring(0, x); + n = n.substring(x); + indexIntoNewText += x; + advanceIfPossible(); + + if (builder) { + if (authorSlicer) { + authorSlicer.setBuilder(builder); + authorSlicer.takeChars(x, str); + } + else { + builder.appendNewText(str); + } + } + else { + if (authorSlicer) authorSlicer.skipChars(x); + return str; + } + }, + consumeAvailableTakenChars: function() { + return self.consumeTakenChars(self.numTakenChars()); + }, + consumeAvailableNewChars: function() { + return self.consumeNewChars(self.numNewChars()); + }, + appendAvailableNewChars: function(builder) { + return self.appendNewChars(self.numNewChars(), builder); + } + }; + } + + array.follow = function(prev, reverseInsertOrder) { + // prev: Changeset, reverseInsertOrder: boolean + + // A.compose(B.follow(A)) is the merging of Changesets A and B, which operate on the same old text. + // It is always the same as B.compose(A.follow(B, true)). + + assert(prev.oldLen() == this.oldLen(), "mismatched follow: "+prev.oldLen()+"/"+this.oldLen()); + var builder = _makeBuilder(prev.newLen(), !! this.authors); + var a = _makeTraverser(prev); + var b = _makeTraverser(this); + while (a.hasMore() || b.hasMore()) { + if (a.numNewChars() > 0 && ! reverseInsertOrder) { + builder.appendOldText(a.curIndex(), a.numNewChars()); + a.consumeAvailableNewChars(); + } + else if (b.numNewChars() > 0) { + b.appendAvailableNewChars(builder); + } + else if (a.numNewChars() > 0 && reverseInsertOrder) { + builder.appendOldText(a.curIndex(), a.numNewChars()); + a.consumeAvailableNewChars(); + } + else if (! b.hasMore()) a.consumeAvailableTakenChars(); + else if (! a.hasMore()) b.consumeAvailableTakenChars(); + else { + var x = a.takenCharsStart(); + var y = b.takenCharsStart(); + if (x < y) a.consumeTakenChars(min(a.numTakenChars(), y-x)); + else if (y < x) b.consumeTakenChars(min(b.numTakenChars(), x-y)); + else { + var takenByBoth = min(a.numTakenChars(), b.numTakenChars()); + builder.appendOldText(a.curIndex(), takenByBoth); + a.consumeTakenChars(takenByBoth); + b.consumeTakenChars(takenByBoth); + } + } + } + return builder.toChangeset(); + } + + array.encodeToString = function(asBinary) { + var stringDataArray = []; + var numsArray = []; + if (! asBinary) numsArray.push(this[0]); + numsArray.push(this[1], this[2]); + this.eachStrip(function(s, t, n) { + numsArray.push(s, t, n.length); + stringDataArray.push(n); + }, this); + if (! asBinary) { + return numsArray.join(',')+'|'+stringDataArray.join(''); + } + else { + return "A" + Changeset.numberArrayToString(numsArray) + +escapeCrazyUnicode(stringDataArray.join('')); + } + } + + function escapeCrazyUnicode(str) { + return str.replace(/\\/g, '\\\\').replace(/[\ud800-\udfff]/g, function (c) { + return "\\u"+("0000"+c.charCodeAt(0).toString(16)).slice(-4); + }); + } + + array.applyToAttributedText = Changeset.applyToAttributedText; + + function splicesFromChanges(c) { + var splices = []; + // get a list of splices, [startChar, endChar, newText] + var traverser = c.traverser(); + var oldTextLength = c.oldLen(); + var indexIntoOldText = 0; + while (traverser.hasMore() || indexIntoOldText < oldTextLength) { + var newText = ""; + var startChar = indexIntoOldText; + var endChar = indexIntoOldText; + if (traverser.numNewChars() > 0) { + newText = traverser.consumeAvailableNewChars(); + } + if (traverser.hasMore()) { + endChar = traverser.takenCharsStart(); + indexIntoOldText = endChar + traverser.numTakenChars(); + traverser.consumeAvailableTakenChars(); + } + else { + endChar = oldTextLength; + indexIntoOldText = endChar; + } + if (endChar != startChar || newText.length > 0) { + splices.push([startChar, endChar, newText]); + } + } + return splices; + } + + array.toSplices = function() { + return splicesFromChanges(this); + } + + array.characterRangeFollowThis = function(selStartChar, selEndChar, insertionsAfter) { + var changeset = this; + // represent the selection as a changeset that replaces the selection with some finite string. + // Because insertions indicate intention, it doesn't matter what this string is, and even + // if the selectionChangeset is made to "follow" other changes it will still be the only + // insertion. + var selectionChangeset = + Changeset(changeset.oldLen()).builder().appendOldText(0, selStartChar).appendNewText( + "X").appendOldText(selEndChar, changeset.oldLen() - selEndChar).toChangeset(); + var newSelectionChangeset = selectionChangeset.follow(changeset, insertionsAfter); + var selectionSplices = newSelectionChangeset.toSplices(); + function includeChar(i) { + if (! includeChar.calledYet) { + selStartChar = i; + selEndChar = i; + includeChar.calledYet = true; + } + else { + if (i < selStartChar) selStartChar = i; + if (i > selEndChar) selEndChar = i; + } + } + for(var i=0; i TRUNC) a.push("..."); + return a.join(' '); + } + function unescapeCrazyUnicode(str) { + return str.replace(/\\(u....|\\)/g, function(seq) { + if (seq == "\\\\") return "\\"; + return String.fromCharCode(Number("0x"+seq.substring(2))); + }); + } + + var numData, stringData; + var binary = false; + var typ = str.charAt(0); + if (typ == "B" || typ == "A") { + var result = Changeset.numberArrayFromString(str, 1); + numData = result[0]; + stringData = result[1]; + if (typ == "A") { + stringData = unescapeCrazyUnicode(stringData); + } + binary = true; + } + else if (typ == "C") { + var barPosition = str.indexOf('|'); + numData = str.substring(0, barPosition).split(','); + stringData = str.substring(barPosition+1); + } + else { + error("Not a changeset: "+toHex(str)); + } + var stringDataOffset = 0; + var array = []; + var ptr; + if (binary) { + array.push("Changeset", numData[0], numData[1]); + var ptr = 2; + } + else { + array.push(numData[0], Number(numData[1]), Number(numData[2])); + var ptr = 3; + } + while (ptr < numData.length) { + array.push(Number(numData[ptr++]), Number(numData[ptr++])); + var newTextLength = Number(numData[ptr++]); + array.push(stringData.substr(stringDataOffset, newTextLength)); + stringDataOffset += newTextLength; + } + if (stringDataOffset != stringData.length) { + error("Extra character data beyond end of encoded string ("+toHex(str)+")"); + } + return Changeset(array); +}; + +Changeset.numberArrayToString = function(nums) { + var array = []; + function writeNum(n) { + // does not support negative numbers + var twentyEightBit = (n & 0xfffffff); + if (twentyEightBit <= 0x7fff) { + array.push(String.fromCharCode(twentyEightBit)); + } + else { + array.push(String.fromCharCode(0xa000 | (twentyEightBit >> 15), + twentyEightBit & 0x7fff)); + } + } + writeNum(nums.length); + var len = nums.length; + for(var i=0;i 0x7fff) { + if (n >= 0xa000) { + n = (((n & 0x1fff) << 15) | str.charCodeAt(strIndex++)); + } + else { + // legacy format + n = (((n & 0x1fff) << 16) | str.charCodeAt(strIndex++)); + } + } + return n; + } + var len = readNum(); + for(var i=0;i> 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 runMatcher(c) { + // Takes "A" and returns /\u0041+/g . + // Avoid creating new objects unnecessarily by caching matchers + // as properties of this function. + var re = runMatcher[c]; + if (re) return re; + re = runMatcher[c] = new RegExp("\\u"+("0000"+c.charCodeAt(0).toString(16)).slice(-4)+"+", 'g'); + return re; + } + function runLength(str, idx, c) { + var re = runMatcher(c); + re.lastIndex = idx; + var result = re.exec(str); + if (result && result[0]) { + return result[0].length; + } + return 0; + } + + // emptyObj may be a StorableObject + Changeset.initAttributedText = function(emptyObj, initialString, initialAuthor) { + var obj = emptyObj; + obj.authorMap = { 1: (initialAuthor || '') }; + obj.text = (initialString || ''); + obj.attribs = repeatString(chr(1), obj.text.length); + return obj; + }; + Changeset.gcAttributedText = function(atObj) { + // "garbage collect" the list of authors + var removedAuthors = []; + for(var a in atObj.authorMap) { + if (atObj.attribs.indexOf(chr(Number(a))) < 0) { + removedAuthors.push(atObj.authorMap[a]); + delete atObj.authorMap[a]; + } + } + return removedAuthors; + }; + Changeset.cloneAttributedText = function(emptyObj, atObj) { + var obj = emptyObj; + obj.text = atObj.text; // string + if (atObj.attribs) obj.attribs = atObj.attribs; // string + if (atObj.attribs_c) obj.attribs_c = atObj.attribs_c; // string + obj.authorMap = {}; + for(var a in atObj.authorMap) { + obj.authorMap[a] = atObj.authorMap[a]; + } + return obj; + }; + Changeset.applyToAttributedText = function(atObj, C) { + C = (C || this); + var oldText = atObj.text; + var oldAttribs = atObj.attribs; + Changeset._assert(C.isChangeset, "applyToAttributedText: 'this' is not a changeset"); + Changeset._assert(oldText.length == C.oldLen(), + "applyToAttributedText: mismatch "+oldText.length+" / "+C.oldLen()); + var textBuf = []; + var attribsBuf = []; + var authorMap = atObj.authorMap; + function authorId(author) { + for(var a in authorMap) { + if (authorMap[Number(a)] === author) { + return Number(a); + } + } + for(var i=1;i<=60000;i++) { + // don't use "in" because it's currently broken on StorableObjects + if (authorMap[i] === undefined) { + authorMap[i] = author; + return i; + } + } + } + var myBuilder = { appendNewText: function(txt, author) { + // object that acts as a "builder" in that it receives requests from + // authorSlicer to append text attributed to different authors + attribsBuf.push(repeatString(chr(authorId(author)), txt.length)); + } }; + var authorSlicer; + if (C.authors) { + authorSlicer = C.authorSlicer(myBuilder); + } + C.eachStrip(function (s, t, n) { + textBuf.push(oldText.substr(s, t), n); + attribsBuf.push(oldAttribs.substr(s, t)); + if (authorSlicer) { + authorSlicer.takeChars(n.length, n); + } + else { + myBuilder.appendNewText(n, ''); + } + }); + atObj.text = textBuf.join(''); + atObj.attribs = attribsBuf.join(''); + return atObj; + }; + Changeset.getAttributedTextCharAuthor = function(atObj, idx) { + return atObj.authorMap[ord(atObj.attribs.charAt(idx))]; + }; + Changeset.getAttributedTextCharRunLength = function(atObj, idx) { + var c = atObj.attribs.charAt(idx); + return runLength(atObj.attribs, idx, c); + }; + Changeset.eachAuthorInAttributedText = function(atObj, func) { + // call func(author, authorNum) + for(var a in atObj.authorMap) { + if (func(atObj.authorMap[a], Number(a))) break; + } + }; + Changeset.getAttributedTextAuthorByNum = function(atObj, n) { + return atObj.authorMap[n]; + }; + // Compressed attributed text can be cloned, but nothing else until uncompressed!! + Changeset.compressAttributedText = function(atObj) { + // idempotent, mutates the object, returns it + if (atObj.attribs) { + atObj.attribs_c = atObj.attribs.replace(/([\s\S])\1{0,63}/g, function(run) { + return run.charAt(0)+chr(run.length);; + }); + delete atObj.attribs; + } + return atObj; + }; + Changeset.decompressAttributedText = function(atObj) { + // idempotent, mutates the object, returns it + if (atObj.attribs_c) { + atObj.attribs = atObj.attribs_c.replace(/[\s\S][\s\S]/g, function(run) { + return repeatString(run.charAt(0), ord(run.charAt(1))); + }); + delete atObj.attribs_c; + } + return atObj; + }; +})(); diff --git a/trunk/etherpad/src/etherpad/collab/ace/easysync2.js b/trunk/etherpad/src/etherpad/collab/ace/easysync2.js new file mode 100644 index 0000000..0fa1ec4 --- /dev/null +++ b/trunk/etherpad/src/etherpad/collab/ace/easysync2.js @@ -0,0 +1,1968 @@ +// DO NOT EDIT THIS FILE, edit infrastructure/ace/www/easysync2.js +jimport("com.etherpad.Easysync2Support"); + +/** + * 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 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"); + + 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 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= 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= 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 throughIterator"); + var x = '-c*3*4+6|3=az*asdf0*1*2*3+1=1-1+1*0+1=1-1+1|c=c-1'; + assert("throughIterator("+literal(x)+") == "+literal(x)); + })(); + + (function() { + print("> throughSmartAssembler"); + var x = '-c*3*4+6|3=az*asdf0*1*2*3+1=1-1+1*0+1=1-1+1|c=c-1'; + assert("throughSmartAssembler("+literal(x)+") == "+literal(x)); + })(); + + function applyMutations(mu, arrayOfArrays) { + arrayOfArrays.forEach(function (a) { + var result = mu[a[0]].apply(mu, a.slice(1)); + if (a[0] == 'remove' && a[3]) { + assertEqualStrings(a[3], result); + } + }); + } + + function mutationsToChangeset(oldLen, arrayOfArrays) { + var assem = Changeset.smartOpAssembler(); + var op = Changeset.newOp(); + var bank = Changeset.stringAssembler(); + var oldPos = 0; + var newLen = 0; + arrayOfArrays.forEach(function (a) { + if (a[0] == 'skip') { + op.opcode = '='; + op.chars = a[1]; + op.lines = (a[2] || 0); + assem.append(op); + oldPos += op.chars; + newLen += op.chars; + } + else if (a[0] == 'remove') { + op.opcode = '-'; + op.chars = a[1]; + op.lines = (a[2] || 0); + assem.append(op); + oldPos += op.chars; + } + else if (a[0] == 'insert') { + op.opcode = '+'; + bank.append(a[1]); + op.chars = a[1].length; + op.lines = (a[2] || 0); + assem.append(op); + newLen += op.chars; + } + }); + newLen += oldLen - oldPos; + assem.endDocument(); + return Changeset.pack(oldLen, newLen, assem.toString(), + bank.toString()); + } + + function runMutationTest(testId, origLines, muts, correct) { + print("> runMutationTest#"+testId); + var lines = origLines.slice(); + var mu = Changeset.textLinesMutator(lines); + applyMutations(mu, muts); + mu.close(); + assertEqualArrays(correct, lines); + + var inText = origLines.join(''); + var cs = mutationsToChangeset(inText.length, muts); + lines = origLines.slice(); + Changeset.mutateTextLines(cs, lines); + assertEqualArrays(correct, lines); + + var correctText = correct.join(''); + //print(literal(cs)); + var outText = Changeset.applyToText(cs, inText); + assertEqualStrings(correctText, outText); + } + + runMutationTest(1, ["apple\n", "banana\n", "cabbage\n", "duffle\n", "eggplant\n"], + [['remove',1,0,"a"],['insert',"tu"],['remove',1,0,"p"],['skip',4,1],['skip',7,1], + ['insert',"cream\npie\n",2],['skip',2],['insert',"bot"],['insert',"\n",1], + ['insert',"bu"],['skip',3],['remove',3,1,"ge\n"],['remove',6,0,"duffle"]], + ["tuple\n","banana\n","cream\n","pie\n", "cabot\n","bubba\n","eggplant\n"]); + + runMutationTest(2, ["apple\n", "banana\n", "cabbage\n", "duffle\n", "eggplant\n"], + [['remove',1,0,"a"],['remove',1,0,"p"],['insert',"tu"],['skip',11,2], + ['insert',"cream\npie\n",2],['skip',2],['insert',"bot"],['insert',"\n",1], + ['insert',"bu"],['skip',3],['remove',3,1,"ge\n"],['remove',6,0,"duffle"]], + ["tuple\n","banana\n","cream\n","pie\n", "cabot\n","bubba\n","eggplant\n"]); + + runMutationTest(3, ["apple\n", "banana\n", "cabbage\n", "duffle\n", "eggplant\n"], + [['remove',6,1,"apple\n"],['skip',15,2],['skip',6],['remove',1,1,"\n"], + ['remove',8,0,"eggplant"],['skip',1,1]], + ["banana\n","cabbage\n","duffle\n"]); + + runMutationTest(4, ["15\n"], + [['skip',1],['insert',"\n2\n3\n4\n",4],['skip',2,1]], + ["1\n","2\n","3\n","4\n","5\n"]); + + runMutationTest(5, ["1\n","2\n","3\n","4\n","5\n"], + [['skip',1],['remove',7,4,"\n2\n3\n4\n"],['skip',2,1]], + ["15\n"]); + + runMutationTest(6, ["123\n","abc\n","def\n","ghi\n","xyz\n"], + [['insert',"0"],['skip',4,1],['skip',4,1],['remove',8,2,"def\nghi\n"],['skip',4,1]], + ["0123\n", "abc\n", "xyz\n"]); + + runMutationTest(7, ["apple\n", "banana\n", "cabbage\n", "duffle\n", "eggplant\n"], + [['remove',6,1,"apple\n"],['skip',15,2,true],['skip',6,0,true],['remove',1,1,"\n"], + ['remove',8,0,"eggplant"],['skip',1,1,true]], + ["banana\n","cabbage\n","duffle\n"]); + + function poolOrArray(attribs) { + if (attribs.getAttrib) { + return attribs; // it's already an attrib pool + } + else { + // assume it's an array of attrib strings to be split and added + var p = new AttribPool(); + attribs.forEach(function (kv) { p.putAttrib(kv.split(',')); }); + return p; + } + } + + function runApplyToAttributionTest(testId, attribs, cs, inAttr, outCorrect) { + print("> applyToAttribution#"+testId); + var p = poolOrArray(attribs); + var result = Changeset.applyToAttribution( + Changeset.checkRep(cs), inAttr, p); + assertEqualStrings(outCorrect, result); + } + + // turn cactus\n into actusabcd\n + runApplyToAttributionTest(1, ['bold,', 'bold,true'], + "Z:7>3-1*0=1*1=1=3+4$abcd", + "+1*1+1|1+5", "+1*1+1|1+8"); + + // turn "david\ngreenspan\n" into "david\ngreen\n" + runApplyToAttributionTest(2, ['bold,', 'bold,true'], + "Z:g<4*1|1=6*1=5-4$", + "|2+g", "*1|1+6*1+5|1+1"); + + (function() { + print("> mutatorHasMore"); + var lines = ["1\n", "2\n", "3\n", "4\n"]; + var mu; + + mu = Changeset.textLinesMutator(lines); + assert(mu.hasMore()+' == true'); + mu.skip(8,4); + assert(mu.hasMore()+' == false'); + mu.close(); + assert(mu.hasMore()+' == false'); + + // still 1,2,3,4 + mu = Changeset.textLinesMutator(lines); + assert(mu.hasMore()+' == true'); + mu.remove(2,1); + assert(mu.hasMore()+' == true'); + mu.skip(2,1); + assert(mu.hasMore()+' == true'); + mu.skip(2,1); + assert(mu.hasMore()+' == true'); + mu.skip(2,1); + assert(mu.hasMore()+' == false'); + mu.insert("5\n", 1); + assert(mu.hasMore()+' == false'); + mu.close(); + assert(mu.hasMore()+' == false'); + + // 2,3,4,5 now + mu = Changeset.textLinesMutator(lines); + assert(mu.hasMore()+' == true'); + mu.remove(6,3); + assert(mu.hasMore()+' == true'); + mu.remove(2,1); + assert(mu.hasMore()+' == false'); + mu.insert("hello\n", 1); + assert(mu.hasMore()+' == false'); + mu.close(); + assert(mu.hasMore()+' == false'); + + })(); + + function runMutateAttributionTest(testId, attribs, cs, alines, outCorrect) { + print("> runMutateAttributionTest#"+testId); + var p = poolOrArray(attribs); + var alines2 = Array.prototype.slice.call(alines); + var result = Changeset.mutateAttributionLines( + Changeset.checkRep(cs), alines2, p); + assertEqualArrays(outCorrect, alines2); + + print("> runMutateAttributionTest#"+testId+".applyToAttribution"); + function removeQuestionMarks(a) { return a.replace(/\?/g, ''); } + var inMerged = Changeset.joinAttributionLines(alines.map(removeQuestionMarks)); + var correctMerged = Changeset.joinAttributionLines(outCorrect.map(removeQuestionMarks)); + var mergedResult = Changeset.applyToAttribution(cs, inMerged, p); + assertEqualStrings(correctMerged, mergedResult); + } + + // turn 123\n 456\n 789\n into 123\n 456\n 789\n + runMutateAttributionTest(1, ["bold,true"], "Z:c>0|1=4=1*0=1$", ["|1+4", "|1+4", "|1+4"], + ["|1+4", "+1*0+1|1+2", "|1+4"]); + + // make a document bold + runMutateAttributionTest(2, ["bold,true"], "Z:c>0*0|3=c$", ["|1+4", "|1+4", "|1+4"], + ["*0|1+4", "*0|1+4", "*0|1+4"]); + + // clear bold on document + runMutateAttributionTest(3, ["bold,","bold,true"], "Z:c>0*0|3=c$", + ["*1+1+1*1+1|1+1", "+1*1+1|1+2", "*1+1+1*1+1|1+1"], + ["|1+4", "|1+4", "|1+4"]); + + // add a character on line 3 of a document with 5 blank lines, and make sure + // the optimization that skips purely-kept lines is working; if any attribution string + // with a '?' is parsed it will cause an error. + runMutateAttributionTest(4, ['foo,bar','line,1','line,2','line,3','line,4','line,5'], + "Z:5>1|2=2+1$x", + ["?*1|1+1", "?*2|1+1", "*3|1+1", "?*4|1+1", "?*5|1+1"], + ["?*1|1+1", "?*2|1+1", "+1*3|1+1", "?*4|1+1", "?*5|1+1"]); + + var testPoolWithChars = (function() { + var p = new AttribPool(); + p.putAttrib(['char','newline']); + for(var i=1;i<36;i++) { + p.putAttrib(['char',Changeset.numToString(i)]); + } + p.putAttrib(['char','']); + return p; + })(); + + // based on runMutationTest#1 + runMutateAttributionTest(5, testPoolWithChars, + "Z:11>7-2*t+1*u+1|2=b|2+a=2*b+1*o+1*t+1*0|1+1*b+1*u+1=3|1-3-6$"+ + "tucream\npie\nbot\nbu", + ["*a+1*p+2*l+1*e+1*0|1+1", + "*b+1*a+1*n+1*a+1*n+1*a+1*0|1+1", + "*c+1*a+1*b+2*a+1*g+1*e+1*0|1+1", + "*d+1*u+1*f+2*l+1*e+1*0|1+1", + "*e+1*g+2*p+1*l+1*a+1*n+1*t+1*0|1+1"], + ["*t+1*u+1*p+1*l+1*e+1*0|1+1", + "*b+1*a+1*n+1*a+1*n+1*a+1*0|1+1", + "|1+6", + "|1+4", + "*c+1*a+1*b+1*o+1*t+1*0|1+1", + "*b+1*u+1*b+2*a+1*0|1+1", + "*e+1*g+2*p+1*l+1*a+1*n+1*t+1*0|1+1"]); + + // based on runMutationTest#3 + runMutateAttributionTest(6, testPoolWithChars, + "Z:117=1|4+7$\n2\n3\n4\n", + ["*1+1*5|1+2"], + ["*1+1|1+1","|1+2","|1+2","|1+2","*5|1+2"]); + + // based on runMutationTest#5 + runMutateAttributionTest(8, testPoolWithChars, + "Z:a<7=1|4-7$", + ["*1|1+2","*2|1+2","*3|1+2","*4|1+2","*5|1+2"], + ["*1+1*5|1+2"]); + + // based on runMutationTest#6 + runMutateAttributionTest(9, testPoolWithChars, + "Z:k<7*0+1*10|2=8|2-8$0", + ["*1+1*2+1*3+1|1+1","*a+1*b+1*c+1|1+1", + "*d+1*e+1*f+1|1+1","*g+1*h+1*i+1|1+1","?*x+1*y+1*z+1|1+1"], + ["*0+1|1+4", "|1+4", "?*x+1*y+1*z+1|1+1"]); + + runMutateAttributionTest(10, testPoolWithChars, + "Z:6>4=1+1=1+1|1=1+1=1*0+1$abcd", + ["|1+3", "|1+3"], + ["|1+5", "+2*0+1|1+2"]); + + + runMutateAttributionTest(11, testPoolWithChars, + "Z:s>1|1=4=6|1+1$\n", + ["*0|1+4", "*0|1+8", "*0+5|1+1", "*0|1+1", "*0|1+5", "*0|1+1", "*0|1+1", "*0|1+1", "|1+1"], + ["*0|1+4", "*0+6|1+1", "*0|1+2", "*0+5|1+1", "*0|1+1", "*0|1+5", "*0|1+1", "*0|1+1", "*0|1+1", "|1+1"]); + + function randomInlineString(len, rand) { + var assem = Changeset.stringAssembler(); + for(var i=0;i 1) doOp(); + for(var i=0;i<5;i++) doOp(); // do some more (only insertions will happen) + + var outText = outTextAssem.toString()+'\n'; + opAssem.endDocument(); + var cs = Changeset.pack(oldLen, outText.length, opAssem.toString(), charBank.toString()); + Changeset.checkRep(cs); + return [cs, outText]; + } + + function testCompose(randomSeed) { + var rand = new java.util.Random(randomSeed); + print("> testCompose#"+randomSeed); + + var p = new AttribPool(); + + var startText = randomMultiline(10, 20, rand)+'\n'; + + var x1 = randomTestChangeset(startText, rand); + var change1 = x1[0]; + var text1 = x1[1]; + + var x2 = randomTestChangeset(text1, rand); + var change2 = x2[0]; + var text2 = x2[1]; + + var x3 = randomTestChangeset(text2, rand); + var change3 = x3[0]; + var text3 = x3[1]; + + //print(literal(Changeset.toBaseTen(startText))); + //print(literal(Changeset.toBaseTen(change1))); + //print(literal(Changeset.toBaseTen(change2))); + var change12 = Changeset.checkRep(Changeset.compose(change1, change2, p)); + var change23 = Changeset.checkRep(Changeset.compose(change2, change3, p)); + var change123 = Changeset.checkRep(Changeset.compose(change12, change3, p)); + var change123a = Changeset.checkRep(Changeset.compose(change1, change23, p)); + assertEqualStrings(change123, change123a); + + assertEqualStrings(text2, Changeset.applyToText(change12, startText)); + assertEqualStrings(text3, Changeset.applyToText(change23, text1)); + assertEqualStrings(text3, Changeset.applyToText(change123, startText)); + } + + for(var i=0;i<30;i++) testCompose(i); + + (function simpleComposeAttributesTest() { + print("> simpleComposeAttributesTest"); + var p = new AttribPool(); + p.putAttrib(['bold','']); + p.putAttrib(['bold','true']); + var cs1 = Changeset.checkRep("Z:2>1*1+1*1=1$x"); + var cs2 = Changeset.checkRep("Z:3>0*0|1=3$"); + var cs12 = Changeset.checkRep(Changeset.compose(cs1, cs2, p)); + assertEqualStrings("Z:2>1+1*0|1=2$x", cs12); + })(); + + (function followAttributesTest() { + var p = new AttribPool(); + p.putAttrib(['x','']); + p.putAttrib(['x','abc']); + p.putAttrib(['x','def']); + p.putAttrib(['y','']); + p.putAttrib(['y','abc']); + p.putAttrib(['y','def']); + + function testFollow(a, b, afb, bfa, merge) { + assertEqualStrings(afb, Changeset.followAttributes(a, b, p)); + assertEqualStrings(bfa, Changeset.followAttributes(b, a, p)); + assertEqualStrings(merge, Changeset.composeAttributes(a, afb, true, p)); + assertEqualStrings(merge, Changeset.composeAttributes(b, bfa, true, p)); + } + + testFollow('', '', '', '', ''); + testFollow('*0', '', '', '*0', '*0'); + testFollow('*0', '*0', '', '', '*0'); + testFollow('*0', '*1', '', '*0', '*0'); + testFollow('*1', '*2', '', '*1', '*1'); + testFollow('*0*1', '', '', '*0*1', '*0*1'); + testFollow('*0*4', '*2*3', '*3', '*0', '*0*3'); + testFollow('*0*4', '*2', '', '*0*4', '*0*4'); + })(); + + function testFollow(randomSeed) { + var rand = new java.util.Random(randomSeed + 1000); + print("> testFollow#"+randomSeed); + + var p = new AttribPool(); + + var startText = randomMultiline(10, 20, rand)+'\n'; + + var cs1 = randomTestChangeset(startText, rand)[0]; + var cs2 = randomTestChangeset(startText, rand)[0]; + + var afb = Changeset.checkRep(Changeset.follow(cs1, cs2, false, p)); + var bfa = Changeset.checkRep(Changeset.follow(cs2, cs1, true, p)); + + var merge1 = Changeset.checkRep(Changeset.compose(cs1, afb)); + var merge2 = Changeset.checkRep(Changeset.compose(cs2, bfa)); + + assertEqualStrings(merge1, merge2); + } + + for(var i=0;i<30;i++) testFollow(i); + + function testSplitJoinAttributionLines(randomSeed) { + var rand = new java.util.Random(randomSeed + 2000); + print("> testSplitJoinAttributionLines#"+randomSeed); + + var doc = randomMultiline(10, 20, rand)+'\n'; + + function stringToOps(str) { + var assem = Changeset.mergingOpAssembler(); + var o = Changeset.newOp('+'); + o.chars = 1; + for(var i=0;i testMoveOpsToNewPool"); + + var pool1 = new AttribPool(); + var pool2 = new AttribPool(); + + pool1.putAttrib(['baz','qux']); + pool1.putAttrib(['foo','bar']); + + pool2.putAttrib(['foo','bar']); + + assertEqualStrings(Changeset.moveOpsToNewPool('Z:1>2*1+1*0+1$ab', pool1, pool2), 'Z:1>2*0+1*1+1$ab'); + assertEqualStrings(Changeset.moveOpsToNewPool('*1+1*0+1', pool1, pool2), '*0+1*1+1'); + })(); + + + (function testMakeSplice() { + print("> testMakeSplice"); + + var t = "a\nb\nc\n"; + var t2 = Changeset.applyToText(Changeset.makeSplice(t, 5, 0, "def"), t); + assertEqualStrings("a\nb\ncdef\n", t2); + + })(); + + (function testToSplices() { + print("> testToSplices"); + + var cs = Changeset.checkRep('Z:z>9*0=1=4-3+9=1|1-4-4+1*0+a$123456789abcdefghijk'); + var correctSplices = [[5, 8, "123456789"], [9, 17, "abcdefghijk"]]; + assertEqualArrays(correctSplices, Changeset.toSplices(cs)); + })(); + + function testCharacterRangeFollow(testId, cs, oldRange, insertionsAfter, correctNewRange) { + print("> testCharacterRangeFollow#"+testId); + + var cs = Changeset.checkRep(cs); + assertEqualArrays(correctNewRange, Changeset.characterRangeFollow(cs, oldRange[0], oldRange[1], + insertionsAfter)); + + } + + testCharacterRangeFollow(1, 'Z:z>9*0=1=4-3+9=1|1-4-4+1*0+a$123456789abcdefghijk', + [7, 10], false, [14, 15]); + testCharacterRangeFollow(2, "Z:bc<6|x=b4|2-6$", [400, 407], false, [400, 401]); + testCharacterRangeFollow(3, "Z:4>0-3+3$abc", [0,3], false, [3,3]); + testCharacterRangeFollow(4, "Z:4>0-3+3$abc", [0,3], true, [0,0]); + testCharacterRangeFollow(5, "Z:5>1+1=1-3+3$abcd", [1,4], false, [5,5]); + testCharacterRangeFollow(6, "Z:5>1+1=1-3+3$abcd", [1,4], true, [2,2]); + testCharacterRangeFollow(7, "Z:5>1+1=1-3+3$abcd", [0,6], false, [1,7]); + testCharacterRangeFollow(8, "Z:5>1+1=1-3+3$abcd", [0,3], false, [1,2]); + testCharacterRangeFollow(9, "Z:5>1+1=1-3+3$abcd", [2,5], false, [5,6]); + testCharacterRangeFollow(10, "Z:2>1+1$a", [0,0], false, [1,1]); + testCharacterRangeFollow(11, "Z:2>1+1$a", [0,0], true, [0,0]); + + (function testOpAttributeValue() { + print("> testOpAttributeValue"); + + var p = new AttribPool(); + p.putAttrib(['name','david']); + p.putAttrib(['color','green']); + + assertEqualStrings("david", Changeset.opAttributeValue(Changeset.stringOp('*0*1+1'), 'name', p)); + assertEqualStrings("david", Changeset.opAttributeValue(Changeset.stringOp('*0+1'), 'name', p)); + assertEqualStrings("", Changeset.opAttributeValue(Changeset.stringOp('*1+1'), 'name', p)); + assertEqualStrings("", Changeset.opAttributeValue(Changeset.stringOp('+1'), 'name', p)); + assertEqualStrings("green", Changeset.opAttributeValue(Changeset.stringOp('*0*1+1'), 'color', p)); + assertEqualStrings("green", Changeset.opAttributeValue(Changeset.stringOp('*1+1'), 'color', p)); + assertEqualStrings("", Changeset.opAttributeValue(Changeset.stringOp('*0+1'), 'color', p)); + assertEqualStrings("", Changeset.opAttributeValue(Changeset.stringOp('+1'), 'color', p)); + })(); + + function testAppendATextToAssembler(testId, atext, correctOps) { + print("> testAppendATextToAssembler#"+testId); + + var assem = Changeset.smartOpAssembler(); + Changeset.appendATextToAssembler(atext, assem); + assertEqualStrings(correctOps, assem.toString()); + } + + testAppendATextToAssembler(1, {text:"\n", attribs:"|1+1"}, ""); + testAppendATextToAssembler(2, {text:"\n\n", attribs:"|2+2"}, "|1+1"); + testAppendATextToAssembler(3, {text:"\n\n", attribs:"*x|2+2"}, "*x|1+1"); + testAppendATextToAssembler(4, {text:"\n\n", attribs:"*x|1+1|1+1"}, "*x|1+1"); + testAppendATextToAssembler(5, {text:"foo\n", attribs:"|1+4"}, "+3"); + testAppendATextToAssembler(6, {text:"\nfoo\n", attribs:"|2+5"}, "|1+1+3"); + testAppendATextToAssembler(7, {text:"\nfoo\n", attribs:"*x|2+5"}, "*x|1+1*x+3"); + testAppendATextToAssembler(8, {text:"\n\n\nfoo\n", attribs:"|2+2*x|2+5"}, "|2+2*x|1+1*x+3"); + + function testMakeAttribsString(testId, pool, opcode, attribs, correctString) { + print("> testMakeAttribsString#"+testId); + + var p = poolOrArray(pool); + var str = Changeset.makeAttribsString(opcode, attribs, p); + assertEqualStrings(correctString, str); + } + + testMakeAttribsString(1, ['bold,'], '+', [['bold','']], ''); + testMakeAttribsString(2, ['abc,def','bold,'], '=', [['bold','']], '*1'); + testMakeAttribsString(3, ['abc,def','bold,true'], '+', [['abc','def'],['bold','true']], '*0*1'); + testMakeAttribsString(4, ['abc,def','bold,true'], '+', [['bold','true'],['abc','def']], '*0*1'); + + function testSubattribution(testId, astr, start, end, correctOutput) { + print("> testSubattribution#"+testId); + + var str = Changeset.subattribution(astr, start, end); + assertEqualStrings(correctOutput, str); + } + + testSubattribution(1, "+1", 0, 0, ""); + testSubattribution(2, "+1", 0, 1, "+1"); + testSubattribution(3, "+1", 0, undefined, "+1"); + testSubattribution(4, "|1+1", 0, 0, ""); + testSubattribution(5, "|1+1", 0, 1, "|1+1"); + testSubattribution(6, "|1+1", 0, undefined, "|1+1"); + testSubattribution(7, "*0+1", 0, 0, ""); + testSubattribution(8, "*0+1", 0, 1, "*0+1"); + testSubattribution(9, "*0+1", 0, undefined, "*0+1"); + testSubattribution(10, "*0|1+1", 0, 0, ""); + testSubattribution(11, "*0|1+1", 0, 1, "*0|1+1"); + testSubattribution(12, "*0|1+1", 0, undefined, "*0|1+1"); + testSubattribution(13, "*0+2+1*1+3", 0, 1, "*0+1"); + testSubattribution(14, "*0+2+1*1+3", 0, 2, "*0+2"); + testSubattribution(15, "*0+2+1*1+3", 0, 3, "*0+2+1"); + testSubattribution(16, "*0+2+1*1+3", 0, 4, "*0+2+1*1+1"); + testSubattribution(17, "*0+2+1*1+3", 0, 5, "*0+2+1*1+2"); + testSubattribution(18, "*0+2+1*1+3", 0, 6, "*0+2+1*1+3"); + testSubattribution(19, "*0+2+1*1+3", 0, 7, "*0+2+1*1+3"); + testSubattribution(20, "*0+2+1*1+3", 0, undefined, "*0+2+1*1+3"); + testSubattribution(21, "*0+2+1*1+3", 1, undefined, "*0+1+1*1+3"); + testSubattribution(22, "*0+2+1*1+3", 2, undefined, "+1*1+3"); + testSubattribution(23, "*0+2+1*1+3", 3, undefined, "*1+3"); + testSubattribution(24, "*0+2+1*1+3", 4, undefined, "*1+2"); + testSubattribution(25, "*0+2+1*1+3", 5, undefined, "*1+1"); + testSubattribution(26, "*0+2+1*1+3", 6, undefined, ""); + testSubattribution(27, "*0+2+1*1|1+3", 0, 1, "*0+1"); + testSubattribution(28, "*0+2+1*1|1+3", 0, 2, "*0+2"); + testSubattribution(29, "*0+2+1*1|1+3", 0, 3, "*0+2+1"); + testSubattribution(30, "*0+2+1*1|1+3", 0, 4, "*0+2+1*1+1"); + testSubattribution(31, "*0+2+1*1|1+3", 0, 5, "*0+2+1*1+2"); + testSubattribution(32, "*0+2+1*1|1+3", 0, 6, "*0+2+1*1|1+3"); + testSubattribution(33, "*0+2+1*1|1+3", 0, 7, "*0+2+1*1|1+3"); + testSubattribution(34, "*0+2+1*1|1+3", 0, undefined, "*0+2+1*1|1+3"); + testSubattribution(35, "*0+2+1*1|1+3", 1, undefined, "*0+1+1*1|1+3"); + testSubattribution(36, "*0+2+1*1|1+3", 2, undefined, "+1*1|1+3"); + testSubattribution(37, "*0+2+1*1|1+3", 3, undefined, "*1|1+3"); + testSubattribution(38, "*0+2+1*1|1+3", 4, undefined, "*1|1+2"); + testSubattribution(39, "*0+2+1*1|1+3", 5, undefined, "*1|1+1"); + testSubattribution(40, "*0+2+1*1|1+3", 1, 5, "*0+1+1*1+2"); + testSubattribution(41, "*0+2+1*1|1+3", 2, 6, "+1*1|1+3"); + testSubattribution(42, "*0+2+1*1+3", 2, 6, "+1*1+3"); + + function testFilterAttribNumbers(testId, cs, filter, correctOutput) { + print("> testFilterAttribNumbers#"+testId); + + var str = Changeset.filterAttribNumbers(cs, filter); + assertEqualStrings(correctOutput, str); + } + + testFilterAttribNumbers(1, "*0*1+1+2+3*1+4*2+5*0*2*1*b*c+6", + function(n) { return (n%2) == 0; }, + "*0+1+2+3+4*2+5*0*2*c+6"); + testFilterAttribNumbers(2, "*0*1+1+2+3*1+4*2+5*0*2*1*b*c+6", + function(n) { return (n%2) == 1; }, + "*1+1+2+3*1+4+5*1*b+6"); + + function testInverse(testId, cs, lines, alines, pool, correctOutput) { + print("> testInverse#"+testId); + + pool = poolOrArray(pool); + var str = Changeset.inverse(Changeset.checkRep(cs), lines, alines, pool); + assertEqualStrings(correctOutput, str); + } + + // take "FFFFTTTTT" and apply "-FT--FFTT", the inverse of which is "--F--TT--" + testInverse(1, "Z:9>0=1*0=1*1=1=2*0=2*1|1=2$", null, ["+4*1+5"], ['bold,','bold,true'], + "Z:9>0=2*0=1=2*1=2$"); + + function testMutateTextLines(testId, cs, lines, correctLines) { + print("> testMutateTextLines#"+testId); + + var a = lines.slice(); + Changeset.mutateTextLines(cs, a); + assertEqualArrays(correctLines, a); + } + + testMutateTextLines(1, "Z:4<1|1-2-1|1+1+1$\nc", ["a\n", "b\n"], ["\n", "c\n"]); + testMutateTextLines(2, "Z:4>0|1-2-1|2+3$\nc\n", ["a\n", "b\n"], ["\n", "c\n", "\n"]); + + function testInverseRandom(randomSeed) { + var rand = new java.util.Random(randomSeed + 3000); + print("> testInverseRandom#"+randomSeed); + + var p = poolOrArray(['apple,','apple,true','banana,','banana,true']); + + var startText = randomMultiline(10, 20, rand)+'\n'; + var alines = Changeset.splitAttributionLines(Changeset.makeAttribution(startText), startText); + var lines = startText.slice(0,-1).split('\n').map(function(s) { return s+'\n'; }); + + var stylifier = randomTestChangeset(startText, rand, true)[0]; + + //print(alines.join('\n')); + Changeset.mutateAttributionLines(stylifier, alines, p); + //print(stylifier); + //print(alines.join('\n')); + Changeset.mutateTextLines(stylifier, lines); + + var changeset = randomTestChangeset(lines.join(''), rand, true)[0]; + var inverseChangeset = Changeset.inverse(changeset, lines, alines, p); + + var origLines = lines.slice(); + var origALines = alines.slice(); + + Changeset.mutateTextLines(changeset, lines); + Changeset.mutateAttributionLines(changeset, alines, p); + //print(origALines.join('\n')); + //print(changeset); + //print(inverseChangeset); + //print(origLines.map(function(s) { return '1: '+s.slice(0,-1); }).join('\n')); + //print(lines.map(function(s) { return '2: '+s.slice(0,-1); }).join('\n')); + //print(alines.join('\n')); + Changeset.mutateTextLines(inverseChangeset, lines); + Changeset.mutateAttributionLines(inverseChangeset, alines, p); + //print(lines.map(function(s) { return '3: '+s.slice(0,-1); }).join('\n')); + + assertEqualArrays(origLines, lines); + assertEqualArrays(origALines, alines); + } + + for(var i=0;i<30;i++) testInverseRandom(i); +} \ No newline at end of file diff --git a/trunk/etherpad/src/etherpad/collab/ace/linestylefilter.js b/trunk/etherpad/src/etherpad/collab/ace/linestylefilter.js new file mode 100644 index 0000000..c7f79a5 --- /dev/null +++ b/trunk/etherpad/src/etherpad/collab/ace/linestylefilter.js @@ -0,0 +1,253 @@ +// DO NOT EDIT THIS FILE, edit infrastructure/ace/www/linestylefilter.js +import("etherpad.collab.ace.easysync2.Changeset"); + +/** + * 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= 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/etherpad/collab/collab_server.js b/trunk/etherpad/src/etherpad/collab/collab_server.js new file mode 100644 index 0000000..78c9921 --- /dev/null +++ b/trunk/etherpad/src/etherpad/collab/collab_server.js @@ -0,0 +1,778 @@ +/** + * 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. + */ + +import("comet"); +import("ejs"); +import("etherpad.collab.ace.easysync2.{AttribPool,Changeset}"); +import("etherpad.log"); +import("etherpad.pad.activepads"); +import("etherpad.pad.model"); +import("etherpad.pad.padutils"); +import("etherpad.pad.padusers"); +import("etherpad.pad.padevents"); +import("etherpad.pad.pad_security"); +import("etherpad.pro.pro_padmeta"); +import("fastJSON"); +import("fileutils.readFile"); +import("jsutils.{eachProperty,keys}"); +import("etherpad.collab.collabroom_server.*"); +import("etherpad.collab.readonly_server"); +jimport("java.util.concurrent.ConcurrentHashMap"); + +var PADPAGE_ROOMTYPE = "padpage"; + +function onStartup() { + +} + +function _padIdToRoom(padId) { + return "padpage/"+padId; +} + +function _roomToPadId(roomName) { + return roomName.substring(roomName.indexOf("/")+1); +} + +function removeFromMemory(pad) { + // notification so we can free stuff + if (getNumConnections(pad) == 0) { + var tempObj = pad.tempObj(); + tempObj.revisionSockets = {}; + } +} + +function _getPadConnections(pad) { + return getRoomConnections(_padIdToRoom(pad.getId())); +} + +function guestKnock(globalPadId, guestId, displayName) { + var askedSomeone = false; + + // requires that we somehow have permission on this pad + model.accessPadGlobal(globalPadId, function(pad) { + var connections = _getPadConnections(pad); + connections.forEach(function(connection) { + // only send to pro users + if (! padusers.isGuest(connection.data.userInfo.userId)) { + askedSomeone = true; + var msg = { type: "SERVER_MESSAGE", + payload: { type: 'GUEST_PROMPT', + userId: guestId, + displayName: displayName } }; + sendMessage(connection.connectionId, msg); + } + }); + }); + + if (! askedSomeone) { + pad_security.answerKnock(guestId, globalPadId, "denied"); + } +} + +function _verifyUserId(userId) { + var result; + if (padusers.isGuest(userId)) { + // allow cookie-verified guest even if user has signed in + result = (userId == padusers.getGuestUserId()); + } + else { + result = (userId == padusers.getUserId()); + } + return result; +} + +function _checkChangesetAndPool(cs, pool) { + Changeset.checkRep(cs); + Changeset.eachAttribNumber(cs, function(n) { + if (! pool.getAttrib(n)) { + throw new Error("Attribute pool is missing attribute "+n+" for changeset "+cs); + } + }); +} + +function _doWarn(str) { + log.warn(appjet.executionId+": "+str); +} + +function _doInfo(str) { + log.info(appjet.executionId+": "+str); +} + +function _getPadRevisionSockets(pad) { + var revisionSockets = pad.tempObj().revisionSockets; + if (! revisionSockets) { + revisionSockets = {}; // rev# -> socket id + pad.tempObj().revisionSockets = revisionSockets; + } + return revisionSockets; +} + +function applyUserChanges(pad, baseRev, changeset, optSocketId, optAuthor) { + // changeset must be already adapted to the server's apool + + var apool = pad.pool(); + var r = baseRev; + while (r < pad.getHeadRevisionNumber()) { + r++; + var c = pad.getRevisionChangeset(r); + changeset = Changeset.follow(c, changeset, false, apool); + } + + var prevText = pad.text(); + if (Changeset.oldLen(changeset) != prevText.length) { + _doWarn("Can't apply USER_CHANGES "+changeset+" to document of length "+ + prevText.length); + return; + } + + var thisAuthor = ''; + if (optSocketId) { + var connectionId = getSocketConnectionId(optSocketId); + if (connectionId) { + var connection = getConnection(connectionId); + if (connection) { + thisAuthor = connection.data.userInfo.userId; + } + } + } + if (optAuthor) { + thisAuthor = optAuthor; + } + + pad.appendRevision(changeset, thisAuthor); + var newRev = pad.getHeadRevisionNumber(); + if (optSocketId) { + _getPadRevisionSockets(pad)[newRev] = optSocketId; + } + + var correctionChangeset = _correctMarkersInPad(pad.atext(), pad.pool()); + if (correctionChangeset) { + pad.appendRevision(correctionChangeset); + } + + ///// make document end in blank line if it doesn't: + if (pad.text().lastIndexOf("\n\n") != pad.text().length-2) { + var nlChangeset = Changeset.makeSplice( + pad.text(), pad.text().length-1, 0, "\n"); + pad.appendRevision(nlChangeset); + } + + updatePadClients(pad); + + activepads.touch(pad.getId()); + padevents.onEditPad(pad, thisAuthor); +} + +function updateClient(pad, connectionId) { + var conn = getConnection(connectionId); + if (! conn) { + return; + } + var lastRev = conn.data.lastRev; + var userId = conn.data.userInfo.userId; + var socketId = conn.socketId; + while (lastRev < pad.getHeadRevisionNumber()) { + var r = ++lastRev; + var author = pad.getRevisionAuthor(r); + var revisionSockets = _getPadRevisionSockets(pad); + if (revisionSockets[r] === socketId) { + sendMessage(connectionId, {type:"ACCEPT_COMMIT", newRev:r}); + } + else { + var forWire = Changeset.prepareForWire(pad.getRevisionChangeset(r), pad.pool()); + var msg = {type:"NEW_CHANGES", newRev:r, + changeset: forWire.translated, + apool: forWire.pool, + author: author}; + sendMessage(connectionId, msg); + } + } + conn.data.lastRev = pad.getHeadRevisionNumber(); + updateRoomConnectionData(connectionId, conn.data); +} + +function updatePadClients(pad) { + _getPadConnections(pad).forEach(function(connection) { + updateClient(pad, connection.connectionId); + }); + + readonly_server.updatePadClients(pad); +} + +function applyMissedChanges(pad, missedChanges) { + var userInfo = missedChanges.userInfo; + var baseRev = missedChanges.baseRev; + var committedChangeset = missedChanges.committedChangeset; // may be falsy + var furtherChangeset = missedChanges.furtherChangeset; // may be falsy + var apool = pad.pool(); + + if (! _verifyUserId(userInfo.userId)) { + return; + } + + if (committedChangeset) { + var wireApool1 = (new AttribPool()).fromJsonable(missedChanges.committedChangesetAPool); + _checkChangesetAndPool(committedChangeset, wireApool1); + committedChangeset = pad.adoptChangesetAttribs(committedChangeset, wireApool1); + } + if (furtherChangeset) { + var wireApool2 = (new AttribPool()).fromJsonable(missedChanges.furtherChangesetAPool); + _checkChangesetAndPool(furtherChangeset, wireApool2); + furtherChangeset = pad.adoptChangesetAttribs(furtherChangeset, wireApool2); + } + + var commitWasMissed = !! committedChangeset; + if (commitWasMissed) { + var commitSocketId = missedChanges.committedChangesetSocketId; + var revisionSockets = _getPadRevisionSockets(pad); + // was the commit really missed, or did the client just not hear back? + // look for later changeset by this socket + var r = baseRev; + while (r < pad.getHeadRevisionNumber()) { + r++; + var s = revisionSockets[r]; + if (! s) { + // changes are too old, have to drop them. + return; + } + if (s == commitSocketId) { + commitWasMissed = false; + break; + } + } + } + if (! commitWasMissed) { + // commit already incorporated by the server + committedChangeset = null; + } + + var changeset; + if (committedChangeset && furtherChangeset) { + changeset = Changeset.compose(committedChangeset, furtherChangeset, apool); + } + else { + changeset = (committedChangeset || furtherChangeset); + } + + if (changeset) { + var author = userInfo.userId; + + applyUserChanges(pad, baseRev, changeset, null, author); + } +} + +function getAllPadsWithConnections() { + // returns array of global pad id strings + return getAllRoomsOfType(PADPAGE_ROOMTYPE).map(_roomToPadId); +} + +function broadcastServerMessage(msgObj) { + var msg = {type: "SERVER_MESSAGE", payload: msgObj}; + getAllRoomsOfType(PADPAGE_ROOMTYPE).forEach(function(roomName) { + getRoomConnections(roomName).forEach(function(connection) { + sendMessage(connection.connectionId, msg); + }); + }); +} + +function appendPadText(pad, txt) { + txt = model.cleanText(txt); + var oldFullText = pad.text(); + _applyChangesetToPad(pad, Changeset.makeSplice(oldFullText, + oldFullText.length-1, 0, txt)); +} + +function setPadText(pad, txt) { + txt = model.cleanText(txt); + var oldFullText = pad.text(); + // replace text except for the existing final (virtual) newline + _applyChangesetToPad(pad, Changeset.makeSplice(oldFullText, 0, + oldFullText.length-1, txt)); +} + +function setPadAText(pad, atext) { + var oldFullText = pad.text(); + var deletion = Changeset.makeSplice(oldFullText, 0, oldFullText.length-1, ""); + + var assem = Changeset.smartOpAssembler(); + Changeset.appendATextToAssembler(atext, assem); + var charBank = atext.text.slice(0, -1); + var insertion = Changeset.checkRep(Changeset.pack(1, atext.text.length, + assem.toString(), charBank)); + + var cs = Changeset.compose(deletion, insertion, pad.pool()); + Changeset.checkRep(cs); + + _applyChangesetToPad(pad, cs); +} + +function applyChangesetToPad(pad, changeset) { + Changeset.checkRep(changeset); + + _applyChangesetToPad(pad, changeset); +} + +function _applyChangesetToPad(pad, changeset) { + pad.appendRevision(changeset); + updatePadClients(pad); +} + +function getHistoricalAuthorData(pad, author) { + var authorData = pad.getAuthorData(author); + if (authorData) { + var data = {}; + if ((typeof authorData.colorId) == "number") { + data.colorId = authorData.colorId; + } + if (authorData.name) { + data.name = authorData.name; + } + else { + var uname = padusers.getNameForUserId(author); + if (uname) { + data.name = uname; + } + } + return data; + } + return null; +} + +function buildHistoricalAuthorDataMapFromAText(pad, atext) { + var map = {}; + pad.eachATextAuthor(atext, function(author, authorNum) { + var data = getHistoricalAuthorData(pad, author); + if (data) { + map[author] = data; + } + }); + return map; +} + +function buildHistoricalAuthorDataMapForPadHistory(pad) { + var map = {}; + pad.pool().eachAttrib(function(key, value) { + if (key == 'author') { + var author = value; + var data = getHistoricalAuthorData(pad, author); + if (data) { + map[author] = data; + } + } + }); + return map; +} + +function getATextForWire(pad, optRev) { + var atext; + if ((optRev && ! isNaN(Number(optRev))) || (typeof optRev) == "number") { + atext = pad.getInternalRevisionAText(Number(optRev)); + } + else { + atext = pad.atext(); + } + + var historicalAuthorData = buildHistoricalAuthorDataMapFromAText(pad, atext); + + var attribsForWire = Changeset.prepareForWire(atext.attribs, pad.pool()); + var apool = attribsForWire.pool; + // mutate atext (translate attribs for wire): + atext.attribs = attribsForWire.translated; + + return {atext:atext, apool:apool.toJsonable(), + historicalAuthorData:historicalAuthorData }; +} + +function getCollabClientVars(pad) { + // construct object that is made available on the client + // as collab_client_vars + + var forWire = getATextForWire(pad); + + return { + initialAttributedText: forWire.atext, + rev: pad.getHeadRevisionNumber(), + padId: pad.getLocalId(), + globalPadId: pad.getId(), + historicalAuthorData: forWire.historicalAuthorData, + apool: forWire.apool, + clientIp: request.clientAddr, + clientAgent: request.headers["User-Agent"] + }; +} + +function getNumConnections(pad) { + return _getPadConnections(pad).length; +} + +function getConnectedUsers(pad) { + var users = []; + _getPadConnections(pad).forEach(function(connection) { + users.push(connection.data.userInfo); + }); + return users; +} + + +function bootAllUsersFromPad(pad, reason) { + return bootUsersFromPad(pad, reason); +} + +function bootUsersFromPad(pad, reason, userInfoFilter) { + var connections = _getPadConnections(pad); + var bootedUserInfos = []; + connections.forEach(function(connection) { + if ((! userInfoFilter) || userInfoFilter(connection.data.userInfo)) { + bootedUserInfos.push(connection.data.userInfo); + bootConnection(connection.connectionId); + } + }); + return bootedUserInfos; +} + +function dumpStorageToString(pad) { + var lines = []; + var errors = []; + var head = pad.getHeadRevisionNumber(); + try { + for(var i=0;i<=head;i++) { + lines.push("changeset "+i+" "+Changeset.toBaseTen(pad.getRevisionChangeset(i))); + } + } + catch (e) { + errors.push("!!!!! Error in changeset "+i+": "+e.message); + } + for(var i=0;i<=head;i++) { + lines.push("author "+i+" "+pad.getRevisionAuthor(i)); + } + for(var i=0;i<=head;i++) { + lines.push("time "+i+" "+pad.getRevisionDate(i)); + } + var revisionSockets = _getPadRevisionSockets(pad); + for(var k in revisionSockets) lines.push("socket "+k+" "+revisionSockets[k]); + return errors.concat(lines).join('\n'); +} + +function _getPadIdForSocket(socketId) { + var connectionId = getSocketConnectionId(socketId); + if (connectionId) { + var connection = getConnection(connectionId); + if (connection) { + return _roomToPadId(connection.roomName); + } + } + return null; +} + +function _getUserIdForSocket(socketId) { + var connectionId = getSocketConnectionId(socketId); + if (connectionId) { + var connection = getConnection(connectionId); + if (connection) { + return connection.data.userInfo.userId; + } + } + return null; +} + +function _serverDebug(msg) { /* nothing */ } + +function _accessSocketPad(socketId, accessType, padFunc, dontRequirePad) { + return _accessCollabPad(_getPadIdForSocket(socketId), accessType, + padFunc, dontRequirePad); +} + +function _accessConnectionPad(connection, accessType, padFunc, dontRequirePad) { + return _accessCollabPad(_roomToPadId(connection.roomName), accessType, + padFunc, dontRequirePad); +} + +function _accessCollabPad(padId, accessType, padFunc, dontRequirePad) { + if (! padId) { + if (! dontRequirePad) { + _doWarn("Collab operation \""+accessType+"\" aborted because socket "+socketId+" has no pad."); + } + return; + } + else { + return _accessExistingPad(padId, accessType, function(pad) { + return padFunc(pad); + }, dontRequirePad); + } +} + +function _accessExistingPad(padId, accessType, padFunc, dontRequireExist) { + return model.accessPadGlobal(padId, function(pad) { + if (! pad.exists()) { + if (! dontRequireExist) { + _doWarn("Collab operation \""+accessType+"\" aborted because pad "+padId+" doesn't exist."); + } + return; + } + else { + return padFunc(pad); + } + }); +} + +function _handlePadUserInfo(pad, userInfo) { + var author = userInfo.userId; + var colorId = Number(userInfo.colorId); + var name = userInfo.name; + + if (! author) return; + + // update map from author to that author's last known color and name + var data = {colorId: colorId}; + if (name) data.name = name; + pad.setAuthorData(author, data); + padusers.notifyUserData(data); +} + +function _sendUserInfoMessage(connectionId, type, userInfo) { + if (translateSpecialKey(userInfo.specialKey) != 'invisible') { + sendMessage(connectionId, {type: type, userInfo: userInfo }); + } +} + + +function getRoomCallbacks(roomName) { + var callbacks = {}; + callbacks.introduceUsers = + function (joiningConnection, existingConnection) { + // notify users of each other + _sendUserInfoMessage(existingConnection.connectionId, + "USER_NEWINFO", + joiningConnection.data.userInfo); + _sendUserInfoMessage(joiningConnection.connectionId, + "USER_NEWINFO", + existingConnection.data.userInfo); + }; + callbacks.extroduceUsers = + function (leavingConnection, existingConnection) { + _sendUserInfoMessage(existingConnection.connectionId, "USER_LEAVE", + leavingConnection.data.userInfo); + }; + callbacks.onAddConnection = + function (data) { + model.accessPadGlobal(_roomToPadId(roomName), function(pad) { + _handlePadUserInfo(pad, data.userInfo); + padevents.onUserJoin(pad, data.userInfo); + readonly_server.updateUserInfo(pad, data.userInfo); + }); + }; + callbacks.onRemoveConnection = + function (data) { + model.accessPadGlobal(_roomToPadId(roomName), function(pad) { + padevents.onUserLeave(pad, data.userInfo); + }); + }; + callbacks.handleConnect = + function (data) { + if (roomName.indexOf("padpage/") != 0) { + return null; + } + if (! (data.userInfo && data.userInfo.userId && + _verifyUserId(data.userInfo.userId))) { + return null; + } + return data.userInfo; + }; + callbacks.clientReady = + function(newConnection, data) { + var padId = _roomToPadId(newConnection.roomName); + + if (data.stats) { + log.custom("padclientstats", {padId:padId, stats:data.stats}); + } + + var lastRev = data.lastRev; + var isReconnectOf = data.isReconnectOf; + var isCommitPending = !! data.isCommitPending; + var connectionId = newConnection.connectionId; + + newConnection.data.lastRev = lastRev; + updateRoomConnectionData(connectionId, newConnection.data); + + if (padutils.isProPadId(padId)) { + pro_padmeta.accessProPad(padId, function(propad) { + // tell client about pad title + sendMessage(connectionId, {type: "CLIENT_MESSAGE", payload: { + type: "padtitle", title: propad.getDisplayTitle() } }); + sendMessage(connectionId, {type: "CLIENT_MESSAGE", payload: { + type: "padpassword", password: propad.getPassword() } }); + }); + } + + _accessExistingPad(padId, "CLIENT_READY", function(pad) { + sendMessage(connectionId, {type: "CLIENT_MESSAGE", payload: { + type: "padoptions", options: pad.getPadOptionsObj() } }); + + updateClient(pad, connectionId); + + }); + + if (isCommitPending) { + // tell client that if it hasn't received an ACCEPT_COMMIT by now, it isn't coming. + sendMessage(connectionId, {type:"NO_COMMIT_PENDING"}); + } + }; + callbacks.handleMessage = function(connection, msg) { + _handleCometMessage(connection, msg); + }; + return callbacks; +} + +var _specialKeys = [['x375b', 'invisible']]; + +function translateSpecialKey(specialKey) { + // code -> name + for(var i=0;i<_specialKeys.length;i++) { + if (_specialKeys[i][0] == specialKey) { + return _specialKeys[i][1]; + } + } + return null; +} + +function getSpecialKey(name) { + // name -> code + for(var i=0;i<_specialKeys.length;i++) { + if (_specialKeys[i][1] == name) { + return _specialKeys[i][0]; + } + } + return null; +} + +function _updateDocumentConnectionUserInfo(pad, socketId, userInfo) { + var connectionId = getSocketConnectionId(socketId); + if (connectionId) { + var updatingConnection = getConnection(connectionId); + updatingConnection.data.userInfo = userInfo; + updateRoomConnectionData(connectionId, updatingConnection.data); + _getPadConnections(pad).forEach(function(connection) { + if (connection.socketId != updatingConnection.socketId) { + _sendUserInfoMessage(connection.connectionId, + "USER_NEWINFO", userInfo); + } + }); + + _handlePadUserInfo(pad, userInfo); + padevents.onUserInfoChange(pad, userInfo); + readonly_server.updateUserInfo(pad, userInfo); + } +} + +function _handleCometMessage(connection, msg) { + + var socketUserId = connection.data.userInfo.userId; + if (! (socketUserId && _verifyUserId(socketUserId))) { + // user has signed out or cleared cookies, no longer auth'ed + bootConnection(connection.connectionId, "unauth"); + } + + if (msg.type == "USER_CHANGES") { + try { + _accessConnectionPad(connection, "USER_CHANGES", function(pad) { + var baseRev = msg.baseRev; + var wireApool = (new AttribPool()).fromJsonable(msg.apool); + var changeset = msg.changeset; + if (changeset) { + _checkChangesetAndPool(changeset, wireApool); + changeset = pad.adoptChangesetAttribs(changeset, wireApool); + applyUserChanges(pad, baseRev, changeset, connection.socketId); + } + }); + } + catch (e if e.easysync) { + _doWarn("Changeset error handling USER_CHANGES: "+e); + } + } + else if (msg.type == "USERINFO_UPDATE") { + _accessConnectionPad(connection, "USERINFO_UPDATE", function(pad) { + var userInfo = msg.userInfo; + // security check + if (userInfo.userId == connection.data.userInfo.userId) { + _updateDocumentConnectionUserInfo(pad, + connection.socketId, userInfo); + } + else { + // drop on the floor + } + }); + } + else if (msg.type == "CLIENT_MESSAGE") { + _accessConnectionPad(connection, "CLIENT_MESSAGE", function(pad) { + var payload = msg.payload; + if (payload.authId && + payload.authId != connection.data.userInfo.userId) { + // authId, if present, must actually be the sender's userId; + // here it wasn't + } + else { + getRoomConnections(connection.roomName).forEach( + function(conn) { + if (conn.socketId != connection.socketId) { + sendMessage(conn.connectionId, + {type: "CLIENT_MESSAGE", payload: payload}); + } + }); + padevents.onClientMessage(pad, connection.data.userInfo, + payload); + } + }); + } +} + +function _correctMarkersInPad(atext, apool) { + var text = atext.text; + + // collect char positions of line markers (e.g. bullets) in new atext + // that aren't at the start of a line + var badMarkers = []; + var iter = Changeset.opIterator(atext.attribs); + var offset = 0; + while (iter.hasNext()) { + var op = iter.next(); + var listValue = Changeset.opAttributeValue(op, 'list', apool); + if (listValue) { + for(var i=0;i 0 && text.charAt(offset-1) != '\n') { + badMarkers.push(offset); + } + offset++; + } + } + else { + offset += op.chars; + } + } + + if (badMarkers.length == 0) { + return null; + } + + // create changeset that removes these bad markers + offset = 0; + var builder = Changeset.builder(text.length); + badMarkers.forEach(function(pos) { + builder.keepText(text.substring(offset, pos)); + builder.remove(1); + offset = pos+1; + }); + return builder.toString(); +} diff --git a/trunk/etherpad/src/etherpad/collab/collabroom_server.js b/trunk/etherpad/src/etherpad/collab/collabroom_server.js new file mode 100644 index 0000000..ab1f844 --- /dev/null +++ b/trunk/etherpad/src/etherpad/collab/collabroom_server.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. + */ + +import("execution"); +import("comet"); +import("fastJSON"); +import("cache_utils.syncedWithCache"); +import("etherpad.collab.collab_server"); +import("etherpad.collab.readonly_server"); +import("etherpad.log"); +jimport("java.util.concurrent.ConcurrentSkipListMap"); +jimport("java.util.concurrent.CopyOnWriteArraySet"); + +function onStartup() { + execution.initTaskThreadPool("collabroom_async", 1); +} + +function _doWarn(str) { + log.warn(appjet.executionId+": "+str); +} + +// deep-copies (recursively clones) an object (or value) +function _deepCopy(obj) { + if ((typeof obj) != 'object' || !obj) { + return obj; + } + var o = {}; + for(var k in obj) { + if (obj.hasOwnProperty(k)) { + var v = obj[k]; + if ((typeof v) == 'object' && v) { + o[k] = _deepCopy(v); + } + else { + o[k] = v; + } + } + } + return o; +} + +// calls func inside a global lock on the cache +function _withCache(func) { + return syncedWithCache("collabroom_server", function(cache) { + if (! cache.rooms) { + // roomName -> { connections: CopyOnWriteArraySet, + // type: } + cache.rooms = new ConcurrentSkipListMap(); + } + if (! cache.allConnections) { + // connectionId -> connection object + cache.allConnections = new ConcurrentSkipListMap(); + } + return func(cache); + }); +} + +// accesses cache without lock +function _getCache() { + return _withCache(function(cache) { return cache; }); +} + +// if roomType is null, will only update an existing connection +// (otherwise will insert or update as appropriate) +function _putConnection(connection, roomType) { + var roomName = connection.roomName; + var connectionId = connection.connectionId; + var socketId = connection.socketId; + var data = connection.data; + + _withCache(function(cache) { + var rooms = cache.rooms; + if (! rooms.containsKey(roomName)) { + // connection refers to room that doesn't exist / is empty + if (roomType) { + rooms.put(roomName, {connections: new CopyOnWriteArraySet(), + type: roomType}); + } + else { + return; + } + } + if (roomType) { + rooms.get(roomName).connections.add(connectionId); + cache.allConnections.put(connectionId, connection); + } + else { + cache.allConnections.replace(connectionId, connection); + } + }); +} + +function _removeConnection(connection) { + _withCache(function(cache) { + var rooms = cache.rooms; + var thisRoom = connection.roomName; + var thisConnectionId = connection.connectionId; + if (rooms.containsKey(thisRoom)) { + var roomConnections = rooms.get(thisRoom).connections; + roomConnections.remove(thisConnectionId); + if (roomConnections.isEmpty()) { + rooms.remove(thisRoom); + } + } + cache.allConnections.remove(thisConnectionId); + }); +} + +function _getConnection(connectionId) { + // return a copy of the connection object + return _deepCopy(_getCache().allConnections.get(connectionId) || null); +} + +function _getConnections(roomName) { + var array = []; + + var roomObj = _getCache().rooms.get(roomName); + if (roomObj) { + var roomConnections = roomObj.connections; + var iter = roomConnections.iterator(); + while (iter.hasNext()) { + var cid = iter.next(); + var conn = _getConnection(cid); + if (conn) { + array.push(conn); + } + } + } + return array; +} + +function sendMessage(connectionId, msg) { + var connection = _getConnection(connectionId); + if (connection) { + _sendMessageToSocket(connection.socketId, msg); + if (! comet.isConnected(connection.socketId)) { + // defunct socket, disconnect (later) + execution.scheduleTask("collabroom_async", + "collabRoomDisconnectSocket", + 0, [connection.connectionId, + connection.socketId]); + } + } +} + +function _sendMessageToSocket(socketId, msg) { + var msgString = fastJSON.stringify({type: "COLLABROOM", data: msg}); + comet.sendMessage(socketId, msgString); +} + +function disconnectDefunctSocket(connectionId, socketId) { + var connection = _getConnection(connectionId); + if (connection && connection.socketId == socketId) { + removeRoomConnection(connectionId); + } +} + +function _bootSocket(socketId, reason) { + if (reason) { + _sendMessageToSocket(socketId, + {type: "DISCONNECT_REASON", reason: reason}); + } + comet.disconnect(socketId); +} + +function bootConnection(connectionId, reason) { + var connection = _getConnection(connectionId); + if (connection) { + _bootSocket(connection.socketId, reason); + removeRoomConnection(connectionId); + } +} + +function getCallbacksForRoom(roomName, roomType) { + if (! roomType) { + var room = _getCache().rooms.get(roomName); + if (room) { + roomType = room.type; + } + } + + var emptyCallbacks = {}; + emptyCallbacks.introduceUsers = + function (joiningConnection, existingConnection) {}; + emptyCallbacks.extroduceUsers = + function extroduceUsers(leavingConnection, existingConnection) {}; + emptyCallbacks.onAddConnection = function (joiningData) {}; + emptyCallbacks.onRemoveConnection = function (leavingData) {}; + emptyCallbacks.handleConnect = + function(data) { return /*userInfo or */null; }; + emptyCallbacks.clientReady = function(newConnection, data) {}; + emptyCallbacks.handleMessage = function(connection, msg) {}; + + if (roomType == collab_server.PADPAGE_ROOMTYPE) { + return collab_server.getRoomCallbacks(roomName, emptyCallbacks); + } + else if (roomType == readonly_server.PADVIEW_ROOMTYPE) { + return readonly_server.getRoomCallbacks(roomName, emptyCallbacks); + } + else { + //java.lang.System.out.println("UNKNOWN ROOMTYPE: "+roomType); + return emptyCallbacks; + } +} + +// roomName must be globally unique, just within roomType; +// data must have a userInfo.userId +function addRoomConnection(roomName, roomType, + connectionId, socketId, data) { + var callbacks = getCallbacksForRoom(roomName, roomType); + + comet.setAttribute(socketId, "connectionId", connectionId); + + bootConnection(connectionId, "userdup"); + var joiningConnection = {roomName:roomName, + connectionId:connectionId, socketId:socketId, + data:data}; + _putConnection(joiningConnection, roomType); + var connections = _getConnections(roomName); + var joiningUser = data.userInfo.userId; + + connections.forEach(function(connection) { + if (connection.socketId != socketId) { + var user = connection.data.userInfo.userId; + if (user == joiningUser) { + bootConnection(connection.connectionId, "userdup"); + } + else { + callbacks.introduceUsers(joiningConnection, connection); + } + } + }); + + callbacks.onAddConnection(data); + + return joiningConnection; +} + +function removeRoomConnection(connectionId) { + var leavingConnection = _getConnection(connectionId); + if (leavingConnection) { + var roomName = leavingConnection.roomName; + var callbacks = getCallbacksForRoom(roomName); + + _removeConnection(leavingConnection); + + _getConnections(roomName).forEach(function (connection) { + callbacks.extroduceUsers(leavingConnection, connection); + }); + + callbacks.onRemoveConnection(leavingConnection.data); + } +} + +function getConnection(connectionId) { + return _getConnection(connectionId); +} + +function updateRoomConnectionData(connectionId, data) { + var connection = _getConnection(connectionId); + if (connection) { + connection.data = data; + _putConnection(connection); + } +} + +function getRoomConnections(roomName) { + return _getConnections(roomName); +} + +function getAllRoomsOfType(roomType) { + var rooms = _getCache().rooms; + var roomsIter = rooms.entrySet().iterator(); + var array = []; + while (roomsIter.hasNext()) { + var entry = roomsIter.next(); + var roomName = entry.getKey(); + var roomStruct = entry.getValue(); + if (roomStruct.type == roomType) { + array.push(roomName); + } + } + return array; +} + +function getSocketConnectionId(socketId) { + var result = comet.getAttribute(socketId, "connectionId"); + return result && String(result); +} + +function handleComet(cometOp, cometId, msg) { + var cometEvent = cometOp; + + function requireTruthy(x, id) { + if (!x) { + _doWarn("Collab operation rejected due to missing value, case "+id); + if (messageSocketId) { + comet.disconnect(messageSocketId); + } + response.stop(); + } + return x; + } + + if (cometEvent != "disconnect" && cometEvent != "message") { + response.stop(); + } + + var messageSocketId = requireTruthy(cometId, 2); + var messageConnectionId = getSocketConnectionId(messageSocketId); + + if (cometEvent == "disconnect") { + if (messageConnectionId) { + removeRoomConnection(messageConnectionId); + } + } + else if (cometEvent == "message") { + if (msg.type == "CLIENT_READY") { + var roomType = requireTruthy(msg.roomType, 4); + var roomName = requireTruthy(msg.roomName, 11); + + var socketId = messageSocketId; + var connectionId = messageSocketId; + var clientReadyData = requireTruthy(msg.data, 12); + + var callbacks = getCallbacksForRoom(roomName, roomType); + var userInfo = + requireTruthy(callbacks.handleConnect(clientReadyData), 13); + + var newConnection = addRoomConnection(roomName, roomType, + connectionId, socketId, + {userInfo: userInfo}); + + callbacks.clientReady(newConnection, clientReadyData); + } + else { + if (messageConnectionId) { + var connection = getConnection(messageConnectionId); + if (connection) { + var callbacks = getCallbacksForRoom(connection.roomName); + callbacks.handleMessage(connection, msg); + } + } + } + } +} \ No newline at end of file diff --git a/trunk/etherpad/src/etherpad/collab/genimg.js b/trunk/etherpad/src/etherpad/collab/genimg.js new file mode 100644 index 0000000..04d1b3b --- /dev/null +++ b/trunk/etherpad/src/etherpad/collab/genimg.js @@ -0,0 +1,55 @@ +/** + * 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. + */ + +import("sync"); +import("image"); +import("blob"); + +//jimport("java.lang.System.out.println"); + +function _cache() { + sync.callsyncIfTrue(appjet.cache, + function() { return ! appjet.cache["etherpad-genimg"]; }, + function() { appjet.cache["etherpad-genimg"] = { paths: {}}; }); + return appjet.cache["etherpad-genimg"]; +} + +function renderPath(path) { + if (_cache().paths[path]) { + //println("CACHE HIT"); + } + else { + //println("CACHE MISS"); + var regexResult = null; + var img = null; + if ((regexResult = + /solid\/([0-9]+)x([0-9]+)\/([0-9a-fA-F]{6})\.gif/.exec(path))) { + var width = Number(regexResult[1]); + var height = Number(regexResult[2]); + var color = regexResult[3]; + img = image.solidColorImageBlob(width, height, color); + } + else { + // our "broken image" image, red and partly transparent + img = image.pixelsToImageBlob(2, 2, [0x00000000, 0xffff0000, + 0xffff0000, 0x00000000], true, "gif"); + } + _cache().paths[path] = img; + } + + blob.serveBlob(_cache().paths[path]); + return true; +} diff --git a/trunk/etherpad/src/etherpad/collab/json_sans_eval.js b/trunk/etherpad/src/etherpad/collab/json_sans_eval.js new file mode 100644 index 0000000..6cbd497 --- /dev/null +++ b/trunk/etherpad/src/etherpad/collab/json_sans_eval.js @@ -0,0 +1,178 @@ +// Copyright (C) 2008 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. + +/** + * Parses a string of well-formed JSON text. + * + * If the input is not well-formed, then behavior is undefined, but it is + * deterministic and is guaranteed not to modify any object other than its + * return value. + * + * This does not use `eval` so is less likely to have obscure security bugs than + * json2.js. + * It is optimized for speed, so is much faster than json_parse.js. + * + * This library should be used whenever security is a concern (when JSON may + * come from an untrusted source), speed is a concern, and erroring on malformed + * JSON is *not* a concern. + * + * Pros Cons + * +-----------------------+-----------------------+ + * json_sans_eval.js | Fast, secure | Not validating | + * +-----------------------+-----------------------+ + * json_parse.js | Validating, secure | Slow | + * +-----------------------+-----------------------+ + * json2.js | Fast, some validation | Potentially insecure | + * +-----------------------+-----------------------+ + * + * json2.js is very fast, but potentially insecure since it calls `eval` to + * parse JSON data, so an attacker might be able to supply strange JS that + * looks like JSON, but that executes arbitrary javascript. + * If you do have to use json2.js with untrusted data, make sure you keep + * your version of json2.js up to date so that you get patches as they're + * released. + * + * @param {string} json per RFC 4627 + * @return {Object|Array} + * @author Mike Samuel + */ +var jsonParse = (function () { + var number + = '(?:-?\\b(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\\b)'; + var oneChar = '(?:[^\\0-\\x08\\x0a-\\x1f\"\\\\]' + + '|\\\\(?:[\"/\\\\bfnrt]|u[0-9A-Fa-f]{4}|x7c))'; + var string = '(?:\"' + oneChar + '*\")'; + + // Will match a value in a well-formed JSON file. + // If the input is not well-formed, may match strangely, but not in an unsafe + // way. + // Since this only matches value tokens, it does not match whitespace, colons, + // or commas. + var jsonToken = new RegExp( + '(?:false|true|null|[\\{\\}\\[\\]]' + + '|' + number + + '|' + string + + ')', 'g'); + + // Matches escape sequences in a string literal + var escapeSequence = new RegExp('\\\\(?:([^ux]|x7c)|u(.{4}))', 'g'); + + // Decodes escape sequences in object literals + var escapes = { + '"': '"', + '/': '/', + '\\': '\\', + 'b': '\b', + 'f': '\f', + 'n': '\n', + 'r': '\r', + 't': '\t', + 'x7c': '|' + }; + function unescapeOne(_, ch, hex) { + return ch ? escapes[ch] : String.fromCharCode(parseInt(hex, 16)); + } + + // A non-falsy value that coerces to the empty string when used as a key. + var EMPTY_STRING = new String(''); + var SLASH = '\\'; + + // Constructor to use based on an open token. + var firstTokenCtors = { '{': Object, '[': Array }; + + return function (json) { + // Split into tokens + var toks = json.match(jsonToken); + // Construct the object to return + var result; + var tok = toks[0]; + if ('{' === tok) { + result = {}; + } else if ('[' === tok) { + result = []; + } else { + throw new Error(tok); + } + + // If undefined, the key in an object key/value record to use for the next + // value parsed. + var key; + // Loop over remaining tokens maintaining a stack of uncompleted objects and + // arrays. + var stack = [result]; + for (var i = 1, n = toks.length; i < n; ++i) { + tok = toks[i]; + + var cont; + switch (tok.charCodeAt(0)) { + default: // sign or digit + cont = stack[0]; + cont[key || cont.length] = +(tok); + key = void 0; + break; + case 0x22: // '"' + tok = tok.substring(1, tok.length - 1); + if (tok.indexOf(SLASH) !== -1) { + tok = tok.replace(escapeSequence, unescapeOne); + } + cont = stack[0]; + if (!key) { + if (cont instanceof Array) { + key = cont.length; + } else { + key = tok || EMPTY_STRING; // Use as key for next value seen. + break; + } + } + cont[key] = tok; + key = void 0; + break; + case 0x5b: // '[' + cont = stack[0]; + stack.unshift(cont[key || cont.length] = []); + key = void 0; + break; + case 0x5d: // ']' + stack.shift(); + break; + case 0x66: // 'f' + cont = stack[0]; + cont[key || cont.length] = false; + key = void 0; + break; + case 0x6e: // 'n' + cont = stack[0]; + cont[key || cont.length] = null; + key = void 0; + break; + case 0x74: // 't' + cont = stack[0]; + cont[key || cont.length] = true; + key = void 0; + break; + case 0x7b: // '{' + cont = stack[0]; + stack.unshift(cont[key || cont.length] = {}); + key = void 0; + break; + case 0x7d: // '}' + stack.shift(); + break; + } + } + // Fail if we've got an uncompleted object. + if (stack.length) { throw new Error(); } + return result; + }; +})(); diff --git a/trunk/etherpad/src/etherpad/collab/readonly_server.js b/trunk/etherpad/src/etherpad/collab/readonly_server.js new file mode 100644 index 0000000..e367f04 --- /dev/null +++ b/trunk/etherpad/src/etherpad/collab/readonly_server.js @@ -0,0 +1,174 @@ +/** + * 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. + */ + +import("comet"); +import("ejs"); +import("etherpad.collab.ace.easysync2.{AttribPool,Changeset}"); +import("etherpad.log"); +import("etherpad.pad.activepads"); +import("etherpad.pad.model"); +import("etherpad.pad.padutils"); +import("etherpad.pad.padevents"); +import("etherpad.pro.pro_padmeta"); +import("fastJSON"); +import("fileutils.readFile"); +import("jsutils.eachProperty"); +import("etherpad.collab.server_utils.*"); +import("etherpad.collab.collabroom_server"); + +jimport("java.util.concurrent.ConcurrentHashMap"); + +jimport("java.lang.System.out.println"); + +var PADVIEW_ROOMTYPE = 'padview'; + +var _serverDebug = println;//function(x) {}; + +// "view id" is either a padId or an ro.id +function _viewIdToRoom(padId) { + return "padview/"+padId; +} + +function _roomToViewId(roomName) { + return roomName.substring(roomName.indexOf("/")+1); +} + +function getRoomCallbacks(roomName, emptyCallbacks) { + var callbacks = emptyCallbacks; + + var viewId = _roomToViewId(roomName); + + callbacks.handleConnect = function(data) { + if (data.userInfo && data.userInfo.userId) { + return data.userInfo; + } + return null; + }; + callbacks.clientReady = + function(newConnection, data) { + newConnection.data.lastRev = data.lastRev; + collabroom_server.updateRoomConnectionData(newConnection.connectionId, + newConnection.data); + }; + + return callbacks; +} + +function updatePadClients(pad) { + var padId = pad.getId(); + var roId = padIdToReadonly(padId); + + function update(connection) { + updateClient(pad, connection.connectionId); + } + + collabroom_server.getRoomConnections(_viewIdToRoom(padId)).forEach(update); + collabroom_server.getRoomConnections(_viewIdToRoom(roId)).forEach(update); +} + +// Get arrays of text lines and attribute lines for a revision +// of a pad. +function _getPadLines(pad, revNum) { + var atext; + if (revNum >= 0) { + atext = pad.getInternalRevisionAText(revNum); + } else { + atext = Changeset.makeAText("\n"); + } + + var result = {}; + result.textlines = Changeset.splitTextLines(atext.text); + result.alines = Changeset.splitAttributionLines(atext.attribs, + atext.text); + return result; +} + +function updateClient(pad, connectionId) { + var conn = collabroom_server.getConnection(connectionId); + if (! conn) { + return; + } + var lastRev = conn.data.lastRev; + while (lastRev < pad.getHeadRevisionNumber()) { + var r = ++lastRev; + var author = pad.getRevisionAuthor(r); + var lines = _getPadLines(pad, r-1); + var wirePool = new AttribPool(); + var forwards = pad.getRevisionChangeset(r); + var backwards = Changeset.inverse(forwards, lines.textlines, + lines.alines, pad.pool()); + var forwards2 = Changeset.moveOpsToNewPool(forwards, pad.pool(), + wirePool); + var backwards2 = Changeset.moveOpsToNewPool(backwards, pad.pool(), + wirePool); + + function revTime(r) { + var date = pad.getRevisionDate(r); + var s = Math.floor((+date)/1000); + //java.lang.System.out.println("time "+r+": "+s); + return s; + } + + var msg = {type:"NEW_CHANGES", newRev:r, + changeset: forwards2, + changesetBack: backwards2, + apool: wirePool.toJsonable(), + author: author, + timeDelta: revTime(r) - revTime(r-1) }; + collabroom_server.sendMessage(connectionId, msg); + } + conn.data.lastRev = pad.getHeadRevisionNumber(); + collabroom_server.updateRoomConnectionData(connectionId, conn.data); +} + +function sendMessageToPadConnections(pad, msg) { + var padId = pad.getId(); + var roId = padIdToReadonly(padId); + + function update(connection) { + collabroom_server.sendMessage(connection.connectionId, msg); + } + + collabroom_server.getRoomConnections(_viewIdToRoom(padId)).forEach(update); + collabroom_server.getRoomConnections(_viewIdToRoom(roId)).forEach(update); +} + +function updateUserInfo(pad, userInfo) { + var msg = { type:"NEW_AUTHORDATA", + author: userInfo.userId, + data: {} }; + var hasData = false; + if ((typeof (userInfo.colorId)) == "number") { + msg.data.colorId = userInfo.colorId; + hasData = true; + } + if (userInfo.name) { + msg.data.name = userInfo.name; + hasData = true; + } + if (hasData) { + sendMessageToPadConnections(pad, msg); + } +} + +function broadcastNewRevision(pad, revObj) { + var msg = { type:"NEW_SAVEDREV", + savedRev: revObj }; + + delete revObj.ip; // we try not to share info like IP addresses on slider + + sendMessageToPadConnections(pad, msg); +} diff --git a/trunk/etherpad/src/etherpad/collab/server_utils.js b/trunk/etherpad/src/etherpad/collab/server_utils.js new file mode 100644 index 0000000..ece3aea --- /dev/null +++ b/trunk/etherpad/src/etherpad/collab/server_utils.js @@ -0,0 +1,204 @@ +/** + * 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. + */ + +import("comet"); +import("ejs"); +import("etherpad.collab.ace.easysync2.{AttribPool,Changeset}"); +import("etherpad.log"); +import("etherpad.pad.activepads"); +import("etherpad.pad.model"); +import("etherpad.pad.padutils"); +import("etherpad.pad.padevents"); +import("etherpad.pro.pro_padmeta"); +import("fastJSON"); +import("fileutils.readFile"); +import("jsutils.eachProperty"); + +jimport("java.util.Random"); +jimport("java.lang.System"); + +import("etherpad.collab.collab_server"); +// importClass(java.util.Random); +// importClass(java.lang.System); + +var _serverDebug = function() {}; +var _dmesg = function() { System.out.println(arguments[0]+""); }; + +/// Begin readonly/padId conversion code +/// TODO: refactor into new file? +var _baseRandomNumber = 0x123123; // keep this number seekrit + +function _map(array, func) { + for(var i=0; i 1) { + for(var i=0; i= start && charcode <= end; +} + +/* a short little testing function, converts back and forth */ +// function _testEncrypt(str) { +// var encrypted = padIdToReadonly(str); +// var decrypted = readonlyToPadId(encrypted); +// _dmesg(str + " " + encrypted + " " + decrypted); +// if(decrypted != str) { +// _dmesg("ERROR: " + str + " and " + decrypted + " do not match"); +// } +// } + +// _testEncrypt("testing$"); diff --git a/trunk/etherpad/src/etherpad/control/aboutcontrol.js b/trunk/etherpad/src/etherpad/control/aboutcontrol.js new file mode 100644 index 0000000..9d77142 --- /dev/null +++ b/trunk/etherpad/src/etherpad/control/aboutcontrol.js @@ -0,0 +1,263 @@ +/** + * 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. + */ + +import("email.sendEmail"); +import("funhtml.*", "stringutils.*"); +import("netutils"); +import("execution"); + +import("etherpad.utils.*"); +import("etherpad.log"); +import("etherpad.globals.*"); +import("etherpad.quotas"); +import("etherpad.sessions.getSession"); +import("etherpad.store.eepnet_trial"); +import("etherpad.store.checkout"); +import("etherpad.store.eepnet_checkout"); + +jimport("java.lang.System.out.println"); + +//---------------------------------------------------------------- + +function render_product() { + if (request.params.from) { response.redirect(request.path); } + renderFramed("about/product_body.ejs"); +} + +function render_faq() { + renderFramed("about/faq_body.ejs", { + LI: LI, + H2: H2, + A: A, + html: html + }); +} + +function render_pne_faq() { + renderFramed("about/pne-faq.ejs"); +} + +function render_company() { + renderFramed("about/company_body.ejs"); +} + +function render_contact() { + renderFramed("about/contact_body.ejs"); +} + +function render_privacy() { + renderFramed("about/privacy_body.ejs"); +} + +function render_tos() { + renderFramed("about/tos_body.ejs"); +} + +function render_testimonials() { + renderFramed("about/testimonials.ejs"); +} + +function render_appjet() { + response.redirect("/ep/blog/posts/etherpad-and-appjet"); +// renderFramed("about/appjet_body.ejs"); +} + +function render_screencast() { + if (request.params.from) { response.redirect(request.path); } + var screencastUrl; +// if (isProduction()) { + screencastUrl = encodeURIComponent("http://etherpad.s3.amazonaws.com/epscreencast800x600.flv"); +// } else { +// screencastUrl = encodeURIComponent("/static/flv/epscreencast800x600.flv"); +// } + renderFramed("about/screencast_body.ejs", {screencastUrl: screencastUrl}); +} + +function render_forums() { + renderFramed("about/forums_body.ejs"); +} + +function render_blog() { + renderFramed("about/blog_body.ejs"); +} + +function render_really_real_time() { + renderFramed("about/simultaneously.ejs"); +} + +function render_simultaneously() { + renderFramed("about/simultaneously.ejs"); +} + +//---------------------------------------------------------------- +// pricing +//---------------------------------------------------------------- + +function render_pricing() { + renderFramed("about/pricing.ejs", { + trialDays: eepnet_trial.getTrialDays(), + costPerUser: checkout.dollars(eepnet_checkout.COST_PER_USER) + }); +} + +function render_pricing_free() { + renderFramed("about/pricing_free.ejs", { + maxUsersPerPad: quotas.getMaxSimultaneousPadEditors() + }); +} + +function render_pricing_eepnet() { + renderFramed("about/pricing_eepnet.ejs", { + trialDays: eepnet_trial.getTrialDays(), + costPerUser: checkout.dollars(eepnet_checkout.COST_PER_USER) + }); +} + +function render_pricing_pro() { + renderFramed("about/pricing_pro.ejs", {}); +} + +function render_eepnet_pricing_contact_post() { + response.setContentType("text/plain; charset=utf-8"); + var data = {}; + var fields = ['firstName', 'lastName', 'email', 'orgName', + 'jobTitle', 'phone', 'estUsers', 'industry']; + + if (!getSession().pricingContactData) { + getSession().pricingContactData = {}; + } + + function err(m) { + response.write(m); + response.stop(); + } + + fields.forEach(function(f) { + getSession().pricingContactData[f] = request.params[f]; + }); + + fields.forEach(function(f) { + data[f] = request.params[f]; + if (!(data[f] && (data[f].length > 0))) { + err("All fields are required."); + } + }); + + if (!isValidEmail(data.email)) { + err("Error: Invalid Email"); + } + + // log this data to a file + fields.ip = request.clientAddr; + fields.sessionReferer = getSession().initialReferer; + log.custom("eepnet_pricing_inquiry", fields); + + // submit web2lead + var ref = getSession().initialReferer; + var googleQuery = extractGoogleQuery(ref); + var wlparams = { + oid: "00D80000000b7ey", + first_name: data.firstName, + last_name: data.lastName, + email: data.email, + company: data.orgName, + title: data.jobTitle, + phone: data.phone, + '00N80000003FYtG': data.estUsers, + '00N80000003FYto': ref, + '00N80000003FYuI': googleQuery, + lead_source: 'EEPNET Pricing Inquiry', + industry: data.industry, + retURL: 'http://'+request.host+'/ep/store/salesforce-web2lead-ok' + }; + + var result = netutils.urlPost( + "http://www.salesforce.com/servlet/servlet.WebToLead?encoding=UTF-8", + wlparams, {}); + + // now send an email sales notification + var hostname = ipToHostname(request.clientAddr) || "unknown"; + var subject = 'EEPNET Pricing Inquiry: '+data.email+' / '+hostname; + var body = [ + "", "This is an automated email.", "", + data.firstName+" "+data.lastName+" ("+data.orgName+") has inquired about EEPNET pricing.", + "", + "This record has automatically been added to SalesForce. See the salesforce lead page for more details.", + "", "Session Referer: "+ref, "" + ].join("\n"); + var toAddr = 'sales@pad.spline.inf.fu-berlin.de'; + if (isTestEmail(data.email)) { + toAddr = 'blackhole@appjet.com'; + } + sendEmail(toAddr, 'sales@pad.spline.inf.fu-berlin.de', subject, {}, body); + + // all done! + response.write("OK"); +} + +function render_pricing_interest_signup() { + response.setContentType('text/plain; charset=utf-8'); + + var email = request.params.email; + var interestedNet = request.params.interested_net; + var interestedHosted = request.params.interested_hosted; + + if (!isValidEmail(email)) { + response.write("Error: Invalid Email"); + response.stop(); + } + + log.custom("pricing_interest", + {email: email, + net: interestedNet, + hosted: interestedHosted}); + + response.write('OK'); +} + +function render_pricing_eepnet_users() { + renderFramed('about/pricing_eepnet_users.ejs', {}); +} + +function render_pricing_eepnet_support() { + renderFramed('about/pricing_eepnet_support.ejs', {}); +} + + +//------------------------------------------------------------ +// survey + +function render_survey() { + var id = request.params.id; + log.custom("pro-user-survey", { surveyProAccountId: (id || "unknown") }); + response.redirect("http://www.surveymonkey.com/s.aspx?sm=yT3ALP0pb_2fP_2bHtcfzvpkXQ_3d_3d"); +} + + +//------------------------------------------------------------ + +import("etherpad.billing.billing"); + +function render_testbillingnotify() { + var ret = billing.handlePaypalNotification(); + if (ret.status == 'completion') { + // do something with purchase ret.purchaseInfo + } else if (ret.status != 'redundant') { + java.lang.System.out.println("Whoa error: "+ret.toSource()); + } + response.write("ok"); +} + diff --git a/trunk/etherpad/src/etherpad/control/admincontrol.js b/trunk/etherpad/src/etherpad/control/admincontrol.js new file mode 100644 index 0000000..02f6428 --- /dev/null +++ b/trunk/etherpad/src/etherpad/control/admincontrol.js @@ -0,0 +1,1471 @@ +/** + * 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. + */ + +import("fastJSON"); +import("netutils"); +import("funhtml.*"); +import("stringutils.{html,sprintf,startsWith,md5}"); +import("jsutils.*"); +import("sqlbase.sqlbase"); +import("sqlbase.sqlcommon"); +import("sqlbase.sqlobj"); +import("varz"); +import("comet"); +import("dispatch.{Dispatcher,PrefixMatcher,DirMatcher,forward}"); + +import("etherpad.billing.team_billing"); +import("etherpad.globals.*"); +import("etherpad.utils.*"); +import("etherpad.licensing"); +import("etherpad.sessions.getSession"); +import("etherpad.sessions"); +import("etherpad.statistics.statistics"); +import("etherpad.log"); +import("etherpad.admin.shell"); +import("etherpad.usage_stats.usage_stats"); +import("etherpad.control.blogcontrol"); +import("etherpad.control.pro_beta_control"); +import("etherpad.control.statscontrol"); +import("etherpad.statistics.exceptions"); +import("etherpad.store.checkout"); + +import("etherpad.pad.activepads"); +import("etherpad.pad.model"); +import("etherpad.pad.padutils"); +import("etherpad.pad.dbwriter"); +import("etherpad.collab.collab_server"); + +import("etherpad.pro.pro_accounts"); +import("etherpad.pro.pro_utils"); +import("etherpad.pro.domains"); + +jimport("java.lang.System.out.println"); + +jimport("net.appjet.oui.cometlatencies"); +jimport("net.appjet.oui.appstats"); + + +//---------------------------------------------------------------- + +function _isAuthorizedAdmin() { + if (!isProduction()) { + return true; + } + return (getSession().adminAuth === true); +} + +var _mainLinks = [ + ['exceptions', 'Exceptions Monitor'], + ['usagestats/', 'Usage Stats'], + ['padinspector', 'Pad Inspector'], + ['dashboard', 'Dashboard'], + ['eepnet-licenses', 'EEPNET Licenses'], + ['config', 'appjet.config'], + ['shell', 'Shell'], + ['timings', 'timing data'], + ['broadcast-message', 'Pad Broadcast'], +// ['analytics', 'Google Analytics'], + ['varz', 'varz'], + ['genlicense', 'Manually generate a license key'], + ['flows', 'Flows (warning: slow)'], + ['diagnostics', 'Pad Connection Diagnostics'], + ['cachebrowser', 'Cache Browser'], + ['pne-tracker', 'PNE Tracking Stats'], + ['reload-blog-db', 'Reload blog DB'], + ['pro-domain-accounts', 'Pro Domain Accounts'], + ['beta-valve', 'Beta Valve'], + ['reset-subscription', "Reset Subscription"] +]; + +function onRequest(name) { + if (name == "auth") { + return; + } + if (!_isAuthorizedAdmin()) { + getSession().cont = request.path; + response.redirect('/ep/admin/auth'); + } + + var disp = new Dispatcher(); + disp.addLocations([ + [PrefixMatcher('/ep/admin/usagestats/'), forward(statscontrol)] + ]); + + return disp.dispatch(); +} + +function _commonHead() { + return HEAD(STYLE( + "html {font-family:Verdana,Helvetica,sans-serif;}", + "body {padding: 2em;}" + )); +} + +//---------------------------------------------------------------- + +function render_auth() { + var cont = getSession().cont; + if (getSession().message) { + response.write(DIV(P(B(getSession().message)))); + delete getSession().message; + } + if (request.method == "GET") { + response.write(FORM({method: "POST", action: request.path}, + P("Are you an admin?"), + LABEL("Password:"), + INPUT({type: "password", name: "password", value: ""}), + INPUT({type: "submit", value: "submit"}) + )); + } + if (request.method == "POST") { + var pass = request.params.password; + if (pass === appjet.config['etherpad.adminPass']) { + getSession().adminAuth = true; + if (cont) { + response.redirect(cont); + } else { + response.redirect("/ep/admin/main"); + } + } else { + getSession().message = "Bad Password."; + response.redirect(request.path); + } + } +} + +function render_main() { + var div = DIV(); + + div.push(A({href: "/"}, html("«"), " home")); + div.push(H1("Admin")); + + _mainLinks.forEach(function(l) { + div.push(DIV(A({href: l[0]}, l[1]))); + }); + if (sessions.isAnEtherpadAdmin()) { + div.push(P(A({href: "/ep/admin/setadminmode?v=false"}, + "Exit Admin Mode"))); + } + else { + div.push(P(A({href: "/ep/admin/setadminmode?v=true"}, + "Enter Admin Mode"))); + } + response.write(HTML(_commonHead(), BODY(div))); +} + +//---------------------------------------------------------------- + +function render_config() { + + vars = []; + eachProperty(appjet.config, function(k,v) { + vars.push(k); + }); + + vars.sort(); + + response.setContentType('text/plain; charset=utf-8'); + vars.forEach(function(v) { + response.write("appjet.config."+v+" = "+appjet.config[v]+"\n"); + }); +} + +//---------------------------------------------------------------- + +function render_test() { + response.setContentType("text/plain"); + response.write(Packages.net.appjet.common.util.ExpiringMapping + "\n"); + var m = new Packages.net.appjet.common.util.ExpiringMapping(10 * 1000); + response.write(m.toString() + "\n"); + m.get("test"); + return; + response.write(m.toString()); +} + +function render_dashboard() { + var body = BODY(); + body.push(A({href: '/ep/admin/'}, html("« Admin"))); + body.push(H1({style: "border-bottom: 1px solid black;"}, "Dashboard")); + + /* + body.push(H2({style: "color: #226; font-size: 1em;"}, "License")); + var license = licensing.getLicense(); + body.push(P(TT(" Licensed To (name): "+license.personName))); + body.push(P(TT(" Licensed To (organization): "+license.organizationName))); + body.push(P(TT(" Software Edition: "+license.editionName))); + var quota = ((license.userQuota > 0) ? license.userQuota : 'unlimited'); + body.push(P(TT(" User Quota: "+quota))); + var expires = (license.expiresDate ? (license.expiresDate.toString()) : 'never'); + body.push(P(TT(" Expires: "+expires))); + */ + + /* + body.push(H2({style: "color: #226; font-size: 1em;"}, "Active User Quota")); + + var activeUserCount = licensing.getActiveUserCount(); + var activeUserQuota = licensing.getActiveUserQuota(); + var activeUserWindowStart = licensing.getActiveUserWindowStart(); + + body.push(P(TT(" Since ", B(activeUserWindowStart.toString()), ", ", + "you have used ", B(activeUserCount), " of ", B(activeUserQuota), + " active users."))); +*/ + body.push(H2({style: "color: #226; font-size: 1em;"}, "Uptime")); + body.push(P({style: "margin-left: 25px;"}, "Server running for "+renderServerUptime()+".")) + + body.push(H2({style: "color: #226; font-size: 1em;"}, "Response codes")); + body.push(renderResponseCodes()); + + body.push(H2({style: "color: #226; font-size: 1em;"}, "Comet Connections")); + body.push(renderPadConnections()); + + body.push(H2({style: "color: #226; font-size: 1em;"}, "Comet Stats")); + body.push(renderCometStats()); + + body.push(H2({style: "color: #226; font-size: 1em;"}, "Recurring revenue, monthly")); + body.push(renderRevenueStats()); + + response.write(HTML(_commonHead(), body)); +} + +// Note: This function is called by the PNE dashboard (pro_admin_control.js)! Be careful. +function renderPadConnections() { + var d = DIV(); + var lastCount = cometlatencies.lastCount(); + + if (lastCount.isDefined()) { + var countMap = {}; + Array.prototype.map.call(lastCount.get().elements().collect().toArray().unbox( + java.lang.Class.forName("java.lang.Object")), + function(x) { + countMap[x._1()] = x._2(); + }); + var totalConnected = 0; + var ul = UL(); + eachProperty(countMap, function(k,v) { + ul.push(LI(k+": "+v)); + if (/^\d+$/.test(v)) { + totalConnected += Number(v); + } + }); + ul.push(LI(B("Total: ", totalConnected))); + d.push(ul); + } else { + d.push("Still collecting data... check back in a minute."); + } + return d; +} + +// Note: This function is called by the PNE dashboard (pro_admin_control.js)! Be careful. +function renderCometStats() { + var d = DIV(); + var lastStats = cometlatencies.lastStats(); + var lastCount = cometlatencies.lastCount(); + + + if (lastStats.isDefined()) { + d.push(P("Realtime transport latency percentiles (microseconds):")); + var ul = UL(); + lastStats.map(scalaF1(function(s) { + ['50', '90', '95', '99', 'max'].forEach(function(id) { + var fn = id; + if (id != "max") { + fn = ("p"+fn); + id = id+"%"; + } + ul.push(LI(id, ": <", s[fn](), html("µ"), "s")); + }); + })); + d.push(ul); + } else { + d.push(P("Still collecting data... check back in a minutes.")); + } + + /* ["p50", "p90", "p95", "p99", "max"].forEach(function(id) { + ul.push(LI(B( + + return DIV(P(sprintf("50%% %d\t90%% %d\t95%% %d\t99%% %d\tmax %d", + s.p50(), s.p90(), s.p95(), s.p99(), s.max())), + P(sprintf("%d total messages", s.count()))); + }})).get();*/ + + + return d; +} + +// Note: This function is called by the PNE dashboard (pro_admin_control.js)! Be careful. +function renderResponseCodes() { + var statusCodeFrequencyNames = ["minute", "hour", "day", "week"]; + var data = { }; + var statusCodes = appstats.stati(); + for (var i = 0; i < statusCodes.length; ++i) { + var name = statusCodeFrequencyNames[i]; + var map = statusCodes[i]; + map.foreach(scalaF1(function(pair) { + if (! (pair._1() in data)) data[pair._1()] = {}; + var scmap = data[pair._1()]; + scmap[name] = pair._2().count(); + })); + }; + var stats = TABLE({id: "responsecodes-table", style: "margin-left: 25px;", + border: 1, cellspacing: 0, cellpadding: 4}, + TR.apply(TR, statusCodeFrequencyNames.map(function(name) { + return TH({colspan: 2}, "Last", html(" "), name); + }))); + var sortedStati = []; + eachProperty(data, function(k) { + sortedStati.push(k); + }); + sortedStati.sort(); + sortedStati.forEach(function(k, i) { // k is status code. + var row = TR(); + statusCodeFrequencyNames.forEach(function(name) { + row.push(TD({style: 'width: 2em;'}, data[k][name] ? k+":" : "")); + row.push(TD(data[k][name] ? data[k][name] : "")); + }); + stats.push(row); + }); + return stats; +} + +// Note: This function is called by the PNE dashboard (pro_admin_control.js)! Be careful. +function renderServerUptime() { + var labels = ["seconds", "minutes", "hours", "days"]; + var ratios = [60, 60, 24]; + var time = appjet.uptime / 1000; + var pos = 0; + while (pos < ratios.length && time / ratios[pos] > 1.1) { + time = time / ratios[pos]; + pos++; + } + return sprintf("%.1f %s", time, labels[pos]); +} + +function renderRevenueStats() { + var subs = team_billing.getAllSubscriptions(); + var total = 0; + var totalUsers = 0; + subs.forEach(function(sub) { + var users = team_billing.getMaxUsers(sub.customer); + var cost = team_billing.calculateSubscriptionCost(users, sub.coupon); + if (cost > 0) { + totalUsers += users; + total += cost; + } + }); + return "US $"+checkout.dollars(total)+", from "+subs.length+" domains and "+totalUsers+" users."; +} + +//---------------------------------------------------------------- +// Broadcasting Messages +//---------------------------------------------------------------- + +function render_broadcast_message_get() { + var body = BODY(FORM({action: request.path, method: 'post'}, + H3('Broadcast Message to All Active Pad Clients:'), + TEXTAREA({name: 'msgtext', style: 'width: 100%; height: 100px;'}), + H3('JavaScript code to be eval()ed on client (optional, be careful!): '), + TEXTAREA({name: 'jscode', style: 'width: 100%; height: 100px;'}), + INPUT({type: 'submit', value: 'Broadcast Now'}))); + response.write(HTML(body)); +} + +function render_broadcast_message_post() { + var msgText = request.params.msgtext; + var jsCode = request.params.jscode; + if (!(msgText || jsCode)) { + response.write("No mesage text or jscode specified."); + response.stop(); + return; + } + collab_server.broadcastServerMessage({ + type: 'NOTICE', + text: msgText, + js: jsCode + }); + response.write(HTML(BODY(P("OK"), P(A({href: request.path}, "back"))))); +} + +function render_shell() { + shell.handleRequest(); +} + +//---------------------------------------------------------------- +// pad inspector +//---------------------------------------------------------------- + +function _getPadUrl(globalPadId) { + var superdomain = pro_utils.getRequestSuperdomain(); + var domain; + if (padutils.isProPadId(globalPadId)) { + var domainId = padutils.getDomainId(globalPadId); + domain = domains.getDomainRecord(domainId).subDomain + + '.' + superdomain; + } + else { + domain = superdomain; + } + var localId = padutils.globalToLocalId(globalPadId); + return "http://"+httpHost(domain)+"/"+localId; +} + +function render_padinspector_get() { + var padId = request.params.padId; + if (!padId) { + response.write(FORM({action: request.path, method: 'get', style: 'border: 1px solid #ccc; background-color: #eee; padding: .2em 1em;'}, + P("Pad Lookup: ", + INPUT({name: 'padId', value: ''}), + INPUT({type: 'submit'})))); + + // show recently active pads; the number of them may vary; lots of + // activity in a pad will push others off the list + response.write(H3("Recently Active Pads:")); + var recentlyActiveTable = TABLE({cellspacing: 0, cellpadding: 6, border: 1, + style: 'font-family: monospace;'}); + var recentPads = activepads.getActivePads(); + recentPads.forEach(function (info) { + var time = info.timestamp; // number + var pid = info.padId; + model.accessPadGlobal(pid, function(pad) { + if (pad.exists()) { + var numRevisions = pad.getHeadRevisionNumber(); + var connected = collab_server.getNumConnections(pad); + recentlyActiveTable.push( + TR(TD(B(pid)), + TD({style: 'font-style: italic;'}, timeAgo(time)), + TD(connected+" connected"), + TD(numRevisions+" revisions"), + TD(A({href: qpath({padId: pid, revtext: "HEAD"})}, "HEAD")), + TD(A({href: qpath({padId: pid})}, "inspect")), + TD(A({href: qpath({padId: pid, snoop: 1})}, "snoop")) + )); + } + }, "r"); + }); + response.write(recentlyActiveTable); + response.stop(); + } + if (startsWith(padId, '/')) { + padId = padId.substr(1); + } + if (request.params.snoop) { + sessions.setIsAnEtherpadAdmin(true); + response.redirect(_getPadUrl(padId)); + } + if (request.params.setsupportstimeslider) { + var v = (String(request.params.setsupportstimeslider).toLowerCase() == + 'true'); + model.accessPadGlobal(padId, function(pad) { + pad.setSupportsTimeSlider(v); + }); + response.write("on pad "+padId+": setSupportsTimeSlider("+v+")"); + response.stop(); + } + model.accessPadGlobal(padId, function(pad) { + if (! pad.exists()) { + response.write("Pad not found: /"+padId); + } + else { + var headRev = pad.getHeadRevisionNumber(); + var div = DIV({style: 'font-family: monospace;'}); + + if (request.params.revtext) { + var i; + if (request.params.revtext == "HEAD") { + i = headRev; + } else { + i = Number(request.params.revtext); + } + var infoObj = {}; + div.push(H2(A({href: request.path}, "PadInspector"), + ' > ', A({href: request.path+'?padId='+padId}, "/"+padId), + ' > ', "Revision ", i, "/", headRev, + SPAN({style: 'color: #949;'}, ' [ ', pad.getRevisionDate(i).toString(), ' ] '))); + div.push(H3("Browse Revisions: ", + ((i > 0) ? A({id: 'previous', href: qpath({revtext: (i-1)})}, '<< previous') : ''), + ' ', + ((i < pad.getHeadRevisionNumber()) ? A({id: 'next', href: qpath({revtext:(i+1)})}, 'next >>') : '')), + DIV({style: 'padding: 1em; border: 1px solid #ccc;'}, + pad.getRevisionText(i, infoObj))); + if (infoObj.badLastChar) { + div.push(P("Bad last character of text (not newline): "+infoObj.badLastChar)); + } + } else if (request.params.dumpstorage) { + div.push(P(collab_server.dumpStorageToString(pad))); + } else if (request.params.showlatest) { + div.push(P(pad.text())); + } else { + div.push(H2(A({href: request.path}, "PadInspector"), ' > ', "/"+padId)); + // no action + div.push(P(A({href: qpath({revtext: 'HEAD'})}, 'HEAD='+headRev))); + div.push(P(A({href: qpath({dumpstorage: 1})}, 'dumpstorage'))); + var supportsTimeSlider = pad.getSupportsTimeSlider(); + if (supportsTimeSlider) { + div.push(P(A({href: qpath({setsupportstimeslider: 'false'})}, 'hide slider'))); + } + else { + div.push(P(A({href: qpath({setsupportstimeslider: 'true'})}, 'show slider'))); + } + } + } + + var script = SCRIPT({type: 'text/javascript'}, html([ + '$(document).keydown(function(e) {', + ' var h = undefined;', + ' if (e.keyCode == 37) { h = $("#previous").attr("href"); }', + ' if (e.keyCode == 39) { h = $("#next").attr("href"); }', + ' if (h) { window.location.href = h; }', + '});' + ].join('\n'))); + + response.write(HTML( + HEAD(SCRIPT({type: 'text/javascript', src: '/static/js/jquery-1.3.2.js?'+(+(new Date))})), + BODY(div, script))); + }, "r"); +} + +function render_analytics() { + response.redirect("https://www.google.com/analytics/reporting/?reset=1&id=12611622"); +} + +//---------------------------------------------------------------- +// eepnet license display +//---------------------------------------------------------------- + +function render_eepnet_licenses() { + var data = sqlobj.selectMulti('eepnet_signups', {}, {orderBy: 'date'}); + var t = TABLE({border: 1, cellspacing: 0, cellpadding: 2}); + var cols = ['date','email','orgName','firstName','lastName', 'jobTitle','phone','estUsers']; + data.forEach(function(x) { + var tr = TR(); + cols.forEach(function(colname) { + tr.push(TD(x[colname])); + }); + t.push(tr); + }); + response.write(HTML(BODY({style: 'font-family: monospace;'}, t))); +} + +//---------------------------------------------------------------- +// pad integrity +//---------------------------------------------------------------- + +/*function render_changesettest_get() { + var nums = [0, 1, 2, 3, 0xfffffff, 0x02345678, 4]; + var str = Changeset.numberArrayToString(nums); + var result = Changeset.numberArrayFromString(str); + var resultArray = result[0]; + var remainingString = result[1]; + var bad = false; + if (remainingString) { + response.write(P("remaining string length is: "+remainingString.length)); + bad = true; + } + if (nums.length != resultArray.length) { + response.write(P("length mismatch: "+nums.length+" / "+resultArray.length)); + bad = true; + } + response.write(P(nums[2])); + for(var i=0;i"); + + sqlbase.createStringArrayTable("SEQUENCES"); + sqlbase.putStringArrayElement("SEQUENCES", "fibo", 0, "1"); + sqlbase.putStringArrayElement("SEQUENCES", "fibo", 1, "1"); + sqlbase.putStringArrayElement("SEQUENCES", "fibo", 2, "2"); + sqlbase.putStringArrayElement("SEQUENCES", "fibo", 3, "3"); + sqlbase.putStringArrayElement("SEQUENCES", "fibo", 4, "5"); + sqlbase.putStringArrayElement("SEQUENCES", "fibo", 30, "number30"); + sqlbase.putStringArrayElement("SEQUENCES", "fibo", 29, "number29"); + sqlbase.deleteStringArrayElement("SEQUENCES", "fibo", 29); + sqlbase.putConsecutiveStringArrayElements("SEQUENCES", "fibo", 19, [19,20,21,22]); + var a = []; + for(var i=0;i<31;i++) { + a.push(sqlbase.getStringArrayElement("SEQUENCES", "fibo", i)); + } + response.write(a.join(',')); // 1,1,2,3,5,,, ... 19,20,21,22, ... ,,,number30 + }); +}*/ + +function render_timings() { + var timer = Packages.net.appjet.ajstdlib.timer; + var opnames = timer.getOpNames(); + + response.write(P(A({href: '/ep/admin/timingsreset'}, "reset all"))); + + var t = TABLE({border: 1, cellspacing: 0, cellpadding: 3, style: 'font-family: monospace;'}); + t.push(TR(TH("operation"), + TH("sample_count"), + TH("total_ms"), + TH("avg_ms"))); + + function r(x) { + return sprintf("%09.2f", x); + } + var rows = []; + for (var i = 0; i < opnames.length; i++) { + var stats = timer.getStats(opnames[i]); + rows.push([String(opnames[i]), + Math.floor(stats[0]), + stats[1], + stats[2]]); + } + + var si = Number(request.params.sb || 0); + + rows.sort(function(a,b) { return cmp(b[si],a[si]); }); + + rows.forEach(function(row) { + t.push(TR(TD(row[0]), + TD(row[1]), + TD(r(row[2])), + TD(r(row[3])))); + }); + + response.write(t); +} + +function render_timingsreset() { + Packages.net.appjet.ajstdlib.timer.reset(); + response.redirect('/ep/admin/timings'); +} + +// function render_jsontest() { +// response.setContentType('text/plain; charset=utf-8'); + +// var a = []; +// a[0] = 5; +// a[1] = 6; +// a[9] = 8; +// a['foo'] = "should appear"; + +// jtest(a); + +// var obj1 = { +// a: 1, +// b: 2, +// q: [true,true,,,,,,false,false,,,,{},{a:{a:{a:{a:{a:{a:[[{a:{a:false}}]]}}}}}}], +// c: "foo", +// d: { +// nested: { obj: 'yo' }, +// bar: "baz" +// }, +// e: 3.6, +// 1: "numeric value", +// 2: "anohter numeric value", +// 2.46: "decimal numeric value", +// foo: 3.212312310, +// bar: 0.234242e-10, +// baz: null, +// ar: [{}, '1', [], [[[[]]]]], +// n1: null, +// n2: undefined, +// n3: false, +// n4: "null", +// n5: "undefined" +// }; + +// jtest(obj1); + +// var obj2 = { +// t1: 1232738532270 +// }; + +// jtest(obj2); + +// // a javascript object plus numeric ids +// var obj3 = {}; +// obj3["foo"] = "bar"; +// obj3[1] = "aaron"; +// obj3[2] = "iba"; + +// jtest(obj3); + +// function jtest(x) { +// response.write('----------------------------------------------------------------\n\n'); + +// var str1 = JSON.stringify(x); +// var str2 = fastJSON.stringify(x); + +// var str1_ = JSON.stringify(JSON.parse(str1)); +// var str2_ = fastJSON.stringify(fastJSON.parse(str2)); + +// response.write([str1,str2].join('\n') + '\n\n'); +// response.write([str1_,str2_].join('\n') + '\n\n'); +// } +// } + +function render_varz() { + var varzes = varz.getSnapshot(); + response.setContentType('text/plain; charset=utf-8'); + for (var k in varzes) { + response.write(k+': '+varzes[k]+'\n'); + } +} + +function render_extest() { + throw new Error("foo"); +} + + +function _diagnosticRecordToHtml(obj) { + function valToHtml(o, noborder) { + if (typeof (o) != 'object') { + return String(o); + } + var t = TABLE((noborder ? {} : {style: "border-left: 1px solid black; border-top: 1px solid black;"})); + if (typeof (o.length) != 'number') { + eachProperty(o, function(k, v) { + var tr = TR(); + tr.push(TD({valign: "top", align: "right"}, B(k))); + tr.push(TD(valToHtml(v))); + t.push(tr); + }); + } else { + if (o.length == 0) return "(empty array)"; + for (var i = 0; i < o.length; ++i) { + var tr = TR(); + tr.push(TD({valign: "top", align: "right"}, B(i))); + tr.push(TD(valToHtml(o[i]))); + t.push(tr); + } + } + return t; + } + return valToHtml(obj, true); +} + +function render_diagnostics() { + var start = Number(request.params.start || 0); + var count = Number(request.params.count || 100); + var diagnostic_entries = sqlbase.getAllJSON("PAD_DIAGNOSTIC", start, count); + var expandArray = request.params.expand || []; + + if (typeof (expandArray) == 'string') expandArray = [expandArray]; + var expand = {}; + for (var i = 0; i < expandArray.length; ++i) { + expand[expandArray[i]] = true; + } + + function makeLink(text, expand, collapse, start0, count0) { + start0 = (typeof(start0) == "number" ? start0 : start); + count0 = count0 || count; + collapse = collapse || []; + expand = expand || []; + + var collapseObj = {}; + for (var i = 0; i < collapse.length; ++i) { + collapseObj[collapse[i]] = true; + } + var expandString = + expandArray.concat(expand).filter(function(x) { return ! collapseObj[x] }).map(function(x) { return "expand="+encodeURIComponent(x) }).join("&"); + + var url = request.path + "?start="+start0+"&count="+count0+"&"+expandString+(expand.length == 1 ? "#"+md5(expand[0]) : ""); + + return A({href: url}, text); + } + + var t = TABLE({border: 1, cellpadding: 2, style: "font-family: monospace;"}); + diagnostic_entries.forEach(function(ent) { + var tr = TR() + tr.push(TD({valign: "top", align: "right"}, (new Date(Number(ent.id.split("-")[0]))).toString())); + tr.push(TD({valign: "top", align: "right"}, ent.id)); + if (expand[ent.id]) { + tr.push(TD(A({name: md5(ent.id)}, makeLink("(collapse)", false, [ent.id])), BR(), + _diagnosticRecordToHtml(ent.value))); + } else { + tr.push(TD(A({name: md5(ent.id)}, makeLink(_diagnosticRecordToHtml({padId: ent.value.padId, disconnectedMessage: ent.value.disconnectedMessage}), [ent.id])))); + } + t.push(tr); + }); + + var body = BODY(); + body.push(P("Showing entries ", start, "-", start+diagnostic_entries.length, ". ", + (start > 0 ? makeLink("Show previous "+count+".", [], [], start-count) : ""), + (diagnostic_entries.length == count ? makeLink("Show next "+count+".", [], [], start+count) : ""))); + body.push(t); + + response.write(HTML(body)); +} + +//---------------------------------------------------------------- +import("etherpad.billing.billing"); + +function render_testbillingdirect() { + var invoiceId = billing.createInvoice(); + var ret = billing.directPurchase(invoiceId, 0, 'EEPNET', 500, 'DISCOUNT', { + cardType: "Visa", + cardNumber: "4501251685453214", + cardExpiration: "042019", + cardCvv: "123", + nameSalutation: "Dr.", + nameFirst: "John", + nameMiddle: "D", + nameLast: "Zamfirescu", + nameSuffix: "none", + addressStreet: "531 Main St. Apt. 1227", + addressStreet2: "", + addressCity: "New York", + addressState: "NY", + addressCountry: "US", + addressZip: "10044" + }, "https://"+request.host+"/ep/about/testbillingnotify"); + if (ret.status == 'success') { + response.write(P("Success! Invoice id: "+ret.purchaseInfo.invoiceId+" for "+ret.purchaseInfo.cost)); + } else { + response.write(P("Failure: "+ret.toSource())) + } +} + +function render_testbillingrecurring() { + var invoiceId = billing.createInvoice(); + var ret = billing.directPurchase(invoiceId, 0, 'EEPNET', 1, 'DISCOUNT', { + cardType: "Visa", + cardNumber: "4501251685453214", + cardExpiration: "042019", + cardCvv: "123", + nameSalutation: "Dr.", + nameFirst: "John", + nameMiddle: "D", + nameLast: "Zamfirescu", + nameSuffix: "none", + addressStreet: "531 Main St. Apt. 1227", + addressStreet2: "", + addressCity: "New York", + addressState: "NY", + addressCountry: "US", + addressZip: "10044" + }, "https://"+request.host+"/ep/about/testbillingnotify", true); + if (ret.status == 'success') { + var transactionId = billing.getTransaction(ret.purchaseInfo.transactionId).txnId; + var purchaseId = ret.purchaseInfo.purchaseId; + response.write(P("Direct billing successful. PayPal transaction id: ", transactionId)); + + invoiceId = billing.createInvoice(); + ret = billing.asyncRecurringPurchase( + invoiceId, purchaseId, transactionId, 500, + "https://"+request.host+"/ep/about/testbillingnotify"); + if (ret.status == 'success') { + response.write(P("Woot! Recurrent billing successful! ", ret.purchaseInfo.invoiceId, " for ", ret.purchaseInfo.cost)); + } else { + response.write(P("Failure: "+ret.toSource())); + } + } else { + response.write("Direct billing failure: "+ret.toSource()); + } +} + +function render_testbillingexpress() { + var urlPrefix = "http://"+request.host+request.path; + var session = sessions.getSession(); + var notifyUrl = "http://"+request.host+"/ep/about/testbillingnotify"; + + switch (request.params.step) { + case '0': + response.write(P("You'll be charged $400 for EEPNET. Click the link below to go to paypal.")); + response.write(A({href: urlPrefix+"?step=1"}, "Link")); + break; + case '1': + var ret = billing.beginExpressPurchase(1, 'EEPNET', 400, 'DISCOUNT', urlPrefix+"?step=2", urlPrefix+"?step=0", notifyUrl); + if (ret.status != 'success') { + response.write("Error: "+ret.debug.toSource()); + response.stop(); + } + session.purchaseInfo = ret.purchaseInfo; + response.redirect(paypalPurchaseUrl(ret.purchaseInfo.token)); + break; + case '2': + var ret = billing.continueExpressPurchase(session.purchaseInfo); + if (! ret.status == 'success') { + response.write("Error: "+ret.debug.toSource()); + response.stop(); + } + session.payerInfo = ret.payerInfo; + + response.write(P("You approved the transaction. Click 'confirm' to confirm.")); + response.write(A({href: urlPrefix+"?step=3"}, "Confirm")); + break; + case '3': + var ret = billing.completeExpressPurchase(session.purchaseInfo, session.payerInfo, notifyUrl); + if (ret.status == 'failure') { + response.write("Error: "+ret.debug.toSource()); + response.stop(); + } + if (ret.status == 'pending') { + response.write("Your charge is pending. You will be notified by email when your payment clears. Your invoice number is "+session.purchaseInfo.invoiceId); + response.stop(); + } + + response.write(P("Purchase completed: invoice # is "+session.purchaseInfo.invoiceId+" for "+session.purchaseInfo.cost)); + break; + default: + response.redirect(request.path+"?step=0"); + } +} + +//---------------------------------------------------------------- + +function render_genlicense_get() { + + var t = TABLE({border: 1}); + function ti(id, label) { + t.push(TR(TD({align: "right"}, LABEL({htmlFor: id}, label+":")), + TD(INPUT({id: id, name: id, type: 'text', size: 40})))); + } + + ti("name", "Name of Licensee"); + ti("org", "Name of Organization"); + ti("userQuota", "User Quota"); + + t.push(TR(TD({align: "right"}, LABEL("Software Edtition:")), + TD( SELECT({name: "edition"}, + OPTION({value: licensing.getEditionId('PRIVATE_NETWORK_EVALUATION')}, + "Private Network EVALUATION"), + OPTION({value: licensing.getEditionId('PRIVATE_NETWORK')}, + "Private Network"))))); + + ti("expdays", "Number of days until expiration\n(leave blank if never expires)"); + + t.push(TR(TD({colspan: 2}, INPUT({type: "submit"})))); + + var f = FORM({action: request.path, method: "post"}); + f.push(t); + + response.write(HTML(BODY(f))); +} + +function render_genlicense_post() { + var name = request.params.name; + var org = request.params.org; + var editionId = +request.params.edition; + var editionName = licensing.getEditionName(editionId); + var userQuota = +request.params.userQuota; + + var expiresTime = null; + if (request.params.expdays) { + expiresTime = +(new Date) + 1000*60*60*24*(+request.params.expdays); + } + + var licenseKey = licensing.generateNewKey( + name, + org, + expiresTime, + editionId, + userQuota + ); + + // verify + if (!licensing.isValidKey(licenseKey)) { + throw Error("License key I just created is not valid: "+licenseKey); + } + + // TODO: write to database?? + // + + // display + var licenseInfo = licensing.decodeLicenseInfoFromKey(licenseKey); + var t = TABLE({border: 1}); + function line(k, v) { + t.push(TR(TD({align: "right"}, k+":"), + TD(v))); + } + + var key = licenseKey.split(":")[2]; + if ((key.length % 2) != 0) { + key = key + "+"; + } + var keyLine1 = key.substr(0, key.length/2); + var keyLine2 = key.substr(key.length/2, key.length); + + line("Name", licenseInfo.personName); + line("Organization", licenseInfo.organizationName); + line("Key", P(keyLine1, BR(), keyLine2)); + line("Software Edition", licenseInfo.editionName); + line("User Quota", licenseInfo.userQuota); + line("Expires", (+licenseInfo.expiresDate > 0) ? licenseInfo.expiresDate.toString() : "(never)"); + + response.write(HTML(BODY(t))); +} + +//---------------------------------------------------------------- + +import("etherpad.metrics.metrics"); + +function render_flows() { + if (request.params.imgId && getSession()[request.params.imgId]) { + var arr = getSession()[request.params.imgId]; + metrics[arr[0]](arr[1], Array.prototype.slice.call(arr[2])); + response.stop(); + } + + function drawHistogram(name, h) { + var imgKey = Math.round(Math.random()*1e12); + print(IMG({src: request.path+"?imgId="+imgKey})); + getSession()[imgKey] = ["respondWithPieChart", name, h]; + } + + var body = BODY(); + function print() { + for (var i = 0; i < arguments.length; ++i) { + body.push(arguments[i]); + } + } + + var [startDate, endDate] = [7, 1].map(function(a) { return new Date(Date.now() - 86400*1000*a); }); + + var allFlows = metrics.getFlows(startDate, endDate); + +/* + print(P("All flows:")); + + eachProperty(allFlows, function(k, flows) { + print(P(k, html(" » "))); + flows.forEach(function(flow) { + print(P(flow.toString())); + }); + }); + response.write(HTML(body)); + return; +*/ + + print(P("Parsing logs from: "+startDate+" through "+endDate)); + + var fs = + [metrics.getFunnel(startDate, endDate, ['/ep/about/pricing', '/ep/about/pricing-eepnet', '/ep/store/eepnet-eval-signup'], true), + metrics.getFunnel(startDate, endDate, ['/ep/about/pricing', '/ep/about/pricing-free'], true), + metrics.getFunnel(startDate, endDate, ['/ep/about/pricing', '/ep/about/pricing-eepod'], true), + metrics.getFunnel(startDate, endDate, ['/ep/about/pricing', '/ep/store/eepnet-eval-signup'], true), + metrics.getFunnel(startDate, endDate, ['/', '(pad)']), + metrics.getFunnel(startDate, endDate, ['/', '/ep/pad/newpad'], true), + metrics.getFunnel(startDate, endDate, ['/ep/about/screencast', '(pad)'])]; + + function vcnt(i, i2) { + return fs[i].visitorCounts[i2]; + } + function pct(f) { + return ""+Math.round(f*10000)/100+"%" + } + function cntAndPct(i, i2) { + if (i2 === undefined) { i2 = 1; } + return ""+vcnt(i, i2)+" ("+pct(vcnt(i, i2)/vcnt(i, i2-1))+")"; + } + print(P("Of ", vcnt(0, 0), " visitors to the pricing page, ", + cntAndPct(0), " of them viewed eepnet, (", cntAndPct(0, 2), " of those downloaded), ", + cntAndPct(1), " of them viewed free, and ", + cntAndPct(2), " of them viewed eepod. ", + cntAndPct(3), " of them clicked on the eval signup link straight up." + ), + P("Of ", vcnt(4, 0), " visitors to the home page, ", + cntAndPct(4), " of them went to a pad page in the same flow; ", + cntAndPct(5), " of them clicked the new pad button immediately."), + P("Of ", vcnt(6, 0), " vistitors to the screencast page, ", + cntAndPct(6), " of them visisted a pad page in the same flow.")); + + var origins = metrics.getOrigins(startDate, endDate, true); + print(P("Flow first origins: ")); + drawHistogram("first origins", origins.flowFirsts); + + var firstHits = metrics.getOrigins(startDate, endDate, false, true); + var padFirstHits = 0; + var nonPadFirstHits = 0; + print(P("First paths hit: ")); + drawHistogram("first paths", firstHits.flowFirsts); + firstHits.flowFirsts.filter(function(x) { + if (x.value != '/' && ! startsWith(x.value, "/ep/")) { + padFirstHits += x.count; + return false; + } + nonPadFirstHits += x.count; + return true; + }); + print(P("Some pad page: "+padFirstHits), + P("Non-pad page: "+nonPadFirstHits)); + + var exitsFromHomepage = metrics.getExits(startDate, endDate, '/', true); + print(P("Exits from homepage: ")); + drawHistogram("exits", exitsFromHomepage.histogram) + + response.write(HTML(body)); +} + +//---------------------------------------------------------------- + +import("etherpad.pad.pad_migrations"); + +function render_padmigrations() { + var residue = (request.params.r || 0); + var modulus = (request.params.m || 1); + var name = (request.params.n || (residue+"%"+modulus)); + pad_migrations.runBackgroundMigration(residue, modulus, name); + response.write("done"); + return true; +} + +// TODO: add ability to delete entries? +// TODO: show sizes? +function render_cachebrowser() { + var path = request.params.path; + if (path && path.charAt(0) == ',') { + path = path.substr(1); + } + var pathArg = (path || ""); + var c = appjet.cache; + if (path) { + path.split(",").forEach(function(part) { + c = c[part]; + }); + } + + var d = DIV({style: 'font-family: monospace; text-decoration: none;'}); + + d.push(H3("appjet.cache --> "+pathArg.split(",").join(" --> "))); + + var t = TABLE({border: 1}); + keys(c).sort().forEach(function(k) { + var v = c[k]; + if (v && (typeof(v) == 'object') && (!v.getDate)) { + t.push(TR(TD(A({style: 'text-decoration: none;', + href: request.path+"?path="+pathArg+","+k}, k)))); + } else { + t.push(TR(TD(k), TD(v))); + } + }); + + d.push(t); + response.write(d); +} + +function render_pne_tracker_get() { + var data = sqlobj.selectMulti('pne_tracking_data', {}, {}); + data.sort(function(x, y) { return cmp(y.date, x.date); }); + + var t = TABLE(); + + var headrow = TR(); + ['date', 'remote host', 'keyHash', 'name', 'value'].forEach(function(x) { + headrow.push(TH({align: "left", style: "padding: 0 6px;"}, x)); + }); + t.push(headrow); + + data.forEach(function(d) { + var tr = TR(); + + tr.push(TD(d.date.toString().split(' ').slice(0,5).join('-'))); + + if (d.remoteIp) { + tr.push(TD(netutils.getHostnameFromIp(d.remoteIp) || d.remoteIp)); + } else { + tr.push(TD("-")); + } + + if (d.keyHash) { + tr.push(TD(A({href: '/ep/admin/pne-tracker-lookup-keyhash?hash='+d.keyHash}, d.keyHash))); + } else { + tr.push(TD("-")); + } + + tr.push(TD(d.name)); + tr.push(TD(d.value)); + + t.push(tr); + }); + + response.write(HTML(HEAD(html(""), + BODY({style: "font-family: monospace; font-size: 12px;"}, t)))); +} + +function render_pne_tracker_lookup_keyhash_get() { + var hash = request.params.hash; + // brute force it + var allLicenses = sqlobj.selectMulti('eepnet_signups', {}, {}); + var record = null; + var i = 0; + while (i < allLicenses.length && record == null) { + var d = allLicenses[i]; + if (md5(d.licenseKey).substr(0, 16) == hash) { + record = d; + } + i++; + } + if (!record) { + response.write("Not found. Perhaps this was a test download from local development, or a paid customer whose licenses we don't currently look through on this page."); + } else { + var kl = keys(record).sort(); + var t = TABLE(); + kl.forEach(function(k) { + t.push(TR(TH({align: "right"}, k+":"), + TD({style: "padding-left: 1em;"}, record[k]))); + }); + response.write(HTML(BODY(DIV({style: "font-family: monospace;"}, + DIV(H1("Trial Signup Record:")), t)))); + } +} + +function render_reload_blog_db_get() { + var d = DIV(); + if (request.params.ok) { + d.push(DIV(P("OK"))); + } + d.push(FORM({method: "post", action: request.path}, + INPUT({type: "submit", value: "Reload Blog DB Now"}))); + response.write(HTML(BODY(d))); +} + +function render_reload_blog_db_post() { + blogcontrol.reloadBlogDb(); + response.redirect(request.path+"?ok=1"); +} + +function render_pro_domain_accounts() { + var accounts = sqlobj.selectMulti('pro_accounts', {}, {}); + var domains = sqlobj.selectMulti('pro_domains', {}, {}); + + // build domain map + var domainMap = {}; + domains.forEach(function(d) { domainMap[d.id] = d; }); + accounts.sort(function(a,b) { return cmp(b.lastLoginDate, a.lastLoginDate); }); + + var b = BODY({style: "font-family: monospace;"}); + b.push(accounts.length + " pro accounts."); + var t = TABLE({border: 1}); + t.push(TR(TH("email"), + TH("domain"), + TH("lastLogin"))); + accounts.forEach(function(u) { + t.push(TR(TD(u.email), + TD(domainMap[u.domainId].subDomain+"."+request.domain), + TD(u.lastLoginDate))); + }); + + b.push(t); + + response.write(HTML(b)); +} + + +function render_beta_valve_get() { + var d = DIV( + P("Beta Valve Status: ", + (pro_beta_control.isValveOpen() ? + SPAN({style: "color: green;"}, B("OPEN")) : + SPAN({style: "color: red;"}, B("CLOSED")))), + P(FORM({action: '/ep/admin/beta-valve-toggle', method: "post"}, + BUTTON({type: "submit"}, "Toggle")))); + + var t = TABLE({border: 1, cellspacing: 0, cellpadding: 4, style: "font-family: monospace;"}); + var signupList = sqlobj.selectMulti('pro_beta_signups', {}, {}); + signupList.sort(function(a, b) { + return cmp(b.signupDate, a.signupDate); + }); + + d.push(HR()); + + if (getSession().betaAdminMessage) { + d.push(DIV({style: "border: 1px solid #ccc; padding: 1em; background: #eee;"}, + getSession().betaAdminMessage)); + delete getSession().betaAdminMessage; + } + + d.push(P(signupList.length + " beta signups")); + + d.push(FORM({action: '/ep/admin/beta-invite-multisend', method: 'post'}, + P("Send ", INPUT({type: 'text', name: 'count', size: 3}), " invites."), + INPUT({type: "submit"}))); + + t.push(TR(TH("id"), TH("email"), TH("signupDate"), + TH("activationDate"), TH("activationCode"), TH(' '))); + + signupList.forEach(function(s) { + var tr = TR(); + tr.push(TD(s.id), + TD(s.email), + TD(s.signupDate), + TD(s.isActivated ? s.activationDate : "-"), + TD(s.activationCode)); + if (!s.activationCode) { + tr.push(TD(FORM({action: '/ep/admin/beta-invite-send', method: 'post'}, + INPUT({type: 'hidden', name: 'id', value: s.id}), + INPUT({type: 'submit', value: "Send Invite"})))); + } else { + tr.push(TD(' ')); + } + t.push(tr); + }); + d.push(t); + response.write(d); +} + +function render_beta_valve_toggle_post() { + pro_beta_control.toggleValve(); + response.redirect('/ep/admin/beta-valve'); +} + +function render_beta_invite_send_post() { + var id = request.params.id; + pro_beta_control.sendInvite(id); + response.redirect('/ep/admin/beta-valve'); +} + +function render_beta_invite_multisend_post() { + var count = request.params.count; + var signupList = sqlobj.selectMulti('pro_beta_signups', {}, {}); + signupList.sort(function(a, b) { + return cmp(a.signupDate, b.signupDate); + }); + var sent = 0; + for (var i = 0; ((i < signupList.length) && (sent < count)); i++) { + var record = signupList[i]; + if (!record.activationCode) { + pro_beta_control.sendInvite(record.id); + sent++; + } + } + getSession().betaAdminMessage = (sent+" invites sent."); + response.redirect('/ep/admin/beta-valve'); +} + +function render_usagestats() { + response.redirect("/ep/admin/usagestats/"); +} + +function render_exceptions() { + exceptions.render(); +} + +function render_setadminmode() { + sessions.setIsAnEtherpadAdmin( + String(request.params.v).toLowerCase() == "true"); + response.redirect("/ep/admin/"); +} + +// -------------------------------------------------------------- +// billing-related +// -------------------------------------------------------------- + +// some of these functions are only used from selenium tests, and so have no UI. + +function render_setdomainpaidthrough() { + var domainName = request.params.domain; + var when = new Date(Number(request.params.paidthrough)); + if (! domainName || ! when) { + response.write("fail"); + response.stop(); + } + var domain = domains.getDomainRecordFromSubdomain(domainName); + var domainId = domain.id; + + var subscription = team_billing.getSubscriptionForCustomer(domainId); + if (subscription) { + billing.updatePurchase(subscription.id, {paidThrough: when}); + team_billing.domainCacheClear(domainId); + response.write("OK"); + } else { + response.write("fail"); + } +} + +function render_runsubscriptions() { + team_billing.processAllSubscriptions(); + response.write("OK"); +} + +function render_reset_subscription() { + var body = BODY(); + if (request.isGet) { + body.push(FORM({method: "POST"}, + "Subdomain: ", INPUT({type: "text", name: "subdomain"}), BUTTON({name: "clear"}, "Go"))); + } else if (request.isPost) { + if (! request.params.confirm) { + var domain = domains.getDomainRecordFromSubdomain(request.params.subdomain); + var admins = pro_accounts.listAllDomainAdmins(domain.id); + body.push(P("Domain ", domain.subDomain, ".", request.domain, "; admins:")); + var p = UL(); + admins.forEach(function(admin) { + p.push(LI(admin.fullName, " <", admin.email, ">")); + }); + body.push(p); + var subscription = team_billing.getSubscriptionForCustomer(domain.id); + if (subscription) { + body.push(P("Subscription is currently ", subscription.status, ", and paid through: ", checkout.formatDate(subscription.paidThrough), ".")) + body.push(FORM({method: "POST"}, + INPUT({type: "hidden", name: "subdomain", value: request.params.subdomain}), + "Are you sure? ", BUTTON({name: "confirm", value: "yes"}, "YES"))); + } else { + body.push(P("No current subscription")); + } + } else { + var domain = domains.getDomainRecordFromSubdomain(request.params.subdomain); + sqlcommon.inTransaction(function() { + team_billing.resetMaxUsers(domain.id); + sqlobj.deleteRows('billing_purchase', {customer: domain.id, type: 'subscription'}); + team_billing.domainCacheClear(domain.id); + team_billing.clearRecurringBillingInfo(domain.id); + }); + body.push("Done!") + } + } + body.push(A({href: request.path}, html("« back"))); + response.write(HTML(body)); +} \ No newline at end of file diff --git a/trunk/etherpad/src/etherpad/control/blogcontrol.js b/trunk/etherpad/src/etherpad/control/blogcontrol.js new file mode 100644 index 0000000..9ec485d --- /dev/null +++ b/trunk/etherpad/src/etherpad/control/blogcontrol.js @@ -0,0 +1,199 @@ +/** + * 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. + */ + +//blogcontrol + +import("jsutils.*"); +import("atomfeed"); +import("funhtml.*"); + +import("etherpad.globals.*"); +import("etherpad.utils.*"); +import("etherpad.quotas"); + +//---------------------------------------------------------------- +// bloghelpers +//---------------------------------------------------------------- +bloghelpers = {}; +bloghelpers.disqusDeveloper = function() { + if (isProduction()) { + return ''; + } + return [ + '' + ].join('\n'); +}; + +bloghelpers.feedburnerUrl = function() { + var name = isProduction() ? "TheEtherPadBlog" : "TheEtherPadBlogDev"; + return "http://feeds.feedburner.com/"+name; +}; + +bloghelpers.feedLink = function() { + return [ + '' + ].join(''); +}; + +bloghelpers.dfmt = function(d) { + return d.toString().split(' ').slice(0,3).join(' '); +}; + +bloghelpers.feedbuttonHtml = function() { + var aProps = { + href: bloghelpers.feedburnerUrl(), + rel: "alternate", + type: "application/rss+xml" + }; + + return SPAN(A(aProps, + IMG({src: "http://www.feedburner.com/fb/images/pub/feed-icon32x32.png", + alt: "EtherPad Blog Feed", + style: "vertical-align:middle; border:0;"}))).toHTML(); +}; + +bloghelpers.getMaxUsersPerPad = function() { + return quotas.getMaxSimultaneousPadEditors() +}; + +//---------------------------------------------------------------- +// posts "database" +//---------------------------------------------------------------- + +function _wrapPost(p) { + var wp = {}; + keys(p).forEach(function(k) { wp[k] = p[k]; }); + wp.url = function() { + return "http://"+request.host+"/ep/blog/posts/"+p.id; + }; + wp.renderContent = function() { + return renderTemplateAsString("blog/posts/"+p.id+".ejs", + {post: wp, bloghelpers: bloghelpers}); + }; + return wp; +} + +function _addPost(id, title, author, published, updated) { + if (!appjet.cache.blogDB) { + appjet.cache.blogDB = { + posts: [], + postMap: {} + }; + } + var p = {id: id, title: title, author: author, published: published, updated: updated}; + appjet.cache.blogDB.posts.push(p); + appjet.cache.blogDB.postMap[p.id] = p; +} + +function _getPostById(id) { + var p = appjet.cache.blogDB.postMap[id]; + if (!p) { return null; } + return _wrapPost(p); +} + +function _getAllPosts() { + return []; +} + +function _sortBlogDB() { + appjet.cache.blogDB.posts.sort(function(a,b) { return cmp(b.published, a.published); }); +} + +//---------------------------------------------------------------- +// Posts +//---------------------------------------------------------------- + +function _initBlogDB() { + return; +} + +function reloadBlogDb() { + delete appjet.cache.blogDB; + _initBlogDB(); +} + +function onStartup() { + reloadBlogDb(); +} + +//---------------------------------------------------------------- +// onRequest +//---------------------------------------------------------------- +function onRequest(name) { + // nothing yet. +} + +//---------------------------------------------------------------- +// main +//---------------------------------------------------------------- +function render_main() { + renderFramed('blog/blog_main_body.ejs', + {posts: _getAllPosts(), bloghelpers: bloghelpers}); +} + +//---------------------------------------------------------------- +// render_feed +//---------------------------------------------------------------- +function render_feed() { + var lastModified = new Date(); // TODO: most recent of all entries modified + + var entries = []; + _getAllPosts().forEach(function(post) { + entries.push({ + title: post.title, + author: post.author, + published: post.published, + updated: post.updated, + href: post.url(), + content: post.renderContent() + }); + }); + + response.setContentType("application/atom+xml; charset=utf-8"); + + response.write(atomfeed.renderFeed( + "The EtherPad Blog", new Date(), entries, + "http://"+request.host+"/ep/blog/")); +} + +//---------------------------------------------------------------- +// render_post +//---------------------------------------------------------------- +function render_post(name) { + var p = _getPostById(name); + if (!p) { + return false; + } + renderFramed('blog/blog_post_body.ejs', { + post: p, bloghelpers: bloghelpers, + posts: _getAllPosts() + }); + return true; +} + +//---------------------------------------------------------------- +// render_new_from_etherpad() +//---------------------------------------------------------------- + +function render_new_from_etherpad() { + return ""; +} + diff --git a/trunk/etherpad/src/etherpad/control/connection_diagnostics_control.js b/trunk/etherpad/src/etherpad/control/connection_diagnostics_control.js new file mode 100644 index 0000000..aaa1bb3 --- /dev/null +++ b/trunk/etherpad/src/etherpad/control/connection_diagnostics_control.js @@ -0,0 +1,87 @@ +/** + * 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. + */ + +import("etherpad.utils.*"); +import("etherpad.helpers.*"); + +//---------------------------------------------------------------- +// Connection diagnostics +//---------------------------------------------------------------- + +/* +function _getDiagnosticsCollection() { + var db = storage.getRoot("connection_diagnostics"); + if (!db.diagnostics) { + db.diagnostics = new StorableCollection(); + } + return db.diagnostics; +} +*/ + +function render_main_get() { + /* + var diagnostics = _getDiagnosticsCollection(); + + var data = new StorableObject({ + ip: request.clientAddr, + userAgent: request.headers['User-Agent'] + }); + + diagnostics.add(data); + + helpers.addClientVars({ + diagnosticStorableId: data.id + }); +*/ + renderFramed("main/connection_diagnostics_body.ejs"); +} + +function render_submitdata_post() { + response.setContentType('text/plain; charset=utf-8'); + /* + var id = request.params.diagnosticStorableId; + var storedData = storage.getStorable(id); + if (!storedData) { + response.write("Error retreiving diagnostics record."); + response.stop(); + } + var diagnosticData = JSON.parse(request.params.dataJson); + eachProperty(diagnosticData, function(k,v) { + storedData[k] = v; + }); +*/ + response.write("OK"); +} + +function render_submitemail_post() { + response.setContentType('text/plain; charset=utf-8'); + /* + var id = request.params.diagnosticStorableId; + var data = storage.getStorable(id); + if (!data) { + response.write("Error retreiving diagnostics record."); + response.stop(); + } + var email = request.params.email; + if (!isValidEmail(email)) { + response.write("Invalid email address."); + response.stop(); + } + data.email = email; +*/ + response.write("OK"); +} + diff --git a/trunk/etherpad/src/etherpad/control/global_pro_account_control.js b/trunk/etherpad/src/etherpad/control/global_pro_account_control.js new file mode 100644 index 0000000..65d2124 --- /dev/null +++ b/trunk/etherpad/src/etherpad/control/global_pro_account_control.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. + */ + +import("funhtml.*"); +import("stringutils"); +import("stringutils.*"); +import("email.sendEmail"); +import("cache_utils.syncedWithCache"); + +import("etherpad.utils.*"); +import("etherpad.sessions.getSession"); + +import("etherpad.pro.domains"); +import("etherpad.pro.pro_accounts"); +import("etherpad.pro.pro_utils"); + +jimport("java.lang.System.out.println"); + +function onRequest() { + if (!getSession().oldFormData) { + getSession().oldFormData = {}; + } + return false; // not handled yet. +} + +function _errorDiv() { + var m = getSession().proAccountControlError; + delete getSession().proAccountControlError; + if (m) { + return DIV({className: "error"}, m); + } + return ""; +} + +function _redirectError(m) { + getSession().proAccountControlError = m; + response.redirect(request.path); +} + + +function render_main_get() { + response.redirect('/ep/pro-account/sign-in'); +} + +function render_sign_in_get() { + renderFramed('pro-account/sign-in.ejs', { + oldData: getSession().oldFormData, + errorDiv: _errorDiv + }); +} + + +function render_sign_in_post() { + var email = trim(request.params.email); + var password = request.params.password; + var subDomain = request.params.subDomain; + + subDomain = subDomain.toLowerCase(); + + getSession().oldFormData.email = email; + getSession().oldFormData.subDomain = subDomain; + + var domainRecord = domains.getDomainRecordFromSubdomain(subDomain); + if (!domainRecord) { + _redirectError("Site address not found: "+subDomain+"."+request.host); + } + + var instantSigninKey = stringutils.randomString(20); + syncedWithCache('global_signin_passwords', function(c) { + c[instantSigninKey] = { + email: email, + password: password + }; + }); + + response.redirect( + "https://"+subDomain+"."+httpsHost(request.host)+ + "/ep/account/sign-in?instantSigninKey="+instantSigninKey); +} + +function render_recover_get() { + renderFramed('pro-account/recover.ejs', { + oldData: getSession().oldFormData, + errorDiv: _errorDiv + }); +} + +function render_recover_post() { + + function _recoverLink(accountRecord, domainRecord) { + var host = (domainRecord.subDomain + "." + httpsHost(request.host)); + return ( + "https://"+host+"/ep/account/forgot-password?instantSubmit=1&email="+ + encodeURIComponent(accountRecord.email)); + } + + var email = trim(request.params.email); + + // lookup all domains associated with this email + var accountList = pro_accounts.getAllAccountsWithEmail(email); + println("account records matching ["+email+"]: "+accountList.length); + + var domainList = []; + for (var i = 0; i < accountList.length; i++) { + domainList[i] = domains.getDomainRecord(accountList[i].domainId); + } + + if (accountList.length == 0) { + _redirectError("No accounts were found associated with the email address \""+email+"\"."); + } + if (accountList.length == 1) { + response.redirect(_recoverLink(accountList[0], domainList[0])); + } + if (accountList.length > 1) { + var fromAddr = '"EtherPad" '; + var subj = "EtherPad: account information"; + var body = renderTemplateAsString( + 'pro/account/global-multi-domain-recover-email.ejs', { + accountList: accountList, + domainList: domainList, + recoverLink: _recoverLink, + email: email + } + ); + sendEmail(email, fromAddr, subj, {}, body); + pro_utils.renderFramedMessage("Instructions have been sent to "+email+"."); + } +} + + diff --git a/trunk/etherpad/src/etherpad/control/historycontrol.js b/trunk/etherpad/src/etherpad/control/historycontrol.js new file mode 100644 index 0000000..a78cfad --- /dev/null +++ b/trunk/etherpad/src/etherpad/control/historycontrol.js @@ -0,0 +1,226 @@ +/** + * 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. + */ + +import("fastJSON"); +import("etherpad.utils.render404"); +import("etherpad.pad.model"); +import("etherpad.collab.collab_server"); +import("etherpad.collab.ace.easysync2.*"); +import("jsutils.eachProperty"); + +function _urlCache() { + if (!appjet.cache.historyUrlCache) { + appjet.cache.historyUrlCache = {}; + } + return appjet.cache.historyUrlCache; +} + +function _replyWithJSONAndCache(obj) { + obj.apiversion = _VERSION; + var output = fastJSON.stringify(obj); + _urlCache()[request.path] = output; + response.write(output); + response.stop(); +} + +function _replyWithJSON(obj) { + obj.apiversion = _VERSION; + response.write(fastJSON.stringify(obj)); + response.stop(); +} + +function _error(msg, num) { + _replyWithJSON({error: String(msg), errornum: num}); +} + +var _VERSION = 1; + +var _ERROR_REVISION_NUMBER_TOO_LARGE = 14; + +function _do_text(padId, r) { + if (! padId) render404(); + model.accessPadGlobal(padId, function(pad) { + if (! pad.exists()) { + render404(); + } + if (r > pad.getHeadRevisionNumber()) { + _error("Revision number too large", _ERROR_REVISION_NUMBER_TOO_LARGE); + } + var text = pad.getInternalRevisionText(r); + text = _censorText(text); + _replyWithJSONAndCache({ text: text }); + }); +} + +function _do_stat(padId) { + var obj = {}; + if (! padId) { + obj.exists = false; + } + else { + model.accessPadGlobal(padId, function(pad) { + if (! pad.exists()) { + obj.exists = false; + } + else { + obj.exists = true; + obj.latestRev = pad.getHeadRevisionNumber(); + } + }); + } + _replyWithJSON(obj); +} + +function _censorText(text) { + // may not change length of text + return text.replace(/(http:\/\/pad.spline.inf.fu-berlin.de\/)(\w+)/g, function(url, u1, u2) { + return u1 + u2.replace(/\w/g, '-'); + }); +} + +function _do_changes(padId, first, last) { + if (! padId) render404(); + + var charPool = []; + var changeList = []; + + function charPoolText(txt) { + charPool.push(txt); + return _encodeVarInt(txt.length); + } + + model.accessPadGlobal(padId, function(pad) { + + if (first > pad.getHeadRevisionNumber() || last > pad.getHeadRevisionNumber()) { + _error("Revision number too large", _ERROR_REVISION_NUMBER_TOO_LARGE); + } + + var curAText = Changeset.makeAText("\n"); + if (first > 0) { + curAText = pad.getInternalRevisionAText(first - 1); + } + curAText.text = _censorText(curAText.text); + var lastTimestamp = null; + for(var r=first;r<=last;r++) { + var binRev = []; + var timestamp = +pad.getRevisionDate(r); + binRev.push(_encodeTimeStamp(timestamp, lastTimestamp)); + lastTimestamp = timestamp; + binRev.push(_encodeVarInt(1)); // fake author + + var c = pad.getRevisionChangeset(r); + var splices = Changeset.toSplices(c); + splices.forEach(function (splice) { + var startChar = splice[0]; + var endChar = splice[1]; + var newText = splice[2]; + oldText = curAText.text.substring(startChar, endChar); + + if (oldText.length == 0) { + binRev.push('+'); + binRev.push(_encodeVarInt(startChar)); + binRev.push(charPoolText(newText)); + } + else if (newText.length == 0) { + binRev.push('-'); + binRev.push(_encodeVarInt(startChar)); + binRev.push(charPoolText(oldText)); + } + else { + binRev.push('*'); + binRev.push(_encodeVarInt(startChar)); + binRev.push(charPoolText(oldText)); + binRev.push(charPoolText(newText)); + } + }); + changeList.push(binRev.join('')); + + curAText = Changeset.applyToAText(c, curAText, pad.pool()); + } + + _replyWithJSONAndCache({charPool: charPool.join(''), changes: changeList.join(',')}); + + }); +} + +function render_history(padOpaqueRef, rest) { + if (_urlCache()[request.path]) { + response.write(_urlCache()[request.path]); + response.stop(); + return true; + } + var padId; + if (padOpaqueRef == "CSi1xgbFXl" || padOpaqueRef == "13sentences") { + // made-up, hard-coded opaque ref, should be a table for these + padId = "jbg5HwzUX8"; + } + else if (padOpaqueRef == "dO1j7Zf34z" || padOpaqueRef == "foundervisa") { + // made-up, hard-coded opaque ref, should be a table for these + padId = "3hS7kQyDXG"; + } + else { + padId = null; + } + var regexResult; + if ((regexResult = /^stat$/.exec(rest))) { + _do_stat(padId); + } + else if ((regexResult = /^text\/(\d+)$/.exec(rest))) { + var r = Number(regexResult[1]); + _do_text(padId, r); + } + else if ((regexResult = /^changes\/(\d+)-(\d+)$/.exec(rest))) { + _do_changes(padId, Number(regexResult[1]), Number(regexResult[2])); + } + else { + return false; + } +} + +function _encodeVarInt(num) { + var n = +num; + if (isNaN(n)) { + throw new Error("Can't encode non-number "+num); + } + var chars = []; + var done = false; + while (! done) { + if (n < 32) done = true; + var nd = (n % 32); + if (chars.length > 0) { + // non-first, will become non-last digit + nd = (nd | 32); + } + chars.push(_BASE64_DIGITS[nd]); + n = Math.floor(n / 32) + } + return chars.reverse().join(''); +} +var _BASE64_DIGITS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._"; + +function _encodeTimeStamp(tMillis, baseMillis) { + var t = Math.floor(tMillis/1000); + var base = Math.floor(baseMillis/1000); + var absolute = ["+", t]; + var resultPair = absolute; + if (((typeof base) == "number") && base <= t) { + var relative = ["", t - base]; + if (relative[1] < absolute[1]) { + resultPair = relative; + } + } + return resultPair[0] + _encodeVarInt(resultPair[1]); +} diff --git a/trunk/etherpad/src/etherpad/control/loadtestcontrol.js b/trunk/etherpad/src/etherpad/control/loadtestcontrol.js new file mode 100644 index 0000000..2a4e3f7 --- /dev/null +++ b/trunk/etherpad/src/etherpad/control/loadtestcontrol.js @@ -0,0 +1,93 @@ +/** + * 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. + */ + +import("etherpad.pad.model"); +import("etherpad.pad.padutils"); +import("etherpad.pad.dbwriter"); +import("etherpad.pad.activepads"); +import("etherpad.control.pad.pad_control"); +import("etherpad.collab.collab_server"); + +// NOTE: we need to talk before enabling this again, for potential security vulnerabilities. +var LOADTEST_ENABLED = false; + +function onRequest() { + if (!LOADTEST_ENABLED) { + response.forbid(); + } +} + +function render_createpad() { + var padId = request.params.padId; + + padutils.accessPadLocal(padId, function(pad) { + if (! pad.exists()) { + pad.create(pad_control.getDefaultPadText()); + } + }); + + activepads.touch(padId); + response.write("OK"); +} + +function render_readpad() { + var padId = request.params.padId; + + padutils.accessPadLocal(padId, function(pad) { + /* nothing */ + }); + + activepads.touch(padId); + response.write("OK"); +} + +function render_appendtopad() { + var padId = request.params.padId; + var text = request.params.text; + + padutils.accessPadLocal(padId, function(pad) { + collab_server.appendPadText(pad, text); + }); + + activepads.touch(padId); + response.write("OK"); +} + +function render_flushpad() { + var padId = request.params.padId; + + padutils.accessPadLocal(padId, function(pad) { + dbwriter.writePadNow(pad, true); + }); + + activepads.touch(padId); + response.write("OK"); +} + +function render_setpadtext() { + var padId = request.params.padId; + var text = request.params.text; + + padutils.accessPadLocal(padId, function(pad) { + collab_server.setPadText(pad, text); + }); + + activepads.touch(padId); + response.write("OK"); +} + + + diff --git a/trunk/etherpad/src/etherpad/control/maincontrol.js b/trunk/etherpad/src/etherpad/control/maincontrol.js new file mode 100644 index 0000000..261ddaf --- /dev/null +++ b/trunk/etherpad/src/etherpad/control/maincontrol.js @@ -0,0 +1,54 @@ +/** + * 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. + */ + +import("fastJSON"); +import("funhtml.*"); +import("stringutils.toHTML"); + +import("etherpad.globals.*"); +import("etherpad.helpers.*"); +import("etherpad.licensing"); +import("etherpad.log"); +import("etherpad.utils.*"); + +import("etherpad.control.blogcontrol"); + +import("etherpad.pad.model"); +import("etherpad.collab.collab_server"); + +jimport("java.lang.System.out.println"); + +//---------------------------------------------------------------- + +function render_main() { + if (request.path == '/ep/') { + response.redirect('/'); + } + renderFramed('main/home.ejs', { + newFromEtherpad: blogcontrol.render_new_from_etherpad() + }); + return true; +} + +function render_support() { + renderFramed("main/support_body.ejs"); +} + +function render_changelog_get() { + renderFramed("main/changelog.ejs"); +} + + diff --git a/trunk/etherpad/src/etherpad/control/pad/pad_changeset_control.js b/trunk/etherpad/src/etherpad/control/pad/pad_changeset_control.js new file mode 100644 index 0000000..5af7ed0 --- /dev/null +++ b/trunk/etherpad/src/etherpad/control/pad/pad_changeset_control.js @@ -0,0 +1,280 @@ +/** + * 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. + */ + +import("etherpad.helpers"); +import("etherpad.pad.model"); +import("etherpad.pad.padutils"); +import("etherpad.utils.*"); +import("fastJSON"); +import("etherpad.collab.server_utils.*"); +import("etherpad.collab.ace.easysync2.{AttribPool,Changeset}"); +import("cache_utils.syncedWithCache"); +import("etherpad.log"); +jimport("net.appjet.common.util.LimitedSizeMapping"); + +import("stringutils"); +import("stringutils.sprintf"); + +var _JSON_CACHE_SIZE = 10000; + +// to clear: appjet.cache.pad_changeset_control.jsoncache.map.clear() +function _getJSONCache() { + return syncedWithCache('pad_changeset_control.jsoncache', function(cache) { + if (! cache.map) { + cache.map = new LimitedSizeMapping(_JSON_CACHE_SIZE); + } + return cache.map; + }); +} + +var _profiler = { + t: 0, + laps: [], + active: false, + start: function() { + _profiler.t = +new Date; + _profiler.laps = []; + //_profiler.active = true; + }, + lap: function(name) { + if (! _profiler.active) return; + var t2 = +new Date; + _profiler.laps.push([name, t2 - _profiler.t]); + }, + dump: function(info) { + if (! _profiler.active) return; + function padright(s, len) { + s = String(s); + return s + new Array(Math.max(0,len-s.length+1)).join(' '); + } + var str = padright(info,20)+": "; + _profiler.laps.forEach(function(e) { + str += padright(e.join(':'), 8); + }); + java.lang.System.out.println(str); + }, + stop: function() { + _profiler.active = false; + } +}; + +function onRequest() { + _profiler.start(); + + var parts = request.path.split('/'); + // TODO(kroo): create a mapping between padId and read-only id + var urlId = parts[4]; + var padId = parseUrlId(urlId).localPadId; + // var revisionId = parts[5]; + + padutils.accessPadLocal(padId, function(pad) { + if (! pad.exists() && pad.getSupportsTimeSlider()) { + response.forbid(); + } + }, 'r'); + + // use the query string to specify start and end revision numbers + var startRev = parseInt(request.params["s"]); + var endRev = startRev + 100 * parseInt(request.params["g"]); + var granularity = parseInt(request.params["g"]); + + _profiler.lap('A'); + var changesetsJson = + getCacheableChangesetInfoJSON(padId, startRev, endRev, granularity); + _profiler.lap('X'); + + //TODO(kroo): set content-type to javascript + response.write(changesetsJson); + _profiler.lap('J'); + if (request.acceptsGzip) { + response.setGzip(true); + } + + _profiler.lap('Z'); + _profiler.dump(startRev+'/'+granularity+'/'+endRev); + _profiler.stop(); + + return true; +} + +function getCacheableChangesetInfoJSON(padId, startNum, endNum, granularity) { + padutils.accessPadLocal(padId, function(pad) { + var lastRev = pad.getHeadRevisionNumber(); + if (endNum > lastRev+1) { + endNum = lastRev+1; + } + endNum = Math.floor(endNum / granularity)*granularity; + }, 'r'); + + var cacheKey = "C/"+startNum+"/"+endNum+"/"+granularity+"/"+ + padutils.getGlobalPadId(padId); + + var cache = _getJSONCache(); + + var cachedJson = cache.get(cacheKey); + if (cachedJson) { + cache.touch(cacheKey); + //java.lang.System.out.println("HIT! "+cacheKey); + return cachedJson; + } + else { + var result = getChangesetInfo(padId, startNum, endNum, granularity); + var json = fastJSON.stringify(result); + cache.put(cacheKey, json); + //java.lang.System.out.println("MISS! "+cacheKey); + return json; + } +} + +// uses changesets whose numbers are between startRev (inclusive) +// and endRev (exclusive); 0 <= startNum < endNum +function getChangesetInfo(padId, startNum, endNum, granularity) { + var forwardsChangesets = []; + var backwardsChangesets = []; + var timeDeltas = []; + var apool = new AttribPool(); + + var callId = stringutils.randomString(10); + + log.custom("getchangesetinfo", {event: "start", callId:callId, + padId:padId, startNum:startNum, + endNum:endNum, granularity:granularity}); + + // This function may take a while and avoids holding a lock on the pad. + // Though the pad may change during execution of this function, + // after we retrieve the HEAD revision number, all other accesses + // are unaffected by new revisions being added to the pad. + + var lastRev; + padutils.accessPadLocal(padId, function(pad) { + lastRev = pad.getHeadRevisionNumber(); + }, 'r'); + + if (endNum > lastRev+1) { + endNum = lastRev+1; + } + endNum = Math.floor(endNum / granularity)*granularity; + + var lines; + padutils.accessPadLocal(padId, function(pad) { + lines = _getPadLines(pad, startNum-1); + }, 'r'); + _profiler.lap('L'); + + var compositeStart = startNum; + while (compositeStart < endNum) { + var whileBodyResult = padutils.accessPadLocal(padId, function(pad) { + _profiler.lap('c0'); + if (compositeStart + granularity > endNum) { + return "break"; + } + var compositeEnd = compositeStart + granularity; + var forwards = _composePadChangesets(pad, compositeStart, compositeEnd); + _profiler.lap('c1'); + var backwards = Changeset.inverse(forwards, lines.textlines, + lines.alines, pad.pool()); + + _profiler.lap('c2'); + Changeset.mutateAttributionLines(forwards, lines.alines, pad.pool()); + _profiler.lap('c3'); + Changeset.mutateTextLines(forwards, lines.textlines); + _profiler.lap('c4'); + + var forwards2 = Changeset.moveOpsToNewPool(forwards, pad.pool(), apool); + _profiler.lap('c5'); + var backwards2 = Changeset.moveOpsToNewPool(backwards, pad.pool(), apool); + _profiler.lap('c6'); + function revTime(r) { + var date = pad.getRevisionDate(r); + var s = Math.floor((+date)/1000); + //java.lang.System.out.println("time "+r+": "+s); + return s; + } + + var t1, t2; + if (compositeStart == 0) { + t1 = revTime(0); + } + else { + t1 = revTime(compositeStart - 1); + } + t2 = revTime(compositeEnd - 1); + timeDeltas.push(t2 - t1); + + _profiler.lap('c7'); + forwardsChangesets.push(forwards2); + backwardsChangesets.push(backwards2); + + compositeStart += granularity; + }, 'r'); + if (whileBodyResult == "break") { + break; + } + } + + log.custom("getchangesetinfo", {event: "finish", callId:callId, + padId:padId, startNum:startNum, + endNum:endNum, granularity:granularity}); + + return { forwardsChangesets:forwardsChangesets, + backwardsChangesets:backwardsChangesets, + apool: apool.toJsonable(), + actualEndNum: endNum, + timeDeltas: timeDeltas }; +} + +// Compose a series of consecutive changesets from a pad. +// precond: startNum < endNum +function _composePadChangesets(pad, startNum, endNum) { + if (endNum - startNum > 1) { + var csFromPad = pad.getCoarseChangeset(startNum, endNum - startNum); + if (csFromPad) { + //java.lang.System.out.println("HIT! "+startNum+"-"+endNum); + return csFromPad; + } + else { + //java.lang.System.out.println("MISS! "+startNum+"-"+endNum); + } + //java.lang.System.out.println("composePadChangesets: "+startNum+','+endNum); + } + var changeset = pad.getRevisionChangeset(startNum); + for(var r=startNum+1; r= 0) { + atext = pad.getInternalRevisionAText(revNum); + } + else { + atext = Changeset.makeAText("\n"); + } + _profiler.lap('PL1'); + var result = {}; + result.textlines = Changeset.splitTextLines(atext.text); + _profiler.lap('PL2'); + result.alines = Changeset.splitAttributionLines(atext.attribs, + atext.text); + _profiler.lap('PL3'); + return result; +} \ No newline at end of file diff --git a/trunk/etherpad/src/etherpad/control/pad/pad_control.js b/trunk/etherpad/src/etherpad/control/pad/pad_control.js new file mode 100644 index 0000000..3c32202 --- /dev/null +++ b/trunk/etherpad/src/etherpad/control/pad/pad_control.js @@ -0,0 +1,780 @@ +/** + * 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. + */ + +import("funhtml.*"); +import("comet"); +import("email.sendEmail"); +import("fastJSON"); +import("jsutils.eachProperty"); +import("sqlbase.sqlbase"); +import("stringutils.{toHTML,md5}"); +import("stringutils"); + +import("etherpad.collab.collab_server"); +import("etherpad.debug.dmesg"); +import("etherpad.globals.*"); +import("etherpad.helpers"); +import("etherpad.licensing"); +import("etherpad.quotas"); +import("etherpad.log"); +import("etherpad.log.{logRequest,logException}"); +import("etherpad.sessions"); +import("etherpad.sessions.getSession"); +import("etherpad.utils.*"); +import("etherpad.pro.pro_padmeta"); +import("etherpad.pro.pro_pad_db"); +import("etherpad.pro.pro_utils"); +import("etherpad.pro.pro_accounts.getSessionProAccount"); +import("etherpad.pro.domains"); +import("etherpad.pro.pro_config"); +import("etherpad.pne.pne_utils"); +import("etherpad.pro.pro_quotas"); + +import("etherpad.pad.revisions"); +import("etherpad.pad.chatarchive"); +import("etherpad.pad.model"); +import("etherpad.pad.padutils"); +import("etherpad.pad.padusers"); +import("etherpad.control.pad.pad_view_control"); +import("etherpad.control.pad.pad_changeset_control"); +import("etherpad.control.pad.pad_importexport_control"); +import("etherpad.collab.readonly_server"); + +import("dispatch.{Dispatcher,PrefixMatcher,DirMatcher,forward}"); + +jimport("java.lang.System.out.println"); + +var DISABLE_PAD_CREATION = false; + +function onStartup() { + sqlbase.createJSONTable("PAD_DIAGNOSTIC"); +} + +function onRequest() { + + // TODO: take a hard look at /ep/pad/FOO/BAR/ dispatching. + // Perhaps standardize on /ep/pad//foo + if (request.path.indexOf('/ep/pad/auth/') == 0) { + if (request.isGet) { + return render_auth_get(); + } + if (request.isPost) { + return render_auth_post(); + } + } + + if (pro_utils.isProDomainRequest()) { + pro_quotas.perRequestBillingCheck(); + } + + var disp = new Dispatcher(); + disp.addLocations([ + [PrefixMatcher('/ep/pad/view/'), forward(pad_view_control)], + [PrefixMatcher('/ep/pad/changes/'), forward(pad_changeset_control)], + [PrefixMatcher('/ep/pad/impexp/'), forward(pad_importexport_control)], + [PrefixMatcher('/ep/pad/export/'), pad_importexport_control.renderExport] + ]); + return disp.dispatch(); +} + +//---------------------------------------------------------------- +// utils +//---------------------------------------------------------------- + +function getDefaultPadText() { + if (pro_utils.isProDomainRequest()) { + return pro_config.getConfig().defaultPadText; + } + return renderTemplateAsString("misc/pad_default.ejs", {padUrl: request.url.split("?", 1)[0]}); +} + +function assignName(pad, userId) { + if (padusers.isGuest(userId)) { + // use pad-specific name if possible + var userData = pad.getAuthorData(userId); + var nm = (userData && userData.name) || padusers.getUserName() || null; + + // don't let name guest typed in last once we've assigned a name + // for this pad, so the user can change it + delete getSession().guestDisplayName; + + return nm; + } + else { + return padusers.getUserName(); + } +} + +function assignColorId(pad, userId) { + // use pad-specific color if possible + var userData = pad.getAuthorData(userId); + if (userData && ('colorId' in userData)) { + return userData.colorId; + } + + // assign random unique color + function r(n) { + return Math.floor(Math.random() * n); + } + var colorsUsed = {}; + var users = collab_server.getConnectedUsers(pad); + var availableColors = []; + users.forEach(function(u) { + colorsUsed[u.colorId] = true; + }); + for (var i = 0; i < COLOR_PALETTE.length; i++) { + if (!colorsUsed[i]) { + availableColors.push(i); + } + } + if (availableColors.length > 0) { + return availableColors[r(availableColors.length)]; + } else { + return r(COLOR_PALETTE.length); + } +} + +function _getPrivs() { + return { + maxRevisions: quotas.getMaxSavedRevisionsPerPad() + }; +} + +//---------------------------------------------------------------- +// linkfile (a file that users can save that redirects them to +// a particular pad; auto-download) +//---------------------------------------------------------------- +function render_linkfile() { + var padId = request.params.padId; + + renderHtml("pad/pad_download_link.ejs", { + padId: padId + }); + + response.setHeader("Content-Disposition", "attachment; filename=\""+padId+".html\""); +} + +//---------------------------------------------------------------- +// newpad +//---------------------------------------------------------------- + +function render_newpad() { + var session = getSession(); + var padId; + + if (pro_utils.isProDomainRequest()) { + padId = pro_pad_db.getNextPadId(); + } else { + padId = randomUniquePadId(); + } + + session.instantCreate = padId; + response.redirect("/"+padId); +} + +// Tokbox +function render_newpad_xml_post() { + var localPadId; + if (pro_utils.isProDomainRequest()) { + localPadId = pro_pad_db.getNextPadId(); + } else { + localPadId = randomUniquePadId(); + } + // + if (DISABLE_PAD_CREATION) { + if (! pro_utils.isProDomainRequest()) { + utils.render500(); + return; + } + } + // + + padutils.accessPadLocal(localPadId, function(pad) { + if (!pad.exists()) { + pad.create(getDefaultPadText()); + } + }); + response.setContentType('text/plain; charset=utf-8'); + response.write([ + '', + 'http://'+request.host+'/'+localPadId+'', + '' + ].join('\n')); +} + +//---------------------------------------------------------------- +// pad +//---------------------------------------------------------------- + +function _createIfNecessary(localPadId, pad) { + if (pad.exists()) { + delete getSession().instantCreate; + return; + } + // make sure localPadId is valid. + var validPadId = padutils.makeValidLocalPadId(localPadId); + if (localPadId != validPadId) { + response.redirect('/'+validPadId); + } + // + if (DISABLE_PAD_CREATION) { + if (! pro_utils.isProDomainRequest()) { + response.redirect("/ep/pad/create?padId="+encodeURIComponent(localPadId)); + return; + } + } + // + // tokbox may use createImmediately + if (request.params.createImmediately || getSession().instantCreate == localPadId) { + pad.create(getDefaultPadText()); + delete getSession().instantCreate; + return; + } + response.redirect("/ep/pad/create?padId="+encodeURIComponent(localPadId)); +} + +function _promptForMobileDevices(pad) { + // TODO: also work with blackbery and windows mobile and others + if (request.userAgent.isIPhone() && (!request.params.skipIphoneCheck)) { + renderHtml("pad/pad_iphone_body.ejs", {padId: pad.getLocalId()}); + response.stop(); + } +} + +function _checkPadQuota(pad) { + var numConnectedUsers = collab_server.getNumConnections(pad); + var maxUsersPerPad = quotas.getMaxSimultaneousPadEditors(pad.getId()); + + if (numConnectedUsers >= maxUsersPerPad) { + log.info("rendered-padfull"); + renderFramed('pad/padfull_body.ejs', + {maxUsersPerPad: maxUsersPerPad, padId: pad.getLocalId()}); + response.stop(); + } + + if (pne_utils.isPNE()) { + if (!licensing.canSessionUserJoin()) { + renderFramed('pad/total_users_exceeded.ejs', { + userQuota: licensing.getActiveUserQuota(), + activeUserWindowHours: licensing.getActiveUserWindowHours() + }); + response.stop(); + } + } +} + +function _checkIfDeleted(pad) { + // TODO: move to access control check on access? + if (pro_utils.isProDomainRequest()) { + pro_padmeta.accessProPad(pad.getId(), function(propad) { + if (propad.exists() && propad.isDeleted()) { + renderNoticeString("This pad has been deleted."); + response.stop(); + } + }); + } +} + +function render_pad(localPadId) { + var proTitle = null, documentBarTitle, initialPassword = null; + var isPro = isProDomainRequest(); + var userId = padusers.getUserId(); + + var opts = {}; + var globalPadId; + + if (isPro) { + pro_quotas.perRequestBillingCheck(); + } + + padutils.accessPadLocal(localPadId, function(pad) { + globalPadId = pad.getId(); + request.cache.globalPadId = globalPadId; + _createIfNecessary(localPadId, pad); + _promptForMobileDevices(pad); + _checkPadQuota(pad); + _checkIfDeleted(pad); + + if (request.params.inviteTo) { + getSession().nameGuess = request.params.inviteTo; + response.redirect('/'+localPadId); + } + var displayName; + if (request.params.displayName) { // tokbox + displayName = String(request.params.displayName); + } + else { + displayName = assignName(pad, userId); + } + + if (isProDomainRequest()) { + pro_padmeta.accessProPadLocal(localPadId, function(propad) { + proTitle = propad.getDisplayTitle(); + initialPassword = propad.getPassword(); + }); + } + documentBarTitle = (proTitle || "Public Pad"); + + var specialKey = request.params.specialKey || + (sessions.isAnEtherpadAdmin() ? collab_server.getSpecialKey('invisible') : + null); + if (request.params.fullScreen) { // tokbox, embedding + opts.fullScreen = true; + } + if (request.params.tokbox) { + opts.tokbox = true; + } + if (request.params.sidebar) { + opts.sidebar = Boolean(Number(request.params.sidebar)); + } + + helpers.addClientVars({ + padId: localPadId, + globalPadId: globalPadId, + userAgent: request.headers["User-Agent"], + collab_client_vars: collab_server.getCollabClientVars(pad), + debugEnabled: request.params.djs, + clientIp: request.clientAddr, + colorPalette: COLOR_PALETTE, + nameGuess: (getSession().nameGuess || null), + initialRevisionList: revisions.getRevisionList(pad), + serverTimestamp: +(new Date), + accountPrivs: _getPrivs(), + chatHistory: chatarchive.getRecentChatBlock(pad, 30), + numConnectedUsers: collab_server.getNumConnections(pad), + isProPad: isPro, + initialTitle: documentBarTitle, + initialPassword: initialPassword, + initialOptions: pad.getPadOptionsObj(), + userIsGuest: padusers.isGuest(userId), + userId: userId, + userName: displayName, + userColor: assignColorId(pad, userId), + specialKey: specialKey, + specialKeyTranslation: collab_server.translateSpecialKey(specialKey), + opts: opts + }); + }); + + var isProUser = (isPro && ! padusers.isGuest(userId)); + + var isFullWidth = false; + var hideSidebar = false; + var cookiePrefs = padutils.getPrefsCookieData(); + if (cookiePrefs) { + isFullWidth = !! cookiePrefs.fullWidth; + hideSidebar = !! cookiePrefs.hideSidebar; + } + if (opts.fullScreen) { + isFullWidth = true; + if (opts.tokbox) { + hideSidebar = true; + } + } + if ('sidebar' in opts) { + hideSidebar = ! opts.sidebar; + } + var bodyClass = (isFullWidth ? "fullwidth" : "limwidth")+ + " "+(isPro ? "propad" : "nonpropad")+" "+ + (isProUser ? "prouser" : "nonprouser"); + + var cookiePrefsToSet = {fullWidth:isFullWidth, hideSidebar:hideSidebar}; + helpers.addClientVars({cookiePrefsToSet: cookiePrefsToSet}); + + renderHtml("pad/pad_body2.ejs", + {localPadId:localPadId, + pageTitle:toHTML(proTitle || localPadId), + initialTitle:toHTML(documentBarTitle), + bodyClass: bodyClass, + hasOffice: hasOffice(), + isPro: isPro, + isProAccountHolder: isProUser, + account: getSessionProAccount(), // may be falsy + toHTML: toHTML, + prefs: {isFullWidth:isFullWidth, hideSidebar:hideSidebar}, + signinUrl: '/ep/account/sign-in?cont='+ + encodeURIComponent(request.url), + fullSuperdomain: pro_utils.getFullSuperdomainHost() + }); + return true; +} + +function render_create_get() { + var padId = request.params.padId; + // + var template = (DISABLE_PAD_CREATION && ! pro_utils.isProDomainRequest()) ? + "pad/create_body_rafter.ejs" : + "pad/create_body.ejs"; + // + renderFramed(template, {padId: padId, + fullSuperdomain: pro_utils.getFullSuperdomainHost()}); +} + +function render_create_post() { + var padId = request.params.padId; + getSession().instantCreate = padId; + response.redirect("/"+padId); +} + +//---------------------------------------------------------------- +// saverevision +//---------------------------------------------------------------- + +function render_saverevision_post() { + var padId = request.params.padId; + var savedBy = request.params.savedBy; + var savedById = request.params.savedById; + var revNum = request.params.revNum; + var privs = _getPrivs(); + padutils.accessPadLocal(padId, function(pad) { + if (! pad.exists()) { response.notFound(); } + var currentRevs = revisions.getRevisionList(pad); + if (currentRevs.length >= privs.maxRevisions) { + response.forbid(); + } + var savedRev = revisions.saveNewRevision(pad, savedBy, savedById, + revNum); + readonly_server.broadcastNewRevision(pad, savedRev); + response.setContentType('text/x-json'); + response.write(fastJSON.stringify(revisions.getRevisionList(pad))); + }); +} + +function render_saverevisionlabel_post() { + var userId = request.params.userId; + var padId = request.params.padId; + var revId = request.params.revId; + var newLabel = request.params.newLabel; + padutils.accessPadLocal(padId, function(pad) { + revisions.setLabel(pad, revId, userId, newLabel); + response.setContentType('text/x-json'); + response.write(fastJSON.stringify(revisions.getRevisionList(pad))); + }); +} + +function render_getrevisionatext_get() { + var padId = request.params.padId; + var revId = request.params.revId; + var result = null; + + var rev = padutils.accessPadLocal(padId, function(pad) { + var r = revisions.getStoredRevision(pad, revId); + var forWire = collab_server.getATextForWire(pad, r.revNum); + result = {atext:forWire.atext, apool:forWire.apool, + historicalAuthorData:forWire.historicalAuthorData}; + return r; + }, "r"); + + response.setContentType('text/plain; charset=utf-8'); + response.write(fastJSON.stringify(result)); +} + +//---------------------------------------------------------------- +// reconnect +//---------------------------------------------------------------- + +function _recordDiagnosticInfo(padId, diagnosticInfoJson) { + + var diagnosticInfo = {}; + try { + diagnosticInfo = fastJSON.parse(diagnosticInfoJson); + } catch (ex) { + log.warn("Error parsing diagnosticInfoJson: "+ex); + diagnosticInfo = {error: "error parsing JSON"}; + } + + // ignore userdups, unauth + if (diagnosticInfo.disconnectedMessage == "userdup" || + diagnosticInfo.disconnectedMessage == "unauth") { + return; + } + + var d = new Date(); + + diagnosticInfo.date = +d; + diagnosticInfo.strDate = String(d); + diagnosticInfo.clientAddr = request.clientAddr; + diagnosticInfo.padId = padId; + diagnosticInfo.headers = {}; + eachProperty(request.headers, function(k,v) { + diagnosticInfo.headers[k] = v; + }); + + var uid = diagnosticInfo.uniqueId; + + sqlbase.putJSON("PAD_DIAGNOSTIC", (diagnosticInfo.date)+"-"+uid, diagnosticInfo); + +} + +function recordMigratedDiagnosticInfo(objArray) { + objArray.forEach(function(obj) { + sqlbase.putJSON("PAD_DIAGNOSTIC", (obj.date)+"-"+obj.uniqueId, obj); + }); +} + +function render_reconnect() { + var localPadId = request.params.padId; + var globalPadId = padutils.getGlobalPadId(localPadId); + var userId = (padutils.getPrefsCookieUserId() || undefined); + var hasClientErrors = false; + var uniqueId; + try { + var obj = fastJSON.parse(request.params.diagnosticInfo); + uniqueId = obj.uniqueId; + errorMessage = obj.disconnectedMessage; + hasClientErrors = obj.collabDiagnosticInfo.errors.length > 0; + } catch (e) { + // guess it doesn't have errors. + } + + log.custom("reconnect", {globalPadId: globalPadId, userId: userId, + uniqueId: uniqueId, + hasClientErrors: hasClientErrors, + errorMessage: errorMessage }); + + try { + _recordDiagnosticInfo(globalPadId, request.params.diagnosticInfo); + } catch (ex) { + log.warn("Error recording diagnostic info: "+ex+" / "+request.params.diagnosticInfo); + } + + try { + _applyMissedChanges(localPadId, request.params.missedChanges); + } catch (ex) { + log.warn("Error applying missed changes: "+ex+" / "+request.params.missedChanges); + } + + response.redirect('/'+localPadId); +} + +/* posted asynchronously by the client as soon as reconnect dialogue appears. */ +function render_connection_diagnostic_info_post() { + var localPadId = request.params.padId; + var globalPadId = padutils.getGlobalPadId(localPadId); + var userId = (padutils.getPrefsCookieUserId() || undefined); + var hasClientErrors = false; + var uniqueId; + var errorMessage; + try { + var obj = fastJSON.parse(request.params.diagnosticInfo); + uniqueId = obj.uniqueId; + errorMessage = obj.disconnectedMessage; + hasClientErrors = obj.collabDiagnosticInfo.errors.length > 0; + } catch (e) { + // guess it doesn't have errors. + } + log.custom("disconnected_autopost", {globalPadId: globalPadId, userId: userId, + uniqueId: uniqueId, + hasClientErrors: hasClientErrors, + errorMessage: errorMessage}); + + try { + _recordDiagnosticInfo(globalPadId, request.params.diagnosticInfo); + } catch (ex) { + log.warn("Error recording diagnostic info: "+ex+" / "+request.params.diagnosticInfo); + } + response.setContentType('text/plain; charset=utf-8'); + response.write("OK"); +} + +function _applyMissedChanges(localPadId, missedChangesJson) { + var missedChanges; + try { + missedChanges = fastJSON.parse(missedChangesJson); + } catch (ex) { + log.warn("Error parsing missedChangesJson: "+ex); + return; + } + + padutils.accessPadLocal(localPadId, function(pad) { + if (pad.exists()) { + collab_server.applyMissedChanges(pad, missedChanges); + } + }); +} + +//---------------------------------------------------------------- +// feedback +//---------------------------------------------------------------- + +function render_feedback_post() { + var feedback = request.params.feedback; + var localPadId = request.params.padId; + var globalPadId = padutils.getGlobalPadId(localPadId); + var username = request.params.username; + var email = request.params.email; + var subject = 'EtherPad Feedback from '+request.clientAddr+' / '+globalPadId+' / '+username; + + if (feedback.indexOf("@") > 0) { + subject = "@ "+subject; + } + + feedback += "\n\n--\n"; + feedback += ("User Agent: "+request.headers['User-Agent'] + "\n"); + feedback += ("Session Referer: "+getSession().initialReferer + "\n"); + feedback += ("Email: "+email+"\n"); + + // log feedback + var userId = padutils.getPrefsCookieUserId(); + log.custom("feedback", { + globalPadId: globalPadId, + userId: userId, + email: email, + username: username, + feedback: request.params.feedback}); + + sendEmail( + 'feedback@pad.spline.inf.fu-berlin.de', + 'feedback@pad.spline.inf.fu-berlin.de', + subject, + {}, + feedback + ); + response.write("OK"); +} + +//---------------------------------------------------------------- +// emailinvite +//---------------------------------------------------------------- + +function render_emailinvite_post() { + var toEmails = String(request.params.toEmails).split(','); + var padId = String(request.params.padId); + var username = String(request.params.username); + var subject = String(request.params.subject); + var message = String(request.params.message); + + log.custom("padinvite", + {toEmails: toEmails, padId: padId, username: username, + subject: subject, message: message}); + + var fromAddr = '"EtherPad" '; + // client enforces non-empty subject and message + var subj = '[EtherPad] '+subject; + var body = renderTemplateAsString('email/padinvite.ejs', + {body: message}); + var headers = {}; + var proAccount = getSessionProAccount(); + if (proAccount) { + headers['Reply-To'] = proAccount.email; + } + + response.setContentType('text/plain; charset=utf-8'); + try { + sendEmail(toEmails, fromAddr, subj, headers, body); + response.write("OK"); + } catch (e) { + logException(e); + response.setStatusCode(500); + response.write("Error"); + } +} + +//---------------------------------------------------------------- +// time-slider +//---------------------------------------------------------------- +function render_slider() { + var parts = request.path.split('/'); + var padOpaqueRef = parts[4]; + + helpers.addClientVars({padOpaqueRef:padOpaqueRef}); + + renderHtml("pad/padslider_body.ejs", { + // properties go here + }); + + return true; +} + +//---------------------------------------------------------------- +// auth +//---------------------------------------------------------------- + +function render_auth_get() { + var parts = request.path.split('/'); + var localPadId = parts[4]; + var errDiv; + if (getSession().padPassErr) { + errDiv = DIV({style: "border: 1px solid #fcc; background: #ffeeee; padding: 1em; margin: 1em 0;"}, + B(getSession().padPassErr)); + delete getSession().padPassErr; + } else { + errDiv = DIV(); + } + renderFramedHtml(function() { + return DIV({className: "fpcontent"}, + DIV({style: "margin: 1em;"}, + errDiv, + FORM({style: "border: 1px solid #ccc; padding: 1em; background: #fff6cc;", + action: request.path+'?'+request.query, + method: "post"}, + LABEL(B("Please enter the password required to access this pad:")), + BR(), BR(), + INPUT({type: "text", name: "password"}), INPUT({type: "submit", value: "Submit"}) + /*DIV(BR(), "Or ", A({href: '/ep/account/sign-in'}, "sign in"), ".")*/ + )), + DIV({style: "padding: 0 1em;"}, + P({style: "color: #444;"}, + "If you have forgotten a pad's password, contact your site administrator.", + " Site administrators can recover lost pad text through the \"Admin\" tab.") + ) + ); + }); + return true; +} + +function render_auth_post() { + var parts = request.path.split('/'); + var localPadId = parts[4]; + var domainId = domains.getRequestDomainId(); + if (!getSession().padPasswordAuth) { + getSession().padPasswordAuth = {}; + } + var currentPassword = pro_padmeta.accessProPadLocal(localPadId, function(propad) { + return propad.getPassword(); + }); + if (request.params.password == currentPassword) { + var globalPadId = padutils.getGlobalPadId(localPadId); + getSession().padPasswordAuth[globalPadId] = true; + } else { + getSession().padPasswordAuth[globalPadId] = false; + getSession().padPassErr = "Incorrect password."; + } + var cont = request.params.cont; + if (!cont) { + cont = '/'+localPadId; + } + response.redirect(cont); +} + +//---------------------------------------------------------------- +// chathistory +//---------------------------------------------------------------- + +function render_chathistory_get() { + var padId = request.params.padId; + var start = Number(request.params.start || 0); + var end = Number(request.params.end || 0); + var result = null; + + var rev = padutils.accessPadLocal(padId, function(pad) { + result = chatarchive.getChatBlock(pad, start, end); + }, "r"); + + response.setContentType('text/plain; charset=utf-8'); + response.write(fastJSON.stringify(result)); +} + diff --git a/trunk/etherpad/src/etherpad/control/pad/pad_importexport_control.js b/trunk/etherpad/src/etherpad/control/pad/pad_importexport_control.js new file mode 100644 index 0000000..b7e5f4d --- /dev/null +++ b/trunk/etherpad/src/etherpad/control/pad/pad_importexport_control.js @@ -0,0 +1,319 @@ +/** + * 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. + */ + +import("jsutils.arrayToSet"); +import("stringutils.{toHTML,md5}"); +import("stringutils"); +import("sync"); +import("varz"); + +import("etherpad.control.pad.pad_view_control.getRevisionInfo"); +import("etherpad.helpers"); +import("etherpad.importexport.importexport"); +import("etherpad.log"); +import("etherpad.pad.model"); +import("etherpad.pad.padutils"); +import("etherpad.pad.importhtml"); +import("etherpad.pad.exporthtml"); +import("etherpad.sessions"); +import("etherpad.sessions.getSession"); +import("etherpad.utils.{render404,renderFramedError}"); +import("etherpad.collab.server_utils"); + +function _log(obj) { + log.custom("import-export", obj); +} + +//--------------------------------------- +// utilities +//--------------------------------------- + +function _getPadTextBytes(padId, revNum) { + if (revNum === undefined) { + return null; + } + return padutils.accessPadLocal(padId, function(pad) { + if (pad.exists()) { + var txt = exporthtml.getPadPlainText(pad, revNum); + return (new java.lang.String(txt)).getBytes("UTF-8"); + } else { + return null; + } + }, 'r'); +} + +function _getPadHtmlBytes(padId, revNum, noDocType) { + if (revNum === undefined) { + return null; + } + var html = padutils.accessPadLocal(padId, function(pad) { + if (pad.exists()) { + return exporthtml.getPadHTMLDocument(pad, revNum, noDocType); + } + }); + if (html) { + return (new java.lang.String(html)).getBytes("UTF-8"); + } else { + return null; + } +} + +function _getFileExtension(fileName, def) { + if (fileName.lastIndexOf('.') > 0) { + return fileName.substr(fileName.lastIndexOf('.')+1); + } else { + return def; + } +} + +function _guessFileType(contentType, fileName) { + function _f(str) { return function() { return str; }} + var unchangedExtensions = + arrayToSet(['txt', 'htm', 'html', 'doc', 'docx', 'rtf', 'pdf', 'odt']); + var textExtensions = + arrayToSet(['js', 'scala', 'java', 'c', 'cpp', 'log', 'h', 'htm', 'html', 'css', 'php', 'xhtml', + 'dhtml', 'jsp', 'asp', 'sh', 'bat', 'pl', 'py']); + var contentTypes = { + 'text/plain': 'txt', + 'text/html': 'html', + 'application/msword': 'doc', + 'application/vnd.oasis.opendocument.text': 'odt', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'docx', + 'text/rtf': 'rtf', + 'application/pdf': 'pdf' + } + + var ext = _getFileExtension(fileName); + if (ext) { + if (unchangedExtensions[ext]) { + return ext; + } else if (textExtensions[ext]) { + return 'txt'; + } + } + if (contentType in contentTypes) { + return contentTypes[contentType] + } + // unknown type, nothing to return. + _log({type: "warning", error: "unknown-type", contentType: contentType, fileName: fileName}); +} + +function _noteExportFailure() { + varz.incrementInt("export-failed"); +} + +function _noteImportFailure() { + varz.incrementInt("import-failed"); +} + +//--------------------------------------- +// export +//--------------------------------------- + +// handles /ep/pad/export/* +function renderExport() { + var parts = request.path.split('/'); + var padId = server_utils.parseUrlId(parts[4]).localPadId; + var revisionId = parts[5]; + var rev = null; + var format = request.params.format || 'txt'; + + if (! request.cache.skipAccess) { + _log({type: "request", direction: "export", format: format}); + rev = getRevisionInfo(padId, revisionId); + if (! rev) { + render404(); + } + request.cache.skipAccess = true; + } + + var result = _exportToFormat(padId, revisionId, (rev || {}).revNum, format); + if (result === true) { + response.stop(); + } else { + renderFramedError(result); + } + return true; +} + +function _exportToFormat(padId, revisionId, revNum, format) { + var bytes = _doExportConversion(format, + function() { return _getPadTextBytes(padId, revNum); }, + function(noDocType) { return _getPadHtmlBytes(padId, revNum, noDocType); }); + if (! bytes) { + return "Unable to convert file for export... try a different format?" + } else if (typeof(bytes) == 'string') { + return bytes + } else { + response.setContentType(importexport.formats[format]); + response.setHeader("Content-Disposition", "attachment; filename=\""+padId+"-"+revisionId+"."+format+"\""); + response.writeBytes(bytes); + return true; + } +} + + +function _doExportConversion(format, getTextBytes, getHtmlBytes) { + if (! (format in importexport.formats)) { + return false; + } + var bytes; + var srcFormat; + + if (format == 'txt') { + bytes = getTextBytes(); + srcFormat = 'txt'; + } else { + bytes = getHtmlBytes(format == 'doc' || format == 'odt'); + srcFormat = 'html'; + } + if (bytes == null) { + bytes = java.lang.reflect.Array.newInstance(java.lang.Byte.TYPE, 0); + } + + try { + var ret = importexport.convertFile(srcFormat, format, bytes); + if (typeof(ret) == 'string') { + _log({type: "error", error: "export-failed", format: format, message: ret}); + _noteExportFailure(); + return ret; + } + bytes = ret; + } catch (e) { + if (e.javaException instanceof org.mortbay.jetty.RetryRequest) { + throw e.javaException + } + if (e.javaException || e.rhinoException) { + net.appjet.oui.exceptionlog.apply(e.javaException || e.rhinoException); + } + bytes = null; + } + if (bytes == null || bytes.length == 0) { + _log({type: "error", error: "export-failed", format: format, message: ret}); + _noteExportFailure(); + return false; + } + return bytes; +} + +//--------------------------------------- +// import +//--------------------------------------- + +function _getImportInfo(key) { + var session = getSession(); + sync.callsyncIfTrue(session, function() { return ! ('importexport' in session) }, + function() { + session.importexport = {}; + }); + var tokens = session.importexport; + sync.callsyncIfTrue(tokens, function() { return ! (key in tokens) }, + function() { + tokens[key] = {}; + }); + return tokens[key]; +} + +function render_import() { + function _r(code) { + response.setContentType("text/html"); + response.write(""); + response.stop(); + } + + if (! request.isPost) { + response.stop(); + } + + var padId = decodeURIComponent(request.params.padId); + if (! padId) { + response.stop(); + } + + var file = request.files.file; + if (! file) { + _r('parent.pad.handleImportExportFrameCall("importFailed", "Please select a file to import.")'); + } + + var bytes = file.bytes; + var type = _guessFileType(file.contentType, file.filesystemName); + + _log({type: "request", direction: "import", format: type}); + + if (! type) { + type = _getFileExtension(file.filesystemName, "no file extension found"); + _r('parent.pad.handleImportExportFrameCall("importFailed", "'+importexport.errorUnsupported(type)+'")'); + } + + var token = md5(bytes); + var state = _getImportInfo(token); + state.bytes = bytes; + state.type = type; + + _r("parent.pad.handleImportExportFrameCall('importSuccessful', '"+token+"')"); +} + + +function render_import2() { + var token = request.params.token; + + function _r(txt) { + response.write(txt); + response.stop(); + } + + if (! token) { _r("fail"); } + + var state = _getImportInfo(token); + if (! state.type || ! state.bytes) { _r("fail"); } + + var newBytes; + try { + newBytes = importexport.convertFile(state.type, "html", state.bytes); + } catch (e) { + if (e.javaException instanceof org.mortbay.jetty.RetryRequest) { + throw e.javaException; + } + net.appjet.oui.exceptionlog.apply(e); + throw e; + } + + if (typeof(newBytes) == 'string') { + _log({type: "error", error: "import-failed", format: state.type, message: newBytes}); + _noteImportFailure(); + _r("msg:"+newBytes); + } + + if (! newBytes || newBytes.length == 0) { + _r("fail"); + } + + var newHTML; + try { + newHTML = String(new java.lang.String(newBytes, "UTF-8")); + } catch (e) { + _r("fail"); + } + + if (! request.params.padId) { _r("fail"); } + padutils.accessPadLocal(request.params.padId, function(pad) { + if (! pad.exists()) { + _r("fail"); + } + importhtml.setPadHTML(pad, newHTML); + }); + _r("ok"); +} diff --git a/trunk/etherpad/src/etherpad/control/pad/pad_view_control.js b/trunk/etherpad/src/etherpad/control/pad/pad_view_control.js new file mode 100644 index 0000000..0606d2c --- /dev/null +++ b/trunk/etherpad/src/etherpad/control/pad/pad_view_control.js @@ -0,0 +1,287 @@ +/** + * 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. + */ + +import("etherpad.helpers"); +import("etherpad.pad.model"); +import("etherpad.pad.padusers"); +import("etherpad.pad.padutils"); +import("etherpad.pad.exporthtml"); +import("etherpad.pro.pro_accounts"); +import("etherpad.pro.pro_padmeta"); +import("etherpad.utils.*"); +import("etherpad.pad.revisions"); +import("stringutils.toHTML"); +import("etherpad.collab.server_utils.*"); +import("etherpad.collab.collab_server.buildHistoricalAuthorDataMapForPadHistory"); +import("etherpad.collab.collab_server.getATextForWire"); +import("etherpad.control.pad.pad_changeset_control.getChangesetInfo"); +import("etherpad.globals"); +import("fastJSON"); +import("etherpad.collab.ace.easysync2.Changeset"); +import("etherpad.collab.ace.linestylefilter.linestylefilter"); +import("etherpad.collab.ace.domline.domline"); + +//---------------------------------------------------------------- +// view (viewing a static revision of a pad) +//---------------------------------------------------------------- + +function onRequest() { + var parts = request.path.split('/'); + // TODO(kroo): create a mapping between padId and read-only id + var readOnlyIdOrLocalPadId = parts[4]; + var parseResult = parseUrlId(readOnlyIdOrLocalPadId); + var isReadOnly = parseResult.isReadOnly; + var viewId = parseResult.viewId; + var localPadId = parseResult.localPadId; + var globalPadId = parseResult.globalPadId; + var roPadId = parseResult.roPadId; + var revisionId = parts[5]; + + var rev = getRevisionInfo(localPadId, revisionId); + if (! rev) { + return false; + } + + if (request.params.pt == 1) { + var padText = padutils.accessPadLocal(localPadId, function(pad) { + return pad.getRevisionText(rev.revNum); + }, 'r'); + + response.setContentType('text/plain; charset=utf-8'); + response.write(padText); + } else { + var padContents, totalRevs, atextForWire, savedRevisions; + var supportsSlider; + padutils.accessPadLocal(localPadId, function(pad) { + padContents = [_getPadHTML(pad, rev.revNum), + pad.getRevisionText(rev.revNum)]; + totalRevs = pad.getHeadRevisionNumber(); + atextForWire = getATextForWire(pad, rev.revNum); + savedRevisions = revisions.getRevisionList(pad); + supportsSlider = pad.getSupportsTimeSlider(); + }, 'r'); + + var _add = function(dict, anotherdict) { + for(var key in anotherdict) { + dict[key] = anotherdict[key]; + } + return dict; + } + + var getAdaptiveChangesetsArray = function(array, start, granularity) { + array = array || []; + start = start || 0; + granularity = granularity || Math.pow(10, Math.floor(Math.log(totalRevs+1) / Math.log(10))); + var changeset = _add(getChangesetInfo(localPadId, start, totalRevs+1, granularity), { + start: start, + granularity: Math.floor(granularity) + }); + array.push(changeset); + if(changeset.actualEndNum != totalRevs+1 && granularity > 1) + getAdaptiveChangesetsArray(array, changeset.actualEndNum, Math.floor(granularity / 10)); + return array; + } + var initialChangesets = []; + if (supportsSlider) { + initialChangesets = getAdaptiveChangesetsArray( + [ + _add(getChangesetInfo(localPadId, Math.floor(rev.revNum / 1000)*1000, Math.floor(rev.revNum / 1000)*1000+1000, 100), { + start: Math.floor(rev.revNum / 1000)*1000, + granularity: 100 + }), + _add(getChangesetInfo(localPadId, Math.floor(rev.revNum / 100)*100, Math.floor(rev.revNum / 100)*100+100, 10), { + start: Math.floor(rev.revNum / 100)*100, + granularity: 10 + }), + _add(getChangesetInfo(localPadId, Math.floor(rev.revNum / 10)*10, Math.floor(rev.revNum / 10)*10+10, 1), { + start: Math.floor(rev.revNum / 10)*10, + granularity: 1 + })] + ); + } + + var zpad = function(str, length) { + str = str+""; + while(str.length < length) + str = '0'+str; + return str; + }; + var dateFormat = function(savedWhen) { + var date = new Date(savedWhen); + 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("")); + }; + + var proTitle = null; + var initialPassword = null; + if (isProDomainRequest()) { + pro_padmeta.accessProPadLocal(localPadId, function(propad) { + proTitle = propad.getDisplayTitle(); + initialPassword = propad.getPassword(); + }); + } + var documentBarTitle = (proTitle || "Public Pad"); + + var padHTML = padContents[0]; + var padText = padContents[1]; + + var historicalAuthorData = padutils.accessPadLocal(localPadId, function(pad) { + return buildHistoricalAuthorDataMapForPadHistory(pad); + }, 'r'); + + helpers.addClientVars({ + viewId: viewId, + initialPadContents: padText, + revNum: rev.revNum, + totalRevs: totalRevs, + initialChangesets: initialChangesets, + initialStyledContents: atextForWire, + savedRevisions: savedRevisions, + currentTime: rev.timestamp, + sliderEnabled: (!appjet.cache.killSlider) && request.params.slider != 0, + supportsSlider: supportsSlider, + historicalAuthorData: historicalAuthorData, + colorPalette: globals.COLOR_PALETTE, + padIdForUrl: readOnlyIdOrLocalPadId, + fullWidth: request.params.fullScreen == 1, + disableRightBar: request.params.sidebar == 0, + }); + + var userId = padusers.getUserId(); + var isPro = isProDomainRequest(); + var isProUser = (isPro && ! padusers.isGuest(userId)); + + var bodyClass = ["limwidth", + (isPro ? "propad" : "nonpropad"), + (isProUser ? "prouser" : "nonprouser")].join(" "); + + renderHtml("pad/padview_body.ejs", { + bodyClass: bodyClass, + isPro: isPro, + isProAccountHolder: isProUser, + account: pro_accounts.getSessionProAccount(), + signinUrl: '/ep/account/sign-in?cont='+ + encodeURIComponent(request.url), + padId: readOnlyIdOrLocalPadId, + padTitle: documentBarTitle, + rlabel: rev.label, + padHTML: padHTML, + padText: padText, + savedBy: rev.savedBy, + savedIp: rev.ip, + savedWhen: rev.timestamp, + toHTML: toHTML, + revisionId: revisionId, + dateFormat: dateFormat(rev.timestamp), + readOnly: isReadOnly, + roPadId: roPadId, + hasOffice: hasOffice() + }); + } + + return true; +} + +function getRevisionInfo(localPadId, revisionId) { + var rev = padutils.accessPadLocal(localPadId, function(pad) { + if (!pad.exists()) { + return null; + } + var r; + if (revisionId == "latest") { + // a "fake" revision for HEAD + var headRevNum = pad.getHeadRevisionNumber(); + r = { + revNum: headRevNum, + label: "Latest text of pad "+localPadId, + savedBy: null, + savedIp: null, + timestamp: +pad.getRevisionDate(headRevNum) + }; + } else if (revisionId == "autorecover") { + var revNum = _findLastGoodRevisionInPad(pad); + r = { + revNum: revNum, + label: "Auto-recovered text of pad "+localPadId, + savedBy: null, + savedIp: null, + timestamp: +pad.getRevisionDate(revNum) + }; + } else if(revisionId.indexOf("rev.") === 0) { + var revNum = parseInt(revisionId.split(".")[1]); + var latest = pad.getHeadRevisionNumber(); + if(revNum > latest) + revNum = latest; + r = { + revNum: revNum, + label: "Version " + revNum, + savedBy: null, + savedIp: null, + timestamp: +pad.getRevisionDate(revNum) + } + + } else { + r = revisions.getStoredRevision(pad, revisionId); + } + if (!r) { + return null; + } + return r; + }, "r"); + return rev; +} + +function _findLastGoodRevisionInPad(pad) { + var revNum = pad.getHeadRevisionNumber(); + function valueOrNullOnError(f) { + try { return f(); } catch (e) { return null; } + } + function isAcceptable(strOrNull) { + return (strOrNull && strOrNull.length > 20); + } + while (revNum > 0 && + ! isAcceptable(valueOrNullOnError(function() { return pad.getRevisionText(revNum); }))) { + revNum--; + } + return revNum; +} + +function _getPadHTML(pad, revNum) { + var atext = pad.getInternalRevisionAText(revNum); + var textlines = Changeset.splitTextLines(atext.text); + var alines = Changeset.splitAttributionLines(atext.attribs, + atext.text); + + var pieces = []; + var apool = pad.pool(); + for(var i=0;i', + node.innerHTML, '\n'); + } + return pieces.join(''); +} \ No newline at end of file diff --git a/trunk/etherpad/src/etherpad/control/pne_manual_control.js b/trunk/etherpad/src/etherpad/control/pne_manual_control.js new file mode 100644 index 0000000..0dd65f8 --- /dev/null +++ b/trunk/etherpad/src/etherpad/control/pne_manual_control.js @@ -0,0 +1,75 @@ +/** + * 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. + */ + +import("funhtml.*"); + +import("etherpad.utils.*"); +import("etherpad.globals.*"); + +function onRequest() { + var p = request.path.split('/')[3]; + if (!p) { + p = "main"; + } + if (_getTitle(p)) { + _renderManualPage(p); + return true; + } else { + return false; + } +} + +function _getTitle(t) { + var titles = { + 'main': " ", + 'installation-guide': "Installation Guide", + 'upgrade-guide': "Upgrade Guide", + 'configuration-guide': "Configuration Guide", + 'troubleshooting': "Troubleshooting", + 'faq': "FAQ", + 'changelog': "ChangeLog" + }; + return titles[t]; +} + +function _renderTopnav(p) { + var d = DIV({className: "pne-manual-topnav"}); + if (p != "main") { + d.push(A({href: '/ep/pne-manual/'}, "PNE Manual"), + " > ", + _getTitle(p)); + } + return d; +} + +function _renderManualPage(p, data) { + data = (data || {}); + data.pneVersion = PNE_RELEASE_VERSION; + + function getContent() { + return renderTemplateAsString('pne-manual/'+p+'.ejs', data); + } + renderFramed('pne-manual/manual-template.ejs', { + getContent: getContent, + renderTopnav: function() { return _renderTopnav(p); }, + title: _getTitle(p), + id: p, + }); + return true; +} + + + diff --git a/trunk/etherpad/src/etherpad/control/pne_tracker_control.js b/trunk/etherpad/src/etherpad/control/pne_tracker_control.js new file mode 100644 index 0000000..ee36645 --- /dev/null +++ b/trunk/etherpad/src/etherpad/control/pne_tracker_control.js @@ -0,0 +1,48 @@ +/** + * 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. + */ + +import("image"); +import("blob"); +import("sqlbase.sqlobj"); +import("jsutils.*"); + +function render_t() { + var data = { + date: new Date(), + remoteIp: request.clientAddr + }; + if (request.params.k) { + data.keyHash = request.params.k; + } + var found = false; + eachProperty(request.params, function(name, value) { + if (name != "k") { + data.name = name; + data.value = value; + found = true; + } + }); + if (found) { + sqlobj.insert('pne_tracking_data', data); + } + + // serve a 1x1 white image + if (!appjet.cache.pneTrackingImage) { + appjet.cache.pneTrackingImage = image.solidColorImageBlob(1, 1, "ffffff"); + } + blob.serveBlob(appjet.cache.pneTrackingImage); +} + diff --git a/trunk/etherpad/src/etherpad/control/pro/account_control.js b/trunk/etherpad/src/etherpad/control/pro/account_control.js new file mode 100644 index 0000000..031dbe6 --- /dev/null +++ b/trunk/etherpad/src/etherpad/control/pro/account_control.js @@ -0,0 +1,369 @@ +/** + * 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. + */ + +import("stringutils"); +import("stringutils.*"); +import("funhtml.*"); +import("email.sendEmail"); +import("cache_utils.syncedWithCache"); + +import("etherpad.helpers"); +import("etherpad.utils.*"); +import("etherpad.sessions.getSession"); +import("etherpad.pro.pro_accounts"); +import("etherpad.pro.pro_accounts.getSessionProAccount"); +import("etherpad.pro.domains"); +import("etherpad.pro.pro_utils"); +import("etherpad.pro.pro_account_auto_signin"); +import("etherpad.pro.pro_config"); +import("etherpad.pad.pad_security"); +import("etherpad.pad.padutils"); +import("etherpad.pad.padusers"); +import("etherpad.collab.collab_server"); + +function onRequest() { + if (!getSession().tempFormData) { + getSession().tempFormData = {}; + } + + return false; // path not handled here +} + +//-------------------------------------------------------------------------------- +// helpers +//-------------------------------------------------------------------------------- + +function _redirOnError(m, clearQuery) { + if (m) { + getSession().accountFormError = m; + + var dest = request.url; + if (clearQuery) { + dest = request.path; + } + response.redirect(dest); + } +} + +function setSigninNotice(m) { + getSession().accountSigninNotice = m; +} + +function setSessionError(m) { + getSession().accountFormError = m; +} + +function _topDiv(id, name) { + var m = getSession()[name]; + if (m) { + delete getSession()[name]; + return DIV({id: id}, m); + } else { + return ''; + } +} + +function _messageDiv() { return _topDiv('account-message', 'accountMessage'); } +function _errorDiv() { return _topDiv('account-error', 'accountFormError'); } +function _signinNoticeDiv() { return _topDiv('signin-notice', 'accountSigninNotice'); } + +function _renderTemplate(name, data) { + data.messageDiv = _messageDiv; + data.errorDiv = _errorDiv; + data.signinNotice = _signinNoticeDiv; + data.tempFormData = getSession().tempFormData; + renderFramed('pro/account/'+name+'.ejs', data); +} + +//---------------------------------------------------------------- +// /ep/account/ +//---------------------------------------------------------------- + +function render_main_get() { + _renderTemplate('my-account', { + account: getSessionProAccount(), + changePass: getSession().changePass + }); +} + +function render_update_info_get() { + response.redirect('/ep/account/'); +} + +function render_update_info_post() { + var fullName = request.params.fullName; + var email = trim(request.params.email); + + getSession().tempFormData.email = email; + getSession().tempFormData.fullName = fullName; + + _redirOnError(pro_accounts.validateEmail(email)); + _redirOnError(pro_accounts.validateFullName(fullName)); + + pro_accounts.setEmail(getSessionProAccount(), email); + pro_accounts.setFullName(getSessionProAccount(), fullName); + + getSession().accountMessage = "Info updated."; + response.redirect('/ep/account/'); +} + +function render_update_password_get() { + response.redirect('/ep/account/'); +} + +function render_update_password_post() { + var password = request.params.password; + var passwordConfirm = request.params.passwordConfirm; + + if (password != passwordConfirm) { _redirOnError('Passwords did not match.'); } + + _redirOnError(pro_accounts.validatePassword(password)); + + pro_accounts.setPassword(getSessionProAccount(), password); + + if (getSession().changePass) { + delete getSession().changePass; + response.redirect('/'); + } + + getSession().accountMessage = "Password updated."; + response.redirect('/ep/account/'); +} + +//-------------------------------------------------------------------------------- +// signin/signout +//-------------------------------------------------------------------------------- + +function render_sign_in_get() { + if (request.params.uid && request.params.tp) { + var m = pro_accounts.authenticateTempSignIn(Number(request.params.uid), request.params.tp); + if (m) { + getSession().accountFormError = m; + response.redirect('/ep/account/'); + } + } + if (request.params.instantSigninKey) { + _attemptInstantSignin(request.params.instantSigninKey); + } + if (getSession().recentlySignedOut && getSession().accountFormError) { + delete getSession().accountFormError; + delete getSession().recentlySignedOut; + } + // Note: must check isAccountSignedIn before calling checkAutoSignin()! + if (pro_accounts.isAccountSignedIn()) { + _redirectToPostSigninDestination(); + } + pro_account_auto_signin.checkAutoSignin(); + var domainRecord = domains.getRequestDomainRecord(); + var showGuestBox = false; + if (request.params.guest && request.params.padId) { + showGuestBox = true; + } + _renderTemplate('signin', { + domain: pro_utils.getFullProDomain(), + siteName: toHTML(pro_config.getConfig().siteName), + email: getSession().tempFormData.email || "", + password: getSession().tempFormData.password || "", + rememberMe: getSession().tempFormData.rememberMe || false, + showGuestBox: showGuestBox, + localPadId: request.params.padId + }); +} + +function _attemptInstantSignin(key) { + // See src/etherpad/control/global_pro_account_control.js + var email = null; + var password = null; + syncedWithCache('global_signin_passwords', function(c) { + if (c[key]) { + email = c[key].email; + password = c[key].password; + } + delete c[key]; + }); + getSession().tempFormData.email = email; + _redirOnError(pro_accounts.authenticateSignIn(email, password), true); +} + +function render_sign_in_post() { + var email = trim(request.params.email); + var password = request.params.password; + + getSession().tempFormData.email = email; + getSession().tempFormData.rememberMe = request.params.rememberMe; + + _redirOnError(pro_accounts.authenticateSignIn(email, password)); + pro_account_auto_signin.setAutoSigninCookie(request.params.rememberMe); + _redirectToPostSigninDestination(); +} + +function render_guest_sign_in_get() { + var localPadId = request.params.padId; + var domainId = domains.getRequestDomainId(); + var globalPadId = padutils.makeGlobalId(domainId, localPadId); + var userId = padusers.getUserId(); + + pro_account_auto_signin.checkAutoSignin(); + pad_security.clearKnockStatus(userId, globalPadId); + + _renderTemplate('signin-guest', { + localPadId: localPadId, + errorMessage: getSession().guestAccessError, + siteName: toHTML(pro_config.getConfig().siteName), + guestName: padusers.getUserName() || "" + }); +} + +function render_guest_sign_in_post() { + function _err(m) { + if (m) { + getSession().guestAccessError = m; + response.redirect(request.url); + } + } + var displayName = request.params.guestDisplayName; + var localPadId = request.params.localPadId; + if (!(displayName && displayName.length > 0)) { + _err("Please enter a display name"); + } + getSession().guestDisplayName = displayName; + response.redirect('/ep/account/guest-knock?padId='+encodeURIComponent(localPadId)+ + "&guestDisplayName="+encodeURIComponent(displayName)); +} + +function render_guest_knock_get() { + var localPadId = request.params.padId; + helpers.addClientVars({ + localPadId: localPadId, + guestDisplayName: request.params.guestDisplayName, + padUrl: "http://"+httpHost(request.host)+"/"+localPadId + }); + _renderTemplate('guest-knock', {}); +} + +function render_guest_knock_post() { + var localPadId = request.params.padId; + var displayName = request.params.guestDisplayName; + var domainId = domains.getRequestDomainId(); + var globalPadId = padutils.makeGlobalId(domainId, localPadId); + var userId = padusers.getUserId(); + + response.setContentType("text/plain; charset=utf-8"); + // has the knock already been answsered? + var currentAnswer = pad_security.getKnockAnswer(userId, globalPadId); + if (currentAnswer) { + response.write(currentAnswer); + } else { + collab_server.guestKnock(globalPadId, userId, displayName); + response.write("wait"); + } +} + +function _redirectToPostSigninDestination() { + var cont = request.params.cont; + if (!cont) { cont = '/'; } + response.redirect(cont); +} + +function render_sign_out() { + pro_account_auto_signin.setAutoSigninCookie(false); + pro_accounts.signOut(); + delete getSession().padPasswordAuth; + getSession().recentlySignedOut = true; + response.redirect("/"); +} + +//-------------------------------------------------------------------------------- +// create-admin-account (eepnet only) +//-------------------------------------------------------------------------------- + +function render_create_admin_account_get() { + if (pro_accounts.doesAdminExist()) { + renderFramedError("An admin account already exists on this domain."); + response.stop(); + } + _renderTemplate('create-admin-account', {}); +} + +function render_create_admin_account_post() { + var email = trim(request.params.email); + var password = request.params.password; + var passwordConfirm = request.params.passwordConfirm; + var fullName = request.params.fullName; + + getSession().tempFormData.email = email; + getSession().tempFormData.fullName = fullName; + + if (password != passwordConfirm) { _redirOnError('Passwords did not match.'); } + + _redirOnError(pro_accounts.validateEmail(email)); + _redirOnError(pro_accounts.validateFullName(fullName)); + _redirOnError(pro_accounts.validatePassword(password)); + + pro_accounts.createNewAccount(null, fullName, email, password, true); + + var u = pro_accounts.getAccountByEmail(email, null); + + // TODO: should we send a welcome email here? + //pro_accounts.sendWelcomeEmail(u); + + _redirOnError(pro_accounts.authenticateSignIn(email, password)); + + response.redirect("/"); +} + + +//-------------------------------------------------------------------------------- +// forgot password +//-------------------------------------------------------------------------------- + +function render_forgot_password_get() { + if (request.params.instantSubmit && request.params.email) { + render_forgot_password_post(); + } else { + _renderTemplate('forgot-password', { + email: getSession().tempFormData.email || "" + }); + } +} + +function render_forgot_password_post() { + var email = trim(request.params.email); + + getSession().tempFormData.email = email; + + var u = pro_accounts.getAccountByEmail(email, null); + if (!u) { + _redirOnError("Account not found: "+email); + } + + var tempPass = stringutils.randomString(10); + pro_accounts.setTempPassword(u, tempPass); + + var subj = "EtherPad: Request to reset your password on "+request.domain; + var body = renderTemplateAsString('pro/account/forgot-password-email.ejs', { + account: u, + recoverUrl: pro_accounts.getTempSigninUrl(u, tempPass) + }); + var fromAddr = pro_utils.getEmailFromAddr(); + sendEmail(u.email, fromAddr, subj, {}, body); + + getSession().accountMessage = "An email has been sent to "+u.email+" with instructions to reset the password."; + response.redirect(request.path); +} + + + diff --git a/trunk/etherpad/src/etherpad/control/pro/admin/account_manager_control.js b/trunk/etherpad/src/etherpad/control/pro/admin/account_manager_control.js new file mode 100644 index 0000000..8f93b2e --- /dev/null +++ b/trunk/etherpad/src/etherpad/control/pro/admin/account_manager_control.js @@ -0,0 +1,260 @@ +/** + * 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. + */ + +import("funhtml.*"); +import("stringutils"); +import("stringutils.*"); +import("email.sendEmail"); + +import("etherpad.globals.*"); +import("etherpad.utils.*"); +import("etherpad.sessions.getSession"); + +import("etherpad.control.pro.admin.pro_admin_control"); + +import("etherpad.pne.pne_utils"); +import("etherpad.pro.pro_accounts"); +import("etherpad.pro.pro_accounts.getSessionProAccount"); +import("etherpad.pro.pro_utils"); +import("etherpad.pro.pro_config"); +import("etherpad.pro.domains"); +import("etherpad.billing.team_billing"); + +jimport("java.lang.System.out.println"); + +function _err(m) { + if (m) { + getSession().accountManagerError = m; + response.redirect(request.path); + } +} + +function _renderTopDiv(mid, htmlId) { + var m = getSession()[mid]; + if (m) { + delete getSession()[mid]; + return DIV({id: htmlId}, m); + } else { + return ''; + } +} + +function _errorDiv() { return _renderTopDiv('accountManagerError', 'error-message'); } +function _messageDiv() { return _renderTopDiv('accountManagerMessage', 'message'); } +function _warningDiv() { return _renderTopDiv('accountManagerWarning', 'warning'); } + +function onRequest() { + var parts = request.path.split('/'); + + function dispatchAccountAction(action, handlerGet, handlerPost) { + if ((parts[4] == action) && (isNumeric(parts[5]))) { + if (request.isGet) { handlerGet(+parts[5]); } + if (request.isPost) { handlerPost(+parts[5]); } + return true; + } + return false; + } + + if (dispatchAccountAction('account', render_account_get, render_account_post)) { + return true; + } + if (dispatchAccountAction('delete-account', render_delete_account_get, render_delete_account_post)) { + return true; + }; + + return false; +} + +function render_main() { + var accountList = pro_accounts.listAllDomainAccounts(); + pro_admin_control.renderAdminPage('account-manager', { + accountList: accountList, + messageDiv: _messageDiv, + warningDiv: _warningDiv + }); +} + +function render_new_get() { + pro_admin_control.renderAdminPage('new-account', { + oldData: getSession().accountManagerFormData || {}, + stringutils: stringutils, + errorDiv: _errorDiv + }); +} + +function _ensureBillingOK() { + var activeAccounts = pro_accounts.getCachedActiveCount(domains.getRequestDomainId()); + if (activeAccounts < PRO_FREE_ACCOUNTS) { + return; + } + + var status = team_billing.getDomainStatus(domains.getRequestDomainId()); + if (!((status == team_billing.CURRENT) + || (status == team_billing.PAST_DUE))) { + _err(SPAN( + "A payment profile is required to create more than ", PRO_FREE_ACCOUNTS, + " accounts. ", + A({href: "/ep/admin/billing/", id: "billinglink"}, "Manage billing"))); + } +} + +function render_new_post() { + if (request.params.cancel) { + response.redirect('/ep/admin/account-manager/'); + } + + _ensureBillingOK(); + + var fullName = request.params.fullName; + var email = trim(request.params.email); + var tempPass = request.params.tempPass; + var makeAdmin = !!request.params.makeAdmin; + + getSession().accountManagerFormData = { + fullName: fullName, + email: email, + tempPass: tempPass, + makeAdmin: makeAdmin + }; + + // validation + if (!tempPass) { + tempPass = stringutils.randomString(6); + } + + _err(pro_accounts.validateEmail(email)); + _err(pro_accounts.validateFullName(fullName)); + _err(pro_accounts.validatePassword(tempPass)); + + var existingAccount = pro_accounts.getAccountByEmail(email, null); + if (existingAccount) { + _err("There is already a account with that email address."); + } + + pro_accounts.createNewAccount(null, fullName, email, tempPass, makeAdmin); + var account = pro_accounts.getAccountByEmail(email, null); + + pro_accounts.setTempPassword(account, tempPass); + sendWelcomeEmail(account, tempPass); + + delete getSession().accountManagerFormData; + getSession().accountManagerMessage = "Account "+fullName+" ("+email+") created successfully."; + response.redirect('/ep/admin/account-manager/'); +} + +function sendWelcomeEmail(account, tempPass) { + var subj = "Welcome to EtherPad on "+pro_utils.getFullProDomain()+"!"; + var toAddr = account.email; + var fromAddr = pro_utils.getEmailFromAddr(); + + var body = renderTemplateAsString('pro/account/account-welcome-email.ejs', { + account: account, + adminAccount: getSessionProAccount(), + signinLink: pro_accounts.getTempSigninUrl(account, tempPass), + toEmail: toAddr, + siteName: pro_config.getConfig().siteName + }); + try { + sendEmail(toAddr, fromAddr, subj, {}, body); + } catch (ex) { + var d = DIV(); + d.push(P("Warning: unable to send welcome email.")); + if (pne_utils.isPNE()) { + d.push(P("Perhaps you have not ", + A({href: '/ep/admin/pne-config'}, "Configured SMTP on this server", "?"))); + } + getSession().accountManagerWarning = d; + } +} + +// Managing a single account. +function render_account_get(accountId) { + var account = pro_accounts.getAccountById(accountId); + if (!account) { + response.write("Account not found."); + return true; + } + pro_admin_control.renderAdminPage('manage-account', { + account: account, + errorDiv: _errorDiv, + warningDiv: _warningDiv + }); +} + +function render_account_post(accountId) { + if (request.params.cancel) { + response.redirect('/ep/admin/account-manager/'); + } + var newFullName = request.params.newFullName; + var newEmail = request.params.newEmail; + var newIsAdmin = !!request.params.newIsAdmin; + + _err(pro_accounts.validateEmail(newEmail)); + _err(pro_accounts.validateFullName(newFullName)); + + if ((!newIsAdmin) && (accountId == getSessionProAccount().id)) { + _err("You cannot remove your own administrator privileges."); + } + + var account = pro_accounts.getAccountById(accountId); + if (!account) { + response.write("Account not found."); + return true; + } + + pro_accounts.setEmail(account, newEmail); + pro_accounts.setFullName(account, newFullName); + pro_accounts.setIsAdmin(account, newIsAdmin); + + getSession().accountManageMessage = "Info updated."; + response.redirect('/ep/admin/account-manager/'); +} + +function render_delete_account_get(accountId) { + var account = pro_accounts.getAccountById(accountId); + if (!account) { + response.write("Account not found."); + return true; + } + pro_admin_control.renderAdminPage('delete-account', { + account: account, + errorDiv: _errorDiv + }); +} + +function render_delete_account_post(accountId) { + if (request.params.cancel) { + response.redirect("/ep/admin/account-manager/account/"+accountId); + } + + if (accountId == getSessionProAccount().id) { + getSession().accountManagerError = "You cannot delete your own account."; + response.redirect("/ep/admin/account-manager/account/"+accountId); + } + + var account = pro_accounts.getAccountById(accountId); + if (!account) { + response.write("Account not found."); + return true; + } + + pro_accounts.setDeleted(account); + getSession().accountManagerMessage = "The account "+account.fullName+" <"+account.email+"> has been deleted."; + response.redirect("/ep/admin/account-manager/"); +} + + + diff --git a/trunk/etherpad/src/etherpad/control/pro/admin/license_manager_control.js b/trunk/etherpad/src/etherpad/control/pro/admin/license_manager_control.js new file mode 100644 index 0000000..ca6d6a6 --- /dev/null +++ b/trunk/etherpad/src/etherpad/control/pro/admin/license_manager_control.js @@ -0,0 +1,128 @@ +/** + * 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. + */ + +import("fileutils.writeRealFile"); +import("stringutils"); + +import("etherpad.licensing"); +import("etherpad.sessions.getSession"); +import("etherpad.utils.*"); +import("etherpad.pne.pne_utils"); + +import("etherpad.control.pro.admin.pro_admin_control"); + +jimport("java.lang.System.out.println"); + +//---------------------------------------------------------------- +// license manager +//---------------------------------------------------------------- + +function getPath() { + return '/ep/admin/pne-license-manager/'; +} + +function _getTemplateData(data) { + var licenseInfo = licensing.getLicense(); + data.licenseInfo = licenseInfo; + data.isUnlicensed = !licenseInfo; + data.isEvaluation = licensing.isEvaluation(); + data.isExpired = licensing.isExpired(); + data.isTooOld = licensing.isVersionTooOld(); + data.errorMessage = (getSession().errorMessage || null); + data.runningVersionString = pne_utils.getVersionString(); + data.licenseVersionString = licensing.getVersionString(); + return data; +} + +function render_main_get() { + licensing.reloadLicense(); + var licenseInfo = licensing.getLicense(); + if (!licenseInfo || licensing.isExpired()) { + response.redirect(getPath()+'edit'); + } + + pro_admin_control.renderAdminPage('pne-license-manager', + _getTemplateData({edit: false})); +} + +function render_edit_get() { + licensing.reloadLicense(); + + if (request.params.btn) { response.redirect(request.path); } + + var licenseInfo = licensing.getLicense(); + var oldData = getSession().oldLicenseData; + if (!oldData) { + oldData = {}; + if (licenseInfo) { + oldData.orgName = licenseInfo.organizationName; + oldData.personName = licenseInfo.personName; + } + } + + pro_admin_control.renderAdminPage('pne-license-manager', + _getTemplateData({edit: true, oldData: oldData})); + + delete getSession().errorMessage; +} + +function render_edit_post() { + pne_utils.enableTrackingAgain(); + + function _trim(s) { + if (!s) { return ''; } + return stringutils.trim(s); + } + function _clean(s) { + s = s.replace(/\W/g, ''); + s = s.replace(/\+/g, ''); + return s; + } + + if (request.params.cancel) { + delete getSession().oldLicenseData; + response.redirect(getPath()); + } + + var personName = _trim(request.params.personName); + var orgName = _trim(request.params.orgName); + var licenseString = _clean(request.params.licenseString); + + getSession().oldLicenseData = { + personName: personName, orgName: orgName, licenseString: licenseString}; + + var key = [personName,orgName,licenseString].join(":"); + println("validating key [ "+key+" ]"); + + if (!licensing.isValidKey(key)) { + getSession().errorMessage = "Invalid License Key"; + response.redirect(request.path); + } + + // valid key. write to disk. + var writeSuccess = false; + try { + println("writing key file: ./data/license.key"); + writeRealFile("./data/license.key", key); + writeSuccess = true; + } catch (ex) { + println("exception: "+ex); + getSession().errorMessage = "Failed to write key to disk. (Do you have permission to write ./data/license.key ?)."; + } + response.redirect(getPath()); +} + + diff --git a/trunk/etherpad/src/etherpad/control/pro/admin/pro_admin_control.js b/trunk/etherpad/src/etherpad/control/pro/admin/pro_admin_control.js new file mode 100644 index 0000000..f9ce179 --- /dev/null +++ b/trunk/etherpad/src/etherpad/control/pro/admin/pro_admin_control.js @@ -0,0 +1,283 @@ +/** + * 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. + */ + +import("stringutils"); +import("funhtml.*"); +import("dispatch.{Dispatcher,DirMatcher,forward}"); + +import("etherpad.licensing"); +import("etherpad.control.admincontrol"); +import("etherpad.control.pro.admin.license_manager_control"); +import("etherpad.control.pro.admin.account_manager_control"); +import("etherpad.control.pro.admin.pro_config_control"); +import("etherpad.control.pro.admin.team_billing_control"); + +import("etherpad.pad.padutils"); + +import("etherpad.admin.shell"); +import("etherpad.sessions"); +import("etherpad.sessions.getSession"); + +import("etherpad.pne.pne_utils"); +import("etherpad.pro.pro_accounts"); +import("etherpad.utils.*"); + +//---------------------------------------------------------------- + +var _pathPrefix = '/ep/admin/'; + +var _PRO = 1; +var _PNE_ONLY = 2; +var _ONDEMAND_ONLY = 3; + +function _getLeftnavItems() { + var nav = [ + _PRO, [ + [_PRO, null, "Admin"], + [_PNE_ONLY, "pne-dashboard", "Server Dashboard"], + [_PNE_ONLY, "pne-license-manager/", "Manage License"], + [_PRO, "account-manager/", "Manage Accounts"], + [_PRO, "recover-padtext", "Recover Pad Text"], + [_PRO, null, "Configuration"], + [_PRO, [[_PNE_ONLY, "pne-config", "Private Server Configuration"], + [_PRO, "pro-config", "Application Configuration"]]], + [_PNE_ONLY, null, "Documentation"], + [_PNE_ONLY, "/ep/pne-manual/", "Administrator's Manual"], + ] + ]; + return nav; +} + +function renderAdminLeftNav() { + function _make(x) { + if ((x[0] == _PNE_ONLY) && !pne_utils.isPNE()) { + return null; + } + if ((x[0] == _ONDEMAND_ONLY) && pne_utils.isPNE()) { + return null; + } + + if (x[1] instanceof Array) { + return _makelist(x[1]); + } else { + return _makeitem(x); + } + } + var selected; + function _makeitem(x) { + if (x[1]) { + var p = x[1]; + if (x[1].charAt(0) != '/') { + p = _pathPrefix+p; + } + var li = LI(A({href: p}, x[2])); + if (stringutils.startsWith(request.path, p)) { + // select the longest prefix match. + if (! selected || p.length > selected.path.length) { + selected = {path: p, li: li}; + } + } + return li; + } else { + return LI(DIV({className: 'leftnav-title'}, x[2])); + } + } + function _makelist(x) { + var ul = UL(); + x.forEach(function(y) { + var t = _make(y); + if (t) { ul.push(t); } + }); + return ul; + } + var d = DIV(_make(_getLeftnavItems())); + if (selected) { + selected.li.attribs.className = "selected"; + } + // leftnav looks stupid when it's not very tall. + for (var i = 0; i < 10; i++) { d.push(BR()); } + return d; +} + +function renderAdminPage(p, data) { + appjet.requestCache.proTopNavSelection = 'admin'; + function getAdminContent() { + if (typeof(p) == 'function') { + return p(); + } else { + return renderTemplateAsString('pro/admin/'+p+'.ejs', data); + } + } + renderFramed('pro/admin/admin-template.ejs', { + getAdminContent: getAdminContent, + renderAdminLeftNav: renderAdminLeftNav, + validLicense: pne_utils.isServerLicensed(), + }); +} + +//---------------------------------------------------------------- + +function onRequest() { + var disp = new Dispatcher(); + disp.addLocations([ + [DirMatcher(license_manager_control.getPath()), forward(license_manager_control)], + [DirMatcher('/ep/admin/account-manager/'), forward(account_manager_control)], + [DirMatcher('/ep/admin/pro-config/'), forward(pro_config_control)], + [DirMatcher('/ep/admin/billing/'), forward(team_billing_control)], + ]); + + if (disp.dispatch()) { + return true; + } + + // request will be handled by this module. + pro_accounts.requireAdminAccount(); +} + +function render_main() { +// renderAdminPage('admin'); + response.redirect('/ep/admin/account-manager/') +} + +function render_pne_dashboard() { + renderAdminPage('pne-dashboard', { + renderUptime: admincontrol.renderServerUptime, + renderResponseCodes: admincontrol.renderResponseCodes, + renderPadConnections: admincontrol.renderPadConnections, + renderTransportStats: admincontrol.renderCometStats, + todayActiveUsers: licensing.getActiveUserCount(), + userQuota: licensing.getActiveUserQuota() + }); +} + +var _documentedServerOptions = [ + 'listen', + 'listenSecure', + 'transportUseWildcardSubdomains', + 'sslKeyStore', + 'sslKeyPassword', + 'etherpad.soffice', + 'etherpad.adminPass', + 'etherpad.SQL_JDBC_DRIVER', + 'etherpad.SQL_JDBC_URL', + 'etherpad.SQL_USERNAME', + 'etherpad.SQL_PASSWORD', + 'smtpServer', + 'smtpUser', + 'smtpPass', + 'configFile', + 'etherpad.licenseKey', + 'verbose' +]; + +function render_pne_config_get() { + renderAdminPage('pne-config', { + propKeys: _documentedServerOptions, + appjetConfig: appjet.config + }); +} + +function render_pne_advanced_get() { + response.redirect("/ep/admin/shell"); +} + +function render_shell_get() { + if (!(pne_utils.isPNE() || sessions.isAnEtherpadAdmin())) { + return false; + } + appjet.requestCache.proTopNavSelection = 'admin'; + renderAdminPage('pne-shell', { + oldCmd: getSession().pneAdminShellCmd, + result: getSession().pneAdminShellResult, + elapsedMs: getSession().pneAdminShellElapsed + }); + delete getSession().pneAdminShellResult; + delete getSession().pneAdminShellElapsed; +} + +function render_shell_post() { + if (!(pne_utils.isPNE() || sessions.isAnEtherpadAdmin())) { + return false; + } + var cmd = request.params.cmd; + var start = +(new Date); + getSession().pneAdminShellCmd = cmd; + getSession().pneAdminShellResult = shell.getResult(cmd); + getSession().pneAdminShellElapsed = +(new Date) - start; + response.redirect(request.path); +} + +function render_recover_padtext_get() { + function getNumRevisions(localPadId) { + return padutils.accessPadLocal(localPadId, function(pad) { + if (!pad.exists()) { return null; } + return 1+pad.getHeadRevisionNumber(); + }); + } + function getPadText(localPadId, revNum) { + return padutils.accessPadLocal(localPadId, function(pad) { + if (!pad.exists()) { return null; } + return pad.getRevisionText(revNum); + }); + } + + var localPadId = request.params.localPadId; + var revNum = request.params.revNum; + + var d = DIV({style: "font-size: .8em;"}); + + d.push(FORM({action: request.path, method: "get"}, + P({style: "margin-top: 0;"}, LABEL("Pad ID: "), + INPUT({type: "text", name: "localPadId", value: localPadId || ""}), + INPUT({type: "submit", value: "Submit"})))); + + var showPadHelp = false; + var revisions = null; + + if (!localPadId) { + showPadHelp = true; + } else { + revisions = getNumRevisions(localPadId); + if (!revisions) { + d.push(P("Pad not found: "+localPadId)); + } else { + d.push(P(B(localPadId), " has ", revisions, " revisions.")); + d.push(P("Enter a revision number (0-"+revisions+") to recover the pad text for that revision:")); + d.push(FORM({action: request.path, method: "get"}, + P(LABEL("Revision number:"), + INPUT({type: "hidden", name: "localPadId", value: localPadId}), + INPUT({type: "text", name: "revNum", value: revNum || (revisions - 1)}), + INPUT({type: "submit", value: "Submit"})))); + } + } + + if (showPadHelp) { + d.push(P({style: "font-size: 1em; color: #555;"}, + 'The pad ID is the same as the URL to the pad, without the leading "/".', + ' For example, if the pad lives at http://pad.spline.inf.fu-berlin.de/foobar,', + ' then the pad ID is "foobar" (without the quotes).')) + } + + if (revisions && revNum && (revNum < revisions)) { + var padText = getPadText(localPadId, revNum); + d.push(P(B("Pad text for ["+localPadId+"] revision #"+revNum))); + d.push(DIV({style: "font-family: monospace; border: 1px solid #ccc; background: #ffe; padding: 1em;"}, padText)); + } + + renderAdminPage(function() { return d; }); +} + + diff --git a/trunk/etherpad/src/etherpad/control/pro/admin/pro_config_control.js b/trunk/etherpad/src/etherpad/control/pro/admin/pro_config_control.js new file mode 100644 index 0000000..b03da45 --- /dev/null +++ b/trunk/etherpad/src/etherpad/control/pro/admin/pro_config_control.js @@ -0,0 +1,54 @@ +/** + * 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. + */ + +import("funhtml.*"); + +import("etherpad.sessions.getSession"); +import("etherpad.control.pro.admin.pro_admin_control"); +import("etherpad.pro.pro_config"); + +function _renderTopDiv(mid, htmlId) { + var m = getSession()[mid]; + if (m) { + delete getSession()[mid]; + return DIV({id: htmlId}, m); + } else { + return ''; + } +} + +function _messageDiv() { + return _renderTopDiv('proConfigMessage', 'pro-config-message'); +} + +function render_main_get() { + pro_config.reloadConfig(); + var config = pro_config.getConfig(); + pro_admin_control.renderAdminPage('pro-config', { + config: config, + messageDiv: _messageDiv + }); +} + +function render_main_post() { + pro_config.setConfigVal('siteName', request.params.siteName); + pro_config.setConfigVal('alwaysHttps', !!request.params.alwaysHttps); + pro_config.setConfigVal('defaultPadText', request.params.defaultPadText); + getSession().proConfigMessage = "New settings applied."; + response.redirect(request.path); +} + + diff --git a/trunk/etherpad/src/etherpad/control/pro/admin/team_billing_control.js b/trunk/etherpad/src/etherpad/control/pro/admin/team_billing_control.js new file mode 100644 index 0000000..5be6a0e --- /dev/null +++ b/trunk/etherpad/src/etherpad/control/pro/admin/team_billing_control.js @@ -0,0 +1,447 @@ +/** + * 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. + */ + +import("dateutils"); +import("email.sendEmail"); +import("fastJSON"); +import("funhtml.*"); +import("jsutils.*"); +import("sqlbase.sqlcommon.inTransaction"); +import("stringutils.*"); + +import("etherpad.billing.billing"); +import("etherpad.billing.fields"); +import("etherpad.billing.team_billing"); +import("etherpad.control.pro.admin.pro_admin_control"); +import("etherpad.globals"); +import("etherpad.helpers"); +import("etherpad.pro.domains"); +import("etherpad.pro.pro_accounts"); +import("etherpad.pro.pro_utils"); +import("etherpad.sessions"); +import("etherpad.store.checkout"); +import("etherpad.utils.*"); + +import("static.js.billing_shared.{billing=>billingJS}"); + +var billingButtonName = "Confirm" + +function _cart() { + var s = sessions.getSession(); + if (! s.proBillingCart) { + s.proBillingCart = {}; + } + return s.proBillingCart; +} + +function _billingForm() { + return renderTemplateAsString('store/eepnet-checkout/billing-info.ejs', { + cart: _cart(), + billingButtonName: billingButtonName, + billingFinalPhrase: "", + helpers: helpers, + errorIfInvalid: _errorIfInvalid, + billing: billingJS, + obfuscateCC: checkout.obfuscateCC, + dollars: checkout.dollars, + countryList: fields.countryList, + usaStateList: fields.usaStateList, + getFullSuperdomainHost: pro_utils.getFullSuperdomainHost, + showCouponCode: true, + }); +} + +function _plural(num) { + return (num == 1 ? "" : "s"); +} + +function _billingSummary(domainId, subscription) { + var paymentInfo = team_billing.getRecurringBillingInfo(domainId); + if (! paymentInfo) { + return; + } + var latestInvoice = team_billing.getLatestPaidInvoice(subscription.id); + var usersSoFar = team_billing.getMaxUsers(domainId); + var costSoFar = team_billing.calculateSubscriptionCost(usersSoFar, subscription.coupon); + + var lastPaymentString = + (latestInvoice ? + "US $"+checkout.dollars(billing.centsToDollars(latestInvoice.amt))+ + " ("+latestInvoice.users+" account"+_plural(latestInvoice.users)+")"+ + ", on "+checkout.formatDate(latestInvoice.time) : + "None"); + + var coupon = false; + if (subscription.coupon) { + println("has a coupon: "+subscription.coupon); + var cval = team_billing.getCouponValue(subscription.coupon); + coupon = []; + if (cval.freeUsers) { + coupon.push(cval.freeUsers+" free user"+(cval.freeUsers == 1 ? "" : "s")); + } + if (cval.pctDiscount) { + coupon.push(cval.pctDiscount+"% savings"); + } + coupon = coupon.join(", "); + } + + return { + fullName: paymentInfo.fullname, + paymentSummary: + paymentInfo.paymentsummary + + (paymentInfo.expiration ? + ", expires "+checkout.formatExpiration(paymentInfo.expiration) : + ""), + lastPayment: lastPaymentString, + nextPayment: checkout.formatDate(subscription.paidThrough), + maxUsers: usersSoFar, + estimatedPayment: "US $"+checkout.dollars(costSoFar), + coupon: coupon + } +} + +function _statusMessage() { + if (_cart().statusMessage) { + return toHTML(P({style: "color: green;"}, _cart().statusMessage)); + } else { + return ''; + } +} + +function renderMainPage(doEdit) { + var cart = _cart(); + var domainId = domains.getRequestDomainId(); + var subscription = team_billing.getSubscriptionForCustomer(domainId); + var pendingInvoice = team_billing.getLatestPendingInvoice(domainId) + var usersSoFar = team_billing.getMaxUsers(domainId); + var costSoFar = team_billing.calculateSubscriptionCost(usersSoFar, subscription && subscription.coupon); + + checkout.guessBillingNames(cart, pro_accounts.getSessionProAccount().fullName); + if (! cart.billingReferralCode) { + if (subscription && subscription.coupon) { + cart.billingReferralCode = subscription.coupon; + } + } + + var summary = _billingSummary(domainId, subscription); + if (! summary) { + doEdit = true; + } + + pro_admin_control.renderAdminPage('manage-billing', { + billingForm: _billingForm, + doEdit: doEdit, + paymentInfo: summary, + getFullSuperdomainHost: pro_utils.getFullSuperdomainHost, + firstCharge: checkout.formatDate(subscription ? subscription.paidThrough : dateutils.nextMonth(new Date)), + billingButtonName: billingButtonName, + errorDiv: _errorDiv, + showBackButton: (summary != undefined), + statusMessage: _statusMessage, + isBehind: (subscription ? subscription.paidThrough < Date.now() - 86400*1000 : false), + amountDue: "US $"+checkout.dollars(billing.centsToDollars(pendingInvoice ? pendingInvoice.amt : costSoFar*100)), + cart: _cart() + }); + + delete _cart().errorId; + delete _cart().errorMsg; + delete _cart().statusMessage; +} + +function render_main() { + renderMainPage(false); +} + +function render_edit() { + renderMainPage(true); +} + +function _errorDiv() { + var m = _cart().errorMsg; + if (m) { + return DIV({className: 'errormsg', id: 'errormsg'}, m); + } else { + return ''; + } +} + +function _validationError(id, errorMessage) { + var cart = _cart(); + cart.errorMsg = errorMessage; + cart.errorId = {}; + if (id instanceof Array) { + id.forEach(function(k) { + cart.errorId[k] = true; + }); + } else { + cart.errorId[id] = true; + } + response.redirect('/ep/admin/billing/edit'); +} + +function _errorIfInvalid(id) { + var cart = _cart(); + if (cart.errorId && cart.errorId[id]) { + return 'error'; + } else { + return ''; + } +} + +function paypalNotifyUrl() { + return request.scheme+"://"+pro_utils.getFullSuperdomainHost()+"/ep/store/paypalnotify"; +} + +function _paymentSummary(payInfo) { + return payInfo.cardType + " ending in " + payInfo.cardNumber.substr(-4); +} + +function _expiration(payInfo) { + return payInfo.cardExpiration; +} + +function _attemptAuthorization(success_f) { + var cart = _cart(); + var domain = domains.getRequestDomainRecord(); + var domainId = domain.id; + var domainName = domain.subDomain; + var payInfo = checkout.generatePayInfo(cart); + var proAccount = pro_accounts.getSessionProAccount(); + var fullName = cart.billingFirstName+" "+cart.billingLastName; + var email = proAccount.email; + + // PCI rules require that we not store the CVV longer than necessary to complete the transaction + var savedCvv = payInfo.cardCvv; + delete payInfo.cardCvv; + checkout.writeToEncryptedLog(fastJSON.stringify({date: String(new Date()), domain: domain, payInfo: payInfo})); + payInfo.cardCvv = savedCvv; + + var result = billing.authorizePurchase(payInfo, paypalNotifyUrl()); + if (result.status == 'success') { + billing.log({type: 'new-subscription', + name: fullName, + domainId: domainId, + domainName: domainName}); + success_f(result); + } else if (result.status == 'pending') { + _validationError('', "Your authorization is pending. When it clears, your account will be activated. "+ + "You may choose to pay by different means now, or wait until your authorization clears."); + } else if (result.status == 'failure') { + var paypalResult = result.debug; + billing.log({'type': 'FATAL', value: "Direct purchase failed on paypal.", cart: cart, paypal: paypalResult}); + checkout.validateErrorFields(_validationError, "There seems to be an error in your billing information."+ + " Please verify and correct your ", + result.errorField.userErrors); + checkout.validateErrorFields(_validationError, "The bank declined your billing information. Please try a different ", + result.errorField.permanentErrors); + _validationError('', "A temporary error has prevented processing of your payment. Please try again later."); + } else { + billing.log({'type': 'FATAL', value: "Unknown error: "+result.status+" - debug: "+result.debug}); + sendEmail('support@pad.spline.inf.fu-berlin.de', 'urgent@pad.spline.inf.fu-berlin.de', 'UNKNOWN ERROR WARNING!', {}, + "Hey,\n\nThis is a billing system error. Some unknown error occurred. "+ + "This shouldn't ever happen. Probably good to let J.D. know. \n\n"+ + fastJSON.stringify(cart)); + _validationError('', "An unknown error occurred. We're looking into it!") + } +} + +function _processNewSubscription() { + _attemptAuthorization(function(result) { + var domain = domains.getRequestDomainRecord(); + var domainId = domain.id; + var domainName = domain.subDomain; + + var cart = _cart(); + var payInfo = checkout.generatePayInfo(cart); + var proAccount = pro_accounts.getSessionProAccount(); + var fullName = cart.billingFirstName+" "+cart.billingLastName; + var email = proAccount.email; + + inTransaction(function() { + + var subscriptionId = team_billing.createSubscription(domainId, cart.billingReferralCode); + + team_billing.setRecurringBillingInfo( + domainId, + fullName, + email, + _paymentSummary(payInfo), + _expiration(payInfo), + result.purchaseInfo.paypalId); + }); + + if (globals.isProduction()) { + sendEmail('sales@pad.spline.inf.fu-berlin.de', 'sales@pad.spline.inf.fu-berlin.de', "EtherPad: New paid pro account for "+fullName, {}, + "This is an automatic notification.\n\n"+fullName+" ("+email+") successfully set up "+ + "a billing profile for domain: "+domainName+"."); + } + }); +} + +function _updateExistingSubscription(subscription) { + var cart = _cart(); + + _attemptAuthorization(function(result) { + inTransaction(function() { + var cart = _cart(); + var domain = domains.getRequestDomainId(); + var payInfo = checkout.generatePayInfo(cart); + var proAccount = pro_accounts.getSessionProAccount(); + var fullName = cart.billingFirstName+" "+cart.billingLastName; + var email = proAccount.email; + + var subscriptionId = subscription.id; + + team_billing.setRecurringBillingInfo( + domain, + fullName, + email, + _paymentSummary(payInfo), + _expiration(payInfo), + result.purchaseInfo.paypalId); + }); + }); + + if (subscription.paidThrough < new Date) { + // if they're behind, do the purchase! + if (team_billing.processSubscription(subscription)) { + cart.statusMessage = "Your payment was successful, and your account is now up to date! You will receive a receipt by email." + } else { + cart.statusMessage = "Your payment failed; you will receive further instructions by email."; + } + } +} + +function _processBillingInfo() { + var cart = _cart(); + var domain = domains.getRequestDomainId(); + + var subscription = team_billing.getSubscriptionForCustomer(domain); + if (! subscription) { + _processNewSubscription(); + response.redirect('/ep/admin/billing/'); + } else { + team_billing.updateSubscriptionCouponCode(subscription.id, cart.billingReferralCode); + if (cart.billingCCNumber.length > 0) { + _updateExistingSubscription(subscription); + } + response.redirect('/ep/admin/billing') + } +} + +function _processPaypalPurchase() { + var domain = domains.getRequestDomainId(); + billing.log({type: "paypal-attempt", + domain: domain, + message: "Someone tried to use paypal to pay for on-demand."+ + " They got an error message. If this happens a lot, we should implement paypal."}) + java.lang.Thread.sleep(5000); + _validationError('billingPurchaseType', "There was an error contacting PayPal. Please try another payment type.") +} + +function _processInvoicePurchase() { + var output = [ + "Name: "+cart.billingFirstName+" "+cart.billingLastName, + "\nAddress: ", + cart.billingAddressLine1+(cart.billingAddressLine2.length > 0 ? "\n"+cart.billingAddressLine2 : ""), + cart.billingCity + ", " + (cart.billingState.length > 0 ? cart.billingState : cart.billingProvince), + cart.billingZipCode.length > 0 ? cart.billingZipCode : cart.billingPostalCode, + cart.billingCountry, + "\nEmail: ", + pro_accounts.getSessionProAccount().email + ].join("\n"); + var recipient = (globals.isProduction() ? 'sales@pad.spline.inf.fu-berlin.de' : 'jd@appjet.com'); + sendEmail( + recipient, + 'sales@pad.spline.inf.fu-berlin.de', + 'Invoice payment request - '+pro_utils.getProRequestSubdomain(), + {}, + "Hi there,\n\nA pro user tried to pay by invoice. Their information follows."+ + "\n\nThanks!\n\n"+output); + _validationError('', "Your information has been sent to our sales department; a salesperson will contact you shortly regarding your invoice request.") +} + +function render_apply() { + var cart = _cart(); + eachProperty(request.params, function(k, v) { + if (startsWith(k, "billing")) { + if (k == "billingCCNumber" && v.charAt(0) == 'X') { return; } + cart[k] = toHTML(v); + } + }); + + if (! request.params.backbutton) { + var allPaymentFields = ["billingCCNumber", "billingExpirationMonth", "billingExpirationYear", "billingCSC", "billingAddressLine1", "billingAddressLine2", "billingCity", "billingState", "billingZipCode", "billingProvince", "billingPostalCode"]; + var allBlank = true; + allPaymentFields.forEach(function(field) { if (cart[field].length > 0) { allBlank = false; }}); + if (! allBlank) { + checkout.validateBillingCart(_validationError, cart); + } + } else { + response.redirect("/ep/admin/billing/"); + } + + var couponCode = cart.billingReferralCode; + + if (couponCode.length != 0 && (couponCode.length != 8 || ! team_billing.getCouponValue(couponCode))) { + _validationError('billingReferralCode', 'Invalid referral code entered. Please verify your code and try again.'); + } + + if (cart.billingPurchaseType == 'paypal') { + _processPaypalPurchase(); + } else if (cart.billingPurchaseType == 'invoice') { + _processInvoicePurchase(); + } + + _processBillingInfo(); +} + +function handlePaypalNotify() { + // XXX: handle delayed paypal authorization +} + +function render_invoices() { + if (request.params.id) { + var purchaseId = team_billing.getSubscriptionForCustomer(domains.getRequestDomainId()).id; + var invoice = billing.getInvoice(request.params.id); + if (invoice.purchase != purchaseId) { + response.redirect(request.path); + } + + var transaction; + var adjustments = billing.getAdjustments(invoice.id); + if (adjustments.length == 1) { + transaction = billing.getTransaction(adjustments[0].transaction); + } + + pro_admin_control.renderAdminPage('single-invoice', { + formatDate: checkout.formatDate, + dollars: checkout.dollars, + centsToDollars: billing.centsToDollars, + invoice: invoice, + transaction: transaction + }); + } else { + var invoices = team_billing.getAllInvoices(domains.getRequestDomainId()); + + pro_admin_control.renderAdminPage('billing-invoices', { + invoices: invoices, + formatDate: checkout.formatDate, + dollars: checkout.dollars, + centsToDollars: billing.centsToDollars + }); + } +} \ No newline at end of file diff --git a/trunk/etherpad/src/etherpad/control/pro/pro_main_control.js b/trunk/etherpad/src/etherpad/control/pro/pro_main_control.js new file mode 100644 index 0000000..b4e3bc4 --- /dev/null +++ b/trunk/etherpad/src/etherpad/control/pro/pro_main_control.js @@ -0,0 +1,150 @@ +/** + * 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. + */ + +import("stringutils"); +import("dispatch.{Dispatcher,DirMatcher,forward}"); +import("funhtml.*"); +import("cache_utils.syncedWithCache"); + +import("etherpad.helpers"); +import("etherpad.utils.*"); +import("etherpad.sessions.getSession"); +import("etherpad.licensing"); +import("etherpad.pne.pne_utils"); +import("etherpad.pro.pro_pad_db"); +import("etherpad.pro.domains"); +import("etherpad.pro.pro_accounts"); +import("etherpad.pro.pro_accounts.getSessionProAccount"); +import("etherpad.pro.pro_padlist"); + +import("etherpad.control.pro.account_control"); +import("etherpad.control.pro.pro_padlist_control"); +import("etherpad.control.pro.admin.pro_admin_control"); +import("etherpad.control.pro.admin.account_manager_control"); + +import("etherpad.pad.activepads"); +import("etherpad.pad.model"); + + +function onRequest() { + var disp = new Dispatcher(); + disp.addLocations([ + [DirMatcher('/ep/account/'), forward(account_control)], + [DirMatcher('/ep/admin/'), forward(pro_admin_control)], + [DirMatcher('/ep/padlist/'), forward(pro_padlist_control)], + ]); + return disp.dispatch(); +} + +function render_main() { + if (request.path == '/ep/') { + response.redirect('/'); + } + + // recent pad list + var livePads = pro_pad_db.listLiveDomainPads(); + var recentPads = pro_pad_db.listAllDomainPads(); + + var renderLivePads = function() { + return pro_padlist.renderPadList(livePads, ['title', 'connectedUsers'], 10); + } + + var renderRecentPads = function() { + return pro_padlist.renderPadList(recentPads, ['title'], 10); + }; + + var r = domains.getRequestDomainRecord(); + + renderFramed('pro/pro_home.ejs', { + isEvaluation: licensing.isEvaluation(), + account: getSessionProAccount(), + isPNE: pne_utils.isPNE(), + pneVersion: pne_utils.getVersionString(), + livePads: livePads, + recentPads: recentPads, + renderRecentPads: renderRecentPads, + renderLivePads: renderLivePads, + orgName: r.orgName + }); + return true; +} + +function render_finish_activation_get() { + if (!isActivationAllowed()) { + response.redirect('/'); + } + + var accountList = pro_accounts.listAllDomainAccounts(); + if (accountList.length > 1) { + response.redirect('/'); + } + if (accountList.length == 0) { + throw Error("accountList.length should never be 0."); + } + + var acct = accountList[0]; + var tempPass = stringutils.randomString(10); + pro_accounts.setTempPassword(acct, tempPass); + account_manager_control.sendWelcomeEmail(acct, tempPass); + + var domainId = domains.getRequestDomainId(); + + syncedWithCache('pro-activations', function(c) { + delete c[domainId]; + }); + + renderNoticeString( + DIV({style: "font-size: 16pt; border: 1px solid green; background: #eeffee; margin: 2em 4em; padding: 1em;"}, + P("Success! You will receive an email shortly with instructions."), + DIV({style: "display: none;", id: "reference"}, acct.id, ":", tempPass))); +} + +function isActivationAllowed() { + if (request.path != '/ep/finish-activation') { + return false; + } + var allowed = false; + var domainId = domains.getRequestDomainId(); + return syncedWithCache('pro-activations', function(c) { + if (c[domainId]) { + return true; + } + return false; + }); +} + +function render_payment_required_get() { + // Users get to this page when there is a problem with billing: + // possibilities: + // * they try to create a new account but they have not entered + // payment information + // + // * their credit card lapses and any pro request fails. + // + // * others? + + var message = getSession().billingProblem || "A payment is required to proceed."; + var adminList = pro_accounts.listAllDomainAdmins(); + + renderFramed("pro/pro-payment-required.ejs", { + message: message, + isAdmin: pro_accounts.isAdminSignedIn(), + adminList: adminList + }); +} + + + diff --git a/trunk/etherpad/src/etherpad/control/pro/pro_padlist_control.js b/trunk/etherpad/src/etherpad/control/pro/pro_padlist_control.js new file mode 100644 index 0000000..9a90c67 --- /dev/null +++ b/trunk/etherpad/src/etherpad/control/pro/pro_padlist_control.js @@ -0,0 +1,200 @@ +/** + * 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. + */ + +import("funhtml.*"); +import("jsutils.*"); +import("stringutils"); + +import("etherpad.sessions.getSession"); +import("etherpad.utils.*"); +import("etherpad.helpers"); +import("etherpad.pad.exporthtml"); +import("etherpad.pad.padutils"); +import("etherpad.pro.pro_padmeta"); +import("etherpad.pro.pro_pad_db"); +import("etherpad.pro.domains"); +import("etherpad.pro.pro_accounts"); +import("etherpad.pro.pro_padlist"); + +jimport("java.lang.System.out.println"); + +function onRequest(name) { + if (name == "all_pads.zip") { + render_all_pads_zip_get(); + return true; + } else { + return false; + } +} + +function _getBaseUrl() { return "/ep/padlist/"; } + +function _renderPadNav() { + var d = DIV({id: "padlist-nav"}); + var ul = UL(); + var items = [ + ['allpads', 'all-pads', "All Pads"], + ['mypads', 'my-pads', "My Pads"], + ['archivedpads', 'archived-pads', "Archived Pads"] + ]; + for (var i = 0; i < items.length; i++) { + var item = items[i]; + var cn = ""; + if (request.path.split("/").slice(-1)[0] == item[1]) { + cn = "selected"; + } + ul.push(LI(A({id: "nav-"+item[1], href: _getBaseUrl()+item[1], className: cn}, item[2]))); + } + ul.push(html(helpers.clearFloats())); + d.push(ul); + d.push(FORM({id: "newpadform", method: "get", action: "/ep/pad/newpad"}, + INPUT({type: "submit", value: "New Pad"}))); + d.push(html(helpers.clearFloats())); + return d; +} + +function _renderPage(name, data) { + getSession().latestPadlistView = request.path + "?" + request.query; + var r = domains.getRequestDomainRecord(); + appjet.requestCache.proTopNavSelection = 'padlist'; + data.renderPadNav = _renderPadNav; + data.orgName = r.orgName; + data.renderNotice = function() { + var m = getSession().padlistMessage; + if (m) { + delete getSession().padlistMessage; + return DIV({className: "padlist-notice"}, m); + } else { + return ""; + } + }; + + renderFramed("pro/padlist/"+name+".ejs", data); +} + +function _renderListPage(padList, showingDesc, columns) { + _renderPage("pro-padlist", { + padList: padList, + renderPadList: function() { + return pro_padlist.renderPadList(padList, columns); + }, + renderShowingDesc: function(count) { + return DIV({id: "showing-desc"}, + "Showing "+showingDesc+" ("+count+")."); + }, + isAdmin: pro_accounts.isAdminSignedIn() + }); +} + +function render_main() { + if (!getSession().latestPadlistView) { + getSession().latestPadlistView = "/ep/padlist/all-pads"; + } + response.redirect(getSession().latestPadlistView); +} + +function render_all_pads_get() { + _renderListPage( + pro_pad_db.listAllDomainPads(), + "all pads", + ['secure', 'title', 'lastEditedDate', 'editors', 'actions']); +} + +function render_all_pads_zip_get() { + if (! pro_accounts.isAdminSignedIn()) { + response.redirect(_getBaseUrl()+"all-pads"); + } + var bytes = new java.io.ByteArrayOutputStream(); + var zos = new java.util.zip.ZipOutputStream(bytes); + + var pads = pro_pad_db.listAllDomainPads(); + pads.forEach(function(pad) { + var padHtml; + var title; + padutils.accessPadLocal(pad.localPadId, function(p) { + title = padutils.getProDisplayTitle(pad.localPadId, pad.title); + padHtml = exporthtml.getPadHTML(p); + }, "r"); + + title = title.replace(/[^\w\s]/g, "-") + ".html"; + zos.putNextEntry(new java.util.zip.ZipEntry(title)); + var padBytes = (new java.lang.String(renderTemplateAsString('pad/exporthtml.ejs', { + content: padHtml, + pre: false + }))).getBytes("UTF-8"); + + zos.write(padBytes, 0, padBytes.length); + zos.closeEntry(); + }); + zos.close(); + response.setContentType("application/zip"); + response.writeBytes(bytes.toByteArray()); +} + +function render_my_pads_get() { + _renderListPage( + pro_pad_db.listMyPads(), + "pads created by me", + ['secure', 'title', 'lastEditedDate', 'editors', 'actions']); +} + +function render_archived_pads_get() { + helpers.addClientVars({ + showingArchivedPads: true + }); + _renderListPage( + pro_pad_db.listArchivedPads(), + "archived pads", + ['secure', 'title', 'lastEditedDate', 'actions']); +} + +function render_edited_by_get() { + var editorId = request.params.editorId; + var editorName = pro_accounts.getFullNameById(editorId); + _renderListPage( + pro_pad_db.listPadsByEditor(editorId), + "pads edited by "+editorName, + ['secure', 'title', 'lastEditedDate', 'editors', 'actions']); +} + +function render_delete_post() { + var localPadId = request.params.padIdToDelete; + + pro_padmeta.accessProPadLocal(localPadId, function(propad) { + propad.markDeleted(); + getSession().padlistMessage = 'Pad "'+propad.getDisplayTitle()+'" has been deleted.'; + }); + + response.redirect(request.params.returnPath); +} + +function render_toggle_archive_post() { + var localPadId = request.params.padIdToToggleArchive; + + pro_padmeta.accessProPadLocal(localPadId, function(propad) { + if (propad.isArchived()) { + propad.unmarkArchived(); + getSession().padlistMessage = 'Pad "'+propad.getDisplayTitle()+'" has been un-archived.'; + } else { + propad.markArchived(); + getSession().padlistMessage = 'Pad "'+propad.getDisplayTitle()+'" has been archived. You can view archived pads by clicking on the "Archived" tab at the top of the pad list.'; + } + }); + + response.redirect(request.params.returnPath); +} + + diff --git a/trunk/etherpad/src/etherpad/control/pro_beta_control.js b/trunk/etherpad/src/etherpad/control/pro_beta_control.js new file mode 100644 index 0000000..ec99b43 --- /dev/null +++ b/trunk/etherpad/src/etherpad/control/pro_beta_control.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. + */ + +import("funhtml.*", "stringutils.*"); +import("sqlbase.sqlobj"); +import("sqlbase.sqlcommon"); +import("stringutils"); +import("email.sendEmail"); + +import("etherpad.utils.*"); +import("etherpad.log"); +import("etherpad.sessions.getSession"); + +jimport("java.lang.System.out.println"); + +function render_main_get() { + if (isValveOpen()) { + response.redirect("/ep/pro-signup/"); + } + renderFramed("beta/signup.ejs", { + errorMsg: getSession().betaSignupError + }); + delete getSession().betaSignupError; +} + +function render_signup_post() { + // record in sql: [id, email, activated=false, activationCode] + // log to disk + + var email = request.params.email; + if (!isValidEmail(email)) { + getSession().betaSignupError = "Invalid email address."; + response.redirect('/ep/beta-account/'); + } + + // does email already exist? + if (sqlobj.selectSingle('pro_beta_signups', {email: email})) { + getSession().betaSignupError = "Email already signed up."; + response.redirect('/ep/beta-account/'); + } + + sqlobj.insert('pro_beta_signups', { + email: email, + isActivated: false, + signupDate: new Date() + }); + + response.redirect('/ep/beta-account/signup-ok'); +} + +function render_signup_ok() { + renderNoticeString( + DIV({style: "font-size: 16pt; border: 1px solid green; background: #eeffee; margin: 2em 4em; padding: 1em;"}, + P("Great! We'll be in touch."), + P("In the meantime, you can ", A({href: '/ep/pad/newpad', style: 'text-decoration: underline;'}, + "create a public pad"), " right now."))); +} + +// return string if not valid, falsy otherwise. +function isValidCode(code) { + if (isValveOpen()) { + return undefined; + } + + function wr(m) { + return DIV(P(m), P("You can sign up for the beta ", + A({href: "/ep/beta-account/"}, "here"))); + } + + if (!code) { + return wr("Invalid activation code."); + } + var record = sqlobj.selectSingle('pro_beta_signups', { activationCode: code }); + if (!record) { + return wr("Invalid activation code."); + } + if (record.isActivated) { + return wr("That activation code has already been used."); + } + return undefined; +} + +function isValveOpen() { + if (appjet.cache.proBetaValveIsOpen === undefined) { + appjet.cache.proBetaValveIsOpen = true; + } + return appjet.cache.proBetaValveIsOpen; +} + +function toggleValve() { + appjet.cache.proBetaValveIsOpen = !appjet.cache.proBetaValveIsOpen; +} + +function sendInvite(recordId) { + var record = sqlobj.selectSingle('pro_beta_signups', {id: recordId}); + if (record.activationCode) { + getSession().betaAdminMessage = "Already active"; + return; + } + + // create activation code + var code = stringutils.randomString(10); + sqlcommon.inTransaction(function() { + sqlobj.update('pro_beta_signups', {id: recordId}, {activationCode: code}); + var body = renderTemplateAsString('email/pro_beta_invite.ejs', { + toAddr: record.email, + signupAgo: timeAgo(record.signupDate), + signupCode: code, + activationUrl: "http://"+httpHost(request.host)+"/ep/pro-signup/?sc="+code + }); + sendEmail(record.email, "EtherPad ", + "Your EtherPad Professional Beta Account", {}, body); + }); + + getSession().betaAdminMessage = "Invite sent."; +} + +function notifyActivated(code) { + println("updating: "+code); + sqlobj.update('pro_beta_signups', {activationCode: code}, + {isActivated: true, activationDate: new Date()}); +} + diff --git a/trunk/etherpad/src/etherpad/control/pro_signup_control.js b/trunk/etherpad/src/etherpad/control/pro_signup_control.js new file mode 100644 index 0000000..6bf7cc3 --- /dev/null +++ b/trunk/etherpad/src/etherpad/control/pro_signup_control.js @@ -0,0 +1,173 @@ +/** + * 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. + */ + +import("jsutils.*"); +import("cache_utils.syncedWithCache"); +import("funhtml.*"); +import("stringutils"); +import("stringutils.*"); +import("sqlbase.sqlcommon"); + +import("etherpad.sessions.getSession"); +import("etherpad.utils.*"); + +import("etherpad.pro.pro_accounts"); +import("etherpad.pro.domains"); + +import("etherpad.control.pro_beta_control"); +import("etherpad.control.pro.admin.account_manager_control"); + +import("etherpad.helpers"); + +function onRequest() { + if (!getSession().ods) { + getSession().ods = {}; + } + if (request.method == "POST") { + // add params to cart + eachProperty(request.params, function(k,v) { + getSession().ods[k] = stringutils.toHTML(v); + }); + } +} + +function _errorDiv() { + var m = getSession().errorMessage; + if (m) { + delete getSession().errorMessage; + return DIV({className: 'err'}, m); + } + return ""; +} + +function _input(id, type) { + return INPUT({type: type ? type : 'text', name: id, id: id, + value: getSession().ods[id] || ""}); +} + +function _inf(id, label, type) { + return DIV( + DIV({style: "width: 100px; text-align: right; float: left; padding-top: 3px;"}, label, ": "), + DIV({style: "text-align: left; float: left;"}, + _input(id, type)), + DIV({style: "height: 6px; clear: both;"}, " ")); +} + +function render_main_get() { + // observe activation code + if (request.params.sc) { + getSession().betaActivationCode = request.params.sc; + response.redirect(request.path); + } + + // validate activation code + var activationCode = getSession().betaActivationCode; + var err = pro_beta_control.isValidCode(activationCode); + if (err) { + renderNoticeString(DIV({style: "border: 1px solid red; background: #fdd; font-weight: bold; padding: 1em;"}, + err)); + response.stop(); + } + + // serve activation page + renderFramed('main/pro_signup_body.ejs', { + errorDiv: _errorDiv, + input: _input, + inf: _inf + }); +} + +function _err(m) { + if (m) { + getSession().errorMessage = m; + response.redirect(request.path); + } +} + +function render_main_post() { + var subdomain = trim(String(request.params.subdomain).toLowerCase()); + var fullName = request.params.fullName; + var email = trim(request.params.email); + + // validate activation code + var activationCode = getSession().betaActivationCode; + var err = pro_beta_control.isValidCode(activationCode); + if (err) { + resonse.write(err); + } + + /* + var password = request.params.password; + var passwordConfirm = request.params.passwordConfirm; + */ + var orgName = subdomain; + + //---- basic validation ---- + if (!/^\w[\w\d\-]*$/.test(subdomain)) { + _err("Invalid domain: "+subdomain); + } + if (subdomain.length < 2) { + _err("Subdomain must be at least 2 characters."); + } + if (subdomain.length > 60) { + _err("Subdomain must be <= 60 characters."); + } + +/* + if (password != passwordConfirm) { + _err("Passwords do not match."); + } + */ + + _err(pro_accounts.validateFullName(fullName)); + _err(pro_accounts.validateEmail(email)); + + if (!(email.match(/[Ff][Uu]-[Bb][Ee][Rr][Ll][Ii][Nn].[Dd][Ee]$/))) { _err("Please use your *.fu-berlin.de email address."); } +// _err(pro_accounts.validatePassword(password)); + + //---- database validation ---- + + if (domains.doesSubdomainExist(subdomain)) { + _err("The domain "+subdomain+" is already in use."); + } + + //---- looks good. create records! ---- + + // TODO: log a bunch of stuff, and request IP address, etc. + + var ok = false; + sqlcommon.inTransaction(function() { + var tempPass = stringutils.randomString(10); + // TODO: move validation code into domains.createNewSubdomain... + var domainId = domains.createNewSubdomain(subdomain, orgName); + var accountId = pro_accounts.createNewAccount(domainId, fullName, email, tempPass, true); + // send welcome email + syncedWithCache('pro-activations', function(c) { + c[domainId] = true; + }); + ok = true; + if (activationCode) { + pro_beta_control.notifyActivated(activationCode); + } + }); + + if (ok) { + response.redirect('http://'+subdomain+"."+request.host+'/ep/finish-activation'); + } else { + response.write("There was an error processing your request."); + } +} + diff --git a/trunk/etherpad/src/etherpad/control/scriptcontrol.js b/trunk/etherpad/src/etherpad/control/scriptcontrol.js new file mode 100644 index 0000000..16efc60 --- /dev/null +++ b/trunk/etherpad/src/etherpad/control/scriptcontrol.js @@ -0,0 +1,75 @@ +/** + * 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. + */ + +import("etherpad.pad.dbwriter"); +import("etherpad.utils.*"); +import("etherpad.globals.*"); + +function onRequest() { + if (!isProduction()) { + return; + } + if (request.params.auth != 'f83kg840d12jk') { + response.forbid(); + } +} + +function render_setdbwritable() { + var dbwritable = (String(request.params.value).toLowerCase() != 'false'); // default to true + + dbwriter.setWritableState({constant: dbwritable}); + + response.write("OK, set to "+dbwritable); +} + +function render_getdbwritable() { + var state = dbwriter.getWritableState(); + + response.write(String(dbwriter.getWritableStateDescription(state))); +} + +function render_pausedbwriter() { + var seconds = request.params.seconds; + var seconds = Number(seconds || 0); + if (isNaN(seconds)) seconds = 0; + + var finishTime = (+new Date())+(1000*seconds); + dbwriter.setWritableState({trueAfter: finishTime}); + + response.write("Paused dbwriter for "+seconds+" seconds."); +} + +function render_fake_pne_on() { + if (isProduction()) { + response.write("has no effect in production."); + } else { + appjet.cache.fakePNE = true; + response.write("OK"); + } +} + +function render_fake_pne_off() { + if (isProduction()) { + response.write("has no effect in production."); + } else { + appjet.cache.fakePNE = false; + response.write("OK"); + } +} + + + + diff --git a/trunk/etherpad/src/etherpad/control/static_control.js b/trunk/etherpad/src/etherpad/control/static_control.js new file mode 100644 index 0000000..5c087b6 --- /dev/null +++ b/trunk/etherpad/src/etherpad/control/static_control.js @@ -0,0 +1,65 @@ +/** + * 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. + */ + +import("faststatic"); +import("dispatch.{Dispatcher,PrefixMatcher,forward}"); + +import("etherpad.utils.*"); +import("etherpad.globals.*"); + +function onRequest() { + var staticBase = '/static'; + + var opts = {cache: isProduction()}; + + var serveFavicon = faststatic.singleFileServer(staticBase + '/favicon.ico', opts); + var serveCrossDomain = faststatic.singleFileServer(staticBase + '/crossdomain.xml', opts); + var serveStaticDir = faststatic.directoryServer(staticBase, opts); + var serveCompressed = faststatic.compressedFileServer(opts); + var serveJs = faststatic.directoryServer(staticBase+'/js/', opts); + var serveCss = faststatic.directoryServer(staticBase+'/css/', opts); + var serveSwf = faststatic.directoryServer(staticBase+'/swf/', opts); + var serveHtml = faststatic.directoryServer(staticBase+'/html/', opts); + var serveZip = faststatic.directoryServer(staticBase+'/zip/', opts); + + var disp = new Dispatcher(); + + disp.addLocations([ + ['/favicon.ico', serveFavicon], + ['/robots.txt', serveRobotsTxt], + ['/crossdomain.xml', serveCrossDomain], + [PrefixMatcher('/static/html/'), serveHtml], + [PrefixMatcher('/static/js/'), serveJs], + [PrefixMatcher('/static/css/'), serveCss], + [PrefixMatcher('/static/swf/'), serveSwf], + [PrefixMatcher('/static/zip/'), serveZip], + [PrefixMatcher('/static/compressed/'), serveCompressed], + [PrefixMatcher('/static/'), serveStaticDir] + ]); + + return disp.dispatch(); +} + +function serveRobotsTxt(name) { + response.neverCache(); + response.setContentType('text/plain'); + response.write('User-agent: *\n'); + if (!isProduction()) { + response.write('Disallow: /\n'); + } + response.stop(); + return true; +} diff --git a/trunk/etherpad/src/etherpad/control/statscontrol.js b/trunk/etherpad/src/etherpad/control/statscontrol.js new file mode 100644 index 0000000..3659107 --- /dev/null +++ b/trunk/etherpad/src/etherpad/control/statscontrol.js @@ -0,0 +1,1214 @@ +/** + * 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. + */ + +import("fastJSON"); +import("netutils"); +import("funhtml.*"); +import("stringutils.{html,sprintf,startsWith,md5}"); +import("jsutils.*"); +import("sqlbase.sqlbase"); +import("sqlbase.sqlcommon"); +import("sqlbase.sqlobj"); +import("dispatch.{Dispatcher,PrefixMatcher,DirMatcher,forward}"); + +import("etherpad.globals.*"); +import("etherpad.utils.*"); +import("etherpad.sessions.getSession"); +import("etherpad.sessions"); +import("etherpad.statistics.statistics"); +import("etherpad.log"); +import("etherpad.usage_stats.usage_stats"); +import("etherpad.helpers"); + +//---------------------------------------------------------------- +// Usagestats +//---------------------------------------------------------------- + +var _defaultPrefs = { + topNCount: 5, + granularity: 1440 +} + +function onRequest() { + keys(_defaultPrefs).forEach(function(prefName) { + if (request.params[prefName]) { + _prefs()[prefName] = request.params[prefName]; + } + }); + if (request.isPost) { + response.redirect( + request.path+ + (request.query ? "?"+request.query : "")+ + (request.params.fragment ? "#"+request.params.fragment : "")); + } +} + +function _prefs() { + if (! sessions.getSession().statsPrefs) { + sessions.getSession().statsPrefs = {} + } + return sessions.getSession().statsPrefs; +} + +function _pref(pname) { + return _prefs()[pname] || _defaultPrefs[pname]; +} + +function _topN() { + return _pref('topNCount'); +} +function _showLiveStats() { + return _timescale() < 1440; + // return _pref('granularity') == 'live'; +} +function _showHistStats() { + return _timescale() >= 1440 + // return _pref('showLiveOrHistorical') == 'hist'; +} +function _timescale() { + return Number(_pref('granularity')) || 1; +} + +// types: +// compare - compare one or more single-value stats +// top - show top values over time +// histogram - show histogram over time + +var statDisplays = { + users: [ + { name: "visitors", + description: "User visits, total over a %t period", + type: "compare", + stats: [ {stat: "site_pageviews", + description: "Page views", + color: "FFA928" }, + {stat: "site_unique_ips", + description: "Unique IPs", + color: "00FF00" } ] }, + + // free pad usage + { name: "free pad usage, 1 day", + description: "Free pad.spline.inf.fu-berlin.de users, total over a %t period", + type: "compare", + stats: [ {stat: "active_user_ids", + description: "All users", + color: "FFA928" }, + {stat: "users_1day_returning_7days", + description: "Users returning after 7 days", + color: "00FF00"}, + {stat: "users_1day_returning_30days", + description: "Users returning after 30 days", + color: "FF0000"} ] }, + { name: "free pad usage, 7 day", + description: "Free pad.spline.inf.fu-berlin.de users over the last 7 days", + type: "compare", + options: { hideLive: true, latestUseHistorical: true}, + stats: [ {stat: "active_user_ids_7days", + description: "All users", + color: "FFA928" }, + {stat: "users_7day_returning_7days", + description: "Users returning after 7 days", + color: "00FF00"}, + {stat: "users_7day_returning_30days", + description: "Users returning after 30 days", + color: "FF0000"} ] }, + { name: "free pad usage, 30 day", + description: "Free pad.spline.inf.fu-berlin.de users over the last 30 days", + type: "compare", + options: { hideLive: true, latestUseHistorical: true}, + stats: [ {stat: "active_user_ids_30days", + description: "All users", + color: "FFA928" }, + {stat: "users_30day_returning_7days", + description: "Users returning after 7 days", + color: "00FF00"}, + {stat: "users_30day_returning_30days", + description: "Users returning after 30 days", + color: "FF0000"} ] }, + + // pro pad usage + { name: "active pro accounts, 1 day", + description: "Active pro accounts, total over a %t period", + type: "compare", + stats: [ {stat: "active_pro_accounts", + description: "All accounts", + color: "FFA928" }, + {stat: "pro_accounts_1day_returning_7days", + description: "Accounts older than 7 days", + color: "00FF00"}, + {stat: "pro_accounts_1day_returning_30days", + description: "Accounts older than 30 days", + color: "FF0000"} ] }, + { name: "active pro accounts, 7 day", + description: "Active pro accounts over the last 7 days", + type: "compare", + options: { hideLive: true, latestUseHistorical: true}, + stats: [ {stat: "active_pro_accounts_7days", + description: "All accounts", + color: "FFA928" }, + {stat: "pro_accounts_7day_returning_7days", + description: "Accounts older than 7 days", + color: "00FF00"}, + {stat: "pro_accounts_7day_returning_30days", + description: "Accounts older than 30 days", + color: "FF0000"} ] }, + { name: "active pro accounts, 30 day", + description: "Active pro accounts over the last 30 days", + type: "compare", + options: { hideLive: true, latestUseHistorical: true}, + stats: [ {stat: "active_pro_accounts_30days", + description: "All accounts", + color: "FFA928" }, + {stat: "pro_accounts_30day_returning_7days", + description: "Accounts older than 7 days", + color: "00FF00"}, + {stat: "pro_accounts_30day_returning_30days", + description: "Accounts older than 30 days", + color: "FF0000"} ] }, + + // other stats + { name: "pad connections", + description: "Number of active comet connections, mean over a %t period", + type: "top", + options: {showOthers: false}, + stats: ["streaming_connections"] }, + { name: "referers", + description: "Referers, number of hits over a %t period", + type: "top", + options: {showOthers: false}, + stats: ["top_referers"] }, + ], + product: [ + { name: "pads", + description: "Newly-created and active pads, total over a %t period", + type: "compare", + stats: [ {stat: "active_pads", + description: "Active pads", + color: "FFA928" }, + {stat: "new_pads", + description: "New pads", + color: "FF0000" }] }, + { name: "chats", + description: "Chat messages and active chatters, total over a %t period", + type: "compare", + stats: [ {stat: "chat_messages", + description: "Messages", + color: "FFA928" }, + {stat: "active_chatters", + description: "Chatters", + color: "FF0000" }] }, + { name: "import/export", + description: "Imports and Exports, total over a %t period", + type: "compare", + stats: [ {stat: {f: '+', args: ["imports_exports_counts:export", "imports_exports_counts:import"]}, + description: "Total", + color: "FFA928" }, + {stat: "imports_exports_counts:export", + description: "Exports", + color: "FF0000"}, + {stat: "imports_exports_counts:import", + description: "Imports", + color: "00FF00"}] }, + { name: "revenue", + description: "Revenue, total over a %t period", + type: "compare", + stats: [ {stat: "revenue", + description: "Revenue", + color: "FFA928"}] } + ], + performance: [ + { name: "dynamic page latencies", + description: "Slowest dynamic pages: mean load time in milliseconds over a %t period", + type: "top", + options: {showOthers: false}, + stats: ["execution_latencies"] }, + { name: "pad startup latencies", + description: "Pad startup times: percent load time in milliseconds over a %t period", + type: "histogram", + stats: ["pad_startup_times"] }, + { name: "stream post latencies", + description: "Comet post latencies, percentiles in milliseconds over a %t period", + type: "histogram", + stats: ["streaming_latencies"] }, + ], + health: [ + { name: "disconnect causes", + description: "Causes of disconnects, total over a %t period", + type: "top", + stats: ["disconnect_causes"] }, + { name: "paths with 404s", + description: "'Not found' responses, by path, number served over a %t period", + type: "top", + stats: ["paths_404"] }, + { name: "exceptions", + description: "Total number of server exceptions over a %t period", + type: "compare", + stats: [ {stat: "exceptions", + description: "Exceptions", + color: "FF1928" } ] }, + { name: "paths with 500s", + type: "top", + description: "'500' responses, by path, number served over a %t period", + type: "top", + stats: ["paths_500"] }, + { name: "paths with exceptions", + description: "responses with exceptions, by path, number served over a %t period", + type: "top", + stats: ["paths_exception"] }, + { name: "disconnects with client-side errors", + description: "user disconnects with an error on the client side, number over a %t period", + type: "compare", + stats: [ { stat: "disconnects_with_clientside_errors", + description: "Disconnects with errors", + color: "FFA928" } ] }, + { name: "unnecessary disconnects", + description: "disconnects that were avoidable, number over a %t period", + type: "compare", + stats: [ { stat: "streaming_disconnects:disconnected_userids", + description: "Number of unique users disconnected", + color: "FFA928" }, + { stat: "streaming_disconnects:total_disconnects", + description: "Total number of disconnects", + color: "FF0000" } ] }, + ] +} + +function getUsedStats(statStructure) { + var stats = {}; + function getStructureValues(statStructure) { + if (typeof(statStructure) == 'string') { + stats[statStructure] = true; + } else { + statStructure.args.forEach(getStructureValues); + } + } + getStructureValues(statStructure); + return keys(stats); +} + +function getStatData(statStructure, values_f) { + function getStructureValues(statStructure) { + if (typeof(statStructure) == 'string') { + return values_f(statStructure); + } else if (typeof(statStructure) == 'number') { + return statStructure; + } else { + var args = statStructure.args.map(getStructureValues); + return { + f: statStructure.f, + args: args + } + } + } + + var mappedStructure = getStructureValues(statStructure); + + function evalStructure(statStructure) { + if ((typeof(statStructure) == 'number') || (statStructure instanceof Array)) { + return statStructure; + } else { + var merge_f = statStructure.f; + if (typeof(merge_f) == 'string') { + switch (merge_f) { + case '+': + merge_f = function() { + var sum = 0; + for (var i = 0; i < arguments.length; ++i) { + sum += arguments[i]; + } + return sum; + } + break; + case '*': + merge_f = function() { + var product = 0; + for (var i = 0; i < arguments.length; ++i) { + product *= arguments[i]; + } + return product; + } + break; + case '/': + merge_f = function(a, b) { return a / b; } + break; + case '-': + merge_f = function(a, b) { return a - b; } + break; + } + } + var evaluatedArguments = statStructure.args.map(evalStructure); + var length = -1; + evaluatedArguments.forEach(function(arg) { + if (typeof(arg) == 'object' && (arg instanceof Array)) { + length = arg.length; + } + }); + evaluatedArguments = evaluatedArguments.map(function(arg) { + if (typeof(arg) == 'number') { + var newArg = new Array(length); + for (var i = 0; i < newArg.length; ++i) { + newArg[i] = arg; + } + return newArg + } else { + return arg; + } + }); + return mergeArrays.apply(this, [merge_f].concat(evaluatedArguments)); + } + } + return evalStructure(mappedStructure); +} + +var googleChartSimpleEncoding = "ABCDEFGHIJLKMNOPQRSTUVQXYZabcdefghijklmnopqrstuvwxyz0123456789-."; +function _enc(value) { + return googleChartSimpleEncoding[Math.floor(value/64)] + googleChartSimpleEncoding[value%64]; +} + +function drawSparkline(dataSets, labels, colors, minutes) { + var max = 1; + var maxLength = 0; + dataSets.forEach(function(dataSet, i) { + if (dataSet.length > maxLength) { + maxLength = dataSet.length; + } + dataSet.forEach(function(point) { + if (point > max) { + max = point; + } + }); + }); + var data = dataSets.map(function(dataSet) { + var chars = dataSet.map(function(x) { + if (x !== undefined) { + return _enc(Math.round(x/max*4095)); + } else { + return "__"; + } + }).join(""); + while (chars.length < maxLength*2) { + chars = "__"+chars; + } + return chars; + }).join(","); + var timeLabels; + if (minutes < 60*24) { + timeLabels = [4,3,2,1,0].map(function(t) { + var minutesPerTick = minutes/4; + var d = new Date(Date.now() - minutesPerTick*60000*t); + return (d.getHours()%12 || 12)+":"+(d.getMinutes() < 10 ? "0" : "")+d.getMinutes()+(d.getHours() < 12 ? "am":"pm"); + }).join("|"); + } else { + timeLabels = [4,3,2,1,0].map(function(t) { + var daysPerTick = (minutes/(60*24))/4; + var d = new Date(Date.now() - t*daysPerTick*24*60*60*1000); + return (d.getMonth()+1)+"/"+d.getDate(); + }).join("|"); + } + var pointLabels = dataSets.map(function(dataSet, i) { + return ["t"+dataSet[dataSet.length-1],colors[i],i,maxLength-1,12,0].join(","); + }).join("|"); + labels = labels.map(function(label) { + return encodeURIComponent((label.length > 73) ? label.slice(0, 70) + "..." : label); + }); + var step = Math.round(max/10); + step = Math.round(step/Math.pow(10, String(step).length-1))*Math.pow(10, String(step).length-1); + var srcUrl = + "http://chart.apis.google.com/chart?chs=600x300&cht=lc&chd=e:"+data+ + "&chxt=y,x&chco="+colors.join(",")+"&chxr=0,0,"+max+","+step+"&chxl=1:|"+timeLabels+ + "&chdl="+labels.join("|")+"&chdlp=b&chm="+pointLabels; + return toHTML(IMG({src: srcUrl})); +} + +var liveDataNumSamples = 20; + +function extractStatValuesFunction(nameToValues_f) { + return function(statName) { + var value; + if (statName.indexOf(":") >= 0) { + [statName, value] = statName.split(":"); + } + var h = nameToValues_f(statName); + if (value) { + h = h.map(function(topValues) { + if (! topValues) { return; } + var tv = topValues.topValues; + for (var i = 0; i < tv.length; ++i) { + if (tv[i].value == value) { + return tv[i].count; + } + } + return 0; + }); + } + return h; + } +} + +function sparkline_compare(history_f, minutesPerSample, stat) { + var histories = stat.stats.map(function(stat) { + var samples = getStatData(stat.stat, extractStatValuesFunction(history_f)); + return [samples, stat.description, stat.color]; + }); + return drawSparkline(histories.map(function(history) { return history[0] }), + histories.map(function(history) { return history[1] }), + histories.map(function(history) { return history[2] }), + minutesPerSample*histories[0][0].length); +} + +function sparkline_top(history_f, minutesPerSample, stat) { + var showOthers = ! stat.options || stat.options.showOthers != false; + var history = stat.stats.map(history_f)[0]; + + if (history.length == 0) { + return "no data"; + } + var topRecents = {}; + var topRecents_arr = []; + history.forEach(function(tv) { + if (! tv) { return; } + if (tv.topValues.length > 0) { + topRecents_arr = tv.topValues.map(function(x) { return x.value; }); + } + }); + + if (topRecents_arr.length == 0) { + return "no data"; + } + topRecents_arr = topRecents_arr.slice(0, _topN()); + topRecents_arr.forEach(function(value, i) { + topRecents[value] = i; + }); + + if (showOthers) { + topRecents_arr.push("Other"); + } + var max = 1; + var values = topRecents_arr.map(function() { return history.map(function() { return 0 }); }); + + history.forEach(function(tv, i) { + if (! tv) { return; } + tv.topValues.forEach(function(entry) { + if (entry.count > max) { + max = entry.count; + } + if (entry.value in topRecents) { + values[topRecents[entry.value]][i] = entry.count; + } else if (showOthers) { + values[values.length-1][i] += entry.count; + } + }); + }); + return drawSparkline( + values, + topRecents_arr, + ["FF0000", "00FF00", "0000FF", "FF00FF", "00FFFF"].slice(0, topRecents_arr.length-1).concat("FFA928"), + minutesPerSample*history.length); +} + +function sparkline_histogram(history_f, minutesPerSample, stat) { + var history = stat.stats.map(history_f)[0]; + + if (history.length == 0) { + return "no data"; + } + var percentiles = [50, 90, 95, 99]; + var data = percentiles.map(function() { return []; }) + history.forEach(function(hist) { + percentiles.forEach(function(pct, i) { + data[i].push((hist ? hist[""+pct] : undefined)); + }); + }); + return drawSparkline( + data, + percentiles.map(function(pct) { return ""+pct+"%"; }), + ["FF0000","FF00FF","FFA928","00FF00"].reverse(), + minutesPerSample*history.length); +} + +function liveHistoryFunction(minutesPerSample) { + return function(statName) { + return statistics.liveSnapshot(statName).history(minutesPerSample, liveDataNumSamples); + } +} + +function _listStats(statName, count) { + var options = { orderBy: '-timestamp,id' }; + if (count !== undefined) { + options.limit = count; + } + return sqlobj.selectMulti('statistics', {name: statName}, options); +} + +function ancientHistoryFunction(time) { + return function(statName) { + var seenDates = {}; + var samples = _listStats(statName); + + samples = samples.reverse().map(function(json) { + if (seenDates[""+json.timestamp]) { return; } + seenDates[""+json.timestamp] = true; + return {timestamp: json.timestamp, json: json.value}; + }).filter(function(x) { return x !== undefined }); + + samples = samples.reverse().slice(0, Math.round(time/(24*60))); + var samplesWithEmptyValues = []; + for (var i = 0; i < samples.length-1; ++i) { + var current = samples[i]; + var next = samples[i+1]; + samplesWithEmptyValues.push(current.json); + for (var j = current.timestamp+86400*1000; j < next.timestamp; j += 86400*1000) { + samplesWithEmptyValues.push(undefined); + } + } + if (samples.length > 0) { + samplesWithEmptyValues.push(samples[samples.length-1].json); + } + samplesWithEmptyValues = samplesWithEmptyValues.map(function(json) { + if (! json) { return; } + var obj = fastJSON.parse(json); + if (keys(obj).length == 1 && 'value' in obj) { + obj = obj.value; + } + return obj; + }); + + return samplesWithEmptyValues.reverse(); + } +} + +function sparkline(history_f, minutesPerSample, stat) { + if (this["sparkline_"+stat.type]) { + return this["sparkline_"+stat.type](history_f, minutesPerSample, stat); + } else { + return "No sparkline handler!"; + } +} + +function liveLatestFunction(minutesPerSample) { + return function(statName) { + return [statistics.liveSnapshot(statName).latest(minutesPerSample)]; + } +} + +function liveTotal(statName) { + return [statistics.liveSnapshot(statName).total]; +} + +function historyLatest(statName) { + return _listStats(statName, 1).map(function(x) { + var value = fastJSON.parse(x.value); + if (keys(value).length == 1 && 'value' in value) { + value = value.value; + } + return value; + }); +} + +function latest_compare(latest_f, stat) { + return stat.stats.map(function(stat) { + var sample = getStatData(stat.stat, extractStatValuesFunction(latest_f))[0]; + return { value: sample, description: stat.description }; + }); +} + +function latest_top(latest_f, stat) { + var showOthers = ! stat.options || stat.options.showOthers != false; + + var sample = stat.stats.map(latest_f)[0][0]; + if (! sample) { + return []; + } + var total = sample.count; + + var values = sample.topValues.slice(0, _topN()).map(function(v) { + total -= v.count; + return { value: v.count, description: v.value }; + }); + if (showOthers) { + values.push({value: total, description: "Other"}); + } + return values; +} + +function latest_histogram(latest_f, stat) { + var sample = stat.stats.map(latest_f)[0][0]; + + if (! sample) { + return "no data"; + } + + var percentiles = [0, 1, 5, 10, 25, 50, 75, 90, 95, 99, 100].filter(function(pct) { return ((""+pct) in sample) }); + + var xpos = percentiles.map(function(x, i) { return sample[x] }); + var xMax = 0; + var xMin = 1e12; + xpos.forEach(function(x) { xMax = (x > xMax ? x : xMax); xMin = (x < xMin ? x : xMin); }); + xposNormalized = xpos.map(function(x) { return Math.round((x-xMin)/(xMax-xMin || 1)*100); }); + + var ypos = percentiles.slice(1).map(function(y, i) { return (y-percentiles[i])/(xpos[i+1] || 1); }); + var yMax = 0; + ypos.forEach(function(y) { yMax = (y > yMax ? y : yMax); }); + yposNormalized = ypos.map(function(y) { return Math.round(y/yMax*100); }); + + // var proposedLabels = mergeArrays(function(x, y) { return {pos: x, label: y}; }, xposNormalized, xpos); + // var keepLabels = [{pos: 0, label: 0}]; + // proposedLabels.forEach(function(label) { + // if (label.pos - keepLabels[keepLabels.length-1].pos > 10) { + // keepLabels.push(label); + // } + // }); + // + // var labelPos = keepLabels.map(function(x) { return x.pos }); + // var labels = keepLabels.map(function(x) { return x.label }); + + return toHTML(IMG({src: + "http://chart.apis.google.com/chart?chs=340x100&cht=lxy&chd=t:"+xposNormalized.join(",")+"|0,"+yposNormalized.join(",")+ + "&chxt=x&chxr=0,"+xMin+","+xMax+","+Math.floor((xMax-xMin)/5) // "l=0:|"+labels.join("|")+"&chxp=0,"+labelPos.join(",") + })); +} + +function latest(latest_f, stat) { + if (this["latest_"+stat.type]) { + return this["latest_"+stat.type](latest_f, stat); + } else { + return "No latest handler!"; + } +} + +function dropdown(name, options, selected) { + var select; + if (typeof(name) == 'string') { + select = SELECT({name: name}); + } else { + select = SELECT(name); + } + + function addOption(value, content) { + var opt = OPTION({value: value}, content || value); + if (value == selected) { + opt.attribs.selected = "selected"; + } + select.push(opt); + } + + if (options instanceof Array) { + options.forEach(f_limitArgs(this, addOption, 1)); + } else { + eachProperty(options, addOption); + } + return select; +} + +function render_main() { + var categoriesToStats = {}; + + eachProperty(statDisplays, function(catName, statArray) { + categoriesToStats[catName] = statArray.map(_renderableStat); + }); + + renderHtml('statistics/stat_page.ejs', + {eachProperty: eachProperty, + statCategoryNames: keys(categoriesToStats), + categoriesToStats: categoriesToStats, + optionsForm: _optionsForm() }); +} + +function _optionsForm() { + return FORM({id: "statprefs", method: "POST"}, "Show data with granularity: ", + // dropdown({name: 'showLiveOrHistorical', onchange: 'formChanged();'}, + // {live: 'live', hist: 'historical'}, + // _pref('showLiveOrHistorical')), + // (_showLiveStats() ? + // SPAN(" with granularity ", + dropdown({name: 'granularity', onchange: 'formChanged();'}, + {"1": '1 minute', "5": '5 minutes', "60": '1 hour', "1440": '1 day'}, + _pref('granularity')), // ), + // : ""), + " top N:", + INPUT({type: "text", name: "topNCount", value: _topN()}), + INPUT({type: "submit", name: "Set", value: "set N"}), + INPUT({type: "hidden", name: "fragment", id: "fragment", value: "health"})); +} + +// function render_main() { +// var body = BODY(); +// +// var cat = request.params.cat; +// if (!cat) { +// cat = 'health'; +// } +// +// body.push(A({id: "backtoadmin", href: "/ep/admin/"}, html("«"), " back to admin")); +// body.push(_renderTopnav(cat)); +// +// body.push(form); +// +// if (request.params.stat) { +// body.push(A({className: "viewall", +// href: qpath({stat: null})}, html("«"), " view all")); +// } +// +// var statNames = statDisplays[cat]; +// statNames.forEach(function(sn) { +// if (!request.params.stat || (request.params.stat == sn)) { +// body.push(_renderableStat(sn)); +// } +// }); +// +// helpers.includeCss('admin/admin-stats.css'); +// response.write(HTML(HEAD(html(helpers.cssIncludes())), body)); +// } + +function _getLatest(stat) { + var minutesPerSample = _timescale(); + + if (_showLiveStats()) { + return latest(liveLatestFunction(minutesPerSample), stat); + } else { + return latest(liveTotal, stat); + } +} + +function _getGraph(stat) { + var minutesPerSample = _timescale(); + + if (_showLiveStats()) { + return html(sparkline(liveHistoryFunction(minutesPerSample), minutesPerSample, stat)); + } else { + return html(sparkline(ancientHistoryFunction(60*24*60), 24*60, stat)); + } +} + +function _getDataLinks(stat) { + if (_showLiveStats()) { + return; + } + + function listToLinks(list) { + var links = []; //SPAN({className: "datalink"}, "(data for "); + list.forEach(function(statName) { + links.push(toHTML(A({href: "/ep/admin/usagestats/data?statName="+statName}, statName))); + }); +// links.push(")"); + return links; + } + + switch (stat.type) { + case 'compare': + var stats = []; + stat.stats.map(function(stat) { return getUsedStats(stat.stat); }).forEach(function(list) { + stats = stats.concat(list); + }); + return listToLinks(stats); + case 'top': + return listToLinks(stat.stats); + case 'histogram': + return listToLinks(stat.stats); + } +} + +function _renderableStat(stat) { + var minutesPerSample = _timescale(); + + var period = (_showLiveStats() ? minutesPerSample : 24*60); + + if (period < 24*60 && stat.hideLive) { + return; + } + + if (period < 60) { + period = ""+period+"-minute"; + } else if (period < 24*60) { + period = ""+period/(60)+"-hour"; + } else if (period >= 24*60) { + period = ""+period/(24*60)+"-day"; + } + var graph = _getGraph(stat); + var id = stat.name.replace(/[^a-zA-Z0-9]/g, ""); + + var displayName = stat.description.replace("%t", period); + var latest = _getLatest(stat); + var dataLinks = _getDataLinks(stat); + + return { + id: id, + specialState: "", + displayName: displayName, + name: stat.name, + graph: graph, + latest: latest, + dataLinks: dataLinks + } +} + +function render_data() { + var sn = request.params.statName; + var t = TABLE({border: 1, cellpadding: 2, style: "font-family: monospace;"}); + _listStats(sn).forEach(function(s) { + var tr = TR(); + tr.push(TD((s.id))); + tr.push(TD((new Date(s.timestamp * 1000)).toString())); + tr.push(TD(s.value)); + t.push(tr); + }); + response.write(HTML(BODY(t))); +} + + +// function renderStat(body, statName) { +// var div = DIV({className: 'statbox'}); +// div.push(A({className: "stat-title", href: qpath({stat: statName})}, +// statName, descriptions[statName] || "")); +// if (_showHistStats()) { +// div.push( +// DIV({className: "stat-graph"}, +// A({href: '/ep/admin/usagestats/graph?size=1080x420&statName='+statName}, +// IMG({src: '/ep/admin/usagestats/graph?size=400x200&statName='+statName, +// style: 'border: 1px solid #ccc; margin: 10px 0 0 20px;'})), +// BR(), +// DIV({style: 'text-align: right;'}, +// A({style: 'text-decoration: none; font-size: .8em;', +// href: '/ep/admin/usagestats/data?statName='+statName}, "(data)"))) +// ); +// } +// if (_showLiveStats()) { +// var data = statistics.getStatData(statName); +// var displayData = statistics.liveSnapshot(data); +// var t = TABLE({border: 0}); +// var tcount = 0; +// ["minute", "hour", "fourHour", "day"].forEach(function(timescale) { +// if (! _showTimescale(timescale)) { return; } +// var tr = TR(); +// t.push(tr); +// tr.push(TD({valign: "top"}, B("Last ", timescale))); +// var td = TD(); +// var cell = SPAN(); +// tr.push(td); +// td.push(cell); +// switch (data.plotType) { +// case 'line': +// cell.push(B(displayData[timescale])); break; +// case 'topValues': +// var top = displayData[timescale].topValues; +// if (tcount != 0) { +// tr[0].attribs.style = cell.attribs.style = "border-top: 2px solid black;"; +// } +// // println(statName+" / top length: "+top.length); +// for (var i = 0; i < Math.min(_topN(), top.length); ++i) { +// cell.push(B(top[i].count), ": ", top[i].value, BR()); +// } +// break; +// case 'histogram': +// var percentiles = displayData[timescale]; +// var pcts = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100]; +// var max = percentiles["100"] || 1000; +// cell.push(IMG({src: "http://chart.apis.google.com/chart?chs=340x100&cht=bvs&chd=t:"+ +// pcts.map(function(pct) { return Math.round(percentiles[""+pct]/max*100); }).join(",")+ +// "&chxt=x,y&chxl=0:|"+ +// pcts.map(function(pct) { return ""+pct+"%"; }).join("|")+ +// "&chxr=0,0,100|1,0,"+max+""})) +// // td.push("50%: ", B(percentiles["50"]), " ", +// // "90%: ", B(percentiles["90"]), " ", +// // "max: ", B(percentiles["100"])); +// break; +// } +// tcount++; +// }); +// div.push(DIV({className: "stat-table"}, t)); +// div.push(html(helpers.clearFloats())); +// } +// body.push(div); +// } +// ======= +// >>>>>>> Stashed changes:etherpad/src/etherpad/control/statscontrol.js + + +// old output. + +// +// function getStatsForCategory(category) { +// var statnames = statistics.getAllStatNames(); +// +// var matchingStatNames = []; +// statnames.forEach(function(sn) { +// if (statistics.getStatData(sn).category == category) { +// matchingStatNames.push(sn); +// } +// }); +// +// return matchingStatNames; +// } +// +// function renderCategoryList() { +// var body = BODY(); +// +// catNames = getCategoryNames(); +// body.push(P("Please select a statistics category:")); +// catNames.sort().forEach(function(catname) { +// body.push(P(A({href: "/ep/admin/usagestats/?cat="+catname}, catname))); +// }); +// response.write(body); +// } +// +// function getCategoryNames() { +// var statnames = statistics.getAllStatNames(); +// var catNames = {}; +// statnames.forEach(function(sn) { +// catNames[statistics.getStatData(sn).category] = true; +// }); +// return keys(catNames); +// } +// +// function dropdown(name, options, selected) { +// var select; +// if (typeof(name) == 'string') { +// select = SELECT({name: name}); +// } else { +// select = SELECT(name); +// } +// +// function addOption(value, content) { +// var opt = OPTION({value: value}, content || value); +// if (value == selected) { +// opt.attribs.selected = "selected"; +// } +// select.push(opt); +// } +// +// if (options instanceof Array) { +// options.forEach(f_limitArgs(this, addOption, 1)); +// } else { +// eachProperty(options, addOption); +// } +// return select; +// } +// +// function getCategorizedStats() { +// var statnames = statistics.getAllStatNames(); +// var categories = {} +// statnames.forEach(function(sn) { +// var category = statistics.getStatData(sn).category +// if (! categories[category]) { +// categories[category] = []; +// } +// categories[category].push(statistics.getStatData(sn)); +// }); +// return categories; +// } +// +// function render_ajax() { +// var categoriesToStats = getCategorizedStats(); +// +// eachProperty(categoriesToStats, function(catName, statArray) { +// categoriesToStats[catName] = statArray.map(function(statObject) { +// return { +// specialState: "", +// displayName: statObject.name, +// name: statObject.name, +// data: liveStatDisplayHtml(statObject) +// } +// }) +// }); +// +// renderHtml('statistics/stat_page.ejs', +// {eachProperty: eachProperty, +// statCategoryNames: keys(categoriesToStats), +// categoriesToStats: categoriesToStats }); +// } + +// function render_main() { +// var body = BODY(); +// +// var statNames = statistics.getAllStatNames(); //getStatsForCategory(request.params.cat); +// statNames.forEach(function(sn) { +// renderStat(body, sn); +// }); +// response.write(body); +// } +// +// var descriptions = { +// execution_latencies: ", mean response time in milliseconds", +// static_file_latencies: ", mean response time in milliseconds", +// pad_startup_times: ", max response time in milliseconds of fastest N% of requests" +// }; +// +// function liveStatDisplayHtml(statObject) { +// var displayData = statistics.liveSnapshot(statObject); +// switch (statObject.plotType) { +// case 'line': +// return displayData; +// case 'topValues': +// var data = {} +// eachProperty(displayData, function(timescale, tsdata) { +// data[timescale] = "" +// var top = tsdata.topValues; +// for (var i = 0; i < Math.min(_topN(), top.length); ++i) { +// data[timescale] += [B(top[i].count), ": ", top[i].value, BR()].map(toHTML).join(""); +// } +// if (data[timescale] == "") { +// data[timescale] = "(no data)"; +// } +// }); +// return data; +// case 'histogram': +// var imgs = {} +// eachProperty(displayData, function(timescale, tsdata) { +// var percentiles = tsdata; +// var pcts = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100]; +// var max = percentiles["100"] || 1000; +// imgs[timescale] = +// toHTML(IMG({src: "http://chart.apis.google.com/chart?chs=400x100&cht=bvs&chd=t:"+ +// pcts.map(function(pct) { return Math.round(percentiles[""+pct]/max*100); }).join(",")+ +// "&chxt=x,y&chxl=0:|"+ +// pcts.map(function(pct) { return ""+pct+"%"; }).join("|")+ +// "&chxr=0,0,100|1,0,"+max+""})); +// }); +// return imgs; +// } +// } +// +// function renderStat(body, statName) { +// var div = DIV({style: 'float: left; text-align: center; margin: 3px; border: 1px solid black;'}) +// div.push(P(statName, descriptions[statName] || "")); +// if (_showLiveStats()) { +// var data = statistics.getStatData(statName); +// var displayData = statistics.liveSnapshot(data); +// var t = TABLE(); +// var tcount = 0; +// ["minute", "hour", "fourHour", "day"].forEach(function(timescale) { +// if (! _showTimescale(timescale)) { return; } +// var tr = TR(); +// t.push(tr); +// tr.push(TD("last ", timescale)); +// var td = TD(); +// tr.push(td); +// switch (data.plotType) { +// case 'line': +// td.push(B(displayData[timescale])); break; +// case 'topValues': +// var top = displayData[timescale].topValues; +// if (tcount != 0) { +// tr[0].attribs.style = td.attribs.style = "border-top: 1px solid gray;"; +// } +// // println(statName+" / top length: "+top.length); +// for (var i = 0; i < Math.min(_topN(), top.length); ++i) { +// td.push(B(top[i].count), ": ", top[i].value, BR()); +// } +// break; +// case 'histogram': +// var percentiles = displayData[timescale]; +// var pcts = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100]; +// var max = percentiles["100"] || 1000; +// td.push(IMG({src: "http://chart.apis.google.com/chart?chs=340x100&cht=bvs&chd=t:"+ +// pcts.map(function(pct) { return Math.round(percentiles[""+pct]/max*100); }).join(",")+ +// "&chxt=x,y&chxl=0:|"+ +// pcts.map(function(pct) { return ""+pct+"%"; }).join("|")+ +// "&chxr=0,0,100|1,0,"+max+""})) +// // td.push("50%: ", B(percentiles["50"]), " ", +// // "90%: ", B(percentiles["90"]), " ", +// // "max: ", B(percentiles["100"])); +// break; +// } +// tcount++; +// }); +// div.push(t) +// } +// if (_showHistStats()) { +// div.push(A({href: '/ep/admin/usagestats/graph?size=1080x420&statName='+statName}, +// IMG({src: '/ep/admin/usagestats/graph?size=400x200&statName='+statName, +// style: 'border: 1px solid #ccc; margin: 10px 0 0 20px;'})), +// BR(), +// DIV({style: 'text-align: right;'}, +// A({style: 'text-decoration: none; font-size: .8em;', +// href: '/ep/admin/usagestats/data?statName='+statName}, "(data)"))); +// } +// body.push(div); +// } +// +// function render_graph() { +// var sn = request.params.statName; +// if (!sn) { +// render404(); +// } +// usage_stats.respondWithGraph(sn); +// } +// +// +// function render_exceptions() { +// var logNames = ["frontend/exception", "backend/exceptions"]; +// } + +// function render_updatehistory() { +// +// sqlcommon.withConnection(function(conn) { +// var stmnt = "delete from statistics;"; +// var s = conn.createStatement(); +// sqlcommon.closing(s, function() { +// s.execute(stmnt); +// }); +// }); +// +// var processed = {}; +// +// function _domonth(y, m) { +// for (var i = 0; i < 32; i++) { +// _processStatsDay(y, m, i, processed); +// } +// } +// +// _domonth(2008, 10); +// _domonth(2008, 11); +// _domonth(2008, 12); +// _domonth(2009, 1); +// _domonth(2009, 2); +// _domonth(2009, 3); +// _domonth(2009, 4); +// _domonth(2009, 5); +// _domonth(2009, 6); +// _domonth(2009, 7); +// +// response.redirect('/ep/admin/usagestats'); +// } + +// function _processStatsDay(year, month, date, processed) { +// var now = new Date(); +// var day = new Date(); +// +// for (var i = 0; i < 10; i++) { +// day.setFullYear(year); +// day.setDate(date); +// day.setMonth(month-1); +// } +// +// if ((+day < +now) && +// (!((day.getFullYear() == now.getFullYear()) && +// (day.getMonth() == now.getMonth()) && +// (day.getDate() == now.getDate())))) { +// +// var dayNoon = statistics.noon(day); +// +// if (processed[dayNoon]) { +// return; +// } else { +// statistics.processLogDay(new Date(dayNoon)); +// processed[dayNoon] = true; +// } +// } else { +// /* nothing */ +// } +// } + diff --git a/trunk/etherpad/src/etherpad/control/store/eepnet_checkout_control.js b/trunk/etherpad/src/etherpad/control/store/eepnet_checkout_control.js new file mode 100644 index 0000000..ddd4973 --- /dev/null +++ b/trunk/etherpad/src/etherpad/control/store/eepnet_checkout_control.js @@ -0,0 +1,757 @@ +/** + * 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. + */ + +import("email.sendEmail"); +import("fastJSON"); +import("funhtml.*"); +import("jsutils.*"); +import("sqlbase.sqlobj"); +import("stringutils"); +import("sync"); + +import("etherpad.billing.billing"); +import("etherpad.billing.fields"); +import("etherpad.globals"); +import("etherpad.globals.*"); +import("etherpad.helpers"); +import("etherpad.licensing"); +import("etherpad.pro.pro_utils"); +import("etherpad.sessions.{getSession,getTrackingId,getSessionId}"); +import("etherpad.store.checkout"); +import("etherpad.store.eepnet_checkout"); +import("etherpad.utils.*"); + +import("static.js.billing_shared.{billing=>billingJS}"); + +jimport("java.lang.System.out.println"); + +//---------------------------------------------------------------- + +var STORE_URL = '/ep/store/eepnet-checkout/'; + +var _pageSequence = [ + ['purchase', "Number of Users", true], + ['support-contract', "Support Contract", true], + ['license-info', "License Information", true], + ['billing-info', "Billing Information", true], + ['confirmation', "Confirmation", false] +]; + +var _specialPages = { + 'receipt': ['receipt', "Receipt", false] +} + +//---------------------------------------------------------------- + +function _cart() { + return getSession().eepnetCart; +} + +function _currentPageSegment() { + return request.path.split('/')[4]; +} + +function _currentPageId() { + return _applyToCurrentPageSequenceEntry(function(ps) { return ps[0]; }); +} + +function _applyToCurrentPageSequenceEntry(f) { + for (var i = 0; i < _pageSequence.length; i++) { + if (_pageSequence[i][0] == _currentPageSegment()) { + return f(_pageSequence[i], i, true); + } + } + if (_specialPages[_currentPageSegment()]) { + return f(_specialPages[_currentPageSegment()], -1, false); + } + return undefined; +} + +function _currentPageIndex() { + return _applyToCurrentPageSequenceEntry(function(ps, i) { return i; }); +} + +function _currentPageTitle() { + return _applyToCurrentPageSequenceEntry(function(ps) { return ps[1]; }); +} + +function _currentPageShowCart() { + return _applyToCurrentPageSequenceEntry(function(ps) { return ps[2]; }); +} + +function _currentPageInFlow() { + return _applyToCurrentPageSequenceEntry(function(ps, i, isSpecial) { return isSpecial }); +} + +function _pageId(d) { + return _applyToCurrentPageSequenceEntry(function(ps, i) { + if (_pageSequence[i+d]) { + return _pageSequence[i+d][0]; + } + }); +} + +function _nextPageId() { return _pageId(+1); } +function _prevPageId() { return _pageId(-1); } + +function _advancePage() { + response.redirect(_pathTo(_nextPageId())); +} + +function _pathTo(id) { + return STORE_URL+id; +} + +// anything starting with 'billing' is also ok. +function _isAutomaticallySetParam(p) { + var _automaticallySetParams = arrayToSet([ + 'numUsers', 'couponCode', 'supportContract', + 'email', 'ownerName', 'orgName', 'licenseAgreement' + ]); + + return _automaticallySetParams[p] || stringutils.startsWith(p, "billing"); +} + +function _lastSubmittedPage() { + var cart = _cart(); + return isNaN(cart.lastSubmittedPage) ? -1 : Number(cart.lastSubmittedPage); +} + +function _shallowSafeCopy(obj) { + return billing.clearKeys(obj, [ + {name: 'billingCCNumber', + valueTest: function(s) { return /^\d{15,16}$/.test(s) }, + valueReplace: billing.replaceWithX }, + {name: 'billingCSC', + valueTest: function(s) { return /^\d{3,4}$/.test(s) }, + valueReplace: billing.replaceWithX }]); +} + +function onRequest() { + billing.log({ + 'type': "billing-request", + 'date': +(new Date), + 'method': request.method, + 'path': request.path, + 'query': request.query, + 'host': request.host, + 'scheme': request.scheme, + 'params': _shallowSafeCopy(request.params), + 'cart': _shallowSafeCopy(_cart()) + }); + if (request.path == STORE_URL+"paypalnotify") { + _handlePaypalNotification(); + } + if (request.path == STORE_URL+"paypalredirect") { + _handlePayPalRedirect(); + } + var cart = _cart(); + if (!cart || request.params.clearcart) { + getSession().eepnetCart = { + lastSubmittedPage: -1, + invoiceId: billing.createInvoice() + }; + if (request.params.clearcart) { + response.redirect(request.path); + } + if (_currentPageId() != 'purchase') { + response.redirect(_pathTo('purchase')); + } + cart = _cart(); + } + if (request.params.invoice) { + cart.billingPurchaseType = 'invoice'; + } + if (cart.purchaseComplete && _currentPageId() != 'receipt') { + cart.showStartOverMessage = true; + response.redirect(_pathTo('receipt')); + } + // somehow user got too far? + if (_currentPageIndex() > _lastSubmittedPage() + 1) { + response.redirect(_pathTo(_pageSequence[_lastSubmittedPage()+1][0])); + } + if (request.isGet) { + // see if this is a standard cart-page get + if (_currentPageId()) { + _renderCartPage(); + return true; + } + } + if (request.isPost) { + // add params to cart + eachProperty(request.params, function(k,v) { + if (! _isAutomaticallySetParam(k)) { return; } + if (k == "billingCCNumber" && v.charAt(0) == 'X') { return; } + cart[k] = stringutils.toHTML(v); + }); + if (_currentPageId() == 'license-info' && ! request.params.licenseAgreement) { + delete cart.licenseAgreement; + } + if (_currentPageIndex() > cart.lastSubmittedPage) { + cart.lastSubmittedPage = _currentPageIndex(); + } + } + if (request.params.backbutton) { + _updateCosts(); + response.redirect(_pathTo(_prevPageId())); + } + return false; // commence auto-dispatch +} + +function _getCoupon(code) { + return sqlobj.selectSingle('checkout_referral', {id: code}); +} + +function _supportCost() { + var cart = _cart(); + return Math.max(eepnet_checkout.SUPPORT_MIN_COST, eepnet_checkout.SUPPORT_COST_PCT/100*cart.baseCost); +} + +function _discountedSupportCost() { + var cart = _cart(); + if ('couponSupportPctDiscount' in cart) { + return _supportCost() - + (cart.couponSupportPctDiscount ? + cart.couponSupportPctDiscount/100 * _supportCost() : + 0); + } +} + +function _updateCosts() { + var cart = _cart(); + + if (cart.numUsers) { + cart.numUsers = Number(cart.numUsers); + + cart.baseCost = cart.numUsers * eepnet_checkout.COST_PER_USER; + + if (cart.supportContract == "true") { + cart.supportCost = _supportCost(); + } else { + delete cart.supportCost; + } + + var coupon = _getCoupon(cart.couponCode); + if (coupon) { + for (i in coupon) { + cart["coupon"+stringutils.makeTitle(i)] = coupon[i]; + } + cart.coupon = coupon; + } else { + for (i in cart.coupon) { + delete cart["coupon"+stringutils.makeTitle(i)]; + } + delete cart.coupon; + } + + if (cart.couponProductPctDiscount) { + cart.productReferralDiscount = + cart.couponProductPctDiscount/100 * cart.baseCost; + } else { + delete cart.productReferralDiscount; + } + if (cart.couponSupportPctDiscount) { + cart.supportReferralDiscount = + cart.couponSupportPctDiscount/100 * (cart.supportCost || 0); + } else { + delete cart.supportReferralDiscount; + } + cart.subTotal = + cart.baseCost - (cart.productReferralDiscount || 0) + + (cart.supportCost || 0) - (cart.supportReferralDiscount || 0); + + if (cart.couponTotalPctDiscount) { + cart.totalReferralDiscount = + cart.couponTotalPctDiscount/100 * cart.subTotal; + } else { + delete cart.totalReferralDiscount; + } + + if (cart.couponFreeUsersCount || cart.couponFreeUsersPct) { + cart.freeUserCount = + Math.round(cart.couponFreeUsersCount + + cart.couponFreeUsersPct/100 * cart.numUsers); + } else { + delete cart.freeUserCount; + } + cart.userCount = Number(cart.numUsers) + Number(cart.freeUserCount || 0); + + cart.total = + cart.subTotal - (cart.totalReferralDiscount || 0); + } +} + +//---------------------------------------------------------------- +// template helper functions +//---------------------------------------------------------------- + +function _cartDebug() { + if (globals.isProduction()) { + return ''; + } + + var d = DIV({style: 'font-family: monospace; font-size: 1em; border: 1px solid #ccc; padding: 1em; margin: 1em;'}); + d.push(H3({style: "font-size: 1.5em; font-weight: bold;"}, "Debug Info:")); + var t = TABLE({border: 1, cellspacing: 0, cellpadding: 4}); + keys(_cart()).sort().forEach(function(k) { + var v = _cart()[k]; + if (typeof(v) == 'object' && v != null) { + v = v.toSource(); + } + t.push(TR(TD({style: 'padding: 2px 6px;', align: 'right'}, k), + TD({style: 'padding: 2px 6px;', align: 'left'}, v))); + }); + d.push(t); + return d; +} + +var billingButtonName = "Review Order"; + +function _templateContext(extra) { + var cart = _cart(); + + var pageId = _currentPageId(); + + var ret = { + cart: cart, + costPerUser: eepnet_checkout.COST_PER_USER, + supportCostPct: eepnet_checkout.SUPPORT_COST_PCT, + supportMinCost: eepnet_checkout.SUPPORT_MIN_COST, + errorIfInvalid: _errorIfInvalid, + dollars: checkout.dollars, + countryList: fields.countryList, + usaStateList: fields.usaStateList, + obfuscateCC: checkout.obfuscateCC, + helpers: helpers, + inFlow: _currentPageInFlow(), + displayCart: _displayCart, + displaySummary: _displaySummary, + pathTo: _pathTo, + billing: billingJS, + handlePayPalRedirect: _handlePayPalRedirect, + supportCost: _supportCost, + discountedSupportCost: _discountedSupportCost, + billingButtonName: billingButtonName, + billingFinalPhrase: "

You will not be charged until you review"+ + " and confirm your order on the next page.

", + getFullSuperdomainHost: pro_utils.getFullSuperdomainHost, + showCouponCode: false + }; + eachProperty(extra, function(k, v) { + ret[k] = v; + }); + return ret; +} + +function _displayCart(cartid, editable) { + return renderTemplateAsString('store/eepnet-checkout/cart.ejs', _templateContext({ + shoppingcartid: cartid || "shoppingcart", + editable: editable + })); +} + +function _displaySummary(editable) { + return renderTemplateAsString('store/eepnet-checkout/summary.ejs', _templateContext({ + editable: editable + })); +} + +function _renderCartPage() { + var cart = _cart(); + + var pageId = _currentPageId(); + var title = _currentPageTitle(); + + function _getContent() { + return renderTemplateAsString('store/eepnet-checkout/'+pageId+'.ejs', _templateContext()); + } + + renderFramed('store/eepnet-checkout/checkout-template.ejs', { + cartDebug: _cartDebug, + errorDiv: _errorDiv, + pageId: pageId, + getContent: _getContent, + title: title, + inFlow: _currentPageInFlow(), + displayCart: _displayCart, + showCart: _currentPageShowCart(), + cart: cart, + billingButtonName: billingButtonName + }); + + // clear errors + delete cart.errorMsg; + delete cart.errorId; +} + +function _errorDiv() { + var m = _cart().errorMsg; + if (m) { + return DIV({className: 'errormsg', id: 'errormsg'}, m); + } else { + return ''; + } +} + +function _errorIfInvalid(id) { + var e = _cart().errorId + if (e && e[id]) { + return 'error'; + } else { + return ''; + } +} + +function _validationError(id, msg, pageId) { + var cart = _cart(); + cart.errorMsg = msg; + cart.errorId = {}; + if (id instanceof Array) { + id.forEach(function(k) { + cart.errorId[k] = true; + }); + } else { + cart.errorId[id] = true; + } + if (pageId) { + response.redirect(_pathTo(pageId)); + } + response.redirect(request.path); +} + +//-------------------------------------------------------------------------------- +// main +//-------------------------------------------------------------------------------- + +function render_main() { + response.redirect(STORE_URL+'purchase'); +} + +//-------------------------------------------------------------------------------- +// cart +//-------------------------------------------------------------------------------- + +function render_purchase_post() { + var cart = _cart(); + + // validate numUsers and couponCode + if (! checkout.isOnlyDigits(cart.numUsers)) { + _validationError("numUsers", "Please enter a valid number of users."); + } + if (Number(cart.numUsers) < 1) { + _validationError("numUsers", "Please specify at least one user."); + } + + if (cart.couponCode && (cart.couponCode.length != 8 || ! _getCoupon(cart.couponCode))) { + _validationError("couponCode", "That coupon code does not appear to be valid."); + } + + _updateCosts(); + _advancePage(); +} + +//-------------------------------------------------------------------------------- +// support-contract +//-------------------------------------------------------------------------------- + +function render_support_contract_post() { + var cart = _cart(); + + if (cart.supportContract != "true" && cart.supportContract != "false") { + _validationError("supportContract", "Please select one of the options."); + } + + _updateCosts(); + _advancePage(); +} + +//-------------------------------------------------------------------------------- +// license-info +//-------------------------------------------------------------------------------- + +function render_license_info_post() { + var cart = _cart(); + + if (!isValidEmail(cart.email)) { + _validationError("email", "That email address does not look valid."); + } + if (!cart.ownerName) { + _validationError("ownerName", "Please enter a license owner name."); + } + if (!cart.orgName) { + _validationError("orgName", "Please enter an organization name."); + } + if (!cart.licenseAgreement) { + _validationError("licenseAgreement", "You must agree to the terms of the license to purchase EtherPad PNE."); + } + + if ((! cart.billingFirstName) && ! (cart.billingLastName)) { + var nameParts = cart.ownerName.split(/\s+/); + if (nameParts.length == 1) { + cart.billingFirstName = nameParts[0]; + } else { + cart.billingLastName = nameParts[nameParts.length-1]; + cart.billingFirstName = nameParts.slice(0, nameParts.length-1).join(' '); + } + } + + _updateCosts(); + _advancePage(); +} + +//-------------------------------------------------------------------------------- +// billing-info +//-------------------------------------------------------------------------------- + +function render_billing_info_post() { + var cart = _cart(); + + checkout.validateBillingCart(_validationError, cart); + if (cart.billingPurchaseType == 'paypal') { + _beginPaypalPurchase(); + } + + _updateCosts(); + _advancePage(); +} + +function _absoluteUrl(id) { + return request.scheme+"://"+request.host+_pathTo(id); +} + +function _beginPaypalPurchase() { + _updateCosts(); + + var cart = _cart(); + + var purchase = _generatePurchaseRecord(); + var result = + billing.beginExpressPurchase(cart.invoiceId, cart.customerId, + "EEPNET", cart.total || 0.01, cart.couponCode || "", + _absoluteUrl('paypalredirect?status=ok'), + _absoluteUrl('paypalredirect?status=fail'), + _absoluteUrl('paypalnotify')); + if (result.status != 'success') { + _validationError("billingPurchaseType", + "PayPal purchase not available at the moment. "+ + "Please try again later, or try using a different payment option."); + } + cart.paypalPurchaseInfo = result.purchaseInfo; + response.redirect(billing.paypalPurchaseUrl(result.purchaseInfo.token)); +} + +//-------------------------------------------------------------------------------- +// confirmation +//-------------------------------------------------------------------------------- + +function _handlePaypalNotification() { + var ret = billing.handlePaypalNotification(); + if (ret.status == 'completion') { + var purchaseInfo = ret.purchaseInfo; + var eepnetPurchase = eepnet_checkout.getPurchaseByInvoiceId(purchaseInfo.invoiceId); + var fakeCart = { + ownerName: eepnetPurchase.owner, + orgName: eepnetPurchase.organization, + email: eepnetPurchase.emails, + customerId: eepnetPurchase.id, + userCount: eepnetPurchase.numUsers, + receiptEmail: eepnetPurchase.receiptEmail, + } + eepnet_checkout.generateLicenseKey(fakeCart); + eepnet_checkout.sendReceiptEmail(fakeCart); + eepnet_checkout.sendLicenseEmail(fakeCart); + billing.log({type: 'purchase-complete', dollars: purchaseInfo.cost}); + } +} + +function _handlePayPalRedirect() { + var cart = _cart(); + + if (request.params.status == 'ok' && cart.paypalPurchaseInfo) { + var result = billing.continueExpressPurchase(cart.paypalPurchaseInfo); + if (result.status == 'success') { + cart.paypalPayerInfo = result.payerInfo; + response.redirect(_pathTo('confirmation')); + } else { + _validationError("billingPurchaseType", + "There was an error processing your payment through PayPal. "+ + "Please try again later, or use a different payment option.", + 'billing-info'); + } + } else { + _validationError("billingPurchaseType", + "PayPal payment didn't go through. "+ + "Please try again later, or use a different payment option.", + 'billing-info'); + } +} + +function _recordPurchase(p) { + return sqlobj.insert("checkout_purchase", p); +} + +function _generatePurchaseRecord() { + var cart = _cart(); + + if (! cart.invoiceId) { + throw Error("No invoice id!"); + } + + var purchase = { + invoiceId: cart.invoiceId, + email: cart.email, + firstName: cart.billingFirstName, + lastName: cart.billingLastName, + owner: cart.ownerName || "", + organization: cart.orgName || "", + addressLine1: cart.billingAddressLine1 || "", + addressLine2: cart.billingAddressLine2 || "", + city: cart.billingCity || "", + state: cart.billingState || "", + zip: cart.billingZipCode || "", + referral: cart.couponCode, + cents: cart.total*100, // cents here. + numUsers: cart.userCount, + purchaseType: cart.billingPurchaseType, + } + cart.customerId = _recordPurchase(purchase); + return purchase; +} + +function _performCreditCardPurchase() { + var cart = _cart(); + var purchase = _generatePurchaseRecord(); + var payInfo = checkout.generatePayInfo(cart); + + // log everything but the CVV, which we're not allowed to store + // any longer than it takes to process this transaction. + var savedCvv = payInfo.cardCvv; + delete payInfo.cardCvv; + checkout.writeToEncryptedLog(fastJSON.stringify({date: String(new Date()), purchase: purchase, customerId: cart.customerId, payInfo: payInfo})); + payInfo.cardCvv = savedCvv; + + var result = + billing.directPurchase(cart.invoiceId, cart.customerId, + "EEPNET", cart.total || 0.01, + cart.couponCode || "", + payInfo, _absoluteUrl('paypalnotify')); + + if (result.status == 'success') { + cart.status = 'success'; + cart.purchaseComplete = true; + eepnet_checkout.generateLicenseKey(cart); + eepnet_checkout.sendReceiptEmail(cart); + eepnet_checkout.sendLicenseEmail(cart); + billing.log({type: 'purchase-complete', dollars: cart.total, + email: cart.email, user: cart.ownerName, + org: cart.organization}); + // TODO: generate key and include in receipt page, and add to purchase table. + } else if (result.status == 'pending') { + cart.status = 'pending'; + cart.purchaseComplete = true; + eepnet_checkout.sendReceiptEmail(cart); + // save the receipt email text to resend later. + eepnet_checkout.updatePurchaseWithReceipt(cart.customerId, + eepnet_checkout.receiptEmailText(cart)); + } else if (result.status == 'failure') { + var paypalResult = result.debug; + billing.log({'type': 'FATAL', value: "Direct purchase failed on paypal.", cart: cart, paypal: paypalResult}); + if (result.errorField.permanentErrors[0] == 'invoiceId') { + // repeat invoice id. damnit, this is bad. + sendEmail('support@pad.spline.inf.fu-berlin.de', 'urgent@pad.spline.inf.fu-berlin.de', 'DUPLICATE INVOICE WARNING!', {}, + "Hey,\n\nThis is a billing system error. The EEPNET checkout tried to make a "+ + "purchase with PayPal and got a duplicate invoice error on invoice ID "+cart.invoiceId+ + ".\n\nUnless you're expecting this (or recently ran a selenium test, or have reason to "+ + "believe this isn't an exceptional condition, please look into this "+ + "and get back to the user ASAP!\n\n"+fastJSON.stringify(cart)); + _validationError('', "Your payment was processed, but we cannot proceed. "+ + "You will hear from us shortly via email. (If you don't hear from us "+ + "within 24 hours, please email "+ + "sales@pad.spline.inf.fu-berlin.de.)"); + } + checkout.validateErrorFields(function(x, y) { _validationError(x, y, 'billing-info') }, "There seems to be an error in your billing information."+ + " Please verify and correct your ", + result.errorField.userErrors); + checkout.validateErrorFields(function(x, y) { _validationError(x, y, 'billing-info') }, "The bank declined your billing information. Please try a different ", + result.errorField.permanentErrors); + _validationError('', "A temporary error has prevented processing of your payment. Please try again later."); + } else { + billing.log({'type': 'FATAL', value: "Unknown error: "+result.status+" - debug: "+result.debug}); + sendEmail('support@pad.spline.inf.fu-berlin.de', 'urgent@pad.spline.inf.fu-berlin.de', 'UNKNOWN ERROR WARNING!', {}, + "Hey,\n\nThis is a billing system error. Some unknown error occurred. "+ + "This shouldn't ever happen. Probably good to let J.D. know. \n\n"+ + fastJSON.stringify(cart)); + _validationError('', "An unknown error occurred. We're looking into it!") + } +} + +function _completePaypalPurchase() { + var cart = _cart(); + var purchaseInfo = cart.paypalPurchaseInfo; + var payerInfo = cart.paypalPayerInfo; + + var result = billing.completeExpressPurchase(purchaseInfo, payerInfo, _absoluteUrl('paypalnotify')); + if (result.status == 'success') { + cart.status = 'success'; + cart.purchaseComplete = true; + eepnet_checkout.generateLicenseKey(cart); + eepnet_checkout.sendReceiptEmail(cart); + eepnet_checkout.sendLicenseEmail(cart); + billing.log({type: 'purchase-complete', dollars: cart.total, + email: cart.email, user: cart.ownerName, + org: cart.organization}); + + } else if (result.status == 'pending') { + cart.status = 'pending'; + cart.purchaseComplete = true; + eepnet_checkout.sendReceiptEmail(cart); + // save the receipt email text to resend later. + eepnet_checkout.updatePurchaseWithReceipt(cart.customerId, + eepnet_checkout.receiptEmailText(cart)); + } else { + billing.log({'type': 'FATAL', value: "Paypal failed.", cart: cart, paypal: paypalResult}); + _validationError("billingPurchaseType", + "There was an error processing your payment through PayPal. "+ + "Please try again later, or use a different payment option.", + 'billing-info'); + } +} + +function _showReceipt() { + response.redirect(_pathTo('receipt')); +} + +function render_confirmation_post() { + var cart = _cart(); + + _updateCosts(); // no fishy business, please. + + if (cart.billingPurchaseType == 'creditcard') { + _performCreditCardPurchase(); + _showReceipt(); + } else if (cart.billingPurchaseType == 'paypal') { + _completePaypalPurchase(); + _showReceipt(); + } +} + +//-------------------------------------------------------------------------------- +// receipt +//-------------------------------------------------------------------------------- + +function render_receipt_post() { + response.redirect(request.path); +} diff --git a/trunk/etherpad/src/etherpad/control/store/storecontrol.js b/trunk/etherpad/src/etherpad/control/store/storecontrol.js new file mode 100644 index 0000000..43569e4 --- /dev/null +++ b/trunk/etherpad/src/etherpad/control/store/storecontrol.js @@ -0,0 +1,201 @@ +/** + * 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. + */ + +import("dispatch.{Dispatcher,DirMatcher,forward}"); +import("fastJSON"); +import("funhtml.*"); + +import('etherpad.globals.*'); +import("etherpad.store.eepnet_trial"); +import("etherpad.store.eepnet_checkout"); +import("etherpad.sessions.getSession"); +import("etherpad.utils.*"); + +import("etherpad.control.store.eepnet_checkout_control"); +import("etherpad.control.pro.admin.team_billing_control"); + +jimport("java.lang.System.out.println"); + +//---------------------------------------------------------------- + +function onStartup() {} + +function onRequest() { + var disp = new Dispatcher(); + disp.addLocations([ + [DirMatcher('/ep/store/eepnet-checkout/'), forward(eepnet_checkout_control)], + ]); + return disp.dispatch(); +} + +//---------------------------------------------------------------- + +function render_main() { + response.redirect("/ep/about/pricing"); +} + +//---------------------------------------------------------------- +// Flow goes through these 4 pages in order: +//---------------------------------------------------------------- + +function render_eepnet_eval_signup_get() { + renderFramed("store/eepnet_eval_signup.ejs", { + trialDays: eepnet_trial.getTrialDays(), + oldData: (getSession().pricingContactData || {}), + sfIndustryList: eepnet_trial.getSalesforceIndustryList() + }); + delete getSession().errorMsg; +} + +// function render_eepnet_eval_signup_post() { +// response.setContentType("text/plain; charset=utf-8"); +// var data = {}; +// var fields = ['firstName', 'lastName', 'email', 'orgName', +// 'jobTitle', 'phone', 'estUsers', 'industry']; +// +// if (!getSession().pricingContactData) { +// getSession().pricingContactData = {}; +// } +// +// function _redirectErr(msg) { +// response.write(fastJSON.stringify({error: msg})); +// response.stop(); +// } +// +// fields.forEach(function(f) { +// getSession().pricingContactData[f] = request.params[f]; +// }); +// +// fields.forEach(function(f) { +// data[f] = request.params[f]; +// if (!(data[f] && (data[f].length > 0))) { +// _redirectErr("All fields are required."); +// } +// }); +// +// // validate email +// if (!isValidEmail(data.email)) { +// _redirectErr("That email address doesn't look valid."); +// } +// +// // check that email not already registered. +// if (eepnet_trial.hasEmailAlreadyDownloaded(data.email)) { +// _redirectErr("That email has already downloaded a free trial."+ +// ' Recover a lost license key here.'); +// } +// +// // Looks good! Create and email license key... +// eepnet_trial.createAndMailNewLicense(data); +// getSession().message = "A license key has been sent to "+data.email; +// +// // Generate web2lead info and return it +// var web2leadData = eepnet_trial.getWeb2LeadData(data, request.clientAddr, getSession().initialReferer); +// response.write(fastJSON.stringify(web2leadData)); +// } +// +// function render_salesforce_web2lead_ok() { +// renderFramedHtml([ +// '' +// ].join('\n')); +// } +// +// function render_eepnet_eval_download() { +// // NOTE: keep this URL around for historical reasons? +// response.redirect("/ep/store/eepnet-download"); +// } +// +// function render_eepnet_download() { +// renderFramed("store/eepnet_download.ejs", { +// message: (getSession().message || null), +// versionString: (PNE_RELEASE_VERSION+" ("+PNE_RELEASE_DATE +")") +// }); +// delete getSession().message; +// } +// +// function render_eepnet_download_zip() { +// response.redirect("/static/zip/pne-release/etherpad-pne-"+PNE_RELEASE_VERSION+".zip"); +// } +// +// function render_eepnet_download_nextsteps() { +// renderFramed("store/eepnet_eval_nextsteps.ejs"); +// } + +//---------------------------------------------------------------- +// recover a lost license +//---------------------------------------------------------------- +function render_eepnet_recover_license_get() { + var d = DIV({className: "fpcontent"}); + + d.push(P("Recover your lost license key.")); + + if (getSession().message) { + d.push(DIV({id: "resultmsg", + style: "border: 1px solid #333; padding: 0 1em; background: #efe; margin: 1em 0;"}, getSession().message)); + delete getSession().message; + } + if (getSession().error) { + d.push(DIV({id: "errormsg", + style: "border: 1px solid red; padding: 0 1em; background: #fee; margin: 1em 0;"}, getSession().error)); + delete getSession().error; + } + + d.push(FORM({style: "border: 1px solid #222; padding: 2em; background: #eee;", + action: request.path, method: "post"}, + LABEL({htmlFor: "email"}, + "Your email address:"), + INPUT({type: "text", name: "email", id: "email"}), + INPUT({type: "submit", id: "submit", value: "Submit"}))); + + renderFramedHtml(d); +} + +function render_eepnet_recover_license_post() { + var email = request.params.email; + if (!eepnet_trial.hasEmailAlreadyDownloaded(email) && !eepnet_trialhasEmailAlreadyPurchased(email)) { + getSession().error = P("License not found for email: \"", email, "\"."); + response.redirect(request.path); + } + if (eepnet_checkout.hasEmailAlreadyPurchased(email)) { + eepnet_checkout.mailLostLicense(email); + } else if (eepnet_trial.hasEmailAlreadyDownloaded(email)) { + eepnet_trial.mailLostLicense(email); + } + getSession().message = P("Your license information has been sent to ", email, "."); + response.redirect(request.path); +} + +//---------------------------------------------------------------- +function render_eepnet_purchase_get() { + renderFramed("store/eepnet_purchase.ejs", {}); +} + +//-------------------------------------------------------------------------------- +// csc-help page +//-------------------------------------------------------------------------------- + +function render_csc_help_get() { + response.write(renderTemplateAsString("store/csc-help.ejs")); +} + +//-------------------------------------------------------------------------------- +// paypal notifications for pro +//-------------------------------------------------------------------------------- + +function render_paypalnotify() { + team_billing_control.handlePaypalNotify(); +} diff --git a/trunk/etherpad/src/etherpad/control/testcontrol.js b/trunk/etherpad/src/etherpad/control/testcontrol.js new file mode 100644 index 0000000..ed13006 --- /dev/null +++ b/trunk/etherpad/src/etherpad/control/testcontrol.js @@ -0,0 +1,74 @@ +/** + * 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. + */ + +import("etherpad.globals.*"); +import("etherpad.utils.*"); + +jimport("java.lang.System.out.println"); + +//---------------------------------------------------------------- +var tests = [ + "t0000_test", + "t0001_sqlbase_transaction_rollback", + "t0002_license_generation", + "t0003_persistent_vars", + "t0004_sqlobj", + "t0005_easysync" +]; + +var tscope = this; +tests.forEach(function(t) { + import.call(tscope, 'etherpad.testing.unit_tests.'+t); +}); +//---------------------------------------------------------------- + +function _testName(x) { + x = x.replace(/^t\d+\_/, ''); + return x; +} + +function render_run() { + response.setContentType("text/plain; charset=utf-8"); + if (isProduction() && (request.params.p != "waverunner")) { + response.write("access denied"); + response.stop(); + } + + var singleTest = request.params.t; + var numRun = 0; + + println("----------------------------------------------------------------"); + println("running tests"); + println("----------------------------------------------------------------"); + tests.forEach(function(t) { + var testName = _testName(t); + if (singleTest && (singleTest != testName)) { + return; + } + println("running test: "+testName); + numRun++; + tscope[t].run(); + println("|| pass ||"); + }); + println("----------------------------------------------------------------"); + + if (numRun == 0) { + response.write("Error: no tests found"); + } else { + response.write("OK"); + } +} + diff --git a/trunk/etherpad/src/etherpad/db_migrations/m0000_test.js b/trunk/etherpad/src/etherpad/db_migrations/m0000_test.js new file mode 100644 index 0000000..7df9bfd --- /dev/null +++ b/trunk/etherpad/src/etherpad/db_migrations/m0000_test.js @@ -0,0 +1,23 @@ +/** + * 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 run() { + // nothing +} + + + diff --git a/trunk/etherpad/src/etherpad/db_migrations/m0001_eepnet_signups_init.js b/trunk/etherpad/src/etherpad/db_migrations/m0001_eepnet_signups_init.js new file mode 100644 index 0000000..0e65779 --- /dev/null +++ b/trunk/etherpad/src/etherpad/db_migrations/m0001_eepnet_signups_init.js @@ -0,0 +1,38 @@ +/** + * 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. + */ + +import("sqlbase.sqlobj"); +import("sqlbase.sqlcommon"); +import("etherpad.utils.isPrivateNetworkEdition"); + +function run() { + if (isPrivateNetworkEdition()) { + return; + } + + sqlobj.createTable('eepnet_signups', { + id: 'INT NOT NULL '+sqlcommon.autoIncrementClause()+' PRIMARY KEY', + email: 'VARCHAR(128) NOT NULL UNIQUE', + date: 'TIMESTAMP', + signupIp: 'VARCHAR(16)', + fullName: 'VARCHAR(255) NOT NULL', + orgName: 'VARCHAR(255) NOT NULL', + jobTitle: 'VARCHAR(255) NOT NULL', + estUsers: 'VARCHAR(255) NOT NULL', + licenseKey: 'VARCHAR(1024) NOT NULL' + }); +} + diff --git a/trunk/etherpad/src/etherpad/db_migrations/m0002_eepnet_signups_2.js b/trunk/etherpad/src/etherpad/db_migrations/m0002_eepnet_signups_2.js new file mode 100644 index 0000000..786e4e9 --- /dev/null +++ b/trunk/etherpad/src/etherpad/db_migrations/m0002_eepnet_signups_2.js @@ -0,0 +1,47 @@ +/** + * 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. + */ + +import("sqlbase.sqlobj"); +import("etherpad.utils.isPrivateNetworkEdition"); + +function run() { + if (isPrivateNetworkEdition()) { + return; + } + + // add new columns. + sqlobj.addColumns('eepnet_signups', { + firstName: 'VARCHAR(128) NOT NULL DEFAULT \'\'', + lastName: 'VARCHAR(128) NOT NULL DEFAULT \'\'', + phone: 'VARCHAR(128) NOT NULL DEFAULT \'\'' + }); + + // split name into first/last + var rows = sqlobj.selectMulti('eepnet_signups', {}, {}); + rows.forEach(function(r) { + var name = r.fullName; + r.firstName = (r.fullName.split(' ')[0]) || "?"; + r.lastName = (r.fullName.split(' ').slice(1).join(' ')) || "?"; + r.phone = "?"; + sqlobj.updateSingle('eepnet_signups', {id: r.id}, r); + }); + + // drop column fullName + sqlobj.dropColumn('eepnet_signups', 'fullName'); +} + + + diff --git a/trunk/etherpad/src/etherpad/db_migrations/m0003_create_tests_table_v2.js b/trunk/etherpad/src/etherpad/db_migrations/m0003_create_tests_table_v2.js new file mode 100644 index 0000000..f121145 --- /dev/null +++ b/trunk/etherpad/src/etherpad/db_migrations/m0003_create_tests_table_v2.js @@ -0,0 +1,29 @@ +/** + * 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. + */ + +import("sqlbase.sqlobj"); +import("sqlbase.sqlcommon"); +import("etherpad.utils.isPrivateNetworkEdition"); + +function run() { + if (sqlcommon.doesTableExist('just_a_test')) { + sqlobj.dropTable('just_a_test'); + } + sqlobj.createTable('just_a_test', { + id: 'INT NOT NULL '+sqlcommon.autoIncrementClause()+' PRIMARY KEY', + x: 'VARCHAR(128)' + }); +} diff --git a/trunk/etherpad/src/etherpad/db_migrations/m0004_convert_all_tables_to_innodb.js b/trunk/etherpad/src/etherpad/db_migrations/m0004_convert_all_tables_to_innodb.js new file mode 100644 index 0000000..959865d --- /dev/null +++ b/trunk/etherpad/src/etherpad/db_migrations/m0004_convert_all_tables_to_innodb.js @@ -0,0 +1,38 @@ +/** + * 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. + */ + +import("sqlbase.sqlcommon"); +import("sqlbase.sqlobj"); + +import('etherpad.db_migrations.migration_runner.dmesg'); + +function run() { + // This migration only applies to MySQL + if (!sqlcommon.isMysql()) { + return; + } + + var tables = sqlobj.listTables(); + tables.forEach(function(t) { + if (sqlobj.getTableEngine(t) != "InnoDB") { + dmesg("Converting table "+t+" to InnoDB..."); + sqlobj.setTableEngine(t, "InnoDB"); + } + }); + +}; + + diff --git a/trunk/etherpad/src/etherpad/db_migrations/m0005_create_billing_tables.js b/trunk/etherpad/src/etherpad/db_migrations/m0005_create_billing_tables.js new file mode 100644 index 0000000..0dfd37e --- /dev/null +++ b/trunk/etherpad/src/etherpad/db_migrations/m0005_create_billing_tables.js @@ -0,0 +1,73 @@ +/** + * 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. + */ + +import("etherpad.utils.isPrivateNetworkEdition"); +import("sqlbase.sqlobj"); + +function run() { + if (isPrivateNetworkEdition()) { + return; + } + + var idColspec = "INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY"; + + sqlobj.createTable('billing_purchase', { + id: idColspec, + type: "ENUM('onetimepurchase', 'subscription')", + customer: "INT(11) NOT NULL", + product: "VARCHAR(128) NOT NULL", + cost: "INT(11) NOT NULL", + coupon: "VARCHAR(128) NOT NULL", + time: "DATETIME", + paidThrough: "DATETIME", + status: "ENUM('active', 'inactive')" + }, { + type: true, + customer: true, + product: true + }); + + sqlobj.createTable('billing_invoice', { + id: idColspec, + time: "DATETIME", + purchase: "INT(11) NOT NULL", + amt: "INT(11) NOT NULL", + status: "ENUM('pending', 'paid', 'void', 'refunded')" + }, { + status: true + }); + + sqlobj.createTable('billing_transaction', { + id: idColspec, + customer: "INT(11)", + time: "DATETIME", + amt: "INT(11)", + payInfo: "VARCHAR(128)", + txnId: "VARCHAR(128)", // depends on gateway used? + status: "ENUM('new', 'success', 'failure', 'pending')" + }, { + customer: true, + txnId: true + }); + + sqlobj.createTable('billing_adjustment', { + id: idColspec, + transaction: "INT(11)", + invoice: "INT(11)", + time: "DATETIME", + amt: "INT(11)" + }); +} diff --git a/trunk/etherpad/src/etherpad/db_migrations/m0006_eepnet_signups_3.js b/trunk/etherpad/src/etherpad/db_migrations/m0006_eepnet_signups_3.js new file mode 100644 index 0000000..349b27a --- /dev/null +++ b/trunk/etherpad/src/etherpad/db_migrations/m0006_eepnet_signups_3.js @@ -0,0 +1,29 @@ +/** + * 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. + */ + +import("etherpad.utils.isPrivateNetworkEdition"); +import("sqlbase.sqlobj"); + +function run() { + if (isPrivateNetworkEdition()) { + return; + } + + // add new columns. + sqlobj.addColumns('eepnet_signups', { + industry: 'VARCHAR(128)', + }); +} diff --git a/trunk/etherpad/src/etherpad/db_migrations/m0007_create_pro_tables_v4.js b/trunk/etherpad/src/etherpad/db_migrations/m0007_create_pro_tables_v4.js new file mode 100644 index 0000000..bda5853 --- /dev/null +++ b/trunk/etherpad/src/etherpad/db_migrations/m0007_create_pro_tables_v4.js @@ -0,0 +1,67 @@ +/** + * 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. + */ + +import("sqlbase.sqlcommon"); +import("sqlbase.sqlobj"); + +function run() { + ['pro_domains', 'pro_users', 'pro_padmeta'].forEach(function(t) { + if (sqlcommon.doesTableExist(t)) { + sqlobj.dropTable(t); + } + }); + + sqlobj.createTable('pro_domains', { + id: sqlobj.getIdColspec(), + subDomain: 'VARCHAR(128) UNIQUE NOT NULL', + extDomain: 'VARCHAR(128) DEFAULT NULL', + orgName: 'VARCHAR(128)' + }); + + sqlobj.createIndex('pro_domains', ['subDomain']); + sqlobj.createIndex('pro_domains', ['extDomain']); + + sqlobj.createTable('pro_users', { + id: sqlobj.getIdColspec(), + domainId: 'INT NOT NULL', + fullName: 'VARCHAR(128) NOT NULL', + email: 'VARCHAR(128) NOT NULL', // not unique because same + // email can be on multiple domains. + passwordHash: 'VARCHAR(128) NOT NULL', + createdDate: sqlobj.getDateColspec("NOT NULL"), + lastLoginDate: sqlobj.getDateColspec("DEFAULT NULL"), + isAdmin: sqlobj.getBoolColspec("DEFAULT 0") + }); + + sqlobj.createTable('pro_padmeta', { + id: sqlobj.getIdColspec(), + domainId: 'INT NOT NULL', + localPadId: 'VARCHAR(128) NOT NULL', + title: 'VARCHAR(128)', + creatorId: 'INT DEFAULT NULL', + createdDate: sqlobj.getDateColspec("NOT NULL"), + lastEditorId: 'INT DEFAULT NULL', + lastEditedDate: sqlobj.getDateColspec("DEFAULT NULL") + }); + + sqlobj.createIndex('pro_padmeta', ['domainId', 'localPadId']); + + var pneDomain = "<>"; + if (!sqlobj.selectSingle('pro_domains', {subDomain: pneDomain})) { + sqlobj.insert('pro_domains', {subDomain: pneDomain}); + } +} + diff --git a/trunk/etherpad/src/etherpad/db_migrations/m0008_persistent_vars.js b/trunk/etherpad/src/etherpad/db_migrations/m0008_persistent_vars.js new file mode 100644 index 0000000..30e379a --- /dev/null +++ b/trunk/etherpad/src/etherpad/db_migrations/m0008_persistent_vars.js @@ -0,0 +1,31 @@ +/** + * 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. + */ + +import("sqlbase.sqlcommon"); +import("sqlbase.sqlobj"); + +function run() { + + var idColspec = 'INT NOT NULL '+sqlcommon.autoIncrementClause()+' PRIMARY KEY'; + + sqlobj.createTable('persistent_vars', { + id: idColspec, + name: 'VARCHAR(128) UNIQUE NOT NULL', + stringVal: 'VARCHAR(1024)' + }); + +} + diff --git a/trunk/etherpad/src/etherpad/db_migrations/m0009_pad_tables.js b/trunk/etherpad/src/etherpad/db_migrations/m0009_pad_tables.js new file mode 100644 index 0000000..93f5a62 --- /dev/null +++ b/trunk/etherpad/src/etherpad/db_migrations/m0009_pad_tables.js @@ -0,0 +1,31 @@ +/** + * 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. + */ + +import("sqlbase.sqlbase"); + +function run() { + + // These table creations used to be in etherpad.pad.model.onStartup, but + // they make more sense here because later migrations access these tables. + sqlbase.createJSONTable("PAD_META"); + sqlbase.createJSONTable("PAD_APOOL"); + sqlbase.createStringArrayTable("PAD_REVS"); + sqlbase.createStringArrayTable("PAD_CHAT"); + sqlbase.createStringArrayTable("PAD_REVMETA"); + sqlbase.createStringArrayTable("PAD_AUTHORS"); + +} + diff --git a/trunk/etherpad/src/etherpad/db_migrations/m0010_pad_sqlmeta.js b/trunk/etherpad/src/etherpad/db_migrations/m0010_pad_sqlmeta.js new file mode 100644 index 0000000..36150b1 --- /dev/null +++ b/trunk/etherpad/src/etherpad/db_migrations/m0010_pad_sqlmeta.js @@ -0,0 +1,71 @@ +/** + * 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. + */ + +import("sqlbase.sqlbase"); +import("sqlbase.sqlobj"); +import("sqlbase.sqlcommon"); +import("etherpad.utils.startConsoleProgressBar"); + + +function run() { + + sqlobj.dropAndCreateTable('PAD_SQLMETA', { + id: 'VARCHAR(128) PRIMARY KEY NOT NULL', + version: 'INT NOT NULL', + creationTime: sqlobj.getDateColspec('NOT NULL'), + lastWriteTime: sqlobj.getDateColspec('NOT NULL'), + headRev: 'INT NOT NULL' + }); + + sqlobj.createIndex('PAD_SQLMETA', ['version']); + + var allPadIds = sqlbase.getAllJSONKeys("PAD_META"); + + // If this is a new database, there are no pads; else + // it is an old database with version 1 pads. + if (allPadIds.length == 0) { + return; + } + + var numPadsTotal = allPadIds.length; + var numPadsSoFar = 0; + var progressBar = startConsoleProgressBar(); + + allPadIds.forEach(function(padId) { + var meta = sqlbase.getJSON("PAD_META", padId); + + sqlobj.insert("PAD_SQLMETA", { + id: padId, + version: 1, + creationTime: new Date(meta.creationTime || 0), + lastWriteTime: new Date(), + headRev: meta.head + }); + + delete meta.creationTime; // now stored in SQLMETA + delete meta.version; // just in case (was used during development) + delete meta.dirty; // no longer stored in DB + delete meta.lastAccess; // no longer stored in DB + + sqlbase.putJSON("PAD_META", padId, meta); + + numPadsSoFar++; + progressBar.update(numPadsSoFar/numPadsTotal, numPadsSoFar+"/"+numPadsTotal+" pads"); + }); + + progressBar.finish(); +} + diff --git a/trunk/etherpad/src/etherpad/db_migrations/m0011_pro_users_temppass.js b/trunk/etherpad/src/etherpad/db_migrations/m0011_pro_users_temppass.js new file mode 100644 index 0000000..5ac8b26 --- /dev/null +++ b/trunk/etherpad/src/etherpad/db_migrations/m0011_pro_users_temppass.js @@ -0,0 +1,33 @@ +/** + * 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. + */ + +import("sqlbase.sqlcommon"); +import("sqlbase.sqlobj"); + +function run() { + // allow null values in passwordHash + if (sqlcommon.isDerby()) { + sqlobj.alterColumn('pro_users', 'passwordHash', 'NULL'); + } else { + sqlobj.modifyColumn('pro_users', 'passwordHash', 'VARCHAR(128)'); + } + sqlobj.addColumns('pro_users', { + tempPassHash: 'VARCHAR(128)' + }); +} + + + diff --git a/trunk/etherpad/src/etherpad/db_migrations/m0012_pro_users_auto_signin.js b/trunk/etherpad/src/etherpad/db_migrations/m0012_pro_users_auto_signin.js new file mode 100644 index 0000000..ddd4cf6 --- /dev/null +++ b/trunk/etherpad/src/etherpad/db_migrations/m0012_pro_users_auto_signin.js @@ -0,0 +1,30 @@ +/** + * 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. + */ + +import("sqlbase.sqlcommon"); +import("sqlbase.sqlobj"); + +function run() { + sqlobj.createTable('pro_users_auto_signin', { + id: sqlobj.getIdColspec(), + cookie: 'VARCHAR(128) UNIQUE NOT NULL', + userId: 'INT UNIQUE NOT NULL', + expires: sqlobj.getDateColspec('NOT NULL') + }); + sqlobj.createIndex('pro_users_auto_signin', ['cookie']); + sqlobj.createIndex('pro_users_auto_signin', ['userId']); +} + diff --git a/trunk/etherpad/src/etherpad/db_migrations/m0013_pne_padv2_upgrade.js b/trunk/etherpad/src/etherpad/db_migrations/m0013_pne_padv2_upgrade.js new file mode 100644 index 0000000..146923a --- /dev/null +++ b/trunk/etherpad/src/etherpad/db_migrations/m0013_pne_padv2_upgrade.js @@ -0,0 +1,54 @@ +/** + * 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. + */ + +import("etherpad.utils.startConsoleProgressBar"); +import("etherpad.pad.easysync2migration"); +import("etherpad.pne.pne_utils"); +import("sqlbase.sqlobj"); +import("etherpad.log"); + +function run() { + + // this is a PNE-only migration + if (! pne_utils.isPNE()) { + return; + } + + var migrationsNeeded = sqlobj.selectMulti("PAD_SQLMETA", {version: 1}); + + if (migrationsNeeded.length == 0) { + return; + } + + var migrationsTotal = migrationsNeeded.length; + var migrationsSoFar = 0; + var progressBar = startConsoleProgressBar(); + + migrationsNeeded.forEach(function(obj) { + var padId = String(obj.id); + + log.info("Migrating pad "+padId+" from version 1 to version 2..."); + easysync2migration.migratePad(padId); + sqlobj.update("PAD_SQLMETA", {id: padId}, {version: 2}); + log.info("Migrated pad "+padId+"."); + + migrationsSoFar++; + progressBar.update(migrationsSoFar/migrationsTotal, migrationsSoFar+"/"+migrationsTotal+" pads"); + }); + + progressBar.finish(); +} + diff --git a/trunk/etherpad/src/etherpad/db_migrations/m0014_pne_globalpadids.js b/trunk/etherpad/src/etherpad/db_migrations/m0014_pne_globalpadids.js new file mode 100644 index 0000000..445b32d --- /dev/null +++ b/trunk/etherpad/src/etherpad/db_migrations/m0014_pne_globalpadids.js @@ -0,0 +1,102 @@ +/** + * 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. + */ + +import("etherpad.utils.startConsoleProgressBar"); +import("etherpad.pne.pne_utils"); +import("sqlbase.sqlobj"); +import("sqlbase.sqlbase"); +import("etherpad.log"); +import("sqlbase.sqlcommon.*"); +import("etherpad.pad.padutils"); + +function run() { + + // this is a PNE-only migration + if (! pne_utils.isPNE()) { + return; + } + + var renamesNeeded = sqlobj.selectMulti("PAD_SQLMETA", {}); + + if (renamesNeeded.length == 0) { + return; + } + + var renamesTotal = renamesNeeded.length; + var renamesSoFar = 0; + var progressBar = startConsoleProgressBar(); + + renamesNeeded.forEach(function(obj) { + var oldPadId = String(obj.id); + var newPadId; + if (/^1\$[a-zA-Z0-9\-]+$/.test(oldPadId)) { + // not expecting a user pad beginning with "1$"; + // this case is to avoid trashing dev databases + newPadId = oldPadId; + } + else { + var localPadId = padutils.makeValidLocalPadId(oldPadId); + newPadId = "1$"+localPadId; + + // PAD_SQLMETA + obj.id = newPadId; + sqlobj.deleteRows("PAD_SQLMETA", {id:oldPadId}); + sqlobj.insert("PAD_SQLMETA", obj); + + // PAD_META + var meta = sqlbase.getJSON("PAD_META", oldPadId); + meta.padId = newPadId; + sqlbase.deleteJSON("PAD_META", oldPadId); + sqlbase.putJSON("PAD_META", newPadId, meta); + + // PAD_APOOL + var apool = sqlbase.getJSON("PAD_APOOL", oldPadId); + sqlbase.deleteJSON("PAD_APOOL", oldPadId); + sqlbase.putJSON("PAD_APOOL", newPadId, apool); + + function renamePadInStringArrayTable(arrayName) { + var stmnt = "UPDATE "+btquote("PAD_"+arrayName.toUpperCase()+"_META")+ + " SET "+btquote("ID")+" = ? WHERE "+btquote("ID")+" = ?"; + return withConnection(function(conn) { + var pstmnt = conn.prepareStatement(stmnt); + return closing(pstmnt, function() { + pstmnt.setString(1, newPadId); + pstmnt.setString(2, oldPadId); + pstmnt.executeUpdate(); + }); + }); + } + + renamePadInStringArrayTable("revs"); + renamePadInStringArrayTable("chat"); + renamePadInStringArrayTable("revmeta"); + renamePadInStringArrayTable("authors"); + + sqlobj.insert('pro_padmeta', { + localPadId: localPadId, + title: localPadId, + createdDate: obj.creationTime, + domainId: 1 // PNE + }); + } + + renamesSoFar++; + progressBar.update(renamesSoFar/renamesTotal, renamesSoFar+"/"+renamesTotal+" pads"); + }); + + progressBar.finish(); +} + diff --git a/trunk/etherpad/src/etherpad/db_migrations/m0015_padmeta_passwords.js b/trunk/etherpad/src/etherpad/db_migrations/m0015_padmeta_passwords.js new file mode 100644 index 0000000..8fa98bb --- /dev/null +++ b/trunk/etherpad/src/etherpad/db_migrations/m0015_padmeta_passwords.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. + */ + +import("sqlbase.sqlobj"); + +function run() { + sqlobj.addColumns('pro_padmeta', { + password: 'VARCHAR(128) DEFAULT NULL' + }); +} + + diff --git a/trunk/etherpad/src/etherpad/db_migrations/m0016_pne_tracking_data.js b/trunk/etherpad/src/etherpad/db_migrations/m0016_pne_tracking_data.js new file mode 100644 index 0000000..abcc93f --- /dev/null +++ b/trunk/etherpad/src/etherpad/db_migrations/m0016_pne_tracking_data.js @@ -0,0 +1,35 @@ +/** + * 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. + */ + +import("sqlbase.sqlobj"); +import("etherpad.utils.isPrivateNetworkEdition"); + + +function run() { + if (isPrivateNetworkEdition()) { + return; + } + + sqlobj.createTable('pne_tracking_data', { + id: sqlobj.getIdColspec(), + date: sqlobj.getDateColspec("NOT NULL"), + keyHash: 'VARCHAR(128) DEFAULT NULL', + name: 'VARCHAR(128) NOT NULL', + value: 'VARCHAR(1024) NOT NULL' + }); +} + + diff --git a/trunk/etherpad/src/etherpad/db_migrations/m0017_pne_tracking_data_v2.js b/trunk/etherpad/src/etherpad/db_migrations/m0017_pne_tracking_data_v2.js new file mode 100644 index 0000000..1067840 --- /dev/null +++ b/trunk/etherpad/src/etherpad/db_migrations/m0017_pne_tracking_data_v2.js @@ -0,0 +1,30 @@ +/** + * 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. + */ + +import("sqlbase.sqlobj"); +import("etherpad.utils.isPrivateNetworkEdition"); + +function run() { + if (isPrivateNetworkEdition()) { + return; + } + + sqlobj.addColumns('pne_tracking_data', { + remoteIp: 'VARCHAR(128) NOT NULL' + }); +} + + diff --git a/trunk/etherpad/src/etherpad/db_migrations/m0018_eepnet_checkout_tables.js b/trunk/etherpad/src/etherpad/db_migrations/m0018_eepnet_checkout_tables.js new file mode 100644 index 0000000..6e10000 --- /dev/null +++ b/trunk/etherpad/src/etherpad/db_migrations/m0018_eepnet_checkout_tables.js @@ -0,0 +1,82 @@ +/** + * 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. + */ + +import("etherpad.utils.isPrivateNetworkEdition"); +import("sqlbase.sqlobj"); + +function run() { + if (isPrivateNetworkEdition()) { + return; + } + + var idColspec = "INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY"; + + sqlobj.createTable('checkout_purchase', { + id: idColspec, + invoiceId: "INT NOT NULL", + owner: "VARCHAR(128) NOT NULL", + email: "VARCHAR(128) NOT NULL", + organization: "VARCHAR(128) NOT NULL", + firstName: "VARCHAR(100) NOT NULL", + lastName: "VARCHAR(100) NOT NULL", + addressLine1: "VARCHAR(100) NOT NULL", + addressLine2: "VARCHAR(100) NOT NULL", + city: "VARCHAR(40) NOT NULL", + state: "VARCHAR(2) NOT NULL", + zip: "VARCHAR(10) NOT NULL", + numUsers: "INT NOT NULL", + date: "TIMESTAMP NOT NULL", + cents: "INT NOT NULL", + referral: "VARCHAR(8)", + receiptEmail: "TEXT", + purchaseType: "ENUM('creditcard', 'invoice', 'paypal') NOT NULL", + licenseKey: "VARCHAR(1024)" + }, { + email: true, + invoiceId: true + }); + + sqlobj.createTable('checkout_referral', { + id: "VARCHAR(8) NOT NULL PRIMARY KEY", + productPctDiscount: "INT", + supportPctDiscount: "INT", + totalPctDiscount: "INT", + freeUsersCount: "INT", + freeUsersPct: "INT" + }); + + // add a sample referral code. + sqlobj.insert('checkout_referral', { + id: 'EPCO6128', + productPctDiscount: 50, + supportPctDiscount: 25, + totalPctDiscount: 15, + freeUsersCount: 20, + freeUsersPct: 10 + }); + + // add a "free" referral code. + sqlobj.insert('checkout_referral', { + id: 'EP99FREE', + totalPctDiscount: 99 + }); + + sqlobj.insert('checkout_referral', { + id: 'EPFREE68', + totalPctDiscount: 100 + }); + +} diff --git a/trunk/etherpad/src/etherpad/db_migrations/m0019_padmeta_deleted.js b/trunk/etherpad/src/etherpad/db_migrations/m0019_padmeta_deleted.js new file mode 100644 index 0000000..1f9ecbb --- /dev/null +++ b/trunk/etherpad/src/etherpad/db_migrations/m0019_padmeta_deleted.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. + */ + +import("sqlbase.sqlobj"); + +function run() { + sqlobj.addColumns('pro_padmeta', { + isDeleted: sqlobj.getBoolColspec("NOT NULL DEFAULT 0") + }); +} + diff --git a/trunk/etherpad/src/etherpad/db_migrations/m0020_padmeta_archived.js b/trunk/etherpad/src/etherpad/db_migrations/m0020_padmeta_archived.js new file mode 100644 index 0000000..a776622 --- /dev/null +++ b/trunk/etherpad/src/etherpad/db_migrations/m0020_padmeta_archived.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. + */ + +import("sqlbase.sqlobj"); + +function run() { + sqlobj.addColumns('pro_padmeta', { + isArchived: sqlobj.getBoolColspec("NOT NULL DEFAULT 0") + }); +} + + diff --git a/trunk/etherpad/src/etherpad/db_migrations/m0021_pro_padmeta_json.js b/trunk/etherpad/src/etherpad/db_migrations/m0021_pro_padmeta_json.js new file mode 100644 index 0000000..9f357b7 --- /dev/null +++ b/trunk/etherpad/src/etherpad/db_migrations/m0021_pro_padmeta_json.js @@ -0,0 +1,57 @@ +/** + * 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. + */ + +import("fastJSON"); +import("sqlbase.sqlobj"); +import("sqlbase.sqlcommon"); + +function run() { + sqlobj.addColumns('pro_padmeta', { + proAttrsJson: sqlobj.getLongtextColspec("") + }); + + // convert all existing columns into metaJSON + + sqlcommon.inTransaction(function() { + var records = sqlobj.selectMulti('pro_padmeta', {}, {}); + records.forEach(function(r) { + migrateRecord(r); + }); + }); +} + +function migrateRecord(r) { + var editors = []; + if (r.creatorId) { + editors.push(r.creatorId); + } + if (r.lastEditorId) { + if (editors.indexOf(r.lastEditorId) < 0) { + editors.push(r.lastEditorId); + } + } + editors.sort(); + + var proAttrs = { + editors: editors, + }; + + var proAttrsJson = fastJSON.stringify(proAttrs); + + sqlobj.update('pro_padmeta', {id: r.id}, {proAttrsJson: proAttrsJson}); +} + + diff --git a/trunk/etherpad/src/etherpad/db_migrations/m0022_create_userids_table.js b/trunk/etherpad/src/etherpad/db_migrations/m0022_create_userids_table.js new file mode 100644 index 0000000..23ca8d3 --- /dev/null +++ b/trunk/etherpad/src/etherpad/db_migrations/m0022_create_userids_table.js @@ -0,0 +1,30 @@ +/** + * 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. + */ + +import("etherpad.utils.isPrivateNetworkEdition"); +import("sqlbase.sqlobj"); + +function run() { + if (isPrivateNetworkEdition()) { + return; + } + + sqlobj.createTable('pad_cookie_userids', { + id: "VARCHAR(40) NOT NULL PRIMARY KEY", + createdDate: sqlobj.getDateColspec("NOT NULL"), + lastActiveDate: sqlobj.getDateColspec("NOT NULL") + }); +} \ No newline at end of file diff --git a/trunk/etherpad/src/etherpad/db_migrations/m0023_create_usagestats_table.js b/trunk/etherpad/src/etherpad/db_migrations/m0023_create_usagestats_table.js new file mode 100644 index 0000000..927cdc9 --- /dev/null +++ b/trunk/etherpad/src/etherpad/db_migrations/m0023_create_usagestats_table.js @@ -0,0 +1,32 @@ +/** + * 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. + */ + +import("etherpad.utils.isPrivateNetworkEdition"); +import("sqlbase.sqlobj"); +import("sqlbase.sqlcommon"); + +function run() { + if (isPrivateNetworkEdition()) { + return; + } + + sqlobj.createTable('usage_stats', { + id: 'INT NOT NULL '+sqlcommon.autoIncrementClause()+' PRIMARY KEY', + name: 'VARCHAR(128) NOT NULL', + timestamp: 'INT NOT NULL', + value: 'INT NOT NULL' + }); +} diff --git a/trunk/etherpad/src/etherpad/db_migrations/m0024_statistics_table.js b/trunk/etherpad/src/etherpad/db_migrations/m0024_statistics_table.js new file mode 100644 index 0000000..9d6e58c --- /dev/null +++ b/trunk/etherpad/src/etherpad/db_migrations/m0024_statistics_table.js @@ -0,0 +1,42 @@ +/** + * 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. + */ + +import("etherpad.utils.isPrivateNetworkEdition"); +import("sqlbase.sqlobj"); +import("sqlbase.sqlcommon"); +import("fastJSON"); + +function run() { + if (isPrivateNetworkEdition()) { + return; + } + + sqlobj.createTable('statistics', { + id: 'INT NOT NULL '+sqlcommon.autoIncrementClause()+' PRIMARY KEY', + name: 'VARCHAR(128) NOT NULL', + timestamp: 'INT NOT NULL', + value: 'TEXT NOT NULL' + }); + + var oldStats = sqlobj.selectMulti('usage_stats', {}); + oldStats.forEach(function(stat) { + sqlobj.insert('statistics', { + timestamp: stat.timestamp, + name: stat.name, + value: fastJSON.stringify({value: stat.value}) + }); + }); +} diff --git a/trunk/etherpad/src/etherpad/db_migrations/m0025_rename_pro_users_table.js b/trunk/etherpad/src/etherpad/db_migrations/m0025_rename_pro_users_table.js new file mode 100644 index 0000000..a429f41 --- /dev/null +++ b/trunk/etherpad/src/etherpad/db_migrations/m0025_rename_pro_users_table.js @@ -0,0 +1,26 @@ +/** + * 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. + */ + +import("sqlbase.sqlobj"); +import("sqlbase.sqlcommon"); + +function run() { + sqlobj.renameTable('pro_users', 'pro_accounts'); + sqlobj.renameTable('pro_users_auto_signin', 'pro_accounts_auto_signin'); + sqlobj.changeColumn('pro_accounts_auto_signin', 'userId', 'accountId INT UNIQUE NOT NULL'); + sqlobj.createIndex('pro_accounts_auto_signin', ['accountId']); +} + diff --git a/trunk/etherpad/src/etherpad/db_migrations/m0026_create_guests_table.js b/trunk/etherpad/src/etherpad/db_migrations/m0026_create_guests_table.js new file mode 100644 index 0000000..7c41309 --- /dev/null +++ b/trunk/etherpad/src/etherpad/db_migrations/m0026_create_guests_table.js @@ -0,0 +1,37 @@ +/** + * 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. + */ + +import("sqlbase.sqlcommon"); +import("sqlbase.sqlobj"); + +function run() { + if (sqlcommon.doesTableExist("pad_guests")) { + sqlobj.dropTable("pad_guests"); + } + + sqlobj.createTable('pad_guests', { + id: sqlobj.getIdColspec(), + privateKey: 'VARCHAR(63) UNIQUE NOT NULL', + userId: 'VARCHAR(63) UNIQUE NOT NULL', + createdDate: sqlobj.getDateColspec("NOT NULL"), + lastActiveDate: sqlobj.getDateColspec("NOT NULL"), + data: sqlobj.getLongtextColspec("") + }); + + sqlobj.createIndex('pad_guests', ['privateKey']); + sqlobj.createIndex('pad_guests', ['userId']); +} + diff --git a/trunk/etherpad/src/etherpad/db_migrations/m0027_pro_config.js b/trunk/etherpad/src/etherpad/db_migrations/m0027_pro_config.js new file mode 100644 index 0000000..9cbb629 --- /dev/null +++ b/trunk/etherpad/src/etherpad/db_migrations/m0027_pro_config.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. + */ + +import("sqlbase.sqlobj"); + +function run() { + sqlobj.createTable('pro_config', { + id: sqlobj.getIdColspec(), + domainId: 'INT', + name: 'VARCHAR(128)', + jsonVal: sqlobj.getLongtextColspec("") + }); +} + diff --git a/trunk/etherpad/src/etherpad/db_migrations/m0028_ondemand_beta_emails.js b/trunk/etherpad/src/etherpad/db_migrations/m0028_ondemand_beta_emails.js new file mode 100644 index 0000000..f708363 --- /dev/null +++ b/trunk/etherpad/src/etherpad/db_migrations/m0028_ondemand_beta_emails.js @@ -0,0 +1,29 @@ +/** + * 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. + */ + +import("sqlbase.sqlobj"); + +function run() { + sqlobj.createTable('pro_beta_signups', { + id: sqlobj.getIdColspec(), + email: 'VARCHAR(256)', + activationCode: 'VARCHAR(128)', + isActivated: sqlobj.getBoolColspec(), + signupDate: sqlobj.getDateColspec(), + activationDate: sqlobj.getDateColspec() + }); +} + diff --git a/trunk/etherpad/src/etherpad/db_migrations/m0029_lowercase_subdomains.js b/trunk/etherpad/src/etherpad/db_migrations/m0029_lowercase_subdomains.js new file mode 100644 index 0000000..36b76ab --- /dev/null +++ b/trunk/etherpad/src/etherpad/db_migrations/m0029_lowercase_subdomains.js @@ -0,0 +1,31 @@ +/** + * 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. + */ + +import("sqlbase.sqlobj"); + +function run() { + var recordList = sqlobj.selectMulti('pro_domains', {}); + recordList.forEach(function(r) { + var subDomain = r.subDomain; + if (subDomain != subDomain.toLowerCase()) { + // delete this domain record and all accounts associated with it. + sqlobj.deleteRows('pro_domains', {id: r.id}); + sqlobj.deleteRows('pro_accounts', {domainId: r.id}); + } + }); +} + + diff --git a/trunk/etherpad/src/etherpad/db_migrations/m0030_fix_statistics_values.js b/trunk/etherpad/src/etherpad/db_migrations/m0030_fix_statistics_values.js new file mode 100644 index 0000000..aeaa40f --- /dev/null +++ b/trunk/etherpad/src/etherpad/db_migrations/m0030_fix_statistics_values.js @@ -0,0 +1,26 @@ +/** + * 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. + */ + +import("sqlbase.sqlobj"); +import("etherpad.utils.isPrivateNetworkEdition"); + +function run() { + if (isPrivateNetworkEdition()) { + return; + } + + sqlobj.modifyColumn('statistics', 'value', 'MEDIUMTEXT NOT NULL'); +} \ No newline at end of file diff --git a/trunk/etherpad/src/etherpad/db_migrations/m0031_deleted_pro_users.js b/trunk/etherpad/src/etherpad/db_migrations/m0031_deleted_pro_users.js new file mode 100644 index 0000000..b9744a3 --- /dev/null +++ b/trunk/etherpad/src/etherpad/db_migrations/m0031_deleted_pro_users.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. + */ + +import("sqlbase.sqlobj"); + +function run() { + sqlobj.addColumns('pro_accounts', { + isDeleted: sqlobj.getBoolColspec("NOT NULL DEFAULT 0") + }); +} + diff --git a/trunk/etherpad/src/etherpad/db_migrations/m0032_reduce_topvalues_counts.js b/trunk/etherpad/src/etherpad/db_migrations/m0032_reduce_topvalues_counts.js new file mode 100644 index 0000000..5e748f5 --- /dev/null +++ b/trunk/etherpad/src/etherpad/db_migrations/m0032_reduce_topvalues_counts.js @@ -0,0 +1,39 @@ +/** + * 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. + */ + +import("sqlbase.sqlobj"); +import("etherpad.utils.isPrivateNetworkEdition"); +import("fastJSON"); + +import("etherpad.statistics.statistics"); + +function run() { + if (isPrivateNetworkEdition()) { + return; + } + + statistics.getAllStatNames().forEach(function(statName) { + if (statistics.getStatData(statName).dataType == 'topValues') { + var entries = sqlobj.selectMulti('statistics', {name: statName}); + entries.forEach(function(statEntry) { + var value = fastJSON.parse(statEntry.value); + value.topValues = value.topValues.slice(0, 50); + statEntry.value = fastJSON.stringify(value); + sqlobj.update('statistics', {id: statEntry.id}, statEntry); + }); + } + }); +} \ No newline at end of file diff --git a/trunk/etherpad/src/etherpad/db_migrations/m0033_pro_account_usage.js b/trunk/etherpad/src/etherpad/db_migrations/m0033_pro_account_usage.js new file mode 100644 index 0000000..4b33f52 --- /dev/null +++ b/trunk/etherpad/src/etherpad/db_migrations/m0033_pro_account_usage.js @@ -0,0 +1,30 @@ +/** + * 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. + */ + +import("sqlbase.sqlobj"); + +function run() { + sqlobj.createTable('pro_account_usage', { + id: sqlobj.getIdColspec(), + domainId: 'INT NOT NULL UNIQUE', + count: 'INT NOT NULL DEFAULT 0', + lastReset: sqlobj.getDateColspec(), + lastUpdated: sqlobj.getDateColspec() + }); + sqlobj.createIndex('pro_account_usage', ['domainId']); +} + + diff --git a/trunk/etherpad/src/etherpad/db_migrations/m0034_create_recurring_billing_table.js b/trunk/etherpad/src/etherpad/db_migrations/m0034_create_recurring_billing_table.js new file mode 100644 index 0000000..491581b --- /dev/null +++ b/trunk/etherpad/src/etherpad/db_migrations/m0034_create_recurring_billing_table.js @@ -0,0 +1,42 @@ +/** + * 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. + */ + +import("etherpad.utils.isPrivateNetworkEdition"); +import("sqlbase.sqlobj"); + +function run() { + if (isPrivateNetworkEdition()) { + return; + } + + var idColspec = "INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY"; + + sqlobj.createTable('billing_payment_info', { + customer: "INT(11) NOT NULL PRIMARY KEY", + fullname: "VARCHAR(128)", + paymentsummary: "VARCHAR(128)", + expiration: "VARCHAR(6)", // MMYYYY + transaction: "VARCHAR(128)" + }); + + sqlobj.addColumns('billing_purchase', { + error: "TEXT" + }); + + sqlobj.addColumns('billing_invoice', { + users: "INT(11)" + }) +} \ No newline at end of file diff --git a/trunk/etherpad/src/etherpad/db_migrations/m0035_add_email_to_paymentinfo.js b/trunk/etherpad/src/etherpad/db_migrations/m0035_add_email_to_paymentinfo.js new file mode 100644 index 0000000..a49e9f9 --- /dev/null +++ b/trunk/etherpad/src/etherpad/db_migrations/m0035_add_email_to_paymentinfo.js @@ -0,0 +1,28 @@ +/** + * 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. + */ + +import("etherpad.utils.isPrivateNetworkEdition"); +import("sqlbase.sqlobj"); + +function run() { + if (isPrivateNetworkEdition()) { + return; + } + + sqlobj.addColumns('billing_payment_info', { + email: "VARCHAR(255)" + }); +} \ No newline at end of file diff --git a/trunk/etherpad/src/etherpad/db_migrations/m0036_create_missing_subscription_records.js b/trunk/etherpad/src/etherpad/db_migrations/m0036_create_missing_subscription_records.js new file mode 100644 index 0000000..ce77734 --- /dev/null +++ b/trunk/etherpad/src/etherpad/db_migrations/m0036_create_missing_subscription_records.js @@ -0,0 +1,45 @@ +/** + * 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. + */ + +import("dateutils"); +import("etherpad.utils.isPrivateNetworkEdition"); +import("sqlbase.sqlobj"); + +function run() { + if (isPrivateNetworkEdition()) { + return; + } + + var allDomains = sqlobj.selectMulti('pro_domains', {}, {}); + + allDomains.forEach(function(domain) { + var domainId = domain.id; + var accounts = sqlobj.selectMulti('pro_accounts', {domainId: domainId}, {}); + if (accounts.length > 3) { + if (! sqlobj.selectSingle('billing_purchase', {product: "ONDEMAND", customer: domainId}, {})) { + sqlobj.insert('billing_purchase', { + product: "ONDEMAND", + paidThrough: dateutils.noon(new Date(Date.now()-1000*86400)), + type: 'subscription', + customer: domainId, + status: 'inactive', + cost: 0, + coupon: "" + }); + } + } + }); +} \ No newline at end of file diff --git a/trunk/etherpad/src/etherpad/db_migrations/m0037_create_pro_referral_table.js b/trunk/etherpad/src/etherpad/db_migrations/m0037_create_pro_referral_table.js new file mode 100644 index 0000000..7a9982c --- /dev/null +++ b/trunk/etherpad/src/etherpad/db_migrations/m0037_create_pro_referral_table.js @@ -0,0 +1,32 @@ +/** + * 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. + */ + +import("etherpad.utils.isPrivateNetworkEdition"); +import("sqlbase.sqlobj"); + +function run() { + if (isPrivateNetworkEdition()) { + return; + } + + var idColspec = "INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY"; + + sqlobj.createTable('checkout_pro_referral', { + id: "VARCHAR(8) NOT NULL PRIMARY KEY", + pctDiscount: "INT", + freeUsers: "INT", + }); +} \ No newline at end of file diff --git a/trunk/etherpad/src/etherpad/db_migrations/m0038_pad_coarse_revs.js b/trunk/etherpad/src/etherpad/db_migrations/m0038_pad_coarse_revs.js new file mode 100644 index 0000000..1e9a53c --- /dev/null +++ b/trunk/etherpad/src/etherpad/db_migrations/m0038_pad_coarse_revs.js @@ -0,0 +1,26 @@ +/** + * 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. + */ + +import("sqlbase.sqlbase"); + +function run() { + + sqlbase.createStringArrayTable("PAD_REVS10"); + sqlbase.createStringArrayTable("PAD_REVS100"); + sqlbase.createStringArrayTable("PAD_REVS1000"); + +} + diff --git a/trunk/etherpad/src/etherpad/db_migrations/migration_runner.js b/trunk/etherpad/src/etherpad/db_migrations/migration_runner.js new file mode 100644 index 0000000..ddf201d --- /dev/null +++ b/trunk/etherpad/src/etherpad/db_migrations/migration_runner.js @@ -0,0 +1,147 @@ +/** + * 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. + */ + +// Database migrations. + +import("sqlbase.sqlcommon"); +import("sqlbase.sqlobj"); + +import("etherpad.globals.*"); +import("etherpad.utils.*"); +import("etherpad.log"); +import("etherpad.pne.pne_utils"); + +jimport("java.lang.System.out.println"); + +//---------------------------------------------------------------- +// 1 migration per file +//---------------------------------------------------------------- + +var migrations = [ + "m0000_test", + "m0001_eepnet_signups_init", + "m0002_eepnet_signups_2", + "m0003_create_tests_table_v2", + "m0004_convert_all_tables_to_innodb", + "m0005_create_billing_tables", + "m0006_eepnet_signups_3", + "m0007_create_pro_tables_v4", + "m0008_persistent_vars", + "m0009_pad_tables", + "m0010_pad_sqlmeta", + "m0011_pro_users_temppass", + "m0012_pro_users_auto_signin", + "m0013_pne_padv2_upgrade", + "m0014_pne_globalpadids", + "m0015_padmeta_passwords", + "m0016_pne_tracking_data", + "m0017_pne_tracking_data_v2", + "m0018_eepnet_checkout_tables", + "m0019_padmeta_deleted", + "m0020_padmeta_archived", + "m0021_pro_padmeta_json", + "m0022_create_userids_table", + "m0023_create_usagestats_table", + "m0024_statistics_table", + "m0025_rename_pro_users_table", + "m0026_create_guests_table", + "m0027_pro_config", + "m0028_ondemand_beta_emails", + "m0029_lowercase_subdomains", + "m0030_fix_statistics_values", + "m0031_deleted_pro_users", + "m0032_reduce_topvalues_counts", + "m0033_pro_account_usage", + "m0034_create_recurring_billing_table", + "m0035_add_email_to_paymentinfo", + "m0036_create_missing_subscription_records", + "m0037_create_pro_referral_table", + "m0038_pad_coarse_revs" +]; + +var mscope = this; +migrations.forEach(function(m) { + import.call(mscope, "etherpad.db_migrations."+m); +}); + +//---------------------------------------------------------------- + +function dmesg(m) { + if ((!isProduction()) || appjet.cache.db_migrations_print_debug) { + log.info(m); + println(m); + } +} + +function onStartup() { + appjet.cache.db_migrations_print_debug = true; + if (!sqlcommon.doesTableExist("db_migrations")) { + appjet.cache.db_migrations_print_debug = false; + sqlobj.createTable('db_migrations', { + id: 'INT NOT NULL '+sqlcommon.autoIncrementClause()+' PRIMARY KEY', + name: 'VARCHAR(255) NOT NULL UNIQUE', + completed: 'TIMESTAMP' + }); + } + + if (pne_utils.isPNE()) { pne_utils.checkDbVersionUpgrade(); } + runMigrations(); + if (pne_utils.isPNE()) { pne_utils.saveDbVersion(); } +} + +function _migrationName(m) { + m = m.replace(/^m\d+\_/, ''); + m = m.replace(/\_/g, '-'); + return m; +} + +function getCompletedMigrations() { + var completedMigrationsList = sqlobj.selectMulti('db_migrations', {}, {}); + var completedMigrations = {}; + + completedMigrationsList.forEach(function(c) { + completedMigrations[c.name] = true; + }); + + return completedMigrations; +} + +function runMigrations() { + var completedMigrations = getCompletedMigrations(); + + dmesg("Checking for database migrations..."); + migrations.forEach(function(m) { + var name = _migrationName(m); + if (!completedMigrations[name]) { + sqlcommon.inTransaction(function() { + dmesg("performing database migration: ["+name+"]"); + var startTime = +(new Date); + + mscope[m].run(); + + var elapsedMs = +(new Date) - startTime; + dmesg("migration completed in "+elapsedMs+"ms"); + + sqlobj.insert('db_migrations', { + name: name, + completed: new Date() + }); + }); + } + }); +} + + diff --git a/trunk/etherpad/src/etherpad/debug.js b/trunk/etherpad/src/etherpad/debug.js new file mode 100644 index 0000000..069ad14 --- /dev/null +++ b/trunk/etherpad/src/etherpad/debug.js @@ -0,0 +1,26 @@ +/** + * 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. + */ + +import("etherpad.globals.*"); + +jimport("java.lang.System.out.println"); + +function dmesg(m) { + if (!isProduction()) { + println(m); + } +} + diff --git a/trunk/etherpad/src/etherpad/globals.js b/trunk/etherpad/src/etherpad/globals.js new file mode 100644 index 0000000..2bae776 --- /dev/null +++ b/trunk/etherpad/src/etherpad/globals.js @@ -0,0 +1,41 @@ +/** + * 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 variabls +//---------------------------------------------------------------- + +var COMETPATH = "/comet"; + +var COLOR_PALETTE = ['#ffc6c6','#ffe2bf','#fffcbf','#cbffb3','#b3fff1','#c6e7ff','#dcccff','#ffd9fb']; + +function isProduction() { + return (appjet.config['etherpad.isProduction'] == "true"); +} + +var SUPERDOMAINS = { + 'localhost': true, + 'pad.spline.inf.fu-berlin.de': true, + 'pad.spline.de': true, + 'pad.spline.nomad': true +}; + +var PNE_RELEASE_VERSION = "1.1.3"; +var PNE_RELEASE_DATE = "June 15, 2009"; + +var PRO_FREE_ACCOUNTS = 1e9; + + diff --git a/trunk/etherpad/src/etherpad/helpers.js b/trunk/etherpad/src/etherpad/helpers.js new file mode 100644 index 0000000..cafa201 --- /dev/null +++ b/trunk/etherpad/src/etherpad/helpers.js @@ -0,0 +1,276 @@ +/** + * 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. + */ + +import("fastJSON"); +import("jsutils.eachProperty"); +import("faststatic"); +import("comet"); +import("funhtml.META"); + +import("etherpad.globals.*"); +import("etherpad.debug.dmesg"); + +import("etherpad.pro.pro_utils"); + +jimport("java.lang.System.out.println"); + +//---------------------------------------------------------------- +// array that supports contains() in O(1) + +var _UniqueArray = function() { + this._a = []; + this._m = {}; +}; +_UniqueArray.prototype.add = function(x) { + if (!this._m[x]) { + this._a.push(x); + this._m[x] = true; + } +}; +_UniqueArray.prototype.asArray = function() { + return this._a; +}; + +//---------------------------------------------------------------- +// EJS template helpers +//---------------------------------------------------------------- + +function _hd() { + if (!appjet.requestCache.helperData) { + appjet.requestCache.helperData = { + clientVars: {}, + htmlTitle: "", + headExtra: "", + bodyId: "", + bodyClasses: new _UniqueArray(), + cssIncludes: new _UniqueArray(), + jsIncludes: new _UniqueArray(), + includeCometJs: false, + suppressGA: false, + showHeader: true, + robotsPolicy: null + }; + } + return appjet.requestCache.helperData; +} + +function addBodyClass(c) { + _hd().bodyClasses.add(c); +} + +function addClientVars(vars) { + eachProperty(vars, function(k,v) { + _hd().clientVars[k] = v; + }); +} + +function addToHead(stuff) { + _hd().headExtra += stuff; +} + +function setHtmlTitle(t) { + _hd().htmlTitle = t; +} + +function setBodyId(id) { + _hd().bodyId = id; +} + +function includeJs(relpath) { + _hd().jsIncludes.add(relpath); +} + +function includeJQuery() { + includeJs("jquery-1.3.2.js"); +} + +function includeCss(relpath) { + _hd().cssIncludes.add(relpath); +} + +function includeCometJs() { + _hd().includeCometJs = true; +} + +function suppressGA() { + _hd().suppressGA = true; +} + +function hideHeader() { + _hd().showHeader = false; +} + +//---------------------------------------------------------------- +// for rendering HTML +//---------------------------------------------------------------- + +function bodyClasses() { + return _hd().bodyClasses.asArray().join(' '); +} + +function clientVarsScript() { + var x = _hd().clientVars; + x = fastJSON.stringify(x); + if (x == '{}') { + return ''; + } + x = x.replace(/', + ' // ', + '' + ].join('\n'); +} + +function htmlTitle() { + return _hd().htmlTitle; +} + +function bodyId() { + return _hd().bodyId; +} + +function baseHref() { + return request.scheme + "://"+ request.host + "/"; +} + +function headExtra() { + return _hd().headExtra; +} + +function jsIncludes() { + if (isProduction()) { + var jsincludes = _hd().jsIncludes.asArray(); + if (_hd().includeCometJs) { + jsincludes.splice(0, 0, { + getPath: function() { return 'comet-client.js'; }, + getContents: function() { return comet.clientCode(); }, + getMTime: function() { return comet.clientMTime(); } + }); + } + if (jsincludes.length < 1) { return ''; } + var key = faststatic.getCompressedFilesKey('js', '/static/js', jsincludes); + return ''; + } else { + var ts = +(new Date); + var r = []; + if (_hd().includeCometJs) { + r.push(''); + } + _hd().jsIncludes.asArray().forEach(function(relpath) { + r.push(''); + }); + return r.join('\n'); + } +} + +function cssIncludes() { + if (isProduction()) { + var key = faststatic.getCompressedFilesKey('css', '/static/css', _hd().cssIncludes.asArray()); + return ''; + } else { + var ts = +(new Date); + var r = []; + _hd().cssIncludes.asArray().forEach(function(relpath) { + r.push(''); + }); + return r.join('\n'); + } +} + +function oemail(username) { + return '<'+ + username+'@p*d.sp***e.inf.fu-berlin.de>'; +} + +function googleAnalytics() { + // GA disabled always now. + return ''; + + if (!isProduction()) { return ''; } + if (_hd().suppressGA) { return ''; } + return [ + '', + '' + ].join('\n'); +} + +function isHeaderVisible() { + return _hd().showHeader; +} + +function setRobotsPolicy(policy) { + _hd().robotsPolicy = policy; +} +function robotsMeta() { + if (!_hd().robotsPolicy) { return ''; } + var content = ""; + content += (_hd().robotsPolicy.index ? 'INDEX' : 'NOINDEX'); + content += ", "; + content += (_hd().robotsPolicy.follow ? 'FOLLOW' : 'NOFOLLOW'); + return META({name: "ROBOTS", content: content}); +} + +function thawteSiteSeal() { + return [ + '
', + '', + '', + '', + '', + '', + '', + '', + '
', + '', + '
', + '', + '', + 'ABOUT SSL CERTIFICATES', + '', + '
', + '
' + ].join('\n'); +} + +function clearFloats() { + return '
'; +} + +function rafterBlogUrl() { + return '/ep/blog/posts/google-acquires-appjet'; +} + +function rafterNote() { + return """
+ Note: We are no longer accepting new accounts. Read more. +
"""; +} + +function rafterTerminationDate() { + return "March 31, 2010"; +} + diff --git a/trunk/etherpad/src/etherpad/importexport/importexport.js b/trunk/etherpad/src/etherpad/importexport/importexport.js new file mode 100644 index 0000000..304a1f4 --- /dev/null +++ b/trunk/etherpad/src/etherpad/importexport/importexport.js @@ -0,0 +1,241 @@ +/** + * 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. + */ + +jimport("java.io.File"); +jimport("java.io.FileOutputStream"); +jimport("java.lang.System.out.println"); +jimport("java.io.ByteArrayInputStream"); +jimport("java.io.ByteArrayOutputStream"); +jimport("java.io.DataInputStream"); +jimport("java.io.DataOutputStream"); +jimport("net.appjet.common.sars.SarsClient"); +jimport("com.etherpad.openofficeservice.OpenOfficeService"); +jimport("com.etherpad.openofficeservice.UnsupportedFormatException"); +jimport("com.etherpad.openofficeservice.TemporaryFailure"); + +import("etherpad.log"); +import("etherpad.utils"); +import("sync"); +import("execution"); +import("varz"); +import("exceptionutils"); + +function _log(obj) { + log.custom("import-export", obj); +} + +function onStartup() { + execution.initTaskThreadPool("importexport", 1); +} + +var formats = { + pdf: 'application/pdf', + doc: 'application/msword', + html: 'text/html; charset=utf-8', + odt: 'application/vnd.oasis.opendocument.text', + txt: 'text/plain; charset=utf-8' +} + +function _createTempFile(bytes, type) { + var f = File.createTempFile("ooconvert-", (type === null ? null : (type == "" ? "" : "."+type))); + if (bytes) { + var fos = new FileOutputStream(f); + fos.write(bytes); + } + return f; +} + +function _initConverterClient(convertServer) { + if (convertServer) { + var convertHost = convertServer.split(":")[0]; + var convertPort = Number(convertServer.split(":")[1]); + if (! appjet.scopeCache.converter) { + var converter = new SarsClient("ooffice-password", convertHost, convertPort); + appjet.scopeCache.converter = converter; + converter.setConnectTimeout(5000); + converter.setReadTimeout(40000); + appjet.scopeCache.converter.connect(); + } + return appjet.scopeCache.converter; + } else { + return null; + } +} + +function _conversionSarsFailure() { + delete appjet.scopeCache.converter; +} + +function errorUnsupported(from) { + return "Unsupported file type"+(from ? ": "+from+"." : ".")+" Etherpad can only import txt, html, rtf, doc, and docx files."; +} +var errorTemporary = "A temporary failure occurred; please try again later."; + +function doSlowFileConversion(from, to, bytes, continuation) { + var bytes = convertFileSlowly(from, to, bytes); + continuation.resume(); + return bytes; +} + +function _convertOverNetwork(convertServer, from, to, bytes) { + var c = _initConverterClient(convertServer); + var reqBytes = new ByteArrayOutputStream(); + var req = new DataOutputStream(reqBytes); + req.writeUTF(from); + req.writeUTF(to); + req.writeInt(bytes.length); + req.write(bytes, 0, bytes.length); + + var retBtyes; + try { + retBytes = c.message(reqBytes.toByteArray()); + } catch (e) { + if (e.javaException) { + net.appjet.oui.exceptionlog.apply(e.javaException) + } + _conversionSarsFailure(); + return "A communications failure occurred; please try again later."; + } + + if (retBytes.length == 0) { + return "An unknown failure occurred; please try again later. (#5)"; + } + var res = new DataInputStream(new ByteArrayInputStream(retBytes)); + var status = res.readInt(); + if (status == 0) { // success + var len = res.readInt(); + var resBytes = java.lang.reflect.Array.newInstance(java.lang.Byte.TYPE, len); + res.readFully(resBytes); + return resBytes; + } else if (status == 1) { + return errorTemporary; + } else if (status == 2) { + var permFailureCode = res.readInt(); + if (permFailureCode == 0) { + return "An unknown failure occurred. (#1)"; + } else if (permFailureCode == 1) { + return errorUnsupported(from); + } + } else { + return "An unknown failure occurred. (#2)"; + } +} + +function convertFileSlowly(from, to, bytes) { + var convertServer = appjet.config["etherpad.sofficeConversionServer"]; + if (convertServer) { + return _convertOverNetwork(convertServer, from, to, bytes); + } + + if (! utils.hasOffice()) { + return "EtherPad is not configured to import or export formats other than txt and html. Please contact your system administrator for details."; + } + OpenOfficeService.setExecutable(appjet.config["etherpad.soffice"]); + try { + return OpenOfficeService.convertFile(from, to, bytes); + } catch (e) { + if (e.javaException instanceof TemporaryFailure) { + return errorTemporary; + } else if (e.javaException instanceof UnsupportedFormatException) { + return errorUnsupported(from); + } else { + return "An unknown failure occurred. (#3)"; + } + } +} + +function _noteConversionAttempt() { + varz.incrementInt("importexport-conversions-attempted"); +} + +function _noteConversionSuccess() { + varz.incrementInt("importexport-conversions-successful"); +} + +function _noteConversionFailure() { + varz.incrementInt("importexport-conversions-failed"); +} + +function _noteConversionTimeout() { + varz.incrementInt("importexport-conversions-timeout"); +} + +function _noteConversionImpossible() { + varz.incrementInt("importexport-conversions-impossible"); +} + +function precomputedConversionResult(from, to, bytes) { + try { + var retBytes = request.cache.conversionCallable.get(500, java.util.concurrent.TimeUnit.MILLISECONDS); + var delay = Date.now() - request.cache.startTime; + _log({type: "conversion-latency", from: from, to: to, + numBytes: request.cache.conversionByteLength, + delay: delay}); + varz.addToInt("importexport-total-conversion-millis", delay); + if (typeof(retBytes) == 'string') { + _log({type: "error", error: "conversion-failed", from: from, to: to, + numBytes: request.cache.conversionByteLength, + delay: delay}); + _noteConversionFailure(); + } else { + _noteConversionSuccess(); + } + return retBytes; + } catch (e) { + if (e.javaException instanceof java.util.concurrent.TimeoutException) { + _noteConversionTimeout(); + request.cache.conversionCallable.cancel(false); + _log({type: "error", error: "conversion-failed", from: from, to: to, + numBytes: request.cache.conversionByteLength, + delay: -1}); + return "Conversion timed out. Please try again later."; + } + _log({type: "error", error: "conversion-failed", from: from, to: to, + numBytes: request.cache.conversionByteLength, + trace: exceptionutils.getStackTracePlain(e)}); + _noteConversionFailure(); + return "An unknown failure occurred. (#4)"; + } +} + +function convertFile(from, to, bytes) { + if (request.cache.conversionCallable) { + return precomputedConversionResult(from, to, bytes); + } + + _noteConversionAttempt(); + if (from == to) { + _noteConversionSuccess(); + return bytes; + } + if (from == "txt" && to == "html") { + _noteConversionSuccess(); + return (new java.lang.String(utils.renderTemplateAsString('pad/exporthtml.ejs', { + content: String(new java.lang.String(bytes, "UTF-8")).replace(/&/g, "&").replace(/ 0; }); + this.__defineGetter__("lastEvent", function() { + return this.events[this.events.length-1]; + }); + this.__defineGetter__("visits", function() { + if (! visitsCache) { + visitsCache = this.events.filter(function(x) { return x.type == "visit" }); + } + return visitsCache; + }); + startEvent.flow = this; + this.push(startEvent); +} +Flow.prototype.toString = function() { + return "["+this.events.map(function(x) { return x.toString(); }).join(", ")+"]"; +} +Flow.prototype.includesVisit = function(path, index, useExactIndexMatch) { + if (! this.visitedPaths[path]) return false; + if (useExactIndexMatch) { + return this.visitedPaths[path].some(function(x) { return x == index }); + } else { + if (index) { + for (var i = 0; i < this.visitedPaths[path].length; ++i) { + if (this.visitedPaths[path][i] >= index) + return this.visitedPaths[path][i]; + } + return false; + } else { + return true; + } + } +} +Flow.prototype.visitIndices = function(path) { + return this.visitedPaths[path] || []; +} + +function getKeyForDate(date) { + return date.getYear()+":"+date.getMonth()+":"+date.getDay(); +} + +function parseEvents(dates) { + if (! appjet.cache["metrics-events"]) { + appjet.cache["metrics-events"] = {}; + } + var events = {}; + function eventArray(key) { + if (! events[key]) { + events[key] = []; + } + return events[key]; + } + + dates.sort(function(a, b) { return a.getTime() - b.getTime(); }); + dates.forEach(function(day) { + if (! appjet.cache["metrics-events"][getKeyForDate(day)]) { + var daysEvents = {}; + function daysEventArray(key) { + if (! daysEvents[key]) { + daysEvents[key] = []; + } + return daysEvents[key]; + } + var requestLog = frontendLogFileName("request", day); + if (requestLog) { + eachFileLine(requestLog, function(line) { + var s = line.split("\t"); + var sessionKey = s[3]; + if (sessionKey == "-") { return; } + var time = new Date(Number(s[1])); + var path = s[7]; + var referer = (s[9] == "-" ? null : s[9]); + var userAgent = s[10]; + var statusCode = s[5]; + // Remove bots and other automatic or irrelevant requests. + // There's got to be something better than a whitelist. + if (userAgent.indexOf("Mozilla") < 0 && + userAgent.indexOf("Opera") < 0) { + return; + } + if (path == "/favicon.ico") { return; } + daysEventArray(sessionKey).push(new Event(time, "visit", new VisitData(path, referer))); + }); + } + var padEventLog = frontendLogFileName("padevents", day); + if (padEventLog) { + eachFileLine(padEventLog, function(line) { + var s = line.split("\t"); + var sessionKey = s[7]; + if (sessionKey == "-") { return; } + var time = new Date(Number(s[1])); + var padId = s[3]; + var evt = s[2]; + daysEventArray(sessionKey).push(new Event(time, evt, padId)); + }); + } + var chatLog = frontendLogFileName("chat", day); + if (chatLog) { + eachFileLine(chatLog, function(line) { + var s = line.split("\t"); + var sessionKey = s[4]; + if (sessionKey == "-") { return; } + var time = new Date(Number(s[1])); + var padId = s[2]; + daysEventArray(sessionKey).push(new Event(time, "chat", padId)); + }); + } + eachProperty(daysEvents, function(k, v) { + v.sort(function(a, b) { return a.time.getTime() - b.time.getTime()}); + }); + appjet.cache["metrics-events"][getKeyForDate(day)] = daysEvents; + } + eachProperty(appjet.cache["metrics-events"][getKeyForDate(day)], function(k, v) { + Array.prototype.push.apply(eventArray(k), v); + }); + }); + + return events; +} + +function getFlows(startDate, endDate) { + if (! endDate) { endDate = startDate; } + if (! appjet.cache.flows || request.params.clearCache == "1") { + appjet.cache.flows = {}; + } + if (appjet.cache.flows[getKeyForDate(startDate)+"-"+getKeyForDate(endDate)]) { + return appjet.cache.flows[getKeyForDate(startDate)+"-"+getKeyForDate(endDate)]; + } + + var datesForEvents = []; + for (var i = startDate; i.getTime() <= endDate.getTime(); i = new Date(i.getTime()+86400*1000)) { + datesForEvents.push(i); + } + + var events = parseEvents(datesForEvents); + var flows = {}; + + eachProperty(events, function(k, eventArray) { + flows[k] = []; + function lastFlow() { + var f = flows[k]; + if (f.length > 0) { + return f[f.length-1]; + } + } + var lastTime = 0; + eventArray.forEach(function(evt) { + var l = lastFlow(); + + if (l && (l.lastEvent.time.getTime() + _idleTime > evt.time.getTime() || l.isInPad)) { + l.push(evt); + } else { + flows[k].push(new Flow(k, evt)); + } + }); + }); + appjet.cache.flows[getKeyForDate(startDate)+"-"+getKeyForDate(endDate)] = flows; + return flows; +} + +function _uniq(array) { + var seen = {}; + return array.filter(function(x) { + if (seen[x]) { + return false; + } + seen[x] = true; + return true; + }); +} + +function getFunnel(startDate, endDate, pathsArray, useConsecutivePaths) { + var flows = getFlows(startDate, endDate) + + var flowsAtStep = pathsArray.map(function() { return []; }); + eachProperty(flows, function(k, flowArray) { + flowArray.forEach(function(flow) { + if (flow.includesVisit(pathsArray[0])) { + flowsAtStep[0].push({f: flow, i: flow.visitIndices(pathsArray[0])}); + } + }); + }); + for (var i = 0; i < pathsArray.length-1; ++i) { + flowsAtStep[i].forEach(function(fobj) { + var newIndices = fobj.i.map(function(index) { + var nextIndex = + fobj.f.includesVisit(pathsArray[i+1], index+1, useConsecutivePaths); + if (nextIndex !== false) { + return (useConsecutivePaths ? index+1 : nextIndex); + } + }).filter(function(x) { return x !== undefined; }); + if (newIndices.length > 0) { + flowsAtStep[i+1].push({f: fobj.f, i: newIndices}); + } + }); + } + return { + flows: flowsAtStep.map(function(x) { return x.map(function(y) { return y.f; }); }), + visitCounts: flowsAtStep.map(function(x) { return x.length; }), + visitorCounts: flowsAtStep.map(function(x) { + return _uniq(x.map(function(y) { return y.f.sessionKey; })).length + }) + }; +} + +function makeHistogram(array) { + var counts = {}; + for (var i = 0; i < array.length; ++i) { + var value = array[i] + if (! counts[value]) { + counts[value] = 0; + } + counts[value]++; + } + var histogram = []; + eachProperty(counts, function(k, v) { + histogram.push({value: k, count: v, fraction: (v / array.length)}); + }); + histogram.sort(function(a, b) { return b.count - a.count; }); + return histogram; +} + +function getOrigins(startDate, endDate, useReferer, shouldAggregatePads) { + var key = (useReferer ? "referer" : "url"); + var flows = getFlows(startDate, endDate); + + var sessionKeyFirsts = []; + var flowFirsts = []; + eachProperty(flows, function(k, flowArray) { + if (flowArray[0].visits[0] && flowArray[0].visits[0].data && + flowArray[0].visits[0].data[key]) { + var path = flowArray[0].visits[0].data[key]; + sessionKeyFirsts.push( + (shouldAggregatePads && ! useReferer && _isPadUrl(path) ? + "(pad)" : path)); + } + flowArray.forEach(function(flow) { + if (flow.visits[0] && flow.visits[0].data && + flow.visits[0].data[key]) { + var path = flow.visits[0].data[key]; + flowFirsts.push( + (shouldAggregatePads && ! useReferer && _isPadUrl(path) ? + "(pad)" : path)); + } + }); + }); + + if (useReferer) { + flowFirsts = flowFirsts.filter(function(x) { return ! startsWith(x, "http://pad.spline.inf.fu-berlin.de"); }); + sessionKeyFirsts = sessionKeyFirsts.filter(function(x) { return ! startsWith(x, "http://pad.spline.inf.fu-berlin.de"); }); + } + + return { + flowFirsts: makeHistogram(flowFirsts), + sessionKeyFirsts: makeHistogram(sessionKeyFirsts) + } +} + +function getExits(startDate, endDate, src, shouldAggregatePads) { + var flows = getFlows(startDate, endDate); + + var exits = []; + + eachProperty(flows, function(k, flowArray) { + flowArray.forEach(function(flow) { + var indices = flow.visitIndices(src); + for (var i = 0; i < indices.length; ++i) { + if (indices[i]+1 < flow.visits.length) { + if (src != flow.visits[indices[i]+1].data.url) { + exits.push(flow.visits[indices[i]+1]); + } + } else { + exits.push("(nothing)"); + } + } + }); + }); + return { + nextVisits: exits, + histogram: makeHistogram(exits.map(function(x) { + if (typeof(x) == 'string') return x; + return ((! shouldAggregatePads) || ! _isPadUrl(x.data.url) ? + x.data.url : "(pad)" ) + })) + } +} + +jimport("org.jfree.data.general.DefaultPieDataset"); +jimport("org.jfree.chart.plot.PiePlot"); +jimport("org.jfree.chart.ChartUtilities"); +jimport("org.jfree.chart.JFreeChart"); + +function _fToPct(f) { + return Math.round(f*10000)/100; +} + +function _shorten(str) { + if (startsWith(str, "http://")) { + str = str.substring("http://".length); + } + var len = 35; + if (str.length > len) { + return str.substring(0, len-3)+"..." + } else { + return str; + } +} + +function respondWithPieChart(name, histogram) { + var width = 900; + var height = 300; + + var ds = new DefaultPieDataset(); + + var cumulative = 0; + var other = 0; + var otherCount = 0; + histogram.forEach(function(x, i) { + cumulative += x.fraction; + if (cumulative < 0.98 && x.fraction > .01) { + ds.setValue(_shorten(x.value)+"\n ("+x.count+" visits - "+_fToPct(x.fraction)+"%)", x.fraction); + } else { + other += x.fraction; + otherCount += x.count; + } + }); + if (other > 0) { + ds.setValue("Other ("+otherCount + " visits - "+_fToPct(other)+"%)", other); + } + + var piePlot = new PiePlot(ds); + + var chart = new JFreeChart(piePlot); + chart.setTitle(name); + chart.removeLegend(); + + var jos = new java.io.ByteArrayOutputStream(); + ChartUtilities.writeChartAsJPEG( + jos, 1.0, chart, width, height); + + response.setContentType('image/jpeg'); + response.writeBytes(jos.toByteArray()); +} + + + + + + + + + + + + diff --git a/trunk/etherpad/src/etherpad/pad/activepads.js b/trunk/etherpad/src/etherpad/pad/activepads.js new file mode 100644 index 0000000..07f5e2e --- /dev/null +++ b/trunk/etherpad/src/etherpad/pad/activepads.js @@ -0,0 +1,52 @@ +/** + * 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. + */ + +import("jsutils.cmp"); + +jimport("net.appjet.common.util.LimitedSizeMapping"); + +var HISTORY_SIZE = 100; + +function _getMap() { + if (!appjet.cache['activepads']) { + appjet.cache['activepads'] = { + map: new LimitedSizeMapping(HISTORY_SIZE) + }; + } + return appjet.cache['activepads'].map; +} + +function touch(padId) { + _getMap().put(padId, +(new Date)); +} + +function getActivePads() { + var m = _getMap(); + var a = m.listAllKeys().toArray(); + var activePads = []; + for (var i = 0; i < a.length; i++) { + activePads.push({ + padId: a[i], + timestamp: m.get(a[i]) + }); + } + + activePads.sort(function(a,b) { return cmp(b.timestamp,a.timestamp); }); + return activePads; +} + + + diff --git a/trunk/etherpad/src/etherpad/pad/chatarchive.js b/trunk/etherpad/src/etherpad/pad/chatarchive.js new file mode 100644 index 0000000..2f8e33a --- /dev/null +++ b/trunk/etherpad/src/etherpad/pad/chatarchive.js @@ -0,0 +1,67 @@ +/** + * 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. + */ + +import("stringutils"); +import("etherpad.log"); + +jimport("java.lang.System.out.println"); + +function onChatMessage(pad, senderUserInfo, msg) { + pad.appendChatMessage({ + name: senderUserInfo.name, + userId: senderUserInfo.userId, + time: +(new Date), + lineText: msg.lineText + }); +} + +function getRecentChatBlock(pad, howMany) { + var numMessages = pad.getNumChatMessages(); + var firstToGet = Math.max(0, numMessages - howMany); + + return getChatBlock(pad, firstToGet, numMessages); +} + +function getChatBlock(pad, start, end) { + if (start < 0) { + start = 0; + } + if (end > pad.getNumChatMessages()) { + end = pad.getNumChatMessages(); + } + + var historicalAuthorData = {}; + var lines = []; + var block = {start: start, end: end, + historicalAuthorData: historicalAuthorData, + lines: lines}; + + for(var i=start; i state.trueAfter; + } + else return true; +} + +function getWritableStateDescription(state) { + var v = _isDBWritable(); + var restOfMessage = ""; + if (state.trueAfter !== undefined) { + var now = +new Date(); + var then = state.trueAfter; + var diffSeconds = java.lang.String.format("%.1f", Math.abs(now - then)/1000); + if (now < then) { + restOfMessage = " until "+diffSeconds+" seconds from now"; + } + else { + restOfMessage = " since "+diffSeconds+" seconds ago"; + } + } + return v+restOfMessage; +} + +function _dbwriter() { + return appjet.cache.dbwriter; +} + +function onStartup() { + appjet.cache.dbwriter = {}; + var dbwriter = _dbwriter(); + dbwriter.pendingWrites = new ConcurrentHashMap(); + dbwriter.scheduledFor = new ConcurrentHashMap(); // padId --> long + dbwriter.dbWritable = { constant: true }; + + execution.initTaskThreadPool("dbwriter", 4); + // we don't wait for scheduled tasks in the infreq pool to run and complete + execution.initTaskThreadPool("dbwriter_infreq", 1); + + _scheduleCheckForStalePads(); +} + +function _scheduleCheckForStalePads() { + execution.scheduleTask("dbwriter_infreq", "checkForStalePads", AGE_FOR_PAD_FLUSH_MS, []); +} + +function onShutdown() { + log.info("Doing final DB writes before shutdown..."); + var success = execution.shutdownAndWaitOnTaskThreadPool("dbwriter", 10000); + if (! success) { + log.warn("ERROR! DB WRITER COULD NOT SHUTDOWN THREAD POOL!"); + } +} + +function _logException(e) { + var exc = utils.toJavaException(e); + log.warn("writeAllToDB: Error writing to SQL! Written to exceptions.log: "+exc); + log.logException(exc); + exceptionlog.apply(exc); +} + +function taskFlushPad(padId, reason) { + var dbwriter = _dbwriter(); + if (! _isDBWritable()) { + // DB is unwritable, delay + execution.scheduleTask("dbwriter_infreq", "flushPad", DBUNWRITABLE_WRITE_DELAY_MS, [padId, reason]); + return; + } + + model.accessPadGlobal(padId, function(pad) { + writePadNow(pad, true); + }, "r"); + + log.info("taskFlushPad: flushed "+padId+(reason?(" (reason: "+reason+")"):'')); +} + +function taskWritePad(padId) { + var dbwriter = _dbwriter(); + if (! _isDBWritable()) { + // DB is unwritable, delay + dbwriter.scheduledFor.put(padId, (+(new Date)+DBUNWRITABLE_WRITE_DELAY_MS)); + execution.scheduleTask("dbwriter", "writePad", DBUNWRITABLE_WRITE_DELAY_MS, [padId]); + return; + } + + profiler.reset(); + var t1 = profiler.rcb("lock wait"); + model.accessPadGlobal(padId, function(pad) { + t1(); + _dbwriter().pendingWrites.remove(padId); // do this first + + var success = false; + try { + var t2 = profiler.rcb("write"); + writePadNow(pad); + t2(); + + success = true; + } + finally { + if (! success) { + log.warn("DB WRITER FAILED TO WRITE PAD: "+padId); + } + profiler.print(); + } + }, "r"); +} + +function taskCheckForStalePads() { + // do this first + _scheduleCheckForStalePads(); + + if (! _isDBWritable()) return; + + // get "active" pads into an array + var padIter = appjet.cache.pads.meta.keySet().iterator(); + var padList = []; + while (padIter.hasNext()) { padList.push(padIter.next()); } + + var numStale = 0; + + for (var i = 0; i < padList.length; i++) { + if (! _isDBWritable()) break; + var p = padList[i]; + if (model.isPadLockHeld(p)) { + // skip it, don't want to lock up stale pad flusher + } + else { + accessPadGlobal(p, function(pad) { + if (pad.exists()) { + var padAge = (+new Date()) - pad._meta.status.lastAccess; + if (padAge > AGE_FOR_PAD_FLUSH_MS) { + writePadNow(pad, true); + numStale++; + } + } + }, "r"); + } + } + + log.info("taskCheckForStalePads: flushed "+numStale+" stale pads"); +} + +function notifyPadDirty(padId) { + var dbwriter = _dbwriter(); + if (! dbwriter.pendingWrites.containsKey(padId)) { + dbwriter.pendingWrites.put(padId, "pending"); + dbwriter.scheduledFor.put(padId, (+(new Date)+MIN_WRITE_INTERVAL_MS)); + execution.scheduleTask("dbwriter", "writePad", MIN_WRITE_INTERVAL_MS, [padId]); + } +} + +function scheduleFlushPad(padId, reason) { + execution.scheduleTask("dbwriter_infreq", "flushPad", 0, [padId, reason]); +} + +/*function _dbwriterLoopBody(executor) { + try { + var info = writeAllToDB(executor); + if (!info.boring) { + log.info("DB writer: "+info.toSource()); + } + java.lang.Thread.sleep(Math.max(0, MIN_WRITE_INTERVAL_MS - info.elapsed)); + } + catch (e) { + _logException(e); + java.lang.Thread.sleep(MIN_WRITE_INTERVAL_MS); + } +} + +function _startInThread(name, func) { + (new Thread(new Runnable({ + run: function() { + func(); + } + }), name)).start(); +} + +function killDBWriterThreadAndWait() { + appjet.cache.abortDBWriter = true; + while (appjet.cache.runningDBWriter) { + java.lang.Thread.sleep(100); + } +}*/ + +/*function writeAllToDB(executor, andFlush) { + if (!executor) { + executor = new ScheduledThreadPoolExecutor(NUM_WRITER_THREADS); + } + + profiler.reset(); + var startWriteTime = profiler.time(); + var padCount = new AtomicInteger(0); + var writeCount = new AtomicInteger(0); + var removeCount = new AtomicInteger(0); + + // get pads into an array + var padIter = appjet.cache.pads.meta.keySet().iterator(); + var padList = []; + while (padIter.hasNext()) { padList.push(padIter.next()); } + + var latch = new CountDownLatch(padList.length); + + for (var i = 0; i < padList.length; i++) { + _spawnCall(executor, function(p) { + try { + var padWriteResult = {}; + accessPadGlobal(p, function(pad) { + if (pad.exists()) { + padCount.getAndIncrement(); + padWriteResult = writePad(pad, andFlush); + if (padWriteResult.didWrite) writeCount.getAndIncrement(); + if (padWriteResult.didRemove) removeCount.getAndIncrement(); + } + }, "r"); + } catch (e) { + _logException(e); + } finally { + latch.countDown(); + } + }, padList[i]); + } + + // wait for them all to finish + latch.await(); + + var endWriteTime = profiler.time(); + var elapsed = Math.round((endWriteTime - startWriteTime)/1000)/1000; + var interesting = (writeCount.get() > 0 || removeCount.get() > 0); + + var obj = {padCount:padCount.get(), writeCount:writeCount.get(), elapsed:elapsed, removeCount:removeCount.get()}; + if (! interesting) obj.boring = true; + if (interesting) { + profiler.record("writeAll", profiler.time()-startWriteTime); + profiler.print(); + } + + return obj; +}*/ + +function writePadNow(pad, andFlush) { + var didWrite = false; + var didRemove = false; + + if (pad.exists()) { + var dbUpToDate = false; + if (pad._meta.status.dirty) { + /*log.info("Writing pad "+pad.getId());*/ + pad._meta.status.dirty = false; + //var t1 = +new Date(); + pad.writeToDB(); + //var t2 = +new Date(); + didWrite = true; + + //log.info("Wrote pad "+pad.getId()+" in "+(t2-t1)+" ms."); + + var now = +(new Date); + var sched = _dbwriter().scheduledFor.get(pad.getId()); + if (sched) { + var delay = now - sched; + if (delay > MIN_WRITE_DELAY_NOTIFY_MS) { + log.warn("dbwriter["+pad.getId()+"] behind schedule by "+delay+"ms"); + } + _dbwriter().scheduledFor.remove(pad.getId()); + } + } + if (andFlush) { + // remove from cache + model.removeFromMemory(pad); + didRemove = true; + } + } + return {didWrite:didWrite, didRemove:didRemove}; +} + +/*function _spawnCall(executor, func, varargs) { + var args = Array.prototype.slice.call(arguments, 2); + var that = this; + executor.schedule(new Runnable({ + run: function() { + func.apply(that, args); + } + }), 0, TimeUnit.MICROSECONDS); +}*/ + diff --git a/trunk/etherpad/src/etherpad/pad/easysync2migration.js b/trunk/etherpad/src/etherpad/pad/easysync2migration.js new file mode 100644 index 0000000..c2a1523 --- /dev/null +++ b/trunk/etherpad/src/etherpad/pad/easysync2migration.js @@ -0,0 +1,675 @@ +/** + * 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. + */ + + +import("etherpad.collab.ace.easysync1"); +import("etherpad.collab.ace.easysync2"); +import("sqlbase.sqlbase"); +import("fastJSON"); +import("sqlbase.sqlcommon.*"); +import("etherpad.collab.ace.contentcollector.sanitizeUnicode"); + +function _getPadStringArrayNumId(padId, arrayName) { + var stmnt = "SELECT NUMID FROM "+btquote("PAD_"+arrayName.toUpperCase()+"_META")+ + " WHERE ("+btquote("ID")+" = ?)"; + + return withConnection(function(conn) { + var pstmnt = conn.prepareStatement(stmnt); + return closing(pstmnt, function() { + pstmnt.setString(1, padId); + var resultSet = pstmnt.executeQuery(); + return closing(resultSet, function() { + if (! resultSet.next()) { + return -1; + } + return resultSet.getInt(1); + }); + }); + }); +} + +function _getEntirePadStringArray(padId, arrayName) { + var numId = _getPadStringArrayNumId(padId, arrayName); + if (numId < 0) { + return []; + } + + var stmnt = "SELECT PAGESTART, OFFSETS, DATA FROM "+btquote("PAD_"+arrayName.toUpperCase()+"_TEXT")+ + " WHERE ("+btquote("NUMID")+" = ?)"; + + return withConnection(function(conn) { + var pstmnt = conn.prepareStatement(stmnt); + return closing(pstmnt, function() { + pstmnt.setInt(1, numId); + var resultSet = pstmnt.executeQuery(); + return closing(resultSet, function() { + var array = []; + while (resultSet.next()) { + var pageStart = resultSet.getInt(1); + var lengthsString = resultSet.getString(2); + var dataString = resultSet.getString(3); + var dataIndex = 0; + var arrayIndex = pageStart; + lengthsString.split(',').forEach(function(len) { + if (len) { + len = Number(len); + array[arrayIndex] = dataString.substr(dataIndex, len); + dataIndex += len; + } + arrayIndex++; + }); + } + return array; + }); + }); + }); +} + +function _overwriteEntirePadStringArray(padId, arrayName, array) { + var numId = _getPadStringArrayNumId(padId, arrayName); + if (numId < 0) { + // generate numId + withConnection(function(conn) { + var ps = conn.prepareStatement("INSERT INTO "+btquote("PAD_"+arrayName.toUpperCase()+"_META")+ + " ("+btquote("ID")+") VALUES (?)", + java.sql.Statement.RETURN_GENERATED_KEYS); + closing(ps, function() { + ps.setString(1, padId); + ps.executeUpdate(); + var keys = ps.getGeneratedKeys(); + if ((! keys) || (! keys.next())) { + throw new Error("Couldn't generate key for "+arrayName+" table for pad "+padId); + } + closing(keys, function() { + numId = keys.getInt(1); + }); + }); + }); + } + + withConnection(function(conn) { + + var stmnt1 = "DELETE FROM "+btquote("PAD_"+arrayName.toUpperCase()+"_TEXT")+ + " WHERE ("+btquote("NUMID")+" = ?)"; + var pstmnt1 = conn.prepareStatement(stmnt1); + closing(pstmnt1, function() { + pstmnt1.setInt(1, numId); + pstmnt1.executeUpdate(); + }); + + var PAGE_SIZE = 20; + var numPages = Math.floor((array.length-1) / PAGE_SIZE + 1); + + var PAGES_PER_BATCH = 20; + var curPage = 0; + + while (curPage < numPages) { + var stmnt2 = "INSERT INTO "+btquote("PAD_"+arrayName.toUpperCase()+"_TEXT")+ + " ("+btquote("NUMID")+", "+btquote("PAGESTART")+", "+btquote("OFFSETS")+ + ", "+btquote("DATA")+") VALUES (?, ?, ?, ?)"; + var pstmnt2 = conn.prepareStatement(stmnt2); + closing(pstmnt2, function() { + for(var n=0;n 0) { + //P var diffs = []; + //P for(var i=0;i= (c.oldLen() - 1)) { + c[c.length-2] = c.oldLen() - c[c.length-3]; + } + else { + c.push(c.oldLen() - 1, 1, ""); + } + } + + var isExtraNewlineInOutput = false; + if (isExtraNewlineInSource) { + cs[1] += 1; // oldLen ++ + } + if ((cs[cs.length-1] && cs[cs.length-1].slice(-1) != '\n') || + ((! cs[cs.length-1]) && inputText.charAt(cs[cs.length-3] + cs[cs.length-2] - 1) != '\n')) { + // new text won't end with newline! + if (isExtraNewlineInSource) { + keepLastCharacter(cs); + } + else { + cs[cs.length-1] += "\n"; + } + cs[2] += 1; // newLen ++ + isExtraNewlineInOutput = true; + } + + var oldLen = cs.oldLen(); + var newLen = cs.newLen(); + + // final-newline-preserving modifications to changeset {{{ + // These fixes are required for changesets that don't respect the + // new rule that the final newline of the document not be touched, + // and also for changesets tweaked above. It is important that the + // fixed changesets obey all the constraints on version 1 changesets + // so that they may become valid version 2 changesets. + { + function collapsePotentialEmptyLastTake(c) { + if (c[c.length-2] == 0 && c.length > 6) { + if (! c[c.length-1]) { + // last strip doesn't take or insert now + c.length -= 3; + } + else { + // the last two strips should be merged + // e.g. fo\n -> rock\nbar\n: then in this block, + // "Changeset,3,9,0,0,r,1,1,ck,2,0,\nbar" becomes + // "Changeset,3,9,0,0,r,1,1,ck\nbar" + c[c.length-4] += c[c.length-1]; + c.length -= 3; + } + } + } + var lastStripStart = cs[cs.length-3]; + var lastStripTake = cs[cs.length-2]; + var lastStripInsert = cs[cs.length-1]; + if (lastStripStart + lastStripTake == oldLen && lastStripInsert) { + // an insert at end + // e.g. foo\n -> foo\nbar\n: + // "Changeset,4,8,0,4,bar\n" becomes "Changeset,4,8,0,3,\nbar,3,1," + // first make the previous newline part of the insertion + cs[cs.length-2] -= 1; + cs[cs.length-1] = '\n'+cs[cs.length-1].slice(0,-1); + collapsePotentialEmptyLastTake(cs); + keepLastCharacter(cs); + } + else if (lastStripStart + lastStripTake < oldLen && ! lastStripInsert) { + // ends with pure deletion + cs[cs.length-2] -= 1; + collapsePotentialEmptyLastTake(cs); + keepLastCharacter(cs); + } + else if (lastStripStart + lastStripTake < oldLen) { + // ends with replacement + cs[cs.length-1] = cs[cs.length-1].slice(0,-1); + keepLastCharacter(cs); + } + } + // }}} + + var ops = []; + var lastOpcode = ''; + function appendOp(opcode, text, startChar, endChar) { + function num(n) { + return easysync2.Changeset.numToString(n); + } + var lines = 0; + var lastNewlineEnd = startChar; + for (;;) { + var index = text.indexOf('\n', lastNewlineEnd); + if (index < 0 || index >= endChar) { + break; + } + lines++; + lastNewlineEnd = index+1; + } + var a = (opcode == '+' ? attribs : ''); + var multilineChars = (lastNewlineEnd - startChar); + var seqLength = endChar - startChar; + var op = ''; + if (lines > 0) { + op = [a, '|', num(lines), opcode, num(multilineChars)].join(''); + } + if (multilineChars < seqLength) { + op += [a, opcode, num(seqLength - multilineChars)].join(''); + } + if (op) { + // we reorder a single - and a single + + if (opcode == '-' && lastOpcode == '+') { + ops.splice(ops.length-1, 0, op); + } + else { + ops.push(op); + lastOpcode = opcode; + } + } + } + + var oldPos = 0; + + var textPieces = []; + var charBankPieces = []; + cs.eachStrip(function(start, take, insert) { + if (start > oldPos) { + appendOp('-', inputText, oldPos, start); + } + if (take) { + if (start+take < oldLen || insert) { + appendOp('=', inputText, start, start+take); + } + textPieces.push(inputText.substring(start, start+take)); + } + if (insert) { + appendOp('+', insert, 0, insert.length); + textPieces.push(insert); + charBankPieces.push(insert); + } + oldPos = start+take; + }); + // ... and no final deletions after the newline fixing. + + var newCs = easysync2.Changeset.pack(oldLen, newLen, ops.join(''), + sanitizeUnicode(charBankPieces.join(''))); + var newText = textPieces.join(''); + + return [newCs, newText, isExtraNewlineInOutput]; +} + +//////////////////////////////////////////////////////////////////////////////// + +// unicode issues: 5SaYQp7cKV + +// // hard-coded just for testing; any pad is allowed to have corruption. +// var newlineCorruptedPads = [ +// '0OCGFKkjDv', '14dWjOiOxP', '1LL8XQCBjC', '1jMnjEEK6e', '21', +// '23DytOPN7d', '32YzfdT2xS', '3E6GB7l7FZ', '3Un8qaCfJh', '3YAj3rC9em', +// '3vY2eaHSw5', '4834RRTLlg', '4Fm1iVSTWI', '5NpTNqWHGC', '7FYNSdYQVa', +// '7RZCbvgw1z', '8EVpyN6HyY', '8P5mPRxPVr', '8aHFRmLxKR', '8dsj9eGQfP', +// 'BSoGobOJZZ', 'Bf0uVghKy0', 'C2f3umStKd', 'CHlu2CA8F3', 'D2WEwgvg1W', +// 'DNLTpuP2wl', 'DwNpm2TDgu', 'EKPByZ3EGZ', 'FwQxu6UKQx', 'HUn9O34rFl', +// 'JKZhxMo20E', 'JVjuukL42N', 'JVuBlWxaxL', 'Jmw5lPNYcl', 'KnZHz6jE2P', +// 'Luyp6ylbgR', 'MB6lPoN1eI', 'McsCrQUM6c', 'NWIuVobIw9', 'OKERTLQCCn', +// 'OchiOchi', 'OfhKHCB8jJ', 'OkM3Jv3XY9', 'PX5Z89mx29', 'PdmKQIvOEd', +// 'R9NQNB66qt', 'RvULFSvCbV', 'RyLJC6Qo1x', 'SBlKLwr2Ag', 'SavD72Q9P7', +// 'SfXyxseAeF', 'TTGZ4yO2PI', 'U3U7rT3d6w', 'UFmqpQIDAi', 'V7Or0QQk4m', +// 'VPCM5ReAQm', 'VvIYHzIJUY', 'W0Ccc3BVGb', 'Wv3cGgSgjg', 'WwVPgaZUK5', +// 'WyIFUJXfm5', 'XxESEsgQ6R', 'Yc5Yq3WCuU', 'ZRqCFaRx6h', 'ZepX6TLFbD', +// 'bSeImT5po4', 'bqIlTkFDiH', 'btt9vNPSQ9', 'c97YJj8PSN', 'd9YV3sypKF', +// 'eDzzkrwDRU', 'eFQJZWclzo', 'eaz44OhFDu', 'ehKkx1YpLA', 'ep', +// 'foNq3v3e9T', 'form6rooma', 'fqhtIHG0Ii', 'fvZyCRZjv2', 'gZnadICPYV', +// 'gvGXtMKhQk', 'h7AYuTxUOd', 'hc1UZSti3J', 'hrFQtae2jW', 'i8rENUZUMu', +// 'iFW9dceEmh', 'iRNEc8SlOc', 'jEDsDgDlaK', 'jo8ngXlSJh', 'kgJrB9Gh2M', +// 'klassennetz76da2661f8ceccfe74faf97d25a4b418', +// 'klassennetzf06d4d8176d0804697d9650f836cb1f7', 'lDHgmfyiSu', +// 'mA1cbvxFwA', 'mSJpW1th29', 'mXHAqv1Emu', 'monocles12', 'n0NhU3FxxT', +// 'ng7AlzPb5b', 'ntbErnnuyz', 'oVnMO0dX80', 'omOTPVY3Gl', 'p5aNFCfYG9', +// 'pYxjVCILuL', 'phylab', 'pjVBFmnhf1', 'qGohFW3Lbr', 'qYlbjeIHDs', +// 'qgf4OwkFI6', 'qsi', 'rJQ09pRexM', 'snNjlS1aLC', 'tYKC53TDF9', +// 'u1vZmL8Yjv', 'ur4sb7DBJB', 'vesti', 'w9NJegEAZt', 'wDwlSCby2s', +// 'wGFJJRT514', 'wTgEoQGqng', 'xomMZGhius', 'yFEFYWBSvr', 'z7tGFKsGk6', +// 'zIJWNK8Z4i', 'zNMGJYI7hq']; + +// function _time(f) { +// var t1 = +(new Date); +// f(); +// var t2 = +(new Date); +// return t2 - t1; +// } + +// function listAllRevisionCounts() { +// var padList = sqlbase.getAllJSONKeys("PAD_META"); +// //padList.length = 10; +// padList = padList.slice(68000, 68100); +// padList.forEach(function(id) { +// model.accessPadGlobal(id, function(pad) { +// System.out.println((new java.lang.Integer(pad.getHeadRevisionNumber()).toString())+ +// " "+id); +// dbwriter.writePadNow(pad, true); +// }, 'r'); +// }); +// } + +// function verifyAllPads() { +// //var padList = sqlbase.getAllJSONKeys("PAD_META"); +// //padList = newlineCorruptedPads; +// var padList = ['0OCGFKkjDv']; +// //padList = ['form6rooma']; +// //padList.length = 10; +// var numOks = 0; +// var numErrors = 0; +// var numNewlineBugs = 0; +// var longestPad; +// var longestPadTime = -1; +// System.out.println(padList.length+" pads."); +// var totalTime = _time(function() { +// padList.forEach(function(id) { +// model.accessPadGlobal(id, function(pad) { +// var padTime = _time(function() { +// System.out.print(id+"... "); +// try { +// verifyMigration(pad); +// System.out.println("OK ("+(++numOks)+")"); +// } +// catch (e) { +// System.out.println("ERROR ("+(++numErrors)+")"+(e.finalNewlineMissing?" [newline]":"")); +// System.out.println(e.toString()); +// if (e.finalNewlineMissing) { +// numNewlineBugs++; +// } +// } +// }); +// if (padTime > longestPadTime) { +// longestPadTime = padTime; +// longestPad = id; +// } +// }, 'r'); +// }); +// }); +// System.out.println("finished verifyAllPads in "+(totalTime/1000)+" seconds."); +// System.out.println(numOks+" OK"); +// System.out.println(numErrors+" ERROR"); +// System.out.println("Most time-consuming pad: "+longestPad+" / "+longestPadTime+" ms"); +// } + +// function _literal(v) { +// if ((typeof v) == "string") { +// return '"'+v.replace(/[\\\"]/g, '\\$1').replace(/\n/g, '\\n')+'"'; +// } +// else return v.toSource(); +// } + +// function _putFile(str, path) { +// var writer = new java.io.FileWriter(path); +// writer.write(str); +// writer.close(); +// } diff --git a/trunk/etherpad/src/etherpad/pad/exporthtml.js b/trunk/etherpad/src/etherpad/pad/exporthtml.js new file mode 100644 index 0000000..2512603 --- /dev/null +++ b/trunk/etherpad/src/etherpad/pad/exporthtml.js @@ -0,0 +1,383 @@ +/** + * 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. + */ + +import("etherpad.collab.ace.easysync2.Changeset"); + +function getPadPlainText(pad, revNum) { + var atext = ((revNum !== undefined) ? pad.getInternalRevisionAText(revNum) : + pad.atext()); + var textLines = atext.text.slice(0,-1).split('\n'); + var attribLines = Changeset.splitAttributionLines(atext.attribs, atext.text); + var apool = pad.pool(); + + var pieces = []; + for(var i=0;i= 0) { + anumMap[propTrueNum] = i; + } + }); + + function getLineHTML(text, attribs) { + var propVals = [false, false, false]; + var ENTER = 1; + var STAY = 2; + var LEAVE = 0; + + // Use order of tags (b/i/u) as order of nesting, for simplicity + // and decent nesting. For example, + // Just bold Bold and italics Just italics + // becomes + // Just bold Bold and italics Just italics + + var taker = Changeset.stringIterator(text); + var assem = Changeset.stringAssembler(); + + function emitOpenTag(i) { + assem.append('<'); + assem.append(tags[i]); + assem.append('>'); + } + function emitCloseTag(i) { + assem.append(''); + } + + var urls = _findURLs(text); + + var idx = 0; + function processNextChars(numChars) { + if (numChars <= 0) { + return; + } + + var iter = Changeset.opIterator(Changeset.subattribution(attribs, + idx, idx+numChars)); + idx += numChars; + + while (iter.hasNext()) { + var o = iter.next(); + var propChanged = false; + Changeset.eachAttribNumber(o.attribs, function(a) { + if (a in anumMap) { + var i = anumMap[a]; // i = 0 => bold, etc. + if (! propVals[i]) { + propVals[i] = ENTER; + propChanged = true; + } + else { + propVals[i] = STAY; + } + } + }); + for(var i=0;i=0; i--) { + if (propVals[i] === LEAVE) { + emitCloseTag(i); + propVals[i] = false; + } + else if (propVals[i] === STAY) { + emitCloseTag(i); + } + } + for(var i=0; i=0; i--) { + if (propVals[i]) { + emitCloseTag(i); + propVals[i] = false; + } + } + } // end processNextChars + + if (urls) { + urls.forEach(function(urlData) { + var startIndex = urlData[0]; + var url = urlData[1]; + var urlLength = url.length; + processNextChars(startIndex - idx); + assem.append(''); + processNextChars(urlLength); + assem.append(''); + }); + } + processNextChars(text.length - idx); + + return _processSpaces(assem.toString()); + } // end getLineHTML + + var pieces = []; + + // Need to deal with constraints imposed on HTML lists; can + // only gain one level of nesting at once, can't change type + // mid-list, etc. + // People might use weird indenting, e.g. skip a level, + // so we want to do something reasonable there. We also + // want to deal gracefully with blank lines. + var lists = []; // e.g. [[1,'bullet'], [3,'bullet'], ...] + for(var i=0;i 0) { + // do list stuff + var whichList = -1; // index into lists or -1 + if (line.listLevel) { + whichList = lists.length; + for(var j=lists.length-1;j>=0;j--) { + if (line.listLevel <= lists[j][0]) { + whichList = j; + } + } + } + + if (whichList >= lists.length) { + lists.push([line.listLevel, line.listTypeName]); + pieces.push('
  • ', lineContent || '
    '); + } + else if (whichList == -1) { + if (line.text) { + // non-blank line, end all lists + pieces.push(new Array(lists.length+1).join('
  • ')); + lists.length = 0; + pieces.push(lineContent, ''); + } + else { + pieces.push('
    '); + } + } + else { + while (whichList < lists.length-1) { + pieces.push(''); + lists.length--; + } + pieces.push('
  • ', lineContent || '
    '); + } + } + else { + pieces.push(lineContent, ''); + } + } + pieces.push(new Array(lists.length+1).join('
  • ')); + + return pieces.join(''); +} + +function _analyzeLine(text, aline, apool) { + var line = {}; + + // identify list + var lineMarker = 0; + line.listLevel = 0; + if (aline) { + var opIter = Changeset.opIterator(aline); + if (opIter.hasNext()) { + var listType = Changeset.opAttributeValue(opIter.next(), 'list', apool); + if (listType) { + lineMarker = 1; + listType = /([a-z]+)([12345678])/.exec(listType); + if (listType) { + line.listTypeName = listType[1]; + line.listLevel = Number(listType[2]); + } + } + } + } + if (lineMarker) { + line.text = text.substring(1); + line.aline = Changeset.subattribution(aline, 1); + } + else { + line.text = text; + line.aline = aline; + } + + return line; +} + +function getPadHTMLDocument(pad, revNum, noDocType) { + var head = (noDocType?'':'\n')+ + '\n'+ + (noDocType?'': + '\n'+ + '\n'+ + '\n'+ + ''+'/'+pad.getId()+'\n'+ + '\n' + + '\n')+ + ''; + + var foot = '\n\n'; + + return head + getPadHTML(pad, revNum) + foot; +} + +function _escapeHTML(s) { + var re = /[&<>]/g; + if (! re.MAP) { + // persisted across function calls! + re.MAP = { + '&': '&', + '<': '<', + '>': '>', + }; + } + return s.replace(re, function(c) { return re.MAP[c]; }); +} + +// copied from ACE +function _processSpaces(s) { + var doesWrap = true; + if (s.indexOf("<") < 0 && ! doesWrap) { + // short-cut + return s.replace(/ /g, ' '); + } + 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] = ' '; + endOfLine = false; + beforeSpace = true; + } + else if (p.charAt(0) != "<") { + endOfLine = false; + beforeSpace = false; + } + } + // beginning of line is nbsp + for(var i=0;i 0 && + ((typeof topNode.children[topNode.children.length-1]) == "string")) { + // coallesce + topNode.children.push(topNode.children.pop() + v); + } + else { + topNode.children.push(v); + } + } + } + else if (t == ')') { + topNode = nodeStack.pop(); + } + }); + + return bodyNode; +} + +function _trimDomNode(n) { + function isWhitespace(str) { + return /^\s*$/.test(str); + } + function trimBeginningOrEnd(n, endNotBeginning) { + var cc = n.children; + var backwards = endNotBeginning; + if (cc) { + var i = (backwards ? cc.length-1 : 0); + var done = false; + var hitActualText = false; + while (! done) { + if (! (backwards ? (i >= 0) : (i < cc.length-1))) { + done = true; + } + else { + var c = cc[i]; + if ((typeof c) == "string") { + if (! isWhitespace(c)) { + // actual text + hitActualText = true; + break; + } + else { + // whitespace + cc[i] = ''; + } + } + else { + // recurse + if (trimBeginningOrEnd(cc[i], endNotBeginning)) { + hitActualText = true; + break; + } + } + i += (backwards ? -1 : 1); + } + } + n.children = n.children.filter(function(x) { return !!x; }); + return hitActualText; + } + return false; + } + trimBeginningOrEnd(n, false); + trimBeginningOrEnd(n, true); +} + +function htmlToAText(html, apool) { + var body = _htmlBody2js(html); + _trimDomNode(body); + + var dom = { + isNodeText: function(n) { + return (typeof n) == "string"; + }, + nodeTagName: function(n) { + return ((typeof n) == "object") && n.name; + }, + nodeValue: function(n) { + return String(n); + }, + nodeNumChildren: function(n) { + return (((typeof n) == "object") && n.children && n.children.length) || 0; + }, + nodeChild: function(n, i) { + return (((typeof n) == "object") && n.children && n.children[i]) || null; + }, + nodeProp: function(n, p) { + return (((typeof n) == "object") && n.attrs && n.attrs[p]) || null; + }, + nodeAttr: function(n, a) { + return (((typeof n) == "object") && n.attrs && n.attrs[a]) || null; + }, + optNodeInnerHTML: function(n) { + return null; + } + } + + var cc = makeContentCollector(true, null, apool, dom); + for(var i=0; i= 0 && x*level == start)) { + return null; + } + + var cs = _getPadStringArray(padId, "revs"+level).getEntry(x); + + if (! cs) { + return null; + } + + return cs; + }, + getSupportsTimeSlider: function() { + if (! ('supportsTimeSlider' in meta)) { + if (padutils.isProPadId(padId)) { + return true; + } + else { + return false; + } + } + else { + return !! meta.supportsTimeSlider; + } + }, + setSupportsTimeSlider: function(v) { + meta.supportsTimeSlider = v; + }, + get _meta() { return meta; } + }; + + try { + padutils.setCurrentPad(padId); + appjet.requestCache.padsAccessing[padId] = pad; + return padFunc(pad); + } + finally { + padutils.clearCurrentPad(); + delete appjet.requestCache.padsAccessing[padId]; + if (meta) { + if (mode != "r") { + meta.status.dirty = true; + } + if (meta.status.dirty) { + dbwriter.notifyPadDirty(padId); + } + } + } + }); + }); +} + +/** + * Call an arbitrary function with no arguments inside an exclusive + * lock on a padId, and return the result. + */ +function doWithPadLock(padId, func) { + var lockName = "document/"+padId; + return sync.doWithStringLock(lockName, func); +} + +function isPadLockHeld(padId) { + var lockName = "document/"+padId; + return GlobalSynchronizer.isHeld(lockName); +} + +/** + * Get pad meta-data object, which is stored in SQL as JSON + * but cached in appjet.cache. Returns null if pad doesn't + * exist at all (does NOT create it). Requires pad lock. + */ +function _getPadMetaData(padId) { + var padMeta = appjet.cache.pads.meta.get(padId); + if (! padMeta) { + // not in cache + padMeta = sqlbase.getJSON("PAD_META", padId); + if (! padMeta) { + // not in SQL + padMeta = null; + } + else { + appjet.cache.pads.meta.put(padId, padMeta); + } + } + return padMeta; +} + +/** + * Sets a pad's meta-data object, such as when creating + * a pad for the first time. Requires pad lock. + */ +function _insertPadMetaData(padId, obj) { + appjet.cache.pads.meta.put(padId, obj); +} + +/** + * Removes a pad's meta data, writing through to the database. + * Used for the rare case of deleting a pad. + */ +function _removePadMetaData(padId) { + appjet.cache.pads.meta.remove(padId); + sqlbase.deleteJSON("PAD_META", padId); +} + +function _getPadAPool(padId) { + var padAPool = appjet.cache.pads.apool.get(padId); + if (! padAPool) { + // not in cache + padAPool = new AttribPool(); + padAPoolJson = sqlbase.getJSON("PAD_APOOL", padId); + if (padAPoolJson) { + // in SQL + padAPool.fromJsonable(padAPoolJson); + } + appjet.cache.pads.apool.put(padId, padAPool); + } + return padAPool; +} + +/** + * Removes a pad's apool data, writing through to the database. + * Used for the rare case of deleting a pad. + */ +function _removePadAPool(padId) { + appjet.cache.pads.apool.remove(padId); + sqlbase.deleteJSON("PAD_APOOL", padId); +} + +/** + * Get an object for a pad that's not persisted in storage, + * e.g. for tracking open connections. Creates object + * if necessary. Requires pad lock. + */ +function _getPadTemp(padId) { + var padTemp = appjet.cache.pads.temp.get(padId); + if (! padTemp) { + padTemp = {}; + appjet.cache.pads.temp.put(padId, padTemp); + } + return padTemp; +} + +/** + * Returns an object with methods for manipulating a string array, where name + * is something like "revs" or "chat". The object must be acquired and used + * all within a pad lock. + */ +function _getPadStringArray(padId, name) { + var padFoo = appjet.cache.pads[name].get(padId); + if (! padFoo) { + padFoo = {}; + // writes go into writeCache, which is authoritative for reads; + // reads cause pages to be read into readCache + padFoo.readCache = {}; + padFoo.writeCache = {}; + appjet.cache.pads[name].put(padId, padFoo); + } + var tableName = "PAD_"+name.toUpperCase(); + var self = { + getEntry: function(idx) { + var n = Number(idx); + if (padFoo.writeCache[n]) return padFoo.writeCache[n]; + if (padFoo.readCache[n]) return padFoo.readCache[n]; + sqlbase.getPageStringArrayElements(tableName, padId, n, padFoo.readCache); + return padFoo.readCache[n]; // null if not present in SQL + }, + setEntry: function(idx, value) { + var n = Number(idx); + var v = String(value); + padFoo.writeCache[n] = v; + }, + getJSONEntry: function(idx) { + var result = self.getEntry(idx); + if (! result) return result; + return fastJSON.parse(String(result)); + }, + setJSONEntry: function(idx, valueObj) { + self.setEntry(idx, fastJSON.stringify(valueObj)); + }, + writeToDB: function() { + sqlbase.putDictStringArrayElements(tableName, padId, padFoo.writeCache); + // copy key-vals of writeCache into readCache + var readCache = padFoo.readCache; + var writeCache = padFoo.writeCache; + for(var p in writeCache) { + readCache[p] = writeCache[p]; + } + padFoo.writeCache = {}; + } + }; + return self; +} + +/** + * Destroy a string array; writes through to the database. Must be + * called within a pad lock. + */ +function _destroyPadStringArray(padId, name) { + appjet.cache.pads[name].remove(padId); + var tableName = "PAD_"+name.toUpperCase(); + sqlbase.clearStringArray(tableName, padId); +} + +/** + * SELECT the row of PAD_SQLMETA for the given pad. Requires pad lock. + */ +function _getPadSqlMeta(padId) { + return sqlobj.selectSingle("PAD_SQLMETA", { id: padId }); +} + +function _writePadSqlMeta(padId, updates) { + sqlobj.update("PAD_SQLMETA", { id: padId }, updates); +} + + +// called from dbwriter +function removeFromMemory(pad) { + // safe to call if all data is written to SQL, otherwise will lose data; + var padId = pad.getId(); + appjet.cache.pads.meta.remove(padId); + appjet.cache.pads.revs.remove(padId); + appjet.cache.pads.revs10.remove(padId); + appjet.cache.pads.revs100.remove(padId); + appjet.cache.pads.revs1000.remove(padId); + appjet.cache.pads.chat.remove(padId); + appjet.cache.pads.revmeta.remove(padId); + appjet.cache.pads.apool.remove(padId); + collab_server.removeFromMemory(pad); +} + + diff --git a/trunk/etherpad/src/etherpad/pad/noprowatcher.js b/trunk/etherpad/src/etherpad/pad/noprowatcher.js new file mode 100644 index 0000000..8eb2a92 --- /dev/null +++ b/trunk/etherpad/src/etherpad/pad/noprowatcher.js @@ -0,0 +1,110 @@ +/** + * 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. + */ + +/* + * noprowatcher keeps track of when a pad has had no pro user + * in it for a certain period of time, after which all guests + * are booted. + */ + +import("etherpad.pad.padutils"); +import("etherpad.collab.collab_server"); +import("etherpad.pad.padusers"); +import("etherpad.pad.pad_security"); +import("etherpad.pad.model"); +import("cache_utils.syncedWithCache"); +import("execution"); +import("etherpad.sessions"); + +function onStartup() { + execution.initTaskThreadPool("noprowatcher", 1); +} + +function getNumProUsers(pad) { + var n = 0; + collab_server.getConnectedUsers(pad).forEach(function(info) { + if (! padusers.isGuest(info.userId)) { + n++; // found a non-guest + } + }); + return n; +} + +var _EMPTY_TIME = 60000; + +function checkPad(padOrPadId) { + if ((typeof padOrPadId) == "string") { + return model.accessPadGlobal(padOrPadId, function(pad) { + return checkPad(pad); + }); + } + var pad = padOrPadId; + + if (! padutils.isProPad(pad)) { + return; // public pad + } + + if (pad.getGuestPolicy() == 'allow') { + return; // public access + } + + if (sessions.isAnEtherpadAdmin()) { + return; + } + + var globalPadId = pad.getId(); + + var numConnections = collab_server.getNumConnections(pad); + var numProUsers = getNumProUsers(pad); + syncedWithCache('noprowatcher.no_pros_since', function(noProsSince) { + if (! numConnections) { + // no connections, clear state and we're done + delete noProsSince[globalPadId]; + } + else if (numProUsers) { + // pro users in pad, so we're not in a span of time with + // no pro users + delete noProsSince[globalPadId]; + } + else { + // no pro users in pad + var since = noProsSince[globalPadId]; + if (! since) { + // no entry in cache, that means last time we checked + // there were still pro users, but now there aren't + noProsSince[globalPadId] = +new Date; + execution.scheduleTask("noprowatcher", "noProWatcherCheckPad", + _EMPTY_TIME+1000, [globalPadId]); + } + else { + // already in a span of time with no pro users + if ((+new Date) - since > _EMPTY_TIME) { + // _EMPTY_TIME milliseconds since we first noticed no pro users + collab_server.bootAllUsersFromPad(pad, "unauth"); + pad_security.revokeAllPadAccess(globalPadId); + } + } + } + }); +} + +function onUserJoin(pad, userInfo) { + checkPad(pad); +} + +function onUserLeave(pad, userInfo) { + checkPad(pad); +} \ No newline at end of file diff --git a/trunk/etherpad/src/etherpad/pad/pad_migrations.js b/trunk/etherpad/src/etherpad/pad/pad_migrations.js new file mode 100644 index 0000000..e81cf63 --- /dev/null +++ b/trunk/etherpad/src/etherpad/pad/pad_migrations.js @@ -0,0 +1,206 @@ +/** + * 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. + */ + +import("sqlbase.sqlobj"); +import("etherpad.pad.model"); +import("etherpad.pad.easysync2migration"); +import("sqlbase.sqlcommon"); +import("sqlbase.sqlobj"); +import("etherpad.log"); +import("etherpad.pne.pne_utils"); +jimport("java.util.concurrent.ConcurrentHashMap"); +jimport("java.lang.System"); +jimport("java.util.ArrayList"); +jimport("java.util.Collections"); + +function onStartup() { + if (! appjet.cache.pad_migrations) { + appjet.cache.pad_migrations = {}; + } + + // this part can be removed when all pads are migrated on pad.spline.inf.fu-berlin.de + //if (! pne_utils.isPNE()) { + // System.out.println("Building cache for live migrations..."); + // initLiveMigration(); + //} +} + +function initLiveMigration() { + + if (! appjet.cache.pad_migrations) { + appjet.cache.pad_migrations = {}; + } + appjet.cache.pad_migrations.doingAnyLiveMigrations = true; + appjet.cache.pad_migrations.doingBackgroundLiveMigrations = true; + appjet.cache.pad_migrations.padMap = new ConcurrentHashMap(); + + // presence of a pad in padMap indicates migration is needed + var padMap = _padMap(); + var migrationsNeeded = sqlobj.selectMulti("PAD_SQLMETA", {version: 1}); + migrationsNeeded.forEach(function(obj) { + padMap.put(String(obj.id), {from: obj.version}); + }); +} + +function _padMap() { + return appjet.cache.pad_migrations.padMap; +} + +function _doingItLive() { + return !! appjet.cache.pad_migrations.doingAnyLiveMigrations; +} + +function checkPadStatus(padId) { + if (! _doingItLive()) { + return "ready"; + } + var info = _padMap().get(padId); + if (! info) { + return "ready"; + } + else if (info.migrating) { + return "migrating"; + } + else { + return "oldversion"; + } +} + +function ensureMigrated(padId, async) { + if (! _doingItLive()) { + return false; + } + + var info = _padMap().get(padId); + if (! info) { + // pad is up-to-date + return false; + } + else if (async && info.migrating) { + // pad is already being migrated, don't wait on the lock + return false; + } + + return model.doWithPadLock(padId, function() { + // inside pad lock... + var info = _padMap().get(padId); + if (!info) { + return false; + } + // migrate from version 1 to version 2 in a transaction + var migrateSucceeded = false; + try { + info.migrating = true; + log.info("Migrating pad "+padId+" from version 1 to version 2..."); + + var success = false; + var whichTry = 1; + while ((! success) && whichTry <= 3) { + success = sqlcommon.inTransaction(function() { + try { + easysync2migration.migratePad(padId); + sqlobj.update("PAD_SQLMETA", {id: padId}, {version: 2}); + return true; + } + catch (e if (e.toString().indexOf("try restarting transaction") >= 0)) { + whichTry++; + return false; + } + }); + if (! success) { + java.lang.Thread.sleep(Math.floor(Math.random()*200)); + } + } + if (! success) { + throw new Error("too many retries"); + } + + migrateSucceeded = true; + log.info("Migrated pad "+padId+"."); + _padMap().remove(padId); + } + finally { + info.migrating = false; + if (! migrateSucceeded) { + log.info("Migration failed for pad "+padId+"."); + throw new Error("Migration failed for pad "+padId+"."); + } + } + return true; + }); +} + +function numUnmigratedPads() { + if (! _doingItLive()) { + return 0; + } + + return _padMap().size(); +} + +////////// BACKGROUND MIGRATIONS + +function _logPadMigration(runnerId, padNumber, padTotal, timeMs, fourCharResult, padId) { + log.custom("pad_migrations", { + runnerId: runnerId, + padNumber: Math.round(padNumber+1), + padTotal: Math.round(padTotal), + timeMs: Math.round(timeMs), + fourCharResult: fourCharResult, + padId: padId}); +} + +function _getNeededMigrationsArrayList(filter) { + var L = new ArrayList(_padMap().keySet()); + for(var i=L.size()-1; i>=0; i--) { + if (! filter(String(L.get(i)))) { + L.remove(i); + } + } + return L; +} + +function runBackgroundMigration(residue, modulus, runnerId) { + var L = _getNeededMigrationsArrayList(function(padId) { + return (padId.charCodeAt(0) % modulus) == residue; + }); + Collections.shuffle(L); + + var totalPads = L.size(); + for(var i=0;i Guest + + // returns either "allow", "ask", or "deny" + var guestPolicy = model.accessPadGlobal(globalPadId, function(p) { + if (!p.exists()) { + return "deny"; + } else { + return p.getGuestPolicy(); + } + }); + + var numProUsers = model.accessPadGlobal(globalPadId, function(pad) { + return noprowatcher.getNumProUsers(pad); + }); + + if (guestPolicy == "allow") { + return; + } + if (guestPolicy == "deny") { + pro_accounts.requireAccount("Guests are not allowed to join that pad. Please sign in."); + } + if (guestPolicy == "ask") { + if (numProUsers < 1) { + pro_accounts.requireAccount("This pad's security policy does not allow guests to join unless an account-holder is connected to the pad."); + } + var userId = padusers.getUserId(); + + // one of {"approved", "denied", undefined} + var knockAnswer = getKnockAnswer(userId, globalPadId); + if (knockAnswer == "approved") { + return; + } else { + var localPadId = padutils.globalToLocalId(globalPadId); + response.redirect('/ep/account/guest-sign-in?padId='+encodeURIComponent(localPadId)); + } + } +} + +function _checkPasswordSecurity(globalPadId) { + if (!getSession().padPasswordAuth) { + getSession().padPasswordAuth = {}; + } + if (getSession().padPasswordAuth[globalPadId] == true) { + return; + } + var domainId = padutils.getDomainId(globalPadId); + var localPadId = globalPadId.split("$")[1]; + + if (stringutils.startsWith(request.path, "/ep/admin/recover-padtext")) { + return; + } + + var p = pro_padmeta.accessProPad(globalPadId, function(propad) { + if (propad.exists()) { + return propad.getPassword(); + } else { + return null; + } + }); + if (p) { + response.redirect('/ep/pad/auth/'+localPadId+'?cont='+encodeURIComponent(request.url)); + } +} + diff --git a/trunk/etherpad/src/etherpad/pad/padevents.js b/trunk/etherpad/src/etherpad/pad/padevents.js new file mode 100644 index 0000000..52b303c --- /dev/null +++ b/trunk/etherpad/src/etherpad/pad/padevents.js @@ -0,0 +1,170 @@ +/** + * 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. + */ + +// src/etherpad/events.js + +import("etherpad.licensing"); +import("etherpad.log"); +import("etherpad.pad.chatarchive"); +import("etherpad.pad.activepads"); +import("etherpad.pad.padutils"); +import("etherpad.sessions"); +import("etherpad.pro.pro_padmeta"); +import("etherpad.pro.pro_pad_db"); +import("etherpad.pad.padusers"); +import("etherpad.pad.pad_security"); +import("etherpad.pad.noprowatcher"); +import("etherpad.collab.collab_server"); +jimport("java.lang.System.out.println"); + +function onNewPad(pad) { + log.custom("padevents", { + type: "newpad", + padId: pad.getId() + }); + pro_pad_db.onCreatePad(pad); +} + +function onDestroyPad(pad) { + log.custom("padevents", { + type: "destroypad", + padId: pad.getId() + }); + pro_pad_db.onDestroyPad(pad); +} + +function onUserJoin(pad, userInfo) { + log.callCatchingExceptions(function() { + + var name = userInfo.name || "unnamed"; + log.custom("padevents", { + type: "userjoin", + padId: pad.getId(), + username: name, + ip: userInfo.ip, + userId: userInfo.userId + }); + activepads.touch(pad.getId()); + licensing.onUserJoin(userInfo); + log.onUserJoin(userInfo.userId); + padusers.notifyActive(); + noprowatcher.onUserJoin(pad, userInfo); + + }); +} + +function onUserLeave(pad, userInfo) { + log.callCatchingExceptions(function() { + + var name = userInfo.name || "unnamed"; + log.custom("padevents", { + type: "userleave", + padId: pad.getId(), + username: name, + ip: userInfo.ip, + userId: userInfo.userId + }); + activepads.touch(pad.getId()); + licensing.onUserLeave(userInfo); + noprowatcher.onUserLeave(pad, userInfo); + + }); +} + +function onUserInfoChange(pad, userInfo) { + log.callCatchingExceptions(function() { + + activepads.touch(pad.getId()); + + }); +} + +function onClientMessage(pad, senderUserInfo, msg) { + var padId = pad.getId(); + activepads.touch(padId); + + if (msg.type == "chat") { + + chatarchive.onChatMessage(pad, senderUserInfo, msg); + + var name = "unnamed"; + if (senderUserInfo.name) { + name = senderUserInfo.name; + } + + log.custom("chat", { + padId: padId, + userId: senderUserInfo.userId, + username: name, + text: msg.lineText + }); + } + else if (msg.type == "padtitle") { + if (msg.title && padutils.isProPadId(pad.getId())) { + pro_padmeta.accessProPad(pad.getId(), function(propad) { + propad.setTitle(String(msg.title).substring(0, 80)); + }); + } + } + else if (msg.type == "padpassword") { + if (padutils.isProPadId(pad.getId())) { + pro_padmeta.accessProPad(pad.getId(), function(propad) { + propad.setPassword(msg.password || null); + }); + } + } + else if (msg.type == "padoptions") { + // options object is a full set of options or just + // some options to change + var opts = msg.options; + var padOptions = pad.getPadOptionsObj(); + if (opts.view) { + if (! padOptions.view) { + padOptions.view = {}; + } + for(var k in opts.view) { + padOptions.view[k] = opts.view[k]; + } + } + if (opts.guestPolicy) { + padOptions.guestPolicy = opts.guestPolicy; + if (opts.guestPolicy == 'deny') { + // boot guests! + collab_server.bootUsersFromPad(pad, "unauth", function(userInfo) { + return padusers.isGuest(userInfo.userId); }).forEach(function(userInfo) { + pad_security.revokePadUserAccess(padId, userInfo.userId); }); + } + } + } + else if (msg.type == "guestanswer") { + if ((! msg.authId) || padusers.isGuest(msg.authId)) { + // not a pro user, forbid. + } + else { + pad_security.answerKnock(msg.guestId, padId, msg.answer); + } + } +} + +function onEditPad(pad, authorId) { + log.callCatchingExceptions(function() { + + pro_pad_db.onEditPad(pad, authorId); + + }); +} + + diff --git a/trunk/etherpad/src/etherpad/pad/padusers.js b/trunk/etherpad/src/etherpad/pad/padusers.js new file mode 100644 index 0000000..f04f0eb --- /dev/null +++ b/trunk/etherpad/src/etherpad/pad/padusers.js @@ -0,0 +1,397 @@ +/** + * 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. + */ + +import("sqlbase.sqlobj"); +import("fastJSON"); +import("stringutils"); +import("jsutils.eachProperty"); +import("sync"); +import("etherpad.sessions"); +import("etherpad.pro.pro_utils"); +import("etherpad.pro.pro_accounts"); +import("etherpad.pro.pro_accounts.getSessionProAccount"); +import("etherpad.pro.domains"); +import("stringutils.randomHash"); + +var _table = cachedSqlTable('pad_guests', 'pad_guests', + ['id', 'privateKey', 'userId'], processGuestRow); +function processGuestRow(row) { + row.data = fastJSON.parse(row.data); +} + +function notifySignIn() { + /*if (pro_accounts.isAccountSignedIn()) { + var proId = getUserId(); + var guestId = _getGuestUserId(); + + var guestUser = _getGuestByKey('userId', guestId); + if (guestUser) { + var mods = {}; + mods.data = guestUser.data; + // associate guest with proId + mods.data.replacement = proId; + // de-associate ET cookie with guest, otherwise + // the ET cookie would provide a semi-permanent way + // to effect changes under the pro account's name! + mods.privateKey = "replaced$"+_randomString(20); + _updateGuest('userId', guestId, mods); + } + }*/ +} + +function notifyActive() { + if (isGuest(getUserId())) { + _updateGuest('userId', getUserId(), {}); + } +} + +function notifyUserData(userData) { + var uid = getUserId(); + if (isGuest(uid)) { + var data = _getGuestByKey('userId', uid).data; + if (userData.name) { + data.name = userData.name; + } + _updateGuest('userId', uid, {data: data}); + } +} + +function getUserId() { + if (pro_accounts.isAccountSignedIn()) { + return "p."+(getSessionProAccount().id); + } + else { + return getGuestUserId(); + } +} + +function getUserName() { + var uid = getUserId(); + if (isGuest(uid)) { + var fromSession = sessions.getSession().guestDisplayName; + return fromSession || _getGuestByKey('userId', uid).data.name || null; + } + else { + return getSessionProAccount().fullName; + } +} + +function getAccountIdForProAuthor(uid) { + if (uid.indexOf("p.") == 0) { + return Number(uid.substring(2)); + } + else { + return -1; + } +} + +function getNameForUserId(uid) { + if (isGuest(uid)) { + return _getGuestByKey('userId', uid).data.name || null; + } + else { + var accountNum = getAccountIdForProAuthor(uid); + if (accountNum < 0) { + return null; + } + else { + return pro_accounts.getAccountById(accountNum).fullName; + } + } +} + +function isGuest(userId) { + return /^g/.test(userId); +} + +function getGuestUserId() { + // cache the userId in the requestCache, + // for efficiency and consistency + var c = appjet.requestCache; + if (c.padGuestUserId === undefined) { + c.padGuestUserId = _computeGuestUserId(); + } + return c.padGuestUserId; +} + +function _getGuestTrackerId() { + // get ET cookie + var tid = sessions.getTrackingId(); + if (tid == '-') { + // no tracking cookie? not a normal request? + return null; + } + + // get domain ID + var domain = "-"; + if (pro_utils.isProDomainRequest()) { + // e.g. "3" + domain = String(domains.getRequestDomainId()); + } + + // combine them + return domain+"$"+tid; +} + +function _insertGuest(obj) { + // only requires 'userId' in obj + + obj.createdDate = new Date; + obj.lastActiveDate = new Date; + if (! obj.data) { + obj.data = {}; + } + if ((typeof obj.data) == "object") { + obj.data = fastJSON.stringify(obj.data); + } + if (! obj.privateKey) { + // private keys must be unique + obj.privateKey = "notracker$"+_randomString(20); + } + + return _table.insert(obj); +} + +function _getGuestByKey(keyColumn, value) { + return _table.getByKey(keyColumn, value); +} + +function _updateGuest(keyColumn, value, obj) { + var obj2 = {}; + eachProperty(obj, function(k,v) { + if (k == "data" && (typeof v) == "object") { + obj2.data = fastJSON.stringify(v); + } + else { + obj2[k] = v; + } + }); + + obj2.lastActiveDate = new Date; + + _table.updateByKey(keyColumn, value, obj2); +} + +function _newGuestUserId() { + return "g."+_randomString(16); +} + +function _computeGuestUserId() { + // always returns some userId + + var privateKey = _getGuestTrackerId(); + + if (! privateKey) { + // no tracking cookie, pretend there is one + privateKey = randomHash(16); + } + + var userFromTracker = _table.getByKey('privateKey', privateKey); + if (userFromTracker) { + // we know this guy + return userFromTracker.userId; + } + + // generate userId + var userId = _newGuestUserId(); + var guest = {userId:userId, privateKey:privateKey}; + var data = {}; + guest.data = data; + + var prefsCookieData = _getPrefsCookieData(); + if (prefsCookieData) { + // found an old prefs cookie with an old userId + var oldUserId = prefsCookieData.userId; + // take the name and preferences + if ('name' in prefsCookieData) { + data.name = prefsCookieData.name; + } + /*['fullWidth','viewZoom'].forEach(function(pref) { + if (pref in prefsCookieData) { + data.prefs[pref] = prefsCookieData[pref]; + } + });*/ + } + + _insertGuest(guest); + return userId; +} + +function _getPrefsCookieData() { + // get userId from old prefs cookie if possible, + // but don't allow modern usernames + + var prefsCookie = request.cookies['prefs']; + if (! prefsCookie) { + return null; + } + if (prefsCookie.charAt(0) != '%') { + return null; + } + try { + var cookieData = fastJSON.parse(unescape(prefsCookie)); + // require one to three digits followed by dot at beginning of userId + if (/^[0-9]{1,3}\./.test(String(cookieData.userId))) { + return cookieData; + } + } + catch (e) { + return null; + } + + return null; +} + +function _randomString(len) { + // use only numbers and lowercase letters + var pieces = []; + for(var i=0;i 0); +} + +function isProPad(pad) { + return isProPadId(pad.getId()); +} + +function getDomainId(globalPadId) { + var parts = globalPadId.split("$"); + if (parts.length < 2) { + return null; + } else { + return Number(parts[0]); + } +} + +function makeValidLocalPadId(str) { + return str.replace(/[^a-zA-Z0-9\-]/g, '-'); +} + +function getProDisplayTitle(localPadId, title) { + if (title) { + return title; + } + if (stringutils.isNumeric(localPadId)) { + return ("Untitled "+localPadId); + } else { + return (localPadId); + } +} + diff --git a/trunk/etherpad/src/etherpad/pad/revisions.js b/trunk/etherpad/src/etherpad/pad/revisions.js new file mode 100644 index 0000000..c7c84e8 --- /dev/null +++ b/trunk/etherpad/src/etherpad/pad/revisions.js @@ -0,0 +1,103 @@ +/** + * 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. + */ + +import("jsutils.cmp"); +import("stringutils"); + +import("etherpad.utils.*"); + +jimport("java.lang.System.out.println"); + +/* revisionList is an array of revisionInfo structures. + * + * Each revisionInfo structure looks like: + * + * { + * timestamp: a number unix timestamp + * label: string + * savedBy: string of author name + * savedById: string id of the author + * revNum: revision number in the edit history + * id: the view id of the (formerly the id of the StorableObject) + * } + */ + +/* returns array */ +function _getRevisionsArray(pad) { + var dataRoot = pad.getDataRoot(); + if (!dataRoot.savedRevisions) { + dataRoot.savedRevisions = []; + } + dataRoot.savedRevisions.sort(function(a,b) { + return cmp(b.timestamp, a.timestamp); + }); + return dataRoot.savedRevisions; +} + +function _getPadRevisionById(pad, savedRevId) { + var revs = _getRevisionsArray(pad); + var rev; + for(var i=0;i (24*60*60*1000)) { + throw new Error("revision is too old to label: "+savedRevId); + }*/ + rev.label = newLabel; +} + +function getStoredRevision(pad, savedRevId) { + return _getPadRevisionById(pad, savedRevId); +} + diff --git a/trunk/etherpad/src/etherpad/pne/pne_utils.js b/trunk/etherpad/src/etherpad/pne/pne_utils.js new file mode 100644 index 0000000..74e0598 --- /dev/null +++ b/trunk/etherpad/src/etherpad/pne/pne_utils.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. + */ + +import("funhtml.*"); +import("stringutils.md5"); +import("sqlbase.persistent_vars"); + +import("etherpad.licensing"); + +jimport("java.lang.System.out.println"); +jimport("java.lang.System"); + + +function isPNE() { + if (appjet.cache.fakePNE || appjet.config['etherpad.fakePNE']) { + return true; + } + if (getVersionString()) { + return true; + } + return false; +} + +/** + * Versioning scheme: we basically just use the apache scheme of MAJOR.MINOR.PATCH: + * + * Versions are denoted using a standard triplet of integers: MAJOR.MINOR.PATCH. The + * basic intent is that MAJOR versions are incompatible, large-scale upgrades of the API. + * MINOR versions retain source and binary compatibility with older minor versions, and + * changes in the PATCH level are perfectly compatible, forwards and backwards. + */ + +function getVersionString() { + return appjet.config['etherpad.pneVersion']; +} + +function parseVersionString(x) { + var parts = x.split('.'); + return { + major: Number(parts[0] || 0), + minor: Number(parts[1] || 0), + patch: Number(parts[2] || 0) + }; +} + +/* returns {major: int, minor: int, patch: int} */ +function getVersionNumbers() { + return parseVersionString(getVersionString()); +} + +function checkDbVersionUpgrade() { + var dbVersionString = persistent_vars.get("db_pne_version"); + var runningVersionString = getVersionString(); + + if (!dbVersionString) { + println("Upgrading to Private Network Edition, version: "+runningVersionString); + return; + } + + var dbVersion = parseVersionString(dbVersionString); + var runningVersion = getVersionNumbers(); + var force = (appjet.config['etherpad.forceDbUpgrade'] == "true"); + + if (!force && (runningVersion.major != dbVersion.major)) { + println("Error: you are attempting to update an EtherPad["+dbVersionString+ + "] database to version ["+runningVersionString+"]. This is not possible."); + println("Exiting..."); + System.exit(1); + } + if (!force && (runningVersion.minor < dbVersion.minor)) { + println("Error: your etherpad database is at a newer version ["+dbVersionString+"] than"+ + " the current running etherpad ["+runningVersionString+"]. Please upgrade to the "+ + " latest version."); + println("Exiting..."); + System.exit(1); + } + if (!force && (runningVersion.minor > (dbVersion.minor + 1))) { + println("\n\nWARNING: you are attempting to upgrade from version "+dbVersionString+" to version "+ + runningVersionString+". It is recommended that you upgrade one minor version at a time."+ + " (The \"minor\" version number is the second number separated by dots. For example,"+ + " if you are running version 1.2, it is recommended that you upgrade to 1.3 and then 1.4 "+ + " instead of going directly from 1.2 to 1.4."); + println("\n\nIf you really want to do this, you can force us to attempt the upgrade with "+ + " the --etherpad.forceDbUpgrade=true flag."); + println("\n\nExiting..."); + System.exit(1); + } + if (runningVersion.minor > dbVersion.minor) { + println("Upgrading database to version "+runningVersionString); + } +} + +function saveDbVersion() { + var dbVersionString = persistent_vars.get("db_pne_version"); + if (getVersionString() != dbVersionString) { + persistent_vars.put('db_pne_version', getVersionString()); + println("Upgraded Private Network Edition version to ["+getVersionString()+"]"); + } +} + +// These are a list of some of the config vars documented in the PNE manual. They are here +// temporarily, until we move them to the PNE config UI. + +var _eepneAllowedConfigVars = [ + 'configFile', + 'etherpad.useMySQL', + 'etherpad.SQL_JDBC_DRIVER', + 'etherpad.SQL_JDBC_URL', + 'etherpad.SQL_PASSWORD', + 'etherpad.SQL_USERNAME', + 'etherpad.adminPass', + 'etherpad.licenseKey', + 'listen', + 'listenSecure', + 'smtpPass', + 'smtpServer', + 'smtpUser', + 'sslKeyPassword', + 'sslKeyStore' +]; + +function isServerLicensed() { + var licenseInfo = licensing.getLicense(); + if (!licenseInfo) { + return false; + } + if (licensing.isVersionTooOld()) { + return false; + } + if (licensing.isExpired()) { + return false; + } + return true; +} + +function enableTrackingAgain() { + delete appjet.cache.noMorePneTracking; +} + +function pneTrackerHtml() { + if (!isPNE()) { + return ""; + } + if (appjet.cache.noMorePneTracking) { + return ""; + } + + var div = DIV({style: "height: 1px; width: 1px; overflow: hidden;"}); + + var licenseInfo = licensing.getLicense(); + var key = null; + if (licenseInfo) { + key = md5(licenseInfo.key).substr(0, 16); + } + + function trackData(name, value) { + var imgurl = "http://pad.spline.inf.fu-berlin.de/ep/tpne/t?"; + if (key) { + imgurl += ("k="+key+"&"); + } + imgurl += (encodeURIComponent(name) + "=" + encodeURIComponent(value)); + div.push(IMG({src: imgurl})); + } + + trackData("ping", "1"); + trackData("dbdriver", appjet.config['etherpad.SQL_JDBC_DRIVER']); + trackData("request.url", request.url); + + appjet.cache.noMorePneTracking = true; + return div; +} + + + diff --git a/trunk/etherpad/src/etherpad/pro/domains.js b/trunk/etherpad/src/etherpad/pro/domains.js new file mode 100644 index 0000000..e56a408 --- /dev/null +++ b/trunk/etherpad/src/etherpad/pro/domains.js @@ -0,0 +1,141 @@ +/** + * 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. + */ + +// Library for managing subDomains + +import("jsutils.*"); +import("sqlbase.sqlobj"); + +import("etherpad.pro.pro_utils"); +import("etherpad.pne.pne_utils"); +import("etherpad.licensing"); + +jimport("java.lang.System.out.println"); + +// reserved domains +var reservedSubdomains = { + 'alpha': 1, + 'beta': 1, + 'blog': 1, + 'comet': 1, + 'diagnostic': 1, + 'forums': 1, + 'forumsdev': 1, + 'staging': 1, + 'web': 1, + 'www': 1 +}; + +function _getCache() { + if (!appjet.cache.pro_domains) { + appjet.cache.pro_domains = { + records: {id: {}, subDomain: {}} + }; + } + return appjet.cache.pro_domains; +} + +function doesSubdomainExist(subDomain) { + if (reservedSubdomains[subDomain]) { + return true; + } + if (getDomainRecordFromSubdomain(subDomain) != null) { + return true; + } + return false; +} + +function _updateCache(locator) { + var record = sqlobj.selectSingle('pro_domains', locator); + var recordCache = _getCache().records; + + if (record) { + // update both maps: recordCache.id, recordCache.subDomain + keys(recordCache).forEach(function(key) { + recordCache[key][record[key]] = record; + }); + } else { + // write false for whatever hit with this locator + keys(locator).forEach(function(key) { + recordCache[key][locator[key]] = false; + }); + } +} + +function getDomainRecord(domainId) { + if (!(domainId in _getCache().records.id)) { + _updateCache({id: domainId}); + } + var record = _getCache().records.id[domainId]; + return (record ? record : null); +} + +function getDomainRecordFromSubdomain(subDomain) { + subDomain = subDomain.toLowerCase(); + if (!(subDomain in _getCache().records.subDomain)) { + _updateCache({subDomain: subDomain}); + } + var record = _getCache().records.subDomain[subDomain]; + return (record ? record : null); +} + +/** returns id of newly created subDomain */ +function createNewSubdomain(subDomain, orgName) { + var id = sqlobj.insert('pro_domains', {subDomain: subDomain, orgName: orgName}); + _updateCache({id: id}); + return id; +} + +function getPrivateNetworkDomainId() { + var r = getDomainRecordFromSubdomain('<>'); + if (!r) { + throw Error("<> does not exist in the domains table!"); + } + return r.id; +} + +/** returns null if not found. */ +function getRequestDomainRecord() { + if (pne_utils.isPNE()) { + var r = getDomainRecord(getPrivateNetworkDomainId()); + if (appjet.cache.fakePNE) { + r.orgName = "fake"; + } else { + var licenseInfo = licensing.getLicense(); + if (licenseInfo) { + r.orgName = licenseInfo.organizationName; + } else { + r.orgName = "Private Network Edition TRIAL"; + } + } + return r; + } else { + var subDomain = pro_utils.getProRequestSubdomain(); + var r = getDomainRecordFromSubdomain(subDomain); + return r; + } +} + +/* throws exception if not pro domain request. */ +function getRequestDomainId() { + var r = getRequestDomainRecord(); + if (!r) { + throw Error("Error getting request domain id."); + } + return r.id; +} + + diff --git a/trunk/etherpad/src/etherpad/pro/pro_account_auto_signin.js b/trunk/etherpad/src/etherpad/pro/pro_account_auto_signin.js new file mode 100644 index 0000000..ebcd227 --- /dev/null +++ b/trunk/etherpad/src/etherpad/pro/pro_account_auto_signin.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. + */ + +import("sqlbase.sqlobj"); +import("stringutils"); + +import("etherpad.pro.pro_accounts"); +import("etherpad.pro.pro_accounts.getSessionProAccount"); + +jimport("java.lang.System.out.println"); + +var _COOKIE_NAME = "PUAS"; + +function dmesg(m) { + if (false) { + println("[pro-account-auto-sign-in]: "+m); + } +} + +function checkAutoSignin() { + dmesg("checking auto sign-in..."); + if (pro_accounts.isAccountSignedIn()) { + dmesg("account already signed in..."); + // don't mess with already signed-in account + return; + } + var cookie = request.cookies[_COOKIE_NAME]; + if (!cookie) { + dmesg("no auto-sign-in cookie found..."); + return; + } + var record = sqlobj.selectSingle('pro_accounts_auto_signin', {cookie: cookie}, {}); + if (!record) { + return; + } + + var now = +(new Date); + if (+record.expires < now) { + sqlobj.deleteRows('pro_accounts_auto_signin', {id: record.id}); + response.deleteCookie(_COOKIE_NAME); + dmesg("deleted expired record..."); + return; + } + // do auto-signin (bypasses normal security) + dmesg("Doing auto sign in..."); + var account = pro_accounts.getAccountById(record.accountId); + pro_accounts.signInSession(account); + response.redirect('/ep/account/sign-in?cont='+encodeURIComponent(request.url)); +} + +function setAutoSigninCookie(rememberMe) { + if (!pro_accounts.isAccountSignedIn()) { + return; // only call this function after account is already signed in. + } + + var accountId = getSessionProAccount().id; + // delete any existing auto-signins for this account. + sqlobj.deleteRows('pro_accounts_auto_signin', {accountId: accountId}); + + // set this insecure cookie just to indicate that account is auto-sign-in-able + response.setCookie({ + name: "ASIE", + value: (rememberMe ? "T" : "F"), + path: "/", + domain: request.domain, + expires: new Date(32503708800000), // year 3000 + }); + + if (!rememberMe) { + return; + } + + var cookie = stringutils.randomHash(16); + var now = +(new Date); + var expires = new Date(now + 1000*60*60*24*30); // 30 days + //var expires = new Date(now + 1000 * 60 * 5); // 2 minutes + + sqlobj.insert('pro_accounts_auto_signin', {cookie: cookie, accountId: accountId, expires: expires}); + response.setCookie({ + name: _COOKIE_NAME, + value: cookie, + path: "/ep/account/", + domain: request.domain, + expires: new Date(32503708800000), // year 3000 + secure: true + }); +} + diff --git a/trunk/etherpad/src/etherpad/pro/pro_accounts.js b/trunk/etherpad/src/etherpad/pro/pro_accounts.js new file mode 100644 index 0000000..2024970 --- /dev/null +++ b/trunk/etherpad/src/etherpad/pro/pro_accounts.js @@ -0,0 +1,496 @@ +/** + * 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. + */ + +// library for pro accounts + +import("funhtml.*"); +import("sqlbase.sqlobj"); +import("sqlbase.sqlcommon.inTransaction"); +import("email.sendEmail"); +import("cache_utils.syncedWithCache"); +import("stringutils.*"); + +import("etherpad.globals.*"); +import("etherpad.sessions"); +import("etherpad.sessions.getSession"); +import("etherpad.utils.*"); +import("etherpad.pro.domains"); +import("etherpad.control.pro.account_control"); +import("etherpad.pro.pro_utils"); +import("etherpad.pro.pro_quotas"); +import("etherpad.pad.padusers"); +import("etherpad.log"); +import("etherpad.billing.team_billing"); + +jimport("org.mindrot.BCrypt"); +jimport("java.lang.System.out.println"); + +function _dmesg(m) { + if (!isProduction()) { + println(m); + } +} + +function _computePasswordHash(p) { + var pwh; + pwh = BCrypt.hashpw(p, BCrypt.gensalt(10)); + return pwh; +} + +function _withCache(name, fn) { + return syncedWithCache('pro_accounts.'+name, fn); +} + +//---------------------------------------------------------------- +// validation +//---------------------------------------------------------------- + +function validateEmail(email) { + if (!email) { return "Email is required."; } + if (!isValidEmail(email)) { return "\""+email+"\" does not look like a valid email address."; } + return null; +} + +function validateFullName(name) { + if (!name) { return "Full name is required."; } + if (name.length < 2) { return "Full name must be at least 2 characters."; } + return null; +} + +function validatePassword(p) { + if (!p) { return "Password is required."; } + if (p.length < 6) { return "Passwords must be at least 6 characters."; } + return null; +} + +function validateEmailDomainPair(email, domainId) { + // TODO: make sure the same email address cannot exist more than once within + // the same domainid. +} + +/* if domainId is null, then use domainId of current request. */ +function createNewAccount(domainId, fullName, email, password, isAdmin) { + if (!domainId) { + domainId = domains.getRequestDomainId(); + } + email = trim(email); + isAdmin = !!isAdmin; // convert to bool + + // validation + var e; + e = validateEmail(email); if (e) { throw Error(e); } + e = validateFullName(fullName); if (e) { throw Error(e); } + e = validatePassword(password); if (e) { throw Error(e); } + + // xss normalization + fullName = toHTML(fullName); + + // make sure account does not already exist on this domain. + var ret = inTransaction(function() { + var existingAccount = getAccountByEmail(email, domainId); + if (existingAccount) { + throw Error("There is already an account with that email address."); + } + // No existing account. Proceed. + var now = new Date(); + var account = { + domainId: domainId, + fullName: fullName, + email: email, + passwordHash: _computePasswordHash(password), + createdDate: now, + isAdmin: isAdmin + }; + return sqlobj.insert('pro_accounts', account); + }); + + _withCache('does-domain-admin-exist', function(cache) { + delete cache[domainId]; + }); + + pro_quotas.updateAccountUsageCount(domainId); + updateCachedActiveCount(domainId); + + if (ret) { + log.custom('pro-accounts', + {type: "account-created", + accountId: ret, + domainId: domainId, + name: fullName, + email: email, + admin: isAdmin}); + } + + return ret; +} + +function _checkAccess(account) { + if (sessions.isAnEtherpadAdmin()) { + return; + } + if (account.domainId != domains.getRequestDomainId()) { + throw Error("access denied"); + } +} + +function setPassword(account, newPass) { + _checkAccess(account); + var passHash = _computePasswordHash(newPass); + sqlobj.update('pro_accounts', {id: account.id}, {passwordHash: passHash}); + markDirtySessionAccount(account.id); +} + +function setTempPassword(account, tempPass) { + _checkAccess(account); + var tempPassHash = _computePasswordHash(tempPass); + sqlobj.update('pro_accounts', {id: account.id}, {tempPassHash: tempPassHash}); + markDirtySessionAccount(account.id); +} + +function setEmail(account, newEmail) { + _checkAccess(account); + sqlobj.update('pro_accounts', {id: account.id}, {email: newEmail}); + markDirtySessionAccount(account.id); +} + +function setFullName(account, newName) { + _checkAccess(account); + sqlobj.update('pro_accounts', {id: account.id}, {fullName: newName}); + markDirtySessionAccount(account.id); +} + +function setIsAdmin(account, newVal) { + _checkAccess(account); + sqlobj.update('pro_accounts', {id: account.id}, {isAdmin: newVal}); + markDirtySessionAccount(account.id); +} + +function setDeleted(account) { + _checkAccess(account); + if (!isNumeric(account.id)) { + throw new Error("Invalid account id: "+account.id); + } + sqlobj.update('pro_accounts', {id: account.id}, {isDeleted: true}); + markDirtySessionAccount(account.id); + pro_quotas.updateAccountUsageCount(account.domainId); + updateCachedActiveCount(account.domainId); + + log.custom('pro-accounts', + {type: "account-deleted", + accountId: account.id, + domainId: account.domainId, + name: account.fullName, + email: account.email, + admin: account.isAdmin, + createdDate: account.createdDate.getTime()}); +} + +//---------------------------------------------------------------- + +function doesAdminExist() { + var domainId = domains.getRequestDomainId(); + return _withCache('does-domain-admin-exist', function(cache) { + if (cache[domainId] === undefined) { + _dmesg("cache miss for doesAdminExist (domainId="+domainId+")"); + var admins = sqlobj.selectMulti('pro_accounts', {domainId: domainId, isAdmin: true}, {}); + cache[domainId] = (admins.length > 0); + } + return cache[domainId] + }); +} + +function getSessionProAccount() { + if (sessions.isAnEtherpadAdmin()) { + return getEtherpadAdminAccount(); + } + var account = getSession().proAccount; + if (!account) { + return null; + } + if (account.isDeleted) { + delete getSession().proAccount; + return null; + } + return account; +} + +function isAccountSignedIn() { + if (getSessionProAccount()) { + return true; + } else { + return false; + } +} + +function isAdminSignedIn() { + return isAccountSignedIn() && getSessionProAccount().isAdmin; +} + +function requireAccount(message) { + if ((request.path == "/ep/account/sign-in") || + (request.path == "/ep/account/sign-out") || + (request.path == "/ep/account/guest-sign-in") || + (request.path == "/ep/account/guest-knock") || + (request.path == "/ep/account/forgot-password")) { + return; + } + + function checkSessionAccount() { + if (!getSessionProAccount()) { + if (message) { + account_control.setSigninNotice(message); + } + response.redirect('/ep/account/sign-in?cont='+encodeURIComponent(request.url)); + } + } + + checkSessionAccount(); + + if (getSessionProAccount().domainId != domains.getRequestDomainId()) { + // This should theoretically never happen unless the account is spoofing cookies / trying to + // hack the site. + pro_utils.renderFramedMessage("Permission denied."); + response.stop(); + } + // update dirty session account if necessary + _withCache('dirty-session-accounts', function(cache) { + var uid = getSessionProAccount().id; + if (cache[uid]) { + reloadSessionAccountData(uid); + cache[uid] = false; + } + }); + + // need to check again in case dirty update caused account to be marked + // deleted. + checkSessionAccount(); +} + +function requireAdminAccount() { + requireAccount(); + if (!getSessionProAccount().isAdmin) { + pro_utils.renderFramedMessage("Permission denied."); + response.stop(); + } +} + +/* returns undefined on success, error string otherise. */ +function authenticateSignIn(email, password) { + var accountRecord = getAccountByEmail(email, null); + if (!accountRecord) { + return "Account not found: "+email; + } + + if (BCrypt.checkpw(password, accountRecord.passwordHash) != true) { + return "Incorrect password. Please try again."; + } + + signInSession(accountRecord); + + return undefined; // success +} + +function signOut() { + delete getSession().proAccount; +} + +function authenticateTempSignIn(uid, tempPass) { + var emsg = "That password reset link that is no longer valid."; + + var account = getAccountById(uid); + if (!account) { + return emsg+" (Account not found.)"; + } + if (account.domainId != domains.getRequestDomainId()) { + return emsg+" (Wrong domain.)"; + } + if (!account.tempPassHash) { + return emsg+" (Expired.)"; + } + if (BCrypt.checkpw(tempPass, account.tempPassHash) != true) { + return emsg+" (Bad temp pass.)"; + } + + signInSession(account); + + getSession().accountMessage = "Please choose a new password"; + getSession().changePass = true; + + response.redirect("/ep/account/"); +} + +function signInSession(account) { + account.lastLoginDate = new Date(); + account.tempPassHash = null; + sqlobj.updateSingle('pro_accounts', {id: account.id}, account); + reloadSessionAccountData(account.id); + padusers.notifySignIn(); +} + +function listAllDomainAccounts(domainId) { + if (domainId === undefined) { + domainId = domains.getRequestDomainId(); + } + var records = sqlobj.selectMulti('pro_accounts', + {domainId: domainId, isDeleted: false}, {}); + return records; +} + +function listAllDomainAdmins(domainId) { + if (domainId === undefined) { + domainId = domains.getRequestDomainId(); + } + var records = sqlobj.selectMulti('pro_accounts', + {domainId: domainId, isDeleted: false, isAdmin: true}, + {}); + return records; +} + +function getActiveCount(domainId) { + var records = sqlobj.selectMulti('pro_accounts', + {domainId: domainId, isDeleted: false}, {}); + return records.length; +} + +/* getAccountById works for deleted and non-deleted accounts. + * The assumption is that cases whewre you look up an account by ID, you + * want the account info even if the account has been deleted. For + * example, when asking who created a pad. + */ +function getAccountById(accountId) { + var r = sqlobj.selectSingle('pro_accounts', {id: accountId}); + if (r) { + return r; + } else { + return undefined; + } +} + +/* getting an account by email only returns the account if it is + * not deleted. The assumption is that when you look up an account by + * email address, you only want active accounts. Furthermore, some + * deleted accounts may match a given email, but only one non-deleted + * account should ever match a single (email,domainId) pair. + */ +function getAccountByEmail(email, domainId) { + if (!domainId) { + domainId = domains.getRequestDomainId(); + } + var r = sqlobj.selectSingle('pro_accounts', {domainId: domainId, email: email, isDeleted: false}); + if (r) { + return r; + } else { + return undefined; + } +} + +function getFullNameById(id) { + if (!id) { + return null; + } + + return _withCache('names-by-id', function(cache) { + if (cache[id] === undefined) { + _dmesg("cache miss for getFullNameById (accountId="+id+")"); + var r = getAccountById(id); + if (r) { + cache[id] = r.fullName; + } else { + cache[id] = false; + } + } + if (cache[id]) { + return cache[id]; + } else { + return null; + } + }); +} + +function getTempSigninUrl(account, tempPass) { + return [ + 'https://', httpsHost(pro_utils.getFullProHost()), '/ep/account/sign-in?', + 'uid=', account.id, '&tp=', tempPass + ].join(''); +} + + +// TODO: this session account object storage / dirty cache is a +// ridiculous hack. What we should really do is have a caching/access +// layer for accounts similar to accessPad() and accessProPadMeta(), and +// have that abstraction take care of caching and marking accounts as +// dirty. This can be incorporated into getSessionProAccount(), and we +// should actually refactor that into accessSessionProAccount(). + +/* will force session data for this account to be updated next time that + * account requests a page. */ +function markDirtySessionAccount(uid) { + var domainId = domains.getRequestDomainId(); + + _withCache('dirty-session-accounts', function(cache) { + cache[uid] = true; + }); + _withCache('names-by-id', function(cache) { + delete cache[uid]; + }); + _withCache('does-domain-admin-exist', function(cache) { + delete cache[domainId]; + }); +} + +function reloadSessionAccountData(uid) { + if (!uid) { + uid = getSessionProAccount().id; + } + getSession().proAccount = getAccountById(uid); +} + +function getAllAccountsWithEmail(email) { + var accountRecords = sqlobj.selectMulti('pro_accounts', {email: email, isDeleted: false}, {}); + return accountRecords; +} + +function getEtherpadAdminAccount() { + return { + id: 0, + isAdmin: true, + fullName: "ETHERPAD ADMIN", + email: "support@pad.spline.inf.fu-berlin.de", + domainId: domains.getRequestDomainId(), + isDeleted: false + }; +} + +function getCachedActiveCount(domainId) { + return _withCache('user-counts.'+domainId, function(c) { + if (!c.count) { + c.count = getActiveCount(domainId); + } + return c.count; + }); +} + +function updateCachedActiveCount(domainId) { + _withCache('user-counts.'+domainId, function(c) { + c.count = getActiveCount(domainId); + }); +} + + + + + + diff --git a/trunk/etherpad/src/etherpad/pro/pro_config.js b/trunk/etherpad/src/etherpad/pro/pro_config.js new file mode 100644 index 0000000..d2d119f --- /dev/null +++ b/trunk/etherpad/src/etherpad/pro/pro_config.js @@ -0,0 +1,92 @@ +/** + * 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. + */ + +import("fastJSON"); +import("sqlbase.sqlobj"); +import("cache_utils.syncedWithCache"); + +import("etherpad.globals.*"); +import("etherpad.utils.*"); +import("etherpad.pro.domains"); +import("etherpad.pro.pro_utils"); + +function _guessSiteName() { + var x = request.host.split('.')[0]; + x = (x.charAt(0).toUpperCase() + x.slice(1)); + return x; +} + +function _getDefaultConfig() { + return { + siteName: _guessSiteName(), + alwaysHttps: false, + defaultPadText: renderTemplateAsString("misc/pad_default.ejs") + }; +} + +// must be fast! gets called per request, on every request. +function getConfig() { + if (!pro_utils.isProDomainRequest()) { + return null; + } + + if (!appjet.cache.pro_config) { + appjet.cache.pro_config = {}; + } + + var domainId = domains.getRequestDomainId(); + if (!appjet.cache.pro_config[domainId]) { + reloadConfig(); + } + + return appjet.cache.pro_config[domainId]; +} + +function reloadConfig() { + var domainId = domains.getRequestDomainId(); + var config = _getDefaultConfig(); + var records = sqlobj.selectMulti('pro_config', {domainId: domainId}, {}); + + records.forEach(function(r) { + var name = r.name; + var val = fastJSON.parse(r.jsonVal).x; + config[name] = val; + }); + + if (!appjet.cache.pro_config) { + appjet.cache.pro_config = {}; + } + + appjet.cache.pro_config[domainId] = config; +} + +function setConfigVal(name, val) { + var domainId = domains.getRequestDomainId(); + var jsonVal = fastJSON.stringify({x: val}); + + var r = sqlobj.selectSingle('pro_config', {domainId: domainId, name: name}); + if (!r) { + sqlobj.insert('pro_config', + {domainId: domainId, name: name, jsonVal: jsonVal}); + } else { + sqlobj.update('pro_config', + {name: name, domainId: domainId}, + {jsonVal: jsonVal}); + } + + reloadConfig(); +} + diff --git a/trunk/etherpad/src/etherpad/pro/pro_pad_db.js b/trunk/etherpad/src/etherpad/pro/pro_pad_db.js new file mode 100644 index 0000000..dbb412c --- /dev/null +++ b/trunk/etherpad/src/etherpad/pro/pro_pad_db.js @@ -0,0 +1,232 @@ +/** + * 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. + */ + +import("fastJSON"); +import("sqlbase.sqlobj"); +import("cache_utils.syncedWithCache"); +import("stringutils"); + +import("etherpad.pad.padutils"); +import("etherpad.collab.collab_server"); + +import("etherpad.pro.pro_pad_editors"); +import("etherpad.pro.domains"); +import("etherpad.pro.pro_accounts.getSessionProAccount"); + +jimport("java.lang.System.out.println"); + + +// TODO: actually implement the cache part + +// NOTE: must return a deep-CLONE of the actual record, because caller +// may proceed to mutate the returned record. + +function _makeRecord(r) { + if (!r) { + return null; + } + r.proAttrs = {}; + if (r.proAttrsJson) { + r.proAttrs = fastJSON.parse(r.proAttrsJson); + } + if (!r.proAttrs.editors) { + r.proAttrs.editors = []; + } + r.proAttrs.editors.sort(); + return r; +} + +function getSingleRecord(domainId, localPadId) { + // TODO: make clone + // TODO: use cache + var record = sqlobj.selectSingle('pro_padmeta', {domainId: domainId, localPadId: localPadId}); + return _makeRecord(record); +} + +function update(padRecord) { + // TODO: use cache + + padRecord.proAttrsJson = fastJSON.stringify(padRecord.proAttrs); + delete padRecord.proAttrs; + + sqlobj.update('pro_padmeta', {id: padRecord.id}, padRecord); +} + + +//-------------------------------------------------------------------------------- +// create/edit/destory events +//-------------------------------------------------------------------------------- + +function onCreatePad(pad) { + if (!padutils.isProPad(pad)) { return; } + + var data = { + domainId: padutils.getDomainId(pad.getId()), + localPadId: padutils.getLocalPadId(pad), + createdDate: new Date(), + }; + + if (getSessionProAccount()) { + data.creatorId = getSessionProAccount().id; + } + + sqlobj.insert('pro_padmeta', data); +} + +// Not a normal part of the UI. This is only called from admin interface, +// and thus should actually destroy all record of the pad. +function onDestroyPad(pad) { + if (!padutils.isProPad(pad)) { return; } + + sqlobj.deleteRows('pro_padmeta', { + domainId: padutils.getDomainId(pad.getId()), + localPadId: padutils.getLocalPadId(pad) + }); +} + +// Called within the context of a comet post. +function onEditPad(pad, padAuthorId) { + if (!padutils.isProPad(pad)) { return; } + + var editorId = undefined; + if (getSessionProAccount()) { + editorId = getSessionProAccount().id; + } + + if (!(editorId && (editorId > 0))) { + return; // etherpad admins + } + + pro_pad_editors.notifyEdit( + padutils.getDomainId(pad.getId()), + padutils.getLocalPadId(pad), + editorId, + new Date() + ); +} + +//-------------------------------------------------------------------------------- +// accessing the pad list. +//-------------------------------------------------------------------------------- + +function _makeRecordList(lis) { + lis.forEach(function(r) { + r = _makeRecord(r); + }); + return lis; +} + +function listMyPads() { + var domainId = domains.getRequestDomainId(); + var accountId = getSessionProAccount().id; + + var padlist = sqlobj.selectMulti('pro_padmeta', {domainId: domainId, creatorId: accountId, isDeleted: false, isArchived: false}); + return _makeRecordList(padlist); +} + +function listAllDomainPads() { + var domainId = domains.getRequestDomainId(); + var padlist = sqlobj.selectMulti('pro_padmeta', {domainId: domainId, isDeleted: false, isArchived: false}); + return _makeRecordList(padlist); +} + +function listArchivedPads() { + var domainId = domains.getRequestDomainId(); + var padlist = sqlobj.selectMulti('pro_padmeta', {domainId: domainId, isDeleted: false, isArchived: true}); + return _makeRecordList(padlist); +} + +function listPadsByEditor(editorId) { + editorId = Number(editorId); + var domainId = domains.getRequestDomainId(); + var padlist = sqlobj.selectMulti('pro_padmeta', {domainId: domainId, isDeleted: false, isArchived: false}); + padlist = _makeRecordList(padlist); + padlist = padlist.filter(function(p) { + // NOTE: could replace with binary search to speed things up, + // since we know that editors array is sorted. + return (p.proAttrs.editors.indexOf(editorId) >= 0); + }); + return padlist; +} + +function listLiveDomainPads() { + var thisDomainId = domains.getRequestDomainId(); + var allLivePadIds = collab_server.getAllPadsWithConnections(); + var livePadMap = {}; + + allLivePadIds.forEach(function(globalId) { + if (padutils.isProPadId(globalId)) { + var domainId = padutils.getDomainId(globalId); + var localId = padutils.globalToLocalId(globalId); + if (domainId == thisDomainId) { + livePadMap[localId] = true; + } + } + }); + + var padList = listAllDomainPads(); + padList = padList.filter(function(p) { + return (!!livePadMap[p.localPadId]); + }); + + return padList; +} + +//-------------------------------------------------------------------------------- +// misc utils +//-------------------------------------------------------------------------------- + + +function _withCache(name, fn) { + return syncedWithCache('pro-padmeta.'+name, fn); +} + +function _withDomainCache(domainId, name, fn) { + return _withCache(name+"."+domainId, fn); +} + + + +// returns the next pad ID to use for a newly-created pad on this domain. +function getNextPadId() { + var domainId = domains.getRequestDomainId(); + return _withDomainCache(domainId, 'padcounters', function(c) { + var ret; + if (c.x === undefined) { + c.x = _getLargestNumericPadId(domainId) + 1; + } + while (sqlobj.selectSingle('pro_padmeta', {domainId: domainId, localPadId: String(c.x)})) { + c.x++; + } + ret = c.x; + c.x++; + return ret; + }); +} + +function _getLargestNumericPadId(domainId) { + var max = 0; + var allPads = listAllDomainPads(); + allPads.forEach(function(p) { + if (stringutils.isNumeric(p.localPadId)) { + max = Math.max(max, Number(p.localPadId)); + } + }); + return max; +} + + + diff --git a/trunk/etherpad/src/etherpad/pro/pro_pad_editors.js b/trunk/etherpad/src/etherpad/pro/pro_pad_editors.js new file mode 100644 index 0000000..a90f05b --- /dev/null +++ b/trunk/etherpad/src/etherpad/pro/pro_pad_editors.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. + */ + +import("execution"); +import("jsutils.*"); +import("cache_utils.syncedWithCache"); + +import("etherpad.pad.padutils"); +import("etherpad.pro.pro_padmeta"); +import("etherpad.log"); + +var _DOMAIN_EDIT_WRITE_INTERVAL = 2000; // 2 seconds + +function _withCache(name, fn) { + return syncedWithCache('pro-padmeta.'+name, fn); +} + +function _withDomainCache(domainId, name, fn) { + return _withCache(name+"."+domainId, fn); +} + + +function onStartup() { + execution.initTaskThreadPool("pro-padmeta-edits", 1); +} + +function onShutdown() { + var success = execution.shutdownAndWaitOnTaskThreadPool("pro-padmeta-edits", 4000); + if (!success) { + log.warn("Warning: pro.padmeta failed to flush pad edits on shutdown."); + } +} + +function notifyEdit(domainId, localPadId, editorId, editTime) { + if (!editorId) { + // guest editors + return; + } + _withDomainCache(domainId, "edits", function(c) { + if (!c[localPadId]) { + c[localPadId] = { + lastEditorId: editorId, + lastEditTime: editTime, + recentEditors: [] + }; + } + var info = c[localPadId]; + if (info.recentEditors.indexOf(editorId) < 0) { + info.recentEditors.push(editorId); + } + }); + _flushPadEditsEventually(domainId); +} + + +function _flushPadEditsEventually(domainId) { + // Make sure there is a recurring edit-writer for this domain + _withDomainCache(domainId, "recurring-edit-writers", function(c) { + if (!c[domainId]) { + flushEditsNow(domainId); + c[domainId] = true; + } + }); +} + +function flushEditsNow(domainId) { + if (!appjet.cache.shutdownHandlerIsRunning) { + execution.scheduleTask("pro-padmeta-edits", "proPadmetaFlushEdits", + _DOMAIN_EDIT_WRITE_INTERVAL, [domainId]); + } + + _withDomainCache(domainId, "edits", function(edits) { + var padIdList = keys(edits); + padIdList.forEach(function(localPadId) { + _writePadEditsToDbNow(domainId, localPadId, edits[localPadId]); + delete edits[localPadId]; + }); + }); +} + +function _writePadEditsToDbNow(domainId, localPadId, editInfo) { + var globalPadId = padutils.makeGlobalId(domainId, localPadId); + pro_padmeta.accessProPad(globalPadId, function(propad) { + propad.setLastEditedDate(editInfo.lastEditTime); + propad.setLastEditor(editInfo.lastEditorId); + editInfo.recentEditors.forEach(function(eid) { + propad.addEditor(eid); + }); + }); +} + diff --git a/trunk/etherpad/src/etherpad/pro/pro_padlist.js b/trunk/etherpad/src/etherpad/pro/pro_padlist.js new file mode 100644 index 0000000..73b179c --- /dev/null +++ b/trunk/etherpad/src/etherpad/pro/pro_padlist.js @@ -0,0 +1,289 @@ +/** + * 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. + */ + +import("funhtml.*"); +import("jsutils.*"); +import("stringutils"); + +import("etherpad.utils.*"); +import("etherpad.helpers"); +import("etherpad.pad.padutils"); +import("etherpad.collab.collab_server"); + +import("etherpad.pro.pro_accounts"); + +function _getColumnMeta() { + // returns map of {id --> { + // title, + // sortFn(a,b), + // render(p) + // } + + function _dateNum(d) { + if (!d) { + return 0; + } + return -1 * (+d); + } + + + var cols = {}; + + function addAvailableColumn(id, cdata) { + if (!cdata.render) { + cdata.render = function(p) { + return p[id]; + }; + } + if (!cdata.cmpFn) { + cdata.cmpFn = function(a,b) { + return cmp(a[id], b[id]); + }; + } + cdata.id = id; + cols[id] = cdata; + } + + addAvailableColumn('public', { + title: "", + render: function(p) { + // TODO: implement an icon with hover text that says public vs. + // private + return ""; + }, + cmpFn: function(a,b) { + return 0; // not sort-able + } + }); + addAvailableColumn('secure', { + title: "", + render: function(p) { + if (p.password) { + return IMG({src: '/static/img/may09/padlock.gif'}); + } else { + return ""; + } + }, + cmpFn: function(a,b) { + return cmp(a.password, b.password); + } + }); + addAvailableColumn('title', { + title: "Title", + render: function(p) { + var t = padutils.getProDisplayTitle(p.localPadId, p.title); + return A({href: "/"+p.localPadId}, t); + }, + sortFn: function(a, b) { + return cmp(padutils.getProDisplayTitle(a.localPadId, a.title), + padutils.getProDisplayTitle(b.localPadId, b.title)); + } + }); + addAvailableColumn('creatorId', { + title: "Creator", + render: function(p) { + return pro_accounts.getFullNameById(p.creatorId); + }, + sortFn: function(a, b) { + return cmp(pro_accounts.getFullNameById(a.creatorId), + pro_accounts.getFullNameById(b.creatorId)); + } + }); + addAvailableColumn('createdDate', { + title: "Created", + render: function(p) { + return timeAgo(p.createdDate); + }, + sortFn: function(a, b) { + return cmp(_dateNum(a.createdDate), _dateNum(b.createdDate)); + } + }); + addAvailableColumn('lastEditorId', { + title: "Last Editor", + render: function(p) { + if (p.lastEditorId) { + return pro_accounts.getFullNameById(p.lastEditorId); + } else { + return ""; + } + }, + sortFn: function(a, b) { + var a_ = a.lastEditorId ? pro_accounts.getFullNameById(a.lastEditorId) : "ZZZZZZZZZZ"; + var b_ = b.lastEditorId ? pro_accounts.getFullNameById(b.lastEditorId) : "ZZZZZZZZZZ"; + return cmp(a_, b_); + } + }); + + addAvailableColumn('editors', { + title: "Editors", + render: function(p) { + var editors = []; + p.proAttrs.editors.forEach(function(editorId) { + editors.push([editorId, pro_accounts.getFullNameById(editorId)]); + }); + editors.sort(function(a,b) { return cmp(a[1], b[1]); }); + var sp = SPAN(); + for (var i = 0; i < editors.length; i++) { + if (i > 0) { + sp.push(", "); + } + sp.push(A({href: "/ep/padlist/edited-by?editorId="+editors[i][0]}, editors[i][1])); + } + return sp; + } + }); + + addAvailableColumn('lastEditedDate', { + title: "Last Edited", + render: function(p) { + if (p.lastEditedDate) { + return timeAgo(p.lastEditedDate); + } else { + return "never"; + } + }, + sortFn: function(a,b) { + return cmp(_dateNum(a.lastEditedDate), _dateNum(b.lastEditedDate)); + } + }); + addAvailableColumn('localPadId', { + title: "Path", + }); + addAvailableColumn('actions', { + title: "", + render: function(p) { + return DIV({className: "gear-drop", id: "pad-gear-"+p.id}, " "); + } + }); + + addAvailableColumn('connectedUsers', { + title: "Connected Users", + render: function(p) { + var names = []; + padutils.accessPadLocal(p.localPadId, function(pad) { + var userList = collab_server.getConnectedUsers(pad); + userList.forEach(function(u) { + if (collab_server.translateSpecialKey(u.specialKey) != 'invisible') { + // excludes etherpad admin user + names.push(u.name); + } + }); + }); + return names.join(", "); + } + }); + + return cols; +} + +function _sortPads(padList) { + var meta = _getColumnMeta(); + var sortId = _getCurrentSortId(); + var reverse = false; + if (sortId.charAt(0) == '-') { + reverse = true; + sortId = sortId.slice(1); + } + padList.sort(function(a,b) { return cmp(a.localPadId, b.localPadId); }); + padList.sort(function(a,b) { return meta[sortId].sortFn(a, b); }); + if (reverse) { padList.reverse(); } +} + +function _addClientVars(padList) { + var padTitles = {}; // maps localPadId -> title + var localPadIds = {}; // maps padmetaId -> localPadId + padList.forEach(function(p) { + padTitles[p.localPadId] = stringutils.toHTML(padutils.getProDisplayTitle(p.localPadId, p.title)); + localPadIds[p.id] = p.localPadId; + }); + helpers.addClientVars({ + padTitles: padTitles, + localPadIds: localPadIds + }); +} + +function _getCurrentSortId() { + return request.params.sortBy || "lastEditedDate"; +} + +function _renderColumnHeader(m) { + var sp = SPAN(); + var sortBy = _getCurrentSortId(); + if (m.sortFn) { + var d = {sortBy: m.id}; + var arrow = ""; + if (sortBy == m.id) { + d.sortBy = ("-"+m.id); + arrow = html("↓"); + } + if (sortBy == ("-"+m.id)) { + arrow = html("↑"); + } + sp.push(arrow, " ", A({href: qpath(d)}, m.title)); + } else { + sp.push(m.title); + } + return sp; +} + +function renderPadList(padList, columnIds, limit) { + _sortPads(padList); + _addClientVars(padList); + + if (limit && (limit < padList.length)) { + padList = padList.slice(0,limit); + } + + var showSecurityInfo = false; + padList.forEach(function(p) { + if (p.password && p.password.length > 0) { showSecurityInfo = true; } + }); + if (!showSecurityInfo && (columnIds[0] == 'secure')) { + columnIds.shift(); + } + + var columnMeta = _getColumnMeta(); + + var t = TABLE({id: "padtable", cellspacing:"0", cellpadding:"0"}); + var toprow = TR({className: "toprow"}); + columnIds.forEach(function(cid) { + toprow.push(TH(_renderColumnHeader(columnMeta[cid]))); + }); + t.push(toprow); + + padList.forEach(function(p) { + // Note that this id is always numeric, and is the actual + // canonical padmeta id. + var row = TR({id: 'padmeta-'+p.id}); + var first = true; + for (var i = 0; i < columnIds.length; i++) { + var cid = columnIds[i]; + var m = columnMeta[cid]; + var classes = cid; + if (i == 0) { + classes += (" first"); + } + if (i == (columnIds.length - 1)) { + classes += (" last"); + } + row.push(TD({className: classes}, m.render(p))); + } + t.push(row); + }); + + return t; +} + diff --git a/trunk/etherpad/src/etherpad/pro/pro_padmeta.js b/trunk/etherpad/src/etherpad/pro/pro_padmeta.js new file mode 100644 index 0000000..6f911b2 --- /dev/null +++ b/trunk/etherpad/src/etherpad/pro/pro_padmeta.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. + */ + +import("stringutils"); +import("cache_utils.syncedWithCache"); +import("sync"); + +import("etherpad.pad.padutils"); +import("etherpad.pro.pro_pad_db"); + +function _doWithProPadLock(domainId, localPadId, func) { + var lockName = ["pro-pad", domainId, localPadId].join("/"); + return sync.doWithStringLock(lockName, func); +} + +function accessProPad(globalPadId, fn) { + // retrieve pad from cache + var domainId = padutils.getDomainId(globalPadId); + if (!domainId) { + throw Error("not a pro pad: "+globalPadId); + } + var localPadId = padutils.globalToLocalId(globalPadId); + var padRecord = pro_pad_db.getSingleRecord(domainId, localPadId); + + return _doWithProPadLock(domainId, localPadId, function() { + var isDirty = false; + + var proPad = { + exists: function() { return !!padRecord; }, + getDomainId: function() { return domainId; }, + getLocalPadId: function() { return localPadId; }, + getGlobalId: function() { return globalPadId; }, + getDisplayTitle: function() { return padutils.getProDisplayTitle(localPadId, padRecord.title); }, + setTitle: function(newTitle) { + padRecord.title = newTitle; + isDirty = true; + }, + isDeleted: function() { return padRecord.isDeleted; }, + markDeleted: function() { + padRecord.isDeleted = true; + isDirty = true; + }, + getPassword: function() { return padRecord.password; }, + setPassword: function(newPass) { + if (newPass == "") { + newPass = null; + } + padRecord.password = newPass; + isDirty = true; + }, + isArchived: function() { return padRecord.isArchived; }, + markArchived: function() { + padRecord.isArchived = true; + isDirty = true; + }, + unmarkArchived: function() { + padRecord.isArchived = false; + isDirty = true; + }, + setLastEditedDate: function(d) { + padRecord.lastEditedDate = d; + isDirty = true; + }, + addEditor: function(editorId) { + var es = String(editorId); + if (es && es.length > 0 && stringutils.isNumeric(editorId)) { + if (padRecord.proAttrs.editors.indexOf(editorId) < 0) { + padRecord.proAttrs.editors.push(editorId); + padRecord.proAttrs.editors.sort(); + } + isDirty = true; + } + }, + setLastEditor: function(editorId) { + var es = String(editorId); + if (es && es.length > 0 && stringutils.isNumeric(editorId)) { + padRecord.lastEditorId = editorId; + this.addEditor(editorId); + isDirty = true; + } + } + }; + + var ret = fn(proPad); + + if (isDirty) { + pro_pad_db.update(padRecord); + } + + return ret; + }); +} + +function accessProPadLocal(localPadId, fn) { + var globalPadId = padutils.getGlobalPadId(localPadId); + return accessProPad(globalPadId, fn); +} + diff --git a/trunk/etherpad/src/etherpad/pro/pro_quotas.js b/trunk/etherpad/src/etherpad/pro/pro_quotas.js new file mode 100644 index 0000000..ed69e1c --- /dev/null +++ b/trunk/etherpad/src/etherpad/pro/pro_quotas.js @@ -0,0 +1,141 @@ +/** + * 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. + */ + +import("funhtml.*"); +import("stringutils.startsWith"); +import("sqlbase.sqlobj"); +import("sqlbase.sqlcommon.inTransaction"); + +import("etherpad.billing.team_billing"); +import("etherpad.globals.*"); +import("etherpad.pro.pro_accounts"); +import("etherpad.pro.domains"); +import("etherpad.sessions.getSession"); +import("etherpad.store.checkout"); + +function _createRecordIfNecessary(domainId) { + inTransaction(function() { + var r = sqlobj.selectSingle('pro_account_usage', {domainId: domainId}); + if (!r) { + var count = pro_accounts.getActiveCount(domainId); + sqlobj.insert('pro_account_usage', { + domainId: domainId, + count: count, + lastReset: (new Date), + lastUpdated: (new Date) + }); + } + }); +} + +/** + * Called after a successful payment has been made. + * Effect: counts the current number of domain accounts and stores that + * as the current account usage count. + */ +function resetAccountUsageCount(domainId) { + _createRecordIfNecessary(domainId); + var newCount = pro_accounts.getActiveCount(domainId); + sqlobj.update( + 'pro_account_usage', + {domainId: domainId}, + {count: newCount, lastUpdated: (new Date), lastReset: (new Date)} + ); +} + +/** + * Returns the max number of accounts that have existed simultaneously + * since the last reset. + */ +function getAccountUsageCount(domainId) { + _createRecordIfNecessary(domainId); + var record = sqlobj.selectSingle('pro_account_usage', {domainId: domainId}); + return record.count; +} + + +/** + * Updates the current account usage count by computing: + * usage_count = max(current_accounts, usage_count) + */ +function updateAccountUsageCount(domainId) { + _createRecordIfNecessary(domainId); + var record = sqlobj.selectSingle('pro_account_usage', {domainId: domainId}); + var currentCount = pro_accounts.getActiveCount(domainId); + var newCount = Math.max(record.count, currentCount); + sqlobj.update( + 'pro_account_usage', + {domainId: domainId}, + {count: newCount, lastUpdated: (new Date)} + ); +} + +// called per request + +function _generateGlobalBillingNotice(status) { + if (status == team_billing.CURRENT) { + return; + } + var notice = SPAN(); + if (status == team_billing.PAST_DUE) { + var suspensionDate = checkout.formatDate(team_billing.getDomainSuspensionDate(domains.getRequestDomainId())); + notice.push( + "Warning: your account is past due and will be suspended on ", + suspensionDate, "."); + } + if (status == team_billing.SUSPENDED) { + notice.push( + "Warning: your account is suspended because it is more than ", + team_billing.GRACE_PERIOD_DAYS, " days past due."); + } + + if (pro_accounts.isAdminSignedIn()) { + notice.push(" ", A({href: "/ep/admin/billing/"}, "Manage billing"), "."); + } else { + getSession().billingProblem = "Payment is required for sites with more than "+PRO_FREE_ACCOUNTS+" accounts."; + notice.push(" ", "Please ", + A({href: "/ep/payment-required"}, "contact a site administrator"), "."); + } + request.cache.globalProNotice = notice; +} + +function perRequestBillingCheck() { + // Do nothing if under the free account limit. + var activeAccounts = pro_accounts.getCachedActiveCount(domains.getRequestDomainId()); + if (activeAccounts <= PRO_FREE_ACCOUNTS) { + return; + } + + var status = team_billing.getDomainStatus(domains.getRequestDomainId()); + _generateGlobalBillingNotice(status); + + // now see if we need to block the request because of account + // suspension + if (status != team_billing.SUSPENDED) { + return; + } + // These path sare still OK if a suspension is on. + if ((startsWith(request.path, "/ep/account/") || + startsWith(request.path, "/ep/admin/") || + startsWith(request.path, "/ep/pro-help/") || + startsWith(request.path, "/ep/payment-required"))) { + return; + } + + getSession().billingProblem = "Payment is required for sites with more than "+PRO_FREE_ACCOUNTS+" accounts."; + response.redirect('/ep/payment-required'); +} + diff --git a/trunk/etherpad/src/etherpad/pro/pro_utils.js b/trunk/etherpad/src/etherpad/pro/pro_utils.js new file mode 100644 index 0000000..1dc2468 --- /dev/null +++ b/trunk/etherpad/src/etherpad/pro/pro_utils.js @@ -0,0 +1,165 @@ +/** + * 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. + */ + +import("funhtml.*"); +import("stringutils.startsWith"); + +import("etherpad.utils.*"); +import("etherpad.globals.*"); +import("etherpad.log"); +import("etherpad.pne.pne_utils"); +import("etherpad.pro.pro_accounts"); +import("etherpad.pro.pro_accounts.getSessionProAccount"); +import("etherpad.pro.domains"); +import("etherpad.pro.pro_quotas"); +import("etherpad.sessions"); +import("etherpad.sessions.getSession"); + +import("etherpad.control.pro.pro_main_control"); + +jimport("java.lang.System.out.println"); + +function _stripComet(x) { + if (x.indexOf('.comet.') > 0) { + x = x.split('.comet.')[1]; + } + return x; +} + +function getProRequestSubdomain() { + var d = _stripComet(request.domain); + return d.split('.')[0]; +} + +function getRequestSuperdomain() { + var parts = request.domain.split('.'); + while (parts.length > 0) { + var domain = parts.join('.'); + if (SUPERDOMAINS[domain]) { + return domain; + } + parts.shift(); // Remove next level + } +} + +function isProDomainRequest() { + // the result of this function never changes within the same request. + var c = appjet.requestCache; + if (c.isProDomainRequest === undefined) { + c.isProDomainRequest = _computeIsProDomainRequest(); + } + return c.isProDomainRequest; +} + +function _computeIsProDomainRequest() { + if (pne_utils.isPNE()) { + return true; + } + + var domain = _stripComet(request.domain); + + if (SUPERDOMAINS[domain]) { + return false; + } + + var requestSuperdomain = getRequestSuperdomain(); + + if (SUPERDOMAINS[requestSuperdomain]) { + // now see if this subdomain is actually in our database. + if (domains.getRequestDomainRecord()) { + return true; + } else { + return false; + } + } + + return false; +} + +function preDispatchAccountCheck() { + // if account is not logged in, redirect to /ep/account/login + // + // if it's PNE and there is no admin account, allow them to create an admin + // account. + + if (pro_main_control.isActivationAllowed()) { + return; + } + + if (!pro_accounts.doesAdminExist()) { + if (request.path != '/ep/account/create-admin-account') { + // should only happen for eepnet installs + response.redirect('/ep/account/create-admin-account'); + } + } else { + pro_accounts.requireAccount(); + } + + pro_quotas.perRequestBillingCheck(); +} + +function renderFramedMessage(m) { + renderFramedHtml( + DIV( + {style: "font-size: 2em; padding: 2em; margin: 4em; border: 1px solid #ccc; background: #e6e6e6;"}, + m)); +} + +function getFullProDomain() { + // TODO: have a special config param for this? --etherpad.canonicalDomain + return request.domain; +} + +// domain, including port if necessary +function getFullProHost() { + var h = getFullProDomain(); + var parts = request.host.split(':'); + if (parts.length > 1) { + h += (':' + parts[1]); + } + return h; +} + +function getFullSuperdomainHost() { + if (isProDomainRequest()) { + var h = getRequestSuperdomain() + var parts = request.host.split(':'); + if (parts.length > 1) { + h += (':' + parts[1]); + } + return h; + } else { + return request.host; + } +} + +function getEmailFromAddr() { + var fromDomain = 'pad.spline.inf.fu-berlin.de'; + if (pne_utils.isPNE()) { + fromDomain = getFullProDomain(); + } + return ('"EtherPad" '); +} + +function renderGlobalProNotice() { + if (request.cache.globalProNotice) { + return DIV({className: 'global-pro-notice'}, + request.cache.globalProNotice); + } else { + return ""; + } +} + diff --git a/trunk/etherpad/src/etherpad/quotas.js b/trunk/etherpad/src/etherpad/quotas.js new file mode 100644 index 0000000..7e939ec --- /dev/null +++ b/trunk/etherpad/src/etherpad/quotas.js @@ -0,0 +1,50 @@ +/** + * 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. + */ + +import("stringutils"); +import("etherpad.licensing"); +import("etherpad.utils.*"); +import("etherpad.pne.pne_utils"); + +// TODO: hook into PNE? + +function getMaxSimultaneousPadEditors(globalPadId) { + if (isProDomainRequest()) { + if (pne_utils.isPNE()) { + return licensing.getMaxUsersPerPad(); + } else { + return 1e6; + } + } else { + // pad.spline.inf.fu-berlin.de public pads + if (globalPadId && stringutils.startsWith(globalPadId, "conf-")) { + return 64; + } else { + return 16; + } + } + return 1e6; +} + +function getMaxSavedRevisionsPerPad() { + if (isProDomainRequest()) { + return 1e3; + } else { + // free public pad.spline.inf.fu-berlin.de + return 100; + } +} + diff --git a/trunk/etherpad/src/etherpad/sessions.js b/trunk/etherpad/src/etherpad/sessions.js new file mode 100644 index 0000000..c218da8 --- /dev/null +++ b/trunk/etherpad/src/etherpad/sessions.js @@ -0,0 +1,203 @@ +/** + * 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. + */ + +import("sessions"); +import("stringutils.randomHash"); +import("funhtml.*"); + +import("etherpad.log"); +import("etherpad.globals.*"); +import("etherpad.pro.pro_utils"); +import("etherpad.utils.*"); +import("cache_utils.syncedWithCache"); + +jimport("java.lang.System.out.println"); + +var _TRACKING_COOKIE_NAME = "ET"; +var _SESSION_COOKIE_NAME = "ES"; + +function _updateInitialReferrer(data) { + + if (data.initialReferer) { + return; + } + + var ref = request.headers["Referer"]; + + if (!ref) { + return; + } + if (ref.indexOf('http://'+request.host) == 0) { + return; + } + if (ref.indexOf('https://'+request.host) == 0) { + return; + } + + data.initialReferer = ref; + log.custom("referers", {referer: ref}); +} + +function _getScopedDomain(subDomain) { + var d = request.domain; + if (d.indexOf(".") == -1) { + // special case for "localhost". For some reason, firefox does not like cookie domains + // to be ".localhost". + return undefined; + } + if (subDomain) { + d = subDomain + "." + d; + } + return "." + d; +} +//-------------------------------------------------------------------------------- + +// pass in subDomain to get the session data for a particular subdomain -- +// intended for debugging. +function getSession(subDomain) { + var sessionData = sessions.getSession({ + cookieName: _SESSION_COOKIE_NAME, + domain: _getScopedDomain(subDomain) + }); + _updateInitialReferrer(sessionData); + return sessionData; +} + +function getSessionId() { + return sessions.getSessionId(_SESSION_COOKIE_NAME, false, _getScopedDomain()); +} + +function _getGlobalSessionId() { + return (request.isDefined && request.cookies[_SESSION_COOKIE_NAME]) || null; +} + +function isAnEtherpadAdmin() { + var sessionId = _getGlobalSessionId(); + if (! sessionId) { + return false; + } + + return syncedWithCache("isAnEtherpadAdmin", function(c) { + return !! c[sessionId]; + }); +} + +function setIsAnEtherpadAdmin(v) { + var sessionId = _getGlobalSessionId(); + if (! sessionId) { + return; + } + + syncedWithCache("isAnEtherpadAdmin", function(c) { + if (v) { + c[sessionId] = true; + } + else { + delete c[sessionId]; + } + }); +} + +//-------------------------------------------------------------------------------- + +function setTrackingCookie() { + if (request.cookies[_TRACKING_COOKIE_NAME]) { + return; + } + + var trackingVal = randomHash(16); + var expires = new Date(32503708800000); // year 3000 + + response.setCookie({ + name: _TRACKING_COOKIE_NAME, + value: trackingVal, + path: "/", + domain: _getScopedDomain(), + expires: expires + }); +} + +function getTrackingId() { + // returns '-' if no tracking ID (caller can assume) + return (request.cookies[_TRACKING_COOKIE_NAME] || response.getCookie(_TRACKING_COOKIE_NAME) || '-'); +} + +//-------------------------------------------------------------------------------- + +function preRequestCookieCheck() { + if (isStaticRequest()) { + return; + } + + // If this function completes without redirecting, then it means + // there is a valid session cookie and tracking cookie. + + if (request.cookies[_SESSION_COOKIE_NAME] && + request.cookies[_TRACKING_COOKIE_NAME]) { + + if (request.params.cookieShouldBeSet) { + response.redirect(qpath({cookieShouldBeSet: null})); + } + return; + } + + // Only superdomains can set cookies. + var isSuperdomain = SUPERDOMAINS[request.domain]; + + if (isSuperdomain) { + // superdomain without cookies + + getSession(); + setTrackingCookie(); + + // check if we need to redirect back to a subdomain. + if ((request.path == "/") && + (request.params.setCookie) && + (request.params.contUrl)) { + + var contUrl = request.params.contUrl; + if (contUrl.indexOf("?") == -1) { + contUrl += "?"; + } + contUrl += "&cookieShouldBeSet=1"; + response.redirect(contUrl); + } + } else { + var parts = request.domain.split("."); + if (parts.length < 3) { + // invalid superdomain + response.write("invalid superdomain"); + response.stop(); + } + // subdomain without cookies + if (request.params.cookieShouldBeSet) { + log.warn("Cookie failure!"); + renderFramedHtml(DIV({style: "border: 1px solid #ccc; padding: 1em; width: 600px; margin: 1em auto; font-size: 1.4em;"}, + P("Please enable cookies in your browser in order to access this site."), + BR(), + P(A({href: "/"}, "Continue")))); + response.stop(); + } else { + var contUrl = request.url; + var p = request.host.split(':')[1]; + p = (p ? (":"+p) : ""); + response.redirect(request.scheme+"://"+pro_utils.getRequestSuperdomain()+p+ + "/?setCookie=1&contUrl="+encodeURIComponent(contUrl)); + } + } +} + + diff --git a/trunk/etherpad/src/etherpad/statistics/exceptions.js b/trunk/etherpad/src/etherpad/statistics/exceptions.js new file mode 100644 index 0000000..723085d --- /dev/null +++ b/trunk/etherpad/src/etherpad/statistics/exceptions.js @@ -0,0 +1,231 @@ +/** + * 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. + */ + + +import("fastJSON"); +import("etherpad.log"); +import("cache_utils.syncedWithCache"); +import("funhtml.*"); +import("jsutils.{eachProperty,keys}"); + +function _dayKey(date) { + return [date.getFullYear(), date.getMonth()+1, date.getDate()].join(','); +} + +function _dateAddDays(date, numDays) { + return new Date((+date) + numDays*1000*60*60*24); +} + +function _loadDay(date) { + var fileName = log.frontendLogFileName('exception', date); + if (! fileName) { + return []; + } + var reader = new java.io.BufferedReader(new java.io.FileReader(fileName)); + var line = null; + var array = []; + while ((line = reader.readLine()) !== null) { + array.push(fastJSON.parse(line)); + } + return array; +} + +function _accessLatestLogs(func) { + syncedWithCache("etherpad.statistics.exceptions", function(exc) { + if (! exc.byDay) { + exc.byDay = {}; + } + // always reload today from disk + var now = new Date(); + var today = now; + var todayKey = _dayKey(today); + exc.byDay[todayKey] = _loadDay(today); + var activeKeys = {}; + activeKeys[todayKey] = true; + // load any of 7 previous days that aren't loaded or + // were not loaded as a historical day + for(var i=1;i<=7;i++) { + var pastDay = _dateAddDays(today, -i); + var pastDayKey = _dayKey(pastDay); + activeKeys[pastDayKey] = true; + if ((! exc.byDay[pastDayKey]) || (! exc.byDay[pastDayKey].sealed)) { + exc.byDay[pastDayKey] = _loadDay(pastDay); + exc.byDay[pastDayKey].sealed = true; // in the past, won't change + } + } + // clear old days + for(var k in exc.byDay) { + if (! (k in activeKeys)) { + delete exc.byDay[k]; + } + } + + var logs = { + getDay: function(daysAgo) { + return exc.byDay[_dayKey(_dateAddDays(today, -daysAgo))]; + }, + eachLineInLastNDays: function(n, func) { + var oldest = _dateAddDays(now, -n); + var oldestNum = +oldest; + for(var i=n;i>=0;i--) { + var lines = logs.getDay(i); + lines.forEach(function(line) { + if (line.date > oldestNum) { + func(line); + } + }); + } + } + }; + + func(logs); + }); +} + +function _exceptionHash(line) { + // skip the first line of jsTrace, take hashCode of rest + var trace = line.jsTrace; + var stack = trace.substring(trace.indexOf('\n') + 1); + return new java.lang.String(stack).hashCode(); +} + +// Used to take a series of strings and produce an array of +// [common prefix, example middle, common suffix], or +// [string] if the strings are the same. Takes oldInfo +// and returns newInfo; each is either null or an array +// of length 1 or 3. +function _accumCommonPrefixSuffix(oldInfo, newString) { + function _commonPrefixLength(a, b) { + var x = 0; + while (x < a.length && x < b.length && a.charAt(x) == b.charAt(x)) { + x++; + } + return x; + } + + function _commonSuffixLength(a, b) { + var x = 0; + while (x < a.length && x < b.length && + a.charAt(a.length-1-x) == b.charAt(b.length-1-x)) { + x++; + } + return x; + } + + if (! oldInfo) { + return [newString]; + } + else if (oldInfo.length == 1) { + var oldString = oldInfo[0]; + if (oldString == newString) { + return oldInfo; + } + var newInfo = []; + var a = _commonPrefixLength(oldString, newString); + newInfo[0] = newString.substring(0, a); + oldString = oldString.substring(a); + newString = newString.substring(a); + var b = _commonSuffixLength(oldString, newString); + newInfo[2] = newString.slice(-b); + oldString = oldString.slice(0, -b); + newString = newString.slice(0, -b); + newInfo[1] = newString; + return newInfo; + } + else { + // oldInfo.length == 3 + var a = _commonPrefixLength(oldInfo[0], newString); + var b = _commonSuffixLength(oldInfo[2], newString); + return [newString.slice(0, a), newString.slice(a, -b), + newString.slice(-b)]; + } +} + +function render() { + + _accessLatestLogs(function(logs) { + var weekCounts = {}; + var totalWeekCount = 0; + + // count exceptions of each kind in last week + logs.eachLineInLastNDays(7, function(line) { + var hash = _exceptionHash(line); + weekCounts[hash] = (weekCounts[hash] || 0) + 1; + totalWeekCount++; + }); + + var dayData = {}; + var totalDayCount = 0; + + // accumulate data about each exception in last 24 hours + logs.eachLineInLastNDays(1, function(line) { + var hash = _exceptionHash(line); + var oldData = dayData[hash]; + var data = (oldData || {}); + if (! oldData) { + data.hash = hash; + data.trace = line.jsTrace.substring(line.jsTrace.indexOf('\n')+1); + data.trackers = {}; + } + var msg = line.jsTrace.substring(0, line.jsTrace.indexOf('\n')); + data.message = _accumCommonPrefixSuffix(data.message, msg); + data.count = (data.count || 0)+1; + data.trackers[line.tracker] = true; + totalDayCount++; + dayData[hash] = data; + }); + + // put day datas in an array and sort + var dayDatas = []; + eachProperty(dayData, function(k,v) { + dayDatas.push(v); + }); + dayDatas.sort(function(a, b) { + return b.count - a.count; + }); + + // process + dayDatas.forEach(function(data) { + data.weekCount = (weekCounts[data.hash] || 0); + data.numTrackers = keys(data.trackers).length; + }); + + // gen HTML + function num(n) { return SPAN({className:'num'}, n); } + + response.write(STYLE(html(".trace { height: 300px; overflow: auto; background: #eee; margin-left: 1em; font-family: monospace; border: 1px solid #833; padding: 4px; }\n"+ + ".exc { margin: 1em 0; }\n"+ + ".num { font-size: 150%; }"))); + + response.write(P("Total exceptions in past day: ", num(totalDayCount), + ", past week: ", totalWeekCount)); + + response.write(P(SMALL(EM("Data on this page is live.")))); + + response.write(H2("Exceptions grouped by stack trace:")); + + dayDatas.forEach(function(data) { + response.write(DIV({className:'exc'}, + 'Past day: ',num(data.count),', Past week: ', + data.weekCount,', Different tracker cookies today: ', + data.numTrackers, + '\n',data.message[0], + (data.message[1] && I(data.message[1])) || '', + (data.message[2] || ''),'\n', + DIV({className:'trace'}, data.trace))); + }); + }); +} diff --git a/trunk/etherpad/src/etherpad/statistics/statistics.js b/trunk/etherpad/src/etherpad/statistics/statistics.js new file mode 100644 index 0000000..8174405 --- /dev/null +++ b/trunk/etherpad/src/etherpad/statistics/statistics.js @@ -0,0 +1,1248 @@ +/** + * 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. + */ + +import("dateutils.noon"); +import("execution"); +import("exceptionutils"); +import("fastJSON"); +import("fileutils.fileLineIterator"); +import("jsutils.*"); +import("sqlbase.sqlobj"); + +import("etherpad.log"); + +jimport("net.appjet.oui.GenericLoggerUtils"); +jimport("net.appjet.oui.LoggableFromJson"); +jimport("net.appjet.oui.FilterWrangler"); +jimport("java.lang.System.out.println"); +jimport("net.appjet.common.util.ExpiringMapping"); + +var millisInDay = 86400*1000; + +function _stats() { + if (! appjet.cache.statistics) { + appjet.cache.statistics = {}; + } + return appjet.cache.statistics; +} + +function onStartup() { + execution.initTaskThreadPool("statistics", 1); + _scheduleNextDailyUpdate(); + + onReset(); +} + +function _info(m) { + log.info({type: 'statistics', message: m}); +} + +function _warn(m) { + log.info({type: 'statistics', message: m}); +} + +function _statData() { + return _stats().stats; +} + +function getAllStatNames() { + return keys(_statData()); +} + +function getStatData(statName) { + return _statData()[statName]; +} + +function _setStatData(statName, data) { + _statData()[statName] = data; +} + +function liveSnapshot(stat) { + var statObject; + if (typeof(stat) == 'string') { + // "stat" is the stat name. + statObject = getStatData(stat); + } else if (typeof(stat) == 'object') { + statObject = stat; + } else { + return; + } + return _callFunction(statObject.snapshot_f, + statObject.name, statObject.options, statObject.data); +} + +// ------------------------------------------------------------------ +// stats processing +// ------------------------------------------------------------------ + +// some useful constants +var LIVE = 'live'; +var HIST = 'historical'; +var HITS = 'hits'; +var UNIQ = 'uniques'; +var VALS = 'values'; +var HGRM = 'histogram'; + +// helpers + +function _date(d) { + return new Date(d); +} + +function _saveStat(day, name, value) { + var timestamp = Math.floor(day.valueOf() / 1000); + _info({statistic: name, + timestamp: timestamp, + value: value}); + try { + sqlobj.insert('statistics', { + name: name, + timestamp: timestamp, + value: fastJSON.stringify(value) + }); + } catch (e) { + var msg; + try { + msg = e.getMessage(); + } catch (e2) { + try { + msg = e.toSource(); + } catch (e3) { + msg = "(none)"; + } + } + _warn("failed to save stat "+name+": "+msg); + } +} + +function _convertScalaTopValuesToJs(topValues) { + var totalValue = topValues._1(); + var countsMap = topValues._2(); + countsObj = {}; + countsMap.foreach(scalaF1(function(pair) { countsObj[pair._1()] = pair._2(); })); + return {total: totalValue, counts: countsObj}; +} + +function _fakeMap() { + var map = {} + return { + get: function(k) { return map[k]; }, + put: function(k, v) { map[k] = v; }, + remove: function(k) { delete map[k]; } + } +} + +function _withinSecondsOf(numSeconds, t1, t2) { + return (t1 > t2-numSeconds*1000) && (t1 < t2+numSeconds*1000); +} + +function _callFunction(functionName, arg1, arg2, etc) { + var f = this[functionName]; + var args = Array.prototype.slice.call(arguments, 1); + return f.apply(this, args); +} + +// trackers and other init functions + +function _hitTracker(trackerType, timescaleType) { + var className; + switch (trackerType) { + case HITS: className = "BucketedLastHits"; break; + case UNIQ: className = "BucketedUniques"; break; + case VALS: className = "BucketedValueCounts"; break; + case HGRM: className = "BucketedLastHitsHistogram"; break; + } + var tracker; + switch (timescaleType) { + case LIVE: + tracker = new net.appjet.oui[className](24*60*60*1000); + break; + case HIST: + // timescale just needs to be longer than a day. + tracker = new net.appjet.oui[className](365*24*60*60*1000, true); + break; + } + + var conversionData = { + total_f: "count", + history_f: "history", + latest_f: "latest", + }; + switch (trackerType) { + case HITS: case UNIQ: + conversionData.conversionFunction = + function(x) { return x; } // no conversion necessary. + break; + case VALS: + conversionData.conversionFunction = _convertScalaTopValuesToJs + break; + case HGRM: + conversionData.conversionFunction = + function(hFunc) { return function(pct) { return hFunc.apply(pct); } } + break; + } + + + return { + tracker: tracker, + conversionData: conversionData, + hit: function(d, n1, n2) { + d = _date(d); + if (n2 === undefined) { + this.tracker.hit(d, n1); + } else { + this.tracker.hit(d, n1, n2); + } + }, + get total() { + return this.conversionData.conversionFunction(this.tracker[this.conversionData.total_f]()); + }, + history: function(bucketsPerSample, numSamples) { + var scalaArray = this.tracker[this.conversionData.history_f](bucketsPerSample, numSamples); + var jsArray = []; + for (var i = 0; i < scalaArray.length(); ++i) { + jsArray.push(this.conversionData.conversionFunction(scalaArray.apply(i))); + } + return jsArray; + }, + latest: function(bucketsPerSample) { + return this.conversionData.conversionFunction(this.tracker[this.conversionData.latest_f](bucketsPerSample)); + } + } +} + +function _initCount(statName, options, timescaleType) { + return _hitTracker(HITS, timescaleType); +} +function _initUniques(statName, options, timescaleType) { + return _hitTracker(UNIQ, timescaleType); +} +function _initTopValues(statName, options, timescaleType) { + return _hitTracker(VALS, timescaleType); +} +function _initHistogram(statName, options, timescaleType) { + return _hitTracker(HGRM, timescaleType); +} + +function _initLatencies(statName, options, type) { + var hits = _initTopValues(statName, options, type); + var latencies = _initTopValues(statName, options, type); + + return { + hit: function(d, value, latency) { + hits.hit(d, value); + latencies.hit(d, value, latency); + }, + hits: hits, + latencies: latencies + } +} + +function _initDisconnectTracker(statName, options, timescaleType) { + return { + map: (timescaleType == LIVE ? new ExpiringMapping(60*1000) : _fakeMap()), + counter: _initCount(statName, options, timescaleType), + uniques: _initUniques(statName, options, timescaleType), + isLive: timescaleType == LIVE + } +} + +// update functions + +function _updateCount(statName, options, logName, data, logObject) { + // println("update count: "+statName+" on log "+logName+", with data: "+data.toSource()+" with log entry: "+logObject.toSource()); + if (options.filter == null || options.filter(logObject)) { + data.hit(logObject.date, 1); + } +} + +function _updateSum(statName, options, logName, data, logObject) { + // println("update sum: "+statName+" on log "+logName+", with data: "+data.toSource()+" with log entry: "+logObject.toSource()); + if (options.filter == null || options.filter(logObject)) { + data.hit(logObject.date, Math.round(Number(logObject[options.fieldName]))); + } +} + +function _updateUniquenessCount(statName, options, logName, data, logObject) { + // println("update uniqueness: "+statName+" on log "+logName+", with data: "+data.toSource()+" with log entry: "+logObject.toSource()); + if (options.filter == null || options.filter(logObject)) { + var value = logObject[options.fieldName]; + if (value === undefined) { return; } + data.hit(logObject.date, value); + } +} + +function _updateTopValues(statName, options, logName, data, logObject) { + // println("update topvalues: "+statName+" on log "+logName+", with data: "+data.toSource()+" with log entry: "+logObject.toSource()); + + if (options.filter == null || options.filter(logObject)) { + var value = logObject[options.fieldName]; + if (value === undefined) { return; } + if (options.canonicalizer) { + value = options.canonicalizer(value); + } + data.hit(logObject.date, value); + } +} + +function _updateLatencies(statName, options, logName, data, logObject) { + // println("update latencies: "+statName+" on log "+logName+", with data: "+data.toSource()+" with log entry: "+logObject.toSource()); + + if (options.filter == null || options.filter(logObject)) { + var value = logObject[options.fieldName]; + var latency = logObject[options.latencyFieldName]; + if (value === undefined) { return; } + data.hit(logObject.date, value, latency); + } +} + +function _updateDisconnectTracker(statName, options, logName, data, logObject) { + if (logName == "frontend/padevents" && logObject.type != "userleave") { + // we only care about userleaves from the padevents log. + return; + } + + var [evtPrefix, otherPrefix] = + (logName == "frontend/padevents" ? ["l-", "d-"] : ["d-", "l-"]); + var dateLong = logObject.date; + var userId = logObject.session; + + var lastOtherEvent = data.map.get(otherPrefix+userId); + if (lastOtherEvent != null && _withinSecondsOf(60, dateLong, lastOtherEvent.date)) { + data.counter.hit(logObject.date, 1); + data.uniques.hit(logObject.date, userId); + data.map.remove(otherPrefix+userId); + if (data.isLive) { + log.custom("avoidable_disconnects", + {userId: userId, + errorMessage: lastOtherEvent.errorMessage || logObject.errorMessage}); + } + } else { + data.map.put(evtPrefix+userId, {date: dateLong, message: logObject.errorMessage}); + } +} + +// snapshot functions + +function _lazySnapshot(snapshot) { + var total; + var history = {}; + var latest = {}; + return { + get total() { + if (total === undefined) { + total = snapshot.total; + } + return total; + }, + history: function(bucketsPerSample, numSamples) { + if (history[""+bucketsPerSample+":"+numSamples] === undefined) { + history[""+bucketsPerSample+":"+numSamples] = snapshot.history(bucketsPerSample, numSamples); + } + return history[""+bucketsPerSample+":"+numSamples]; + }, + latest: function(bucketsPerSample) { + if (latest[""+bucketsPerSample] === undefined) { + latest[""+bucketsPerSample] = snapshot.latest(bucketsPerSample); + } + return latest[""+bucketsPerSample]; + } + } +} + +function _snapshotTotal(statName, options, data) { + return _lazySnapshot(data); +} + +function _convertTopValue(topValue) { + var counts = topValue.counts; + var sortedValues = keys(counts).sort(function(x, y) { + return counts[y] - counts[x]; + }).map(function(key) { + return { value: key, count: counts[key] }; + }); + return {count: topValue.total, topValues: sortedValues.slice(0, 50) }; +} + +function _snapshotTopValues(statName, options, data) { + var convertedData = {}; + + return _lazySnapshot({ + get total() { + return _convertTopValue(data.total); + }, + history: function(bucketsPerSample, numSamples) { + return data.history(bucketsPerSample, numSamples).map(_convertTopValue); + }, + latest: function(bucketsPerSample) { + return _convertTopValue(data.latest(bucketsPerSample)); + } + }); +} + +function _snapshotLatencies(statName, options, data) { + // convert the hits + total latencies into a topValues-style data object. + var hits = data.hits; + var totalLatencies = data.latencies; + + function convertCountsObjects(latencyCounts, hitCounts) { + var mergedCounts = {} + keys(latencyCounts.counts).forEach(function(value) { + mergedCounts[value] = + Math.round(latencyCounts.counts[value] / (hitCounts.counts[value] || 1)); + }); + return {counts: mergedCounts, total: latencyCounts.total / (hitCounts.total || 1)}; + } + + // ...and then convert that object into a snapshot. + return _snapshotTopValues(statName, options, { + get total() { + return convertCountsObjects(totalLatencies.total, hits.total); + }, + history: function(bucketsPerSample, numSamples) { + return mergeArrays( + convertCountsObjects, + totalLatencies.history(bucketsPerSample, numSamples), + hits.history(bucketsPerSample, numSamples)); + }, + latest: function(bucketsPerSample) { + return convertCountsObjects(totalLatencies.latest(bucketsPerSample), hits.latest(bucketsPerSample)); + } + }); +} + +function _snapshotDisconnectTracker(statName, options, data) { + var topValues = {}; + var counts = data.counter; + var uniques = data.uniques; + function topValue(counts, uniques) { + return { + count: counts, + topValues: [{value: "total_disconnects", count: counts}, + {value: "disconnected_userids", count: uniques}] + } + } + return _lazySnapshot({ + get total() { + return topValue(counts.total, uniques.total); + }, + history: function(bucketsPerSample, numSamples) { + return mergeArrays( + topValue, + counts.history(bucketsPerSample, numSamples), + uniques.history(bucketsPerSample, numSamples)); + }, + latest: function(bucketsPerSample) { + return topValue(counts.latest(bucketsPerSample), uniques.latest(bucketsPerSample)); + } + }); +} + +function _generateLogInterestMap(statNames) { + var interests = {}; + statNames.forEach(function(statName) { + var logs = getStatData(statName).logNames; + logs.forEach(function(logName) { + if (! interests[logName]) { + interests[logName] = {}; + } + interests[logName][statName] = true; + }); + }); + return interests; +} + + +// ------------------------------------------------------------------ +// stat generators +// ------------------------------------------------------------------ + +// statSpec has these properties +// name +// dataType - line, topvalues, histogram, etc. +// logNames +// init_f +// update_f +// snapshot_f +// options - object containing any additional data, passed in to to the various functions. + +// init_f gets (statName, options, "live"|"historical") +// update_f gets (statName, options, logName, data, logObject) +// snapshot_f gets (statName, options, data) +function addStat(statSpec) { + var statName = statSpec.name; + if (! getStatData(statName)) { + var initialData = + _callFunction(statSpec.init_f, statName, statSpec.options, LIVE); + _setStatData(statName, { + data: initialData, + }); + } + + var s = getStatData(statName); + + s.options = statSpec.options; + s.name = statName; + s.logNames = statSpec.logNames; + s.dataType = statSpec.dataType; + s.historicalDays = ("historicalDays" in statSpec ? statSpec.historicalDays : 1); + + s.init_f = statSpec.init_f; + s.update_f = statSpec.update_f; + s.snapshot_f = statSpec.snapshot_f; + + function registerInterest(logName) { + if (! _stats().logNamesToInterestedStatNames[logName]) { + _stats().logNamesToInterestedStatNames[logName] = {}; + } + _stats().logNamesToInterestedStatNames[logName][statName] = true; + } + statSpec.logNames.forEach(registerInterest); +} + +function addSimpleCount(statName, historicalDays, logName, filter) { + addStat({ + name: statName, + dataType: "line", + logNames: (logName instanceof Array ? logName : [logName]), + init_f: "_initCount", + update_f: "_updateCount", + snapshot_f: "_snapshotTotal", + options: { filter: filter }, + historicalDays: historicalDays || 1 + }); +} + +function addSimpleSum(statName, historicalDays, logName, filter, fieldName) { + addStat({ + name: statName, + dataType: "line", + logNames: (logName instanceof Array ? logName : [logName]), + init_f: "_initCount", + update_f: "_updateSum", + snapshot_f: "_snapshotTotal", + options: { filter: filter, fieldName: fieldName }, + historicalDays: historicalDays || 1 + }); +} + +function addUniquenessCount(statName, historicalDays, logName, filter, fieldName) { + addStat({ + name: statName, + dataType: "line", + logNames: (logName instanceof Array ? logName : [logName]), + init_f: "_initUniques", + update_f: "_updateUniquenessCount", + snapshot_f: "_snapshotTotal", + options: { filter: filter, fieldName: fieldName }, + historicalDays: historicalDays || 1 + }) +} + +function addTopValuesStat(statName, historicalDays, logName, filter, fieldName, canonicalizer) { + addStat({ + name: statName, + dataType: "topValues", + logNames: (logName instanceof Array ? logName : [logName]), + init_f: "_initTopValues", + update_f: "_updateTopValues", + snapshot_f: "_snapshotTopValues", + options: { filter: filter, fieldName: fieldName, canonicalizer: canonicalizer }, + historicalDays: historicalDays || 1 + }); +} + +function addLatenciesStat(statName, historicalDays, logName, filter, fieldName, latencyFieldName) { + addStat({ + name: statName, + dataType: "topValues", + logNames: (logName instanceof Array ? logName : [logName]), + init_f: "_initLatencies", + update_f: "_updateLatencies", + snapshot_f: "_snapshotLatencies", + options: { filter: filter, fieldName: fieldName, latencyFieldName: latencyFieldName }, + historicalDays: historicalDays || 1 + }); +} + + +// RETURNING USERS + +function _initReturningUsers(statName, options, timescaleType) { + return { cache: {}, uniques: _initUniques(statName, options, timescaleType) }; +} + +function _returningUsersUserId(logObject) { + if (logObject.type == "userjoin") { + return logObject.userId; + } +} + +function _returningUsersUserCreationDate(userId) { + var record = sqlobj.selectSingle('pad_cookie_userids', {id: userId}); + if (record) { + return record.createdDate.getTime(); + } +} + +function _returningUsersAccountId(logObject) { + return logObject.proAccountId; +} + +function _returningUsersAccountCreationDate(accountId) { + var record = sqlobj.selectSingle('pro_accounts', {id: accountId}); + if (record) { + return record.createdDate.getTime(); + } +} + + +function _updateReturningUsers(statName, options, logName, data, logObject) { + var userId = (options.useProAccountId ? _returningUsersAccountId(logObject) : _returningUsersUserId(logObject)); + if (! userId) { return; } + var date = logObject.date; + if (! data.cache[""+userId]) { + var creationTime = (options.useProAccountId ? _returningUsersAccountCreationDate(userId) : _returningUsersUserCreationDate(userId)); + if (! creationTime) { return; } // hm. weird case. + data.cache[""+userId] = creationTime; + } + if (data.cache[""+userId] < date - options.registeredNDaysAgo*24*60*60*1000) { + data.uniques.hit(logObject.date, ""+userId); + } +} +function _snapshotReturningUsers(statName, options, data) { + return _lazySnapshot(data.uniques); +} + +function addReturningUserStat(statName, pastNDays, registeredNDaysAgo) { + addStat({ + name: statName, + dataType: "line", + logNames: ["frontend/padevents"], + init_f: "_initReturningUsers", + update_f: "_updateReturningUsers", + snapshot_f: "_snapshotReturningUsers", + options: { registeredNDaysAgo: registeredNDaysAgo }, + historicalDays: pastNDays + }); +} + +function addReturningProAccountStat(statName, pastNDays, registeredNDaysAgo) { + addStat({ + name: statName, + dataType: "line", + logNames: ["frontend/request"], + init_f: "_initReturningUsers", + update_f: "_updateReturningUsers", + snapshot_f: "_snapshotReturningUsers", + options: { registeredNDaysAgo: registeredNDaysAgo, useProAccountId: true }, + historicalDays: pastNDays + }); +} + + +function addDisconnectStat() { + addStat({ + name: "streaming_disconnects", + dataType: "topValues", + logNames: ["frontend/padevents", "frontend/reconnect", "frontend/disconnected_autopost"], + init_f: "_initDisconnectTracker", + update_f: "_updateDisconnectTracker", + snapshot_f: "_snapshotDisconnectTracker", + options: {} + }); +} + +// PAD STARTUP LATENCY +function _initPadStartupLatency(statName, options, timescaleType) { + return { + recentGets: (timescaleType == LIVE ? new ExpiringMapping(60*1000) : _fakeMap()), + latencies: _initHistogram(statName, options, timescaleType), + } +} + +function _updatePadStartupLatency(statName, options, logName, data, logObject) { + var session = logObject.session; + if (logName == "frontend/request") { + if (! ('padId' in logObject)) { return; } + var padId = logObject.padId; + if (! data.recentGets.get(session)) { + data.recentGets.put(session, {}); + } + data.recentGets.get(session)[padId] = logObject.date; + } + if (logName == "frontend/padevents") { + if (logObject.type != 'userjoin') { return; } + if (! data.recentGets.get(session)) { return; } + var padId = logObject.padId; + var getTime = data.recentGets.get(session)[padId]; + if (! getTime) { return; } + delete data.recentGets.get(session)[padId]; + var latency = logObject.date - getTime; + if (latency < 60*1000) { + // latencies longer than 60 seconds don't represent data we care about for this stat. + data.latencies.hit(logObject.date, latency); + } + } +} + +function _snapshotPadStartupLatency(statName, options, data) { + var latencies = data.latencies; + function convertHistogram(histogram_f) { + var deciles = {}; + [0, 1, 5, 10, 25, 50, 75, 90, 95, 99, 100].forEach(function(pct) { + deciles[""+pct] = histogram_f(pct); + }); + return deciles; + } + return _lazySnapshot({ + latencies: latencies, + get total() { + return convertHistogram(this.latencies.total); + }, + history: function(bucketsPerSample, numSamples) { + return this.latencies.history(bucketsPerSample, numSamples).map(convertHistogram); + }, + latest: function(bucketsPerSample) { + return convertHistogram(this.latencies.latest(bucketsPerSample)); + } + }); +} + +function addPadStartupLatencyStat() { + addStat({ + name: "pad_startup_times", + dataType: "histogram", + logNames: ["frontend/padevents", "frontend/request"], + init_f: "_initPadStartupLatency", + update_f: "_updatePadStartupLatency", + snapshot_f: "_snapshotPadStartupLatency", + options: {} + }); +} + + +function _initSampleTracker(statName, options, timescaleType) { + return { + samples: Array(1440), // 1 hour at 1 sample/minute + nextSample: 0, + numSamples: 0 + } +} + +function _updateSampleTracker(statName, options, logName, data, logObject) { + if (options.filter && ! options.filter(logObject)) { + return; + } + if (options.fieldName && ! (options.fieldName in logObject)) { + return; + } + data.samples[data.nextSample] = (options.fieldName ? logObject[fieldName] : logObject); + data.nextSample++; + data.nextSample %= data.samples.length; + data.numSamples = Math.min(data.samples.length, data.numSamples+1); +} + +function _snapshotSampleTracker(statName, options, data) { + function indexTransform(i) { + return (data.nextSample-data.numSamples+i + data.samples.length) % data.samples.length; + } + var merge_f = options.mergeFunction || function(a, b) { return a+b; } + var process_f = options.processFunction || function(a) { return a; } + function mergeValues(values) { + if (values.length <= 1) { return values[0]; } + var t = values[0]; + for (var i = 1; i < values.length; ++i) { + t = merge_f(values[i], t); + } + return t; + } + return _lazySnapshot({ + get total() { + var t = []; + for (var i = 0; i < data.numSamples; ++i) { + t.push(data.samples[indexTransform(i)]); + } + return process_f(mergeValues(t), t.length); + }, + history: function(bucketsPerSample, numSamples) { + var allSamples = []; + for (var i = data.numSamples-1; i >= Math.max(0, data.numSamples - bucketsPerSample*numSamples); --i) { + allSamples.push(data.samples[indexTransform(i)]); + } + var out = []; + for (var i = 0; i < numSamples && i*bucketsPerSample < allSamples.length; ++i) { + var subArray = []; + for (var j = 0; j < bucketsPerSample && i*bucketsPerSample+j < allSamples.length; ++j) { + subArray.push(allSamples[i*bucketsPerSample+j]); + } + out.push(process_f(mergeValues(subArray), subArray.length)); + } + return out.reverse(); + }, + latest: function(bucketsPerSample) { + var t = []; + for (var i = data.numSamples-1; i >= Math.max(0, data.numSamples-bucketsPerSample); --i) { + t.push(data.samples[indexTransform(i)]); + } + return process_f(mergeValues(t), t.length); + } + }); +} + +function addSampleTracker(statName, logName, filter, fieldName, mergeFunction, processFunction) { + addStat({ + name: statName, + dataType: "histogram", + logNames: (logName instanceof Array ? logName : [logName]), + init_f: "_initSampleTracker", + update_f: "_updateSampleTracker", + snapshot_f: "_snapshotSampleTracker", + options: { filter: filter, fieldName: fieldName, + mergeFunction: mergeFunction, processFunction: processFunction } + }); +} + +function addCometLatencySampleTracker(statName) { + addSampleTracker(statName, "backend/server-events", typeMatcher("streaming-message-latencies"), null, + function(a, b) { + var ret = {}; + ["count", "p50", "p90", "p95", "p99", "max"].forEach(function(key) { + ret[key] = (Number(a[key]) || 0) + (Number(b[key]) || 0); + }); + return ret; + }, + function(v, count) { + if (count == 0) { + return { + "50": 0, "90": 0, "95": 0, "99": 0, "100": 0 + } + } + var ret = {count: v.count}; + ["p50", "p90", "p95", "p99", "max"].forEach(function(key) { + ret[key] = (Number(v[key]) || 0)/(Number(count) || 1); + }); + return {"50": Math.round(ret.p50/1000), + "90": Math.round(ret.p90/1000), + "95": Math.round(ret.p95/1000), + "99": Math.round(ret.p99/1000), + "100": Math.round(ret.max/1000)}; + }); +} + +function addConnectionTypeSampleTracker(statName) { + var caredAboutFields = ["streaming", "longpolling", "shortpolling", "(unconnected)"]; + + addSampleTracker(statName, "backend/server-events", typeMatcher("streaming-connection-count"), null, + function(a, b) { + var ret = {}; + caredAboutFields.forEach(function(k) { + ret[k] = (Number(a[k]) || 0) + (Number(b[k]) || 0); + }); + return ret; + }, + function(v, count) { + if (count == 0) { + return _convertTopValue({total: 0, counts: {}}); + } + var values = {}; + var total = 0; + caredAboutFields.forEach(function(k) { + values[k] = Math.round((Number(v[k]) || 0)/count); + total += values[k]; + }); + values["Total"] = total; + return _convertTopValue({ + total: Math.round(total), + counts: values + }); + }); +} + +// helpers for filter functions + +function expectedHostnames() { + var hostPart = appjet.config.listenHost || "localhost"; + if (appjet.config.listenSecureHost != hostPart) { + hostPart = "("+hostPart+"|"+(appjet.config.listenSecureHost || "localhost")+")"; + } + var ports = []; + if (appjet.config.listenPort != 80) { + ports.push(""+appjet.config.listenPort); + } + if (appjet.config.listenSecurePort != 443) { + ports.push(""+appjet.config.listenSecurePort); + } + var portPart = (ports.length > 0 ? ":("+ports.join("|")+")" : ""); + return hostPart + portPart; +} + +function fieldMatcher(fieldName, fieldValue) { + if (fieldValue instanceof RegExp) { + return function(logObject) { + return fieldValue.test(logObject[fieldName]); + } + } else { + return function(logObject) { + return logObject[fieldName] == fieldValue; + } + } +} + +function typeMatcher(type) { + return fieldMatcher("type", type); +} + +function invertMatcher(f) { + return function(logObject) { + return ! f(logObject); + } +} + +function setupStatsCollector() { + var c; + + function unwatchLog(logName) { + GenericLoggerUtils.clearWrangler(logName.split('/')[1], c.wranglers[logName]); + } + function watchLog(logName) { + c.wranglers[logName] = new Packages.net.appjet.oui.LogWrangler({ + tell: function(lpb) { + c.queue.add({logName: logName, json: lpb.json()}); + } + }); + c.wranglers[logName].watch(logName.split('/')[1]); + } + + c = _stats().liveCollector; + if (c) { + c.watchedLogs.forEach(unwatchLog); + delete c.wrangler; + } else { + c = _stats().liveCollector = {}; + } + c.watchedLogs = keys(_stats().logNamesToInterestedStatNames); + c.queue = new java.util.concurrent.ConcurrentLinkedQueue(); + c.wranglers = {}; + c.watchedLogs.forEach(watchLog); + + if (! c.updateTask || c.updateTask.isDone()) { + c.updateTask = execution.scheduleTask('statistics', "statisticsLiveUpdate", 2000, []); + } +} + +serverhandlers.tasks.statisticsLiveUpdate = function() { + var c = _stats().liveCollector; + try { + while (true) { + var obj = c.queue.poll(); + if (obj != null) { + var statNames = + keys(_stats().logNamesToInterestedStatNames[obj.logName]); + var logObject = fastJSON.parse(obj.json); + statNames.forEach(function(statName) { + var statObject = getStatData(statName); + _callFunction(statObject.update_f, + statName, statObject.options, obj.logName, statObject.data, logObject); + }); + } else { + break; + } + } + } catch (e) { + println("EXCEPTION IN LIVE UPDATE: "+e+" / "+e.fileName+":"+e.lineNumber) + println(exceptionutils.getStackTracePlain(new net.appjet.bodylock.JSRuntimeException(String(e), e.javaException || e.rhinoException))); + } finally { + c.updateTask = execution.scheduleTask('statistics', "statisticsLiveUpdate", 2000, []); + } +} + +function onReset() { + // this gets refilled every reset. + _stats().logNamesToInterestedStatNames = {}; + + // we'll want to keep around the live data, though, so this is conditionally set. + if (! _stats().stats) { + _stats().stats = {}; + } + + addSimpleCount("site_pageviews", 1, "frontend/request", null); + addUniquenessCount("site_unique_ips", 1, "frontend/request", null, "clientAddr"); + + addUniquenessCount("active_user_ids", 1, "frontend/padevents", typeMatcher("userjoin"), "userId"); + addUniquenessCount("active_user_ids_7days", 7, "frontend/padevents", typeMatcher("userjoin"), "userId"); + addUniquenessCount("active_user_ids_30days", 30, "frontend/padevents", typeMatcher("userjoin"), "userId"); + + addUniquenessCount("active_pro_accounts", 1, "frontend/request", invertMatcher(fieldMatcher("proAccountId", undefined)), + "proAccountId"); + addUniquenessCount("active_pro_accounts_7days", 7, "frontend/request", invertMatcher(fieldMatcher("proAccountId", undefined)), + "proAccountId"); + addUniquenessCount("active_pro_accounts_30days", 30, "frontend/request", invertMatcher(fieldMatcher("proAccountId", undefined)), + "proAccountId"); + + + addUniquenessCount("active_pads", 1, "frontend/padevents", typeMatcher("userjoin"), "padId"); + addSimpleCount("new_pads", 1, "frontend/padevents", typeMatcher("newpad")); + + addSimpleCount("chat_messages", 1, "frontend/chat", null); + addUniquenessCount("active_chatters", 1, "frontend/chat", null, "userId"); + + addSimpleCount("exceptions", 1, "frontend/exception", null); + + addSimpleCount("eepnet_trial_downloads", 1, "frontend/eepnet_download_info", null); + + addSimpleSum("revenue", 1, "frontend/billing", typeMatcher("purchase-complete"), "dollars") + + var hostRegExp = new RegExp("^https?:\\/\\/([-a-zA-Z0-9]+.)?"+expectedHostnames()+"\\/"); + addTopValuesStat("top_referers", 1, "frontend/request", + invertMatcher(fieldMatcher( + "referer", hostRegExp)), + "referer"); + + addTopValuesStat("paths_404", 1, "frontend/request", fieldMatcher("statusCode", 404), "path"); + addTopValuesStat("paths_500", 1, "frontend/request", fieldMatcher("statusCode", 500), "path"); + addTopValuesStat("paths_exception", 1, "frontend/exception", null, "path"); + + addTopValuesStat("top_exceptions", 1, ["frontend/exception", "backend/exceptions"], + invertMatcher(fieldMatcher("trace", undefined)), + "trace", function(trace) { + var jstrace = trace.split("\n").filter(function(line) { + return /^\tat JS\$.*?\.js:\d+\)$/.test(line); + }); + if (jstrace.length > 3) { + return "JS Exception:\n"+jstrace.slice(0, 10).join("\n").replace(/\t[^\(]*/g, ""); + } + return trace.split("\n").slice(1, 10).join("\n").replace(/\t/g, ""); + }); + + addReturningUserStat("users_1day_returning_7days", 1, 7); + addReturningUserStat("users_7day_returning_7days", 7, 7); + addReturningUserStat("users_30day_returning_7days", 30, 7); + + addReturningUserStat("users_1day_returning_30days", 1, 30); + addReturningUserStat("users_7day_returning_30days", 7, 30); + addReturningUserStat("users_30day_returning_30days", 30, 30); + + addReturningProAccountStat("pro_accounts_1day_returning_7days", 1, 7); + addReturningProAccountStat("pro_accounts_7day_returning_7days", 7, 7); + addReturningProAccountStat("pro_accounts_30day_returning_7days", 30, 7); + + addReturningProAccountStat("pro_accounts_1day_returning_30days", 1, 30); + addReturningProAccountStat("pro_accounts_7day_returning_30days", 7, 30); + addReturningProAccountStat("pro_accounts_30day_returning_30days", 30, 30); + + + addDisconnectStat(); + addTopValuesStat("disconnect_causes", 1, "frontend/avoidable_disconnects", null, "errorMessage"); + + var staticFileRegExp = /^\/static\/|^\/favicon.ico/; + addLatenciesStat("execution_latencies", 1, "backend/latency", + invertMatcher(fieldMatcher('path', staticFileRegExp)), + "path", "time"); + addLatenciesStat("static_file_latencies", 1, "backend/latency", + fieldMatcher('path', staticFileRegExp), + "path", "time"); + + addUniquenessCount("disconnects_with_clientside_errors", 1, + ["frontend/reconnect", "frontend/disconnected_autopost"], + fieldMatcher("hasClientErrors", true), "uniqueId"); + + addTopValuesStat("imports_exports_counts", 1, "frontend/import-export", + typeMatcher("request"), "direction"); + + addPadStartupLatencyStat(); + + addCometLatencySampleTracker("streaming_latencies"); + addConnectionTypeSampleTracker("streaming_connections"); + // TODO: add more stats here. + + setupStatsCollector(); +} + +//---------------------------------------------------------------- +// Log processing +//---------------------------------------------------------------- + +function _whichStats(statNames) { + var whichStats = _statData(); + var logNamesToInterestedStatNames = _stats().logNamesToInterestedStatNames; + + if (statNames) { + whichStats = {}; + statNames.forEach(function(statName) { whichStats[statName] = getStatData(statName) }); + logNamesToInterestedStatNames = _generateLogInterestMap(statNames); + } + + return [whichStats, logNamesToInterestedStatNames]; +} + +function _initStatDataMap(statNames) { + var [whichStats, logNamesToInterestedStatNames] = _whichStats(statNames); + + var statDataMap = {}; + + function initStat(statName, statObject) { + statDataMap[statName] = + _callFunction(statObject.init_f, statName, statObject.options, HIST); + } + eachProperty(whichStats, initStat); + + return statDataMap; +} + +function _saveStats(day, statDataMap, statNames) { + var [whichStats, logNamesToInterestedStatNames] = _whichStats(statNames); + + function saveStat(statName, statObject) { + var value = _callFunction(statObject.snapshot_f, + statName, statObject.options, statDataMap[statName]).total; + if (typeof(value) != 'object') { + value = {value: value}; + } + _saveStat(day, statName, value); + } + eachProperty(whichStats, saveStat); +} + +function _processSingleDayLogs(day, logNamesToInterestedStatNames, statDataMap) { + var iterators = {}; + keys(logNamesToInterestedStatNames).forEach(function(logName) { + var [prefix, logId] = logName.split("/"); + var fileName = log.logFileName(prefix, logId, day); + if (! fileName) { + _info("No such file: "+logName+" on day "+day); + return; + } + iterators[logName] = fileLineIterator(fileName); + }); + + var numIterators = keys(iterators).length; + if (numIterators == 0) { + _info("No logs to process on day "+day); + return; + } + var sortedLogObjects = new java.util.PriorityQueue(numIterators, + new java.util.Comparator({ + compare: function(o1, o2) { return o1.logObject.date - o2.logObject.date } + })); + + function lineToLogObject(logName, json) { + return {logName: logName, logObject: fastJSON.parse(json)}; + } + + // begin by filling the queue with one object from each log. + eachProperty(iterators, function(logName, iterator) { + if (iterator.hasNext) { + sortedLogObjects.add(lineToLogObject(logName, iterator.next)); + } + }); + + // update with all log objects, in date order (enforced by priority queue). + while (! sortedLogObjects.isEmpty()) { + var nextObject = sortedLogObjects.poll(); + var logName = nextObject.logName; + + keys(logNamesToInterestedStatNames[logName]).forEach(function(statName) { + var statObject = getStatData(statName); + _callFunction(statObject.update_f, + statName, statObject.options, logName, statDataMap[statName], nextObject.logObject); + }); + + // get next entry from this log, if there is one. + if (iterators[logName].hasNext) { + sortedLogObjects.add(lineToLogObject(logName, iterators[logName].next)); + } + } +} + +function processStatsForDay(day, statNames, statDataMap) { + var [whichStats, logNamesToInterestedStatNames] = _whichStats(statNames); + + // process the logs, notifying the right statistics updaters. + _processSingleDayLogs(day, logNamesToInterestedStatNames, statDataMap); +} + +//---------------------------------------------------------------- +// Daily update +//---------------------------------------------------------------- +serverhandlers.tasks.statisticsDailyUpdate = function() { +// do nothing for now. + +// dailyUpdate(); +}; + +function _scheduleNextDailyUpdate() { + // Run at 1:11am every day + var now = +(new Date); + var tomorrow = new Date(now + 1000*60*60*24); + tomorrow.setHours(1); + tomorrow.setMinutes(11); + tomorrow.setMilliseconds(111); + log.info("Scheduling next daily statistics update for: "+tomorrow.toString()); + var delay = +tomorrow - (+(new Date)); + execution.scheduleTask("statistics", "statisticsDailyUpdate", delay, []); +} + +function processStatsAsOfDay(date, statNames) { + var latestDay = noon(new Date(date - 1000*60*60*24)); + + _processLogsForNeededDays(latestDay, statNames); +} + +function _processLogsForNeededDays(latestDay, statNames) { + if (! statNames) { + statNames = getAllStatNames(); + } + var statDataMap = _initStatDataMap(statNames); + + var agesToStats = []; + var atLeastOneStat = true; + for (var i = 0; atLeastOneStat; ++i) { + atLeastOneStat = false; + agesToStats[i] = []; + statNames.forEach(function(statName) { + var statData = getStatData(statName); + if (statData.historicalDays > i) { + atLeastOneStat = true; + agesToStats[i].push(statName); + } + }); + } + agesToStats.pop(); + + for (var i = agesToStats.length-1; i >= 0; --i) { + var day = new Date(+latestDay - i*24*60*60*1000); + processStatsForDay(day, agesToStats[i], statDataMap); + } + _saveStats(latestDay, statDataMap, statNames); +} + +function doDailyUpdate(date) { + var now = (date === undefined ? new Date() : date); + var yesterdayNoon = noon(new Date(+now - 1000*60*60*24)); + + _processLogsForNeededDays(yesterdayNoon); +} + +function dailyUpdate() { + try { + doDailyUpdate(); + } catch (ex) { + log.warn("statistics.dailyUpdate() failed: "+ex.toString()); + } finally { + _scheduleNextDailyUpdate(); + } +} \ No newline at end of file diff --git a/trunk/etherpad/src/etherpad/store/checkout.js b/trunk/etherpad/src/etherpad/store/checkout.js new file mode 100644 index 0000000..2a4d7e7 --- /dev/null +++ b/trunk/etherpad/src/etherpad/store/checkout.js @@ -0,0 +1,300 @@ +/** + * 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. + */ + +import("dateutils"); +import("email.sendEmail"); +import("jsutils.*"); +import("sqlbase.sqlobj"); +import("stringutils"); +import("sync"); + +import("etherpad.globals"); +import("etherpad.globals.*"); +import("etherpad.licensing"); +import("etherpad.utils.*"); + +import("static.js.billing_shared.{billing=>billingJS}"); + +function dollars(x, nocommas) { + if (! x) { return "0.00"; } + var s = String(x); + var dollars = s.split('.')[0]; + var pennies = s.split('.')[1]; + + if (!dollars) { + dollars = "0"; + } + + if (!nocommas && dollars.length > 3) { + var newDollars = []; + newDollars.push(dollars[dollars.length-1]); + + for (var i = 1; i < dollars.length; ++i) { + if (i % 3 == 0) { + newDollars.push(","); + } + newDollars.push(dollars[dollars.length-1-i]); + } + dollars = newDollars.reverse().join(''); + } + + if (!pennies) { + pennies = "00"; + } + + if (pennies.length == 1) { + pennies = pennies + "0"; + } + + if (pennies.length > 2) { + pennies = pennies.substr(0,2); + } + + return [dollars,pennies].join('.'); +} + +function obfuscateCC(x) { + if (x.length == 16 || x.length == 15) { + return stringutils.repeat("X", x.length-4) + x.substr(-4); + } else { + return x; + } +} + + +// validation functions + +function isOnlyDigits(s) { + return /^[0-9]+$/.test(s); +} + +function isOnlyLettersAndSpaces(s) { + return /^[a-zA-Z ]+$/.test(s); +} + +function isLength(s, minLen, maxLen) { + if (maxLen === undefined) { + return (typeof(s) == 'string' && s.length == minLen); + } else { + return (typeof(s) == 'string' && s.length >= minLen && s.length <= maxLen); + } +} + +function errorMissing(validationError, name, description) { + validationError(name, "Please enter a "+description+"."); +} + +function errorTooSomething(validationError, name, description, max, tooWhat, betterAdjective) { + validationError(name, "Your "+description+" is too " + tooWhat + "; please provide a "+description+ + " that is "+max+" characters or "+betterAdjective); +} + +function validateString(validationError, s, name, description, mustExist, maxLength, minLength) { + if (mustExist && ! s) { + errorMissing(validationError, name, description); + } + if (s && s.length > maxLength) { + errorTooSomething(validationError, name, description, maxLength, "long", "shorter"); + } + if (minLength > 0 && s.length < minLength) { + errorTooSomething(validationError, name, description, minLength, "short", "longer"); + } +} + +function validateZip(validationError, s) { + if (! s) { + errorMissing(validationError, 'billingZipCode', "ZIP code"); + } + if (! (/^\d{5}(-\d{4})?$/.test(s))) { + validationError('billingZipCode', "Please enter a valid ZIP code"); + } +} + +function validateBillingCart(validationError, cart) { + var p = cart; + + if (! isOnlyLettersAndSpaces(p.billingFirstName)) { + validationError("billingFirstName", "Name fields may only contain alphanumeric characters."); + } + + if (! isOnlyLettersAndSpaces(p.billingLastName)) { + validationError("billingLastName", "Name fields may only contain alphanumeric characters."); + } + + var validPurchaseTypes = arrayToSet(['creditcard', 'invoice', 'paypal']); + if (! p.billingPurchaseType in validPurchaseTypes) { + validationError("billingPurchaseType", "Please select a valid purchase type.") + } + + switch (p.billingPurchaseType) { + case 'creditcard': + if (! billingJS.validateCcNumber(p.billingCCNumber)) { + validationError("billingCCNumber", "Your card number doesn't appear to be valid."); + } + if (! isOnlyDigits(p.billingExpirationMonth) || + ! isLength(p.billingExpirationMonth, 1, 2)) { + validationError("billingMeta", "Invalid expiration month."); + } + if (! isOnlyDigits(p.billingExpirationYear) || + ! isLength(p.billingExpirationYear, 1, 2)) { + validationError("billingMeta", "Invalid expiration year."); + } + if (Number("20"+p.billingExpirationYear) <= (new Date()).getFullYear() && + Number(p.billingExpirationMonth) < (new Date()).getMonth()+1) { + validationError("billingMeta", "Invalid expiration date."); + } + var ccType = billingJS.getCcType(p.billingCCNumber); + if (! isOnlyDigits(p.billingCSC) || + ! isLength(p.billingCSC, (ccType == 'amex' ? 4 : 3))) { + validationError("billingMeta", "Invalid CSC."); + } + // falling through here! + case 'invoice': + validateString(validationError, p.billingCountry, "billingCountry", "country name", true, 2); + validateString(validationError, p.billingAddressLine1, "billingAddressLine1", "billing address", true, 100); + validateString(validationError, p.billingAddressLine2, "billingAddressLine2", "billing address", false, 100); + validateString(validationError, p.billingCity, "billingCity", "city name", true, 40); + if (p.billingCountry == "US") { + validateString(validationError, p.billingState, "billingState", "state name", true, 2); + validateZip(validationError, p.billingZipCode); + } else { + validateString(validationError, p.billingProvince, "billingProvince", "province name", true, 40, 1); + validateString(validationError, p.billingPostalCode, "billingPostalCode", "postal code", true, 20, 5); + } + } +} + +function _cardType(number) { + var cardType = billingJS.getCcType(number); + switch (cardType) { + case 'visa': + return "Visa"; + case 'amex': + return "Amex"; + case 'disc': + return "Discover"; + case 'mc': + return "MasterCard"; + } +} + +function generatePayInfo(cart) { + var isUs = cart.billingCountry == "US"; + + var payInfo = { + cardType: _cardType(cart.billingCCNumber), + cardNumber: cart.billingCCNumber, + cardExpiration: ""+cart.billingExpirationMonth+"20"+cart.billingExpirationYear, + cardCvv: cart.billingCSC, + + nameSalutation: "", + nameFirst: cart.billingFirstName, + nameMiddle: "", + nameLast: cart.billingLastName, + nameSuffix: "", + + addressStreet: cart.billingAddressLine1, + addressStreet2: cart.billingAddressLine2, + addressCity: cart.billingCity, + addressState: (isUs ? cart.billingState : cart.billingProvince), + addressZip: (isUs ? cart.billingZipCode : cart.billingPostalCode), + addressCountry: cart.billingCountry + } + + return payInfo; +} + +var billingCartFieldMap = { + cardType: {f: ["billingCCNumber"], d: "credit card number"}, + cardNumber: { f: ["billingCCNumber"], d: "credit card number"}, + cardExpiration: { f: ["billingMeta", "billingMeta"], d: "expiration date" }, + cardCvv: { f: ["billingMeta"], d: "card security code" }, + card: { f: ["billingCCNumber", "billingMeta"], d: "credit card"}, + nameFirst: { f: ["billingFirstName"], d: "first name" }, + nameLast: {f: ["billingLastName"], d: "last name" }, + addressStreet: { f: ["billingAddressLine1"], d: "billing address" }, + addressStreet2: { f: ["billingAddressLine2"], d: "billing address" }, + addressCity: { f: ["billingCity"], d: "city" }, + addressState: { f: ["billingState", "billingProvince"], d: "state or province" }, + addressCountry: { f: ["billingCountry"], d: "country" }, + addressZip: { f: ["billingZipCode", "billingPostalCode"], d: "ZIP or postal code" }, + address: { f: ["billingAddressLine1", "billingAddressLine2", "billingCity", "billingState", "billingCountry", "billingZipCode"], d: "address" } +} + +function validateErrorFields(validationError, errorPrefix, fieldList) { + if (fieldList.length > 0) { + var errorMsg; + var errorFields; + errorMsg = errorPrefix + + fieldList.map(function(field) { return billingCartFieldMap[field].d }).join(", ") + + "."; + errorFields = []; + fieldList.forEach(function(field) { + errorFields = errorFields.concat(billingCartFieldMap[field].f); + }); + validationError(errorFields, errorMsg); + } +} + +function guessBillingNames(cart, name) { + if (! cart.billingFirstName && ! cart.billingLastName) { + var nameParts = name.split(/\s+/); + if (nameParts.length == 1) { + cart.billingFirstName = nameParts[0]; + } else { + cart.billingLastName = nameParts[nameParts.length-1]; + cart.billingFirstName = nameParts.slice(0, nameParts.length-1).join(' '); + } + } +} + +function writeToEncryptedLog(s) { + if (! appjet.config["etherpad.billingEncryptedLog"]) { + // no need to log, this probably isn't the live server. + return; + } + var e = net.appjet.oui.Encryptomatic; + sync.callsyncIfTrue(appjet.cache, + function() { return ! appjet.cache.billingEncryptedLog }, + function() { + appjet.cache.billingEncryptedLog = { + writer: new java.io.FileWriter(appjet.config["etherpad.billingEncryptedLog"], true), + key: e.readPublicKey("RSA", new java.io.FileInputStream(appjet.config["etherpad.billingPublicKey"])) + } + }); + var l = appjet.cache.billingEncryptedLog; + sync.callsync(l, function() { + l.writer.write(e.bytesToAscii(e.encrypt( + new java.io.ByteArrayInputStream((new java.lang.String(s)).getBytes("UTF-8")), + l.key))+"\n"); + l.writer.flush(); + }) +} + +function formatExpiration(expiration) { + return dateutils.shortMonths[Number(expiration.substr(0, 2))-1]+" "+expiration.substr(2); +} + +function formatDate(date) { + return dateutils.months[date.getMonth()]+" "+date.getDate()+", "+date.getFullYear(); +} + +function salesEmail(to, from, subject, headers, body) { + sendEmail(to, from, subject, headers, body); + if (globals.isProduction()) { + sendEmail("sales@pad.spline.inf.fu-berlin.de", from, subject, headers, body); + } +} \ No newline at end of file diff --git a/trunk/etherpad/src/etherpad/store/eepnet_checkout.js b/trunk/etherpad/src/etherpad/store/eepnet_checkout.js new file mode 100644 index 0000000..62137d3 --- /dev/null +++ b/trunk/etherpad/src/etherpad/store/eepnet_checkout.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. + */ + +import("email.sendEmail"); +import("sqlbase.sqlobj"); +import("stringutils"); + +import("etherpad.globals"); +import("etherpad.globals.*"); +import("etherpad.licensing"); +import("etherpad.utils.*"); +import("etherpad.store.checkout.*"); + +var COST_PER_USER = 99; +var SUPPORT_COST_PCT = 20; +var SUPPORT_MIN_COST = 50; + +function getPurchaseByEmail(email) { + return sqlobj.selectSingle('checkout_purchase', {email: email}); +} + +function hasEmailAlreadyPurchased(email) { + var purchase = getPurchaseByEmail(email); + return purchase && purchase.licenseKey ? true : false; +} + +function mailLostLicense(email) { + var purchase = getPurchaseByEmail(email); + if (purchase && purchase.licenseKey) { + sendLicenseEmail({ + email: email, + ownerName: purchase.owner, + orgName: purchase.organization, + licenseKey: purchase.licenseKey + }); + } +} + +function _updatePurchaseWithKey(id, key) { + sqlobj.updateSingle('checkout_purchase', {id: id}, {licenseKey: key}); +} + +function updatePurchaseWithReceipt(id, text) { + sqlobj.updateSingle('checkout_purchase', {id: id}, {receiptEmail: text}); +} + +function getPurchaseByInvoiceId(id) { + sqlobj.selectSingle('checkout_purchase', {invoiceId: id}); +} + +function generateLicenseKey(cart) { + var licenseKey = licensing.generateNewKey(cart.ownerName, cart.orgName, null, 2, cart.userCount); + cart.licenseKey = licenseKey; + _updatePurchaseWithKey(cart.customerId, cart.licenseKey); + return licenseKey; +} + +function receiptEmailText(cart) { + return renderTemplateAsString('email/eepnet_purchase_receipt.ejs', { + cart: cart, + dollars: dollars, + obfuscateCC: obfuscateCC + }); +} + +function licenseEmailText(userName, licenseKey) { + return renderTemplateAsString('email/eepnet_license_info.ejs', { + userName: userName, + licenseKey: licenseKey, + isEvaluation: false + }); +} + +function sendReceiptEmail(cart) { + var receipt = cart.receiptEmail || receiptEmailText(cart); + + salesEmail(cart.email, "sales@pad.spline.inf.fu-berlin.de", + "EtherPad: Receipt for "+cart.ownerName+" ("+cart.orgName+")", + {}, receipt); +} + +function sendLicenseEmail(cart) { + var licenseEmail = licenseEmailText(cart.ownerName, cart.licenseKey); + + salesEmail(cart.email, "sales@pad.spline.inf.fu-berlin.de", + "EtherPad: License Key for "+cart.ownerName+" ("+cart.orgName+")", + {}, licenseEmail); +} \ No newline at end of file diff --git a/trunk/etherpad/src/etherpad/store/eepnet_trial.js b/trunk/etherpad/src/etherpad/store/eepnet_trial.js new file mode 100644 index 0000000..570d351 --- /dev/null +++ b/trunk/etherpad/src/etherpad/store/eepnet_trial.js @@ -0,0 +1,241 @@ +/** + * 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. + */ + +import("email.sendEmail"); +import("sqlbase.sqlobj"); +import("sqlbase.sqlcommon"); +import("execution"); + +import("etherpad.sessions.getSession"); +import("etherpad.log"); +import("etherpad.licensing"); +import("etherpad.utils.*"); +import("etherpad.globals.*"); + +//---------------------------------------------------------------- + +function getTrialDays() { + return 30; +} + +function getTrialUserQuota() { + return 100; +} + +function mailLicense(data, licenseKey, expiresDate) { + var toAddr = data.email; + if (isTestEmail(toAddr)) { + toAddr = "blackhole@appjet.com"; + } + var subject = ('EtherPad: Trial License Information for '+ + data.firstName+' '+data.lastName+' ('+data.orgName+')'); + + var emailBody = renderTemplateAsString("email/eepnet_license_info.ejs", { + userName: data.firstName+" "+data.lastName, + licenseKey: licenseKey, + expiresDate: expiresDate, + isEvaluation: true + }); + + sendEmail( + toAddr, + 'sales@pad.spline.inf.fu-berlin.de', + subject, + {}, + emailBody + ); +} + +function mailLostLicense(email) { + var data = sqlobj.selectSingle('eepnet_signups', {email: email}); + var keyInfo = licensing.decodeLicenseInfoFromKey(data.licenseKey); + var expiresDate = keyInfo.expiresDate; + + mailLicense(data, data.licenseKey, expiresDate); +} + +function hasEmailAlreadyDownloaded(email) { + var existingRecord = sqlobj.selectSingle('eepnet_signups', {email: email}); + if (existingRecord) { + return true; + } else { + return false + } +} + +function createAndMailNewLicense(data) { + sqlcommon.inTransaction(function() { + var expiresDate = new Date(+(new Date)+(1000*60*60*24*getTrialDays())); + var licenseKey = licensing.generateNewKey( + data.firstName + ' ' + data.lastName, + data.orgName, + +expiresDate, + licensing.getEditionId('PRIVATE_NETWORK_EVALUATION'), + getTrialUserQuota() + ); + + // confirm key + if (!licensing.isValidKey(licenseKey)) { + throw Error("License key I just created is not valid: "+l); + } + + // Log all this precious info + _logDownloadData(data, licenseKey); + + // Store in database + sqlobj.insert("eepnet_signups", { + firstName: data.firstName, + lastName: data.lastName, + email: data.email, + orgName: data.orgName, + jobTitle: data.jobTitle, + date: new Date(), + signupIp: String(request.clientAddr).substr(0,16), + estUsers: data.estUsers, + licenseKey: licenseKey, + phone: data.phone, + industry: data.industry + }); + + mailLicense(data, licenseKey, expiresDate); + + // Send sales notification + var clientAddr = request.clientAddr; + var initialReferer = getSession().initialReferer; + execution.async(function() { + _sendSalesNotification(data, clientAddr, initialReferer); + }); + + }); // end transaction +} + +function _logDownloadData(data, licenseKey) { + log.custom("eepnet_download_info", { + email: data.email, + firstName: data.firstName, + lastName: data.lastName, + org: data.orgName, + jobTitle: data.jobTitle, + phone: data.phone, + estUsers: data.estUsers, + licenseKey: licenseKey, + ip: request.clientAddr, + industry: data.industry, + referer: getSession().initialReferer + }); +} + +function getWeb2LeadData(data, ip, ref) { + var googleQuery = extractGoogleQuery(ref); + var w2ldata = { + oid: "00D80000000b7ey", + first_name: data.firstName, + last_name: data.lastName, + email: data.email, + company: data.orgName, + title: data.jobTitle, + phone: data.phone, + '00N80000003FYtG': data.estUsers, + '00N80000003FYto': ref, + '00N80000003FYuI': googleQuery, + lead_source: 'EEPNET Download', + industry: data.industry + }; + + if (!isProduction()) { +// w2ldata.debug = "1"; +// w2ldata.debugEmail = "aaron@appjet.com"; + } + + return w2ldata; +} + +function _sendSalesNotification(data, ip, ref) { + var hostname = ipToHostname(ip) || "unknown"; + + var subject = "EEPNET Trial Download: "+[data.orgName, data.firstName + ' ' + data.lastName, data.email].join(" / "); + + var body = [ + "", + "This is an automated message.", + "", + "Somebody downloaded a "+getTrialDays()+"-day trial of EEPNET.", + "", + "This lead should be automatically added to the AppJet salesforce account.", + "", + "Organization: "+data.orgName, + "Industry: "+data.industry, + "Full Name: "+data.firstName + ' ' + data.lastName, + "Job Title: "+data.jobTitle, + "Email: "+data.email, + 'Phone: '+data.phone, + "Est. Users: "+data.estUsers, + "IP Address: "+ip+" ("+hostname+")", + "Session Referer: "+ref, + "" + ].join("\n"); + + var toAddr = 'sales@pad.spline.inf.fu-berlin.de'; + if (isTestEmail(data.email)) { + toAddr = 'blackhole@appjet.com'; + } + sendEmail( + toAddr, + 'sales@pad.spline.inf.fu-berlin.de', + subject, + {'Reply-To': data.email}, + body + ); +} + +function getSalesforceIndustryList() { + return [ + '--None--', + 'Agriculture', + 'Apparel', + 'Banking', + 'Biotechnology', + 'Chemicals', + 'Communications', + 'Construction', + 'Consulting', + 'Education', + 'Electronics', + 'Energy', + 'Engineering', + 'Entertainment', + 'Environmental', + 'Finance', + 'Food & Beverage', + 'Government', + 'Healthcare', + 'Hospitality', + 'Insurance', + 'Machinery', + 'Manufacturing', + 'Media', + 'Not For Profit', + 'Other', + 'Recreation', + 'Retail', + 'Shipping', + 'Technology', + 'Telecommunications', + 'Transportation', + 'Utilities' + ]; +} + diff --git a/trunk/etherpad/src/etherpad/testing/testutils.js b/trunk/etherpad/src/etherpad/testing/testutils.js new file mode 100644 index 0000000..eac7840 --- /dev/null +++ b/trunk/etherpad/src/etherpad/testing/testutils.js @@ -0,0 +1,23 @@ +/** + * 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 assertTruthy(x) { + if (!x) { + throw new Error("assertTruthy failure: "+x); + } +} + + diff --git a/trunk/etherpad/src/etherpad/testing/unit_tests/t0000_test.js b/trunk/etherpad/src/etherpad/testing/unit_tests/t0000_test.js new file mode 100644 index 0000000..9e0e78b --- /dev/null +++ b/trunk/etherpad/src/etherpad/testing/unit_tests/t0000_test.js @@ -0,0 +1,22 @@ +/** + * 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 run() { + return "This is a test test."; +} + + diff --git a/trunk/etherpad/src/etherpad/testing/unit_tests/t0001_sqlbase_transaction_rollback.js b/trunk/etherpad/src/etherpad/testing/unit_tests/t0001_sqlbase_transaction_rollback.js new file mode 100644 index 0000000..96a74e4 --- /dev/null +++ b/trunk/etherpad/src/etherpad/testing/unit_tests/t0001_sqlbase_transaction_rollback.js @@ -0,0 +1,48 @@ +/** + * 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. + */ + +import("sqlbase.sqlcommon.{withConnection,inTransaction,closing}"); +import("sqlbase.sqlobj"); + +import("etherpad.testing.testutils.*"); + +function run() { + + withConnection(function(conn) { + var s = conn.createStatement(); + closing(s, function() { + s.execute("delete from just_a_test"); + }); + }); + + sqlobj.insert("just_a_test", {id: 1, x: "a"}); + + try { // this should fail + inTransaction(function(conn) { + sqlobj.updateSingle("just_a_test", {id: 1}, {id: 1, x: "b"}); + // note: this will be pritned to the console, but that's OK + throw Error(); + }); + } catch (e) {} + + var testRecord = sqlobj.selectSingle("just_a_test", {id: 1}); + + assertTruthy(testRecord.x == "a"); +} + + + + diff --git a/trunk/etherpad/src/etherpad/testing/unit_tests/t0002_license_generation.js b/trunk/etherpad/src/etherpad/testing/unit_tests/t0002_license_generation.js new file mode 100644 index 0000000..67c79d8 --- /dev/null +++ b/trunk/etherpad/src/etherpad/testing/unit_tests/t0002_license_generation.js @@ -0,0 +1,89 @@ +/** + * 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. + */ + +import("stringutils"); +import("sqlbase.sqlobj"); + +import("etherpad.licensing"); + +jimport("java.util.Random"); + +function run() { + var r = new Random(0); + + function testLicense(name, org, expires, editionId, userQuota) { + function keydataString() { + return "{name: "+name+", org: "+org+", expires: "+expires+", editionId: "+editionId+", userQuota: "+userQuota+"}"; + } + var key = licensing.generateNewKey(name, org, expires, editionId, userQuota); + var info = licensing.decodeLicenseInfoFromKey(key); + if (!info) { + println("Generated key does not decode at all: "+keydataString()); + println(" generated key: "+key); + throw new Error("Generated key does not decode at all. See stdout."); + } + function testMatch(name, x, y) { + if (x != y) { + println("key match error ("+name+"): ["+x+"] != ["+y+"]"); + println(" key data: "+keydataString()); + println(" generated key: "+key); + println(" decoded key: "+info.toSource()); + throw new Error(name+" mismatch. see stdout."); + } + } + testMatch("personName", info.personName, name); + testMatch("orgName", info.organizationName, org); + testMatch("expires", +info.expiresDate, +expires); + testMatch("editionName", info.editionName, licensing.getEditionName(editionId)); + testMatch("userQuota", +info.userQuota, +userQuota); + } + + testLicense("aaron", "test", +(new Date)+1000*60*60*24*30, licensing.getEditionId('PRIVATE_NETWORK_EVALUATION'), 1001); + + for (var editionId = 0; editionId < 3; editionId++) { + for (var unlimitedUsers = 0; unlimitedUsers <= 1; unlimitedUsers++) { + for (var noExpiry = 0; noExpiry <= 1; noExpiry++) { + for (var j = 0; j < 100; j++) { + var name = stringutils.randomString(1+r.nextInt(39)); + var org = stringutils.randomString(1+r.nextInt(39)); + var expires = null; + if (noExpiry == 0) { + expires = +(new Date)+(1000*60*60*24*r.nextInt(100)); + } + var userQuota = -1; + if (unlimitedUsers == 1) { + userQuota = r.nextInt(1e6); + } + + testLicense(name, org, expires, editionId, userQuota); + } + } + } + } + + // test that all previously generated keys continue to decode. + var historicalKeys = sqlobj.selectMulti('eepnet_signups', {}, {}); + historicalKeys.forEach(function(d) { + var key = d.licenseKey; + if (key && !licensing.isValidKey(key)) { + throw new Error("Historical license key no longer validates: "+key); + } + }); + +} + + + diff --git a/trunk/etherpad/src/etherpad/testing/unit_tests/t0003_persistent_vars.js b/trunk/etherpad/src/etherpad/testing/unit_tests/t0003_persistent_vars.js new file mode 100644 index 0000000..0898fbe --- /dev/null +++ b/trunk/etherpad/src/etherpad/testing/unit_tests/t0003_persistent_vars.js @@ -0,0 +1,42 @@ +/** + * 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. + */ + +import("sqlbase.persistent_vars"); + +import("stringutils"); + +import("etherpad.testing.testutils.*"); + +function run() { + var varname = stringutils.randomString(50); + var varval = stringutils.randomString(50); + + var x = persistent_vars.get(varname); + assertTruthy(!x); + + persistent_vars.put(varname, varval); + + for (var i = 0; i < 3; i++) { + x = persistent_vars.get(varname); + assertTruthy(x == varval); + } + + persistent_vars.remove(varname); + + var x = persistent_vars.get(varname); + assertTruthy(!x); +} + diff --git a/trunk/etherpad/src/etherpad/testing/unit_tests/t0004_sqlobj.js b/trunk/etherpad/src/etherpad/testing/unit_tests/t0004_sqlobj.js new file mode 100644 index 0000000..7f8c996 --- /dev/null +++ b/trunk/etherpad/src/etherpad/testing/unit_tests/t0004_sqlobj.js @@ -0,0 +1,214 @@ +/** + * 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. + */ + +import("jsutils.*"); +import("stringutils"); + +import("sqlbase.sqlcommon"); +import("sqlbase.sqlobj"); + +import("etherpad.globals.*"); +import("etherpad.testing.testutils.*"); + +function run() { + cleanUpTables(); + testGeneral(); + testAlterColumn(); + cleanUpTables(); +} + +function _getTestTableName() { + return 'sqlobj_unit_test_'+stringutils.randomString(10); +} + +function testGeneral() { + + if (isProduction()) { + return; // we dont run this in productin! + } + + // create a test table + var tableName = _getTestTableName(); + + sqlobj.createTable(tableName, { + id: sqlobj.getIdColspec(), + varChar: 'VARCHAR(128)', + dateTime: sqlobj.getDateColspec("NOT NULL"), + int11: 'INT', + tinyInt: sqlobj.getBoolColspec("DEFAULT 0") + }); + + // add some columns + sqlobj.addColumns(tableName, { + a: 'VARCHAR(256)', + b: 'VARCHAR(256)', + c: 'VARCHAR(256)', + d: 'VARCHAR(256)' + }); + + // drop columns + sqlobj.dropColumn(tableName, 'c'); + sqlobj.dropColumn(tableName, 'd'); + + // list tables and make sure it contains tableName + var l = sqlobj.listTables(); + var found = false; + l.forEach(function(x) { + if (x == tableName) { found = true; } + }); + assertTruthy(found); + + if (sqlcommon.isMysql()) { + for (var i = 0; i < 3; i++) { + ['MyISAM', 'InnoDB'].forEach(function(e) { + sqlobj.setTableEngine(tableName, e); + assertTruthy(e == sqlobj.getTableEngine(tableName)); + }); + } + } + + sqlobj.createIndex(tableName, ['a', 'b']); + sqlobj.createIndex(tableName, ['int11', 'a', 'b']); + + // test null columns + for (var i = 0; i < 10; i++) { + var id = sqlobj.insert(tableName, {dateTime: new Date(), a: null, b: null}); + sqlobj.deleteRows(tableName, {id: id}); + } + + //---------------------------------------------------------------- + // data management + //---------------------------------------------------------------- + + // insert + selectSingle + function _randomDate() { + // millisecond accuracy is lost in DB. + var d = +(new Date); + d = Math.round(d / 1000) * 1000; + return new Date(d); + } + var obj_data_list = []; + for (var i = 0; i < 40; i++) { + var obj_data = { + varChar: stringutils.randomString(20), + dateTime: _randomDate(), + int11: +(new Date) % 10000, + tinyInt: !!(+(new Date) % 2), + a: "foo", + b: "bar" + }; + obj_data_list.push(obj_data); + + var obj_id = sqlobj.insert(tableName, obj_data); + var obj_result = sqlobj.selectSingle(tableName, {id: obj_id}); + + assertTruthy(obj_result.id == obj_id); + keys(obj_data).forEach(function(k) { + var d1 = obj_data[k]; + var d2 = obj_result[k]; + if (k == "dateTime") { + d1 = +d1; + d2 = +d2; + } + if (d1 != d2) { + throw Error("result mismatch ["+k+"]: "+d1+" != "+d2); + } + }); + } + + // selectMulti: no constraints, no options + var obj_result_list = sqlobj.selectMulti(tableName, {}, {}); + assertTruthy(obj_result_list.length == obj_data_list.length); + // orderBy + ['int11', 'a', 'b'].forEach(function(colName) { + obj_result_list = sqlobj.selectMulti(tableName, {}, {orderBy: colName}); + assertTruthy(obj_result_list.length == obj_data_list.length); + for (var i = 1; i < obj_result_list.length; i++) { + assertTruthy(obj_result_list[i-1][colName] <= obj_result_list[i][colName]); + } + + obj_result_list = sqlobj.selectMulti(tableName, {}, {orderBy: "-"+colName}); + assertTruthy(obj_result_list.length == obj_data_list.length); + for (var i = 1; i < obj_result_list.length; i++) { + assertTruthy(obj_result_list[i-1][colName] >= obj_result_list[i][colName]); + } + }); + + // selectMulti: with constraints + var obj_result_list1 = sqlobj.selectMulti(tableName, {tinyInt: true}, {}); + var obj_result_list2 = sqlobj.selectMulti(tableName, {tinyInt: false}, {}); + assertTruthy((obj_result_list1.length + obj_result_list2.length) == obj_data_list.length); + obj_result_list1.forEach(function(o) { + assertTruthy(o.tinyInt == true); + }); + obj_result_list2.forEach(function(o) { + assertTruthy(o.tinyInt == false); + }); + + // updateSingle + obj_result_list1.forEach(function(o) { + o.a = "ttt"; + sqlobj.updateSingle(tableName, {id: o.id}, o); + }); + // update + sqlobj.update(tableName, {tinyInt: false}, {a: "fff"}); + // verify + obj_result_list = sqlobj.selectMulti(tableName, {}, {}); + obj_result_list.forEach(function(o) { + if (o.tinyInt) { + assertTruthy(o.a == "ttt"); + } else { + assertTruthy(o.a == "fff"); + } + }); + + // deleteRows + sqlobj.deleteRows(tableName, {a: "ttt"}); + sqlobj.deleteRows(tableName, {a: "fff"}); + // verify + obj_result_list = sqlobj.selectMulti(tableName, {}, {}); + assertTruthy(obj_result_list.length == 0); +} + +function cleanUpTables() { + // delete testing table (and any other old testing tables) + sqlobj.listTables().forEach(function(t) { + if (t.indexOf("sqlobj_unit_test") == 0) { + sqlobj.dropTable(t); + } + }); +} + +function testAlterColumn() { + var tableName = _getTestTableName(); + + sqlobj.createTable(tableName, { + x: 'INT', + a: 'INT NOT NULL', + b: 'INT NOT NULL' + }); + + if (sqlcommon.isMysql()) { + sqlobj.modifyColumn(tableName, 'a', 'INT'); + sqlobj.modifyColumn(tableName, 'b', 'INT'); + } else { + sqlobj.alterColumn(tableName, 'a', 'NULL'); + sqlobj.alterColumn(tableName, 'b', 'NULL'); + } + + sqlobj.insert(tableName, {a: 5}); +} + diff --git a/trunk/etherpad/src/etherpad/testing/unit_tests/t0005_easysync.js b/trunk/etherpad/src/etherpad/testing/unit_tests/t0005_easysync.js new file mode 100644 index 0000000..9cd3f21 --- /dev/null +++ b/trunk/etherpad/src/etherpad/testing/unit_tests/t0005_easysync.js @@ -0,0 +1,22 @@ +/** + * 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. + */ + + +import("etherpad.collab.ace.easysync2_tests"); + +function run() { + easysync2_tests.runTests(); +} \ No newline at end of file diff --git a/trunk/etherpad/src/etherpad/usage_stats/usage_stats.js b/trunk/etherpad/src/etherpad/usage_stats/usage_stats.js new file mode 100644 index 0000000..59074ed --- /dev/null +++ b/trunk/etherpad/src/etherpad/usage_stats/usage_stats.js @@ -0,0 +1,162 @@ +/** + * 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. + */ + +import("execution"); +import("sqlbase.sqlobj"); +import("sqlbase.sqlcommon"); +import("jsutils.*"); +import("fastJSON"); + +import("etherpad.log"); +import("etherpad.log.frontendLogFileName"); +import("etherpad.statistics.statistics"); +import("fileutils.eachFileLine"); + +jimport("java.lang.System.out.println"); +jimport("java.io.BufferedReader"); +jimport("java.io.FileReader"); +jimport("java.io.File"); +jimport("java.awt.Color"); + +jimport("org.jfree.chart.ChartFactory"); +jimport("org.jfree.chart.ChartUtilities"); +jimport("org.jfree.chart.JFreeChart"); +jimport("org.jfree.chart.axis.DateAxis"); +jimport("org.jfree.chart.axis.NumberAxis"); +jimport("org.jfree.chart.plot.XYPlot"); +jimport("org.jfree.chart.renderer.xy.XYLineAndShapeRenderer"); +jimport("org.jfree.data.time.Day"); +jimport("org.jfree.data.time.TimeSeries"); +jimport("org.jfree.data.time.TimeSeriesCollection"); + +//---------------------------------------------------------------- +// Database reading/writing +//---------------------------------------------------------------- + + +function _listStats(statName) { + return sqlobj.selectMulti('statistics', {name: statName}, {orderBy: '-timestamp'}); +} + +// public accessor +function getStatData(statName) { + return _listStats(statName); +} + +//---------------------------------------------------------------- +// HTML & Graph generating +//---------------------------------------------------------------- + +function respondWithGraph(statName) { + var width = 500; + var height = 300; + if (request.params.size) { + var parts = request.params.size.split('x'); + width = +parts[0]; + height = +parts[1]; + } + + var dataset = new TimeSeriesCollection(); + var hideLegend = true; + + switch (statistics.getStatData(statName).plotType) { + case 'line': + var ts = new TimeSeries(statName); + + _listStats(statName).forEach(function(stat) { + var day = new Day(new java.util.Date(stat.timestamp * 1000)); + ts.addOrUpdate(day, fastJSON.parse(stat.value).value); + }); + dataset.addSeries(ts); + break; + case 'topValues': + hideLegend = false; + var stats = _listStats(statName); + if (stats.length == 0) break; + var latestStat = fastJSON.parse(stats[0].value); + var valuesToWatch = []; + var series = {}; + var nLines = 5; + function forEachFirstN(n, stat, f) { + for (var i = 0; i < Math.min(n, stat.topValues.length); i++) { + f(stat.topValues[i].value, stat.topValues[i].count); + } + } + forEachFirstN(nLines, latestStat, function(value, count) { + valuesToWatch.push(value); + series[value] = new TimeSeries(value); + }); + stats.forEach(function(stat) { + var day = new Day(new java.util.Date(stat.timestamp*1000)); + var statData = fastJSON.parse(stat.value); + valuesToWatch.forEach(function(value) { series[value].addOrUpdate(day, 0); }) + forEachFirstN(nLines, statData, function(value, count) { + if (series[value]) { + series[value].addOrUpdate(day, count); + } + }); + }); + valuesToWatch.forEach(function(value) { + dataset.addSeries(series[value]); + }); + break; + case 'histogram': + hideLegend = false; + var stats = _listStats(statName); + percentagesToGraph = ["50", "90", "100"]; + series = {}; + percentagesToGraph.forEach(function(pct) { + series[pct] = new TimeSeries(pct+"%"); + dataset.addSeries(series[pct]); + }); + if (stats.length == 0) break; + stats.forEach(function(stat) { + var day = new Day(new java.util.Date(stat.timestamp*1000)); + var statData = fastJSON.parse(stat.value); + eachProperty(series, function(pct, timeseries) { + timeseries.addOrUpdate(day, statData[pct] || 0); + }); + }); + break; + } + + var domainAxis = new DateAxis(""); + var rangeAxis = new NumberAxis(); + var renderer = new XYLineAndShapeRenderer(); + + var numSeries = dataset.getSeriesCount(); + var colors = [Color.blue, Color.red, Color.green, Color.orange, Color.pink, Color.magenta]; + for (var i = 0; i < numSeries; ++i) { + renderer.setSeriesPaint(i, colors[i]); + renderer.setSeriesShapesVisible(i, false); + } + + var plot = new XYPlot(dataset, domainAxis, rangeAxis, renderer); + + var chart = new JFreeChart(plot); + chart.setTitle(statName); + if (hideLegend) { + chart.removeLegend(); + } + + var jos = new java.io.ByteArrayOutputStream(); + ChartUtilities.writeChartAsJPEG( + jos, 1.0, chart, width, height); + + response.setContentType('image/jpeg'); + response.writeBytes(jos.toByteArray()); +} + diff --git a/trunk/etherpad/src/etherpad/utils.js b/trunk/etherpad/src/etherpad/utils.js new file mode 100644 index 0000000..da9972f --- /dev/null +++ b/trunk/etherpad/src/etherpad/utils.js @@ -0,0 +1,396 @@ +/** + * 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. + */ + +import("exceptionutils"); +import("fileutils.{readFile,fileLastModified}"); +import("ejs.EJS"); +import("funhtml.*"); +import("stringutils"); +import("stringutils.startsWith"); +import("jsutils.*"); + +import("etherpad.sessions"); +import("etherpad.sessions.getSession"); +import("etherpad.globals.*"); +import("etherpad.helpers"); +import("etherpad.collab.collab_server"); +import("etherpad.pad.model"); +import("etherpad.pro.domains"); +import("etherpad.pne.pne_utils"); +import("etherpad.pro.pro_utils"); +import("etherpad.pro.pro_config"); +import("etherpad.pro.pro_accounts"); +import("etherpad.pro.pro_accounts.getSessionProAccount"); + +jimport("java.lang.System.out.print"); +jimport("java.lang.System.out.println"); + +//---------------------------------------------------------------- +// utilities +//---------------------------------------------------------------- + +// returns globally-unique padId +function randomUniquePadId() { + var id = stringutils.randomString(10); + while (model.accessPadGlobal(id, function(p) { return p.exists(); }, "r")) { + id = stringutils.randomString(10); + } + return id; +} + +//---------------------------------------------------------------- +// template rendering +//---------------------------------------------------------------- + +function renderTemplateAsString(filename, data) { + data = data || {}; + data.helpers = helpers; // global helpers + + var f = "/templates/"+filename; + if (! appjet.scopeCache.ejs) { + appjet.scopeCache.ejs = {}; + } + var cacheObj = appjet.scopeCache.ejs[filename]; + if (cacheObj === undefined || fileLastModified(f) > cacheObj.mtime) { + var templateText = readFile(f); + cacheObj = {}; + cacheObj.tmpl = new EJS({text: templateText, name: filename}); + cacheObj.mtime = fileLastModified(f); + appjet.scopeCache.ejs[filename] = cacheObj; + } + var html = cacheObj.tmpl.render(data); + return html; +} + +function renderTemplate(filename, data) { + response.write(renderTemplateAsString(filename, data)); + if (request.acceptsGzip) { + response.setGzip(true); + } +} + +function renderHtml(bodyFileName, data) { + var bodyHtml = renderTemplateAsString(bodyFileName, data); + response.write(renderTemplateAsString("html.ejs", {bodyHtml: bodyHtml})); + if (request.acceptsGzip) { + response.setGzip(true); + } +} + +function renderFramedHtml(contentHtml) { + var getContentHtml; + if (typeof(contentHtml) == 'function') { + getContentHtml = contentHtml; + } else { + getContentHtml = function() { return contentHtml; } + } + + var template = "framed/framedpage.ejs"; + if (isProDomainRequest()) { + template = "framed/framedpage-pro.ejs"; + } + + renderHtml(template, { + renderHeader: renderMainHeader, + renderFooter: renderMainFooter, + getContentHtml: getContentHtml, + isProDomainRequest: isProDomainRequest(), + renderGlobalProNotice: pro_utils.renderGlobalProNotice + }); +} + +function renderFramed(bodyFileName, data) { + function _getContentHtml() { + return renderTemplateAsString(bodyFileName, data); + } + renderFramedHtml(_getContentHtml); +} + +function renderFramedError(error) { + var content = DIV({className: 'fpcontent'}, + DIV({style: "padding: 2em 1em;"}, + DIV({style: "padding: 1em; border: 1px solid #faa; background: #fdd;"}, + B("Error: "), error))); + renderFramedHtml(content); +} + +function renderNotice(bodyFileName, data) { + renderNoticeString(renderTemplateAsString(bodyFileName, data)); +} + +function renderNoticeString(contentHtml) { + renderFramed("notice.ejs", {content: contentHtml}); +} + +function render404(noStop) { + response.reset(); + response.setStatusCode(404); + renderFramedHtml(DIV({className: "fpcontent"}, + DIV({style: "padding: 2em 1em;"}, + DIV({style: "border: 1px solid #aaf; background: #def; padding: 1em; font-size: 150%;"}, + "404 not found: "+request.path)))); + if (! noStop) { + response.stop(); + } +} + +function render500(ex) { + response.reset(); + response.setStatusCode(500); + var trace = null; + if (ex && (!isProduction())) { + trace = exceptionutils.getStackTracePlain(ex); + } + renderFramed("500_body.ejs", {trace: trace}); +} + +function _renderEtherpadDotComHeader(data) { + if (!data) { + data = {selected: ''}; + } + data.html = stringutils.html; + data.UL = UL; + data.LI = LI; + data.A = A; + data.isPNE = isPrivateNetworkEdition(); + return renderTemplateAsString("framed/framedheader.ejs", data); +} + +function _renderProHeader(data) { + if (!pro_accounts.isAccountSignedIn()) { + return '
     
    '; + } + + var r = domains.getRequestDomainRecord(); + if (!data) { data = {}; } + data.navSelection = (data.navSelection || appjet.requestCache.proTopNavSelection || ''); + data.proDomainOrgName = pro_config.getConfig().siteName; + data.isPNE = isPrivateNetworkEdition(); + data.account = getSessionProAccount(); + data.validLicense = pne_utils.isServerLicensed(); + data.pneTrackerHtml = pne_utils.pneTrackerHtml(); + data.isAnEtherpadAdmin = sessions.isAnEtherpadAdmin(); + data.fullSuperdomain = pro_utils.getFullSuperdomainHost(); + return renderTemplateAsString("framed/framedheader-pro.ejs", data); +} + +function renderMainHeader(data) { + if (isProDomainRequest()) { + return _renderProHeader(data); + } else { + return _renderEtherpadDotComHeader(data); + } +} + +function renderMainFooter() { + return renderTemplateAsString("framed/framedfooter.ejs", { + isProDomainRequest: isProDomainRequest() + }); +} + +//---------------------------------------------------------------- +// isValidEmail +//---------------------------------------------------------------- + +// TODO: make better and use the better version on the client in +// various places as well (pad.js and etherpad.js) +function isValidEmail(x) { + return (x && + ((x.length > 0) && + (x.match(/^[\w\.\_\+\-]+\@[\w\_\-]+\.[\w\_\-\.]+$/)))); +} + +//---------------------------------------------------------------- + +function timeAgo(d, now) { + if (!now) { now = new Date(); } + + function format(n, word) { + n = Math.round(n); + return ('' + n + ' ' + word + (n != 1 ? 's' : '') + ' ago'); + } + + d = (+now - (+d)) / 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'); +}; + + +//---------------------------------------------------------------- +// linking to a set of new CGI parameters +//---------------------------------------------------------------- +function qpath(m) { + var q = {}; + if (request.query) { + request.query.split('&').forEach(function(kv) { + if (kv) { + var parts = kv.split('='); + q[parts[0]] = parts[1]; + } + }); + } + eachProperty(m, function(k,v) { + q[k] = v; + }); + var r = request.path + '?'; + eachProperty(q, function(k,v) { + if (v !== undefined && v !== null) { + r += ('&' + k + '=' + v); + } + }); + return r; +} + +//---------------------------------------------------------------- + +function ipToHostname(ip) { + var DNS = Packages.org.xbill.DNS; + + if (!DNS.Address.isDottedQuad(ip)) { + return null + } + + try { + var addr = DNS.Address.getByAddress(ip); + return DNS.Address.getHostName(addr); + } catch (ex) { + return null; + } +} + +function extractGoogleQuery(ref) { + ref = String(ref); + ref = ref.toLowerCase(); + if (!(ref.indexOf("google") >= 0)) { + return ""; + } + + ref = ref.split('?')[1]; + + var q = ""; + ref.split("&").forEach(function(x) { + var parts = x.split("="); + if (parts[0] == "q") { + q = parts[1]; + } + }); + + q = decodeURIComponent(q); + q = q.replace(/\+/g, " "); + + return q; +} + +function isTestEmail(x) { + return (x.indexOf("+appjetseleniumtest+") >= 0); +} + +function isPrivateNetworkEdition() { + return pne_utils.isPNE(); +} + +function isProDomainRequest() { + return pro_utils.isProDomainRequest(); +} + +function hasOffice() { + return appjet.config["etherpad.soffice"] || appjet.config["etherpad.sofficeConversionServer"]; +} + +////////// console progress bar + +function startConsoleProgressBar(barWidth, updateIntervalSeconds) { + barWidth = barWidth || 40; + updateIntervalSeconds = ((typeof updateIntervalSeconds) == "number" ? updateIntervalSeconds : 1.0); + + var unseenStatus = null; + var lastPrintTime = 0; + var column = 0; + + function replaceLineWith(str) { + //print((new Array(column+1)).join('\b')+str); + print('\r'+str); + column = str.length; + } + + var bar = { + update: function(frac, msg, force) { + var t = +new Date(); + if ((!force) && ((t - lastPrintTime)/1000 < updateIntervalSeconds)) { + unseenStatus = {frac:frac, msg:msg}; + } + else { + var pieces = []; + pieces.push(' ', (' '+Math.round(frac*100)).slice(-3), '%', ' ['); + var barEndLoc = Math.max(0, Math.min(barWidth-1, Math.floor(frac*barWidth))); + for(var i=0;i'); + else pieces.push(' '); + } + pieces.push('] ', msg || ''); + replaceLineWith(pieces.join('')); + + unseenStatus = null; + lastPrintTime = t; + } + }, + finish: function() { + if (unseenStatus) { + bar.update(unseenStatus.frac, unseenStatus.msg, true); + } + println(); + } + }; + + println(); + bar.update(0, null, true); + + return bar; +} + +function isStaticRequest() { + return (startsWith(request.path, '/static/') || + startsWith(request.path, '/favicon.ico') || + startsWith(request.path, '/robots.txt')); +} + +function httpsHost(h) { + h = h.split(":")[0]; // strip any existing port + if (appjet.config.listenSecurePort != "443") { + h = (h + ":" + appjet.config.listenSecurePort); + } + return h; +} + +function httpHost(h) { + h = h.split(":")[0]; // strip any existing port + if (appjet.config.listenPort != "80") { + h = (h + ":" + appjet.config.listenPort); + } + return h; +} + +function toJavaException(e) { + var exc = ((e instanceof java.lang.Throwable) && e) || e.rhinoException || e.javaException || + new java.lang.Throwable(e.message+"/"+e.fileName+"/"+e.lineNumber); + return exc; +} diff --git a/trunk/etherpad/src/main.js b/trunk/etherpad/src/main.js new file mode 100644 index 0000000..503bf9d --- /dev/null +++ b/trunk/etherpad/src/main.js @@ -0,0 +1,418 @@ +/** + * 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. + */ + +import("dispatch.{Dispatcher,PrefixMatcher,DirMatcher,forward}"); +import("exceptionutils"); +import("fastJSON"); +import("jsutils.*"); +import("sqlbase.sqlcommon"); +import("stringutils"); +import("sessions.{readLatestSessionsFromDisk,writeSessionsToDisk}"); + +import("etherpad.billing.team_billing"); +import("etherpad.globals.*"); +import("etherpad.log.{logRequest,logException}"); +import("etherpad.log"); +import("etherpad.utils.*"); +import("etherpad.statistics.statistics"); +import("etherpad.sessions"); +import("etherpad.db_migrations.migration_runner"); +import("etherpad.importexport.importexport"); +import("etherpad.legacy_urls"); + +import("etherpad.control.aboutcontrol"); +import("etherpad.control.admincontrol"); +import("etherpad.control.blogcontrol"); +import("etherpad.control.connection_diagnostics_control"); +import("etherpad.control.global_pro_account_control"); +import("etherpad.control.historycontrol"); +import("etherpad.control.loadtestcontrol"); +import("etherpad.control.maincontrol"); +import("etherpad.control.pad.pad_control"); +import("etherpad.control.pne_manual_control"); +import("etherpad.control.pne_tracker_control"); +import("etherpad.control.pro.admin.license_manager_control"); +import("etherpad.control.pro_beta_control"); +import("etherpad.control.pro.pro_main_control"); +import("etherpad.control.pro_signup_control"); +import("etherpad.control.scriptcontrol"); +import("etherpad.control.static_control"); +import("etherpad.control.store.storecontrol"); +import("etherpad.control.testcontrol"); + +import("etherpad.pne.pne_utils"); +import("etherpad.pro.pro_pad_editors"); +import("etherpad.pro.pro_utils"); +import("etherpad.pro.pro_config"); + +import("etherpad.collab.collabroom_server"); +import("etherpad.collab.collab_server"); +import("etherpad.collab.readonly_server"); +import("etherpad.collab.genimg"); +import("etherpad.pad.model"); +import("etherpad.pad.dbwriter"); +import("etherpad.pad.pad_migrations"); +import("etherpad.pad.noprowatcher"); + +jimport("java.lang.System.out.println"); + +serverhandlers.startupHandler = function() { + // Order matters. + checkSystemRequirements(); + + var sp = function(k) { return appjet.config['etherpad.SQL_'+k] || null; }; + sqlcommon.init(sp('JDBC_DRIVER'), sp('JDBC_URL'), sp('USERNAME'), sp('PASSWORD')); + + log.onStartup(); + statistics.onStartup(); + migration_runner.onStartup(); + pad_migrations.onStartup(); + model.onStartup(); + collab_server.onStartup(); + pad_control.onStartup(); + dbwriter.onStartup(); + blogcontrol.onStartup(); + importexport.onStartup(); + pro_pad_editors.onStartup(); + noprowatcher.onStartup(); + team_billing.onStartup(); + collabroom_server.onStartup(); + readLatestSessionsFromDisk(); +}; + +serverhandlers.resetHandler = function() { + statistics.onReset(); +} + +serverhandlers.shutdownHandler = function() { + appjet.cache.shutdownHandlerIsRunning = true; + + log.callCatchingExceptions(writeSessionsToDisk); + log.callCatchingExceptions(dbwriter.onShutdown); + log.callCatchingExceptions(sqlcommon.onShutdown); + log.callCatchingExceptions(pro_pad_editors.onShutdown); +}; + +//---------------------------------------------------------------- +// request handling +//---------------------------------------------------------------- + +serverhandlers.requestHandler = function() { + checkRequestIsWellFormed(); + sessions.preRequestCookieCheck(); + checkHost(); + checkHTTPS(); + handlePath(); +}; + +// In theory, this should never get called. +// Exceptions that are thrown in frontend etherpad javascript should +// always be caught and treated specially. +// If serverhandlers.errorHandler gets called, then it's a bug in the frontend. +serverhandlers.errorHandler = function(ex) { + logException(ex); + response.setStatusCode(500); + if (request.isDefined) { + render500(ex); + } else { + if (! isProduction()) { + response.write(exceptionutils.getStackTracePlain(ex)); + } else { + response.write(ex.getMessage()); + } + } +}; + +serverhandlers.postRequestHandler = function() { + logRequest(); +}; + +//---------------------------------------------------------------- +// Scheduled tasks +//---------------------------------------------------------------- + +serverhandlers.tasks.writePad = function(globalPadId) { + dbwriter.taskWritePad(globalPadId); +}; +serverhandlers.tasks.flushPad = function(globalPadId, reason) { + dbwriter.taskFlushPad(globalPadId, reason); +}; +serverhandlers.tasks.checkForStalePads = function() { + dbwriter.taskCheckForStalePads(); +}; +serverhandlers.tasks.statisticsDailyUpdate = function() { + //statistics.dailyUpdate(); +}; +serverhandlers.tasks.doSlowFileConversion = function(from, to, bytes, cont) { + return importexport.doSlowFileConversion(from, to, bytes, cont); +}; +serverhandlers.tasks.proPadmetaFlushEdits = function(domainId) { + pro_pad_editors.flushEditsNow(domainId); +}; +serverhandlers.tasks.noProWatcherCheckPad = function(globalPadId) { + noprowatcher.checkPad(globalPadId); +}; +serverhandlers.tasks.collabRoomDisconnectSocket = function(connectionId, socketId) { + collabroom_server.disconnectDefunctSocket(connectionId, socketId); +}; + +//---------------------------------------------------------------- +// cometHandler() +//---------------------------------------------------------------- + +serverhandlers.cometHandler = function(op, id, data) { + checkRequestIsWellFormed(); + if (!data) { + // connect/disconnect message, notify all comet receivers + collabroom_server.handleComet(op, id, data); + return; + } + + while (data[data.length-1] == '\u0000') { + data = data.substr(0, data.length-1); + } + + var wrapper; + try { + wrapper = fastJSON.parse(data); + } catch (err) { + try { + // after removing \u0000 might have to add '}' + wrapper = fastJSON.parse(data+'}'); + } + catch (err) { + log.custom("invalid-json", {data: data}); + throw err; + } + } + if(wrapper.type == "COLLABROOM") { + collabroom_server.handleComet(op, id, wrapper.data); + } else { + //println("incorrectly wrapped data: " + wrapper['type']); + } +}; + +//---------------------------------------------------------------- +// sarsHandler() +//---------------------------------------------------------------- + +serverhandlers.sarsHandler = function(str) { + str = String(str); + println("sarsHandler: parsing JSON string (length="+str.length+")"); + var message = fastJSON.parse(str); + println("dispatching SARS message of type "+message.type); + if (message.type == "migrateDiagnosticRecords") { + pad_control.recordMigratedDiagnosticInfo(message.records); + return 'OK'; + } + return 'UNKNOWN_MESSAGE_TYPE'; +}; + +//---------------------------------------------------------------- +// checkSystemRequirements() +//---------------------------------------------------------------- +function checkSystemRequirements() { + var jv = Packages.java.lang.System.getProperty("java.version"); + jv = +(String(jv).split(".").slice(0,2).join(".")); + if (jv < 1.6) { + println("Error: EtherPad requires JVM 1.6 or greater."); + println("Your version of the JVM is: "+jv); + println("Aborting..."); + Packages.java.lang.System.exit(1); + } +} + +function checkRequestIsWellFormed() { + // We require the "host" field to be present. + // This should always be true, as long as the protocl is HTTP/1.1 + // TODO: check (request.protocol != "HTTP/1.1") + if (request.isDefined && !request.host) { + response.setStatusCode(505); + response.setContentType('text/plain'); + response.write('Protocol not supported. HTTP/1.1 required.'); + response.stop(); + } +} + +//---------------------------------------------------------------- +// checkHost() +//---------------------------------------------------------------- +function checkHost() { + if (appjet.config['etherpad.skipHostnameCheck'] == "true") { + return; + } + + if (isPrivateNetworkEdition()) { + return; + } + + // we require the domain to either be or a pro domain request. + if (SUPERDOMAINS[request.domain]) { + return; + } + if (pro_utils.isProDomainRequest()) { + return; + } + + // redirect to pad.spline.inf.fu-berlin.de + var newurl = "http://pad.spline.inf.fu-berlin.de/"+request.path; + if (request.query) { newurl += "?"+request.query; } + response.redirect(newurl); +} + +//---------------------------------------------------------------- +// checkHTTPS() +//---------------------------------------------------------------- + +// Check for HTTPS +function checkHTTPS() { + /* Open-source note: this function used to check the protocol and make + * sure that pages that needed to be secure went over HTTPS, and pages + * that didn't go over HTTP. However, when we open-sourced the code, + * we disabled HTTPS because we didn't want to ship the pad.spline.inf.fu-berlin.de + * private crypto keys. --aiba */ + return; + + + if (stringutils.startsWith(request.path, "/static/")) { return; } + + if (sessions.getSession().disableHttps || request.params.disableHttps) { + sessions.getSession().disableHttps = true; + println("setting session diableHttps"); + return; + } + + var _ports = { + http: appjet.config.listenPort, + https: appjet.config.listenSecurePort + }; + var _defaultPorts = { + http: 80, + https: 443 + }; + var _requiredHttpsPrefixes = [ + '/ep/admin', // pro and etherpad + '/ep/account', // pro only + ]; + + var httpsRequired = false; + _requiredHttpsPrefixes.forEach(function(p) { + if (stringutils.startsWith(request.path, p)) { + httpsRequired = true; + } + }); + + if (isProDomainRequest() && pro_config.getConfig().alwaysHttps) { + httpsRequired = true; + } + + if (httpsRequired && !request.isSSL) { + _redirectToScheme("https"); + } + if (!httpsRequired && request.isSSL) { + _redirectToScheme("http"); + } + + function _redirectToScheme(scheme) { + var url = scheme + "://"; + url += request.host.split(':')[0]; // server + + if (_ports[scheme] != _defaultPorts[scheme]) { + url += ':'+_ports[scheme]; + } + + url += request.path; + if (request.query) { + url += "?"+request.query; + } + response.redirect(url); + } +} + +//---------------------------------------------------------------- +// dispatching +//---------------------------------------------------------------- + +function handlePath() { + // Default. Can be overridden in case of static files. + response.neverCache(); + + // these paths are handled identically on all sites/subdomains. + var commonDispatcher = new Dispatcher(); + commonDispatcher.addLocations([ + ['/favicon.ico', forward(static_control)], + ['/robots.txt', forward(static_control)], + ['/crossdomain.xml', forward(static_control)], + [PrefixMatcher('/static/'), forward(static_control)], + [PrefixMatcher('/ep/genimg/'), genimg.renderPath], + [PrefixMatcher('/ep/pad/'), forward(pad_control)], + [PrefixMatcher('/ep/script/'), forward(scriptcontrol)], + [/^\/([^\/]+)$/, pad_control.render_pad], + [DirMatcher('/ep/unit-tests/'), forward(testcontrol)], + [DirMatcher('/ep/pne-manual/'), forward(pne_manual_control)], + ]); + + var etherpadDotComDispatcher = new Dispatcher(); + etherpadDotComDispatcher.addLocations([ + ['/', maincontrol.render_main], + [DirMatcher('/ep/beta-account/'), forward(pro_beta_control)], + [DirMatcher('/ep/pro-signup/'), forward(pro_signup_control)], + [DirMatcher('/ep/about/'), forward(aboutcontrol)], + [DirMatcher('/ep/admin/'), forward(admincontrol)], + [DirMatcher('/ep/blog/posts/'), blogcontrol.render_post], + [DirMatcher('/ep/blog/'), forward(blogcontrol)], + [DirMatcher('/ep/connection-diagnostics/'), forward(connection_diagnostics_control)], + [DirMatcher('/ep/loadtest/'), forward(loadtestcontrol)], + [DirMatcher('/ep/tpne/'), forward(pne_tracker_control)], + [DirMatcher('/ep/pro-account/'), forward(global_pro_account_control)], + [/^\/ep\/pad\/history\/(\w+)\/(.*)$/, historycontrol.render_history], + [PrefixMatcher('/ep/pad/slider/'), pad_control.render_slider], + [DirMatcher('/ep/store/'), forward(storecontrol)], + [PrefixMatcher('/ep/'), forward(maincontrol)] + ]); + + var proDispatcher = new Dispatcher(); + proDispatcher.addLocations([ + ['/', pro_main_control.render_main], + [PrefixMatcher('/ep/'), forward(pro_main_control)], + ]); + + // dispatching logic: first try common, then dispatch to + // pad.spline.inf.fu-berlin.de or pro. + + if (commonDispatcher.dispatch()) { + return; + } + + // Check if there is a pro domain associated with this request. + if (isProDomainRequest()) { + pro_utils.preDispatchAccountCheck(); + if (proDispatcher.dispatch()) { + return; + } + } else { + if (etherpadDotComDispatcher.dispatch()) { + return; + } + } + + if (!isProDomainRequest()) { + legacy_urls.checkPath(); + } + + render404(); +} + 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 @@ + + + + + + + + + + + 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 Binary files /dev/null and b/trunk/etherpad/src/static/favicon.ico 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/about/appjet-logo-large.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/about/appjet-logo-medium.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/about/investors/mitchkapor.jpg 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/about/investors/pb.jpg 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/about/investors/pg.jpg 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/about/investors/sanjeev.jpg 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/about/investors/seth.jpg 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/about/people/aaron-david-iphones-thumb.jpg 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/about/people/aaron-david-iphones.jpg 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/about/people/aaron-google-air.jpg 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/about/people/aaron-headshot-thumb.jpg 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/about/people/aaron-headshot.jpg 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/about/people/aaron-headshot2-thumb.jpg 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/about/people/aaron-headshot2.jpg 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/about/people/aaron-headshot3-thumb.jpg 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/about/people/aaron-headshot3.jpg 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/about/people/daniel-headshot-thumb.jpg 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/about/people/david-headshot-thumb.jpg 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/about/people/david-headshot.jpg 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/about/people/davy-headshot.jpg 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/about/people/jd-headshot-thumb.jpg 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/about/people/jd-headshot.jpg 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/about/people/rhonda-headshot-thumb.jpg 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/about/people/rhonda-headshot.jpg 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/about/pier38.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/about/quote-close.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/about/quote-open.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/about/screencastpreview800x600.jpg 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/account/betawarn.jpg 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/acecarets/000000.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/acecarets/666666.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/acecarets/999999.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/acecarets/default.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/apr09/backgrad.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/apr09/black35.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/apr09/blank.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/apr09/modalbar.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/apr09/newpadicon.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/apr09/shadbot.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/apr09/shadleft.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/apr09/shadleftbot.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/apr09/shadlefttop.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/apr09/shadright.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/apr09/shadrightbot.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/apr09/shadrighttop.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/apr09/topbar.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/apr09/topbarlogo.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/apr09/widthfull.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/apr09/widthfullactive.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/apr09/widthlim.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/apr09/widthlimactive.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/billing/amex.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/billing/creditcard.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/billing/csc-help.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/billing/disc.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/billing/invoice.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/billing/mc.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/billing/paypal.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/billing/visa.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/blog/posts/new-features/fullwidth.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/blog/posts/new-features/importexport.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/blog/posts/new-features/richtext.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/blog/posts/new-features/viewzoom.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/blog/posts/pricing-survey-results.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/blog/posts/pricing-survey.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/blog/posts/time-slider-screenshot.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/davy/bg/home-createpad.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/davy/bg/home-features-bottom.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/davy/bg/home-features-free-bottom.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/davy/bg/home-features-paid-top.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/davy/bg/home-features-top.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/davy/bg/home-nav-selected.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/davy/bg/home-screencast.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/davy/bg/home2.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/davy/bg/product-nav-selected-white.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/davy/bg/product-nav-selected.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/davy/bg/product.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/davy/btn/createpad-home.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/davy/btn/createpad-large.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/davy/btn/createpad-small.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/davy/btn/intro-screencast.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/davy/btn/intro-testimonials.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/davy/btn/learnmore.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/davy/btn/signup-home-2.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/davy/btn/signup-home-3.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/davy/btn/signup-home-4.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/davy/btn/signup-home.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/davy/btn/uses-more.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/davy/gfx/32/114.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/davy/gfx/32/15.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/davy/gfx/32/65.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/davy/gfx/32/78.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/davy/gfx/bullet.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/davy/gfx/home-logo2.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/davy/gfx/home-screencast.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/davy/gfx/plane.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/davy/gfx/product-logo.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/davy/gfx/screenshot.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/davy/gfx/use-meetings.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/davy/gfx/use-meetings.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/davy/gfx/use-programming.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/davy/gfx/use-programming.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/davy/gfx/use-writing.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/davy/gfx/use-writing.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/davy/txt/home-button.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/featuretour/code.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/featuretour/edits.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/featuretour/editsandusers.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/featuretour/padlock.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/featuretour/revisions.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/featuretour/users.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/feb09/framedheaderback.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/feb09/framedheaderlogo.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/feb09/home_firstp.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/feb09/home_firstp.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/feb09/home_firstp2.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/feb09/home_h1.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/feb09/home_h1.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/feb09/home_newpadbutton.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/feb09/home_newpadbutton.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/feb09/home_newpadbutton2.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/feb09/home_newpadbutton_eepnet.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/feb09/hometop_back.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/feb09/nav1.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/feb09/nav1_back.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/feb09/nav2.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/feb09/screencast.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/home/etherpad-mainheader1.jpg 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/home/headergradient.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/home/homeheader1.jpg 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/home/homeheader2.jpg 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/home/leftgrad.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/home/pencilpaperback.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/home/screencapture1.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/home/underdevicon.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/icon/downarrow.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/icon/feed.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/backgrad.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/bottomareagfx.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/colorpicker.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/connectingbar.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/connectionindicator.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/docbarstates.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/docbarstates2.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/docbarstates3.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/docpaneledge.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/docpaneledge2.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/docpanelmiddle.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/docpanelmiddle2.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/editbar.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/editbar2.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/editbar3.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/editbar3.xcf 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/editbarback.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/feedbackbox2.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/fileicons.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/hdraggie.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/inviteshare.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/inviteshare2.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/layoutbuttons.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/overlay.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/overlay2.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/padtop.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/padtop2.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/padtop3.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/padtop4.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/padtop4.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/padtop4.xcf 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/padtop5.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/padtop5.xcf 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/padtopback.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/padtopback2.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/protop.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/protop.xcf 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/public.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/savedrevarrows.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/savedrevsgfx2.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/sharebox2.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/sharebox3.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/sharebox4.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/sharedistri.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/syncdone.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/syncing.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/syncing2.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/jun09/pad/viewbargfx.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-gloss-cyan-menu-item-hover.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-gloss-menu-item-hover.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-gloss-semitransparent-menu-item-hover.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-human-menu-item-hover.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-osx-menu-item-hover.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-vista-bg.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-vista-menu-item-hover.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/lib/jquery.contextmenu.images/cmenu-xp-bg.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/may09/bold.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/may09/doc.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/may09/doc.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/may09/html.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/may09/html.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/may09/italic.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/may09/leftarrow.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/may09/leftarrow2.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/may09/link.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/may09/link.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/may09/odt.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/may09/odt.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/may09/padlock.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/may09/padlockopen.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/may09/passwordlocked.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/may09/passwordlocked_cropped.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/may09/passwordnone.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/may09/paypal.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/may09/pdf.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/may09/pdf.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/may09/redo.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/may09/txt.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/may09/txt.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/may09/underline.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/may09/undo.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/miniplane.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/misc/diagnostic-links.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/misc/status-ball.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/misc/traclogo.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/atlonglast.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/banner1.jpg 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/banner2.jpg 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/banner3.jpg 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/banner4.jpg 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/banner5.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/banner6.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/banner7.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/banner8.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/banner9.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/bannerback5.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/bannerback6.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/bodyback1.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/bodyback2.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/bodyback3.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/bodyback4.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/bodyback5.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/bodybacktop1.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/computers.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/computers2.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/glossyblue.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/glossyblue2.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/glossyblueh.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/insetrect.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/minilogo1-05e.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/minilogo1-07f.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/minilogo3.jpg 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/minitopback1.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/minitopback2.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/minitopbar1-05e.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/minitopbar2-05e.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/minitopbar2-07f.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/minitopbar3.jpg 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/minitopbar4.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/minitoplogo1.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/minitoplogo2.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/newpadmain.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/newpadmainback.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/newpadmainbackh.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/pageshot.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/pageshotmini.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/sidehead-gradhilite.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/tinytriangle.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/topnav1.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/topnav2.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/topnav3.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/topnav4.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/topnav5.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/topnav6.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/topnavback1.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/topnavback2.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/topnavback3.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/usecasesnavdown.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/usecasesnavdownh.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/usecasesnavup.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/usecasesnavuph.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/oct/watchscreencast.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/animated-orb-orange-12.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/backgrad.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/backshadow/backshadow-940-20-eee-20.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/backshadow/backshadow-940-20-fff-20.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/backshadow/backshadow-940-20-fff-40.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/backshadow/backshadow-940-20-fff-60.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/backshadow/botshadow-940-20-eee-20.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/etherpad-logo-small-grad.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/etherpad-logo-small.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/etherpad-logo-small2.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/expandy-arrow-down.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/expandy-arrow-right.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/expandy-arrow6-down-active.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/expandy-arrow6-down.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/expandy-arrow6-right-active.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/expandy-arrow6-right.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/header-revgrad.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/newpad.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/orb-greenred-12.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/padbg1.jpg 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/padbg2.jpg 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/padbg3.jpg 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/padbg4.jpg 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/padbg5.jpg 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/padhead1.jpg 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/padhead2.jpg 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/padhead3.jpg 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/pencil-icon-small-blue.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/sidehead-grad.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/timeslider/button_depressed.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/timeslider/button_undepressed.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/timeslider/crushed_button_depressed.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/timeslider/crushed_button_undepressed.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/timeslider/crushed_current_location.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/timeslider/crushed_pause.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/timeslider/crushed_play.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/timeslider/crushed_play_button.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/timeslider/crushed_timeslider_mockup.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/timeslider/current_location.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/timeslider/current_location.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/timeslider/pause.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/timeslider/pause.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/timeslider/play.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/timeslider/play.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/timeslider/play_button.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/timeslider/star.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/timeslider/star.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/timeslider/star_selected.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/timeslider/stepper_buttons.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/timeslider/timeslider_background.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/timeslider/timeslider_left.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/timeslider/timeslider_mockup.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pad/timeslider/timeslider_right.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pricing/free.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pricing/group.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pricing/on-demand.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pricing/private-network.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pricing/support.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pro/billing/cards-button.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pro/box/blue-boxtop.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pro/buttons/bluebutton120.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pro/header/pro-header-back.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pro/header/pro-header-logo.png 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pro/header/pro-header-logo.xcf 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pro/header/pro-header-plustopnav-back.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pro/padlist/gear-drop.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pro/padlist/paper-icon.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pro/padlist/trash-icon.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pro/topnav/pro-topnav-back.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/pro/topnav/pro-topnav-notch.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/tinyplane.gif 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 Binary files /dev/null and b/trunk/etherpad/src/static/img/wavejet.jpg 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'; +};var J=function(Q){return'\x3cscript type="text/javascript" src="'+Q+'">\x3c/script>';};var M=J;var N=H; +var L=function(Q){return'\''";};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=''; +var T=["'"+W+"'"];T.push(("('\\n\'');T.push('\' \''); +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,"",(''),'',"\x3cscript>",X,"\x3c/script>",'
    x
    ']; +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 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.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 0) { + goToRevisionIfEnabledCount --; + } else { + goToRevision.apply(goToRevision, arguments); + } +} + +BroadcastSlider.onSlider(goToRevisionIfEnabled); + +(function() { + for(var i=0; i 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 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'); + 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 = $(''); + var swatchtd = $(''); + var swatch = $('
    '); + swatch.css('background-color', clientVars.colorPalette[author.colorId]); + swatchtd.append(swatch); + tr.append(swatchtd); + var nametd = $(''); + nametd.text(author.name || "unnamed"); + tr.append(nametd); + $("#authorstable").append(tr); + } else { + numAnonymous ++; + } + }); + if(numAnonymous > 0) { + var html = ""+(numNamed>0?"...and ":"")+numAnonymous+" unnamed author"+(numAnonymous>1?"s":"")+""; + $("#authorstable").append($(html)); + } if(authors.length == 0) { + $("#authorstable").append($("No Authors")) + } + } + + 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 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 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. Why not?"); + $("#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 maxDebugMessages) { + debugMessages = debugMessages.slice(debugMessages.length-maxDebugMessages, + debugMessages.length); + } + + info.debugMessages = {length: 0}; + for(var i=0;i 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("

    Thanks! We will look at your case shortly.

    "); + } 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("
    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= 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 = '
    • '; + postHtml = '
    '; + } + 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+''; + extraCloseTags = ''+extraCloseTags; + } + if (simpleTags) { + simpleTags.sort(); + extraOpenTags = extraOpenTags+'<'+simpleTags.join('><')+'>'; + simpleTags.reverse(); + extraCloseTags = ''+extraCloseTags; + } + html.push('',extraOpenTags, + perTextNodeProcess(domline.escapeHTML(txt)), + extraCloseTags,''); + } + }; + 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 += ' '; + } + else if (! browser.msie) { + newHTML += '
    '; + } + } + 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 = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''' + }; + } + 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, ' '); + } + 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] = ' '; + endOfLine = false; + beforeSpace = true; + } + else if (p.charAt(0) != "<") { + endOfLine = false; + beforeSpace = false; + } + } + // beginning of line is nbsp + for(var i=0;i= ",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 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"); + + 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 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= 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= 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 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(' '); + } + else { + navcells. + html(''+text+''). + 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", "« "+getProseTitle(n-1), (n == 1 ? "" : "uses"+(n-1))); + var nextTitle = getProseTitle(n+1); + if (! nextTitle) setNavLink("right", ""); + else setNavLink("right", nextTitle+" »", "uses"+(n+1));*/ + /*setNavLink("left", "« "+getProseTitle(0), ""); + setNavLink("right", "");*/ + setNavLink("right", "« "+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 + ">"; + }); + + // 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("", "" ] || + + !tags.indexOf("", "" ] || + + tags.match(/^<(thead|tbody|tfoot|colg|cap)/) && + [ 1, "", "
    " ] || + + !tags.indexOf("", "" ] || + + // matched above + (!tags.indexOf("", "" ] || + + !tags.indexOf("", "" ] || + + // IE can't serialize and + + diff --git a/trunk/etherpad/src/templates/pro/admin/pne-config.ejs b/trunk/etherpad/src/templates/pro/admin/pne-config.ejs new file mode 100644 index 0000000..56fe68d --- /dev/null +++ b/trunk/etherpad/src/templates/pro/admin/pne-config.ejs @@ -0,0 +1,33 @@ +<% /* 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. */ %>

    Private Server Configuration

    + +

    Your private EtherPad server can be configured using either command-line arguments (of the +form --argName=value), or by adding the options to the file +data/etherpad.properties.

    + +

    Learn more about server options in the PNE Server Manual.

    + +

    Current Config Values

    + + + +<% propKeys.forEach(function(k) { %> + +<% }) %> +
    Option NameCurrent Value
    <%= k %><%= appjetConfig[k] %>
    + + + + + diff --git a/trunk/etherpad/src/templates/pro/admin/pne-dashboard.ejs b/trunk/etherpad/src/templates/pro/admin/pne-dashboard.ejs new file mode 100644 index 0000000..6b9b456 --- /dev/null +++ b/trunk/etherpad/src/templates/pro/admin/pne-dashboard.ejs @@ -0,0 +1,40 @@ +<% /* 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. */ %><% helpers.setHtmlTitle("EtherPad Private Server Dashboard") %> + +

    User Quota

    + +

    Your maximum daily unique user quota is: <%= userQuota %>

    +

    So far today, there have been <%= todayActiveUsers %> applied against this quota.

    + +

    Uptime

    + +This server has been running for <%= renderUptime() %>. + +

    HTTP Response Codes

    + +<%= renderResponseCodes() %> + +

    Current Realtime Pad Connections

    + +<%= renderPadConnections() %> + +

    Realtime Transport Performance

    + +<%= renderTransportStats() %> + +
    + * +
    + + diff --git a/trunk/etherpad/src/templates/pro/admin/pne-license-manager.ejs b/trunk/etherpad/src/templates/pro/admin/pne-license-manager.ejs new file mode 100644 index 0000000..42594b8 --- /dev/null +++ b/trunk/etherpad/src/templates/pro/admin/pne-license-manager.ejs @@ -0,0 +1,132 @@ +<% /* 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. */ %><% helpers.setHtmlTitle("EtherPad PNE License Manager"); %> +<% helpers.includeJQuery() %> +<% helpers.includeJs("etherpad.js") %> + +
    + + <% if (isExpired) { %> + +
    +

    Your evaluation license has expired!

    +

    Please contact <%= helpers.oemail("sales") %> or visit the pricing page on pad.spline.inf.fu-berlin.de + to purchase a license key.

    +
    + + <% } %> + + <% if (isTooOld) { %> + +
    +

    The version of EtherPad you are running (<%= runningVersionString %>) is too old. + Please update to version <%= licenseVersionString %> or newer by downloading the latest version on + pad.spline.inf.fu-berlin.de.

    +
    + + <% } %> + + <% if (errorMessage) { %> +
    +

    <%= errorMessage %>

    +
    + <% } %> + + <% if (licenseInfo && !edit) { %> + +

    License Info:

    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    Licensed To: <%= licenseInfo.personName %>
    Organization: <%= licenseInfo.organizationName %>
    Software Edition: <%= licenseInfo.editionName %>
    Maximum Users: <%= licenseInfo.userQuota %>
    Expires: <%= licenseInfo.expiresDate ? licenseInfo.expiresDate.toString() : "never" %>
    + +
    + +
    +
    + +
    +
    + + <% } %> + + <% if (isExpired || !licenseInfo || edit) { %> + +

    Enter New License Info:

    + + <% if (isUnlicensed) { %> +

    Before you can use this copy of EtherPad Private Network Edition, you must first + enter a valid license. Free trial licenses can be obtained obtained here. +

    + <% } %> + +
    +
    + +

    Name:

    + " /> + +

    Organization:

    + " /> + +

    License Key:

    + + +
    + +
    + + +
    +
    + + <% } %> + +
    + diff --git a/trunk/etherpad/src/templates/pro/admin/pne-shell.ejs b/trunk/etherpad/src/templates/pro/admin/pne-shell.ejs new file mode 100644 index 0000000..f398b15 --- /dev/null +++ b/trunk/etherpad/src/templates/pro/admin/pne-shell.ejs @@ -0,0 +1,33 @@ +<% /* 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. */ %><% helpers.setHtmlTitle("Shell") %> + +

    Warning! Be careful with this page.

    + +

    Shell

    + +

    Enter command:

    + +
    + + +
    + +<% if (result) { %> +

    Result

    +
    + <%= result %> +
    +

    Computed in <%= elapsedMs %>ms.

    +<% } %> + diff --git a/trunk/etherpad/src/templates/pro/admin/pro-config.ejs b/trunk/etherpad/src/templates/pro/admin/pro-config.ejs new file mode 100644 index 0000000..32cb610 --- /dev/null +++ b/trunk/etherpad/src/templates/pro/admin/pro-config.ejs @@ -0,0 +1,55 @@ +<% /* 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. */ %> +

    Application Configuration

    + +<%= messageDiv() %> + +
    + + + + + + + + + + + + + + + + + + + + +
    Site Name (appears in + the header of all pages): + +
    Always require all users on this domain to use secure + (HTTPS) connections? + /> +
    Default pad text: + +
    + +
    + +
    + diff --git a/trunk/etherpad/src/templates/pro/admin/single-invoice.ejs b/trunk/etherpad/src/templates/pro/admin/single-invoice.ejs new file mode 100644 index 0000000..aeab184 --- /dev/null +++ b/trunk/etherpad/src/templates/pro/admin/single-invoice.ejs @@ -0,0 +1,47 @@ +<% /* 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. */ %><% + helpers.includeCss('store/ondemand-billing.css'); +%> + +

    Past Invoices

    + +

    Invoice #<%= invoice.id %>, dated <%= formatDate(invoice.time) %>.

    + + + + + + + + + + + + + + + <% if (transaction) { %> + + + + + + + + + <% } %> +
    Invoice status<%= invoice.status == 'paid' ? "Paid" : (invoice.status == 'pending' ? "Pending" : (invoice.status == 'refunded' ? "Refunded" : invoice.status)) %>
    Number of users<%= invoice.users %>
    CostUS $<%= dollars(centsToDollars(invoice.amt)) %>
    Paid on<%= formatDate(transaction.time) %>
    Paid using<%= transaction.payInfo %>
    + + + diff --git a/trunk/etherpad/src/templates/pro/padlist/pro-padlist.ejs b/trunk/etherpad/src/templates/pro/padlist/pro-padlist.ejs new file mode 100644 index 0000000..b762679 --- /dev/null +++ b/trunk/etherpad/src/templates/pro/padlist/pro-padlist.ejs @@ -0,0 +1,49 @@ +<% /* 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. */ %><% helpers.includeCss("lib/jquery.contextmenu.css") %> +<% helpers.includeCss("pro/padlist.css") %> + +<% helpers.includeJQuery() %> +<% helpers.includeJs("lib/jquery.contextmenu.js") %> +<% helpers.includeJs("pro/pro-padlist-client.js") %> + +<% helpers.setHtmlTitle("Pad List - " + orgName + " - EtherPad") %> + +
    + + <%= renderPadNav() %> + <%= renderNotice() %> + <%= renderShowingDesc(padList.length) %> + + <% if (padList.length > 0) { %> + <%= renderPadList() %> +

    <%= padList.length %> pad<% if (padList.length > 1) { %>s<% } %> <% if (isAdmin) { %>(Download all pads as a ZIP archive.) <% } %> +

    + + <% } else { %> +

    No pads in this list.

    + <% } %> + +
    + + + + + + diff --git a/trunk/etherpad/src/templates/pro/pro-payment-required.ejs b/trunk/etherpad/src/templates/pro/pro-payment-required.ejs new file mode 100644 index 0000000..3649990 --- /dev/null +++ b/trunk/etherpad/src/templates/pro/pro-payment-required.ejs @@ -0,0 +1,51 @@ +<% /* 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. */ %><% helpers.includeJQuery() %> +<% helpers.includeJs("etherpad.js") %> +<% helpers.includeCss("pro/payment-required.css") %> + +
    + + +
    +
    + +

    Payment Required

    + +
    <%= message %>
    +
    + + <% if (isAdmin) { %> + + Manage Billing Info + + <% } else { %> +

    Please contact one of the following site administrator to + set up a billing profile on <%= request.domain %>:

    + +
      + <% adminList.forEach(function(a) { %> +
    • <%= a.fullName %>  <<%= TT(a.email) + %>>
    • + <% }); %> +
    + + <% } %> + +

    +

    Questions? Contact <%= helpers.oemail("support") %>.

    + +
    +
    + + diff --git a/trunk/etherpad/src/templates/pro/pro_home.ejs b/trunk/etherpad/src/templates/pro/pro_home.ejs new file mode 100644 index 0000000..bcf7443 --- /dev/null +++ b/trunk/etherpad/src/templates/pro/pro_home.ejs @@ -0,0 +1,103 @@ +<% /* 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. */ %><% helpers.setHtmlTitle(orgName + " - EtherPad"); %> +<% helpers.includeJQuery() %> +<% helpers.includeJs("etherpad.js") %> +<% helpers.includeCss("pro/pro-home.css"); %> +<% helpers.includeCss("pro/padlist.css"); %> + +
    + +
    + Welcome <%= account.fullName %> + <% if (account.isAdmin) { %>(Administrator)<% } %> +
    +
    +
    + + +
    + + Create new pad + + + <% if (livePads.length > 0) { %> +
    +

    Live Pads (currently being edited)

    +
    + <%= renderLivePads() %> +
    +
    + <% } %> + + <% if (recentPads.length > 0) { %> +
    +

    Your Recent Pads:

    +
    + <%= renderRecentPads() %> +
    + View all pads... +
    +
    + <% } %> + +
    + +
    +
    + Latest News +
    + +
    +
    + June 17th, 2009 +
    +
    +
    + +
    +

    Welcome to your EtherPad Beta Account! Please report bugs by + sending email to <%= helpers.oemail("bugs") %>. + +

    We hope you enjoy EtherPad!

    + +

    Sincerely,

    + +

    Spline

    +

    +
    + +
    + + +
    + + <%= helpers.clearFloats() %> + + <% if (isPNE) { %> +
    +
    + EtherPad Private Network Edition (PNE) + Version <%= pneVersion %>
    + + <% if (isEvaluation && evalExpDate) { %> +
    + EVALUATION EDITION: Expires <%= evalExpDate.toString() + %>.
    + <% } %> +
    + <% } %> + +
    + diff --git a/trunk/etherpad/src/templates/statistics/stat_page.ejs b/trunk/etherpad/src/templates/statistics/stat_page.ejs new file mode 100644 index 0000000..22277b3 --- /dev/null +++ b/trunk/etherpad/src/templates/statistics/stat_page.ejs @@ -0,0 +1,89 @@ +<% /* 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. */ %><% + +helpers.includeCss('admin/admin-stats.css'); +helpers.includeJQuery(); +helpers.includeJs('statpage.js'); + +%> + +« back to admin + +
    +
      + <% statCategoryNames.forEach(function(catName) { + %>
    • + <%= catName %> +
    • <% + }); %> +
    +
    + +
     
    + +<%= helpers.clearFloats() %> + +<%= optionsForm %> + +<% function formatLatest(latest) { + if (typeof(latest) == 'string') { + return latest; + } else { + return ''+ + latest.map(function(x) { return ""; }).join("\n")+ + "
    "+x.value+""+x.description+"
    "; + } +} +%> + +<% +function displayStat(statObject) { + %> +
    +

    <%= statObject.name %>

    +
    +

    <%= statObject.displayName %>

    + + + + + +
    + <%= statObject.graph %> + <% if (statObject.dataLinks) { %> + + <% } %> + +

    Latest values:

    + <%= formatLatest(statObject.latest) %> +
    +
    +
    + <% +} + +function displayCategory(categoryName) { + %> +
    + <% + categoriesToStats[categoryName].forEach(displayStat); + %> +
    + <% +} + +statCategoryNames.forEach(displayCategory); + +%> + diff --git a/trunk/etherpad/src/templates/store/csc-help.ejs b/trunk/etherpad/src/templates/store/csc-help.ejs new file mode 100644 index 0000000..3623fac --- /dev/null +++ b/trunk/etherpad/src/templates/store/csc-help.ejs @@ -0,0 +1,23 @@ +<% /* 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. */ %> + + +

    The CSC (or CVC) is the 3-digit number printed on the back of your card. +For American Express, it's the 4-digit number on the front.

    + +cc back + + + + diff --git a/trunk/etherpad/src/templates/store/eepnet-checkout/billing-info.ejs b/trunk/etherpad/src/templates/store/eepnet-checkout/billing-info.ejs new file mode 100644 index 0000000..69e0ead --- /dev/null +++ b/trunk/etherpad/src/templates/store/eepnet-checkout/billing-info.ejs @@ -0,0 +1,183 @@ +<% /* 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 (!cart.billingCountry) { + cart.billingCountry = "US"; + } + helpers.includeJQuery(); + helpers.includeJs("billing_shared.js"); + helpers.includeJs("billing.js"); + + function classesPlusError(classes, id) { + return (classes || []).concat(errorIfInvalid(id) || []).join(' '); + } +%> + +<% function textRow(id, label, classes, notBillingField) { + var val = (cart[id] || ""); + var maxlen=60; + var border; + if (id == "billingCCNumber") { + if (billing.validateCcNumber(val)) { + border = "greenborder"; + } else if (billing.validateCcLength(val)) { + border = "redborder"; + } + val = obfuscateCC(val); + maxlen = 16; + } + var classString = classesPlusError((notBillingField?[]:['billingfield']).concat(classes), id); + return TR({className: classString}, + TD({className: 'pcell'}, + LABEL({htmlFor: id}, label+(label.length > 0 ? ":" : ''))), + TD({className: 'tcell'}, + INPUT({type: 'text', name: id, size:35, maxlength:maxlen, + value: val, + className: border}))); + } %> + +

    Your name:

    + + <%= textRow("billingFirstName", "First Name", [], true) %> + <%= textRow("billingLastName", "Last Name", [], true) %> +
    + +

    Payment information:

    + +<% if (request.scheme == 'https') { %> +
    +

    Your payment information will be sent securely.

    +
    +<% } %> + +<% + function purchaseType(id, title) { + var sel; + if (! cart.billingPurchaseType) { + sel = (id == 'creditcard'); + } else { + sel = (cart.billingPurchaseType == id); + } + %> + + style="display: inline-block; vertical-align: middle;"/> + + + <% + } +%> + +
    +

    ">Pay using: +<% purchaseType('creditcard', 'Credit Card'); %> +<% purchaseType('invoice', 'Invoice'); %> +<% purchaseType('paypal', 'PayPal'); %> +

    +
    + + + <%= textRow("billingCCNumber", "Credit Card Number", ['creditcardreq']) %> + +<% function cardInput(cctype) { + var classes = []; + if (cart.billingCCNumber) { + if (cctype == billing.getCcType(cart.billingCCNumber)) { + classes.push("ccimageselected"); + } + } + classes.push("ccimage"); + var img = IMG({ + src: "/static/img/billing/"+cctype+".gif", + alt: cctype, + className: classes.join(" "), + style: "vertical-align: middle", + id: "img"+cctype}); + return img; + } %> + + + + + + + "> + + + + + + + + + + + + + + <%= textRow("billingAddressLine1", "Address", ['creditcardreq', 'invoicereq']) %> + <%= textRow("billingAddressLine2", "", ['creditcardreq', 'invoicereq']) %> + <%= textRow("billingCity", "City", ['creditcardreq', 'invoicereq']) %> + + + + + + <%= textRow("billingProvince", "Province", ['creditcardreq', 'invoicereq', 'intonly'])%> + <%= textRow("billingZipCode", "Zip Code", ['creditcardreq', 'invoicereq', 'usonly']) %> + <%= textRow("billingPostalCode", "Postal Code", ['creditcardreq', 'invoicereq', 'intonly'])%> + +
      +
    + <% ["visa", "mc", "disc", "amex"].forEach(function(t) { %> + <%= cardInput(t) %> + <% }); %> +
    +
    Expiration (MM/YY): + + / + +     CSC/CVC: + + what's this? +
    (Be sure to enter your credit card billing address below.)
    Country: + +
    State: + +
    Click "<%= billingButtonName %>" below to continue with PayPal.
    + +<% if (showCouponCode) { %> +

    Optional information:

    + + <%= textRow("billingReferralCode", "Referral Code", [], true) %> +
    +<% } %> + + +<%= billingFinalPhrase %> \ No newline at end of file diff --git a/trunk/etherpad/src/templates/store/eepnet-checkout/cart.ejs b/trunk/etherpad/src/templates/store/eepnet-checkout/cart.ejs new file mode 100644 index 0000000..147ff1b --- /dev/null +++ b/trunk/etherpad/src/templates/store/eepnet-checkout/cart.ejs @@ -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. */ %> +
    + + + + + + + <% if (! ('baseCost' in cart) && ! ('supportCost' in cart)) { %> + + + + <% } %> + + <% if (cart.baseCost) { %> + + + + + <% if (cart.couponProductPctDiscount) { %> + + + + + <% } %> + <% } %> + + <% if (cart.supportCost) { %> + + + + + <% if (cart.couponSupportPctDiscount) { %> + + + + + <% } %> + <% } else if (cart.baseCost) { %> + + + + + <% } %> + + <% if (cart.freeUserCount) { %> + + + + + <% } %> + + + + <% + var pctDiscount = cart.couponTotalPctDiscount; + var hasSubtotal = pctDiscount > 0; + %> + <% if (hasSubtotal) { %> + + + + + + + + + <% } %> + + + + +
    ItemCost
    + Nothing selected. +
    + Etherpad Private Network
    + <%= cart.numUsers %> users + <% if (editable) { %> + (">edit) + <% } %> + +
    US$<%= dollars(cart.baseCost) %>
    + Referral - <%= cart.couponProductPctDiscount %>% savings + -US$<%= dollars(cart.productReferralDiscount) %>
    + Support Contract + <% if (editable) { %> + (">edit) + <% } %> + +
    + 1 year +
    US$<%= dollars(cart.supportCost) %>
    + Referral - <%= cart.couponSupportPctDiscount %>% savings + -US$<%= dollars(cart.supportReferralDiscount) %>
    + No Support Contract + <% if (editable) { %> + (">edit) + <% } %> + + US$0.00
    + Bonus Users
    + + Referral - <%= cart.freeUserCount %> free + user<%= (cart.freeUserCount == 1 ? '' : "s") %> + +
    US$0.00
     
    SubtotalUS$<%= dollars(cart.subTotal) %>
    Referral - <%= pctDiscount %>% savings-US$<%= dollars(cart.totalReferralDiscount) %>
    TotalUS$<%= dollars(cart.total) %>
    +
    \ No newline at end of file diff --git a/trunk/etherpad/src/templates/store/eepnet-checkout/checkout-template.ejs b/trunk/etherpad/src/templates/store/eepnet-checkout/checkout-template.ejs new file mode 100644 index 0000000..817f0eb --- /dev/null +++ b/trunk/etherpad/src/templates/store/eepnet-checkout/checkout-template.ejs @@ -0,0 +1,38 @@ +<% /* 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. */ %><% helpers.includeCss("store/eepnet-checkout.css"); %> +<% helpers.includeJQuery() %> +<% helpers.includeJs("etherpad.js") %> +<% helpers.setHtmlTitle(title); %> + +<% +var selectCount = 0; +function select(id, title) { + var className = 'poslabel'; + if (pageId == id) { + className += ' current'; + } + selectCount++; + return SPAN({className: className}, selectCount+". "+title); +} +%> + +
    +
    + +

    Private Network Edition: Purchase Online

    + + <%= helpers.rafterNote() %> + +
    +
    diff --git a/trunk/etherpad/src/templates/store/eepnet-checkout/confirmation.ejs b/trunk/etherpad/src/templates/store/eepnet-checkout/confirmation.ejs new file mode 100644 index 0000000..3b38775 --- /dev/null +++ b/trunk/etherpad/src/templates/store/eepnet-checkout/confirmation.ejs @@ -0,0 +1,33 @@ +<% /* 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. */ %><% +helpers.includeJs('confirmation.js'); +%> + +<% if (request.params.frompaypal) { + handlePayPalRedirect(); +} %> + +<%= displaySummary(true) %> + +<% switch(cart.billingPurchaseType) { + case 'creditcard': case 'paypal': %> +

    If this looks good, click "Purchase" below to complete your purchase.

    + <% break; + case 'invoice': %> +

    If this looks good, print this page and mail it along with a check or other + prearranged payment to:

    AppJet, Inc.
    Pier 38 - Suite 210
    The Embarcadero
    San Francisco, CA 94107

    + <% break; +} + %> + diff --git a/trunk/etherpad/src/templates/store/eepnet-checkout/license-info.ejs b/trunk/etherpad/src/templates/store/eepnet-checkout/license-info.ejs new file mode 100644 index 0000000..4d710f2 --- /dev/null +++ b/trunk/etherpad/src/templates/store/eepnet-checkout/license-info.ejs @@ -0,0 +1,40 @@ +<% /* 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. */ %>

    Your license key will be issued to a particular individual at your organization, and will be delivered to the email address you specify below.

    + + + + + + + + + + + + + + + + + + + + +
    Email address to receive license key:
    Name of license owner (your name):
    Organization or company name:
    + /> + +
    \ No newline at end of file diff --git a/trunk/etherpad/src/templates/store/eepnet-checkout/purchase.ejs b/trunk/etherpad/src/templates/store/eepnet-checkout/purchase.ejs new file mode 100644 index 0000000..49cb3bb --- /dev/null +++ b/trunk/etherpad/src/templates/store/eepnet-checkout/purchase.ejs @@ -0,0 +1,33 @@ +<% /* 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. */ %>

    Thank you for choosing to purchase Enterprise EtherPad Private Network Edition.

    + +

    A license allows a certain number of concurrent users, at a one-time cost of US $<%= dollars(costPerUser) %> per user with no recurring costs. Learn more about how we count users.

    + +

    How many users should your license support?

    + + + + + + +
    Number of Users at US $<%= dollars(costPerUser) %>/user:
    + + + + + + +
    Referral Code (optional):
    diff --git a/trunk/etherpad/src/templates/store/eepnet-checkout/receipt.ejs b/trunk/etherpad/src/templates/store/eepnet-checkout/receipt.ejs new file mode 100644 index 0000000..8d3a2a5 --- /dev/null +++ b/trunk/etherpad/src/templates/store/eepnet-checkout/receipt.ejs @@ -0,0 +1,43 @@ +<% /* 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 (cart.showStartOverMessage) { %> +
    + Your purchase is complete! To purchase another item, please return to the purchase page. +
    +<% } + +switch(cart.status) { + case 'success': + %>

    Thank you for your purchase! This page serves as your receipt. Please print it for your records. You will receive a copy of this receipt and license key by email shortly.

    <% + break; + case 'pending': + %>

    Your purchase is pending approval by PayPal. Once it clears, + usually in 2-5 business days, you will receive a copy of this receipt and + your license key by email.

    <% + break; +} %> + +<% + var instructions = "/ep/pne-manual"; + var download = "/ep/store/eepnet-download-nextsteps"; +%> + +

    To install EtherPad Private Network Edition:

    + + +

    + +<%= displaySummary() %> diff --git a/trunk/etherpad/src/templates/store/eepnet-checkout/summary.ejs b/trunk/etherpad/src/templates/store/eepnet-checkout/summary.ejs new file mode 100644 index 0000000..753873c --- /dev/null +++ b/trunk/etherpad/src/templates/store/eepnet-checkout/summary.ejs @@ -0,0 +1,91 @@ +<% /* 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 textRow(tcell, pcell) { + %> + <%= tcell %> + <%= pcell %> + + <% +} +var keyData = { + ownerName: cart.ownerName, + orgName: cart.orgName +} +if (cart.licenseKey) { + var parts = cart.licenseKey.split(":"); + keyData.ownerName = parts[0]; + keyData.orgName = parts[1]; + keyData.key = parts[2]; + + keyData.keyLine1 = keyData.key.substring(0, keyData.key.length/3); + keyData.keyLine2 = keyData.key.substring(keyData.key.length/3, 2*keyData.key.length/3); + keyData.keyLine3 = keyData.key.substring(2*keyData.key.length/3, keyData.key.length); +} + +function makeRows(arr) { + arr.forEach(function(arr) { textRow(arr[0], arr[1]); }); +} +%> + +

    License Information <% if (editable) { %>(">edit)<% } %>

    + + + <% + makeRows([ + [ "Administrator name:", keyData.ownerName ], + [ "Organization/Company:", keyData.orgName ], + [ "Email address for delivery:", cart.email ], + [ "Total users:", cart.userCount ] + ]); + if (keyData.key) { + textRow("License key:", keyData.keyLine1+"
    "+keyData.keyLine2+"
    "+keyData.keyLine3); + %><% + } + %> +
    + +

    Payment Information <% if (editable) { %>(">edit)<% } %>

    + + + <% + var isUs = cart.billingCountry == "US"; + switch(cart.billingPurchaseType) { + case 'creditcard': + makeRows([ + [ "Credit card number:", obfuscateCC(cart.billingCCNumber) ], + [ "Expiration date:", cart.billingExpirationMonth+" / 20"+cart.billingExpirationYear ] + ]); + // falling through intentional. + case 'invoice': + makeRows([ + [ "Purchaser name:", cart.billingFirstName + " " + cart.billingLastName ], + [ "Purchaser address: ", cart.billingAddressLine1 + "
    " + + (cart.billingAddressLine2 ? cart.billingAddressLine2 + "
    " : "") + + cart.billingCity + ", " + + (isUs?cart.billingState:cart.billingProvince) + "
    " + + (isUs?cart.billingZipCode:cart.billingPostalCode) + + (isUs?'':', '+cart.billingCountry) ], + [ "Invoice number: ", cart.invoiceId ] + ]); + break; + case 'paypal': + textRow("Paid using:", "PayPal"); + textRow("InvoiceNumber:", cart.invoiceId); + } + %> +
    + +

    Summary of Charges

    + +<%= displayCart("shoppingconfirmation", editable) %> diff --git a/trunk/etherpad/src/templates/store/eepnet-checkout/support-contract.ejs b/trunk/etherpad/src/templates/store/eepnet-checkout/support-contract.ejs new file mode 100644 index 0000000..ff33fda --- /dev/null +++ b/trunk/etherpad/src/templates/store/eepnet-checkout/support-contract.ejs @@ -0,0 +1,41 @@ +<% /* 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. */ %>

    A support contract gives you free upgrades and help directly from the engineers who developed EtherPad. Support contracts cost US $<%= dollars(costPerUser * supportCostPct/100) %> per user per year, with a US $<%= dollars(supportMinCost) %> per year minimum. Learn more about support contracts.

    + +

    For the <%= cart.numUsers %>-user license you've selected, a support contract costs US $<%= discountedSupportCost() !== undefined ? dollars(discountedSupportCost()) : dollars(supportCost()) %>. + +

    Do you want a support contract?

    + + + + + + + + + + + +
    + /> + + +
    + /> + + +
    \ No newline at end of file diff --git a/trunk/etherpad/src/templates/store/eepnet_download.ejs b/trunk/etherpad/src/templates/store/eepnet_download.ejs new file mode 100644 index 0000000..42c89ee --- /dev/null +++ b/trunk/etherpad/src/templates/store/eepnet_download.ejs @@ -0,0 +1,43 @@ +<% /* 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. */ %><% helpers.includeCss("store/store.css") %> +<% helpers.includeJQuery() %> +<% helpers.includeJs("store.js") %> + +
    + + <% if (message) { %> +
    + <%= message %> +
    + <% } %> + +

    Download EtherPad Private Network Edition:

    + +

    +
    + + + + + Download Now + +

    Version: <%= versionString %>


    + +
    + + +
    diff --git a/trunk/etherpad/src/templates/store/eepnet_eval_nextsteps.ejs b/trunk/etherpad/src/templates/store/eepnet_eval_nextsteps.ejs new file mode 100644 index 0000000..4c4cec4 --- /dev/null +++ b/trunk/etherpad/src/templates/store/eepnet_eval_nextsteps.ejs @@ -0,0 +1,40 @@ +<% /* 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. */ %>
    +

    Downloading...

    + +

    Your download should begin automatically. If it does not, you + can click this link:

    + + + +

    Next Steps

    + + + +
    + + + diff --git a/trunk/etherpad/src/templates/store/eepnet_eval_signup.ejs b/trunk/etherpad/src/templates/store/eepnet_eval_signup.ejs new file mode 100644 index 0000000..5a1edf4 --- /dev/null +++ b/trunk/etherpad/src/templates/store/eepnet_eval_signup.ejs @@ -0,0 +1,125 @@ +<% /* 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. */ %><% helpers.setHtmlTitle("Sign up for EtherPad PNE Free Trial"); %> +<% helpers.includeJQuery() %> +<% helpers.includeJs("etherpad.js") %> +<% helpers.includeJs("store.js") %> +<% helpers.includeCss("store/store.css") %> + +<% function renderField(maxlen, id, title) { + var oldValue = (oldData[id] || ""); + return DIV(P(LABEL({htmlFor: id}, title), + INPUT({maxlength: maxlen, + type: "text", + className: "signupData", + name: id, + id: id, + value: oldValue}))); + } + + function renderWebLeadField(name) { + return INPUT({type: 'hidden', name: name, id: "wl_"+name, value: ""}); + } +%> + +
    + +

    Private Network Edition: <%= trialDays %>-Day Free Trial

    + +

    Enter your information here to download a free <%= trialDays + %>-day trial of EtherPad Private Network Edition.

    + + + + + +
    + +
    + + <% /* note: these fields should match exactly the eepnet-pricingcontact + form in pricing_eepnet.ejs */ %> + + <%= renderField(40, "firstName", "First Name:") %> + <%= renderField(80, "lastName", "Last Name:") %> + <%= renderField(80, "email", "Your Email (license key will be sent here):") %> + <%= renderField(40, "orgName", "Company/Organization:") %> + +

    + + +

    + + <%= renderField(40, "jobTitle", "Your Title:") %> + <%= renderField(40, "phone", "Phone Number:") %> + <%= renderField(160, "estUsers", "Estimated number of users:") %> + +
    + +

    + +
    + +

    If you already have a license, you + can skip directly to download.

    + +

    You can also recover a + lost license key.

    + +

    Questions? Email <%= helpers.oemail("sales") %>.

    + +
    + +
    + + + + <% [ + "oid", + "first_name", + "last_name", + "email", + "company", + "title", + "phone", + "00N80000003FYtG", + "00N80000003FYto", + "00N80000003FYuI", + "lead_source", + "industry" + ].forEach(function(f) { %> + + <%= renderWebLeadField(f) %> + + <% }); %> + +
    + + + diff --git a/trunk/infrastructure/.gitignore b/trunk/infrastructure/.gitignore new file mode 100644 index 0000000..8cde493 --- /dev/null +++ b/trunk/infrastructure/.gitignore @@ -0,0 +1,5 @@ +appjet/ +build/ +buildjs/ +buildcache/ +main.js \ No newline at end of file diff --git a/trunk/infrastructure/ace/.gitignore b/trunk/infrastructure/ace/.gitignore new file mode 100644 index 0000000..4083037 --- /dev/null +++ b/trunk/infrastructure/ace/.gitignore @@ -0,0 +1 @@ +local diff --git a/trunk/infrastructure/ace/README b/trunk/infrastructure/ace/README new file mode 100644 index 0000000..275684f --- /dev/null +++ b/trunk/infrastructure/ace/README @@ -0,0 +1,69 @@ +===== +ACE2 (originally AppJet Code Editor) +===== + +(This doc started Dec 2009 by dgreenspan.) + +ACE2 is EtherPad's editor, a content-editable-based rich text editor +that supports IE6+, FF(2?/)3+, Safari(3?/)4+. It supports +collaborative editing using operation transforms (easysync2), +undo/redo, copy/paste. + +The name "ACE2" is because this is a rewrite of aiba's original +content-editable AppJet Code Editor. + +== Building it + +In this directory, run `bin/make normal etherpad` (requires scala), +which generates `build/ace2.js` and copies this and other files into +the etherpad source tree. To have the script keep running and +automatically rebuild when source files change, run `bin/make auto +etherpad`. + +The original reason for the build process was that ACE needs to +construct two nested iframes on the client, and to do this without +incurring round-trips to the server, it programmatically loads code +into the outer iframe that loads code into the inner iframe; so the +bulk of ACE's code is compressed into a string literal (twice). Later +on, refactoring meant that the source is also divided into more than a +dozen files that the build script combines, and some are also +server-side EtherPad modules or client-side libraries (or both). + +The master copy of the operational transform (OT) library, easysync2, +lives here. + +In the early days it was possible to run ACE in a sort of development +mode, without the compression and iframe injection, by running it out +of the 'www' directory, but this is no longer possible. + +== Browser support + +We went out of our way to support IE6+. IE's design-mode is quite +robust, though there are some differences in manipulation of the +selection and insertion point and in the DOM representation we had to +use. + +We don't support Opera. Opera is a problematic case, because +apparently JS running in different iframes is run concurrently, not +linearized into a single event thread. This seems to be a rather +obscure fact and is almost difficult to believe. As if iframes don't +complicate scripting enough! + +== Syntax highlighting + +Though syntax highlighting predated rich text as the original +motivation for the design of ACE, support was eventually dropped in +EtherPad. At first the plan was to generalize it to other programming +languages, but the task was deprioritized (and difficult), and during +subsequent optimization and refactoring of ACE, calls to the +incremental lexer were stripped out because they complicated things. + +One plan for multi-language syntax highlighting, never implemented, +was to calculate syntax highlighting on the server as a sort of "style +overlay" and feed updates to the client along with updates to the +document text. + +== Changeset format + +See easysync-notes.txt for some notes on the changeset format, which +was redesigned with the advent of rich text. diff --git a/trunk/infrastructure/ace/bin/make b/trunk/infrastructure/ace/bin/make new file mode 100755 index 0000000..dad11ff --- /dev/null +++ b/trunk/infrastructure/ace/bin/make @@ -0,0 +1,337 @@ +#!/bin/sh +exec scala -classpath lib/yuicompressor-2.4-appjet.jar:lib/rhino-js-1.7r1.jar $0 $@ +!# + +import java.io._; + +def superpack(input: String): String = { + // this function is self-contained; takes a string, returns an expression + // that evaluates to that string + // XXX (This compresses well but decompression is too slow) + + // constraints on special chars: + // - this string must be able to go in a character class + // - each char must be able to go in single quotes + val specialChars = "-~@%$#*^_`()|abcdefghijklmnopqrstuvwxyz=!+,.;:?{}"; + val specialCharsSet:Set[Char] = Set(specialChars:_*); + def containsSpecialChar(str: String) = str.exists(specialCharsSet.contains(_)); + + val toks:Array[String] = ( + "@|[a-zA-Z0-9]+|[^@a-zA-Z0-9]{1,3}").r.findAllIn(input).collect.toArray; + + val stringCounts = { + val m = new scala.collection.mutable.HashMap[String,Int]; + def incrementCount(s: String) = { m(s) = m.getOrElse(s, 0) + 1; } + for(s <- toks) incrementCount(s); + m; + } + + val estimatedSavings = scala.util.Sorting.stableSort( + for((s,n) <- stringCounts.toArray; savings = s.length*n + if (savings > 8 || containsSpecialChar(s))) + yield (s,n,savings), + (x:(String,Int,Int))=> -x._3); + + def strLast(str: String, n: Int) = str.substring(str.length - n, str.length); + // order of encodeNames is very important! + val encodeNames = for(n <- 0 until (36*36); c <- specialChars) yield c.toString+strLast("0"+Integer.toString(n, 36).toUpperCase, 2); + + val thingsToReplace:Seq[String] = estimatedSavings.map(_._1); + assert(encodeNames.length >= thingsToReplace.length); + + val replacements = Map(thingsToReplace.elements.zipWithIndex.map({ + case (str, i) => (str, encodeNames(i)); + }).collect:_*); + def encode(tk: String) = if (replacements.contains(tk)) replacements(tk) else tk; + + val afterReplace = toks.map(encode(_)).mkString.replaceAll( + "(["+specialChars+"])(?=..[^0-9A-Z])(00|0)", "$1"); + + def makeSingleQuotedContents(str: String): String = { + str.replace("\\", "\\\\").replace("'", "\\'").replace("<", "\\x3c").replace("\n", "\\n"). + replace("\r", "\\n").replace("\t", "\\t"); + } + + val expansionMap = new scala.collection.mutable.HashMap[Char,scala.collection.mutable.ArrayBuffer[String]]; + for(i <- 0 until thingsToReplace.length; sc = encodeNames(i).charAt(0); + e = thingsToReplace(i)) { + expansionMap.getOrElseUpdate(sc, new scala.collection.mutable.ArrayBuffer[String]) += + (if (e == "@") "" else e); + } + val expansionMapLiteral = "{"+(for((sc,strs) <- expansionMap) yield { + "'"+sc+"':'"+makeSingleQuotedContents(strs.mkString("@"))+"'"; + }).mkString(",")+"}"; + + val expr = ("(function(m){m="+expansionMapLiteral+ + ";for(var k in m){if(m.hasOwnProperty(k))m[k]=m[k].split('@')};return '"+ + makeSingleQuotedContents(afterReplace)+ + "'.replace(/(["+specialChars+ + "])([0-9A-Z]{0,2})/g,function(a,b,c){return m[b][parseInt(c||'0',36)]||'@'})}())"); + /*val expr = ("(function(m){m="+expansionMapLiteral+ + ";for(var k in m){if(m.hasOwnProperty(k))m[k]=m[k].split('@')};"+ + "var result=[];var i=0;var s='"+makeSingleQuotedContents(afterReplace)+ + "';var len=s.length;while (i= 'A' && a <= 'Z' || a >= '0' && a <= '9')) {c=L[0];i++} else if (!(b >= 'A' && b <= 'Z' || b >= '0' && b <= '9')) {c = L[parseInt(a,36)]; i+=2} else {c = L[parseInt(a+b,36)]; i+=3}; result.push(c||'@'); } else {result.push(x); i++} }; return result.join(''); }())");*/ + + def evaluateString(js: String): String = { + import org.mozilla.javascript._; + ContextFactory.getGlobal.call(new ContextAction { + def run(cx: Context) = { + val scope = cx.initStandardObjects; + cx.evaluateString(scope, js, "", 1, null) } }).asInstanceOf[String]; + } + + def putFile(str: String, path: String): Unit = { + import java.io._; + val writer = new FileWriter(path); + writer.write(str); + writer.close; + } + + val exprOut = evaluateString(expr); + if (exprOut != input) { + putFile(input, "/tmp/superpack.input"); + putFile(expr, "/tmp/superpack.expr"); + putFile(exprOut, "/tmp/superpack.output"); + error("Superpacked string does not evaluate to original string; check /tmp/superpack.*"); + } + + val singleLiteral = "'"+makeSingleQuotedContents(input)+"'"; + if (singleLiteral.length < expr.length) { + singleLiteral; + } + else { + expr; + } +} + +def doMake { + + lazy val isEtherPad = (args.length >= 2 && args(1) == "etherpad"); + lazy val isNoHelma = (args.length >= 2 && args(1) == "nohelma"); + + def getFile(path:String): String = { + val builder = new StringBuilder(1000); + val reader = new BufferedReader(new FileReader(path)); + val buf = new Array[Char](1024); + var numRead = 0; + while({ numRead = reader.read(buf); numRead } != -1) { + builder.append(buf, 0, numRead); + } + reader.close; + return builder.toString; + } + + def putFile(str: String, path: String): Unit = { + val writer = new FileWriter(path); + writer.write(str); + writer.close; + } + + def writeToString(func:(Writer=>Unit)): String = { + val writer = new StringWriter; + func(writer); + return writer.toString; + } + + def compressJS(code: String, wrap: Boolean): String = { + import yuicompressor.org.mozilla.javascript.{ErrorReporter, EvaluatorException}; + object MyErrorReporter extends ErrorReporter { + def warning(message:String, sourceName:String, line:Int, lineSource:String, lineOffset:Int) { + if (message startsWith "Try to use a single 'var' statement per scope.") return; + if (line < 0) System.err.println("\n[WARNING] " + message); + else System.err.println("\n[WARNING] " + line + ':' + lineOffset + ':' + message); + } + def error(message:String, sourceName:String, line:Int, lineSource:String, lineOffset:Int) { + if (line < 0) System.err.println("\n[ERROR] " + message); + else System.err.println("\n[ERROR] " + line + ':' + lineOffset + ':' + message); + } + def runtimeError(message:String, sourceName:String, line:Int, lineSource:String, lineOffset:Int): EvaluatorException = { + error(message, sourceName, line, lineSource, lineOffset); + return new EvaluatorException(message); + } + } + + val munge = true; + val verbose = false; + val optimize = true; + val compressor = new com.yahoo.platform.yui.compressor.JavaScriptCompressor(new StringReader(code), MyErrorReporter); + return writeToString(compressor.compress(_, if (wrap) 100 else -1, munge, verbose, true, !optimize)); + } + + def compressCSS(code: String, wrap: Boolean): String = { + val compressor = new com.yahoo.platform.yui.compressor.CssCompressor(new StringReader(code)); + return writeToString(compressor.compress(_, if (wrap) 100 else -1)); + } + + import java.util.regex.{Pattern, Matcher, MatchResult}; + + def stringReplace(orig: String, regex: String, groupReferences:Boolean, func:(MatchResult=>String)): String = { + val buf = new StringBuffer; + val m = Pattern.compile(regex).matcher(orig); + while (m.find) { + var str = func(m); + if (! groupReferences) { + str = str.replace("\\", "\\\\").replace("$", "\\$"); + } + m.appendReplacement(buf, str); + } + m.appendTail(buf); + return buf.toString; + } + + def stringToExpression(str: String): String = { + var contents = str.replace("\\", "\\\\").replace("'", "\\'").replace("<", "\\x3c").replace("\n", "\\n"). + replace("\r", "\\n").replace("\t", "\\t"); + contents = contents.replace("\\/", "\\\\x2f"); // for Norton Internet Security + val result = "'"+contents+"'"; + result; + } + + val srcDir = "www"; + val destDir = "build"; + var code = getFile(srcDir+"/ace2_outer.js"); + + val useCompression = true; //if (isEtherPad) false else true; + + code = stringReplace(code, "\\$\\$INCLUDE_([A-Z_]+)\\([\"']([^\"']+)[\"']\\)", false, (m:MatchResult) => { + val includeType = m.group(1); + val paths = m.group(2); + val pathsArray = paths.replaceAll("""/\*.*?\*/""", "").split(" +").filter(_.length > 0); + def getSubcode = pathsArray.map(p => getFile(srcDir+"/"+p)).mkString("\n"); + val doPack = (stringToExpression _); + includeType match { + case "JS" => { + var subcode = getSubcode; + subcode = subcode.replaceAll("var DEBUG=true;//\\$\\$[^\n\r]*", "var DEBUG=false;"); + if (useCompression) subcode = compressJS(subcode, true); + "('\\x3cscript type=\"text/javascript\">//\\n')"; + } + case "CSS" => { + var subcode = getSubcode; + if (useCompression) subcode = compressCSS(subcode, false); + "('')"; + } + case "JS_Q" => { + var subcode = getSubcode + subcode = subcode.replaceAll("var DEBUG=true;//\\$\\$[^\n\r]*", "var DEBUG=false;"); + if (useCompression) subcode = compressJS(subcode, true); + "('(\\'\\\\x3cscript type=\"text/javascript\">//\\\\n\\\\x3c/script>\\')')"; + } + case "CSS_Q" => { + var subcode = getSubcode; + if (useCompression) subcode = compressCSS(subcode, false); + "('(\\' + + +
    + + diff --git a/trunk/infrastructure/ace/build/jquery-1.2.1.js b/trunk/infrastructure/ace/build/jquery-1.2.1.js new file mode 100644 index 0000000..b4eb132 --- /dev/null +++ b/trunk/infrastructure/ace/build/jquery-1.2.1.js @@ -0,0 +1,2992 @@ +(function(){ +/* + * jQuery 1.2.1 - New Wave Javascript + * + * Copyright (c) 2007 John Resig (jquery.com) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * $Date: 2007-09-16 23:42:06 -0400 (Sun, 16 Sep 2007) $ + * $Rev: 3353 $ + */ + +// Map over jQuery in case of overwrite +if ( typeof jQuery != "undefined" ) + var _jQuery = jQuery; + +var jQuery = window.jQuery = function(selector, context) { + // If the context is a namespace object, return a new object + return this instanceof jQuery ? + this.init(selector, context) : + new jQuery(selector, context); +}; + +// Map over the $ in case of overwrite +if ( typeof $ != "undefined" ) + var _$ = $; + +// Map the jQuery namespace to the '$' one +window.$ = jQuery; + +var quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/; + +jQuery.fn = jQuery.prototype = { + init: function(selector, context) { + // Make sure that a selection was provided + selector = selector || document; + + // Handle HTML strings + if ( typeof selector == "string" ) { + var m = quickExpr.exec(selector); + if ( m && (m[1] || !context) ) { + // HANDLE: $(html) -> $(array) + if ( m[1] ) + selector = jQuery.clean( [ m[1] ], context ); + + // HANDLE: $("#id") + else { + var tmp = document.getElementById( m[3] ); + if ( tmp ) + // Handle the case where IE and Opera return items + // by name instead of ID + if ( tmp.id != m[3] ) + return jQuery().find( selector ); + else { + this[0] = tmp; + this.length = 1; + return this; + } + else + selector = []; + } + + // HANDLE: $(expr) + } else + return new jQuery( context ).find( selector ); + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction(selector) ) + return new jQuery(document)[ jQuery.fn.ready ? "ready" : "load" ]( selector ); + + return this.setArray( + // HANDLE: $(array) + selector.constructor == Array && selector || + + // HANDLE: $(arraylike) + // Watch for when an array-like object is passed as the selector + (selector.jquery || selector.length && selector != window && !selector.nodeType && selector[0] != undefined && selector[0].nodeType) && jQuery.makeArray( selector ) || + + // HANDLE: $(*) + [ selector ] ); + }, + + jquery: "1.2.1", + + size: function() { + return this.length; + }, + + length: 0, + + get: function( num ) { + return num == undefined ? + + // Return a 'clean' array + jQuery.makeArray( this ) : + + // Return just the object + this[num]; + }, + + pushStack: function( a ) { + var ret = jQuery(a); + ret.prevObject = this; + return ret; + }, + + setArray: function( a ) { + this.length = 0; + Array.prototype.push.apply( this, a ); + return this; + }, + + each: function( fn, args ) { + return jQuery.each( this, fn, args ); + }, + + index: function( obj ) { + var pos = -1; + this.each(function(i){ + if ( this == obj ) pos = i; + }); + return pos; + }, + + attr: function( key, value, type ) { + var obj = key; + + // Look for the case where we're accessing a style value + if ( key.constructor == String ) + if ( value == undefined ) + return this.length && jQuery[ type || "attr" ]( this[0], key ) || undefined; + else { + obj = {}; + obj[ key ] = value; + } + + // Check to see if we're setting style values + return this.each(function(index){ + // Set all the styles + for ( var prop in obj ) + jQuery.attr( + type ? this.style : this, + prop, jQuery.prop(this, obj[prop], type, index, prop) + ); + }); + }, + + css: function( key, value ) { + return this.attr( key, value, "curCSS" ); + }, + + text: function(e) { + if ( typeof e != "object" && e != null ) + return this.empty().append( document.createTextNode( e ) ); + + var t = ""; + jQuery.each( e || this, function(){ + jQuery.each( this.childNodes, function(){ + if ( this.nodeType != 8 ) + t += this.nodeType != 1 ? + this.nodeValue : jQuery.fn.text([ this ]); + }); + }); + return t; + }, + + 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, 1, function(a){ + this.appendChild( a ); + }); + }, + + prepend: function() { + return this.domManip(arguments, true, -1, function(a){ + this.insertBefore( a, this.firstChild ); + }); + }, + + before: function() { + return this.domManip(arguments, false, 1, function(a){ + this.parentNode.insertBefore( a, this ); + }); + }, + + after: function() { + return this.domManip(arguments, false, -1, function(a){ + this.parentNode.insertBefore( a, this.nextSibling ); + }); + }, + + end: function() { + return this.prevObject || jQuery([]); + }, + + find: function(t) { + var data = jQuery.map(this, function(a){ return jQuery.find(t,a); }); + return this.pushStack( /[^+>] [^+>]/.test( t ) || t.indexOf("..") > -1 ? + jQuery.unique( data ) : data ); + }, + + clone: function(events) { + // Do the clone + var ret = this.map(function(){ + return this.outerHTML ? jQuery(this.outerHTML)[0] : 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) { + 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(t) { + return this.pushStack( + jQuery.isFunction( t ) && + jQuery.grep(this, function(el, index){ + return t.apply(el, [index]); + }) || + + jQuery.multiFilter(t,this) ); + }, + + not: function(t) { + return this.pushStack( + t.constructor == String && + jQuery.multiFilter(t, this, true) || + + jQuery.grep(this, function(a) { + return ( t.constructor == Array || t.jquery ) + ? jQuery.inArray( a, t ) < 0 + : a != t; + }) + ); + }, + + add: function(t) { + return this.pushStack( jQuery.merge( + this.get(), + t.constructor == String ? + jQuery(t).get() : + t.length != undefined && (!t.nodeName || jQuery.nodeName(t, "form")) ? + t : [t] ) + ); + }, + + is: function(expr) { + return expr ? jQuery.multiFilter(expr,this).length > 0 : false; + }, + + hasClass: function(expr) { + return this.is("." + expr); + }, + + val: function( val ) { + if ( val == undefined ) { + if ( this.length ) { + var elem = this[0]; + + // We need to handle select boxes special + if ( jQuery.nodeName(elem, "select") ) { + var index = elem.selectedIndex, + a = [], + 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 + var val = jQuery.browser.msie && !option.attributes["value"].specified ? option.text : option.value; + + // We don't need an array for one selects + if ( one ) + return val; + + // Multi-Selects return an array + a.push(val); + } + } + + return a; + + // Everything else, we just grab the value + } else + return this[0].value.replace(/\r/g, ""); + } + } else + return this.each(function(){ + if ( val.constructor == Array && /radio|checkbox/.test(this.type) ) + this.checked = (jQuery.inArray(this.value, val) >= 0 || + jQuery.inArray(this.name, val) >= 0); + else if ( jQuery.nodeName(this, "select") ) { + var tmp = val.constructor == Array ? val : [val]; + + jQuery("option", this).each(function(){ + this.selected = (jQuery.inArray(this.value, tmp) >= 0 || + jQuery.inArray(this.text, tmp) >= 0); + }); + + if ( !tmp.length ) + this.selectedIndex = -1; + } else + this.value = val; + }); + }, + + html: function( val ) { + return val == undefined ? + ( this.length ? this[0].innerHTML : null ) : + this.empty().append( val ); + }, + + replaceWith: function( val ) { + return this.after( val ).remove(); + }, + + eq: function(i){ + return this.slice(i, i+1); + }, + + slice: function() { + return this.pushStack( Array.prototype.slice.apply( this, arguments ) ); + }, + + map: function(fn) { + return this.pushStack(jQuery.map( this, function(elem,i){ + return fn.call( elem, i, elem ); + })); + }, + + andSelf: function() { + return this.add( this.prevObject ); + }, + + domManip: function(args, table, dir, fn) { + var clone = this.length > 1, a; + + return this.each(function(){ + if ( !a ) { + a = jQuery.clean(args, this.ownerDocument); + if ( dir < 0 ) + a.reverse(); + } + + var obj = this; + + if ( table && jQuery.nodeName(this, "table") && jQuery.nodeName(a[0], "tr") ) + obj = this.getElementsByTagName("tbody")[0] || this.appendChild(document.createElement("tbody")); + + jQuery.each( a, function(){ + var elem = clone ? this.cloneNode(true) : this; + if ( !evalScript(0, elem) ) + fn.call( obj, elem ); + }); + }); + } +}; + +function evalScript(i, elem){ + var script = jQuery.nodeName(elem, "script"); + + if ( script ) { + 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); + + } else if ( elem.nodeType == 1 ) + jQuery("script", elem).each(evalScript); + + return script; +} + +jQuery.extend = jQuery.fn.extend = function() { + // copy reference to target object + var target = arguments[0] || {}, a = 1, al = arguments.length, deep = false; + + // Handle a deep copy situation + if ( target.constructor == Boolean ) { + deep = target; + target = arguments[1] || {}; + } + + // extend jQuery itself if only one argument is passed + if ( al == 1 ) { + target = this; + a = 0; + } + + var prop; + + for ( ; a < al; a++ ) + // Only deal with non-null/undefined values + if ( (prop = arguments[a]) != null ) + // Extend the base object + for ( var i in prop ) { + // Prevent never-ending loop + if ( target == prop[i] ) + continue; + + // Recurse if we're merging object values + if ( deep && typeof prop[i] == 'object' && target[i] ) + jQuery.extend( target[i], prop[i] ); + + // Don't bring in undefined values + else if ( prop[i] != undefined ) + target[i] = prop[i]; + } + + // Return the modified object + return target; +}; + +var expando = "jQuery" + (new Date()).getTime(), uuid = 0, win = {}; + +jQuery.extend({ + noConflict: function(deep) { + window.$ = _$; + if ( deep ) + window.jQuery = _jQuery; + return jQuery; + }, + + // This may seem like some crazy code, but trust me when I say that this + // is the only cross-browser way to do this. --John + isFunction: function( fn ) { + return !!fn && typeof fn != "string" && !fn.nodeName && + fn.constructor != Array && /function/i.test( fn + "" ); + }, + + // check if an element is in a XML document + isXMLDoc: function(elem) { + return elem.documentElement && !elem.body || + elem.tagName && elem.ownerDocument && !elem.ownerDocument.body; + }, + + // Evalulates a script in a global context + // Evaluates Async. in Safari 2 :-( + globalEval: function( data ) { + data = jQuery.trim( data ); + if ( data ) { + if ( window.execScript ) + window.execScript( data ); + else if ( jQuery.browser.safari ) + // safari doesn't provide a synchronous global eval + window.setTimeout( data, 0 ); + else + eval.call( window, data ); + } + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase(); + }, + + cache: {}, + + data: function( elem, name, data ) { + elem = elem == window ? win : 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 ? win : 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( obj, fn, args ) { + if ( args ) { + if ( obj.length == undefined ) + for ( var i in obj ) + fn.apply( obj[i], args ); + else + for ( var i = 0, ol = obj.length; i < ol; i++ ) + if ( fn.apply( obj[i], args ) === false ) break; + + // A special, fast, case for the most common use of each + } else { + if ( obj.length == undefined ) + for ( var i in obj ) + fn.call( obj[i], i, obj[i] ); + else + for ( var i = 0, ol = obj.length, val = obj[0]; + i < ol && fn.call(val,i,val) !== false; val = obj[++i] ){} + } + + return obj; + }, + + prop: function(elem, value, type, index, prop){ + // Handle executable functions + if ( jQuery.isFunction( value ) ) + value = value.call( elem, [index] ); + + // exclude the following css properties to add px + var exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i; + + // Handle passing in a number to a CSS property + return value && value.constructor == Number && type == "curCSS" && !exclude.test(prop) ? + value + "px" : + value; + }, + + className: { + // internal only, use addClass("class") + add: function( elem, c ){ + jQuery.each( (c || "").split(/\s+/), function(i, cur){ + if ( !jQuery.className.has( elem.className, cur ) ) + elem.className += ( elem.className ? " " : "" ) + cur; + }); + }, + + // internal only, use removeClass("class") + remove: function( elem, c ){ + elem.className = c != undefined ? + jQuery.grep( elem.className.split(/\s+/), function(cur){ + return !jQuery.className.has( c, cur ); + }).join(" ") : ""; + }, + + // internal only, use is(".class") + has: function( t, c ) { + return jQuery.inArray( c, (t.className || t).toString().split(/\s+/) ) > -1; + } + }, + + swap: function(e,o,f) { + for ( var i in o ) { + e.style["old"+i] = e.style[i]; + e.style[i] = o[i]; + } + f.apply( e, [] ); + for ( var i in o ) + e.style[i] = e.style["old"+i]; + }, + + css: function(e,p) { + if ( p == "height" || p == "width" ) { + var old = {}, oHeight, oWidth, d = ["Top","Bottom","Right","Left"]; + + jQuery.each( d, function(){ + old["padding" + this] = 0; + old["border" + this + "Width"] = 0; + }); + + jQuery.swap( e, old, function() { + if ( jQuery(e).is(':visible') ) { + oHeight = e.offsetHeight; + oWidth = e.offsetWidth; + } else { + e = jQuery(e.cloneNode(true)) + .find(":radio").removeAttr("checked").end() + .css({ + visibility: "hidden", position: "absolute", display: "block", right: "0", left: "0" + }).appendTo(e.parentNode)[0]; + + var parPos = jQuery.css(e.parentNode,"position") || "static"; + if ( parPos == "static" ) + e.parentNode.style.position = "relative"; + + oHeight = e.clientHeight; + oWidth = e.clientWidth; + + if ( parPos == "static" ) + e.parentNode.style.position = "static"; + + e.parentNode.removeChild(e); + } + }); + + return p == "height" ? oHeight : oWidth; + } + + return jQuery.curCSS( e, p ); + }, + + curCSS: function(elem, prop, force) { + var ret, stack = [], swap = []; + + // A helper method for determining if an element's values are broken + function color(a){ + if ( !jQuery.browser.safari ) + return false; + + var ret = document.defaultView.getComputedStyle(a,null); + return !ret || ret.getPropertyValue("color") == ""; + } + + if (prop == "opacity" && jQuery.browser.msie) { + ret = jQuery.attr(elem.style, "opacity"); + return ret == "" ? "1" : ret; + } + + if (prop.match(/float/i)) + prop = styleFloat; + + if (!force && elem.style[prop]) + ret = elem.style[prop]; + + else if (document.defaultView && document.defaultView.getComputedStyle) { + + if (prop.match(/float/i)) + prop = "float"; + + prop = prop.replace(/([A-Z])/g,"-$1").toLowerCase(); + var cur = document.defaultView.getComputedStyle(elem, null); + + if ( cur && !color(elem) ) + ret = cur.getPropertyValue(prop); + + // If the element isn't reporting its values properly in Safari + // then some display: none elements are involved + else { + // Locate all of the parent display: none elements + for ( var a = elem; 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 ( a = 0; a < stack.length; a++ ) + if ( color(stack[a]) ) { + swap[a] = stack[a].style.display; + stack[a].style.display = "block"; + } + + // Since we flip the display style, we have to handle that + // one special, otherwise get the value + ret = prop == "display" && swap[stack.length-1] != null ? + "none" : + document.defaultView.getComputedStyle(elem,null).getPropertyValue(prop) || ""; + + // Finally, revert the display styles back + for ( a = 0; a < swap.length; a++ ) + if ( swap[a] != null ) + stack[a].style.display = swap[a]; + } + + if ( prop == "opacity" && ret == "" ) + ret = "1"; + + } else if (elem.currentStyle) { + var newProp = prop.replace(/\-(\w)/g,function(m,c){return c.toUpperCase();}); + ret = elem.currentStyle[prop] || elem.currentStyle[newProp]; + + // 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) ) { + var style = elem.style.left; + var runtimeStyle = elem.runtimeStyle.left; + elem.runtimeStyle.left = elem.currentStyle.left; + elem.style.left = ret || 0; + ret = elem.style.pixelLeft + "px"; + elem.style.left = style; + elem.runtimeStyle.left = runtimeStyle; + } + } + + return ret; + }, + + clean: function(a, doc) { + var r = []; + doc = doc || document; + + jQuery.each( a, function(i,arg){ + if ( !arg ) return; + + if ( arg.constructor == Number ) + arg = arg.toString(); + + // Convert html string into DOM nodes + if ( typeof arg == "string" ) { + // Fix "XHTML"-style tags in all browsers + arg = arg.replace(/(<(\w+)[^>]*?)\/>/g, function(m, all, tag){ + return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area)$/i)? m : all+">"; + }); + + // Trim whitespace, otherwise indexOf won't work as expected + var s = jQuery.trim(arg).toLowerCase(), div = doc.createElement("div"), tb = []; + + var wrap = + // option or optgroup + !s.indexOf("", ""] || + + !s.indexOf("", ""] || + + s.match(/^<(thead|tbody|tfoot|colg|cap)/) && + [1, "", "
    "] || + + !s.indexOf("", ""] || + + // matched above + (!s.indexOf("", ""] || + + !s.indexOf("", ""] || + + // IE can't serialize and + + + + + + + +
    +
    +
    +

    +

    +

    +

    +
    +
    + + diff --git a/trunk/infrastructure/ace/www/domline.js b/trunk/infrastructure/ace/www/domline.js new file mode 100644 index 0000000..70f86cc --- /dev/null +++ b/trunk/infrastructure/ace/www/domline.js @@ -0,0 +1,210 @@ +// THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.domline + +/** + * 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 = '
    • '; + postHtml = '
    '; + } + 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+''; + extraCloseTags = ''+extraCloseTags; + } + if (simpleTags) { + simpleTags.sort(); + extraOpenTags = extraOpenTags+'<'+simpleTags.join('><')+'>'; + simpleTags.reverse(); + extraCloseTags = ''+extraCloseTags; + } + html.push('',extraOpenTags, + perTextNodeProcess(domline.escapeHTML(txt)), + extraCloseTags,''); + } + }; + 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 += ' '; + } + else if (! browser.msie) { + newHTML += '
    '; + } + } + 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 = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''' + }; + } + 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, ' '); + } + 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] = ' '; + endOfLine = false; + beforeSpace = true; + } + else if (p.charAt(0) != "<") { + endOfLine = false; + beforeSpace = false; + } + } + // beginning of line is nbsp + for(var i=0;i= 0, "bad old text length"); + assert(this[2] >= 0, "bad new text length"); + assert((this.length % 3) == 0, "bad array length"); + assert(this.length >= 6, "must be at least one strip"); + var numStrips = this.numStrips(); + var oldLen = this[1]; + var newLen = this[2]; + // iterate over the "text strips" + var actualNewLen = 0; + this.eachStrip(function(startIndex, numTaken, newText, i) { + var s = startIndex, t = numTaken, n = newText; + var isFirst = (i == 0); + var isLast = (i == numStrips-1); + assert(t >= 0, "can't take negative number of chars"); + assert(isFirst || t > 0, "all strips but first must take"); + assert((t > 0) || (s == 0), "if first strip doesn't take, must have 0 startIndex"); + assert(s >= 0 && s + t <= oldLen, "bad index: "+this.toString()); + assert(t > 0 || n.length > 0 || (isFirst && isLast), "empty strip must be first and only"); + if (! isLast) { + var s2 = this[3 + i*3 + 3]; // startIndex of following strip + var gap = s2 - (s + t); + assert(gap >= 0, "overlapping or out-of-order strips: "+this.toString()); + assert(gap > 0 || n.length > 0, "touching strips with no added text"); + } + actualNewLen += t + n.length; + }); + assert(newLen == actualNewLen, "calculated new text length doesn't match"); + } + + array.applyToText = function(text) { + assert(text.length == this.oldLen(), "mismatched apply: "+text.length+" / "+this.oldLen()); + var buf = []; + this.eachStrip(function (s, t, n) { + buf.push(text.substr(s, t), n); + }); + return buf.join(''); + } + + function _makeBuilder(oldLen, supportAuthors) { + var C = Changeset(oldLen); + if (supportAuthors) { + _ensureAuthors(C); + } + return C.builder(); + } + + function _getNumInserted(C) { + var numChars = 0; + C.eachStrip(function(s,t,n) { + numChars += n.length; + }); + return numChars; + } + + function _ensureAuthors(C) { + if (! C.authors) { + C.setAuthor(); + } + return C; + } + + array.setAuthor = function(author) { + var C = this; + // authors array has even length >= 2; + // alternates [numChars1, author1, numChars2, author2]; + // all numChars > 0 unless there is exactly one, in which + // case it can be == 0. + C.authors = [_getNumInserted(C), author || '']; + return C; + } + + array.builder = function() { + // normal pattern is Changeset(oldLength).builder().appendOldText(...). ... + // builder methods mutate this! + var C = this; + // OOP style: state in environment + var self; + return self = { + appendNewText: function(str, author) { + C[C.length-1] += str; + C[2] += str.length; + + if (C.authors) { + var a = (author || ''); + var lastAuthorPtr = C.authors.length-1; + var lastAuthorLengthPtr = C.authors.length-2; + if ((!a) || a == C.authors[lastAuthorPtr]) { + C.authors[lastAuthorLengthPtr] += str.length; + } + else if (0 == C.authors[lastAuthorLengthPtr]) { + C.authors[lastAuthorLengthPtr] = str.length; + C.authors[lastAuthorPtr] = (a || C.authors[lastAuthorPtr]); + } + else { + C.authors.push(str.length, a); + } + } + + return self; + }, + appendOldText: function(startIndex, numTaken) { + if (numTaken == 0) return self; + // properties of last strip... + var s = C[C.length-3], t = C[C.length-2], n = C[C.length-1]; + if (t == 0 && n == "") { + // must be empty changeset, one strip that doesn't take old chars or add new ones + C[C.length-3] = startIndex; + C[C.length-2] = numTaken; + } + else if (n == "" && (s+t == startIndex)) { + C[C.length-2] += numTaken; // take more + } + else C.push(startIndex, numTaken, ""); // add a strip + C[2] += numTaken; + C.checkRep(); + return self; + }, + toChangeset: function() { return C; } + }; + } + + array.authorSlicer = function(outputBuilder) { + return _makeAuthorSlicer(this, outputBuilder); + } + + function _makeAuthorSlicer(changesetOrAuthorsIn, builderOut) { + // "builderOut" only needs to support appendNewText + var authors; // considered immutable + if (changesetOrAuthorsIn.isChangeset) { + authors = changesetOrAuthorsIn.authors; + } + else { + authors = changesetOrAuthorsIn; + } + + // OOP style: state in environment + var authorPtr = 0; + var charIndex = 0; + var charWithinAuthor = 0; // 0 <= charWithinAuthor <= authors[authorPtr]; max value iff atEnd + var atEnd = false; + function curAuthor() { return authors[authorPtr+1]; } + function curAuthorWidth() { return authors[authorPtr]; } + function assertNotAtEnd() { assert(! atEnd, "_authorSlicer: can't move past end"); } + function forwardInAuthor(numChars) { + charWithinAuthor += numChars; + charIndex += numChars; + } + function nextAuthor() { + assertNotAtEnd(); + assert(charWithinAuthor == curAuthorWidth(), "_authorSlicer: not at author end"); + charWithinAuthor = 0; + authorPtr += 2; + if (authorPtr == authors.length) { + atEnd = true; + } + } + + var self; + return self = { + skipChars: function(n) { + assert(n >= 0, "_authorSlicer: can't skip negative n"); + if (n == 0) return; + assertNotAtEnd(); + + var leftToSkip = n; + while (leftToSkip > 0) { + var leftInAuthor = curAuthorWidth() - charWithinAuthor; + if (leftToSkip >= leftInAuthor) { + forwardInAuthor(leftInAuthor); + leftToSkip -= leftInAuthor; + nextAuthor(); + } + else { + forwardInAuthor(leftToSkip); + leftToSkip = 0; + } + } + }, + takeChars: function(n, text) { + assert(n >= 0, "_authorSlicer: can't take negative n"); + if (n == 0) return; + assertNotAtEnd(); + assert(n == text.length, "_authorSlicer: bad text length"); + + var textLeft = text; + var leftToTake = n; + while (leftToTake > 0) { + if (curAuthorWidth() > 0 && charWithinAuthor < curAuthorWidth()) { + // at least one char to take from current author + var leftInAuthor = (curAuthorWidth() - charWithinAuthor); + assert(leftInAuthor > 0, "_authorSlicer: should have leftInAuthor > 0"); + var toTake = min(leftInAuthor, leftToTake); + assert(toTake > 0, "_authorSlicer: should have toTake > 0"); + builderOut.appendNewText(textLeft.substring(0, toTake), curAuthor()); + forwardInAuthor(toTake); + leftToTake -= toTake; + textLeft = textLeft.substring(toTake); + } + assert(charWithinAuthor <= curAuthorWidth(), "_authorSlicer: past end of author"); + if (charWithinAuthor == curAuthorWidth()) { + nextAuthor(); + } + } + }, + setBuilder: function(builder) { + builderOut = builder; + } + }; + } + + function _makeSlicer(C, output) { + // C: Changeset, output: builder from _makeBuilder + // C is considered immutable, won't change or be changed + + // OOP style: state in environment + var charIndex = 0; // 0 <= charIndex <= C.newLen(); maximum value iff atEnd + var stripIndex = 0; // 0 <= stripIndex <= C.numStrips(); maximum value iff atEnd + var charWithinStrip = 0; // 0 <= charWithinStrip < curStripWidth() + var atEnd = false; + + var authorSlicer; + if (C.authors) { + authorSlicer = _makeAuthorSlicer(C.authors, output); + } + + var ptr = 3; + function curStartIndex() { return C[ptr]; } + function curNumTaken() { return C[ptr+1]; } + function curNewText() { return C[ptr+2]; } + function curStripWidth() { return curNumTaken() + curNewText().length; } + function assertNotAtEnd() { assert(! atEnd, "_slicer: can't move past changeset end"); } + function forwardInStrip(numChars) { + charWithinStrip += numChars; + charIndex += numChars; + } + function nextStrip() { + assertNotAtEnd(); + assert(charWithinStrip == curStripWidth(), "_slicer: not at strip end"); + charWithinStrip = 0; + stripIndex++; + ptr += 3; + if (stripIndex == C.numStrips()) { + atEnd = true; + } + } + function curNumNewCharsInRange(start, end) { + // takes two indices into the current strip's combined "taken" and "new" + // chars, and returns how many "new" chars are included in the range + assert(start <= end, "_slicer: curNumNewCharsInRange given out-of-order indices"); + var nt = curNumTaken(); + var nn = curNewText().length; + var s = nt; + var e = nt+nn; + if (s < start) s = start; + if (e > end) e = end; + if (e < s) return 0; + return e-s; + } + + var self; + return self = { + skipChars: function (n) { + assert(n >= 0, "_slicer: can't skip negative n"); + if (n == 0) return; + assertNotAtEnd(); + + var leftToSkip = n; + while (leftToSkip > 0) { + var leftInStrip = curStripWidth() - charWithinStrip; + if (leftToSkip >= leftInStrip) { + forwardInStrip(leftInStrip); + + if (authorSlicer) + authorSlicer.skipChars(curNumNewCharsInRange(charWithinStrip, + charWithinStrip + leftInStrip)); + + leftToSkip -= leftInStrip; + nextStrip(); + } + else { + if (authorSlicer) + authorSlicer.skipChars(curNumNewCharsInRange(charWithinStrip, + charWithinStrip + leftToSkip)); + + forwardInStrip(leftToSkip); + leftToSkip = 0; + } + } + }, + takeChars: function (n) { + assert(n >= 0, "_slicer: can't take negative n"); + if (n == 0) return; + assertNotAtEnd(); + + var leftToTake = n; + while (leftToTake > 0) { + if (curNumTaken() > 0 && charWithinStrip < curNumTaken()) { + // at least one char to take from current strip's numTaken + var leftInTaken = (curNumTaken() - charWithinStrip); + assert(leftInTaken > 0, "_slicer: should have leftInTaken > 0"); + var toTake = min(leftInTaken, leftToTake); + assert(toTake > 0, "_slicer: should have toTake > 0"); + output.appendOldText(curStartIndex() + charWithinStrip, toTake); + forwardInStrip(toTake); + leftToTake -= toTake; + } + if (leftToTake > 0 && curNewText().length > 0 && charWithinStrip >= curNumTaken() && + charWithinStrip < curStripWidth()) { + // at least one char to take from current strip's newText + var leftInNewText = (curStripWidth() - charWithinStrip); + assert(leftInNewText > 0, "_slicer: should have leftInNewText > 0"); + var toTake = min(leftInNewText, leftToTake); + assert(toTake > 0, "_slicer: should have toTake > 0"); + var newText = curNewText().substr(charWithinStrip - curNumTaken(), toTake); + if (authorSlicer) { + authorSlicer.takeChars(newText.length, newText); + } + else { + output.appendNewText(newText); + } + forwardInStrip(toTake); + leftToTake -= toTake; + } + assert(charWithinStrip <= curStripWidth(), "_slicer: past end of strip"); + if (charWithinStrip == curStripWidth()) { + nextStrip(); + } + } + }, + skipTo: function(n) { + self.skipChars(n - charIndex); + } + }; + } + + array.slicer = function(outputBuilder) { + return _makeSlicer(this, outputBuilder); + } + + array.compose = function(next) { + assert(next.oldLen() == this.newLen(), "mismatched composition"); + + var builder = _makeBuilder(this.oldLen(), !!(this.authors || next.authors)); + var slicer = _makeSlicer(this, builder); + + var authorSlicer; + if (next.authors) { + authorSlicer = _makeAuthorSlicer(next.authors, builder); + } + + next.eachStrip(function(s, t, n) { + slicer.skipTo(s); + slicer.takeChars(t); + if (authorSlicer) { + authorSlicer.takeChars(n.length, n); + } + else { + builder.appendNewText(n); + } + }, this); + + return builder.toChangeset(); + }; + + array.traverser = function() { + return _makeTraverser(this); + } + + function _makeTraverser(C) { + var s = C[3], t = C[4], n = C[5]; + var nextIndex = 6; + var indexIntoNewText = 0; + + var authorSlicer; + if (C.authors) { + authorSlicer = _makeAuthorSlicer(C.authors, null); + } + + function advanceIfPossible() { + if (t == 0 && n == "" && nextIndex < C.length) { + s = C[nextIndex]; + t = C[nextIndex+1]; + n = C[nextIndex+2]; + nextIndex += 3; + } + } + + var self; + return self = { + numTakenChars: function() { + // if starts with taken characters, then how many, else 0 + return (t > 0) ? t : 0; + }, + numNewChars: function() { + // if starts with new characters, then how many, else 0 + return (t == 0 && n.length > 0) ? n.length : 0; + }, + takenCharsStart: function() { + return (self.numTakenChars() > 0) ? s : 0; + }, + hasMore: function() { + return self.numTakenChars() > 0 || self.numNewChars() > 0; + }, + curIndex: function() { + return indexIntoNewText; + }, + consumeTakenChars: function (x) { + assert(self.numTakenChars() > 0, "_traverser: no taken chars"); + assert(x >= 0 && x <= self.numTakenChars(), "_traverser: bad number of taken chars"); + if (x == 0) return; + if (t == x) { s = 0; t = 0; } + else { s += x; t -= x; } + indexIntoNewText += x; + advanceIfPossible(); + }, + consumeNewChars: function(x) { + return self.appendNewChars(x, null); + }, + appendNewChars: function(x, builder) { + assert(self.numNewChars() > 0, "_traverser: no new chars"); + assert(x >= 0 && x <= self.numNewChars(), "_traverser: bad number of new chars"); + if (x == 0) return ""; + var str = n.substring(0, x); + n = n.substring(x); + indexIntoNewText += x; + advanceIfPossible(); + + if (builder) { + if (authorSlicer) { + authorSlicer.setBuilder(builder); + authorSlicer.takeChars(x, str); + } + else { + builder.appendNewText(str); + } + } + else { + if (authorSlicer) authorSlicer.skipChars(x); + return str; + } + }, + consumeAvailableTakenChars: function() { + return self.consumeTakenChars(self.numTakenChars()); + }, + consumeAvailableNewChars: function() { + return self.consumeNewChars(self.numNewChars()); + }, + appendAvailableNewChars: function(builder) { + return self.appendNewChars(self.numNewChars(), builder); + } + }; + } + + array.follow = function(prev, reverseInsertOrder) { + // prev: Changeset, reverseInsertOrder: boolean + + // A.compose(B.follow(A)) is the merging of Changesets A and B, which operate on the same old text. + // It is always the same as B.compose(A.follow(B, true)). + + assert(prev.oldLen() == this.oldLen(), "mismatched follow: "+prev.oldLen()+"/"+this.oldLen()); + var builder = _makeBuilder(prev.newLen(), !! this.authors); + var a = _makeTraverser(prev); + var b = _makeTraverser(this); + while (a.hasMore() || b.hasMore()) { + if (a.numNewChars() > 0 && ! reverseInsertOrder) { + builder.appendOldText(a.curIndex(), a.numNewChars()); + a.consumeAvailableNewChars(); + } + else if (b.numNewChars() > 0) { + b.appendAvailableNewChars(builder); + } + else if (a.numNewChars() > 0 && reverseInsertOrder) { + builder.appendOldText(a.curIndex(), a.numNewChars()); + a.consumeAvailableNewChars(); + } + else if (! b.hasMore()) a.consumeAvailableTakenChars(); + else if (! a.hasMore()) b.consumeAvailableTakenChars(); + else { + var x = a.takenCharsStart(); + var y = b.takenCharsStart(); + if (x < y) a.consumeTakenChars(min(a.numTakenChars(), y-x)); + else if (y < x) b.consumeTakenChars(min(b.numTakenChars(), x-y)); + else { + var takenByBoth = min(a.numTakenChars(), b.numTakenChars()); + builder.appendOldText(a.curIndex(), takenByBoth); + a.consumeTakenChars(takenByBoth); + b.consumeTakenChars(takenByBoth); + } + } + } + return builder.toChangeset(); + } + + array.encodeToString = function(asBinary) { + var stringDataArray = []; + var numsArray = []; + if (! asBinary) numsArray.push(this[0]); + numsArray.push(this[1], this[2]); + this.eachStrip(function(s, t, n) { + numsArray.push(s, t, n.length); + stringDataArray.push(n); + }, this); + if (! asBinary) { + return numsArray.join(',')+'|'+stringDataArray.join(''); + } + else { + return "A" + Changeset.numberArrayToString(numsArray) + +escapeCrazyUnicode(stringDataArray.join('')); + } + } + + function escapeCrazyUnicode(str) { + return str.replace(/\\/g, '\\\\').replace(/[\ud800-\udfff]/g, function (c) { + return "\\u"+("0000"+c.charCodeAt(0).toString(16)).slice(-4); + }); + } + + array.applyToAttributedText = Changeset.applyToAttributedText; + + function splicesFromChanges(c) { + var splices = []; + // get a list of splices, [startChar, endChar, newText] + var traverser = c.traverser(); + var oldTextLength = c.oldLen(); + var indexIntoOldText = 0; + while (traverser.hasMore() || indexIntoOldText < oldTextLength) { + var newText = ""; + var startChar = indexIntoOldText; + var endChar = indexIntoOldText; + if (traverser.numNewChars() > 0) { + newText = traverser.consumeAvailableNewChars(); + } + if (traverser.hasMore()) { + endChar = traverser.takenCharsStart(); + indexIntoOldText = endChar + traverser.numTakenChars(); + traverser.consumeAvailableTakenChars(); + } + else { + endChar = oldTextLength; + indexIntoOldText = endChar; + } + if (endChar != startChar || newText.length > 0) { + splices.push([startChar, endChar, newText]); + } + } + return splices; + } + + array.toSplices = function() { + return splicesFromChanges(this); + } + + array.characterRangeFollowThis = function(selStartChar, selEndChar, insertionsAfter) { + var changeset = this; + // represent the selection as a changeset that replaces the selection with some finite string. + // Because insertions indicate intention, it doesn't matter what this string is, and even + // if the selectionChangeset is made to "follow" other changes it will still be the only + // insertion. + var selectionChangeset = + Changeset(changeset.oldLen()).builder().appendOldText(0, selStartChar).appendNewText( + "X").appendOldText(selEndChar, changeset.oldLen() - selEndChar).toChangeset(); + var newSelectionChangeset = selectionChangeset.follow(changeset, insertionsAfter); + var selectionSplices = newSelectionChangeset.toSplices(); + function includeChar(i) { + if (! includeChar.calledYet) { + selStartChar = i; + selEndChar = i; + includeChar.calledYet = true; + } + else { + if (i < selStartChar) selStartChar = i; + if (i > selEndChar) selEndChar = i; + } + } + for(var i=0; i TRUNC) a.push("..."); + return a.join(' '); + } + function unescapeCrazyUnicode(str) { + return str.replace(/\\(u....|\\)/g, function(seq) { + if (seq == "\\\\") return "\\"; + return String.fromCharCode(Number("0x"+seq.substring(2))); + }); + } + + var numData, stringData; + var binary = false; + var typ = str.charAt(0); + if (typ == "B" || typ == "A") { + var result = Changeset.numberArrayFromString(str, 1); + numData = result[0]; + stringData = result[1]; + if (typ == "A") { + stringData = unescapeCrazyUnicode(stringData); + } + binary = true; + } + else if (typ == "C") { + var barPosition = str.indexOf('|'); + numData = str.substring(0, barPosition).split(','); + stringData = str.substring(barPosition+1); + } + else { + error("Not a changeset: "+toHex(str)); + } + var stringDataOffset = 0; + var array = []; + var ptr; + if (binary) { + array.push("Changeset", numData[0], numData[1]); + var ptr = 2; + } + else { + array.push(numData[0], Number(numData[1]), Number(numData[2])); + var ptr = 3; + } + while (ptr < numData.length) { + array.push(Number(numData[ptr++]), Number(numData[ptr++])); + var newTextLength = Number(numData[ptr++]); + array.push(stringData.substr(stringDataOffset, newTextLength)); + stringDataOffset += newTextLength; + } + if (stringDataOffset != stringData.length) { + error("Extra character data beyond end of encoded string ("+toHex(str)+")"); + } + return Changeset(array); +}; + +Changeset.numberArrayToString = function(nums) { + var array = []; + function writeNum(n) { + // does not support negative numbers + var twentyEightBit = (n & 0xfffffff); + if (twentyEightBit <= 0x7fff) { + array.push(String.fromCharCode(twentyEightBit)); + } + else { + array.push(String.fromCharCode(0xa000 | (twentyEightBit >> 15), + twentyEightBit & 0x7fff)); + } + } + writeNum(nums.length); + var len = nums.length; + for(var i=0;i 0x7fff) { + if (n >= 0xa000) { + n = (((n & 0x1fff) << 15) | str.charCodeAt(strIndex++)); + } + else { + // legacy format + n = (((n & 0x1fff) << 16) | str.charCodeAt(strIndex++)); + } + } + return n; + } + var len = readNum(); + for(var i=0;i> 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 runMatcher(c) { + // Takes "A" and returns /\u0041+/g . + // Avoid creating new objects unnecessarily by caching matchers + // as properties of this function. + var re = runMatcher[c]; + if (re) return re; + re = runMatcher[c] = new RegExp("\\u"+("0000"+c.charCodeAt(0).toString(16)).slice(-4)+"+", 'g'); + return re; + } + function runLength(str, idx, c) { + var re = runMatcher(c); + re.lastIndex = idx; + var result = re.exec(str); + if (result && result[0]) { + return result[0].length; + } + return 0; + } + + // emptyObj may be a StorableObject + Changeset.initAttributedText = function(emptyObj, initialString, initialAuthor) { + var obj = emptyObj; + obj.authorMap = { 1: (initialAuthor || '') }; + obj.text = (initialString || ''); + obj.attribs = repeatString(chr(1), obj.text.length); + return obj; + }; + Changeset.gcAttributedText = function(atObj) { + // "garbage collect" the list of authors + var removedAuthors = []; + for(var a in atObj.authorMap) { + if (atObj.attribs.indexOf(chr(Number(a))) < 0) { + removedAuthors.push(atObj.authorMap[a]); + delete atObj.authorMap[a]; + } + } + return removedAuthors; + }; + Changeset.cloneAttributedText = function(emptyObj, atObj) { + var obj = emptyObj; + obj.text = atObj.text; // string + if (atObj.attribs) obj.attribs = atObj.attribs; // string + if (atObj.attribs_c) obj.attribs_c = atObj.attribs_c; // string + obj.authorMap = {}; + for(var a in atObj.authorMap) { + obj.authorMap[a] = atObj.authorMap[a]; + } + return obj; + }; + Changeset.applyToAttributedText = function(atObj, C) { + C = (C || this); + var oldText = atObj.text; + var oldAttribs = atObj.attribs; + Changeset._assert(C.isChangeset, "applyToAttributedText: 'this' is not a changeset"); + Changeset._assert(oldText.length == C.oldLen(), + "applyToAttributedText: mismatch "+oldText.length+" / "+C.oldLen()); + var textBuf = []; + var attribsBuf = []; + var authorMap = atObj.authorMap; + function authorId(author) { + for(var a in authorMap) { + if (authorMap[Number(a)] === author) { + return Number(a); + } + } + for(var i=1;i<=60000;i++) { + // don't use "in" because it's currently broken on StorableObjects + if (authorMap[i] === undefined) { + authorMap[i] = author; + return i; + } + } + } + var myBuilder = { appendNewText: function(txt, author) { + // object that acts as a "builder" in that it receives requests from + // authorSlicer to append text attributed to different authors + attribsBuf.push(repeatString(chr(authorId(author)), txt.length)); + } }; + var authorSlicer; + if (C.authors) { + authorSlicer = C.authorSlicer(myBuilder); + } + C.eachStrip(function (s, t, n) { + textBuf.push(oldText.substr(s, t), n); + attribsBuf.push(oldAttribs.substr(s, t)); + if (authorSlicer) { + authorSlicer.takeChars(n.length, n); + } + else { + myBuilder.appendNewText(n, ''); + } + }); + atObj.text = textBuf.join(''); + atObj.attribs = attribsBuf.join(''); + return atObj; + }; + Changeset.getAttributedTextCharAuthor = function(atObj, idx) { + return atObj.authorMap[ord(atObj.attribs.charAt(idx))]; + }; + Changeset.getAttributedTextCharRunLength = function(atObj, idx) { + var c = atObj.attribs.charAt(idx); + return runLength(atObj.attribs, idx, c); + }; + Changeset.eachAuthorInAttributedText = function(atObj, func) { + // call func(author, authorNum) + for(var a in atObj.authorMap) { + if (func(atObj.authorMap[a], Number(a))) break; + } + }; + Changeset.getAttributedTextAuthorByNum = function(atObj, n) { + return atObj.authorMap[n]; + }; + // Compressed attributed text can be cloned, but nothing else until uncompressed!! + Changeset.compressAttributedText = function(atObj) { + // idempotent, mutates the object, returns it + if (atObj.attribs) { + atObj.attribs_c = atObj.attribs.replace(/([\s\S])\1{0,63}/g, function(run) { + return run.charAt(0)+chr(run.length);; + }); + delete atObj.attribs; + } + return atObj; + }; + Changeset.decompressAttributedText = function(atObj) { + // idempotent, mutates the object, returns it + if (atObj.attribs_c) { + atObj.attribs = atObj.attribs_c.replace(/[\s\S][\s\S]/g, function(run) { + return repeatString(run.charAt(0), ord(run.charAt(1))); + }); + delete atObj.attribs_c; + } + return atObj; + }; +})(); diff --git a/trunk/infrastructure/ace/www/easysync2.js b/trunk/infrastructure/ace/www/easysync2.js new file mode 100644 index 0000000..efc5b99 --- /dev/null +++ b/trunk/infrastructure/ace/www/easysync2.js @@ -0,0 +1,1968 @@ +// THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.easysync2 +// %APPJET%: jimport("com.etherpad.Easysync2Support"); + +/** + * 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 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"); + + 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 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= 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= 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 throughIterator"); + var x = '-c*3*4+6|3=az*asdf0*1*2*3+1=1-1+1*0+1=1-1+1|c=c-1'; + assert("throughIterator("+literal(x)+") == "+literal(x)); + })(); + + (function() { + print("> throughSmartAssembler"); + var x = '-c*3*4+6|3=az*asdf0*1*2*3+1=1-1+1*0+1=1-1+1|c=c-1'; + assert("throughSmartAssembler("+literal(x)+") == "+literal(x)); + })(); + + function applyMutations(mu, arrayOfArrays) { + arrayOfArrays.forEach(function (a) { + var result = mu[a[0]].apply(mu, a.slice(1)); + if (a[0] == 'remove' && a[3]) { + assertEqualStrings(a[3], result); + } + }); + } + + function mutationsToChangeset(oldLen, arrayOfArrays) { + var assem = Changeset.smartOpAssembler(); + var op = Changeset.newOp(); + var bank = Changeset.stringAssembler(); + var oldPos = 0; + var newLen = 0; + arrayOfArrays.forEach(function (a) { + if (a[0] == 'skip') { + op.opcode = '='; + op.chars = a[1]; + op.lines = (a[2] || 0); + assem.append(op); + oldPos += op.chars; + newLen += op.chars; + } + else if (a[0] == 'remove') { + op.opcode = '-'; + op.chars = a[1]; + op.lines = (a[2] || 0); + assem.append(op); + oldPos += op.chars; + } + else if (a[0] == 'insert') { + op.opcode = '+'; + bank.append(a[1]); + op.chars = a[1].length; + op.lines = (a[2] || 0); + assem.append(op); + newLen += op.chars; + } + }); + newLen += oldLen - oldPos; + assem.endDocument(); + return Changeset.pack(oldLen, newLen, assem.toString(), + bank.toString()); + } + + function runMutationTest(testId, origLines, muts, correct) { + print("> runMutationTest#"+testId); + var lines = origLines.slice(); + var mu = Changeset.textLinesMutator(lines); + applyMutations(mu, muts); + mu.close(); + assertEqualArrays(correct, lines); + + var inText = origLines.join(''); + var cs = mutationsToChangeset(inText.length, muts); + lines = origLines.slice(); + Changeset.mutateTextLines(cs, lines); + assertEqualArrays(correct, lines); + + var correctText = correct.join(''); + //print(literal(cs)); + var outText = Changeset.applyToText(cs, inText); + assertEqualStrings(correctText, outText); + } + + runMutationTest(1, ["apple\n", "banana\n", "cabbage\n", "duffle\n", "eggplant\n"], + [['remove',1,0,"a"],['insert',"tu"],['remove',1,0,"p"],['skip',4,1],['skip',7,1], + ['insert',"cream\npie\n",2],['skip',2],['insert',"bot"],['insert',"\n",1], + ['insert',"bu"],['skip',3],['remove',3,1,"ge\n"],['remove',6,0,"duffle"]], + ["tuple\n","banana\n","cream\n","pie\n", "cabot\n","bubba\n","eggplant\n"]); + + runMutationTest(2, ["apple\n", "banana\n", "cabbage\n", "duffle\n", "eggplant\n"], + [['remove',1,0,"a"],['remove',1,0,"p"],['insert',"tu"],['skip',11,2], + ['insert',"cream\npie\n",2],['skip',2],['insert',"bot"],['insert',"\n",1], + ['insert',"bu"],['skip',3],['remove',3,1,"ge\n"],['remove',6,0,"duffle"]], + ["tuple\n","banana\n","cream\n","pie\n", "cabot\n","bubba\n","eggplant\n"]); + + runMutationTest(3, ["apple\n", "banana\n", "cabbage\n", "duffle\n", "eggplant\n"], + [['remove',6,1,"apple\n"],['skip',15,2],['skip',6],['remove',1,1,"\n"], + ['remove',8,0,"eggplant"],['skip',1,1]], + ["banana\n","cabbage\n","duffle\n"]); + + runMutationTest(4, ["15\n"], + [['skip',1],['insert',"\n2\n3\n4\n",4],['skip',2,1]], + ["1\n","2\n","3\n","4\n","5\n"]); + + runMutationTest(5, ["1\n","2\n","3\n","4\n","5\n"], + [['skip',1],['remove',7,4,"\n2\n3\n4\n"],['skip',2,1]], + ["15\n"]); + + runMutationTest(6, ["123\n","abc\n","def\n","ghi\n","xyz\n"], + [['insert',"0"],['skip',4,1],['skip',4,1],['remove',8,2,"def\nghi\n"],['skip',4,1]], + ["0123\n", "abc\n", "xyz\n"]); + + runMutationTest(7, ["apple\n", "banana\n", "cabbage\n", "duffle\n", "eggplant\n"], + [['remove',6,1,"apple\n"],['skip',15,2,true],['skip',6,0,true],['remove',1,1,"\n"], + ['remove',8,0,"eggplant"],['skip',1,1,true]], + ["banana\n","cabbage\n","duffle\n"]); + + function poolOrArray(attribs) { + if (attribs.getAttrib) { + return attribs; // it's already an attrib pool + } + else { + // assume it's an array of attrib strings to be split and added + var p = new AttribPool(); + attribs.forEach(function (kv) { p.putAttrib(kv.split(',')); }); + return p; + } + } + + function runApplyToAttributionTest(testId, attribs, cs, inAttr, outCorrect) { + print("> applyToAttribution#"+testId); + var p = poolOrArray(attribs); + var result = Changeset.applyToAttribution( + Changeset.checkRep(cs), inAttr, p); + assertEqualStrings(outCorrect, result); + } + + // turn cactus\n into actusabcd\n + runApplyToAttributionTest(1, ['bold,', 'bold,true'], + "Z:7>3-1*0=1*1=1=3+4$abcd", + "+1*1+1|1+5", "+1*1+1|1+8"); + + // turn "david\ngreenspan\n" into "david\ngreen\n" + runApplyToAttributionTest(2, ['bold,', 'bold,true'], + "Z:g<4*1|1=6*1=5-4$", + "|2+g", "*1|1+6*1+5|1+1"); + + (function() { + print("> mutatorHasMore"); + var lines = ["1\n", "2\n", "3\n", "4\n"]; + var mu; + + mu = Changeset.textLinesMutator(lines); + assert(mu.hasMore()+' == true'); + mu.skip(8,4); + assert(mu.hasMore()+' == false'); + mu.close(); + assert(mu.hasMore()+' == false'); + + // still 1,2,3,4 + mu = Changeset.textLinesMutator(lines); + assert(mu.hasMore()+' == true'); + mu.remove(2,1); + assert(mu.hasMore()+' == true'); + mu.skip(2,1); + assert(mu.hasMore()+' == true'); + mu.skip(2,1); + assert(mu.hasMore()+' == true'); + mu.skip(2,1); + assert(mu.hasMore()+' == false'); + mu.insert("5\n", 1); + assert(mu.hasMore()+' == false'); + mu.close(); + assert(mu.hasMore()+' == false'); + + // 2,3,4,5 now + mu = Changeset.textLinesMutator(lines); + assert(mu.hasMore()+' == true'); + mu.remove(6,3); + assert(mu.hasMore()+' == true'); + mu.remove(2,1); + assert(mu.hasMore()+' == false'); + mu.insert("hello\n", 1); + assert(mu.hasMore()+' == false'); + mu.close(); + assert(mu.hasMore()+' == false'); + + })(); + + function runMutateAttributionTest(testId, attribs, cs, alines, outCorrect) { + print("> runMutateAttributionTest#"+testId); + var p = poolOrArray(attribs); + var alines2 = Array.prototype.slice.call(alines); + var result = Changeset.mutateAttributionLines( + Changeset.checkRep(cs), alines2, p); + assertEqualArrays(outCorrect, alines2); + + print("> runMutateAttributionTest#"+testId+".applyToAttribution"); + function removeQuestionMarks(a) { return a.replace(/\?/g, ''); } + var inMerged = Changeset.joinAttributionLines(alines.map(removeQuestionMarks)); + var correctMerged = Changeset.joinAttributionLines(outCorrect.map(removeQuestionMarks)); + var mergedResult = Changeset.applyToAttribution(cs, inMerged, p); + assertEqualStrings(correctMerged, mergedResult); + } + + // turn 123\n 456\n 789\n into 123\n 456\n 789\n + runMutateAttributionTest(1, ["bold,true"], "Z:c>0|1=4=1*0=1$", ["|1+4", "|1+4", "|1+4"], + ["|1+4", "+1*0+1|1+2", "|1+4"]); + + // make a document bold + runMutateAttributionTest(2, ["bold,true"], "Z:c>0*0|3=c$", ["|1+4", "|1+4", "|1+4"], + ["*0|1+4", "*0|1+4", "*0|1+4"]); + + // clear bold on document + runMutateAttributionTest(3, ["bold,","bold,true"], "Z:c>0*0|3=c$", + ["*1+1+1*1+1|1+1", "+1*1+1|1+2", "*1+1+1*1+1|1+1"], + ["|1+4", "|1+4", "|1+4"]); + + // add a character on line 3 of a document with 5 blank lines, and make sure + // the optimization that skips purely-kept lines is working; if any attribution string + // with a '?' is parsed it will cause an error. + runMutateAttributionTest(4, ['foo,bar','line,1','line,2','line,3','line,4','line,5'], + "Z:5>1|2=2+1$x", + ["?*1|1+1", "?*2|1+1", "*3|1+1", "?*4|1+1", "?*5|1+1"], + ["?*1|1+1", "?*2|1+1", "+1*3|1+1", "?*4|1+1", "?*5|1+1"]); + + var testPoolWithChars = (function() { + var p = new AttribPool(); + p.putAttrib(['char','newline']); + for(var i=1;i<36;i++) { + p.putAttrib(['char',Changeset.numToString(i)]); + } + p.putAttrib(['char','']); + return p; + })(); + + // based on runMutationTest#1 + runMutateAttributionTest(5, testPoolWithChars, + "Z:11>7-2*t+1*u+1|2=b|2+a=2*b+1*o+1*t+1*0|1+1*b+1*u+1=3|1-3-6$"+ + "tucream\npie\nbot\nbu", + ["*a+1*p+2*l+1*e+1*0|1+1", + "*b+1*a+1*n+1*a+1*n+1*a+1*0|1+1", + "*c+1*a+1*b+2*a+1*g+1*e+1*0|1+1", + "*d+1*u+1*f+2*l+1*e+1*0|1+1", + "*e+1*g+2*p+1*l+1*a+1*n+1*t+1*0|1+1"], + ["*t+1*u+1*p+1*l+1*e+1*0|1+1", + "*b+1*a+1*n+1*a+1*n+1*a+1*0|1+1", + "|1+6", + "|1+4", + "*c+1*a+1*b+1*o+1*t+1*0|1+1", + "*b+1*u+1*b+2*a+1*0|1+1", + "*e+1*g+2*p+1*l+1*a+1*n+1*t+1*0|1+1"]); + + // based on runMutationTest#3 + runMutateAttributionTest(6, testPoolWithChars, + "Z:117=1|4+7$\n2\n3\n4\n", + ["*1+1*5|1+2"], + ["*1+1|1+1","|1+2","|1+2","|1+2","*5|1+2"]); + + // based on runMutationTest#5 + runMutateAttributionTest(8, testPoolWithChars, + "Z:a<7=1|4-7$", + ["*1|1+2","*2|1+2","*3|1+2","*4|1+2","*5|1+2"], + ["*1+1*5|1+2"]); + + // based on runMutationTest#6 + runMutateAttributionTest(9, testPoolWithChars, + "Z:k<7*0+1*10|2=8|2-8$0", + ["*1+1*2+1*3+1|1+1","*a+1*b+1*c+1|1+1", + "*d+1*e+1*f+1|1+1","*g+1*h+1*i+1|1+1","?*x+1*y+1*z+1|1+1"], + ["*0+1|1+4", "|1+4", "?*x+1*y+1*z+1|1+1"]); + + runMutateAttributionTest(10, testPoolWithChars, + "Z:6>4=1+1=1+1|1=1+1=1*0+1$abcd", + ["|1+3", "|1+3"], + ["|1+5", "+2*0+1|1+2"]); + + + runMutateAttributionTest(11, testPoolWithChars, + "Z:s>1|1=4=6|1+1$\n", + ["*0|1+4", "*0|1+8", "*0+5|1+1", "*0|1+1", "*0|1+5", "*0|1+1", "*0|1+1", "*0|1+1", "|1+1"], + ["*0|1+4", "*0+6|1+1", "*0|1+2", "*0+5|1+1", "*0|1+1", "*0|1+5", "*0|1+1", "*0|1+1", "*0|1+1", "|1+1"]); + + function randomInlineString(len, rand) { + var assem = Changeset.stringAssembler(); + for(var i=0;i 1) doOp(); + for(var i=0;i<5;i++) doOp(); // do some more (only insertions will happen) + + var outText = outTextAssem.toString()+'\n'; + opAssem.endDocument(); + var cs = Changeset.pack(oldLen, outText.length, opAssem.toString(), charBank.toString()); + Changeset.checkRep(cs); + return [cs, outText]; + } + + function testCompose(randomSeed) { + var rand = new java.util.Random(randomSeed); + print("> testCompose#"+randomSeed); + + var p = new AttribPool(); + + var startText = randomMultiline(10, 20, rand)+'\n'; + + var x1 = randomTestChangeset(startText, rand); + var change1 = x1[0]; + var text1 = x1[1]; + + var x2 = randomTestChangeset(text1, rand); + var change2 = x2[0]; + var text2 = x2[1]; + + var x3 = randomTestChangeset(text2, rand); + var change3 = x3[0]; + var text3 = x3[1]; + + //print(literal(Changeset.toBaseTen(startText))); + //print(literal(Changeset.toBaseTen(change1))); + //print(literal(Changeset.toBaseTen(change2))); + var change12 = Changeset.checkRep(Changeset.compose(change1, change2, p)); + var change23 = Changeset.checkRep(Changeset.compose(change2, change3, p)); + var change123 = Changeset.checkRep(Changeset.compose(change12, change3, p)); + var change123a = Changeset.checkRep(Changeset.compose(change1, change23, p)); + assertEqualStrings(change123, change123a); + + assertEqualStrings(text2, Changeset.applyToText(change12, startText)); + assertEqualStrings(text3, Changeset.applyToText(change23, text1)); + assertEqualStrings(text3, Changeset.applyToText(change123, startText)); + } + + for(var i=0;i<30;i++) testCompose(i); + + (function simpleComposeAttributesTest() { + print("> simpleComposeAttributesTest"); + var p = new AttribPool(); + p.putAttrib(['bold','']); + p.putAttrib(['bold','true']); + var cs1 = Changeset.checkRep("Z:2>1*1+1*1=1$x"); + var cs2 = Changeset.checkRep("Z:3>0*0|1=3$"); + var cs12 = Changeset.checkRep(Changeset.compose(cs1, cs2, p)); + assertEqualStrings("Z:2>1+1*0|1=2$x", cs12); + })(); + + (function followAttributesTest() { + var p = new AttribPool(); + p.putAttrib(['x','']); + p.putAttrib(['x','abc']); + p.putAttrib(['x','def']); + p.putAttrib(['y','']); + p.putAttrib(['y','abc']); + p.putAttrib(['y','def']); + + function testFollow(a, b, afb, bfa, merge) { + assertEqualStrings(afb, Changeset.followAttributes(a, b, p)); + assertEqualStrings(bfa, Changeset.followAttributes(b, a, p)); + assertEqualStrings(merge, Changeset.composeAttributes(a, afb, true, p)); + assertEqualStrings(merge, Changeset.composeAttributes(b, bfa, true, p)); + } + + testFollow('', '', '', '', ''); + testFollow('*0', '', '', '*0', '*0'); + testFollow('*0', '*0', '', '', '*0'); + testFollow('*0', '*1', '', '*0', '*0'); + testFollow('*1', '*2', '', '*1', '*1'); + testFollow('*0*1', '', '', '*0*1', '*0*1'); + testFollow('*0*4', '*2*3', '*3', '*0', '*0*3'); + testFollow('*0*4', '*2', '', '*0*4', '*0*4'); + })(); + + function testFollow(randomSeed) { + var rand = new java.util.Random(randomSeed + 1000); + print("> testFollow#"+randomSeed); + + var p = new AttribPool(); + + var startText = randomMultiline(10, 20, rand)+'\n'; + + var cs1 = randomTestChangeset(startText, rand)[0]; + var cs2 = randomTestChangeset(startText, rand)[0]; + + var afb = Changeset.checkRep(Changeset.follow(cs1, cs2, false, p)); + var bfa = Changeset.checkRep(Changeset.follow(cs2, cs1, true, p)); + + var merge1 = Changeset.checkRep(Changeset.compose(cs1, afb)); + var merge2 = Changeset.checkRep(Changeset.compose(cs2, bfa)); + + assertEqualStrings(merge1, merge2); + } + + for(var i=0;i<30;i++) testFollow(i); + + function testSplitJoinAttributionLines(randomSeed) { + var rand = new java.util.Random(randomSeed + 2000); + print("> testSplitJoinAttributionLines#"+randomSeed); + + var doc = randomMultiline(10, 20, rand)+'\n'; + + function stringToOps(str) { + var assem = Changeset.mergingOpAssembler(); + var o = Changeset.newOp('+'); + o.chars = 1; + for(var i=0;i testMoveOpsToNewPool"); + + var pool1 = new AttribPool(); + var pool2 = new AttribPool(); + + pool1.putAttrib(['baz','qux']); + pool1.putAttrib(['foo','bar']); + + pool2.putAttrib(['foo','bar']); + + assertEqualStrings(Changeset.moveOpsToNewPool('Z:1>2*1+1*0+1$ab', pool1, pool2), 'Z:1>2*0+1*1+1$ab'); + assertEqualStrings(Changeset.moveOpsToNewPool('*1+1*0+1', pool1, pool2), '*0+1*1+1'); + })(); + + + (function testMakeSplice() { + print("> testMakeSplice"); + + var t = "a\nb\nc\n"; + var t2 = Changeset.applyToText(Changeset.makeSplice(t, 5, 0, "def"), t); + assertEqualStrings("a\nb\ncdef\n", t2); + + })(); + + (function testToSplices() { + print("> testToSplices"); + + var cs = Changeset.checkRep('Z:z>9*0=1=4-3+9=1|1-4-4+1*0+a$123456789abcdefghijk'); + var correctSplices = [[5, 8, "123456789"], [9, 17, "abcdefghijk"]]; + assertEqualArrays(correctSplices, Changeset.toSplices(cs)); + })(); + + function testCharacterRangeFollow(testId, cs, oldRange, insertionsAfter, correctNewRange) { + print("> testCharacterRangeFollow#"+testId); + + var cs = Changeset.checkRep(cs); + assertEqualArrays(correctNewRange, Changeset.characterRangeFollow(cs, oldRange[0], oldRange[1], + insertionsAfter)); + + } + + testCharacterRangeFollow(1, 'Z:z>9*0=1=4-3+9=1|1-4-4+1*0+a$123456789abcdefghijk', + [7, 10], false, [14, 15]); + testCharacterRangeFollow(2, "Z:bc<6|x=b4|2-6$", [400, 407], false, [400, 401]); + testCharacterRangeFollow(3, "Z:4>0-3+3$abc", [0,3], false, [3,3]); + testCharacterRangeFollow(4, "Z:4>0-3+3$abc", [0,3], true, [0,0]); + testCharacterRangeFollow(5, "Z:5>1+1=1-3+3$abcd", [1,4], false, [5,5]); + testCharacterRangeFollow(6, "Z:5>1+1=1-3+3$abcd", [1,4], true, [2,2]); + testCharacterRangeFollow(7, "Z:5>1+1=1-3+3$abcd", [0,6], false, [1,7]); + testCharacterRangeFollow(8, "Z:5>1+1=1-3+3$abcd", [0,3], false, [1,2]); + testCharacterRangeFollow(9, "Z:5>1+1=1-3+3$abcd", [2,5], false, [5,6]); + testCharacterRangeFollow(10, "Z:2>1+1$a", [0,0], false, [1,1]); + testCharacterRangeFollow(11, "Z:2>1+1$a", [0,0], true, [0,0]); + + (function testOpAttributeValue() { + print("> testOpAttributeValue"); + + var p = new AttribPool(); + p.putAttrib(['name','david']); + p.putAttrib(['color','green']); + + assertEqualStrings("david", Changeset.opAttributeValue(Changeset.stringOp('*0*1+1'), 'name', p)); + assertEqualStrings("david", Changeset.opAttributeValue(Changeset.stringOp('*0+1'), 'name', p)); + assertEqualStrings("", Changeset.opAttributeValue(Changeset.stringOp('*1+1'), 'name', p)); + assertEqualStrings("", Changeset.opAttributeValue(Changeset.stringOp('+1'), 'name', p)); + assertEqualStrings("green", Changeset.opAttributeValue(Changeset.stringOp('*0*1+1'), 'color', p)); + assertEqualStrings("green", Changeset.opAttributeValue(Changeset.stringOp('*1+1'), 'color', p)); + assertEqualStrings("", Changeset.opAttributeValue(Changeset.stringOp('*0+1'), 'color', p)); + assertEqualStrings("", Changeset.opAttributeValue(Changeset.stringOp('+1'), 'color', p)); + })(); + + function testAppendATextToAssembler(testId, atext, correctOps) { + print("> testAppendATextToAssembler#"+testId); + + var assem = Changeset.smartOpAssembler(); + Changeset.appendATextToAssembler(atext, assem); + assertEqualStrings(correctOps, assem.toString()); + } + + testAppendATextToAssembler(1, {text:"\n", attribs:"|1+1"}, ""); + testAppendATextToAssembler(2, {text:"\n\n", attribs:"|2+2"}, "|1+1"); + testAppendATextToAssembler(3, {text:"\n\n", attribs:"*x|2+2"}, "*x|1+1"); + testAppendATextToAssembler(4, {text:"\n\n", attribs:"*x|1+1|1+1"}, "*x|1+1"); + testAppendATextToAssembler(5, {text:"foo\n", attribs:"|1+4"}, "+3"); + testAppendATextToAssembler(6, {text:"\nfoo\n", attribs:"|2+5"}, "|1+1+3"); + testAppendATextToAssembler(7, {text:"\nfoo\n", attribs:"*x|2+5"}, "*x|1+1*x+3"); + testAppendATextToAssembler(8, {text:"\n\n\nfoo\n", attribs:"|2+2*x|2+5"}, "|2+2*x|1+1*x+3"); + + function testMakeAttribsString(testId, pool, opcode, attribs, correctString) { + print("> testMakeAttribsString#"+testId); + + var p = poolOrArray(pool); + var str = Changeset.makeAttribsString(opcode, attribs, p); + assertEqualStrings(correctString, str); + } + + testMakeAttribsString(1, ['bold,'], '+', [['bold','']], ''); + testMakeAttribsString(2, ['abc,def','bold,'], '=', [['bold','']], '*1'); + testMakeAttribsString(3, ['abc,def','bold,true'], '+', [['abc','def'],['bold','true']], '*0*1'); + testMakeAttribsString(4, ['abc,def','bold,true'], '+', [['bold','true'],['abc','def']], '*0*1'); + + function testSubattribution(testId, astr, start, end, correctOutput) { + print("> testSubattribution#"+testId); + + var str = Changeset.subattribution(astr, start, end); + assertEqualStrings(correctOutput, str); + } + + testSubattribution(1, "+1", 0, 0, ""); + testSubattribution(2, "+1", 0, 1, "+1"); + testSubattribution(3, "+1", 0, undefined, "+1"); + testSubattribution(4, "|1+1", 0, 0, ""); + testSubattribution(5, "|1+1", 0, 1, "|1+1"); + testSubattribution(6, "|1+1", 0, undefined, "|1+1"); + testSubattribution(7, "*0+1", 0, 0, ""); + testSubattribution(8, "*0+1", 0, 1, "*0+1"); + testSubattribution(9, "*0+1", 0, undefined, "*0+1"); + testSubattribution(10, "*0|1+1", 0, 0, ""); + testSubattribution(11, "*0|1+1", 0, 1, "*0|1+1"); + testSubattribution(12, "*0|1+1", 0, undefined, "*0|1+1"); + testSubattribution(13, "*0+2+1*1+3", 0, 1, "*0+1"); + testSubattribution(14, "*0+2+1*1+3", 0, 2, "*0+2"); + testSubattribution(15, "*0+2+1*1+3", 0, 3, "*0+2+1"); + testSubattribution(16, "*0+2+1*1+3", 0, 4, "*0+2+1*1+1"); + testSubattribution(17, "*0+2+1*1+3", 0, 5, "*0+2+1*1+2"); + testSubattribution(18, "*0+2+1*1+3", 0, 6, "*0+2+1*1+3"); + testSubattribution(19, "*0+2+1*1+3", 0, 7, "*0+2+1*1+3"); + testSubattribution(20, "*0+2+1*1+3", 0, undefined, "*0+2+1*1+3"); + testSubattribution(21, "*0+2+1*1+3", 1, undefined, "*0+1+1*1+3"); + testSubattribution(22, "*0+2+1*1+3", 2, undefined, "+1*1+3"); + testSubattribution(23, "*0+2+1*1+3", 3, undefined, "*1+3"); + testSubattribution(24, "*0+2+1*1+3", 4, undefined, "*1+2"); + testSubattribution(25, "*0+2+1*1+3", 5, undefined, "*1+1"); + testSubattribution(26, "*0+2+1*1+3", 6, undefined, ""); + testSubattribution(27, "*0+2+1*1|1+3", 0, 1, "*0+1"); + testSubattribution(28, "*0+2+1*1|1+3", 0, 2, "*0+2"); + testSubattribution(29, "*0+2+1*1|1+3", 0, 3, "*0+2+1"); + testSubattribution(30, "*0+2+1*1|1+3", 0, 4, "*0+2+1*1+1"); + testSubattribution(31, "*0+2+1*1|1+3", 0, 5, "*0+2+1*1+2"); + testSubattribution(32, "*0+2+1*1|1+3", 0, 6, "*0+2+1*1|1+3"); + testSubattribution(33, "*0+2+1*1|1+3", 0, 7, "*0+2+1*1|1+3"); + testSubattribution(34, "*0+2+1*1|1+3", 0, undefined, "*0+2+1*1|1+3"); + testSubattribution(35, "*0+2+1*1|1+3", 1, undefined, "*0+1+1*1|1+3"); + testSubattribution(36, "*0+2+1*1|1+3", 2, undefined, "+1*1|1+3"); + testSubattribution(37, "*0+2+1*1|1+3", 3, undefined, "*1|1+3"); + testSubattribution(38, "*0+2+1*1|1+3", 4, undefined, "*1|1+2"); + testSubattribution(39, "*0+2+1*1|1+3", 5, undefined, "*1|1+1"); + testSubattribution(40, "*0+2+1*1|1+3", 1, 5, "*0+1+1*1+2"); + testSubattribution(41, "*0+2+1*1|1+3", 2, 6, "+1*1|1+3"); + testSubattribution(42, "*0+2+1*1+3", 2, 6, "+1*1+3"); + + function testFilterAttribNumbers(testId, cs, filter, correctOutput) { + print("> testFilterAttribNumbers#"+testId); + + var str = Changeset.filterAttribNumbers(cs, filter); + assertEqualStrings(correctOutput, str); + } + + testFilterAttribNumbers(1, "*0*1+1+2+3*1+4*2+5*0*2*1*b*c+6", + function(n) { return (n%2) == 0; }, + "*0+1+2+3+4*2+5*0*2*c+6"); + testFilterAttribNumbers(2, "*0*1+1+2+3*1+4*2+5*0*2*1*b*c+6", + function(n) { return (n%2) == 1; }, + "*1+1+2+3*1+4+5*1*b+6"); + + function testInverse(testId, cs, lines, alines, pool, correctOutput) { + print("> testInverse#"+testId); + + pool = poolOrArray(pool); + var str = Changeset.inverse(Changeset.checkRep(cs), lines, alines, pool); + assertEqualStrings(correctOutput, str); + } + + // take "FFFFTTTTT" and apply "-FT--FFTT", the inverse of which is "--F--TT--" + testInverse(1, "Z:9>0=1*0=1*1=1=2*0=2*1|1=2$", null, ["+4*1+5"], ['bold,','bold,true'], + "Z:9>0=2*0=1=2*1=2$"); + + function testMutateTextLines(testId, cs, lines, correctLines) { + print("> testMutateTextLines#"+testId); + + var a = lines.slice(); + Changeset.mutateTextLines(cs, a); + assertEqualArrays(correctLines, a); + } + + testMutateTextLines(1, "Z:4<1|1-2-1|1+1+1$\nc", ["a\n", "b\n"], ["\n", "c\n"]); + testMutateTextLines(2, "Z:4>0|1-2-1|2+3$\nc\n", ["a\n", "b\n"], ["\n", "c\n", "\n"]); + + function testInverseRandom(randomSeed) { + var rand = new java.util.Random(randomSeed + 3000); + print("> testInverseRandom#"+randomSeed); + + var p = poolOrArray(['apple,','apple,true','banana,','banana,true']); + + var startText = randomMultiline(10, 20, rand)+'\n'; + var alines = Changeset.splitAttributionLines(Changeset.makeAttribution(startText), startText); + var lines = startText.slice(0,-1).split('\n').map(function(s) { return s+'\n'; }); + + var stylifier = randomTestChangeset(startText, rand, true)[0]; + + //print(alines.join('\n')); + Changeset.mutateAttributionLines(stylifier, alines, p); + //print(stylifier); + //print(alines.join('\n')); + Changeset.mutateTextLines(stylifier, lines); + + var changeset = randomTestChangeset(lines.join(''), rand, true)[0]; + var inverseChangeset = Changeset.inverse(changeset, lines, alines, p); + + var origLines = lines.slice(); + var origALines = alines.slice(); + + Changeset.mutateTextLines(changeset, lines); + Changeset.mutateAttributionLines(changeset, alines, p); + //print(origALines.join('\n')); + //print(changeset); + //print(inverseChangeset); + //print(origLines.map(function(s) { return '1: '+s.slice(0,-1); }).join('\n')); + //print(lines.map(function(s) { return '2: '+s.slice(0,-1); }).join('\n')); + //print(alines.join('\n')); + Changeset.mutateTextLines(inverseChangeset, lines); + Changeset.mutateAttributionLines(inverseChangeset, alines, p); + //print(lines.map(function(s) { return '3: '+s.slice(0,-1); }).join('\n')); + + assertEqualArrays(origLines, lines); + assertEqualArrays(origALines, alines); + } + + for(var i=0;i<30;i++) testInverseRandom(i); +} \ No newline at end of file diff --git a/trunk/infrastructure/ace/www/editor.css b/trunk/infrastructure/ace/www/editor.css new file mode 100644 index 0000000..9df127d --- /dev/null +++ b/trunk/infrastructure/ace/www/editor.css @@ -0,0 +1,114 @@ + +/* These CSS rules are included in both the outer and inner ACE iframe. + Also see inner.css, included only in the inner one. +*/ + +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; /* overridden by body.style */ + font-family: monospace; /* overridden by body.style */ + line-height: 16px; /* overridden by body.style */ +} + +body.doesWrap { + white-space: normal; +} + +#innerdocbody { + padding-top: 1px; /* important for some reason? */ + padding-right: 10px; + padding-bottom: 8px; + padding-left: 1px /* prevents characters from looking chopped off in FF3 */; + overflow: hidden; + /* blank 1x1 gif, so that IE8 doesn't consider the body transparent */ + background-image: url(data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==); +} + +#sidediv { + font-size: 11px; + font-family: monospace; + line-height: 16px; /* overridden by sideDiv.style */ + padding-top: 8px; /* EDIT_BODY_PADDING_TOP */ + padding-right: 3px; /* LINE_NUMBER_PADDING_RIGHT - 1 */ + position: absolute; + width: 20px; /* MIN_LINEDIV_WIDTH */ + top: 0; + left: 0; + cursor: default; + color: white; +} + +#sidedivinner { + text-align: right; +} + +.sidedivdelayed { /* class set after sizes are set */ + background-color: #eee; + color: #888 !important; + border-right: 1px solid #999; +} +.sidedivhidden { + display: none; +} + +#outerdocbody iframe { + display: block; /* codemirror says it suppresses bugs */ + position: relative; + left: 32px; /* MIN_LINEDIV_WIDTH + LINE_NUMBER_PADDING_RIGHT + EDIT_BODY_PADDING_LEFT */ + top: 7px; /* EDIT_BODY_PADDING_TOP - 1*/ + border: 0; + width: 1px; /* changed programmatically */ + height: 1px; /* changed programmatically */ +} + +#outerdocbody .hotrect { + border: 1px solid #999; + position: absolute; +} + +/* cause "body" area (e.g. where clicks are heard) to grow horizontally with text */ +body.mozilla, body.safari { + display: table-cell; +} + +body.doesWrap { + display: block !important; +} + +.safari div { + /* prevents the caret from disappearing on the longest line of the doc */ + padding-right: 1px; +} + +p { + margin: 0; +} + +/*b, strong, .Apple-style-span { font-weight: normal !important; font-style: normal !important; + color: red !important; }*/ + +#linemetricsdiv { + position: absolute; + left: -1000px; + top: -1000px; + color: white; + z-index: -1; + font-size: 12px; /* overridden by lineMetricsDiv.style */ + font-family: monospace; /* overridden by lineMetricsDiv.style */ +} + +#overlaysdiv { position: absolute; left: -1000px; top: -1000px; } diff --git a/trunk/infrastructure/ace/www/firebug/errorIcon.png b/trunk/infrastructure/ace/www/firebug/errorIcon.png new file mode 100644 index 0000000..2d75261 Binary files /dev/null and b/trunk/infrastructure/ace/www/firebug/errorIcon.png differ diff --git a/trunk/infrastructure/ace/www/firebug/firebug.css b/trunk/infrastructure/ace/www/firebug/firebug.css new file mode 100644 index 0000000..1f041c4 --- /dev/null +++ b/trunk/infrastructure/ace/www/firebug/firebug.css @@ -0,0 +1,209 @@ + +html, body { + margin: 0; + background: #FFFFFF; + font-family: Lucida Grande, Tahoma, sans-serif; + font-size: 11px; + overflow: hidden; +} + +a { + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +.toolbar { + height: 14px; + border-top: 1px solid ThreeDHighlight; + border-bottom: 1px solid ThreeDShadow; + padding: 2px 6px; + background: ThreeDFace; +} + +.toolbarRight { + position: absolute; + top: 4px; + right: 6px; +} + +#log { + overflow: auto; + position: absolute; + left: 0; + width: 100%; +} + +#commandLine { + position: absolute; + bottom: 0; + left: 0; + width: 100%; + height: 18px; + border: none; + border-top: 1px solid ThreeDShadow; +} + +/************************************************************************************************/ + +.logRow { + position: relative; + border-bottom: 1px solid #D7D7D7; + padding: 2px 4px 1px 6px; + background-color: #FFFFFF; +} + +.logRow-command { + font-family: Monaco, monospace; + color: blue; +} + +.objectBox-null { + padding: 0 2px; + border: 1px solid #666666; + background-color: #888888; + color: #FFFFFF; +} + +.objectBox-string { + font-family: Monaco, monospace; + color: red; + white-space: pre; +} + +.objectBox-number { + color: #000088; +} + +.objectBox-function { + font-family: Monaco, monospace; + color: DarkGreen; +} + +.objectBox-object { + color: DarkGreen; + font-weight: bold; +} + +/************************************************************************************************/ + +.logRow-info, +.logRow-error, +.logRow-warning { + background: #FFFFFF no-repeat 2px 2px; + padding-left: 20px; + padding-bottom: 3px; +} + +.logRow-info { + background-image: url(infoIcon.png); +} + +.logRow-warning { + background-color: cyan; + background-image: url(warningIcon.png); +} + +.logRow-error { + background-color: LightYellow; + background-image: url(errorIcon.png); +} + +.errorMessage { + vertical-align: top; + color: #FF0000; +} + +.objectBox-sourceLink { + position: absolute; + right: 4px; + top: 2px; + padding-left: 8px; + font-family: Lucida Grande, sans-serif; + font-weight: bold; + color: #0000FF; +} + +/************************************************************************************************/ + +.logRow-group { + background: #EEEEEE; + border-bottom: none; +} + +.logGroup { + background: #EEEEEE; +} + +.logGroupBox { + margin-left: 24px; + border-top: 1px solid #D7D7D7; + border-left: 1px solid #D7D7D7; +} + +/************************************************************************************************/ + +.selectorTag, +.selectorId, +.selectorClass { + font-family: Monaco, monospace; + font-weight: normal; +} + +.selectorTag { + color: #0000FF; +} + +.selectorId { + color: DarkBlue; +} + +.selectorClass { + color: red; +} + +/************************************************************************************************/ + +.objectBox-element { + font-family: Monaco, monospace; + color: #000088; +} + +.nodeChildren { + margin-left: 16px; +} + +.nodeTag { + color: blue; +} + +.nodeValue { + color: #FF0000; + font-weight: normal; +} + +.nodeText, +.nodeComment { + margin: 0 2px; + vertical-align: top; +} + +.nodeText { + color: #333333; +} + +.nodeComment { + color: DarkGreen; +} + +/************************************************************************************************/ + +.propertyNameCell { + vertical-align: top; +} + +.propertyName { + font-weight: bold; +} diff --git a/trunk/infrastructure/ace/www/firebug/firebug.html b/trunk/infrastructure/ace/www/firebug/firebug.html new file mode 100644 index 0000000..861e639 --- /dev/null +++ b/trunk/infrastructure/ace/www/firebug/firebug.html @@ -0,0 +1,23 @@ + + + + + + Firebug + + + + +
    + Clear + + Close + +
    +
    + + + + + diff --git a/trunk/infrastructure/ace/www/firebug/firebug.js b/trunk/infrastructure/ace/www/firebug/firebug.js new file mode 100644 index 0000000..d3c1978 --- /dev/null +++ b/trunk/infrastructure/ace/www/firebug/firebug.js @@ -0,0 +1,688 @@ +/** + * 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 (!("console" in window) || !("firebug" in console)) { +(function() +{ + window.console = + { + log: function() + { + logFormatted(arguments, ""); + }, + + debug: function() + { + logFormatted(arguments, "debug"); + }, + + info: function() + { + logFormatted(arguments, "info"); + }, + + warn: function() + { + logFormatted(arguments, "warning"); + }, + + error: function() + { + logFormatted(arguments, "error"); + }, + + assert: function(truth, message) + { + if (!truth) + { + var args = []; + for (var i = 1; i < arguments.length; ++i) + args.push(arguments[i]); + + logFormatted(args.length ? args : ["Assertion Failure"], "error"); + throw message ? message : "Assertion Failure"; + } + }, + + dir: function(object) + { + var html = []; + + var pairs = []; + for (var name in object) + { + try + { + pairs.push([name, object[name]]); + } + catch (exc) + { + } + } + + pairs.sort(function(a, b) { return a[0] < b[0] ? -1 : 1; }); + + html.push(''); + for (var i = 0; i < pairs.length; ++i) + { + var name = pairs[i][0], value = pairs[i][1]; + + html.push('', + '', ''); + } + html.push('
    ', + escapeHTML(name), ''); + appendObject(value, html); + html.push('
    '); + + logRow(html, "dir"); + }, + + dirxml: function(node) + { + var html = []; + + appendNode(node, html); + logRow(html, "dirxml"); + }, + + group: function() + { + logRow(arguments, "group", pushGroup); + }, + + groupEnd: function() + { + logRow(arguments, "", popGroup); + }, + + time: function(name) + { + timeMap[name] = (new Date()).getTime(); + }, + + timeEnd: function(name) + { + if (name in timeMap) + { + var delta = (new Date()).getTime() - timeMap[name]; + logFormatted([name+ ":", delta+"ms"]); + delete timeMap[name]; + } + }, + + count: function() + { + this.warn(["count() not supported."]); + }, + + trace: function() + { + this.warn(["trace() not supported."]); + }, + + profile: function() + { + this.warn(["profile() not supported."]); + }, + + profileEnd: function() + { + }, + + clear: function() + { + consoleBody.innerHTML = ""; + }, + + open: function() + { + toggleConsole(true); + }, + + close: function() + { + if (frameVisible) + toggleConsole(); + } + }; + + // ******************************************************************************************** + + var consoleFrame = null; + var consoleBody = null; + var commandLine = null; + + var frameVisible = false; + var messageQueue = []; + var groupStack = []; + var timeMap = {}; + + var clPrefix = ">>> "; + + var isFirefox = navigator.userAgent.indexOf("Firefox") != -1; + var isIE = navigator.userAgent.indexOf("MSIE") != -1; + var isOpera = navigator.userAgent.indexOf("Opera") != -1; + var isSafari = navigator.userAgent.indexOf("AppleWebKit") != -1; + + // ******************************************************************************************** + + function toggleConsole(forceOpen) + { + frameVisible = forceOpen || !frameVisible; + if (consoleFrame) + consoleFrame.style.visibility = frameVisible ? "visible" : "hidden"; + else + waitForBody(); + } + + function focusCommandLine() + { + toggleConsole(true); + if (commandLine) + commandLine.focus(); + } + + function waitForBody() + { + if (document.body) + createFrame(); + else + setTimeout(waitForBody, 200); + } + + function createFrame() + { + if (consoleFrame) + return; + + window.onFirebugReady = function(doc) + { + window.onFirebugReady = null; + + var toolbar = doc.getElementById("toolbar"); + toolbar.onmousedown = onSplitterMouseDown; + + commandLine = doc.getElementById("commandLine"); + addEvent(commandLine, "keydown", onCommandLineKeyDown); + + addEvent(doc, isIE || isSafari ? "keydown" : "keypress", onKeyDown); + + consoleBody = doc.getElementById("log"); + layout(); + flush(); + } + + var baseURL = getFirebugURL(); + + consoleFrame = document.createElement("iframe"); + consoleFrame.setAttribute("src", baseURL+"/firebug.html"); + consoleFrame.setAttribute("frameBorder", "0"); + consoleFrame.style.visibility = (frameVisible ? "visible" : "hidden"); + consoleFrame.style.zIndex = "2147483647"; + consoleFrame.style.position = "fixed"; + consoleFrame.style.width = "100%"; + consoleFrame.style.left = "0"; + consoleFrame.style.bottom = "0"; + consoleFrame.style.height = "200px"; + document.body.appendChild(consoleFrame); + } + + function getFirebugURL() + { + var scripts = document.getElementsByTagName("script"); + for (var i = 0; i < scripts.length; ++i) + { + if (scripts[i].src.indexOf("firebug.js") != -1) + { + var lastSlash = scripts[i].src.lastIndexOf("/"); + return scripts[i].src.substr(0, lastSlash); + } + } + } + + function evalCommandLine() + { + var text = commandLine.value; + commandLine.value = ""; + + logRow([clPrefix, text], "command"); + + var value; + try + { + value = eval(text); + } + catch (exc) + { + } + + console.log(value); + } + + function layout() + { + var toolbar = consoleBody.ownerDocument.getElementById("toolbar"); + var height = consoleFrame.offsetHeight - (toolbar.offsetHeight + commandLine.offsetHeight); + consoleBody.style.top = toolbar.offsetHeight + "px"; + consoleBody.style.height = height + "px"; + + commandLine.style.top = (consoleFrame.offsetHeight - commandLine.offsetHeight) + "px"; + } + + function logRow(message, className, handler) + { + if (consoleBody) + writeMessage(message, className, handler); + else + { + messageQueue.push([message, className, handler]); + waitForBody(); + } + } + + function flush() + { + var queue = messageQueue; + messageQueue = []; + + for (var i = 0; i < queue.length; ++i) + writeMessage(queue[i][0], queue[i][1], queue[i][2]); + } + + function writeMessage(message, className, handler) + { + var isScrolledToBottom = + consoleBody.scrollTop + consoleBody.offsetHeight >= consoleBody.scrollHeight; + + if (!handler) + handler = writeRow; + + handler(message, className); + + if (isScrolledToBottom) + consoleBody.scrollTop = consoleBody.scrollHeight - consoleBody.offsetHeight; + } + + function appendRow(row) + { + var container = groupStack.length ? groupStack[groupStack.length-1] : consoleBody; + container.appendChild(row); + } + + function writeRow(message, className) + { + var row = consoleBody.ownerDocument.createElement("div"); + row.className = "logRow" + (className ? " logRow-"+className : ""); + row.innerHTML = message.join(""); + appendRow(row); + } + + function pushGroup(message, className) + { + logFormatted(message, className); + + var groupRow = consoleBody.ownerDocument.createElement("div"); + groupRow.className = "logGroup"; + var groupRowBox = consoleBody.ownerDocument.createElement("div"); + groupRowBox.className = "logGroupBox"; + groupRow.appendChild(groupRowBox); + appendRow(groupRowBox); + groupStack.push(groupRowBox); + } + + function popGroup() + { + groupStack.pop(); + } + + // ******************************************************************************************** + + function logFormatted(objects, className) + { + var html = []; + + var format = objects[0]; + var objIndex = 0; + + if (typeof(format) != "string") + { + format = ""; + objIndex = -1; + } + + var parts = parseFormat(format); + for (var i = 0; i < parts.length; ++i) + { + var part = parts[i]; + if (part && typeof(part) == "object") + { + var object = objects[++objIndex]; + part.appender(object, html); + } + else + appendText(part, html); + } + + for (var i = objIndex+1; i < objects.length; ++i) + { + appendText(" ", html); + + var object = objects[i]; + if (typeof(object) == "string") + appendText(object, html); + else + appendObject(object, html); + } + + logRow(html, className); + } + + function parseFormat(format) + { + var parts = []; + + var reg = /((^%|[^\\]%)(\d+)?(\.)([a-zA-Z]))|((^%|[^\\]%)([a-zA-Z]))/; + var appenderMap = {s: appendText, d: appendInteger, i: appendInteger, f: appendFloat}; + + for (var m = reg.exec(format); m; m = reg.exec(format)) + { + var type = m[8] ? m[8] : m[5]; + var appender = type in appenderMap ? appenderMap[type] : appendObject; + var precision = m[3] ? parseInt(m[3]) : (m[4] == "." ? -1 : 0); + + parts.push(format.substr(0, m[0][0] == "%" ? m.index : m.index+1)); + parts.push({appender: appender, precision: precision}); + + format = format.substr(m.index+m[0].length); + } + + parts.push(format); + + return parts; + } + + function escapeHTML(value) + { + function replaceChars(ch) + { + switch (ch) + { + case "<": + return "<"; + case ">": + return ">"; + case "&": + return "&"; + case "'": + return "'"; + case '"': + return """; + } + return "?"; + }; + return String(value).replace(/[<>&"']/g, replaceChars); + } + + function objectToString(object) + { + try + { + return object+""; + } + catch (exc) + { + return null; + } + } + + // ******************************************************************************************** + + function appendText(object, html) + { + html.push(escapeHTML(objectToString(object))); + } + + function appendNull(object, html) + { + html.push('', escapeHTML(objectToString(object)), ''); + } + + function appendString(object, html) + { + html.push('"', escapeHTML(objectToString(object)), + '"'); + } + + function appendInteger(object, html) + { + html.push('', escapeHTML(objectToString(object)), ''); + } + + function appendFloat(object, html) + { + html.push('', escapeHTML(objectToString(object)), ''); + } + + function appendFunction(object, html) + { + var reName = /function ?(.*?)\(/; + var m = reName.exec(objectToString(object)); + var name = m ? m[1] : "function"; + html.push('', escapeHTML(name), '()'); + } + + function appendObject(object, html) + { + try + { + if (object == undefined) + appendNull("undefined", html); + else if (object == null) + appendNull("null", html); + else if (typeof object == "string") + appendString(object, html); + else if (typeof object == "number") + appendInteger(object, html); + else if (typeof object == "function") + appendFunction(object, html); + else if (object.nodeType == 1) + appendSelector(object, html); + else if (typeof object == "object") + appendObjectFormatted(object, html); + else + appendText(object, html); + } + catch (exc) + { + } + } + + function appendObjectFormatted(object, html) + { + var text = objectToString(object); + var reObject = /\[object (.*?)\]/; + + var m = reObject.exec(text); + html.push('', m ? m[1] : text, '') + } + + function appendSelector(object, html) + { + html.push(''); + + html.push('', escapeHTML(object.nodeName.toLowerCase()), ''); + if (object.id) + html.push('#', escapeHTML(object.id), ''); + if (object.className) + html.push('.', escapeHTML(object.className), ''); + + html.push(''); + } + + function appendNode(node, html) + { + if (node.nodeType == 1) + { + html.push( + '
    ', + '<', node.nodeName.toLowerCase(), ''); + + for (var i = 0; i < node.attributes.length; ++i) + { + var attr = node.attributes[i]; + if (!attr.specified) + continue; + + html.push(' ', attr.nodeName.toLowerCase(), + '="', escapeHTML(attr.nodeValue), + '"') + } + + if (node.firstChild) + { + html.push('>
    '); + + for (var child = node.firstChild; child; child = child.nextSibling) + appendNode(child, html); + + html.push('
    </', + node.nodeName.toLowerCase(), '>
    '); + } + else + html.push('/>'); + } + else if (node.nodeType == 3) + { + html.push('
    ', escapeHTML(node.nodeValue), + '
    '); + } + } + + // ******************************************************************************************** + + function addEvent(object, name, handler) + { + if (document.all) + object.attachEvent("on"+name, handler); + else + object.addEventListener(name, handler, false); + } + + function removeEvent(object, name, handler) + { + if (document.all) + object.detachEvent("on"+name, handler); + else + object.removeEventListener(name, handler, false); + } + + function cancelEvent(event) + { + if (document.all) + event.cancelBubble = true; + else + event.stopPropagation(); + } + + function onError(msg, href, lineNo) + { + var html = []; + + var lastSlash = href.lastIndexOf("/"); + var fileName = lastSlash == -1 ? href : href.substr(lastSlash+1); + + html.push( + '', msg, '', + '' + ); + + logRow(html, "error"); + }; + + function onKeyDown(event) + { + if (event.keyCode == 123) + toggleConsole(); + else if ((event.keyCode == 108 || event.keyCode == 76) && event.shiftKey + && (event.metaKey || event.ctrlKey)) + focusCommandLine(); + else + return; + + cancelEvent(event); + } + + function onSplitterMouseDown(event) + { + if (isSafari || isOpera) + return; + + addEvent(document, "mousemove", onSplitterMouseMove); + addEvent(document, "mouseup", onSplitterMouseUp); + + for (var i = 0; i < frames.length; ++i) + { + addEvent(frames[i].document, "mousemove", onSplitterMouseMove); + addEvent(frames[i].document, "mouseup", onSplitterMouseUp); + } + } + + function onSplitterMouseMove(event) + { + var win = document.all + ? event.srcElement.ownerDocument.parentWindow + : event.target.ownerDocument.defaultView; + + var clientY = event.clientY; + if (win != win.parent) + clientY += win.frameElement ? win.frameElement.offsetTop : 0; + + var height = consoleFrame.offsetTop + consoleFrame.clientHeight; + var y = height - clientY; + + consoleFrame.style.height = y + "px"; + layout(); + } + + function onSplitterMouseUp(event) + { + removeEvent(document, "mousemove", onSplitterMouseMove); + removeEvent(document, "mouseup", onSplitterMouseUp); + + for (var i = 0; i < frames.length; ++i) + { + removeEvent(frames[i].document, "mousemove", onSplitterMouseMove); + removeEvent(frames[i].document, "mouseup", onSplitterMouseUp); + } + } + + function onCommandLineKeyDown(event) + { + if (event.keyCode == 13) + evalCommandLine(); + else if (event.keyCode == 27) + commandLine.value = ""; + } + + window.onerror = onError; + addEvent(document, isIE || isSafari ? "keydown" : "keypress", onKeyDown); + + if (document.documentElement.getAttribute("debug") == "true") + toggleConsole(true); +})(); +} diff --git a/trunk/infrastructure/ace/www/firebug/firebugx.js b/trunk/infrastructure/ace/www/firebug/firebugx.js new file mode 100644 index 0000000..b2cc49c --- /dev/null +++ b/trunk/infrastructure/ace/www/firebug/firebugx.js @@ -0,0 +1,26 @@ +/** + * 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 (!("console" in window) || !("firebug" in console)) +{ + var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml", + "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"]; + + window.console = {}; + for (var i = 0; i < names.length; ++i) + window.console[names[i]] = function() {} +} \ No newline at end of file diff --git a/trunk/infrastructure/ace/www/firebug/infoIcon.png b/trunk/infrastructure/ace/www/firebug/infoIcon.png new file mode 100644 index 0000000..da1e533 Binary files /dev/null and b/trunk/infrastructure/ace/www/firebug/infoIcon.png differ diff --git a/trunk/infrastructure/ace/www/firebug/warningIcon.png b/trunk/infrastructure/ace/www/firebug/warningIcon.png new file mode 100644 index 0000000..de51084 Binary files /dev/null and b/trunk/infrastructure/ace/www/firebug/warningIcon.png differ diff --git a/trunk/infrastructure/ace/www/index.html b/trunk/infrastructure/ace/www/index.html new file mode 100644 index 0000000..a1e6e96 --- /dev/null +++ b/trunk/infrastructure/ace/www/index.html @@ -0,0 +1,50 @@ + + + + A Code Editor + + + + + + + + + + + +
    + + diff --git a/trunk/infrastructure/ace/www/inner.css b/trunk/infrastructure/ace/www/inner.css new file mode 100644 index 0000000..7479cfe --- /dev/null +++ b/trunk/infrastructure/ace/www/inner.css @@ -0,0 +1,48 @@ + +/* Firefox (3) is bad about keeping the text cursor in design mode; + various actions (clicking, dragging, scroll-wheel) lose it and it + doesn't come back easily, presumably because of optimizations. + These rules try to maximize the chance Firefox will think the cursor + needs changing again. +*/ +html { cursor: text; } /* in Safari, produces text cursor for whole doc (inc. below body) */ +span { cursor: auto; } + +a { cursor: pointer !important; } + +/*span { padding-bottom: 1px; }/* padding-top: 1px; }*/ + +/*.inspoint_atstart_generic { background: transparent url(/genimg/solid/2x10/000000.gif) repeat-y left top } +.inspoint_atend_generic { background: transparent url(/genimg/solid/2x10/000000.gif) repeat-y right top }*/ + +/*div { background: transparent url(/static/img/acecarets/default.gif) repeat-y left top }*/ + +/*tt { padding-left: 3px; padding-right: 3px; margin-right: -3px; margin-left: -3px; }*/ + +/*div { display: list-item; list-style: disc outside; margin-left: 20px; }*/ +/*div:before { content:"foo" }*/ + +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; } diff --git a/trunk/infrastructure/ace/www/jquery-1.2.1.js b/trunk/infrastructure/ace/www/jquery-1.2.1.js new file mode 100644 index 0000000..b4eb132 --- /dev/null +++ b/trunk/infrastructure/ace/www/jquery-1.2.1.js @@ -0,0 +1,2992 @@ +(function(){ +/* + * jQuery 1.2.1 - New Wave Javascript + * + * Copyright (c) 2007 John Resig (jquery.com) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * $Date: 2007-09-16 23:42:06 -0400 (Sun, 16 Sep 2007) $ + * $Rev: 3353 $ + */ + +// Map over jQuery in case of overwrite +if ( typeof jQuery != "undefined" ) + var _jQuery = jQuery; + +var jQuery = window.jQuery = function(selector, context) { + // If the context is a namespace object, return a new object + return this instanceof jQuery ? + this.init(selector, context) : + new jQuery(selector, context); +}; + +// Map over the $ in case of overwrite +if ( typeof $ != "undefined" ) + var _$ = $; + +// Map the jQuery namespace to the '$' one +window.$ = jQuery; + +var quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/; + +jQuery.fn = jQuery.prototype = { + init: function(selector, context) { + // Make sure that a selection was provided + selector = selector || document; + + // Handle HTML strings + if ( typeof selector == "string" ) { + var m = quickExpr.exec(selector); + if ( m && (m[1] || !context) ) { + // HANDLE: $(html) -> $(array) + if ( m[1] ) + selector = jQuery.clean( [ m[1] ], context ); + + // HANDLE: $("#id") + else { + var tmp = document.getElementById( m[3] ); + if ( tmp ) + // Handle the case where IE and Opera return items + // by name instead of ID + if ( tmp.id != m[3] ) + return jQuery().find( selector ); + else { + this[0] = tmp; + this.length = 1; + return this; + } + else + selector = []; + } + + // HANDLE: $(expr) + } else + return new jQuery( context ).find( selector ); + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction(selector) ) + return new jQuery(document)[ jQuery.fn.ready ? "ready" : "load" ]( selector ); + + return this.setArray( + // HANDLE: $(array) + selector.constructor == Array && selector || + + // HANDLE: $(arraylike) + // Watch for when an array-like object is passed as the selector + (selector.jquery || selector.length && selector != window && !selector.nodeType && selector[0] != undefined && selector[0].nodeType) && jQuery.makeArray( selector ) || + + // HANDLE: $(*) + [ selector ] ); + }, + + jquery: "1.2.1", + + size: function() { + return this.length; + }, + + length: 0, + + get: function( num ) { + return num == undefined ? + + // Return a 'clean' array + jQuery.makeArray( this ) : + + // Return just the object + this[num]; + }, + + pushStack: function( a ) { + var ret = jQuery(a); + ret.prevObject = this; + return ret; + }, + + setArray: function( a ) { + this.length = 0; + Array.prototype.push.apply( this, a ); + return this; + }, + + each: function( fn, args ) { + return jQuery.each( this, fn, args ); + }, + + index: function( obj ) { + var pos = -1; + this.each(function(i){ + if ( this == obj ) pos = i; + }); + return pos; + }, + + attr: function( key, value, type ) { + var obj = key; + + // Look for the case where we're accessing a style value + if ( key.constructor == String ) + if ( value == undefined ) + return this.length && jQuery[ type || "attr" ]( this[0], key ) || undefined; + else { + obj = {}; + obj[ key ] = value; + } + + // Check to see if we're setting style values + return this.each(function(index){ + // Set all the styles + for ( var prop in obj ) + jQuery.attr( + type ? this.style : this, + prop, jQuery.prop(this, obj[prop], type, index, prop) + ); + }); + }, + + css: function( key, value ) { + return this.attr( key, value, "curCSS" ); + }, + + text: function(e) { + if ( typeof e != "object" && e != null ) + return this.empty().append( document.createTextNode( e ) ); + + var t = ""; + jQuery.each( e || this, function(){ + jQuery.each( this.childNodes, function(){ + if ( this.nodeType != 8 ) + t += this.nodeType != 1 ? + this.nodeValue : jQuery.fn.text([ this ]); + }); + }); + return t; + }, + + 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, 1, function(a){ + this.appendChild( a ); + }); + }, + + prepend: function() { + return this.domManip(arguments, true, -1, function(a){ + this.insertBefore( a, this.firstChild ); + }); + }, + + before: function() { + return this.domManip(arguments, false, 1, function(a){ + this.parentNode.insertBefore( a, this ); + }); + }, + + after: function() { + return this.domManip(arguments, false, -1, function(a){ + this.parentNode.insertBefore( a, this.nextSibling ); + }); + }, + + end: function() { + return this.prevObject || jQuery([]); + }, + + find: function(t) { + var data = jQuery.map(this, function(a){ return jQuery.find(t,a); }); + return this.pushStack( /[^+>] [^+>]/.test( t ) || t.indexOf("..") > -1 ? + jQuery.unique( data ) : data ); + }, + + clone: function(events) { + // Do the clone + var ret = this.map(function(){ + return this.outerHTML ? jQuery(this.outerHTML)[0] : 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) { + 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(t) { + return this.pushStack( + jQuery.isFunction( t ) && + jQuery.grep(this, function(el, index){ + return t.apply(el, [index]); + }) || + + jQuery.multiFilter(t,this) ); + }, + + not: function(t) { + return this.pushStack( + t.constructor == String && + jQuery.multiFilter(t, this, true) || + + jQuery.grep(this, function(a) { + return ( t.constructor == Array || t.jquery ) + ? jQuery.inArray( a, t ) < 0 + : a != t; + }) + ); + }, + + add: function(t) { + return this.pushStack( jQuery.merge( + this.get(), + t.constructor == String ? + jQuery(t).get() : + t.length != undefined && (!t.nodeName || jQuery.nodeName(t, "form")) ? + t : [t] ) + ); + }, + + is: function(expr) { + return expr ? jQuery.multiFilter(expr,this).length > 0 : false; + }, + + hasClass: function(expr) { + return this.is("." + expr); + }, + + val: function( val ) { + if ( val == undefined ) { + if ( this.length ) { + var elem = this[0]; + + // We need to handle select boxes special + if ( jQuery.nodeName(elem, "select") ) { + var index = elem.selectedIndex, + a = [], + 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 + var val = jQuery.browser.msie && !option.attributes["value"].specified ? option.text : option.value; + + // We don't need an array for one selects + if ( one ) + return val; + + // Multi-Selects return an array + a.push(val); + } + } + + return a; + + // Everything else, we just grab the value + } else + return this[0].value.replace(/\r/g, ""); + } + } else + return this.each(function(){ + if ( val.constructor == Array && /radio|checkbox/.test(this.type) ) + this.checked = (jQuery.inArray(this.value, val) >= 0 || + jQuery.inArray(this.name, val) >= 0); + else if ( jQuery.nodeName(this, "select") ) { + var tmp = val.constructor == Array ? val : [val]; + + jQuery("option", this).each(function(){ + this.selected = (jQuery.inArray(this.value, tmp) >= 0 || + jQuery.inArray(this.text, tmp) >= 0); + }); + + if ( !tmp.length ) + this.selectedIndex = -1; + } else + this.value = val; + }); + }, + + html: function( val ) { + return val == undefined ? + ( this.length ? this[0].innerHTML : null ) : + this.empty().append( val ); + }, + + replaceWith: function( val ) { + return this.after( val ).remove(); + }, + + eq: function(i){ + return this.slice(i, i+1); + }, + + slice: function() { + return this.pushStack( Array.prototype.slice.apply( this, arguments ) ); + }, + + map: function(fn) { + return this.pushStack(jQuery.map( this, function(elem,i){ + return fn.call( elem, i, elem ); + })); + }, + + andSelf: function() { + return this.add( this.prevObject ); + }, + + domManip: function(args, table, dir, fn) { + var clone = this.length > 1, a; + + return this.each(function(){ + if ( !a ) { + a = jQuery.clean(args, this.ownerDocument); + if ( dir < 0 ) + a.reverse(); + } + + var obj = this; + + if ( table && jQuery.nodeName(this, "table") && jQuery.nodeName(a[0], "tr") ) + obj = this.getElementsByTagName("tbody")[0] || this.appendChild(document.createElement("tbody")); + + jQuery.each( a, function(){ + var elem = clone ? this.cloneNode(true) : this; + if ( !evalScript(0, elem) ) + fn.call( obj, elem ); + }); + }); + } +}; + +function evalScript(i, elem){ + var script = jQuery.nodeName(elem, "script"); + + if ( script ) { + 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); + + } else if ( elem.nodeType == 1 ) + jQuery("script", elem).each(evalScript); + + return script; +} + +jQuery.extend = jQuery.fn.extend = function() { + // copy reference to target object + var target = arguments[0] || {}, a = 1, al = arguments.length, deep = false; + + // Handle a deep copy situation + if ( target.constructor == Boolean ) { + deep = target; + target = arguments[1] || {}; + } + + // extend jQuery itself if only one argument is passed + if ( al == 1 ) { + target = this; + a = 0; + } + + var prop; + + for ( ; a < al; a++ ) + // Only deal with non-null/undefined values + if ( (prop = arguments[a]) != null ) + // Extend the base object + for ( var i in prop ) { + // Prevent never-ending loop + if ( target == prop[i] ) + continue; + + // Recurse if we're merging object values + if ( deep && typeof prop[i] == 'object' && target[i] ) + jQuery.extend( target[i], prop[i] ); + + // Don't bring in undefined values + else if ( prop[i] != undefined ) + target[i] = prop[i]; + } + + // Return the modified object + return target; +}; + +var expando = "jQuery" + (new Date()).getTime(), uuid = 0, win = {}; + +jQuery.extend({ + noConflict: function(deep) { + window.$ = _$; + if ( deep ) + window.jQuery = _jQuery; + return jQuery; + }, + + // This may seem like some crazy code, but trust me when I say that this + // is the only cross-browser way to do this. --John + isFunction: function( fn ) { + return !!fn && typeof fn != "string" && !fn.nodeName && + fn.constructor != Array && /function/i.test( fn + "" ); + }, + + // check if an element is in a XML document + isXMLDoc: function(elem) { + return elem.documentElement && !elem.body || + elem.tagName && elem.ownerDocument && !elem.ownerDocument.body; + }, + + // Evalulates a script in a global context + // Evaluates Async. in Safari 2 :-( + globalEval: function( data ) { + data = jQuery.trim( data ); + if ( data ) { + if ( window.execScript ) + window.execScript( data ); + else if ( jQuery.browser.safari ) + // safari doesn't provide a synchronous global eval + window.setTimeout( data, 0 ); + else + eval.call( window, data ); + } + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase(); + }, + + cache: {}, + + data: function( elem, name, data ) { + elem = elem == window ? win : 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 ? win : 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( obj, fn, args ) { + if ( args ) { + if ( obj.length == undefined ) + for ( var i in obj ) + fn.apply( obj[i], args ); + else + for ( var i = 0, ol = obj.length; i < ol; i++ ) + if ( fn.apply( obj[i], args ) === false ) break; + + // A special, fast, case for the most common use of each + } else { + if ( obj.length == undefined ) + for ( var i in obj ) + fn.call( obj[i], i, obj[i] ); + else + for ( var i = 0, ol = obj.length, val = obj[0]; + i < ol && fn.call(val,i,val) !== false; val = obj[++i] ){} + } + + return obj; + }, + + prop: function(elem, value, type, index, prop){ + // Handle executable functions + if ( jQuery.isFunction( value ) ) + value = value.call( elem, [index] ); + + // exclude the following css properties to add px + var exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i; + + // Handle passing in a number to a CSS property + return value && value.constructor == Number && type == "curCSS" && !exclude.test(prop) ? + value + "px" : + value; + }, + + className: { + // internal only, use addClass("class") + add: function( elem, c ){ + jQuery.each( (c || "").split(/\s+/), function(i, cur){ + if ( !jQuery.className.has( elem.className, cur ) ) + elem.className += ( elem.className ? " " : "" ) + cur; + }); + }, + + // internal only, use removeClass("class") + remove: function( elem, c ){ + elem.className = c != undefined ? + jQuery.grep( elem.className.split(/\s+/), function(cur){ + return !jQuery.className.has( c, cur ); + }).join(" ") : ""; + }, + + // internal only, use is(".class") + has: function( t, c ) { + return jQuery.inArray( c, (t.className || t).toString().split(/\s+/) ) > -1; + } + }, + + swap: function(e,o,f) { + for ( var i in o ) { + e.style["old"+i] = e.style[i]; + e.style[i] = o[i]; + } + f.apply( e, [] ); + for ( var i in o ) + e.style[i] = e.style["old"+i]; + }, + + css: function(e,p) { + if ( p == "height" || p == "width" ) { + var old = {}, oHeight, oWidth, d = ["Top","Bottom","Right","Left"]; + + jQuery.each( d, function(){ + old["padding" + this] = 0; + old["border" + this + "Width"] = 0; + }); + + jQuery.swap( e, old, function() { + if ( jQuery(e).is(':visible') ) { + oHeight = e.offsetHeight; + oWidth = e.offsetWidth; + } else { + e = jQuery(e.cloneNode(true)) + .find(":radio").removeAttr("checked").end() + .css({ + visibility: "hidden", position: "absolute", display: "block", right: "0", left: "0" + }).appendTo(e.parentNode)[0]; + + var parPos = jQuery.css(e.parentNode,"position") || "static"; + if ( parPos == "static" ) + e.parentNode.style.position = "relative"; + + oHeight = e.clientHeight; + oWidth = e.clientWidth; + + if ( parPos == "static" ) + e.parentNode.style.position = "static"; + + e.parentNode.removeChild(e); + } + }); + + return p == "height" ? oHeight : oWidth; + } + + return jQuery.curCSS( e, p ); + }, + + curCSS: function(elem, prop, force) { + var ret, stack = [], swap = []; + + // A helper method for determining if an element's values are broken + function color(a){ + if ( !jQuery.browser.safari ) + return false; + + var ret = document.defaultView.getComputedStyle(a,null); + return !ret || ret.getPropertyValue("color") == ""; + } + + if (prop == "opacity" && jQuery.browser.msie) { + ret = jQuery.attr(elem.style, "opacity"); + return ret == "" ? "1" : ret; + } + + if (prop.match(/float/i)) + prop = styleFloat; + + if (!force && elem.style[prop]) + ret = elem.style[prop]; + + else if (document.defaultView && document.defaultView.getComputedStyle) { + + if (prop.match(/float/i)) + prop = "float"; + + prop = prop.replace(/([A-Z])/g,"-$1").toLowerCase(); + var cur = document.defaultView.getComputedStyle(elem, null); + + if ( cur && !color(elem) ) + ret = cur.getPropertyValue(prop); + + // If the element isn't reporting its values properly in Safari + // then some display: none elements are involved + else { + // Locate all of the parent display: none elements + for ( var a = elem; 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 ( a = 0; a < stack.length; a++ ) + if ( color(stack[a]) ) { + swap[a] = stack[a].style.display; + stack[a].style.display = "block"; + } + + // Since we flip the display style, we have to handle that + // one special, otherwise get the value + ret = prop == "display" && swap[stack.length-1] != null ? + "none" : + document.defaultView.getComputedStyle(elem,null).getPropertyValue(prop) || ""; + + // Finally, revert the display styles back + for ( a = 0; a < swap.length; a++ ) + if ( swap[a] != null ) + stack[a].style.display = swap[a]; + } + + if ( prop == "opacity" && ret == "" ) + ret = "1"; + + } else if (elem.currentStyle) { + var newProp = prop.replace(/\-(\w)/g,function(m,c){return c.toUpperCase();}); + ret = elem.currentStyle[prop] || elem.currentStyle[newProp]; + + // 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) ) { + var style = elem.style.left; + var runtimeStyle = elem.runtimeStyle.left; + elem.runtimeStyle.left = elem.currentStyle.left; + elem.style.left = ret || 0; + ret = elem.style.pixelLeft + "px"; + elem.style.left = style; + elem.runtimeStyle.left = runtimeStyle; + } + } + + return ret; + }, + + clean: function(a, doc) { + var r = []; + doc = doc || document; + + jQuery.each( a, function(i,arg){ + if ( !arg ) return; + + if ( arg.constructor == Number ) + arg = arg.toString(); + + // Convert html string into DOM nodes + if ( typeof arg == "string" ) { + // Fix "XHTML"-style tags in all browsers + arg = arg.replace(/(<(\w+)[^>]*?)\/>/g, function(m, all, tag){ + return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area)$/i)? m : all+">"; + }); + + // Trim whitespace, otherwise indexOf won't work as expected + var s = jQuery.trim(arg).toLowerCase(), div = doc.createElement("div"), tb = []; + + var wrap = + // option or optgroup + !s.indexOf("", ""] || + + !s.indexOf("", ""] || + + s.match(/^<(thead|tbody|tfoot|colg|cap)/) && + [1, "", "
    "] || + + !s.indexOf("", ""] || + + // matched above + (!s.indexOf("", ""] || + + !s.indexOf("", ""] || + + // IE can't serialize and ')"; +// } +// case "CSS" => { +// var subcode = getFile(srcDir+"/"+path); +// if (useCompression) subcode = compressCSS(subcode, false); +// "('')"; +// } +// case "JS_Q" => { +// var subcode = getFile(srcDir+"/"+path); +// subcode = subcode.replaceAll("var DEBUG=true;//\\$\\$[^\n\r]*", "var DEBUG=false;"); +// if (useCompression) subcode = compressJS(subcode, false); +// "('(\\'\n'; + out += '

    '; + } + + // additional tags can be added here (i.e., "if (tags["see"])...") + return out; +} + +/** + * Create an html output file + * @param outputdir directory to put the file + * @param htmlfile name of the file +*/ +function CreateOutputFile(outputdir,htmlfile) +{ + if (outputdir==null) + { + var outname = htmlfile; + } + else + { + var separator = Packages.java.io.File.separator; + var outname = outputdir + separator + htmlfile.substring(htmlfile.lastIndexOf(separator),htmlfile.length); + } + print("output file: " + outname); + return new File(outname); +} + +/** + * Process a javascript file. Puts the generated HTML file in the outdir + * @param filename name of the javascript file + * @inputdir input directory of the file (default null) + */ +function processJSFile(filename,inputdir) +{ + if (debug) print("filename = " + filename + " inputdir = " + inputdir); + + if (!filename.match(/\.js$/)) { + print("Expected filename to end in '.js'; had instead " + + filename + ". I don't treat the file."); + } else { + if (inputdir==null) + { + var inname = filename; + } + else + { + var separator = Packages.java.io.File.separator; + var inname = inputdir + separator + filename; + } + print("Processing file " + inname); + + var f = new File(inname); + + // create the output file + var htmlfile = filename.replace(/\.js$/, ".html"); + + var out = CreateOutputFile(outputdir,htmlfile); + + processFile(f, filename, inputdir, out); + out.close(); + } +} + +/** + * Generate index files containing links to the processed javascript files + * and the generated functions + */ +function GenerateIndex(dirname) +{ + // construct the files index file + var out = CreateOutputFile(outputdir,indexFile); + + // write the beginning of the file + out.writeLine('

    File Index - directory: ' + dirname + ''); + out.writeLine('

    File Index - directory: ' + dirname + '

    \n'); + out.writeLine(''); + out.writeLine(''); + out.writeLine(''); + out.writeLine(''); + + var separator = Packages.java.io.File.separator; + + // sort the index file array + var SortedFileArray = []; + for (var fname in indexFileArray) + SortedFileArray.push(fname); + SortedFileArray.sort(); + + for (var i=0; i < SortedFileArray.length; i++) { + var fname = SortedFileArray[i]; + var htmlfile = fname.replace(/\.js$/, ".html"); + out.writeLine('\n'); + } + out.writeLine('
    FileDescription
    ' + fname + ''); + if (indexFileArray[fname]) + out.writeLine(indexFileArray[fname]); + else + out.writeLine('No comments'); + out.writeLine('
    '); + out.close(); + + // construct the functions index file + var out = CreateOutputFile(outputdir,indexFunction); + + // write the beginning of the file + out.writeLine('
    Function Index - directory: ' + dirname + ''); + out.writeLine('

    Function Index - directory: ' + dirname + '

    \n'); + out.writeLine(''); + out.writeLine(''); + out.writeLine(''); + out.writeLine(''); + + // sort the function array + var SortedFunctionArray = []; + for (var functionname in indexFunctionArray) + SortedFunctionArray.push(functionname); + SortedFunctionArray.sort(); + + for (var j=0; j < SortedFunctionArray.length; j++) { + var funcname = SortedFunctionArray[j]; + with (indexFunctionArray[funcname]) { + var outstr = ''; + out.writeLine(outstr); + } + } + out.writeLine('
    FunctionFiles
    ' + funcname + ''; + var filelst = filename.split("|"); + for (var i in filelst) { + var htmlfile = filelst[i].replace(/\.js$/, ".html"); + outstr += '' + filelst[i] + ' '; + } + outstr += '
    '); + out.close(); +} + + +/** + * prints the options for JSDoc +*/ +function PrintOptions() +{ + print("You can use the following options:\n"); + print("-d: specify an output directory for the generated html files\n"); + print("-i: processes all files in an input directory (you can specify several directories)\n"); + quit(); +} + + +// Main Script +// first read the arguments +if (! arguments) + PrintOptions(); + +for (var i=0; i < arguments.length; i++) { + if (debug) print("argument: + \'" + arguments[i] + "\'"); + if (arguments[i].match(/^\-/)) { + if (String(arguments[i])=="-d"){ + // output directory for the generated html files + + outputdir = String(arguments[i+1]); + if (debug) print("outputdir: + \'" + outputdir + "\'"); + + i++; + } + else if (String(arguments[i])=="-i"){ + // process all files in an input directory + + DirList.push(String(arguments[i+1])); +if (debug) print("inputdir: + \'" + arguments[i+1] + "\'"); + i++; + } + else { + print("Unknown option: " + arguments[i] + "\n"); + PrintOptions(); + } + } + else + { + // we have a single file + if (debug) print("file: + \'" + arguments[i] + "\'"); + + FileList.push(String(arguments[i])); + } +} + +// first handle the single files +for (var i in FileList) + processJSFile(FileList[i],null); + +// then handle the input directories +for (var j in DirList) { + var inputdir = String(DirList[j]); + + print("Process input directory: " + inputdir); + + // clean up index arrays + var indexFileArray = []; + var indexFunctionArray = []; + + // for the directory name get rid of ../../ or ..\..\ + inputDirName = inputdir.replace(/\.\.\/|\.\.\\/g,""); + + indexFile = indexFileName + "_" + inputDirName + ".html"; + indexFunction = indexFunctionName + "_" + inputDirName + ".html"; + +print("indexFile = " + indexFile); +print("indexFunction = " + indexFunction); + + // read the files in the directory + var DirFile = new java.io.File(inputdir); + var lst = DirFile.list(); + var separator = Packages.java.io.File.separator; + + for (var i=0; i < lst.length; i++) + { + processJSFile(String(lst[i]),inputdir); + } + + // generate the index files for the input directory + GenerateIndex(inputDirName); +} + + + diff --git a/trunk/infrastructure/rhino1_7R1/examples/liveConnect.js b/trunk/infrastructure/rhino1_7R1/examples/liveConnect.js new file mode 100644 index 0000000..7befc08 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/examples/liveConnect.js @@ -0,0 +1,57 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Norris Boyd + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +/** + * liveConnect.js: a simple demonstration of JavaScript-to-Java connectivity + */ + +// Create a new StringBuffer. Note that the class name must be fully qualified +// by its package. Packages other than "java" must start with "Packages", i.e., +// "Packages.javax.servlet...". +var sb = new java.lang.StringBuffer(); + +// Now add some stuff to the buffer. +sb.append("hi, mom"); +sb.append(3); // this will add "3.0" to the buffer since all JS numbers + // are doubles by default +sb.append(true); + +// Now print it out. (The toString() method of sb is automatically called +// to convert the buffer to a string.) +// Should print "hi, mom3.0true". +print(sb); diff --git a/trunk/infrastructure/rhino1_7R1/examples/unique.js b/trunk/infrastructure/rhino1_7R1/examples/unique.js new file mode 100644 index 0000000..a4274bb --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/examples/unique.js @@ -0,0 +1,56 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Norris Boyd + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +// unique.js: read the contents of a file and print out the unique lines + +defineClass("File") + +// "arguments[0]" refers to the first argument at the command line to the +// script, if present. If not present, "arguments[0]" will be undefined, +// which will cause f to read from System.in. +var f = new File(arguments[0]); +var o = {} +var line; +while ((line = f.readLine()) != null) { + // Use JavaScript objects' inherent nature as an associative + // array to provide uniqueness + o[line] = true; +} +for (i in o) { + print(i); +} diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/allclasses-frame.html b/trunk/infrastructure/rhino1_7R1/javadoc/allclasses-frame.html new file mode 100644 index 0000000..073b251 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/allclasses-frame.html @@ -0,0 +1,89 @@ + + + + + + +All Classes (Rhino) + + + + + + + + + + + +All Classes +
    + + + + + +
    Callable +
    +ClassCache +
    +ClassCompiler +
    +ClassShutter +
    +CompilerEnvirons +
    +Context +
    +ContextAction +
    +ContextFactory +
    +ContextFactory.Listener +
    +DebuggableScript +
    +EcmaError +
    +ErrorReporter +
    +EvaluatorException +
    +Function +
    +FunctionObject +
    +GeneratedClassLoader +
    +ImporterTopLevel +
    +JavaScriptException +
    +RefCallable +
    +RhinoException +
    +Script +
    +Scriptable +
    +ScriptableInputStream +
    +ScriptableObject +
    +ScriptableOutputStream +
    +SecurityController +
    +Synchronizer +
    +WrapFactory +
    +WrappedException +
    +Wrapper +
    +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/allclasses-noframe.html b/trunk/infrastructure/rhino1_7R1/javadoc/allclasses-noframe.html new file mode 100644 index 0000000..48536b4 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/allclasses-noframe.html @@ -0,0 +1,89 @@ + + + + + + +All Classes (Rhino) + + + + + + + + + + + +All Classes +
    + + + + + +
    Callable +
    +ClassCache +
    +ClassCompiler +
    +ClassShutter +
    +CompilerEnvirons +
    +Context +
    +ContextAction +
    +ContextFactory +
    +ContextFactory.Listener +
    +DebuggableScript +
    +EcmaError +
    +ErrorReporter +
    +EvaluatorException +
    +Function +
    +FunctionObject +
    +GeneratedClassLoader +
    +ImporterTopLevel +
    +JavaScriptException +
    +RefCallable +
    +RhinoException +
    +Script +
    +Scriptable +
    +ScriptableInputStream +
    +ScriptableObject +
    +ScriptableOutputStream +
    +SecurityController +
    +Synchronizer +
    +WrapFactory +
    +WrappedException +
    +Wrapper +
    +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/constant-values.html b/trunk/infrastructure/rhino1_7R1/javadoc/constant-values.html new file mode 100644 index 0000000..b29b7ba --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/constant-values.html @@ -0,0 +1,424 @@ + + + + + + +Constant Field Values (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + +
    + +
    + + + +
    +
    +

    +Constant Field Values

    +
    +
    +Contents + + + + + + +
    +org.mozilla.*
    + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    org.mozilla.javascript.Context
    +public static final java.lang.StringerrorReporterProperty"error reporter"
    +public static final intFEATURE_DYNAMIC_SCOPE7
    +public static final intFEATURE_E4X6
    +public static final intFEATURE_ENHANCED_JAVA_ACCESS13
    +public static final intFEATURE_LOCATION_INFORMATION_IN_ERROR10
    +public static final intFEATURE_MEMBER_EXPR_AS_FUNCTION_NAME2
    +public static final intFEATURE_NON_ECMA_GET_YEAR1
    +public static final intFEATURE_PARENT_PROTO_PROPERTIES5
    +public static final intFEATURE_PARENT_PROTO_PROPRTIES5
    +public static final intFEATURE_RESERVED_KEYWORD_AS_IDENTIFIER3
    +public static final intFEATURE_STRICT_EVAL9
    +public static final intFEATURE_STRICT_MODE11
    +public static final intFEATURE_STRICT_VARS8
    +public static final intFEATURE_TO_STRING_AS_SOURCE4
    +public static final intFEATURE_WARNING_AS_ERROR12
    +public static final java.lang.StringlanguageVersionProperty"language version"
    +public static final intVERSION_1_0100
    +public static final intVERSION_1_1110
    +public static final intVERSION_1_2120
    +public static final intVERSION_1_3130
    +public static final intVERSION_1_4140
    +public static final intVERSION_1_5150
    +public static final intVERSION_1_6160
    +public static final intVERSION_1_7170
    +public static final intVERSION_DEFAULT0
    +public static final intVERSION_UNKNOWN-1
    + +

    + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    org.mozilla.javascript.FunctionObject
    +public static final intJAVA_BOOLEAN_TYPE3
    +public static final intJAVA_DOUBLE_TYPE4
    +public static final intJAVA_INT_TYPE2
    +public static final intJAVA_OBJECT_TYPE6
    +public static final intJAVA_SCRIPTABLE_TYPE5
    +public static final intJAVA_STRING_TYPE1
    +public static final intJAVA_UNSUPPORTED_TYPE0
    + +

    + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    org.mozilla.javascript.ScriptableObject
    +public static final intCONST13
    +public static final intDONTENUM2
    +public static final intEMPTY0
    +public static final intPERMANENT4
    +public static final intREADONLY1
    +public static final intUNINITIALIZED_CONST8
    + +

    + +

    +


    + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/deprecated-list.html b/trunk/infrastructure/rhino1_7R1/javadoc/deprecated-list.html new file mode 100644 index 0000000..923cc54 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/deprecated-list.html @@ -0,0 +1,386 @@ + + + + + + +Deprecated List (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + +
    + +
    + + + +
    +
    +

    +Deprecated API

    +
    +
    +Contents + + + + + + + + + +
    +Deprecated Fields
    org.mozilla.javascript.Context.FEATURE_PARENT_PROTO_PROPRTIES +
    +          In previous releases, this name was given to + FEATURE_PARENT_PROTO_PROPERTIES. 
    +  +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +Deprecated Methods
    org.mozilla.javascript.Context.addContextListener(ContextListener) +
    +            
    org.mozilla.javascript.Context.call(ContextAction) +
    +          use ContextFactory.call(ContextAction) instead as + this method relies on usage of a static singleton "global" + ContextFactory. 
    org.mozilla.javascript.Context.compileReader(Scriptable, Reader, String, int, Object) +
    +            
    org.mozilla.javascript.FunctionObject.convertArg(Context, Scriptable, Object, Class) +
    +          Use FunctionObject.getTypeTag(Class) + and FunctionObject.convertArg(Context, Scriptable, Object, int) + for type convertion. 
    org.mozilla.javascript.Context.enter() +
    +          use ContextFactory.enter() or + ContextFactory.call(ContextAction) instead as this method relies + on usage of a static singleton "global" ContextFactory. 
    org.mozilla.javascript.ContextFactory.enter() +
    +          use ContextFactory.enterContext() instead 
    org.mozilla.javascript.Context.enter(Context) +
    +          use ContextFactory.enterContext(Context) instead as + this method relies on usage of a static singleton "global" ContextFactory. 
    org.mozilla.javascript.SecurityController.execWithDomain(Context, Scriptable, Script, Object) +
    +          The application should not override this method and instead + override + SecurityController.callWithDomain(Object securityDomain, Context cx, Callable callable, Scriptable scope, Scriptable thisObj, Object[] args). 
    org.mozilla.javascript.ContextFactory.exit() +
    +          Use Context.exit() instead. 
    org.mozilla.javascript.ScriptableObject.getAttributes(int, Scriptable) +
    +          Use ScriptableObject.getAttributes(int index). The engine always + ignored the start argument. 
    org.mozilla.javascript.ScriptableObject.getAttributes(String, Scriptable) +
    +          Use ScriptableObject.getAttributes(String name). The engine always + ignored the start argument. 
    org.mozilla.javascript.EcmaError.getColumnNumber() +
    +          Use RhinoException.columnNumber() from the super class. 
    org.mozilla.javascript.EvaluatorException.getColumnNumber() +
    +          Use RhinoException.columnNumber() from the super class. 
    org.mozilla.javascript.EcmaError.getErrorObject() +
    +          Always returns null. 
    org.mozilla.javascript.EcmaError.getLineNumber() +
    +          Use RhinoException.lineNumber() from the super class. 
    org.mozilla.javascript.EvaluatorException.getLineNumber() +
    +          Use RhinoException.lineNumber() from the super class. 
    org.mozilla.javascript.JavaScriptException.getLineNumber() +
    +          Use RhinoException.lineNumber() from the super class. 
    org.mozilla.javascript.EcmaError.getLineSource() +
    +          Use RhinoException.lineSource() from the super class. 
    org.mozilla.javascript.EvaluatorException.getLineSource() +
    +          Use RhinoException.lineSource() from the super class. 
    org.mozilla.javascript.EcmaError.getSourceName() +
    +          Use RhinoException.sourceName() from the super class. 
    org.mozilla.javascript.EvaluatorException.getSourceName() +
    +          Use RhinoException.sourceName() from the super class. 
    org.mozilla.javascript.JavaScriptException.getSourceName() +
    +          Use RhinoException.sourceName() from the super class. 
    org.mozilla.javascript.Context.hasCompileFunctionsWithDynamicScope() +
    +            
    org.mozilla.javascript.ImporterTopLevel.importPackage(Context, Scriptable, Object[], Function) +
    +          Kept only for compatibility. 
    org.mozilla.javascript.ClassCache.isInvokerOptimizationEnabled() +
    +          The method always returns false. 
    org.mozilla.javascript.Context.removeContextListener(ContextListener) +
    +            
    org.mozilla.javascript.ScriptableObject.setAttributes(int, Scriptable, int) +
    +          Use ScriptableObject.setAttributes(int index, int attributes). + The engine always ignored the start argument. 
    org.mozilla.javascript.ScriptableObject.setAttributes(String, Scriptable, int) +
    +          Use ScriptableObject.setAttributes(String name, int attributes). + The engine always ignored the start argument. 
    org.mozilla.javascript.Context.setCachingEnabled(boolean) +
    +            
    org.mozilla.javascript.Context.setCompileFunctionsWithDynamicScope(boolean) +
    +            
    org.mozilla.javascript.ClassCache.setInvokerOptimizationEnabled(boolean) +
    +          The method does nothing. + Invoker optimization is no longer used by Rhino. + On modern JDK like 1.4 or 1.5 the disadvantages of the optimization + like increased memory usage or longer initialization time overweight + small speed increase that can be gained using generated proxy class + to replace reflection. 
    org.mozilla.javascript.Context.toObject(Object, Scriptable, Class) +
    +            
    org.mozilla.javascript.Context.toType(Object, Class) +
    +            
    org.mozilla.javascript.WrappedException.unwrap() +
    +          Use WrappedException.getWrappedException() instead. 
    +  +

    + + + + + + + + + + + + + + +
    +Deprecated Constructors
    org.mozilla.javascript.Context() +
    +          use ContextFactory.enter() or + ContextFactory.call(ContextAction) instead. 
    org.mozilla.javascript.EcmaError(Scriptable, String, int, int, String) +
    +          EcmaError error instances should not be constructed + explicitly since they are generated by the engine. 
    org.mozilla.javascript.JavaScriptException(Object) +
    +          Use WrappedException.WrappedException(Throwable) to report + exceptions in Java code. 
    +  +

    +


    + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/help-doc.html b/trunk/infrastructure/rhino1_7R1/javadoc/help-doc.html new file mode 100644 index 0000000..7a2e353 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/help-doc.html @@ -0,0 +1,217 @@ + + + + + + +API Help (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + +
    + +
    + + + +
    +
    +

    +How This API Document Is Organized

    +
    +This API (Application Programming Interface) document has pages corresponding to the items in the navigation bar, described as follows.

    +Overview

    +
    + +

    +The Overview page is the front page of this API document and provides a list of all packages with a summary for each. This page can also contain an overall description of the set of packages.

    +

    +Package

    +
    + +

    +Each package has a page that contains a list of its classes and interfaces, with a summary for each. This page can contain four categories:

      +
    • Interfaces (italic)
    • Classes
    • Enums
    • Exceptions
    • Errors
    • Annotation Types
    +
    +

    +Class/Interface

    +
    + +

    +Each class, interface, nested class and nested interface has its own separate page. Each of these pages has three sections consisting of a class/interface description, summary tables, and detailed member descriptions:

      +
    • Class inheritance diagram
    • Direct Subclasses
    • All Known Subinterfaces
    • All Known Implementing Classes
    • Class/interface declaration
    • Class/interface description +

      +

    • Nested Class Summary
    • Field Summary
    • Constructor Summary
    • Method Summary +

      +

    • Field Detail
    • Constructor Detail
    • Method Detail
    +Each summary entry contains the first sentence from the detailed description for that item. The summary entries are alphabetical, while the detailed descriptions are in the order they appear in the source code. This preserves the logical groupings established by the programmer.
    + +

    +Annotation Type

    +
    + +

    +Each annotation type has its own separate page with the following sections:

      +
    • Annotation Type declaration
    • Annotation Type description
    • Required Element Summary
    • Optional Element Summary
    • Element Detail
    +
    + +

    +Enum

    +
    + +

    +Each enum has its own separate page with the following sections:

      +
    • Enum declaration
    • Enum description
    • Enum Constant Summary
    • Enum Constant Detail
    +
    +

    +Tree (Class Hierarchy)

    +
    +There is a Class Hierarchy page for all packages, plus a hierarchy for each package. Each hierarchy page contains a list of classes and a list of interfaces. The classes are organized by inheritance structure starting with java.lang.Object. The interfaces do not inherit from java.lang.Object.
      +
    • When viewing the Overview page, clicking on "Tree" displays the hierarchy for all packages.
    • When viewing a particular package, class or interface page, clicking "Tree" displays the hierarchy for only that package.
    +
    +

    +Deprecated API

    +
    +The Deprecated API page lists all of the API that have been deprecated. A deprecated API is not recommended for use, generally due to improvements, and a replacement API is usually given. Deprecated APIs may be removed in future implementations.
    +

    +Index

    +
    +The Index contains an alphabetic list of all classes, interfaces, constructors, methods, and fields.
    +

    +Prev/Next

    +These links take you to the next or previous class, interface, package, or related page.

    +Frames/No Frames

    +These links show and hide the HTML frames. All pages are available with or without frames. +

    +

    +Serialized Form

    +Each serializable or externalizable class has a description of its serialization fields and methods. This information is of interest to re-implementors, not to developers using the API. While there is no link in the navigation bar, you can get to this information by going to any serialized class and clicking "Serialized Form" in the "See also" section of the class description. +

    +

    +Constant Field Values

    +The Constant Field Values page lists the static final fields and their values. +

    + + +This help file applies to API documentation generated using the standard doclet. + +
    +


    + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/index-all.html b/trunk/infrastructure/rhino1_7R1/javadoc/index-all.html new file mode 100644 index 0000000..15ba433 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/index-all.html @@ -0,0 +1,1566 @@ + + + + + + +Index (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + +
    + +
    + + + +A C D E F G H I J L M N O P R S T U V W
    +

    +A

    +
    +
    addActivationName(String) - +Method in class org.mozilla.javascript.Context +
    Add a name to the list of names forcing the creation of real + activation objects for functions. +
    addAsConstructor(Scriptable, Scriptable) - +Method in class org.mozilla.javascript.FunctionObject +
    Define this function as a JavaScript constructor. +
    addContextListener(ContextListener) - +Static method in class org.mozilla.javascript.Context +
    Deprecated.   +
    addExcludedName(String) - +Method in class org.mozilla.javascript.serialize.ScriptableOutputStream +
    Adds a qualified name to the list of object to be excluded from + serialization. +
    addListener(ContextFactory.Listener) - +Method in class org.mozilla.javascript.ContextFactory +
      +
    addOptionalExcludedName(String) - +Method in class org.mozilla.javascript.serialize.ScriptableOutputStream +
    Adds a qualified name to the list of object to be excluded from + serialization. +
    addPropertyChangeListener(PropertyChangeListener) - +Method in class org.mozilla.javascript.Context +
    Register an object to receive notifications when a bound property + has changed +
    associate(ScriptableObject) - +Method in class org.mozilla.javascript.ClassCache +
    Associate ClassCache object with the given top-level scope. +
    associateValue(Object, Object) - +Method in class org.mozilla.javascript.ScriptableObject +
    Associate arbitrary application-specific value with this object. +
    avoidObjectDetection() - +Method in class org.mozilla.javascript.ScriptableObject +
    Emulate the SpiderMonkey (and Firefox) feature of allowing + custom objects to avoid detection by normal "object detection" + code patterns. +
    +
    +

    +C

    +
    +
    call(Context, Scriptable, Scriptable, Object[]) - +Method in interface org.mozilla.javascript.Callable +
    Perform the call. +
    call(ContextAction) - +Static method in class org.mozilla.javascript.Context +
    Deprecated. use ContextFactory.call(ContextAction) instead as + this method relies on usage of a static singleton "global" + ContextFactory. +
    call(ContextFactory, Callable, Scriptable, Scriptable, Object[]) - +Static method in class org.mozilla.javascript.Context +
    Call Callable.call(Context cx, Scriptable scope, Scriptable thisObj, + Object[] args) + using the Context instance associated with the current thread. +
    call(ContextAction) - +Method in class org.mozilla.javascript.ContextFactory +
    Call ContextAction.run(Context cx) + using the Context instance associated with the current thread. +
    call(Context, Scriptable, Scriptable, Object[]) - +Method in interface org.mozilla.javascript.Function +
    Call the function. +
    call(Context, Scriptable, Scriptable, Object[]) - +Method in class org.mozilla.javascript.FunctionObject +
    Performs conversions on argument types if needed and + invokes the underlying Java method or constructor. +
    call(Context, Scriptable, Scriptable, Object[]) - +Method in class org.mozilla.javascript.Synchronizer +
      +
    Callable - Interface in org.mozilla.javascript
    Generic notion of callable object that can execute some script-related code + upon request with specified values for script scope and this objects.
    callMethod(Scriptable, String, Object[]) - +Static method in class org.mozilla.javascript.ScriptableObject +
    Call a method of an object. +
    callMethod(Context, Scriptable, String, Object[]) - +Static method in class org.mozilla.javascript.ScriptableObject +
    Call a method of an object. +
    callWithDomain(Object, Context, Callable, Scriptable, Scriptable, Object[]) - +Method in class org.mozilla.javascript.SecurityController +
    Call Callable.call(Context cx, Scriptable scope, Scriptable thisObj, + Object[] args) + of callable under restricted security domain where an action is + allowed only if it is allowed according to the Java stack on the + moment of the execWithDomain call and securityDomain. +
    checkLanguageVersion(int) - +Static method in class org.mozilla.javascript.Context +
      +
    checkNotSealed() - +Method in class org.mozilla.javascript.ContextFactory +
      +
    checkOptimizationLevel(int) - +Static method in class org.mozilla.javascript.Context +
      +
    ClassCache - Class in org.mozilla.javascript
    Cache of generated classes and data structures to access Java runtime + from JavaScript.
    ClassCache() - +Constructor for class org.mozilla.javascript.ClassCache +
      +
    ClassCompiler - Class in org.mozilla.javascript.optimizer
    Generates class files from script sources.
    ClassCompiler(CompilerEnvirons) - +Constructor for class org.mozilla.javascript.optimizer.ClassCompiler +
    Construct ClassCompiler that uses the specified compiler environment + when generating classes. +
    ClassShutter - Interface in org.mozilla.javascript
    Embeddings that wish to filter Java classes that are visible to scripts +through the LiveConnect, should implement this interface.
    clearCaches() - +Method in class org.mozilla.javascript.ClassCache +
    Empty caches of generated Java classes and Java reflection information. +
    columnNumber() - +Method in exception org.mozilla.javascript.RhinoException +
    The column number of the location of the error, or zero if unknown. +
    compileFunction(Scriptable, String, String, int, Object) - +Method in class org.mozilla.javascript.Context +
    Compile a JavaScript function. +
    compileReader(Scriptable, Reader, String, int, Object) - +Method in class org.mozilla.javascript.Context +
    Deprecated.   +
    compileReader(Reader, String, int, Object) - +Method in class org.mozilla.javascript.Context +
    Compiles the source in the given reader. +
    CompilerEnvirons - Class in org.mozilla.javascript
     
    CompilerEnvirons() - +Constructor for class org.mozilla.javascript.CompilerEnvirons +
      +
    compileString(String, String, int, Object) - +Method in class org.mozilla.javascript.Context +
    Compiles the source in the given string. +
    compileToClassFiles(String, String, int, String) - +Method in class org.mozilla.javascript.optimizer.ClassCompiler +
    Compile JavaScript source into one or more Java class files. +
    CONST - +Static variable in class org.mozilla.javascript.ScriptableObject +
      +
    construct(Context, Scriptable, Object[]) - +Method in interface org.mozilla.javascript.Function +
    Call the function as a constructor. +
    Context - Class in org.mozilla.javascript
    This class represents the runtime context of an executing script.
    Context() - +Constructor for class org.mozilla.javascript.Context +
    Deprecated. use ContextFactory.enter() or + ContextFactory.call(ContextAction) instead. +
    ContextAction - Interface in org.mozilla.javascript
    Interface to represent arbitrary action that requires to have Context + object associated with the current thread for its execution.
    contextCreated(Context) - +Method in interface org.mozilla.javascript.ContextFactory.Listener +
    Notify about newly created Context object. +
    ContextFactory - Class in org.mozilla.javascript
    Factory class that Rhino runtime uses to create new Context + instances.
    ContextFactory() - +Constructor for class org.mozilla.javascript.ContextFactory +
      +
    ContextFactory.Listener - Interface in org.mozilla.javascript
    Listener of Context creation and release events.
    contextReleased(Context) - +Method in interface org.mozilla.javascript.ContextFactory.Listener +
    Notify that the specified Context instance is no longer + associated with the current thread. +
    convertArg(Context, Scriptable, Object, int) - +Static method in class org.mozilla.javascript.FunctionObject +
      +
    convertArg(Context, Scriptable, Object, Class) - +Static method in class org.mozilla.javascript.FunctionObject +
    Deprecated. Use FunctionObject.getTypeTag(Class) + and FunctionObject.convertArg(Context, Scriptable, Object, int) + for type convertion. +
    createClassLoader(ClassLoader) - +Method in class org.mozilla.javascript.Context +
    Create class loader for generated classes. +
    createClassLoader(ClassLoader) - +Method in class org.mozilla.javascript.ContextFactory +
    Create class loader for generated classes. +
    createClassLoader(ClassLoader, Object) - +Method in class org.mozilla.javascript.SecurityController +
    Get class loader-like object that can be used + to define classes with the given security context. +
    createLoader(ClassLoader, Object) - +Static method in class org.mozilla.javascript.SecurityController +
    Create GeneratedClassLoader with restrictions imposed by + staticDomain and all current stack frames. +
    createObject(Context, Scriptable) - +Method in class org.mozilla.javascript.FunctionObject +
    Return new Scriptable instance using the default + constructor for the class of the underlying Java method. +
    +
    +

    +D

    +
    +
    DebuggableScript - Interface in org.mozilla.javascript.debug
    This interface exposes debugging information from executable + code (either functions or top-level scripts).
    decompileFunction(Function, int) - +Method in class org.mozilla.javascript.Context +
    Decompile a JavaScript Function. +
    decompileFunctionBody(Function, int) - +Method in class org.mozilla.javascript.Context +
    Decompile the body of a JavaScript Function. +
    decompileScript(Script, int) - +Method in class org.mozilla.javascript.Context +
    Decompile the script. +
    defineClass(String, byte[]) - +Method in interface org.mozilla.javascript.GeneratedClassLoader +
    Define a new Java class. +
    defineClass(Scriptable, Class) - +Static method in class org.mozilla.javascript.ScriptableObject +
    Defines JavaScript objects from a Java class that implements Scriptable. +
    defineClass(Scriptable, Class, boolean) - +Static method in class org.mozilla.javascript.ScriptableObject +
    Defines JavaScript objects from a Java class, optionally + allowing sealing. +
    defineClass(Scriptable, Class, boolean, boolean) - +Static method in class org.mozilla.javascript.ScriptableObject +
    Defines JavaScript objects from a Java class, optionally + allowing sealing and mapping of Java inheritance to JavaScript + prototype-based inheritance. +
    defineConst(String, Scriptable) - +Method in class org.mozilla.javascript.ScriptableObject +
      +
    defineConstProperty(Scriptable, String) - +Static method in class org.mozilla.javascript.ScriptableObject +
    Utility method to add properties to arbitrary Scriptable object. +
    defineFunctionProperties(String[], Class, int) - +Method in class org.mozilla.javascript.ScriptableObject +
    Search for names in a class, adding the resulting methods + as properties. +
    defineProperty(String, Object, int) - +Method in class org.mozilla.javascript.ScriptableObject +
    Define a JavaScript property. +
    defineProperty(Scriptable, String, Object, int) - +Static method in class org.mozilla.javascript.ScriptableObject +
    Utility method to add properties to arbitrary Scriptable object. +
    defineProperty(String, Class, int) - +Method in class org.mozilla.javascript.ScriptableObject +
    Define a JavaScript property with getter and setter side effects. +
    defineProperty(String, Object, Method, Method, int) - +Method in class org.mozilla.javascript.ScriptableObject +
    Define a JavaScript property. +
    delete(String) - +Method in interface org.mozilla.javascript.Scriptable +
    Removes a property from this object. +
    delete(int) - +Method in interface org.mozilla.javascript.Scriptable +
    Removes a property from this object. +
    delete(String) - +Method in class org.mozilla.javascript.ScriptableObject +
    Removes a named property from the object. +
    delete(int) - +Method in class org.mozilla.javascript.ScriptableObject +
    Removes the indexed property from the object. +
    deleteProperty(Scriptable, String) - +Static method in class org.mozilla.javascript.ScriptableObject +
    Removes the property from an object or its prototype chain. +
    deleteProperty(Scriptable, int) - +Static method in class org.mozilla.javascript.ScriptableObject +
    Removes the property from an object or its prototype chain. +
    details() - +Method in exception org.mozilla.javascript.EcmaError +
      +
    details() - +Method in exception org.mozilla.javascript.JavaScriptException +
      +
    details() - +Method in exception org.mozilla.javascript.RhinoException +
      +
    DONTENUM - +Static variable in class org.mozilla.javascript.ScriptableObject +
    Property attribute indicating property is not enumerated. +
    doTopCall(Callable, Context, Scriptable, Scriptable, Object[]) - +Method in class org.mozilla.javascript.ContextFactory +
    Execute top call to script or function. +
    +
    +

    +E

    +
    +
    EcmaError - Exception in org.mozilla.javascript
    The class of exceptions raised by the engine as described in + ECMA edition 3.
    EcmaError(Scriptable, String, int, int, String) - +Constructor for exception org.mozilla.javascript.EcmaError +
    Deprecated. EcmaError error instances should not be constructed + explicitly since they are generated by the engine. +
    EMPTY - +Static variable in class org.mozilla.javascript.ScriptableObject +
    The empty property attribute. +
    emptyArgs - +Static variable in class org.mozilla.javascript.Context +
    Convenient value to use as zero-length array of objects. +
    enter() - +Static method in class org.mozilla.javascript.Context +
    Deprecated. use ContextFactory.enter() or + ContextFactory.call(ContextAction) instead as this method relies + on usage of a static singleton "global" ContextFactory. +
    enter(Context) - +Static method in class org.mozilla.javascript.Context +
    Deprecated. use ContextFactory.enterContext(Context) instead as + this method relies on usage of a static singleton "global" ContextFactory. +
    enter() - +Method in class org.mozilla.javascript.ContextFactory +
    Deprecated. use ContextFactory.enterContext() instead +
    enterContext() - +Method in class org.mozilla.javascript.ContextFactory +
    Get a context associated with the current thread, creating one if need + be. +
    enterContext(Context) - +Method in class org.mozilla.javascript.ContextFactory +
    Get a Context associated with the current thread, using the given + Context if need be. +
    equivalentValues(Object) - +Method in class org.mozilla.javascript.ScriptableObject +
    Custom == operator. +
    error(String, String, int, String, int) - +Method in interface org.mozilla.javascript.ErrorReporter +
    Report an error. +
    ErrorReporter - Interface in org.mozilla.javascript
    This is interface defines a protocol for the reporting of + errors during JavaScript translation or execution.
    errorReporterProperty - +Static variable in class org.mozilla.javascript.Context +
      +
    evaluateReader(Scriptable, Reader, String, int, Object) - +Method in class org.mozilla.javascript.Context +
    Evaluate a reader as JavaScript source. +
    evaluateString(Scriptable, String, String, int, Object) - +Method in class org.mozilla.javascript.Context +
    Evaluate a JavaScript source string. +
    EvaluatorException - Exception in org.mozilla.javascript
    The class of exceptions thrown by the JavaScript engine.
    EvaluatorException(String) - +Constructor for exception org.mozilla.javascript.EvaluatorException +
      +
    EvaluatorException(String, String, int) - +Constructor for exception org.mozilla.javascript.EvaluatorException +
    Create an exception with the specified detail message. +
    EvaluatorException(String, String, int, String, int) - +Constructor for exception org.mozilla.javascript.EvaluatorException +
    Create an exception with the specified detail message. +
    excludeStandardObjectNames() - +Method in class org.mozilla.javascript.serialize.ScriptableOutputStream +
    Adds the names of the standard objects and their + prototypes to the list of excluded names. +
    exec(Context, Scriptable) - +Method in interface org.mozilla.javascript.Script +
    Execute the script. +
    execIdCall(IdFunctionObject, Context, Scriptable, Scriptable, Object[]) - +Method in class org.mozilla.javascript.ImporterTopLevel +
      +
    execWithDomain(Context, Scriptable, Script, Object) - +Method in class org.mozilla.javascript.SecurityController +
    Deprecated. The application should not override this method and instead + override + SecurityController.callWithDomain(Object securityDomain, Context cx, Callable callable, Scriptable scope, Scriptable thisObj, Object[] args). +
    exit() - +Static method in class org.mozilla.javascript.Context +
    Exit a block of code requiring a Context. +
    exit() - +Method in class org.mozilla.javascript.ContextFactory +
    Deprecated. Use Context.exit() instead. +
    +
    +

    +F

    +
    +
    FEATURE_DYNAMIC_SCOPE - +Static variable in class org.mozilla.javascript.Context +
    Control if dynamic scope should be used for name access. +
    FEATURE_E4X - +Static variable in class org.mozilla.javascript.Context +
    Control if support for E4X(ECMAScript for XML) extension is available. +
    FEATURE_ENHANCED_JAVA_ACCESS - +Static variable in class org.mozilla.javascript.Context +
    Enables enhanced access to Java. +
    FEATURE_LOCATION_INFORMATION_IN_ERROR - +Static variable in class org.mozilla.javascript.Context +
    When the feature is on Rhino will add a "fileName" and "lineNumber" + properties to Error objects automatically. +
    FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME - +Static variable in class org.mozilla.javascript.Context +
    Control if member expression as function name extension is available. +
    FEATURE_NON_ECMA_GET_YEAR - +Static variable in class org.mozilla.javascript.Context +
    Controls behaviour of Date.prototype.getYear(). +
    FEATURE_PARENT_PROTO_PROPERTIES - +Static variable in class org.mozilla.javascript.Context +
    Control if properties __proto__ and __parent__ + are treated specially. +
    FEATURE_PARENT_PROTO_PROPRTIES - +Static variable in class org.mozilla.javascript.Context +
    Deprecated. In previous releases, this name was given to + FEATURE_PARENT_PROTO_PROPERTIES. +
    FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER - +Static variable in class org.mozilla.javascript.Context +
    Control if reserved keywords are treated as identifiers. +
    FEATURE_STRICT_EVAL - +Static variable in class org.mozilla.javascript.Context +
    Control if strict eval mode is enabled. +
    FEATURE_STRICT_MODE - +Static variable in class org.mozilla.javascript.Context +
    Controls whether JS 1.5 'strict mode' is enabled. +
    FEATURE_STRICT_VARS - +Static variable in class org.mozilla.javascript.Context +
    Control if strict variable mode is enabled. +
    FEATURE_TO_STRING_AS_SOURCE - +Static variable in class org.mozilla.javascript.Context +
    Control if toString() should returns the same result + as toSource() when applied to objects and arrays. +
    FEATURE_WARNING_AS_ERROR - +Static variable in class org.mozilla.javascript.Context +
    Controls whether a warning should be treated as an error. +
    findPrototypeId(String) - +Method in class org.mozilla.javascript.ImporterTopLevel +
      +
    Function - Interface in org.mozilla.javascript
    This is interface that all functions in JavaScript must implement.
    FunctionObject - Class in org.mozilla.javascript
     
    FunctionObject(String, Member, Scriptable) - +Constructor for class org.mozilla.javascript.FunctionObject +
    Create a JavaScript function object from a Java method. +
    +
    +

    +G

    +
    +
    GeneratedClassLoader - Interface in org.mozilla.javascript
    Interface to define classes from generated byte code.
    generateObserverCount - +Variable in class org.mozilla.javascript.Context +
      +
    get(Scriptable) - +Static method in class org.mozilla.javascript.ClassCache +
    Search for ClassCache object in the given scope. +
    get(String, Scriptable) - +Method in class org.mozilla.javascript.ImporterTopLevel +
      +
    get(String, Scriptable) - +Method in interface org.mozilla.javascript.Scriptable +
    Get a named property from the object. +
    get(int, Scriptable) - +Method in interface org.mozilla.javascript.Scriptable +
    Get a property from the object selected by an integral index. +
    get(String, Scriptable) - +Method in class org.mozilla.javascript.ScriptableObject +
    Returns the value of the named property or NOT_FOUND. +
    get(int, Scriptable) - +Method in class org.mozilla.javascript.ScriptableObject +
    Returns the value of the indexed property or NOT_FOUND. +
    getAllIds() - +Method in class org.mozilla.javascript.ScriptableObject +
    Returns an array of ids for the properties of the object. +
    getApplicationClassLoader() - +Method in class org.mozilla.javascript.Context +
      +
    getApplicationClassLoader() - +Method in class org.mozilla.javascript.ContextFactory +
    Get ClassLoader to use when searching for Java classes. +
    getArity() - +Method in class org.mozilla.javascript.FunctionObject +
    Return the value defined by the method used to construct the object + (number of parameters of the method, or 1 if the method is a "varargs" + form). +
    getAssociatedValue(Object) - +Method in class org.mozilla.javascript.ScriptableObject +
    Get arbitrary application-specific value associated with this object. +
    getAttributes(String, Scriptable) - +Method in class org.mozilla.javascript.ScriptableObject +
    Deprecated. Use ScriptableObject.getAttributes(String name). The engine always + ignored the start argument. +
    getAttributes(int, Scriptable) - +Method in class org.mozilla.javascript.ScriptableObject +
    Deprecated. Use ScriptableObject.getAttributes(int index). The engine always + ignored the start argument. +
    getAttributes(String) - +Method in class org.mozilla.javascript.ScriptableObject +
    Get the attributes of a named property. +
    getAttributes(int) - +Method in class org.mozilla.javascript.ScriptableObject +
    Get the attributes of an indexed property. +
    getClassName() - +Method in class org.mozilla.javascript.ImporterTopLevel +
      +
    getClassName() - +Method in interface org.mozilla.javascript.Scriptable +
    Get the name of the set of objects implemented by this Java class. +
    getClassName() - +Method in class org.mozilla.javascript.ScriptableObject +
    Return the name of the class. +
    getClassPrototype(Scriptable, String) - +Static method in class org.mozilla.javascript.ScriptableObject +
    Get the prototype for the named class. +
    getColumnNumber() - +Method in exception org.mozilla.javascript.EcmaError +
    Deprecated. Use RhinoException.columnNumber() from the super class. +
    getColumnNumber() - +Method in exception org.mozilla.javascript.EvaluatorException +
    Deprecated. Use RhinoException.columnNumber() from the super class. +
    getCompilerEnv() - +Method in class org.mozilla.javascript.optimizer.ClassCompiler +
    Get the compiler environment the compiler uses. +
    getCurrentContext() - +Static method in class org.mozilla.javascript.Context +
    Get the current Context. +
    getDebuggableView(Script) - +Static method in class org.mozilla.javascript.Context +
    Return DebuggableScript instance if any associated with the script. +
    getDebugger() - +Method in class org.mozilla.javascript.Context +
    Return the current debugger. +
    getDebuggerContextData() - +Method in class org.mozilla.javascript.Context +
    Return the debugger context data associated with current context. +
    getDefaultValue(Class) - +Method in interface org.mozilla.javascript.Scriptable +
    Get the default value of the object with a given hint. +
    getDefaultValue(Class) - +Method in class org.mozilla.javascript.ScriptableObject +
    Implements the [[DefaultValue]] internal method. +
    getDefaultValue(Scriptable, Class) - +Static method in class org.mozilla.javascript.ScriptableObject +
      +
    getDynamicSecurityDomain(Object) - +Method in class org.mozilla.javascript.SecurityController +
    Get dynamic security domain that allows an action only if it is allowed + by the current Java stack and securityDomain. +
    getE4xImplementationFactory() - +Method in class org.mozilla.javascript.Context +
    Returns an object which specifies an E4X implementation to use within + this Context. +
    getE4xImplementationFactory() - +Method in class org.mozilla.javascript.ContextFactory +
    Provides a default + XMLLib.Factory + to be used by the Context instances produced by this + factory. +
    getElements(Scriptable) - +Method in class org.mozilla.javascript.Context +
    Get the elements of a JavaScript array. +
    getErrorMessage() - +Method in exception org.mozilla.javascript.EcmaError +
    Gets the message corresponding to the error. +
    getErrorObject() - +Method in exception org.mozilla.javascript.EcmaError +
    Deprecated. Always returns null. +
    getErrorReporter() - +Method in class org.mozilla.javascript.CompilerEnvirons +
      +
    getErrorReporter() - +Method in class org.mozilla.javascript.Context +
    Get the current error reporter. +
    getFactory() - +Method in class org.mozilla.javascript.Context +
    Return ContextFactory instance used to create this Context. +
    getFunction(int) - +Method in interface org.mozilla.javascript.debug.DebuggableScript +
      +
    getFunctionCount() - +Method in interface org.mozilla.javascript.debug.DebuggableScript +
      +
    getFunctionName() - +Method in interface org.mozilla.javascript.debug.DebuggableScript +
    Get name of the function described by this script. +
    getFunctionName() - +Method in class org.mozilla.javascript.FunctionObject +
      +
    getFunctionPrototype(Scriptable) - +Static method in class org.mozilla.javascript.ScriptableObject +
    Get the Function.prototype property. +
    getGetterOrSetter(String, int, boolean) - +Method in class org.mozilla.javascript.ScriptableObject +
    Get the getter or setter for a given property. +
    getGlobal() - +Static method in class org.mozilla.javascript.ContextFactory +
    Get global ContextFactory. +
    getIds() - +Method in interface org.mozilla.javascript.Scriptable +
    Get an array of property ids. +
    getIds() - +Method in class org.mozilla.javascript.ScriptableObject +
    Returns an array of ids for the properties of the object. +
    getImplementationVersion() - +Method in class org.mozilla.javascript.Context +
    Get the implementation version. +
    getInstructionObserverThreshold() - +Method in class org.mozilla.javascript.Context +
    Get threshold of executed instructions counter that triggers call to + observeInstructionCount(). +
    getLanguageVersion() - +Method in class org.mozilla.javascript.CompilerEnvirons +
      +
    getLanguageVersion() - +Method in class org.mozilla.javascript.Context +
    Get the current language version. +
    getLength() - +Method in class org.mozilla.javascript.FunctionObject +
    Return the same value as FunctionObject.getArity(). +
    getLineNumber() - +Method in exception org.mozilla.javascript.EcmaError +
    Deprecated. Use RhinoException.lineNumber() from the super class. +
    getLineNumber() - +Method in exception org.mozilla.javascript.EvaluatorException +
    Deprecated. Use RhinoException.lineNumber() from the super class. +
    getLineNumber() - +Method in exception org.mozilla.javascript.JavaScriptException +
    Deprecated. Use RhinoException.lineNumber() from the super class. +
    getLineNumbers() - +Method in interface org.mozilla.javascript.debug.DebuggableScript +
    Get array containing the line numbers that + that can be passed to DebugFrame.onLineChange(). +
    getLineSource() - +Method in exception org.mozilla.javascript.EcmaError +
    Deprecated. Use RhinoException.lineSource() from the super class. +
    getLineSource() - +Method in exception org.mozilla.javascript.EvaluatorException +
    Deprecated. Use RhinoException.lineSource() from the super class. +
    getLocale() - +Method in class org.mozilla.javascript.Context +
    Get the current locale. +
    getMainMethodClass() - +Method in class org.mozilla.javascript.optimizer.ClassCompiler +
    Get the name of the class for main method implementation. +
    getMaximumInterpreterStackDepth() - +Method in class org.mozilla.javascript.Context +
    Returns the maximum stack depth (in terms of number of call frames) + allowed in a single invocation of interpreter. +
    getMessage() - +Method in exception org.mozilla.javascript.RhinoException +
      +
    getMethodOrConstructor() - +Method in class org.mozilla.javascript.FunctionObject +
    Get Java method or constructor this function represent. +
    getName() - +Method in exception org.mozilla.javascript.EcmaError +
    Gets the name of the error. +
    getObjectPrototype(Scriptable) - +Static method in class org.mozilla.javascript.ScriptableObject +
    Get the Object.prototype property. +
    getOptimizationLevel() - +Method in class org.mozilla.javascript.CompilerEnvirons +
      +
    getOptimizationLevel() - +Method in class org.mozilla.javascript.Context +
    Get the current optimization level. +
    getParamAndVarCount() - +Method in interface org.mozilla.javascript.debug.DebuggableScript +
    Get number of declared parameters and local variables. +
    getParamCount() - +Method in interface org.mozilla.javascript.debug.DebuggableScript +
    Get number of declared parameters in the function. +
    getParamOrVarName(int) - +Method in interface org.mozilla.javascript.debug.DebuggableScript +
    Get name of a declared parameter or local variable. +
    getParent() - +Method in interface org.mozilla.javascript.debug.DebuggableScript +
      +
    getParentScope() - +Method in interface org.mozilla.javascript.Scriptable +
    Get the parent scope of the object. +
    getParentScope() - +Method in class org.mozilla.javascript.ScriptableObject +
    Returns the parent (enclosing) scope of the object. +
    getProperty(Scriptable, String) - +Static method in class org.mozilla.javascript.ScriptableObject +
    Gets a named property from an object or any object in its prototype chain. +
    getProperty(Scriptable, int) - +Static method in class org.mozilla.javascript.ScriptableObject +
    Gets an indexed property from an object or any object in its prototype chain. +
    getPropertyIds(Scriptable) - +Static method in class org.mozilla.javascript.ScriptableObject +
    Returns an array of all ids from an object and its prototypes. +
    getPrototype() - +Method in interface org.mozilla.javascript.Scriptable +
    Get the prototype of the object. +
    getPrototype() - +Method in class org.mozilla.javascript.ScriptableObject +
    Returns the prototype of the object. +
    getScriptStackTrace() - +Method in exception org.mozilla.javascript.RhinoException +
    Get a string representing the script stack of this exception. +
    getScriptStackTrace(FilenameFilter) - +Method in exception org.mozilla.javascript.RhinoException +
    Get a string representing the script stack of this exception. +
    getSourceName() - +Method in interface org.mozilla.javascript.debug.DebuggableScript +
    Get the name of the source (usually filename or URL) + of the script. +
    getSourceName() - +Method in exception org.mozilla.javascript.EcmaError +
    Deprecated. Use RhinoException.sourceName() from the super class. +
    getSourceName() - +Method in exception org.mozilla.javascript.EvaluatorException +
    Deprecated. Use RhinoException.sourceName() from the super class. +
    getSourceName() - +Method in exception org.mozilla.javascript.JavaScriptException +
    Deprecated. Use RhinoException.sourceName() from the super class. +
    getStaticSecurityDomainClass() - +Static method in class org.mozilla.javascript.SecurityController +
      +
    getStaticSecurityDomainClassInternal() - +Method in class org.mozilla.javascript.SecurityController +
      +
    getTargetExtends() - +Method in class org.mozilla.javascript.optimizer.ClassCompiler +
    Get the class that the generated target will extend. +
    getTargetImplements() - +Method in class org.mozilla.javascript.optimizer.ClassCompiler +
    Get the interfaces that the generated target will implement. +
    getThreadLocal(Object) - +Method in class org.mozilla.javascript.Context +
    Get a value corresponding to a key. +
    getTopLevelScope(Scriptable) - +Static method in class org.mozilla.javascript.ScriptableObject +
    Get the global scope. +
    getTopScopeValue(Scriptable, Object) - +Static method in class org.mozilla.javascript.ScriptableObject +
    Get arbitrary application-specific value associated with the top scope + of the given scope. +
    getTypeTag(Class) - +Static method in class org.mozilla.javascript.FunctionObject +
      +
    getUndefinedValue() - +Static method in class org.mozilla.javascript.Context +
    Get the singleton object that represents the JavaScript Undefined value. +
    getValue() - +Method in exception org.mozilla.javascript.JavaScriptException +
      +
    getWrapFactory() - +Method in class org.mozilla.javascript.Context +
    Return the current WrapFactory, or null if none is defined. +
    getWrappedException() - +Method in exception org.mozilla.javascript.WrappedException +
    Get the wrapped exception. +
    +
    +

    +H

    +
    +
    has(String, Scriptable) - +Method in class org.mozilla.javascript.ImporterTopLevel +
      +
    has(String, Scriptable) - +Method in interface org.mozilla.javascript.Scriptable +
    Indicates whether or not a named property is defined in an object. +
    has(int, Scriptable) - +Method in interface org.mozilla.javascript.Scriptable +
    Indicates whether or not an indexed property is defined in an object. +
    has(String, Scriptable) - +Method in class org.mozilla.javascript.ScriptableObject +
    Returns true if the named property is defined. +
    has(int, Scriptable) - +Method in class org.mozilla.javascript.ScriptableObject +
    Returns true if the property index is defined. +
    hasCompileFunctionsWithDynamicScope() - +Method in class org.mozilla.javascript.Context +
    Deprecated.   +
    hasExcludedName(String) - +Method in class org.mozilla.javascript.serialize.ScriptableOutputStream +
    Returns true if the name is excluded from serialization. +
    hasExplicitGlobal() - +Static method in class org.mozilla.javascript.ContextFactory +
    Check if global factory was set. +
    hasFeature(int) - +Method in class org.mozilla.javascript.Context +
    Controls certain aspects of script semantics. +
    hasFeature(Context, int) - +Method in class org.mozilla.javascript.ContextFactory +
    Implementation of Context.hasFeature(int featureIndex). +
    hasGlobal() - +Static method in class org.mozilla.javascript.SecurityController +
    Check if global SecurityController was already installed. +
    hasInstance(Scriptable) - +Method in interface org.mozilla.javascript.Scriptable +
    The instanceof operator. +
    hasInstance(Scriptable) - +Method in class org.mozilla.javascript.ScriptableObject +
    Implements the instanceof operator. +
    hasProperty(Scriptable, String) - +Static method in class org.mozilla.javascript.ScriptableObject +
    Returns whether a named property is defined in an object or any object + in its prototype chain. +
    hasProperty(Scriptable, int) - +Static method in class org.mozilla.javascript.ScriptableObject +
    Returns whether an indexed property is defined in an object or any object + in its prototype chain. +
    +
    +

    +I

    +
    +
    ImporterTopLevel - Class in org.mozilla.javascript
    Class ImporterTopLevel + + This class defines a ScriptableObject that can be instantiated + as a top-level ("global") object to provide functionality similar + to Java's "import" statement.
    ImporterTopLevel() - +Constructor for class org.mozilla.javascript.ImporterTopLevel +
      +
    ImporterTopLevel(Context) - +Constructor for class org.mozilla.javascript.ImporterTopLevel +
      +
    ImporterTopLevel(Context, boolean) - +Constructor for class org.mozilla.javascript.ImporterTopLevel +
      +
    importPackage(Context, Scriptable, Object[], Function) - +Method in class org.mozilla.javascript.ImporterTopLevel +
    Deprecated. Kept only for compatibility. +
    init(Context, Scriptable, boolean) - +Static method in class org.mozilla.javascript.ImporterTopLevel +
      +
    initApplicationClassLoader(ClassLoader) - +Method in class org.mozilla.javascript.ContextFactory +
    Set explicit class loader to use when searching for Java classes. +
    initColumnNumber(int) - +Method in exception org.mozilla.javascript.RhinoException +
    Initialize the column number of the script statement causing the error. +
    initFromContext(Context) - +Method in class org.mozilla.javascript.CompilerEnvirons +
      +
    initGlobal(ContextFactory) - +Static method in class org.mozilla.javascript.ContextFactory +
    Set global ContextFactory. +
    initGlobal(SecurityController) - +Static method in class org.mozilla.javascript.SecurityController +
    Initialize global controller that will be used for all + security-related operations. +
    initLineNumber(int) - +Method in exception org.mozilla.javascript.RhinoException +
    Initialize the line number of the script statement causing the error. +
    initLineSource(String) - +Method in exception org.mozilla.javascript.RhinoException +
    Initialize the text of the source line containing the error. +
    initPrototypeId(int) - +Method in class org.mozilla.javascript.ImporterTopLevel +
      +
    initSourceName(String) - +Method in exception org.mozilla.javascript.RhinoException +
    Initialize the uri of the script source containing the error. +
    initStandardObjects() - +Method in class org.mozilla.javascript.Context +
    Initialize the standard objects. +
    initStandardObjects(ScriptableObject) - +Method in class org.mozilla.javascript.Context +
    Initialize the standard objects. +
    initStandardObjects(ScriptableObject, boolean) - +Method in class org.mozilla.javascript.Context +
    Initialize the standard objects. +
    initStandardObjects(Context, boolean) - +Method in class org.mozilla.javascript.ImporterTopLevel +
      +
    isActivationNeeded(String) - +Method in class org.mozilla.javascript.Context +
    Check whether the name is in the list of names of objects + forcing the creation of activation objects. +
    isAllowMemberExprAsFunctionName() - +Method in class org.mozilla.javascript.CompilerEnvirons +
      +
    isCachingEnabled() - +Method in class org.mozilla.javascript.ClassCache +
    Check if generated Java classes and Java reflection information + is cached. +
    isConst(String) - +Method in class org.mozilla.javascript.ScriptableObject +
    Returns true if the named property is defined as a const on this object. +
    isFunction() - +Method in interface org.mozilla.javascript.debug.DebuggableScript +
    Returns true if this is a function, false if it is a script. +
    isGenerateDebugInfo() - +Method in class org.mozilla.javascript.CompilerEnvirons +
      +
    isGeneratedScript() - +Method in interface org.mozilla.javascript.debug.DebuggableScript +
    Returns true if this script or function were runtime-generated + from JavaScript using eval function or Function + or Script constructors. +
    isGenerateObserverCount() - +Method in class org.mozilla.javascript.CompilerEnvirons +
      +
    isGeneratingDebug() - +Method in class org.mozilla.javascript.Context +
    Tell whether debug information is being generated. +
    isGeneratingDebugChanged() - +Method in class org.mozilla.javascript.Context +
      +
    isGeneratingSource() - +Method in class org.mozilla.javascript.CompilerEnvirons +
      +
    isGeneratingSource() - +Method in class org.mozilla.javascript.Context +
    Tell whether source information is being generated. +
    isGetterOrSetter(String, int, boolean) - +Method in class org.mozilla.javascript.ScriptableObject +
    Returns whether a property is a getter or a setter +
    isInvokerOptimizationEnabled() - +Method in class org.mozilla.javascript.ClassCache +
    Deprecated. The method always returns false. +
    isJavaPrimitiveWrap() - +Method in class org.mozilla.javascript.WrapFactory +
    Return false if result of Java method, which is instance of + String, Number, Boolean and + Character, should be used directly as JavaScript primitive + type. +
    isReservedKeywordAsIdentifier() - +Method in class org.mozilla.javascript.CompilerEnvirons +
      +
    isSealed() - +Method in class org.mozilla.javascript.Context +
    Checks if this is a sealed Context. +
    isSealed() - +Method in class org.mozilla.javascript.ContextFactory +
    Checks if this is a sealed ContextFactory. +
    isSealed() - +Method in class org.mozilla.javascript.ScriptableObject +
    Return true if this object is sealed. +
    isStrictMode() - +Method in class org.mozilla.javascript.CompilerEnvirons +
      +
    isTopLevel() - +Method in interface org.mozilla.javascript.debug.DebuggableScript +
      +
    isUseDynamicScope() - +Method in class org.mozilla.javascript.CompilerEnvirons +
      +
    isValidLanguageVersion(int) - +Static method in class org.mozilla.javascript.Context +
      +
    isValidOptimizationLevel(int) - +Static method in class org.mozilla.javascript.Context +
      +
    isXmlAvailable() - +Method in class org.mozilla.javascript.CompilerEnvirons +
      +
    +
    +

    +J

    +
    +
    JAVA_BOOLEAN_TYPE - +Static variable in class org.mozilla.javascript.FunctionObject +
      +
    JAVA_DOUBLE_TYPE - +Static variable in class org.mozilla.javascript.FunctionObject +
      +
    JAVA_INT_TYPE - +Static variable in class org.mozilla.javascript.FunctionObject +
      +
    JAVA_OBJECT_TYPE - +Static variable in class org.mozilla.javascript.FunctionObject +
      +
    JAVA_SCRIPTABLE_TYPE - +Static variable in class org.mozilla.javascript.FunctionObject +
      +
    JAVA_STRING_TYPE - +Static variable in class org.mozilla.javascript.FunctionObject +
      +
    JAVA_UNSUPPORTED_TYPE - +Static variable in class org.mozilla.javascript.FunctionObject +
      +
    JavaScriptException - Exception in org.mozilla.javascript
    Java reflection of JavaScript exceptions.
    JavaScriptException(Object) - +Constructor for exception org.mozilla.javascript.JavaScriptException +
    Deprecated. Use WrappedException.WrappedException(Throwable) to report + exceptions in Java code. +
    JavaScriptException(Object, String, int) - +Constructor for exception org.mozilla.javascript.JavaScriptException +
    Create a JavaScript exception wrapping the given JavaScript value +
    javaToJS(Object, Scriptable) - +Static method in class org.mozilla.javascript.Context +
    Convenient method to convert java value to its closest representation + in JavaScript. +
    jsToJava(Object, Class) - +Static method in class org.mozilla.javascript.Context +
    Convert a JavaScript value into the desired type. +
    +
    +

    +L

    +
    +
    languageVersionProperty - +Static variable in class org.mozilla.javascript.Context +
      +
    lineNumber() - +Method in exception org.mozilla.javascript.RhinoException +
    Returns the line number of the statement causing the error, + or zero if not available. +
    lineSource() - +Method in exception org.mozilla.javascript.RhinoException +
    The source text of the line causing the error, or null if unknown. +
    linkClass(Class) - +Method in interface org.mozilla.javascript.GeneratedClassLoader +
    Link the given class. +
    +
    +

    +M

    +
    +
    makeAuxiliaryClassName(String, String) - +Method in class org.mozilla.javascript.optimizer.ClassCompiler +
    Build class name for a auxiliary class generated by compiler. +
    makeContext() - +Method in class org.mozilla.javascript.ContextFactory +
    Create new Context instance to be associated with the current + thread. +
    +
    +

    +N

    +
    +
    newArray(Scriptable, int) - +Method in class org.mozilla.javascript.Context +
    Create an array with a specified initial length. +
    newArray(Scriptable, Object[]) - +Method in class org.mozilla.javascript.Context +
    Create an array with a set of initial elements. +
    newClassSerialNumber() - +Method in class org.mozilla.javascript.ClassCache +
    Internal engine method to return serial number for generated classes + to ensure name uniqueness. +
    newObject(Scriptable) - +Method in class org.mozilla.javascript.Context +
    Create a new JavaScript object. +
    newObject(Scriptable, String) - +Method in class org.mozilla.javascript.Context +
    Create a new JavaScript object by executing the named constructor. +
    newObject(Scriptable, String, Object[]) - +Method in class org.mozilla.javascript.Context +
    Creates a new JavaScript object by executing the named constructor. +
    NOT_FOUND - +Static variable in interface org.mozilla.javascript.Scriptable +
    Value returned from get if the property is not + found. +
    +
    +

    +O

    +
    +
    observeInstructionCount(int) - +Method in class org.mozilla.javascript.Context +
    Allow application to monitor counter of executed script instructions + in Context subclasses. +
    observeInstructionCount(Context, int) - +Method in class org.mozilla.javascript.ContextFactory +
    Implementation of + Context.observeInstructionCount(int instructionCount). +
    onContextCreated(Context) - +Method in class org.mozilla.javascript.ContextFactory +
      +
    onContextReleased(Context) - +Method in class org.mozilla.javascript.ContextFactory +
      +
    org.mozilla.javascript - package org.mozilla.javascript
     
    org.mozilla.javascript.debug - package org.mozilla.javascript.debug
     
    org.mozilla.javascript.optimizer - package org.mozilla.javascript.optimizer
     
    org.mozilla.javascript.serialize - package org.mozilla.javascript.serialize
     
    +
    +

    +P

    +
    +
    PERMANENT - +Static variable in class org.mozilla.javascript.ScriptableObject +
    Property attribute indicating property cannot be deleted. +
    printStackTrace(PrintWriter) - +Method in exception org.mozilla.javascript.RhinoException +
      +
    printStackTrace(PrintStream) - +Method in exception org.mozilla.javascript.RhinoException +
      +
    put(String, Scriptable, Object) - +Method in interface org.mozilla.javascript.Scriptable +
    Sets a named property in this object. +
    put(int, Scriptable, Object) - +Method in interface org.mozilla.javascript.Scriptable +
    Sets an indexed property in this object. +
    put(String, Scriptable, Object) - +Method in class org.mozilla.javascript.ScriptableObject +
    Sets the value of the named property, creating it if need be. +
    put(int, Scriptable, Object) - +Method in class org.mozilla.javascript.ScriptableObject +
    Sets the value of the indexed property, creating it if need be. +
    putConst(String, Scriptable, Object) - +Method in class org.mozilla.javascript.ScriptableObject +
    Sets the value of the named const property, creating it if need be. +
    putConstProperty(Scriptable, String, Object) - +Static method in class org.mozilla.javascript.ScriptableObject +
    Puts a named property in an object or in an object in its prototype chain. +
    putProperty(Scriptable, String, Object) - +Static method in class org.mozilla.javascript.ScriptableObject +
    Puts a named property in an object or in an object in its prototype chain. +
    putProperty(Scriptable, int, Object) - +Static method in class org.mozilla.javascript.ScriptableObject +
    Puts an indexed property in an object or in an object in its prototype chain. +
    putThreadLocal(Object, Object) - +Method in class org.mozilla.javascript.Context +
    Put a value that can later be retrieved using a given key. +
    +
    +

    +R

    +
    +
    READONLY - +Static variable in class org.mozilla.javascript.ScriptableObject +
    Property attribute indicating assignment to this property is ignored. +
    redefineProperty(Scriptable, String, boolean) - +Static method in class org.mozilla.javascript.ScriptableObject +
    If hasProperty(obj, name) would return true, then if the property that + was found is compatible with the new property, this method just returns. +
    refCall(Context, Scriptable, Object[]) - +Method in interface org.mozilla.javascript.RefCallable +
    Perform function call in reference context. +
    RefCallable - Interface in org.mozilla.javascript
    Object that can allows assignments to the result of function calls.
    removeActivationName(String) - +Method in class org.mozilla.javascript.Context +
    Remove a name from the list of names forcing the creation of real + activation objects for functions. +
    removeContextListener(ContextListener) - +Static method in class org.mozilla.javascript.Context +
    Deprecated.   +
    removeExcludedName(String) - +Method in class org.mozilla.javascript.serialize.ScriptableOutputStream +
    Removes a name from the list of names to exclude. +
    removeListener(ContextFactory.Listener) - +Method in class org.mozilla.javascript.ContextFactory +
      +
    removePropertyChangeListener(PropertyChangeListener) - +Method in class org.mozilla.javascript.Context +
    Remove an object from the list of objects registered to receive + notification of changes to a bounded property +
    removeThreadLocal(Object) - +Method in class org.mozilla.javascript.Context +
    Remove values from thread-local storage. +
    replaceObject(Object) - +Method in class org.mozilla.javascript.serialize.ScriptableOutputStream +
      +
    reportError(String, String, int, String, int) - +Static method in class org.mozilla.javascript.Context +
    Report an error using the error reporter for the current thread. +
    reportError(String) - +Static method in class org.mozilla.javascript.Context +
    Report an error using the error reporter for the current thread. +
    reportRuntimeError(String, String, int, String, int) - +Static method in class org.mozilla.javascript.Context +
    Report a runtime error using the error reporter for the current thread. +
    reportRuntimeError(String) - +Static method in class org.mozilla.javascript.Context +
    Report a runtime error using the error reporter for the current thread. +
    reportWarning(String, String, int, String, int) - +Static method in class org.mozilla.javascript.Context +
    Report a warning using the error reporter for the current thread. +
    reportWarning(String) - +Static method in class org.mozilla.javascript.Context +
    Report a warning using the error reporter for the current thread. +
    reportWarning(String, Throwable) - +Static method in class org.mozilla.javascript.Context +
      +
    reportWarningAsError() - +Method in class org.mozilla.javascript.CompilerEnvirons +
      +
    resolveClass(ObjectStreamClass) - +Method in class org.mozilla.javascript.serialize.ScriptableInputStream +
      +
    resolveObject(Object) - +Method in class org.mozilla.javascript.serialize.ScriptableInputStream +
      +
    RhinoException - Exception in org.mozilla.javascript
    The class of exceptions thrown by the JavaScript engine.
    run(Context) - +Method in interface org.mozilla.javascript.ContextAction +
    Execute action using the supplied Context instance. +
    runtimeError(String, String, int, String, int) - +Method in interface org.mozilla.javascript.ErrorReporter +
    Creates an EvaluatorException that may be thrown. +
    +
    +

    +S

    +
    +
    Script - Interface in org.mozilla.javascript
    All compiled scripts implement this interface.
    Scriptable - Interface in org.mozilla.javascript
    This is interface that all objects in JavaScript must implement.
    ScriptableInputStream - Class in org.mozilla.javascript.serialize
    Class ScriptableInputStream is used to read in a JavaScript + object or function previously serialized with a ScriptableOutputStream.
    ScriptableInputStream(InputStream, Scriptable) - +Constructor for class org.mozilla.javascript.serialize.ScriptableInputStream +
    Create a ScriptableInputStream. +
    ScriptableObject - Class in org.mozilla.javascript
    This is the default implementation of the Scriptable interface.
    ScriptableObject() - +Constructor for class org.mozilla.javascript.ScriptableObject +
      +
    ScriptableObject(Scriptable, Scriptable) - +Constructor for class org.mozilla.javascript.ScriptableObject +
      +
    ScriptableOutputStream - Class in org.mozilla.javascript.serialize
    Class ScriptableOutputStream is an ObjectOutputStream used + to serialize JavaScript objects and functions.
    ScriptableOutputStream(OutputStream, Scriptable) - +Constructor for class org.mozilla.javascript.serialize.ScriptableOutputStream +
    ScriptableOutputStream constructor. +
    seal(Object) - +Method in class org.mozilla.javascript.Context +
    Seal this Context object so any attempt to modify any of its properties + including calling Context.enter() and Context.exit() methods will + throw an exception. +
    seal() - +Method in class org.mozilla.javascript.ContextFactory +
    Seal this ContextFactory so any attempt to modify it like to add or + remove its listeners will throw an exception. +
    sealObject() - +Method in class org.mozilla.javascript.ScriptableObject +
    Seal this object. +
    SecurityController - Class in org.mozilla.javascript
    This class describes the support needed to implement security.
    SecurityController() - +Constructor for class org.mozilla.javascript.SecurityController +
      +
    setAllowMemberExprAsFunctionName(boolean) - +Method in class org.mozilla.javascript.CompilerEnvirons +
      +
    setApplicationClassLoader(ClassLoader) - +Method in class org.mozilla.javascript.Context +
      +
    setAttributes(String, Scriptable, int) - +Method in class org.mozilla.javascript.ScriptableObject +
    Deprecated. Use ScriptableObject.setAttributes(String name, int attributes). + The engine always ignored the start argument. +
    setAttributes(int, Scriptable, int) - +Method in class org.mozilla.javascript.ScriptableObject +
    Deprecated. Use ScriptableObject.setAttributes(int index, int attributes). + The engine always ignored the start argument. +
    setAttributes(String, int) - +Method in class org.mozilla.javascript.ScriptableObject +
    Set the attributes of a named property. +
    setAttributes(int, int) - +Method in class org.mozilla.javascript.ScriptableObject +
    Set the attributes of an indexed property. +
    setCachingEnabled(boolean) - +Method in class org.mozilla.javascript.ClassCache +
    Set whether to cache some values. +
    setCachingEnabled(boolean) - +Static method in class org.mozilla.javascript.Context +
    Deprecated.   +
    setClassShutter(ClassShutter) - +Method in class org.mozilla.javascript.Context +
    Set the LiveConnect access filter for this context. +
    setCompileFunctionsWithDynamicScope(boolean) - +Method in class org.mozilla.javascript.Context +
    Deprecated.   +
    setDebugger(Debugger, Object) - +Method in class org.mozilla.javascript.Context +
    Set the associated debugger. +
    setErrorReporter(ErrorReporter) - +Method in class org.mozilla.javascript.CompilerEnvirons +
      +
    setErrorReporter(ErrorReporter) - +Method in class org.mozilla.javascript.Context +
    Change the current error reporter. +
    setGenerateDebugInfo(boolean) - +Method in class org.mozilla.javascript.CompilerEnvirons +
      +
    setGenerateObserverCount(boolean) - +Method in class org.mozilla.javascript.CompilerEnvirons +
    Turn on or off generation of code with callbacks to + track the count of executed instructions. +
    setGenerateObserverCount(boolean) - +Method in class org.mozilla.javascript.Context +
    Turn on or off generation of code with callbacks to + track the count of executed instructions. +
    setGeneratingDebug(boolean) - +Method in class org.mozilla.javascript.Context +
    Specify whether or not debug information should be generated. +
    setGeneratingSource(boolean) - +Method in class org.mozilla.javascript.CompilerEnvirons +
    Specify whether or not source information should be generated. +
    setGeneratingSource(boolean) - +Method in class org.mozilla.javascript.Context +
    Specify whether or not source information should be generated. +
    setGetterOrSetter(String, int, Callable, boolean) - +Method in class org.mozilla.javascript.ScriptableObject +
    XXX: write docs. +
    setInstructionObserverThreshold(int) - +Method in class org.mozilla.javascript.Context +
    Set threshold of executed instructions counter that triggers call to + observeInstructionCount(). +
    setInvokerOptimizationEnabled(boolean) - +Method in class org.mozilla.javascript.ClassCache +
    Deprecated. The method does nothing. + Invoker optimization is no longer used by Rhino. + On modern JDK like 1.4 or 1.5 the disadvantages of the optimization + like increased memory usage or longer initialization time overweight + small speed increase that can be gained using generated proxy class + to replace reflection. +
    setJavaPrimitiveWrap(boolean) - +Method in class org.mozilla.javascript.WrapFactory +
      +
    setLanguageVersion(int) - +Method in class org.mozilla.javascript.CompilerEnvirons +
      +
    setLanguageVersion(int) - +Method in class org.mozilla.javascript.Context +
    Set the language version. +
    setLocale(Locale) - +Method in class org.mozilla.javascript.Context +
    Set the current locale. +
    setMainMethodClass(String) - +Method in class org.mozilla.javascript.optimizer.ClassCompiler +
    Set the class name to use for main method implementation. +
    setMaximumInterpreterStackDepth(int) - +Method in class org.mozilla.javascript.Context +
    Sets the maximum stack depth (in terms of number of call frames) + allowed in a single invocation of interpreter. +
    setOptimizationLevel(int) - +Method in class org.mozilla.javascript.CompilerEnvirons +
      +
    setOptimizationLevel(int) - +Method in class org.mozilla.javascript.Context +
    Set the current optimization level. +
    setParentScope(Scriptable) - +Method in interface org.mozilla.javascript.Scriptable +
    Set the parent scope of the object. +
    setParentScope(Scriptable) - +Method in class org.mozilla.javascript.ScriptableObject +
    Sets the parent (enclosing) scope of the object. +
    setPrototype(Scriptable) - +Method in interface org.mozilla.javascript.Scriptable +
    Set the prototype of the object. +
    setPrototype(Scriptable) - +Method in class org.mozilla.javascript.ScriptableObject +
    Sets the prototype of the object. +
    setReservedKeywordAsIdentifier(boolean) - +Method in class org.mozilla.javascript.CompilerEnvirons +
      +
    setSecurityController(SecurityController) - +Method in class org.mozilla.javascript.Context +
    Set the security controller for this context. +
    setTargetExtends(Class) - +Method in class org.mozilla.javascript.optimizer.ClassCompiler +
    Set the class that the generated target will extend. +
    setTargetImplements(Class[]) - +Method in class org.mozilla.javascript.optimizer.ClassCompiler +
    Set the interfaces that the generated target will implement. +
    setWrapFactory(WrapFactory) - +Method in class org.mozilla.javascript.Context +
    Set a WrapFactory for this Context. +
    setXmlAvailable(boolean) - +Method in class org.mozilla.javascript.CompilerEnvirons +
      +
    sourceName() - +Method in exception org.mozilla.javascript.RhinoException +
    Get the uri of the script source containing the error, or null + if that information is not available. +
    stringIsCompilableUnit(String) - +Method in class org.mozilla.javascript.Context +
    Check whether a string is ready to be compiled. +
    Synchronizer - Class in org.mozilla.javascript
    This class provides support for implementing Java-style synchronized + methods in Javascript.
    Synchronizer(Scriptable) - +Constructor for class org.mozilla.javascript.Synchronizer +
    Create a new synchronized function from an existing one. +
    +
    +

    +T

    +
    +
    throwAsScriptRuntimeEx(Throwable) - +Static method in class org.mozilla.javascript.Context +
    Rethrow the exception wrapping it as the script runtime exception. +
    toBoolean(Object) - +Static method in class org.mozilla.javascript.Context +
    Convert the value to a JavaScript boolean value. +
    toNumber(Object) - +Static method in class org.mozilla.javascript.Context +
    Convert the value to a JavaScript Number value. +
    toObject(Object, Scriptable) - +Static method in class org.mozilla.javascript.Context +
    Convert the value to an JavaScript object value. +
    toObject(Object, Scriptable, Class) - +Static method in class org.mozilla.javascript.Context +
    Deprecated.   +
    toString(Object) - +Static method in class org.mozilla.javascript.Context +
    Convert the value to a JavaScript String value. +
    toType(Object, Class) - +Static method in class org.mozilla.javascript.Context +
    Deprecated.   +
    +
    +

    +U

    +
    +
    UNINITIALIZED_CONST - +Static variable in class org.mozilla.javascript.ScriptableObject +
    Property attribute indicating that this is a const property that has not + been assigned yet. +
    unseal(Object) - +Method in class org.mozilla.javascript.Context +
    Unseal previously sealed Context object. +
    unwrap() - +Method in exception org.mozilla.javascript.WrappedException +
    Deprecated. Use WrappedException.getWrappedException() instead. +
    unwrap() - +Method in interface org.mozilla.javascript.Wrapper +
    Unwrap the object by returning the wrapped value. +
    +
    +

    +V

    +
    +
    VERSION_1_0 - +Static variable in class org.mozilla.javascript.Context +
    JavaScript 1.0 +
    VERSION_1_1 - +Static variable in class org.mozilla.javascript.Context +
    JavaScript 1.1 +
    VERSION_1_2 - +Static variable in class org.mozilla.javascript.Context +
    JavaScript 1.2 +
    VERSION_1_3 - +Static variable in class org.mozilla.javascript.Context +
    JavaScript 1.3 +
    VERSION_1_4 - +Static variable in class org.mozilla.javascript.Context +
    JavaScript 1.4 +
    VERSION_1_5 - +Static variable in class org.mozilla.javascript.Context +
    JavaScript 1.5 +
    VERSION_1_6 - +Static variable in class org.mozilla.javascript.Context +
    JavaScript 1.6 +
    VERSION_1_7 - +Static variable in class org.mozilla.javascript.Context +
    JavaScript 1.7 +
    VERSION_DEFAULT - +Static variable in class org.mozilla.javascript.Context +
    The default version. +
    VERSION_UNKNOWN - +Static variable in class org.mozilla.javascript.Context +
    The unknown version. +
    visibleToScripts(String) - +Method in interface org.mozilla.javascript.ClassShutter +
    Return true iff the Java class with the given name should be exposed + to scripts. +
    +
    +

    +W

    +
    +
    warning(String, String, int, String, int) - +Method in interface org.mozilla.javascript.ErrorReporter +
    Report a warning. +
    wrap(Context, Scriptable, Object, Class) - +Method in class org.mozilla.javascript.WrapFactory +
    Wrap the object. +
    wrapAsJavaObject(Context, Scriptable, Object, Class) - +Method in class org.mozilla.javascript.WrapFactory +
    Wrap Java object as Scriptable instance to allow full access to its + methods and fields from JavaScript. +
    WrapFactory - Class in org.mozilla.javascript
    Embeddings that wish to provide their own custom wrappings for Java + objects may extend this class and call + Context.setWrapFactory(WrapFactory) + Once an instance of this class or an extension of this class is enabled + for a given context (by calling setWrapFactory on that context), Rhino + will call the methods of this class whenever it needs to wrap a value + resulting from a call to a Java method or an access to a Java field.
    WrapFactory() - +Constructor for class org.mozilla.javascript.WrapFactory +
      +
    wrapNewObject(Context, Scriptable, Object) - +Method in class org.mozilla.javascript.WrapFactory +
    Wrap an object newly created by a constructor call. +
    WrappedException - Exception in org.mozilla.javascript
    A wrapper for runtime exceptions.
    WrappedException(Throwable) - +Constructor for exception org.mozilla.javascript.WrappedException +
      +
    Wrapper - Interface in org.mozilla.javascript
    Objects that can wrap other values for reflection in the JS environment + will implement Wrapper.
    +
    +A C D E F G H I J L M N O P R S T U V W + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/index.html b/trunk/infrastructure/rhino1_7R1/javadoc/index.html new file mode 100644 index 0000000..fa48043 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/index.html @@ -0,0 +1,37 @@ + + + + + + +Rhino + + + + + + + + + + + +<H2> +Frame Alert</H2> + +<P> +This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client. +<BR> +Link to<A HREF="overview-summary.html">Non-frame version.</A> + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/Callable.html b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/Callable.html new file mode 100644 index 0000000..07c38c5 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/Callable.html @@ -0,0 +1,224 @@ + + + + + + +Callable (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + +

    + +org.mozilla.javascript +
    +Interface Callable

    +
    +
    All Known Subinterfaces:
    Function, RefCallable
    +
    +
    +
    All Known Implementing Classes:
    org.mozilla.javascript.BaseFunction, org.mozilla.javascript.Delegator, FunctionObject, Synchronizer
    +
    +
    +
    +
    public interface Callable
    + + +

    +Generic notion of callable object that can execute some script-related code + upon request with specified values for script scope and this objects. +

    + +

    +


    + +

    + + + + + + + + + + + + +
    +Method Summary
    + java.lang.Objectcall(Context cx, + Scriptable scope, + Scriptable thisObj, + java.lang.Object[] args) + +
    +          Perform the call.
    +  +

    + + + + + + + + +
    +Method Detail
    + +

    +call

    +
    +java.lang.Object call(Context cx,
    +                      Scriptable scope,
    +                      Scriptable thisObj,
    +                      java.lang.Object[] args)
    +
    +
    Perform the call. +

    +

    +
    Parameters:
    cx - the current Context for this thread
    scope - the scope to use to resolve properties.
    thisObj - the JavaScript this object
    args - the array of arguments +
    Returns:
    the result of the call
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/ClassCache.html b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/ClassCache.html new file mode 100644 index 0000000..8d8b3ef --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/ClassCache.html @@ -0,0 +1,445 @@ + + + + + + +ClassCache (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + +

    + +org.mozilla.javascript +
    +Class ClassCache

    +
    +java.lang.Object
    +  extended by org.mozilla.javascript.ClassCache
    +
    +
    +
    +
    public class ClassCache
    extends java.lang.Object
    + + +

    +Cache of generated classes and data structures to access Java runtime + from JavaScript. +

    + +

    +

    +
    Since:
    +
    Rhino 1.5 Release 5
    +
    Author:
    +
    Igor Bukanov
    +
    +
    + +

    + + + + + + + + + + + +
    +Constructor Summary
    ClassCache() + +
    +           
    +  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +Method Summary
    + booleanassociate(ScriptableObject topScope) + +
    +          Associate ClassCache object with the given top-level scope.
    + voidclearCaches() + +
    +          Empty caches of generated Java classes and Java reflection information.
    +static ClassCacheget(Scriptable scope) + +
    +          Search for ClassCache object in the given scope.
    + booleanisCachingEnabled() + +
    +          Check if generated Java classes and Java reflection information + is cached.
    + booleanisInvokerOptimizationEnabled() + +
    +          Deprecated. The method always returns false.
    + intnewClassSerialNumber() + +
    +          Internal engine method to return serial number for generated classes + to ensure name uniqueness.
    + voidsetCachingEnabled(boolean enabled) + +
    +          Set whether to cache some values.
    + voidsetInvokerOptimizationEnabled(boolean enabled) + +
    +          Deprecated. The method does nothing. + Invoker optimization is no longer used by Rhino. + On modern JDK like 1.4 or 1.5 the disadvantages of the optimization + like increased memory usage or longer initialization time overweight + small speed increase that can be gained using generated proxy class + to replace reflection.
    + + + + + + + +
    Methods inherited from class java.lang.Object
    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    +  +

    + + + + + + + + +
    +Constructor Detail
    + +

    +ClassCache

    +
    +public ClassCache()
    +
    +
    + + + + + + + + +
    +Method Detail
    + +

    +get

    +
    +public static ClassCache get(Scriptable scope)
    +
    +
    Search for ClassCache object in the given scope. + The method first calls + ScriptableObject.getTopLevelScope(Scriptable scope) + to get the top most scope and then tries to locate associated + ClassCache object in the prototype chain of the top scope. +

    +

    +
    Parameters:
    scope - scope to search for ClassCache object. +
    Returns:
    previously associated ClassCache object or a new instance of + ClassCache if no ClassCache object was found.
    See Also:
    associate(ScriptableObject topScope)
    +
    +
    +
    + +

    +associate

    +
    +public boolean associate(ScriptableObject topScope)
    +
    +
    Associate ClassCache object with the given top-level scope. + The ClassCache object can only be associated with the given scope once. +

    +

    +
    Parameters:
    topScope - scope to associate this ClassCache object with. +
    Returns:
    true if no previous ClassCache objects were embedded into + the scope and this ClassCache were successfully associated + or false otherwise.
    See Also:
    get(Scriptable scope)
    +
    +
    +
    + +

    +clearCaches

    +
    +public void clearCaches()
    +
    +
    Empty caches of generated Java classes and Java reflection information. +

    +

    +
    +
    +
    +
    + +

    +isCachingEnabled

    +
    +public final boolean isCachingEnabled()
    +
    +
    Check if generated Java classes and Java reflection information + is cached. +

    +

    +
    +
    +
    +
    + +

    +setCachingEnabled

    +
    +public void setCachingEnabled(boolean enabled)
    +
    +
    Set whether to cache some values. +

    + By default, the engine will cache the results of + Class.getMethods() and similar calls. + This can speed execution dramatically, but increases the memory + footprint. Also, with caching enabled, references may be held to + objects past the lifetime of any real usage. +

    + If caching is enabled and this method is called with a + false argument, the caches will be emptied. +

    + Caching is enabled by default. +

    +

    +
    Parameters:
    enabled - if true, caching is enabled
    See Also:
    clearCaches()
    +
    +
    +
    + +

    +isInvokerOptimizationEnabled

    +
    +public boolean isInvokerOptimizationEnabled()
    +
    +
    Deprecated. The method always returns false. +

    +

    +
    See Also:
    setInvokerOptimizationEnabled(boolean enabled)
    +
    +
    +
    + +

    +setInvokerOptimizationEnabled

    +
    +public void setInvokerOptimizationEnabled(boolean enabled)
    +
    +
    Deprecated. The method does nothing. + Invoker optimization is no longer used by Rhino. + On modern JDK like 1.4 or 1.5 the disadvantages of the optimization + like increased memory usage or longer initialization time overweight + small speed increase that can be gained using generated proxy class + to replace reflection. +

    +

    +
    +
    +
    +
    + +

    +newClassSerialNumber

    +
    +public final int newClassSerialNumber()
    +
    +
    Internal engine method to return serial number for generated classes + to ensure name uniqueness. +

    +

    +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/ClassShutter.html b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/ClassShutter.html new file mode 100644 index 0000000..924cfb5 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/ClassShutter.html @@ -0,0 +1,247 @@ + + + + + + +ClassShutter (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + +

    + +org.mozilla.javascript +
    +Interface ClassShutter

    +
    +
    +
    public interface ClassShutter
    + + +

    +Embeddings that wish to filter Java classes that are visible to scripts +through the LiveConnect, should implement this interface. +

    + +

    +

    +
    Since:
    +
    1.5 Release 4
    +
    Author:
    +
    Norris Boyd
    +
    See Also:
    Context.setClassShutter(ClassShutter)
    +
    + +

    + + + + + + + + + + + + +
    +Method Summary
    + booleanvisibleToScripts(java.lang.String fullClassName) + +
    +          Return true iff the Java class with the given name should be exposed + to scripts.
    +  +

    + + + + + + + + +
    +Method Detail
    + +

    +visibleToScripts

    +
    +boolean visibleToScripts(java.lang.String fullClassName)
    +
    +
    Return true iff the Java class with the given name should be exposed + to scripts. +

    + An embedding may filter which Java classes are exposed through + LiveConnect to JavaScript scripts. +

    + Due to the fact that there is no package reflection in Java, + this method will also be called with package names. There + is no way for Rhino to tell if "Packages.a.b" is a package name + or a class that doesn't exist. What Rhino does is attempt + to load each segment of "Packages.a.b.c": It first attempts to + load class "a", then attempts to load class "a.b", then + finally attempts to load class "a.b.c". On a Rhino installation + without any ClassShutter set, and without any of the + above classes, the expression "Packages.a.b.c" will result in + a [JavaPackage a.b.c] and not an error. +

    + With ClassShutter supplied, Rhino will first call + visibleToScripts before attempting to look up the class name. If + visibleToScripts returns false, the class name lookup is not + performed and subsequent Rhino execution assumes the class is + not present. So for "java.lang.System.out.println" the lookup + of "java.lang.System" is skipped and thus Rhino assumes that + "java.lang.System" doesn't exist. So then for "java.lang.System.out", + Rhino attempts to load the class "java.lang.System.out" because + it assumes that "java.lang.System" is a package name. +

    +

    +

    +
    Parameters:
    fullClassName - the full name of the class (including the package + name, with '.' as a delimiter). For example the + standard string class is "java.lang.String" +
    Returns:
    whether or not to reveal this class to scripts
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/CompilerEnvirons.html b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/CompilerEnvirons.html new file mode 100644 index 0000000..8b5e109 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/CompilerEnvirons.html @@ -0,0 +1,669 @@ + + + + + + +CompilerEnvirons (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + +

    + +org.mozilla.javascript +
    +Class CompilerEnvirons

    +
    +java.lang.Object
    +  extended by org.mozilla.javascript.CompilerEnvirons
    +
    +
    +
    +
    public class CompilerEnvirons
    extends java.lang.Object
    + + +

    +


    + +

    + + + + + + + + + + + +
    +Constructor Summary
    CompilerEnvirons() + +
    +           
    +  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +Method Summary
    + ErrorReportergetErrorReporter() + +
    +           
    + intgetLanguageVersion() + +
    +           
    + intgetOptimizationLevel() + +
    +           
    + voidinitFromContext(Context cx) + +
    +           
    + booleanisAllowMemberExprAsFunctionName() + +
    +           
    + booleanisGenerateDebugInfo() + +
    +           
    + booleanisGenerateObserverCount() + +
    +           
    + booleanisGeneratingSource() + +
    +           
    + booleanisReservedKeywordAsIdentifier() + +
    +           
    + booleanisStrictMode() + +
    +           
    + booleanisUseDynamicScope() + +
    +           
    + booleanisXmlAvailable() + +
    +           
    + booleanreportWarningAsError() + +
    +           
    + voidsetAllowMemberExprAsFunctionName(boolean flag) + +
    +           
    + voidsetErrorReporter(ErrorReporter errorReporter) + +
    +           
    + voidsetGenerateDebugInfo(boolean flag) + +
    +           
    + voidsetGenerateObserverCount(boolean generateObserverCount) + +
    +          Turn on or off generation of code with callbacks to + track the count of executed instructions.
    + voidsetGeneratingSource(boolean generatingSource) + +
    +          Specify whether or not source information should be generated.
    + voidsetLanguageVersion(int languageVersion) + +
    +           
    + voidsetOptimizationLevel(int level) + +
    +           
    + voidsetReservedKeywordAsIdentifier(boolean flag) + +
    +           
    + voidsetXmlAvailable(boolean flag) + +
    +           
    + + + + + + + +
    Methods inherited from class java.lang.Object
    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    +  +

    + + + + + + + + +
    +Constructor Detail
    + +

    +CompilerEnvirons

    +
    +public CompilerEnvirons()
    +
    +
    + + + + + + + + +
    +Method Detail
    + +

    +initFromContext

    +
    +public void initFromContext(Context cx)
    +
    +
    +
    +
    +
    +
    + +

    +getErrorReporter

    +
    +public final ErrorReporter getErrorReporter()
    +
    +
    +
    +
    +
    +
    + +

    +setErrorReporter

    +
    +public void setErrorReporter(ErrorReporter errorReporter)
    +
    +
    +
    +
    +
    +
    + +

    +getLanguageVersion

    +
    +public final int getLanguageVersion()
    +
    +
    +
    +
    +
    +
    + +

    +setLanguageVersion

    +
    +public void setLanguageVersion(int languageVersion)
    +
    +
    +
    +
    +
    +
    + +

    +isGenerateDebugInfo

    +
    +public final boolean isGenerateDebugInfo()
    +
    +
    +
    +
    +
    +
    + +

    +setGenerateDebugInfo

    +
    +public void setGenerateDebugInfo(boolean flag)
    +
    +
    +
    +
    +
    +
    + +

    +isUseDynamicScope

    +
    +public final boolean isUseDynamicScope()
    +
    +
    +
    +
    +
    +
    + +

    +isReservedKeywordAsIdentifier

    +
    +public final boolean isReservedKeywordAsIdentifier()
    +
    +
    +
    +
    +
    +
    + +

    +setReservedKeywordAsIdentifier

    +
    +public void setReservedKeywordAsIdentifier(boolean flag)
    +
    +
    +
    +
    +
    +
    + +

    +isAllowMemberExprAsFunctionName

    +
    +public final boolean isAllowMemberExprAsFunctionName()
    +
    +
    +
    +
    +
    +
    + +

    +setAllowMemberExprAsFunctionName

    +
    +public void setAllowMemberExprAsFunctionName(boolean flag)
    +
    +
    +
    +
    +
    +
    + +

    +isXmlAvailable

    +
    +public final boolean isXmlAvailable()
    +
    +
    +
    +
    +
    +
    + +

    +setXmlAvailable

    +
    +public void setXmlAvailable(boolean flag)
    +
    +
    +
    +
    +
    +
    + +

    +getOptimizationLevel

    +
    +public final int getOptimizationLevel()
    +
    +
    +
    +
    +
    +
    + +

    +setOptimizationLevel

    +
    +public void setOptimizationLevel(int level)
    +
    +
    +
    +
    +
    +
    + +

    +isGeneratingSource

    +
    +public final boolean isGeneratingSource()
    +
    +
    +
    +
    +
    +
    + +

    +isStrictMode

    +
    +public final boolean isStrictMode()
    +
    +
    +
    +
    +
    +
    + +

    +reportWarningAsError

    +
    +public final boolean reportWarningAsError()
    +
    +
    +
    +
    +
    +
    + +

    +setGeneratingSource

    +
    +public void setGeneratingSource(boolean generatingSource)
    +
    +
    Specify whether or not source information should be generated. +

    + Without source information, evaluating the "toString" method + on JavaScript functions produces only "[native code]" for + the body of the function. + Note that code generated without source is not fully ECMA + conformant. +

    +

    +
    +
    +
    +
    + +

    +isGenerateObserverCount

    +
    +public boolean isGenerateObserverCount()
    +
    +
    + +
    Returns:
    true iff code will be generated with callbacks to enable + instruction thresholds
    +
    +
    +
    + +

    +setGenerateObserverCount

    +
    +public void setGenerateObserverCount(boolean generateObserverCount)
    +
    +
    Turn on or off generation of code with callbacks to + track the count of executed instructions. + Currently only affects JVM byte code generation: this slows down the + generated code, but code generated without the callbacks will not + be counted toward instruction thresholds. Rhino's interpretive + mode does instruction counting without inserting callbacks, so + there is no requirement to compile code differently. +

    +

    +
    Parameters:
    generateObserverCount - if true, generated code will contain + calls to accumulate an estimate of the instructions executed.
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/Context.html b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/Context.html new file mode 100644 index 0000000..64fb637 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/Context.html @@ -0,0 +1,3581 @@ + + + + + + +Context (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + +

    + +org.mozilla.javascript +
    +Class Context

    +
    +java.lang.Object
    +  extended by org.mozilla.javascript.Context
    +
    +
    +
    +
    public class Context
    extends java.lang.Object
    + + +

    +This class represents the runtime context of an executing script. + + Before executing a script, an instance of Context must be created + and associated with the thread that will be executing the script. + The Context will be used to store information about the executing + of the script such as the call stack. Contexts are associated with + the current thread using the call(ContextAction) + or enter() methods.

    + + Different forms of script execution are supported. Scripts may be + evaluated from the source directly, or first compiled and then later + executed. Interactive execution is also supported.

    + + Some aspects of script execution, such as type conversions and + object creation, may be accessed directly through methods of + Context. +

    + +

    +

    +
    Author:
    +
    Norris Boyd, Brendan Eich
    +
    See Also:
    Scriptable
    +
    + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +Field Summary
    +static java.lang.Object[]emptyArgs + +
    +          Convenient value to use as zero-length array of objects.
    +static java.lang.StringerrorReporterProperty + +
    +           
    +static intFEATURE_DYNAMIC_SCOPE + +
    +          Control if dynamic scope should be used for name access.
    +static intFEATURE_E4X + +
    +          Control if support for E4X(ECMAScript for XML) extension is available.
    +static intFEATURE_ENHANCED_JAVA_ACCESS + +
    +          Enables enhanced access to Java.
    +static intFEATURE_LOCATION_INFORMATION_IN_ERROR + +
    +          When the feature is on Rhino will add a "fileName" and "lineNumber" + properties to Error objects automatically.
    +static intFEATURE_MEMBER_EXPR_AS_FUNCTION_NAME + +
    +          Control if member expression as function name extension is available.
    +static intFEATURE_NON_ECMA_GET_YEAR + +
    +          Controls behaviour of Date.prototype.getYear().
    +static intFEATURE_PARENT_PROTO_PROPERTIES + +
    +          Control if properties __proto__ and __parent__ + are treated specially.
    +static intFEATURE_PARENT_PROTO_PROPRTIES + +
    +          Deprecated. In previous releases, this name was given to + FEATURE_PARENT_PROTO_PROPERTIES.
    +static intFEATURE_RESERVED_KEYWORD_AS_IDENTIFIER + +
    +          Control if reserved keywords are treated as identifiers.
    +static intFEATURE_STRICT_EVAL + +
    +          Control if strict eval mode is enabled.
    +static intFEATURE_STRICT_MODE + +
    +          Controls whether JS 1.5 'strict mode' is enabled.
    +static intFEATURE_STRICT_VARS + +
    +          Control if strict variable mode is enabled.
    +static intFEATURE_TO_STRING_AS_SOURCE + +
    +          Control if toString() should returns the same result + as toSource() when applied to objects and arrays.
    +static intFEATURE_WARNING_AS_ERROR + +
    +          Controls whether a warning should be treated as an error.
    + booleangenerateObserverCount + +
    +           
    +static java.lang.StringlanguageVersionProperty + +
    +           
    +static intVERSION_1_0 + +
    +          JavaScript 1.0
    +static intVERSION_1_1 + +
    +          JavaScript 1.1
    +static intVERSION_1_2 + +
    +          JavaScript 1.2
    +static intVERSION_1_3 + +
    +          JavaScript 1.3
    +static intVERSION_1_4 + +
    +          JavaScript 1.4
    +static intVERSION_1_5 + +
    +          JavaScript 1.5
    +static intVERSION_1_6 + +
    +          JavaScript 1.6
    +static intVERSION_1_7 + +
    +          JavaScript 1.7
    +static intVERSION_DEFAULT + +
    +          The default version.
    +static intVERSION_UNKNOWN + +
    +          The unknown version.
    +  + + + + + + + + + + +
    +Constructor Summary
    Context() + +
    +          Deprecated. use ContextFactory.enter() or + ContextFactory.call(ContextAction) instead.
    +  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +Method Summary
    + voidaddActivationName(java.lang.String name) + +
    +          Add a name to the list of names forcing the creation of real + activation objects for functions.
    +static voidaddContextListener(org.mozilla.javascript.ContextListener listener) + +
    +          Deprecated.  
    + voidaddPropertyChangeListener(java.beans.PropertyChangeListener l) + +
    +          Register an object to receive notifications when a bound property + has changed
    +static java.lang.Objectcall(ContextAction action) + +
    +          Deprecated. use ContextFactory.call(ContextAction) instead as + this method relies on usage of a static singleton "global" + ContextFactory.
    +static java.lang.Objectcall(ContextFactory factory, + Callable callable, + Scriptable scope, + Scriptable thisObj, + java.lang.Object[] args) + +
    +          Call Callable.call(Context cx, Scriptable scope, Scriptable thisObj, + Object[] args) + using the Context instance associated with the current thread.
    +static voidcheckLanguageVersion(int version) + +
    +           
    +static voidcheckOptimizationLevel(int optimizationLevel) + +
    +           
    + FunctioncompileFunction(Scriptable scope, + java.lang.String source, + java.lang.String sourceName, + int lineno, + java.lang.Object securityDomain) + +
    +          Compile a JavaScript function.
    + ScriptcompileReader(java.io.Reader in, + java.lang.String sourceName, + int lineno, + java.lang.Object securityDomain) + +
    +          Compiles the source in the given reader.
    + ScriptcompileReader(Scriptable scope, + java.io.Reader in, + java.lang.String sourceName, + int lineno, + java.lang.Object securityDomain) + +
    +          Deprecated.  
    + ScriptcompileString(java.lang.String source, + java.lang.String sourceName, + int lineno, + java.lang.Object securityDomain) + +
    +          Compiles the source in the given string.
    + GeneratedClassLoadercreateClassLoader(java.lang.ClassLoader parent) + +
    +          Create class loader for generated classes.
    + java.lang.StringdecompileFunction(Function fun, + int indent) + +
    +          Decompile a JavaScript Function.
    + java.lang.StringdecompileFunctionBody(Function fun, + int indent) + +
    +          Decompile the body of a JavaScript Function.
    + java.lang.StringdecompileScript(Script script, + int indent) + +
    +          Decompile the script.
    +static Contextenter() + +
    +          Deprecated. use ContextFactory.enter() or + ContextFactory.call(ContextAction) instead as this method relies + on usage of a static singleton "global" ContextFactory.
    +static Contextenter(Context cx) + +
    +          Deprecated. use ContextFactory.enterContext(Context) instead as + this method relies on usage of a static singleton "global" ContextFactory.
    + java.lang.ObjectevaluateReader(Scriptable scope, + java.io.Reader in, + java.lang.String sourceName, + int lineno, + java.lang.Object securityDomain) + +
    +          Evaluate a reader as JavaScript source.
    + java.lang.ObjectevaluateString(Scriptable scope, + java.lang.String source, + java.lang.String sourceName, + int lineno, + java.lang.Object securityDomain) + +
    +          Evaluate a JavaScript source string.
    +static voidexit() + +
    +          Exit a block of code requiring a Context.
    + java.lang.ClassLoadergetApplicationClassLoader() + +
    +           
    +static ContextgetCurrentContext() + +
    +          Get the current Context.
    +static DebuggableScriptgetDebuggableView(Script script) + +
    +          Return DebuggableScript instance if any associated with the script.
    + org.mozilla.javascript.debug.DebuggergetDebugger() + +
    +          Return the current debugger.
    + java.lang.ObjectgetDebuggerContextData() + +
    +          Return the debugger context data associated with current context.
    + org.mozilla.javascript.xml.XMLLib.FactorygetE4xImplementationFactory() + +
    +          Returns an object which specifies an E4X implementation to use within + this Context.
    + java.lang.Object[]getElements(Scriptable object) + +
    +          Get the elements of a JavaScript array.
    + ErrorReportergetErrorReporter() + +
    +          Get the current error reporter.
    + ContextFactorygetFactory() + +
    +          Return ContextFactory instance used to create this Context.
    + java.lang.StringgetImplementationVersion() + +
    +          Get the implementation version.
    + intgetInstructionObserverThreshold() + +
    +          Get threshold of executed instructions counter that triggers call to + observeInstructionCount().
    + intgetLanguageVersion() + +
    +          Get the current language version.
    + java.util.LocalegetLocale() + +
    +          Get the current locale.
    + intgetMaximumInterpreterStackDepth() + +
    +          Returns the maximum stack depth (in terms of number of call frames) + allowed in a single invocation of interpreter.
    + intgetOptimizationLevel() + +
    +          Get the current optimization level.
    + java.lang.ObjectgetThreadLocal(java.lang.Object key) + +
    +          Get a value corresponding to a key.
    +static java.lang.ObjectgetUndefinedValue() + +
    +          Get the singleton object that represents the JavaScript Undefined value.
    + WrapFactorygetWrapFactory() + +
    +          Return the current WrapFactory, or null if none is defined.
    + booleanhasCompileFunctionsWithDynamicScope() + +
    +          Deprecated.  
    + booleanhasFeature(int featureIndex) + +
    +          Controls certain aspects of script semantics.
    + ScriptableObjectinitStandardObjects() + +
    +          Initialize the standard objects.
    + ScriptableinitStandardObjects(ScriptableObject scope) + +
    +          Initialize the standard objects.
    + ScriptableObjectinitStandardObjects(ScriptableObject scope, + boolean sealed) + +
    +          Initialize the standard objects.
    + booleanisActivationNeeded(java.lang.String name) + +
    +          Check whether the name is in the list of names of objects + forcing the creation of activation objects.
    + booleanisGeneratingDebug() + +
    +          Tell whether debug information is being generated.
    + booleanisGeneratingDebugChanged() + +
    +           
    + booleanisGeneratingSource() + +
    +          Tell whether source information is being generated.
    + booleanisSealed() + +
    +          Checks if this is a sealed Context.
    +static booleanisValidLanguageVersion(int version) + +
    +           
    +static booleanisValidOptimizationLevel(int optimizationLevel) + +
    +           
    +static java.lang.ObjectjavaToJS(java.lang.Object value, + Scriptable scope) + +
    +          Convenient method to convert java value to its closest representation + in JavaScript.
    +static java.lang.ObjectjsToJava(java.lang.Object value, + java.lang.Class desiredType) + +
    +          Convert a JavaScript value into the desired type.
    + ScriptablenewArray(Scriptable scope, + int length) + +
    +          Create an array with a specified initial length.
    + ScriptablenewArray(Scriptable scope, + java.lang.Object[] elements) + +
    +          Create an array with a set of initial elements.
    + ScriptablenewObject(Scriptable scope) + +
    +          Create a new JavaScript object.
    + ScriptablenewObject(Scriptable scope, + java.lang.String constructorName) + +
    +          Create a new JavaScript object by executing the named constructor.
    + ScriptablenewObject(Scriptable scope, + java.lang.String constructorName, + java.lang.Object[] args) + +
    +          Creates a new JavaScript object by executing the named constructor.
    +protected  voidobserveInstructionCount(int instructionCount) + +
    +          Allow application to monitor counter of executed script instructions + in Context subclasses.
    + voidputThreadLocal(java.lang.Object key, + java.lang.Object value) + +
    +          Put a value that can later be retrieved using a given key.
    + voidremoveActivationName(java.lang.String name) + +
    +          Remove a name from the list of names forcing the creation of real + activation objects for functions.
    +static voidremoveContextListener(org.mozilla.javascript.ContextListener listener) + +
    +          Deprecated.  
    + voidremovePropertyChangeListener(java.beans.PropertyChangeListener l) + +
    +          Remove an object from the list of objects registered to receive + notification of changes to a bounded property
    + voidremoveThreadLocal(java.lang.Object key) + +
    +          Remove values from thread-local storage.
    +static voidreportError(java.lang.String message) + +
    +          Report an error using the error reporter for the current thread.
    +static voidreportError(java.lang.String message, + java.lang.String sourceName, + int lineno, + java.lang.String lineSource, + int lineOffset) + +
    +          Report an error using the error reporter for the current thread.
    +static EvaluatorExceptionreportRuntimeError(java.lang.String message) + +
    +          Report a runtime error using the error reporter for the current thread.
    +static EvaluatorExceptionreportRuntimeError(java.lang.String message, + java.lang.String sourceName, + int lineno, + java.lang.String lineSource, + int lineOffset) + +
    +          Report a runtime error using the error reporter for the current thread.
    +static voidreportWarning(java.lang.String message) + +
    +          Report a warning using the error reporter for the current thread.
    +static voidreportWarning(java.lang.String message, + java.lang.String sourceName, + int lineno, + java.lang.String lineSource, + int lineOffset) + +
    +          Report a warning using the error reporter for the current thread.
    +static voidreportWarning(java.lang.String message, + java.lang.Throwable t) + +
    +           
    + voidseal(java.lang.Object sealKey) + +
    +          Seal this Context object so any attempt to modify any of its properties + including calling enter() and exit() methods will + throw an exception.
    + voidsetApplicationClassLoader(java.lang.ClassLoader loader) + +
    +           
    +static voidsetCachingEnabled(boolean cachingEnabled) + +
    +          Deprecated.  
    + voidsetClassShutter(ClassShutter shutter) + +
    +          Set the LiveConnect access filter for this context.
    + voidsetCompileFunctionsWithDynamicScope(boolean flag) + +
    +          Deprecated.  
    + voidsetDebugger(org.mozilla.javascript.debug.Debugger debugger, + java.lang.Object contextData) + +
    +          Set the associated debugger.
    + ErrorReportersetErrorReporter(ErrorReporter reporter) + +
    +          Change the current error reporter.
    + voidsetGenerateObserverCount(boolean generateObserverCount) + +
    +          Turn on or off generation of code with callbacks to + track the count of executed instructions.
    + voidsetGeneratingDebug(boolean generatingDebug) + +
    +          Specify whether or not debug information should be generated.
    + voidsetGeneratingSource(boolean generatingSource) + +
    +          Specify whether or not source information should be generated.
    + voidsetInstructionObserverThreshold(int threshold) + +
    +          Set threshold of executed instructions counter that triggers call to + observeInstructionCount().
    + voidsetLanguageVersion(int version) + +
    +          Set the language version.
    + java.util.LocalesetLocale(java.util.Locale loc) + +
    +          Set the current locale.
    + voidsetMaximumInterpreterStackDepth(int max) + +
    +          Sets the maximum stack depth (in terms of number of call frames) + allowed in a single invocation of interpreter.
    + voidsetOptimizationLevel(int optimizationLevel) + +
    +          Set the current optimization level.
    + voidsetSecurityController(SecurityController controller) + +
    +          Set the security controller for this context.
    + voidsetWrapFactory(WrapFactory wrapFactory) + +
    +          Set a WrapFactory for this Context.
    + booleanstringIsCompilableUnit(java.lang.String source) + +
    +          Check whether a string is ready to be compiled.
    +static java.lang.RuntimeExceptionthrowAsScriptRuntimeEx(java.lang.Throwable e) + +
    +          Rethrow the exception wrapping it as the script runtime exception.
    +static booleantoBoolean(java.lang.Object value) + +
    +          Convert the value to a JavaScript boolean value.
    +static doubletoNumber(java.lang.Object value) + +
    +          Convert the value to a JavaScript Number value.
    +static ScriptabletoObject(java.lang.Object value, + Scriptable scope) + +
    +          Convert the value to an JavaScript object value.
    +static ScriptabletoObject(java.lang.Object value, + Scriptable scope, + java.lang.Class staticType) + +
    +          Deprecated.  
    +static java.lang.StringtoString(java.lang.Object value) + +
    +          Convert the value to a JavaScript String value.
    +static java.lang.ObjecttoType(java.lang.Object value, + java.lang.Class desiredType) + +
    +          Deprecated.  
    + voidunseal(java.lang.Object sealKey) + +
    +          Unseal previously sealed Context object.
    + + + + + + + +
    Methods inherited from class java.lang.Object
    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    +  +

    + + + + + + + + +
    +Field Detail
    + +

    +VERSION_UNKNOWN

    +
    +public static final int VERSION_UNKNOWN
    +
    +
    The unknown version. +

    +

    +
    See Also:
    Constant Field Values
    +
    +
    + +

    +VERSION_DEFAULT

    +
    +public static final int VERSION_DEFAULT
    +
    +
    The default version. +

    +

    +
    See Also:
    Constant Field Values
    +
    +
    + +

    +VERSION_1_0

    +
    +public static final int VERSION_1_0
    +
    +
    JavaScript 1.0 +

    +

    +
    See Also:
    Constant Field Values
    +
    +
    + +

    +VERSION_1_1

    +
    +public static final int VERSION_1_1
    +
    +
    JavaScript 1.1 +

    +

    +
    See Also:
    Constant Field Values
    +
    +
    + +

    +VERSION_1_2

    +
    +public static final int VERSION_1_2
    +
    +
    JavaScript 1.2 +

    +

    +
    See Also:
    Constant Field Values
    +
    +
    + +

    +VERSION_1_3

    +
    +public static final int VERSION_1_3
    +
    +
    JavaScript 1.3 +

    +

    +
    See Also:
    Constant Field Values
    +
    +
    + +

    +VERSION_1_4

    +
    +public static final int VERSION_1_4
    +
    +
    JavaScript 1.4 +

    +

    +
    See Also:
    Constant Field Values
    +
    +
    + +

    +VERSION_1_5

    +
    +public static final int VERSION_1_5
    +
    +
    JavaScript 1.5 +

    +

    +
    See Also:
    Constant Field Values
    +
    +
    + +

    +VERSION_1_6

    +
    +public static final int VERSION_1_6
    +
    +
    JavaScript 1.6 +

    +

    +
    See Also:
    Constant Field Values
    +
    +
    + +

    +VERSION_1_7

    +
    +public static final int VERSION_1_7
    +
    +
    JavaScript 1.7 +

    +

    +
    See Also:
    Constant Field Values
    +
    +
    + +

    +FEATURE_NON_ECMA_GET_YEAR

    +
    +public static final int FEATURE_NON_ECMA_GET_YEAR
    +
    +
    Controls behaviour of Date.prototype.getYear(). + If hasFeature(FEATURE_NON_ECMA_GET_YEAR) returns true, + Date.prototype.getYear subtructs 1900 only if 1900 <= date < 2000. + The default behavior of hasFeature(int) is always to subtruct + 1900 as rquired by ECMAScript B.2.4. +

    +

    +
    See Also:
    Constant Field Values
    +
    +
    + +

    +FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME

    +
    +public static final int FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME
    +
    +
    Control if member expression as function name extension is available. + If hasFeature(FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME) returns + true, allow function memberExpression(args) { body } to be + syntax sugar for memberExpression = function(args) { body }, + when memberExpression is not a simple identifier. + See ECMAScript-262, section 11.2 for definition of memberExpression. + By default hasFeature(int) returns false. +

    +

    +
    See Also:
    Constant Field Values
    +
    +
    + +

    +FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER

    +
    +public static final int FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER
    +
    +
    Control if reserved keywords are treated as identifiers. + If hasFeature(RESERVED_KEYWORD_AS_IDENTIFIER) returns true, + treat future reserved keyword (see Ecma-262, section 7.5.3) as ordinary + identifiers but warn about this usage. + + By default hasFeature(int) returns false. +

    +

    +
    See Also:
    Constant Field Values
    +
    +
    + +

    +FEATURE_TO_STRING_AS_SOURCE

    +
    +public static final int FEATURE_TO_STRING_AS_SOURCE
    +
    +
    Control if toString() should returns the same result + as toSource() when applied to objects and arrays. + If hasFeature(FEATURE_TO_STRING_AS_SOURCE) returns true, + calling toString() on JS objects gives the same result as + calling toSource(). That is it returns JS source with code + to create an object with all enumeratable fields of the original object + instead of printing [object result of + Scriptable.getClassName()]. +

    + By default hasFeature(int) returns true only if + the current JS version is set to VERSION_1_2. +

    +

    +
    See Also:
    Constant Field Values
    +
    +
    + +

    +FEATURE_PARENT_PROTO_PROPERTIES

    +
    +public static final int FEATURE_PARENT_PROTO_PROPERTIES
    +
    +
    Control if properties __proto__ and __parent__ + are treated specially. + If hasFeature(FEATURE_PARENT_PROTO_PROPERTIES) returns true, + treat __parent__ and __proto__ as special properties. +

    + The properties allow to query and set scope and prototype chains for the + objects. The special meaning of the properties is available + only when they are used as the right hand side of the dot operator. + For example, while x.__proto__ = y changes the prototype + chain of the object x to point to y, + x["__proto__"] = y simply assigns a new value to the property + __proto__ in x even when the feature is on. + + By default hasFeature(int) returns true. +

    +

    +
    See Also:
    Constant Field Values
    +
    +
    + +

    +FEATURE_PARENT_PROTO_PROPRTIES

    +
    +public static final int FEATURE_PARENT_PROTO_PROPRTIES
    +
    +
    Deprecated. In previous releases, this name was given to + FEATURE_PARENT_PROTO_PROPERTIES.
    +
    See Also:
    Constant Field Values
    +
    +
    + +

    +FEATURE_E4X

    +
    +public static final int FEATURE_E4X
    +
    +
    Control if support for E4X(ECMAScript for XML) extension is available. + If hasFeature(FEATURE_E4X) returns true, the XML syntax is available. +

    + By default hasFeature(int) returns true if + the current JS version is set to VERSION_DEFAULT + or is at least VERSION_1_6. +

    +

    +
    Since:
    +
    1.6 Release 1
    +
    See Also:
    Constant Field Values
    +
    +
    + +

    +FEATURE_DYNAMIC_SCOPE

    +
    +public static final int FEATURE_DYNAMIC_SCOPE
    +
    +
    Control if dynamic scope should be used for name access. + If hasFeature(FEATURE_DYNAMIC_SCOPE) returns true, then the name lookup + during name resolution will use the top scope of the script or function + which is at the top of JS execution stack instead of the top scope of the + script or function from the current stack frame if the top scope of + the top stack frame contains the top scope of the current stack frame + on its prototype chain. +

    + This is useful to define shared scope containing functions that can + be called from scripts and functions using private scopes. +

    + By default hasFeature(int) returns false. +

    +

    +
    Since:
    +
    1.6 Release 1
    +
    See Also:
    Constant Field Values
    +
    +
    + +

    +FEATURE_STRICT_VARS

    +
    +public static final int FEATURE_STRICT_VARS
    +
    +
    Control if strict variable mode is enabled. + When the feature is on Rhino reports runtime errors if assignment + to a global variable that does not exist is executed. When the feature + is off such assignments creates new variable in the global scope as + required by ECMA 262. +

    + By default hasFeature(int) returns false. +

    +

    +
    Since:
    +
    1.6 Release 1
    +
    See Also:
    Constant Field Values
    +
    +
    + +

    +FEATURE_STRICT_EVAL

    +
    +public static final int FEATURE_STRICT_EVAL
    +
    +
    Control if strict eval mode is enabled. + When the feature is on Rhino reports runtime errors if non-string + argument is passed to the eval function. When the feature is off + eval simply return non-string argument as is without performing any + evaluation as required by ECMA 262. +

    + By default hasFeature(int) returns false. +

    +

    +
    Since:
    +
    1.6 Release 1
    +
    See Also:
    Constant Field Values
    +
    +
    + +

    +FEATURE_LOCATION_INFORMATION_IN_ERROR

    +
    +public static final int FEATURE_LOCATION_INFORMATION_IN_ERROR
    +
    +
    When the feature is on Rhino will add a "fileName" and "lineNumber" + properties to Error objects automatically. When the feature is off, you + have to explicitly pass them as the second and third argument to the + Error constructor. Note that neither behaviour is fully ECMA 262 + compliant (as 262 doesn't specify a three-arg constructor), but keeping + the feature off results in Error objects that don't have + additional non-ECMA properties when constructed using the ECMA-defined + single-arg constructor and is thus desirable if a stricter ECMA + compliance is desired, specifically adherence to the point 15.11.5. of + the standard. +

    + By default hasFeature(int) returns false. +

    +

    +
    Since:
    +
    1.6 Release 6
    +
    See Also:
    Constant Field Values
    +
    +
    + +

    +FEATURE_STRICT_MODE

    +
    +public static final int FEATURE_STRICT_MODE
    +
    +
    Controls whether JS 1.5 'strict mode' is enabled. + When the feature is on, Rhino reports more than a dozen different + warnings. When the feature is off, these warnings are not generated. + FEATURE_STRICT_MODE implies FEATURE_STRICT_VARS and FEATURE_STRICT_EVAL. +

    + By default hasFeature(int) returns false. +

    +

    +
    Since:
    +
    1.6 Release 6
    +
    See Also:
    Constant Field Values
    +
    +
    + +

    +FEATURE_WARNING_AS_ERROR

    +
    +public static final int FEATURE_WARNING_AS_ERROR
    +
    +
    Controls whether a warning should be treated as an error. +

    +

    +
    Since:
    +
    1.6 Release 6
    +
    See Also:
    Constant Field Values
    +
    +
    + +

    +FEATURE_ENHANCED_JAVA_ACCESS

    +
    +public static final int FEATURE_ENHANCED_JAVA_ACCESS
    +
    +
    Enables enhanced access to Java. + Specifically, controls whether private and protected members can be + accessed, and whether scripts can catch all Java exceptions. +

    + Note that this feature should only be enabled for trusted scripts. +

    + By default hasFeature(int) returns false. +

    +

    +
    Since:
    +
    1.7 Release 1
    +
    See Also:
    Constant Field Values
    +
    +
    + +

    +languageVersionProperty

    +
    +public static final java.lang.String languageVersionProperty
    +
    +
    +
    See Also:
    Constant Field Values
    +
    +
    + +

    +errorReporterProperty

    +
    +public static final java.lang.String errorReporterProperty
    +
    +
    +
    See Also:
    Constant Field Values
    +
    +
    + +

    +emptyArgs

    +
    +public static final java.lang.Object[] emptyArgs
    +
    +
    Convenient value to use as zero-length array of objects. +

    +

    +
    +
    +
    + +

    +generateObserverCount

    +
    +public boolean generateObserverCount
    +
    +
    +
    +
    + + + + + + + + +
    +Constructor Detail
    + +

    +Context

    +
    +public Context()
    +
    +
    Deprecated. use ContextFactory.enter() or + ContextFactory.call(ContextAction) instead. +

    +

    Create a new Context. + + Note that the Context must be associated with a thread before + it can be used to execute a script. +

    +

    + + + + + + + + +
    +Method Detail
    + +

    +getCurrentContext

    +
    +public static Context getCurrentContext()
    +
    +
    Get the current Context. + + The current Context is per-thread; this method looks up + the Context associated with the current thread.

    +

    +

    + +
    Returns:
    the Context associated with the current thread, or + null if no context is associated with the current + thread.
    See Also:
    ContextFactory.enterContext(), +ContextFactory.call(ContextAction)
    +
    +
    +
    + +

    +enter

    +
    +public static Context enter()
    +
    +
    Deprecated. use ContextFactory.enter() or + ContextFactory.call(ContextAction) instead as this method relies + on usage of a static singleton "global" ContextFactory. +

    +

    Same as calling ContextFactory.enterContext() on the global + ContextFactory instance. +

    +

    + +
    Returns:
    a Context associated with the current thread
    See Also:
    getCurrentContext(), +exit(), +call(ContextAction)
    +
    +
    +
    + +

    +enter

    +
    +public static Context enter(Context cx)
    +
    +
    Deprecated. use ContextFactory.enterContext(Context) instead as + this method relies on usage of a static singleton "global" ContextFactory. +

    +

    Get a Context associated with the current thread, using + the given Context if need be. +

    + The same as enter() except that cx + is associated with the current thread and returned if + the current thread has no associated context and cx + is not associated with any other thread. +

    +

    +
    Parameters:
    cx - a Context to associate with the thread if possible +
    Returns:
    a Context associated with the current thread
    See Also:
    ContextFactory.enterContext(Context), +ContextFactory.call(ContextAction)
    +
    +
    +
    + +

    +exit

    +
    +public static void exit()
    +
    +
    Exit a block of code requiring a Context. + + Calling exit() will remove the association between + the current thread and a Context if the prior call to + ContextFactory.enterContext() on this thread newly associated a + Context with this thread. Once the current thread no longer has an + associated Context, it cannot be used to execute JavaScript until it is + again associated with a Context. +

    +

    +
    See Also:
    ContextFactory.enterContext()
    +
    +
    +
    + +

    +call

    +
    +public static java.lang.Object call(ContextAction action)
    +
    +
    Deprecated. use ContextFactory.call(ContextAction) instead as + this method relies on usage of a static singleton "global" + ContextFactory. +

    +

    Call ContextAction.run(Context cx) + using the Context instance associated with the current thread. + If no Context is associated with the thread, then + ContextFactory.getGlobal().makeContext() will be called to + construct new Context instance. The instance will be temporary + associated with the thread during call to + ContextAction.run(Context). +

    +

    + +
    Returns:
    The result of ContextAction.run(Context).
    +
    +
    +
    + +

    +call

    +
    +public static java.lang.Object call(ContextFactory factory,
    +                                    Callable callable,
    +                                    Scriptable scope,
    +                                    Scriptable thisObj,
    +                                    java.lang.Object[] args)
    +
    +
    Call Callable.call(Context cx, Scriptable scope, Scriptable thisObj, + Object[] args) + using the Context instance associated with the current thread. + If no Context is associated with the thread, then + ContextFactory.makeContext() will be called to construct + new Context instance. The instance will be temporary associated + with the thread during call to ContextAction.run(Context). +

    + It is allowed but not advisable to use null for factory + argument in which case the global static singleton ContextFactory + instance will be used to create new context instances. +

    +

    +
    See Also:
    ContextFactory.call(ContextAction)
    +
    +
    +
    + +

    +addContextListener

    +
    +public static void addContextListener(org.mozilla.javascript.ContextListener listener)
    +
    +
    Deprecated.  +

    +

    +
    See Also:
    ContextFactory.addListener(ContextFactory.Listener), +ContextFactory.getGlobal()
    +
    +
    +
    + +

    +removeContextListener

    +
    +public static void removeContextListener(org.mozilla.javascript.ContextListener listener)
    +
    +
    Deprecated.  +

    +

    +
    See Also:
    ContextFactory.removeListener(ContextFactory.Listener), +ContextFactory.getGlobal()
    +
    +
    +
    + +

    +getFactory

    +
    +public final ContextFactory getFactory()
    +
    +
    Return ContextFactory instance used to create this Context. +

    +

    +
    +
    +
    +
    + +

    +isSealed

    +
    +public final boolean isSealed()
    +
    +
    Checks if this is a sealed Context. A sealed Context instance does not + allow to modify any of its properties and will throw an exception + on any such attempt. +

    +

    +
    See Also:
    seal(Object sealKey)
    +
    +
    +
    + +

    +seal

    +
    +public final void seal(java.lang.Object sealKey)
    +
    +
    Seal this Context object so any attempt to modify any of its properties + including calling enter() and exit() methods will + throw an exception. +

    + If sealKey is not null, calling + unseal(Object sealKey) with the same key unseals + the object. If sealKey is null, unsealing is no longer possible. +

    +

    +
    See Also:
    isSealed(), +unseal(Object)
    +
    +
    +
    + +

    +unseal

    +
    +public final void unseal(java.lang.Object sealKey)
    +
    +
    Unseal previously sealed Context object. + The sealKey argument should not be null and should match + sealKey suplied with the last call to + seal(Object) or an exception will be thrown. +

    +

    +
    See Also:
    isSealed(), +seal(Object sealKey)
    +
    +
    +
    + +

    +getLanguageVersion

    +
    +public final int getLanguageVersion()
    +
    +
    Get the current language version. +

    + The language version number affects JavaScript semantics as detailed + in the overview documentation. +

    +

    + +
    Returns:
    an integer that is one of VERSION_1_0, VERSION_1_1, etc.
    +
    +
    +
    + +

    +setLanguageVersion

    +
    +public void setLanguageVersion(int version)
    +
    +
    Set the language version. + +

    + Setting the language version will affect functions and scripts compiled + subsequently. See the overview documentation for version-specific + behavior. +

    +

    +
    Parameters:
    version - the version as specified by VERSION_1_0, VERSION_1_1, etc.
    +
    +
    +
    + +

    +isValidLanguageVersion

    +
    +public static boolean isValidLanguageVersion(int version)
    +
    +
    +
    +
    +
    +
    + +

    +checkLanguageVersion

    +
    +public static void checkLanguageVersion(int version)
    +
    +
    +
    +
    +
    +
    + +

    +getImplementationVersion

    +
    +public final java.lang.String getImplementationVersion()
    +
    +
    Get the implementation version. + +

    + The implementation version is of the form +

    +    "name langVer release relNum date"
    + 
    + where name is the name of the product, langVer is + the language version, relNum is the release number, and + date is the release date for that specific + release in the form "yyyy mm dd". +

    +

    + +
    Returns:
    a string that encodes the product, language version, release + number, and date.
    +
    +
    +
    + +

    +getErrorReporter

    +
    +public final ErrorReporter getErrorReporter()
    +
    +
    Get the current error reporter. +

    +

    +
    See Also:
    ErrorReporter
    +
    +
    +
    + +

    +setErrorReporter

    +
    +public final ErrorReporter setErrorReporter(ErrorReporter reporter)
    +
    +
    Change the current error reporter. +

    +

    + +
    Returns:
    the previous error reporter
    See Also:
    ErrorReporter
    +
    +
    +
    + +

    +getLocale

    +
    +public final java.util.Locale getLocale()
    +
    +
    Get the current locale. Returns the default locale if none has + been set. +

    +

    +
    See Also:
    Locale
    +
    +
    +
    + +

    +setLocale

    +
    +public final java.util.Locale setLocale(java.util.Locale loc)
    +
    +
    Set the current locale. +

    +

    +
    See Also:
    Locale
    +
    +
    +
    + +

    +addPropertyChangeListener

    +
    +public final void addPropertyChangeListener(java.beans.PropertyChangeListener l)
    +
    +
    Register an object to receive notifications when a bound property + has changed +

    +

    +
    Parameters:
    l - the listener
    See Also:
    PropertyChangeEvent, +removePropertyChangeListener(java.beans.PropertyChangeListener)
    +
    +
    +
    + +

    +removePropertyChangeListener

    +
    +public final void removePropertyChangeListener(java.beans.PropertyChangeListener l)
    +
    +
    Remove an object from the list of objects registered to receive + notification of changes to a bounded property +

    +

    +
    Parameters:
    l - the listener
    See Also:
    PropertyChangeEvent, +addPropertyChangeListener(java.beans.PropertyChangeListener)
    +
    +
    +
    + +

    +reportWarning

    +
    +public static void reportWarning(java.lang.String message,
    +                                 java.lang.String sourceName,
    +                                 int lineno,
    +                                 java.lang.String lineSource,
    +                                 int lineOffset)
    +
    +
    Report a warning using the error reporter for the current thread. +

    +

    +
    Parameters:
    message - the warning message to report
    sourceName - a string describing the source, such as a filename
    lineno - the starting line number
    lineSource - the text of the line (may be null)
    lineOffset - the offset into lineSource where problem was detected
    See Also:
    ErrorReporter
    +
    +
    +
    + +

    +reportWarning

    +
    +public static void reportWarning(java.lang.String message)
    +
    +
    Report a warning using the error reporter for the current thread. +

    +

    +
    Parameters:
    message - the warning message to report
    See Also:
    ErrorReporter
    +
    +
    +
    + +

    +reportWarning

    +
    +public static void reportWarning(java.lang.String message,
    +                                 java.lang.Throwable t)
    +
    +
    +
    +
    +
    +
    + +

    +reportError

    +
    +public static void reportError(java.lang.String message,
    +                               java.lang.String sourceName,
    +                               int lineno,
    +                               java.lang.String lineSource,
    +                               int lineOffset)
    +
    +
    Report an error using the error reporter for the current thread. +

    +

    +
    Parameters:
    message - the error message to report
    sourceName - a string describing the source, such as a filename
    lineno - the starting line number
    lineSource - the text of the line (may be null)
    lineOffset - the offset into lineSource where problem was detected
    See Also:
    ErrorReporter
    +
    +
    +
    + +

    +reportError

    +
    +public static void reportError(java.lang.String message)
    +
    +
    Report an error using the error reporter for the current thread. +

    +

    +
    Parameters:
    message - the error message to report
    See Also:
    ErrorReporter
    +
    +
    +
    + +

    +reportRuntimeError

    +
    +public static EvaluatorException reportRuntimeError(java.lang.String message,
    +                                                    java.lang.String sourceName,
    +                                                    int lineno,
    +                                                    java.lang.String lineSource,
    +                                                    int lineOffset)
    +
    +
    Report a runtime error using the error reporter for the current thread. +

    +

    +
    Parameters:
    message - the error message to report
    sourceName - a string describing the source, such as a filename
    lineno - the starting line number
    lineSource - the text of the line (may be null)
    lineOffset - the offset into lineSource where problem was detected +
    Returns:
    a runtime exception that will be thrown to terminate the + execution of the script
    See Also:
    ErrorReporter
    +
    +
    +
    + +

    +reportRuntimeError

    +
    +public static EvaluatorException reportRuntimeError(java.lang.String message)
    +
    +
    Report a runtime error using the error reporter for the current thread. +

    +

    +
    Parameters:
    message - the error message to report
    See Also:
    ErrorReporter
    +
    +
    +
    + +

    +initStandardObjects

    +
    +public final ScriptableObject initStandardObjects()
    +
    +
    Initialize the standard objects. + + Creates instances of the standard objects and their constructors + (Object, String, Number, Date, etc.), setting up 'scope' to act + as a global object as in ECMA 15.1.

    + + This method must be called to initialize a scope before scripts + can be evaluated in that scope.

    + + This method does not affect the Context it is called upon. +

    +

    + +
    Returns:
    the initialized scope
    +
    +
    +
    + +

    +initStandardObjects

    +
    +public final Scriptable initStandardObjects(ScriptableObject scope)
    +
    +
    Initialize the standard objects. + + Creates instances of the standard objects and their constructors + (Object, String, Number, Date, etc.), setting up 'scope' to act + as a global object as in ECMA 15.1.

    + + This method must be called to initialize a scope before scripts + can be evaluated in that scope.

    + + This method does not affect the Context it is called upon. +

    +

    +
    Parameters:
    scope - the scope to initialize, or null, in which case a new + object will be created to serve as the scope +
    Returns:
    the initialized scope. The method returns the value of the scope + argument if it is not null or newly allocated scope object which + is an instance ScriptableObject.
    +
    +
    +
    + +

    +initStandardObjects

    +
    +public ScriptableObject initStandardObjects(ScriptableObject scope,
    +                                            boolean sealed)
    +
    +
    Initialize the standard objects. + + Creates instances of the standard objects and their constructors + (Object, String, Number, Date, etc.), setting up 'scope' to act + as a global object as in ECMA 15.1.

    + + This method must be called to initialize a scope before scripts + can be evaluated in that scope.

    + + This method does not affect the Context it is called upon.

    + + This form of the method also allows for creating "sealed" standard + objects. An object that is sealed cannot have properties added, changed, + or removed. This is useful to create a "superglobal" that can be shared + among several top-level objects. Note that sealing is not allowed in + the current ECMA/ISO language specification, but is likely for + the next version. +

    +

    +
    Parameters:
    scope - the scope to initialize, or null, in which case a new + object will be created to serve as the scope
    sealed - whether or not to create sealed standard objects that + cannot be modified. +
    Returns:
    the initialized scope. The method returns the value of the scope + argument if it is not null or newly allocated scope object.
    Since:
    +
    1.4R3
    +
    +
    +
    +
    + +

    +getUndefinedValue

    +
    +public static java.lang.Object getUndefinedValue()
    +
    +
    Get the singleton object that represents the JavaScript Undefined value. +

    +

    +
    +
    +
    +
    + +

    +evaluateString

    +
    +public final java.lang.Object evaluateString(Scriptable scope,
    +                                             java.lang.String source,
    +                                             java.lang.String sourceName,
    +                                             int lineno,
    +                                             java.lang.Object securityDomain)
    +
    +
    Evaluate a JavaScript source string. + + The provided source name and line number are used for error messages + and for producing debug information. +

    +

    +
    Parameters:
    scope - the scope to execute in
    source - the JavaScript source
    sourceName - a string describing the source, such as a filename
    lineno - the starting line number
    securityDomain - an arbitrary object that specifies security + information about the origin or owner of the script. For + implementations that don't care about security, this value + may be null. +
    Returns:
    the result of evaluating the string
    See Also:
    SecurityController
    +
    +
    +
    + +

    +evaluateReader

    +
    +public final java.lang.Object evaluateReader(Scriptable scope,
    +                                             java.io.Reader in,
    +                                             java.lang.String sourceName,
    +                                             int lineno,
    +                                             java.lang.Object securityDomain)
    +                                      throws java.io.IOException
    +
    +
    Evaluate a reader as JavaScript source. + + All characters of the reader are consumed. +

    +

    +
    Parameters:
    scope - the scope to execute in
    in - the Reader to get JavaScript source from
    sourceName - a string describing the source, such as a filename
    lineno - the starting line number
    securityDomain - an arbitrary object that specifies security + information about the origin or owner of the script. For + implementations that don't care about security, this value + may be null. +
    Returns:
    the result of evaluating the source +
    Throws: +
    java.io.IOException - if an IOException was generated by the Reader
    +
    +
    +
    + +

    +stringIsCompilableUnit

    +
    +public final boolean stringIsCompilableUnit(java.lang.String source)
    +
    +
    Check whether a string is ready to be compiled. +

    + stringIsCompilableUnit is intended to support interactive compilation of + javascript. If compiling the string would result in an error + that might be fixed by appending more source, this method + returns false. In every other case, it returns true. +

    + Interactive shells may accumulate source lines, using this + method after each new line is appended to check whether the + statement being entered is complete. +

    +

    +
    Parameters:
    source - the source buffer to check +
    Returns:
    whether the source is ready for compilation
    Since:
    +
    1.4 Release 2
    +
    +
    +
    +
    + +

    +compileReader

    +
    +public final Script compileReader(Scriptable scope,
    +                                  java.io.Reader in,
    +                                  java.lang.String sourceName,
    +                                  int lineno,
    +                                  java.lang.Object securityDomain)
    +                           throws java.io.IOException
    +
    +
    Deprecated.  +

    +

    + +
    Throws: +
    java.io.IOException
    See Also:
    compileReader(Reader in, String sourceName, int lineno, + Object securityDomain)
    +
    +
    +
    + +

    +compileReader

    +
    +public final Script compileReader(java.io.Reader in,
    +                                  java.lang.String sourceName,
    +                                  int lineno,
    +                                  java.lang.Object securityDomain)
    +                           throws java.io.IOException
    +
    +
    Compiles the source in the given reader. +

    + Returns a script that may later be executed. + Will consume all the source in the reader. +

    +

    +
    Parameters:
    in - the input reader
    sourceName - a string describing the source, such as a filename
    lineno - the starting line number for reporting errors
    securityDomain - an arbitrary object that specifies security + information about the origin or owner of the script. For + implementations that don't care about security, this value + may be null. +
    Returns:
    a script that may later be executed +
    Throws: +
    java.io.IOException - if an IOException was generated by the Reader
    See Also:
    Script
    +
    +
    +
    + +

    +compileString

    +
    +public final Script compileString(java.lang.String source,
    +                                  java.lang.String sourceName,
    +                                  int lineno,
    +                                  java.lang.Object securityDomain)
    +
    +
    Compiles the source in the given string. +

    + Returns a script that may later be executed. +

    +

    +
    Parameters:
    source - the source string
    sourceName - a string describing the source, such as a filename
    lineno - the starting line number for reporting errors
    securityDomain - an arbitrary object that specifies security + information about the origin or owner of the script. For + implementations that don't care about security, this value + may be null. +
    Returns:
    a script that may later be executed
    See Also:
    Script
    +
    +
    +
    + +

    +compileFunction

    +
    +public final Function compileFunction(Scriptable scope,
    +                                      java.lang.String source,
    +                                      java.lang.String sourceName,
    +                                      int lineno,
    +                                      java.lang.Object securityDomain)
    +
    +
    Compile a JavaScript function. +

    + The function source must be a function definition as defined by + ECMA (e.g., "function f(a) { return a; }"). +

    +

    +
    Parameters:
    scope - the scope to compile relative to
    source - the function definition source
    sourceName - a string describing the source, such as a filename
    lineno - the starting line number
    securityDomain - an arbitrary object that specifies security + information about the origin or owner of the script. For + implementations that don't care about security, this value + may be null. +
    Returns:
    a Function that may later be called
    See Also:
    Function
    +
    +
    +
    + +

    +decompileScript

    +
    +public final java.lang.String decompileScript(Script script,
    +                                              int indent)
    +
    +
    Decompile the script. +

    + The canonical source of the script is returned. +

    +

    +
    Parameters:
    script - the script to decompile
    indent - the number of spaces to indent the result +
    Returns:
    a string representing the script source
    +
    +
    +
    + +

    +decompileFunction

    +
    +public final java.lang.String decompileFunction(Function fun,
    +                                                int indent)
    +
    +
    Decompile a JavaScript Function. +

    + Decompiles a previously compiled JavaScript function object to + canonical source. +

    + Returns function body of '[native code]' if no decompilation + information is available. +

    +

    +
    Parameters:
    fun - the JavaScript function to decompile
    indent - the number of spaces to indent the result +
    Returns:
    a string representing the function source
    +
    +
    +
    + +

    +decompileFunctionBody

    +
    +public final java.lang.String decompileFunctionBody(Function fun,
    +                                                    int indent)
    +
    +
    Decompile the body of a JavaScript Function. +

    + Decompiles the body a previously compiled JavaScript Function + object to canonical source, omitting the function header and + trailing brace. + + Returns '[native code]' if no decompilation information is available. +

    +

    +
    Parameters:
    fun - the JavaScript function to decompile
    indent - the number of spaces to indent the result +
    Returns:
    a string representing the function body source.
    +
    +
    +
    + +

    +newObject

    +
    +public final Scriptable newObject(Scriptable scope)
    +
    +
    Create a new JavaScript object. + + Equivalent to evaluating "new Object()". +

    +

    +
    Parameters:
    scope - the scope to search for the constructor and to evaluate + against +
    Returns:
    the new object
    +
    +
    +
    + +

    +newObject

    +
    +public final Scriptable newObject(Scriptable scope,
    +                                  java.lang.String constructorName)
    +
    +
    Create a new JavaScript object by executing the named constructor. + + The call newObject(scope, "Foo") is equivalent to + evaluating "new Foo()". +

    +

    +
    Parameters:
    scope - the scope to search for the constructor and to evaluate against
    constructorName - the name of the constructor to call +
    Returns:
    the new object
    +
    +
    +
    + +

    +newObject

    +
    +public final Scriptable newObject(Scriptable scope,
    +                                  java.lang.String constructorName,
    +                                  java.lang.Object[] args)
    +
    +
    Creates a new JavaScript object by executing the named constructor. + + Searches scope for the named constructor, calls it with + the given arguments, and returns the result.

    + + The code +

    + Object[] args = { "a", "b" };
    + newObject(scope, "Foo", args)
    + is equivalent to evaluating "new Foo('a', 'b')", assuming that the Foo + constructor has been defined in scope. +

    +

    +
    Parameters:
    scope - The scope to search for the constructor and to evaluate + against
    constructorName - the name of the constructor to call
    args - the array of arguments for the constructor +
    Returns:
    the new object
    +
    +
    +
    + +

    +newArray

    +
    +public final Scriptable newArray(Scriptable scope,
    +                                 int length)
    +
    +
    Create an array with a specified initial length. +

    +

    +

    +
    Parameters:
    scope - the scope to create the object in
    length - the initial length (JavaScript arrays may have + additional properties added dynamically). +
    Returns:
    the new array object
    +
    +
    +
    + +

    +newArray

    +
    +public final Scriptable newArray(Scriptable scope,
    +                                 java.lang.Object[] elements)
    +
    +
    Create an array with a set of initial elements. +

    +

    +
    Parameters:
    scope - the scope to create the object in.
    elements - the initial elements. Each object in this array + must be an acceptable JavaScript type and type + of array should be exactly Object[], not + SomeObjectSubclass[]. +
    Returns:
    the new array object.
    +
    +
    +
    + +

    +getElements

    +
    +public final java.lang.Object[] getElements(Scriptable object)
    +
    +
    Get the elements of a JavaScript array. +

    + If the object defines a length property convertible to double number, + then the number is converted Uint32 value as defined in Ecma 9.6 + and Java array of that size is allocated. + The array is initialized with the values obtained by + calling get() on object for each value of i in [0,length-1]. If + there is not a defined value for a property the Undefined value + is used to initialize the corresponding element in the array. The + Java array is then returned. + If the object doesn't define a length property or it is not a number, + empty array is returned. +

    +

    +
    Parameters:
    object - the JavaScript array or array-like object +
    Returns:
    a Java array of objects
    Since:
    +
    1.4 release 2
    +
    +
    +
    +
    + +

    +toBoolean

    +
    +public static boolean toBoolean(java.lang.Object value)
    +
    +
    Convert the value to a JavaScript boolean value. +

    + See ECMA 9.2. +

    +

    +
    Parameters:
    value - a JavaScript value +
    Returns:
    the corresponding boolean value converted using + the ECMA rules
    +
    +
    +
    + +

    +toNumber

    +
    +public static double toNumber(java.lang.Object value)
    +
    +
    Convert the value to a JavaScript Number value. +

    + Returns a Java double for the JavaScript Number. +

    + See ECMA 9.3. +

    +

    +
    Parameters:
    value - a JavaScript value +
    Returns:
    the corresponding double value converted using + the ECMA rules
    +
    +
    +
    + +

    +toString

    +
    +public static java.lang.String toString(java.lang.Object value)
    +
    +
    Convert the value to a JavaScript String value. +

    + See ECMA 9.8. +

    +

    +

    +
    Parameters:
    value - a JavaScript value +
    Returns:
    the corresponding String value converted using + the ECMA rules
    +
    +
    +
    + +

    +toObject

    +
    +public static Scriptable toObject(java.lang.Object value,
    +                                  Scriptable scope)
    +
    +
    Convert the value to an JavaScript object value. +

    + Note that a scope must be provided to look up the constructors + for Number, Boolean, and String. +

    + See ECMA 9.9. +

    + Additionally, arbitrary Java objects and classes will be + wrapped in a Scriptable object with its Java fields and methods + reflected as JavaScript properties of the object. +

    +

    +
    Parameters:
    value - any Java object
    scope - global scope containing constructors for Number, + Boolean, and String +
    Returns:
    new JavaScript object
    +
    +
    +
    + +

    +toObject

    +
    +public static Scriptable toObject(java.lang.Object value,
    +                                  Scriptable scope,
    +                                  java.lang.Class staticType)
    +
    +
    Deprecated.  +

    +

    +
    See Also:
    toObject(Object, Scriptable)
    +
    +
    +
    + +

    +javaToJS

    +
    +public static java.lang.Object javaToJS(java.lang.Object value,
    +                                        Scriptable scope)
    +
    +
    Convenient method to convert java value to its closest representation + in JavaScript. +

    + If value is an instance of String, Number, Boolean, Function or + Scriptable, it is returned as it and will be treated as the corresponding + JavaScript type of string, number, boolean, function and object. +

    + Note that for Number instances during any arithmetic operation in + JavaScript the engine will always use the result of + Number.doubleValue() resulting in a precision loss if + the number can not fit into double. +

    + If value is an instance of Character, it will be converted to string of + length 1 and its JavaScript type will be string. +

    + The rest of values will be wrapped as LiveConnect objects + by calling WrapFactory.wrap(Context cx, Scriptable scope, + Object obj, Class staticType) as in: +

    +    Context cx = Context.getCurrentContext();
    +    return cx.getWrapFactory().wrap(cx, scope, value, null);
    + 
    +

    +

    +
    Parameters:
    value - any Java object
    scope - top scope object +
    Returns:
    value suitable to pass to any API that takes JavaScript values.
    +
    +
    +
    + +

    +jsToJava

    +
    +public static java.lang.Object jsToJava(java.lang.Object value,
    +                                        java.lang.Class desiredType)
    +                                 throws EvaluatorException
    +
    +
    Convert a JavaScript value into the desired type. + Uses the semantics defined with LiveConnect3 and throws an + Illegal argument exception if the conversion cannot be performed. +

    +

    +
    Parameters:
    value - the JavaScript value to convert
    desiredType - the Java type to convert to. Primitive Java + types are represented using the TYPE fields in the corresponding + wrapper class in java.lang. +
    Returns:
    the converted value +
    Throws: +
    EvaluatorException - if the conversion cannot be performed
    +
    +
    +
    + +

    +toType

    +
    +public static java.lang.Object toType(java.lang.Object value,
    +                                      java.lang.Class desiredType)
    +                               throws java.lang.IllegalArgumentException
    +
    +
    Deprecated.  +

    +

    + +
    Throws: +
    java.lang.IllegalArgumentException - if the conversion cannot be performed. + Note that jsToJava(Object, Class) throws + EvaluatorException instead.
    See Also:
    jsToJava(Object, Class)
    +
    +
    +
    + +

    +throwAsScriptRuntimeEx

    +
    +public static java.lang.RuntimeException throwAsScriptRuntimeEx(java.lang.Throwable e)
    +
    +
    Rethrow the exception wrapping it as the script runtime exception. + Unless the exception is instance of EcmaError or + EvaluatorException it will be wrapped as + WrappedException, a subclass of EvaluatorException. + The resulting exception object always contains + source name and line number of script that triggered exception. +

    + This method always throws an exception, its return value is provided + only for convenience to allow a usage like: +

    + throw Context.throwAsScriptRuntimeEx(ex);
    + 
    + to indicate that code after the method is unreachable. +

    +

    + +
    Throws: +
    EvaluatorException +
    EcmaError
    +
    +
    +
    + +

    +isGeneratingDebug

    +
    +public final boolean isGeneratingDebug()
    +
    +
    Tell whether debug information is being generated. +

    +

    +
    Since:
    +
    1.3
    +
    +
    +
    +
    + +

    +setGeneratingDebug

    +
    +public final void setGeneratingDebug(boolean generatingDebug)
    +
    +
    Specify whether or not debug information should be generated. +

    + Setting the generation of debug information on will set the + optimization level to zero. +

    +

    +
    Since:
    +
    1.3
    +
    +
    +
    +
    + +

    +isGeneratingSource

    +
    +public final boolean isGeneratingSource()
    +
    +
    Tell whether source information is being generated. +

    +

    +
    Since:
    +
    1.3
    +
    +
    +
    +
    + +

    +setGeneratingSource

    +
    +public final void setGeneratingSource(boolean generatingSource)
    +
    +
    Specify whether or not source information should be generated. +

    + Without source information, evaluating the "toString" method + on JavaScript functions produces only "[native code]" for + the body of the function. + Note that code generated without source is not fully ECMA + conformant. +

    +

    +
    Since:
    +
    1.3
    +
    +
    +
    +
    + +

    +getOptimizationLevel

    +
    +public final int getOptimizationLevel()
    +
    +
    Get the current optimization level. +

    + The optimization level is expressed as an integer between -1 and + 9. +

    +

    +
    Since:
    +
    1.3
    +
    +
    +
    +
    + +

    +setOptimizationLevel

    +
    +public final void setOptimizationLevel(int optimizationLevel)
    +
    +
    Set the current optimization level. +

    + The optimization level is expected to be an integer between -1 and + 9. Any negative values will be interpreted as -1, and any values + greater than 9 will be interpreted as 9. + An optimization level of -1 indicates that interpretive mode will + always be used. Levels 0 through 9 indicate that class files may + be generated. Higher optimization levels trade off compile time + performance for runtime performance. + The optimizer level can't be set greater than -1 if the optimizer + package doesn't exist at run time. +

    +

    +
    Parameters:
    optimizationLevel - an integer indicating the level of + optimization to perform
    Since:
    +
    1.3
    +
    +
    +
    +
    + +

    +isValidOptimizationLevel

    +
    +public static boolean isValidOptimizationLevel(int optimizationLevel)
    +
    +
    +
    +
    +
    +
    + +

    +checkOptimizationLevel

    +
    +public static void checkOptimizationLevel(int optimizationLevel)
    +
    +
    +
    +
    +
    +
    + +

    +getMaximumInterpreterStackDepth

    +
    +public final int getMaximumInterpreterStackDepth()
    +
    +
    Returns the maximum stack depth (in terms of number of call frames) + allowed in a single invocation of interpreter. If the set depth would be + exceeded, the interpreter will throw an EvaluatorException in the script. + Defaults to Integer.MAX_VALUE. The setting only has effect for + interpreted functions (those compiled with optimization level set to -1). + As the interpreter doesn't use the Java stack but rather manages its own + stack in the heap memory, a runaway recursion in interpreted code would + eventually consume all available memory and cause OutOfMemoryError + instead of a StackOverflowError limited to only a single thread. This + setting helps prevent such situations. +

    +

    + +
    Returns:
    The current maximum interpreter stack depth.
    +
    +
    +
    + +

    +setMaximumInterpreterStackDepth

    +
    +public final void setMaximumInterpreterStackDepth(int max)
    +
    +
    Sets the maximum stack depth (in terms of number of call frames) + allowed in a single invocation of interpreter. If the set depth would be + exceeded, the interpreter will throw an EvaluatorException in the script. + Defaults to Integer.MAX_VALUE. The setting only has effect for + interpreted functions (those compiled with optimization level set to -1). + As the interpreter doesn't use the Java stack but rather manages its own + stack in the heap memory, a runaway recursion in interpreted code would + eventually consume all available memory and cause OutOfMemoryError + instead of a StackOverflowError limited to only a single thread. This + setting helps prevent such situations. +

    +

    +
    Parameters:
    max - the new maximum interpreter stack depth +
    Throws: +
    java.lang.IllegalStateException - if this context's optimization level is not + -1 +
    java.lang.IllegalArgumentException - if the new depth is not at least 1
    +
    +
    +
    + +

    +setSecurityController

    +
    +public final void setSecurityController(SecurityController controller)
    +
    +
    Set the security controller for this context. +

    SecurityController may only be set if it is currently null + and SecurityController.hasGlobal() is false. + Otherwise a SecurityException is thrown. +

    +

    +
    Parameters:
    controller - a SecurityController object +
    Throws: +
    java.lang.SecurityException - if there is already a SecurityController + object for this Context or globally installed.
    See Also:
    SecurityController.initGlobal(SecurityController controller), +SecurityController.hasGlobal()
    +
    +
    +
    + +

    +setClassShutter

    +
    +public final void setClassShutter(ClassShutter shutter)
    +
    +
    Set the LiveConnect access filter for this context. +

    ClassShutter may only be set if it is currently null. + Otherwise a SecurityException is thrown. +

    +

    +
    Parameters:
    shutter - a ClassShutter object +
    Throws: +
    java.lang.SecurityException - if there is already a ClassShutter + object for this Context
    +
    +
    +
    + +

    +getThreadLocal

    +
    +public final java.lang.Object getThreadLocal(java.lang.Object key)
    +
    +
    Get a value corresponding to a key. +

    + Since the Context is associated with a thread it can be + used to maintain values that can be later retrieved using + the current thread. +

    + Note that the values are maintained with the Context, so + if the Context is disassociated from the thread the values + cannot be retrieved. Also, if private data is to be maintained + in this manner the key should be a java.lang.Object + whose reference is not divulged to untrusted code. +

    +

    +
    Parameters:
    key - the key used to lookup the value +
    Returns:
    a value previously stored using putThreadLocal.
    +
    +
    +
    + +

    +putThreadLocal

    +
    +public final void putThreadLocal(java.lang.Object key,
    +                                 java.lang.Object value)
    +
    +
    Put a value that can later be retrieved using a given key. +

    +

    +

    +
    Parameters:
    key - the key used to index the value
    value - the value to save
    +
    +
    +
    + +

    +removeThreadLocal

    +
    +public final void removeThreadLocal(java.lang.Object key)
    +
    +
    Remove values from thread-local storage. +

    +

    +
    Parameters:
    key - the key for the entry to remove.
    Since:
    +
    1.5 release 2
    +
    +
    +
    +
    + +

    +hasCompileFunctionsWithDynamicScope

    +
    +public final boolean hasCompileFunctionsWithDynamicScope()
    +
    +
    Deprecated.  +

    +

    +
    See Also:
    FEATURE_DYNAMIC_SCOPE, +hasFeature(int)
    +
    +
    +
    + +

    +setCompileFunctionsWithDynamicScope

    +
    +public final void setCompileFunctionsWithDynamicScope(boolean flag)
    +
    +
    Deprecated.  +

    +

    +
    See Also:
    FEATURE_DYNAMIC_SCOPE, +hasFeature(int)
    +
    +
    +
    + +

    +setCachingEnabled

    +
    +public static void setCachingEnabled(boolean cachingEnabled)
    +
    +
    Deprecated.  +

    +

    +
    See Also:
    ClassCache.get(Scriptable), +ClassCache.setCachingEnabled(boolean)
    +
    +
    +
    + +

    +setWrapFactory

    +
    +public final void setWrapFactory(WrapFactory wrapFactory)
    +
    +
    Set a WrapFactory for this Context. +

    + The WrapFactory allows custom object wrapping behavior for + Java object manipulated with JavaScript. +

    +

    +
    Since:
    +
    1.5 Release 4
    +
    See Also:
    WrapFactory
    +
    +
    +
    + +

    +getWrapFactory

    +
    +public final WrapFactory getWrapFactory()
    +
    +
    Return the current WrapFactory, or null if none is defined. +

    +

    +
    Since:
    +
    1.5 Release 4
    +
    See Also:
    WrapFactory
    +
    +
    +
    + +

    +getDebugger

    +
    +public final org.mozilla.javascript.debug.Debugger getDebugger()
    +
    +
    Return the current debugger. +

    +

    + +
    Returns:
    the debugger, or null if none is attached.
    +
    +
    +
    + +

    +getDebuggerContextData

    +
    +public final java.lang.Object getDebuggerContextData()
    +
    +
    Return the debugger context data associated with current context. +

    +

    + +
    Returns:
    the debugger data, or null if debugger is not attached
    +
    +
    +
    + +

    +setDebugger

    +
    +public final void setDebugger(org.mozilla.javascript.debug.Debugger debugger,
    +                              java.lang.Object contextData)
    +
    +
    Set the associated debugger. +

    +

    +
    Parameters:
    debugger - the debugger to be used on callbacks from + the engine.
    contextData - arbitrary object that debugger can use to store + per Context data.
    +
    +
    +
    + +

    +getDebuggableView

    +
    +public static DebuggableScript getDebuggableView(Script script)
    +
    +
    Return DebuggableScript instance if any associated with the script. + If callable supports DebuggableScript implementation, the method + returns it. Otherwise null is returned. +

    +

    +
    +
    +
    +
    + +

    +hasFeature

    +
    +public boolean hasFeature(int featureIndex)
    +
    +
    Controls certain aspects of script semantics. + Should be overwritten to alter default behavior. +

    + The default implementation calls + ContextFactory.hasFeature(Context cx, int featureIndex) + that allows to customize Context behavior without introducing + Context subclasses. ContextFactory documentation gives + an example of hasFeature implementation. +

    +

    +
    Parameters:
    featureIndex - feature index to check +
    Returns:
    true if the featureIndex feature is turned on
    See Also:
    FEATURE_NON_ECMA_GET_YEAR, +FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME, +FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER, +FEATURE_TO_STRING_AS_SOURCE, +FEATURE_PARENT_PROTO_PROPRTIES, +FEATURE_E4X, +FEATURE_DYNAMIC_SCOPE, +FEATURE_STRICT_VARS, +FEATURE_STRICT_EVAL, +FEATURE_LOCATION_INFORMATION_IN_ERROR, +FEATURE_STRICT_MODE, +FEATURE_WARNING_AS_ERROR, +FEATURE_ENHANCED_JAVA_ACCESS
    +
    +
    +
    + +

    +getE4xImplementationFactory

    +
    +public org.mozilla.javascript.xml.XMLLib.Factory getE4xImplementationFactory()
    +
    +
    Returns an object which specifies an E4X implementation to use within + this Context. Note + that the XMLLib.Factory interface should be considered experimental. + + The default implementation uses the implementation provided by this + Context's ContextFactory. +

    +

    + +
    Returns:
    An XMLLib.Factory. Should not return null if + FEATURE_E4X is enabled. See hasFeature(int).
    +
    +
    +
    + +

    +getInstructionObserverThreshold

    +
    +public final int getInstructionObserverThreshold()
    +
    +
    Get threshold of executed instructions counter that triggers call to + observeInstructionCount(). + When the threshold is zero, instruction counting is disabled, + otherwise each time the run-time executes at least the threshold value + of script instructions, observeInstructionCount() will + be called. +

    +

    +
    +
    +
    +
    + +

    +setInstructionObserverThreshold

    +
    +public final void setInstructionObserverThreshold(int threshold)
    +
    +
    Set threshold of executed instructions counter that triggers call to + observeInstructionCount(). + When the threshold is zero, instruction counting is disabled, + otherwise each time the run-time executes at least the threshold value + of script instructions, observeInstructionCount() will + be called.

    + Note that the meaning of "instruction" is not guaranteed to be + consistent between compiled and interpretive modes: executing a given + script or function in the different modes will result in different + instruction counts against the threshold. + setGenerateObserverCount(boolean) is called with true if + threshold is greater than zero, false otherwise. +

    +

    +
    Parameters:
    threshold - The instruction threshold
    +
    +
    +
    + +

    +setGenerateObserverCount

    +
    +public void setGenerateObserverCount(boolean generateObserverCount)
    +
    +
    Turn on or off generation of code with callbacks to + track the count of executed instructions. + Currently only affects JVM byte code generation: this slows down the + generated code, but code generated without the callbacks will not + be counted toward instruction thresholds. Rhino's interpretive + mode does instruction counting without inserting callbacks, so + there is no requirement to compile code differently. +

    +

    +
    Parameters:
    generateObserverCount - if true, generated code will contain + calls to accumulate an estimate of the instructions executed.
    +
    +
    +
    + +

    +observeInstructionCount

    +
    +protected void observeInstructionCount(int instructionCount)
    +
    +
    Allow application to monitor counter of executed script instructions + in Context subclasses. + Run-time calls this when instruction counting is enabled and the counter + reaches limit set by setInstructionObserverThreshold(). + The method is useful to observe long running scripts and if necessary + to terminate them. +

    + The instruction counting support is available only for interpreted + scripts generated when the optimization level is set to -1. +

    + The default implementation calls + ContextFactory.observeInstructionCount(Context cx, + int instructionCount) + that allows to customize Context behavior without introducing + Context subclasses. +

    +

    +
    Parameters:
    instructionCount - amount of script instruction executed since + last call to observeInstructionCount +
    Throws: +
    java.lang.Error - to terminate the script
    See Also:
    setOptimizationLevel(int)
    +
    +
    +
    + +

    +createClassLoader

    +
    +public GeneratedClassLoader createClassLoader(java.lang.ClassLoader parent)
    +
    +
    Create class loader for generated classes. + The method calls ContextFactory.createClassLoader(ClassLoader) + using the result of getFactory(). +

    +

    +
    +
    +
    +
    + +

    +getApplicationClassLoader

    +
    +public final java.lang.ClassLoader getApplicationClassLoader()
    +
    +
    +
    +
    +
    +
    + +

    +setApplicationClassLoader

    +
    +public final void setApplicationClassLoader(java.lang.ClassLoader loader)
    +
    +
    +
    +
    +
    +
    + +

    +isGeneratingDebugChanged

    +
    +public final boolean isGeneratingDebugChanged()
    +
    +
    +
    +
    +
    +
    + +

    +addActivationName

    +
    +public void addActivationName(java.lang.String name)
    +
    +
    Add a name to the list of names forcing the creation of real + activation objects for functions. +

    +

    +
    Parameters:
    name - the name of the object to add to the list
    +
    +
    +
    + +

    +isActivationNeeded

    +
    +public final boolean isActivationNeeded(java.lang.String name)
    +
    +
    Check whether the name is in the list of names of objects + forcing the creation of activation objects. +

    +

    +
    Parameters:
    name - the name of the object to test +
    Returns:
    true if an function activation object is needed.
    +
    +
    +
    + +

    +removeActivationName

    +
    +public void removeActivationName(java.lang.String name)
    +
    +
    Remove a name from the list of names forcing the creation of real + activation objects for functions. +

    +

    +
    Parameters:
    name - the name of the object to remove from the list
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/ContextAction.html b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/ContextAction.html new file mode 100644 index 0000000..8e7116c --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/ContextAction.html @@ -0,0 +1,214 @@ + + + + + + +ContextAction (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + +

    + +org.mozilla.javascript +
    +Interface ContextAction

    +
    +
    +
    public interface ContextAction
    + + +

    +Interface to represent arbitrary action that requires to have Context + object associated with the current thread for its execution. +

    + +

    +


    + +

    + + + + + + + + + + + + +
    +Method Summary
    + java.lang.Objectrun(Context cx) + +
    +          Execute action using the supplied Context instance.
    +  +

    + + + + + + + + +
    +Method Detail
    + +

    +run

    +
    +java.lang.Object run(Context cx)
    +
    +
    Execute action using the supplied Context instance. + When Rhino runtime calls the method, cx will be associated + with the current thread as active context. +

    +

    +
    See Also:
    Context.call(ContextAction), +ContextFactory.call(ContextAction)
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/ContextFactory.Listener.html b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/ContextFactory.Listener.html new file mode 100644 index 0000000..6290c8c --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/ContextFactory.Listener.html @@ -0,0 +1,236 @@ + + + + + + +ContextFactory.Listener (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + +

    + +org.mozilla.javascript +
    +Interface ContextFactory.Listener

    +
    +
    Enclosing class:
    ContextFactory
    +
    +
    +
    +
    public static interface ContextFactory.Listener
    + + +

    +Listener of Context creation and release events. +

    + +

    +


    + +

    + + + + + + + + + + + + + + + + +
    +Method Summary
    + voidcontextCreated(Context cx) + +
    +          Notify about newly created Context object.
    + voidcontextReleased(Context cx) + +
    +          Notify that the specified Context instance is no longer + associated with the current thread.
    +  +

    + + + + + + + + +
    +Method Detail
    + +

    +contextCreated

    +
    +void contextCreated(Context cx)
    +
    +
    Notify about newly created Context object. +

    +

    +
    +
    +
    +
    + +

    +contextReleased

    +
    +void contextReleased(Context cx)
    +
    +
    Notify that the specified Context instance is no longer + associated with the current thread. +

    +

    +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/ContextFactory.html b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/ContextFactory.html new file mode 100644 index 0000000..ef61a6f --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/ContextFactory.html @@ -0,0 +1,935 @@ + + + + + + +ContextFactory (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + +

    + +org.mozilla.javascript +
    +Class ContextFactory

    +
    +java.lang.Object
    +  extended by org.mozilla.javascript.ContextFactory
    +
    +
    +
    +
    public class ContextFactory
    extends java.lang.Object
    + + +

    +Factory class that Rhino runtime uses to create new Context + instances. A ContextFactory can also notify listeners + about context creation and release. +

    + When the Rhino runtime needs to create new Context instance during + execution of Context.enter() or Context, it will call + makeContext() of the current global ContextFactory. + See getGlobal() and initGlobal(ContextFactory). +

    + It is also possible to use explicit ContextFactory instances for Context + creation. This is useful to have a set of independent Rhino runtime + instances under single JVM. See call(ContextAction). +

    + The following example demonstrates Context customization to terminate + scripts running more then 10 seconds and to provide better compatibility + with JavaScript code using MSIE-specific features. +

    + import org.mozilla.javascript.*;
    +
    + class MyFactory extends ContextFactory
    + {
    +
    +     // Custom Context to store execution time.
    +     private static class MyContext extends Context
    +     {
    +         long startTime;
    +     }
    +
    +     static {
    +         // Initialize GlobalFactory with custom factory
    +         ContextFactory.initGlobal(new MyFactory());
    +     }
    +
    +     // Override makeContext()
    +     protected Context makeContext()
    +     {
    +         MyContext cx = new MyContext();
    +         // Use pure interpreter mode to allow for
    +         // observeInstructionCount(Context, int) to work
    +         cx.setOptimizationLevel(-1);
    +         // Make Rhino runtime to call observeInstructionCount
    +         // each 10000 bytecode instructions
    +         cx.setInstructionObserverThreshold(10000);
    +         return cx;
    +     }
    +
    +     // Override hasFeature(Context, int)
    +     public boolean hasFeature(Context cx, int featureIndex)
    +     {
    +         // Turn on maximum compatibility with MSIE scripts
    +         switch (featureIndex) {
    +             case Context.FEATURE_NON_ECMA_GET_YEAR:
    +                 return true;
    +
    +             case Context.FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME:
    +                 return true;
    +
    +             case Context.FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER:
    +                 return true;
    +
    +             case Context.FEATURE_PARENT_PROTO_PROPERTIES:
    +                 return false;
    +         }
    +         return super.hasFeature(cx, featureIndex);
    +     }
    +
    +     // Override observeInstructionCount(Context, int)
    +     protected void observeInstructionCount(Context cx, int instructionCount)
    +     {
    +         MyContext mcx = (MyContext)cx;
    +         long currentTime = System.currentTimeMillis();
    +         if (currentTime - mcx.startTime > 10*1000) {
    +             // More then 10 seconds from Context creation time:
    +             // it is time to stop the script.
    +             // Throw Error instance to ensure that script will never
    +             // get control back through catch or finally.
    +             throw new Error();
    +         }
    +     }
    +
    +     // Override doTopCall(Callable,
    +                               Context, Scriptable,
    +                               Scriptable, Object[])
    +     protected Object doTopCall(Callable callable,
    +                                Context cx, Scriptable scope,
    +                                Scriptable thisObj, Object[] args)
    +     {
    +         MyContext mcx = (MyContext)cx;
    +         mcx.startTime = System.currentTimeMillis();
    +
    +         return super.doTopCall(callable, cx, scope, thisObj, args);
    +     }
    +
    + }
    +
    + 
    +

    + +

    +


    + +

    + + + + + + + + + + + +
    +Nested Class Summary
    +static interfaceContextFactory.Listener + +
    +          Listener of Context creation and release events.
    +  + + + + + + + + + + +
    +Constructor Summary
    ContextFactory() + +
    +           
    +  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +Method Summary
    + voidaddListener(ContextFactory.Listener listener) + +
    +           
    + java.lang.Objectcall(ContextAction action) + +
    +          Call ContextAction.run(Context cx) + using the Context instance associated with the current thread.
    +protected  voidcheckNotSealed() + +
    +           
    +protected  GeneratedClassLoadercreateClassLoader(java.lang.ClassLoader parent) + +
    +          Create class loader for generated classes.
    +protected  java.lang.ObjectdoTopCall(Callable callable, + Context cx, + Scriptable scope, + Scriptable thisObj, + java.lang.Object[] args) + +
    +          Execute top call to script or function.
    + Contextenter() + +
    +          Deprecated. use enterContext() instead
    + ContextenterContext() + +
    +          Get a context associated with the current thread, creating one if need + be.
    + ContextenterContext(Context cx) + +
    +          Get a Context associated with the current thread, using the given + Context if need be.
    + voidexit() + +
    +          Deprecated. Use Context.exit() instead.
    + java.lang.ClassLoadergetApplicationClassLoader() + +
    +          Get ClassLoader to use when searching for Java classes.
    +protected  org.mozilla.javascript.xml.XMLLib.FactorygetE4xImplementationFactory() + +
    +          Provides a default + XMLLib.Factory + to be used by the Context instances produced by this + factory.
    +static ContextFactorygetGlobal() + +
    +          Get global ContextFactory.
    +static booleanhasExplicitGlobal() + +
    +          Check if global factory was set.
    +protected  booleanhasFeature(Context cx, + int featureIndex) + +
    +          Implementation of Context.hasFeature(int featureIndex).
    + voidinitApplicationClassLoader(java.lang.ClassLoader loader) + +
    +          Set explicit class loader to use when searching for Java classes.
    +static voidinitGlobal(ContextFactory factory) + +
    +          Set global ContextFactory.
    + booleanisSealed() + +
    +          Checks if this is a sealed ContextFactory.
    +protected  ContextmakeContext() + +
    +          Create new Context instance to be associated with the current + thread.
    +protected  voidobserveInstructionCount(Context cx, + int instructionCount) + +
    +          Implementation of + Context.observeInstructionCount(int instructionCount).
    +protected  voidonContextCreated(Context cx) + +
    +           
    +protected  voidonContextReleased(Context cx) + +
    +           
    + voidremoveListener(ContextFactory.Listener listener) + +
    +           
    + voidseal() + +
    +          Seal this ContextFactory so any attempt to modify it like to add or + remove its listeners will throw an exception.
    + + + + + + + +
    Methods inherited from class java.lang.Object
    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    +  +

    + + + + + + + + +
    +Constructor Detail
    + +

    +ContextFactory

    +
    +public ContextFactory()
    +
    +
    + + + + + + + + +
    +Method Detail
    + +

    +getGlobal

    +
    +public static ContextFactory getGlobal()
    +
    +
    Get global ContextFactory. +

    +

    +
    See Also:
    hasExplicitGlobal(), +initGlobal(ContextFactory)
    +
    +
    +
    + +

    +hasExplicitGlobal

    +
    +public static boolean hasExplicitGlobal()
    +
    +
    Check if global factory was set. + Return true to indicate that initGlobal(ContextFactory) was + already called and false to indicate that the global factory was not + explicitly set. +

    +

    +
    See Also:
    getGlobal(), +initGlobal(ContextFactory)
    +
    +
    +
    + +

    +initGlobal

    +
    +public static void initGlobal(ContextFactory factory)
    +
    +
    Set global ContextFactory. + The method can only be called once. +

    +

    +
    See Also:
    getGlobal(), +hasExplicitGlobal()
    +
    +
    +
    + +

    +makeContext

    +
    +protected Context makeContext()
    +
    +
    Create new Context instance to be associated with the current + thread. + This is a callback method used by Rhino to create Context + instance when it is necessary to associate one with the current + execution thread. makeContext() is allowed to call + Context.seal(Object) on the result to prevent + Context changes by hostile scripts or applets. +

    +

    +
    +
    +
    +
    + +

    +hasFeature

    +
    +protected boolean hasFeature(Context cx,
    +                             int featureIndex)
    +
    +
    Implementation of Context.hasFeature(int featureIndex). + This can be used to customize Context without introducing + additional subclasses. +

    +

    +
    +
    +
    +
    + +

    +getE4xImplementationFactory

    +
    +protected org.mozilla.javascript.xml.XMLLib.Factory getE4xImplementationFactory()
    +
    +
    Provides a default + XMLLib.Factory + to be used by the Context instances produced by this + factory. See Context.getE4xImplementationFactory() for details. + + May return null, in which case E4X functionality is not supported in + Rhino. + + The default implementation now prefers the DOM3 E4X implementation. +

    +

    +
    +
    +
    +
    + +

    +createClassLoader

    +
    +protected GeneratedClassLoader createClassLoader(java.lang.ClassLoader parent)
    +
    +
    Create class loader for generated classes. + This method creates an instance of the default implementation + of GeneratedClassLoader. Rhino uses this interface to load + generated JVM classes when no SecurityController + is installed. + Application can override the method to provide custom class loading. +

    +

    +
    +
    +
    +
    + +

    +getApplicationClassLoader

    +
    +public final java.lang.ClassLoader getApplicationClassLoader()
    +
    +
    Get ClassLoader to use when searching for Java classes. + Unless it was explicitly initialized with + initApplicationClassLoader(ClassLoader) the method returns + null to indicate that Thread.getContextClassLoader() should be used. +

    +

    +
    +
    +
    +
    + +

    +initApplicationClassLoader

    +
    +public final void initApplicationClassLoader(java.lang.ClassLoader loader)
    +
    +
    Set explicit class loader to use when searching for Java classes. +

    +

    +
    See Also:
    getApplicationClassLoader()
    +
    +
    +
    + +

    +doTopCall

    +
    +protected java.lang.Object doTopCall(Callable callable,
    +                                     Context cx,
    +                                     Scriptable scope,
    +                                     Scriptable thisObj,
    +                                     java.lang.Object[] args)
    +
    +
    Execute top call to script or function. + When the runtime is about to execute a script or function that will + create the first stack frame with scriptable code, it calls this method + to perform the real call. In this way execution of any script + happens inside this function. +

    +

    +
    +
    +
    +
    + +

    +observeInstructionCount

    +
    +protected void observeInstructionCount(Context cx,
    +                                       int instructionCount)
    +
    +
    Implementation of + Context.observeInstructionCount(int instructionCount). + This can be used to customize Context without introducing + additional subclasses. +

    +

    +
    +
    +
    +
    + +

    +onContextCreated

    +
    +protected void onContextCreated(Context cx)
    +
    +
    +
    +
    +
    +
    + +

    +onContextReleased

    +
    +protected void onContextReleased(Context cx)
    +
    +
    +
    +
    +
    +
    + +

    +addListener

    +
    +public final void addListener(ContextFactory.Listener listener)
    +
    +
    +
    +
    +
    +
    + +

    +removeListener

    +
    +public final void removeListener(ContextFactory.Listener listener)
    +
    +
    +
    +
    +
    +
    + +

    +isSealed

    +
    +public final boolean isSealed()
    +
    +
    Checks if this is a sealed ContextFactory. +

    +

    +
    See Also:
    seal()
    +
    +
    +
    + +

    +seal

    +
    +public final void seal()
    +
    +
    Seal this ContextFactory so any attempt to modify it like to add or + remove its listeners will throw an exception. +

    +

    +
    See Also:
    isSealed()
    +
    +
    +
    + +

    +checkNotSealed

    +
    +protected final void checkNotSealed()
    +
    +
    +
    +
    +
    +
    + +

    +call

    +
    +public final java.lang.Object call(ContextAction action)
    +
    +
    Call ContextAction.run(Context cx) + using the Context instance associated with the current thread. + If no Context is associated with the thread, then + makeContext() will be called to construct + new Context instance. The instance will be temporary associated + with the thread during call to ContextAction.run(Context). +

    +

    +
    See Also:
    call(ContextAction), +Context.call(ContextFactory factory, Callable callable, + Scriptable scope, Scriptable thisObj, + Object[] args)
    +
    +
    +
    + +

    +enterContext

    +
    +public Context enterContext()
    +
    +
    Get a context associated with the current thread, creating one if need + be. The Context stores the execution state of the JavaScript engine, so + it is required that the context be entered before execution may begin. + Once a thread has entered a Context, then getCurrentContext() may be + called to find the context that is associated with the current thread. +

    + Calling enterContext() will return either the Context + currently associated with the thread, or will create a new context and + associate it with the current thread. Each call to + enterContext() must have a matching call to + Context.exit(). +

    +      Context cx = contextFactory.enterContext();
    +      try {
    +          ...
    +          cx.evaluateString(...);
    +      } finally {
    +          Context.exit();
    +      }
    + 
    + Instead of using enterContext(), exit() pair consider + using call(ContextAction) which guarantees proper association + of Context instances with the current thread. + With this method the above example becomes: +
    +      ContextFactory.call(new ContextAction() {
    +          public Object run(Context cx) {
    +              ...
    +              cx.evaluateString(...);
    +              return null;
    +          }
    +      });
    + 
    +

    +

    + +
    Returns:
    a Context associated with the current thread
    See Also:
    Context.getCurrentContext(), +Context.exit(), +call(ContextAction)
    +
    +
    +
    + +

    +enter

    +
    +public final Context enter()
    +
    +
    Deprecated. use enterContext() instead +

    +

    + +
    Returns:
    a Context associated with the current thread
    +
    +
    +
    + +

    +exit

    +
    +public final void exit()
    +
    +
    Deprecated. Use Context.exit() instead. +

    +

    +
    +
    +
    +
    + +

    +enterContext

    +
    +public final Context enterContext(Context cx)
    +
    +
    Get a Context associated with the current thread, using the given + Context if need be. +

    + The same as enterContext() except that cx + is associated with the current thread and returned if the current thread + has no associated context and cx is not associated with any + other thread. +

    +

    +
    Parameters:
    cx - a Context to associate with the thread if possible +
    Returns:
    a Context associated with the current thread +
    Throws: +
    java.lang.IllegalStateException - if cx is already associated + with a different thread
    See Also:
    enterContext(), +call(ContextAction)
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/EcmaError.html b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/EcmaError.html new file mode 100644 index 0000000..71c774f --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/EcmaError.html @@ -0,0 +1,455 @@ + + + + + + +EcmaError (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + +

    + +org.mozilla.javascript +
    +Class EcmaError

    +
    +java.lang.Object
    +  extended by java.lang.Throwable
    +      extended by java.lang.Exception
    +          extended by java.lang.RuntimeException
    +              extended by org.mozilla.javascript.RhinoException
    +                  extended by org.mozilla.javascript.EcmaError
    +
    +
    +
    All Implemented Interfaces:
    java.io.Serializable
    +
    +
    +
    +
    public class EcmaError
    extends RhinoException
    + + +

    +The class of exceptions raised by the engine as described in + ECMA edition 3. See section 15.11.6 in particular. +

    + +

    +

    +
    See Also:
    Serialized Form
    +
    + +

    + + + + + + + + + + + +
    +Constructor Summary
    EcmaError(Scriptable nativeError, + java.lang.String sourceName, + int lineNumber, + int columnNumber, + java.lang.String lineSource) + +
    +          Deprecated. EcmaError error instances should not be constructed + explicitly since they are generated by the engine.
    +  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +Method Summary
    + java.lang.Stringdetails() + +
    +           
    + intgetColumnNumber() + +
    +          Deprecated. Use RhinoException.columnNumber() from the super class.
    + java.lang.StringgetErrorMessage() + +
    +          Gets the message corresponding to the error.
    + ScriptablegetErrorObject() + +
    +          Deprecated. Always returns null.
    + intgetLineNumber() + +
    +          Deprecated. Use RhinoException.lineNumber() from the super class.
    + java.lang.StringgetLineSource() + +
    +          Deprecated. Use RhinoException.lineSource() from the super class.
    + java.lang.StringgetName() + +
    +          Gets the name of the error.
    + java.lang.StringgetSourceName() + +
    +          Deprecated. Use RhinoException.sourceName() from the super class.
    + + + + + + + +
    Methods inherited from class org.mozilla.javascript.RhinoException
    columnNumber, getMessage, getScriptStackTrace, getScriptStackTrace, initColumnNumber, initLineNumber, initLineSource, initSourceName, lineNumber, lineSource, printStackTrace, printStackTrace, sourceName
    + + + + + + + +
    Methods inherited from class java.lang.Throwable
    fillInStackTrace, getCause, getLocalizedMessage, getStackTrace, initCause, printStackTrace, setStackTrace, toString
    + + + + + + + +
    Methods inherited from class java.lang.Object
    clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
    +  +

    + + + + + + + + +
    +Constructor Detail
    + +

    +EcmaError

    +
    +public EcmaError(Scriptable nativeError,
    +                 java.lang.String sourceName,
    +                 int lineNumber,
    +                 int columnNumber,
    +                 java.lang.String lineSource)
    +
    +
    Deprecated. EcmaError error instances should not be constructed + explicitly since they are generated by the engine. +

    +

    + + + + + + + + +
    +Method Detail
    + +

    +details

    +
    +public java.lang.String details()
    +
    +
    +
    Overrides:
    details in class RhinoException
    +
    +
    +
    +
    +
    +
    + +

    +getName

    +
    +public java.lang.String getName()
    +
    +
    Gets the name of the error. + + ECMA edition 3 defines the following + errors: EvalError, RangeError, ReferenceError, + SyntaxError, TypeError, and URIError. Additional error names + may be added in the future. + + See ECMA edition 3, 15.11.7.9. +

    +

    + +
    Returns:
    the name of the error.
    +
    +
    +
    + +

    +getErrorMessage

    +
    +public java.lang.String getErrorMessage()
    +
    +
    Gets the message corresponding to the error. + + See ECMA edition 3, 15.11.7.10. +

    +

    + +
    Returns:
    an implemenation-defined string describing the error.
    +
    +
    +
    + +

    +getSourceName

    +
    +public java.lang.String getSourceName()
    +
    +
    Deprecated. Use RhinoException.sourceName() from the super class. +

    +

    +
    +
    +
    +
    + +

    +getLineNumber

    +
    +public int getLineNumber()
    +
    +
    Deprecated. Use RhinoException.lineNumber() from the super class. +

    +

    +
    +
    +
    +
    + +

    +getColumnNumber

    +
    +public int getColumnNumber()
    +
    +
    Deprecated. Use RhinoException.columnNumber() from the super class. +

    +

    +
    +
    +
    +
    + +

    +getLineSource

    +
    +public java.lang.String getLineSource()
    +
    +
    Deprecated. Use RhinoException.lineSource() from the super class. +

    +

    +
    +
    +
    +
    + +

    +getErrorObject

    +
    +public Scriptable getErrorObject()
    +
    +
    Deprecated. Always returns null. +

    +

    +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/ErrorReporter.html b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/ErrorReporter.html new file mode 100644 index 0000000..a559d25 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/ErrorReporter.html @@ -0,0 +1,299 @@ + + + + + + +ErrorReporter (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + +

    + +org.mozilla.javascript +
    +Interface ErrorReporter

    +
    +
    +
    public interface ErrorReporter
    + + +

    +This is interface defines a protocol for the reporting of + errors during JavaScript translation or execution. +

    + +

    +

    +
    Author:
    +
    Norris Boyd
    +
    +
    + +

    + + + + + + + + + + + + + + + + + + + + +
    +Method Summary
    + voiderror(java.lang.String message, + java.lang.String sourceName, + int line, + java.lang.String lineSource, + int lineOffset) + +
    +          Report an error.
    + EvaluatorExceptionruntimeError(java.lang.String message, + java.lang.String sourceName, + int line, + java.lang.String lineSource, + int lineOffset) + +
    +          Creates an EvaluatorException that may be thrown.
    + voidwarning(java.lang.String message, + java.lang.String sourceName, + int line, + java.lang.String lineSource, + int lineOffset) + +
    +          Report a warning.
    +  +

    + + + + + + + + +
    +Method Detail
    + +

    +warning

    +
    +void warning(java.lang.String message,
    +             java.lang.String sourceName,
    +             int line,
    +             java.lang.String lineSource,
    +             int lineOffset)
    +
    +
    Report a warning. + + The implementing class may choose to ignore the warning + if it desires. +

    +

    +
    Parameters:
    message - a String describing the warning
    sourceName - a String describing the JavaScript source + where the warning occured; typically a filename or URL
    line - the line number associated with the warning
    lineSource - the text of the line (may be null)
    lineOffset - the offset into lineSource where problem was detected
    +
    +
    +
    + +

    +error

    +
    +void error(java.lang.String message,
    +           java.lang.String sourceName,
    +           int line,
    +           java.lang.String lineSource,
    +           int lineOffset)
    +
    +
    Report an error. + + The implementing class is free to throw an exception if + it desires. + + If execution has not yet begun, the JavaScript engine is + free to find additional errors rather than terminating + the translation. It will not execute a script that had + errors, however. +

    +

    +
    Parameters:
    message - a String describing the error
    sourceName - a String describing the JavaScript source + where the error occured; typically a filename or URL
    line - the line number associated with the error
    lineSource - the text of the line (may be null)
    lineOffset - the offset into lineSource where problem was detected
    +
    +
    +
    + +

    +runtimeError

    +
    +EvaluatorException runtimeError(java.lang.String message,
    +                                java.lang.String sourceName,
    +                                int line,
    +                                java.lang.String lineSource,
    +                                int lineOffset)
    +
    +
    Creates an EvaluatorException that may be thrown. + + runtimeErrors, unlike errors, will always terminate the + current script. +

    +

    +
    Parameters:
    message - a String describing the error
    sourceName - a String describing the JavaScript source + where the error occured; typically a filename or URL
    line - the line number associated with the error
    lineSource - the text of the line (may be null)
    lineOffset - the offset into lineSource where problem was detected +
    Returns:
    an EvaluatorException that will be thrown.
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/EvaluatorException.html b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/EvaluatorException.html new file mode 100644 index 0000000..6f06bb5 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/EvaluatorException.html @@ -0,0 +1,405 @@ + + + + + + +EvaluatorException (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + +

    + +org.mozilla.javascript +
    +Class EvaluatorException

    +
    +java.lang.Object
    +  extended by java.lang.Throwable
    +      extended by java.lang.Exception
    +          extended by java.lang.RuntimeException
    +              extended by org.mozilla.javascript.RhinoException
    +                  extended by org.mozilla.javascript.EvaluatorException
    +
    +
    +
    All Implemented Interfaces:
    java.io.Serializable
    +
    +
    +
    Direct Known Subclasses:
    WrappedException
    +
    +
    +
    +
    public class EvaluatorException
    extends RhinoException
    + + +

    +The class of exceptions thrown by the JavaScript engine. +

    + +

    +

    +
    See Also:
    Serialized Form
    +
    + +

    + + + + + + + + + + + + + + + + + +
    +Constructor Summary
    EvaluatorException(java.lang.String detail) + +
    +           
    EvaluatorException(java.lang.String detail, + java.lang.String sourceName, + int lineNumber) + +
    +          Create an exception with the specified detail message.
    EvaluatorException(java.lang.String detail, + java.lang.String sourceName, + int lineNumber, + java.lang.String lineSource, + int columnNumber) + +
    +          Create an exception with the specified detail message.
    +  + + + + + + + + + + + + + + + + + + + + + + + +
    +Method Summary
    + intgetColumnNumber() + +
    +          Deprecated. Use RhinoException.columnNumber() from the super class.
    + intgetLineNumber() + +
    +          Deprecated. Use RhinoException.lineNumber() from the super class.
    + java.lang.StringgetLineSource() + +
    +          Deprecated. Use RhinoException.lineSource() from the super class.
    + java.lang.StringgetSourceName() + +
    +          Deprecated. Use RhinoException.sourceName() from the super class.
    + + + + + + + +
    Methods inherited from class org.mozilla.javascript.RhinoException
    columnNumber, details, getMessage, getScriptStackTrace, getScriptStackTrace, initColumnNumber, initLineNumber, initLineSource, initSourceName, lineNumber, lineSource, printStackTrace, printStackTrace, sourceName
    + + + + + + + +
    Methods inherited from class java.lang.Throwable
    fillInStackTrace, getCause, getLocalizedMessage, getStackTrace, initCause, printStackTrace, setStackTrace, toString
    + + + + + + + +
    Methods inherited from class java.lang.Object
    clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
    +  +

    + + + + + + + + +
    +Constructor Detail
    + +

    +EvaluatorException

    +
    +public EvaluatorException(java.lang.String detail)
    +
    +
    +
    + +

    +EvaluatorException

    +
    +public EvaluatorException(java.lang.String detail,
    +                          java.lang.String sourceName,
    +                          int lineNumber)
    +
    +
    Create an exception with the specified detail message. + + Errors internal to the JavaScript engine will simply throw a + RuntimeException. +

    +

    +
    Parameters:
    detail - the error message
    sourceName - the name of the source reponsible for the error
    lineNumber - the line number of the source
    +
    +
    + +

    +EvaluatorException

    +
    +public EvaluatorException(java.lang.String detail,
    +                          java.lang.String sourceName,
    +                          int lineNumber,
    +                          java.lang.String lineSource,
    +                          int columnNumber)
    +
    +
    Create an exception with the specified detail message. + + Errors internal to the JavaScript engine will simply throw a + RuntimeException. +

    +

    +
    Parameters:
    detail - the error message
    sourceName - the name of the source responsible for the error
    lineNumber - the line number of the source
    columnNumber - the columnNumber of the source (may be zero if + unknown)
    lineSource - the source of the line containing the error (may be + null if unknown)
    +
    + + + + + + + + +
    +Method Detail
    + +

    +getSourceName

    +
    +public java.lang.String getSourceName()
    +
    +
    Deprecated. Use RhinoException.sourceName() from the super class. +

    +

    +
    +
    +
    +
    + +

    +getLineNumber

    +
    +public int getLineNumber()
    +
    +
    Deprecated. Use RhinoException.lineNumber() from the super class. +

    +

    +
    +
    +
    +
    + +

    +getColumnNumber

    +
    +public int getColumnNumber()
    +
    +
    Deprecated. Use RhinoException.columnNumber() from the super class. +

    +

    +
    +
    +
    +
    + +

    +getLineSource

    +
    +public java.lang.String getLineSource()
    +
    +
    Deprecated. Use RhinoException.lineSource() from the super class. +

    +

    +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/Function.html b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/Function.html new file mode 100644 index 0000000..55712dc --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/Function.html @@ -0,0 +1,297 @@ + + + + + + +Function (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + +

    + +org.mozilla.javascript +
    +Interface Function

    +
    +
    All Superinterfaces:
    Callable, Scriptable
    +
    +
    +
    All Known Implementing Classes:
    org.mozilla.javascript.BaseFunction, org.mozilla.javascript.Delegator, FunctionObject, Synchronizer
    +
    +
    +
    +
    public interface Function
    extends Scriptable, Callable
    + + +

    +This is interface that all functions in JavaScript must implement. + The interface provides for calling functions and constructors. +

    + +

    +

    +
    Author:
    +
    Norris Boyd
    +
    See Also:
    Scriptable
    +
    + +

    + + + + + + + +
    +Field Summary
    + + + + + + + +
    Fields inherited from interface org.mozilla.javascript.Scriptable
    NOT_FOUND
    +  + + + + + + + + + + + + + + + +
    +Method Summary
    + java.lang.Objectcall(Context cx, + Scriptable scope, + Scriptable thisObj, + java.lang.Object[] args) + +
    +          Call the function.
    + Scriptableconstruct(Context cx, + Scriptable scope, + java.lang.Object[] args) + +
    +          Call the function as a constructor.
    + + + + + + + +
    Methods inherited from interface org.mozilla.javascript.Scriptable
    delete, delete, get, get, getClassName, getDefaultValue, getIds, getParentScope, getPrototype, has, has, hasInstance, put, put, setParentScope, setPrototype
    +  +

    + + + + + + + + +
    +Method Detail
    + +

    +call

    +
    +java.lang.Object call(Context cx,
    +                      Scriptable scope,
    +                      Scriptable thisObj,
    +                      java.lang.Object[] args)
    +
    +
    Call the function. + + Note that the array of arguments is not guaranteed to have + length greater than 0. +

    +

    +
    Specified by:
    call in interface Callable
    +
    +
    +
    Parameters:
    cx - the current Context for this thread
    scope - the scope to execute the function relative to. This is + set to the value returned by getParentScope() except + when the function is called from a closure.
    thisObj - the JavaScript this object
    args - the array of arguments +
    Returns:
    the result of the call
    +
    +
    +
    + +

    +construct

    +
    +Scriptable construct(Context cx,
    +                     Scriptable scope,
    +                     java.lang.Object[] args)
    +
    +
    Call the function as a constructor. + + This method is invoked by the runtime in order to satisfy a use + of the JavaScript new operator. This method is + expected to create a new object and return it. +

    +

    +
    +
    +
    +
    Parameters:
    cx - the current Context for this thread
    scope - an enclosing scope of the caller except + when the function is called from a closure.
    args - the array of arguments +
    Returns:
    the allocated object
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/FunctionObject.html b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/FunctionObject.html new file mode 100644 index 0000000..4e9d011 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/FunctionObject.html @@ -0,0 +1,774 @@ + + + + + + +FunctionObject (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + +

    + +org.mozilla.javascript +
    +Class FunctionObject

    +
    +java.lang.Object
    +  extended by org.mozilla.javascript.ScriptableObject
    +      extended by org.mozilla.javascript.IdScriptableObject
    +          extended by org.mozilla.javascript.BaseFunction
    +              extended by org.mozilla.javascript.FunctionObject
    +
    +
    +
    All Implemented Interfaces:
    java.io.Serializable, Callable, org.mozilla.javascript.ConstProperties, org.mozilla.javascript.debug.DebuggableObject, Function, org.mozilla.javascript.IdFunctionCall, Scriptable
    +
    +
    +
    +
    public class FunctionObject
    extends org.mozilla.javascript.BaseFunction
    + + +

    +

    +
    See Also:
    Serialized Form
    +
    + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +Field Summary
    +static intJAVA_BOOLEAN_TYPE + +
    +           
    +static intJAVA_DOUBLE_TYPE + +
    +           
    +static intJAVA_INT_TYPE + +
    +           
    +static intJAVA_OBJECT_TYPE + +
    +           
    +static intJAVA_SCRIPTABLE_TYPE + +
    +           
    +static intJAVA_STRING_TYPE + +
    +           
    +static intJAVA_UNSUPPORTED_TYPE + +
    +           
    + + + + + + + +
    Fields inherited from class org.mozilla.javascript.ScriptableObject
    CONST, DONTENUM, EMPTY, PERMANENT, READONLY, UNINITIALIZED_CONST
    + + + + + + + +
    Fields inherited from interface org.mozilla.javascript.Scriptable
    NOT_FOUND
    +  + + + + + + + + + + +
    +Constructor Summary
    FunctionObject(java.lang.String name, + java.lang.reflect.Member methodOrConstructor, + Scriptable scope) + +
    +          Create a JavaScript function object from a Java method.
    +  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +Method Summary
    + voidaddAsConstructor(Scriptable scope, + Scriptable prototype) + +
    +          Define this function as a JavaScript constructor.
    + java.lang.Objectcall(Context cx, + Scriptable scope, + Scriptable thisObj, + java.lang.Object[] args) + +
    +          Performs conversions on argument types if needed and + invokes the underlying Java method or constructor.
    +static java.lang.ObjectconvertArg(Context cx, + Scriptable scope, + java.lang.Object arg, + java.lang.Class desired) + +
    +          Deprecated. Use getTypeTag(Class) + and convertArg(Context, Scriptable, Object, int) + for type convertion.
    +static java.lang.ObjectconvertArg(Context cx, + Scriptable scope, + java.lang.Object arg, + int typeTag) + +
    +           
    + ScriptablecreateObject(Context cx, + Scriptable scope) + +
    +          Return new Scriptable instance using the default + constructor for the class of the underlying Java method.
    + intgetArity() + +
    +          Return the value defined by the method used to construct the object + (number of parameters of the method, or 1 if the method is a "varargs" + form).
    + java.lang.StringgetFunctionName() + +
    +           
    + intgetLength() + +
    +          Return the same value as getArity().
    + java.lang.reflect.MembergetMethodOrConstructor() + +
    +          Get Java method or constructor this function represent.
    +static intgetTypeTag(java.lang.Class type) + +
    +           
    + + + + + + + +
    Methods inherited from class org.mozilla.javascript.BaseFunction
    construct, execIdCall, fillConstructorProperties, findInstanceIdInfo, findPrototypeId, getClassName, getClassPrototype, getInstanceIdName, getInstanceIdValue, getMaxInstanceId, hasInstance, initPrototypeId, setImmunePrototypeProperty, setInstanceIdValue
    + + + + + + + +
    Methods inherited from class org.mozilla.javascript.IdScriptableObject
    activatePrototypeMap, addIdFunctionProperty, defaultGet, defaultPut, delete, exportAsJSClass, get, getAttributes, has, hasPrototypeMap, incompatibleCallError, initPrototypeConstructor, initPrototypeMethod, initPrototypeValue, instanceIdInfo, put, setAttributes
    + + + + + + + +
    Methods inherited from class org.mozilla.javascript.ScriptableObject
    associateValue, avoidObjectDetection, callMethod, callMethod, defineClass, defineClass, defineClass, defineConst, defineConstProperty, defineFunctionProperties, defineProperty, defineProperty, defineProperty, defineProperty, delete, deleteProperty, deleteProperty, equivalentValues, get, getAllIds, getAssociatedValue, getAttributes, getAttributes, getAttributes, getClassPrototype, getDefaultValue, getDefaultValue, getFunctionPrototype, getGetterOrSetter, getIds, getObjectPrototype, getParentScope, getProperty, getProperty, getPropertyIds, getPrototype, getTopLevelScope, getTopScopeValue, has, hasProperty, hasProperty, isConst, isGetterOrSetter, isSealed, put, putConst, putConstProperty, putProperty, putProperty, redefineProperty, sealObject, setAttributes, setAttributes, setAttributes, setGetterOrSetter, setParentScope, setPrototype
    + + + + + + + +
    Methods inherited from class java.lang.Object
    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    + + + + + + + +
    Methods inherited from interface org.mozilla.javascript.Scriptable
    delete, delete, get, get, getDefaultValue, getIds, getParentScope, getPrototype, has, has, put, put, setParentScope, setPrototype
    +  +

    + + + + + + + + +
    +Field Detail
    + +

    +JAVA_UNSUPPORTED_TYPE

    +
    +public static final int JAVA_UNSUPPORTED_TYPE
    +
    +
    +
    See Also:
    Constant Field Values
    +
    +
    + +

    +JAVA_STRING_TYPE

    +
    +public static final int JAVA_STRING_TYPE
    +
    +
    +
    See Also:
    Constant Field Values
    +
    +
    + +

    +JAVA_INT_TYPE

    +
    +public static final int JAVA_INT_TYPE
    +
    +
    +
    See Also:
    Constant Field Values
    +
    +
    + +

    +JAVA_BOOLEAN_TYPE

    +
    +public static final int JAVA_BOOLEAN_TYPE
    +
    +
    +
    See Also:
    Constant Field Values
    +
    +
    + +

    +JAVA_DOUBLE_TYPE

    +
    +public static final int JAVA_DOUBLE_TYPE
    +
    +
    +
    See Also:
    Constant Field Values
    +
    +
    + +

    +JAVA_SCRIPTABLE_TYPE

    +
    +public static final int JAVA_SCRIPTABLE_TYPE
    +
    +
    +
    See Also:
    Constant Field Values
    +
    +
    + +

    +JAVA_OBJECT_TYPE

    +
    +public static final int JAVA_OBJECT_TYPE
    +
    +
    +
    See Also:
    Constant Field Values
    +
    + + + + + + + + +
    +Constructor Detail
    + +

    +FunctionObject

    +
    +public FunctionObject(java.lang.String name,
    +                      java.lang.reflect.Member methodOrConstructor,
    +                      Scriptable scope)
    +
    +
    Create a JavaScript function object from a Java method. + +

    The member argument must be either a java.lang.reflect.Method + or a java.lang.reflect.Constructor and must match one of two forms.

    + + The first form is a member with zero or more parameters + of the following types: Object, String, boolean, Scriptable, + int, or double. The Long type is not supported + because the double representation of a long (which is the + EMCA-mandated storage type for Numbers) may lose precision. + If the member is a Method, the return value must be void or one + of the types allowed for parameters.

    + + The runtime will perform appropriate conversions based + upon the type of the parameter. A parameter type of + Object specifies that no conversions are to be done. A parameter + of type String will use Context.toString to convert arguments. + Similarly, parameters of type double, boolean, and Scriptable + will cause Context.toNumber, Context.toBoolean, and + Context.toObject, respectively, to be called.

    + + If the method is not static, the Java 'this' value will + correspond to the JavaScript 'this' value. Any attempt + to call the function with a 'this' value that is not + of the right Java type will result in an error.

    + + The second form is the variable arguments (or "varargs") + form. If the FunctionObject will be used as a constructor, + the member must have the following parameters +

    +      (Context cx, Object[] args, Function ctorObj,
    +       boolean inNewExpr)
    + and if it is a Method, be static and return an Object result.

    + + Otherwise, if the FunctionObject will not be used to define a + constructor, the member must be a static Method with parameters + (Context cx, Scriptable thisObj, Object[] args, + Function funObj) +

    + and an Object result.

    + + When the function varargs form is called as part of a function call, + the args parameter contains the + arguments, with thisObj + set to the JavaScript 'this' value. funObj + is the function object for the invoked function.

    + + When the constructor varargs form is called or invoked while evaluating + a new expression, args contains the + arguments, ctorObj refers to this FunctionObject, and + inNewExpr is true if and only if a new + expression caused the call. This supports defining a function that + has different behavior when called as a constructor than when + invoked as a normal function call. (For example, the Boolean + constructor, when called as a function, + will convert to boolean rather than creating a new object.)

    +

    +

    +
    Parameters:
    name - the name of the function
    methodOrConstructor - a java.lang.reflect.Method or a java.lang.reflect.Constructor + that defines the object
    scope - enclosing scope of function
    See Also:
    Scriptable
    +
    + + + + + + + + +
    +Method Detail
    + +

    +getTypeTag

    +
    +public static int getTypeTag(java.lang.Class type)
    +
    +
    + +
    Returns:
    One of JAVA_*_TYPE constants to indicate desired type + or JAVA_UNSUPPORTED_TYPE if the convertion is not + possible
    +
    +
    +
    + +

    +convertArg

    +
    +public static java.lang.Object convertArg(Context cx,
    +                                          Scriptable scope,
    +                                          java.lang.Object arg,
    +                                          int typeTag)
    +
    +
    +
    +
    +
    +
    + +

    +getArity

    +
    +public int getArity()
    +
    +
    Return the value defined by the method used to construct the object + (number of parameters of the method, or 1 if the method is a "varargs" + form). +

    +

    +
    Overrides:
    getArity in class org.mozilla.javascript.BaseFunction
    +
    +
    +
    +
    +
    +
    + +

    +getLength

    +
    +public int getLength()
    +
    +
    Return the same value as getArity(). +

    +

    +
    Overrides:
    getLength in class org.mozilla.javascript.BaseFunction
    +
    +
    +
    +
    +
    +
    + +

    +getFunctionName

    +
    +public java.lang.String getFunctionName()
    +
    +
    +
    Overrides:
    getFunctionName in class org.mozilla.javascript.BaseFunction
    +
    +
    +
    +
    +
    +
    + +

    +getMethodOrConstructor

    +
    +public java.lang.reflect.Member getMethodOrConstructor()
    +
    +
    Get Java method or constructor this function represent. +

    +

    +
    +
    +
    +
    + +

    +addAsConstructor

    +
    +public void addAsConstructor(Scriptable scope,
    +                             Scriptable prototype)
    +
    +
    Define this function as a JavaScript constructor. +

    + Sets up the "prototype" and "constructor" properties. Also + calls setParent and setPrototype with appropriate values. + Then adds the function object as a property of the given scope, using + prototype.getClassName() + as the name of the property. +

    +

    +
    Parameters:
    scope - the scope in which to define the constructor (typically + the global object)
    prototype - the prototype object
    See Also:
    Scriptable.setParentScope(org.mozilla.javascript.Scriptable), +Scriptable.setPrototype(org.mozilla.javascript.Scriptable), +Scriptable.getClassName()
    +
    +
    +
    + +

    +convertArg

    +
    +public static java.lang.Object convertArg(Context cx,
    +                                          Scriptable scope,
    +                                          java.lang.Object arg,
    +                                          java.lang.Class desired)
    +
    +
    Deprecated. Use getTypeTag(Class) + and convertArg(Context, Scriptable, Object, int) + for type convertion. +

    +

    +
    +
    +
    +
    + +

    +call

    +
    +public java.lang.Object call(Context cx,
    +                             Scriptable scope,
    +                             Scriptable thisObj,
    +                             java.lang.Object[] args)
    +
    +
    Performs conversions on argument types if needed and + invokes the underlying Java method or constructor. +

    + Implements Function.call. +

    +

    +
    Specified by:
    call in interface Callable
    Specified by:
    call in interface Function
    Overrides:
    call in class org.mozilla.javascript.BaseFunction
    +
    +
    +
    Parameters:
    cx - the current Context for this thread
    scope - the scope to execute the function relative to. This is + set to the value returned by getParentScope() except + when the function is called from a closure.
    thisObj - the JavaScript this object
    args - the array of arguments +
    Returns:
    the result of the call
    See Also:
    Function.call( + Context, Scriptable, Scriptable, Object[])
    +
    +
    +
    + +

    +createObject

    +
    +public Scriptable createObject(Context cx,
    +                               Scriptable scope)
    +
    +
    Return new Scriptable instance using the default + constructor for the class of the underlying Java method. + Return null to indicate that the call method should be used to create + new objects. +

    +

    +
    Overrides:
    createObject in class org.mozilla.javascript.BaseFunction
    +
    +
    +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/GeneratedClassLoader.html b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/GeneratedClassLoader.html new file mode 100644 index 0000000..2d998ac --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/GeneratedClassLoader.html @@ -0,0 +1,236 @@ + + + + + + +GeneratedClassLoader (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + +

    + +org.mozilla.javascript +
    +Interface GeneratedClassLoader

    +
    +
    +
    public interface GeneratedClassLoader
    + + +

    +Interface to define classes from generated byte code. +

    + +

    +


    + +

    + + + + + + + + + + + + + + + + +
    +Method Summary
    + java.lang.ClassdefineClass(java.lang.String name, + byte[] data) + +
    +          Define a new Java class.
    + voidlinkClass(java.lang.Class cl) + +
    +          Link the given class.
    +  +

    + + + + + + + + +
    +Method Detail
    + +

    +defineClass

    +
    +java.lang.Class defineClass(java.lang.String name,
    +                            byte[] data)
    +
    +
    Define a new Java class. + Classes created via this method should have the same class loader. +

    +

    +
    Parameters:
    name - fully qualified class name
    data - class byte code +
    Returns:
    new class object
    +
    +
    +
    + +

    +linkClass

    +
    +void linkClass(java.lang.Class cl)
    +
    +
    Link the given class. +

    +

    +
    Parameters:
    cl - Class instance returned from the previous call to + defineClass(String, byte[])
    See Also:
    ClassLoader
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/ImporterTopLevel.html b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/ImporterTopLevel.html new file mode 100644 index 0000000..78dd7a7 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/ImporterTopLevel.html @@ -0,0 +1,583 @@ + + + + + + +ImporterTopLevel (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + +

    + +org.mozilla.javascript +
    +Class ImporterTopLevel

    +
    +java.lang.Object
    +  extended by org.mozilla.javascript.ScriptableObject
    +      extended by org.mozilla.javascript.IdScriptableObject
    +          extended by org.mozilla.javascript.ImporterTopLevel
    +
    +
    +
    All Implemented Interfaces:
    java.io.Serializable, org.mozilla.javascript.ConstProperties, org.mozilla.javascript.debug.DebuggableObject, org.mozilla.javascript.IdFunctionCall, Scriptable
    +
    +
    +
    +
    public class ImporterTopLevel
    extends org.mozilla.javascript.IdScriptableObject
    + + +

    +Class ImporterTopLevel + + This class defines a ScriptableObject that can be instantiated + as a top-level ("global") object to provide functionality similar + to Java's "import" statement. +

    + This class can be used to create a top-level scope using the following code: +

    +  Scriptable scope = new ImporterTopLevel(cx);
    + 
    + Then JavaScript code will have access to the following methods: +
      +
    • importClass - will "import" a class by making its unqualified name + available as a property of the top-level scope +
    • importPackage - will "import" all the classes of the package by + searching for unqualified names as classes qualified + by the given package. +
    + The following code from the shell illustrates this use: +
    + js> importClass(java.io.File)
    + js> f = new File('help.txt')
    + help.txt
    + js> importPackage(java.util)
    + js> v = new Vector()
    + []
    +

    + +

    +

    +
    Author:
    +
    Norris Boyd
    +
    See Also:
    Serialized Form
    +
    + +

    + + + + + + + +
    +Field Summary
    + + + + + + + +
    Fields inherited from class org.mozilla.javascript.ScriptableObject
    CONST, DONTENUM, EMPTY, PERMANENT, READONLY, UNINITIALIZED_CONST
    + + + + + + + +
    Fields inherited from interface org.mozilla.javascript.Scriptable
    NOT_FOUND
    +  + + + + + + + + + + + + + + + + +
    +Constructor Summary
    ImporterTopLevel() + +
    +           
    ImporterTopLevel(Context cx) + +
    +           
    ImporterTopLevel(Context cx, + boolean sealed) + +
    +           
    +  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +Method Summary
    + java.lang.ObjectexecIdCall(org.mozilla.javascript.IdFunctionObject f, + Context cx, + Scriptable scope, + Scriptable thisObj, + java.lang.Object[] args) + +
    +          'thisObj' will be null if invoked as constructor, in which case + instance of Scriptable should be returned.
    +protected  intfindPrototypeId(java.lang.String s) + +
    +           
    + java.lang.Objectget(java.lang.String name, + Scriptable start) + +
    +          Returns the value of the named property or NOT_FOUND.
    + java.lang.StringgetClassName() + +
    +          Return the name of the class.
    + booleanhas(java.lang.String name, + Scriptable start) + +
    +          Returns true if the named property is defined.
    + voidimportPackage(Context cx, + Scriptable thisObj, + java.lang.Object[] args, + Function funObj) + +
    +          Deprecated. Kept only for compatibility.
    +static voidinit(Context cx, + Scriptable scope, + boolean sealed) + +
    +           
    +protected  voidinitPrototypeId(int id) + +
    +           
    + voidinitStandardObjects(Context cx, + boolean sealed) + +
    +           
    + + + + + + + +
    Methods inherited from class org.mozilla.javascript.IdScriptableObject
    activatePrototypeMap, addIdFunctionProperty, defaultGet, defaultPut, delete, exportAsJSClass, fillConstructorProperties, findInstanceIdInfo, getAttributes, getInstanceIdName, getInstanceIdValue, getMaxInstanceId, hasPrototypeMap, incompatibleCallError, initPrototypeConstructor, initPrototypeMethod, initPrototypeValue, instanceIdInfo, put, setAttributes, setInstanceIdValue
    + + + + + + + +
    Methods inherited from class org.mozilla.javascript.ScriptableObject
    associateValue, avoidObjectDetection, callMethod, callMethod, defineClass, defineClass, defineClass, defineConst, defineConstProperty, defineFunctionProperties, defineProperty, defineProperty, defineProperty, defineProperty, delete, deleteProperty, deleteProperty, equivalentValues, get, getAllIds, getAssociatedValue, getAttributes, getAttributes, getAttributes, getClassPrototype, getDefaultValue, getDefaultValue, getFunctionPrototype, getGetterOrSetter, getIds, getObjectPrototype, getParentScope, getProperty, getProperty, getPropertyIds, getPrototype, getTopLevelScope, getTopScopeValue, has, hasInstance, hasProperty, hasProperty, isConst, isGetterOrSetter, isSealed, put, putConst, putConstProperty, putProperty, putProperty, redefineProperty, sealObject, setAttributes, setAttributes, setAttributes, setGetterOrSetter, setParentScope, setPrototype
    + + + + + + + +
    Methods inherited from class java.lang.Object
    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    +  +

    + + + + + + + + +
    +Constructor Detail
    + +

    +ImporterTopLevel

    +
    +public ImporterTopLevel()
    +
    +
    +
    + +

    +ImporterTopLevel

    +
    +public ImporterTopLevel(Context cx)
    +
    +
    +
    + +

    +ImporterTopLevel

    +
    +public ImporterTopLevel(Context cx,
    +                        boolean sealed)
    +
    +
    + + + + + + + + +
    +Method Detail
    + +

    +getClassName

    +
    +public java.lang.String getClassName()
    +
    +
    Description copied from class: ScriptableObject
    +
    Return the name of the class. + + This is typically the same name as the constructor. + Classes extending ScriptableObject must implement this abstract + method. +

    +

    +
    Specified by:
    getClassName in interface Scriptable
    Specified by:
    getClassName in class ScriptableObject
    +
    +
    +
    +
    +
    +
    + +

    +init

    +
    +public static void init(Context cx,
    +                        Scriptable scope,
    +                        boolean sealed)
    +
    +
    +
    +
    +
    +
    + +

    +initStandardObjects

    +
    +public void initStandardObjects(Context cx,
    +                                boolean sealed)
    +
    +
    +
    +
    +
    +
    + +

    +has

    +
    +public boolean has(java.lang.String name,
    +                   Scriptable start)
    +
    +
    Description copied from class: ScriptableObject
    +
    Returns true if the named property is defined. +

    +

    +
    Specified by:
    has in interface Scriptable
    Overrides:
    has in class org.mozilla.javascript.IdScriptableObject
    +
    +
    +
    Parameters:
    name - the name of the property
    start - the object in which the lookup began +
    Returns:
    true if and only if the property was found in the object
    See Also:
    Scriptable.get(String, Scriptable), +ScriptableObject.getProperty(Scriptable, String)
    +
    +
    +
    + +

    +get

    +
    +public java.lang.Object get(java.lang.String name,
    +                            Scriptable start)
    +
    +
    Description copied from class: ScriptableObject
    +
    Returns the value of the named property or NOT_FOUND. + + If the property was created using defineProperty, the + appropriate getter method is called. +

    +

    +
    Specified by:
    get in interface Scriptable
    Overrides:
    get in class org.mozilla.javascript.IdScriptableObject
    +
    +
    +
    Parameters:
    name - the name of the property
    start - the object in which the lookup began +
    Returns:
    the value of the property (may be null), or NOT_FOUND
    See Also:
    Context.getUndefinedValue()
    +
    +
    +
    + +

    +importPackage

    +
    +public void importPackage(Context cx,
    +                          Scriptable thisObj,
    +                          java.lang.Object[] args,
    +                          Function funObj)
    +
    +
    Deprecated. Kept only for compatibility. +

    +

    +
    +
    +
    +
    + +

    +initPrototypeId

    +
    +protected void initPrototypeId(int id)
    +
    +
    +
    Overrides:
    initPrototypeId in class org.mozilla.javascript.IdScriptableObject
    +
    +
    +
    +
    +
    +
    + +

    +execIdCall

    +
    +public java.lang.Object execIdCall(org.mozilla.javascript.IdFunctionObject f,
    +                                   Context cx,
    +                                   Scriptable scope,
    +                                   Scriptable thisObj,
    +                                   java.lang.Object[] args)
    +
    +
    Description copied from class: org.mozilla.javascript.IdScriptableObject
    +
    'thisObj' will be null if invoked as constructor, in which case + instance of Scriptable should be returned. +

    +

    +
    Specified by:
    execIdCall in interface org.mozilla.javascript.IdFunctionCall
    Overrides:
    execIdCall in class org.mozilla.javascript.IdScriptableObject
    +
    +
    +
    +
    +
    +
    + +

    +findPrototypeId

    +
    +protected int findPrototypeId(java.lang.String s)
    +
    +
    +
    Overrides:
    findPrototypeId in class org.mozilla.javascript.IdScriptableObject
    +
    +
    +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/JavaScriptException.html b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/JavaScriptException.html new file mode 100644 index 0000000..6dafa13 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/JavaScriptException.html @@ -0,0 +1,375 @@ + + + + + + +JavaScriptException (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + +

    + +org.mozilla.javascript +
    +Class JavaScriptException

    +
    +java.lang.Object
    +  extended by java.lang.Throwable
    +      extended by java.lang.Exception
    +          extended by java.lang.RuntimeException
    +              extended by org.mozilla.javascript.RhinoException
    +                  extended by org.mozilla.javascript.JavaScriptException
    +
    +
    +
    All Implemented Interfaces:
    java.io.Serializable
    +
    +
    +
    +
    public class JavaScriptException
    extends RhinoException
    +
    + +

    +Java reflection of JavaScript exceptions. + Instances of this class are thrown by the JavaScript 'throw' keyword. +

    + +

    +

    +
    Author:
    +
    Mike McCabe
    +
    See Also:
    Serialized Form
    +
    + +

    + + + + + + + + + + + + + + +
    +Constructor Summary
    JavaScriptException(java.lang.Object value) + +
    +          Deprecated. Use WrappedException.WrappedException(Throwable) to report + exceptions in Java code.
    JavaScriptException(java.lang.Object value, + java.lang.String sourceName, + int lineNumber) + +
    +          Create a JavaScript exception wrapping the given JavaScript value
    +  + + + + + + + + + + + + + + + + + + + + + + + +
    +Method Summary
    + java.lang.Stringdetails() + +
    +           
    + intgetLineNumber() + +
    +          Deprecated. Use RhinoException.lineNumber() from the super class.
    + java.lang.StringgetSourceName() + +
    +          Deprecated. Use RhinoException.sourceName() from the super class.
    + java.lang.ObjectgetValue() + +
    +           
    + + + + + + + +
    Methods inherited from class org.mozilla.javascript.RhinoException
    columnNumber, getMessage, getScriptStackTrace, getScriptStackTrace, initColumnNumber, initLineNumber, initLineSource, initSourceName, lineNumber, lineSource, printStackTrace, printStackTrace, sourceName
    + + + + + + + +
    Methods inherited from class java.lang.Throwable
    fillInStackTrace, getCause, getLocalizedMessage, getStackTrace, initCause, printStackTrace, setStackTrace, toString
    + + + + + + + +
    Methods inherited from class java.lang.Object
    clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
    +  +

    + + + + + + + + +
    +Constructor Detail
    + +

    +JavaScriptException

    +
    +public JavaScriptException(java.lang.Object value)
    +
    +
    Deprecated. Use WrappedException.WrappedException(Throwable) to report + exceptions in Java code. +

    +

    +
    + +

    +JavaScriptException

    +
    +public JavaScriptException(java.lang.Object value,
    +                           java.lang.String sourceName,
    +                           int lineNumber)
    +
    +
    Create a JavaScript exception wrapping the given JavaScript value +

    +

    +
    Parameters:
    value - the JavaScript value thrown.
    +
    + + + + + + + + +
    +Method Detail
    + +

    +details

    +
    +public java.lang.String details()
    +
    +
    +
    Overrides:
    details in class RhinoException
    +
    +
    +
    +
    +
    +
    + +

    +getValue

    +
    +public java.lang.Object getValue()
    +
    +
    + +
    Returns:
    the value wrapped by this exception
    +
    +
    +
    + +

    +getSourceName

    +
    +public java.lang.String getSourceName()
    +
    +
    Deprecated. Use RhinoException.sourceName() from the super class. +

    +

    +
    +
    +
    +
    + +

    +getLineNumber

    +
    +public int getLineNumber()
    +
    +
    Deprecated. Use RhinoException.lineNumber() from the super class. +

    +

    +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/RefCallable.html b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/RefCallable.html new file mode 100644 index 0000000..910d0fd --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/RefCallable.html @@ -0,0 +1,233 @@ + + + + + + +RefCallable (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + +

    + +org.mozilla.javascript +
    +Interface RefCallable

    +
    +
    All Superinterfaces:
    Callable
    +
    +
    +
    +
    public interface RefCallable
    extends Callable
    + + +

    +Object that can allows assignments to the result of function calls. +

    + +

    +


    + +

    + + + + + + + + + + + + +
    +Method Summary
    + org.mozilla.javascript.RefrefCall(Context cx, + Scriptable thisObj, + java.lang.Object[] args) + +
    +          Perform function call in reference context.
    + + + + + + + +
    Methods inherited from interface org.mozilla.javascript.Callable
    call
    +  +

    + + + + + + + + +
    +Method Detail
    + +

    +refCall

    +
    +org.mozilla.javascript.Ref refCall(Context cx,
    +                                   Scriptable thisObj,
    +                                   java.lang.Object[] args)
    +
    +
    Perform function call in reference context. + The args array reference should not be stored in any object that is + can be GC-reachable after this method returns. If this is necessary, + for example, to implement Ref methods, then store args.clone(), + not args array itself. +

    +

    +
    +
    +
    +
    Parameters:
    cx - the current Context for this thread
    thisObj - the JavaScript this object
    args - the array of arguments
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/RhinoException.html b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/RhinoException.html new file mode 100644 index 0000000..4ad3d0f --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/RhinoException.html @@ -0,0 +1,544 @@ + + + + + + +RhinoException (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + +

    + +org.mozilla.javascript +
    +Class RhinoException

    +
    +java.lang.Object
    +  extended by java.lang.Throwable
    +      extended by java.lang.Exception
    +          extended by java.lang.RuntimeException
    +              extended by org.mozilla.javascript.RhinoException
    +
    +
    +
    All Implemented Interfaces:
    java.io.Serializable
    +
    +
    +
    Direct Known Subclasses:
    EcmaError, EvaluatorException, JavaScriptException
    +
    +
    +
    +
    public abstract class RhinoException
    extends java.lang.RuntimeException
    + + +

    +The class of exceptions thrown by the JavaScript engine. +

    + +

    +

    +
    See Also:
    Serialized Form
    +
    + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +Method Summary
    + intcolumnNumber() + +
    +          The column number of the location of the error, or zero if unknown.
    + java.lang.Stringdetails() + +
    +           
    + java.lang.StringgetMessage() + +
    +           
    + java.lang.StringgetScriptStackTrace() + +
    +          Get a string representing the script stack of this exception.
    + java.lang.StringgetScriptStackTrace(java.io.FilenameFilter filter) + +
    +          Get a string representing the script stack of this exception.
    + voidinitColumnNumber(int columnNumber) + +
    +          Initialize the column number of the script statement causing the error.
    + voidinitLineNumber(int lineNumber) + +
    +          Initialize the line number of the script statement causing the error.
    + voidinitLineSource(java.lang.String lineSource) + +
    +          Initialize the text of the source line containing the error.
    + voidinitSourceName(java.lang.String sourceName) + +
    +          Initialize the uri of the script source containing the error.
    + intlineNumber() + +
    +          Returns the line number of the statement causing the error, + or zero if not available.
    + java.lang.StringlineSource() + +
    +          The source text of the line causing the error, or null if unknown.
    + voidprintStackTrace(java.io.PrintStream s) + +
    +           
    + voidprintStackTrace(java.io.PrintWriter s) + +
    +           
    + java.lang.StringsourceName() + +
    +          Get the uri of the script source containing the error, or null + if that information is not available.
    + + + + + + + +
    Methods inherited from class java.lang.Throwable
    fillInStackTrace, getCause, getLocalizedMessage, getStackTrace, initCause, printStackTrace, setStackTrace, toString
    + + + + + + + +
    Methods inherited from class java.lang.Object
    clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
    +  +

    + + + + + + + + +
    +Method Detail
    + +

    +getMessage

    +
    +public final java.lang.String getMessage()
    +
    +
    +
    Overrides:
    getMessage in class java.lang.Throwable
    +
    +
    +
    +
    +
    +
    + +

    +details

    +
    +public java.lang.String details()
    +
    +
    +
    +
    +
    +
    + +

    +sourceName

    +
    +public final java.lang.String sourceName()
    +
    +
    Get the uri of the script source containing the error, or null + if that information is not available. +

    +

    +
    +
    +
    +
    + +

    +initSourceName

    +
    +public final void initSourceName(java.lang.String sourceName)
    +
    +
    Initialize the uri of the script source containing the error. +

    +

    +
    Parameters:
    sourceName - the uri of the script source responsible for the error. + It should not be null. +
    Throws: +
    java.lang.IllegalStateException - if the method is called more then once.
    +
    +
    +
    + +

    +lineNumber

    +
    +public final int lineNumber()
    +
    +
    Returns the line number of the statement causing the error, + or zero if not available. +

    +

    +
    +
    +
    +
    + +

    +initLineNumber

    +
    +public final void initLineNumber(int lineNumber)
    +
    +
    Initialize the line number of the script statement causing the error. +

    +

    +
    Parameters:
    lineNumber - the line number in the script source. + It should be positive number. +
    Throws: +
    java.lang.IllegalStateException - if the method is called more then once.
    +
    +
    +
    + +

    +columnNumber

    +
    +public final int columnNumber()
    +
    +
    The column number of the location of the error, or zero if unknown. +

    +

    +
    +
    +
    +
    + +

    +initColumnNumber

    +
    +public final void initColumnNumber(int columnNumber)
    +
    +
    Initialize the column number of the script statement causing the error. +

    +

    +
    Parameters:
    columnNumber - the column number in the script source. + It should be positive number. +
    Throws: +
    java.lang.IllegalStateException - if the method is called more then once.
    +
    +
    +
    + +

    +lineSource

    +
    +public final java.lang.String lineSource()
    +
    +
    The source text of the line causing the error, or null if unknown. +

    +

    +
    +
    +
    +
    + +

    +initLineSource

    +
    +public final void initLineSource(java.lang.String lineSource)
    +
    +
    Initialize the text of the source line containing the error. +

    +

    +
    Parameters:
    lineSource - the text of the source line responsible for the error. + It should not be null. +
    Throws: +
    java.lang.IllegalStateException - if the method is called more then once.
    +
    +
    +
    + +

    +getScriptStackTrace

    +
    +public java.lang.String getScriptStackTrace()
    +
    +
    Get a string representing the script stack of this exception. + If optimization is enabled, this corresponds to all java stack elements + with a source name ending with ".js". +

    +

    + +
    Returns:
    a script stack dump
    Since:
    +
    1.6R6
    +
    +
    +
    +
    + +

    +getScriptStackTrace

    +
    +public java.lang.String getScriptStackTrace(java.io.FilenameFilter filter)
    +
    +
    Get a string representing the script stack of this exception. + If optimization is enabled, this corresponds to all java stack elements + with a source name matching the filter. +

    +

    +
    Parameters:
    filter - the file name filter to determine whether a file is a + script file +
    Returns:
    a script stack dump
    Since:
    +
    1.6R6
    +
    +
    +
    +
    + +

    +printStackTrace

    +
    +public void printStackTrace(java.io.PrintWriter s)
    +
    +
    +
    Overrides:
    printStackTrace in class java.lang.Throwable
    +
    +
    +
    +
    +
    +
    + +

    +printStackTrace

    +
    +public void printStackTrace(java.io.PrintStream s)
    +
    +
    +
    Overrides:
    printStackTrace in class java.lang.Throwable
    +
    +
    +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/Script.html b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/Script.html new file mode 100644 index 0000000..65f250f --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/Script.html @@ -0,0 +1,231 @@ + + + + + + +Script (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + +

    + +org.mozilla.javascript +
    +Interface Script

    +
    +
    +
    public interface Script
    + + +

    +All compiled scripts implement this interface. +

    + This class encapsulates script execution relative to an + object scope. +

    + +

    +

    +
    Since:
    +
    1.3
    +
    Author:
    +
    Norris Boyd
    +
    +
    + +

    + + + + + + + + + + + + +
    +Method Summary
    + java.lang.Objectexec(Context cx, + Scriptable scope) + +
    +          Execute the script.
    +  +

    + + + + + + + + +
    +Method Detail
    + +

    +exec

    +
    +java.lang.Object exec(Context cx,
    +                      Scriptable scope)
    +
    +
    Execute the script. +

    + The script is executed in a particular runtime Context, which + must be associated with the current thread. + The script is executed relative to a scope--definitions and + uses of global top-level variables and functions will access + properties of the scope object. For compliant ECMA + programs, the scope must be an object that has been initialized + as a global object using Context.initStandardObjects. +

    +

    +

    +
    Parameters:
    cx - the Context associated with the current thread
    scope - the scope to execute relative to +
    Returns:
    the result of executing the script
    See Also:
    Context.initStandardObjects()
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/Scriptable.html b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/Scriptable.html new file mode 100644 index 0000000..14ab7aa --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/Scriptable.html @@ -0,0 +1,756 @@ + + + + + + +Scriptable (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + +

    + +org.mozilla.javascript +
    +Interface Scriptable

    +
    +
    All Known Subinterfaces:
    Function
    +
    +
    +
    All Known Implementing Classes:
    org.mozilla.javascript.BaseFunction, org.mozilla.javascript.Delegator, FunctionObject, org.mozilla.javascript.IdScriptableObject, ImporterTopLevel, ScriptableObject, Synchronizer
    +
    +
    +
    +
    public interface Scriptable
    + + +

    +This is interface that all objects in JavaScript must implement. + The interface provides for the management of properties and for + performing conversions. +

    + Host system implementors may find it easier to extend the ScriptableObject + class rather than implementing Scriptable when writing host objects. +

    + There are many static methods defined in ScriptableObject that perform + the multiple calls to the Scriptable interface needed in order to + manipulate properties in prototype chains. +

    +

    + +

    +

    +
    Author:
    +
    Norris Boyd, Nick Thompson, Brendan Eich
    +
    See Also:
    ScriptableObject
    +
    + +

    + + + + + + + + + + + +
    +Field Summary
    +static java.lang.ObjectNOT_FOUND + +
    +          Value returned from get if the property is not + found.
    +  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +Method Summary
    + voiddelete(int index) + +
    +          Removes a property from this object.
    + voiddelete(java.lang.String name) + +
    +          Removes a property from this object.
    + java.lang.Objectget(int index, + Scriptable start) + +
    +          Get a property from the object selected by an integral index.
    + java.lang.Objectget(java.lang.String name, + Scriptable start) + +
    +          Get a named property from the object.
    + java.lang.StringgetClassName() + +
    +          Get the name of the set of objects implemented by this Java class.
    + java.lang.ObjectgetDefaultValue(java.lang.Class hint) + +
    +          Get the default value of the object with a given hint.
    + java.lang.Object[]getIds() + +
    +          Get an array of property ids.
    + ScriptablegetParentScope() + +
    +          Get the parent scope of the object.
    + ScriptablegetPrototype() + +
    +          Get the prototype of the object.
    + booleanhas(int index, + Scriptable start) + +
    +          Indicates whether or not an indexed property is defined in an object.
    + booleanhas(java.lang.String name, + Scriptable start) + +
    +          Indicates whether or not a named property is defined in an object.
    + booleanhasInstance(Scriptable instance) + +
    +          The instanceof operator.
    + voidput(int index, + Scriptable start, + java.lang.Object value) + +
    +          Sets an indexed property in this object.
    + voidput(java.lang.String name, + Scriptable start, + java.lang.Object value) + +
    +          Sets a named property in this object.
    + voidsetParentScope(Scriptable parent) + +
    +          Set the parent scope of the object.
    + voidsetPrototype(Scriptable prototype) + +
    +          Set the prototype of the object.
    +  +

    + + + + + + + + +
    +Field Detail
    + +

    +NOT_FOUND

    +
    +static final java.lang.Object NOT_FOUND
    +
    +
    Value returned from get if the property is not + found. +

    +

    +
    +
    + + + + + + + + +
    +Method Detail
    + +

    +getClassName

    +
    +java.lang.String getClassName()
    +
    +
    Get the name of the set of objects implemented by this Java class. + This corresponds to the [[Class]] operation in ECMA and is used + by Object.prototype.toString() in ECMA.

    + See ECMA 8.6.2 and 15.2.4.2. +

    +

    +
    +
    +
    +
    + +

    +get

    +
    +java.lang.Object get(java.lang.String name,
    +                     Scriptable start)
    +
    +
    Get a named property from the object. + + Looks property up in this object and returns the associated value + if found. Returns NOT_FOUND if not found. + Note that this method is not expected to traverse the prototype + chain. This is different from the ECMA [[Get]] operation. + + Depending on the property selector, the runtime will call + this method or the form of get that takes an + integer: + + + + + + + + + + +
    JavaScript codeJava code
    a.b a.get("b", a)
    a["foo"] a.get("foo", a)
    a[3] a.get(3, a)
    a["3"] a.get(3, a)
    a[3.0] a.get(3, a)
    a["3.0"] a.get("3.0", a)
    a[1.1] a.get("1.1", a)
    a[-4] a.get(-4, a)
    +

    + The values that may be returned are limited to the following: +

      +
    • java.lang.Boolean objects
    • +
    • java.lang.String objects
    • +
    • java.lang.Number objects
    • +
    • org.mozilla.javascript.Scriptable objects
    • +
    • null
    • +
    • The value returned by Context.getUndefinedValue()
    • +
    • NOT_FOUND
    • +
    +

    +

    +
    Parameters:
    name - the name of the property
    start - the object in which the lookup began +
    Returns:
    the value of the property (may be null), or NOT_FOUND
    See Also:
    Context.getUndefinedValue()
    +
    +
    +
    + +

    +get

    +
    +java.lang.Object get(int index,
    +                     Scriptable start)
    +
    +
    Get a property from the object selected by an integral index. + + Identical to get(String, Scriptable) except that + an integral index is used to select the property. +

    +

    +
    Parameters:
    index - the numeric index for the property
    start - the object in which the lookup began +
    Returns:
    the value of the property (may be null), or NOT_FOUND
    See Also:
    get(String,Scriptable)
    +
    +
    +
    + +

    +has

    +
    +boolean has(java.lang.String name,
    +            Scriptable start)
    +
    +
    Indicates whether or not a named property is defined in an object. + + Does not traverse the prototype chain.

    + + The property is specified by a String name + as defined for the get method.

    +

    +

    +
    Parameters:
    name - the name of the property
    start - the object in which the lookup began +
    Returns:
    true if and only if the named property is found in the object
    See Also:
    get(String, Scriptable), +ScriptableObject.getProperty(Scriptable, String)
    +
    +
    +
    + +

    +has

    +
    +boolean has(int index,
    +            Scriptable start)
    +
    +
    Indicates whether or not an indexed property is defined in an object. + + Does not traverse the prototype chain.

    + + The property is specified by an integral index + as defined for the get method.

    +

    +

    +
    Parameters:
    index - the numeric index for the property
    start - the object in which the lookup began +
    Returns:
    true if and only if the indexed property is found in the object
    See Also:
    get(int, Scriptable), +ScriptableObject.getProperty(Scriptable, int)
    +
    +
    +
    + +

    +put

    +
    +void put(java.lang.String name,
    +         Scriptable start,
    +         java.lang.Object value)
    +
    +
    Sets a named property in this object. +

    + The property is specified by a string name + as defined for get. +

    + The possible values that may be passed in are as defined for + get. A class that implements this method may choose + to ignore calls to set certain properties, in which case those + properties are effectively read-only.

    + For properties defined in a prototype chain, + use putProperty in ScriptableObject.

    + Note that if a property a is defined in the prototype p + of an object o, then evaluating o.a = 23 will cause + set to be called on the prototype p with + o as the start parameter. + To preserve JavaScript semantics, it is the Scriptable + object's responsibility to modify o.

    + This design allows properties to be defined in prototypes and implemented + in terms of getters and setters of Java values without consuming slots + in each instance.

    +

    + The values that may be set are limited to the following: +

      +
    • java.lang.Boolean objects
    • +
    • java.lang.String objects
    • +
    • java.lang.Number objects
    • +
    • org.mozilla.javascript.Scriptable objects
    • +
    • null
    • +
    • The value returned by Context.getUndefinedValue()
    • +

    + Arbitrary Java objects may be wrapped in a Scriptable by first calling + Context.toObject. This allows the property of a JavaScript + object to contain an arbitrary Java object as a value.

    + Note that has will be called by the runtime first before + set is called to determine in which object the + property is defined. + Note that this method is not expected to traverse the prototype chain, + which is different from the ECMA [[Put]] operation. +

    +

    +
    Parameters:
    name - the name of the property
    start - the object whose property is being set
    value - value to set the property to
    See Also:
    has(String, Scriptable), +get(String, Scriptable), +ScriptableObject.putProperty(Scriptable, String, Object), +Context.toObject(Object, Scriptable)
    +
    +
    +
    + +

    +put

    +
    +void put(int index,
    +         Scriptable start,
    +         java.lang.Object value)
    +
    +
    Sets an indexed property in this object. +

    + The property is specified by an integral index + as defined for get.

    + + Identical to put(String, Scriptable, Object) except that + an integral index is used to select the property. +

    +

    +
    Parameters:
    index - the numeric index for the property
    start - the object whose property is being set
    value - value to set the property to
    See Also:
    has(int, Scriptable), +get(int, Scriptable), +ScriptableObject.putProperty(Scriptable, int, Object), +Context.toObject(Object, Scriptable)
    +
    +
    +
    + +

    +delete

    +
    +void delete(java.lang.String name)
    +
    +
    Removes a property from this object. + This operation corresponds to the ECMA [[Delete]] except that + the no result is returned. The runtime will guarantee that this + method is called only if the property exists. After this method + is called, the runtime will call Scriptable.has to see if the + property has been removed in order to determine the boolean + result of the delete operator as defined by ECMA 11.4.1. +

    + A property can be made permanent by ignoring calls to remove + it.

    + The property is specified by a String name + as defined for get. +

    + To delete properties defined in a prototype chain, + see deleteProperty in ScriptableObject. +

    +

    +
    Parameters:
    name - the identifier for the property
    See Also:
    get(String, Scriptable), +ScriptableObject.deleteProperty(Scriptable, String)
    +
    +
    +
    + +

    +delete

    +
    +void delete(int index)
    +
    +
    Removes a property from this object. + + The property is specified by an integral index + as defined for get. +

    + To delete properties defined in a prototype chain, + see deleteProperty in ScriptableObject. + + Identical to delete(String) except that + an integral index is used to select the property. +

    +

    +
    Parameters:
    index - the numeric index for the property
    See Also:
    get(int, Scriptable), +ScriptableObject.deleteProperty(Scriptable, int)
    +
    +
    +
    + +

    +getPrototype

    +
    +Scriptable getPrototype()
    +
    +
    Get the prototype of the object. +

    +

    + +
    Returns:
    the prototype
    +
    +
    +
    + +

    +setPrototype

    +
    +void setPrototype(Scriptable prototype)
    +
    +
    Set the prototype of the object. +

    +

    +
    Parameters:
    prototype - the prototype to set
    +
    +
    +
    + +

    +getParentScope

    +
    +Scriptable getParentScope()
    +
    +
    Get the parent scope of the object. +

    +

    + +
    Returns:
    the parent scope
    +
    +
    +
    + +

    +setParentScope

    +
    +void setParentScope(Scriptable parent)
    +
    +
    Set the parent scope of the object. +

    +

    +
    Parameters:
    parent - the parent scope to set
    +
    +
    +
    + +

    +getIds

    +
    +java.lang.Object[] getIds()
    +
    +
    Get an array of property ids. + + Not all property ids need be returned. Those properties + whose ids are not returned are considered non-enumerable. +

    +

    + +
    Returns:
    an array of Objects. Each entry in the array is either + a java.lang.String or a java.lang.Number
    +
    +
    +
    + +

    +getDefaultValue

    +
    +java.lang.Object getDefaultValue(java.lang.Class hint)
    +
    +
    Get the default value of the object with a given hint. + The hints are String.class for type String, Number.class for type + Number, Scriptable.class for type Object, and Boolean.class for + type Boolean.

    + + A hint of null means "no hint". + + See ECMA 8.6.2.6. +

    +

    +
    Parameters:
    hint - the type hint +
    Returns:
    the default value
    +
    +
    +
    + +

    +hasInstance

    +
    +boolean hasInstance(Scriptable instance)
    +
    +
    The instanceof operator. + +

    + The JavaScript code "lhs instanceof rhs" causes rhs.hasInstance(lhs) to + be called. + +

    + The return value is implementation dependent so that embedded host objects can + return an appropriate value. See the JS 1.3 language documentation for more + detail. + +

    This operator corresponds to the proposed EMCA [[HasInstance]] operator. +

    +

    +
    Parameters:
    instance - The value that appeared on the LHS of the instanceof + operator +
    Returns:
    an implementation dependent value
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/ScriptableObject.html b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/ScriptableObject.html new file mode 100644 index 0000000..fce83d6 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/ScriptableObject.html @@ -0,0 +1,2604 @@ + + + + + + +ScriptableObject (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + +

    + +org.mozilla.javascript +
    +Class ScriptableObject

    +
    +java.lang.Object
    +  extended by org.mozilla.javascript.ScriptableObject
    +
    +
    +
    All Implemented Interfaces:
    java.io.Serializable, org.mozilla.javascript.ConstProperties, org.mozilla.javascript.debug.DebuggableObject, Scriptable
    +
    +
    +
    Direct Known Subclasses:
    org.mozilla.javascript.IdScriptableObject
    +
    +
    +
    +
    public abstract class ScriptableObject
    extends java.lang.Object
    implements Scriptable, java.io.Serializable, org.mozilla.javascript.debug.DebuggableObject, org.mozilla.javascript.ConstProperties
    + + +

    +This is the default implementation of the Scriptable interface. This + class provides convenient default behavior that makes it easier to + define host objects. +

    + Various properties and methods of JavaScript objects can be conveniently + defined using methods of ScriptableObject. +

    + Classes extending ScriptableObject must define the getClassName method. +

    + +

    +

    +
    Author:
    +
    Norris Boyd
    +
    See Also:
    Scriptable, +Serialized Form
    +
    + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +Field Summary
    +static intCONST + +
    +           
    +static intDONTENUM + +
    +          Property attribute indicating property is not enumerated.
    +static intEMPTY + +
    +          The empty property attribute.
    +static intPERMANENT + +
    +          Property attribute indicating property cannot be deleted.
    +static intREADONLY + +
    +          Property attribute indicating assignment to this property is ignored.
    +static intUNINITIALIZED_CONST + +
    +          Property attribute indicating that this is a const property that has not + been assigned yet.
    + + + + + + + +
    Fields inherited from interface org.mozilla.javascript.Scriptable
    NOT_FOUND
    +  + + + + + + + + + + + + + +
    +Constructor Summary
    ScriptableObject() + +
    +           
    ScriptableObject(Scriptable scope, + Scriptable prototype) + +
    +           
    +  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +Method Summary
    + java.lang.ObjectassociateValue(java.lang.Object key, + java.lang.Object value) + +
    +          Associate arbitrary application-specific value with this object.
    + booleanavoidObjectDetection() + +
    +          Emulate the SpiderMonkey (and Firefox) feature of allowing + custom objects to avoid detection by normal "object detection" + code patterns.
    +static java.lang.ObjectcallMethod(Context cx, + Scriptable obj, + java.lang.String methodName, + java.lang.Object[] args) + +
    +          Call a method of an object.
    +static java.lang.ObjectcallMethod(Scriptable obj, + java.lang.String methodName, + java.lang.Object[] args) + +
    +          Call a method of an object.
    +static voiddefineClass(Scriptable scope, + java.lang.Class clazz) + +
    +          Defines JavaScript objects from a Java class that implements Scriptable.
    +static voiddefineClass(Scriptable scope, + java.lang.Class clazz, + boolean sealed) + +
    +          Defines JavaScript objects from a Java class, optionally + allowing sealing.
    +static java.lang.StringdefineClass(Scriptable scope, + java.lang.Class clazz, + boolean sealed, + boolean mapInheritance) + +
    +          Defines JavaScript objects from a Java class, optionally + allowing sealing and mapping of Java inheritance to JavaScript + prototype-based inheritance.
    + voiddefineConst(java.lang.String name, + Scriptable start) + +
    +          Reserves a definition spot for a const.
    +static voiddefineConstProperty(Scriptable destination, + java.lang.String propertyName) + +
    +          Utility method to add properties to arbitrary Scriptable object.
    + voiddefineFunctionProperties(java.lang.String[] names, + java.lang.Class clazz, + int attributes) + +
    +          Search for names in a class, adding the resulting methods + as properties.
    +static voiddefineProperty(Scriptable destination, + java.lang.String propertyName, + java.lang.Object value, + int attributes) + +
    +          Utility method to add properties to arbitrary Scriptable object.
    + voiddefineProperty(java.lang.String propertyName, + java.lang.Class clazz, + int attributes) + +
    +          Define a JavaScript property with getter and setter side effects.
    + voiddefineProperty(java.lang.String propertyName, + java.lang.Object value, + int attributes) + +
    +          Define a JavaScript property.
    + voiddefineProperty(java.lang.String propertyName, + java.lang.Object delegateTo, + java.lang.reflect.Method getter, + java.lang.reflect.Method setter, + int attributes) + +
    +          Define a JavaScript property.
    + voiddelete(int index) + +
    +          Removes the indexed property from the object.
    + voiddelete(java.lang.String name) + +
    +          Removes a named property from the object.
    +static booleandeleteProperty(Scriptable obj, + int index) + +
    +          Removes the property from an object or its prototype chain.
    +static booleandeleteProperty(Scriptable obj, + java.lang.String name) + +
    +          Removes the property from an object or its prototype chain.
    +protected  java.lang.ObjectequivalentValues(java.lang.Object value) + +
    +          Custom == operator.
    + java.lang.Objectget(int index, + Scriptable start) + +
    +          Returns the value of the indexed property or NOT_FOUND.
    + java.lang.Objectget(java.lang.String name, + Scriptable start) + +
    +          Returns the value of the named property or NOT_FOUND.
    + java.lang.Object[]getAllIds() + +
    +          Returns an array of ids for the properties of the object.
    + java.lang.ObjectgetAssociatedValue(java.lang.Object key) + +
    +          Get arbitrary application-specific value associated with this object.
    + intgetAttributes(int index) + +
    +          Get the attributes of an indexed property.
    + intgetAttributes(int index, + Scriptable start) + +
    +          Deprecated. Use getAttributes(int index). The engine always + ignored the start argument.
    + intgetAttributes(java.lang.String name) + +
    +          Get the attributes of a named property.
    + intgetAttributes(java.lang.String name, + Scriptable start) + +
    +          Deprecated. Use getAttributes(String name). The engine always + ignored the start argument.
    +abstract  java.lang.StringgetClassName() + +
    +          Return the name of the class.
    +static ScriptablegetClassPrototype(Scriptable scope, + java.lang.String className) + +
    +          Get the prototype for the named class.
    + java.lang.ObjectgetDefaultValue(java.lang.Class typeHint) + +
    +          Implements the [[DefaultValue]] internal method.
    +static java.lang.ObjectgetDefaultValue(Scriptable object, + java.lang.Class typeHint) + +
    +           
    +static ScriptablegetFunctionPrototype(Scriptable scope) + +
    +          Get the Function.prototype property.
    + java.lang.ObjectgetGetterOrSetter(java.lang.String name, + int index, + boolean isSetter) + +
    +          Get the getter or setter for a given property.
    + java.lang.Object[]getIds() + +
    +          Returns an array of ids for the properties of the object.
    +static ScriptablegetObjectPrototype(Scriptable scope) + +
    +          Get the Object.prototype property.
    + ScriptablegetParentScope() + +
    +          Returns the parent (enclosing) scope of the object.
    +static java.lang.ObjectgetProperty(Scriptable obj, + int index) + +
    +          Gets an indexed property from an object or any object in its prototype chain.
    +static java.lang.ObjectgetProperty(Scriptable obj, + java.lang.String name) + +
    +          Gets a named property from an object or any object in its prototype chain.
    +static java.lang.Object[]getPropertyIds(Scriptable obj) + +
    +          Returns an array of all ids from an object and its prototypes.
    + ScriptablegetPrototype() + +
    +          Returns the prototype of the object.
    +static ScriptablegetTopLevelScope(Scriptable obj) + +
    +          Get the global scope.
    +static java.lang.ObjectgetTopScopeValue(Scriptable scope, + java.lang.Object key) + +
    +          Get arbitrary application-specific value associated with the top scope + of the given scope.
    + booleanhas(int index, + Scriptable start) + +
    +          Returns true if the property index is defined.
    + booleanhas(java.lang.String name, + Scriptable start) + +
    +          Returns true if the named property is defined.
    + booleanhasInstance(Scriptable instance) + +
    +          Implements the instanceof operator.
    +static booleanhasProperty(Scriptable obj, + int index) + +
    +          Returns whether an indexed property is defined in an object or any object + in its prototype chain.
    +static booleanhasProperty(Scriptable obj, + java.lang.String name) + +
    +          Returns whether a named property is defined in an object or any object + in its prototype chain.
    + booleanisConst(java.lang.String name) + +
    +          Returns true if the named property is defined as a const on this object.
    +protected  booleanisGetterOrSetter(java.lang.String name, + int index, + boolean setter) + +
    +          Returns whether a property is a getter or a setter
    + booleanisSealed() + +
    +          Return true if this object is sealed.
    + voidput(int index, + Scriptable start, + java.lang.Object value) + +
    +          Sets the value of the indexed property, creating it if need be.
    + voidput(java.lang.String name, + Scriptable start, + java.lang.Object value) + +
    +          Sets the value of the named property, creating it if need be.
    + voidputConst(java.lang.String name, + Scriptable start, + java.lang.Object value) + +
    +          Sets the value of the named const property, creating it if need be.
    +static voidputConstProperty(Scriptable obj, + java.lang.String name, + java.lang.Object value) + +
    +          Puts a named property in an object or in an object in its prototype chain.
    +static voidputProperty(Scriptable obj, + int index, + java.lang.Object value) + +
    +          Puts an indexed property in an object or in an object in its prototype chain.
    +static voidputProperty(Scriptable obj, + java.lang.String name, + java.lang.Object value) + +
    +          Puts a named property in an object or in an object in its prototype chain.
    +static voidredefineProperty(Scriptable obj, + java.lang.String name, + boolean isConst) + +
    +          If hasProperty(obj, name) would return true, then if the property that + was found is compatible with the new property, this method just returns.
    + voidsealObject() + +
    +          Seal this object.
    + voidsetAttributes(int index, + int attributes) + +
    +          Set the attributes of an indexed property.
    + voidsetAttributes(int index, + Scriptable start, + int attributes) + +
    +          Deprecated. Use setAttributes(int index, int attributes). + The engine always ignored the start argument.
    + voidsetAttributes(java.lang.String name, + int attributes) + +
    +          Set the attributes of a named property.
    + voidsetAttributes(java.lang.String name, + Scriptable start, + int attributes) + +
    +          Deprecated. Use setAttributes(String name, int attributes). + The engine always ignored the start argument.
    + voidsetGetterOrSetter(java.lang.String name, + int index, + Callable getterOrSeter, + boolean isSetter) + +
    +          XXX: write docs.
    + voidsetParentScope(Scriptable m) + +
    +          Sets the parent (enclosing) scope of the object.
    + voidsetPrototype(Scriptable m) + +
    +          Sets the prototype of the object.
    + + + + + + + +
    Methods inherited from class java.lang.Object
    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    +  +

    + + + + + + + + +
    +Field Detail
    + +

    +EMPTY

    +
    +public static final int EMPTY
    +
    +
    The empty property attribute. + + Used by getAttributes() and setAttributes(). +

    +

    +
    See Also:
    getAttributes(String), +setAttributes(String, int), +Constant Field Values
    +
    +
    + +

    +READONLY

    +
    +public static final int READONLY
    +
    +
    Property attribute indicating assignment to this property is ignored. +

    +

    +
    See Also:
    #put(String, Scriptable, Object), +getAttributes(String), +setAttributes(String, int), +Constant Field Values
    +
    +
    + +

    +DONTENUM

    +
    +public static final int DONTENUM
    +
    +
    Property attribute indicating property is not enumerated. + + Only enumerated properties will be returned by getIds(). +

    +

    +
    See Also:
    getIds(), +getAttributes(String), +setAttributes(String, int), +Constant Field Values
    +
    +
    + +

    +PERMANENT

    +
    +public static final int PERMANENT
    +
    +
    Property attribute indicating property cannot be deleted. +

    +

    +
    See Also:
    delete(String), +getAttributes(String), +setAttributes(String, int), +Constant Field Values
    +
    +
    + +

    +UNINITIALIZED_CONST

    +
    +public static final int UNINITIALIZED_CONST
    +
    +
    Property attribute indicating that this is a const property that has not + been assigned yet. The first 'const' assignment to the property will + clear this bit. +

    +

    +
    See Also:
    Constant Field Values
    +
    +
    + +

    +CONST

    +
    +public static final int CONST
    +
    +
    +
    See Also:
    Constant Field Values
    +
    + + + + + + + + +
    +Constructor Detail
    + +

    +ScriptableObject

    +
    +public ScriptableObject()
    +
    +
    +
    + +

    +ScriptableObject

    +
    +public ScriptableObject(Scriptable scope,
    +                        Scriptable prototype)
    +
    +
    + + + + + + + + +
    +Method Detail
    + +

    +getClassName

    +
    +public abstract java.lang.String getClassName()
    +
    +
    Return the name of the class. + + This is typically the same name as the constructor. + Classes extending ScriptableObject must implement this abstract + method. +

    +

    +
    Specified by:
    getClassName in interface Scriptable
    +
    +
    +
    +
    +
    +
    + +

    +has

    +
    +public boolean has(java.lang.String name,
    +                   Scriptable start)
    +
    +
    Returns true if the named property is defined. +

    +

    +
    Specified by:
    has in interface Scriptable
    +
    +
    +
    Parameters:
    name - the name of the property
    start - the object in which the lookup began +
    Returns:
    true if and only if the property was found in the object
    See Also:
    Scriptable.get(String, Scriptable), +getProperty(Scriptable, String)
    +
    +
    +
    + +

    +has

    +
    +public boolean has(int index,
    +                   Scriptable start)
    +
    +
    Returns true if the property index is defined. +

    +

    +
    Specified by:
    has in interface Scriptable
    +
    +
    +
    Parameters:
    index - the numeric index for the property
    start - the object in which the lookup began +
    Returns:
    true if and only if the property was found in the object
    See Also:
    Scriptable.get(int, Scriptable), +getProperty(Scriptable, int)
    +
    +
    +
    + +

    +get

    +
    +public java.lang.Object get(java.lang.String name,
    +                            Scriptable start)
    +
    +
    Returns the value of the named property or NOT_FOUND. + + If the property was created using defineProperty, the + appropriate getter method is called. +

    +

    +
    Specified by:
    get in interface Scriptable
    +
    +
    +
    Parameters:
    name - the name of the property
    start - the object in which the lookup began +
    Returns:
    the value of the property (may be null), or NOT_FOUND
    See Also:
    Context.getUndefinedValue()
    +
    +
    +
    + +

    +get

    +
    +public java.lang.Object get(int index,
    +                            Scriptable start)
    +
    +
    Returns the value of the indexed property or NOT_FOUND. +

    +

    +
    Specified by:
    get in interface Scriptable
    +
    +
    +
    Parameters:
    index - the numeric index for the property
    start - the object in which the lookup began +
    Returns:
    the value of the property (may be null), or NOT_FOUND
    See Also:
    Scriptable.get(String,Scriptable)
    +
    +
    +
    + +

    +put

    +
    +public void put(java.lang.String name,
    +                Scriptable start,
    +                java.lang.Object value)
    +
    +
    Sets the value of the named property, creating it if need be. + + If the property was created using defineProperty, the + appropriate setter method is called.

    + + If the property's attributes include READONLY, no action is + taken. + This method will actually set the property in the start + object. +

    +

    +
    Specified by:
    put in interface Scriptable
    +
    +
    +
    Parameters:
    name - the name of the property
    start - the object whose property is being set
    value - value to set the property to
    See Also:
    Scriptable.has(String, Scriptable), +Scriptable.get(String, Scriptable), +putProperty(Scriptable, String, Object), +Context.toObject(Object, Scriptable)
    +
    +
    +
    + +

    +put

    +
    +public void put(int index,
    +                Scriptable start,
    +                java.lang.Object value)
    +
    +
    Sets the value of the indexed property, creating it if need be. +

    +

    +
    Specified by:
    put in interface Scriptable
    +
    +
    +
    Parameters:
    index - the numeric index for the property
    start - the object whose property is being set
    value - value to set the property to
    See Also:
    Scriptable.has(int, Scriptable), +Scriptable.get(int, Scriptable), +putProperty(Scriptable, int, Object), +Context.toObject(Object, Scriptable)
    +
    +
    +
    + +

    +delete

    +
    +public void delete(java.lang.String name)
    +
    +
    Removes a named property from the object. + + If the property is not found, or it has the PERMANENT attribute, + no action is taken. +

    +

    +
    Specified by:
    delete in interface Scriptable
    +
    +
    +
    Parameters:
    name - the name of the property
    See Also:
    Scriptable.get(String, Scriptable), +deleteProperty(Scriptable, String)
    +
    +
    +
    + +

    +delete

    +
    +public void delete(int index)
    +
    +
    Removes the indexed property from the object. + + If the property is not found, or it has the PERMANENT attribute, + no action is taken. +

    +

    +
    Specified by:
    delete in interface Scriptable
    +
    +
    +
    Parameters:
    index - the numeric index for the property
    See Also:
    Scriptable.get(int, Scriptable), +deleteProperty(Scriptable, int)
    +
    +
    +
    + +

    +putConst

    +
    +public void putConst(java.lang.String name,
    +                     Scriptable start,
    +                     java.lang.Object value)
    +
    +
    Sets the value of the named const property, creating it if need be. + + If the property was created using defineProperty, the + appropriate setter method is called.

    + + If the property's attributes include READONLY, no action is + taken. + This method will actually set the property in the start + object. +

    +

    +
    Specified by:
    putConst in interface org.mozilla.javascript.ConstProperties
    +
    +
    +
    Parameters:
    name - the name of the property
    start - the object whose property is being set
    value - value to set the property to
    See Also:
    Scriptable.has(String, Scriptable), +Scriptable.get(String, Scriptable), +putProperty(Scriptable, String, Object), +Context.toObject(Object, Scriptable)
    +
    +
    +
    + +

    +defineConst

    +
    +public void defineConst(java.lang.String name,
    +                        Scriptable start)
    +
    +
    Description copied from interface: org.mozilla.javascript.ConstProperties
    +
    Reserves a definition spot for a const. This will set up a definition + of the const property, but set its value to undefined. The semantics of + the start parameter is the same as for putConst. +

    +

    +
    Specified by:
    defineConst in interface org.mozilla.javascript.ConstProperties
    +
    +
    +
    Parameters:
    name - The name of the property.
    start - The object whose property is being reserved.
    +
    +
    +
    + +

    +isConst

    +
    +public boolean isConst(java.lang.String name)
    +
    +
    Returns true if the named property is defined as a const on this object. +

    +

    +
    Specified by:
    isConst in interface org.mozilla.javascript.ConstProperties
    +
    +
    +
    Parameters:
    name - +
    Returns:
    true if the named property is defined as a const, false + otherwise.
    +
    +
    +
    + +

    +getAttributes

    +
    +public final int getAttributes(java.lang.String name,
    +                               Scriptable start)
    +
    +
    Deprecated. Use getAttributes(String name). The engine always + ignored the start argument. +

    +

    +
    +
    +
    +
    +
    +
    +
    + +

    +getAttributes

    +
    +public final int getAttributes(int index,
    +                               Scriptable start)
    +
    +
    Deprecated. Use getAttributes(int index). The engine always + ignored the start argument. +

    +

    +
    +
    +
    +
    +
    +
    +
    + +

    +setAttributes

    +
    +public final void setAttributes(java.lang.String name,
    +                                Scriptable start,
    +                                int attributes)
    +
    +
    Deprecated. Use setAttributes(String name, int attributes). + The engine always ignored the start argument. +

    +

    +
    +
    +
    +
    +
    +
    +
    + +

    +setAttributes

    +
    +public void setAttributes(int index,
    +                          Scriptable start,
    +                          int attributes)
    +
    +
    Deprecated. Use setAttributes(int index, int attributes). + The engine always ignored the start argument. +

    +

    +
    +
    +
    +
    +
    +
    +
    + +

    +getAttributes

    +
    +public int getAttributes(java.lang.String name)
    +
    +
    Get the attributes of a named property. + + The property is specified by name + as defined for has.

    +

    +

    +
    +
    +
    +
    Parameters:
    name - the identifier for the property +
    Returns:
    the bitset of attributes +
    Throws: +
    EvaluatorException - if the named property is not found
    See Also:
    has(String, Scriptable), +READONLY, +DONTENUM, +PERMANENT, +EMPTY
    +
    +
    +
    + +

    +getAttributes

    +
    +public int getAttributes(int index)
    +
    +
    Get the attributes of an indexed property. +

    +

    +
    +
    +
    +
    Parameters:
    index - the numeric index for the property +
    Returns:
    the bitset of attributes +
    Throws: +
    EvaluatorException - if the named property is not found + is not found
    See Also:
    has(String, Scriptable), +READONLY, +DONTENUM, +PERMANENT, +EMPTY
    +
    +
    +
    + +

    +setAttributes

    +
    +public void setAttributes(java.lang.String name,
    +                          int attributes)
    +
    +
    Set the attributes of a named property. + + The property is specified by name + as defined for has.

    + + The possible attributes are READONLY, DONTENUM, + and PERMANENT. Combinations of attributes + are expressed by the bitwise OR of attributes. + EMPTY is the state of no attributes set. Any unused + bits are reserved for future use. +

    +

    +
    +
    +
    +
    Parameters:
    name - the name of the property
    attributes - the bitset of attributes +
    Throws: +
    EvaluatorException - if the named property is not found
    See Also:
    Scriptable.has(String, Scriptable), +READONLY, +DONTENUM, +PERMANENT, +EMPTY
    +
    +
    +
    + +

    +setAttributes

    +
    +public void setAttributes(int index,
    +                          int attributes)
    +
    +
    Set the attributes of an indexed property. +

    +

    +
    +
    +
    +
    Parameters:
    index - the numeric index for the property
    attributes - the bitset of attributes +
    Throws: +
    EvaluatorException - if the named property is not found
    See Also:
    Scriptable.has(String, Scriptable), +READONLY, +DONTENUM, +PERMANENT, +EMPTY
    +
    +
    +
    + +

    +setGetterOrSetter

    +
    +public void setGetterOrSetter(java.lang.String name,
    +                              int index,
    +                              Callable getterOrSeter,
    +                              boolean isSetter)
    +
    +
    XXX: write docs. +

    +

    +
    +
    +
    +
    +
    +
    +
    + +

    +getGetterOrSetter

    +
    +public java.lang.Object getGetterOrSetter(java.lang.String name,
    +                                          int index,
    +                                          boolean isSetter)
    +
    +
    Get the getter or setter for a given property. Used by __lookupGetter__ + and __lookupSetter__. +

    +

    +
    +
    +
    +
    Parameters:
    name - Name of the object. If nonnull, index must be 0.
    index - Index of the object. If nonzero, name must be null.
    isSetter - If true, return the setter, otherwise return the getter. +
    Returns:
    Null if the property does not exist. Otherwise returns either + the getter or the setter for the property, depending on + the value of isSetter (may be undefined if unset). +
    Throws: +
    java.lang.IllegalArgumentException - if both name and index are nonnull + and nonzero respectively.
    +
    +
    +
    + +

    +isGetterOrSetter

    +
    +protected boolean isGetterOrSetter(java.lang.String name,
    +                                   int index,
    +                                   boolean setter)
    +
    +
    Returns whether a property is a getter or a setter +

    +

    +
    +
    +
    +
    Parameters:
    name - property name
    index - property index
    setter - true to check for a setter, false for a getter +
    Returns:
    whether the property is a getter or a setter
    +
    +
    +
    + +

    +getPrototype

    +
    +public Scriptable getPrototype()
    +
    +
    Returns the prototype of the object. +

    +

    +
    Specified by:
    getPrototype in interface Scriptable
    +
    +
    + +
    Returns:
    the prototype
    +
    +
    +
    + +

    +setPrototype

    +
    +public void setPrototype(Scriptable m)
    +
    +
    Sets the prototype of the object. +

    +

    +
    Specified by:
    setPrototype in interface Scriptable
    +
    +
    +
    Parameters:
    m - the prototype to set
    +
    +
    +
    + +

    +getParentScope

    +
    +public Scriptable getParentScope()
    +
    +
    Returns the parent (enclosing) scope of the object. +

    +

    +
    Specified by:
    getParentScope in interface Scriptable
    +
    +
    + +
    Returns:
    the parent scope
    +
    +
    +
    + +

    +setParentScope

    +
    +public void setParentScope(Scriptable m)
    +
    +
    Sets the parent (enclosing) scope of the object. +

    +

    +
    Specified by:
    setParentScope in interface Scriptable
    +
    +
    +
    Parameters:
    m - the parent scope to set
    +
    +
    +
    + +

    +getIds

    +
    +public java.lang.Object[] getIds()
    +
    +
    Returns an array of ids for the properties of the object. + +

    Any properties with the attribute DONTENUM are not listed.

    +

    +

    +
    Specified by:
    getIds in interface Scriptable
    +
    +
    + +
    Returns:
    an array of java.lang.Objects with an entry for every + listed property. Properties accessed via an integer index will + have a corresponding + Integer entry in the returned array. Properties accessed by + a String will have a String entry in the returned array.
    +
    +
    +
    + +

    +getAllIds

    +
    +public java.lang.Object[] getAllIds()
    +
    +
    Returns an array of ids for the properties of the object. + +

    All properties, even those with attribute DONTENUM, are listed.

    +

    +

    +
    Specified by:
    getAllIds in interface org.mozilla.javascript.debug.DebuggableObject
    +
    +
    + +
    Returns:
    an array of java.lang.Objects with an entry for every + listed property. Properties accessed via an integer index will + have a corresponding + Integer entry in the returned array. Properties accessed by + a String will have a String entry in the returned array.
    +
    +
    +
    + +

    +getDefaultValue

    +
    +public java.lang.Object getDefaultValue(java.lang.Class typeHint)
    +
    +
    Implements the [[DefaultValue]] internal method. + +

    Note that the toPrimitive conversion is a no-op for + every type other than Object, for which [[DefaultValue]] + is called. See ECMA 9.1.

    + + A hint of null means "no hint". +

    +

    +
    Specified by:
    getDefaultValue in interface Scriptable
    +
    +
    +
    Parameters:
    typeHint - the type hint +
    Returns:
    the default value for the object + + See ECMA 8.6.2.6.
    +
    +
    +
    + +

    +getDefaultValue

    +
    +public static java.lang.Object getDefaultValue(Scriptable object,
    +                                               java.lang.Class typeHint)
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    +hasInstance

    +
    +public boolean hasInstance(Scriptable instance)
    +
    +
    Implements the instanceof operator. + +

    This operator has been proposed to ECMA. +

    +

    +
    Specified by:
    hasInstance in interface Scriptable
    +
    +
    +
    Parameters:
    instance - The value that appeared on the LHS of the instanceof + operator +
    Returns:
    true if "this" appears in value's prototype chain
    +
    +
    +
    + +

    +avoidObjectDetection

    +
    +public boolean avoidObjectDetection()
    +
    +
    Emulate the SpiderMonkey (and Firefox) feature of allowing + custom objects to avoid detection by normal "object detection" + code patterns. This is used to implement document.all. + See https://bugzilla.mozilla.org/show_bug.cgi?id=412247. + This is an analog to JOF_DETECTING from SpiderMonkey; see + https://bugzilla.mozilla.org/show_bug.cgi?id=248549. + Other than this special case, embeddings should return false. +

    +

    +
    +
    +
    + +
    Returns:
    true if this object should avoid object detection
    Since:
    +
    1.7R1
    +
    +
    +
    +
    + +

    +equivalentValues

    +
    +protected java.lang.Object equivalentValues(java.lang.Object value)
    +
    +
    Custom == operator. + Must return Scriptable.NOT_FOUND if this object does not + have custom equality operator for the given value, + Boolean.TRUE if this object is equivalent to value, + Boolean.FALSE if this object is not equivalent to + value. +

    + The default implementation returns Boolean.TRUE + if this == value or Scriptable.NOT_FOUND otherwise. + It indicates that by default custom equality is available only if + value is this in which case true is returned. +

    +

    +
    +
    +
    +
    +
    +
    +
    + +

    +defineClass

    +
    +public static void defineClass(Scriptable scope,
    +                               java.lang.Class clazz)
    +                        throws java.lang.IllegalAccessException,
    +                               java.lang.InstantiationException,
    +                               java.lang.reflect.InvocationTargetException
    +
    +
    Defines JavaScript objects from a Java class that implements Scriptable. + + If the given class has a method +
    + static void init(Context cx, Scriptable scope, boolean sealed);
    + + or its compatibility form +
    + static void init(Scriptable scope);
    + + then it is invoked and no further initialization is done.

    + + However, if no such a method is found, then the class's constructors and + methods are used to initialize a class in the following manner.

    + + First, the zero-parameter constructor of the class is called to + create the prototype. If no such constructor exists, + a EvaluatorException is thrown.

    + + Next, all methods are scanned for special prefixes that indicate that they + have special meaning for defining JavaScript objects. + These special prefixes are +

      +
    • jsFunction_ for a JavaScript function +
    • jsStaticFunction_ for a JavaScript function that + is a property of the constructor +
    • jsGet_ for a getter of a JavaScript property +
    • jsSet_ for a setter of a JavaScript property +
    • jsConstructor for a JavaScript function that + is the constructor +

    + + If the method's name begins with "jsFunction_", a JavaScript function + is created with a name formed from the rest of the Java method name + following "jsFunction_". So a Java method named "jsFunction_foo" will + define a JavaScript method "foo". Calling this JavaScript function + will cause the Java method to be called. The parameters of the method + must be of number and types as defined by the FunctionObject class. + The JavaScript function is then added as a property + of the prototype.

    + + If the method's name begins with "jsStaticFunction_", it is handled + similarly except that the resulting JavaScript function is added as a + property of the constructor object. The Java method must be static. + + If the method's name begins with "jsGet_" or "jsSet_", the method is + considered to define a property. Accesses to the defined property + will result in calls to these getter and setter methods. If no + setter is defined, the property is defined as READONLY.

    + + If the method's name is "jsConstructor", the method is + considered to define the body of the constructor. Only one + method of this name may be defined. + If no method is found that can serve as constructor, a Java + constructor will be selected to serve as the JavaScript + constructor in the following manner. If the class has only one + Java constructor, that constructor is used to define + the JavaScript constructor. If the the class has two constructors, + one must be the zero-argument constructor (otherwise an + EvaluatorException would have already been thrown + when the prototype was to be created). In this case + the Java constructor with one or more parameters will be used + to define the JavaScript constructor. If the class has three + or more constructors, an EvaluatorException + will be thrown.

    + + Finally, if there is a method +

    + static void finishInit(Scriptable scope, FunctionObject constructor,
    +                        Scriptable prototype)
    + + it will be called to finish any initialization. The scope + argument will be passed, along with the newly created constructor and + the newly created prototype.

    +

    +

    +
    +
    +
    +
    Parameters:
    scope - The scope in which to define the constructor.
    clazz - The Java class to use to define the JavaScript objects + and properties. +
    Throws: +
    java.lang.IllegalAccessException - if access is not available + to a reflected class member +
    java.lang.InstantiationException - if unable to instantiate + the named class +
    java.lang.reflect.InvocationTargetException - if an exception is thrown + during execution of methods of the named class
    See Also:
    Function, +FunctionObject, +READONLY, +#defineProperty(String, Class, int)
    +
    +
    +
    + +

    +defineClass

    +
    +public static void defineClass(Scriptable scope,
    +                               java.lang.Class clazz,
    +                               boolean sealed)
    +                        throws java.lang.IllegalAccessException,
    +                               java.lang.InstantiationException,
    +                               java.lang.reflect.InvocationTargetException
    +
    +
    Defines JavaScript objects from a Java class, optionally + allowing sealing. + + Similar to defineClass(Scriptable scope, Class clazz) + except that sealing is allowed. An object that is sealed cannot have + properties added or removed. Note that sealing is not allowed in + the current ECMA/ISO language specification, but is likely for + the next version. +

    +

    +
    +
    +
    +
    Parameters:
    scope - The scope in which to define the constructor.
    clazz - The Java class to use to define the JavaScript objects + and properties. The class must implement Scriptable.
    sealed - Whether or not to create sealed standard objects that + cannot be modified. +
    Throws: +
    java.lang.IllegalAccessException - if access is not available + to a reflected class member +
    java.lang.InstantiationException - if unable to instantiate + the named class +
    java.lang.reflect.InvocationTargetException - if an exception is thrown + during execution of methods of the named class
    Since:
    +
    1.4R3
    +
    +
    +
    +
    + +

    +defineClass

    +
    +public static java.lang.String defineClass(Scriptable scope,
    +                                           java.lang.Class clazz,
    +                                           boolean sealed,
    +                                           boolean mapInheritance)
    +                                    throws java.lang.IllegalAccessException,
    +                                           java.lang.InstantiationException,
    +                                           java.lang.reflect.InvocationTargetException
    +
    +
    Defines JavaScript objects from a Java class, optionally + allowing sealing and mapping of Java inheritance to JavaScript + prototype-based inheritance. + + Similar to defineClass(Scriptable scope, Class clazz) + except that sealing and inheritance mapping are allowed. An object + that is sealed cannot have properties added or removed. Note that + sealing is not allowed in the current ECMA/ISO language specification, + but is likely for the next version. +

    +

    +
    +
    +
    +
    Parameters:
    scope - The scope in which to define the constructor.
    clazz - The Java class to use to define the JavaScript objects + and properties. The class must implement Scriptable.
    sealed - Whether or not to create sealed standard objects that + cannot be modified.
    mapInheritance - Whether or not to map Java inheritance to + JavaScript prototype-based inheritance. +
    Returns:
    the class name for the prototype of the specified class +
    Throws: +
    java.lang.IllegalAccessException - if access is not available + to a reflected class member +
    java.lang.InstantiationException - if unable to instantiate + the named class +
    java.lang.reflect.InvocationTargetException - if an exception is thrown + during execution of methods of the named class
    Since:
    +
    1.6R2
    +
    +
    +
    +
    + +

    +defineProperty

    +
    +public void defineProperty(java.lang.String propertyName,
    +                           java.lang.Object value,
    +                           int attributes)
    +
    +
    Define a JavaScript property. + + Creates the property with an initial value and sets its attributes. +

    +

    +
    +
    +
    +
    Parameters:
    propertyName - the name of the property to define.
    value - the initial value of the property
    attributes - the attributes of the JavaScript property
    See Also:
    Scriptable.put(String, Scriptable, Object)
    +
    +
    +
    + +

    +defineProperty

    +
    +public static void defineProperty(Scriptable destination,
    +                                  java.lang.String propertyName,
    +                                  java.lang.Object value,
    +                                  int attributes)
    +
    +
    Utility method to add properties to arbitrary Scriptable object. + If destination is instance of ScriptableObject, calls + defineProperty there, otherwise calls put in destination + ignoring attributes +

    +

    +
    +
    +
    +
    +
    +
    +
    + +

    +defineConstProperty

    +
    +public static void defineConstProperty(Scriptable destination,
    +                                       java.lang.String propertyName)
    +
    +
    Utility method to add properties to arbitrary Scriptable object. + If destination is instance of ScriptableObject, calls + defineProperty there, otherwise calls put in destination + ignoring attributes +

    +

    +
    +
    +
    +
    +
    +
    +
    + +

    +defineProperty

    +
    +public void defineProperty(java.lang.String propertyName,
    +                           java.lang.Class clazz,
    +                           int attributes)
    +
    +
    Define a JavaScript property with getter and setter side effects. + + If the setter is not found, the attribute READONLY is added to + the given attributes.

    + + The getter must be a method with zero parameters, and the setter, if + found, must be a method with one parameter.

    +

    +

    +
    +
    +
    +
    Parameters:
    propertyName - the name of the property to define. This name + also affects the name of the setter and getter + to search for. If the propertyId is "foo", then + clazz will be searched for "getFoo" + and "setFoo" methods.
    clazz - the Java class to search for the getter and setter
    attributes - the attributes of the JavaScript property
    See Also:
    Scriptable.put(String, Scriptable, Object)
    +
    +
    +
    + +

    +defineProperty

    +
    +public void defineProperty(java.lang.String propertyName,
    +                           java.lang.Object delegateTo,
    +                           java.lang.reflect.Method getter,
    +                           java.lang.reflect.Method setter,
    +                           int attributes)
    +
    +
    Define a JavaScript property. + + Use this method only if you wish to define getters and setters for + a given property in a ScriptableObject. To create a property without + special getter or setter side effects, use + defineProperty(String,int). + + If setter is null, the attribute READONLY is added to + the given attributes.

    + + Several forms of getters or setters are allowed. In all cases the + type of the value parameter can be any one of the following types: + Object, String, boolean, Scriptable, byte, short, int, long, float, + or double. The runtime will perform appropriate conversions based + upon the type of the parameter (see description in FunctionObject). + The first forms are nonstatic methods of the class referred to + by 'this': +

    + Object getFoo();
    + void setFoo(SomeType value);
    + Next are static methods that may be of any class; the object whose + property is being accessed is passed in as an extra argument: +
    + static Object getFoo(Scriptable obj);
    + static void setFoo(Scriptable obj, SomeType value);
    + Finally, it is possible to delegate to another object entirely using + the delegateTo parameter. In this case the methods are + nonstatic methods of the class delegated to, and the object whose + property is being accessed is passed in as an extra argument: +
    + Object getFoo(Scriptable obj);
    + void setFoo(Scriptable obj, SomeType value);
    +

    +

    +
    +
    +
    +
    Parameters:
    propertyName - the name of the property to define.
    delegateTo - an object to call the getter and setter methods on, + or null, depending on the form used above.
    getter - the method to invoke to get the value of the property
    setter - the method to invoke to set the value of the property
    attributes - the attributes of the JavaScript property
    +
    +
    +
    + +

    +defineFunctionProperties

    +
    +public void defineFunctionProperties(java.lang.String[] names,
    +                                     java.lang.Class clazz,
    +                                     int attributes)
    +
    +
    Search for names in a class, adding the resulting methods + as properties. + +

    Uses reflection to find the methods of the given names. Then + FunctionObjects are constructed from the methods found, and + are added to this object as properties with the given names. +

    +

    +
    +
    +
    +
    Parameters:
    names - the names of the Methods to add as function properties
    clazz - the class to search for the Methods
    attributes - the attributes of the new properties
    See Also:
    FunctionObject
    +
    +
    +
    + +

    +getObjectPrototype

    +
    +public static Scriptable getObjectPrototype(Scriptable scope)
    +
    +
    Get the Object.prototype property. + See ECMA 15.2.4. +

    +

    +
    +
    +
    +
    +
    +
    +
    + +

    +getFunctionPrototype

    +
    +public static Scriptable getFunctionPrototype(Scriptable scope)
    +
    +
    Get the Function.prototype property. + See ECMA 15.3.4. +

    +

    +
    +
    +
    +
    +
    +
    +
    + +

    +getClassPrototype

    +
    +public static Scriptable getClassPrototype(Scriptable scope,
    +                                           java.lang.String className)
    +
    +
    Get the prototype for the named class. + + For example, getClassPrototype(s, "Date") will first + walk up the parent chain to find the outermost scope, then will + search that scope for the Date constructor, and then will + return Date.prototype. If any of the lookups fail, or + the prototype is not a JavaScript object, then null will + be returned. +

    +

    +
    +
    +
    +
    Parameters:
    scope - an object in the scope chain
    className - the name of the constructor +
    Returns:
    the prototype for the named class, or null if it + cannot be found.
    +
    +
    +
    + +

    +getTopLevelScope

    +
    +public static Scriptable getTopLevelScope(Scriptable obj)
    +
    +
    Get the global scope. + +

    Walks the parent scope chain to find an object with a null + parent scope (the global object). +

    +

    +
    +
    +
    +
    Parameters:
    obj - a JavaScript object +
    Returns:
    the corresponding global scope
    +
    +
    +
    + +

    +sealObject

    +
    +public void sealObject()
    +
    +
    Seal this object. + + A sealed object may not have properties added or removed. Once + an object is sealed it may not be unsealed. +

    +

    +
    +
    +
    +
    Since:
    +
    1.4R3
    +
    +
    +
    +
    + +

    +isSealed

    +
    +public final boolean isSealed()
    +
    +
    Return true if this object is sealed. + + It is an error to attempt to add or remove properties to + a sealed object. +

    +

    +
    +
    +
    + +
    Returns:
    true if sealed, false otherwise.
    Since:
    +
    1.4R3
    +
    +
    +
    +
    + +

    +getProperty

    +
    +public static java.lang.Object getProperty(Scriptable obj,
    +                                           java.lang.String name)
    +
    +
    Gets a named property from an object or any object in its prototype chain. +

    + Searches the prototype chain for a property named name. +

    +

    +

    +
    +
    +
    +
    Parameters:
    obj - a JavaScript object
    name - a property name +
    Returns:
    the value of a property with name name found in + obj or any object in its prototype chain, or + Scriptable.NOT_FOUND if not found
    Since:
    +
    1.5R2
    +
    +
    +
    +
    + +

    +getProperty

    +
    +public static java.lang.Object getProperty(Scriptable obj,
    +                                           int index)
    +
    +
    Gets an indexed property from an object or any object in its prototype chain. +

    + Searches the prototype chain for a property with integral index + index. Note that if you wish to look for properties with numerical + but non-integral indicies, you should use getProperty(Scriptable,String) with + the string value of the index. +

    +

    +

    +
    +
    +
    +
    Parameters:
    obj - a JavaScript object
    index - an integral index +
    Returns:
    the value of a property with index index found in + obj or any object in its prototype chain, or + Scriptable.NOT_FOUND if not found
    Since:
    +
    1.5R2
    +
    +
    +
    +
    + +

    +hasProperty

    +
    +public static boolean hasProperty(Scriptable obj,
    +                                  java.lang.String name)
    +
    +
    Returns whether a named property is defined in an object or any object + in its prototype chain. +

    + Searches the prototype chain for a property named name. +

    +

    +

    +
    +
    +
    +
    Parameters:
    obj - a JavaScript object
    name - a property name +
    Returns:
    the true if property was found
    Since:
    +
    1.5R2
    +
    +
    +
    +
    + +

    +redefineProperty

    +
    +public static void redefineProperty(Scriptable obj,
    +                                    java.lang.String name,
    +                                    boolean isConst)
    +
    +
    If hasProperty(obj, name) would return true, then if the property that + was found is compatible with the new property, this method just returns. + If the property is not compatible, then an exception is thrown. + + A property redefinition is incompatible if the first definition was a + const declaration or if this one is. They are compatible only if neither + was const. +

    +

    +
    +
    +
    +
    +
    +
    +
    + +

    +hasProperty

    +
    +public static boolean hasProperty(Scriptable obj,
    +                                  int index)
    +
    +
    Returns whether an indexed property is defined in an object or any object + in its prototype chain. +

    + Searches the prototype chain for a property with index index. +

    +

    +

    +
    +
    +
    +
    Parameters:
    obj - a JavaScript object
    index - a property index +
    Returns:
    the true if property was found
    Since:
    +
    1.5R2
    +
    +
    +
    +
    + +

    +putProperty

    +
    +public static void putProperty(Scriptable obj,
    +                               java.lang.String name,
    +                               java.lang.Object value)
    +
    +
    Puts a named property in an object or in an object in its prototype chain. +

    + Searches for the named property in the prototype chain. If it is found, + the value of the property in obj is changed through a call + to Scriptable.put(String, Scriptable, Object) on the + prototype passing obj as the start argument. + This allows the prototype to veto the property setting in case the + prototype defines the property with [[ReadOnly]] attribute. If the + property is not found, it is added in obj. +

    +

    +
    +
    +
    +
    Parameters:
    obj - a JavaScript object
    name - a property name
    value - any JavaScript value accepted by Scriptable.put
    Since:
    +
    1.5R2
    +
    +
    +
    +
    + +

    +putConstProperty

    +
    +public static void putConstProperty(Scriptable obj,
    +                                    java.lang.String name,
    +                                    java.lang.Object value)
    +
    +
    Puts a named property in an object or in an object in its prototype chain. +

    + Searches for the named property in the prototype chain. If it is found, + the value of the property in obj is changed through a call + to Scriptable.put(String, Scriptable, Object) on the + prototype passing obj as the start argument. + This allows the prototype to veto the property setting in case the + prototype defines the property with [[ReadOnly]] attribute. If the + property is not found, it is added in obj. +

    +

    +
    +
    +
    +
    Parameters:
    obj - a JavaScript object
    name - a property name
    value - any JavaScript value accepted by Scriptable.put
    Since:
    +
    1.5R2
    +
    +
    +
    +
    + +

    +putProperty

    +
    +public static void putProperty(Scriptable obj,
    +                               int index,
    +                               java.lang.Object value)
    +
    +
    Puts an indexed property in an object or in an object in its prototype chain. +

    + Searches for the indexed property in the prototype chain. If it is found, + the value of the property in obj is changed through a call + to Scriptable.put(int, Scriptable, Object) on the prototype + passing obj as the start argument. This allows + the prototype to veto the property setting in case the prototype defines + the property with [[ReadOnly]] attribute. If the property is not found, + it is added in obj. +

    +

    +
    +
    +
    +
    Parameters:
    obj - a JavaScript object
    index - a property index
    value - any JavaScript value accepted by Scriptable.put
    Since:
    +
    1.5R2
    +
    +
    +
    +
    + +

    +deleteProperty

    +
    +public static boolean deleteProperty(Scriptable obj,
    +                                     java.lang.String name)
    +
    +
    Removes the property from an object or its prototype chain. +

    + Searches for a property with name in obj or + its prototype chain. If it is found, the object's delete + method is called. +

    +

    +
    +
    +
    +
    Parameters:
    obj - a JavaScript object
    name - a property name +
    Returns:
    true if the property doesn't exist or was successfully removed
    Since:
    +
    1.5R2
    +
    +
    +
    +
    + +

    +deleteProperty

    +
    +public static boolean deleteProperty(Scriptable obj,
    +                                     int index)
    +
    +
    Removes the property from an object or its prototype chain. +

    + Searches for a property with index in obj or + its prototype chain. If it is found, the object's delete + method is called. +

    +

    +
    +
    +
    +
    Parameters:
    obj - a JavaScript object
    index - a property index +
    Returns:
    true if the property doesn't exist or was successfully removed
    Since:
    +
    1.5R2
    +
    +
    +
    +
    + +

    +getPropertyIds

    +
    +public static java.lang.Object[] getPropertyIds(Scriptable obj)
    +
    +
    Returns an array of all ids from an object and its prototypes. +

    +

    +

    +
    +
    +
    +
    Parameters:
    obj - a JavaScript object +
    Returns:
    an array of all ids from all object in the prototype chain. + If a given id occurs multiple times in the prototype chain, + it will occur only once in this list.
    Since:
    +
    1.5R2
    +
    +
    +
    +
    + +

    +callMethod

    +
    +public static java.lang.Object callMethod(Scriptable obj,
    +                                          java.lang.String methodName,
    +                                          java.lang.Object[] args)
    +
    +
    Call a method of an object. +

    +

    +
    +
    +
    +
    Parameters:
    obj - the JavaScript object
    methodName - the name of the function property
    args - the arguments for the call
    See Also:
    Context.getCurrentContext()
    +
    +
    +
    + +

    +callMethod

    +
    +public static java.lang.Object callMethod(Context cx,
    +                                          Scriptable obj,
    +                                          java.lang.String methodName,
    +                                          java.lang.Object[] args)
    +
    +
    Call a method of an object. +

    +

    +
    +
    +
    +
    Parameters:
    cx - the Context object associated with the current thread.
    obj - the JavaScript object
    methodName - the name of the function property
    args - the arguments for the call
    +
    +
    +
    + +

    +getAssociatedValue

    +
    +public final java.lang.Object getAssociatedValue(java.lang.Object key)
    +
    +
    Get arbitrary application-specific value associated with this object. +

    +

    +
    +
    +
    +
    Parameters:
    key - key object to select particular value.
    See Also:
    associateValue(Object key, Object value)
    +
    +
    +
    + +

    +getTopScopeValue

    +
    +public static java.lang.Object getTopScopeValue(Scriptable scope,
    +                                                java.lang.Object key)
    +
    +
    Get arbitrary application-specific value associated with the top scope + of the given scope. + The method first calls getTopLevelScope(Scriptable scope) + and then searches the prototype chain of the top scope for the first + object containing the associated value with the given key. +

    +

    +
    +
    +
    +
    Parameters:
    scope - the starting scope.
    key - key object to select particular value.
    See Also:
    getAssociatedValue(Object key)
    +
    +
    +
    + +

    +associateValue

    +
    +public final java.lang.Object associateValue(java.lang.Object key,
    +                                             java.lang.Object value)
    +
    +
    Associate arbitrary application-specific value with this object. + Value can only be associated with the given object and key only once. + The method ignores any subsequent attempts to change the already + associated value. +

    The associated values are not serialized. +

    +

    +
    +
    +
    +
    Parameters:
    key - key object to select particular value.
    value - the value to associate +
    Returns:
    the passed value if the method is called first time for the + given key or old value for any subsequent calls.
    See Also:
    getAssociatedValue(Object key)
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/SecurityController.html b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/SecurityController.html new file mode 100644 index 0000000..92c5c44 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/SecurityController.html @@ -0,0 +1,507 @@ + + + + + + +SecurityController (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + +

    + +org.mozilla.javascript +
    +Class SecurityController

    +
    +java.lang.Object
    +  extended by org.mozilla.javascript.SecurityController
    +
    +
    +
    +
    public abstract class SecurityController
    extends java.lang.Object
    + + +

    +This class describes the support needed to implement security. +

    + Three main pieces of functionality are required to implement + security for JavaScript. First, it must be possible to define + classes with an associated security domain. (This security + domain may be any object incorporating notion of access + restrictions that has meaning to an embedding; for a client-side + JavaScript embedding this would typically be + java.security.ProtectionDomain or similar object depending on an + origin URL and/or a digital certificate.) + Next it must be possible to get a security domain object that + allows a particular action only if all security domains + associated with code on the current Java stack allows it. And + finally, it must be possible to execute script code with + associated security domain injected into Java stack. +

    + These three pieces of functionality are encapsulated in the + SecurityController class. +

    + +

    +

    +
    Since:
    +
    1.5 Release 4
    +
    See Also:
    Context.setSecurityController(SecurityController), +ClassLoader
    +
    + +

    + + + + + + + + + + + +
    +Constructor Summary
    SecurityController() + +
    +           
    +  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +Method Summary
    + java.lang.ObjectcallWithDomain(java.lang.Object securityDomain, + Context cx, + Callable callable, + Scriptable scope, + Scriptable thisObj, + java.lang.Object[] args) + +
    +          Call Callable.call(Context cx, Scriptable scope, Scriptable thisObj, + Object[] args) + of callable under restricted security domain where an action is + allowed only if it is allowed according to the Java stack on the + moment of the execWithDomain call and securityDomain.
    +abstract  GeneratedClassLoadercreateClassLoader(java.lang.ClassLoader parentLoader, + java.lang.Object securityDomain) + +
    +          Get class loader-like object that can be used + to define classes with the given security context.
    +static GeneratedClassLoadercreateLoader(java.lang.ClassLoader parent, + java.lang.Object staticDomain) + +
    +          Create GeneratedClassLoader with restrictions imposed by + staticDomain and all current stack frames.
    + java.lang.ObjectexecWithDomain(Context cx, + Scriptable scope, + Script script, + java.lang.Object securityDomain) + +
    +          Deprecated. The application should not override this method and instead + override + callWithDomain(Object securityDomain, Context cx, Callable callable, Scriptable scope, Scriptable thisObj, Object[] args).
    +abstract  java.lang.ObjectgetDynamicSecurityDomain(java.lang.Object securityDomain) + +
    +          Get dynamic security domain that allows an action only if it is allowed + by the current Java stack and securityDomain.
    +static java.lang.ClassgetStaticSecurityDomainClass() + +
    +           
    + java.lang.ClassgetStaticSecurityDomainClassInternal() + +
    +           
    +static booleanhasGlobal() + +
    +          Check if global SecurityController was already installed.
    +static voidinitGlobal(SecurityController controller) + +
    +          Initialize global controller that will be used for all + security-related operations.
    + + + + + + + +
    Methods inherited from class java.lang.Object
    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    +  +

    + + + + + + + + +
    +Constructor Detail
    + +

    +SecurityController

    +
    +public SecurityController()
    +
    +
    + + + + + + + + +
    +Method Detail
    + +

    +hasGlobal

    +
    +public static boolean hasGlobal()
    +
    +
    Check if global SecurityController was already installed. +

    +

    +
    See Also:
    initGlobal(SecurityController controller)
    +
    +
    +
    + +

    +initGlobal

    +
    +public static void initGlobal(SecurityController controller)
    +
    +
    Initialize global controller that will be used for all + security-related operations. The global controller takes precedence + over already installed Context-specific controllers and cause + any subsequent call to + Context.setSecurityController(SecurityController) + to throw an exception. +

    + The method can only be called once. +

    +

    +
    See Also:
    hasGlobal()
    +
    +
    +
    + +

    +createClassLoader

    +
    +public abstract GeneratedClassLoader createClassLoader(java.lang.ClassLoader parentLoader,
    +                                                       java.lang.Object securityDomain)
    +
    +
    Get class loader-like object that can be used + to define classes with the given security context. +

    +

    +
    Parameters:
    parentLoader - parent class loader to delegate search for classes + not defined by the class loader itself
    securityDomain - some object specifying the security + context of the code that is defined by the returned class loader.
    +
    +
    +
    + +

    +createLoader

    +
    +public static GeneratedClassLoader createLoader(java.lang.ClassLoader parent,
    +                                                java.lang.Object staticDomain)
    +
    +
    Create GeneratedClassLoader with restrictions imposed by + staticDomain and all current stack frames. + The method uses the SecurityController instance associated with the + current Context to construct proper dynamic domain and create + corresponding class loader. + + If no SecurityController is associated with the current Context , + the method calls Context.createClassLoader(ClassLoader parent). +

    +

    +
    Parameters:
    parent - parent class loader. If null, + Context.getApplicationClassLoader() will be used.
    staticDomain - static security domain.
    +
    +
    +
    + +

    +getStaticSecurityDomainClass

    +
    +public static java.lang.Class getStaticSecurityDomainClass()
    +
    +
    +
    +
    +
    +
    + +

    +getStaticSecurityDomainClassInternal

    +
    +public java.lang.Class getStaticSecurityDomainClassInternal()
    +
    +
    +
    +
    +
    +
    + +

    +getDynamicSecurityDomain

    +
    +public abstract java.lang.Object getDynamicSecurityDomain(java.lang.Object securityDomain)
    +
    +
    Get dynamic security domain that allows an action only if it is allowed + by the current Java stack and securityDomain. If + securityDomain is null, return domain representing permissions + allowed by the current stack. +

    +

    +
    +
    +
    +
    + +

    +callWithDomain

    +
    +public java.lang.Object callWithDomain(java.lang.Object securityDomain,
    +                                       Context cx,
    +                                       Callable callable,
    +                                       Scriptable scope,
    +                                       Scriptable thisObj,
    +                                       java.lang.Object[] args)
    +
    +
    Call Callable.call(Context cx, Scriptable scope, Scriptable thisObj, + Object[] args) + of callable under restricted security domain where an action is + allowed only if it is allowed according to the Java stack on the + moment of the execWithDomain call and securityDomain. + Any call to getDynamicSecurityDomain(Object) during + execution of callable.call(cx, scope, thisObj, args) + should return a domain incorporate restrictions imposed by + securityDomain and Java stack on the moment of callWithDomain + invocation. +

    + The method should always be overridden, it is not declared abstract + for compatibility reasons. +

    +

    +
    +
    +
    +
    + +

    +execWithDomain

    +
    +public java.lang.Object execWithDomain(Context cx,
    +                                       Scriptable scope,
    +                                       Script script,
    +                                       java.lang.Object securityDomain)
    +
    +
    Deprecated. The application should not override this method and instead + override + callWithDomain(Object securityDomain, Context cx, Callable callable, Scriptable scope, Scriptable thisObj, Object[] args). +

    +

    +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/Synchronizer.html b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/Synchronizer.html new file mode 100644 index 0000000..05b8fb4 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/Synchronizer.html @@ -0,0 +1,331 @@ + + + + + + +Synchronizer (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + +

    + +org.mozilla.javascript +
    +Class Synchronizer

    +
    +java.lang.Object
    +  extended by org.mozilla.javascript.Delegator
    +      extended by org.mozilla.javascript.Synchronizer
    +
    +
    +
    All Implemented Interfaces:
    Callable, Function, Scriptable
    +
    +
    +
    +
    public class Synchronizer
    extends org.mozilla.javascript.Delegator
    + + +

    +This class provides support for implementing Java-style synchronized + methods in Javascript. + + Synchronized functions are created from ordinary Javascript + functions by the Synchronizer constructor, e.g. + new Packages.org.mozilla.javascript.Synchronizer(fun). + The resulting object is a function that establishes an exclusive + lock on the this object of its invocation. + + The Rhino shell provides a short-cut for the creation of + synchronized methods: sync(fun) has the same effect as + calling the above constructor. +

    + +

    +

    +
    Author:
    +
    Matthias Radestock
    +
    See Also:
    Delegator
    +
    + +

    + + + + + + + +
    +Field Summary
    + + + + + + + +
    Fields inherited from class org.mozilla.javascript.Delegator
    obj
    + + + + + + + +
    Fields inherited from interface org.mozilla.javascript.Scriptable
    NOT_FOUND
    +  + + + + + + + + + + +
    +Constructor Summary
    Synchronizer(Scriptable obj) + +
    +          Create a new synchronized function from an existing one.
    +  + + + + + + + + + + + +
    +Method Summary
    + java.lang.Objectcall(Context cx, + Scriptable scope, + Scriptable thisObj, + java.lang.Object[] args) + +
    +          Call the function.
    + + + + + + + +
    Methods inherited from class org.mozilla.javascript.Delegator
    construct, delete, delete, get, get, getClassName, getDefaultValue, getDelegee, getIds, getParentScope, getPrototype, has, has, hasInstance, newInstance, put, put, setDelegee, setParentScope, setPrototype
    + + + + + + + +
    Methods inherited from class java.lang.Object
    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    +  +

    + + + + + + + + +
    +Constructor Detail
    + +

    +Synchronizer

    +
    +public Synchronizer(Scriptable obj)
    +
    +
    Create a new synchronized function from an existing one. +

    +

    +
    Parameters:
    obj - the existing function
    +
    + + + + + + + + +
    +Method Detail
    + +

    +call

    +
    +public java.lang.Object call(Context cx,
    +                             Scriptable scope,
    +                             Scriptable thisObj,
    +                             java.lang.Object[] args)
    +
    +
    Description copied from interface: Function
    +
    Call the function. + + Note that the array of arguments is not guaranteed to have + length greater than 0. +

    +

    +
    Specified by:
    call in interface Callable
    Specified by:
    call in interface Function
    Overrides:
    call in class org.mozilla.javascript.Delegator
    +
    +
    +
    Parameters:
    cx - the current Context for this thread
    scope - the scope to execute the function relative to. This is + set to the value returned by getParentScope() except + when the function is called from a closure.
    thisObj - the JavaScript this object
    args - the array of arguments +
    Returns:
    the result of the call
    See Also:
    Function.call(org.mozilla.javascript.Context, org.mozilla.javascript.Scriptable, org.mozilla.javascript.Scriptable, java.lang.Object[])
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/WrapFactory.html b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/WrapFactory.html new file mode 100644 index 0000000..675c456 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/WrapFactory.html @@ -0,0 +1,400 @@ + + + + + + +WrapFactory (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + +

    + +org.mozilla.javascript +
    +Class WrapFactory

    +
    +java.lang.Object
    +  extended by org.mozilla.javascript.WrapFactory
    +
    +
    +
    +
    public class WrapFactory
    extends java.lang.Object
    + + +

    +Embeddings that wish to provide their own custom wrappings for Java + objects may extend this class and call + Context.setWrapFactory(WrapFactory) + Once an instance of this class or an extension of this class is enabled + for a given context (by calling setWrapFactory on that context), Rhino + will call the methods of this class whenever it needs to wrap a value + resulting from a call to a Java method or an access to a Java field. +

    + +

    +

    +
    Since:
    +
    1.5 Release 4
    +
    See Also:
    Context.setWrapFactory(WrapFactory)
    +
    + +

    + + + + + + + + + + + +
    +Constructor Summary
    WrapFactory() + +
    +           
    +  + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +Method Summary
    + booleanisJavaPrimitiveWrap() + +
    +          Return false if result of Java method, which is instance of + String, Number, Boolean and + Character, should be used directly as JavaScript primitive + type.
    + voidsetJavaPrimitiveWrap(boolean value) + +
    +           
    + java.lang.Objectwrap(Context cx, + Scriptable scope, + java.lang.Object obj, + java.lang.Class staticType) + +
    +          Wrap the object.
    + ScriptablewrapAsJavaObject(Context cx, + Scriptable scope, + java.lang.Object javaObject, + java.lang.Class staticType) + +
    +          Wrap Java object as Scriptable instance to allow full access to its + methods and fields from JavaScript.
    + ScriptablewrapNewObject(Context cx, + Scriptable scope, + java.lang.Object obj) + +
    +          Wrap an object newly created by a constructor call.
    + + + + + + + +
    Methods inherited from class java.lang.Object
    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    +  +

    + + + + + + + + +
    +Constructor Detail
    + +

    +WrapFactory

    +
    +public WrapFactory()
    +
    +
    + + + + + + + + +
    +Method Detail
    + +

    +wrap

    +
    +public java.lang.Object wrap(Context cx,
    +                             Scriptable scope,
    +                             java.lang.Object obj,
    +                             java.lang.Class staticType)
    +
    +
    Wrap the object. +

    + The value returned must be one of +

      +
    • java.lang.Boolean
    • +
    • java.lang.String
    • +
    • java.lang.Number
    • +
    • org.mozilla.javascript.Scriptable objects
    • +
    • The value returned by Context.getUndefinedValue()
    • +
    • null
    • +
    +

    +

    +
    Parameters:
    cx - the current Context for this thread
    scope - the scope of the executing script
    obj - the object to be wrapped. Note it can be null.
    staticType - type hint. If security restrictions prevent to wrap + object based on its class, staticType will be used instead. +
    Returns:
    the wrapped value.
    +
    +
    +
    + +

    +wrapNewObject

    +
    +public Scriptable wrapNewObject(Context cx,
    +                                Scriptable scope,
    +                                java.lang.Object obj)
    +
    +
    Wrap an object newly created by a constructor call. +

    +

    +
    Parameters:
    cx - the current Context for this thread
    scope - the scope of the executing script
    obj - the object to be wrapped +
    Returns:
    the wrapped value.
    +
    +
    +
    + +

    +wrapAsJavaObject

    +
    +public Scriptable wrapAsJavaObject(Context cx,
    +                                   Scriptable scope,
    +                                   java.lang.Object javaObject,
    +                                   java.lang.Class staticType)
    +
    +
    Wrap Java object as Scriptable instance to allow full access to its + methods and fields from JavaScript. +

    + wrap(Context, Scriptable, Object, Class) and + wrapNewObject(Context, Scriptable, Object) call this method + when they can not convert javaObject to JavaScript primitive + value or JavaScript array. +

    + Subclasses can override the method to provide custom wrappers + for Java objects. +

    +

    +
    Parameters:
    cx - the current Context for this thread
    scope - the scope of the executing script
    javaObject - the object to be wrapped
    staticType - type hint. If security restrictions prevent to wrap + object based on its class, staticType will be used instead. +
    Returns:
    the wrapped value which shall not be null
    +
    +
    +
    + +

    +isJavaPrimitiveWrap

    +
    +public final boolean isJavaPrimitiveWrap()
    +
    +
    Return false if result of Java method, which is instance of + String, Number, Boolean and + Character, should be used directly as JavaScript primitive + type. + By default the method returns true to indicate that instances of + String, Number, Boolean and + Character should be wrapped as any other Java object and + scripts can access any Java method available in these objects. + Use setJavaPrimitiveWrap(boolean) to change this. +

    +

    +
    +
    +
    +
    + +

    +setJavaPrimitiveWrap

    +
    +public final void setJavaPrimitiveWrap(boolean value)
    +
    +
    +
    See Also:
    isJavaPrimitiveWrap()
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/WrappedException.html b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/WrappedException.html new file mode 100644 index 0000000..1109997 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/WrappedException.html @@ -0,0 +1,323 @@ + + + + + + +WrappedException (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + +

    + +org.mozilla.javascript +
    +Class WrappedException

    +
    +java.lang.Object
    +  extended by java.lang.Throwable
    +      extended by java.lang.Exception
    +          extended by java.lang.RuntimeException
    +              extended by org.mozilla.javascript.RhinoException
    +                  extended by org.mozilla.javascript.EvaluatorException
    +                      extended by org.mozilla.javascript.WrappedException
    +
    +
    +
    All Implemented Interfaces:
    java.io.Serializable
    +
    +
    +
    +
    public class WrappedException
    extends EvaluatorException
    + + +

    +A wrapper for runtime exceptions. + + Used by the JavaScript runtime to wrap and propagate exceptions that occur + during runtime. +

    + +

    +

    +
    Author:
    +
    Norris Boyd
    +
    See Also:
    Serialized Form
    +
    + +

    + + + + + + + + + + + +
    +Constructor Summary
    WrappedException(java.lang.Throwable exception) + +
    +           
    +  + + + + + + + + + + + + + + + +
    +Method Summary
    + java.lang.ThrowablegetWrappedException() + +
    +          Get the wrapped exception.
    + java.lang.Objectunwrap() + +
    +          Deprecated. Use getWrappedException() instead.
    + + + + + + + +
    Methods inherited from class org.mozilla.javascript.EvaluatorException
    getColumnNumber, getLineNumber, getLineSource, getSourceName
    + + + + + + + +
    Methods inherited from class org.mozilla.javascript.RhinoException
    columnNumber, details, getMessage, getScriptStackTrace, getScriptStackTrace, initColumnNumber, initLineNumber, initLineSource, initSourceName, lineNumber, lineSource, printStackTrace, printStackTrace, sourceName
    + + + + + + + +
    Methods inherited from class java.lang.Throwable
    fillInStackTrace, getCause, getLocalizedMessage, getStackTrace, initCause, printStackTrace, setStackTrace, toString
    + + + + + + + +
    Methods inherited from class java.lang.Object
    clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
    +  +

    + + + + + + + + +
    +Constructor Detail
    + +

    +WrappedException

    +
    +public WrappedException(java.lang.Throwable exception)
    +
    +
    +
    See Also:
    Context.throwAsScriptRuntimeEx(Throwable e)
    +
    + + + + + + + + +
    +Method Detail
    + +

    +getWrappedException

    +
    +public java.lang.Throwable getWrappedException()
    +
    +
    Get the wrapped exception. +

    +

    + +
    Returns:
    the exception that was presented as a argument to the + constructor when this object was created
    +
    +
    +
    + +

    +unwrap

    +
    +public java.lang.Object unwrap()
    +
    +
    Deprecated. Use getWrappedException() instead. +

    +

    +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/Wrapper.html b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/Wrapper.html new file mode 100644 index 0000000..0d05fdc --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/Wrapper.html @@ -0,0 +1,214 @@ + + + + + + +Wrapper (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + +

    + +org.mozilla.javascript +
    +Interface Wrapper

    +
    +
    +
    public interface Wrapper
    + + +

    +Objects that can wrap other values for reflection in the JS environment + will implement Wrapper. + + Wrapper defines a single method that can be called to unwrap the object. +

    + +

    +


    + +

    + + + + + + + + + + + + +
    +Method Summary
    + java.lang.Objectunwrap() + +
    +          Unwrap the object by returning the wrapped value.
    +  +

    + + + + + + + + +
    +Method Detail
    + +

    +unwrap

    +
    +java.lang.Object unwrap()
    +
    +
    Unwrap the object by returning the wrapped value. +

    +

    + +
    Returns:
    a wrapped value
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/debug/DebuggableScript.html b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/debug/DebuggableScript.html new file mode 100644 index 0000000..1159738 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/debug/DebuggableScript.html @@ -0,0 +1,455 @@ + + + + + + +DebuggableScript (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + +

    + +org.mozilla.javascript.debug +
    +Interface DebuggableScript

    +
    +
    +
    public interface DebuggableScript
    + + +

    +This interface exposes debugging information from executable + code (either functions or top-level scripts). +

    + +

    +


    + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +Method Summary
    + DebuggableScriptgetFunction(int index) + +
    +           
    + intgetFunctionCount() + +
    +           
    + java.lang.StringgetFunctionName() + +
    +          Get name of the function described by this script.
    + int[]getLineNumbers() + +
    +          Get array containing the line numbers that + that can be passed to DebugFrame.onLineChange().
    + intgetParamAndVarCount() + +
    +          Get number of declared parameters and local variables.
    + intgetParamCount() + +
    +          Get number of declared parameters in the function.
    + java.lang.StringgetParamOrVarName(int index) + +
    +          Get name of a declared parameter or local variable.
    + DebuggableScriptgetParent() + +
    +           
    + java.lang.StringgetSourceName() + +
    +          Get the name of the source (usually filename or URL) + of the script.
    + booleanisFunction() + +
    +          Returns true if this is a function, false if it is a script.
    + booleanisGeneratedScript() + +
    +          Returns true if this script or function were runtime-generated + from JavaScript using eval function or Function + or Script constructors.
    + booleanisTopLevel() + +
    +           
    +  +

    + + + + + + + + +
    +Method Detail
    + +

    +isTopLevel

    +
    +boolean isTopLevel()
    +
    +
    +
    +
    +
    +
    + +

    +isFunction

    +
    +boolean isFunction()
    +
    +
    Returns true if this is a function, false if it is a script. +

    +

    +
    +
    +
    +
    + +

    +getFunctionName

    +
    +java.lang.String getFunctionName()
    +
    +
    Get name of the function described by this script. + Return null or an empty string if this script is not a function. +

    +

    +
    +
    +
    +
    + +

    +getParamCount

    +
    +int getParamCount()
    +
    +
    Get number of declared parameters in the function. + Return 0 if this script is not a function. +

    +

    +
    See Also:
    getParamAndVarCount(), +getParamOrVarName(int index)
    +
    +
    +
    + +

    +getParamAndVarCount

    +
    +int getParamAndVarCount()
    +
    +
    Get number of declared parameters and local variables. + Return number of declared global variables if this script is not a + function. +

    +

    +
    See Also:
    getParamCount(), +getParamOrVarName(int index)
    +
    +
    +
    + +

    +getParamOrVarName

    +
    +java.lang.String getParamOrVarName(int index)
    +
    +
    Get name of a declared parameter or local variable. + index should be less then getParamAndVarCount(). + If index < getParamCount(), return + the name of the corresponding parameter, otherwise return the name + of variable. + If this script is not function, return the name of the declared + global variable. +

    +

    +
    +
    +
    +
    + +

    +getSourceName

    +
    +java.lang.String getSourceName()
    +
    +
    Get the name of the source (usually filename or URL) + of the script. +

    +

    +
    +
    +
    +
    + +

    +isGeneratedScript

    +
    +boolean isGeneratedScript()
    +
    +
    Returns true if this script or function were runtime-generated + from JavaScript using eval function or Function + or Script constructors. +

    +

    +
    +
    +
    +
    + +

    +getLineNumbers

    +
    +int[] getLineNumbers()
    +
    +
    Get array containing the line numbers that + that can be passed to DebugFrame.onLineChange(). + Note that line order in the resulting array is arbitrary +

    +

    +
    +
    +
    +
    + +

    +getFunctionCount

    +
    +int getFunctionCount()
    +
    +
    +
    +
    +
    +
    + +

    +getFunction

    +
    +DebuggableScript getFunction(int index)
    +
    +
    +
    +
    +
    +
    + +

    +getParent

    +
    +DebuggableScript getParent()
    +
    +
    +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/debug/package-frame.html b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/debug/package-frame.html new file mode 100644 index 0000000..28878a0 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/debug/package-frame.html @@ -0,0 +1,32 @@ + + + + + + +org.mozilla.javascript.debug (Rhino) + + + + + + + + + + + +org.mozilla.javascript.debug + + + + +
    +Interfaces  + +
    +DebuggableScript
    + + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/debug/package-summary.html b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/debug/package-summary.html new file mode 100644 index 0000000..1a50d7f --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/debug/package-summary.html @@ -0,0 +1,156 @@ + + + + + + +org.mozilla.javascript.debug (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + +
    + +
    + + + +
    +

    +Package org.mozilla.javascript.debug +

    + + + + + + + + + +
    +Interface Summary
    DebuggableScriptThis interface exposes debugging information from executable + code (either functions or top-level scripts).
    +  + +

    +

    +
    +
    + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/debug/package-tree.html b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/debug/package-tree.html new file mode 100644 index 0000000..d32bdf5 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/debug/package-tree.html @@ -0,0 +1,149 @@ + + + + + + +org.mozilla.javascript.debug Class Hierarchy (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + +
    + +
    + + + +
    +
    +

    +Hierarchy For Package org.mozilla.javascript.debug +

    +
    +
    +
    Package Hierarchies:
    All Packages
    +
    +

    +Interface Hierarchy +

    + +
    + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/optimizer/ClassCompiler.html b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/optimizer/ClassCompiler.html new file mode 100644 index 0000000..bdce431 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/optimizer/ClassCompiler.html @@ -0,0 +1,461 @@ + + + + + + +ClassCompiler (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + +

    + +org.mozilla.javascript.optimizer +
    +Class ClassCompiler

    +
    +java.lang.Object
    +  extended by org.mozilla.javascript.optimizer.ClassCompiler
    +
    +
    +
    +
    public class ClassCompiler
    extends java.lang.Object
    + + +

    +Generates class files from script sources. + + since 1.5 Release 5 +

    + +

    +

    +
    Author:
    +
    Igor Bukanov
    +
    +
    + +

    + + + + + + + + + + + +
    +Constructor Summary
    ClassCompiler(CompilerEnvirons compilerEnv) + +
    +          Construct ClassCompiler that uses the specified compiler environment + when generating classes.
    +  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +Method Summary
    + java.lang.Object[]compileToClassFiles(java.lang.String source, + java.lang.String sourceLocation, + int lineno, + java.lang.String mainClassName) + +
    +          Compile JavaScript source into one or more Java class files.
    + CompilerEnvironsgetCompilerEnv() + +
    +          Get the compiler environment the compiler uses.
    + java.lang.StringgetMainMethodClass() + +
    +          Get the name of the class for main method implementation.
    + java.lang.ClassgetTargetExtends() + +
    +          Get the class that the generated target will extend.
    + java.lang.Class[]getTargetImplements() + +
    +          Get the interfaces that the generated target will implement.
    +protected  java.lang.StringmakeAuxiliaryClassName(java.lang.String mainClassName, + java.lang.String auxMarker) + +
    +          Build class name for a auxiliary class generated by compiler.
    + voidsetMainMethodClass(java.lang.String className) + +
    +          Set the class name to use for main method implementation.
    + voidsetTargetExtends(java.lang.Class extendsClass) + +
    +          Set the class that the generated target will extend.
    + voidsetTargetImplements(java.lang.Class[] implementsClasses) + +
    +          Set the interfaces that the generated target will implement.
    + + + + + + + +
    Methods inherited from class java.lang.Object
    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    +  +

    + + + + + + + + +
    +Constructor Detail
    + +

    +ClassCompiler

    +
    +public ClassCompiler(CompilerEnvirons compilerEnv)
    +
    +
    Construct ClassCompiler that uses the specified compiler environment + when generating classes. +

    +

    + + + + + + + + +
    +Method Detail
    + +

    +setMainMethodClass

    +
    +public void setMainMethodClass(java.lang.String className)
    +
    +
    Set the class name to use for main method implementation. + The class must have a method matching + public static void main(Script sc, String[] args), it will be + called when main(String[] args) is called in the generated + class. The class name should be fully qulified name and include the + package name like in org.foo.Bar. +

    +

    +
    +
    +
    +
    + +

    +getMainMethodClass

    +
    +public java.lang.String getMainMethodClass()
    +
    +
    Get the name of the class for main method implementation. +

    +

    +
    See Also:
    setMainMethodClass(String)
    +
    +
    +
    + +

    +getCompilerEnv

    +
    +public CompilerEnvirons getCompilerEnv()
    +
    +
    Get the compiler environment the compiler uses. +

    +

    +
    +
    +
    +
    + +

    +getTargetExtends

    +
    +public java.lang.Class getTargetExtends()
    +
    +
    Get the class that the generated target will extend. +

    +

    +
    +
    +
    +
    + +

    +setTargetExtends

    +
    +public void setTargetExtends(java.lang.Class extendsClass)
    +
    +
    Set the class that the generated target will extend. +

    +

    +
    Parameters:
    extendsClass - the class it extends
    +
    +
    +
    + +

    +getTargetImplements

    +
    +public java.lang.Class[] getTargetImplements()
    +
    +
    Get the interfaces that the generated target will implement. +

    +

    +
    +
    +
    +
    + +

    +setTargetImplements

    +
    +public void setTargetImplements(java.lang.Class[] implementsClasses)
    +
    +
    Set the interfaces that the generated target will implement. +

    +

    +
    Parameters:
    implementsClasses - an array of Class objects, one for each + interface the target will extend
    +
    +
    +
    + +

    +makeAuxiliaryClassName

    +
    +protected java.lang.String makeAuxiliaryClassName(java.lang.String mainClassName,
    +                                                  java.lang.String auxMarker)
    +
    +
    Build class name for a auxiliary class generated by compiler. + If the compiler needs to generate extra classes beyond the main class, + it will call this function to build the auxiliary class name. + The default implementation simply appends auxMarker to mainClassName + but this can be overridden. +

    +

    +
    +
    +
    +
    + +

    +compileToClassFiles

    +
    +public java.lang.Object[] compileToClassFiles(java.lang.String source,
    +                                              java.lang.String sourceLocation,
    +                                              int lineno,
    +                                              java.lang.String mainClassName)
    +
    +
    Compile JavaScript source into one or more Java class files. + The first compiled class will have name mainClassName. + If the results of getTargetExtends() or + getTargetImplements() are not null, then the first compiled + class will extend the specified super class and implement + specified interfaces. +

    +

    + +
    Returns:
    array where elements with even indexes specifies class name + and the following odd index gives class file body as byte[] + array. The initial element of the array always holds + mainClassName and array[1] holds its byte code.
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/optimizer/package-frame.html b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/optimizer/package-frame.html new file mode 100644 index 0000000..1dc284b --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/optimizer/package-frame.html @@ -0,0 +1,32 @@ + + + + + + +org.mozilla.javascript.optimizer (Rhino) + + + + + + + + + + + +org.mozilla.javascript.optimizer + + + + +
    +Classes  + +
    +ClassCompiler
    + + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/optimizer/package-summary.html b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/optimizer/package-summary.html new file mode 100644 index 0000000..8b86a40 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/optimizer/package-summary.html @@ -0,0 +1,155 @@ + + + + + + +org.mozilla.javascript.optimizer (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + +
    + +
    + + + +
    +

    +Package org.mozilla.javascript.optimizer +

    + + + + + + + + + +
    +Class Summary
    ClassCompilerGenerates class files from script sources.
    +  + +

    +

    +
    +
    + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/optimizer/package-tree.html b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/optimizer/package-tree.html new file mode 100644 index 0000000..55e23d0 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/optimizer/package-tree.html @@ -0,0 +1,151 @@ + + + + + + +org.mozilla.javascript.optimizer Class Hierarchy (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + +
    + +
    + + + +
    +
    +

    +Hierarchy For Package org.mozilla.javascript.optimizer +

    +
    +
    +
    Package Hierarchies:
    All Packages
    +
    +

    +Class Hierarchy +

    +
      +
    • java.lang.Object +
    +
    + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/package-frame.html b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/package-frame.html new file mode 100644 index 0000000..f40351c --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/package-frame.html @@ -0,0 +1,100 @@ + + + + + + +org.mozilla.javascript (Rhino) + + + + + + + + + + + +org.mozilla.javascript + + + + +
    +Interfaces  + +
    +Callable +
    +ClassShutter +
    +ContextAction +
    +ContextFactory.Listener +
    +ErrorReporter +
    +Function +
    +GeneratedClassLoader +
    +RefCallable +
    +Script +
    +Scriptable +
    +Wrapper
    + + + + + + +
    +Classes  + +
    +ClassCache +
    +CompilerEnvirons +
    +Context +
    +ContextFactory +
    +FunctionObject +
    +ImporterTopLevel +
    +ScriptableObject +
    +SecurityController +
    +Synchronizer +
    +WrapFactory
    + + + + + + +
    +Exceptions  + +
    +EcmaError +
    +EvaluatorException +
    +JavaScriptException +
    +RhinoException +
    +WrappedException
    + + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/package-summary.html b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/package-summary.html new file mode 100644 index 0000000..c15d3c1 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/package-summary.html @@ -0,0 +1,294 @@ + + + + + + +org.mozilla.javascript (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + +
    + +
    + + + +
    +

    +Package org.mozilla.javascript +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +Interface Summary
    CallableGeneric notion of callable object that can execute some script-related code + upon request with specified values for script scope and this objects.
    ClassShutterEmbeddings that wish to filter Java classes that are visible to scripts +through the LiveConnect, should implement this interface.
    ContextActionInterface to represent arbitrary action that requires to have Context + object associated with the current thread for its execution.
    ContextFactory.ListenerListener of Context creation and release events.
    ErrorReporterThis is interface defines a protocol for the reporting of + errors during JavaScript translation or execution.
    FunctionThis is interface that all functions in JavaScript must implement.
    GeneratedClassLoaderInterface to define classes from generated byte code.
    RefCallableObject that can allows assignments to the result of function calls.
    ScriptAll compiled scripts implement this interface.
    ScriptableThis is interface that all objects in JavaScript must implement.
    WrapperObjects that can wrap other values for reflection in the JS environment + will implement Wrapper.
    +  + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +Class Summary
    ClassCacheCache of generated classes and data structures to access Java runtime + from JavaScript.
    CompilerEnvirons 
    ContextThis class represents the runtime context of an executing script.
    ContextFactoryFactory class that Rhino runtime uses to create new Context + instances.
    FunctionObject 
    ImporterTopLevelClass ImporterTopLevel + + This class defines a ScriptableObject that can be instantiated + as a top-level ("global") object to provide functionality similar + to Java's "import" statement.
    ScriptableObjectThis is the default implementation of the Scriptable interface.
    SecurityControllerThis class describes the support needed to implement security.
    SynchronizerThis class provides support for implementing Java-style synchronized + methods in Javascript.
    WrapFactoryEmbeddings that wish to provide their own custom wrappings for Java + objects may extend this class and call + Context.setWrapFactory(WrapFactory) + Once an instance of this class or an extension of this class is enabled + for a given context (by calling setWrapFactory on that context), Rhino + will call the methods of this class whenever it needs to wrap a value + resulting from a call to a Java method or an access to a Java field.
    +  + +

    + + + + + + + + + + + + + + + + + + + + + + + + + +
    +Exception Summary
    EcmaErrorThe class of exceptions raised by the engine as described in + ECMA edition 3.
    EvaluatorExceptionThe class of exceptions thrown by the JavaScript engine.
    JavaScriptExceptionJava reflection of JavaScript exceptions.
    RhinoExceptionThe class of exceptions thrown by the JavaScript engine.
    WrappedExceptionA wrapper for runtime exceptions.
    +  + +

    +

    +
    +
    + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/package-tree.html b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/package-tree.html new file mode 100644 index 0000000..030f3e0 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/package-tree.html @@ -0,0 +1,185 @@ + + + + + + +org.mozilla.javascript Class Hierarchy (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + +
    + +
    + + + +
    +
    +

    +Hierarchy For Package org.mozilla.javascript +

    +
    +
    +
    Package Hierarchies:
    All Packages
    +
    +

    +Class Hierarchy +

    + +

    +Interface Hierarchy +

    + +
    + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/serialize/ScriptableInputStream.html b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/serialize/ScriptableInputStream.html new file mode 100644 index 0000000..f0d178f --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/serialize/ScriptableInputStream.html @@ -0,0 +1,373 @@ + + + + + + +ScriptableInputStream (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + +

    + +org.mozilla.javascript.serialize +
    +Class ScriptableInputStream

    +
    +java.lang.Object
    +  extended by java.io.InputStream
    +      extended by java.io.ObjectInputStream
    +          extended by org.mozilla.javascript.serialize.ScriptableInputStream
    +
    +
    +
    All Implemented Interfaces:
    java.io.Closeable, java.io.DataInput, java.io.ObjectInput, java.io.ObjectStreamConstants
    +
    +
    +
    +
    public class ScriptableInputStream
    extends java.io.ObjectInputStream
    + + +

    +Class ScriptableInputStream is used to read in a JavaScript + object or function previously serialized with a ScriptableOutputStream. + References to names in the exclusion list + replaced with references to the top-level scope specified during + creation of the ScriptableInputStream. +

    + +

    +

    +
    Author:
    +
    Norris Boyd
    +
    +
    + +

    + + + + + + + +
    +Nested Class Summary
    + + + + + + + +
    Nested classes/interfaces inherited from class java.io.ObjectInputStream
    java.io.ObjectInputStream.GetField
    +  + + + + + + + +
    +Field Summary
    + + + + + + + +
    Fields inherited from interface java.io.ObjectStreamConstants
    baseWireHandle, PROTOCOL_VERSION_1, PROTOCOL_VERSION_2, SC_BLOCK_DATA, SC_ENUM, SC_EXTERNALIZABLE, SC_SERIALIZABLE, SC_WRITE_METHOD, STREAM_MAGIC, STREAM_VERSION, SUBCLASS_IMPLEMENTATION_PERMISSION, SUBSTITUTION_PERMISSION, TC_ARRAY, TC_BASE, TC_BLOCKDATA, TC_BLOCKDATALONG, TC_CLASS, TC_CLASSDESC, TC_ENDBLOCKDATA, TC_ENUM, TC_EXCEPTION, TC_LONGSTRING, TC_MAX, TC_NULL, TC_OBJECT, TC_PROXYCLASSDESC, TC_REFERENCE, TC_RESET, TC_STRING
    +  + + + + + + + + + + +
    +Constructor Summary
    ScriptableInputStream(java.io.InputStream in, + Scriptable scope) + +
    +          Create a ScriptableInputStream.
    +  + + + + + + + + + + + + + + + +
    +Method Summary
    +protected  java.lang.ClassresolveClass(java.io.ObjectStreamClass desc) + +
    +           
    +protected  java.lang.ObjectresolveObject(java.lang.Object obj) + +
    +           
    + + + + + + + +
    Methods inherited from class java.io.ObjectInputStream
    available, close, defaultReadObject, enableResolveObject, read, read, readBoolean, readByte, readChar, readClassDescriptor, readDouble, readFields, readFloat, readFully, readFully, readInt, readLine, readLong, readObject, readObjectOverride, readShort, readStreamHeader, readUnshared, readUnsignedByte, readUnsignedShort, readUTF, registerValidation, resolveProxyClass, skipBytes
    + + + + + + + +
    Methods inherited from class java.io.InputStream
    mark, markSupported, read, reset, skip
    + + + + + + + +
    Methods inherited from class java.lang.Object
    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    + + + + + + + +
    Methods inherited from interface java.io.ObjectInput
    read, skip
    +  +

    + + + + + + + + +
    +Constructor Detail
    + +

    +ScriptableInputStream

    +
    +public ScriptableInputStream(java.io.InputStream in,
    +                             Scriptable scope)
    +                      throws java.io.IOException
    +
    +
    Create a ScriptableInputStream. +

    +

    +
    Parameters:
    in - the InputStream to read from.
    scope - the top-level scope to create the object in. +
    Throws: +
    java.io.IOException
    +
    + + + + + + + + +
    +Method Detail
    + +

    +resolveClass

    +
    +protected java.lang.Class resolveClass(java.io.ObjectStreamClass desc)
    +                                throws java.io.IOException,
    +                                       java.lang.ClassNotFoundException
    +
    +
    +
    Overrides:
    resolveClass in class java.io.ObjectInputStream
    +
    +
    + +
    Throws: +
    java.io.IOException +
    java.lang.ClassNotFoundException
    +
    +
    +
    + +

    +resolveObject

    +
    +protected java.lang.Object resolveObject(java.lang.Object obj)
    +                                  throws java.io.IOException
    +
    +
    +
    Overrides:
    resolveObject in class java.io.ObjectInputStream
    +
    +
    + +
    Throws: +
    java.io.IOException
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/serialize/ScriptableOutputStream.html b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/serialize/ScriptableOutputStream.html new file mode 100644 index 0000000..fb6d1ba --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/serialize/ScriptableOutputStream.html @@ -0,0 +1,458 @@ + + + + + + +ScriptableOutputStream (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + +

    + +org.mozilla.javascript.serialize +
    +Class ScriptableOutputStream

    +
    +java.lang.Object
    +  extended by java.io.OutputStream
    +      extended by java.io.ObjectOutputStream
    +          extended by org.mozilla.javascript.serialize.ScriptableOutputStream
    +
    +
    +
    All Implemented Interfaces:
    java.io.Closeable, java.io.DataOutput, java.io.Flushable, java.io.ObjectOutput, java.io.ObjectStreamConstants
    +
    +
    +
    +
    public class ScriptableOutputStream
    extends java.io.ObjectOutputStream
    + + +

    +Class ScriptableOutputStream is an ObjectOutputStream used + to serialize JavaScript objects and functions. Note that + compiled functions currently cannot be serialized, only + interpreted functions. The top-level scope containing the + object is not written out, but is instead replaced with + another top-level object when the ScriptableInputStream + reads in this object. Also, object corresponding to names + added to the exclude list are not written out but instead + are looked up during deserialization. This approach avoids + the creation of duplicate copies of standard objects + during deserialization. +

    + +

    +

    +
    Author:
    +
    Norris Boyd
    +
    +
    + +

    + + + + + + + +
    +Nested Class Summary
    + + + + + + + +
    Nested classes/interfaces inherited from class java.io.ObjectOutputStream
    java.io.ObjectOutputStream.PutField
    +  + + + + + + + +
    +Field Summary
    + + + + + + + +
    Fields inherited from interface java.io.ObjectStreamConstants
    baseWireHandle, PROTOCOL_VERSION_1, PROTOCOL_VERSION_2, SC_BLOCK_DATA, SC_ENUM, SC_EXTERNALIZABLE, SC_SERIALIZABLE, SC_WRITE_METHOD, STREAM_MAGIC, STREAM_VERSION, SUBCLASS_IMPLEMENTATION_PERMISSION, SUBSTITUTION_PERMISSION, TC_ARRAY, TC_BASE, TC_BLOCKDATA, TC_BLOCKDATALONG, TC_CLASS, TC_CLASSDESC, TC_ENDBLOCKDATA, TC_ENUM, TC_EXCEPTION, TC_LONGSTRING, TC_MAX, TC_NULL, TC_OBJECT, TC_PROXYCLASSDESC, TC_REFERENCE, TC_RESET, TC_STRING
    +  + + + + + + + + + + +
    +Constructor Summary
    ScriptableOutputStream(java.io.OutputStream out, + Scriptable scope) + +
    +          ScriptableOutputStream constructor.
    +  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +Method Summary
    + voidaddExcludedName(java.lang.String name) + +
    +          Adds a qualified name to the list of object to be excluded from + serialization.
    + voidaddOptionalExcludedName(java.lang.String name) + +
    +          Adds a qualified name to the list of object to be excluded from + serialization.
    + voidexcludeStandardObjectNames() + +
    +          Adds the names of the standard objects and their + prototypes to the list of excluded names.
    + booleanhasExcludedName(java.lang.String name) + +
    +          Returns true if the name is excluded from serialization.
    + voidremoveExcludedName(java.lang.String name) + +
    +          Removes a name from the list of names to exclude.
    +protected  java.lang.ObjectreplaceObject(java.lang.Object obj) + +
    +           
    + + + + + + + +
    Methods inherited from class java.io.ObjectOutputStream
    annotateClass, annotateProxyClass, close, defaultWriteObject, drain, enableReplaceObject, flush, putFields, reset, useProtocolVersion, write, write, write, writeBoolean, writeByte, writeBytes, writeChar, writeChars, writeClassDescriptor, writeDouble, writeFields, writeFloat, writeInt, writeLong, writeObject, writeObjectOverride, writeShort, writeStreamHeader, writeUnshared, writeUTF
    + + + + + + + +
    Methods inherited from class java.lang.Object
    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    +  +

    + + + + + + + + +
    +Constructor Detail
    + +

    +ScriptableOutputStream

    +
    +public ScriptableOutputStream(java.io.OutputStream out,
    +                              Scriptable scope)
    +                       throws java.io.IOException
    +
    +
    ScriptableOutputStream constructor. + Creates a ScriptableOutputStream for use in serializing + JavaScript objects. Calls excludeStandardObjectNames. +

    +

    +
    Parameters:
    out - the OutputStream to write to.
    scope - the scope containing the object. +
    Throws: +
    java.io.IOException
    +
    + + + + + + + + +
    +Method Detail
    + +

    +addOptionalExcludedName

    +
    +public void addOptionalExcludedName(java.lang.String name)
    +
    +
    Adds a qualified name to the list of object to be excluded from + serialization. Names excluded from serialization are looked up + in the new scope and replaced upon deserialization. +

    +

    +
    Parameters:
    name - a fully qualified name (of the form "a.b.c", where + "a" must be a property of the top-level object). The object + need not exist, in which case the name is ignored. +
    Throws: +
    java.lang.IllegalArgumentException - if the object is not a + Scriptable.
    +
    +
    +
    + +

    +addExcludedName

    +
    +public void addExcludedName(java.lang.String name)
    +
    +
    Adds a qualified name to the list of object to be excluded from + serialization. Names excluded from serialization are looked up + in the new scope and replaced upon deserialization. +

    +

    +
    Parameters:
    name - a fully qualified name (of the form "a.b.c", where + "a" must be a property of the top-level object) +
    Throws: +
    java.lang.IllegalArgumentException - if the object is not found or is not + a Scriptable.
    +
    +
    +
    + +

    +hasExcludedName

    +
    +public boolean hasExcludedName(java.lang.String name)
    +
    +
    Returns true if the name is excluded from serialization. +

    +

    +
    +
    +
    +
    + +

    +removeExcludedName

    +
    +public void removeExcludedName(java.lang.String name)
    +
    +
    Removes a name from the list of names to exclude. +

    +

    +
    +
    +
    +
    + +

    +excludeStandardObjectNames

    +
    +public void excludeStandardObjectNames()
    +
    +
    Adds the names of the standard objects and their + prototypes to the list of excluded names. +

    +

    +
    +
    +
    +
    + +

    +replaceObject

    +
    +protected java.lang.Object replaceObject(java.lang.Object obj)
    +                                  throws java.io.IOException
    +
    +
    +
    Overrides:
    replaceObject in class java.io.ObjectOutputStream
    +
    +
    + +
    Throws: +
    java.io.IOException
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/serialize/package-frame.html b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/serialize/package-frame.html new file mode 100644 index 0000000..39d85e2 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/serialize/package-frame.html @@ -0,0 +1,34 @@ + + + + + + +org.mozilla.javascript.serialize (Rhino) + + + + + + + + + + + +org.mozilla.javascript.serialize + + + + +
    +Classes  + +
    +ScriptableInputStream +
    +ScriptableOutputStream
    + + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/serialize/package-summary.html b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/serialize/package-summary.html new file mode 100644 index 0000000..568f562 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/serialize/package-summary.html @@ -0,0 +1,161 @@ + + + + + + +org.mozilla.javascript.serialize (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + +
    + +
    + + + +
    +

    +Package org.mozilla.javascript.serialize +

    + + + + + + + + + + + + + +
    +Class Summary
    ScriptableInputStreamClass ScriptableInputStream is used to read in a JavaScript + object or function previously serialized with a ScriptableOutputStream.
    ScriptableOutputStreamClass ScriptableOutputStream is an ObjectOutputStream used + to serialize JavaScript objects and functions.
    +  + +

    +

    +
    +
    + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/serialize/package-tree.html b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/serialize/package-tree.html new file mode 100644 index 0000000..e672261 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/org/mozilla/javascript/serialize/package-tree.html @@ -0,0 +1,163 @@ + + + + + + +org.mozilla.javascript.serialize Class Hierarchy (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + +
    + +
    + + + +
    +
    +

    +Hierarchy For Package org.mozilla.javascript.serialize +

    +
    +
    +
    Package Hierarchies:
    All Packages
    +
    +

    +Class Hierarchy +

    +
      +
    • java.lang.Object
        +
      • java.io.InputStream (implements java.io.Closeable) +
          +
        • java.io.ObjectInputStream (implements java.io.ObjectInput, java.io.ObjectStreamConstants) + +
        +
      • java.io.OutputStream (implements java.io.Closeable, java.io.Flushable) +
          +
        • java.io.ObjectOutputStream (implements java.io.ObjectOutput, java.io.ObjectStreamConstants) + +
        +
      +
    +
    + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/overview-frame.html b/trunk/infrastructure/rhino1_7R1/javadoc/overview-frame.html new file mode 100644 index 0000000..10e9be0 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/overview-frame.html @@ -0,0 +1,48 @@ + + + + + + +Overview List (Rhino) + + + + + + + + + + + + + + + +
    +
    + + + + + +
    All Classes +

    + +Packages +
    +org.mozilla.javascript +
    +org.mozilla.javascript.debug +
    +org.mozilla.javascript.optimizer +
    +org.mozilla.javascript.serialize +
    +

    + +

    +  + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/overview-summary.html b/trunk/infrastructure/rhino1_7R1/javadoc/overview-summary.html new file mode 100644 index 0000000..043bb30 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/overview-summary.html @@ -0,0 +1,161 @@ + + + + + + +Overview (Rhino) + + + + + + + + + + + + +


    + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    +Packages
    org.mozilla.javascript 
    org.mozilla.javascript.debug 
    org.mozilla.javascript.optimizer 
    org.mozilla.javascript.serialize 
    + +


    + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/overview-tree.html b/trunk/infrastructure/rhino1_7R1/javadoc/overview-tree.html new file mode 100644 index 0000000..9c5398c --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/overview-tree.html @@ -0,0 +1,196 @@ + + + + + + +Class Hierarchy (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + +
    + +
    + + + +
    +
    +

    +Hierarchy For All Packages

    +
    +
    +
    Package Hierarchies:
    org.mozilla.javascript, org.mozilla.javascript.debug, org.mozilla.javascript.optimizer, org.mozilla.javascript.serialize
    +
    +

    +Class Hierarchy +

    +
      +
    • java.lang.Object
        +
      • org.mozilla.javascript.ClassCache
      • org.mozilla.javascript.optimizer.ClassCompiler
      • org.mozilla.javascript.CompilerEnvirons
      • org.mozilla.javascript.Context
      • org.mozilla.javascript.ContextFactory
      • org.mozilla.javascript.Delegator (implements org.mozilla.javascript.Function) + +
      • java.io.InputStream (implements java.io.Closeable) +
          +
        • java.io.ObjectInputStream (implements java.io.ObjectInput, java.io.ObjectStreamConstants) + +
        +
      • java.io.OutputStream (implements java.io.Closeable, java.io.Flushable) +
          +
        • java.io.ObjectOutputStream (implements java.io.ObjectOutput, java.io.ObjectStreamConstants) + +
        +
      • org.mozilla.javascript.ScriptableObject (implements org.mozilla.javascript.ConstProperties, org.mozilla.javascript.debug.DebuggableObject, org.mozilla.javascript.Scriptable, java.io.Serializable) +
          +
        • org.mozilla.javascript.IdScriptableObject (implements org.mozilla.javascript.IdFunctionCall) + +
        +
      • org.mozilla.javascript.SecurityController
      • java.lang.Throwable (implements java.io.Serializable) + +
      • org.mozilla.javascript.WrapFactory
      +
    +

    +Interface Hierarchy +

    + +
    + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/package-list b/trunk/infrastructure/rhino1_7R1/javadoc/package-list new file mode 100644 index 0000000..62fc06b --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/package-list @@ -0,0 +1,4 @@ +org.mozilla.javascript +org.mozilla.javascript.debug +org.mozilla.javascript.optimizer +org.mozilla.javascript.serialize diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/resources/inherit.gif b/trunk/infrastructure/rhino1_7R1/javadoc/resources/inherit.gif new file mode 100644 index 0000000..c814867 Binary files /dev/null and b/trunk/infrastructure/rhino1_7R1/javadoc/resources/inherit.gif differ diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/serialized-form.html b/trunk/infrastructure/rhino1_7R1/javadoc/serialized-form.html new file mode 100644 index 0000000..c022624 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/serialized-form.html @@ -0,0 +1,1652 @@ + + + + + + +Serialized Form (Rhino) + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + +
    + +
    + + + +
    +
    +

    +Serialized Form

    +
    +
    + + + + + +
    +Package org.mozilla.javascript
    + +

    + + + + + +
    +Class org.mozilla.javascript.BaseFunction extends org.mozilla.javascript.IdScriptableObject implements Serializable
    + +

    +serialVersionUID: 5311394446546053859L + +

    + + + + + +
    +Serialized Fields
    + +

    +prototypeProperty

    +
    +java.lang.Object prototypeProperty
    +
    +
    +
    +
    +
    +

    +prototypePropertyAttributes

    +
    +int prototypePropertyAttributes
    +
    +
    +
    +
    + +

    + + + + + +
    +Class org.mozilla.javascript.EcmaError extends RhinoException implements Serializable
    + +

    +serialVersionUID: -6261226256957286699L + +

    + + + + + +
    +Serialized Fields
    + +

    +errorName

    +
    +java.lang.String errorName
    +
    +
    +
    +
    +
    +

    +errorMessage

    +
    +java.lang.String errorMessage
    +
    +
    +
    +
    + +

    + + + + + +
    +Class org.mozilla.javascript.EvaluatorException extends RhinoException implements Serializable
    + +

    +serialVersionUID: -8743165779676009808L + +

    + +

    + + + + + +
    +Class org.mozilla.javascript.FunctionObject extends org.mozilla.javascript.BaseFunction implements Serializable
    + +

    +serialVersionUID: -5332312783643935019L + +

    + + + + + +
    +Serialization Methods
    + +

    +

    +readObject

    +
    +private void readObject(java.io.ObjectInputStream in)
    +                 throws java.io.IOException,
    +                        java.lang.ClassNotFoundException
    +
    +
    + +
    Throws: +
    java.io.IOException +
    java.lang.ClassNotFoundException
    +
    +
    + + + + + +
    +Serialized Fields
    + +

    +member

    +
    +org.mozilla.javascript.MemberBox member
    +
    +
    +
    +
    +
    +

    +functionName

    +
    +java.lang.String functionName
    +
    +
    +
    +
    +
    +

    +parmsLength

    +
    +int parmsLength
    +
    +
    +
    +
    +
    +

    +isStatic

    +
    +boolean isStatic
    +
    +
    +
    +
    + +

    + + + + + +
    +Class org.mozilla.javascript.IdFunctionObject extends org.mozilla.javascript.BaseFunction implements Serializable
    + +

    +serialVersionUID: -5332312783643935019L + +

    + + + + + +
    +Serialized Fields
    + +

    +idcall

    +
    +org.mozilla.javascript.IdFunctionCall idcall
    +
    +
    +
    +
    +
    +

    +tag

    +
    +java.lang.Object tag
    +
    +
    +
    +
    +
    +

    +methodId

    +
    +int methodId
    +
    +
    +
    +
    +
    +

    +arity

    +
    +int arity
    +
    +
    +
    +
    +
    +

    +useCallAsConstructor

    +
    +boolean useCallAsConstructor
    +
    +
    +
    +
    +
    +

    +functionName

    +
    +java.lang.String functionName
    +
    +
    +
    +
    + +

    + + + + + +
    +Class org.mozilla.javascript.IdScriptableObject extends ScriptableObject implements Serializable
    + +

    + + + + + +
    +Serialization Methods
    + +

    +

    +readObject

    +
    +private void readObject(java.io.ObjectInputStream stream)
    +                 throws java.io.IOException,
    +                        java.lang.ClassNotFoundException
    +
    +
    + +
    Throws: +
    java.io.IOException +
    java.lang.ClassNotFoundException
    +
    +
    +
    +

    +writeObject

    +
    +private void writeObject(java.io.ObjectOutputStream stream)
    +                  throws java.io.IOException
    +
    +
    + +
    Throws: +
    java.io.IOException
    +
    +
    + +

    + + + + + +
    +Class org.mozilla.javascript.ImporterTopLevel extends org.mozilla.javascript.IdScriptableObject implements Serializable
    + +

    +serialVersionUID: -9095380847465315412L + +

    + + + + + +
    +Serialized Fields
    + +

    +importedPackages

    +
    +org.mozilla.javascript.ObjArray importedPackages
    +
    +
    +
    +
    +
    +

    +topScopeFlag

    +
    +boolean topScopeFlag
    +
    +
    +
    +
    + +

    + + + + + +
    +Class org.mozilla.javascript.JavaScriptException extends RhinoException implements Serializable
    + +

    +serialVersionUID: -7666130513694669293L + +

    + + + + + +
    +Serialized Fields
    + +

    +value

    +
    +java.lang.Object value
    +
    +
    +
    +
    + +

    + + + + + +
    +Class org.mozilla.javascript.LazilyLoadedCtor extends java.lang.Object implements Serializable
    + +

    +serialVersionUID: 1L + +

    + + + + + +
    +Serialized Fields
    + +

    +scope

    +
    +ScriptableObject scope
    +
    +
    +
    +
    +
    +

    +propertyName

    +
    +java.lang.String propertyName
    +
    +
    +
    +
    +
    +

    +className

    +
    +java.lang.String className
    +
    +
    +
    +
    +
    +

    +sealed

    +
    +boolean sealed
    +
    +
    +
    +
    +
    +

    +initializedValue

    +
    +java.lang.Object initializedValue
    +
    +
    +
    +
    +
    +

    +state

    +
    +int state
    +
    +
    +
    +
    + +

    + + + + + +
    +Class org.mozilla.javascript.NativeArray extends org.mozilla.javascript.IdScriptableObject implements Serializable
    + +

    +serialVersionUID: 7331366857676127338L + +

    + + + + + +
    +Serialized Fields
    + +

    +length

    +
    +long length
    +
    +
    Internal representation of the JavaScript array's length property. +

    +

    +
    +
    +
    +

    +dense

    +
    +java.lang.Object[] dense
    +
    +
    Fast storage for dense arrays. Sparse arrays will use the superclass's + hashtable storage scheme. +

    +

    +
    +
    +
    +

    +denseOnly

    +
    +boolean denseOnly
    +
    +
    True if all numeric properties are stored in dense. +

    +

    +
    +
    + +

    + + + + + +
    +Class org.mozilla.javascript.NativeCall extends org.mozilla.javascript.IdScriptableObject implements Serializable
    + +

    +serialVersionUID: -7471457301304454454L + +

    + + + + + +
    +Serialized Fields
    + +

    +function

    +
    +org.mozilla.javascript.NativeFunction function
    +
    +
    +
    +
    +
    +

    +originalArgs

    +
    +java.lang.Object[] originalArgs
    +
    +
    +
    +
    + +

    + + + + + +
    +Class org.mozilla.javascript.NativeFunction extends org.mozilla.javascript.BaseFunction implements Serializable
    + +

    + +

    + + + + + +
    +Class org.mozilla.javascript.NativeGenerator extends org.mozilla.javascript.IdScriptableObject implements Serializable
    + +

    + + + + + +
    +Serialized Fields
    + +

    +function

    +
    +org.mozilla.javascript.NativeFunction function
    +
    +
    +
    +
    +
    +

    +savedState

    +
    +java.lang.Object savedState
    +
    +
    +
    +
    +
    +

    +lineSource

    +
    +java.lang.String lineSource
    +
    +
    +
    +
    +
    +

    +lineNumber

    +
    +int lineNumber
    +
    +
    +
    +
    +
    +

    +firstTime

    +
    +boolean firstTime
    +
    +
    +
    +
    +
    +

    +locked

    +
    +boolean locked
    +
    +
    +
    +
    + +

    + + + + + +
    +Class org.mozilla.javascript.NativeGenerator.GeneratorClosedException extends java.lang.RuntimeException implements Serializable
    + +

    + +

    + + + + + +
    +Class org.mozilla.javascript.NativeGlobal extends java.lang.Object implements Serializable
    + +

    +serialVersionUID: 6080442165748707530L + +

    + +

    + + + + + +
    +Class org.mozilla.javascript.NativeIterator extends org.mozilla.javascript.IdScriptableObject implements Serializable
    + +

    + + + + + +
    +Serialized Fields
    + +

    +objectIterator

    +
    +java.lang.Object objectIterator
    +
    +
    +
    +
    + +

    + + + + + +
    +Class org.mozilla.javascript.NativeJavaArray extends org.mozilla.javascript.NativeJavaObject implements Serializable
    + +

    +serialVersionUID: -924022554283675333L + +

    + + + + + +
    +Serialized Fields
    + +

    +array

    +
    +java.lang.Object array
    +
    +
    +
    +
    +
    +

    +length

    +
    +int length
    +
    +
    +
    +
    +
    +

    +cls

    +
    +java.lang.Class<T> cls
    +
    +
    +
    +
    + +

    + + + + + +
    +Class org.mozilla.javascript.NativeJavaClass extends org.mozilla.javascript.NativeJavaObject implements Serializable
    + +

    +serialVersionUID: -6460763940409461664L + +

    + + + + + +
    +Serialized Fields
    + +

    +staticFieldAndMethods

    +
    +java.util.Hashtable<K,V> staticFieldAndMethods
    +
    +
    +
    +
    + +

    + + + + + +
    +Class org.mozilla.javascript.NativeJavaConstructor extends org.mozilla.javascript.BaseFunction implements Serializable
    + +

    +serialVersionUID: -8149253217482668463L + +

    + + + + + +
    +Serialized Fields
    + +

    +ctor

    +
    +org.mozilla.javascript.MemberBox ctor
    +
    +
    +
    +
    + +

    + + + + + +
    +Class org.mozilla.javascript.NativeJavaMethod extends org.mozilla.javascript.BaseFunction implements Serializable
    + +

    +serialVersionUID: -3440381785576412928L + +

    + + + + + +
    +Serialized Fields
    + +

    +methods

    +
    +org.mozilla.javascript.MemberBox[] methods
    +
    +
    +
    +
    +
    +

    +functionName

    +
    +java.lang.String functionName
    +
    +
    +
    +
    + +

    + + + + + +
    +Class org.mozilla.javascript.NativeJavaObject extends java.lang.Object implements Serializable
    + +

    +serialVersionUID: -6948590651130498591L + +

    + + + + + +
    +Serialization Methods
    + +

    +

    +readObject

    +
    +private void readObject(java.io.ObjectInputStream in)
    +                 throws java.io.IOException,
    +                        java.lang.ClassNotFoundException
    +
    +
    + +
    Throws: +
    java.io.IOException +
    java.lang.ClassNotFoundException
    +
    +
    +
    +

    +writeObject

    +
    +private void writeObject(java.io.ObjectOutputStream out)
    +                  throws java.io.IOException
    +
    +
    + +
    Throws: +
    java.io.IOException
    +
    +
    + + + + + +
    +Serialized Fields
    + +

    +prototype

    +
    +Scriptable prototype
    +
    +
    The prototype of this object. +

    +

    +
    +
    +
    +

    +parent

    +
    +Scriptable parent
    +
    +
    The parent scope of this object. +

    +

    +
    +
    + +

    + + + + + +
    +Class org.mozilla.javascript.NativeJavaPackage extends ScriptableObject implements Serializable
    + +

    +serialVersionUID: 7445054382212031523L + +

    + + + + + +
    +Serialized Fields
    + +

    +packageName

    +
    +java.lang.String packageName
    +
    +
    +
    +
    +
    +

    +classLoader

    +
    +java.lang.ClassLoader classLoader
    +
    +
    +
    +
    + +

    + + + + + +
    +Class org.mozilla.javascript.NativeJavaTopPackage extends org.mozilla.javascript.NativeJavaPackage implements Serializable
    + +

    +serialVersionUID: -1455787259477709999L + +

    + +

    + + + + + +
    +Class org.mozilla.javascript.NativeObject extends org.mozilla.javascript.IdScriptableObject implements Serializable
    + +

    +serialVersionUID: -6345305608474346996L + +

    + +

    + + + + + +
    +Class org.mozilla.javascript.NativeWith extends java.lang.Object implements Serializable
    + +

    +serialVersionUID: 1L + +

    + + + + + +
    +Serialized Fields
    + +

    +prototype

    +
    +Scriptable prototype
    +
    +
    +
    +
    +
    +

    +parent

    +
    +Scriptable parent
    +
    +
    +
    +
    + +

    + + + + + +
    +Class org.mozilla.javascript.ObjArray extends java.lang.Object implements Serializable
    + +

    +serialVersionUID: 4174889037736658296L + +

    + + + + + +
    +Serialization Methods
    + +

    +

    +readObject

    +
    +private void readObject(java.io.ObjectInputStream is)
    +                 throws java.io.IOException,
    +                        java.lang.ClassNotFoundException
    +
    +
    + +
    Throws: +
    java.io.IOException +
    java.lang.ClassNotFoundException
    +
    +
    +
    +

    +writeObject

    +
    +private void writeObject(java.io.ObjectOutputStream os)
    +                  throws java.io.IOException
    +
    +
    + +
    Throws: +
    java.io.IOException
    +
    +
    + + + + + +
    +Serialized Fields
    + +

    +size

    +
    +int size
    +
    +
    +
    +
    +
    +

    +sealed

    +
    +boolean sealed
    +
    +
    +
    +
    + +

    + + + + + +
    +Class org.mozilla.javascript.ObjToIntMap extends java.lang.Object implements Serializable
    + +

    +serialVersionUID: -1542220580748809402L + +

    + + + + + +
    +Serialization Methods
    + +

    +

    +readObject

    +
    +private void readObject(java.io.ObjectInputStream in)
    +                 throws java.io.IOException,
    +                        java.lang.ClassNotFoundException
    +
    +
    + +
    Throws: +
    java.io.IOException +
    java.lang.ClassNotFoundException
    +
    +
    +
    +

    +writeObject

    +
    +private void writeObject(java.io.ObjectOutputStream out)
    +                  throws java.io.IOException
    +
    +
    + +
    Throws: +
    java.io.IOException
    +
    +
    + + + + + +
    +Serialized Fields
    + +

    +power

    +
    +int power
    +
    +
    +
    +
    +
    +

    +keyCount

    +
    +int keyCount
    +
    +
    +
    +
    + +

    + + + + + +
    +Class org.mozilla.javascript.Ref extends java.lang.Object implements Serializable
    + +

    + +

    + + + + + +
    +Class org.mozilla.javascript.RhinoException extends java.lang.RuntimeException implements Serializable
    + +

    + + + + + +
    +Serialized Fields
    + +

    +sourceName

    +
    +java.lang.String sourceName
    +
    +
    +
    +
    +
    +

    +lineNumber

    +
    +int lineNumber
    +
    +
    +
    +
    +
    +

    +lineSource

    +
    +java.lang.String lineSource
    +
    +
    +
    +
    +
    +

    +columnNumber

    +
    +int columnNumber
    +
    +
    +
    +
    +
    +

    +interpreterStackInfo

    +
    +java.lang.Object interpreterStackInfo
    +
    +
    +
    +
    +
    +

    +interpreterLineData

    +
    +int[] interpreterLineData
    +
    +
    +
    +
    + +

    + + + + + +
    +Class org.mozilla.javascript.ScriptableObject extends java.lang.Object implements Serializable
    + +

    + + + + + +
    +Serialization Methods
    + +

    +

    +readObject

    +
    +private void readObject(java.io.ObjectInputStream in)
    +                 throws java.io.IOException,
    +                        java.lang.ClassNotFoundException
    +
    +
    + +
    Throws: +
    java.io.IOException +
    java.lang.ClassNotFoundException
    +
    +
    +
    +

    +writeObject

    +
    +private void writeObject(java.io.ObjectOutputStream out)
    +                  throws java.io.IOException
    +
    +
    + +
    Throws: +
    java.io.IOException
    +
    +
    + + + + + +
    +Serialized Fields
    + +

    +prototypeObject

    +
    +Scriptable prototypeObject
    +
    +
    The prototype of this object. +

    +

    +
    +
    +
    +

    +parentScopeObject

    +
    +Scriptable parentScopeObject
    +
    +
    The parent scope of this object. +

    +

    +
    +
    +
    +

    +count

    +
    +int count
    +
    +
    +
    +
    + +

    + + + + + +
    +Class org.mozilla.javascript.UintMap extends java.lang.Object implements Serializable
    + +

    +serialVersionUID: 4242698212885848444L + +

    + + + + + +
    +Serialization Methods
    + +

    +

    +readObject

    +
    +private void readObject(java.io.ObjectInputStream in)
    +                 throws java.io.IOException,
    +                        java.lang.ClassNotFoundException
    +
    +
    + +
    Throws: +
    java.io.IOException +
    java.lang.ClassNotFoundException
    +
    +
    +
    +

    +writeObject

    +
    +private void writeObject(java.io.ObjectOutputStream out)
    +                  throws java.io.IOException
    +
    +
    + +
    Throws: +
    java.io.IOException
    +
    +
    + + + + + +
    +Serialized Fields
    + +

    +power

    +
    +int power
    +
    +
    +
    +
    +
    +

    +keyCount

    +
    +int keyCount
    +
    +
    +
    +
    + +

    + + + + + +
    +Class org.mozilla.javascript.Undefined extends java.lang.Object implements Serializable
    + +

    +serialVersionUID: 9195680630202616767L + +

    + + + + + +
    +Serialization Methods
    + +

    +

    +readResolve

    +
    +public java.lang.Object readResolve()
    +
    +
    +
    +
    + + +

    + + + + + +
    +Class org.mozilla.javascript.UniqueTag extends java.lang.Object implements Serializable
    + +

    +serialVersionUID: -4320556826714577259L + +

    + + + + + +
    +Serialization Methods
    + +

    +

    +readResolve

    +
    +public java.lang.Object readResolve()
    +
    +
    +
    +
    + + + + + + +
    +Serialized Fields
    + +

    +tagId

    +
    +int tagId
    +
    +
    +
    +
    + +

    + + + + + +
    +Class org.mozilla.javascript.WrappedException extends EvaluatorException implements Serializable
    + +

    +serialVersionUID: -1551979216966520648L + +

    + + + + + +
    +Serialized Fields
    + +

    +exception

    +
    +java.lang.Throwable exception
    +
    +
    +
    +
    + +

    +


    + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + + diff --git a/trunk/infrastructure/rhino1_7R1/javadoc/stylesheet.css b/trunk/infrastructure/rhino1_7R1/javadoc/stylesheet.css new file mode 100644 index 0000000..6ea9e51 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/javadoc/stylesheet.css @@ -0,0 +1,29 @@ +/* Javadoc style sheet */ + +/* Define colors, fonts and other style attributes here to override the defaults */ + +/* Page background color */ +body { background-color: #FFFFFF; color:#000000 } + +/* Headings */ +h1 { font-size: 145% } + +/* Table colors */ +.TableHeadingColor { background: #CCCCFF; color:#000000 } /* Dark mauve */ +.TableSubHeadingColor { background: #EEEEFF; color:#000000 } /* Light mauve */ +.TableRowColor { background: #FFFFFF; color:#000000 } /* White */ + +/* Font used in left-hand frame lists */ +.FrameTitleFont { font-size: 100%; font-family: Helvetica, Arial, sans-serif; color:#000000 } +.FrameHeadingFont { font-size: 90%; font-family: Helvetica, Arial, sans-serif; color:#000000 } +.FrameItemFont { font-size: 90%; font-family: Helvetica, Arial, sans-serif; color:#000000 } + +/* Navigation bar fonts and colors */ +.NavBarCell1 { background-color:#EEEEFF; color:#000000} /* Light mauve */ +.NavBarCell1Rev { background-color:#00008B; color:#FFFFFF} /* Dark Blue */ +.NavBarFont1 { font-family: Arial, Helvetica, sans-serif; color:#000000;color:#000000;} +.NavBarFont1Rev { font-family: Arial, Helvetica, sans-serif; color:#FFFFFF;color:#FFFFFF;} + +.NavBarCell2 { font-family: Arial, Helvetica, sans-serif; background-color:#FFFFFF; color:#000000} +.NavBarCell3 { font-family: Arial, Helvetica, sans-serif; background-color:#FFFFFF; color:#000000} + diff --git a/trunk/infrastructure/rhino1_7R1/lib/jsr173_1.0_api.jar b/trunk/infrastructure/rhino1_7R1/lib/jsr173_1.0_api.jar new file mode 100644 index 0000000..fef9a9c Binary files /dev/null and b/trunk/infrastructure/rhino1_7R1/lib/jsr173_1.0_api.jar differ diff --git a/trunk/infrastructure/rhino1_7R1/lib/xbean.jar b/trunk/infrastructure/rhino1_7R1/lib/xbean.jar new file mode 100644 index 0000000..8a4dff4 Binary files /dev/null and b/trunk/infrastructure/rhino1_7R1/lib/xbean.jar differ diff --git a/trunk/infrastructure/rhino1_7R1/src/build.xml b/trunk/infrastructure/rhino1_7R1/src/build.xml new file mode 100644 index 0000000..a0a1e13 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/build.xml @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/trunk/infrastructure/rhino1_7R1/src/manifest b/trunk/infrastructure/rhino1_7R1/src/manifest new file mode 100644 index 0000000..b7d0c06 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/manifest @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: org.mozilla.javascript.tools.shell.Main +Class-Path: xbean.jar diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/classfile/ByteCode.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/classfile/ByteCode.java new file mode 100644 index 0000000..fa4713e --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/classfile/ByteCode.java @@ -0,0 +1,274 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Roger Lawrence + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.classfile; + +/** + * This class provides opcode values expected by the JVM in Java class files. + * + * It also provides tables for internal use by the ClassFileWriter. + * + * @author Roger Lawrence + */ +public class ByteCode { + + /** + * The byte opcodes defined by the Java Virtual Machine. + */ + public static final int + NOP = 0x00, + ACONST_NULL = 0x01, + ICONST_M1 = 0x02, + ICONST_0 = 0x03, + ICONST_1 = 0x04, + ICONST_2 = 0x05, + ICONST_3 = 0x06, + ICONST_4 = 0x07, + ICONST_5 = 0x08, + LCONST_0 = 0x09, + LCONST_1 = 0x0A, + FCONST_0 = 0x0B, + FCONST_1 = 0x0C, + FCONST_2 = 0x0D, + DCONST_0 = 0x0E, + DCONST_1 = 0x0F, + BIPUSH = 0x10, + SIPUSH = 0x11, + LDC = 0x12, + LDC_W = 0x13, + LDC2_W = 0x14, + ILOAD = 0x15, + LLOAD = 0x16, + FLOAD = 0x17, + DLOAD = 0x18, + ALOAD = 0x19, + ILOAD_0 = 0x1A, + ILOAD_1 = 0x1B, + ILOAD_2 = 0x1C, + ILOAD_3 = 0x1D, + LLOAD_0 = 0x1E, + LLOAD_1 = 0x1F, + LLOAD_2 = 0x20, + LLOAD_3 = 0x21, + FLOAD_0 = 0x22, + FLOAD_1 = 0x23, + FLOAD_2 = 0x24, + FLOAD_3 = 0x25, + DLOAD_0 = 0x26, + DLOAD_1 = 0x27, + DLOAD_2 = 0x28, + DLOAD_3 = 0x29, + ALOAD_0 = 0x2A, + ALOAD_1 = 0x2B, + ALOAD_2 = 0x2C, + ALOAD_3 = 0x2D, + IALOAD = 0x2E, + LALOAD = 0x2F, + FALOAD = 0x30, + DALOAD = 0x31, + AALOAD = 0x32, + BALOAD = 0x33, + CALOAD = 0x34, + SALOAD = 0x35, + ISTORE = 0x36, + LSTORE = 0x37, + FSTORE = 0x38, + DSTORE = 0x39, + ASTORE = 0x3A, + ISTORE_0 = 0x3B, + ISTORE_1 = 0x3C, + ISTORE_2 = 0x3D, + ISTORE_3 = 0x3E, + LSTORE_0 = 0x3F, + LSTORE_1 = 0x40, + LSTORE_2 = 0x41, + LSTORE_3 = 0x42, + FSTORE_0 = 0x43, + FSTORE_1 = 0x44, + FSTORE_2 = 0x45, + FSTORE_3 = 0x46, + DSTORE_0 = 0x47, + DSTORE_1 = 0x48, + DSTORE_2 = 0x49, + DSTORE_3 = 0x4A, + ASTORE_0 = 0x4B, + ASTORE_1 = 0x4C, + ASTORE_2 = 0x4D, + ASTORE_3 = 0x4E, + IASTORE = 0x4F, + LASTORE = 0x50, + FASTORE = 0x51, + DASTORE = 0x52, + AASTORE = 0x53, + BASTORE = 0x54, + CASTORE = 0x55, + SASTORE = 0x56, + POP = 0x57, + POP2 = 0x58, + DUP = 0x59, + DUP_X1 = 0x5A, + DUP_X2 = 0x5B, + DUP2 = 0x5C, + DUP2_X1 = 0x5D, + DUP2_X2 = 0x5E, + SWAP = 0x5F, + IADD = 0x60, + LADD = 0x61, + FADD = 0x62, + DADD = 0x63, + ISUB = 0x64, + LSUB = 0x65, + FSUB = 0x66, + DSUB = 0x67, + IMUL = 0x68, + LMUL = 0x69, + FMUL = 0x6A, + DMUL = 0x6B, + IDIV = 0x6C, + LDIV = 0x6D, + FDIV = 0x6E, + DDIV = 0x6F, + IREM = 0x70, + LREM = 0x71, + FREM = 0x72, + DREM = 0x73, + INEG = 0x74, + LNEG = 0x75, + FNEG = 0x76, + DNEG = 0x77, + ISHL = 0x78, + LSHL = 0x79, + ISHR = 0x7A, + LSHR = 0x7B, + IUSHR = 0x7C, + LUSHR = 0x7D, + IAND = 0x7E, + LAND = 0x7F, + IOR = 0x80, + LOR = 0x81, + IXOR = 0x82, + LXOR = 0x83, + IINC = 0x84, + I2L = 0x85, + I2F = 0x86, + I2D = 0x87, + L2I = 0x88, + L2F = 0x89, + L2D = 0x8A, + F2I = 0x8B, + F2L = 0x8C, + F2D = 0x8D, + D2I = 0x8E, + D2L = 0x8F, + D2F = 0x90, + I2B = 0x91, + I2C = 0x92, + I2S = 0x93, + LCMP = 0x94, + FCMPL = 0x95, + FCMPG = 0x96, + DCMPL = 0x97, + DCMPG = 0x98, + IFEQ = 0x99, + IFNE = 0x9A, + IFLT = 0x9B, + IFGE = 0x9C, + IFGT = 0x9D, + IFLE = 0x9E, + IF_ICMPEQ = 0x9F, + IF_ICMPNE = 0xA0, + IF_ICMPLT = 0xA1, + IF_ICMPGE = 0xA2, + IF_ICMPGT = 0xA3, + IF_ICMPLE = 0xA4, + IF_ACMPEQ = 0xA5, + IF_ACMPNE = 0xA6, + GOTO = 0xA7, + JSR = 0xA8, + RET = 0xA9, + TABLESWITCH = 0xAA, + LOOKUPSWITCH = 0xAB, + IRETURN = 0xAC, + LRETURN = 0xAD, + FRETURN = 0xAE, + DRETURN = 0xAF, + ARETURN = 0xB0, + RETURN = 0xB1, + GETSTATIC = 0xB2, + PUTSTATIC = 0xB3, + GETFIELD = 0xB4, + PUTFIELD = 0xB5, + INVOKEVIRTUAL = 0xB6, + INVOKESPECIAL = 0xB7, + INVOKESTATIC = 0xB8, + INVOKEINTERFACE = 0xB9, + NEW = 0xBB, + NEWARRAY = 0xBC, + ANEWARRAY = 0xBD, + ARRAYLENGTH = 0xBE, + ATHROW = 0xBF, + CHECKCAST = 0xC0, + INSTANCEOF = 0xC1, + MONITORENTER = 0xC2, + MONITOREXIT = 0xC3, + WIDE = 0xC4, + MULTIANEWARRAY = 0xC5, + IFNULL = 0xC6, + IFNONNULL = 0xC7, + GOTO_W = 0xC8, + JSR_W = 0xC9, + BREAKPOINT = 0xCA, + + IMPDEP1 = 0xFE, + IMPDEP2 = 0xFF; + + /** + * Types for the NEWARRAY opcode. + */ + public static final byte + T_BOOLEAN = 4, + T_CHAR = 5, + T_FLOAT = 6, + T_DOUBLE = 7, + T_BYTE = 8, + T_SHORT = 9, + T_INT = 10, + T_LONG = 11; + + +} diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/classfile/ClassFileWriter.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/classfile/ClassFileWriter.java new file mode 100644 index 0000000..b9c6c96 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/classfile/ClassFileWriter.java @@ -0,0 +1,3038 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Roger Lawrence + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.classfile; + +import org.mozilla.javascript.ObjToIntMap; +import org.mozilla.javascript.ObjArray; +import org.mozilla.javascript.UintMap; + +import java.io.*; + +/** + * ClassFileWriter + * + * A ClassFileWriter is used to write a Java class file. Methods are + * provided to create fields and methods, and within methods to write + * Java bytecodes. + * + * @author Roger Lawrence + */ +public class ClassFileWriter { + + /** + * Thrown for cases where the error in generating the class file is + * due to a program size constraints rather than a likely bug in the + * compiler. + */ + public static class ClassFileFormatException extends RuntimeException { + ClassFileFormatException(String message) { + super(message); + } + } + + /** + * Construct a ClassFileWriter for a class. + * + * @param className the name of the class to write, including + * full package qualification. + * @param superClassName the name of the superclass of the class + * to write, including full package qualification. + * @param sourceFileName the name of the source file to use for + * producing debug information, or null if debug information + * is not desired + */ + public ClassFileWriter(String className, String superClassName, + String sourceFileName) + { + generatedClassName = className; + itsConstantPool = new ConstantPool(this); + itsThisClassIndex = itsConstantPool.addClass(className); + itsSuperClassIndex = itsConstantPool.addClass(superClassName); + if (sourceFileName != null) + itsSourceFileNameIndex = itsConstantPool.addUtf8(sourceFileName); + itsFlags = ACC_PUBLIC; + } + + public final String getClassName() + { + return generatedClassName; + } + + /** + * Add an interface implemented by this class. + * + * This method may be called multiple times for classes that + * implement multiple interfaces. + * + * @param interfaceName a name of an interface implemented + * by the class being written, including full package + * qualification. + */ + public void addInterface(String interfaceName) { + short interfaceIndex = itsConstantPool.addClass(interfaceName); + itsInterfaces.add(new Short(interfaceIndex)); + } + + public static final short + ACC_PUBLIC = 0x0001, + ACC_PRIVATE = 0x0002, + ACC_PROTECTED = 0x0004, + ACC_STATIC = 0x0008, + ACC_FINAL = 0x0010, + ACC_SYNCHRONIZED = 0x0020, + ACC_VOLATILE = 0x0040, + ACC_TRANSIENT = 0x0080, + ACC_NATIVE = 0x0100, + ACC_ABSTRACT = 0x0400; + + /** + * Set the class's flags. + * + * Flags must be a set of the following flags, bitwise or'd + * together: + * ACC_PUBLIC + * ACC_PRIVATE + * ACC_PROTECTED + * ACC_FINAL + * ACC_ABSTRACT + * TODO: check that this is the appropriate set + * @param flags the set of class flags to set + */ + public void setFlags(short flags) { + itsFlags = flags; + } + + static String getSlashedForm(String name) + { + return name.replace('.', '/'); + } + + /** + * Convert Java class name in dot notation into + * "Lname-with-dots-replaced-by-slashes;" form suitable for use as + * JVM type signatures. + */ + public static String classNameToSignature(String name) + { + int nameLength = name.length(); + int colonPos = 1 + nameLength; + char[] buf = new char[colonPos + 1]; + buf[0] = 'L'; + buf[colonPos] = ';'; + name.getChars(0, nameLength, buf, 1); + for (int i = 1; i != colonPos; ++i) { + if (buf[i] == '.') { + buf[i] = '/'; + } + } + return new String(buf, 0, colonPos + 1); + } + + /** + * Add a field to the class. + * + * @param fieldName the name of the field + * @param type the type of the field using ... + * @param flags the attributes of the field, such as ACC_PUBLIC, etc. + * bitwise or'd together + */ + public void addField(String fieldName, String type, short flags) { + short fieldNameIndex = itsConstantPool.addUtf8(fieldName); + short typeIndex = itsConstantPool.addUtf8(type); + itsFields.add(new ClassFileField(fieldNameIndex, typeIndex, flags)); + } + + /** + * Add a field to the class. + * + * @param fieldName the name of the field + * @param type the type of the field using ... + * @param flags the attributes of the field, such as ACC_PUBLIC, etc. + * bitwise or'd together + * @param value an initial integral value + */ + public void addField(String fieldName, String type, short flags, + int value) + { + short fieldNameIndex = itsConstantPool.addUtf8(fieldName); + short typeIndex = itsConstantPool.addUtf8(type); + ClassFileField field = new ClassFileField(fieldNameIndex, typeIndex, + flags); + field.setAttributes(itsConstantPool.addUtf8("ConstantValue"), + (short)0, + (short)0, + itsConstantPool.addConstant(value)); + itsFields.add(field); + } + + /** + * Add a field to the class. + * + * @param fieldName the name of the field + * @param type the type of the field using ... + * @param flags the attributes of the field, such as ACC_PUBLIC, etc. + * bitwise or'd together + * @param value an initial long value + */ + public void addField(String fieldName, String type, short flags, + long value) + { + short fieldNameIndex = itsConstantPool.addUtf8(fieldName); + short typeIndex = itsConstantPool.addUtf8(type); + ClassFileField field = new ClassFileField(fieldNameIndex, typeIndex, + flags); + field.setAttributes(itsConstantPool.addUtf8("ConstantValue"), + (short)0, + (short)2, + itsConstantPool.addConstant(value)); + itsFields.add(field); + } + + /** + * Add a field to the class. + * + * @param fieldName the name of the field + * @param type the type of the field using ... + * @param flags the attributes of the field, such as ACC_PUBLIC, etc. + * bitwise or'd together + * @param value an initial double value + */ + public void addField(String fieldName, String type, short flags, + double value) + { + short fieldNameIndex = itsConstantPool.addUtf8(fieldName); + short typeIndex = itsConstantPool.addUtf8(type); + ClassFileField field = new ClassFileField(fieldNameIndex, typeIndex, + flags); + field.setAttributes(itsConstantPool.addUtf8("ConstantValue"), + (short)0, + (short)2, + itsConstantPool.addConstant(value)); + itsFields.add(field); + } + + /** + * Add Information about java variable to use when generating the local + * variable table. + * + * @param name variable name. + * @param type variable type as bytecode descriptor string. + * @param startPC the starting bytecode PC where this variable is live, + * or -1 if it does not have a Java register. + * @param register the Java register number of variable + * or -1 if it does not have a Java register. + */ + public void addVariableDescriptor(String name, String type, int startPC, int register) + { + int nameIndex = itsConstantPool.addUtf8(name); + int descriptorIndex = itsConstantPool.addUtf8(type); + int [] chunk = { nameIndex, descriptorIndex, startPC, register }; + if (itsVarDescriptors == null) { + itsVarDescriptors = new ObjArray(); + } + itsVarDescriptors.add(chunk); + } + + /** + * Add a method and begin adding code. + * + * This method must be called before other methods for adding code, + * exception tables, etc. can be invoked. + * + * @param methodName the name of the method + * @param type a string representing the type + * @param flags the attributes of the field, such as ACC_PUBLIC, etc. + * bitwise or'd together + */ + public void startMethod(String methodName, String type, short flags) { + short methodNameIndex = itsConstantPool.addUtf8(methodName); + short typeIndex = itsConstantPool.addUtf8(type); + itsCurrentMethod = new ClassFileMethod(methodNameIndex, typeIndex, + flags); + itsMethods.add(itsCurrentMethod); + } + + /** + * Complete generation of the method. + * + * After this method is called, no more code can be added to the + * method begun with startMethod. + * + * @param maxLocals the maximum number of local variable slots + * (a.k.a. Java registers) used by the method + */ + public void stopMethod(short maxLocals) { + if (itsCurrentMethod == null) + throw new IllegalStateException("No method to stop"); + + fixLabelGotos(); + + itsMaxLocals = maxLocals; + + int lineNumberTableLength = 0; + if (itsLineNumberTable != null) { + // 6 bytes for the attribute header + // 2 bytes for the line number count + // 4 bytes for each entry + lineNumberTableLength = 6 + 2 + (itsLineNumberTableTop * 4); + } + + int variableTableLength = 0; + if (itsVarDescriptors != null) { + // 6 bytes for the attribute header + // 2 bytes for the variable count + // 10 bytes for each entry + variableTableLength = 6 + 2 + (itsVarDescriptors.size() * 10); + } + + int attrLength = 2 + // attribute_name_index + 4 + // attribute_length + 2 + // max_stack + 2 + // max_locals + 4 + // code_length + itsCodeBufferTop + + 2 + // exception_table_length + (itsExceptionTableTop * 8) + + 2 + // attributes_count + lineNumberTableLength + + variableTableLength; + + if (attrLength > 65536) { + // See http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html, + // section 4.10, "The amount of code per non-native, non-abstract + // method is limited to 65536 bytes... + throw new ClassFileFormatException( + "generated bytecode for method exceeds 64K limit."); + } + byte[] codeAttribute = new byte[attrLength]; + int index = 0; + int codeAttrIndex = itsConstantPool.addUtf8("Code"); + index = putInt16(codeAttrIndex, codeAttribute, index); + attrLength -= 6; // discount the attribute header + index = putInt32(attrLength, codeAttribute, index); + index = putInt16(itsMaxStack, codeAttribute, index); + index = putInt16(itsMaxLocals, codeAttribute, index); + index = putInt32(itsCodeBufferTop, codeAttribute, index); + System.arraycopy(itsCodeBuffer, 0, codeAttribute, index, + itsCodeBufferTop); + index += itsCodeBufferTop; + + if (itsExceptionTableTop > 0) { + index = putInt16(itsExceptionTableTop, codeAttribute, index); + for (int i = 0; i < itsExceptionTableTop; i++) { + ExceptionTableEntry ete = itsExceptionTable[i]; + short startPC = (short)getLabelPC(ete.itsStartLabel); + short endPC = (short)getLabelPC(ete.itsEndLabel); + short handlerPC = (short)getLabelPC(ete.itsHandlerLabel); + short catchType = ete.itsCatchType; + if (startPC == -1) + throw new IllegalStateException("start label not defined"); + if (endPC == -1) + throw new IllegalStateException("end label not defined"); + if (handlerPC == -1) + throw new IllegalStateException( + "handler label not defined"); + + index = putInt16(startPC, codeAttribute, index); + index = putInt16(endPC, codeAttribute, index); + index = putInt16(handlerPC, codeAttribute, index); + index = putInt16(catchType, codeAttribute, index); + } + } + else { + // write 0 as exception table length + index = putInt16(0, codeAttribute, index); + } + + int attributeCount = 0; + if (itsLineNumberTable != null) + attributeCount++; + if (itsVarDescriptors != null) + attributeCount++; + index = putInt16(attributeCount, codeAttribute, index); + + if (itsLineNumberTable != null) { + int lineNumberTableAttrIndex + = itsConstantPool.addUtf8("LineNumberTable"); + index = putInt16(lineNumberTableAttrIndex, codeAttribute, index); + int tableAttrLength = 2 + (itsLineNumberTableTop * 4); + index = putInt32(tableAttrLength, codeAttribute, index); + index = putInt16(itsLineNumberTableTop, codeAttribute, index); + for (int i = 0; i < itsLineNumberTableTop; i++) { + index = putInt32(itsLineNumberTable[i], codeAttribute, index); + } + } + + if (itsVarDescriptors != null) { + int variableTableAttrIndex + = itsConstantPool.addUtf8("LocalVariableTable"); + index = putInt16(variableTableAttrIndex, codeAttribute, index); + int varCount = itsVarDescriptors.size(); + int tableAttrLength = 2 + (varCount * 10); + index = putInt32(tableAttrLength, codeAttribute, index); + index = putInt16(varCount, codeAttribute, index); + for (int i = 0; i < varCount; i++) { + int[] chunk = (int[])itsVarDescriptors.get(i); + int nameIndex = chunk[0]; + int descriptorIndex = chunk[1]; + int startPC = chunk[2]; + int register = chunk[3]; + int length = itsCodeBufferTop - startPC; + + index = putInt16(startPC, codeAttribute, index); + index = putInt16(length, codeAttribute, index); + index = putInt16(nameIndex, codeAttribute, index); + index = putInt16(descriptorIndex, codeAttribute, index); + index = putInt16(register, codeAttribute, index); + } + } + + itsCurrentMethod.setCodeAttribute(codeAttribute); + + itsExceptionTable = null; + itsExceptionTableTop = 0; + itsLineNumberTableTop = 0; + itsCodeBufferTop = 0; + itsCurrentMethod = null; + itsMaxStack = 0; + itsStackTop = 0; + itsLabelTableTop = 0; + itsFixupTableTop = 0; + itsVarDescriptors = null; + } + + /** + * Add the single-byte opcode to the current method. + * + * @param theOpCode the opcode of the bytecode + */ + public void add(int theOpCode) { + if (opcodeCount(theOpCode) != 0) + throw new IllegalArgumentException("Unexpected operands"); + int newStack = itsStackTop + stackChange(theOpCode); + if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack); + if (DEBUGCODE) + System.out.println("Add " + bytecodeStr(theOpCode)); + addToCodeBuffer(theOpCode); + itsStackTop = (short)newStack; + if (newStack > itsMaxStack) itsMaxStack = (short)newStack; + if (DEBUGSTACK) { + System.out.println("After "+bytecodeStr(theOpCode) + +" stack = "+itsStackTop); + } + } + + /** + * Add a single-operand opcode to the current method. + * + * @param theOpCode the opcode of the bytecode + * @param theOperand the operand of the bytecode + */ + public void add(int theOpCode, int theOperand) { + if (DEBUGCODE) { + System.out.println("Add "+bytecodeStr(theOpCode) + +", "+Integer.toHexString(theOperand)); + } + int newStack = itsStackTop + stackChange(theOpCode); + if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack); + + switch (theOpCode) { + case ByteCode.GOTO : + // fallthru... + case ByteCode.IFEQ : + case ByteCode.IFNE : + case ByteCode.IFLT : + case ByteCode.IFGE : + case ByteCode.IFGT : + case ByteCode.IFLE : + case ByteCode.IF_ICMPEQ : + case ByteCode.IF_ICMPNE : + case ByteCode.IF_ICMPLT : + case ByteCode.IF_ICMPGE : + case ByteCode.IF_ICMPGT : + case ByteCode.IF_ICMPLE : + case ByteCode.IF_ACMPEQ : + case ByteCode.IF_ACMPNE : + case ByteCode.JSR : + case ByteCode.IFNULL : + case ByteCode.IFNONNULL : { + if ((theOperand & 0x80000000) != 0x80000000) { + if ((theOperand < 0) || (theOperand > 65535)) + throw new IllegalArgumentException( + "Bad label for branch"); + } + int branchPC = itsCodeBufferTop; + addToCodeBuffer(theOpCode); + if ((theOperand & 0x80000000) != 0x80000000) { + // hard displacement + addToCodeInt16(theOperand); + } + else { // a label + int targetPC = getLabelPC(theOperand); + if (DEBUGLABELS) { + int theLabel = theOperand & 0x7FFFFFFF; + System.out.println("Fixing branch to " + + theLabel + " at " + targetPC + + " from " + branchPC); + } + if (targetPC != -1) { + int offset = targetPC - branchPC; + addToCodeInt16(offset); + } + else { + addLabelFixup(theOperand, branchPC + 1); + addToCodeInt16(0); + } + } + } + break; + + case ByteCode.BIPUSH : + if ((byte)theOperand != theOperand) + throw new IllegalArgumentException("out of range byte"); + addToCodeBuffer(theOpCode); + addToCodeBuffer((byte)theOperand); + break; + + case ByteCode.SIPUSH : + if ((short)theOperand != theOperand) + throw new IllegalArgumentException("out of range short"); + addToCodeBuffer(theOpCode); + addToCodeInt16(theOperand); + break; + + case ByteCode.NEWARRAY : + if (!(0 <= theOperand && theOperand < 256)) + throw new IllegalArgumentException("out of range index"); + addToCodeBuffer(theOpCode); + addToCodeBuffer(theOperand); + break; + + case ByteCode.GETFIELD : + case ByteCode.PUTFIELD : + if (!(0 <= theOperand && theOperand < 65536)) + throw new IllegalArgumentException("out of range field"); + addToCodeBuffer(theOpCode); + addToCodeInt16(theOperand); + break; + + case ByteCode.LDC : + case ByteCode.LDC_W : + case ByteCode.LDC2_W : + if (!(0 <= theOperand && theOperand < 65536)) + throw new IllegalArgumentException("out of range index"); + if (theOperand >= 256 + || theOpCode == ByteCode.LDC_W + || theOpCode == ByteCode.LDC2_W) + { + if (theOpCode == ByteCode.LDC) { + addToCodeBuffer(ByteCode.LDC_W); + } else { + addToCodeBuffer(theOpCode); + } + addToCodeInt16(theOperand); + } else { + addToCodeBuffer(theOpCode); + addToCodeBuffer(theOperand); + } + break; + + case ByteCode.RET : + case ByteCode.ILOAD : + case ByteCode.LLOAD : + case ByteCode.FLOAD : + case ByteCode.DLOAD : + case ByteCode.ALOAD : + case ByteCode.ISTORE : + case ByteCode.LSTORE : + case ByteCode.FSTORE : + case ByteCode.DSTORE : + case ByteCode.ASTORE : + if (!(0 <= theOperand && theOperand < 65536)) + throw new ClassFileFormatException("out of range variable"); + if (theOperand >= 256) { + addToCodeBuffer(ByteCode.WIDE); + addToCodeBuffer(theOpCode); + addToCodeInt16(theOperand); + } + else { + addToCodeBuffer(theOpCode); + addToCodeBuffer(theOperand); + } + break; + + default : + throw new IllegalArgumentException( + "Unexpected opcode for 1 operand"); + } + + itsStackTop = (short)newStack; + if (newStack > itsMaxStack) itsMaxStack = (short)newStack; + if (DEBUGSTACK) { + System.out.println("After "+bytecodeStr(theOpCode) + +" stack = "+itsStackTop); + } + } + + /** + * Generate the load constant bytecode for the given integer. + * + * @param k the constant + */ + public void addLoadConstant(int k) { + switch (k) { + case 0: add(ByteCode.ICONST_0); break; + case 1: add(ByteCode.ICONST_1); break; + case 2: add(ByteCode.ICONST_2); break; + case 3: add(ByteCode.ICONST_3); break; + case 4: add(ByteCode.ICONST_4); break; + case 5: add(ByteCode.ICONST_5); break; + default: + add(ByteCode.LDC, itsConstantPool.addConstant(k)); + break; + } + } + + /** + * Generate the load constant bytecode for the given long. + * + * @param k the constant + */ + public void addLoadConstant(long k) { + add(ByteCode.LDC2_W, itsConstantPool.addConstant(k)); + } + + /** + * Generate the load constant bytecode for the given float. + * + * @param k the constant + */ + public void addLoadConstant(float k) { + add(ByteCode.LDC, itsConstantPool.addConstant(k)); + } + + /** + * Generate the load constant bytecode for the given double. + * + * @param k the constant + */ + public void addLoadConstant(double k) { + add(ByteCode.LDC2_W, itsConstantPool.addConstant(k)); + } + + /** + * Generate the load constant bytecode for the given string. + * + * @param k the constant + */ + public void addLoadConstant(String k) { + add(ByteCode.LDC, itsConstantPool.addConstant(k)); + } + + /** + * Add the given two-operand bytecode to the current method. + * + * @param theOpCode the opcode of the bytecode + * @param theOperand1 the first operand of the bytecode + * @param theOperand2 the second operand of the bytecode + */ + public void add(int theOpCode, int theOperand1, int theOperand2) { + if (DEBUGCODE) { + System.out.println("Add "+bytecodeStr(theOpCode) + +", "+Integer.toHexString(theOperand1) + +", "+Integer.toHexString(theOperand2)); + } + int newStack = itsStackTop + stackChange(theOpCode); + if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack); + + if (theOpCode == ByteCode.IINC) { + if (!(0 <= theOperand1 && theOperand1 < 65536)) + throw new ClassFileFormatException("out of range variable"); + if (!(0 <= theOperand2 && theOperand2 < 65536)) + throw new ClassFileFormatException("out of range increment"); + + if (theOperand1 > 255 || theOperand2 < -128 || theOperand2 > 127) { + addToCodeBuffer(ByteCode.WIDE); + addToCodeBuffer(ByteCode.IINC); + addToCodeInt16(theOperand1); + addToCodeInt16(theOperand2); + } + else { + addToCodeBuffer(ByteCode.WIDE); + addToCodeBuffer(ByteCode.IINC); + addToCodeBuffer(theOperand1); + addToCodeBuffer(theOperand2); + } + } + else if (theOpCode == ByteCode.MULTIANEWARRAY) { + if (!(0 <= theOperand1 && theOperand1 < 65536)) + throw new IllegalArgumentException("out of range index"); + if (!(0 <= theOperand2 && theOperand2 < 256)) + throw new IllegalArgumentException("out of range dimensions"); + + addToCodeBuffer(ByteCode.MULTIANEWARRAY); + addToCodeInt16(theOperand1); + addToCodeBuffer(theOperand2); + } + else { + throw new IllegalArgumentException( + "Unexpected opcode for 2 operands"); + } + itsStackTop = (short)newStack; + if (newStack > itsMaxStack) itsMaxStack = (short)newStack; + if (DEBUGSTACK) { + System.out.println("After "+bytecodeStr(theOpCode) + +" stack = "+itsStackTop); + } + + } + + public void add(int theOpCode, String className) { + if (DEBUGCODE) { + System.out.println("Add "+bytecodeStr(theOpCode) + +", "+className); + } + int newStack = itsStackTop + stackChange(theOpCode); + if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack); + switch (theOpCode) { + case ByteCode.NEW : + case ByteCode.ANEWARRAY : + case ByteCode.CHECKCAST : + case ByteCode.INSTANCEOF : { + short classIndex = itsConstantPool.addClass(className); + addToCodeBuffer(theOpCode); + addToCodeInt16(classIndex); + } + break; + + default : + throw new IllegalArgumentException( + "bad opcode for class reference"); + } + itsStackTop = (short)newStack; + if (newStack > itsMaxStack) itsMaxStack = (short)newStack; + if (DEBUGSTACK) { + System.out.println("After "+bytecodeStr(theOpCode) + +" stack = "+itsStackTop); + } + } + + + public void add(int theOpCode, String className, String fieldName, + String fieldType) + { + if (DEBUGCODE) { + System.out.println("Add "+bytecodeStr(theOpCode) + +", "+className+", "+fieldName+", "+fieldType); + } + int newStack = itsStackTop + stackChange(theOpCode); + char fieldTypeChar = fieldType.charAt(0); + int fieldSize = (fieldTypeChar == 'J' || fieldTypeChar == 'D') + ? 2 : 1; + switch (theOpCode) { + case ByteCode.GETFIELD : + case ByteCode.GETSTATIC : + newStack += fieldSize; + break; + case ByteCode.PUTSTATIC : + case ByteCode.PUTFIELD : + newStack -= fieldSize; + break; + default : + throw new IllegalArgumentException( + "bad opcode for field reference"); + } + if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack); + short fieldRefIndex = itsConstantPool.addFieldRef(className, + fieldName, fieldType); + addToCodeBuffer(theOpCode); + addToCodeInt16(fieldRefIndex); + + itsStackTop = (short)newStack; + if (newStack > itsMaxStack) itsMaxStack = (short)newStack; + if (DEBUGSTACK) { + System.out.println("After "+bytecodeStr(theOpCode) + +" stack = "+itsStackTop); + } + } + + public void addInvoke(int theOpCode, String className, String methodName, + String methodType) + { + if (DEBUGCODE) { + System.out.println("Add "+bytecodeStr(theOpCode) + +", "+className+", "+methodName+", " + +methodType); + } + int parameterInfo = sizeOfParameters(methodType); + int parameterCount = parameterInfo >>> 16; + int stackDiff = (short)parameterInfo; + + int newStack = itsStackTop + stackDiff; + newStack += stackChange(theOpCode); // adjusts for 'this' + if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack); + + switch (theOpCode) { + case ByteCode.INVOKEVIRTUAL : + case ByteCode.INVOKESPECIAL : + case ByteCode.INVOKESTATIC : + case ByteCode.INVOKEINTERFACE : { + addToCodeBuffer(theOpCode); + if (theOpCode == ByteCode.INVOKEINTERFACE) { + short ifMethodRefIndex + = itsConstantPool.addInterfaceMethodRef( + className, methodName, + methodType); + addToCodeInt16(ifMethodRefIndex); + addToCodeBuffer(parameterCount + 1); + addToCodeBuffer(0); + } + else { + short methodRefIndex = itsConstantPool.addMethodRef( + className, methodName, + methodType); + addToCodeInt16(methodRefIndex); + } + } + break; + + default : + throw new IllegalArgumentException( + "bad opcode for method reference"); + } + itsStackTop = (short)newStack; + if (newStack > itsMaxStack) itsMaxStack = (short)newStack; + if (DEBUGSTACK) { + System.out.println("After "+bytecodeStr(theOpCode) + +" stack = "+itsStackTop); + } + } + + /** + * Generate code to load the given integer on stack. + * + * @param k the constant + */ + public void addPush(int k) + { + if ((byte)k == k) { + if (k == -1) { + add(ByteCode.ICONST_M1); + } else if (0 <= k && k <= 5) { + add((byte)(ByteCode.ICONST_0 + k)); + } else { + add(ByteCode.BIPUSH, (byte)k); + } + } else if ((short)k == k) { + add(ByteCode.SIPUSH, (short)k); + } else { + addLoadConstant(k); + } + } + + public void addPush(boolean k) + { + add(k ? ByteCode.ICONST_1 : ByteCode.ICONST_0); + } + + /** + * Generate code to load the given long on stack. + * + * @param k the constant + */ + public void addPush(long k) + { + int ik = (int)k; + if (ik == k) { + addPush(ik); + add(ByteCode.I2L); + } else { + addLoadConstant(k); + } + } + + /** + * Generate code to load the given double on stack. + * + * @param k the constant + */ + public void addPush(double k) + { + if (k == 0.0) { + // zero + add(ByteCode.DCONST_0); + if (1.0 / k < 0) { + // Negative zero + add(ByteCode.DNEG); + } + } else if (k == 1.0 || k == -1.0) { + add(ByteCode.DCONST_1); + if (k < 0) { + add(ByteCode.DNEG); + } + } else { + addLoadConstant(k); + } + } + + /** + * Generate the code to leave on stack the given string even if the + * string encoding exeeds the class file limit for single string constant + * + * @param k the constant + */ + public void addPush(String k) { + int length = k.length(); + int limit = itsConstantPool.getUtfEncodingLimit(k, 0, length); + if (limit == length) { + addLoadConstant(k); + return; + } + // Split string into picies fitting the UTF limit and generate code for + // StringBuffer sb = new StringBuffer(length); + // sb.append(loadConstant(piece_1)); + // ... + // sb.append(loadConstant(piece_N)); + // sb.toString(); + final String SB = "java/lang/StringBuffer"; + add(ByteCode.NEW, SB); + add(ByteCode.DUP); + addPush(length); + addInvoke(ByteCode.INVOKESPECIAL, SB, "", "(I)V"); + int cursor = 0; + for (;;) { + add(ByteCode.DUP); + String s = k.substring(cursor, limit); + addLoadConstant(s); + addInvoke(ByteCode.INVOKEVIRTUAL, SB, "append", + "(Ljava/lang/String;)Ljava/lang/StringBuffer;"); + add(ByteCode.POP); + if (limit == length) { + break; + } + cursor = limit; + limit = itsConstantPool.getUtfEncodingLimit(k, limit, length); + } + addInvoke(ByteCode.INVOKEVIRTUAL, SB, "toString", + "()Ljava/lang/String;"); + } + + /** + * Check if k fits limit on string constant size imposed by class file + * format. + * + * @param k the string constant + */ + public boolean isUnderStringSizeLimit(String k) + { + return itsConstantPool.isUnderUtfEncodingLimit(k); + } + + /** + * Store integer from stack top into the given local. + * + * @param local number of local register + */ + public void addIStore(int local) + { + xop(ByteCode.ISTORE_0, ByteCode.ISTORE, local); + } + + /** + * Store long from stack top into the given local. + * + * @param local number of local register + */ + public void addLStore(int local) + { + xop(ByteCode.LSTORE_0, ByteCode.LSTORE, local); + } + + /** + * Store float from stack top into the given local. + * + * @param local number of local register + */ + public void addFStore(int local) + { + xop(ByteCode.FSTORE_0, ByteCode.FSTORE, local); + } + + /** + * Store double from stack top into the given local. + * + * @param local number of local register + */ + public void addDStore(int local) + { + xop(ByteCode.DSTORE_0, ByteCode.DSTORE, local); + } + + /** + * Store object from stack top into the given local. + * + * @param local number of local register + */ + public void addAStore(int local) + { + xop(ByteCode.ASTORE_0, ByteCode.ASTORE, local); + } + + /** + * Load integer from the given local into stack. + * + * @param local number of local register + */ + public void addILoad(int local) + { + xop(ByteCode.ILOAD_0, ByteCode.ILOAD, local); + } + + /** + * Load long from the given local into stack. + * + * @param local number of local register + */ + public void addLLoad(int local) + { + xop(ByteCode.LLOAD_0, ByteCode.LLOAD, local); + } + + /** + * Load float from the given local into stack. + * + * @param local number of local register + */ + public void addFLoad(int local) + { + xop(ByteCode.FLOAD_0, ByteCode.FLOAD, local); + } + + /** + * Load double from the given local into stack. + * + * @param local number of local register + */ + public void addDLoad(int local) + { + xop(ByteCode.DLOAD_0, ByteCode.DLOAD, local); + } + + /** + * Load object from the given local into stack. + * + * @param local number of local register + */ + public void addALoad(int local) + { + xop(ByteCode.ALOAD_0, ByteCode.ALOAD, local); + } + + /** + * Load "this" into stack. + */ + public void addLoadThis() + { + add(ByteCode.ALOAD_0); + } + + private void xop(int shortOp, int op, int local) + { + switch (local) { + case 0: + add(shortOp); + break; + case 1: + add(shortOp + 1); + break; + case 2: + add(shortOp + 2); + break; + case 3: + add(shortOp + 3); + break; + default: + add(op, local); + } + } + + public int addTableSwitch(int low, int high) + { + if (DEBUGCODE) { + System.out.println("Add "+bytecodeStr(ByteCode.TABLESWITCH) + +" "+low+" "+high); + } + if (low > high) + throw new ClassFileFormatException("Bad bounds: "+low+' '+ high); + + int newStack = itsStackTop + stackChange(ByteCode.TABLESWITCH); + if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack); + + int entryCount = high - low + 1; + int padSize = 3 & ~itsCodeBufferTop; // == 3 - itsCodeBufferTop % 4 + + int N = addReservedCodeSpace(1 + padSize + 4 * (1 + 2 + entryCount)); + int switchStart = N; + itsCodeBuffer[N++] = (byte)ByteCode.TABLESWITCH; + while (padSize != 0) { + itsCodeBuffer[N++] = 0; + --padSize; + } + N += 4; // skip default offset + N = putInt32(low, itsCodeBuffer, N); + putInt32(high, itsCodeBuffer, N); + + itsStackTop = (short)newStack; + if (newStack > itsMaxStack) itsMaxStack = (short)newStack; + if (DEBUGSTACK) { + System.out.println("After "+bytecodeStr(ByteCode.TABLESWITCH) + +" stack = "+itsStackTop); + } + + return switchStart; + } + + public final void markTableSwitchDefault(int switchStart) + { + setTableSwitchJump(switchStart, -1, itsCodeBufferTop); + } + + public final void markTableSwitchCase(int switchStart, int caseIndex) + { + setTableSwitchJump(switchStart, caseIndex, itsCodeBufferTop); + } + + public final void markTableSwitchCase(int switchStart, int caseIndex, + int stackTop) + { + if (!(0 <= stackTop && stackTop <= itsMaxStack)) + throw new IllegalArgumentException("Bad stack index: "+stackTop); + itsStackTop = (short)stackTop; + setTableSwitchJump(switchStart, caseIndex, itsCodeBufferTop); + } + + public void setTableSwitchJump(int switchStart, int caseIndex, + int jumpTarget) + { + if (!(0 <= jumpTarget && jumpTarget <= itsCodeBufferTop)) + throw new IllegalArgumentException("Bad jump target: "+jumpTarget); + if (!(caseIndex >= -1)) + throw new IllegalArgumentException("Bad case index: "+caseIndex); + + int padSize = 3 & ~switchStart; // == 3 - switchStart % 4 + int caseOffset; + if (caseIndex < 0) { + // default label + caseOffset = switchStart + 1 + padSize; + } else { + caseOffset = switchStart + 1 + padSize + 4 * (3 + caseIndex); + } + if (!(0 <= switchStart + && switchStart <= itsCodeBufferTop - 4 * 4 - padSize - 1)) + { + throw new IllegalArgumentException( + switchStart+" is outside a possible range of tableswitch" + +" in already generated code"); + } + if ((0xFF & itsCodeBuffer[switchStart]) != ByteCode.TABLESWITCH) { + throw new IllegalArgumentException( + switchStart+" is not offset of tableswitch statement"); + } + if (!(0 <= caseOffset && caseOffset + 4 <= itsCodeBufferTop)) { + // caseIndex >= -1 does not guarantee that caseOffset >= 0 due + // to a possible overflow. + throw new ClassFileFormatException( + "Too big case index: "+caseIndex); + } + // ALERT: perhaps check against case bounds? + putInt32(jumpTarget - switchStart, itsCodeBuffer, caseOffset); + } + + public int acquireLabel() + { + int top = itsLabelTableTop; + if (itsLabelTable == null || top == itsLabelTable.length) { + if (itsLabelTable == null) { + itsLabelTable = new int[MIN_LABEL_TABLE_SIZE]; + }else { + int[] tmp = new int[itsLabelTable.length * 2]; + System.arraycopy(itsLabelTable, 0, tmp, 0, top); + itsLabelTable = tmp; + } + } + itsLabelTableTop = top + 1; + itsLabelTable[top] = -1; + return top | 0x80000000; + } + + public void markLabel(int label) + { + if (!(label < 0)) + throw new IllegalArgumentException("Bad label, no biscuit"); + + label &= 0x7FFFFFFF; + if (label > itsLabelTableTop) + throw new IllegalArgumentException("Bad label"); + + if (itsLabelTable[label] != -1) { + throw new IllegalStateException("Can only mark label once"); + } + + itsLabelTable[label] = itsCodeBufferTop; + } + + public void markLabel(int label, short stackTop) + { + markLabel(label); + itsStackTop = stackTop; + } + + public void markHandler(int theLabel) { + itsStackTop = 1; + markLabel(theLabel); + } + + private int getLabelPC(int label) + { + if (!(label < 0)) + throw new IllegalArgumentException("Bad label, no biscuit"); + label &= 0x7FFFFFFF; + if (!(label < itsLabelTableTop)) + throw new IllegalArgumentException("Bad label"); + return itsLabelTable[label]; + } + + private void addLabelFixup(int label, int fixupSite) + { + if (!(label < 0)) + throw new IllegalArgumentException("Bad label, no biscuit"); + label &= 0x7FFFFFFF; + if (!(label < itsLabelTableTop)) + throw new IllegalArgumentException("Bad label"); + int top = itsFixupTableTop; + if (itsFixupTable == null || top == itsFixupTable.length) { + if (itsFixupTable == null) { + itsFixupTable = new long[MIN_FIXUP_TABLE_SIZE]; + }else { + long[] tmp = new long[itsFixupTable.length * 2]; + System.arraycopy(itsFixupTable, 0, tmp, 0, top); + itsFixupTable = tmp; + } + } + itsFixupTableTop = top + 1; + itsFixupTable[top] = ((long)label << 32) | fixupSite; + } + + private void fixLabelGotos() + { + byte[] codeBuffer = itsCodeBuffer; + for (int i = 0; i < itsFixupTableTop; i++) { + long fixup = itsFixupTable[i]; + int label = (int)(fixup >> 32); + int fixupSite = (int)fixup; + int pc = itsLabelTable[label]; + if (pc == -1) { + // Unlocated label + throw new RuntimeException(); + } + // -1 to get delta from instruction start + int offset = pc - (fixupSite - 1); + if ((short)offset != offset) { + throw new ClassFileFormatException + ("Program too complex: too big jump offset"); + } + codeBuffer[fixupSite] = (byte)(offset >> 8); + codeBuffer[fixupSite + 1] = (byte)offset; + } + itsFixupTableTop = 0; + } + + /** + * Get the current offset into the code of the current method. + * + * @return an integer representing the offset + */ + public int getCurrentCodeOffset() { + return itsCodeBufferTop; + } + + public short getStackTop() { + return itsStackTop; + } + + public void setStackTop(short n) { + itsStackTop = n; + } + + public void adjustStackTop(int delta) { + int newStack = itsStackTop + delta; + if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack); + itsStackTop = (short)newStack; + if (newStack > itsMaxStack) itsMaxStack = (short)newStack; + if (DEBUGSTACK) { + System.out.println("After "+"adjustStackTop("+delta+")" + +" stack = "+itsStackTop); + } + } + + private void addToCodeBuffer(int b) + { + int N = addReservedCodeSpace(1); + itsCodeBuffer[N] = (byte)b; + } + + private void addToCodeInt16(int value) + { + int N = addReservedCodeSpace(2); + putInt16(value, itsCodeBuffer, N); + } + + private int addReservedCodeSpace(int size) + { + if (itsCurrentMethod == null) + throw new IllegalArgumentException("No method to add to"); + int oldTop = itsCodeBufferTop; + int newTop = oldTop + size; + if (newTop > itsCodeBuffer.length) { + int newSize = itsCodeBuffer.length * 2; + if (newTop > newSize) { newSize = newTop; } + byte[] tmp = new byte[newSize]; + System.arraycopy(itsCodeBuffer, 0, tmp, 0, oldTop); + itsCodeBuffer = tmp; + } + itsCodeBufferTop = newTop; + return oldTop; + } + + public void addExceptionHandler(int startLabel, int endLabel, + int handlerLabel, String catchClassName) + { + if ((startLabel & 0x80000000) != 0x80000000) + throw new IllegalArgumentException("Bad startLabel"); + if ((endLabel & 0x80000000) != 0x80000000) + throw new IllegalArgumentException("Bad endLabel"); + if ((handlerLabel & 0x80000000) != 0x80000000) + throw new IllegalArgumentException("Bad handlerLabel"); + + /* + * If catchClassName is null, use 0 for the catch_type_index; which + * means catch everything. (Even when the verifier has let you throw + * something other than a Throwable.) + */ + short catch_type_index = (catchClassName == null) + ? 0 + : itsConstantPool.addClass(catchClassName); + ExceptionTableEntry newEntry = new ExceptionTableEntry( + startLabel, + endLabel, + handlerLabel, + catch_type_index); + int N = itsExceptionTableTop; + if (N == 0) { + itsExceptionTable = new ExceptionTableEntry[ExceptionTableSize]; + } else if (N == itsExceptionTable.length) { + ExceptionTableEntry[] tmp = new ExceptionTableEntry[N * 2]; + System.arraycopy(itsExceptionTable, 0, tmp, 0, N); + itsExceptionTable = tmp; + } + itsExceptionTable[N] = newEntry; + itsExceptionTableTop = N + 1; + + } + + public void addLineNumberEntry(short lineNumber) { + if (itsCurrentMethod == null) + throw new IllegalArgumentException("No method to stop"); + int N = itsLineNumberTableTop; + if (N == 0) { + itsLineNumberTable = new int[LineNumberTableSize]; + } else if (N == itsLineNumberTable.length) { + int[] tmp = new int[N * 2]; + System.arraycopy(itsLineNumberTable, 0, tmp, 0, N); + itsLineNumberTable = tmp; + } + itsLineNumberTable[N] = (itsCodeBufferTop << 16) + lineNumber; + itsLineNumberTableTop = N + 1; + } + + /** + * Write the class file to the OutputStream. + * + * @param oStream the stream to write to + * @throws IOException if writing to the stream produces an exception + */ + public void write(OutputStream oStream) + throws IOException + { + byte[] array = toByteArray(); + oStream.write(array); + } + + private int getWriteSize() + { + int size = 0; + + if (itsSourceFileNameIndex != 0) { + itsConstantPool.addUtf8("SourceFile"); + } + + size += 8; //writeLong(FileHeaderConstant); + size += itsConstantPool.getWriteSize(); + size += 2; //writeShort(itsFlags); + size += 2; //writeShort(itsThisClassIndex); + size += 2; //writeShort(itsSuperClassIndex); + size += 2; //writeShort(itsInterfaces.size()); + size += 2 * itsInterfaces.size(); + + size += 2; //writeShort(itsFields.size()); + for (int i = 0; i < itsFields.size(); i++) { + size += ((ClassFileField)(itsFields.get(i))).getWriteSize(); + } + + size += 2; //writeShort(itsMethods.size()); + for (int i = 0; i < itsMethods.size(); i++) { + size += ((ClassFileMethod)(itsMethods.get(i))).getWriteSize(); + } + + if (itsSourceFileNameIndex != 0) { + size += 2; //writeShort(1); attributes count + size += 2; //writeShort(sourceFileAttributeNameIndex); + size += 4; //writeInt(2); + size += 2; //writeShort(itsSourceFileNameIndex); + }else { + size += 2; //out.writeShort(0); no attributes + } + + return size; + } + + /** + * Get the class file as array of bytesto the OutputStream. + */ + public byte[] toByteArray() + { + int dataSize = getWriteSize(); + byte[] data = new byte[dataSize]; + int offset = 0; + + short sourceFileAttributeNameIndex = 0; + if (itsSourceFileNameIndex != 0) { + sourceFileAttributeNameIndex = itsConstantPool.addUtf8( + "SourceFile"); + } + + offset = putInt64(FileHeaderConstant, data, offset); + offset = itsConstantPool.write(data, offset); + offset = putInt16(itsFlags, data, offset); + offset = putInt16(itsThisClassIndex, data, offset); + offset = putInt16(itsSuperClassIndex, data, offset); + offset = putInt16(itsInterfaces.size(), data, offset); + for (int i = 0; i < itsInterfaces.size(); i++) { + int interfaceIndex = ((Short)(itsInterfaces.get(i))).shortValue(); + offset = putInt16(interfaceIndex, data, offset); + } + offset = putInt16(itsFields.size(), data, offset); + for (int i = 0; i < itsFields.size(); i++) { + ClassFileField field = (ClassFileField)itsFields.get(i); + offset = field.write(data, offset); + } + offset = putInt16(itsMethods.size(), data, offset); + for (int i = 0; i < itsMethods.size(); i++) { + ClassFileMethod method = (ClassFileMethod)itsMethods.get(i); + offset = method.write(data, offset); + } + if (itsSourceFileNameIndex != 0) { + offset = putInt16(1, data, offset); // attributes count + offset = putInt16(sourceFileAttributeNameIndex, data, offset); + offset = putInt32(2, data, offset); + offset = putInt16(itsSourceFileNameIndex, data, offset); + } else { + offset = putInt16(0, data, offset); // no attributes + } + + if (offset != dataSize) { + // Check getWriteSize is consistent with write! + throw new RuntimeException(); + } + + return data; + } + + static int putInt64(long value, byte[] array, int offset) + { + offset = putInt32((int)(value >>> 32), array, offset); + return putInt32((int)value, array, offset); + } + + private static void badStack(int value) + { + String s; + if (value < 0) { s = "Stack underflow: "+value; } + else { s = "Too big stack: "+value; } + throw new IllegalStateException(s); + } + + /* + Really weird. Returns an int with # parameters in hi 16 bits, and + stack difference removal of parameters from stack and pushing the + result (it does not take into account removal of this in case of + non-static methods). + If Java really supported references we wouldn't have to be this + perverted. + */ + private static int sizeOfParameters(String pString) + { + int length = pString.length(); + int rightParenthesis = pString.lastIndexOf(')'); + if (3 <= length /* minimal signature takes at least 3 chars: ()V */ + && pString.charAt(0) == '(' + && 1 <= rightParenthesis && rightParenthesis + 1 < length) + { + boolean ok = true; + int index = 1; + int stackDiff = 0; + int count = 0; + stringLoop: + while (index != rightParenthesis) { + switch (pString.charAt(index)) { + default: + ok = false; + break stringLoop; + case 'J' : + case 'D' : + --stackDiff; + // fall thru + case 'B' : + case 'S' : + case 'C' : + case 'I' : + case 'Z' : + case 'F' : + --stackDiff; + ++count; + ++index; + continue; + case '[' : + ++index; + int c = pString.charAt(index); + while (c == '[') { + ++index; + c = pString.charAt(index); + } + switch (c) { + default: + ok = false; + break stringLoop; + case 'J' : + case 'D' : + case 'B' : + case 'S' : + case 'C' : + case 'I' : + case 'Z' : + case 'F' : + --stackDiff; + ++count; + ++index; + continue; + case 'L': + // fall thru + } + // fall thru + case 'L' : { + --stackDiff; + ++count; + ++index; + int semicolon = pString.indexOf(';', index); + if (!(index + 1 <= semicolon + && semicolon < rightParenthesis)) + { + ok = false; + break stringLoop; + } + index = semicolon + 1; + continue; + } + } + } + if (ok) { + switch (pString.charAt(rightParenthesis + 1)) { + default: + ok = false; + break; + case 'J' : + case 'D' : + ++stackDiff; + // fall thru + case 'B' : + case 'S' : + case 'C' : + case 'I' : + case 'Z' : + case 'F' : + case 'L' : + case '[' : + ++stackDiff; + // fall thru + case 'V' : + break; + } + if (ok) { + return ((count << 16) | (0xFFFF & stackDiff)); + } + } + } + throw new IllegalArgumentException( + "Bad parameter signature: "+pString); + } + + static int putInt16(int value, byte[] array, int offset) + { + array[offset + 0] = (byte)(value >>> 8); + array[offset + 1] = (byte)value; + return offset + 2; + } + + static int putInt32(int value, byte[] array, int offset) + { + array[offset + 0] = (byte)(value >>> 24); + array[offset + 1] = (byte)(value >>> 16); + array[offset + 2] = (byte)(value >>> 8); + array[offset + 3] = (byte)value; + return offset + 4; + } + + /** + * Number of operands accompanying the opcode. + */ + static int opcodeCount(int opcode) + { + switch (opcode) { + case ByteCode.AALOAD: + case ByteCode.AASTORE: + case ByteCode.ACONST_NULL: + case ByteCode.ALOAD_0: + case ByteCode.ALOAD_1: + case ByteCode.ALOAD_2: + case ByteCode.ALOAD_3: + case ByteCode.ARETURN: + case ByteCode.ARRAYLENGTH: + case ByteCode.ASTORE_0: + case ByteCode.ASTORE_1: + case ByteCode.ASTORE_2: + case ByteCode.ASTORE_3: + case ByteCode.ATHROW: + case ByteCode.BALOAD: + case ByteCode.BASTORE: + case ByteCode.BREAKPOINT: + case ByteCode.CALOAD: + case ByteCode.CASTORE: + case ByteCode.D2F: + case ByteCode.D2I: + case ByteCode.D2L: + case ByteCode.DADD: + case ByteCode.DALOAD: + case ByteCode.DASTORE: + case ByteCode.DCMPG: + case ByteCode.DCMPL: + case ByteCode.DCONST_0: + case ByteCode.DCONST_1: + case ByteCode.DDIV: + case ByteCode.DLOAD_0: + case ByteCode.DLOAD_1: + case ByteCode.DLOAD_2: + case ByteCode.DLOAD_3: + case ByteCode.DMUL: + case ByteCode.DNEG: + case ByteCode.DREM: + case ByteCode.DRETURN: + case ByteCode.DSTORE_0: + case ByteCode.DSTORE_1: + case ByteCode.DSTORE_2: + case ByteCode.DSTORE_3: + case ByteCode.DSUB: + case ByteCode.DUP: + case ByteCode.DUP2: + case ByteCode.DUP2_X1: + case ByteCode.DUP2_X2: + case ByteCode.DUP_X1: + case ByteCode.DUP_X2: + case ByteCode.F2D: + case ByteCode.F2I: + case ByteCode.F2L: + case ByteCode.FADD: + case ByteCode.FALOAD: + case ByteCode.FASTORE: + case ByteCode.FCMPG: + case ByteCode.FCMPL: + case ByteCode.FCONST_0: + case ByteCode.FCONST_1: + case ByteCode.FCONST_2: + case ByteCode.FDIV: + case ByteCode.FLOAD_0: + case ByteCode.FLOAD_1: + case ByteCode.FLOAD_2: + case ByteCode.FLOAD_3: + case ByteCode.FMUL: + case ByteCode.FNEG: + case ByteCode.FREM: + case ByteCode.FRETURN: + case ByteCode.FSTORE_0: + case ByteCode.FSTORE_1: + case ByteCode.FSTORE_2: + case ByteCode.FSTORE_3: + case ByteCode.FSUB: + case ByteCode.I2B: + case ByteCode.I2C: + case ByteCode.I2D: + case ByteCode.I2F: + case ByteCode.I2L: + case ByteCode.I2S: + case ByteCode.IADD: + case ByteCode.IALOAD: + case ByteCode.IAND: + case ByteCode.IASTORE: + case ByteCode.ICONST_0: + case ByteCode.ICONST_1: + case ByteCode.ICONST_2: + case ByteCode.ICONST_3: + case ByteCode.ICONST_4: + case ByteCode.ICONST_5: + case ByteCode.ICONST_M1: + case ByteCode.IDIV: + case ByteCode.ILOAD_0: + case ByteCode.ILOAD_1: + case ByteCode.ILOAD_2: + case ByteCode.ILOAD_3: + case ByteCode.IMPDEP1: + case ByteCode.IMPDEP2: + case ByteCode.IMUL: + case ByteCode.INEG: + case ByteCode.IOR: + case ByteCode.IREM: + case ByteCode.IRETURN: + case ByteCode.ISHL: + case ByteCode.ISHR: + case ByteCode.ISTORE_0: + case ByteCode.ISTORE_1: + case ByteCode.ISTORE_2: + case ByteCode.ISTORE_3: + case ByteCode.ISUB: + case ByteCode.IUSHR: + case ByteCode.IXOR: + case ByteCode.L2D: + case ByteCode.L2F: + case ByteCode.L2I: + case ByteCode.LADD: + case ByteCode.LALOAD: + case ByteCode.LAND: + case ByteCode.LASTORE: + case ByteCode.LCMP: + case ByteCode.LCONST_0: + case ByteCode.LCONST_1: + case ByteCode.LDIV: + case ByteCode.LLOAD_0: + case ByteCode.LLOAD_1: + case ByteCode.LLOAD_2: + case ByteCode.LLOAD_3: + case ByteCode.LMUL: + case ByteCode.LNEG: + case ByteCode.LOR: + case ByteCode.LREM: + case ByteCode.LRETURN: + case ByteCode.LSHL: + case ByteCode.LSHR: + case ByteCode.LSTORE_0: + case ByteCode.LSTORE_1: + case ByteCode.LSTORE_2: + case ByteCode.LSTORE_3: + case ByteCode.LSUB: + case ByteCode.LUSHR: + case ByteCode.LXOR: + case ByteCode.MONITORENTER: + case ByteCode.MONITOREXIT: + case ByteCode.NOP: + case ByteCode.POP: + case ByteCode.POP2: + case ByteCode.RETURN: + case ByteCode.SALOAD: + case ByteCode.SASTORE: + case ByteCode.SWAP: + case ByteCode.WIDE: + return 0; + case ByteCode.ALOAD: + case ByteCode.ANEWARRAY: + case ByteCode.ASTORE: + case ByteCode.BIPUSH: + case ByteCode.CHECKCAST: + case ByteCode.DLOAD: + case ByteCode.DSTORE: + case ByteCode.FLOAD: + case ByteCode.FSTORE: + case ByteCode.GETFIELD: + case ByteCode.GETSTATIC: + case ByteCode.GOTO: + case ByteCode.GOTO_W: + case ByteCode.IFEQ: + case ByteCode.IFGE: + case ByteCode.IFGT: + case ByteCode.IFLE: + case ByteCode.IFLT: + case ByteCode.IFNE: + case ByteCode.IFNONNULL: + case ByteCode.IFNULL: + case ByteCode.IF_ACMPEQ: + case ByteCode.IF_ACMPNE: + case ByteCode.IF_ICMPEQ: + case ByteCode.IF_ICMPGE: + case ByteCode.IF_ICMPGT: + case ByteCode.IF_ICMPLE: + case ByteCode.IF_ICMPLT: + case ByteCode.IF_ICMPNE: + case ByteCode.ILOAD: + case ByteCode.INSTANCEOF: + case ByteCode.INVOKEINTERFACE: + case ByteCode.INVOKESPECIAL: + case ByteCode.INVOKESTATIC: + case ByteCode.INVOKEVIRTUAL: + case ByteCode.ISTORE: + case ByteCode.JSR: + case ByteCode.JSR_W: + case ByteCode.LDC: + case ByteCode.LDC2_W: + case ByteCode.LDC_W: + case ByteCode.LLOAD: + case ByteCode.LSTORE: + case ByteCode.NEW: + case ByteCode.NEWARRAY: + case ByteCode.PUTFIELD: + case ByteCode.PUTSTATIC: + case ByteCode.RET: + case ByteCode.SIPUSH: + return 1; + + case ByteCode.IINC: + case ByteCode.MULTIANEWARRAY: + return 2; + + case ByteCode.LOOKUPSWITCH: + case ByteCode.TABLESWITCH: + return -1; + } + throw new IllegalArgumentException("Bad opcode: "+opcode); + } + + /** + * The effect on the operand stack of a given opcode. + */ + static int stackChange(int opcode) + { + // For INVOKE... accounts only for popping this (unless static), + // ignoring parameters and return type + switch (opcode) { + case ByteCode.DASTORE: + case ByteCode.LASTORE: + return -4; + + case ByteCode.AASTORE: + case ByteCode.BASTORE: + case ByteCode.CASTORE: + case ByteCode.DCMPG: + case ByteCode.DCMPL: + case ByteCode.FASTORE: + case ByteCode.IASTORE: + case ByteCode.LCMP: + case ByteCode.SASTORE: + return -3; + + case ByteCode.DADD: + case ByteCode.DDIV: + case ByteCode.DMUL: + case ByteCode.DREM: + case ByteCode.DRETURN: + case ByteCode.DSTORE: + case ByteCode.DSTORE_0: + case ByteCode.DSTORE_1: + case ByteCode.DSTORE_2: + case ByteCode.DSTORE_3: + case ByteCode.DSUB: + case ByteCode.IF_ACMPEQ: + case ByteCode.IF_ACMPNE: + case ByteCode.IF_ICMPEQ: + case ByteCode.IF_ICMPGE: + case ByteCode.IF_ICMPGT: + case ByteCode.IF_ICMPLE: + case ByteCode.IF_ICMPLT: + case ByteCode.IF_ICMPNE: + case ByteCode.LADD: + case ByteCode.LAND: + case ByteCode.LDIV: + case ByteCode.LMUL: + case ByteCode.LOR: + case ByteCode.LREM: + case ByteCode.LRETURN: + case ByteCode.LSTORE: + case ByteCode.LSTORE_0: + case ByteCode.LSTORE_1: + case ByteCode.LSTORE_2: + case ByteCode.LSTORE_3: + case ByteCode.LSUB: + case ByteCode.LXOR: + case ByteCode.POP2: + return -2; + + case ByteCode.AALOAD: + case ByteCode.ARETURN: + case ByteCode.ASTORE: + case ByteCode.ASTORE_0: + case ByteCode.ASTORE_1: + case ByteCode.ASTORE_2: + case ByteCode.ASTORE_3: + case ByteCode.ATHROW: + case ByteCode.BALOAD: + case ByteCode.CALOAD: + case ByteCode.D2F: + case ByteCode.D2I: + case ByteCode.FADD: + case ByteCode.FALOAD: + case ByteCode.FCMPG: + case ByteCode.FCMPL: + case ByteCode.FDIV: + case ByteCode.FMUL: + case ByteCode.FREM: + case ByteCode.FRETURN: + case ByteCode.FSTORE: + case ByteCode.FSTORE_0: + case ByteCode.FSTORE_1: + case ByteCode.FSTORE_2: + case ByteCode.FSTORE_3: + case ByteCode.FSUB: + case ByteCode.GETFIELD: + case ByteCode.IADD: + case ByteCode.IALOAD: + case ByteCode.IAND: + case ByteCode.IDIV: + case ByteCode.IFEQ: + case ByteCode.IFGE: + case ByteCode.IFGT: + case ByteCode.IFLE: + case ByteCode.IFLT: + case ByteCode.IFNE: + case ByteCode.IFNONNULL: + case ByteCode.IFNULL: + case ByteCode.IMUL: + case ByteCode.INVOKEINTERFACE: // + case ByteCode.INVOKESPECIAL: // but needs to account for + case ByteCode.INVOKEVIRTUAL: // pops 'this' (unless static) + case ByteCode.IOR: + case ByteCode.IREM: + case ByteCode.IRETURN: + case ByteCode.ISHL: + case ByteCode.ISHR: + case ByteCode.ISTORE: + case ByteCode.ISTORE_0: + case ByteCode.ISTORE_1: + case ByteCode.ISTORE_2: + case ByteCode.ISTORE_3: + case ByteCode.ISUB: + case ByteCode.IUSHR: + case ByteCode.IXOR: + case ByteCode.L2F: + case ByteCode.L2I: + case ByteCode.LOOKUPSWITCH: + case ByteCode.LSHL: + case ByteCode.LSHR: + case ByteCode.LUSHR: + case ByteCode.MONITORENTER: + case ByteCode.MONITOREXIT: + case ByteCode.POP: + case ByteCode.PUTFIELD: + case ByteCode.SALOAD: + case ByteCode.TABLESWITCH: + return -1; + + case ByteCode.ANEWARRAY: + case ByteCode.ARRAYLENGTH: + case ByteCode.BREAKPOINT: + case ByteCode.CHECKCAST: + case ByteCode.D2L: + case ByteCode.DALOAD: + case ByteCode.DNEG: + case ByteCode.F2I: + case ByteCode.FNEG: + case ByteCode.GETSTATIC: + case ByteCode.GOTO: + case ByteCode.GOTO_W: + case ByteCode.I2B: + case ByteCode.I2C: + case ByteCode.I2F: + case ByteCode.I2S: + case ByteCode.IINC: + case ByteCode.IMPDEP1: + case ByteCode.IMPDEP2: + case ByteCode.INEG: + case ByteCode.INSTANCEOF: + case ByteCode.INVOKESTATIC: + case ByteCode.L2D: + case ByteCode.LALOAD: + case ByteCode.LNEG: + case ByteCode.NEWARRAY: + case ByteCode.NOP: + case ByteCode.PUTSTATIC: + case ByteCode.RET: + case ByteCode.RETURN: + case ByteCode.SWAP: + case ByteCode.WIDE: + return 0; + + case ByteCode.ACONST_NULL: + case ByteCode.ALOAD: + case ByteCode.ALOAD_0: + case ByteCode.ALOAD_1: + case ByteCode.ALOAD_2: + case ByteCode.ALOAD_3: + case ByteCode.BIPUSH: + case ByteCode.DUP: + case ByteCode.DUP_X1: + case ByteCode.DUP_X2: + case ByteCode.F2D: + case ByteCode.F2L: + case ByteCode.FCONST_0: + case ByteCode.FCONST_1: + case ByteCode.FCONST_2: + case ByteCode.FLOAD: + case ByteCode.FLOAD_0: + case ByteCode.FLOAD_1: + case ByteCode.FLOAD_2: + case ByteCode.FLOAD_3: + case ByteCode.I2D: + case ByteCode.I2L: + case ByteCode.ICONST_0: + case ByteCode.ICONST_1: + case ByteCode.ICONST_2: + case ByteCode.ICONST_3: + case ByteCode.ICONST_4: + case ByteCode.ICONST_5: + case ByteCode.ICONST_M1: + case ByteCode.ILOAD: + case ByteCode.ILOAD_0: + case ByteCode.ILOAD_1: + case ByteCode.ILOAD_2: + case ByteCode.ILOAD_3: + case ByteCode.JSR: + case ByteCode.JSR_W: + case ByteCode.LDC: + case ByteCode.LDC_W: + case ByteCode.MULTIANEWARRAY: + case ByteCode.NEW: + case ByteCode.SIPUSH: + return 1; + + case ByteCode.DCONST_0: + case ByteCode.DCONST_1: + case ByteCode.DLOAD: + case ByteCode.DLOAD_0: + case ByteCode.DLOAD_1: + case ByteCode.DLOAD_2: + case ByteCode.DLOAD_3: + case ByteCode.DUP2: + case ByteCode.DUP2_X1: + case ByteCode.DUP2_X2: + case ByteCode.LCONST_0: + case ByteCode.LCONST_1: + case ByteCode.LDC2_W: + case ByteCode.LLOAD: + case ByteCode.LLOAD_0: + case ByteCode.LLOAD_1: + case ByteCode.LLOAD_2: + case ByteCode.LLOAD_3: + return 2; + } + throw new IllegalArgumentException("Bad opcode: "+opcode); + } + + /* + * Number of bytes of operands generated after the opcode. + * Not in use currently. + */ +/* + int extra(int opcode) + { + switch (opcode) { + case ByteCode.AALOAD: + case ByteCode.AASTORE: + case ByteCode.ACONST_NULL: + case ByteCode.ALOAD_0: + case ByteCode.ALOAD_1: + case ByteCode.ALOAD_2: + case ByteCode.ALOAD_3: + case ByteCode.ARETURN: + case ByteCode.ARRAYLENGTH: + case ByteCode.ASTORE_0: + case ByteCode.ASTORE_1: + case ByteCode.ASTORE_2: + case ByteCode.ASTORE_3: + case ByteCode.ATHROW: + case ByteCode.BALOAD: + case ByteCode.BASTORE: + case ByteCode.BREAKPOINT: + case ByteCode.CALOAD: + case ByteCode.CASTORE: + case ByteCode.D2F: + case ByteCode.D2I: + case ByteCode.D2L: + case ByteCode.DADD: + case ByteCode.DALOAD: + case ByteCode.DASTORE: + case ByteCode.DCMPG: + case ByteCode.DCMPL: + case ByteCode.DCONST_0: + case ByteCode.DCONST_1: + case ByteCode.DDIV: + case ByteCode.DLOAD_0: + case ByteCode.DLOAD_1: + case ByteCode.DLOAD_2: + case ByteCode.DLOAD_3: + case ByteCode.DMUL: + case ByteCode.DNEG: + case ByteCode.DREM: + case ByteCode.DRETURN: + case ByteCode.DSTORE_0: + case ByteCode.DSTORE_1: + case ByteCode.DSTORE_2: + case ByteCode.DSTORE_3: + case ByteCode.DSUB: + case ByteCode.DUP2: + case ByteCode.DUP2_X1: + case ByteCode.DUP2_X2: + case ByteCode.DUP: + case ByteCode.DUP_X1: + case ByteCode.DUP_X2: + case ByteCode.F2D: + case ByteCode.F2I: + case ByteCode.F2L: + case ByteCode.FADD: + case ByteCode.FALOAD: + case ByteCode.FASTORE: + case ByteCode.FCMPG: + case ByteCode.FCMPL: + case ByteCode.FCONST_0: + case ByteCode.FCONST_1: + case ByteCode.FCONST_2: + case ByteCode.FDIV: + case ByteCode.FLOAD_0: + case ByteCode.FLOAD_1: + case ByteCode.FLOAD_2: + case ByteCode.FLOAD_3: + case ByteCode.FMUL: + case ByteCode.FNEG: + case ByteCode.FREM: + case ByteCode.FRETURN: + case ByteCode.FSTORE_0: + case ByteCode.FSTORE_1: + case ByteCode.FSTORE_2: + case ByteCode.FSTORE_3: + case ByteCode.FSUB: + case ByteCode.I2B: + case ByteCode.I2C: + case ByteCode.I2D: + case ByteCode.I2F: + case ByteCode.I2L: + case ByteCode.I2S: + case ByteCode.IADD: + case ByteCode.IALOAD: + case ByteCode.IAND: + case ByteCode.IASTORE: + case ByteCode.ICONST_0: + case ByteCode.ICONST_1: + case ByteCode.ICONST_2: + case ByteCode.ICONST_3: + case ByteCode.ICONST_4: + case ByteCode.ICONST_5: + case ByteCode.ICONST_M1: + case ByteCode.IDIV: + case ByteCode.ILOAD_0: + case ByteCode.ILOAD_1: + case ByteCode.ILOAD_2: + case ByteCode.ILOAD_3: + case ByteCode.IMPDEP1: + case ByteCode.IMPDEP2: + case ByteCode.IMUL: + case ByteCode.INEG: + case ByteCode.IOR: + case ByteCode.IREM: + case ByteCode.IRETURN: + case ByteCode.ISHL: + case ByteCode.ISHR: + case ByteCode.ISTORE_0: + case ByteCode.ISTORE_1: + case ByteCode.ISTORE_2: + case ByteCode.ISTORE_3: + case ByteCode.ISUB: + case ByteCode.IUSHR: + case ByteCode.IXOR: + case ByteCode.L2D: + case ByteCode.L2F: + case ByteCode.L2I: + case ByteCode.LADD: + case ByteCode.LALOAD: + case ByteCode.LAND: + case ByteCode.LASTORE: + case ByteCode.LCMP: + case ByteCode.LCONST_0: + case ByteCode.LCONST_1: + case ByteCode.LDIV: + case ByteCode.LLOAD_0: + case ByteCode.LLOAD_1: + case ByteCode.LLOAD_2: + case ByteCode.LLOAD_3: + case ByteCode.LMUL: + case ByteCode.LNEG: + case ByteCode.LOR: + case ByteCode.LREM: + case ByteCode.LRETURN: + case ByteCode.LSHL: + case ByteCode.LSHR: + case ByteCode.LSTORE_0: + case ByteCode.LSTORE_1: + case ByteCode.LSTORE_2: + case ByteCode.LSTORE_3: + case ByteCode.LSUB: + case ByteCode.LUSHR: + case ByteCode.LXOR: + case ByteCode.MONITORENTER: + case ByteCode.MONITOREXIT: + case ByteCode.NOP: + case ByteCode.POP2: + case ByteCode.POP: + case ByteCode.RETURN: + case ByteCode.SALOAD: + case ByteCode.SASTORE: + case ByteCode.SWAP: + case ByteCode.WIDE: + return 0; + + case ByteCode.ALOAD: + case ByteCode.ASTORE: + case ByteCode.BIPUSH: + case ByteCode.DLOAD: + case ByteCode.DSTORE: + case ByteCode.FLOAD: + case ByteCode.FSTORE: + case ByteCode.ILOAD: + case ByteCode.ISTORE: + case ByteCode.LDC: + case ByteCode.LLOAD: + case ByteCode.LSTORE: + case ByteCode.NEWARRAY: + case ByteCode.RET: + return 1; + + case ByteCode.ANEWARRAY: + case ByteCode.CHECKCAST: + case ByteCode.GETFIELD: + case ByteCode.GETSTATIC: + case ByteCode.GOTO: + case ByteCode.IFEQ: + case ByteCode.IFGE: + case ByteCode.IFGT: + case ByteCode.IFLE: + case ByteCode.IFLT: + case ByteCode.IFNE: + case ByteCode.IFNONNULL: + case ByteCode.IFNULL: + case ByteCode.IF_ACMPEQ: + case ByteCode.IF_ACMPNE: + case ByteCode.IF_ICMPEQ: + case ByteCode.IF_ICMPGE: + case ByteCode.IF_ICMPGT: + case ByteCode.IF_ICMPLE: + case ByteCode.IF_ICMPLT: + case ByteCode.IF_ICMPNE: + case ByteCode.IINC: + case ByteCode.INSTANCEOF: + case ByteCode.INVOKEINTERFACE: + case ByteCode.INVOKESPECIAL: + case ByteCode.INVOKESTATIC: + case ByteCode.INVOKEVIRTUAL: + case ByteCode.JSR: + case ByteCode.LDC2_W: + case ByteCode.LDC_W: + case ByteCode.NEW: + case ByteCode.PUTFIELD: + case ByteCode.PUTSTATIC: + case ByteCode.SIPUSH: + return 2; + + case ByteCode.MULTIANEWARRAY: + return 3; + + case ByteCode.GOTO_W: + case ByteCode.JSR_W: + return 4; + + case ByteCode.LOOKUPSWITCH: // depends on alignment + case ByteCode.TABLESWITCH: // depends on alignment + return -1; + } + throw new IllegalArgumentException("Bad opcode: "+opcode); + } +*/ + private static String bytecodeStr(int code) + { + if (DEBUGSTACK || DEBUGCODE) { + switch (code) { + case ByteCode.NOP: return "nop"; + case ByteCode.ACONST_NULL: return "aconst_null"; + case ByteCode.ICONST_M1: return "iconst_m1"; + case ByteCode.ICONST_0: return "iconst_0"; + case ByteCode.ICONST_1: return "iconst_1"; + case ByteCode.ICONST_2: return "iconst_2"; + case ByteCode.ICONST_3: return "iconst_3"; + case ByteCode.ICONST_4: return "iconst_4"; + case ByteCode.ICONST_5: return "iconst_5"; + case ByteCode.LCONST_0: return "lconst_0"; + case ByteCode.LCONST_1: return "lconst_1"; + case ByteCode.FCONST_0: return "fconst_0"; + case ByteCode.FCONST_1: return "fconst_1"; + case ByteCode.FCONST_2: return "fconst_2"; + case ByteCode.DCONST_0: return "dconst_0"; + case ByteCode.DCONST_1: return "dconst_1"; + case ByteCode.BIPUSH: return "bipush"; + case ByteCode.SIPUSH: return "sipush"; + case ByteCode.LDC: return "ldc"; + case ByteCode.LDC_W: return "ldc_w"; + case ByteCode.LDC2_W: return "ldc2_w"; + case ByteCode.ILOAD: return "iload"; + case ByteCode.LLOAD: return "lload"; + case ByteCode.FLOAD: return "fload"; + case ByteCode.DLOAD: return "dload"; + case ByteCode.ALOAD: return "aload"; + case ByteCode.ILOAD_0: return "iload_0"; + case ByteCode.ILOAD_1: return "iload_1"; + case ByteCode.ILOAD_2: return "iload_2"; + case ByteCode.ILOAD_3: return "iload_3"; + case ByteCode.LLOAD_0: return "lload_0"; + case ByteCode.LLOAD_1: return "lload_1"; + case ByteCode.LLOAD_2: return "lload_2"; + case ByteCode.LLOAD_3: return "lload_3"; + case ByteCode.FLOAD_0: return "fload_0"; + case ByteCode.FLOAD_1: return "fload_1"; + case ByteCode.FLOAD_2: return "fload_2"; + case ByteCode.FLOAD_3: return "fload_3"; + case ByteCode.DLOAD_0: return "dload_0"; + case ByteCode.DLOAD_1: return "dload_1"; + case ByteCode.DLOAD_2: return "dload_2"; + case ByteCode.DLOAD_3: return "dload_3"; + case ByteCode.ALOAD_0: return "aload_0"; + case ByteCode.ALOAD_1: return "aload_1"; + case ByteCode.ALOAD_2: return "aload_2"; + case ByteCode.ALOAD_3: return "aload_3"; + case ByteCode.IALOAD: return "iaload"; + case ByteCode.LALOAD: return "laload"; + case ByteCode.FALOAD: return "faload"; + case ByteCode.DALOAD: return "daload"; + case ByteCode.AALOAD: return "aaload"; + case ByteCode.BALOAD: return "baload"; + case ByteCode.CALOAD: return "caload"; + case ByteCode.SALOAD: return "saload"; + case ByteCode.ISTORE: return "istore"; + case ByteCode.LSTORE: return "lstore"; + case ByteCode.FSTORE: return "fstore"; + case ByteCode.DSTORE: return "dstore"; + case ByteCode.ASTORE: return "astore"; + case ByteCode.ISTORE_0: return "istore_0"; + case ByteCode.ISTORE_1: return "istore_1"; + case ByteCode.ISTORE_2: return "istore_2"; + case ByteCode.ISTORE_3: return "istore_3"; + case ByteCode.LSTORE_0: return "lstore_0"; + case ByteCode.LSTORE_1: return "lstore_1"; + case ByteCode.LSTORE_2: return "lstore_2"; + case ByteCode.LSTORE_3: return "lstore_3"; + case ByteCode.FSTORE_0: return "fstore_0"; + case ByteCode.FSTORE_1: return "fstore_1"; + case ByteCode.FSTORE_2: return "fstore_2"; + case ByteCode.FSTORE_3: return "fstore_3"; + case ByteCode.DSTORE_0: return "dstore_0"; + case ByteCode.DSTORE_1: return "dstore_1"; + case ByteCode.DSTORE_2: return "dstore_2"; + case ByteCode.DSTORE_3: return "dstore_3"; + case ByteCode.ASTORE_0: return "astore_0"; + case ByteCode.ASTORE_1: return "astore_1"; + case ByteCode.ASTORE_2: return "astore_2"; + case ByteCode.ASTORE_3: return "astore_3"; + case ByteCode.IASTORE: return "iastore"; + case ByteCode.LASTORE: return "lastore"; + case ByteCode.FASTORE: return "fastore"; + case ByteCode.DASTORE: return "dastore"; + case ByteCode.AASTORE: return "aastore"; + case ByteCode.BASTORE: return "bastore"; + case ByteCode.CASTORE: return "castore"; + case ByteCode.SASTORE: return "sastore"; + case ByteCode.POP: return "pop"; + case ByteCode.POP2: return "pop2"; + case ByteCode.DUP: return "dup"; + case ByteCode.DUP_X1: return "dup_x1"; + case ByteCode.DUP_X2: return "dup_x2"; + case ByteCode.DUP2: return "dup2"; + case ByteCode.DUP2_X1: return "dup2_x1"; + case ByteCode.DUP2_X2: return "dup2_x2"; + case ByteCode.SWAP: return "swap"; + case ByteCode.IADD: return "iadd"; + case ByteCode.LADD: return "ladd"; + case ByteCode.FADD: return "fadd"; + case ByteCode.DADD: return "dadd"; + case ByteCode.ISUB: return "isub"; + case ByteCode.LSUB: return "lsub"; + case ByteCode.FSUB: return "fsub"; + case ByteCode.DSUB: return "dsub"; + case ByteCode.IMUL: return "imul"; + case ByteCode.LMUL: return "lmul"; + case ByteCode.FMUL: return "fmul"; + case ByteCode.DMUL: return "dmul"; + case ByteCode.IDIV: return "idiv"; + case ByteCode.LDIV: return "ldiv"; + case ByteCode.FDIV: return "fdiv"; + case ByteCode.DDIV: return "ddiv"; + case ByteCode.IREM: return "irem"; + case ByteCode.LREM: return "lrem"; + case ByteCode.FREM: return "frem"; + case ByteCode.DREM: return "drem"; + case ByteCode.INEG: return "ineg"; + case ByteCode.LNEG: return "lneg"; + case ByteCode.FNEG: return "fneg"; + case ByteCode.DNEG: return "dneg"; + case ByteCode.ISHL: return "ishl"; + case ByteCode.LSHL: return "lshl"; + case ByteCode.ISHR: return "ishr"; + case ByteCode.LSHR: return "lshr"; + case ByteCode.IUSHR: return "iushr"; + case ByteCode.LUSHR: return "lushr"; + case ByteCode.IAND: return "iand"; + case ByteCode.LAND: return "land"; + case ByteCode.IOR: return "ior"; + case ByteCode.LOR: return "lor"; + case ByteCode.IXOR: return "ixor"; + case ByteCode.LXOR: return "lxor"; + case ByteCode.IINC: return "iinc"; + case ByteCode.I2L: return "i2l"; + case ByteCode.I2F: return "i2f"; + case ByteCode.I2D: return "i2d"; + case ByteCode.L2I: return "l2i"; + case ByteCode.L2F: return "l2f"; + case ByteCode.L2D: return "l2d"; + case ByteCode.F2I: return "f2i"; + case ByteCode.F2L: return "f2l"; + case ByteCode.F2D: return "f2d"; + case ByteCode.D2I: return "d2i"; + case ByteCode.D2L: return "d2l"; + case ByteCode.D2F: return "d2f"; + case ByteCode.I2B: return "i2b"; + case ByteCode.I2C: return "i2c"; + case ByteCode.I2S: return "i2s"; + case ByteCode.LCMP: return "lcmp"; + case ByteCode.FCMPL: return "fcmpl"; + case ByteCode.FCMPG: return "fcmpg"; + case ByteCode.DCMPL: return "dcmpl"; + case ByteCode.DCMPG: return "dcmpg"; + case ByteCode.IFEQ: return "ifeq"; + case ByteCode.IFNE: return "ifne"; + case ByteCode.IFLT: return "iflt"; + case ByteCode.IFGE: return "ifge"; + case ByteCode.IFGT: return "ifgt"; + case ByteCode.IFLE: return "ifle"; + case ByteCode.IF_ICMPEQ: return "if_icmpeq"; + case ByteCode.IF_ICMPNE: return "if_icmpne"; + case ByteCode.IF_ICMPLT: return "if_icmplt"; + case ByteCode.IF_ICMPGE: return "if_icmpge"; + case ByteCode.IF_ICMPGT: return "if_icmpgt"; + case ByteCode.IF_ICMPLE: return "if_icmple"; + case ByteCode.IF_ACMPEQ: return "if_acmpeq"; + case ByteCode.IF_ACMPNE: return "if_acmpne"; + case ByteCode.GOTO: return "goto"; + case ByteCode.JSR: return "jsr"; + case ByteCode.RET: return "ret"; + case ByteCode.TABLESWITCH: return "tableswitch"; + case ByteCode.LOOKUPSWITCH: return "lookupswitch"; + case ByteCode.IRETURN: return "ireturn"; + case ByteCode.LRETURN: return "lreturn"; + case ByteCode.FRETURN: return "freturn"; + case ByteCode.DRETURN: return "dreturn"; + case ByteCode.ARETURN: return "areturn"; + case ByteCode.RETURN: return "return"; + case ByteCode.GETSTATIC: return "getstatic"; + case ByteCode.PUTSTATIC: return "putstatic"; + case ByteCode.GETFIELD: return "getfield"; + case ByteCode.PUTFIELD: return "putfield"; + case ByteCode.INVOKEVIRTUAL: return "invokevirtual"; + case ByteCode.INVOKESPECIAL: return "invokespecial"; + case ByteCode.INVOKESTATIC: return "invokestatic"; + case ByteCode.INVOKEINTERFACE: return "invokeinterface"; + case ByteCode.NEW: return "new"; + case ByteCode.NEWARRAY: return "newarray"; + case ByteCode.ANEWARRAY: return "anewarray"; + case ByteCode.ARRAYLENGTH: return "arraylength"; + case ByteCode.ATHROW: return "athrow"; + case ByteCode.CHECKCAST: return "checkcast"; + case ByteCode.INSTANCEOF: return "instanceof"; + case ByteCode.MONITORENTER: return "monitorenter"; + case ByteCode.MONITOREXIT: return "monitorexit"; + case ByteCode.WIDE: return "wide"; + case ByteCode.MULTIANEWARRAY: return "multianewarray"; + case ByteCode.IFNULL: return "ifnull"; + case ByteCode.IFNONNULL: return "ifnonnull"; + case ByteCode.GOTO_W: return "goto_w"; + case ByteCode.JSR_W: return "jsr_w"; + case ByteCode.BREAKPOINT: return "breakpoint"; + + case ByteCode.IMPDEP1: return "impdep1"; + case ByteCode.IMPDEP2: return "impdep2"; + } + } + return ""; + } + + final char[] getCharBuffer(int minimalSize) + { + if (minimalSize > tmpCharBuffer.length) { + int newSize = tmpCharBuffer.length * 2; + if (minimalSize > newSize) { newSize = minimalSize; } + tmpCharBuffer = new char[newSize]; + } + return tmpCharBuffer; + } + + private static final int LineNumberTableSize = 16; + private static final int ExceptionTableSize = 4; + + private final static long FileHeaderConstant = 0xCAFEBABE0003002DL; + // Set DEBUG flags to true to get better checking and progress info. + private static final boolean DEBUGSTACK = false; + private static final boolean DEBUGLABELS = false; + private static final boolean DEBUGCODE = false; + + private String generatedClassName; + + private ExceptionTableEntry itsExceptionTable[]; + private int itsExceptionTableTop; + + private int itsLineNumberTable[]; // pack start_pc & line_number together + private int itsLineNumberTableTop; + + private byte[] itsCodeBuffer = new byte[256]; + private int itsCodeBufferTop; + + private ConstantPool itsConstantPool; + + private ClassFileMethod itsCurrentMethod; + private short itsStackTop; + + private short itsMaxStack; + private short itsMaxLocals; + + private ObjArray itsMethods = new ObjArray(); + private ObjArray itsFields = new ObjArray(); + private ObjArray itsInterfaces = new ObjArray(); + + private short itsFlags; + private short itsThisClassIndex; + private short itsSuperClassIndex; + private short itsSourceFileNameIndex; + + private static final int MIN_LABEL_TABLE_SIZE = 32; + private int[] itsLabelTable; + private int itsLabelTableTop; + +// itsFixupTable[i] = (label_index << 32) | fixup_site + private static final int MIN_FIXUP_TABLE_SIZE = 40; + private long[] itsFixupTable; + private int itsFixupTableTop; + private ObjArray itsVarDescriptors; + + private char[] tmpCharBuffer = new char[64]; +} + +final class ExceptionTableEntry +{ + + ExceptionTableEntry(int startLabel, int endLabel, + int handlerLabel, short catchType) + { + itsStartLabel = startLabel; + itsEndLabel = endLabel; + itsHandlerLabel = handlerLabel; + itsCatchType = catchType; + } + + int itsStartLabel; + int itsEndLabel; + int itsHandlerLabel; + short itsCatchType; +} + +final class ClassFileField +{ + + ClassFileField(short nameIndex, short typeIndex, short flags) + { + itsNameIndex = nameIndex; + itsTypeIndex = typeIndex; + itsFlags = flags; + itsHasAttributes = false; + } + + void setAttributes(short attr1, short attr2, short attr3, int index) + { + itsHasAttributes = true; + itsAttr1 = attr1; + itsAttr2 = attr2; + itsAttr3 = attr3; + itsIndex = index; + } + + int write(byte[] data, int offset) + { + offset = ClassFileWriter.putInt16(itsFlags, data, offset); + offset = ClassFileWriter.putInt16(itsNameIndex, data, offset); + offset = ClassFileWriter.putInt16(itsTypeIndex, data, offset); + if (!itsHasAttributes) { + // write 0 attributes + offset = ClassFileWriter.putInt16(0, data, offset); + } else { + offset = ClassFileWriter.putInt16(1, data, offset); + offset = ClassFileWriter.putInt16(itsAttr1, data, offset); + offset = ClassFileWriter.putInt16(itsAttr2, data, offset); + offset = ClassFileWriter.putInt16(itsAttr3, data, offset); + offset = ClassFileWriter.putInt16(itsIndex, data, offset); + } + return offset; + } + + int getWriteSize() + { + int size = 2 * 3; + if (!itsHasAttributes) { + size += 2; + } else { + size += 2 + 2 * 4; + } + return size; + } + + private short itsNameIndex; + private short itsTypeIndex; + private short itsFlags; + private boolean itsHasAttributes; + private short itsAttr1, itsAttr2, itsAttr3; + private int itsIndex; +} + +final class ClassFileMethod +{ + + ClassFileMethod(short nameIndex, short typeIndex, short flags) + { + itsNameIndex = nameIndex; + itsTypeIndex = typeIndex; + itsFlags = flags; + } + + void setCodeAttribute(byte codeAttribute[]) + { + itsCodeAttribute = codeAttribute; + } + + int write(byte[] data, int offset) + { + offset = ClassFileWriter.putInt16(itsFlags, data, offset); + offset = ClassFileWriter.putInt16(itsNameIndex, data, offset); + offset = ClassFileWriter.putInt16(itsTypeIndex, data, offset); + // Code attribute only + offset = ClassFileWriter.putInt16(1, data, offset); + System.arraycopy(itsCodeAttribute, 0, data, offset, + itsCodeAttribute.length); + offset += itsCodeAttribute.length; + return offset; + } + + int getWriteSize() + { + return 2 * 4 + itsCodeAttribute.length; + } + + private short itsNameIndex; + private short itsTypeIndex; + private short itsFlags; + private byte[] itsCodeAttribute; + +} + +final class ConstantPool +{ + + ConstantPool(ClassFileWriter cfw) + { + this.cfw = cfw; + itsTopIndex = 1; // the zero'th entry is reserved + itsPool = new byte[ConstantPoolSize]; + itsTop = 0; + } + + private static final int ConstantPoolSize = 256; + private static final byte + CONSTANT_Class = 7, + CONSTANT_Fieldref = 9, + CONSTANT_Methodref = 10, + CONSTANT_InterfaceMethodref = 11, + CONSTANT_String = 8, + CONSTANT_Integer = 3, + CONSTANT_Float = 4, + CONSTANT_Long = 5, + CONSTANT_Double = 6, + CONSTANT_NameAndType = 12, + CONSTANT_Utf8 = 1; + + int write(byte[] data, int offset) + { + offset = ClassFileWriter.putInt16((short)itsTopIndex, data, offset); + System.arraycopy(itsPool, 0, data, offset, itsTop); + offset += itsTop; + return offset; + } + + int getWriteSize() + { + return 2 + itsTop; + } + + int addConstant(int k) + { + ensure(5); + itsPool[itsTop++] = CONSTANT_Integer; + itsTop = ClassFileWriter.putInt32(k, itsPool, itsTop); + return (short)(itsTopIndex++); + } + + int addConstant(long k) + { + ensure(9); + itsPool[itsTop++] = CONSTANT_Long; + itsTop = ClassFileWriter.putInt64(k, itsPool, itsTop); + int index = itsTopIndex; + itsTopIndex += 2; + return index; + } + + int addConstant(float k) + { + ensure(5); + itsPool[itsTop++] = CONSTANT_Float; + int bits = Float.floatToIntBits(k); + itsTop = ClassFileWriter.putInt32(bits, itsPool, itsTop); + return itsTopIndex++; + } + + int addConstant(double k) + { + ensure(9); + itsPool[itsTop++] = CONSTANT_Double; + long bits = Double.doubleToLongBits(k); + itsTop = ClassFileWriter.putInt64(bits, itsPool, itsTop); + int index = itsTopIndex; + itsTopIndex += 2; + return index; + } + + int addConstant(String k) + { + int utf8Index = 0xFFFF & addUtf8(k); + int theIndex = itsStringConstHash.getInt(utf8Index, -1); + if (theIndex == -1) { + theIndex = itsTopIndex++; + ensure(3); + itsPool[itsTop++] = CONSTANT_String; + itsTop = ClassFileWriter.putInt16(utf8Index, itsPool, itsTop); + itsStringConstHash.put(utf8Index, theIndex); + } + return theIndex; + } + + boolean isUnderUtfEncodingLimit(String s) + { + int strLen = s.length(); + if (strLen * 3 <= MAX_UTF_ENCODING_SIZE) { + return true; + } else if (strLen > MAX_UTF_ENCODING_SIZE) { + return false; + } + return strLen == getUtfEncodingLimit(s, 0, strLen); + } + + /** + * Get maximum i such that start <= i <= end and + * s.substring(start, i) fits JVM UTF string encoding limit. + */ + int getUtfEncodingLimit(String s, int start, int end) + { + if ((end - start) * 3 <= MAX_UTF_ENCODING_SIZE) { + return end; + } + int limit = MAX_UTF_ENCODING_SIZE; + for (int i = start; i != end; i++) { + int c = s.charAt(i); + if (0 != c && c <= 0x7F) { + --limit; + } else if (c < 0x7FF) { + limit -= 2; + } else { + limit -= 3; + } + if (limit < 0) { + return i; + } + } + return end; + } + + short addUtf8(String k) + { + int theIndex = itsUtf8Hash.get(k, -1); + if (theIndex == -1) { + int strLen = k.length(); + boolean tooBigString; + if (strLen > MAX_UTF_ENCODING_SIZE) { + tooBigString = true; + } else { + tooBigString = false; + // Ask for worst case scenario buffer when each char takes 3 + // bytes + ensure(1 + 2 + strLen * 3); + int top = itsTop; + + itsPool[top++] = CONSTANT_Utf8; + top += 2; // skip length + + char[] chars = cfw.getCharBuffer(strLen); + k.getChars(0, strLen, chars, 0); + + for (int i = 0; i != strLen; i++) { + int c = chars[i]; + if (c != 0 && c <= 0x7F) { + itsPool[top++] = (byte)c; + } else if (c > 0x7FF) { + itsPool[top++] = (byte)(0xE0 | (c >> 12)); + itsPool[top++] = (byte)(0x80 | ((c >> 6) & 0x3F)); + itsPool[top++] = (byte)(0x80 | (c & 0x3F)); + } else { + itsPool[top++] = (byte)(0xC0 | (c >> 6)); + itsPool[top++] = (byte)(0x80 | (c & 0x3F)); + } + } + + int utfLen = top - (itsTop + 1 + 2); + if (utfLen > MAX_UTF_ENCODING_SIZE) { + tooBigString = true; + } else { + // Write back length + itsPool[itsTop + 1] = (byte)(utfLen >>> 8); + itsPool[itsTop + 2] = (byte)utfLen; + + itsTop = top; + theIndex = itsTopIndex++; + itsUtf8Hash.put(k, theIndex); + } + } + if (tooBigString) { + throw new IllegalArgumentException("Too big string"); + } + } + return (short)theIndex; + } + + private short addNameAndType(String name, String type) + { + short nameIndex = addUtf8(name); + short typeIndex = addUtf8(type); + ensure(5); + itsPool[itsTop++] = CONSTANT_NameAndType; + itsTop = ClassFileWriter.putInt16(nameIndex, itsPool, itsTop); + itsTop = ClassFileWriter.putInt16(typeIndex, itsPool, itsTop); + return (short)(itsTopIndex++); + } + + short addClass(String className) + { + int theIndex = itsClassHash.get(className, -1); + if (theIndex == -1) { + String slashed = className; + if (className.indexOf('.') > 0) { + slashed = ClassFileWriter.getSlashedForm(className); + theIndex = itsClassHash.get(slashed, -1); + if (theIndex != -1) { + itsClassHash.put(className, theIndex); + } + } + if (theIndex == -1) { + int utf8Index = addUtf8(slashed); + ensure(3); + itsPool[itsTop++] = CONSTANT_Class; + itsTop = ClassFileWriter.putInt16(utf8Index, itsPool, itsTop); + theIndex = itsTopIndex++; + itsClassHash.put(slashed, theIndex); + if (className != slashed) { + itsClassHash.put(className, theIndex); + } + } + } + return (short)theIndex; + } + + short addFieldRef(String className, String fieldName, String fieldType) + { + FieldOrMethodRef ref = new FieldOrMethodRef(className, fieldName, + fieldType); + + int theIndex = itsFieldRefHash.get(ref, -1); + if (theIndex == -1) { + short ntIndex = addNameAndType(fieldName, fieldType); + short classIndex = addClass(className); + ensure(5); + itsPool[itsTop++] = CONSTANT_Fieldref; + itsTop = ClassFileWriter.putInt16(classIndex, itsPool, itsTop); + itsTop = ClassFileWriter.putInt16(ntIndex, itsPool, itsTop); + theIndex = itsTopIndex++; + itsFieldRefHash.put(ref, theIndex); + } + return (short)theIndex; + } + + short addMethodRef(String className, String methodName, + String methodType) + { + FieldOrMethodRef ref = new FieldOrMethodRef(className, methodName, + methodType); + + int theIndex = itsMethodRefHash.get(ref, -1); + if (theIndex == -1) { + short ntIndex = addNameAndType(methodName, methodType); + short classIndex = addClass(className); + ensure(5); + itsPool[itsTop++] = CONSTANT_Methodref; + itsTop = ClassFileWriter.putInt16(classIndex, itsPool, itsTop); + itsTop = ClassFileWriter.putInt16(ntIndex, itsPool, itsTop); + theIndex = itsTopIndex++; + itsMethodRefHash.put(ref, theIndex); + } + return (short)theIndex; + } + + short addInterfaceMethodRef(String className, + String methodName, String methodType) + { + short ntIndex = addNameAndType(methodName, methodType); + short classIndex = addClass(className); + ensure(5); + itsPool[itsTop++] = CONSTANT_InterfaceMethodref; + itsTop = ClassFileWriter.putInt16(classIndex, itsPool, itsTop); + itsTop = ClassFileWriter.putInt16(ntIndex, itsPool, itsTop); + return (short)(itsTopIndex++); + } + + void ensure(int howMuch) + { + if (itsTop + howMuch > itsPool.length) { + int newCapacity = itsPool.length * 2; + if (itsTop + howMuch > newCapacity) { + newCapacity = itsTop + howMuch; + } + byte[] tmp = new byte[newCapacity]; + System.arraycopy(itsPool, 0, tmp, 0, itsTop); + itsPool = tmp; + } + } + + private ClassFileWriter cfw; + + private static final int MAX_UTF_ENCODING_SIZE = 65535; + + private UintMap itsStringConstHash = new UintMap(); + private ObjToIntMap itsUtf8Hash = new ObjToIntMap(); + private ObjToIntMap itsFieldRefHash = new ObjToIntMap(); + private ObjToIntMap itsMethodRefHash = new ObjToIntMap(); + private ObjToIntMap itsClassHash = new ObjToIntMap(); + + private int itsTop; + private int itsTopIndex; + private byte itsPool[]; +} + +final class FieldOrMethodRef +{ + FieldOrMethodRef(String className, String name, String type) + { + this.className = className; + this.name = name; + this.type = type; + } + + public boolean equals(Object obj) + { + if (!(obj instanceof FieldOrMethodRef)) { return false; } + FieldOrMethodRef x = (FieldOrMethodRef)obj; + return className.equals(x.className) + && name.equals(x.name) + && type.equals(x.type); + } + + public int hashCode() + { + if (hashCode == -1) { + int h1 = className.hashCode(); + int h2 = name.hashCode(); + int h3 = type.hashCode(); + hashCode = h1 ^ h2 ^ h3; + } + return hashCode; + } + + private String className; + private String name; + private String type; + private int hashCode = -1; +} diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Arguments.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Arguments.java new file mode 100644 index 0000000..954b078 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Arguments.java @@ -0,0 +1,311 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Norris Boyd + * Igor Bukanov + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript; + +/** + * This class implements the "arguments" object. + * + * See ECMA 10.1.8 + * + * @see org.mozilla.javascript.NativeCall + * @author Norris Boyd + */ +final class Arguments extends IdScriptableObject +{ + static final long serialVersionUID = 4275508002492040609L; + + public Arguments(NativeCall activation) + { + this.activation = activation; + + Scriptable parent = activation.getParentScope(); + setParentScope(parent); + setPrototype(ScriptableObject.getObjectPrototype(parent)); + + args = activation.originalArgs; + lengthObj = new Integer(args.length); + + NativeFunction f = activation.function; + calleeObj = f; + + int version = f.getLanguageVersion(); + if (version <= Context.VERSION_1_3 + && version != Context.VERSION_DEFAULT) + { + callerObj = null; + } else { + callerObj = NOT_FOUND; + } + } + + public String getClassName() + { + return "Object"; + } + + public boolean has(int index, Scriptable start) + { + if (0 <= index && index < args.length) { + if (args[index] != NOT_FOUND) { + return true; + } + } + return super.has(index, start); + } + + public Object get(int index, Scriptable start) + { + if (0 <= index && index < args.length) { + Object value = args[index]; + if (value != NOT_FOUND) { + if (sharedWithActivation(index)) { + NativeFunction f = activation.function; + String argName = f.getParamOrVarName(index); + value = activation.get(argName, activation); + if (value == NOT_FOUND) Kit.codeBug(); + } + return value; + } + } + return super.get(index, start); + } + + private boolean sharedWithActivation(int index) + { + NativeFunction f = activation.function; + int definedCount = f.getParamCount(); + if (index < definedCount) { + // Check if argument is not hidden by later argument with the same + // name as hidden arguments are not shared with activation + if (index < definedCount - 1) { + String argName = f.getParamOrVarName(index); + for (int i = index + 1; i < definedCount; i++) { + if (argName.equals(f.getParamOrVarName(i))) { + return false; + } + } + } + return true; + } + return false; + } + + public void put(int index, Scriptable start, Object value) + { + if (0 <= index && index < args.length) { + if (args[index] != NOT_FOUND) { + if (sharedWithActivation(index)) { + String argName; + argName = activation.function.getParamOrVarName(index); + activation.put(argName, activation, value); + return; + } + synchronized (this) { + if (args[index] != NOT_FOUND) { + if (args == activation.originalArgs) { + args = args.clone(); + } + args[index] = value; + return; + } + } + } + } + super.put(index, start, value); + } + + public void delete(int index) + { + if (0 <= index && index < args.length) { + synchronized (this) { + if (args[index] != NOT_FOUND) { + if (args == activation.originalArgs) { + args = args.clone(); + } + args[index] = NOT_FOUND; + return; + } + } + } + super.delete(index); + } + +// #string_id_map# + + private static final int + Id_callee = 1, + Id_length = 2, + Id_caller = 3, + + MAX_INSTANCE_ID = 3; + + protected int getMaxInstanceId() + { + return MAX_INSTANCE_ID; + } + + protected int findInstanceIdInfo(String s) + { + int id; +// #generated# Last update: 2007-05-09 08:15:04 EDT + L0: { id = 0; String X = null; int c; + if (s.length()==6) { + c=s.charAt(5); + if (c=='e') { X="callee";id=Id_callee; } + else if (c=='h') { X="length";id=Id_length; } + else if (c=='r') { X="caller";id=Id_caller; } + } + if (X!=null && X!=s && !X.equals(s)) id = 0; + break L0; + } +// #/generated# + + if (id == 0) return super.findInstanceIdInfo(s); + + int attr; + switch (id) { + case Id_callee: + case Id_caller: + case Id_length: + attr = DONTENUM; + break; + default: throw new IllegalStateException(); + } + return instanceIdInfo(attr, id); + } + +// #/string_id_map# + + protected String getInstanceIdName(int id) + { + switch (id) { + case Id_callee: return "callee"; + case Id_length: return "length"; + case Id_caller: return "caller"; + } + return null; + } + + protected Object getInstanceIdValue(int id) + { + switch (id) { + case Id_callee: return calleeObj; + case Id_length: return lengthObj; + case Id_caller: { + Object value = callerObj; + if (value == UniqueTag.NULL_VALUE) { value = null; } + else if (value == null) { + NativeCall caller = activation.parentActivationCall; + if (caller != null) { + value = caller.get("arguments", caller); + } + } + return value; + } + } + return super.getInstanceIdValue(id); + } + + protected void setInstanceIdValue(int id, Object value) + { + switch (id) { + case Id_callee: calleeObj = value; return; + case Id_length: lengthObj = value; return; + case Id_caller: + callerObj = (value != null) ? value : UniqueTag.NULL_VALUE; + return; + } + super.setInstanceIdValue(id, value); + } + + Object[] getIds(boolean getAll) + { + Object[] ids = super.getIds(getAll); + if (getAll && args.length != 0) { + boolean[] present = null; + int extraCount = args.length; + for (int i = 0; i != ids.length; ++i) { + Object id = ids[i]; + if (id instanceof Integer) { + int index = ((Integer)id).intValue(); + if (0 <= index && index < args.length) { + if (present == null) { + present = new boolean[args.length]; + } + if (!present[index]) { + present[index] = true; + extraCount--; + } + } + } + } + if (extraCount != 0) { + Object[] tmp = new Object[extraCount + ids.length]; + System.arraycopy(ids, 0, tmp, extraCount, ids.length); + ids = tmp; + int offset = 0; + for (int i = 0; i != args.length; ++i) { + if (present == null || !present[i]) { + ids[offset] = new Integer(i); + ++offset; + } + } + if (offset != extraCount) Kit.codeBug(); + } + } + return ids; + } + +// Fields to hold caller, callee and length properties, +// where NOT_FOUND value tags deleted properties. +// In addition if callerObj == NULL_VALUE, it tags null for scripts, as +// initial callerObj == null means access to caller arguments available +// only in JS <= 1.3 scripts + private Object callerObj; + private Object calleeObj; + private Object lengthObj; + + private NativeCall activation; + +// Initially args holds activation.getOriginalArgs(), but any modification +// of its elements triggers creation of a copy. If its element holds NOT_FOUND, +// it indicates deleted index, in which case super class is queried. + private Object[] args; +} diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/BaseFunction.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/BaseFunction.java new file mode 100644 index 0000000..d7d8992 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/BaseFunction.java @@ -0,0 +1,553 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Norris Boyd + * Igor Bukanov + * Roger Lawrence + * Mike McCabe + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript; + +/** + * The base class for Function objects + * See ECMA 15.3. + * @author Norris Boyd + */ +public class BaseFunction extends IdScriptableObject implements Function +{ + + static final long serialVersionUID = 5311394446546053859L; + + private static final Object FUNCTION_TAG = new Object(); + + static void init(Scriptable scope, boolean sealed) + { + BaseFunction obj = new BaseFunction(); + // Function.prototype attributes: see ECMA 15.3.3.1 + obj.prototypePropertyAttributes = DONTENUM | READONLY | PERMANENT; + obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed); + } + + public BaseFunction() + { + } + + public BaseFunction(Scriptable scope, Scriptable prototype) + { + super(scope, prototype); + } + + public String getClassName() { + return "Function"; + } + + /** + * Implements the instanceof operator for JavaScript Function objects. + *

    + * + * foo = new Foo();
    + * foo instanceof Foo; // true
    + *
    + * + * @param instance The value that appeared on the LHS of the instanceof + * operator + * @return true if the "prototype" property of "this" appears in + * value's prototype chain + * + */ + public boolean hasInstance(Scriptable instance) + { + Object protoProp = ScriptableObject.getProperty(this, "prototype"); + if (protoProp instanceof Scriptable) { + return ScriptRuntime.jsDelegatesTo(instance, (Scriptable)protoProp); + } + throw ScriptRuntime.typeError1("msg.instanceof.bad.prototype", + getFunctionName()); + } + +// #string_id_map# + + private static final int + Id_length = 1, + Id_arity = 2, + Id_name = 3, + Id_prototype = 4, + Id_arguments = 5, + + MAX_INSTANCE_ID = 5; + + protected int getMaxInstanceId() + { + return MAX_INSTANCE_ID; + } + + protected int findInstanceIdInfo(String s) + { + int id; +// #generated# Last update: 2007-05-09 08:15:15 EDT + L0: { id = 0; String X = null; int c; + L: switch (s.length()) { + case 4: X="name";id=Id_name; break L; + case 5: X="arity";id=Id_arity; break L; + case 6: X="length";id=Id_length; break L; + case 9: c=s.charAt(0); + if (c=='a') { X="arguments";id=Id_arguments; } + else if (c=='p') { X="prototype";id=Id_prototype; } + break L; + } + if (X!=null && X!=s && !X.equals(s)) id = 0; + break L0; + } +// #/generated# +// #/string_id_map# + + if (id == 0) return super.findInstanceIdInfo(s); + + int attr; + switch (id) { + case Id_length: + case Id_arity: + case Id_name: + attr = DONTENUM | READONLY | PERMANENT; + break; + case Id_prototype: + attr = prototypePropertyAttributes; + break; + case Id_arguments: + attr = DONTENUM | PERMANENT; + break; + default: throw new IllegalStateException(); + } + return instanceIdInfo(attr, id); + } + + protected String getInstanceIdName(int id) + { + switch (id) { + case Id_length: return "length"; + case Id_arity: return "arity"; + case Id_name: return "name"; + case Id_prototype: return "prototype"; + case Id_arguments: return "arguments"; + } + return super.getInstanceIdName(id); + } + + protected Object getInstanceIdValue(int id) + { + switch (id) { + case Id_length: return ScriptRuntime.wrapInt(getLength()); + case Id_arity: return ScriptRuntime.wrapInt(getArity()); + case Id_name: return getFunctionName(); + case Id_prototype: return getPrototypeProperty(); + case Id_arguments: return getArguments(); + } + return super.getInstanceIdValue(id); + } + + protected void setInstanceIdValue(int id, Object value) + { + if (id == Id_prototype) { + if ((prototypePropertyAttributes & READONLY) == 0) { + prototypeProperty = (value != null) + ? value : UniqueTag.NULL_VALUE; + } + return; + } else if (id == Id_arguments) { + if (value == NOT_FOUND) { + // This should not be called since "arguments" is PERMANENT + Kit.codeBug(); + } + defaultPut("arguments", value); + } + super.setInstanceIdValue(id, value); + } + + protected void fillConstructorProperties(IdFunctionObject ctor) + { + // Fix up bootstrapping problem: getPrototype of the IdFunctionObject + // can not return Function.prototype because Function object is not + // yet defined. + ctor.setPrototype(this); + super.fillConstructorProperties(ctor); + } + + protected void initPrototypeId(int id) + { + String s; + int arity; + switch (id) { + case Id_constructor: arity=1; s="constructor"; break; + case Id_toString: arity=1; s="toString"; break; + case Id_toSource: arity=1; s="toSource"; break; + case Id_apply: arity=2; s="apply"; break; + case Id_call: arity=1; s="call"; break; + default: throw new IllegalArgumentException(String.valueOf(id)); + } + initPrototypeMethod(FUNCTION_TAG, id, s, arity); + } + + static boolean isApply(IdFunctionObject f) { + return f.hasTag(FUNCTION_TAG) && f.methodId() == Id_apply; + } + + static boolean isApplyOrCall(IdFunctionObject f) { + if(f.hasTag(FUNCTION_TAG)) { + switch(f.methodId()) { + case Id_apply: + case Id_call: + return true; + } + } + return false; + } + + public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, + Scriptable thisObj, Object[] args) + { + if (!f.hasTag(FUNCTION_TAG)) { + return super.execIdCall(f, cx, scope, thisObj, args); + } + int id = f.methodId(); + switch (id) { + case Id_constructor: + return jsConstructor(cx, scope, args); + + case Id_toString: { + BaseFunction realf = realFunction(thisObj, f); + int indent = ScriptRuntime.toInt32(args, 0); + return realf.decompile(indent, 0); + } + + case Id_toSource: { + BaseFunction realf = realFunction(thisObj, f); + int indent = 0; + int flags = Decompiler.TO_SOURCE_FLAG; + if (args.length != 0) { + indent = ScriptRuntime.toInt32(args[0]); + if (indent >= 0) { + flags = 0; + } else { + indent = 0; + } + } + return realf.decompile(indent, flags); + } + + case Id_apply: + case Id_call: + return ScriptRuntime.applyOrCall(id == Id_apply, + cx, scope, thisObj, args); + } + throw new IllegalArgumentException(String.valueOf(id)); + } + + private BaseFunction realFunction(Scriptable thisObj, IdFunctionObject f) + { + Object x = thisObj.getDefaultValue(ScriptRuntime.FunctionClass); + if (x instanceof BaseFunction) { + return (BaseFunction)x; + } + throw ScriptRuntime.typeError1("msg.incompat.call", + f.getFunctionName()); + } + + /** + * Make value as DontEnum, DontDelete, ReadOnly + * prototype property of this Function object + */ + public void setImmunePrototypeProperty(Object value) + { + if ((prototypePropertyAttributes & READONLY) != 0) { + throw new IllegalStateException(); + } + prototypeProperty = (value != null) ? value : UniqueTag.NULL_VALUE; + prototypePropertyAttributes = DONTENUM | PERMANENT | READONLY; + } + + protected Scriptable getClassPrototype() + { + Object protoVal = getPrototypeProperty(); + if (protoVal instanceof Scriptable) { + return (Scriptable) protoVal; + } + return getClassPrototype(this, "Object"); + } + + /** + * Should be overridden. + */ + public Object call(Context cx, Scriptable scope, Scriptable thisObj, + Object[] args) + { + return Undefined.instance; + } + + public Scriptable construct(Context cx, Scriptable scope, Object[] args) + { + Scriptable result = createObject(cx, scope); + if (result != null) { + Object val = call(cx, scope, result, args); + if (val instanceof Scriptable) { + result = (Scriptable)val; + } + } else { + Object val = call(cx, scope, null, args); + if (!(val instanceof Scriptable)) { + // It is program error not to return Scriptable from + // the call method if createObject returns null. + throw new IllegalStateException( + "Bad implementaion of call as constructor, name=" + +getFunctionName()+" in "+getClass().getName()); + } + result = (Scriptable)val; + if (result.getPrototype() == null) { + result.setPrototype(getClassPrototype()); + } + if (result.getParentScope() == null) { + Scriptable parent = getParentScope(); + if (result != parent) { + result.setParentScope(parent); + } + } + } + return result; + } + + /** + * Creates new script object. + * The default implementation of {@link #construct} uses the method to + * to get the value for thisObj argument when invoking + * {@link #call}. + * The methos is allowed to return null to indicate that + * {@link #call} will create a new object itself. In this case + * {@link #construct} will set scope and prototype on the result + * {@link #call} unless they are already set. + */ + public Scriptable createObject(Context cx, Scriptable scope) + { + Scriptable newInstance = new NativeObject(); + newInstance.setPrototype(getClassPrototype()); + newInstance.setParentScope(getParentScope()); + return newInstance; + } + + /** + * Decompile the source information associated with this js + * function/script back into a string. + * + * @param indent How much to indent the decompiled result. + * + * @param flags Flags specifying format of decompilation output. + */ + String decompile(int indent, int flags) + { + StringBuffer sb = new StringBuffer(); + boolean justbody = (0 != (flags & Decompiler.ONLY_BODY_FLAG)); + if (!justbody) { + sb.append("function "); + sb.append(getFunctionName()); + sb.append("() {\n\t"); + } + sb.append("[native code, arity="); + sb.append(getArity()); + sb.append("]\n"); + if (!justbody) { + sb.append("}\n"); + } + return sb.toString(); + } + + public int getArity() { return 0; } + + public int getLength() { return 0; } + + public String getFunctionName() + { + return ""; + } + + final Object getPrototypeProperty() { + Object result = prototypeProperty; + if (result == null) { + synchronized (this) { + result = prototypeProperty; + if (result == null) { + setupDefaultPrototype(); + result = prototypeProperty; + } + } + } + else if (result == UniqueTag.NULL_VALUE) { result = null; } + return result; + } + + private void setupDefaultPrototype() + { + NativeObject obj = new NativeObject(); + final int attr = ScriptableObject.DONTENUM; + obj.defineProperty("constructor", this, attr); + // put the prototype property into the object now, then in the + // wacky case of a user defining a function Object(), we don't + // get an infinite loop trying to find the prototype. + prototypeProperty = obj; + Scriptable proto = getObjectPrototype(this); + if (proto != obj) { + // not the one we just made, it must remain grounded + obj.setPrototype(proto); + } + } + + private Object getArguments() + { + // .arguments is deprecated, so we use a slow + // way of getting it that doesn't add to the invocation cost. + // TODO: add warning, error based on version + Object value = defaultGet("arguments"); + if (value != NOT_FOUND) { + // Should after changing .arguments its + // activation still be available during Function call? + // This code assumes it should not: + // defaultGet("arguments") != NOT_FOUND + // means assigned arguments + return value; + } + Context cx = Context.getContext(); + NativeCall activation = ScriptRuntime.findFunctionActivation(cx, this); + return (activation == null) + ? null + : activation.get("arguments", activation); + } + + private static Object jsConstructor(Context cx, Scriptable scope, + Object[] args) + { + int arglen = args.length; + StringBuffer sourceBuf = new StringBuffer(); + + sourceBuf.append("function "); + /* version != 1.2 Function constructor behavior - + * print 'anonymous' as the function name if the + * version (under which the function was compiled) is + * less than 1.2... or if it's greater than 1.2, because + * we need to be closer to ECMA. + */ + if (cx.getLanguageVersion() != Context.VERSION_1_2) { + sourceBuf.append("anonymous"); + } + sourceBuf.append('('); + + // Append arguments as coma separated strings + for (int i = 0; i < arglen - 1; i++) { + if (i > 0) { + sourceBuf.append(','); + } + sourceBuf.append(ScriptRuntime.toString(args[i])); + } + sourceBuf.append(") {"); + if (arglen != 0) { + // append function body + String funBody = ScriptRuntime.toString(args[arglen - 1]); + sourceBuf.append(funBody); + } + sourceBuf.append('}'); + String source = sourceBuf.toString(); + + int[] linep = new int[1]; + String filename = Context.getSourcePositionFromStack(linep); + if (filename == null) { + filename = ""; + linep[0] = 1; + } + + String sourceURI = ScriptRuntime. + makeUrlForGeneratedScript(false, filename, linep[0]); + + Scriptable global = ScriptableObject.getTopLevelScope(scope); + + ErrorReporter reporter; + reporter = DefaultErrorReporter.forEval(cx.getErrorReporter()); + + Evaluator evaluator = Context.createInterpreter(); + if (evaluator == null) { + throw new JavaScriptException("Interpreter not present", + filename, linep[0]); + } + + // Compile with explicit interpreter instance to force interpreter + // mode. + return cx.compileFunction(global, source, evaluator, reporter, + sourceURI, 1, null); + } + + protected int findPrototypeId(String s) + { + int id; +// #string_id_map# +// #generated# Last update: 2007-05-09 08:15:15 EDT + L0: { id = 0; String X = null; int c; + L: switch (s.length()) { + case 4: X="call";id=Id_call; break L; + case 5: X="apply";id=Id_apply; break L; + case 8: c=s.charAt(3); + if (c=='o') { X="toSource";id=Id_toSource; } + else if (c=='t') { X="toString";id=Id_toString; } + break L; + case 11: X="constructor";id=Id_constructor; break L; + } + if (X!=null && X!=s && !X.equals(s)) id = 0; + break L0; + } +// #/generated# + return id; + } + + private static final int + Id_constructor = 1, + Id_toString = 2, + Id_toSource = 3, + Id_apply = 4, + Id_call = 5, + + MAX_PROTOTYPE_ID = 5; + +// #/string_id_map# + + private Object prototypeProperty; + // For function object instances, attribute is PERMANENT; see ECMA 15.3.5.2 + private int prototypePropertyAttributes = PERMANENT; +} + diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Callable.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Callable.java new file mode 100644 index 0000000..03e0fce --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Callable.java @@ -0,0 +1,59 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Igor Bukanov, igor@fastmail.fm + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript; + +/** + * Generic notion of callable object that can execute some script-related code + * upon request with specified values for script scope and this objects. + */ +public interface Callable +{ + /** + * Perform the call. + * + * @param cx the current Context for this thread + * @param scope the scope to use to resolve properties. + * @param thisObj the JavaScript this object + * @param args the array of arguments + * @return the result of the call + */ + public Object call(Context cx, Scriptable scope, Scriptable thisObj, + Object[] args); +} + diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ClassCache.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ClassCache.java new file mode 100644 index 0000000..9047278 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ClassCache.java @@ -0,0 +1,220 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Igor Bukanov, igor@fastmail.fm + * Norris Boyd + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript; + +import java.util.Map; +import java.util.HashMap; + +/** + * Cache of generated classes and data structures to access Java runtime + * from JavaScript. + * + * @author Igor Bukanov + * + * @since Rhino 1.5 Release 5 + */ +public class ClassCache +{ + private static final Object AKEY = new Object(); + private volatile boolean cachingIsEnabled = true; + private HashMap,JavaMembers> classTable + = new HashMap,JavaMembers>(); + private HashMap,JavaMembers> javaAdapterGeneratedClasses + = new HashMap,JavaMembers>(); + private HashMap> classAdapterCache + = new HashMap>(); + private HashMap,Object> interfaceAdapterCache; + private int generatedClassSerial; + + /** + * Search for ClassCache object in the given scope. + * The method first calls + * {@link ScriptableObject#getTopLevelScope(Scriptable scope)} + * to get the top most scope and then tries to locate associated + * ClassCache object in the prototype chain of the top scope. + * + * @param scope scope to search for ClassCache object. + * @return previously associated ClassCache object or a new instance of + * ClassCache if no ClassCache object was found. + * + * @see #associate(ScriptableObject topScope) + */ + public static ClassCache get(Scriptable scope) + { + ClassCache cache = (ClassCache) + ScriptableObject.getTopScopeValue(scope, AKEY); + if (cache == null) { + throw new RuntimeException("Can't find top level scope for " + + "ClassCache.get"); + } + return cache; + } + + /** + * Associate ClassCache object with the given top-level scope. + * The ClassCache object can only be associated with the given scope once. + * + * @param topScope scope to associate this ClassCache object with. + * @return true if no previous ClassCache objects were embedded into + * the scope and this ClassCache were successfully associated + * or false otherwise. + * + * @see #get(Scriptable scope) + */ + public boolean associate(ScriptableObject topScope) + { + if (topScope.getParentScope() != null) { + // Can only associate cache with top level scope + throw new IllegalArgumentException(); + } + if (this == topScope.associateValue(AKEY, this)) { + return true; + } + return false; + } + + /** + * Empty caches of generated Java classes and Java reflection information. + */ + public synchronized void clearCaches() + { + classTable.clear(); + javaAdapterGeneratedClasses.clear(); + classAdapterCache.clear(); + interfaceAdapterCache = null; + } + + /** + * Check if generated Java classes and Java reflection information + * is cached. + */ + public final boolean isCachingEnabled() + { + return cachingIsEnabled; + } + + /** + * Set whether to cache some values. + *

    + * By default, the engine will cache the results of + * Class.getMethods() and similar calls. + * This can speed execution dramatically, but increases the memory + * footprint. Also, with caching enabled, references may be held to + * objects past the lifetime of any real usage. + *

    + * If caching is enabled and this method is called with a + * false argument, the caches will be emptied. + *

    + * Caching is enabled by default. + * + * @param enabled if true, caching is enabled + * + * @see #clearCaches() + */ + public synchronized void setCachingEnabled(boolean enabled) + { + if (enabled == cachingIsEnabled) + return; + if (!enabled) + clearCaches(); + cachingIsEnabled = enabled; + } + + /** + * @return a map from classes to associated JavaMembers objects + */ + Map,JavaMembers> getClassCacheMap() { + return classTable; + } + + Map> getInterfaceAdapterCacheMap() + { + return classAdapterCache; + } + + /** + * @deprecated + * The method always returns false. + * @see #setInvokerOptimizationEnabled(boolean enabled) + */ + public boolean isInvokerOptimizationEnabled() + { + return false; + } + + /** + * @deprecated + * The method does nothing. + * Invoker optimization is no longer used by Rhino. + * On modern JDK like 1.4 or 1.5 the disadvantages of the optimization + * like increased memory usage or longer initialization time overweight + * small speed increase that can be gained using generated proxy class + * to replace reflection. + */ + public synchronized void setInvokerOptimizationEnabled(boolean enabled) + { + } + + /** + * Internal engine method to return serial number for generated classes + * to ensure name uniqueness. + */ + public final synchronized int newClassSerialNumber() + { + return ++generatedClassSerial; + } + + Object getInterfaceAdapter(Class cl) + { + return interfaceAdapterCache == null + ? null + : interfaceAdapterCache.get(cl); + } + + synchronized void cacheInterfaceAdapter(Class cl, Object iadapter) + { + if (cachingIsEnabled) { + if (interfaceAdapterCache == null) { + interfaceAdapterCache = new HashMap,Object>(); + } + interfaceAdapterCache.put(cl, iadapter); + } + } +} diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ClassShutter.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ClassShutter.java new file mode 100644 index 0000000..d5f4cd6 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ClassShutter.java @@ -0,0 +1,89 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Norris Boyd + * Igor Bukanov + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +// API class + +package org.mozilla.javascript; + +/** +Embeddings that wish to filter Java classes that are visible to scripts +through the LiveConnect, should implement this interface. + +@see Context#setClassShutter(ClassShutter) +@since 1.5 Release 4 +@author Norris Boyd +*/ + + public interface ClassShutter { + + /** + * Return true iff the Java class with the given name should be exposed + * to scripts. + *

    + * An embedding may filter which Java classes are exposed through + * LiveConnect to JavaScript scripts. + *

    + * Due to the fact that there is no package reflection in Java, + * this method will also be called with package names. There + * is no way for Rhino to tell if "Packages.a.b" is a package name + * or a class that doesn't exist. What Rhino does is attempt + * to load each segment of "Packages.a.b.c": It first attempts to + * load class "a", then attempts to load class "a.b", then + * finally attempts to load class "a.b.c". On a Rhino installation + * without any ClassShutter set, and without any of the + * above classes, the expression "Packages.a.b.c" will result in + * a [JavaPackage a.b.c] and not an error. + *

    + * With ClassShutter supplied, Rhino will first call + * visibleToScripts before attempting to look up the class name. If + * visibleToScripts returns false, the class name lookup is not + * performed and subsequent Rhino execution assumes the class is + * not present. So for "java.lang.System.out.println" the lookup + * of "java.lang.System" is skipped and thus Rhino assumes that + * "java.lang.System" doesn't exist. So then for "java.lang.System.out", + * Rhino attempts to load the class "java.lang.System.out" because + * it assumes that "java.lang.System" is a package name. + *

    + * @param fullClassName the full name of the class (including the package + * name, with '.' as a delimiter). For example the + * standard string class is "java.lang.String" + * @return whether or not to reveal this class to scripts + */ + public boolean visibleToScripts(String fullClassName); +} diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/CompilerEnvirons.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/CompilerEnvirons.java new file mode 100644 index 0000000..645d098 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/CompilerEnvirons.java @@ -0,0 +1,233 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Igor Bukanov, igor@fastmail.fm + * Bob Jervis + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript; + +import java.util.Hashtable; + +public class CompilerEnvirons +{ + public CompilerEnvirons() + { + errorReporter = DefaultErrorReporter.instance; + languageVersion = Context.VERSION_DEFAULT; + generateDebugInfo = true; + useDynamicScope = false; + reservedKeywordAsIdentifier = false; + allowMemberExprAsFunctionName = false; + xmlAvailable = true; + optimizationLevel = 0; + generatingSource = true; + strictMode = false; + warningAsError = false; + generateObserverCount = false; + } + + public void initFromContext(Context cx) + { + setErrorReporter(cx.getErrorReporter()); + this.languageVersion = cx.getLanguageVersion(); + useDynamicScope = cx.compileFunctionsWithDynamicScopeFlag; + generateDebugInfo = (!cx.isGeneratingDebugChanged() + || cx.isGeneratingDebug()); + reservedKeywordAsIdentifier + = cx.hasFeature(Context.FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER); + allowMemberExprAsFunctionName + = cx.hasFeature(Context.FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME); + strictMode + = cx.hasFeature(Context.FEATURE_STRICT_MODE); + warningAsError = cx.hasFeature(Context.FEATURE_WARNING_AS_ERROR); + xmlAvailable + = cx.hasFeature(Context.FEATURE_E4X); + + optimizationLevel = cx.getOptimizationLevel(); + + generatingSource = cx.isGeneratingSource(); + activationNames = cx.activationNames; + + // Observer code generation in compiled code : + generateObserverCount = cx.generateObserverCount; + } + + public final ErrorReporter getErrorReporter() + { + return errorReporter; + } + + public void setErrorReporter(ErrorReporter errorReporter) + { + if (errorReporter == null) throw new IllegalArgumentException(); + this.errorReporter = errorReporter; + } + + public final int getLanguageVersion() + { + return languageVersion; + } + + public void setLanguageVersion(int languageVersion) + { + Context.checkLanguageVersion(languageVersion); + this.languageVersion = languageVersion; + } + + public final boolean isGenerateDebugInfo() + { + return generateDebugInfo; + } + + public void setGenerateDebugInfo(boolean flag) + { + this.generateDebugInfo = flag; + } + + public final boolean isUseDynamicScope() + { + return useDynamicScope; + } + + public final boolean isReservedKeywordAsIdentifier() + { + return reservedKeywordAsIdentifier; + } + + public void setReservedKeywordAsIdentifier(boolean flag) + { + reservedKeywordAsIdentifier = flag; + } + + public final boolean isAllowMemberExprAsFunctionName() + { + return allowMemberExprAsFunctionName; + } + + public void setAllowMemberExprAsFunctionName(boolean flag) + { + allowMemberExprAsFunctionName = flag; + } + + public final boolean isXmlAvailable() + { + return xmlAvailable; + } + + public void setXmlAvailable(boolean flag) + { + xmlAvailable = flag; + } + + public final int getOptimizationLevel() + { + return optimizationLevel; + } + + public void setOptimizationLevel(int level) + { + Context.checkOptimizationLevel(level); + this.optimizationLevel = level; + } + + public final boolean isGeneratingSource() + { + return generatingSource; + } + + public final boolean isStrictMode() + { + return strictMode; + } + + public final boolean reportWarningAsError() + { + return warningAsError; + } + + /** + * Specify whether or not source information should be generated. + *

    + * Without source information, evaluating the "toString" method + * on JavaScript functions produces only "[native code]" for + * the body of the function. + * Note that code generated without source is not fully ECMA + * conformant. + */ + public void setGeneratingSource(boolean generatingSource) + { + this.generatingSource = generatingSource; + } + + /** + * @return true iff code will be generated with callbacks to enable + * instruction thresholds + */ + public boolean isGenerateObserverCount() { + return generateObserverCount; + } + + /** + * Turn on or off generation of code with callbacks to + * track the count of executed instructions. + * Currently only affects JVM byte code generation: this slows down the + * generated code, but code generated without the callbacks will not + * be counted toward instruction thresholds. Rhino's interpretive + * mode does instruction counting without inserting callbacks, so + * there is no requirement to compile code differently. + * @param generateObserverCount if true, generated code will contain + * calls to accumulate an estimate of the instructions executed. + */ + public void setGenerateObserverCount(boolean generateObserverCount) { + this.generateObserverCount = generateObserverCount; + } + + private ErrorReporter errorReporter; + + private int languageVersion; + private boolean generateDebugInfo; + private boolean useDynamicScope; + private boolean reservedKeywordAsIdentifier; + private boolean allowMemberExprAsFunctionName; + private boolean xmlAvailable; + private int optimizationLevel; + private boolean generatingSource; + private boolean strictMode; + private boolean warningAsError; + private boolean generateObserverCount; + Hashtable activationNames; +} + diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ConstProperties.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ConstProperties.java new file mode 100644 index 0000000..860db79 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ConstProperties.java @@ -0,0 +1,109 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +// API class + +package org.mozilla.javascript; + +public interface ConstProperties { + /** + * Sets a named const property in this object. + *

    + * The property is specified by a string name + * as defined for Scriptable.get. + *

    + * The possible values that may be passed in are as defined for + * Scriptable.get. A class that implements this method may choose + * to ignore calls to set certain properties, in which case those + * properties are effectively read-only.

    + * For properties defined in a prototype chain, + * use putProperty in ScriptableObject.

    + * Note that if a property a is defined in the prototype p + * of an object o, then evaluating o.a = 23 will cause + * set to be called on the prototype p with + * o as the start parameter. + * To preserve JavaScript semantics, it is the Scriptable + * object's responsibility to modify o.

    + * This design allows properties to be defined in prototypes and implemented + * in terms of getters and setters of Java values without consuming slots + * in each instance.

    + *

    + * The values that may be set are limited to the following: + *

      + *
    • java.lang.Boolean objects
    • + *
    • java.lang.String objects
    • + *
    • java.lang.Number objects
    • + *
    • org.mozilla.javascript.Scriptable objects
    • + *
    • null
    • + *
    • The value returned by Context.getUndefinedValue()
    • + *

    + * Arbitrary Java objects may be wrapped in a Scriptable by first calling + * Context.toObject. This allows the property of a JavaScript + * object to contain an arbitrary Java object as a value.

    + * Note that has will be called by the runtime first before + * set is called to determine in which object the + * property is defined. + * Note that this method is not expected to traverse the prototype chain, + * which is different from the ECMA [[Put]] operation. + * @param name the name of the property + * @param start the object whose property is being set + * @param value value to set the property to + * @see org.mozilla.javascript.Scriptable#has(String, Scriptable) + * @see org.mozilla.javascript.Scriptable#get(String, Scriptable) + * @see org.mozilla.javascript.ScriptableObject#putProperty(Scriptable, String, Object) + * @see org.mozilla.javascript.Context#toObject(Object, Scriptable) + */ + public void putConst(String name, Scriptable start, Object value); + + /** + * Reserves a definition spot for a const. This will set up a definition + * of the const property, but set its value to undefined. The semantics of + * the start parameter is the same as for putConst. + * @param name The name of the property. + * @param start The object whose property is being reserved. + */ + public void defineConst(String name, Scriptable start); + + /** + * Returns true if the named property is defined as a const on this object. + * @param name + * @return true if the named property is defined as a const, false + * otherwise. + */ + public boolean isConst(String name); +} diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Context.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Context.java new file mode 100644 index 0000000..0833883 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Context.java @@ -0,0 +1,2526 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +// API class + +package org.mozilla.javascript; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.io.CharArrayWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.Reader; +import java.io.StringWriter; +import java.io.Writer; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Hashtable; +import java.util.Locale; + +import org.mozilla.javascript.debug.DebuggableScript; +import org.mozilla.javascript.debug.Debugger; +import org.mozilla.javascript.xml.XMLLib; + +/** + * This class represents the runtime context of an executing script. + * + * Before executing a script, an instance of Context must be created + * and associated with the thread that will be executing the script. + * The Context will be used to store information about the executing + * of the script such as the call stack. Contexts are associated with + * the current thread using the {@link #call(ContextAction)} + * or {@link #enter()} methods.

    + * + * Different forms of script execution are supported. Scripts may be + * evaluated from the source directly, or first compiled and then later + * executed. Interactive execution is also supported.

    + * + * Some aspects of script execution, such as type conversions and + * object creation, may be accessed directly through methods of + * Context. + * + * @see Scriptable + * @author Norris Boyd + * @author Brendan Eich + */ + +public class Context +{ + /** + * Language versions. + * + * All integral values are reserved for future version numbers. + */ + + /** + * The unknown version. + */ + public static final int VERSION_UNKNOWN = -1; + + /** + * The default version. + */ + public static final int VERSION_DEFAULT = 0; + + /** + * JavaScript 1.0 + */ + public static final int VERSION_1_0 = 100; + + /** + * JavaScript 1.1 + */ + public static final int VERSION_1_1 = 110; + + /** + * JavaScript 1.2 + */ + public static final int VERSION_1_2 = 120; + + /** + * JavaScript 1.3 + */ + public static final int VERSION_1_3 = 130; + + /** + * JavaScript 1.4 + */ + public static final int VERSION_1_4 = 140; + + /** + * JavaScript 1.5 + */ + public static final int VERSION_1_5 = 150; + + /** + * JavaScript 1.6 + */ + public static final int VERSION_1_6 = 160; + + /** + * JavaScript 1.7 + */ + public static final int VERSION_1_7 = 170; + + /** + * Controls behaviour of Date.prototype.getYear(). + * If hasFeature(FEATURE_NON_ECMA_GET_YEAR) returns true, + * Date.prototype.getYear subtructs 1900 only if 1900 <= date < 2000. + * The default behavior of {@link #hasFeature(int)} is always to subtruct + * 1900 as rquired by ECMAScript B.2.4. + */ + public static final int FEATURE_NON_ECMA_GET_YEAR = 1; + + /** + * Control if member expression as function name extension is available. + * If hasFeature(FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME) returns + * true, allow function memberExpression(args) { body } to be + * syntax sugar for memberExpression = function(args) { body }, + * when memberExpression is not a simple identifier. + * See ECMAScript-262, section 11.2 for definition of memberExpression. + * By default {@link #hasFeature(int)} returns false. + */ + public static final int FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME = 2; + + /** + * Control if reserved keywords are treated as identifiers. + * If hasFeature(RESERVED_KEYWORD_AS_IDENTIFIER) returns true, + * treat future reserved keyword (see Ecma-262, section 7.5.3) as ordinary + * identifiers but warn about this usage. + * + * By default {@link #hasFeature(int)} returns false. + */ + public static final int FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER = 3; + + /** + * Control if toString() should returns the same result + * as toSource() when applied to objects and arrays. + * If hasFeature(FEATURE_TO_STRING_AS_SOURCE) returns true, + * calling toString() on JS objects gives the same result as + * calling toSource(). That is it returns JS source with code + * to create an object with all enumeratable fields of the original object + * instead of printing [object result of + * {@link Scriptable#getClassName()}]. + *

    + * By default {@link #hasFeature(int)} returns true only if + * the current JS version is set to {@link #VERSION_1_2}. + */ + public static final int FEATURE_TO_STRING_AS_SOURCE = 4; + + /** + * Control if properties __proto__ and __parent__ + * are treated specially. + * If hasFeature(FEATURE_PARENT_PROTO_PROPERTIES) returns true, + * treat __parent__ and __proto__ as special properties. + *

    + * The properties allow to query and set scope and prototype chains for the + * objects. The special meaning of the properties is available + * only when they are used as the right hand side of the dot operator. + * For example, while x.__proto__ = y changes the prototype + * chain of the object x to point to y, + * x["__proto__"] = y simply assigns a new value to the property + * __proto__ in x even when the feature is on. + * + * By default {@link #hasFeature(int)} returns true. + */ + public static final int FEATURE_PARENT_PROTO_PROPERTIES = 5; + + /** + * @deprecated In previous releases, this name was given to + * FEATURE_PARENT_PROTO_PROPERTIES. + */ + public static final int FEATURE_PARENT_PROTO_PROPRTIES = 5; + + /** + * Control if support for E4X(ECMAScript for XML) extension is available. + * If hasFeature(FEATURE_E4X) returns true, the XML syntax is available. + *

    + * By default {@link #hasFeature(int)} returns true if + * the current JS version is set to {@link #VERSION_DEFAULT} + * or is at least {@link #VERSION_1_6}. + * @since 1.6 Release 1 + */ + public static final int FEATURE_E4X = 6; + + /** + * Control if dynamic scope should be used for name access. + * If hasFeature(FEATURE_DYNAMIC_SCOPE) returns true, then the name lookup + * during name resolution will use the top scope of the script or function + * which is at the top of JS execution stack instead of the top scope of the + * script or function from the current stack frame if the top scope of + * the top stack frame contains the top scope of the current stack frame + * on its prototype chain. + *

    + * This is useful to define shared scope containing functions that can + * be called from scripts and functions using private scopes. + *

    + * By default {@link #hasFeature(int)} returns false. + * @since 1.6 Release 1 + */ + public static final int FEATURE_DYNAMIC_SCOPE = 7; + + /** + * Control if strict variable mode is enabled. + * When the feature is on Rhino reports runtime errors if assignment + * to a global variable that does not exist is executed. When the feature + * is off such assignments creates new variable in the global scope as + * required by ECMA 262. + *

    + * By default {@link #hasFeature(int)} returns false. + * @since 1.6 Release 1 + */ + public static final int FEATURE_STRICT_VARS = 8; + + /** + * Control if strict eval mode is enabled. + * When the feature is on Rhino reports runtime errors if non-string + * argument is passed to the eval function. When the feature is off + * eval simply return non-string argument as is without performing any + * evaluation as required by ECMA 262. + *

    + * By default {@link #hasFeature(int)} returns false. + * @since 1.6 Release 1 + */ + public static final int FEATURE_STRICT_EVAL = 9; + + /** + * When the feature is on Rhino will add a "fileName" and "lineNumber" + * properties to Error objects automatically. When the feature is off, you + * have to explicitly pass them as the second and third argument to the + * Error constructor. Note that neither behaviour is fully ECMA 262 + * compliant (as 262 doesn't specify a three-arg constructor), but keeping + * the feature off results in Error objects that don't have + * additional non-ECMA properties when constructed using the ECMA-defined + * single-arg constructor and is thus desirable if a stricter ECMA + * compliance is desired, specifically adherence to the point 15.11.5. of + * the standard. + *

    + * By default {@link #hasFeature(int)} returns false. + * @since 1.6 Release 6 + */ + public static final int FEATURE_LOCATION_INFORMATION_IN_ERROR = 10; + + /** + * Controls whether JS 1.5 'strict mode' is enabled. + * When the feature is on, Rhino reports more than a dozen different + * warnings. When the feature is off, these warnings are not generated. + * FEATURE_STRICT_MODE implies FEATURE_STRICT_VARS and FEATURE_STRICT_EVAL. + *

    + * By default {@link #hasFeature(int)} returns false. + * @since 1.6 Release 6 + */ + public static final int FEATURE_STRICT_MODE = 11; + + /** + * Controls whether a warning should be treated as an error. + * @since 1.6 Release 6 + */ + public static final int FEATURE_WARNING_AS_ERROR = 12; + + /** + * Enables enhanced access to Java. + * Specifically, controls whether private and protected members can be + * accessed, and whether scripts can catch all Java exceptions. + *

    + * Note that this feature should only be enabled for trusted scripts. + *

    + * By default {@link #hasFeature(int)} returns false. + * @since 1.7 Release 1 + */ + public static final int FEATURE_ENHANCED_JAVA_ACCESS = 13; + + + public static final String languageVersionProperty = "language version"; + public static final String errorReporterProperty = "error reporter"; + + /** + * Convenient value to use as zero-length array of objects. + */ + public static final Object[] emptyArgs = ScriptRuntime.emptyArgs; + + /** + * Create a new Context. + * + * Note that the Context must be associated with a thread before + * it can be used to execute a script. + * @deprecated use {@link ContextFactory#enter()} or + * {@link ContextFactory#call(ContextAction)} instead. + */ + public Context() + { + this(ContextFactory.getGlobal()); + } + + Context(ContextFactory factory) + { + assert factory != null; + this.factory = factory; + setLanguageVersion(VERSION_DEFAULT); + optimizationLevel = codegenClass != null ? 0 : -1; + maximumInterpreterStackDepth = Integer.MAX_VALUE; + } + + /** + * Get the current Context. + * + * The current Context is per-thread; this method looks up + * the Context associated with the current thread.

    + * + * @return the Context associated with the current thread, or + * null if no context is associated with the current + * thread. + * @see ContextFactory#enterContext() + * @see ContextFactory#call(ContextAction) + */ + public static Context getCurrentContext() + { + Object helper = VMBridge.instance.getThreadContextHelper(); + return VMBridge.instance.getContext(helper); + } + + /** + * Same as calling {@link ContextFactory#enterContext()} on the global + * ContextFactory instance. + * @deprecated use {@link ContextFactory#enter()} or + * {@link ContextFactory#call(ContextAction)} instead as this method relies + * on usage of a static singleton "global" ContextFactory. + * @return a Context associated with the current thread + * @see #getCurrentContext() + * @see #exit() + * @see #call(ContextAction) + */ + public static Context enter() + { + return enter(null); + } + + /** + * Get a Context associated with the current thread, using + * the given Context if need be. + *

    + * The same as enter() except that cx + * is associated with the current thread and returned if + * the current thread has no associated context and cx + * is not associated with any other thread. + * @param cx a Context to associate with the thread if possible + * @return a Context associated with the current thread + * @deprecated use {@link ContextFactory#enterContext(Context)} instead as + * this method relies on usage of a static singleton "global" ContextFactory. + * @see ContextFactory#enterContext(Context) + * @see ContextFactory#call(ContextAction) + */ + public static Context enter(Context cx) + { + return enter(cx, ContextFactory.getGlobal()); + } + + static final Context enter(Context cx, ContextFactory factory) + { + Object helper = VMBridge.instance.getThreadContextHelper(); + Context old = VMBridge.instance.getContext(helper); + if (old != null) { + cx = old; + } else { + if (cx == null) { + cx = factory.makeContext(); + if (cx.enterCount != 0) { + throw new IllegalStateException("factory.makeContext() returned Context instance already associated with some thread"); + } + factory.onContextCreated(cx); + if (factory.isSealed() && !cx.isSealed()) { + cx.seal(null); + } + } else { + if (cx.enterCount != 0) { + throw new IllegalStateException("can not use Context instance already associated with some thread"); + } + } + VMBridge.instance.setContext(helper, cx); + } + ++cx.enterCount; + return cx; + } + + /** + * Exit a block of code requiring a Context. + * + * Calling exit() will remove the association between + * the current thread and a Context if the prior call to + * {@link ContextFactory#enterContext()} on this thread newly associated a + * Context with this thread. Once the current thread no longer has an + * associated Context, it cannot be used to execute JavaScript until it is + * again associated with a Context. + * @see ContextFactory#enterContext() + */ + public static void exit() + { + Object helper = VMBridge.instance.getThreadContextHelper(); + Context cx = VMBridge.instance.getContext(helper); + if (cx == null) { + throw new IllegalStateException( + "Calling Context.exit without previous Context.enter"); + } + if (cx.enterCount < 1) Kit.codeBug(); + if (--cx.enterCount == 0) { + VMBridge.instance.setContext(helper, null); + cx.factory.onContextReleased(cx); + } + } + + /** + * Call {@link ContextAction#run(Context cx)} + * using the Context instance associated with the current thread. + * If no Context is associated with the thread, then + * ContextFactory.getGlobal().makeContext() will be called to + * construct new Context instance. The instance will be temporary + * associated with the thread during call to + * {@link ContextAction#run(Context)}. + * @deprecated use {@link ContextFactory#call(ContextAction)} instead as + * this method relies on usage of a static singleton "global" + * ContextFactory. + * @return The result of {@link ContextAction#run(Context)}. + */ + public static Object call(ContextAction action) + { + return call(ContextFactory.getGlobal(), action); + } + + /** + * Call {@link + * Callable#call(Context cx, Scriptable scope, Scriptable thisObj, + * Object[] args)} + * using the Context instance associated with the current thread. + * If no Context is associated with the thread, then + * {@link ContextFactory#makeContext()} will be called to construct + * new Context instance. The instance will be temporary associated + * with the thread during call to {@link ContextAction#run(Context)}. + *

    + * It is allowed but not advisable to use null for factory + * argument in which case the global static singleton ContextFactory + * instance will be used to create new context instances. + * @see ContextFactory#call(ContextAction) + */ + public static Object call(ContextFactory factory, final Callable callable, + final Scriptable scope, final Scriptable thisObj, + final Object[] args) + { + if(factory == null) { + factory = ContextFactory.getGlobal(); + } + return call(factory, new ContextAction() { + public Object run(Context cx) { + return callable.call(cx, scope, thisObj, args); + } + }); + } + + /** + * The method implements {@links ContextFactory#call(ContextAction)} logic. + */ + static Object call(ContextFactory factory, ContextAction action) { + Context cx = enter(null, factory); + try { + return action.run(cx); + } + finally { + exit(); + } + } + + /** + * @deprecated + * @see ContextFactory#addListener(ContextFactory.Listener) + * @see ContextFactory#getGlobal() + */ + public static void addContextListener(ContextListener listener) + { + // Special workaround for the debugger + String DBG = "org.mozilla.javascript.tools.debugger.Main"; + if (DBG.equals(listener.getClass().getName())) { + Class cl = listener.getClass(); + Class factoryClass = Kit.classOrNull( + "org.mozilla.javascript.ContextFactory"); + Class[] sig = { factoryClass }; + Object[] args = { ContextFactory.getGlobal() }; + try { + Method m = cl.getMethod("attachTo", sig); + m.invoke(listener, args); + } catch (Exception ex) { + RuntimeException rex = new RuntimeException(); + Kit.initCause(rex, ex); + throw rex; + } + return; + } + + ContextFactory.getGlobal().addListener(listener); + } + + /** + * @deprecated + * @see ContextFactory#removeListener(ContextFactory.Listener) + * @see ContextFactory#getGlobal() + */ + public static void removeContextListener(ContextListener listener) + { + ContextFactory.getGlobal().addListener(listener); + } + + /** + * Return {@link ContextFactory} instance used to create this Context. + */ + public final ContextFactory getFactory() + { + return factory; + } + + /** + * Checks if this is a sealed Context. A sealed Context instance does not + * allow to modify any of its properties and will throw an exception + * on any such attempt. + * @see #seal(Object sealKey) + */ + public final boolean isSealed() + { + return sealed; + } + + /** + * Seal this Context object so any attempt to modify any of its properties + * including calling {@link #enter()} and {@link #exit()} methods will + * throw an exception. + *

    + * If sealKey is not null, calling + * {@link #unseal(Object sealKey)} with the same key unseals + * the object. If sealKey is null, unsealing is no longer possible. + * + * @see #isSealed() + * @see #unseal(Object) + */ + public final void seal(Object sealKey) + { + if (sealed) onSealedMutation(); + sealed = true; + this.sealKey = sealKey; + } + + /** + * Unseal previously sealed Context object. + * The sealKey argument should not be null and should match + * sealKey suplied with the last call to + * {@link #seal(Object)} or an exception will be thrown. + * + * @see #isSealed() + * @see #seal(Object sealKey) + */ + public final void unseal(Object sealKey) + { + if (sealKey == null) throw new IllegalArgumentException(); + if (this.sealKey != sealKey) throw new IllegalArgumentException(); + if (!sealed) throw new IllegalStateException(); + sealed = false; + this.sealKey = null; + } + + static void onSealedMutation() + { + throw new IllegalStateException(); + } + + /** + * Get the current language version. + *

    + * The language version number affects JavaScript semantics as detailed + * in the overview documentation. + * + * @return an integer that is one of VERSION_1_0, VERSION_1_1, etc. + */ + public final int getLanguageVersion() + { + return version; + } + + /** + * Set the language version. + * + *

    + * Setting the language version will affect functions and scripts compiled + * subsequently. See the overview documentation for version-specific + * behavior. + * + * @param version the version as specified by VERSION_1_0, VERSION_1_1, etc. + */ + public void setLanguageVersion(int version) + { + if (sealed) onSealedMutation(); + checkLanguageVersion(version); + Object listeners = propertyListeners; + if (listeners != null && version != this.version) { + firePropertyChangeImpl(listeners, languageVersionProperty, + new Integer(this.version), + new Integer(version)); + } + this.version = version; + } + + public static boolean isValidLanguageVersion(int version) + { + switch (version) { + case VERSION_DEFAULT: + case VERSION_1_0: + case VERSION_1_1: + case VERSION_1_2: + case VERSION_1_3: + case VERSION_1_4: + case VERSION_1_5: + case VERSION_1_6: + case VERSION_1_7: + return true; + } + return false; + } + + public static void checkLanguageVersion(int version) + { + if (isValidLanguageVersion(version)) { + return; + } + throw new IllegalArgumentException("Bad language version: "+version); + } + + /** + * Get the implementation version. + * + *

    + * The implementation version is of the form + *

    +     *    "name langVer release relNum date"
    +     * 
    + * where name is the name of the product, langVer is + * the language version, relNum is the release number, and + * date is the release date for that specific + * release in the form "yyyy mm dd". + * + * @return a string that encodes the product, language version, release + * number, and date. + */ + public final String getImplementationVersion() + { + // XXX Probably it would be better to embed this directly into source + // with special build preprocessing but that would require some ant + // tweaking and then replacing token in resource files was simpler + if (implementationVersion == null) { + implementationVersion + = ScriptRuntime.getMessage0("implementation.version"); + } + return implementationVersion; + } + + /** + * Get the current error reporter. + * + * @see org.mozilla.javascript.ErrorReporter + */ + public final ErrorReporter getErrorReporter() + { + if (errorReporter == null) { + return DefaultErrorReporter.instance; + } + return errorReporter; + } + + /** + * Change the current error reporter. + * + * @return the previous error reporter + * @see org.mozilla.javascript.ErrorReporter + */ + public final ErrorReporter setErrorReporter(ErrorReporter reporter) + { + if (sealed) onSealedMutation(); + if (reporter == null) throw new IllegalArgumentException(); + ErrorReporter old = getErrorReporter(); + if (reporter == old) { + return old; + } + Object listeners = propertyListeners; + if (listeners != null) { + firePropertyChangeImpl(listeners, errorReporterProperty, + old, reporter); + } + this.errorReporter = reporter; + return old; + } + + /** + * Get the current locale. Returns the default locale if none has + * been set. + * + * @see java.util.Locale + */ + + public final Locale getLocale() + { + if (locale == null) + locale = Locale.getDefault(); + return locale; + } + + /** + * Set the current locale. + * + * @see java.util.Locale + */ + public final Locale setLocale(Locale loc) + { + if (sealed) onSealedMutation(); + Locale result = locale; + locale = loc; + return result; + } + + /** + * Register an object to receive notifications when a bound property + * has changed + * @see java.beans.PropertyChangeEvent + * @see #removePropertyChangeListener(java.beans.PropertyChangeListener) + * @param l the listener + */ + public final void addPropertyChangeListener(PropertyChangeListener l) + { + if (sealed) onSealedMutation(); + propertyListeners = Kit.addListener(propertyListeners, l); + } + + /** + * Remove an object from the list of objects registered to receive + * notification of changes to a bounded property + * @see java.beans.PropertyChangeEvent + * @see #addPropertyChangeListener(java.beans.PropertyChangeListener) + * @param l the listener + */ + public final void removePropertyChangeListener(PropertyChangeListener l) + { + if (sealed) onSealedMutation(); + propertyListeners = Kit.removeListener(propertyListeners, l); + } + + /** + * Notify any registered listeners that a bounded property has changed + * @see #addPropertyChangeListener(java.beans.PropertyChangeListener) + * @see #removePropertyChangeListener(java.beans.PropertyChangeListener) + * @see java.beans.PropertyChangeListener + * @see java.beans.PropertyChangeEvent + * @param property the bound property + * @param oldValue the old value + * @param newValue the new value + */ + final void firePropertyChange(String property, Object oldValue, + Object newValue) + { + Object listeners = propertyListeners; + if (listeners != null) { + firePropertyChangeImpl(listeners, property, oldValue, newValue); + } + } + + private void firePropertyChangeImpl(Object listeners, String property, + Object oldValue, Object newValue) + { + for (int i = 0; ; ++i) { + Object l = Kit.getListener(listeners, i); + if (l == null) + break; + if (l instanceof PropertyChangeListener) { + PropertyChangeListener pcl = (PropertyChangeListener)l; + pcl.propertyChange(new PropertyChangeEvent( + this, property, oldValue, newValue)); + } + } + } + + /** + * Report a warning using the error reporter for the current thread. + * + * @param message the warning message to report + * @param sourceName a string describing the source, such as a filename + * @param lineno the starting line number + * @param lineSource the text of the line (may be null) + * @param lineOffset the offset into lineSource where problem was detected + * @see org.mozilla.javascript.ErrorReporter + */ + public static void reportWarning(String message, String sourceName, + int lineno, String lineSource, + int lineOffset) + { + Context cx = Context.getContext(); + if (cx.hasFeature(FEATURE_WARNING_AS_ERROR)) + reportError(message, sourceName, lineno, lineSource, lineOffset); + else + cx.getErrorReporter().warning(message, sourceName, lineno, + lineSource, lineOffset); + } + + /** + * Report a warning using the error reporter for the current thread. + * + * @param message the warning message to report + * @see org.mozilla.javascript.ErrorReporter + */ + public static void reportWarning(String message) + { + int[] linep = { 0 }; + String filename = getSourcePositionFromStack(linep); + Context.reportWarning(message, filename, linep[0], null, 0); + } + + public static void reportWarning(String message, Throwable t) + { + int[] linep = { 0 }; + String filename = getSourcePositionFromStack(linep); + Writer sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + pw.println(message); + t.printStackTrace(pw); + pw.flush(); + Context.reportWarning(sw.toString(), filename, linep[0], null, 0); + } + + /** + * Report an error using the error reporter for the current thread. + * + * @param message the error message to report + * @param sourceName a string describing the source, such as a filename + * @param lineno the starting line number + * @param lineSource the text of the line (may be null) + * @param lineOffset the offset into lineSource where problem was detected + * @see org.mozilla.javascript.ErrorReporter + */ + public static void reportError(String message, String sourceName, + int lineno, String lineSource, + int lineOffset) + { + Context cx = getCurrentContext(); + if (cx != null) { + cx.getErrorReporter().error(message, sourceName, lineno, + lineSource, lineOffset); + } else { + throw new EvaluatorException(message, sourceName, lineno, + lineSource, lineOffset); + } + } + + /** + * Report an error using the error reporter for the current thread. + * + * @param message the error message to report + * @see org.mozilla.javascript.ErrorReporter + */ + public static void reportError(String message) + { + int[] linep = { 0 }; + String filename = getSourcePositionFromStack(linep); + Context.reportError(message, filename, linep[0], null, 0); + } + + /** + * Report a runtime error using the error reporter for the current thread. + * + * @param message the error message to report + * @param sourceName a string describing the source, such as a filename + * @param lineno the starting line number + * @param lineSource the text of the line (may be null) + * @param lineOffset the offset into lineSource where problem was detected + * @return a runtime exception that will be thrown to terminate the + * execution of the script + * @see org.mozilla.javascript.ErrorReporter + */ + public static EvaluatorException reportRuntimeError(String message, + String sourceName, + int lineno, + String lineSource, + int lineOffset) + { + Context cx = getCurrentContext(); + if (cx != null) { + return cx.getErrorReporter(). + runtimeError(message, sourceName, lineno, + lineSource, lineOffset); + } else { + throw new EvaluatorException(message, sourceName, lineno, + lineSource, lineOffset); + } + } + + static EvaluatorException reportRuntimeError0(String messageId) + { + String msg = ScriptRuntime.getMessage0(messageId); + return reportRuntimeError(msg); + } + + static EvaluatorException reportRuntimeError1(String messageId, + Object arg1) + { + String msg = ScriptRuntime.getMessage1(messageId, arg1); + return reportRuntimeError(msg); + } + + static EvaluatorException reportRuntimeError2(String messageId, + Object arg1, Object arg2) + { + String msg = ScriptRuntime.getMessage2(messageId, arg1, arg2); + return reportRuntimeError(msg); + } + + static EvaluatorException reportRuntimeError3(String messageId, + Object arg1, Object arg2, + Object arg3) + { + String msg = ScriptRuntime.getMessage3(messageId, arg1, arg2, arg3); + return reportRuntimeError(msg); + } + + static EvaluatorException reportRuntimeError4(String messageId, + Object arg1, Object arg2, + Object arg3, Object arg4) + { + String msg + = ScriptRuntime.getMessage4(messageId, arg1, arg2, arg3, arg4); + return reportRuntimeError(msg); + } + + /** + * Report a runtime error using the error reporter for the current thread. + * + * @param message the error message to report + * @see org.mozilla.javascript.ErrorReporter + */ + public static EvaluatorException reportRuntimeError(String message) + { + int[] linep = { 0 }; + String filename = getSourcePositionFromStack(linep); + return Context.reportRuntimeError(message, filename, linep[0], null, 0); + } + + /** + * Initialize the standard objects. + * + * Creates instances of the standard objects and their constructors + * (Object, String, Number, Date, etc.), setting up 'scope' to act + * as a global object as in ECMA 15.1.

    + * + * This method must be called to initialize a scope before scripts + * can be evaluated in that scope.

    + * + * This method does not affect the Context it is called upon. + * + * @return the initialized scope + */ + public final ScriptableObject initStandardObjects() + { + return initStandardObjects(null, false); + } + + /** + * Initialize the standard objects. + * + * Creates instances of the standard objects and their constructors + * (Object, String, Number, Date, etc.), setting up 'scope' to act + * as a global object as in ECMA 15.1.

    + * + * This method must be called to initialize a scope before scripts + * can be evaluated in that scope.

    + * + * This method does not affect the Context it is called upon. + * + * @param scope the scope to initialize, or null, in which case a new + * object will be created to serve as the scope + * @return the initialized scope. The method returns the value of the scope + * argument if it is not null or newly allocated scope object which + * is an instance {@link ScriptableObject}. + */ + public final Scriptable initStandardObjects(ScriptableObject scope) + { + return initStandardObjects(scope, false); + } + + /** + * Initialize the standard objects. + * + * Creates instances of the standard objects and their constructors + * (Object, String, Number, Date, etc.), setting up 'scope' to act + * as a global object as in ECMA 15.1.

    + * + * This method must be called to initialize a scope before scripts + * can be evaluated in that scope.

    + * + * This method does not affect the Context it is called upon.

    + * + * This form of the method also allows for creating "sealed" standard + * objects. An object that is sealed cannot have properties added, changed, + * or removed. This is useful to create a "superglobal" that can be shared + * among several top-level objects. Note that sealing is not allowed in + * the current ECMA/ISO language specification, but is likely for + * the next version. + * + * @param scope the scope to initialize, or null, in which case a new + * object will be created to serve as the scope + * @param sealed whether or not to create sealed standard objects that + * cannot be modified. + * @return the initialized scope. The method returns the value of the scope + * argument if it is not null or newly allocated scope object. + * @since 1.4R3 + */ + public ScriptableObject initStandardObjects(ScriptableObject scope, + boolean sealed) + { + return ScriptRuntime.initStandardObjects(this, scope, sealed); + } + + /** + * Get the singleton object that represents the JavaScript Undefined value. + */ + public static Object getUndefinedValue() + { + return Undefined.instance; + } + + /** + * Evaluate a JavaScript source string. + * + * The provided source name and line number are used for error messages + * and for producing debug information. + * + * @param scope the scope to execute in + * @param source the JavaScript source + * @param sourceName a string describing the source, such as a filename + * @param lineno the starting line number + * @param securityDomain an arbitrary object that specifies security + * information about the origin or owner of the script. For + * implementations that don't care about security, this value + * may be null. + * @return the result of evaluating the string + * @see org.mozilla.javascript.SecurityController + */ + public final Object evaluateString(Scriptable scope, String source, + String sourceName, int lineno, + Object securityDomain) + { + Script script = compileString(source, sourceName, lineno, + securityDomain); + if (script != null) { + return script.exec(this, scope); + } else { + return null; + } + } + + /** + * Evaluate a reader as JavaScript source. + * + * All characters of the reader are consumed. + * + * @param scope the scope to execute in + * @param in the Reader to get JavaScript source from + * @param sourceName a string describing the source, such as a filename + * @param lineno the starting line number + * @param securityDomain an arbitrary object that specifies security + * information about the origin or owner of the script. For + * implementations that don't care about security, this value + * may be null. + * @return the result of evaluating the source + * + * @exception IOException if an IOException was generated by the Reader + */ + public final Object evaluateReader(Scriptable scope, Reader in, + String sourceName, int lineno, + Object securityDomain) + throws IOException + { + Script script = compileReader(scope, in, sourceName, lineno, + securityDomain); + if (script != null) { + return script.exec(this, scope); + } else { + return null; + } + } + + /** + * Check whether a string is ready to be compiled. + *

    + * stringIsCompilableUnit is intended to support interactive compilation of + * javascript. If compiling the string would result in an error + * that might be fixed by appending more source, this method + * returns false. In every other case, it returns true. + *

    + * Interactive shells may accumulate source lines, using this + * method after each new line is appended to check whether the + * statement being entered is complete. + * + * @param source the source buffer to check + * @return whether the source is ready for compilation + * @since 1.4 Release 2 + */ + public final boolean stringIsCompilableUnit(String source) + { + boolean errorseen = false; + CompilerEnvirons compilerEnv = new CompilerEnvirons(); + compilerEnv.initFromContext(this); + // no source name or source text manager, because we're just + // going to throw away the result. + compilerEnv.setGeneratingSource(false); + /*APPJET*/ + Parser p = InformativeParser.makeParser(compilerEnv, + DefaultErrorReporter.instance); + try { + p.parse(source, null, 1); + } catch (EvaluatorException ee) { + errorseen = true; + } + // Return false only if an error occurred as a result of reading past + // the end of the file, i.e. if the source could be fixed by + // appending more source. + if (errorseen && p.eof()) + return false; + else + return true; + } + + /** + * @deprecated + * @see #compileReader(Reader in, String sourceName, int lineno, + * Object securityDomain) + */ + public final Script compileReader(Scriptable scope, Reader in, + String sourceName, int lineno, + Object securityDomain) + throws IOException + { + return compileReader(in, sourceName, lineno, securityDomain); + } + + /** + * Compiles the source in the given reader. + *

    + * Returns a script that may later be executed. + * Will consume all the source in the reader. + * + * @param in the input reader + * @param sourceName a string describing the source, such as a filename + * @param lineno the starting line number for reporting errors + * @param securityDomain an arbitrary object that specifies security + * information about the origin or owner of the script. For + * implementations that don't care about security, this value + * may be null. + * @return a script that may later be executed + * @exception IOException if an IOException was generated by the Reader + * @see org.mozilla.javascript.Script + */ + public final Script compileReader(Reader in, String sourceName, + int lineno, Object securityDomain) + throws IOException + { + if (lineno < 0) { + // For compatibility IllegalArgumentException can not be thrown here + lineno = 0; + } + return (Script) compileImpl(null, in, null, sourceName, lineno, + securityDomain, false, null, null); + } + + /** + * Compiles the source in the given string. + *

    + * Returns a script that may later be executed. + * + * @param source the source string + * @param sourceName a string describing the source, such as a filename + * @param lineno the starting line number for reporting errors + * @param securityDomain an arbitrary object that specifies security + * information about the origin or owner of the script. For + * implementations that don't care about security, this value + * may be null. + * @return a script that may later be executed + * @see org.mozilla.javascript.Script + */ + public final Script compileString(String source, + String sourceName, int lineno, + Object securityDomain) + { + if (lineno < 0) { + // For compatibility IllegalArgumentException can not be thrown here + lineno = 0; + } + return compileString(source, null, null, sourceName, lineno, + securityDomain); + } + + final Script compileString(String source, + Evaluator compiler, + ErrorReporter compilationErrorReporter, + String sourceName, int lineno, + Object securityDomain) + { + try { + return (Script) compileImpl(null, null, source, sourceName, lineno, + securityDomain, false, + compiler, compilationErrorReporter); + } catch (IOException ex) { + // Should not happen when dealing with source as string + throw new RuntimeException(); + } + } + + /** + * Compile a JavaScript function. + *

    + * The function source must be a function definition as defined by + * ECMA (e.g., "function f(a) { return a; }"). + * + * @param scope the scope to compile relative to + * @param source the function definition source + * @param sourceName a string describing the source, such as a filename + * @param lineno the starting line number + * @param securityDomain an arbitrary object that specifies security + * information about the origin or owner of the script. For + * implementations that don't care about security, this value + * may be null. + * @return a Function that may later be called + * @see org.mozilla.javascript.Function + */ + public final Function compileFunction(Scriptable scope, String source, + String sourceName, int lineno, + Object securityDomain) + { + return compileFunction(scope, source, null, null, sourceName, lineno, + securityDomain); + } + + final Function compileFunction(Scriptable scope, String source, + Evaluator compiler, + ErrorReporter compilationErrorReporter, + String sourceName, int lineno, + Object securityDomain) + { + try { + return (Function) compileImpl(scope, null, source, sourceName, + lineno, securityDomain, true, + compiler, compilationErrorReporter); + } + catch (IOException ioe) { + // Should never happen because we just made the reader + // from a String + throw new RuntimeException(); + } + } + + /** + * Decompile the script. + *

    + * The canonical source of the script is returned. + * + * @param script the script to decompile + * @param indent the number of spaces to indent the result + * @return a string representing the script source + */ + public final String decompileScript(Script script, int indent) + { + NativeFunction scriptImpl = (NativeFunction) script; + return scriptImpl.decompile(indent, 0); + } + + /** + * Decompile a JavaScript Function. + *

    + * Decompiles a previously compiled JavaScript function object to + * canonical source. + *

    + * Returns function body of '[native code]' if no decompilation + * information is available. + * + * @param fun the JavaScript function to decompile + * @param indent the number of spaces to indent the result + * @return a string representing the function source + */ + public final String decompileFunction(Function fun, int indent) + { + if (fun instanceof BaseFunction) + return ((BaseFunction)fun).decompile(indent, 0); + else + return "function " + fun.getClassName() + + "() {\n\t[native code]\n}\n"; + } + + /** + * Decompile the body of a JavaScript Function. + *

    + * Decompiles the body a previously compiled JavaScript Function + * object to canonical source, omitting the function header and + * trailing brace. + * + * Returns '[native code]' if no decompilation information is available. + * + * @param fun the JavaScript function to decompile + * @param indent the number of spaces to indent the result + * @return a string representing the function body source. + */ + public final String decompileFunctionBody(Function fun, int indent) + { + if (fun instanceof BaseFunction) { + BaseFunction bf = (BaseFunction)fun; + return bf.decompile(indent, Decompiler.ONLY_BODY_FLAG); + } + // ALERT: not sure what the right response here is. + return "[native code]\n"; + } + + /** + * Create a new JavaScript object. + * + * Equivalent to evaluating "new Object()". + * @param scope the scope to search for the constructor and to evaluate + * against + * @return the new object + */ + public final Scriptable newObject(Scriptable scope) + { + return newObject(scope, "Object", ScriptRuntime.emptyArgs); + } + + /** + * Create a new JavaScript object by executing the named constructor. + * + * The call newObject(scope, "Foo") is equivalent to + * evaluating "new Foo()". + * + * @param scope the scope to search for the constructor and to evaluate against + * @param constructorName the name of the constructor to call + * @return the new object + */ + public final Scriptable newObject(Scriptable scope, String constructorName) + { + return newObject(scope, constructorName, ScriptRuntime.emptyArgs); + } + + /** + * Creates a new JavaScript object by executing the named constructor. + * + * Searches scope for the named constructor, calls it with + * the given arguments, and returns the result.

    + * + * The code + *

    +     * Object[] args = { "a", "b" };
    +     * newObject(scope, "Foo", args)
    + * is equivalent to evaluating "new Foo('a', 'b')", assuming that the Foo + * constructor has been defined in scope. + * + * @param scope The scope to search for the constructor and to evaluate + * against + * @param constructorName the name of the constructor to call + * @param args the array of arguments for the constructor + * @return the new object + */ + public final Scriptable newObject(Scriptable scope, String constructorName, + Object[] args) + { + scope = ScriptableObject.getTopLevelScope(scope); + Function ctor = ScriptRuntime.getExistingCtor(this, scope, + constructorName); + if (args == null) { args = ScriptRuntime.emptyArgs; } + return ctor.construct(this, scope, args); + } + + /** + * Create an array with a specified initial length. + *

    + * @param scope the scope to create the object in + * @param length the initial length (JavaScript arrays may have + * additional properties added dynamically). + * @return the new array object + */ + public final Scriptable newArray(Scriptable scope, int length) + { + NativeArray result = new NativeArray(length); + ScriptRuntime.setObjectProtoAndParent(result, scope); + return result; + } + + /** + * Create an array with a set of initial elements. + * + * @param scope the scope to create the object in. + * @param elements the initial elements. Each object in this array + * must be an acceptable JavaScript type and type + * of array should be exactly Object[], not + * SomeObjectSubclass[]. + * @return the new array object. + */ + public final Scriptable newArray(Scriptable scope, Object[] elements) + { + if (elements.getClass().getComponentType() != ScriptRuntime.ObjectClass) + throw new IllegalArgumentException(); + NativeArray result = new NativeArray(elements); + ScriptRuntime.setObjectProtoAndParent(result, scope); + return result; + } + + /** + * Get the elements of a JavaScript array. + *

    + * If the object defines a length property convertible to double number, + * then the number is converted Uint32 value as defined in Ecma 9.6 + * and Java array of that size is allocated. + * The array is initialized with the values obtained by + * calling get() on object for each value of i in [0,length-1]. If + * there is not a defined value for a property the Undefined value + * is used to initialize the corresponding element in the array. The + * Java array is then returned. + * If the object doesn't define a length property or it is not a number, + * empty array is returned. + * @param object the JavaScript array or array-like object + * @return a Java array of objects + * @since 1.4 release 2 + */ + public final Object[] getElements(Scriptable object) + { + return ScriptRuntime.getArrayElements(object); + } + + /** + * Convert the value to a JavaScript boolean value. + *

    + * See ECMA 9.2. + * + * @param value a JavaScript value + * @return the corresponding boolean value converted using + * the ECMA rules + */ + public static boolean toBoolean(Object value) + { + return ScriptRuntime.toBoolean(value); + } + + /** + * Convert the value to a JavaScript Number value. + *

    + * Returns a Java double for the JavaScript Number. + *

    + * See ECMA 9.3. + * + * @param value a JavaScript value + * @return the corresponding double value converted using + * the ECMA rules + */ + public static double toNumber(Object value) + { + return ScriptRuntime.toNumber(value); + } + + /** + * Convert the value to a JavaScript String value. + *

    + * See ECMA 9.8. + *

    + * @param value a JavaScript value + * @return the corresponding String value converted using + * the ECMA rules + */ + public static String toString(Object value) + { + return ScriptRuntime.toString(value); + } + + /** + * Convert the value to an JavaScript object value. + *

    + * Note that a scope must be provided to look up the constructors + * for Number, Boolean, and String. + *

    + * See ECMA 9.9. + *

    + * Additionally, arbitrary Java objects and classes will be + * wrapped in a Scriptable object with its Java fields and methods + * reflected as JavaScript properties of the object. + * + * @param value any Java object + * @param scope global scope containing constructors for Number, + * Boolean, and String + * @return new JavaScript object + */ + public static Scriptable toObject(Object value, Scriptable scope) + { + return ScriptRuntime.toObject(scope, value); + } + + /** + * @deprecated + * @see #toObject(Object, Scriptable) + */ + public static Scriptable toObject(Object value, Scriptable scope, + Class staticType) + { + return ScriptRuntime.toObject(scope, value); + } + + /** + * Convenient method to convert java value to its closest representation + * in JavaScript. + *

    + * If value is an instance of String, Number, Boolean, Function or + * Scriptable, it is returned as it and will be treated as the corresponding + * JavaScript type of string, number, boolean, function and object. + *

    + * Note that for Number instances during any arithmetic operation in + * JavaScript the engine will always use the result of + * Number.doubleValue() resulting in a precision loss if + * the number can not fit into double. + *

    + * If value is an instance of Character, it will be converted to string of + * length 1 and its JavaScript type will be string. + *

    + * The rest of values will be wrapped as LiveConnect objects + * by calling {@link WrapFactory#wrap(Context cx, Scriptable scope, + * Object obj, Class staticType)} as in: + *

    +     *    Context cx = Context.getCurrentContext();
    +     *    return cx.getWrapFactory().wrap(cx, scope, value, null);
    +     * 
    + * + * @param value any Java object + * @param scope top scope object + * @return value suitable to pass to any API that takes JavaScript values. + */ + public static Object javaToJS(Object value, Scriptable scope) + { + if (value instanceof String || value instanceof Number + || value instanceof Boolean || value instanceof Scriptable) + { + return value; + } else if (value instanceof Character) { + return String.valueOf(((Character)value).charValue()); + } else { + Context cx = Context.getContext(); + return cx.getWrapFactory().wrap(cx, scope, value, null); + } + } + + /** + * Convert a JavaScript value into the desired type. + * Uses the semantics defined with LiveConnect3 and throws an + * Illegal argument exception if the conversion cannot be performed. + * @param value the JavaScript value to convert + * @param desiredType the Java type to convert to. Primitive Java + * types are represented using the TYPE fields in the corresponding + * wrapper class in java.lang. + * @return the converted value + * @throws EvaluatorException if the conversion cannot be performed + */ + public static Object jsToJava(Object value, Class desiredType) + throws EvaluatorException + { + return NativeJavaObject.coerceTypeImpl(desiredType, value); + } + + /** + * @deprecated + * @see #jsToJava(Object, Class) + * @throws IllegalArgumentException if the conversion cannot be performed. + * Note that {@link #jsToJava(Object, Class)} throws + * {@link EvaluatorException} instead. + */ + public static Object toType(Object value, Class desiredType) + throws IllegalArgumentException + { + try { + return jsToJava(value, desiredType); + } catch (EvaluatorException ex) { + IllegalArgumentException + ex2 = new IllegalArgumentException(ex.getMessage()); + Kit.initCause(ex2, ex); + throw ex2; + } + } + + /** + * Rethrow the exception wrapping it as the script runtime exception. + * Unless the exception is instance of {@link EcmaError} or + * {@link EvaluatorException} it will be wrapped as + * {@link WrappedException}, a subclass of {@link EvaluatorException}. + * The resulting exception object always contains + * source name and line number of script that triggered exception. + *

    + * This method always throws an exception, its return value is provided + * only for convenience to allow a usage like: + *

    +     * throw Context.throwAsScriptRuntimeEx(ex);
    +     * 
    + * to indicate that code after the method is unreachable. + * @throws EvaluatorException + * @throws EcmaError + */ + public static RuntimeException throwAsScriptRuntimeEx(Throwable e) + { + while ((e instanceof InvocationTargetException)) { + e = ((InvocationTargetException) e).getTargetException(); + } + // special handling of Error so scripts would not catch them + if (e instanceof Error) { + Context cx = getContext(); + if (cx == null || + !cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS)) + { + throw (Error)e; + } + } + if (e instanceof RhinoException) { + throw (RhinoException)e; + } + throw new WrappedException(e); + } + + /** + * Tell whether debug information is being generated. + * @since 1.3 + */ + public final boolean isGeneratingDebug() + { + return generatingDebug; + } + + /** + * Specify whether or not debug information should be generated. + *

    + * Setting the generation of debug information on will set the + * optimization level to zero. + * @since 1.3 + */ + public final void setGeneratingDebug(boolean generatingDebug) + { + if (sealed) onSealedMutation(); + generatingDebugChanged = true; + if (generatingDebug && getOptimizationLevel() > 0) + setOptimizationLevel(0); + this.generatingDebug = generatingDebug; + } + + /** + * Tell whether source information is being generated. + * @since 1.3 + */ + public final boolean isGeneratingSource() + { + return generatingSource; + } + + /** + * Specify whether or not source information should be generated. + *

    + * Without source information, evaluating the "toString" method + * on JavaScript functions produces only "[native code]" for + * the body of the function. + * Note that code generated without source is not fully ECMA + * conformant. + * @since 1.3 + */ + public final void setGeneratingSource(boolean generatingSource) + { + if (sealed) onSealedMutation(); + this.generatingSource = generatingSource; + } + + /** + * Get the current optimization level. + *

    + * The optimization level is expressed as an integer between -1 and + * 9. + * @since 1.3 + * + */ + public final int getOptimizationLevel() + { + return optimizationLevel; + } + + /** + * Set the current optimization level. + *

    + * The optimization level is expected to be an integer between -1 and + * 9. Any negative values will be interpreted as -1, and any values + * greater than 9 will be interpreted as 9. + * An optimization level of -1 indicates that interpretive mode will + * always be used. Levels 0 through 9 indicate that class files may + * be generated. Higher optimization levels trade off compile time + * performance for runtime performance. + * The optimizer level can't be set greater than -1 if the optimizer + * package doesn't exist at run time. + * @param optimizationLevel an integer indicating the level of + * optimization to perform + * @since 1.3 + * + */ + public final void setOptimizationLevel(int optimizationLevel) + { + if (sealed) onSealedMutation(); + if (optimizationLevel == -2) { + // To be compatible with Cocoon fork + optimizationLevel = -1; + } + checkOptimizationLevel(optimizationLevel); + if (codegenClass == null) + optimizationLevel = -1; + this.optimizationLevel = optimizationLevel; + } + + public static boolean isValidOptimizationLevel(int optimizationLevel) + { + return -1 <= optimizationLevel && optimizationLevel <= 9; + } + + public static void checkOptimizationLevel(int optimizationLevel) + { + if (isValidOptimizationLevel(optimizationLevel)) { + return; + } + throw new IllegalArgumentException( + "Optimization level outside [-1..9]: "+optimizationLevel); + } + + /** + * Returns the maximum stack depth (in terms of number of call frames) + * allowed in a single invocation of interpreter. If the set depth would be + * exceeded, the interpreter will throw an EvaluatorException in the script. + * Defaults to Integer.MAX_VALUE. The setting only has effect for + * interpreted functions (those compiled with optimization level set to -1). + * As the interpreter doesn't use the Java stack but rather manages its own + * stack in the heap memory, a runaway recursion in interpreted code would + * eventually consume all available memory and cause OutOfMemoryError + * instead of a StackOverflowError limited to only a single thread. This + * setting helps prevent such situations. + * + * @return The current maximum interpreter stack depth. + */ + public final int getMaximumInterpreterStackDepth() + { + return maximumInterpreterStackDepth; + } + + /** + * Sets the maximum stack depth (in terms of number of call frames) + * allowed in a single invocation of interpreter. If the set depth would be + * exceeded, the interpreter will throw an EvaluatorException in the script. + * Defaults to Integer.MAX_VALUE. The setting only has effect for + * interpreted functions (those compiled with optimization level set to -1). + * As the interpreter doesn't use the Java stack but rather manages its own + * stack in the heap memory, a runaway recursion in interpreted code would + * eventually consume all available memory and cause OutOfMemoryError + * instead of a StackOverflowError limited to only a single thread. This + * setting helps prevent such situations. + * + * @param max the new maximum interpreter stack depth + * @throws IllegalStateException if this context's optimization level is not + * -1 + * @throws IllegalArgumentException if the new depth is not at least 1 + */ + public final void setMaximumInterpreterStackDepth(int max) + { + if(sealed) onSealedMutation(); + if(optimizationLevel != -1) { + throw new IllegalStateException("Cannot set maximumInterpreterStackDepth when optimizationLevel != -1"); + } + if(max < 1) { + throw new IllegalArgumentException("Cannot set maximumInterpreterStackDepth to less than 1"); + } + maximumInterpreterStackDepth = max; + } + + /** + * Set the security controller for this context. + *

    SecurityController may only be set if it is currently null + * and {@link SecurityController#hasGlobal()} is false. + * Otherwise a SecurityException is thrown. + * @param controller a SecurityController object + * @throws SecurityException if there is already a SecurityController + * object for this Context or globally installed. + * @see SecurityController#initGlobal(SecurityController controller) + * @see SecurityController#hasGlobal() + */ + public final void setSecurityController(SecurityController controller) + { + if (sealed) onSealedMutation(); + if (controller == null) throw new IllegalArgumentException(); + if (securityController != null) { + throw new SecurityException("Can not overwrite existing SecurityController object"); + } + if (SecurityController.hasGlobal()) { + throw new SecurityException("Can not overwrite existing global SecurityController object"); + } + securityController = controller; + } + + /** + * Set the LiveConnect access filter for this context. + *

    {@link ClassShutter} may only be set if it is currently null. + * Otherwise a SecurityException is thrown. + * @param shutter a ClassShutter object + * @throws SecurityException if there is already a ClassShutter + * object for this Context + */ + public final void setClassShutter(ClassShutter shutter) + { + if (sealed) onSealedMutation(); + if (shutter == null) throw new IllegalArgumentException(); + if (classShutter != null) { + throw new SecurityException("Cannot overwrite existing " + + "ClassShutter object"); + } + classShutter = shutter; + } + + final ClassShutter getClassShutter() + { + return classShutter; + } + + /** + * Get a value corresponding to a key. + *

    + * Since the Context is associated with a thread it can be + * used to maintain values that can be later retrieved using + * the current thread. + *

    + * Note that the values are maintained with the Context, so + * if the Context is disassociated from the thread the values + * cannot be retrieved. Also, if private data is to be maintained + * in this manner the key should be a java.lang.Object + * whose reference is not divulged to untrusted code. + * @param key the key used to lookup the value + * @return a value previously stored using putThreadLocal. + */ + public final Object getThreadLocal(Object key) + { + if (hashtable == null) + return null; + return hashtable.get(key); + } + + /** + * Put a value that can later be retrieved using a given key. + *

    + * @param key the key used to index the value + * @param value the value to save + */ + public final void putThreadLocal(Object key, Object value) + { + if (sealed) onSealedMutation(); + if (hashtable == null) + hashtable = new Hashtable(); + hashtable.put(key, value); + } + + /** + * Remove values from thread-local storage. + * @param key the key for the entry to remove. + * @since 1.5 release 2 + */ + public final void removeThreadLocal(Object key) + { + if (sealed) onSealedMutation(); + if (hashtable == null) + return; + hashtable.remove(key); + } + + /** + * @deprecated + * @see #FEATURE_DYNAMIC_SCOPE + * @see #hasFeature(int) + */ + public final boolean hasCompileFunctionsWithDynamicScope() + { + return compileFunctionsWithDynamicScopeFlag; + } + + /** + * @deprecated + * @see #FEATURE_DYNAMIC_SCOPE + * @see #hasFeature(int) + */ + public final void setCompileFunctionsWithDynamicScope(boolean flag) + { + if (sealed) onSealedMutation(); + compileFunctionsWithDynamicScopeFlag = flag; + } + + /** + * @deprecated + * @see ClassCache#get(Scriptable) + * @see ClassCache#setCachingEnabled(boolean) + */ + public static void setCachingEnabled(boolean cachingEnabled) + { + } + + /** + * Set a WrapFactory for this Context. + *

    + * The WrapFactory allows custom object wrapping behavior for + * Java object manipulated with JavaScript. + * @see WrapFactory + * @since 1.5 Release 4 + */ + public final void setWrapFactory(WrapFactory wrapFactory) + { + if (sealed) onSealedMutation(); + if (wrapFactory == null) throw new IllegalArgumentException(); + this.wrapFactory = wrapFactory; + } + + /** + * Return the current WrapFactory, or null if none is defined. + * @see WrapFactory + * @since 1.5 Release 4 + */ + public final WrapFactory getWrapFactory() + { + if (wrapFactory == null) { + wrapFactory = new WrapFactory(); + } + return wrapFactory; + } + + /** + * Return the current debugger. + * @return the debugger, or null if none is attached. + */ + public final Debugger getDebugger() + { + return debugger; + } + + /** + * Return the debugger context data associated with current context. + * @return the debugger data, or null if debugger is not attached + */ + public final Object getDebuggerContextData() + { + return debuggerData; + } + + /** + * Set the associated debugger. + * @param debugger the debugger to be used on callbacks from + * the engine. + * @param contextData arbitrary object that debugger can use to store + * per Context data. + */ + public final void setDebugger(Debugger debugger, Object contextData) + { + if (sealed) onSealedMutation(); + this.debugger = debugger; + debuggerData = contextData; + } + + /** + * Return DebuggableScript instance if any associated with the script. + * If callable supports DebuggableScript implementation, the method + * returns it. Otherwise null is returned. + */ + public static DebuggableScript getDebuggableView(Script script) + { + if (script instanceof NativeFunction) { + return ((NativeFunction)script).getDebuggableView(); + } + return null; + } + + /** + * Controls certain aspects of script semantics. + * Should be overwritten to alter default behavior. + *

    + * The default implementation calls + * {@link ContextFactory#hasFeature(Context cx, int featureIndex)} + * that allows to customize Context behavior without introducing + * Context subclasses. {@link ContextFactory} documentation gives + * an example of hasFeature implementation. + * + * @param featureIndex feature index to check + * @return true if the featureIndex feature is turned on + * @see #FEATURE_NON_ECMA_GET_YEAR + * @see #FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME + * @see #FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER + * @see #FEATURE_TO_STRING_AS_SOURCE + * @see #FEATURE_PARENT_PROTO_PROPRTIES + * @see #FEATURE_E4X + * @see #FEATURE_DYNAMIC_SCOPE + * @see #FEATURE_STRICT_VARS + * @see #FEATURE_STRICT_EVAL + * @see #FEATURE_LOCATION_INFORMATION_IN_ERROR + * @see #FEATURE_STRICT_MODE + * @see #FEATURE_WARNING_AS_ERROR + * @see #FEATURE_ENHANCED_JAVA_ACCESS + */ + public boolean hasFeature(int featureIndex) + { + ContextFactory f = getFactory(); + return f.hasFeature(this, featureIndex); + } + + /** + Returns an object which specifies an E4X implementation to use within + this Context. Note + that the XMLLib.Factory interface should be considered experimental. + + The default implementation uses the implementation provided by this + Context's {@link ContextFactory}. + + @return An XMLLib.Factory. Should not return null if + {@link #FEATURE_E4X} is enabled. See {@link #hasFeature}. + */ + public XMLLib.Factory getE4xImplementationFactory() { + return getFactory().getE4xImplementationFactory(); + } + + /** + * Get threshold of executed instructions counter that triggers call to + * observeInstructionCount(). + * When the threshold is zero, instruction counting is disabled, + * otherwise each time the run-time executes at least the threshold value + * of script instructions, observeInstructionCount() will + * be called. + */ + public final int getInstructionObserverThreshold() + { + return instructionThreshold; + } + + /** + * Set threshold of executed instructions counter that triggers call to + * observeInstructionCount(). + * When the threshold is zero, instruction counting is disabled, + * otherwise each time the run-time executes at least the threshold value + * of script instructions, observeInstructionCount() will + * be called.

    + * Note that the meaning of "instruction" is not guaranteed to be + * consistent between compiled and interpretive modes: executing a given + * script or function in the different modes will result in different + * instruction counts against the threshold. + * {@link #setGenerateObserverCount} is called with true if + * threshold is greater than zero, false otherwise. + * @param threshold The instruction threshold + */ + public final void setInstructionObserverThreshold(int threshold) + { + if (sealed) onSealedMutation(); + if (threshold < 0) throw new IllegalArgumentException(); + instructionThreshold = threshold; + setGenerateObserverCount(threshold > 0); + } + + /** + * Turn on or off generation of code with callbacks to + * track the count of executed instructions. + * Currently only affects JVM byte code generation: this slows down the + * generated code, but code generated without the callbacks will not + * be counted toward instruction thresholds. Rhino's interpretive + * mode does instruction counting without inserting callbacks, so + * there is no requirement to compile code differently. + * @param generateObserverCount if true, generated code will contain + * calls to accumulate an estimate of the instructions executed. + */ + public void setGenerateObserverCount(boolean generateObserverCount) { + this.generateObserverCount = generateObserverCount; + } + + /** + * Allow application to monitor counter of executed script instructions + * in Context subclasses. + * Run-time calls this when instruction counting is enabled and the counter + * reaches limit set by setInstructionObserverThreshold(). + * The method is useful to observe long running scripts and if necessary + * to terminate them. + *

    + * The instruction counting support is available only for interpreted + * scripts generated when the optimization level is set to -1. + *

    + * The default implementation calls + * {@link ContextFactory#observeInstructionCount(Context cx, + * int instructionCount)} + * that allows to customize Context behavior without introducing + * Context subclasses. + * + * @param instructionCount amount of script instruction executed since + * last call to observeInstructionCount + * @throws Error to terminate the script + * @see #setOptimizationLevel(int) + */ + protected void observeInstructionCount(int instructionCount) + { + ContextFactory f = getFactory(); + f.observeInstructionCount(this, instructionCount); + } + + /** + * Create class loader for generated classes. + * The method calls {@link ContextFactory#createClassLoader(ClassLoader)} + * using the result of {@link #getFactory()}. + */ + public GeneratedClassLoader createClassLoader(ClassLoader parent) + { + ContextFactory f = getFactory(); + return f.createClassLoader(parent); + } + + public final ClassLoader getApplicationClassLoader() + { + if (applicationClassLoader == null) { + ContextFactory f = getFactory(); + ClassLoader loader = f.getApplicationClassLoader(); + if (loader == null) { + ClassLoader threadLoader + = VMBridge.instance.getCurrentThreadClassLoader(); + if (threadLoader != null + && Kit.testIfCanLoadRhinoClasses(threadLoader)) + { + // Thread.getContextClassLoader is not cached since + // its caching prevents it from GC which may lead to + // a memory leak and hides updates to + // Thread.getContextClassLoader + return threadLoader; + } + // Thread.getContextClassLoader can not load Rhino classes, + // try to use the loader of ContextFactory or Context + // subclasses. + Class fClass = f.getClass(); + if (fClass != ScriptRuntime.ContextFactoryClass) { + loader = fClass.getClassLoader(); + } else { + loader = getClass().getClassLoader(); + } + } + applicationClassLoader = loader; + } + return applicationClassLoader; + } + + public final void setApplicationClassLoader(ClassLoader loader) + { + if (sealed) onSealedMutation(); + if (loader == null) { + // restore default behaviour + applicationClassLoader = null; + return; + } + if (!Kit.testIfCanLoadRhinoClasses(loader)) { + throw new IllegalArgumentException( + "Loader can not resolve Rhino classes"); + } + applicationClassLoader = loader; + } + + /********** end of API **********/ + + /** + * Internal method that reports an error for missing calls to + * enter(). + */ + static Context getContext() + { + Context cx = getCurrentContext(); + if (cx == null) { + throw new RuntimeException( + "No Context associated with current Thread"); + } + return cx; + } + + private Object compileImpl(Scriptable scope, + Reader sourceReader, String sourceString, + String sourceName, int lineno, + Object securityDomain, boolean returnFunction, + Evaluator compiler, + ErrorReporter compilationErrorReporter) + throws IOException + { + if(sourceName == null) { + sourceName = "unnamed script"; + } + if (securityDomain != null && getSecurityController() == null) { + throw new IllegalArgumentException( + "securityDomain should be null if setSecurityController() was never called"); + } + + // One of sourceReader or sourceString has to be null + if (!(sourceReader == null ^ sourceString == null)) Kit.codeBug(); + // scope should be given if and only if compiling function + if (!(scope == null ^ returnFunction)) Kit.codeBug(); + + CompilerEnvirons compilerEnv = new CompilerEnvirons(); + compilerEnv.initFromContext(this); + if (compilationErrorReporter == null) { + compilationErrorReporter = compilerEnv.getErrorReporter(); + } + + if (debugger != null) { + if (sourceReader != null) { + sourceString = Kit.readReader(sourceReader); + sourceReader = null; + } + } + + /*APPJET*/ + Parser p = InformativeParser.makeParser(compilerEnv, + compilationErrorReporter); + if (returnFunction) { + p.calledByCompileFunction = true; + } + ScriptOrFnNode tree; + if (sourceString != null) { + tree = p.parse(sourceString, sourceName, lineno); + } else { + tree = p.parse(sourceReader, sourceName, lineno); + } + if (returnFunction) { + if (!(tree.getFunctionCount() == 1 + && tree.getFirstChild() != null + && tree.getFirstChild().getType() == Token.FUNCTION)) + { + // XXX: the check just look for the first child + // and allows for more nodes after it for compatibility + // with sources like function() {};;; + throw new IllegalArgumentException( + "compileFunction only accepts source with single JS function: "+sourceString); + } + } + + if (compiler == null) { + compiler = createCompiler(); + } + + String encodedSource = p.getEncodedSource(); + + Object bytecode = compiler.compile(compilerEnv, + tree, encodedSource, + returnFunction); + + if (debugger != null) { + if (sourceString == null) Kit.codeBug(); + if (bytecode instanceof DebuggableScript) { + DebuggableScript dscript = (DebuggableScript)bytecode; + notifyDebugger_r(this, dscript, sourceString); + } else { + throw new RuntimeException("NOT SUPPORTED"); + } + } + + Object result; + if (returnFunction) { + result = compiler.createFunctionObject(this, scope, bytecode, securityDomain); + } else { + result = compiler.createScriptObject(bytecode, securityDomain); + } + + return result; + } + + private static void notifyDebugger_r(Context cx, DebuggableScript dscript, + String debugSource) + { + cx.debugger.handleCompilationDone(cx, dscript, debugSource); + for (int i = 0; i != dscript.getFunctionCount(); ++i) { + notifyDebugger_r(cx, dscript.getFunction(i), debugSource); + } + } + + private static Class codegenClass = Kit.classOrNull( + "org.mozilla.javascript.optimizer.Codegen"); + private static Class interpreterClass = Kit.classOrNull( + "org.mozilla.javascript.Interpreter"); + + private Evaluator createCompiler() + { + Evaluator result = null; + if (optimizationLevel >= 0 && codegenClass != null) { + result = (Evaluator)Kit.newInstanceOrNull(codegenClass); + } + if (result == null) { + result = createInterpreter(); + } + return result; + } + + static Evaluator createInterpreter() + { + return (Evaluator)Kit.newInstanceOrNull(interpreterClass); + } + + static String getSourcePositionFromStack(int[] linep) + { + Context cx = getCurrentContext(); + if (cx == null) + return null; + if (cx.lastInterpreterFrame != null) { + Evaluator evaluator = createInterpreter(); + if (evaluator != null) + return evaluator.getSourcePositionFromStack(cx, linep); + } + /** + * A bit of a hack, but the only way to get filename and line + * number from an enclosing frame. + */ + CharArrayWriter writer = new CharArrayWriter(); + RuntimeException re = new RuntimeException(); + re.printStackTrace(new PrintWriter(writer)); + String s = writer.toString(); + int open = -1; + int close = -1; + int colon = -1; + for (int i=0; i < s.length(); i++) { + char c = s.charAt(i); + if (c == ':') + colon = i; + else if (c == '(') + open = i; + else if (c == ')') + close = i; + else if (c == '\n' && open != -1 && close != -1 && colon != -1 && + open < colon && colon < close) + { + String fileStr = s.substring(open + 1, colon); + if (!fileStr.endsWith(".java")) { + String lineStr = s.substring(colon + 1, close); + try { + linep[0] = Integer.parseInt(lineStr); + if (linep[0] < 0) { + linep[0] = 0; + } + return fileStr; + } + catch (NumberFormatException e) { + // fall through + } + } + open = close = colon = -1; + } + } + + return null; + } + + RegExpProxy getRegExpProxy() + { + if (regExpProxy == null) { + Class cl = Kit.classOrNull( + "org.mozilla.javascript.regexp.RegExpImpl"); + if (cl != null) { + regExpProxy = (RegExpProxy)Kit.newInstanceOrNull(cl); + } + } + return regExpProxy; + } + + final boolean isVersionECMA1() + { + return version == VERSION_DEFAULT || version >= VERSION_1_3; + } + +// The method must NOT be public or protected + SecurityController getSecurityController() + { + SecurityController global = SecurityController.global(); + if (global != null) { + return global; + } + return securityController; + } + + public final boolean isGeneratingDebugChanged() + { + return generatingDebugChanged; + } + + /** + * Add a name to the list of names forcing the creation of real + * activation objects for functions. + * + * @param name the name of the object to add to the list + */ + public void addActivationName(String name) + { + if (sealed) onSealedMutation(); + if (activationNames == null) + activationNames = new Hashtable(5); + activationNames.put(name, name); + } + + /** + * Check whether the name is in the list of names of objects + * forcing the creation of activation objects. + * + * @param name the name of the object to test + * + * @return true if an function activation object is needed. + */ + public final boolean isActivationNeeded(String name) + { + return activationNames != null && activationNames.containsKey(name); + } + + /** + * Remove a name from the list of names forcing the creation of real + * activation objects for functions. + * + * @param name the name of the object to remove from the list + */ + public void removeActivationName(String name) + { + if (sealed) onSealedMutation(); + if (activationNames != null) + activationNames.remove(name); + } + + private static String implementationVersion; + + private final ContextFactory factory; + private boolean sealed; + private Object sealKey; + + Scriptable topCallScope; + NativeCall currentActivationCall; + XMLLib cachedXMLLib; + + // for Objects, Arrays to tag themselves as being printed out, + // so they don't print themselves out recursively. + // Use ObjToIntMap instead of java.util.HashSet for JDK 1.1 compatibility + ObjToIntMap iterating; + + Object interpreterSecurityDomain; + + int version; + + private SecurityController securityController; + private ClassShutter classShutter; + private ErrorReporter errorReporter; + RegExpProxy regExpProxy; + private Locale locale; + private boolean generatingDebug; + private boolean generatingDebugChanged; + private boolean generatingSource=true; + boolean compileFunctionsWithDynamicScopeFlag; + boolean useDynamicScope; + private int optimizationLevel; + private int maximumInterpreterStackDepth; + private WrapFactory wrapFactory; + Debugger debugger; + private Object debuggerData; + private int enterCount; + private Object propertyListeners; + private Hashtable hashtable; + private ClassLoader applicationClassLoader; + + /** + * This is the list of names of objects forcing the creation of + * function activation records. + */ + Hashtable activationNames; + + // For the interpreter to store the last frame for error reports etc. + Object lastInterpreterFrame; + + // For the interpreter to store information about previous invocations + // interpreter invocations + ObjArray previousInterpreterInvocations; + + // For instruction counting (interpreter only) + int instructionCount; + int instructionThreshold; + + // It can be used to return the second index-like result from function + int scratchIndex; + + // It can be used to return the second uint32 result from function + long scratchUint32; + + // It can be used to return the second Scriptable result from function + Scriptable scratchScriptable; + + // Generate an observer count on compiled code + public boolean generateObserverCount = false; +} diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ContextAction.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ContextAction.java new file mode 100644 index 0000000..1c584a9 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ContextAction.java @@ -0,0 +1,59 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Igor Bukanov, igor@fastmail.fm + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +// API class + +package org.mozilla.javascript; + +/** + * Interface to represent arbitrary action that requires to have Context + * object associated with the current thread for its execution. + */ +public interface ContextAction +{ + /** + * Execute action using the supplied Context instance. + * When Rhino runtime calls the method, cx will be associated + * with the current thread as active context. + * + * @see Context#call(ContextAction) + * @see ContextFactory#call(ContextAction) + */ + public Object run(Context cx); +} + diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ContextFactory.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ContextFactory.java new file mode 100644 index 0000000..4f9fde2 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ContextFactory.java @@ -0,0 +1,594 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Igor Bukanov, igor@fastmail.fm + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +// API class + +package org.mozilla.javascript; + +/** + * Factory class that Rhino runtime uses to create new {@link Context} + * instances. A ContextFactory can also notify listeners + * about context creation and release. + *

    + * When the Rhino runtime needs to create new {@link Context} instance during + * execution of {@link Context#enter()} or {@link Context}, it will call + * {@link #makeContext()} of the current global ContextFactory. + * See {@link #getGlobal()} and {@link #initGlobal(ContextFactory)}. + *

    + * It is also possible to use explicit ContextFactory instances for Context + * creation. This is useful to have a set of independent Rhino runtime + * instances under single JVM. See {@link #call(ContextAction)}. + *

    + * The following example demonstrates Context customization to terminate + * scripts running more then 10 seconds and to provide better compatibility + * with JavaScript code using MSIE-specific features. + *

    + * import org.mozilla.javascript.*;
    + *
    + * class MyFactory extends ContextFactory
    + * {
    + *
    + *     // Custom {@link Context} to store execution time.
    + *     private static class MyContext extends Context
    + *     {
    + *         long startTime;
    + *     }
    + *
    + *     static {
    + *         // Initialize GlobalFactory with custom factory
    + *         ContextFactory.initGlobal(new MyFactory());
    + *     }
    + *
    + *     // Override {@link #makeContext()}
    + *     protected Context makeContext()
    + *     {
    + *         MyContext cx = new MyContext();
    + *         // Use pure interpreter mode to allow for
    + *         // {@link #observeInstructionCount(Context, int)} to work
    + *         cx.setOptimizationLevel(-1);
    + *         // Make Rhino runtime to call observeInstructionCount
    + *         // each 10000 bytecode instructions
    + *         cx.setInstructionObserverThreshold(10000);
    + *         return cx;
    + *     }
    + *
    + *     // Override {@link #hasFeature(Context, int)}
    + *     public boolean hasFeature(Context cx, int featureIndex)
    + *     {
    + *         // Turn on maximum compatibility with MSIE scripts
    + *         switch (featureIndex) {
    + *             case {@link Context#FEATURE_NON_ECMA_GET_YEAR}:
    + *                 return true;
    + *
    + *             case {@link Context#FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME}:
    + *                 return true;
    + *
    + *             case {@link Context#FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER}:
    + *                 return true;
    + *
    + *             case {@link Context#FEATURE_PARENT_PROTO_PROPERTIES}:
    + *                 return false;
    + *         }
    + *         return super.hasFeature(cx, featureIndex);
    + *     }
    + *
    + *     // Override {@link #observeInstructionCount(Context, int)}
    + *     protected void observeInstructionCount(Context cx, int instructionCount)
    + *     {
    + *         MyContext mcx = (MyContext)cx;
    + *         long currentTime = System.currentTimeMillis();
    + *         if (currentTime - mcx.startTime > 10*1000) {
    + *             // More then 10 seconds from Context creation time:
    + *             // it is time to stop the script.
    + *             // Throw Error instance to ensure that script will never
    + *             // get control back through catch or finally.
    + *             throw new Error();
    + *         }
    + *     }
    + *
    + *     // Override {@link #doTopCall(Callable,
    +                               Context, Scriptable,
    +                               Scriptable, Object[])}
    + *     protected Object doTopCall(Callable callable,
    + *                                Context cx, Scriptable scope,
    + *                                Scriptable thisObj, Object[] args)
    + *     {
    + *         MyContext mcx = (MyContext)cx;
    + *         mcx.startTime = System.currentTimeMillis();
    + *
    + *         return super.doTopCall(callable, cx, scope, thisObj, args);
    + *     }
    + *
    + * }
    + *
    + * 
    + */ + +public class ContextFactory +{ + private static volatile boolean hasCustomGlobal; + private static ContextFactory global = new ContextFactory(); + + private volatile boolean sealed; + + private final Object listenersLock = new Object(); + private volatile Object listeners; + private boolean disabledListening; + private ClassLoader applicationClassLoader; + + /** + * Listener of {@link Context} creation and release events. + */ + public interface Listener + { + /** + * Notify about newly created {@link Context} object. + */ + public void contextCreated(Context cx); + + /** + * Notify that the specified {@link Context} instance is no longer + * associated with the current thread. + */ + public void contextReleased(Context cx); + } + + /** + * Get global ContextFactory. + * + * @see #hasExplicitGlobal() + * @see #initGlobal(ContextFactory) + */ + public static ContextFactory getGlobal() + { + return global; + } + + /** + * Check if global factory was set. + * Return true to indicate that {@link #initGlobal(ContextFactory)} was + * already called and false to indicate that the global factory was not + * explicitly set. + * + * @see #getGlobal() + * @see #initGlobal(ContextFactory) + */ + public static boolean hasExplicitGlobal() + { + return hasCustomGlobal; + } + + /** + * Set global ContextFactory. + * The method can only be called once. + * + * @see #getGlobal() + * @see #hasExplicitGlobal() + */ + public synchronized static void initGlobal(ContextFactory factory) + { + if (factory == null) { + throw new IllegalArgumentException(); + } + if (hasCustomGlobal) { + throw new IllegalStateException(); + } + hasCustomGlobal = true; + global = factory; + } + + /** + * Create new {@link Context} instance to be associated with the current + * thread. + * This is a callback method used by Rhino to create {@link Context} + * instance when it is necessary to associate one with the current + * execution thread. makeContext() is allowed to call + * {@link Context#seal(Object)} on the result to prevent + * {@link Context} changes by hostile scripts or applets. + */ + protected Context makeContext() + { + return new Context(this); + } + + /** + * Implementation of {@link Context#hasFeature(int featureIndex)}. + * This can be used to customize {@link Context} without introducing + * additional subclasses. + */ + protected boolean hasFeature(Context cx, int featureIndex) + { + int version; + switch (featureIndex) { + case Context.FEATURE_NON_ECMA_GET_YEAR: + /* + * During the great date rewrite of 1.3, we tried to track the + * evolving ECMA standard, which then had a definition of + * getYear which always subtracted 1900. Which we + * implemented, not realizing that it was incompatible with + * the old behavior... now, rather than thrash the behavior + * yet again, we've decided to leave it with the - 1900 + * behavior and point people to the getFullYear method. But + * we try to protect existing scripts that have specified a + * version... + */ + version = cx.getLanguageVersion(); + return (version == Context.VERSION_1_0 + || version == Context.VERSION_1_1 + || version == Context.VERSION_1_2); + + case Context.FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME: + return false; + + case Context.FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER: + return false; + + case Context.FEATURE_TO_STRING_AS_SOURCE: + version = cx.getLanguageVersion(); + return version == Context.VERSION_1_2; + + case Context.FEATURE_PARENT_PROTO_PROPERTIES: + return true; + + case Context.FEATURE_E4X: + version = cx.getLanguageVersion(); + return (version == Context.VERSION_DEFAULT + || version >= Context.VERSION_1_6); + + case Context.FEATURE_DYNAMIC_SCOPE: + return false; + + case Context.FEATURE_STRICT_VARS: + return false; + + case Context.FEATURE_STRICT_EVAL: + return false; + + case Context.FEATURE_LOCATION_INFORMATION_IN_ERROR: + return false; + + case Context.FEATURE_STRICT_MODE: + return false; + + case Context.FEATURE_WARNING_AS_ERROR: + return false; + + case Context.FEATURE_ENHANCED_JAVA_ACCESS: + return false; + } + // It is a bug to call the method with unknown featureIndex + throw new IllegalArgumentException(String.valueOf(featureIndex)); + } + + private boolean isDom3Present() { + Class nodeClass = Kit.classOrNull("org.w3c.dom.Node"); + if (nodeClass == null) return false; + // Check to see whether DOM3 is present; use a new method defined in + // DOM3 that is vital to our implementation + try { + nodeClass.getMethod("getUserData", new Class[] { String.class }); + return true; + } catch (NoSuchMethodException e) { + return false; + } + } + + /** + * Provides a default + * {@link org.mozilla.javascript.xml.XMLLib.Factory XMLLib.Factory} + * to be used by the Context instances produced by this + * factory. See {@link Context#getE4xImplementationFactory} for details. + * + * May return null, in which case E4X functionality is not supported in + * Rhino. + * + * The default implementation now prefers the DOM3 E4X implementation. + */ + protected org.mozilla.javascript.xml.XMLLib.Factory + getE4xImplementationFactory() + { + // Must provide default implementation, rather than abstract method, + // so that past implementors of ContextFactory do not fail at runtime + // upon invocation of this method. + // Note that the default implementation returns null if we + // neither have XMLBeans nor a DOM3 implementation present. + + if (isDom3Present()) { + return org.mozilla.javascript.xml.XMLLib.Factory.create( + "org.mozilla.javascript.xmlimpl.XMLLibImpl" + ); + } else if (Kit.classOrNull("org.apache.xmlbeans.XmlCursor") != null) { + return org.mozilla.javascript.xml.XMLLib.Factory.create( + "org.mozilla.javascript.xml.impl.xmlbeans.XMLLibImpl" + ); + } else { + return null; + } + } + + + /** + * Create class loader for generated classes. + * This method creates an instance of the default implementation + * of {@link GeneratedClassLoader}. Rhino uses this interface to load + * generated JVM classes when no {@link SecurityController} + * is installed. + * Application can override the method to provide custom class loading. + */ + protected GeneratedClassLoader createClassLoader(ClassLoader parent) + { + return new DefiningClassLoader(parent); + } + + /** + * Get ClassLoader to use when searching for Java classes. + * Unless it was explicitly initialized with + * {@link #initApplicationClassLoader(ClassLoader)} the method returns + * null to indicate that Thread.getContextClassLoader() should be used. + */ + public final ClassLoader getApplicationClassLoader() + { + return applicationClassLoader; + } + + /** + * Set explicit class loader to use when searching for Java classes. + * + * @see #getApplicationClassLoader() + */ + public final void initApplicationClassLoader(ClassLoader loader) + { + if (loader == null) + throw new IllegalArgumentException("loader is null"); + if (!Kit.testIfCanLoadRhinoClasses(loader)) + throw new IllegalArgumentException( + "Loader can not resolve Rhino classes"); + + if (this.applicationClassLoader != null) + throw new IllegalStateException( + "applicationClassLoader can only be set once"); + checkNotSealed(); + + this.applicationClassLoader = loader; + } + + /** + * Execute top call to script or function. + * When the runtime is about to execute a script or function that will + * create the first stack frame with scriptable code, it calls this method + * to perform the real call. In this way execution of any script + * happens inside this function. + */ + protected Object doTopCall(Callable callable, + Context cx, Scriptable scope, + Scriptable thisObj, Object[] args) + { + return callable.call(cx, scope, thisObj, args); + } + + /** + * Implementation of + * {@link Context#observeInstructionCount(int instructionCount)}. + * This can be used to customize {@link Context} without introducing + * additional subclasses. + */ + protected void observeInstructionCount(Context cx, int instructionCount) + { + } + + protected void onContextCreated(Context cx) + { + Object listeners = this.listeners; + for (int i = 0; ; ++i) { + Listener l = (Listener)Kit.getListener(listeners, i); + if (l == null) + break; + l.contextCreated(cx); + } + } + + protected void onContextReleased(Context cx) + { + Object listeners = this.listeners; + for (int i = 0; ; ++i) { + Listener l = (Listener)Kit.getListener(listeners, i); + if (l == null) + break; + l.contextReleased(cx); + } + } + + public final void addListener(Listener listener) + { + checkNotSealed(); + synchronized (listenersLock) { + if (disabledListening) { + throw new IllegalStateException(); + } + listeners = Kit.addListener(listeners, listener); + } + } + + public final void removeListener(Listener listener) + { + checkNotSealed(); + synchronized (listenersLock) { + if (disabledListening) { + throw new IllegalStateException(); + } + listeners = Kit.removeListener(listeners, listener); + } + } + + /** + * The method is used only to implement + * Context.disableStaticContextListening() + */ + final void disableContextListening() + { + checkNotSealed(); + synchronized (listenersLock) { + disabledListening = true; + listeners = null; + } + } + + /** + * Checks if this is a sealed ContextFactory. + * @see #seal() + */ + public final boolean isSealed() + { + return sealed; + } + + /** + * Seal this ContextFactory so any attempt to modify it like to add or + * remove its listeners will throw an exception. + * @see #isSealed() + */ + public final void seal() + { + checkNotSealed(); + sealed = true; + } + + protected final void checkNotSealed() + { + if (sealed) throw new IllegalStateException(); + } + + /** + * Call {@link ContextAction#run(Context cx)} + * using the {@link Context} instance associated with the current thread. + * If no Context is associated with the thread, then + * {@link #makeContext()} will be called to construct + * new Context instance. The instance will be temporary associated + * with the thread during call to {@link ContextAction#run(Context)}. + * + * @see ContextFactory#call(ContextAction) + * @see Context#call(ContextFactory factory, Callable callable, + * Scriptable scope, Scriptable thisObj, + * Object[] args) + */ + public final Object call(ContextAction action) + { + return Context.call(this, action); + } + + /** + * Get a context associated with the current thread, creating one if need + * be. The Context stores the execution state of the JavaScript engine, so + * it is required that the context be entered before execution may begin. + * Once a thread has entered a Context, then getCurrentContext() may be + * called to find the context that is associated with the current thread. + *

    + * Calling enterContext() will return either the Context + * currently associated with the thread, or will create a new context and + * associate it with the current thread. Each call to + * enterContext() must have a matching call to + * {@link Context#exit()}. + *

    +     *      Context cx = contextFactory.enterContext();
    +     *      try {
    +     *          ...
    +     *          cx.evaluateString(...);
    +     *      } finally {
    +     *          Context.exit();
    +     *      }
    +     * 
    + * Instead of using enterContext(), exit() pair consider + * using {@link #call(ContextAction)} which guarantees proper association + * of Context instances with the current thread. + * With this method the above example becomes: + *
    +     *      ContextFactory.call(new ContextAction() {
    +     *          public Object run(Context cx) {
    +     *              ...
    +     *              cx.evaluateString(...);
    +     *              return null;
    +     *          }
    +     *      });
    +     * 
    + * @return a Context associated with the current thread + * @see Context#getCurrentContext() + * @see Context#exit() + * @see #call(ContextAction) + */ + public Context enterContext() + { + return enterContext(null); + } + + /** + * @deprecated use {@link #enterContext()} instead + * @return a Context associated with the current thread + */ + public final Context enter() + { + return enterContext(null); + } + + /** + * @deprecated Use {@link Context#exit()} instead. + */ + public final void exit() + { + Context.exit(); + } + + /** + * Get a Context associated with the current thread, using the given + * Context if need be. + *

    + * The same as enterContext() except that cx + * is associated with the current thread and returned if the current thread + * has no associated context and cx is not associated with any + * other thread. + * @param cx a Context to associate with the thread if possible + * @return a Context associated with the current thread + * @see #enterContext() + * @see #call(ContextAction) + * @throws IllegalStateException if cx is already associated + * with a different thread + */ + public final Context enterContext(Context cx) + { + return Context.enter(cx, this); + } +} \ No newline at end of file diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ContextListener.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ContextListener.java new file mode 100644 index 0000000..5e17145 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ContextListener.java @@ -0,0 +1,60 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Norris Boyd + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +// API class + +package org.mozilla.javascript; + +/** + * @deprecated Embeddings that wish to customize newly created + * {@link Context} instances should implement + * {@link ContextFactory.Listener}. + */ +public interface ContextListener extends ContextFactory.Listener +{ + + /** + * @deprecated Rhino runtime never calls the method. + */ + public void contextEntered(Context cx); + + /** + * @deprecated Rhino runtime never calls the method. + */ + public void contextExited(Context cx); +} diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/DToA.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/DToA.java new file mode 100644 index 0000000..ad2a68a --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/DToA.java @@ -0,0 +1,1271 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Waldemar Horwat + * Roger Lawrence + * Attila Szegedi + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +/**************************************************************** + * + * The author of this software is David M. Gay. + * + * Copyright (c) 1991, 2000, 2001 by Lucent Technologies. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + * + ***************************************************************/ + +package org.mozilla.javascript; + +import java.math.BigInteger; + +class DToA { + + +/* "-0.0000...(1073 zeros after decimal point)...0001\0" is the longest string that we could produce, + * which occurs when printing -5e-324 in binary. We could compute a better estimate of the size of + * the output string and malloc fewer bytes depending on d and base, but why bother? */ + + private static final int DTOBASESTR_BUFFER_SIZE = 1078; + + private static char BASEDIGIT(int digit) { + return (char)((digit >= 10) ? 'a' - 10 + digit : '0' + digit); + } + + static final int + DTOSTR_STANDARD = 0, /* Either fixed or exponential format; round-trip */ + DTOSTR_STANDARD_EXPONENTIAL = 1, /* Always exponential format; round-trip */ + DTOSTR_FIXED = 2, /* Round to digits after the decimal point; exponential if number is large */ + DTOSTR_EXPONENTIAL = 3, /* Always exponential format; significant digits */ + DTOSTR_PRECISION = 4; /* Either fixed or exponential format; significant digits */ + + + private static final int Frac_mask = 0xfffff; + private static final int Exp_shift = 20; + private static final int Exp_msk1 = 0x100000; + + private static final long Frac_maskL = 0xfffffffffffffL; + private static final int Exp_shiftL = 52; + private static final long Exp_msk1L = 0x10000000000000L; + + private static final int Bias = 1023; + private static final int P = 53; + + private static final int Exp_shift1 = 20; + private static final int Exp_mask = 0x7ff00000; + private static final int Exp_mask_shifted = 0x7ff; + private static final int Bndry_mask = 0xfffff; + private static final int Log2P = 1; + + private static final int Sign_bit = 0x80000000; + private static final int Exp_11 = 0x3ff00000; + private static final int Ten_pmax = 22; + private static final int Quick_max = 14; + private static final int Bletch = 0x10; + private static final int Frac_mask1 = 0xfffff; + private static final int Int_max = 14; + private static final int n_bigtens = 5; + + + private static final double tens[] = { + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, + 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, + 1e20, 1e21, 1e22 + }; + + private static final double bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 }; + + private static int lo0bits(int y) + { + int k; + int x = y; + + if ((x & 7) != 0) { + if ((x & 1) != 0) + return 0; + if ((x & 2) != 0) { + return 1; + } + return 2; + } + k = 0; + if ((x & 0xffff) == 0) { + k = 16; + x >>>= 16; + } + if ((x & 0xff) == 0) { + k += 8; + x >>>= 8; + } + if ((x & 0xf) == 0) { + k += 4; + x >>>= 4; + } + if ((x & 0x3) == 0) { + k += 2; + x >>>= 2; + } + if ((x & 1) == 0) { + k++; + x >>>= 1; + if ((x & 1) == 0) + return 32; + } + return k; + } + + /* Return the number (0 through 32) of most significant zero bits in x. */ + private static int hi0bits(int x) + { + int k = 0; + + if ((x & 0xffff0000) == 0) { + k = 16; + x <<= 16; + } + if ((x & 0xff000000) == 0) { + k += 8; + x <<= 8; + } + if ((x & 0xf0000000) == 0) { + k += 4; + x <<= 4; + } + if ((x & 0xc0000000) == 0) { + k += 2; + x <<= 2; + } + if ((x & 0x80000000) == 0) { + k++; + if ((x & 0x40000000) == 0) + return 32; + } + return k; + } + + private static void stuffBits(byte bits[], int offset, int val) + { + bits[offset] = (byte)(val >> 24); + bits[offset + 1] = (byte)(val >> 16); + bits[offset + 2] = (byte)(val >> 8); + bits[offset + 3] = (byte)(val); + } + + /* Convert d into the form b*2^e, where b is an odd integer. b is the returned + * Bigint and e is the returned binary exponent. Return the number of significant + * bits in b in bits. d must be finite and nonzero. */ + private static BigInteger d2b(double d, int[] e, int[] bits) + { + byte dbl_bits[]; + int i, k, y, z, de; + long dBits = Double.doubleToLongBits(d); + int d0 = (int)(dBits >>> 32); + int d1 = (int)(dBits); + + z = d0 & Frac_mask; + d0 &= 0x7fffffff; /* clear sign bit, which we ignore */ + + if ((de = (d0 >>> Exp_shift)) != 0) + z |= Exp_msk1; + + if ((y = d1) != 0) { + dbl_bits = new byte[8]; + k = lo0bits(y); + y >>>= k; + if (k != 0) { + stuffBits(dbl_bits, 4, y | z << (32 - k)); + z >>= k; + } + else + stuffBits(dbl_bits, 4, y); + stuffBits(dbl_bits, 0, z); + i = (z != 0) ? 2 : 1; + } + else { + // JS_ASSERT(z); + dbl_bits = new byte[4]; + k = lo0bits(z); + z >>>= k; + stuffBits(dbl_bits, 0, z); + k += 32; + i = 1; + } + if (de != 0) { + e[0] = de - Bias - (P-1) + k; + bits[0] = P - k; + } + else { + e[0] = de - Bias - (P-1) + 1 + k; + bits[0] = 32*i - hi0bits(z); + } + return new BigInteger(dbl_bits); + } + + static String JS_dtobasestr(int base, double d) + { + if (!(2 <= base && base <= 36)) + throw new IllegalArgumentException("Bad base: "+base); + + /* Check for Infinity and NaN */ + if (Double.isNaN(d)) { + return "NaN"; + } else if (Double.isInfinite(d)) { + return (d > 0.0) ? "Infinity" : "-Infinity"; + } else if (d == 0) { + // ALERT: should it distinguish -0.0 from +0.0 ? + return "0"; + } + + boolean negative; + if (d >= 0.0) { + negative = false; + } else { + negative = true; + d = -d; + } + + /* Get the integer part of d including '-' sign. */ + String intDigits; + + double dfloor = Math.floor(d); + long lfloor = (long)dfloor; + if (lfloor == dfloor) { + // int part fits long + intDigits = Long.toString((negative) ? -lfloor : lfloor, base); + } else { + // BigInteger should be used + long floorBits = Double.doubleToLongBits(dfloor); + int exp = (int)(floorBits >> Exp_shiftL) & Exp_mask_shifted; + long mantissa; + if (exp == 0) { + mantissa = (floorBits & Frac_maskL) << 1; + } else { + mantissa = (floorBits & Frac_maskL) | Exp_msk1L; + } + if (negative) { + mantissa = -mantissa; + } + exp -= 1075; + BigInteger x = BigInteger.valueOf(mantissa); + if (exp > 0) { + x = x.shiftLeft(exp); + } else if (exp < 0) { + x = x.shiftRight(-exp); + } + intDigits = x.toString(base); + } + + if (d == dfloor) { + // No fraction part + return intDigits; + } else { + /* We have a fraction. */ + + char[] buffer; /* The output string */ + int p; /* index to current position in the buffer */ + int digit; + double df; /* The fractional part of d */ + BigInteger b; + + buffer = new char[DTOBASESTR_BUFFER_SIZE]; + p = 0; + df = d - dfloor; + + long dBits = Double.doubleToLongBits(d); + int word0 = (int)(dBits >> 32); + int word1 = (int)(dBits); + + int[] e = new int[1]; + int[] bbits = new int[1]; + + b = d2b(df, e, bbits); +// JS_ASSERT(e < 0); + /* At this point df = b * 2^e. e must be less than zero because 0 < df < 1. */ + + int s2 = -(word0 >>> Exp_shift1 & Exp_mask >> Exp_shift1); + if (s2 == 0) + s2 = -1; + s2 += Bias + P; + /* 1/2^s2 = (nextDouble(d) - d)/2 */ +// JS_ASSERT(-s2 < e); + BigInteger mlo = BigInteger.valueOf(1); + BigInteger mhi = mlo; + if ((word1 == 0) && ((word0 & Bndry_mask) == 0) + && ((word0 & (Exp_mask & Exp_mask << 1)) != 0)) { + /* The special case. Here we want to be within a quarter of the last input + significant digit instead of one half of it when the output string's value is less than d. */ + s2 += Log2P; + mhi = BigInteger.valueOf(1< df = b/2^s2 > 0; + * (d - prevDouble(d))/2 = mlo/2^s2; + * (nextDouble(d) - d)/2 = mhi/2^s2. */ + BigInteger bigBase = BigInteger.valueOf(base); + + boolean done = false; + do { + b = b.multiply(bigBase); + BigInteger[] divResult = b.divideAndRemainder(s); + b = divResult[1]; + digit = (char)(divResult[0].intValue()); + if (mlo == mhi) + mlo = mhi = mlo.multiply(bigBase); + else { + mlo = mlo.multiply(bigBase); + mhi = mhi.multiply(bigBase); + } + + /* Do we yet have the shortest string that will round to d? */ + int j = b.compareTo(mlo); + /* j is b/2^s2 compared with mlo/2^s2. */ + BigInteger delta = s.subtract(mhi); + int j1 = (delta.signum() <= 0) ? 1 : b.compareTo(delta); + /* j1 is b/2^s2 compared with 1 - mhi/2^s2. */ + if (j1 == 0 && ((word1 & 1) == 0)) { + if (j > 0) + digit++; + done = true; + } else + if (j < 0 || (j == 0 && ((word1 & 1) == 0))) { + if (j1 > 0) { + /* Either dig or dig+1 would work here as the least significant digit. + Use whichever would produce an output value closer to d. */ + b = b.shiftLeft(1); + j1 = b.compareTo(s); + if (j1 > 0) /* The even test (|| (j1 == 0 && (digit & 1))) is not here because it messes up odd base output + * such as 3.5 in base 3. */ + digit++; + } + done = true; + } else if (j1 > 0) { + digit++; + done = true; + } +// JS_ASSERT(digit < (uint32)base); + buffer[p++] = BASEDIGIT(digit); + } while (!done); + + StringBuffer sb = new StringBuffer(intDigits.length() + 1 + p); + sb.append(intDigits); + sb.append('.'); + sb.append(buffer, 0, p); + return sb.toString(); + } + + } + + /* dtoa for IEEE arithmetic (dmg): convert double to ASCII string. + * + * Inspired by "How to Print Floating-Point Numbers Accurately" by + * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 92-101]. + * + * Modifications: + * 1. Rather than iterating, we use a simple numeric overestimate + * to determine k = floor(log10(d)). We scale relevant + * quantities using O(log2(k)) rather than O(k) multiplications. + * 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't + * try to generate digits strictly left to right. Instead, we + * compute with fewer bits and propagate the carry if necessary + * when rounding the final digit up. This is often faster. + * 3. Under the assumption that input will be rounded nearest, + * mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22. + * That is, we allow equality in stopping tests when the + * round-nearest rule will give the same floating-point value + * as would satisfaction of the stopping test with strict + * inequality. + * 4. We remove common factors of powers of 2 from relevant + * quantities. + * 5. When converting floating-point integers less than 1e16, + * we use floating-point arithmetic rather than resorting + * to multiple-precision integers. + * 6. When asked to produce fewer than 15 digits, we first try + * to get by with floating-point arithmetic; we resort to + * multiple-precision integer arithmetic only if we cannot + * guarantee that the floating-point calculation has given + * the correctly rounded result. For k requested digits and + * "uniformly" distributed input, the probability is + * something like 10^(k-15) that we must resort to the Long + * calculation. + */ + + static int word0(double d) + { + long dBits = Double.doubleToLongBits(d); + return (int)(dBits >> 32); + } + + static double setWord0(double d, int i) + { + long dBits = Double.doubleToLongBits(d); + dBits = ((long)i << 32) | (dBits & 0x0FFFFFFFFL); + return Double.longBitsToDouble(dBits); + } + + static int word1(double d) + { + long dBits = Double.doubleToLongBits(d); + return (int)(dBits); + } + + /* Return b * 5^k. k must be nonnegative. */ + // XXXX the C version built a cache of these + static BigInteger pow5mult(BigInteger b, int k) + { + return b.multiply(BigInteger.valueOf(5).pow(k)); + } + + static boolean roundOff(StringBuffer buf) + { + int i = buf.length(); + while (i != 0) { + --i; + char c = buf.charAt(i); + if (c != '9') { + buf.setCharAt(i, (char)(c + 1)); + buf.setLength(i + 1); + return false; + } + } + buf.setLength(0); + return true; + } + + /* Always emits at least one digit. */ + /* If biasUp is set, then rounding in modes 2 and 3 will round away from zero + * when the number is exactly halfway between two representable values. For example, + * rounding 2.5 to zero digits after the decimal point will return 3 and not 2. + * 2.49 will still round to 2, and 2.51 will still round to 3. */ + /* bufsize should be at least 20 for modes 0 and 1. For the other modes, + * bufsize should be two greater than the maximum number of output characters expected. */ + static int + JS_dtoa(double d, int mode, boolean biasUp, int ndigits, + boolean[] sign, StringBuffer buf) + { + /* Arguments ndigits, decpt, sign are similar to those + of ecvt and fcvt; trailing zeros are suppressed from + the returned string. If not null, *rve is set to point + to the end of the return value. If d is +-Infinity or NaN, + then *decpt is set to 9999. + + mode: + 0 ==> shortest string that yields d when read in + and rounded to nearest. + 1 ==> like 0, but with Steele & White stopping rule; + e.g. with IEEE P754 arithmetic , mode 0 gives + 1e23 whereas mode 1 gives 9.999999999999999e22. + 2 ==> max(1,ndigits) significant digits. This gives a + return value similar to that of ecvt, except + that trailing zeros are suppressed. + 3 ==> through ndigits past the decimal point. This + gives a return value similar to that from fcvt, + except that trailing zeros are suppressed, and + ndigits can be negative. + 4-9 should give the same return values as 2-3, i.e., + 4 <= mode <= 9 ==> same return as mode + 2 + (mode & 1). These modes are mainly for + debugging; often they run slower but sometimes + faster than modes 2-3. + 4,5,8,9 ==> left-to-right digit generation. + 6-9 ==> don't try fast floating-point estimate + (if applicable). + + Values of mode other than 0-9 are treated as mode 0. + + Sufficient space is allocated to the return value + to hold the suppressed trailing zeros. + */ + + int b2, b5, i, ieps, ilim, ilim0, ilim1, + j, j1, k, k0, m2, m5, s2, s5; + char dig; + long L; + long x; + BigInteger b, b1, delta, mlo, mhi, S; + int[] be = new int[1]; + int[] bbits = new int[1]; + double d2, ds, eps; + boolean spec_case, denorm, k_check, try_quick, leftright; + + if ((word0(d) & Sign_bit) != 0) { + /* set sign for everything, including 0's and NaNs */ + sign[0] = true; + // word0(d) &= ~Sign_bit; /* clear sign bit */ + d = setWord0(d, word0(d) & ~Sign_bit); + } + else + sign[0] = false; + + if ((word0(d) & Exp_mask) == Exp_mask) { + /* Infinity or NaN */ + buf.append(((word1(d) == 0) && ((word0(d) & Frac_mask) == 0)) ? "Infinity" : "NaN"); + return 9999; + } + if (d == 0) { +// no_digits: + buf.setLength(0); + buf.append('0'); /* copy "0" to buffer */ + return 1; + } + + b = d2b(d, be, bbits); + if ((i = (word0(d) >>> Exp_shift1 & (Exp_mask>>Exp_shift1))) != 0) { + d2 = setWord0(d, (word0(d) & Frac_mask1) | Exp_11); + /* log(x) ~=~ log(1.5) + (x-1.5)/1.5 + * log10(x) = log(x) / log(10) + * ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10)) + * log10(d) = (i-Bias)*log(2)/log(10) + log10(d2) + * + * This suggests computing an approximation k to log10(d) by + * + * k = (i - Bias)*0.301029995663981 + * + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 ); + * + * We want k to be too large rather than too small. + * The error in the first-order Taylor series approximation + * is in our favor, so we just round up the constant enough + * to compensate for any error in the multiplication of + * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077, + * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14, + * adding 1e-13 to the constant term more than suffices. + * Hence we adjust the constant term to 0.1760912590558. + * (We could get a more accurate k by invoking log10, + * but this is probably not worthwhile.) + */ + i -= Bias; + denorm = false; + } + else { + /* d is denormalized */ + i = bbits[0] + be[0] + (Bias + (P-1) - 1); + x = (i > 32) ? word0(d) << (64 - i) | word1(d) >>> (i - 32) : word1(d) << (32 - i); +// d2 = x; +// word0(d2) -= 31*Exp_msk1; /* adjust exponent */ + d2 = setWord0(x, word0(x) - 31*Exp_msk1); + i -= (Bias + (P-1) - 1) + 1; + denorm = true; + } + /* At this point d = f*2^i, where 1 <= f < 2. d2 is an approximation of f. */ + ds = (d2-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981; + k = (int)ds; + if (ds < 0.0 && ds != k) + k--; /* want k = floor(ds) */ + k_check = true; + if (k >= 0 && k <= Ten_pmax) { + if (d < tens[k]) + k--; + k_check = false; + } + /* At this point floor(log10(d)) <= k <= floor(log10(d))+1. + If k_check is zero, we're guaranteed that k = floor(log10(d)). */ + j = bbits[0] - i - 1; + /* At this point d = b/2^j, where b is an odd integer. */ + if (j >= 0) { + b2 = 0; + s2 = j; + } + else { + b2 = -j; + s2 = 0; + } + if (k >= 0) { + b5 = 0; + s5 = k; + s2 += k; + } + else { + b2 -= k; + b5 = -k; + s5 = 0; + } + /* At this point d/10^k = (b * 2^b2 * 5^b5) / (2^s2 * 5^s5), where b is an odd integer, + b2 >= 0, b5 >= 0, s2 >= 0, and s5 >= 0. */ + if (mode < 0 || mode > 9) + mode = 0; + try_quick = true; + if (mode > 5) { + mode -= 4; + try_quick = false; + } + leftright = true; + ilim = ilim1 = 0; + switch(mode) { + case 0: + case 1: + ilim = ilim1 = -1; + i = 18; + ndigits = 0; + break; + case 2: + leftright = false; + /* no break */ + case 4: + if (ndigits <= 0) + ndigits = 1; + ilim = ilim1 = i = ndigits; + break; + case 3: + leftright = false; + /* no break */ + case 5: + i = ndigits + k + 1; + ilim = i; + ilim1 = i - 1; + if (i <= 0) + i = 1; + } + /* ilim is the maximum number of significant digits we want, based on k and ndigits. */ + /* ilim1 is the maximum number of significant digits we want, based on k and ndigits, + when it turns out that k was computed too high by one. */ + + boolean fast_failed = false; + if (ilim >= 0 && ilim <= Quick_max && try_quick) { + + /* Try to get by with floating-point arithmetic. */ + + i = 0; + d2 = d; + k0 = k; + ilim0 = ilim; + ieps = 2; /* conservative */ + /* Divide d by 10^k, keeping track of the roundoff error and avoiding overflows. */ + if (k > 0) { + ds = tens[k&0xf]; + j = k >> 4; + if ((j & Bletch) != 0) { + /* prevent overflows */ + j &= Bletch - 1; + d /= bigtens[n_bigtens-1]; + ieps++; + } + for(; (j != 0); j >>= 1, i++) + if ((j & 1) != 0) { + ieps++; + ds *= bigtens[i]; + } + d /= ds; + } + else if ((j1 = -k) != 0) { + d *= tens[j1 & 0xf]; + for(j = j1 >> 4; (j != 0); j >>= 1, i++) + if ((j & 1) != 0) { + ieps++; + d *= bigtens[i]; + } + } + /* Check that k was computed correctly. */ + if (k_check && d < 1.0 && ilim > 0) { + if (ilim1 <= 0) + fast_failed = true; + else { + ilim = ilim1; + k--; + d *= 10.; + ieps++; + } + } + /* eps bounds the cumulative error. */ +// eps = ieps*d + 7.0; +// word0(eps) -= (P-1)*Exp_msk1; + eps = ieps*d + 7.0; + eps = setWord0(eps, word0(eps) - (P-1)*Exp_msk1); + if (ilim == 0) { + S = mhi = null; + d -= 5.0; + if (d > eps) { + buf.append('1'); + k++; + return k + 1; + } + if (d < -eps) { + buf.setLength(0); + buf.append('0'); /* copy "0" to buffer */ + return 1; + } + fast_failed = true; + } + if (!fast_failed) { + fast_failed = true; + if (leftright) { + /* Use Steele & White method of only + * generating digits needed. + */ + eps = 0.5/tens[ilim-1] - eps; + for(i = 0;;) { + L = (long)d; + d -= L; + buf.append((char)('0' + L)); + if (d < eps) { + return k + 1; + } + if (1.0 - d < eps) { +// goto bump_up; + char lastCh; + while (true) { + lastCh = buf.charAt(buf.length() - 1); + buf.setLength(buf.length() - 1); + if (lastCh != '9') break; + if (buf.length() == 0) { + k++; + lastCh = '0'; + break; + } + } + buf.append((char)(lastCh + 1)); + return k + 1; + } + if (++i >= ilim) + break; + eps *= 10.0; + d *= 10.0; + } + } + else { + /* Generate ilim digits, then fix them up. */ + eps *= tens[ilim-1]; + for(i = 1;; i++, d *= 10.0) { + L = (long)d; + d -= L; + buf.append((char)('0' + L)); + if (i == ilim) { + if (d > 0.5 + eps) { +// goto bump_up; + char lastCh; + while (true) { + lastCh = buf.charAt(buf.length() - 1); + buf.setLength(buf.length() - 1); + if (lastCh != '9') break; + if (buf.length() == 0) { + k++; + lastCh = '0'; + break; + } + } + buf.append((char)(lastCh + 1)); + return k + 1; + } + else + if (d < 0.5 - eps) { + stripTrailingZeroes(buf); +// while(*--s == '0') ; +// s++; + return k + 1; + } + break; + } + } + } + } + if (fast_failed) { + buf.setLength(0); + d = d2; + k = k0; + ilim = ilim0; + } + } + + /* Do we have a "small" integer? */ + + if (be[0] >= 0 && k <= Int_max) { + /* Yes. */ + ds = tens[k]; + if (ndigits < 0 && ilim <= 0) { + S = mhi = null; + if (ilim < 0 || d < 5*ds || (!biasUp && d == 5*ds)) { + buf.setLength(0); + buf.append('0'); /* copy "0" to buffer */ + return 1; + } + buf.append('1'); + k++; + return k + 1; + } + for(i = 1;; i++) { + L = (long) (d / ds); + d -= L*ds; + buf.append((char)('0' + L)); + if (i == ilim) { + d += d; + if ((d > ds) || (d == ds && (((L & 1) != 0) || biasUp))) { +// bump_up: +// while(*--s == '9') +// if (s == buf) { +// k++; +// *s = '0'; +// break; +// } +// ++*s++; + char lastCh; + while (true) { + lastCh = buf.charAt(buf.length() - 1); + buf.setLength(buf.length() - 1); + if (lastCh != '9') break; + if (buf.length() == 0) { + k++; + lastCh = '0'; + break; + } + } + buf.append((char)(lastCh + 1)); + } + break; + } + d *= 10.0; + if (d == 0) + break; + } + return k + 1; + } + + m2 = b2; + m5 = b5; + mhi = mlo = null; + if (leftright) { + if (mode < 2) { + i = (denorm) ? be[0] + (Bias + (P-1) - 1 + 1) : 1 + P - bbits[0]; + /* i is 1 plus the number of trailing zero bits in d's significand. Thus, + (2^m2 * 5^m5) / (2^(s2+i) * 5^s5) = (1/2 lsb of d)/10^k. */ + } + else { + j = ilim - 1; + if (m5 >= j) + m5 -= j; + else { + s5 += j -= m5; + b5 += j; + m5 = 0; + } + if ((i = ilim) < 0) { + m2 -= i; + i = 0; + } + /* (2^m2 * 5^m5) / (2^(s2+i) * 5^s5) = (1/2 * 10^(1-ilim))/10^k. */ + } + b2 += i; + s2 += i; + mhi = BigInteger.valueOf(1); + /* (mhi * 2^m2 * 5^m5) / (2^s2 * 5^s5) = one-half of last printed (when mode >= 2) or + input (when mode < 2) significant digit, divided by 10^k. */ + } + /* We still have d/10^k = (b * 2^b2 * 5^b5) / (2^s2 * 5^s5). Reduce common factors in + b2, m2, and s2 without changing the equalities. */ + if (m2 > 0 && s2 > 0) { + i = (m2 < s2) ? m2 : s2; + b2 -= i; + m2 -= i; + s2 -= i; + } + + /* Fold b5 into b and m5 into mhi. */ + if (b5 > 0) { + if (leftright) { + if (m5 > 0) { + mhi = pow5mult(mhi, m5); + b1 = mhi.multiply(b); + b = b1; + } + if ((j = b5 - m5) != 0) + b = pow5mult(b, j); + } + else + b = pow5mult(b, b5); + } + /* Now we have d/10^k = (b * 2^b2) / (2^s2 * 5^s5) and + (mhi * 2^m2) / (2^s2 * 5^s5) = one-half of last printed or input significant digit, divided by 10^k. */ + + S = BigInteger.valueOf(1); + if (s5 > 0) + S = pow5mult(S, s5); + /* Now we have d/10^k = (b * 2^b2) / (S * 2^s2) and + (mhi * 2^m2) / (S * 2^s2) = one-half of last printed or input significant digit, divided by 10^k. */ + + /* Check for special case that d is a normalized power of 2. */ + spec_case = false; + if (mode < 2) { + if ( (word1(d) == 0) && ((word0(d) & Bndry_mask) == 0) + && ((word0(d) & (Exp_mask & Exp_mask << 1)) != 0) + ) { + /* The special case. Here we want to be within a quarter of the last input + significant digit instead of one half of it when the decimal output string's value is less than d. */ + b2 += Log2P; + s2 += Log2P; + spec_case = true; + } + } + + /* Arrange for convenient computation of quotients: + * shift left if necessary so divisor has 4 leading 0 bits. + * + * Perhaps we should just compute leading 28 bits of S once + * and for all and pass them and a shift to quorem, so it + * can do shifts and ors to compute the numerator for q. + */ + byte [] S_bytes = S.toByteArray(); + int S_hiWord = 0; + for (int idx = 0; idx < 4; idx++) { + S_hiWord = (S_hiWord << 8); + if (idx < S_bytes.length) + S_hiWord |= (S_bytes[idx] & 0xFF); + } + if ((i = (((s5 != 0) ? 32 - hi0bits(S_hiWord) : 1) + s2) & 0x1f) != 0) + i = 32 - i; + /* i is the number of leading zero bits in the most significant word of S*2^s2. */ + if (i > 4) { + i -= 4; + b2 += i; + m2 += i; + s2 += i; + } + else if (i < 4) { + i += 28; + b2 += i; + m2 += i; + s2 += i; + } + /* Now S*2^s2 has exactly four leading zero bits in its most significant word. */ + if (b2 > 0) + b = b.shiftLeft(b2); + if (s2 > 0) + S = S.shiftLeft(s2); + /* Now we have d/10^k = b/S and + (mhi * 2^m2) / S = maximum acceptable error, divided by 10^k. */ + if (k_check) { + if (b.compareTo(S) < 0) { + k--; + b = b.multiply(BigInteger.valueOf(10)); /* we botched the k estimate */ + if (leftright) + mhi = mhi.multiply(BigInteger.valueOf(10)); + ilim = ilim1; + } + } + /* At this point 1 <= d/10^k = b/S < 10. */ + + if (ilim <= 0 && mode > 2) { + /* We're doing fixed-mode output and d is less than the minimum nonzero output in this mode. + Output either zero or the minimum nonzero output depending on which is closer to d. */ + if ((ilim < 0 ) + || ((i = b.compareTo(S = S.multiply(BigInteger.valueOf(5)))) < 0) + || ((i == 0 && !biasUp))) { + /* Always emit at least one digit. If the number appears to be zero + using the current mode, then emit one '0' digit and set decpt to 1. */ + /*no_digits: + k = -1 - ndigits; + goto ret; */ + buf.setLength(0); + buf.append('0'); /* copy "0" to buffer */ + return 1; +// goto no_digits; + } +// one_digit: + buf.append('1'); + k++; + return k + 1; + } + if (leftright) { + if (m2 > 0) + mhi = mhi.shiftLeft(m2); + + /* Compute mlo -- check for special case + * that d is a normalized power of 2. + */ + + mlo = mhi; + if (spec_case) { + mhi = mlo; + mhi = mhi.shiftLeft(Log2P); + } + /* mlo/S = maximum acceptable error, divided by 10^k, if the output is less than d. */ + /* mhi/S = maximum acceptable error, divided by 10^k, if the output is greater than d. */ + + for(i = 1;;i++) { + BigInteger[] divResult = b.divideAndRemainder(S); + b = divResult[1]; + dig = (char)(divResult[0].intValue() + '0'); + /* Do we yet have the shortest decimal string + * that will round to d? + */ + j = b.compareTo(mlo); + /* j is b/S compared with mlo/S. */ + delta = S.subtract(mhi); + j1 = (delta.signum() <= 0) ? 1 : b.compareTo(delta); + /* j1 is b/S compared with 1 - mhi/S. */ + if ((j1 == 0) && (mode == 0) && ((word1(d) & 1) == 0)) { + if (dig == '9') { + buf.append('9'); + if (roundOff(buf)) { + k++; + buf.append('1'); + } + return k + 1; +// goto round_9_up; + } + if (j > 0) + dig++; + buf.append(dig); + return k + 1; + } + if ((j < 0) + || ((j == 0) + && (mode == 0) + && ((word1(d) & 1) == 0) + )) { + if (j1 > 0) { + /* Either dig or dig+1 would work here as the least significant decimal digit. + Use whichever would produce a decimal value closer to d. */ + b = b.shiftLeft(1); + j1 = b.compareTo(S); + if (((j1 > 0) || (j1 == 0 && (((dig & 1) == 1) || biasUp))) + && (dig++ == '9')) { + buf.append('9'); + if (roundOff(buf)) { + k++; + buf.append('1'); + } + return k + 1; +// goto round_9_up; + } + } + buf.append(dig); + return k + 1; + } + if (j1 > 0) { + if (dig == '9') { /* possible if i == 1 */ +// round_9_up: +// *s++ = '9'; +// goto roundoff; + buf.append('9'); + if (roundOff(buf)) { + k++; + buf.append('1'); + } + return k + 1; + } + buf.append((char)(dig + 1)); + return k + 1; + } + buf.append(dig); + if (i == ilim) + break; + b = b.multiply(BigInteger.valueOf(10)); + if (mlo == mhi) + mlo = mhi = mhi.multiply(BigInteger.valueOf(10)); + else { + mlo = mlo.multiply(BigInteger.valueOf(10)); + mhi = mhi.multiply(BigInteger.valueOf(10)); + } + } + } + else + for(i = 1;; i++) { +// (char)(dig = quorem(b,S) + '0'); + BigInteger[] divResult = b.divideAndRemainder(S); + b = divResult[1]; + dig = (char)(divResult[0].intValue() + '0'); + buf.append(dig); + if (i >= ilim) + break; + b = b.multiply(BigInteger.valueOf(10)); + } + + /* Round off last digit */ + + b = b.shiftLeft(1); + j = b.compareTo(S); + if ((j > 0) || (j == 0 && (((dig & 1) == 1) || biasUp))) { +// roundoff: +// while(*--s == '9') +// if (s == buf) { +// k++; +// *s++ = '1'; +// goto ret; +// } +// ++*s++; + if (roundOff(buf)) { + k++; + buf.append('1'); + return k + 1; + } + } + else { + stripTrailingZeroes(buf); +// while(*--s == '0') ; +// s++; + } +// ret: +// Bfree(S); +// if (mhi) { +// if (mlo && mlo != mhi) +// Bfree(mlo); +// Bfree(mhi); +// } +// ret1: +// Bfree(b); +// JS_ASSERT(s < buf + bufsize); + return k + 1; + } + + private static void + stripTrailingZeroes(StringBuffer buf) + { +// while(*--s == '0') ; +// s++; + int bl = buf.length(); + while(bl-->0 && buf.charAt(bl) == '0') { + // empty + } + buf.setLength(bl + 1); + } + + /* Mapping of JSDToStrMode -> JS_dtoa mode */ + private static final int dtoaModes[] = { + 0, /* DTOSTR_STANDARD */ + 0, /* DTOSTR_STANDARD_EXPONENTIAL, */ + 3, /* DTOSTR_FIXED, */ + 2, /* DTOSTR_EXPONENTIAL, */ + 2}; /* DTOSTR_PRECISION */ + + static void + JS_dtostr(StringBuffer buffer, int mode, int precision, double d) + { + int decPt; /* Position of decimal point relative to first digit returned by JS_dtoa */ + boolean[] sign = new boolean[1]; /* true if the sign bit was set in d */ + int nDigits; /* Number of significand digits returned by JS_dtoa */ + +// JS_ASSERT(bufferSize >= (size_t)(mode <= DTOSTR_STANDARD_EXPONENTIAL ? DTOSTR_STANDARD_BUFFER_SIZE : +// DTOSTR_VARIABLE_BUFFER_SIZE(precision))); + + if (mode == DTOSTR_FIXED && (d >= 1e21 || d <= -1e21)) + mode = DTOSTR_STANDARD; /* Change mode here rather than below because the buffer may not be large enough to hold a large integer. */ + + decPt = JS_dtoa(d, dtoaModes[mode], mode >= DTOSTR_FIXED, precision, sign, buffer); + nDigits = buffer.length(); + + /* If Infinity, -Infinity, or NaN, return the string regardless of the mode. */ + if (decPt != 9999) { + boolean exponentialNotation = false; + int minNDigits = 0; /* Minimum number of significand digits required by mode and precision */ + int p; + + switch (mode) { + case DTOSTR_STANDARD: + if (decPt < -5 || decPt > 21) + exponentialNotation = true; + else + minNDigits = decPt; + break; + + case DTOSTR_FIXED: + if (precision >= 0) + minNDigits = decPt + precision; + else + minNDigits = decPt; + break; + + case DTOSTR_EXPONENTIAL: +// JS_ASSERT(precision > 0); + minNDigits = precision; + /* Fall through */ + case DTOSTR_STANDARD_EXPONENTIAL: + exponentialNotation = true; + break; + + case DTOSTR_PRECISION: +// JS_ASSERT(precision > 0); + minNDigits = precision; + if (decPt < -5 || decPt > precision) + exponentialNotation = true; + break; + } + + /* If the number has fewer than minNDigits, pad it with zeros at the end */ + if (nDigits < minNDigits) { + p = minNDigits; + nDigits = minNDigits; + do { + buffer.append('0'); + } while (buffer.length() != p); + } + + if (exponentialNotation) { + /* Insert a decimal point if more than one significand digit */ + if (nDigits != 1) { + buffer.insert(1, '.'); + } + buffer.append('e'); + if ((decPt - 1) >= 0) + buffer.append('+'); + buffer.append(decPt - 1); +// JS_snprintf(numEnd, bufferSize - (numEnd - buffer), "e%+d", decPt-1); + } else if (decPt != nDigits) { + /* Some kind of a fraction in fixed notation */ +// JS_ASSERT(decPt <= nDigits); + if (decPt > 0) { + /* dd...dd . dd...dd */ + buffer.insert(decPt, '.'); + } else { + /* 0 . 00...00dd...dd */ + for (int i = 0; i < 1 - decPt; i++) + buffer.insert(0, '0'); + buffer.insert(1, '.'); + } + } + } + + /* If negative and neither -0.0 nor NaN, output a leading '-'. */ + if (sign[0] && + !(word0(d) == Sign_bit && word1(d) == 0) && + !((word0(d) & Exp_mask) == Exp_mask && + ((word1(d) != 0) || ((word0(d) & Frac_mask) != 0)))) { + buffer.insert(0, '-'); + } + } + +} + diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Decompiler.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Decompiler.java new file mode 100644 index 0000000..8547d37 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Decompiler.java @@ -0,0 +1,918 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mike Ang + * Igor Bukanov + * Bob Jervis + * Mike McCabe + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript; + +/** + * The following class save decompilation information about the source. + * Source information is returned from the parser as a String + * associated with function nodes and with the toplevel script. When + * saved in the constant pool of a class, this string will be UTF-8 + * encoded, and token values will occupy a single byte. + + * Source is saved (mostly) as token numbers. The tokens saved pretty + * much correspond to the token stream of a 'canonical' representation + * of the input program, as directed by the parser. (There were a few + * cases where tokens could have been left out where decompiler could + * easily reconstruct them, but I left them in for clarity). (I also + * looked adding source collection to TokenStream instead, where I + * could have limited the changes to a few lines in getToken... but + * this wouldn't have saved any space in the resulting source + * representation, and would have meant that I'd have to duplicate + * parser logic in the decompiler to disambiguate situations where + * newlines are important.) The function decompile expands the + * tokens back into their string representations, using simple + * lookahead to correct spacing and indentation. + * + * Assignments are saved as two-token pairs (Token.ASSIGN, op). Number tokens + * are stored inline, as a NUMBER token, a character representing the type, and + * either 1 or 4 characters representing the bit-encoding of the number. String + * types NAME, STRING and OBJECT are currently stored as a token type, + * followed by a character giving the length of the string (assumed to + * be less than 2^16), followed by the characters of the string + * inlined into the source string. Changing this to some reference to + * to the string in the compiled class' constant pool would probably + * save a lot of space... but would require some method of deriving + * the final constant pool entry from information available at parse + * time. + */ +public class Decompiler +{ + /** + * Flag to indicate that the decompilation should omit the + * function header and trailing brace. + */ + public static final int ONLY_BODY_FLAG = 1 << 0; + + /** + * Flag to indicate that the decompilation generates toSource result. + */ + public static final int TO_SOURCE_FLAG = 1 << 1; + + /** + * Decompilation property to specify initial ident value. + */ + public static final int INITIAL_INDENT_PROP = 1; + + /** + * Decompilation property to specify default identation offset. + */ + public static final int INDENT_GAP_PROP = 2; + + /** + * Decompilation property to specify identation offset for case labels. + */ + public static final int CASE_GAP_PROP = 3; + + // Marker to denote the last RC of function so it can be distinguished from + // the last RC of object literals in case of function expressions + private static final int FUNCTION_END = Token.LAST_TOKEN + 1; + + String getEncodedSource() + { + return sourceToString(0); + } + + int getCurrentOffset() + { + return sourceTop; + } + + int markFunctionStart(int functionType) + { + int savedOffset = getCurrentOffset(); + addToken(Token.FUNCTION); + append((char)functionType); + return savedOffset; + } + + int markFunctionEnd(int functionStart) + { + int offset = getCurrentOffset(); + append((char)FUNCTION_END); + return offset; + } + + void addToken(int token) + { + if (!(0 <= token && token <= Token.LAST_TOKEN)) + throw new IllegalArgumentException(); + + append((char)token); + } + + void addEOL(int token) + { + if (!(0 <= token && token <= Token.LAST_TOKEN)) + throw new IllegalArgumentException(); + + append((char)token); + append((char)Token.EOL); + } + + void addName(String str) + { + addToken(Token.NAME); + appendString(str); + } + + void addString(String str) + { + addToken(Token.STRING); + appendString(str); + } + + void addRegexp(String regexp, String flags) + { + addToken(Token.REGEXP); + appendString('/' + regexp + '/' + flags); + } + + void addNumber(double n) + { + addToken(Token.NUMBER); + + /* encode the number in the source stream. + * Save as NUMBER type (char | char char char char) + * where type is + * 'D' - double, 'S' - short, 'J' - long. + + * We need to retain float vs. integer type info to keep the + * behavior of liveconnect type-guessing the same after + * decompilation. (Liveconnect tries to present 1.0 to Java + * as a float/double) + * OPT: This is no longer true. We could compress the format. + + * This may not be the most space-efficient encoding; + * the chars created below may take up to 3 bytes in + * constant pool UTF-8 encoding, so a Double could take + * up to 12 bytes. + */ + + long lbits = (long)n; + if (lbits != n) { + // if it's floating point, save as a Double bit pattern. + // (12/15/97 our scanner only returns Double for f.p.) + lbits = Double.doubleToLongBits(n); + append('D'); + append((char)(lbits >> 48)); + append((char)(lbits >> 32)); + append((char)(lbits >> 16)); + append((char)lbits); + } + else { + // we can ignore negative values, bc they're already prefixed + // by NEG + if (lbits < 0) Kit.codeBug(); + + // will it fit in a char? + // this gives a short encoding for integer values up to 2^16. + if (lbits <= Character.MAX_VALUE) { + append('S'); + append((char)lbits); + } + else { // Integral, but won't fit in a char. Store as a long. + append('J'); + append((char)(lbits >> 48)); + append((char)(lbits >> 32)); + append((char)(lbits >> 16)); + append((char)lbits); + } + } + } + + private void appendString(String str) + { + int L = str.length(); + int lengthEncodingSize = 1; + if (L >= 0x8000) { + lengthEncodingSize = 2; + } + int nextTop = sourceTop + lengthEncodingSize + L; + if (nextTop > sourceBuffer.length) { + increaseSourceCapacity(nextTop); + } + if (L >= 0x8000) { + // Use 2 chars to encode strings exceeding 32K, were the highest + // bit in the first char indicates presence of the next byte + sourceBuffer[sourceTop] = (char)(0x8000 | (L >>> 16)); + ++sourceTop; + } + sourceBuffer[sourceTop] = (char)L; + ++sourceTop; + str.getChars(0, L, sourceBuffer, sourceTop); + sourceTop = nextTop; + } + + private void append(char c) + { + if (sourceTop == sourceBuffer.length) { + increaseSourceCapacity(sourceTop + 1); + } + sourceBuffer[sourceTop] = c; + ++sourceTop; + } + + private void increaseSourceCapacity(int minimalCapacity) + { + // Call this only when capacity increase is must + if (minimalCapacity <= sourceBuffer.length) Kit.codeBug(); + int newCapacity = sourceBuffer.length * 2; + if (newCapacity < minimalCapacity) { + newCapacity = minimalCapacity; + } + char[] tmp = new char[newCapacity]; + System.arraycopy(sourceBuffer, 0, tmp, 0, sourceTop); + sourceBuffer = tmp; + } + + private String sourceToString(int offset) + { + if (offset < 0 || sourceTop < offset) Kit.codeBug(); + return new String(sourceBuffer, offset, sourceTop - offset); + } + + /** + * Decompile the source information associated with this js + * function/script back into a string. For the most part, this + * just means translating tokens back to their string + * representations; there's a little bit of lookahead logic to + * decide the proper spacing/indentation. Most of the work in + * mapping the original source to the prettyprinted decompiled + * version is done by the parser. + * + * @param source encoded source tree presentation + * + * @param flags flags to select output format + * + * @param properties indentation properties + * + */ + public static String decompile(String source, int flags, + UintMap properties) + { + int length = source.length(); + if (length == 0) { return ""; } + + int indent = properties.getInt(INITIAL_INDENT_PROP, 0); + if (indent < 0) throw new IllegalArgumentException(); + int indentGap = properties.getInt(INDENT_GAP_PROP, 4); + if (indentGap < 0) throw new IllegalArgumentException(); + int caseGap = properties.getInt(CASE_GAP_PROP, 2); + if (caseGap < 0) throw new IllegalArgumentException(); + + StringBuffer result = new StringBuffer(); + boolean justFunctionBody = (0 != (flags & Decompiler.ONLY_BODY_FLAG)); + boolean toSource = (0 != (flags & Decompiler.TO_SOURCE_FLAG)); + + // Spew tokens in source, for debugging. + // as TYPE number char + if (printSource) { + System.err.println("length:" + length); + for (int i = 0; i < length; ++i) { + // Note that tokenToName will fail unless Context.printTrees + // is true. + String tokenname = null; + if (Token.printNames) { + tokenname = Token.name(source.charAt(i)); + } + if (tokenname == null) { + tokenname = "---"; + } + String pad = tokenname.length() > 7 + ? "\t" + : "\t\t"; + System.err.println + (tokenname + + pad + (int)source.charAt(i) + + "\t'" + ScriptRuntime.escapeString + (source.substring(i, i+1)) + + "'"); + } + System.err.println(); + } + + int braceNesting = 0; + boolean afterFirstEOL = false; + int i = 0; + int topFunctionType; + if (source.charAt(i) == Token.SCRIPT) { + ++i; + topFunctionType = -1; + } else { + topFunctionType = source.charAt(i + 1); + } + + if (!toSource) { + // add an initial newline to exactly match js. + result.append('\n'); + for (int j = 0; j < indent; j++) + result.append(' '); + } else { + if (topFunctionType == FunctionNode.FUNCTION_EXPRESSION) { + result.append('('); + } + } + + while (i < length) { + switch(source.charAt(i)) { + case Token.GET: + case Token.SET: + result.append(source.charAt(i) == Token.GET ? "get " : "set "); + ++i; + i = printSourceString(source, i + 1, false, result); + // Now increment one more to get past the FUNCTION token + ++i; + break; + + case Token.NAME: + case Token.REGEXP: // re-wrapped in '/'s in parser... + i = printSourceString(source, i + 1, false, result); + continue; + + case Token.STRING: + i = printSourceString(source, i + 1, true, result); + continue; + + case Token.NUMBER: + i = printSourceNumber(source, i + 1, result); + continue; + + case Token.TRUE: + result.append("true"); + break; + + case Token.FALSE: + result.append("false"); + break; + + case Token.NULL: + result.append("null"); + break; + + case Token.THIS: + result.append("this"); + break; + + case Token.FUNCTION: + ++i; // skip function type + result.append("function "); + break; + + case FUNCTION_END: + // Do nothing + break; + + case Token.COMMA: + result.append(", "); + break; + + case Token.LC: + ++braceNesting; + if (Token.EOL == getNext(source, length, i)) + indent += indentGap; + result.append('{'); + break; + + case Token.RC: { + --braceNesting; + /* don't print the closing RC if it closes the + * toplevel function and we're called from + * decompileFunctionBody. + */ + if (justFunctionBody && braceNesting == 0) + break; + + result.append('}'); + switch (getNext(source, length, i)) { + case Token.EOL: + case FUNCTION_END: + indent -= indentGap; + break; + case Token.WHILE: + case Token.ELSE: + indent -= indentGap; + result.append(' '); + break; + } + break; + } + case Token.LP: + result.append('('); + break; + + case Token.RP: + result.append(')'); + if (Token.LC == getNext(source, length, i)) + result.append(' '); + break; + + case Token.LB: + result.append('['); + break; + + case Token.RB: + result.append(']'); + break; + + case Token.EOL: { + if (toSource) break; + boolean newLine = true; + if (!afterFirstEOL) { + afterFirstEOL = true; + if (justFunctionBody) { + /* throw away just added 'function name(...) {' + * and restore the original indent + */ + result.setLength(0); + indent -= indentGap; + newLine = false; + } + } + if (newLine) { + result.append('\n'); + } + + /* add indent if any tokens remain, + * less setback if next token is + * a label, case or default. + */ + if (i + 1 < length) { + int less = 0; + int nextToken = source.charAt(i + 1); + if (nextToken == Token.CASE + || nextToken == Token.DEFAULT) + { + less = indentGap - caseGap; + } else if (nextToken == Token.RC) { + less = indentGap; + } + + /* elaborate check against label... skip past a + * following inlined NAME and look for a COLON. + */ + else if (nextToken == Token.NAME) { + int afterName = getSourceStringEnd(source, i + 2); + if (source.charAt(afterName) == Token.COLON) + less = indentGap; + } + + for (; less < indent; less++) + result.append(' '); + } + break; + } + case Token.DOT: + result.append('.'); + break; + + case Token.NEW: + result.append("new "); + break; + + case Token.DELPROP: + result.append("delete "); + break; + + case Token.IF: + result.append("if "); + break; + + case Token.ELSE: + result.append("else "); + break; + + case Token.FOR: + result.append("for "); + break; + + case Token.IN: + result.append(" in "); + break; + + case Token.WITH: + result.append("with "); + break; + + case Token.WHILE: + result.append("while "); + break; + + case Token.DO: + result.append("do "); + break; + + case Token.TRY: + result.append("try "); + break; + + case Token.CATCH: + result.append("catch "); + break; + + case Token.FINALLY: + result.append("finally "); + break; + + case Token.THROW: + result.append("throw "); + break; + + case Token.SWITCH: + result.append("switch "); + break; + + case Token.BREAK: + result.append("break"); + if (Token.NAME == getNext(source, length, i)) + result.append(' '); + break; + + case Token.CONTINUE: + result.append("continue"); + if (Token.NAME == getNext(source, length, i)) + result.append(' '); + break; + + case Token.CASE: + result.append("case "); + break; + + case Token.DEFAULT: + result.append("default"); + break; + + case Token.RETURN: + result.append("return"); + if (Token.SEMI != getNext(source, length, i)) + result.append(' '); + break; + + case Token.VAR: + result.append("var "); + break; + + case Token.LET: + result.append("let "); + break; + + case Token.SEMI: + result.append(';'); + if (Token.EOL != getNext(source, length, i)) { + // separators in FOR + result.append(' '); + } + break; + + case Token.ASSIGN: + result.append(" = "); + break; + + case Token.ASSIGN_ADD: + result.append(" += "); + break; + + case Token.ASSIGN_SUB: + result.append(" -= "); + break; + + case Token.ASSIGN_MUL: + result.append(" *= "); + break; + + case Token.ASSIGN_DIV: + result.append(" /= "); + break; + + case Token.ASSIGN_MOD: + result.append(" %= "); + break; + + case Token.ASSIGN_BITOR: + result.append(" |= "); + break; + + case Token.ASSIGN_BITXOR: + result.append(" ^= "); + break; + + case Token.ASSIGN_BITAND: + result.append(" &= "); + break; + + case Token.ASSIGN_LSH: + result.append(" <<= "); + break; + + case Token.ASSIGN_RSH: + result.append(" >>= "); + break; + + case Token.ASSIGN_URSH: + result.append(" >>>= "); + break; + + case Token.HOOK: + result.append(" ? "); + break; + + case Token.OBJECTLIT: + // pun OBJECTLIT to mean colon in objlit property + // initialization. + // This needs to be distinct from COLON in the general case + // to distinguish from the colon in a ternary... which needs + // different spacing. + result.append(':'); + break; + + case Token.COLON: + if (Token.EOL == getNext(source, length, i)) + // it's the end of a label + result.append(':'); + else + // it's the middle part of a ternary + result.append(" : "); + break; + + case Token.OR: + result.append(" || "); + break; + + case Token.AND: + result.append(" && "); + break; + + case Token.BITOR: + result.append(" | "); + break; + + case Token.BITXOR: + result.append(" ^ "); + break; + + case Token.BITAND: + result.append(" & "); + break; + + case Token.SHEQ: + result.append(" === "); + break; + + case Token.SHNE: + result.append(" !== "); + break; + + case Token.EQ: + result.append(" == "); + break; + + case Token.NE: + result.append(" != "); + break; + + case Token.LE: + result.append(" <= "); + break; + + case Token.LT: + result.append(" < "); + break; + + case Token.GE: + result.append(" >= "); + break; + + case Token.GT: + result.append(" > "); + break; + + case Token.INSTANCEOF: + result.append(" instanceof "); + break; + + case Token.LSH: + result.append(" << "); + break; + + case Token.RSH: + result.append(" >> "); + break; + + case Token.URSH: + result.append(" >>> "); + break; + + case Token.TYPEOF: + result.append("typeof "); + break; + + case Token.VOID: + result.append("void "); + break; + + case Token.CONST: + result.append("const "); + break; + + case Token.YIELD: + result.append("yield "); + break; + + case Token.NOT: + result.append('!'); + break; + + case Token.BITNOT: + result.append('~'); + break; + + case Token.POS: + result.append('+'); + break; + + case Token.NEG: + result.append('-'); + break; + + case Token.INC: + result.append("++"); + break; + + case Token.DEC: + result.append("--"); + break; + + case Token.ADD: + result.append(" + "); + break; + + case Token.SUB: + result.append(" - "); + break; + + case Token.MUL: + result.append(" * "); + break; + + case Token.DIV: + result.append(" / "); + break; + + case Token.MOD: + result.append(" % "); + break; + + case Token.COLONCOLON: + result.append("::"); + break; + + case Token.DOTDOT: + result.append(".."); + break; + + case Token.DOTQUERY: + result.append(".("); + break; + + case Token.XMLATTR: + result.append('@'); + break; + + default: + // If we don't know how to decompile it, raise an exception. + throw new RuntimeException("Token: " + + Token.name(source.charAt(i))); + } + ++i; + } + + if (!toSource) { + // add that trailing newline if it's an outermost function. + if (!justFunctionBody) + result.append('\n'); + } else { + if (topFunctionType == FunctionNode.FUNCTION_EXPRESSION) { + result.append(')'); + } + } + + return result.toString(); + } + + private static int getNext(String source, int length, int i) + { + return (i + 1 < length) ? source.charAt(i + 1) : Token.EOF; + } + + private static int getSourceStringEnd(String source, int offset) + { + return printSourceString(source, offset, false, null); + } + + private static int printSourceString(String source, int offset, + boolean asQuotedString, + StringBuffer sb) + { + int length = source.charAt(offset); + ++offset; + if ((0x8000 & length) != 0) { + length = ((0x7FFF & length) << 16) | source.charAt(offset); + ++offset; + } + if (sb != null) { + String str = source.substring(offset, offset + length); + if (!asQuotedString) { + sb.append(str); + } else { + sb.append('"'); + sb.append(ScriptRuntime.escapeString(str)); + sb.append('"'); + } + } + return offset + length; + } + + private static int printSourceNumber(String source, int offset, + StringBuffer sb) + { + double number = 0.0; + char type = source.charAt(offset); + ++offset; + if (type == 'S') { + if (sb != null) { + int ival = source.charAt(offset); + number = ival; + } + ++offset; + } else if (type == 'J' || type == 'D') { + if (sb != null) { + long lbits; + lbits = (long)source.charAt(offset) << 48; + lbits |= (long)source.charAt(offset + 1) << 32; + lbits |= (long)source.charAt(offset + 2) << 16; + lbits |= source.charAt(offset + 3); + if (type == 'J') { + number = lbits; + } else { + number = Double.longBitsToDouble(lbits); + } + } + offset += 4; + } else { + // Bad source + throw new RuntimeException(); + } + if (sb != null) { + sb.append(ScriptRuntime.numberToString(number, 10)); + } + return offset; + } + + private char[] sourceBuffer = new char[128]; + +// Per script/function source buffer top: parent source does not include a +// nested functions source and uses function index as a reference instead. + private int sourceTop; + +// whether to do a debug print of the source information, when decompiling. + private static final boolean printSource = false; + +} diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/DefaultErrorReporter.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/DefaultErrorReporter.java new file mode 100644 index 0000000..c7d93d4 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/DefaultErrorReporter.java @@ -0,0 +1,113 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Norris Boyd + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript; + +/** + * This is the default error reporter for JavaScript. + * + * @author Norris Boyd + */ +class DefaultErrorReporter implements ErrorReporter +{ + static final DefaultErrorReporter instance = new DefaultErrorReporter(); + + private boolean forEval; + private ErrorReporter chainedReporter; + + private DefaultErrorReporter() { } + + static ErrorReporter forEval(ErrorReporter reporter) + { + DefaultErrorReporter r = new DefaultErrorReporter(); + r.forEval = true; + r.chainedReporter = reporter; + return r; + } + + public void warning(String message, String sourceURI, int line, + String lineText, int lineOffset) + { + if (chainedReporter != null) { + chainedReporter.warning( + message, sourceURI, line, lineText, lineOffset); + } else { + // Do nothing + } + } + + public void error(String message, String sourceURI, int line, + String lineText, int lineOffset) + { + if (forEval) { + // Assume error message strings that start with "TypeError: " + // should become TypeError exceptions. A bit of a hack, but we + // don't want to change the ErrorReporter interface. + String error = "SyntaxError"; + final String TYPE_ERROR_NAME = "TypeError"; + final String DELIMETER = ": "; + final String prefix = TYPE_ERROR_NAME + DELIMETER; + if (message.startsWith(prefix)) { + error = TYPE_ERROR_NAME; + message = message.substring(prefix.length()); + } + throw ScriptRuntime.constructError(error, message, sourceURI, + line, lineText, lineOffset); + } + if (chainedReporter != null) { + chainedReporter.error( + message, sourceURI, line, lineText, lineOffset); + } else { + throw runtimeError( + message, sourceURI, line, lineText, lineOffset); + } + } + + public EvaluatorException runtimeError(String message, String sourceURI, + int line, String lineText, + int lineOffset) + { + if (chainedReporter != null) { + return chainedReporter.runtimeError( + message, sourceURI, line, lineText, lineOffset); + } else { + return new EvaluatorException( + message, sourceURI, line, lineText, lineOffset); + } + } +} diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/DefiningClassLoader.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/DefiningClassLoader.java new file mode 100644 index 0000000..5864b5d --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/DefiningClassLoader.java @@ -0,0 +1,88 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Norris Boyd + * Roger Lawrence + * Patrick Beard + * Igor Bukanov + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript; + +/** + * Load generated classes. + * + * @author Norris Boyd + */ +public class DefiningClassLoader extends ClassLoader + implements GeneratedClassLoader +{ + public DefiningClassLoader() { + this.parentLoader = getClass().getClassLoader(); + } + + public DefiningClassLoader(ClassLoader parentLoader) { + this.parentLoader = parentLoader; + } + + public Class defineClass(String name, byte[] data) { + // Use our own protection domain for the generated classes. + // TODO: we might want to use a separate protection domain for classes + // compiled from scripts, based on where the script was loaded from. + return super.defineClass(name, data, 0, data.length, + SecurityUtilities.getProtectionDomain(getClass())); + } + + public void linkClass(Class cl) { + resolveClass(cl); + } + + public Class loadClass(String name, boolean resolve) + throws ClassNotFoundException + { + Class cl = findLoadedClass(name); + if (cl == null) { + if (parentLoader != null) { + cl = parentLoader.loadClass(name); + } else { + cl = findSystemClass(name); + } + } + if (resolve) { + resolveClass(cl); + } + return cl; + } + + private final ClassLoader parentLoader; +} diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Delegator.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Delegator.java new file mode 100644 index 0000000..e044863 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Delegator.java @@ -0,0 +1,266 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Delegator.java, released + * Sep 27, 2000. + * + * The Initial Developer of the Original Code is + * Matthias Radestock. . + * Portions created by the Initial Developer are Copyright (C) 2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +// API class + +package org.mozilla.javascript; + +/** + * This is a helper class for implementing wrappers around Scriptable + * objects. It implements the Function interface and delegates all + * invocations to a delegee Scriptable object. The normal use of this + * class involves creating a sub-class and overriding one or more of + * the methods. + * + * A useful application is the implementation of interceptors, + * pre/post conditions, debugging. + * + * @see Function + * @see Scriptable + * @author Matthias Radestock + */ + +public class Delegator implements Function { + + protected Scriptable obj = null; + + /** + * Create a Delegator prototype. + * + * This constructor should only be used for creating prototype + * objects of Delegator. + * + * @see org.mozilla.javascript.Delegator#construct + */ + public Delegator() { + } + + /** + * Create a new Delegator that forwards requests to a delegee + * Scriptable object. + * + * @param obj the delegee + * @see org.mozilla.javascript.Scriptable + */ + public Delegator(Scriptable obj) { + this.obj = obj; + } + + /** + * Crete new Delegator instance. + * The default implementation calls this.getClass().newInstance(). + * + * @see #construct(Context cx, Scriptable scope, Object[] args) + */ + protected Delegator newInstance() + { + try { + return this.getClass().newInstance(); + } catch (Exception ex) { + throw Context.throwAsScriptRuntimeEx(ex); + } + } + + /** + * Retrieve the delegee. + * + * @return the delegee + */ + public Scriptable getDelegee() { + return obj; + } + /** + * Set the delegee. + * + * @param obj the delegee + * @see org.mozilla.javascript.Scriptable + */ + public void setDelegee(Scriptable obj) { + this.obj = obj; + } + /** + * @see org.mozilla.javascript.Scriptable#getClassName + */ + public String getClassName() { + return obj.getClassName(); + } + /** + * @see org.mozilla.javascript.Scriptable#get(String, Scriptable) + */ + public Object get(String name, Scriptable start) { + return obj.get(name,start); + } + /** + * @see org.mozilla.javascript.Scriptable#get(int, Scriptable) + */ + public Object get(int index, Scriptable start) { + return obj.get(index,start); + } + /** + * @see org.mozilla.javascript.Scriptable#has(String, Scriptable) + */ + public boolean has(String name, Scriptable start) { + return obj.has(name,start); + } + /** + * @see org.mozilla.javascript.Scriptable#has(int, Scriptable) + */ + public boolean has(int index, Scriptable start) { + return obj.has(index,start); + } + /** + * @see org.mozilla.javascript.Scriptable#put(String, Scriptable, Object) + */ + public void put(String name, Scriptable start, Object value) { + obj.put(name,start,value); + } + /** + * @see org.mozilla.javascript.Scriptable#put(int, Scriptable, Object) + */ + public void put(int index, Scriptable start, Object value) { + obj.put(index,start,value); + } + /** + * @see org.mozilla.javascript.Scriptable#delete(String) + */ + public void delete(String name) { + obj.delete(name); + } + /** + * @see org.mozilla.javascript.Scriptable#delete(int) + */ + public void delete(int index) { + obj.delete(index); + } + /** + * @see org.mozilla.javascript.Scriptable#getPrototype + */ + public Scriptable getPrototype() { + return obj.getPrototype(); + } + /** + * @see org.mozilla.javascript.Scriptable#setPrototype + */ + public void setPrototype(Scriptable prototype) { + obj.setPrototype(prototype); + } + /** + * @see org.mozilla.javascript.Scriptable#getParentScope + */ + public Scriptable getParentScope() { + return obj.getParentScope(); + } + /** + * @see org.mozilla.javascript.Scriptable#setParentScope + */ + public void setParentScope(Scriptable parent) { + obj.setParentScope(parent); + } + /** + * @see org.mozilla.javascript.Scriptable#getIds + */ + public Object[] getIds() { + return obj.getIds(); + } + /** + * Note that this method does not get forwarded to the delegee if + * the hint parameter is null, + * ScriptRuntime.ScriptableClass or + * ScriptRuntime.FunctionClass. Instead the object + * itself is returned. + * + * @param hint the type hint + * @return the default value + * + * @see org.mozilla.javascript.Scriptable#getDefaultValue + */ + public Object getDefaultValue(Class hint) { + return (hint == null || + hint == ScriptRuntime.ScriptableClass || + hint == ScriptRuntime.FunctionClass) ? + this : obj.getDefaultValue(hint); + } + /** + * @see org.mozilla.javascript.Scriptable#hasInstance + */ + public boolean hasInstance(Scriptable instance) { + return obj.hasInstance(instance); + } + /** + * @see org.mozilla.javascript.Function#call + */ + public Object call(Context cx, Scriptable scope, Scriptable thisObj, + Object[] args) + { + return ((Function)obj).call(cx,scope,thisObj,args); + } + + /** + * Note that if the delegee is null, + * this method creates a new instance of the Delegator itself + * rathert than forwarding the call to the + * delegee. This permits the use of Delegator + * prototypes. + * + * @param cx the current Context for this thread + * @param scope an enclosing scope of the caller except + * when the function is called from a closure. + * @param args the array of arguments + * @return the allocated object + * + * @see Function#construct(Context, Scriptable, Object[]) + */ + public Scriptable construct(Context cx, Scriptable scope, Object[] args) + { + if (obj == null) { + //this little trick allows us to declare prototype objects for + //Delegators + Delegator n = newInstance(); + Scriptable delegee; + if (args.length == 0) { + delegee = new NativeObject(); + } else { + delegee = ScriptRuntime.toObject(cx, scope, args[0]); + } + n.setDelegee(delegee); + return n; + } + else { + return ((Function)obj).construct(cx,scope,args); + } + } +} diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/EcmaError.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/EcmaError.java new file mode 100644 index 0000000..1fd8f03 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/EcmaError.java @@ -0,0 +1,160 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Roger Lawrence + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +// API class + +package org.mozilla.javascript; + +/** + * The class of exceptions raised by the engine as described in + * ECMA edition 3. See section 15.11.6 in particular. + */ +public class EcmaError extends RhinoException +{ + static final long serialVersionUID = -6261226256957286699L; + + private String errorName; + private String errorMessage; + + /** + * Create an exception with the specified detail message. + * + * Errors internal to the JavaScript engine will simply throw a + * RuntimeException. + * + * @param sourceName the name of the source reponsible for the error + * @param lineNumber the line number of the source + * @param columnNumber the columnNumber of the source (may be zero if + * unknown) + * @param lineSource the source of the line containing the error (may be + * null if unknown) + */ + EcmaError(String errorName, String errorMessage, + String sourceName, int lineNumber, + String lineSource, int columnNumber) + { + recordErrorOrigin(sourceName, lineNumber, lineSource, columnNumber); + this.errorName = errorName; + this.errorMessage = errorMessage; + } + + /** + * @deprecated EcmaError error instances should not be constructed + * explicitly since they are generated by the engine. + */ + public EcmaError(Scriptable nativeError, String sourceName, + int lineNumber, int columnNumber, String lineSource) + { + this("InternalError", ScriptRuntime.toString(nativeError), + sourceName, lineNumber, lineSource, columnNumber); + } + + public String details() + { + return errorName+": "+errorMessage; + } + + /** + * Gets the name of the error. + * + * ECMA edition 3 defines the following + * errors: EvalError, RangeError, ReferenceError, + * SyntaxError, TypeError, and URIError. Additional error names + * may be added in the future. + * + * See ECMA edition 3, 15.11.7.9. + * + * @return the name of the error. + */ + public String getName() + { + return errorName; + } + + /** + * Gets the message corresponding to the error. + * + * See ECMA edition 3, 15.11.7.10. + * + * @return an implemenation-defined string describing the error. + */ + public String getErrorMessage() + { + return errorMessage; + } + + /** + * @deprecated Use {@link RhinoException#sourceName()} from the super class. + */ + public String getSourceName() + { + return sourceName(); + } + + /** + * @deprecated Use {@link RhinoException#lineNumber()} from the super class. + */ + public int getLineNumber() + { + return lineNumber(); + } + + /** + * @deprecated + * Use {@link RhinoException#columnNumber()} from the super class. + */ + public int getColumnNumber() { + return columnNumber(); + } + + /** + * @deprecated Use {@link RhinoException#lineSource()} from the super class. + */ + public String getLineSource() { + return lineSource(); + } + + /** + * @deprecated + * Always returns null. + */ + public Scriptable getErrorObject() + { + return null; + } +} diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ErrorReporter.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ErrorReporter.java new file mode 100644 index 0000000..4649370 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ErrorReporter.java @@ -0,0 +1,106 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Norris Boyd + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +// API class + +package org.mozilla.javascript; + +/** + * This is interface defines a protocol for the reporting of + * errors during JavaScript translation or execution. + * + * @author Norris Boyd + */ + +public interface ErrorReporter { + + /** + * Report a warning. + * + * The implementing class may choose to ignore the warning + * if it desires. + * + * @param message a String describing the warning + * @param sourceName a String describing the JavaScript source + * where the warning occured; typically a filename or URL + * @param line the line number associated with the warning + * @param lineSource the text of the line (may be null) + * @param lineOffset the offset into lineSource where problem was detected + */ + void warning(String message, String sourceName, int line, + String lineSource, int lineOffset); + + /** + * Report an error. + * + * The implementing class is free to throw an exception if + * it desires. + * + * If execution has not yet begun, the JavaScript engine is + * free to find additional errors rather than terminating + * the translation. It will not execute a script that had + * errors, however. + * + * @param message a String describing the error + * @param sourceName a String describing the JavaScript source + * where the error occured; typically a filename or URL + * @param line the line number associated with the error + * @param lineSource the text of the line (may be null) + * @param lineOffset the offset into lineSource where problem was detected + */ + void error(String message, String sourceName, int line, + String lineSource, int lineOffset); + + /** + * Creates an EvaluatorException that may be thrown. + * + * runtimeErrors, unlike errors, will always terminate the + * current script. + * + * @param message a String describing the error + * @param sourceName a String describing the JavaScript source + * where the error occured; typically a filename or URL + * @param line the line number associated with the error + * @param lineSource the text of the line (may be null) + * @param lineOffset the offset into lineSource where problem was detected + * @return an EvaluatorException that will be thrown. + */ + EvaluatorException runtimeError(String message, String sourceName, + int line, String lineSource, + int lineOffset); +} diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Evaluator.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Evaluator.java new file mode 100644 index 0000000..e222af3 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Evaluator.java @@ -0,0 +1,118 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Contributor(s): + * Norris Boyd + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript; + +import java.util.List; + +/** + * Abstraction of evaluation, which can be implemented either by an + * interpreter or compiler. + */ +public interface Evaluator { + + /** + * Compile the script or function from intermediate representation + * tree into an executable form. + * + * @param compilerEnv Compiler environment + * @param tree intermediate representation + * @param encodedSource encoding of the source code for decompilation + * @param returnFunction if true, compiling a function + * @return an opaque object that can be passed to either + * createFunctionObject or createScriptObject, depending on the + * value of returnFunction + */ + public Object compile(CompilerEnvirons compilerEnv, + ScriptOrFnNode tree, + String encodedSource, + boolean returnFunction); + + /** + * Create a function object. + * + * @param cx Current context + * @param scope scope of the function + * @param bytecode opaque object returned by compile + * @param staticSecurityDomain security domain + * @return Function object that can be called + */ + public Function createFunctionObject(Context cx, Scriptable scope, + Object bytecode, Object staticSecurityDomain); + + /** + * Create a script object. + * + * @param bytecode opaque object returned by compile + * @param staticSecurityDomain security domain + * @return Script object that can be evaluated + */ + public Script createScriptObject(Object bytecode, + Object staticSecurityDomain); + + /** + * Capture stack information from the given exception. + * @param ex an exception thrown during execution + */ + public void captureStackInfo(RhinoException ex); + + /** + * Get the source position information by examining the stack. + * @param cx Context + * @param linep Array object of length >= 1; getSourcePositionFromStack + * will assign the line number to linep[0]. + * @return the name of the file or other source container + */ + public String getSourcePositionFromStack(Context cx, int[] linep); + + /** + * Given a native stack trace, patch it with script-specific source + * and line information + * @param ex exception + * @param nativeStackTrace the native stack trace + * @return patched stack trace + */ + public String getPatchedStack(RhinoException ex, + String nativeStackTrace); + + /** + * Get the script stack for the given exception + * @param ex exception from execution + * @return list of strings for the stack trace + */ + public List getScriptStack(RhinoException ex); + + /** + * Mark the given script to indicate it was created by a call to + * eval() or to a Function constructor. + * @param script script to mark as from eval + */ + public void setEvalScriptFlag(Script script); +} diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/EvaluatorException.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/EvaluatorException.java new file mode 100644 index 0000000..7b4e7cc --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/EvaluatorException.java @@ -0,0 +1,123 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Norris Boyd + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + + +package org.mozilla.javascript; + +/** + * The class of exceptions thrown by the JavaScript engine. + */ +public class EvaluatorException extends RhinoException +{ + static final long serialVersionUID = -8743165779676009808L; + + public EvaluatorException(String detail) + { + super(detail); + } + + /** + * Create an exception with the specified detail message. + * + * Errors internal to the JavaScript engine will simply throw a + * RuntimeException. + * + * @param detail the error message + * @param sourceName the name of the source reponsible for the error + * @param lineNumber the line number of the source + */ + public EvaluatorException(String detail, String sourceName, + int lineNumber) + { + this(detail, sourceName, lineNumber, null, 0); + } + + /** + * Create an exception with the specified detail message. + * + * Errors internal to the JavaScript engine will simply throw a + * RuntimeException. + * + * @param detail the error message + * @param sourceName the name of the source responsible for the error + * @param lineNumber the line number of the source + * @param columnNumber the columnNumber of the source (may be zero if + * unknown) + * @param lineSource the source of the line containing the error (may be + * null if unknown) + */ + public EvaluatorException(String detail, String sourceName, int lineNumber, + String lineSource, int columnNumber) + { + super(detail); + recordErrorOrigin(sourceName, lineNumber, lineSource, columnNumber); + } + + /** + * @deprecated Use {@link RhinoException#sourceName()} from the super class. + */ + public String getSourceName() + { + return sourceName(); + } + + /** + * @deprecated Use {@link RhinoException#lineNumber()} from the super class. + */ + public int getLineNumber() + { + return lineNumber(); + } + + /** + * @deprecated Use {@link RhinoException#columnNumber()} from the super class. + */ + public int getColumnNumber() + { + return columnNumber(); + } + + /** + * @deprecated Use {@link RhinoException#lineSource()} from the super class. + */ + public String getLineSource() + { + return lineSource(); + } + +} diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Function.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Function.java new file mode 100644 index 0000000..a4377e6 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Function.java @@ -0,0 +1,84 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Norris Boyd + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +// API class + +package org.mozilla.javascript; + +/** + * This is interface that all functions in JavaScript must implement. + * The interface provides for calling functions and constructors. + * + * @see org.mozilla.javascript.Scriptable + * @author Norris Boyd + */ + +public interface Function extends Scriptable, Callable +{ + /** + * Call the function. + * + * Note that the array of arguments is not guaranteed to have + * length greater than 0. + * + * @param cx the current Context for this thread + * @param scope the scope to execute the function relative to. This is + * set to the value returned by getParentScope() except + * when the function is called from a closure. + * @param thisObj the JavaScript this object + * @param args the array of arguments + * @return the result of the call + */ + public Object call(Context cx, Scriptable scope, Scriptable thisObj, + Object[] args); + + /** + * Call the function as a constructor. + * + * This method is invoked by the runtime in order to satisfy a use + * of the JavaScript new operator. This method is + * expected to create a new object and return it. + * + * @param cx the current Context for this thread + * @param scope an enclosing scope of the caller except + * when the function is called from a closure. + * @param args the array of arguments + * @return the allocated object + */ + public Scriptable construct(Context cx, Scriptable scope, Object[] args); +} diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/FunctionNode.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/FunctionNode.java new file mode 100644 index 0000000..484167e --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/FunctionNode.java @@ -0,0 +1,117 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Norris Boyd + * Roger Lawrence + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript; +import java.util.ArrayList; +import java.util.HashMap; + +public class FunctionNode extends ScriptOrFnNode { + + public FunctionNode(String name) { + super(Token.FUNCTION); + functionName = name; + } + + public String getFunctionName() { + return functionName; + } + + public boolean requiresActivation() { + return itsNeedsActivation; + } + + public boolean getIgnoreDynamicScope() { + return itsIgnoreDynamicScope; + } + + public boolean isGenerator() { + return itsIsGenerator; + } + + public void addResumptionPoint(Node target) { + if (generatorResumePoints == null) + generatorResumePoints = new ArrayList(); + generatorResumePoints.add(target); + } + + public ArrayList getResumptionPoints() { + return generatorResumePoints; + } + + public HashMap getLiveLocals() { + return liveLocals; + } + + public void addLiveLocals(Node node, int[] locals) { + if (liveLocals == null) + liveLocals = new HashMap(); + liveLocals.put(node, locals); + } + + /** + * There are three types of functions that can be defined. The first + * is a function statement. This is a function appearing as a top-level + * statement (i.e., not nested inside some other statement) in either a + * script or a function. + * + * The second is a function expression, which is a function appearing in + * an expression except for the third type, which is... + * + * The third type is a function expression where the expression is the + * top-level expression in an expression statement. + * + * The three types of functions have different treatment and must be + * distinguished. + */ + public static final int FUNCTION_STATEMENT = 1; + public static final int FUNCTION_EXPRESSION = 2; + public static final int FUNCTION_EXPRESSION_STATEMENT = 3; + + public int getFunctionType() { + return itsFunctionType; + } + + String functionName; + int itsFunctionType; + boolean itsNeedsActivation; + boolean itsIgnoreDynamicScope; + boolean itsIsGenerator; + ArrayList generatorResumePoints; + HashMap liveLocals; +} diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/FunctionObject.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/FunctionObject.java new file mode 100644 index 0000000..8fa4e68 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/FunctionObject.java @@ -0,0 +1,569 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Norris Boyd + * Igor Bukanov + * David C. Navas + * Ted Neward + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +// API class + +package org.mozilla.javascript; + +import java.lang.reflect.*; +import java.io.*; + +public class FunctionObject extends BaseFunction +{ + static final long serialVersionUID = -5332312783643935019L; + + /** + * Create a JavaScript function object from a Java method. + * + *

    The member argument must be either a java.lang.reflect.Method + * or a java.lang.reflect.Constructor and must match one of two forms.

    + * + * The first form is a member with zero or more parameters + * of the following types: Object, String, boolean, Scriptable, + * int, or double. The Long type is not supported + * because the double representation of a long (which is the + * EMCA-mandated storage type for Numbers) may lose precision. + * If the member is a Method, the return value must be void or one + * of the types allowed for parameters.

    + * + * The runtime will perform appropriate conversions based + * upon the type of the parameter. A parameter type of + * Object specifies that no conversions are to be done. A parameter + * of type String will use Context.toString to convert arguments. + * Similarly, parameters of type double, boolean, and Scriptable + * will cause Context.toNumber, Context.toBoolean, and + * Context.toObject, respectively, to be called.

    + * + * If the method is not static, the Java 'this' value will + * correspond to the JavaScript 'this' value. Any attempt + * to call the function with a 'this' value that is not + * of the right Java type will result in an error.

    + * + * The second form is the variable arguments (or "varargs") + * form. If the FunctionObject will be used as a constructor, + * the member must have the following parameters + *

    +     *      (Context cx, Object[] args, Function ctorObj,
    +     *       boolean inNewExpr)
    + * and if it is a Method, be static and return an Object result.

    + * + * Otherwise, if the FunctionObject will not be used to define a + * constructor, the member must be a static Method with parameters + * (Context cx, Scriptable thisObj, Object[] args, + * Function funObj) + *

    +     * and an Object result.

    + * + * When the function varargs form is called as part of a function call, + * the args parameter contains the + * arguments, with thisObj + * set to the JavaScript 'this' value. funObj + * is the function object for the invoked function.

    + * + * When the constructor varargs form is called or invoked while evaluating + * a new expression, args contains the + * arguments, ctorObj refers to this FunctionObject, and + * inNewExpr is true if and only if a new + * expression caused the call. This supports defining a function that + * has different behavior when called as a constructor than when + * invoked as a normal function call. (For example, the Boolean + * constructor, when called as a function, + * will convert to boolean rather than creating a new object.)

    + * + * @param name the name of the function + * @param methodOrConstructor a java.lang.reflect.Method or a java.lang.reflect.Constructor + * that defines the object + * @param scope enclosing scope of function + * @see org.mozilla.javascript.Scriptable + */ + public FunctionObject(String name, Member methodOrConstructor, + Scriptable scope) + { + if (methodOrConstructor instanceof Constructor) { + member = new MemberBox((Constructor) methodOrConstructor); + isStatic = true; // well, doesn't take a 'this' + } else { + member = new MemberBox((Method) methodOrConstructor); + isStatic = member.isStatic(); + } + String methodName = member.getName(); + this.functionName = name; + Class[] types = member.argTypes; + int arity = types.length; + if (arity == 4 && (types[1].isArray() || types[2].isArray())) { + // Either variable args or an error. + if (types[1].isArray()) { + if (!isStatic || + types[0] != ScriptRuntime.ContextClass || + types[1].getComponentType() != ScriptRuntime.ObjectClass || + types[2] != ScriptRuntime.FunctionClass || + types[3] != Boolean.TYPE) + { + throw Context.reportRuntimeError1( + "msg.varargs.ctor", methodName); + } + parmsLength = VARARGS_CTOR; + } else { + if (!isStatic || + types[0] != ScriptRuntime.ContextClass || + types[1] != ScriptRuntime.ScriptableClass || + types[2].getComponentType() != ScriptRuntime.ObjectClass || + types[3] != ScriptRuntime.FunctionClass) + { + throw Context.reportRuntimeError1( + "msg.varargs.fun", methodName); + } + parmsLength = VARARGS_METHOD; + } + } else { + parmsLength = arity; + if (arity > 0) { + typeTags = new byte[arity]; + for (int i = 0; i != arity; ++i) { + int tag = getTypeTag(types[i]); + if (tag == JAVA_UNSUPPORTED_TYPE) { + throw Context.reportRuntimeError2( + "msg.bad.parms", types[i].getName(), methodName); + } + typeTags[i] = (byte)tag; + } + } + } + + if (member.isMethod()) { + Method method = member.method(); + Class returnType = method.getReturnType(); + if (returnType == Void.TYPE) { + hasVoidReturn = true; + } else { + returnTypeTag = getTypeTag(returnType); + } + } else { + Class ctorType = member.getDeclaringClass(); + if (!ScriptRuntime.ScriptableClass.isAssignableFrom(ctorType)) { + throw Context.reportRuntimeError1( + "msg.bad.ctor.return", ctorType.getName()); + } + } + + ScriptRuntime.setFunctionProtoAndParent(this, scope); + } + + /** + * @return One of JAVA_*_TYPE constants to indicate desired type + * or {@link #JAVA_UNSUPPORTED_TYPE} if the convertion is not + * possible + */ + public static int getTypeTag(Class type) + { + if (type == ScriptRuntime.StringClass) + return JAVA_STRING_TYPE; + if (type == ScriptRuntime.IntegerClass || type == Integer.TYPE) + return JAVA_INT_TYPE; + if (type == ScriptRuntime.BooleanClass || type == Boolean.TYPE) + return JAVA_BOOLEAN_TYPE; + if (type == ScriptRuntime.DoubleClass || type == Double.TYPE) + return JAVA_DOUBLE_TYPE; + if (ScriptRuntime.ScriptableClass.isAssignableFrom(type)) + return JAVA_SCRIPTABLE_TYPE; + if (type == ScriptRuntime.ObjectClass) + return JAVA_OBJECT_TYPE; + + // Note that the long type is not supported; see the javadoc for + // the constructor for this class + + return JAVA_UNSUPPORTED_TYPE; + } + + public static Object convertArg(Context cx, Scriptable scope, + Object arg, int typeTag) + { + switch (typeTag) { + case JAVA_STRING_TYPE: + if (arg instanceof String) + return arg; + return ScriptRuntime.toString(arg); + case JAVA_INT_TYPE: + if (arg instanceof Integer) + return arg; + return new Integer(ScriptRuntime.toInt32(arg)); + case JAVA_BOOLEAN_TYPE: + if (arg instanceof Boolean) + return arg; + return ScriptRuntime.toBoolean(arg) ? Boolean.TRUE + : Boolean.FALSE; + case JAVA_DOUBLE_TYPE: + if (arg instanceof Double) + return arg; + return new Double(ScriptRuntime.toNumber(arg)); + case JAVA_SCRIPTABLE_TYPE: + if (arg instanceof Scriptable) + return arg; + return ScriptRuntime.toObject(cx, scope, arg); + case JAVA_OBJECT_TYPE: + return arg; + default: + throw new IllegalArgumentException(); + } + } + + /** + * Return the value defined by the method used to construct the object + * (number of parameters of the method, or 1 if the method is a "varargs" + * form). + */ + public int getArity() { + return parmsLength < 0 ? 1 : parmsLength; + } + + /** + * Return the same value as {@link #getArity()}. + */ + public int getLength() { + return getArity(); + } + + public String getFunctionName() + { + return (functionName == null) ? "" : functionName; + } + + /** + * Get Java method or constructor this function represent. + */ + public Member getMethodOrConstructor() + { + if (member.isMethod()) { + return member.method(); + } else { + return member.ctor(); + } + } + + static Method findSingleMethod(Method[] methods, String name) + { + Method found = null; + for (int i = 0, N = methods.length; i != N; ++i) { + Method method = methods[i]; + if (method != null && name.equals(method.getName())) { + if (found != null) { + throw Context.reportRuntimeError2( + "msg.no.overload", name, + method.getDeclaringClass().getName()); + } + found = method; + } + } + return found; + } + + /** + * Returns all public methods declared by the specified class. This excludes + * inherited methods. + * + * @param clazz the class from which to pull public declared methods + * @return the public methods declared in the specified class + * @see Class#getDeclaredMethods() + */ + static Method[] getMethodList(Class clazz) { + Method[] methods = null; + try { + // getDeclaredMethods may be rejected by the security manager + // but getMethods is more expensive + if (!sawSecurityException) + methods = clazz.getDeclaredMethods(); + } catch (SecurityException e) { + // If we get an exception once, give up on getDeclaredMethods + sawSecurityException = true; + } + if (methods == null) { + methods = clazz.getMethods(); + } + int count = 0; + for (int i=0; i < methods.length; i++) { + if (sawSecurityException + ? methods[i].getDeclaringClass() != clazz + : !Modifier.isPublic(methods[i].getModifiers())) + { + methods[i] = null; + } else { + count++; + } + } + Method[] result = new Method[count]; + int j=0; + for (int i=0; i < methods.length; i++) { + if (methods[i] != null) + result[j++] = methods[i]; + } + return result; + } + + /** + * Define this function as a JavaScript constructor. + *

    + * Sets up the "prototype" and "constructor" properties. Also + * calls setParent and setPrototype with appropriate values. + * Then adds the function object as a property of the given scope, using + * prototype.getClassName() + * as the name of the property. + * + * @param scope the scope in which to define the constructor (typically + * the global object) + * @param prototype the prototype object + * @see org.mozilla.javascript.Scriptable#setParentScope + * @see org.mozilla.javascript.Scriptable#setPrototype + * @see org.mozilla.javascript.Scriptable#getClassName + */ + public void addAsConstructor(Scriptable scope, Scriptable prototype) + { + initAsConstructor(scope, prototype); + defineProperty(scope, prototype.getClassName(), + this, ScriptableObject.DONTENUM); + } + + void initAsConstructor(Scriptable scope, Scriptable prototype) + { + ScriptRuntime.setFunctionProtoAndParent(this, scope); + setImmunePrototypeProperty(prototype); + + prototype.setParentScope(this); + + defineProperty(prototype, "constructor", this, + ScriptableObject.DONTENUM | + ScriptableObject.PERMANENT | + ScriptableObject.READONLY); + setParentScope(scope); + } + + /** + * @deprecated Use {@link #getTypeTag(Class)} + * and {@link #convertArg(Context, Scriptable, Object, int)} + * for type convertion. + */ + public static Object convertArg(Context cx, Scriptable scope, + Object arg, Class desired) + { + int tag = getTypeTag(desired); + if (tag == JAVA_UNSUPPORTED_TYPE) { + throw Context.reportRuntimeError1 + ("msg.cant.convert", desired.getName()); + } + return convertArg(cx, scope, arg, tag); + } + + /** + * Performs conversions on argument types if needed and + * invokes the underlying Java method or constructor. + *

    + * Implements Function.call. + * + * @see org.mozilla.javascript.Function#call( + * Context, Scriptable, Scriptable, Object[]) + */ + public Object call(Context cx, Scriptable scope, Scriptable thisObj, + Object[] args) + { + Object result; + boolean checkMethodResult = false; + + if (parmsLength < 0) { + if (parmsLength == VARARGS_METHOD) { + Object[] invokeArgs = { cx, thisObj, args, this }; + result = member.invoke(null, invokeArgs); + checkMethodResult = true; + } else { + boolean inNewExpr = (thisObj == null); + Boolean b = inNewExpr ? Boolean.TRUE : Boolean.FALSE; + Object[] invokeArgs = { cx, args, this, b }; + result = (member.isCtor()) + ? member.newInstance(invokeArgs) + : member.invoke(null, invokeArgs); + } + + } else { + if (!isStatic) { + Class clazz = member.getDeclaringClass(); + if (!clazz.isInstance(thisObj)) { + boolean compatible = false; + if (thisObj == scope) { + Scriptable parentScope = getParentScope(); + if (scope != parentScope) { + // Call with dynamic scope for standalone function, + // use parentScope as thisObj + compatible = clazz.isInstance(parentScope); + if (compatible) { + thisObj = parentScope; + } + } + } + if (!compatible) { + // Couldn't find an object to call this on. + throw ScriptRuntime.typeError1("msg.incompat.call", + functionName); + } + } + } + + Object[] invokeArgs; + if (parmsLength == args.length) { + // Do not allocate new argument array if java arguments are + // the same as the original js ones. + invokeArgs = args; + for (int i = 0; i != parmsLength; ++i) { + Object arg = args[i]; + Object converted = convertArg(cx, scope, arg, typeTags[i]); + if (arg != converted) { + if (invokeArgs == args) { + invokeArgs = args.clone(); + } + invokeArgs[i] = converted; + } + } + } else if (parmsLength == 0) { + invokeArgs = ScriptRuntime.emptyArgs; + } else { + invokeArgs = new Object[parmsLength]; + for (int i = 0; i != parmsLength; ++i) { + Object arg = (i < args.length) + ? args[i] + : Undefined.instance; + invokeArgs[i] = convertArg(cx, scope, arg, typeTags[i]); + } + } + + if (member.isMethod()) { + result = member.invoke(thisObj, invokeArgs); + checkMethodResult = true; + } else { + result = member.newInstance(invokeArgs); + } + + } + + if (checkMethodResult) { + if (hasVoidReturn) { + result = Undefined.instance; + } else if (returnTypeTag == JAVA_UNSUPPORTED_TYPE) { + result = cx.getWrapFactory().wrap(cx, scope, result, null); + } + // XXX: the code assumes that if returnTypeTag == JAVA_OBJECT_TYPE + // then the Java method did a proper job of converting the + // result to JS primitive or Scriptable to avoid + // potentially costly Context.javaToJS call. + } + + return result; + } + + /** + * Return new {@link Scriptable} instance using the default + * constructor for the class of the underlying Java method. + * Return null to indicate that the call method should be used to create + * new objects. + */ + public Scriptable createObject(Context cx, Scriptable scope) { + if (member.isCtor() || parmsLength == VARARGS_CTOR) { + return null; + } + Scriptable result; + try { + result = (Scriptable) member.getDeclaringClass().newInstance(); + } catch (Exception ex) { + throw Context.throwAsScriptRuntimeEx(ex); + } + + result.setPrototype(getClassPrototype()); + result.setParentScope(getParentScope()); + return result; + } + + boolean isVarArgsMethod() { + return parmsLength == VARARGS_METHOD; + } + + boolean isVarArgsConstructor() { + return parmsLength == VARARGS_CTOR; + } + + private void readObject(ObjectInputStream in) + throws IOException, ClassNotFoundException + { + in.defaultReadObject(); + if (parmsLength > 0) { + Class[] types = member.argTypes; + typeTags = new byte[parmsLength]; + for (int i = 0; i != parmsLength; ++i) { + typeTags[i] = (byte)getTypeTag(types[i]); + } + } + if (member.isMethod()) { + Method method = member.method(); + Class returnType = method.getReturnType(); + if (returnType == Void.TYPE) { + hasVoidReturn = true; + } else { + returnTypeTag = getTypeTag(returnType); + } + } + } + + private static final short VARARGS_METHOD = -1; + private static final short VARARGS_CTOR = -2; + + private static boolean sawSecurityException; + + public static final int JAVA_UNSUPPORTED_TYPE = 0; + public static final int JAVA_STRING_TYPE = 1; + public static final int JAVA_INT_TYPE = 2; + public static final int JAVA_BOOLEAN_TYPE = 3; + public static final int JAVA_DOUBLE_TYPE = 4; + public static final int JAVA_SCRIPTABLE_TYPE = 5; + public static final int JAVA_OBJECT_TYPE = 6; + + MemberBox member; + private String functionName; + private transient byte[] typeTags; + private int parmsLength; + private transient boolean hasVoidReturn; + private transient int returnTypeTag; + private boolean isStatic; +} diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/GeneratedClassLoader.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/GeneratedClassLoader.java new file mode 100644 index 0000000..0f73615 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/GeneratedClassLoader.java @@ -0,0 +1,66 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Igor Bukanov + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +// API class + +package org.mozilla.javascript; + +/** + * Interface to define classes from generated byte code. + */ +public interface GeneratedClassLoader { + + /** + * Define a new Java class. + * Classes created via this method should have the same class loader. + * + * @param name fully qualified class name + * @param data class byte code + * @return new class object + */ + public Class defineClass(String name, byte[] data); + + /** + * Link the given class. + * + * @param cl Class instance returned from the previous call to + * {@link #defineClass(String, byte[])} + * @see java.lang.ClassLoader + */ + public void linkClass(Class cl); +} diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/IRFactory.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/IRFactory.java new file mode 100644 index 0000000..1f51cb1 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/IRFactory.java @@ -0,0 +1,1607 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Norris Boyd + * Igor Bukanov + * Ethan Hugg + * Bob Jervis + * Terry Lucas + * Milen Nankov + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript; + +import java.util.List; +import java.util.ArrayList; + +/** + * This class allows the creation of nodes, and follows the Factory pattern. + * + * @see Node + * @author Mike McCabe + * @author Norris Boyd + */ +final class IRFactory +{ + IRFactory(Parser parser) + { + this.parser = parser; + } + + ScriptOrFnNode createScript() + { + return new ScriptOrFnNode(Token.SCRIPT); + } + + /** + * Script (for associating file/url names with toplevel scripts.) + */ + void initScript(ScriptOrFnNode scriptNode, Node body) + { + Node children = body.getFirstChild(); + if (children != null) { scriptNode.addChildrenToBack(children); } + } + + /** + * Leaf + */ + Node createLeaf(int nodeType) + { + return new Node(nodeType); + } + + /** + * Statement leaf nodes. + */ + + Node createSwitch(Node expr, int lineno) + { + // + // The switch will be rewritten from: + // + // switch (expr) { + // case test1: statements1; + // ... + // default: statementsDefault; + // ... + // case testN: statementsN; + // } + // + // to: + // + // { + // switch (expr) { + // case test1: goto label1; + // ... + // case testN: goto labelN; + // } + // goto labelDefault; + // label1: + // statements1; + // ... + // labelDefault: + // statementsDefault; + // ... + // labelN: + // statementsN; + // breakLabel: + // } + // + // where inside switch each "break;" without label will be replaced + // by "goto breakLabel". + // + // If the original switch does not have the default label, then + // the transformed code would contain after the switch instead of + // goto labelDefault; + // the following goto: + // goto breakLabel; + // + + Node.Jump switchNode = new Node.Jump(Token.SWITCH, expr, lineno); + Node block = new Node(Token.BLOCK, switchNode); + return block; + } + + /** + * If caseExpression argument is null it indicate default label. + */ + void addSwitchCase(Node switchBlock, Node caseExpression, Node statements) + { + if (switchBlock.getType() != Token.BLOCK) throw Kit.codeBug(); + Node.Jump switchNode = (Node.Jump)switchBlock.getFirstChild(); + if (switchNode.getType() != Token.SWITCH) throw Kit.codeBug(); + + Node gotoTarget = Node.newTarget(); + if (caseExpression != null) { + Node.Jump caseNode = new Node.Jump(Token.CASE, caseExpression); + caseNode.target = gotoTarget; + switchNode.addChildToBack(caseNode); + } else { + switchNode.setDefault(gotoTarget); + } + switchBlock.addChildToBack(gotoTarget); + switchBlock.addChildToBack(statements); + } + + void closeSwitch(Node switchBlock) + { + if (switchBlock.getType() != Token.BLOCK) throw Kit.codeBug(); + Node.Jump switchNode = (Node.Jump)switchBlock.getFirstChild(); + if (switchNode.getType() != Token.SWITCH) throw Kit.codeBug(); + + Node switchBreakTarget = Node.newTarget(); + // switchNode.target is only used by NodeTransformer + // to detect switch end + switchNode.target = switchBreakTarget; + + Node defaultTarget = switchNode.getDefault(); + if (defaultTarget == null) { + defaultTarget = switchBreakTarget; + } + + switchBlock.addChildAfter(makeJump(Token.GOTO, defaultTarget), + switchNode); + switchBlock.addChildToBack(switchBreakTarget); + } + + Node createVariables(int token, int lineno) + { + return new Node(token, lineno); + } + + Node createExprStatement(Node expr, int lineno) + { + int type; + if (parser.insideFunction()) { + type = Token.EXPR_VOID; + } else { + type = Token.EXPR_RESULT; + } + return new Node(type, expr, lineno); + } + + Node createExprStatementNoReturn(Node expr, int lineno) + { + return new Node(Token.EXPR_VOID, expr, lineno); + } + + Node createDefaultNamespace(Node expr, int lineno) + { + // default xml namespace requires activation + setRequiresActivation(); + Node n = createUnary(Token.DEFAULTNAMESPACE, expr); + Node result = createExprStatement(n, lineno); + return result; + } + + /** + * Name + */ + Node createName(String name) + { + checkActivationName(name, Token.NAME); + return Node.newString(Token.NAME, name); + } + + private Node createName(int type, String name, Node child) + { + Node result = createName(name); + result.setType(type); + if (child != null) + result.addChildToBack(child); + return result; + } + + /** + * String (for literals) + */ + Node createString(String string) + { + return Node.newString(string); + } + + /** + * Number (for literals) + */ + Node createNumber(double number) + { + return Node.newNumber(number); + } + + /** + * Catch clause of try/catch/finally + * @param varName the name of the variable to bind to the exception + * @param catchCond the condition under which to catch the exception. + * May be null if no condition is given. + * @param stmts the statements in the catch clause + * @param lineno the starting line number of the catch clause + */ + Node createCatch(String varName, Node catchCond, Node stmts, int lineno) + { + if (catchCond == null) { + catchCond = new Node(Token.EMPTY); + } + return new Node(Token.CATCH, createName(varName), + catchCond, stmts, lineno); + } + + /** + * Throw + */ + Node createThrow(Node expr, int lineno) + { + return new Node(Token.THROW, expr, lineno); + } + + /** + * Return + */ + Node createReturn(Node expr, int lineno) + { + return expr == null + ? new Node(Token.RETURN, lineno) + : new Node(Token.RETURN, expr, lineno); + } + + /** + * Debugger + */ + Node createDebugger(int lineno) + { + return new Node(Token.DEBUGGER, lineno); + } + + /** + * Label + */ + Node createLabel(int lineno) + { + return new Node.Jump(Token.LABEL, lineno); + } + + Node getLabelLoop(Node label) + { + return ((Node.Jump)label).getLoop(); + } + + /** + * Label + */ + Node createLabeledStatement(Node labelArg, Node statement) + { + Node.Jump label = (Node.Jump)labelArg; + + // Make a target and put it _after_ the statement + // node. And in the LABEL node, so breaks get the + // right target. + + Node breakTarget = Node.newTarget(); + Node block = new Node(Token.BLOCK, label, statement, breakTarget); + label.target = breakTarget; + + return block; + } + + /** + * Break (possibly labeled) + */ + Node createBreak(Node breakStatement, int lineno) + { + Node.Jump n = new Node.Jump(Token.BREAK, lineno); + Node.Jump jumpStatement; + int t = breakStatement.getType(); + if (t == Token.LOOP || t == Token.LABEL) { + jumpStatement = (Node.Jump)breakStatement; + } else if (t == Token.BLOCK + && breakStatement.getFirstChild().getType() == Token.SWITCH) + { + jumpStatement = (Node.Jump)breakStatement.getFirstChild(); + } else { + throw Kit.codeBug(); + } + n.setJumpStatement(jumpStatement); + return n; + } + + /** + * Continue (possibly labeled) + */ + Node createContinue(Node loop, int lineno) + { + if (loop.getType() != Token.LOOP) Kit.codeBug(); + Node.Jump n = new Node.Jump(Token.CONTINUE, lineno); + n.setJumpStatement((Node.Jump)loop); + return n; + } + + /** + * Statement block + * Creates the empty statement block + * Must make subsequent calls to add statements to the node + */ + Node createBlock(int lineno) + { + return new Node(Token.BLOCK, lineno); + } + + FunctionNode createFunction(String name) + { + return new FunctionNode(name); + } + + Node initFunction(FunctionNode fnNode, int functionIndex, + Node statements, int functionType) + { + fnNode.itsFunctionType = functionType; + fnNode.addChildToBack(statements); + + int functionCount = fnNode.getFunctionCount(); + if (functionCount != 0) { + // Functions containing other functions require activation objects + fnNode.itsNeedsActivation = true; + } + + if (functionType == FunctionNode.FUNCTION_EXPRESSION) { + String name = fnNode.getFunctionName(); + if (name != null && name.length() != 0) { + // A function expression needs to have its name as a + // variable (if it isn't already allocated as a variable). + // See ECMA Ch. 13. We add code to the beginning of the + // function to initialize a local variable of the + // function's name to the function value. + Node setFn = new Node(Token.EXPR_VOID, + new Node(Token.SETNAME, + Node.newString(Token.BINDNAME, name), + new Node(Token.THISFN))); + statements.addChildrenToFront(setFn); + } + } + + // Add return to end if needed. + Node lastStmt = statements.getLastChild(); + if (lastStmt == null || lastStmt.getType() != Token.RETURN) { + statements.addChildToBack(new Node(Token.RETURN)); + } + + Node result = Node.newString(Token.FUNCTION, + fnNode.getFunctionName()); + result.putIntProp(Node.FUNCTION_PROP, functionIndex); + return result; + } + + /** + * Add a child to the back of the given node. This function + * breaks the Factory abstraction, but it removes a requirement + * from implementors of Node. + */ + void addChildToBack(Node parent, Node child) + { + parent.addChildToBack(child); + } + + /** + * Create a node that can be used to hold lexically scoped variable + * definitions (via let declarations). + * + * @param token the token of the node to create + * @param lineno line number of source + * @return the created node + */ + Node createScopeNode(int token, int lineno) { + return new Node.Scope(token, lineno); + } + + /** + * Create loop node. The parser will later call + * createWhile|createDoWhile|createFor|createForIn + * to finish loop generation. + */ + Node createLoopNode(Node loopLabel, int lineno) + { + Node.Jump result = new Node.Scope(Token.LOOP, lineno); + if (loopLabel != null) { + ((Node.Jump)loopLabel).setLoop(result); + } + return result; + } + + /** + * While + */ + Node createWhile(Node loop, Node cond, Node body) + { + return createLoop((Node.Jump)loop, LOOP_WHILE, body, cond, + null, null); + } + + /** + * DoWhile + */ + Node createDoWhile(Node loop, Node body, Node cond) + { + return createLoop((Node.Jump)loop, LOOP_DO_WHILE, body, cond, + null, null); + } + + /** + * For + */ + Node createFor(Node loop, Node init, Node test, Node incr, Node body) + { + if (init.getType() == Token.LET) { + // rewrite "for (let i=s; i < N; i++)..." as + // "let (i=s) { for (; i < N; i++)..." so that "s" is evaluated + // outside the scope of the for. + Node.Scope let = Node.Scope.splitScope((Node.Scope)loop); + let.setType(Token.LET); + let.addChildrenToBack(init); + let.addChildToBack(createLoop((Node.Jump)loop, LOOP_FOR, body, test, + new Node(Token.EMPTY), incr)); + return let; + } + return createLoop((Node.Jump)loop, LOOP_FOR, body, test, init, incr); + } + + private Node createLoop(Node.Jump loop, int loopType, Node body, Node cond, + Node init, Node incr) + { + Node bodyTarget = Node.newTarget(); + Node condTarget = Node.newTarget(); + if (loopType == LOOP_FOR && cond.getType() == Token.EMPTY) { + cond = new Node(Token.TRUE); + } + Node.Jump IFEQ = new Node.Jump(Token.IFEQ, cond); + IFEQ.target = bodyTarget; + Node breakTarget = Node.newTarget(); + + loop.addChildToBack(bodyTarget); + loop.addChildrenToBack(body); + if (loopType == LOOP_WHILE || loopType == LOOP_FOR) { + // propagate lineno to condition + loop.addChildrenToBack(new Node(Token.EMPTY, loop.getLineno())); + } + loop.addChildToBack(condTarget); + loop.addChildToBack(IFEQ); + loop.addChildToBack(breakTarget); + + loop.target = breakTarget; + Node continueTarget = condTarget; + + if (loopType == LOOP_WHILE || loopType == LOOP_FOR) { + // Just add a GOTO to the condition in the do..while + loop.addChildToFront(makeJump(Token.GOTO, condTarget)); + + if (loopType == LOOP_FOR) { + int initType = init.getType(); + if (initType != Token.EMPTY) { + if (initType != Token.VAR && initType != Token.LET) { + init = new Node(Token.EXPR_VOID, init); + } + loop.addChildToFront(init); + } + Node incrTarget = Node.newTarget(); + loop.addChildAfter(incrTarget, body); + if (incr.getType() != Token.EMPTY) { + incr = new Node(Token.EXPR_VOID, incr); + loop.addChildAfter(incr, incrTarget); + } + continueTarget = incrTarget; + } + } + + loop.setContinue(continueTarget); + + return loop; + } + + /** + * For .. In + * + */ + Node createForIn(int declType, Node loop, Node lhs, Node obj, Node body, + boolean isForEach) + { + int destructuring = -1; + int destructuringLen = 0; + Node lvalue; + int type = lhs.getType(); + if (type == Token.VAR || type == Token.LET) { + Node lastChild = lhs.getLastChild(); + if (lhs.getFirstChild() != lastChild) { + /* + * check that there was only one variable given. + * we can't do this in the parser, because then the + * parser would have to know something about the + * 'init' node of the for-in loop. + */ + parser.reportError("msg.mult.index"); + } + if (lastChild.getType() == Token.ARRAYLIT || + lastChild.getType() == Token.OBJECTLIT) + { + type = destructuring = lastChild.getType(); + lvalue = lastChild; + destructuringLen = lastChild.getIntProp( + Node.DESTRUCTURING_ARRAY_LENGTH, 0); + } else if (lastChild.getType() == Token.NAME) { + lvalue = Node.newString(Token.NAME, lastChild.getString()); + } else { + parser.reportError("msg.bad.for.in.lhs"); + return obj; + } + } else if (type == Token.ARRAYLIT || type == Token.OBJECTLIT) { + destructuring = type; + lvalue = lhs; + destructuringLen = lhs.getIntProp(Node.DESTRUCTURING_ARRAY_LENGTH, 0); + } else { + lvalue = makeReference(lhs); + if (lvalue == null) { + parser.reportError("msg.bad.for.in.lhs"); + return obj; + } + } + + Node localBlock = new Node(Token.LOCAL_BLOCK); + int initType = (isForEach) ? Token.ENUM_INIT_VALUES : + (destructuring != -1) ? Token.ENUM_INIT_ARRAY : + Token.ENUM_INIT_KEYS; + Node init = new Node(initType, obj); + init.putProp(Node.LOCAL_BLOCK_PROP, localBlock); + Node cond = new Node(Token.ENUM_NEXT); + cond.putProp(Node.LOCAL_BLOCK_PROP, localBlock); + Node id = new Node(Token.ENUM_ID); + id.putProp(Node.LOCAL_BLOCK_PROP, localBlock); + + Node newBody = new Node(Token.BLOCK); + Node assign; + if (destructuring != -1) { + assign = createDestructuringAssignment(declType, lvalue, id); + if (!isForEach && (destructuring == Token.OBJECTLIT || + destructuringLen != 2)) + { + // destructuring assignment is only allowed in for..each or + // with an array type of length 2 (to hold key and value) + parser.reportError("msg.bad.for.in.destruct"); + } + } else { + assign = simpleAssignment(lvalue, id); + } + newBody.addChildToBack(new Node(Token.EXPR_VOID, assign)); + newBody.addChildToBack(body); + + loop = createWhile(loop, cond, newBody); + loop.addChildToFront(init); + if (type == Token.VAR || type == Token.LET) + loop.addChildToFront(lhs); + localBlock.addChildToBack(loop); + + return localBlock; + } + + /** + * Try/Catch/Finally + * + * The IRFactory tries to express as much as possible in the tree; + * the responsibilities remaining for Codegen are to add the Java + * handlers: (Either (but not both) of TARGET and FINALLY might not + * be defined) + + * - a catch handler for javascript exceptions that unwraps the + * exception onto the stack and GOTOes to the catch target + + * - a finally handler + + * ... and a goto to GOTO around these handlers. + */ + Node createTryCatchFinally(Node tryBlock, Node catchBlocks, + Node finallyBlock, int lineno) + { + boolean hasFinally = (finallyBlock != null) + && (finallyBlock.getType() != Token.BLOCK + || finallyBlock.hasChildren()); + + // short circuit + if (tryBlock.getType() == Token.BLOCK && !tryBlock.hasChildren() + && !hasFinally) + { + return tryBlock; + } + + boolean hasCatch = catchBlocks.hasChildren(); + + // short circuit + if (!hasFinally && !hasCatch) { + // bc finally might be an empty block... + return tryBlock; + } + + + Node handlerBlock = new Node(Token.LOCAL_BLOCK); + Node.Jump pn = new Node.Jump(Token.TRY, tryBlock, lineno); + pn.putProp(Node.LOCAL_BLOCK_PROP, handlerBlock); + + if (hasCatch) { + // jump around catch code + Node endCatch = Node.newTarget(); + pn.addChildToBack(makeJump(Token.GOTO, endCatch)); + + // make a TARGET for the catch that the tcf node knows about + Node catchTarget = Node.newTarget(); + pn.target = catchTarget; + // mark it + pn.addChildToBack(catchTarget); + + // + // Given + // + // try { + // tryBlock; + // } catch (e if condition1) { + // something1; + // ... + // + // } catch (e if conditionN) { + // somethingN; + // } catch (e) { + // somethingDefault; + // } + // + // rewrite as + // + // try { + // tryBlock; + // goto after_catch: + // } catch (x) { + // with (newCatchScope(e, x)) { + // if (condition1) { + // something1; + // goto after_catch; + // } + // } + // ... + // with (newCatchScope(e, x)) { + // if (conditionN) { + // somethingN; + // goto after_catch; + // } + // } + // with (newCatchScope(e, x)) { + // somethingDefault; + // goto after_catch; + // } + // } + // after_catch: + // + // If there is no default catch, then the last with block + // arround "somethingDefault;" is replaced by "rethrow;" + + // It is assumed that catch handler generation will store + // exeception object in handlerBlock register + + // Block with local for exception scope objects + Node catchScopeBlock = new Node(Token.LOCAL_BLOCK); + + // expects catchblocks children to be (cond block) pairs. + Node cb = catchBlocks.getFirstChild(); + boolean hasDefault = false; + int scopeIndex = 0; + while (cb != null) { + int catchLineNo = cb.getLineno(); + + Node name = cb.getFirstChild(); + Node cond = name.getNext(); + Node catchStatement = cond.getNext(); + cb.removeChild(name); + cb.removeChild(cond); + cb.removeChild(catchStatement); + + // Add goto to the catch statement to jump out of catch + // but prefix it with LEAVEWITH since try..catch produces + // "with"code in order to limit the scope of the exception + // object. + catchStatement.addChildToBack(new Node(Token.LEAVEWITH)); + catchStatement.addChildToBack(makeJump(Token.GOTO, endCatch)); + + // Create condition "if" when present + Node condStmt; + if (cond.getType() == Token.EMPTY) { + condStmt = catchStatement; + hasDefault = true; + } else { + condStmt = createIf(cond, catchStatement, null, + catchLineNo); + } + + // Generate code to create the scope object and store + // it in catchScopeBlock register + Node catchScope = new Node(Token.CATCH_SCOPE, name, + createUseLocal(handlerBlock)); + catchScope.putProp(Node.LOCAL_BLOCK_PROP, catchScopeBlock); + catchScope.putIntProp(Node.CATCH_SCOPE_PROP, scopeIndex); + catchScopeBlock.addChildToBack(catchScope); + + // Add with statement based on catch scope object + catchScopeBlock.addChildToBack( + createWith(createUseLocal(catchScopeBlock), condStmt, + catchLineNo)); + + // move to next cb + cb = cb.getNext(); + ++scopeIndex; + } + pn.addChildToBack(catchScopeBlock); + if (!hasDefault) { + // Generate code to rethrow if no catch clause was executed + Node rethrow = new Node(Token.RETHROW); + rethrow.putProp(Node.LOCAL_BLOCK_PROP, handlerBlock); + pn.addChildToBack(rethrow); + } + + pn.addChildToBack(endCatch); + } + + if (hasFinally) { + Node finallyTarget = Node.newTarget(); + pn.setFinally(finallyTarget); + + // add jsr finally to the try block + pn.addChildToBack(makeJump(Token.JSR, finallyTarget)); + + // jump around finally code + Node finallyEnd = Node.newTarget(); + pn.addChildToBack(makeJump(Token.GOTO, finallyEnd)); + + pn.addChildToBack(finallyTarget); + Node fBlock = new Node(Token.FINALLY, finallyBlock); + fBlock.putProp(Node.LOCAL_BLOCK_PROP, handlerBlock); + pn.addChildToBack(fBlock); + + pn.addChildToBack(finallyEnd); + } + handlerBlock.addChildToBack(pn); + return handlerBlock; + } + + /** + * Throw, Return, Label, Break and Continue are defined in ASTFactory. + */ + + /** + * With + */ + Node createWith(Node obj, Node body, int lineno) + { + setRequiresActivation(); + Node result = new Node(Token.BLOCK, lineno); + result.addChildToBack(new Node(Token.ENTERWITH, obj)); + Node bodyNode = new Node(Token.WITH, body, lineno); + result.addChildrenToBack(bodyNode); + result.addChildToBack(new Node(Token.LEAVEWITH)); + return result; + } + + /** + * DOTQUERY + */ + public Node createDotQuery (Node obj, Node body, int lineno) + { + setRequiresActivation(); + Node result = new Node(Token.DOTQUERY, obj, body, lineno); + return result; + } + + Node createArrayLiteral(ObjArray elems, int skipCount, int destructuringLen) + { + int length = elems.size(); + int[] skipIndexes = null; + if (skipCount != 0) { + skipIndexes = new int[skipCount]; + } + Node array = new Node(Token.ARRAYLIT); + for (int i = 0, j = 0; i != length; ++i) { + Node elem = (Node)elems.get(i); + if (elem != null) { + array.addChildToBack(elem); + } else { + skipIndexes[j] = i; + ++j; + } + } + if (skipCount != 0) { + array.putProp(Node.SKIP_INDEXES_PROP, skipIndexes); + } + array.putIntProp(Node.DESTRUCTURING_ARRAY_LENGTH, destructuringLen); + return array; + } + + /** + * Object Literals + *
    createObjectLiteral rewrites its argument as object + * creation plus object property entries, so later compiler + * stages don't need to know about object literals. + */ + Node createObjectLiteral(ObjArray elems) + { + int size = elems.size() / 2; + Node object = new Node(Token.OBJECTLIT); + Object[] properties; + if (size == 0) { + properties = ScriptRuntime.emptyArgs; + } else { + properties = new Object[size]; + for (int i = 0; i != size; ++i) { + properties[i] = elems.get(2 * i); + Node value = (Node)elems.get(2 * i + 1); + object.addChildToBack(value); + } + } + object.putProp(Node.OBJECT_IDS_PROP, properties); + return object; + } + + /** + * Regular expressions + */ + Node createRegExp(int regexpIndex) + { + Node n = new Node(Token.REGEXP); + n.putIntProp(Node.REGEXP_PROP, regexpIndex); + return n; + } + + /** + * If statement + */ + Node createIf(Node cond, Node ifTrue, Node ifFalse, int lineno) + { + int condStatus = isAlwaysDefinedBoolean(cond); + if (condStatus == ALWAYS_TRUE_BOOLEAN) { + return ifTrue; + } else if (condStatus == ALWAYS_FALSE_BOOLEAN) { + if (ifFalse != null) { + return ifFalse; + } + // Replace if (false) xxx by empty block + return new Node(Token.BLOCK, lineno); + } + + Node result = new Node(Token.BLOCK, lineno); + Node ifNotTarget = Node.newTarget(); + Node.Jump IFNE = new Node.Jump(Token.IFNE, cond); + IFNE.target = ifNotTarget; + + result.addChildToBack(IFNE); + result.addChildrenToBack(ifTrue); + + if (ifFalse != null) { + Node endTarget = Node.newTarget(); + result.addChildToBack(makeJump(Token.GOTO, endTarget)); + result.addChildToBack(ifNotTarget); + result.addChildrenToBack(ifFalse); + result.addChildToBack(endTarget); + } else { + result.addChildToBack(ifNotTarget); + } + + return result; + } + + Node createCondExpr(Node cond, Node ifTrue, Node ifFalse) + { + int condStatus = isAlwaysDefinedBoolean(cond); + if (condStatus == ALWAYS_TRUE_BOOLEAN) { + return ifTrue; + } else if (condStatus == ALWAYS_FALSE_BOOLEAN) { + return ifFalse; + } + return new Node(Token.HOOK, cond, ifTrue, ifFalse); + } + + /** + * Unary + */ + Node createUnary(int nodeType, Node child) + { + int childType = child.getType(); + switch (nodeType) { + case Token.DELPROP: { + Node n; + if (childType == Token.NAME) { + // Transform Delete(Name "a") + // to Delete(Bind("a"), String("a")) + child.setType(Token.BINDNAME); + Node left = child; + Node right = Node.newString(child.getString()); + n = new Node(nodeType, left, right); + } else if (childType == Token.GETPROP || + childType == Token.GETELEM) + { + Node left = child.getFirstChild(); + Node right = child.getLastChild(); + child.removeChild(left); + child.removeChild(right); + n = new Node(nodeType, left, right); + } else if (childType == Token.GET_REF) { + Node ref = child.getFirstChild(); + child.removeChild(ref); + n = new Node(Token.DEL_REF, ref); + } else { + n = new Node(Token.TRUE); + } + return n; + } + case Token.TYPEOF: + if (childType == Token.NAME) { + child.setType(Token.TYPEOFNAME); + return child; + } + break; + case Token.BITNOT: + if (childType == Token.NUMBER) { + int value = ScriptRuntime.toInt32(child.getDouble()); + child.setDouble(~value); + return child; + } + break; + case Token.NEG: + if (childType == Token.NUMBER) { + child.setDouble(-child.getDouble()); + return child; + } + break; + case Token.NOT: { + int status = isAlwaysDefinedBoolean(child); + if (status != 0) { + int type; + if (status == ALWAYS_TRUE_BOOLEAN) { + type = Token.FALSE; + } else { + type = Token.TRUE; + } + if (childType == Token.TRUE || childType == Token.FALSE) { + child.setType(type); + return child; + } + return new Node(type); + } + break; + } + } + return new Node(nodeType, child); + } + + Node createYield(Node child, int lineno) + { + if (!parser.insideFunction()) { + parser.reportError("msg.bad.yield"); + } + setRequiresActivation(); + setIsGenerator(); + if (child != null) + return new Node(Token.YIELD, child, lineno); + else + return new Node(Token.YIELD, lineno); + } + + Node createCallOrNew(int nodeType, Node child) + { + int type = Node.NON_SPECIALCALL; + if (child.getType() == Token.NAME) { + String name = child.getString(); + if (name.equals("eval")) { + type = Node.SPECIALCALL_EVAL; + } else if (name.equals("With")) { + type = Node.SPECIALCALL_WITH; + } + } else if (child.getType() == Token.GETPROP) { + String name = child.getLastChild().getString(); + if (name.equals("eval")) { + type = Node.SPECIALCALL_EVAL; + } + } + Node node = new Node(nodeType, child); + if (type != Node.NON_SPECIALCALL) { + // Calls to these functions require activation objects. + setRequiresActivation(); + node.putIntProp(Node.SPECIALCALL_PROP, type); + } + return node; + } + + Node createIncDec(int nodeType, boolean post, Node child) + { + child = makeReference(child); + if (child == null) { + String msg; + if (nodeType == Token.DEC) { + msg = "msg.bad.decr"; + } else { + msg = "msg.bad.incr"; + } + parser.reportError(msg); + return null; + } + + int childType = child.getType(); + + switch (childType) { + case Token.NAME: + case Token.GETPROP: + case Token.GETELEM: + case Token.GET_REF: { + Node n = new Node(nodeType, child); + int incrDecrMask = 0; + if (nodeType == Token.DEC) { + incrDecrMask |= Node.DECR_FLAG; + } + if (post) { + incrDecrMask |= Node.POST_FLAG; + } + n.putIntProp(Node.INCRDECR_PROP, incrDecrMask); + return n; + } + } + throw Kit.codeBug(); + } + + Node createPropertyGet(Node target, String namespace, String name, + int memberTypeFlags) + { + if (namespace == null && memberTypeFlags == 0) { + if (target == null) { + return createName(name); + } + checkActivationName(name, Token.GETPROP); + if (ScriptRuntime.isSpecialProperty(name)) { + Node ref = new Node(Token.REF_SPECIAL, target); + ref.putProp(Node.NAME_PROP, name); + return new Node(Token.GET_REF, ref); + } + return new Node(Token.GETPROP, target, createString(name)); + } + Node elem = createString(name); + memberTypeFlags |= Node.PROPERTY_FLAG; + return createMemberRefGet(target, namespace, elem, memberTypeFlags); + } + + Node createElementGet(Node target, String namespace, Node elem, + int memberTypeFlags) + { + // OPT: could optimize to createPropertyGet + // iff elem is string that can not be number + if (namespace == null && memberTypeFlags == 0) { + // stand-alone [aaa] as primary expression is array literal + // declaration and should not come here! + if (target == null) throw Kit.codeBug(); + return new Node(Token.GETELEM, target, elem); + } + return createMemberRefGet(target, namespace, elem, memberTypeFlags); + } + + private Node createMemberRefGet(Node target, String namespace, Node elem, + int memberTypeFlags) + { + Node nsNode = null; + if (namespace != null) { + // See 11.1.2 in ECMA 357 + if (namespace.equals("*")) { + nsNode = new Node(Token.NULL); + } else { + nsNode = createName(namespace); + } + } + Node ref; + if (target == null) { + if (namespace == null) { + ref = new Node(Token.REF_NAME, elem); + } else { + ref = new Node(Token.REF_NS_NAME, nsNode, elem); + } + } else { + if (namespace == null) { + ref = new Node(Token.REF_MEMBER, target, elem); + } else { + ref = new Node(Token.REF_NS_MEMBER, target, nsNode, elem); + } + } + if (memberTypeFlags != 0) { + ref.putIntProp(Node.MEMBER_TYPE_PROP, memberTypeFlags); + } + return new Node(Token.GET_REF, ref); + } + + /** + * Binary + */ + Node createBinary(int nodeType, Node left, Node right) + { + switch (nodeType) { + + case Token.ADD: + // numerical addition and string concatenation + if (left.type == Token.STRING) { + String s2; + if (right.type == Token.STRING) { + s2 = right.getString(); + } else if (right.type == Token.NUMBER) { + s2 = ScriptRuntime.numberToString(right.getDouble(), 10); + } else { + break; + } + String s1 = left.getString(); + left.setString(s1.concat(s2)); + return left; + } else if (left.type == Token.NUMBER) { + if (right.type == Token.NUMBER) { + left.setDouble(left.getDouble() + right.getDouble()); + return left; + } else if (right.type == Token.STRING) { + String s1, s2; + s1 = ScriptRuntime.numberToString(left.getDouble(), 10); + s2 = right.getString(); + right.setString(s1.concat(s2)); + return right; + } + } + // can't do anything if we don't know both types - since + // 0 + object is supposed to call toString on the object and do + // string concantenation rather than addition + break; + + case Token.SUB: + // numerical subtraction + if (left.type == Token.NUMBER) { + double ld = left.getDouble(); + if (right.type == Token.NUMBER) { + //both numbers + left.setDouble(ld - right.getDouble()); + return left; + } else if (ld == 0.0) { + // first 0: 0-x -> -x + return new Node(Token.NEG, right); + } + } else if (right.type == Token.NUMBER) { + if (right.getDouble() == 0.0) { + //second 0: x - 0 -> +x + // can not make simply x because x - 0 must be number + return new Node(Token.POS, left); + } + } + break; + + case Token.MUL: + // numerical multiplication + if (left.type == Token.NUMBER) { + double ld = left.getDouble(); + if (right.type == Token.NUMBER) { + //both numbers + left.setDouble(ld * right.getDouble()); + return left; + } else if (ld == 1.0) { + // first 1: 1 * x -> +x + return new Node(Token.POS, right); + } + } else if (right.type == Token.NUMBER) { + if (right.getDouble() == 1.0) { + //second 1: x * 1 -> +x + // can not make simply x because x - 0 must be number + return new Node(Token.POS, left); + } + } + // can't do x*0: Infinity * 0 gives NaN, not 0 + break; + + case Token.DIV: + // number division + if (right.type == Token.NUMBER) { + double rd = right.getDouble(); + if (left.type == Token.NUMBER) { + // both constants -- just divide, trust Java to handle x/0 + left.setDouble(left.getDouble() / rd); + return left; + } else if (rd == 1.0) { + // second 1: x/1 -> +x + // not simply x to force number convertion + return new Node(Token.POS, left); + } + } + break; + + case Token.AND: { + // Since x && y gives x, not false, when Boolean(x) is false, + // and y, not Boolean(y), when Boolean(x) is true, x && y + // can only be simplified if x is defined. See bug 309957. + + int leftStatus = isAlwaysDefinedBoolean(left); + if (leftStatus == ALWAYS_FALSE_BOOLEAN) { + // if the first one is false, just return it + return left; + } else if (leftStatus == ALWAYS_TRUE_BOOLEAN) { + // if first is true, set to second + return right; + } + break; + } + + case Token.OR: { + // Since x || y gives x, not true, when Boolean(x) is true, + // and y, not Boolean(y), when Boolean(x) is false, x || y + // can only be simplified if x is defined. See bug 309957. + + int leftStatus = isAlwaysDefinedBoolean(left); + if (leftStatus == ALWAYS_TRUE_BOOLEAN) { + // if the first one is true, just return it + return left; + } else if (leftStatus == ALWAYS_FALSE_BOOLEAN) { + // if first is false, set to second + return right; + } + break; + } + } + + return new Node(nodeType, left, right); + } + + private Node simpleAssignment(Node left, Node right) + { + int nodeType = left.getType(); + switch (nodeType) { + case Token.NAME: + left.setType(Token.BINDNAME); + return new Node(Token.SETNAME, left, right); + + case Token.GETPROP: + case Token.GETELEM: { + Node obj = left.getFirstChild(); + Node id = left.getLastChild(); + int type; + if (nodeType == Token.GETPROP) { + type = Token.SETPROP; + } else { + type = Token.SETELEM; + } + return new Node(type, obj, id, right); + } + case Token.GET_REF: { + Node ref = left.getFirstChild(); + checkMutableReference(ref); + return new Node(Token.SET_REF, ref, right); + } + } + + throw Kit.codeBug(); + } + + private void checkMutableReference(Node n) + { + int memberTypeFlags = n.getIntProp(Node.MEMBER_TYPE_PROP, 0); + if ((memberTypeFlags & Node.DESCENDANTS_FLAG) != 0) { + parser.reportError("msg.bad.assign.left"); + } + } + + Node createAssignment(int assignType, Node left, Node right) + { + Node ref = makeReference(left); + if (ref == null) { + if (left.getType() == Token.ARRAYLIT || + left.getType() == Token.OBJECTLIT) + { + if (assignType != Token.ASSIGN) { + parser.reportError("msg.bad.destruct.op"); + return right; + } + return createDestructuringAssignment(-1, left, right); + } + parser.reportError("msg.bad.assign.left"); + return right; + } + left = ref; + + int assignOp; + switch (assignType) { + case Token.ASSIGN: + return simpleAssignment(left, right); + case Token.ASSIGN_BITOR: assignOp = Token.BITOR; break; + case Token.ASSIGN_BITXOR: assignOp = Token.BITXOR; break; + case Token.ASSIGN_BITAND: assignOp = Token.BITAND; break; + case Token.ASSIGN_LSH: assignOp = Token.LSH; break; + case Token.ASSIGN_RSH: assignOp = Token.RSH; break; + case Token.ASSIGN_URSH: assignOp = Token.URSH; break; + case Token.ASSIGN_ADD: assignOp = Token.ADD; break; + case Token.ASSIGN_SUB: assignOp = Token.SUB; break; + case Token.ASSIGN_MUL: assignOp = Token.MUL; break; + case Token.ASSIGN_DIV: assignOp = Token.DIV; break; + case Token.ASSIGN_MOD: assignOp = Token.MOD; break; + default: throw Kit.codeBug(); + } + + int nodeType = left.getType(); + switch (nodeType) { + case Token.NAME: { + Node op = new Node(assignOp, left, right); + Node lvalueLeft = Node.newString(Token.BINDNAME, left.getString()); + return new Node(Token.SETNAME, lvalueLeft, op); + } + case Token.GETPROP: + case Token.GETELEM: { + Node obj = left.getFirstChild(); + Node id = left.getLastChild(); + + int type = nodeType == Token.GETPROP + ? Token.SETPROP_OP + : Token.SETELEM_OP; + + Node opLeft = new Node(Token.USE_STACK); + Node op = new Node(assignOp, opLeft, right); + return new Node(type, obj, id, op); + } + case Token.GET_REF: { + ref = left.getFirstChild(); + checkMutableReference(ref); + Node opLeft = new Node(Token.USE_STACK); + Node op = new Node(assignOp, opLeft, right); + return new Node(Token.SET_REF_OP, ref, op); + } + } + + throw Kit.codeBug(); + } + + /** + * Given a destructuring assignment with a left hand side parsed + * as an array or object literal and a right hand side expression, + * rewrite as a series of assignments to the variables defined in + * left from property accesses to the expression on the right. + * @param type declaration type: Token.VAR or Token.LET or -1 + * @param left array or object literal containing NAME nodes for + * variables to assign + * @param right expression to assign from + * @return expression that performs a series of assignments to + * the variables defined in left + */ + Node createDestructuringAssignment(int type, Node left, Node right) + { + String tempName = parser.currentScriptOrFn.getNextTempName(); + Node result = destructuringAssignmentHelper(type, left, right, + tempName); + Node comma = result.getLastChild(); + comma.addChildToBack(createName(tempName)); + return result; + } + + private Node destructuringAssignmentHelper(int variableType, Node left, + Node right, String tempName) + { + Node result = createScopeNode(Token.LETEXPR, + parser.getCurrentLineNumber()); + result.addChildToFront(new Node(Token.LET, + createName(Token.NAME, tempName, right))); + try { + parser.pushScope(result); + parser.defineSymbol(Token.LET, tempName); + } finally { + parser.popScope(); + } + Node comma = new Node(Token.COMMA); + result.addChildToBack(comma); + final int setOp = variableType == Token.CONST ? Token.SETCONST + : Token.SETNAME; + List destructuringNames = new ArrayList(); + boolean empty = true; + int type = left.getType(); + if (type == Token.ARRAYLIT) { + int index = 0; + int[] skipIndices = (int[])left.getProp(Node.SKIP_INDEXES_PROP); + int skip = 0; + Node n = left.getFirstChild(); + for (;;) { + if (skipIndices != null) { + while (skip < skipIndices.length && + skipIndices[skip] == index) { + skip++; + index++; + } + } + if (n == null) + break; + Node rightElem = new Node(Token.GETELEM, + createName(tempName), + createNumber(index)); + if (n.getType() == Token.NAME) { + String name = n.getString(); + comma.addChildToBack(new Node(setOp, + createName(Token.BINDNAME, name, null), + rightElem)); + if (variableType != -1) { + parser.defineSymbol(variableType, name); + destructuringNames.add(name); + } + } else { + comma.addChildToBack( + destructuringAssignmentHelper(variableType, n, + rightElem, + parser.currentScriptOrFn.getNextTempName())); + } + index++; + empty = false; + n = n.getNext(); + } + } else if (type == Token.OBJECTLIT) { + int index = 0; + Object[] propertyIds = (Object[]) + left.getProp(Node.OBJECT_IDS_PROP); + for (Node n = left.getFirstChild(); n != null; n = n.getNext()) + { + Object id = propertyIds[index]; + Node rightElem = id instanceof String + ? new Node(Token.GETPROP, + createName(tempName), + createString((String)id)) + : new Node(Token.GETELEM, + createName(tempName), + createNumber(((Number)id).intValue())); + if (n.getType() == Token.NAME) { + String name = n.getString(); + comma.addChildToBack(new Node(setOp, + createName(Token.BINDNAME, name, null), + rightElem)); + if (variableType != -1) { + parser.defineSymbol(variableType, name); + destructuringNames.add(name); + } + } else { + comma.addChildToBack( + destructuringAssignmentHelper(variableType, n, + rightElem, + parser.currentScriptOrFn.getNextTempName())); + } + index++; + empty = false; + } + } else if (type == Token.GETPROP || type == Token.GETELEM) { + comma.addChildToBack(simpleAssignment(left, createName(tempName))); + } else { + parser.reportError("msg.bad.assign.left"); + } + if (empty) { + // Don't want a COMMA node with no children. Just add a zero. + comma.addChildToBack(createNumber(0)); + } + result.putProp(Node.DESTRUCTURING_NAMES, destructuringNames); + return result; + } + + Node createUseLocal(Node localBlock) + { + if (Token.LOCAL_BLOCK != localBlock.getType()) throw Kit.codeBug(); + Node result = new Node(Token.LOCAL_LOAD); + result.putProp(Node.LOCAL_BLOCK_PROP, localBlock); + return result; + } + + private Node.Jump makeJump(int type, Node target) + { + Node.Jump n = new Node.Jump(type); + n.target = target; + return n; + } + + private Node makeReference(Node node) + { + int type = node.getType(); + switch (type) { + case Token.NAME: + case Token.GETPROP: + case Token.GETELEM: + case Token.GET_REF: + return node; + case Token.CALL: + node.setType(Token.REF_CALL); + return new Node(Token.GET_REF, node); + } + // Signal caller to report error + return null; + } + + // Check if Node always mean true or false in boolean context + private static int isAlwaysDefinedBoolean(Node node) + { + switch (node.getType()) { + case Token.FALSE: + case Token.NULL: + return ALWAYS_FALSE_BOOLEAN; + case Token.TRUE: + return ALWAYS_TRUE_BOOLEAN; + case Token.NUMBER: { + double num = node.getDouble(); + if (num == num && num != 0.0) { + return ALWAYS_TRUE_BOOLEAN; + } else { + return ALWAYS_FALSE_BOOLEAN; + } + } + } + return 0; + } + + private void checkActivationName(String name, int token) + { + if (parser.insideFunction()) { + boolean activation = false; + if ("arguments".equals(name) + || (parser.compilerEnv.activationNames != null + && parser.compilerEnv.activationNames.containsKey(name))) + { + activation = true; + } else if ("length".equals(name)) { + if (token == Token.GETPROP + && parser.compilerEnv.getLanguageVersion() + == Context.VERSION_1_2) + { + // Use of "length" in 1.2 requires an activation object. + activation = true; + } + } + if (activation) { + setRequiresActivation(); + } + } + } + + private void setRequiresActivation() + { + if (parser.insideFunction()) { + ((FunctionNode)parser.currentScriptOrFn).itsNeedsActivation = true; + } + } + + private void setIsGenerator() + { + if (parser.insideFunction()) { + ((FunctionNode)parser.currentScriptOrFn).itsIsGenerator = true; + } + } + + private Parser parser; + + private static final int LOOP_DO_WHILE = 0; + private static final int LOOP_WHILE = 1; + private static final int LOOP_FOR = 2; + + private static final int ALWAYS_TRUE_BOOLEAN = 1; + private static final int ALWAYS_FALSE_BOOLEAN = -1; +} diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/IdFunctionCall.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/IdFunctionCall.java new file mode 100644 index 0000000..713fabf --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/IdFunctionCall.java @@ -0,0 +1,55 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Igor Bukanov + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript; + +/** + * Master for id-based functions that knows their properties and how to + * execute them. + */ +public interface IdFunctionCall +{ + /** + * 'thisObj' will be null if invoked as constructor, in which case + * instance of Scriptable should be returned + */ + public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, + Scriptable thisObj, Object[] args); + +} + diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/IdFunctionObject.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/IdFunctionObject.java new file mode 100644 index 0000000..c578dfa --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/IdFunctionObject.java @@ -0,0 +1,189 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Igor Bukanov + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +// API class + +package org.mozilla.javascript; + +public class IdFunctionObject extends BaseFunction +{ + + static final long serialVersionUID = -5332312783643935019L; + + public IdFunctionObject(IdFunctionCall idcall, Object tag, int id, int arity) + { + if (arity < 0) + throw new IllegalArgumentException(); + + this.idcall = idcall; + this.tag = tag; + this.methodId = id; + this.arity = arity; + if (arity < 0) throw new IllegalArgumentException(); + } + + public IdFunctionObject(IdFunctionCall idcall, Object tag, int id, + String name, int arity, Scriptable scope) + { + super(scope, null); + + if (arity < 0) + throw new IllegalArgumentException(); + if (name == null) + throw new IllegalArgumentException(); + + this.idcall = idcall; + this.tag = tag; + this.methodId = id; + this.arity = arity; + this.functionName = name; + } + + public void initFunction(String name, Scriptable scope) + { + if (name == null) throw new IllegalArgumentException(); + if (scope == null) throw new IllegalArgumentException(); + this.functionName = name; + setParentScope(scope); + } + + public final boolean hasTag(Object tag) + { + return this.tag == tag; + } + + public final int methodId() + { + return methodId; + } + + public final void markAsConstructor(Scriptable prototypeProperty) + { + useCallAsConstructor = true; + setImmunePrototypeProperty(prototypeProperty); + } + + public final void addAsProperty(Scriptable target) + { + ScriptableObject.defineProperty(target, functionName, this, + ScriptableObject.DONTENUM); + } + + public void exportAsScopeProperty() + { + addAsProperty(getParentScope()); + } + + public Scriptable getPrototype() + { + // Lazy initialization of prototype: for native functions this + // may not be called at all + Scriptable proto = super.getPrototype(); + if (proto == null) { + proto = getFunctionPrototype(getParentScope()); + setPrototype(proto); + } + return proto; + } + + public Object call(Context cx, Scriptable scope, Scriptable thisObj, + Object[] args) + { + return idcall.execIdCall(this, cx, scope, thisObj, args); + } + + public Scriptable createObject(Context cx, Scriptable scope) + { + if (useCallAsConstructor) { + return null; + } + // Throw error if not explicitly coded to be used as constructor, + // to satisfy ECMAScript standard (see bugzilla 202019). + // To follow current (2003-05-01) SpiderMonkey behavior, change it to: + // return super.createObject(cx, scope); + throw ScriptRuntime.typeError1("msg.not.ctor", functionName); + } + + String decompile(int indent, int flags) + { + StringBuffer sb = new StringBuffer(); + boolean justbody = (0 != (flags & Decompiler.ONLY_BODY_FLAG)); + if (!justbody) { + sb.append("function "); + sb.append(getFunctionName()); + sb.append("() { "); + } + sb.append("[native code for "); + if (idcall instanceof Scriptable) { + Scriptable sobj = (Scriptable)idcall; + sb.append(sobj.getClassName()); + sb.append('.'); + } + sb.append(getFunctionName()); + sb.append(", arity="); + sb.append(getArity()); + sb.append(justbody ? "]\n" : "] }\n"); + return sb.toString(); + } + + public int getArity() + { + return arity; + } + + public int getLength() { return getArity(); } + + public String getFunctionName() + { + return (functionName == null) ? "" : functionName; + } + + public final RuntimeException unknown() + { + // It is program error to call id-like methods for unknown function + return new IllegalArgumentException( + "BAD FUNCTION ID="+methodId+" MASTER="+idcall); + } + + private final IdFunctionCall idcall; + private final Object tag; + private final int methodId; + private int arity; + private boolean useCallAsConstructor; + private String functionName; +} diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/IdScriptableObject.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/IdScriptableObject.java new file mode 100644 index 0000000..2b3ecf3 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/IdScriptableObject.java @@ -0,0 +1,734 @@ +/* -*- Mode: java; tab-width: 4; indent-tabs-mode: 1; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Igor Bukanov + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript; + +import java.io.*; + +/** +Base class for native object implementation that uses IdFunctionObject to export its methods to script via .prototype object. + +Any descendant should implement at least the following methods: + findInstanceIdInfo + getInstanceIdName + execIdCall + methodArity + +To define non-function properties, the descendant should override + getInstanceIdValue + setInstanceIdValue +to get/set property value and provide its default attributes. + + +To customize initializition of constructor and protype objects, descendant +may override scopeInit or fillConstructorProperties methods. + +*/ +public abstract class IdScriptableObject extends ScriptableObject + implements IdFunctionCall +{ + private transient volatile PrototypeValues prototypeValues; + + private static final class PrototypeValues implements Serializable + { + static final long serialVersionUID = 3038645279153854371L; + + private static final int VALUE_SLOT = 0; + private static final int NAME_SLOT = 1; + private static final int SLOT_SPAN = 2; + + private IdScriptableObject obj; + private int maxId; + private volatile Object[] valueArray; + private volatile short[] attributeArray; + private volatile int lastFoundId = 1; + + // The following helps to avoid creation of valueArray during runtime + // initialization for common case of "constructor" property + int constructorId; + private IdFunctionObject constructor; + private short constructorAttrs; + + PrototypeValues(IdScriptableObject obj, int maxId) + { + if (obj == null) throw new IllegalArgumentException(); + if (maxId < 1) throw new IllegalArgumentException(); + this.obj = obj; + this.maxId = maxId; + } + + final int getMaxId() + { + return maxId; + } + + final void initValue(int id, String name, Object value, int attributes) + { + if (!(1 <= id && id <= maxId)) + throw new IllegalArgumentException(); + if (name == null) + throw new IllegalArgumentException(); + if (value == NOT_FOUND) + throw new IllegalArgumentException(); + ScriptableObject.checkValidAttributes(attributes); + if (obj.findPrototypeId(name) != id) + throw new IllegalArgumentException(name); + + if (id == constructorId) { + if (!(value instanceof IdFunctionObject)) { + throw new IllegalArgumentException("consructor should be initialized with IdFunctionObject"); + } + constructor = (IdFunctionObject)value; + constructorAttrs = (short)attributes; + return; + } + + initSlot(id, name, value, attributes); + } + + private void initSlot(int id, String name, Object value, + int attributes) + { + Object[] array = valueArray; + if (array == null) + throw new IllegalStateException(); + + if (value == null) { + value = UniqueTag.NULL_VALUE; + } + int index = (id - 1) * SLOT_SPAN; + synchronized (this) { + Object value2 = array[index + VALUE_SLOT]; + if (value2 == null) { + array[index + VALUE_SLOT] = value; + array[index + NAME_SLOT] = name; + attributeArray[id - 1] = (short)attributes; + } else { + if (!name.equals(array[index + NAME_SLOT])) + throw new IllegalStateException(); + } + } + } + + final IdFunctionObject createPrecachedConstructor() + { + if (constructorId != 0) throw new IllegalStateException(); + constructorId = obj.findPrototypeId("constructor"); + if (constructorId == 0) { + throw new IllegalStateException( + "No id for constructor property"); + } + obj.initPrototypeId(constructorId); + if (constructor == null) { + throw new IllegalStateException( + obj.getClass().getName()+".initPrototypeId() did not " + +"initialize id="+constructorId); + } + constructor.initFunction(obj.getClassName(), + ScriptableObject.getTopLevelScope(obj)); + constructor.markAsConstructor(obj); + return constructor; + } + + final int findId(String name) + { + Object[] array = valueArray; + if (array == null) { + return obj.findPrototypeId(name); + } + int id = lastFoundId; + if (name == array[(id - 1) * SLOT_SPAN + NAME_SLOT]) { + return id; + } + id = obj.findPrototypeId(name); + if (id != 0) { + int nameSlot = (id - 1) * SLOT_SPAN + NAME_SLOT; + // Make cache to work! + array[nameSlot] = name; + lastFoundId = id; + } + return id; + } + + final boolean has(int id) + { + Object[] array = valueArray; + if (array == null) { + // Not yet initialized, assume all exists + return true; + } + int valueSlot = (id - 1) * SLOT_SPAN + VALUE_SLOT; + Object value = array[valueSlot]; + if (value == null) { + // The particular entry has not been yet initialized + return true; + } + return value != NOT_FOUND; + } + + final Object get(int id) + { + Object value = ensureId(id); + if (value == UniqueTag.NULL_VALUE) { + value = null; + } + return value; + } + + final void set(int id, Scriptable start, Object value) + { + if (value == NOT_FOUND) throw new IllegalArgumentException(); + ensureId(id); + int attr = attributeArray[id - 1]; + if ((attr & READONLY) == 0) { + if (start == obj) { + if (value == null) { + value = UniqueTag.NULL_VALUE; + } + int valueSlot = (id - 1) * SLOT_SPAN + VALUE_SLOT; + synchronized (this) { + valueArray[valueSlot] = value; + } + } + else { + int nameSlot = (id - 1) * SLOT_SPAN + NAME_SLOT; + String name = (String)valueArray[nameSlot]; + start.put(name, start, value); + } + } + } + + final void delete(int id) + { + ensureId(id); + int attr = attributeArray[id - 1]; + if ((attr & PERMANENT) == 0) { + int valueSlot = (id - 1) * SLOT_SPAN + VALUE_SLOT; + synchronized (this) { + valueArray[valueSlot] = NOT_FOUND; + attributeArray[id - 1] = EMPTY; + } + } + } + + final int getAttributes(int id) + { + ensureId(id); + return attributeArray[id - 1]; + } + + final void setAttributes(int id, int attributes) + { + ScriptableObject.checkValidAttributes(attributes); + ensureId(id); + synchronized (this) { + attributeArray[id - 1] = (short)attributes; + } + } + + final Object[] getNames(boolean getAll, Object[] extraEntries) + { + Object[] names = null; + int count = 0; + for (int id = 1; id <= maxId; ++id) { + Object value = ensureId(id); + if (getAll || (attributeArray[id - 1] & DONTENUM) == 0) { + if (value != NOT_FOUND) { + int nameSlot = (id - 1) * SLOT_SPAN + NAME_SLOT; + String name = (String)valueArray[nameSlot]; + if (names == null) { + names = new Object[maxId]; + } + names[count++] = name; + } + } + } + if (count == 0) { + return extraEntries; + } else if (extraEntries == null || extraEntries.length == 0) { + if (count != names.length) { + Object[] tmp = new Object[count]; + System.arraycopy(names, 0, tmp, 0, count); + names = tmp; + } + return names; + } else { + int extra = extraEntries.length; + Object[] tmp = new Object[extra + count]; + System.arraycopy(extraEntries, 0, tmp, 0, extra); + System.arraycopy(names, 0, tmp, extra, count); + return tmp; + } + } + + private Object ensureId(int id) + { + Object[] array = valueArray; + if (array == null) { + synchronized (this) { + array = valueArray; + if (array == null) { + array = new Object[maxId * SLOT_SPAN]; + valueArray = array; + attributeArray = new short[maxId]; + } + } + } + int valueSlot = (id - 1) * SLOT_SPAN + VALUE_SLOT; + Object value = array[valueSlot]; + if (value == null) { + if (id == constructorId) { + initSlot(constructorId, "constructor", + constructor, constructorAttrs); + constructor = null; // no need to refer it any longer + } else { + obj.initPrototypeId(id); + } + value = array[valueSlot]; + if (value == null) { + throw new IllegalStateException( + obj.getClass().getName()+".initPrototypeId(int id) " + +"did not initialize id="+id); + } + } + return value; + } + } + + public IdScriptableObject() + { + } + + public IdScriptableObject(Scriptable scope, Scriptable prototype) + { + super(scope, prototype); + } + + protected final Object defaultGet(String name) + { + return super.get(name, this); + } + + protected final void defaultPut(String name, Object value) + { + super.put(name, this, value); + } + + public boolean has(String name, Scriptable start) + { + int info = findInstanceIdInfo(name); + if (info != 0) { + int attr = (info >>> 16); + if ((attr & PERMANENT) != 0) { + return true; + } + int id = (info & 0xFFFF); + return NOT_FOUND != getInstanceIdValue(id); + } + if (prototypeValues != null) { + int id = prototypeValues.findId(name); + if (id != 0) { + return prototypeValues.has(id); + } + } + return super.has(name, start); + } + + public Object get(String name, Scriptable start) + { + int info = findInstanceIdInfo(name); + if (info != 0) { + int id = (info & 0xFFFF); + return getInstanceIdValue(id); + } + if (prototypeValues != null) { + int id = prototypeValues.findId(name); + if (id != 0) { + return prototypeValues.get(id); + } + } + return super.get(name, start); + } + + public void put(String name, Scriptable start, Object value) + { + int info = findInstanceIdInfo(name); + if (info != 0) { + if (start == this && isSealed()) { + throw Context.reportRuntimeError1("msg.modify.sealed", + name); + } + int attr = (info >>> 16); + if ((attr & READONLY) == 0) { + if (start == this) { + int id = (info & 0xFFFF); + setInstanceIdValue(id, value); + } + else { + start.put(name, start, value); + } + } + return; + } + if (prototypeValues != null) { + int id = prototypeValues.findId(name); + if (id != 0) { + if (start == this && isSealed()) { + throw Context.reportRuntimeError1("msg.modify.sealed", + name); + } + prototypeValues.set(id, start, value); + return; + } + } + super.put(name, start, value); + } + + public void delete(String name) + { + int info = findInstanceIdInfo(name); + if (info != 0) { + // Let the super class to throw exceptions for sealed objects + if (!isSealed()) { + int attr = (info >>> 16); + if ((attr & PERMANENT) == 0) { + int id = (info & 0xFFFF); + setInstanceIdValue(id, NOT_FOUND); + } + return; + } + } + if (prototypeValues != null) { + int id = prototypeValues.findId(name); + if (id != 0) { + if (!isSealed()) { + prototypeValues.delete(id); + } + return; + } + } + super.delete(name); + } + + public int getAttributes(String name) + { + int info = findInstanceIdInfo(name); + if (info != 0) { + int attr = (info >>> 16); + return attr; + } + if (prototypeValues != null) { + int id = prototypeValues.findId(name); + if (id != 0) { + return prototypeValues.getAttributes(id); + } + } + return super.getAttributes(name); + } + + public void setAttributes(String name, int attributes) + { + ScriptableObject.checkValidAttributes(attributes); + int info = findInstanceIdInfo(name); + if (info != 0) { + int currentAttributes = (info >>> 16); + if (attributes != currentAttributes) { + throw new RuntimeException( + "Change of attributes for this id is not supported"); + } + return; + } + if (prototypeValues != null) { + int id = prototypeValues.findId(name); + if (id != 0) { + prototypeValues.setAttributes(id, attributes); + return; + } + } + super.setAttributes(name, attributes); + } + + Object[] getIds(boolean getAll) + { + Object[] result = super.getIds(getAll); + + if (prototypeValues != null) { + result = prototypeValues.getNames(getAll, result); + } + + int maxInstanceId = getMaxInstanceId(); + if (maxInstanceId != 0) { + Object[] ids = null; + int count = 0; + + for (int id = maxInstanceId; id != 0; --id) { + String name = getInstanceIdName(id); + int info = findInstanceIdInfo(name); + if (info != 0) { + int attr = (info >>> 16); + if ((attr & PERMANENT) == 0) { + if (NOT_FOUND == getInstanceIdValue(id)) { + continue; + } + } + if (getAll || (attr & DONTENUM) == 0) { + if (count == 0) { + // Need extra room for no more then [1..id] names + ids = new Object[id]; + } + ids[count++] = name; + } + } + } + if (count != 0) { + if (result.length == 0 && ids.length == count) { + result = ids; + } + else { + Object[] tmp = new Object[result.length + count]; + System.arraycopy(result, 0, tmp, 0, result.length); + System.arraycopy(ids, 0, tmp, result.length, count); + result = tmp; + } + } + } + return result; + } + + /** + * Get maximum id findInstanceIdInfo can generate. + */ + protected int getMaxInstanceId() + { + return 0; + } + + protected static int instanceIdInfo(int attributes, int id) + { + return (attributes << 16) | id; + } + + /** + * Map name to id of instance property. + * Should return 0 if not found or the result of + * {@link #instanceIdInfo(int, int)}. + */ + protected int findInstanceIdInfo(String name) + { + return 0; + } + + /** Map id back to property name it defines. + */ + protected String getInstanceIdName(int id) + { + throw new IllegalArgumentException(String.valueOf(id)); + } + + /** Get id value. + ** If id value is constant, descendant can call cacheIdValue to store + ** value in the permanent cache. + ** Default implementation creates IdFunctionObject instance for given id + ** and cache its value + */ + protected Object getInstanceIdValue(int id) + { + throw new IllegalStateException(String.valueOf(id)); + } + + /** + * Set or delete id value. If value == NOT_FOUND , the implementation + * should make sure that the following getInstanceIdValue return NOT_FOUND. + */ + protected void setInstanceIdValue(int id, Object value) + { + throw new IllegalStateException(String.valueOf(id)); + } + + /** 'thisObj' will be null if invoked as constructor, in which case + ** instance of Scriptable should be returned. */ + public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, + Scriptable thisObj, Object[] args) + { + throw f.unknown(); + } + + public final IdFunctionObject exportAsJSClass(int maxPrototypeId, + Scriptable scope, + boolean sealed) + { + // Set scope and prototype unless this is top level scope itself + if (scope != this && scope != null) { + setParentScope(scope); + setPrototype(getObjectPrototype(scope)); + } + + activatePrototypeMap(maxPrototypeId); + IdFunctionObject ctor = prototypeValues.createPrecachedConstructor(); + if (sealed) { + sealObject(); + } + fillConstructorProperties(ctor); + if (sealed) { + ctor.sealObject(); + } + ctor.exportAsScopeProperty(); + return ctor; + } + + public final boolean hasPrototypeMap() + { + return prototypeValues != null; + } + + public final void activatePrototypeMap(int maxPrototypeId) + { + PrototypeValues values = new PrototypeValues(this, maxPrototypeId); + synchronized (this) { + if (prototypeValues != null) + throw new IllegalStateException(); + prototypeValues = values; + } + } + + public final void initPrototypeMethod(Object tag, int id, String name, + int arity) + { + Scriptable scope = ScriptableObject.getTopLevelScope(this); + IdFunctionObject f = newIdFunction(tag, id, name, arity, scope); + prototypeValues.initValue(id, name, f, DONTENUM); + } + + public final void initPrototypeConstructor(IdFunctionObject f) + { + int id = prototypeValues.constructorId; + if (id == 0) + throw new IllegalStateException(); + if (f.methodId() != id) + throw new IllegalArgumentException(); + if (isSealed()) { f.sealObject(); } + prototypeValues.initValue(id, "constructor", f, DONTENUM); + } + + public final void initPrototypeValue(int id, String name, Object value, + int attributes) + { + prototypeValues.initValue(id, name, value, attributes); + } + + protected void initPrototypeId(int id) + { + throw new IllegalStateException(String.valueOf(id)); + } + + protected int findPrototypeId(String name) + { + throw new IllegalStateException(name); + } + + protected void fillConstructorProperties(IdFunctionObject ctor) + { + } + + protected void addIdFunctionProperty(Scriptable obj, Object tag, int id, + String name, int arity) + { + Scriptable scope = ScriptableObject.getTopLevelScope(obj); + IdFunctionObject f = newIdFunction(tag, id, name, arity, scope); + f.addAsProperty(obj); + } + + /** + * Utility method to construct type error to indicate incompatible call + * when converting script thisObj to a particular type is not possible. + * Possible usage would be to have a private function like realThis: + *

    +     *  private static NativeSomething realThis(Scriptable thisObj,
    +     *                                          IdFunctionObject f)
    +     *  {
    +     *      if (!(thisObj instanceof NativeSomething))
    +     *          throw incompatibleCallError(f);
    +     *      return (NativeSomething)thisObj;
    +     * }
    +     * 
    + * Note that although such function can be implemented universally via + * java.lang.Class.isInstance(), it would be much more slower. + * @param f function that is attempting to convert 'this' + * object. + * @return Scriptable object suitable for a check by the instanceof + * operator. + * @throws RuntimeException if no more instanceof target can be found + */ + protected static EcmaError incompatibleCallError(IdFunctionObject f) + { + throw ScriptRuntime.typeError1("msg.incompat.call", + f.getFunctionName()); + } + + private IdFunctionObject newIdFunction(Object tag, int id, String name, + int arity, Scriptable scope) + { + IdFunctionObject f = new IdFunctionObject(this, tag, id, name, arity, + scope); + if (isSealed()) { f.sealObject(); } + return f; + } + + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException + { + stream.defaultReadObject(); + int maxPrototypeId = stream.readInt(); + if (maxPrototypeId != 0) { + activatePrototypeMap(maxPrototypeId); + } + } + + private void writeObject(ObjectOutputStream stream) + throws IOException + { + stream.defaultWriteObject(); + int maxPrototypeId = 0; + if (prototypeValues != null) { + maxPrototypeId = prototypeValues.getMaxId(); + } + stream.writeInt(maxPrototypeId); + } + +} + diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ImporterTopLevel.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ImporterTopLevel.java new file mode 100644 index 0000000..294deab --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ImporterTopLevel.java @@ -0,0 +1,318 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Norris Boyd + * Igor Bukanov + * Matthias Radestock + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +// API class + +package org.mozilla.javascript; + +/** + * Class ImporterTopLevel + * + * This class defines a ScriptableObject that can be instantiated + * as a top-level ("global") object to provide functionality similar + * to Java's "import" statement. + *

    + * This class can be used to create a top-level scope using the following code: + *

    + *  Scriptable scope = new ImporterTopLevel(cx);
    + * 
    + * Then JavaScript code will have access to the following methods: + *
      + *
    • importClass - will "import" a class by making its unqualified name + * available as a property of the top-level scope + *
    • importPackage - will "import" all the classes of the package by + * searching for unqualified names as classes qualified + * by the given package. + *
    + * The following code from the shell illustrates this use: + *
    + * js> importClass(java.io.File)
    + * js> f = new File('help.txt')
    + * help.txt
    + * js> importPackage(java.util)
    + * js> v = new Vector()
    + * []
    + *
    + * @author Norris Boyd
    + */
    +public class ImporterTopLevel extends IdScriptableObject
    +{
    +    static final long serialVersionUID = -9095380847465315412L;
    +
    +    private static final Object IMPORTER_TAG = new Object();
    +
    +    public ImporterTopLevel() { }
    +
    +    public ImporterTopLevel(Context cx) {
    +        this(cx, false);
    +    }
    +
    +    public ImporterTopLevel(Context cx, boolean sealed)
    +    {
    +        initStandardObjects(cx, sealed);
    +    }
    +
    +    public String getClassName()
    +    {
    +        return (topScopeFlag) ? "global" : "JavaImporter";
    +    }
    +
    +    public static void init(Context cx, Scriptable scope, boolean sealed)
    +    {
    +        ImporterTopLevel obj = new ImporterTopLevel();
    +        obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
    +    }
    +
    +    public void initStandardObjects(Context cx, boolean sealed)
    +    {
    +        // Assume that Context.initStandardObjects initialize JavaImporter
    +        // property lazily so the above init call is not yet called
    +        cx.initStandardObjects(this, sealed);
    +        topScopeFlag = true;
    +        // If seal is true then exportAsJSClass(cx, seal) would seal
    +        // this obj. Since this is scope as well, it would not allow
    +        // to add variables.
    +        IdFunctionObject ctor = exportAsJSClass(MAX_PROTOTYPE_ID, this, false);
    +        if (sealed) {
    +            ctor.sealObject();
    +        }
    +        // delete "constructor" defined by exportAsJSClass so "constructor"
    +        // name would refer to Object.constructor
    +        // and not to JavaImporter.prototype.constructor.
    +        delete("constructor");
    +    }
    +
    +    public boolean has(String name, Scriptable start) {
    +        return super.has(name, start)
    +               || getPackageProperty(name, start) != NOT_FOUND;
    +    }
    +
    +    public Object get(String name, Scriptable start) {
    +        Object result = super.get(name, start);
    +        if (result != NOT_FOUND)
    +            return result;
    +        result = getPackageProperty(name, start);
    +        return result;
    +    }
    +
    +    private Object getPackageProperty(String name, Scriptable start) {
    +        Object result = NOT_FOUND;
    +        Object[] elements;
    +        synchronized (importedPackages) {
    +            elements = importedPackages.toArray();
    +        }
    +        for (int i=0; i < elements.length; i++) {
    +            NativeJavaPackage p = (NativeJavaPackage) elements[i];
    +            Object v = p.getPkgProperty(name, start, false);
    +            if (v != null && !(v instanceof NativeJavaPackage)) {
    +                if (result == NOT_FOUND) {
    +                    result = v;
    +                } else {
    +                    throw Context.reportRuntimeError2(
    +                        "msg.ambig.import", result.toString(), v.toString());
    +                }
    +            }
    +        }
    +        return result;
    +    }
    +
    +    /**
    +     * @deprecated Kept only for compatibility.
    +     */
    +    public void importPackage(Context cx, Scriptable thisObj, Object[] args,
    +                              Function funObj)
    +    {
    +        js_importPackage(args);
    +    }
    +
    +    private Object js_construct(Scriptable scope, Object[] args)
    +    {
    +        ImporterTopLevel result = new ImporterTopLevel();
    +        for (int i = 0; i != args.length; ++i) {
    +            Object arg = args[i];
    +            if (arg instanceof NativeJavaClass) {
    +                result.importClass((NativeJavaClass)arg);
    +            } else if (arg instanceof NativeJavaPackage) {
    +                result.importPackage((NativeJavaPackage)arg);
    +            } else {
    +                throw Context.reportRuntimeError1(
    +                    "msg.not.class.not.pkg", Context.toString(arg));
    +            }
    +        }
    +        // set explicitly prototype and scope
    +        // as otherwise in top scope mode BaseFunction.construct
    +        // would keep them set to null. It also allow to use
    +        // JavaImporter without new and still get properly
    +        // initialized object.
    +        result.setParentScope(scope);
    +        result.setPrototype(this);
    +        return result;
    +    }
    +
    +    private Object js_importClass(Object[] args)
    +    {
    +        for (int i = 0; i != args.length; i++) {
    +            Object arg = args[i];
    +            if (!(arg instanceof NativeJavaClass)) {
    +                throw Context.reportRuntimeError1(
    +                    "msg.not.class", Context.toString(arg));
    +            }
    +            importClass((NativeJavaClass)arg);
    +        }
    +        return Undefined.instance;
    +    }
    +
    +    private Object js_importPackage(Object[] args)
    +    {
    +        for (int i = 0; i != args.length; i++) {
    +            Object arg = args[i];
    +            if (!(arg instanceof NativeJavaPackage)) {
    +                throw Context.reportRuntimeError1(
    +                    "msg.not.pkg", Context.toString(arg));
    +            }
    +            importPackage((NativeJavaPackage)arg);
    +        }
    +        return Undefined.instance;
    +    }
    +
    +    private void importPackage(NativeJavaPackage pkg)
    +    {
    +        if(pkg == null) {
    +            return;
    +        }
    +        synchronized (importedPackages) {
    +            for (int j = 0; j != importedPackages.size(); j++) {
    +                if (pkg.equals(importedPackages.get(j))) {
    +                    return;
    +                }
    +            }
    +            importedPackages.add(pkg);
    +        }
    +    }
    +
    +    private void importClass(NativeJavaClass cl)
    +    {
    +        String s = cl.getClassObject().getName();
    +        String n = s.substring(s.lastIndexOf('.')+1);
    +        Object val = get(n, this);
    +        if (val != NOT_FOUND && val != cl) {
    +            throw Context.reportRuntimeError1("msg.prop.defined", n);
    +        }
    +        //defineProperty(n, cl, DONTENUM);
    +        put(n, this, cl);
    +    }
    +
    +    protected void initPrototypeId(int id)
    +    {
    +        String s;
    +        int arity;
    +        switch (id) {
    +          case Id_constructor:   arity=0; s="constructor";   break;
    +          case Id_importClass:   arity=1; s="importClass";   break;
    +          case Id_importPackage: arity=1; s="importPackage"; break;
    +          default: throw new IllegalArgumentException(String.valueOf(id));
    +        }
    +        initPrototypeMethod(IMPORTER_TAG, id, s, arity);
    +    }
    +
    +    public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope,
    +                             Scriptable thisObj, Object[] args)
    +    {
    +        if (!f.hasTag(IMPORTER_TAG)) {
    +            return super.execIdCall(f, cx, scope, thisObj, args);
    +        }
    +        int id = f.methodId();
    +        switch (id) {
    +          case Id_constructor:
    +            return js_construct(scope, args);
    +
    +          case Id_importClass:
    +            return realThis(thisObj, f).js_importClass(args);
    +
    +          case Id_importPackage:
    +            return realThis(thisObj, f).js_importPackage(args);
    +        }
    +        throw new IllegalArgumentException(String.valueOf(id));
    +    }
    +
    +    private ImporterTopLevel realThis(Scriptable thisObj, IdFunctionObject f)
    +    {
    +        if (topScopeFlag) {
    +            // when used as top scope importPackage and importClass are global
    +            // function that ignore thisObj
    +            return this;
    +        }
    +        if (!(thisObj instanceof ImporterTopLevel))
    +            throw incompatibleCallError(f);
    +        return (ImporterTopLevel)thisObj;
    +    }
    +
    +// #string_id_map#
    +
    +    protected int findPrototypeId(String s)
    +    {
    +        int id;
    +// #generated# Last update: 2007-05-09 08:15:24 EDT
    +        L0: { id = 0; String X = null; int c;
    +            int s_length = s.length();
    +            if (s_length==11) {
    +                c=s.charAt(0);
    +                if (c=='c') { X="constructor";id=Id_constructor; }
    +                else if (c=='i') { X="importClass";id=Id_importClass; }
    +            }
    +            else if (s_length==13) { X="importPackage";id=Id_importPackage; }
    +            if (X!=null && X!=s && !X.equals(s)) id = 0;
    +            break L0;
    +        }
    +// #/generated#
    +        return id;
    +    }
    +
    +    private static final int
    +        Id_constructor          = 1,
    +        Id_importClass          = 2,
    +        Id_importPackage        = 3,
    +        MAX_PROTOTYPE_ID        = 3;
    +
    +// #/string_id_map#
    +
    +    private ObjArray importedPackages = new ObjArray();
    +    private boolean topScopeFlag;
    +}
    diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/InformativeParser.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/InformativeParser.java
    new file mode 100644
    index 0000000..c73db34
    --- /dev/null
    +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/InformativeParser.java
    @@ -0,0 +1,225 @@
    +package org.mozilla.javascript;
    +
    +import java.io.IOException;
    +
    +/**
    + *  Subclass of Rhino's Parser that saves information about the token stream
    + *  and error message to allow more helpful error messages.
    + *
    + * @author David Greenspan for AppJet
    + */
    +
    +/*
    +  This class is written with speed in mind, to some extent.  Rhino's tokenizer
    +  is pretty efficient, and we wouldn't want to slow it down by, for example,
    +  creating a TokenInfo object on the heap for every token seen.
    + */
    +
    +/*APPJET*/
    +public class InformativeParser extends Parser {
    +
    +    public static class InformativeEvaluatorException extends EvaluatorException {
    +	final ParseErrorInfo pei;
    +	
    +	InformativeEvaluatorException(String errorMessage,
    +				      String sourceName, int lineNumber,
    +				      String lineSource, int columnNumber,
    +				      ParseErrorInfo peInfo) {
    +	    super(errorMessage, sourceName,
    +		  lineNumber, lineSource, columnNumber);
    +	    pei = peInfo;
    +	}
    +
    +	public ParseErrorInfo getParseErrorInfo() {
    +	    return pei;
    +	}
    +    }
    +
    +    public static class ParseErrorInfo {
    +	ParseErrorInfo() {}
    +
    +	String messageId = null;
    +	String messageArg = null;
    +	
    +	final int tokenMaxHistory = 10;
    +	// ring buffers
    +	final int[] tokenTypes = new int[tokenMaxHistory];
    +	final String[] tokenStrings = new String[tokenMaxHistory];
    +	final double[] tokenNumbers = new double[tokenMaxHistory];
    +	final int[] tokenLineNumbers = new int[tokenMaxHistory];
    +	final int[] tokenLineOffsets = new int[tokenMaxHistory];
    +	int nextBufPos = 0;
    +	int historyLength = 0;
    +	boolean tokenPeeking = false;
    +	int peekSlot;
    +
    +	void reportPeekToken(int type, String str, double num, int lineno,
    +			     int lineOffset) {
    +	    if (! tokenPeeking) {
    +		peekSlot = nextBufPos;
    +		tokenTypes[nextBufPos] = type;
    +		tokenStrings[nextBufPos] = str;
    +		tokenNumbers[nextBufPos] = num;
    +		tokenLineNumbers[nextBufPos] = lineno;
    +		tokenLineOffsets[nextBufPos] = lineOffset;
    +		
    +		nextBufPos++;
    +		if (nextBufPos == tokenMaxHistory) nextBufPos = 0;
    +		if (historyLength < tokenMaxHistory) historyLength++;
    +		tokenPeeking = true;
    +	    }
    +	}
    +
    +	void reportConsumeToken() {
    +	    tokenPeeking = false;
    +	}
    +
    +	private TokenInfo backToken(int n) {
    +	    // 0 is most recent token added to history
    +	    if (n >= historyLength) return null;
    +	    int i = (nextBufPos - 1 - n);
    +	    while (i < 0) i += tokenMaxHistory;
    +	    return new TokenInfo(tokenTypes[i], tokenStrings[i],
    +				 tokenNumbers[i], tokenLineNumbers[i],
    +				 tokenLineOffsets[i]);
    +	}
    +	
    +	public String getMessageId() { return messageId; }
    +	public String getMessageArg() { return messageArg; }
    +	public TokenInfo getPeekToken() {
    +	    if (tokenPeeking) return backToken(0);
    +	    return null;
    +	}
    +	public TokenInfo getPrevToken(int n) {
    +	    // 1 = last non-peek token seen, 2 = before that, etc.
    +	    if (! tokenPeeking) n--;
    +	    return backToken(n);
    +	}
    +	public TokenInfo getPrevToken() {
    +	    return getPrevToken(1);
    +	}
    +    }
    +
    +    public static class TokenInfo {
    +	private int type, lineno, lineOffset;
    +	private String str;
    +	private double num;
    +	TokenInfo(int type, String str, double num, int lineno,
    +		  int lineOffset) {
    +	    this.type = type; this.str = str; this.num = num;
    +	    this.lineno = lineno; this.lineOffset = lineOffset;
    +	}
    +	public int getType() { return type; }
    +	public int getLineNumber() { return lineno; }
    +	public int getLineOffset() { return lineOffset; }
    +	public double getNumber() { return num; }
    +	public String getString() { return str; }
    +    }
    +    
    +    ParseErrorInfo info = new ParseErrorInfo();
    +
    +    void doErrorReporterError(String message, String sourceURI, int line,
    +			      String lineText, int lineOffset) {
    +	
    +	throw new InformativeEvaluatorException(message, sourceURI, line,
    +						lineText, lineOffset, info);
    +	
    +    }
    +    
    +    public InformativeParser(CompilerEnvirons compilerEnv) {
    +	// we override most calls to the parent's ErrorReporter anyway
    +	super(compilerEnv, DefaultErrorReporter.instance);
    +    }
    +
    +    @Override int peekToken() throws IOException {
    +	int tt = super.peekToken();
    +	info.reportPeekToken(tt, ts.getString(), ts.getNumber(),
    +			     ts.getLineno(), ts.getOffset());
    +	return tt;
    +    }
    +    @Override void consumeToken() {
    +	super.consumeToken();
    +	info.reportConsumeToken();
    +    }
    +
    +    @Override void addWarning(String messageId, String messageArg)
    +    {
    +	info.messageId = messageId;
    +	info.messageArg = messageArg;
    +	
    +        String message = ScriptRuntime.getMessage1(messageId, messageArg);
    +        if (compilerEnv.reportWarningAsError()) {
    +            ++syntaxErrorCount;
    +            doErrorReporterError(message, sourceURI, ts.getLineno(),
    +				 ts.getLine(), ts.getOffset());
    +        }
    +	else { /* don't report */ }
    +    }
    +    
    +    @Override void addError(String messageId)
    +    {
    +	info.messageId = messageId;
    +	
    +	++syntaxErrorCount;
    +        String message = ScriptRuntime.getMessage0(messageId);
    +        doErrorReporterError(message, sourceURI, ts.getLineno(),
    +			     ts.getLine(), ts.getOffset());
    +    }
    +
    +    @Override void addError(String messageId, String messageArg)
    +    {
    +	info.messageId = messageId;
    +	info.messageArg = messageArg;
    +	
    +	++syntaxErrorCount;
    +        String message = ScriptRuntime.getMessage1(messageId, messageArg);
    +        doErrorReporterError(message, sourceURI, ts.getLineno(),
    +			     ts.getLine(), ts.getOffset());
    +    }
    +
    +    @Override protected Decompiler createDecompiler(CompilerEnvirons env) {
    +	return new MyDecompiler();
    +    }
    +    
    +    public static final ErrorReporter THROW_INFORMATIVE_ERRORS
    +	= new ErrorReporter() {
    +		public void warning(String message, String sourceURI, int line,
    +				    String lineText, int lineOffset) {
    +		    DefaultErrorReporter.instance.warning
    +			(message, sourceURI, line, lineText, lineOffset);
    +		}
    +		public void error(String message, String sourceURI, int line,
    +				  String lineText, int lineOffset) {
    +		    DefaultErrorReporter.instance.error
    +			(message, sourceURI, line, lineText, lineOffset);
    +		}
    +		public EvaluatorException runtimeError(String message,
    +						       String sourceURI,
    +						       int line, String lineText,
    +						       int lineOffset) {
    +		    return DefaultErrorReporter.instance.runtimeError
    +			(message, sourceURI, line, lineText, lineOffset);
    +		}
    +		
    +	    };
    +    
    +    public static Parser makeParser(CompilerEnvirons compilerEnv,
    +				    ErrorReporter errorReporter) {
    +	if (errorReporter == THROW_INFORMATIVE_ERRORS) {
    +	    return new InformativeParser(compilerEnv);
    +	}
    +	else {
    +	    return new Parser(compilerEnv, errorReporter);
    +	}
    +    }
    +
    +    private class MyDecompiler extends Decompiler {
    +	@Override void addRegexp(String regexp, String flags) {
    +	    super.addRegexp(regexp, flags);
    +	    String str = '/'+regexp+'/'+flags;
    +	    info.reportPeekToken(Token.REGEXP, str, ts.getNumber(),
    +				 ts.getLineno(), ts.getOffset());
    +	    info.reportConsumeToken();
    +	}
    +    }
    +}
    \ No newline at end of file
    diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/InterfaceAdapter.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/InterfaceAdapter.java
    new file mode 100644
    index 0000000..877e6a2
    --- /dev/null
    +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/InterfaceAdapter.java
    @@ -0,0 +1,156 @@
    +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
    + *
    + * ***** BEGIN LICENSE BLOCK *****
    + * Version: MPL 1.1/GPL 2.0
    + *
    + * The contents of this file are subject to the Mozilla Public License Version
    + * 1.1 (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.mozilla.org/MPL/
    + *
    + * Software distributed under the License is distributed on an "AS IS" basis,
    + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
    + * for the specific language governing rights and limitations under the
    + * License.
    + *
    + * The Original Code is Rhino code, released
    + * May 6, 1999.
    + *
    + * The Initial Developer of the Original Code is
    + * Netscape Communications Corporation.
    + * Portions created by the Initial Developer are Copyright (C) 1997-1999
    + * the Initial Developer. All Rights Reserved.
    + *
    + * Contributor(s):
    + *   Igor Bukanov
    + *
    + * Alternatively, the contents of this file may be used under the terms of
    + * the GNU General Public License Version 2 or later (the "GPL"), in which
    + * case the provisions of the GPL are applicable instead of those above. If
    + * you wish to allow use of your version of this file only under the terms of
    + * the GPL and not to allow others to use your version of this file under the
    + * MPL, indicate your decision by deleting the provisions above and replacing
    + * them with the notice and other provisions required by the GPL. If you do
    + * not delete the provisions above, a recipient may use your version of this
    + * file under either the MPL or the GPL.
    + *
    + * ***** END LICENSE BLOCK ***** */
    +
    +package org.mozilla.javascript;
    +
    +import java.lang.reflect.Method;
    +
    +/**
    + * Adapter to use JS function as implementation of Java interfaces with
    + * single method or multiple methods with the same signature.
    + */
    +public class InterfaceAdapter
    +{
    +    private final Object proxyHelper;
    +
    +    /**
    +     * Make glue object implementing interface cl that will
    +     * call the supplied JS function when called.
    +     * Only interfaces were all methods have the same signature is supported.
    +     *
    +     * @return The glue object or null if cl is not interface or
    +     *         has methods with different signatures.
    +     */
    +    static Object create(Context cx, Class cl, Callable function)
    +    {
    +        if (!cl.isInterface()) throw new IllegalArgumentException();
    +
    +        Scriptable topScope = ScriptRuntime.getTopCallScope(cx);
    +        ClassCache cache = ClassCache.get(topScope);
    +        InterfaceAdapter adapter;
    +        adapter = (InterfaceAdapter)cache.getInterfaceAdapter(cl);
    +        ContextFactory cf = cx.getFactory();
    +        if (adapter == null) {
    +            Method[] methods = cl.getMethods();
    +            if (methods.length == 0) {
    +                throw Context.reportRuntimeError2(
    +                    "msg.no.empty.interface.conversion",
    +                    String.valueOf(function),
    +                    cl.getClass().getName());
    +            }
    +            boolean canCallFunction = false;
    +          canCallFunctionChecks: {
    +                Class[] argTypes = methods[0].getParameterTypes();
    +                // check that the rest of methods has the same signature
    +                for (int i = 1; i != methods.length; ++i) {
    +                    Class[] types2 = methods[i].getParameterTypes();
    +                    if (types2.length != argTypes.length) {
    +                        break canCallFunctionChecks;
    +                    }
    +                    for (int j = 0; j != argTypes.length; ++j) {
    +                        if (types2[j] != argTypes[j]) {
    +                            break canCallFunctionChecks;
    +                        }
    +                    }
    +                }
    +                canCallFunction= true;
    +            }
    +            if (!canCallFunction) {
    +                throw Context.reportRuntimeError2(
    +                    "msg.no.function.interface.conversion",
    +                    String.valueOf(function),
    +                    cl.getClass().getName());
    +            }
    +            adapter = new InterfaceAdapter(cf, cl);
    +            cache.cacheInterfaceAdapter(cl, adapter);
    +        }
    +        return VMBridge.instance.newInterfaceProxy(
    +            adapter.proxyHelper, cf, adapter, function, topScope);
    +    }
    +
    +    private InterfaceAdapter(ContextFactory cf, Class cl)
    +    {
    +        this.proxyHelper
    +            = VMBridge.instance.getInterfaceProxyHelper(
    +                cf, new Class[] { cl });
    +    }
    +
    +    public Object invoke(ContextFactory cf,
    +                         final Object target,
    +                         final Scriptable topScope,
    +                         final Method method,
    +                         final Object[] args)
    +    {
    +        ContextAction action = new ContextAction() {
    +                public Object run(Context cx)
    +                {
    +                    return invokeImpl(cx, target, topScope, method, args);
    +                }
    +            };
    +        return cf.call(action);
    +    }
    +
    +    Object invokeImpl(Context cx,
    +                      Object target,
    +                      Scriptable topScope,
    +                      Method method,
    +                      Object[] args)
    +    {
    +        int N = (args == null) ? 0 : args.length;
    +
    +        Callable function = (Callable)target;
    +        Scriptable thisObj = topScope;
    +        Object[] jsargs = new Object[N + 1];
    +        jsargs[N] = method.getName();
    +        if (N != 0) {
    +            WrapFactory wf = cx.getWrapFactory();
    +            for (int i = 0; i != N; ++i) {
    +                jsargs[i] = wf.wrap(cx, topScope, args[i], null);
    +            }
    +        }
    +
    +        Object result = function.call(cx, topScope, thisObj, jsargs);
    +        Class javaResultType = method.getReturnType();
    +        if (javaResultType == Void.TYPE) {
    +            result = null;
    +        } else {
    +            result = Context.jsToJava(result, javaResultType);
    +        }
    +        return result;
    +    }
    +}
    diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/InterpretedFunction.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/InterpretedFunction.java
    new file mode 100644
    index 0000000..db84299
    --- /dev/null
    +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/InterpretedFunction.java
    @@ -0,0 +1,221 @@
    +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
    + *
    + * ***** BEGIN LICENSE BLOCK *****
    + * Version: MPL 1.1/GPL 2.0
    + *
    + * The contents of this file are subject to the Mozilla Public License Version
    + * 1.1 (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.mozilla.org/MPL/
    + *
    + * Software distributed under the License is distributed on an "AS IS" basis,
    + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
    + * for the specific language governing rights and limitations under the
    + * License.
    + *
    + * The Original Code is Rhino code, released
    + * May 6, 1999.
    + *
    + * The Initial Developer of the Original Code is
    + * Netscape Communications Corporation.
    + * Portions created by the Initial Developer are Copyright (C) 1997-2000
    + * the Initial Developer. All Rights Reserved.
    + *
    + * Contributor(s):
    + *   Igor Bukanov
    + *   Bob Jervis
    + *   Roger Lawrence
    + *
    + * Alternatively, the contents of this file may be used under the terms of
    + * the GNU General Public License Version 2 or later (the "GPL"), in which
    + * case the provisions of the GPL are applicable instead of those above. If
    + * you wish to allow use of your version of this file only under the terms of
    + * the GPL and not to allow others to use your version of this file under the
    + * MPL, indicate your decision by deleting the provisions above and replacing
    + * them with the notice and other provisions required by the GPL. If you do
    + * not delete the provisions above, a recipient may use your version of this
    + * file under either the MPL or the GPL.
    + *
    + * ***** END LICENSE BLOCK ***** */
    +
    +package org.mozilla.javascript;
    +
    +import org.mozilla.javascript.debug.DebuggableScript;
    +
    +final class InterpretedFunction extends NativeFunction implements Script
    +{
    +    static final long serialVersionUID = 541475680333911468L;
    +
    +    InterpreterData idata;
    +    SecurityController securityController;
    +    Object securityDomain;
    +    Scriptable[] functionRegExps;
    +
    +    private InterpretedFunction(InterpreterData idata,
    +                                Object staticSecurityDomain)
    +    {
    +        this.idata = idata;
    +
    +        // Always get Context from the current thread to
    +        // avoid security breaches via passing mangled Context instances
    +        // with bogus SecurityController
    +        Context cx = Context.getContext();
    +        SecurityController sc = cx.getSecurityController();
    +        Object dynamicDomain;
    +        if (sc != null) {
    +            dynamicDomain = sc.getDynamicSecurityDomain(staticSecurityDomain);
    +        } else {
    +            if (staticSecurityDomain != null) {
    +                throw new IllegalArgumentException();
    +            }
    +            dynamicDomain = null;
    +        }
    +
    +        this.securityController = sc;
    +        this.securityDomain = dynamicDomain;
    +    }
    +
    +    private InterpretedFunction(InterpretedFunction parent, int index)
    +    {
    +        this.idata = parent.idata.itsNestedFunctions[index];
    +        this.securityController = parent.securityController;
    +        this.securityDomain = parent.securityDomain;
    +    }
    +
    +    /**
    +     * Create script from compiled bytecode.
    +     */
    +    static InterpretedFunction createScript(InterpreterData idata,
    +                                            Object staticSecurityDomain)
    +    {
    +        InterpretedFunction f;
    +        f = new InterpretedFunction(idata, staticSecurityDomain);
    +        return f;
    +    }
    +
    +    /**
    +     * Create function compiled from Function(...) constructor.
    +     */
    +    static InterpretedFunction createFunction(Context cx,Scriptable scope,
    +                                              InterpreterData idata,
    +                                              Object staticSecurityDomain)
    +    {
    +        InterpretedFunction f;
    +        f = new InterpretedFunction(idata, staticSecurityDomain);
    +        f.initInterpretedFunction(cx, scope);
    +        return f;
    +    }
    +
    +    /**
    +     * Create function embedded in script or another function.
    +     */
    +    static InterpretedFunction createFunction(Context cx, Scriptable scope,
    +                                              InterpretedFunction  parent,
    +                                              int index)
    +    {
    +        InterpretedFunction f = new InterpretedFunction(parent, index);
    +        f.initInterpretedFunction(cx, scope);
    +        return f;
    +    }
    +
    +    Scriptable[] createRegExpWraps(Context cx, Scriptable scope)
    +    {
    +        if (idata.itsRegExpLiterals == null) Kit.codeBug();
    +
    +        RegExpProxy rep = ScriptRuntime.checkRegExpProxy(cx);
    +        int N = idata.itsRegExpLiterals.length;
    +        Scriptable[] array = new Scriptable[N];
    +        for (int i = 0; i != N; ++i) {
    +            array[i] = rep.wrapRegExp(cx, scope, idata.itsRegExpLiterals[i]);
    +        }
    +        return array;
    +    }
    +
    +    private void initInterpretedFunction(Context cx, Scriptable scope)
    +    {
    +        initScriptFunction(cx, scope);
    +        if (idata.itsRegExpLiterals != null) {
    +            functionRegExps = createRegExpWraps(cx, scope);
    +        }
    +    }
    +
    +    public String getFunctionName()
    +    {
    +        return (idata.itsName == null) ? "" : idata.itsName;
    +    }
    +
    +    /**
    +     * Calls the function.
    +     * @param cx the current context 
    +     * @param scope the scope used for the call
    +     * @param thisObj the value of "this"
    +     * @param args function arguments. Must not be null. You can use 
    +     * {@link ScriptRuntime#emptyArgs} to pass empty arguments.
    +     * @return the result of the function call.
    +     */
    +    public Object call(Context cx, Scriptable scope, Scriptable thisObj,
    +                       Object[] args)
    +    {
    +        if (!ScriptRuntime.hasTopCall(cx)) {
    +            return ScriptRuntime.doTopCall(this, cx, scope, thisObj, args);
    +        }
    +        return Interpreter.interpret(this, cx, scope, thisObj, args);
    +    }
    +
    +    public Object exec(Context cx, Scriptable scope)
    +    {
    +        if (idata.itsFunctionType != 0) {
    +            // Can only be applied to scripts
    +            throw new IllegalStateException();
    +        }
    +        if (!ScriptRuntime.hasTopCall(cx)) {
    +            // It will go through "call" path. but they are equivalent
    +            return ScriptRuntime.doTopCall(
    +                this, cx, scope, scope, ScriptRuntime.emptyArgs);
    +        }
    +        return Interpreter.interpret(
    +            this, cx, scope, scope, ScriptRuntime.emptyArgs);
    +    }
    +
    +    public String getEncodedSource()
    +    {
    +        return Interpreter.getEncodedSource(idata);
    +    }
    +
    +    public DebuggableScript getDebuggableView()
    +    {
    +        return idata;
    +    }
    +
    +    public Object resumeGenerator(Context cx, Scriptable scope, int operation,
    +                                  Object state, Object value)
    +    {
    +        return Interpreter.resumeGenerator(cx, scope, operation, state, value);
    +    }
    +
    +    protected int getLanguageVersion()
    +    {
    +        return idata.languageVersion;
    +    }
    +
    +    protected int getParamCount()
    +    {
    +        return idata.argCount;
    +    }
    +
    +    protected int getParamAndVarCount()
    +    {
    +        return idata.argNames.length;
    +    }
    +
    +    protected String getParamOrVarName(int index)
    +    {
    +        return idata.argNames[index];
    +    }
    +
    +    protected boolean getParamOrVarConst(int index)
    +    {
    +        return idata.argIsConst[index];
    +    }
    +}
    +
    diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Interpreter.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Interpreter.java
    new file mode 100644
    index 0000000..a68c025
    --- /dev/null
    +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Interpreter.java
    @@ -0,0 +1,4643 @@
    +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
    + *
    + * ***** BEGIN LICENSE BLOCK *****
    + * Version: MPL 1.1/GPL 2.0
    + *
    + * The contents of this file are subject to the Mozilla Public License Version
    + * 1.1 (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.mozilla.org/MPL/
    + *
    + * Software distributed under the License is distributed on an "AS IS" basis,
    + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
    + * for the specific language governing rights and limitations under the
    + * License.
    + *
    + * The Original Code is Rhino code, released
    + * May 6, 1999.
    + *
    + * The Initial Developer of the Original Code is
    + * Netscape Communications Corporation.
    + * Portions created by the Initial Developer are Copyright (C) 1997-2000
    + * the Initial Developer. All Rights Reserved.
    + *
    + * Contributor(s):
    + *   Patrick Beard
    + *   Norris Boyd
    + *   Igor Bukanov
    + *   Ethan Hugg
    + *   Bob Jervis
    + *   Terry Lucas
    + *   Roger Lawrence
    + *   Milen Nankov
    + *   Hannes Wallnoefer
    + *
    + * Alternatively, the contents of this file may be used under the terms of
    + * the GNU General Public License Version 2 or later (the "GPL"), in which
    + * case the provisions of the GPL are applicable instead of those above. If
    + * you wish to allow use of your version of this file only under the terms of
    + * the GPL and not to allow others to use your version of this file under the
    + * MPL, indicate your decision by deleting the provisions above and replacing
    + * them with the notice and other provisions required by the GPL. If you do
    + * not delete the provisions above, a recipient may use your version of this
    + * file under either the MPL or the GPL.
    + *
    + * ***** END LICENSE BLOCK ***** */
    +
    +package org.mozilla.javascript;
    +
    +import java.io.PrintStream;
    +import java.io.Serializable;
    +import java.util.List;
    +import java.util.ArrayList;
    +
    +import org.mozilla.javascript.continuations.Continuation;
    +import org.mozilla.javascript.debug.DebugFrame;
    +
    +public class Interpreter implements Evaluator
    +{
    +
    +// Additional interpreter-specific codes
    +
    +    private static final int
    +
    +    // Stack: ... value1 -> ... value1 value1
    +        Icode_DUP                       = -1,
    +
    +    // Stack: ... value2 value1 -> ... value2 value1 value2 value1
    +        Icode_DUP2                      = -2,
    +
    +    // Stack: ... value2 value1 -> ... value1 value2
    +        Icode_SWAP                      = -3,
    +
    +    // Stack: ... value1 -> ...
    +        Icode_POP                       = -4,
    +
    +    // Store stack top into return register and then pop it
    +        Icode_POP_RESULT                = -5,
    +
    +    // To jump conditionally and pop additional stack value
    +        Icode_IFEQ_POP                  = -6,
    +
    +    // various types of ++/--
    +        Icode_VAR_INC_DEC               = -7,
    +        Icode_NAME_INC_DEC              = -8,
    +        Icode_PROP_INC_DEC              = -9,
    +        Icode_ELEM_INC_DEC              = -10,
    +        Icode_REF_INC_DEC               = -11,
    +
    +    // load/save scope from/to local
    +        Icode_SCOPE_LOAD                = -12,
    +        Icode_SCOPE_SAVE                = -13,
    +
    +        Icode_TYPEOFNAME                = -14,
    +
    +    // helper for function calls
    +        Icode_NAME_AND_THIS             = -15,
    +        Icode_PROP_AND_THIS             = -16,
    +        Icode_ELEM_AND_THIS             = -17,
    +        Icode_VALUE_AND_THIS            = -18,
    +
    +    // Create closure object for nested functions
    +        Icode_CLOSURE_EXPR              = -19,
    +        Icode_CLOSURE_STMT              = -20,
    +
    +    // Special calls
    +        Icode_CALLSPECIAL               = -21,
    +
    +    // To return undefined value
    +        Icode_RETUNDEF                  = -22,
    +
    +    // Exception handling implementation
    +        Icode_GOSUB                     = -23,
    +        Icode_STARTSUB                  = -24,
    +        Icode_RETSUB                    = -25,
    +
    +    // To indicating a line number change in icodes.
    +        Icode_LINE                      = -26,
    +
    +    // To store shorts and ints inline
    +        Icode_SHORTNUMBER               = -27,
    +        Icode_INTNUMBER                 = -28,
    +
    +    // To create and populate array to hold values for [] and {} literals
    +        Icode_LITERAL_NEW               = -29,
    +        Icode_LITERAL_SET               = -30,
    +
    +    // Array literal with skipped index like [1,,2]
    +        Icode_SPARE_ARRAYLIT            = -31,
    +
    +    // Load index register to prepare for the following index operation
    +        Icode_REG_IND_C0                = -32,
    +        Icode_REG_IND_C1                = -33,
    +        Icode_REG_IND_C2                = -34,
    +        Icode_REG_IND_C3                = -35,
    +        Icode_REG_IND_C4                = -36,
    +        Icode_REG_IND_C5                = -37,
    +        Icode_REG_IND1                  = -38,
    +        Icode_REG_IND2                  = -39,
    +        Icode_REG_IND4                  = -40,
    +
    +    // Load string register to prepare for the following string operation
    +        Icode_REG_STR_C0                = -41,
    +        Icode_REG_STR_C1                = -42,
    +        Icode_REG_STR_C2                = -43,
    +        Icode_REG_STR_C3                = -44,
    +        Icode_REG_STR1                  = -45,
    +        Icode_REG_STR2                  = -46,
    +        Icode_REG_STR4                  = -47,
    +
    +    // Version of getvar/setvar that read var index directly from bytecode
    +        Icode_GETVAR1                   = -48,
    +        Icode_SETVAR1                   = -49,
    +
    +    // Load unefined
    +        Icode_UNDEF                     = -50,
    +        Icode_ZERO                      = -51,
    +        Icode_ONE                       = -52,
    +
    +    // entrance and exit from .()
    +       Icode_ENTERDQ                    = -53,
    +       Icode_LEAVEDQ                    = -54,
    +
    +       Icode_TAIL_CALL                  = -55,
    +
    +    // Clear local to allow GC its context
    +       Icode_LOCAL_CLEAR                = -56,
    +
    +    // Literal get/set
    +       Icode_LITERAL_GETTER             = -57,
    +       Icode_LITERAL_SETTER             = -58,
    +
    +    // const
    +       Icode_SETCONST                   = -59,
    +       Icode_SETCONSTVAR                = -60,
    +       Icode_SETCONSTVAR1               = -61,
    +
    +    // Generator opcodes (along with Token.YIELD)
    +       Icode_GENERATOR                  = -62,
    +       Icode_GENERATOR_END              = -63,
    +
    +       Icode_DEBUGGER                   = -64,
    +
    +       // Last icode
    +        MIN_ICODE                       = -64;
    +
    +    // data for parsing
    +
    +    private CompilerEnvirons compilerEnv;
    +
    +    private boolean itsInFunctionFlag;
    +    private boolean itsInTryFlag;
    +
    +    private InterpreterData itsData;
    +    private ScriptOrFnNode scriptOrFn;
    +    private int itsICodeTop;
    +    private int itsStackDepth;
    +    private int itsLineNumber;
    +    private int itsDoubleTableTop;
    +    private ObjToIntMap itsStrings = new ObjToIntMap(20);
    +    private int itsLocalTop;
    +
    +    private static final int MIN_LABEL_TABLE_SIZE = 32;
    +    private static final int MIN_FIXUP_TABLE_SIZE = 40;
    +    private int[] itsLabelTable;
    +    private int itsLabelTableTop;
    +// itsFixupTable[i] = (label_index << 32) | fixup_site
    +    private long[] itsFixupTable;
    +    private int itsFixupTableTop;
    +    private ObjArray itsLiteralIds = new ObjArray();
    +
    +    private int itsExceptionTableTop;
    +    private static final int EXCEPTION_TRY_START_SLOT  = 0;
    +    private static final int EXCEPTION_TRY_END_SLOT    = 1;
    +    private static final int EXCEPTION_HANDLER_SLOT    = 2;
    +    private static final int EXCEPTION_TYPE_SLOT       = 3;
    +    private static final int EXCEPTION_LOCAL_SLOT      = 4;
    +    private static final int EXCEPTION_SCOPE_SLOT      = 5;
    +    // SLOT_SIZE: space for try start/end, handler, start, handler type,
    +    //            exception local and scope local
    +    private static final int EXCEPTION_SLOT_SIZE       = 6;
    +
    +// ECF_ or Expression Context Flags constants: for now only TAIL is available
    +    private static final int ECF_TAIL = 1 << 0;
    +
    +    /**
    +     * Class to hold data corresponding to one interpreted call stack frame.
    +     */
    +    private static class CallFrame implements Cloneable, Serializable
    +    {
    +        static final long serialVersionUID = -2843792508994958978L;
    +
    +        CallFrame parentFrame;
    +        // amount of stack frames before this one on the interpretation stack
    +        int frameIndex;
    +        // If true indicates read-only frame that is a part of continuation
    +        boolean frozen;
    +
    +        InterpretedFunction fnOrScript;
    +        InterpreterData idata;
    +
    +// Stack structure
    +// stack[0 <= i < localShift]: arguments and local variables
    +// stack[localShift <= i <= emptyStackTop]: used for local temporaries
    +// stack[emptyStackTop < i < stack.length]: stack data
    +// sDbl[i]: if stack[i] is UniqueTag.DOUBLE_MARK, sDbl[i] holds the number value
    +
    +        Object[] stack;
    +        int[] stackAttributes;
    +        double[] sDbl;
    +        CallFrame varSource; // defaults to this unless continuation frame
    +        int localShift;
    +        int emptyStackTop;
    +
    +        DebugFrame debuggerFrame;
    +        boolean useActivation;
    +
    +        Scriptable thisObj;
    +        Scriptable[] scriptRegExps;
    +
    +// The values that change during interpretation
    +
    +        Object result;
    +        double resultDbl;
    +        int pc;
    +        int pcPrevBranch;
    +        int pcSourceLineStart;
    +        Scriptable scope;
    +
    +        int savedStackTop;
    +        int savedCallOp;
    +        Object throwable;
    +
    +        CallFrame cloneFrozen()
    +        {
    +            if (!frozen) Kit.codeBug();
    +
    +            CallFrame copy;
    +            try {
    +                copy = (CallFrame)clone();
    +            } catch (CloneNotSupportedException ex) {
    +                throw new IllegalStateException();
    +            }
    +
    +            // clone stack but keep varSource to point to values
    +            // from this frame to share variables.
    +
    +            copy.stack = stack.clone();
    +            copy.stackAttributes = stackAttributes.clone();
    +            copy.sDbl = sDbl.clone();
    +
    +            copy.frozen = false;
    +            return copy;
    +        }
    +    }
    +
    +    private static final class ContinuationJump implements Serializable
    +    {
    +        static final long serialVersionUID = 7687739156004308247L;
    +
    +        CallFrame capturedFrame;
    +        CallFrame branchFrame;
    +        Object result;
    +        double resultDbl;
    +
    +        ContinuationJump(Continuation c, CallFrame current)
    +        {
    +            this.capturedFrame = (CallFrame)c.getImplementation();
    +            if (this.capturedFrame == null || current == null) {
    +                // Continuation and current execution does not share
    +                // any frames if there is nothing to capture or
    +                // if there is no currently executed frames
    +                this.branchFrame = null;
    +            } else {
    +                // Search for branch frame where parent frame chains starting
    +                // from captured and current meet.
    +                CallFrame chain1 = this.capturedFrame;
    +                CallFrame chain2 = current;
    +
    +                // First work parents of chain1 or chain2 until the same
    +                // frame depth.
    +                int diff = chain1.frameIndex - chain2.frameIndex;
    +                if (diff != 0) {
    +                    if (diff < 0) {
    +                        // swap to make sure that
    +                        // chain1.frameIndex > chain2.frameIndex and diff > 0
    +                        chain1 = current;
    +                        chain2 = this.capturedFrame;
    +                        diff = -diff;
    +                    }
    +                    do {
    +                        chain1 = chain1.parentFrame;
    +                    } while (--diff != 0);
    +                    if (chain1.frameIndex != chain2.frameIndex) Kit.codeBug();
    +                }
    +
    +                // Now walk parents in parallel until a shared frame is found
    +                // or until the root is reached.
    +                while (chain1 != chain2 && chain1 != null) {
    +                    chain1 = chain1.parentFrame;
    +                    chain2 = chain2.parentFrame;
    +                }
    +
    +                this.branchFrame = chain1;
    +                if (this.branchFrame != null && !this.branchFrame.frozen)
    +                    Kit.codeBug();
    +            }
    +        }
    +    }
    +
    +    private static CallFrame captureFrameForGenerator(CallFrame frame) {
    +      frame.frozen = true;
    +      CallFrame result = frame.cloneFrozen();
    +      frame.frozen = false;
    +
    +      // now isolate this frame from its previous context
    +      result.parentFrame = null;
    +      result.frameIndex = 0;
    +
    +      return result;
    +    }
    +
    +    static {
    +        // Checks for byte code consistencies, good compiler can eliminate them
    +
    +        if (Token.LAST_BYTECODE_TOKEN > 127) {
    +            String str = "Violation of Token.LAST_BYTECODE_TOKEN <= 127";
    +            System.err.println(str);
    +            throw new IllegalStateException(str);
    +        }
    +        if (MIN_ICODE < -128) {
    +            String str = "Violation of Interpreter.MIN_ICODE >= -128";
    +            System.err.println(str);
    +            throw new IllegalStateException(str);
    +        }
    +    }
    +
    +    private static String bytecodeName(int bytecode)
    +    {
    +        if (!validBytecode(bytecode)) {
    +            throw new IllegalArgumentException(String.valueOf(bytecode));
    +        }
    +
    +        if (!Token.printICode) {
    +            return String.valueOf(bytecode);
    +        }
    +
    +        if (validTokenCode(bytecode)) {
    +            return Token.name(bytecode);
    +        }
    +
    +        switch (bytecode) {
    +          case Icode_DUP:              return "DUP";
    +          case Icode_DUP2:             return "DUP2";
    +          case Icode_SWAP:             return "SWAP";
    +          case Icode_POP:              return "POP";
    +          case Icode_POP_RESULT:       return "POP_RESULT";
    +          case Icode_IFEQ_POP:         return "IFEQ_POP";
    +          case Icode_VAR_INC_DEC:      return "VAR_INC_DEC";
    +          case Icode_NAME_INC_DEC:     return "NAME_INC_DEC";
    +          case Icode_PROP_INC_DEC:     return "PROP_INC_DEC";
    +          case Icode_ELEM_INC_DEC:     return "ELEM_INC_DEC";
    +          case Icode_REF_INC_DEC:      return "REF_INC_DEC";
    +          case Icode_SCOPE_LOAD:       return "SCOPE_LOAD";
    +          case Icode_SCOPE_SAVE:       return "SCOPE_SAVE";
    +          case Icode_TYPEOFNAME:       return "TYPEOFNAME";
    +          case Icode_NAME_AND_THIS:    return "NAME_AND_THIS";
    +          case Icode_PROP_AND_THIS:    return "PROP_AND_THIS";
    +          case Icode_ELEM_AND_THIS:    return "ELEM_AND_THIS";
    +          case Icode_VALUE_AND_THIS:   return "VALUE_AND_THIS";
    +          case Icode_CLOSURE_EXPR:     return "CLOSURE_EXPR";
    +          case Icode_CLOSURE_STMT:     return "CLOSURE_STMT";
    +          case Icode_CALLSPECIAL:      return "CALLSPECIAL";
    +          case Icode_RETUNDEF:         return "RETUNDEF";
    +          case Icode_GOSUB:            return "GOSUB";
    +          case Icode_STARTSUB:         return "STARTSUB";
    +          case Icode_RETSUB:           return "RETSUB";
    +          case Icode_LINE:             return "LINE";
    +          case Icode_SHORTNUMBER:      return "SHORTNUMBER";
    +          case Icode_INTNUMBER:        return "INTNUMBER";
    +          case Icode_LITERAL_NEW:      return "LITERAL_NEW";
    +          case Icode_LITERAL_SET:      return "LITERAL_SET";
    +          case Icode_SPARE_ARRAYLIT:   return "SPARE_ARRAYLIT";
    +          case Icode_REG_IND_C0:       return "REG_IND_C0";
    +          case Icode_REG_IND_C1:       return "REG_IND_C1";
    +          case Icode_REG_IND_C2:       return "REG_IND_C2";
    +          case Icode_REG_IND_C3:       return "REG_IND_C3";
    +          case Icode_REG_IND_C4:       return "REG_IND_C4";
    +          case Icode_REG_IND_C5:       return "REG_IND_C5";
    +          case Icode_REG_IND1:         return "LOAD_IND1";
    +          case Icode_REG_IND2:         return "LOAD_IND2";
    +          case Icode_REG_IND4:         return "LOAD_IND4";
    +          case Icode_REG_STR_C0:       return "REG_STR_C0";
    +          case Icode_REG_STR_C1:       return "REG_STR_C1";
    +          case Icode_REG_STR_C2:       return "REG_STR_C2";
    +          case Icode_REG_STR_C3:       return "REG_STR_C3";
    +          case Icode_REG_STR1:         return "LOAD_STR1";
    +          case Icode_REG_STR2:         return "LOAD_STR2";
    +          case Icode_REG_STR4:         return "LOAD_STR4";
    +          case Icode_GETVAR1:          return "GETVAR1";
    +          case Icode_SETVAR1:          return "SETVAR1";
    +          case Icode_UNDEF:            return "UNDEF";
    +          case Icode_ZERO:             return "ZERO";
    +          case Icode_ONE:              return "ONE";
    +          case Icode_ENTERDQ:          return "ENTERDQ";
    +          case Icode_LEAVEDQ:          return "LEAVEDQ";
    +          case Icode_TAIL_CALL:        return "TAIL_CALL";
    +          case Icode_LOCAL_CLEAR:      return "LOCAL_CLEAR";
    +          case Icode_LITERAL_GETTER:   return "LITERAL_GETTER";
    +          case Icode_LITERAL_SETTER:   return "LITERAL_SETTER";
    +          case Icode_SETCONST:         return "SETCONST";
    +          case Icode_SETCONSTVAR:      return "SETCONSTVAR";
    +          case Icode_SETCONSTVAR1:     return "SETCONSTVAR1";
    +          case Icode_GENERATOR:        return "GENERATOR";
    +          case Icode_GENERATOR_END:    return "GENERATOR_END";
    +          case Icode_DEBUGGER:         return "DEBUGGER";
    +        }
    +
    +        // icode without name
    +        throw new IllegalStateException(String.valueOf(bytecode));
    +    }
    +
    +    private static boolean validIcode(int icode)
    +    {
    +        return MIN_ICODE <= icode && icode <= -1;
    +    }
    +
    +    private static boolean validTokenCode(int token)
    +    {
    +        return Token.FIRST_BYTECODE_TOKEN <= token
    +               && token <= Token.LAST_BYTECODE_TOKEN;
    +    }
    +
    +    private static boolean validBytecode(int bytecode)
    +    {
    +        return validIcode(bytecode) || validTokenCode(bytecode);
    +    }
    +
    +    public Object compile(CompilerEnvirons compilerEnv,
    +                          ScriptOrFnNode tree,
    +                          String encodedSource,
    +                          boolean returnFunction)
    +    {
    +        this.compilerEnv = compilerEnv;
    +        new NodeTransformer().transform(tree);
    +
    +        if (Token.printTrees) {
    +            /*APPJET*///System.out.println(tree.toStringTree(tree));
    +        }
    +
    +        if (returnFunction) {
    +            tree = tree.getFunctionNode(0);
    +        }
    +
    +        scriptOrFn = tree;
    +        itsData = new InterpreterData(compilerEnv.getLanguageVersion(),
    +                                      scriptOrFn.getSourceName(),
    +                                      encodedSource);
    +        itsData.topLevel = true;
    +
    +        if (returnFunction) {
    +            generateFunctionICode();
    +        } else {
    +            generateICodeFromTree(scriptOrFn);
    +        }
    +
    +        return itsData;
    +    }
    +
    +    public Script createScriptObject(Object bytecode, Object staticSecurityDomain)
    +    {
    +        if(bytecode != itsData)
    +        {
    +            Kit.codeBug();
    +        }
    +        return InterpretedFunction.createScript(itsData,
    +                                                staticSecurityDomain);
    +    }
    +
    +    public void setEvalScriptFlag(Script script) {
    +        ((InterpretedFunction)script).idata.evalScriptFlag = true;
    +    }
    +    
    +
    +    public Function createFunctionObject(Context cx, Scriptable scope,
    +            Object bytecode, Object staticSecurityDomain)
    +    {
    +        if(bytecode != itsData)
    +        {
    +            Kit.codeBug();
    +        }
    +        return InterpretedFunction.createFunction(cx, scope, itsData,
    +                                                  staticSecurityDomain);
    +    }
    +
    +    private void generateFunctionICode()
    +    {
    +        itsInFunctionFlag = true;
    +
    +        FunctionNode theFunction = (FunctionNode)scriptOrFn;
    +
    +        itsData.itsFunctionType = theFunction.getFunctionType();
    +        itsData.itsNeedsActivation = theFunction.requiresActivation();
    +        itsData.itsName = theFunction.getFunctionName();
    +        if (!theFunction.getIgnoreDynamicScope()) {
    +            if (compilerEnv.isUseDynamicScope()) {
    +                itsData.useDynamicScope = true;
    +            }
    +        }
    +        if (theFunction.isGenerator()) {
    +          addIcode(Icode_GENERATOR);
    +          addUint16(theFunction.getBaseLineno() & 0xFFFF);
    +        }
    +
    +        generateICodeFromTree(theFunction.getLastChild());
    +    }
    +
    +    private void generateICodeFromTree(Node tree)
    +    {
    +        generateNestedFunctions();
    +
    +        generateRegExpLiterals();
    +
    +        visitStatement(tree, 0);
    +        fixLabelGotos();
    +        // add RETURN_RESULT only to scripts as function always ends with RETURN
    +        if (itsData.itsFunctionType == 0) {
    +            addToken(Token.RETURN_RESULT);
    +        }
    +
    +        if (itsData.itsICode.length != itsICodeTop) {
    +            // Make itsData.itsICode length exactly itsICodeTop to save memory
    +            // and catch bugs with jumps beyond icode as early as possible
    +            byte[] tmp = new byte[itsICodeTop];
    +            System.arraycopy(itsData.itsICode, 0, tmp, 0, itsICodeTop);
    +            itsData.itsICode = tmp;
    +        }
    +        if (itsStrings.size() == 0) {
    +            itsData.itsStringTable = null;
    +        } else {
    +            itsData.itsStringTable = new String[itsStrings.size()];
    +            ObjToIntMap.Iterator iter = itsStrings.newIterator();
    +            for (iter.start(); !iter.done(); iter.next()) {
    +                String str = (String)iter.getKey();
    +                int index = iter.getValue();
    +                if (itsData.itsStringTable[index] != null) Kit.codeBug();
    +                itsData.itsStringTable[index] = str;
    +            }
    +        }
    +        if (itsDoubleTableTop == 0) {
    +            itsData.itsDoubleTable = null;
    +        } else if (itsData.itsDoubleTable.length != itsDoubleTableTop) {
    +            double[] tmp = new double[itsDoubleTableTop];
    +            System.arraycopy(itsData.itsDoubleTable, 0, tmp, 0,
    +                             itsDoubleTableTop);
    +            itsData.itsDoubleTable = tmp;
    +        }
    +        if (itsExceptionTableTop != 0
    +            && itsData.itsExceptionTable.length != itsExceptionTableTop)
    +        {
    +            int[] tmp = new int[itsExceptionTableTop];
    +            System.arraycopy(itsData.itsExceptionTable, 0, tmp, 0,
    +                             itsExceptionTableTop);
    +            itsData.itsExceptionTable = tmp;
    +        }
    +
    +        itsData.itsMaxVars = scriptOrFn.getParamAndVarCount();
    +        // itsMaxFrameArray: interpret method needs this amount for its
    +        // stack and sDbl arrays
    +        itsData.itsMaxFrameArray = itsData.itsMaxVars
    +                                   + itsData.itsMaxLocals
    +                                   + itsData.itsMaxStack;
    +
    +        itsData.argNames = scriptOrFn.getParamAndVarNames();
    +        itsData.argIsConst = scriptOrFn.getParamAndVarConst();
    +        itsData.argCount = scriptOrFn.getParamCount();
    +
    +        itsData.encodedSourceStart = scriptOrFn.getEncodedSourceStart();
    +        itsData.encodedSourceEnd = scriptOrFn.getEncodedSourceEnd();
    +
    +        if (itsLiteralIds.size() != 0) {
    +            itsData.literalIds = itsLiteralIds.toArray();
    +        }
    +
    +        if (Token.printICode) dumpICode(itsData);
    +    }
    +
    +    private void generateNestedFunctions()
    +    {
    +        int functionCount = scriptOrFn.getFunctionCount();
    +        if (functionCount == 0) return;
    +
    +        InterpreterData[] array = new InterpreterData[functionCount];
    +        for (int i = 0; i != functionCount; i++) {
    +            FunctionNode def = scriptOrFn.getFunctionNode(i);
    +            Interpreter jsi = new Interpreter();
    +            jsi.compilerEnv = compilerEnv;
    +            jsi.scriptOrFn = def;
    +            jsi.itsData = new InterpreterData(itsData);
    +            jsi.generateFunctionICode();
    +            array[i] = jsi.itsData;
    +        }
    +        itsData.itsNestedFunctions = array;
    +    }
    +
    +    private void generateRegExpLiterals()
    +    {
    +        int N = scriptOrFn.getRegexpCount();
    +        if (N == 0) return;
    +
    +        Context cx = Context.getContext();
    +        RegExpProxy rep = ScriptRuntime.checkRegExpProxy(cx);
    +        Object[] array = new Object[N];
    +        for (int i = 0; i != N; i++) {
    +            String string = scriptOrFn.getRegexpString(i);
    +            String flags = scriptOrFn.getRegexpFlags(i);
    +            array[i] = rep.compileRegExp(cx, string, flags);
    +        }
    +        itsData.itsRegExpLiterals = array;
    +    }
    +
    +    private void updateLineNumber(Node node)
    +    {
    +        int lineno = node.getLineno();
    +        if (lineno != itsLineNumber && lineno >= 0) {
    +            if (itsData.firstLinePC < 0) {
    +                itsData.firstLinePC = lineno;
    +            }
    +            itsLineNumber = lineno;
    +            addIcode(Icode_LINE);
    +            addUint16(lineno & 0xFFFF);
    +        }
    +    }
    +
    +    private RuntimeException badTree(Node node)
    +    {
    +        throw new RuntimeException(node.toString());
    +    }
    +
    +    private void visitStatement(Node node, int initialStackDepth)
    +    {
    +        int type = node.getType();
    +        Node child = node.getFirstChild();
    +        switch (type) {
    +
    +          case Token.FUNCTION:
    +            {
    +                int fnIndex = node.getExistingIntProp(Node.FUNCTION_PROP);
    +                int fnType = scriptOrFn.getFunctionNode(fnIndex).
    +                                 getFunctionType();
    +                // Only function expressions or function expression
    +                // statements need closure code creating new function
    +                // object on stack as function statements are initialized
    +                // at script/function start.
    +                // In addition, function expressions can not be present here
    +                // at statement level, they must only be present as expressions.
    +                if (fnType == FunctionNode.FUNCTION_EXPRESSION_STATEMENT) {
    +                    addIndexOp(Icode_CLOSURE_STMT, fnIndex);
    +                } else {
    +                    if (fnType != FunctionNode.FUNCTION_STATEMENT) {
    +                        throw Kit.codeBug();
    +                    }
    +                }
    +                // For function statements or function expression statements
    +                // in scripts, we need to ensure that the result of the script
    +                // is the function if it is the last statement in the script.
    +                // For example, eval("function () {}") should return a
    +                // function, not undefined.
    +                if (!itsInFunctionFlag) {
    +                    addIndexOp(Icode_CLOSURE_EXPR, fnIndex);
    +                    stackChange(1);
    +                    addIcode(Icode_POP_RESULT);
    +                    stackChange(-1);
    +                }
    +            }
    +            break;
    +
    +          case Token.LABEL:
    +          case Token.LOOP:
    +          case Token.BLOCK:
    +          case Token.EMPTY:
    +          case Token.WITH:
    +            updateLineNumber(node);
    +          case Token.SCRIPT:
    +            // fall through
    +            while (child != null) {
    +                visitStatement(child, initialStackDepth);
    +                child = child.getNext();
    +            }
    +            break;
    +
    +          case Token.ENTERWITH:
    +            visitExpression(child, 0);
    +            addToken(Token.ENTERWITH);
    +            stackChange(-1);
    +            break;
    +
    +          case Token.LEAVEWITH:
    +            addToken(Token.LEAVEWITH);
    +            break;
    +
    +          case Token.LOCAL_BLOCK:
    +            {
    +                int local = allocLocal();
    +                node.putIntProp(Node.LOCAL_PROP, local);
    +                updateLineNumber(node);
    +                while (child != null) {
    +                    visitStatement(child, initialStackDepth);
    +                    child = child.getNext();
    +                }
    +                addIndexOp(Icode_LOCAL_CLEAR, local);
    +                releaseLocal(local);
    +            }
    +            break;
    +
    +          case Token.DEBUGGER:
    +            addIcode(Icode_DEBUGGER);
    +            break;
    +
    +          case Token.SWITCH:
    +            updateLineNumber(node);
    +            // See comments in IRFactory.createSwitch() for description
    +            // of SWITCH node
    +            {
    +                visitExpression(child, 0);
    +                for (Node.Jump caseNode = (Node.Jump)child.getNext();
    +                     caseNode != null;
    +                     caseNode = (Node.Jump)caseNode.getNext())
    +                {
    +                    if (caseNode.getType() != Token.CASE)
    +                        throw badTree(caseNode);
    +                    Node test = caseNode.getFirstChild();
    +                    addIcode(Icode_DUP);
    +                    stackChange(1);
    +                    visitExpression(test, 0);
    +                    addToken(Token.SHEQ);
    +                    stackChange(-1);
    +                    // If true, Icode_IFEQ_POP will jump and remove case
    +                    // value from stack
    +                    addGoto(caseNode.target, Icode_IFEQ_POP);
    +                    stackChange(-1);
    +                }
    +                addIcode(Icode_POP);
    +                stackChange(-1);
    +            }
    +            break;
    +
    +          case Token.TARGET:
    +            markTargetLabel(node);
    +            break;
    +
    +          case Token.IFEQ :
    +          case Token.IFNE :
    +            {
    +                Node target = ((Node.Jump)node).target;
    +                visitExpression(child, 0);
    +                addGoto(target, type);
    +                stackChange(-1);
    +            }
    +            break;
    +
    +          case Token.GOTO:
    +            {
    +                Node target = ((Node.Jump)node).target;
    +                addGoto(target, type);
    +            }
    +            break;
    +
    +          case Token.JSR:
    +            {
    +                Node target = ((Node.Jump)node).target;
    +                addGoto(target, Icode_GOSUB);
    +            }
    +            break;
    +
    +          case Token.FINALLY:
    +            {
    +                // Account for incomming GOTOSUB address
    +                stackChange(1);
    +                int finallyRegister = getLocalBlockRef(node);
    +                addIndexOp(Icode_STARTSUB, finallyRegister);
    +                stackChange(-1);
    +                while (child != null) {
    +                    visitStatement(child, initialStackDepth);
    +                    child = child.getNext();
    +                }
    +                addIndexOp(Icode_RETSUB, finallyRegister);
    +            }
    +            break;
    +
    +          case Token.EXPR_VOID:
    +          case Token.EXPR_RESULT:
    +            updateLineNumber(node);
    +            visitExpression(child, 0);
    +            addIcode((type == Token.EXPR_VOID) ? Icode_POP : Icode_POP_RESULT);
    +            stackChange(-1);
    +            break;
    +
    +          case Token.TRY:
    +            {
    +                Node.Jump tryNode = (Node.Jump)node;
    +                int exceptionObjectLocal = getLocalBlockRef(tryNode);
    +                int scopeLocal = allocLocal();
    +
    +                addIndexOp(Icode_SCOPE_SAVE, scopeLocal);
    +
    +                int tryStart = itsICodeTop;
    +                boolean savedFlag = itsInTryFlag;
    +                itsInTryFlag = true;
    +                while (child != null) {
    +                    visitStatement(child, initialStackDepth);
    +                    child = child.getNext();
    +                }
    +                itsInTryFlag = savedFlag;
    +
    +                Node catchTarget = tryNode.target;
    +                if (catchTarget != null) {
    +                    int catchStartPC
    +                        = itsLabelTable[getTargetLabel(catchTarget)];
    +                    addExceptionHandler(
    +                        tryStart, catchStartPC, catchStartPC,
    +                        false, exceptionObjectLocal, scopeLocal);
    +                }
    +                Node finallyTarget = tryNode.getFinally();
    +                if (finallyTarget != null) {
    +                    int finallyStartPC
    +                        = itsLabelTable[getTargetLabel(finallyTarget)];
    +                    addExceptionHandler(
    +                        tryStart, finallyStartPC, finallyStartPC,
    +                        true, exceptionObjectLocal, scopeLocal);
    +                }
    +
    +                addIndexOp(Icode_LOCAL_CLEAR, scopeLocal);
    +                releaseLocal(scopeLocal);
    +            }
    +            break;
    +
    +          case Token.CATCH_SCOPE:
    +            {
    +                int localIndex = getLocalBlockRef(node);
    +                int scopeIndex = node.getExistingIntProp(Node.CATCH_SCOPE_PROP);
    +                String name = child.getString();
    +                child = child.getNext();
    +                visitExpression(child, 0); // load expression object
    +                addStringPrefix(name);
    +                addIndexPrefix(localIndex);
    +                addToken(Token.CATCH_SCOPE);
    +                addUint8(scopeIndex != 0 ? 1 : 0);
    +                stackChange(-1);
    +            }
    +            break;
    +
    +          case Token.THROW:
    +            updateLineNumber(node);
    +            visitExpression(child, 0);
    +            addToken(Token.THROW);
    +            addUint16(itsLineNumber & 0xFFFF);
    +            stackChange(-1);
    +            break;
    +
    +          case Token.RETHROW:
    +            updateLineNumber(node);
    +            addIndexOp(Token.RETHROW, getLocalBlockRef(node));
    +            break;
    +
    +          case Token.RETURN:
    +            updateLineNumber(node);
    +            if (node.getIntProp(Node.GENERATOR_END_PROP, 0) != 0) {
    +                // We're in a generator, so change RETURN to GENERATOR_END
    +                addIcode(Icode_GENERATOR_END);
    +                addUint16(itsLineNumber & 0xFFFF);
    +            } else if (child != null) {
    +                visitExpression(child, ECF_TAIL);
    +                addToken(Token.RETURN);
    +                stackChange(-1);
    +            } else {
    +                addIcode(Icode_RETUNDEF);
    +            }
    +            break;
    +
    +          case Token.RETURN_RESULT:
    +            updateLineNumber(node);
    +            addToken(Token.RETURN_RESULT);
    +            break;
    +
    +          case Token.ENUM_INIT_KEYS:
    +          case Token.ENUM_INIT_VALUES:
    +          case Token.ENUM_INIT_ARRAY:
    +            visitExpression(child, 0);
    +            addIndexOp(type, getLocalBlockRef(node));
    +            stackChange(-1);
    +            break;
    +
    +          case Icode_GENERATOR:
    +            break;
    +
    +          default:
    +            throw badTree(node);
    +        }
    +
    +        if (itsStackDepth != initialStackDepth) {
    +            throw Kit.codeBug();
    +        }
    +    }
    +
    +    private void visitExpression(Node node, int contextFlags)
    +    {
    +        int type = node.getType();
    +        Node child = node.getFirstChild();
    +        int savedStackDepth = itsStackDepth;
    +        switch (type) {
    +
    +          case Token.FUNCTION:
    +            {
    +                int fnIndex = node.getExistingIntProp(Node.FUNCTION_PROP);
    +                FunctionNode fn = scriptOrFn.getFunctionNode(fnIndex);
    +                // See comments in visitStatement for Token.FUNCTION case
    +                if (fn.getFunctionType() != FunctionNode.FUNCTION_EXPRESSION) {
    +                    throw Kit.codeBug();
    +                }
    +                addIndexOp(Icode_CLOSURE_EXPR, fnIndex);
    +                stackChange(1);
    +            }
    +            break;
    +
    +          case Token.LOCAL_LOAD:
    +            {
    +                int localIndex = getLocalBlockRef(node);
    +                addIndexOp(Token.LOCAL_LOAD, localIndex);
    +                stackChange(1);
    +            }
    +            break;
    +
    +          case Token.COMMA:
    +            {
    +                Node lastChild = node.getLastChild();
    +                while (child != lastChild) {
    +                    visitExpression(child, 0);
    +                    addIcode(Icode_POP);
    +                    stackChange(-1);
    +                    child = child.getNext();
    +                }
    +                // Preserve tail context flag if any
    +                visitExpression(child, contextFlags & ECF_TAIL);
    +            }
    +            break;
    +
    +          case Token.USE_STACK:
    +            // Indicates that stack was modified externally,
    +            // like placed catch object
    +            stackChange(1);
    +            break;
    +
    +          case Token.REF_CALL:
    +          case Token.CALL:
    +          case Token.NEW:
    +            {
    +                if (type == Token.NEW) {
    +                    visitExpression(child, 0);
    +                } else {
    +                    generateCallFunAndThis(child);
    +                }
    +                int argCount = 0;
    +                while ((child = child.getNext()) != null) {
    +                    visitExpression(child, 0);
    +                    ++argCount;
    +                }
    +                int callType = node.getIntProp(Node.SPECIALCALL_PROP,
    +                                               Node.NON_SPECIALCALL);
    +                if (callType != Node.NON_SPECIALCALL) {
    +                    // embed line number and source filename
    +                    addIndexOp(Icode_CALLSPECIAL, argCount);
    +                    addUint8(callType);
    +                    addUint8(type == Token.NEW ? 1 : 0);
    +                    addUint16(itsLineNumber & 0xFFFF);
    +                } else {
    +                    // Only use the tail call optimization if we're not in a try
    +                    // or we're not generating debug info (since the
    +                    // optimization will confuse the debugger)
    +                    if (type == Token.CALL && (contextFlags & ECF_TAIL) != 0 &&
    +                        !compilerEnv.isGenerateDebugInfo() && !itsInTryFlag)
    +                    {
    +                        type = Icode_TAIL_CALL;
    +                    }
    +                    addIndexOp(type, argCount);
    +                }
    +                // adjust stack
    +                if (type == Token.NEW) {
    +                    // new: f, args -> result
    +                    stackChange(-argCount);
    +                } else {
    +                    // call: f, thisObj, args -> result
    +                    // ref_call: f, thisObj, args -> ref
    +                    stackChange(-1 - argCount);
    +                }
    +                if (argCount > itsData.itsMaxCalleeArgs) {
    +                    itsData.itsMaxCalleeArgs = argCount;
    +                }
    +            }
    +            break;
    +
    +          case Token.AND:
    +          case Token.OR:
    +            {
    +                visitExpression(child, 0);
    +                addIcode(Icode_DUP);
    +                stackChange(1);
    +                int afterSecondJumpStart = itsICodeTop;
    +                int jump = (type == Token.AND) ? Token.IFNE : Token.IFEQ;
    +                addGotoOp(jump);
    +                stackChange(-1);
    +                addIcode(Icode_POP);
    +                stackChange(-1);
    +                child = child.getNext();
    +                // Preserve tail context flag if any
    +                visitExpression(child, contextFlags & ECF_TAIL);
    +                resolveForwardGoto(afterSecondJumpStart);
    +            }
    +            break;
    +
    +          case Token.HOOK:
    +            {
    +                Node ifThen = child.getNext();
    +                Node ifElse = ifThen.getNext();
    +                visitExpression(child, 0);
    +                int elseJumpStart = itsICodeTop;
    +                addGotoOp(Token.IFNE);
    +                stackChange(-1);
    +                // Preserve tail context flag if any
    +                visitExpression(ifThen, contextFlags & ECF_TAIL);
    +                int afterElseJumpStart = itsICodeTop;
    +                addGotoOp(Token.GOTO);
    +                resolveForwardGoto(elseJumpStart);
    +                itsStackDepth = savedStackDepth;
    +                // Preserve tail context flag if any
    +                visitExpression(ifElse, contextFlags & ECF_TAIL);
    +                resolveForwardGoto(afterElseJumpStart);
    +            }
    +            break;
    +
    +          case Token.GETPROP:
    +          case Token.GETPROPNOWARN:
    +            visitExpression(child, 0);
    +            child = child.getNext();
    +            addStringOp(type, child.getString());
    +            break;
    +
    +          case Token.GETELEM:
    +          case Token.DELPROP:
    +          case Token.BITAND:
    +          case Token.BITOR:
    +          case Token.BITXOR:
    +          case Token.LSH:
    +          case Token.RSH:
    +          case Token.URSH:
    +          case Token.ADD:
    +          case Token.SUB:
    +          case Token.MOD:
    +          case Token.DIV:
    +          case Token.MUL:
    +          case Token.EQ:
    +          case Token.NE:
    +          case Token.SHEQ:
    +          case Token.SHNE:
    +          case Token.IN:
    +          case Token.INSTANCEOF:
    +          case Token.LE:
    +          case Token.LT:
    +          case Token.GE:
    +          case Token.GT:
    +            visitExpression(child, 0);
    +            child = child.getNext();
    +            visitExpression(child, 0);
    +            addToken(type);
    +            stackChange(-1);
    +            break;
    +
    +          case Token.POS:
    +          case Token.NEG:
    +          case Token.NOT:
    +          case Token.BITNOT:
    +          case Token.TYPEOF:
    +          case Token.VOID:
    +            visitExpression(child, 0);
    +            if (type == Token.VOID) {
    +                addIcode(Icode_POP);
    +                addIcode(Icode_UNDEF);
    +            } else {
    +                addToken(type);
    +            }
    +            break;
    +
    +          case Token.GET_REF:
    +          case Token.DEL_REF:
    +            visitExpression(child, 0);
    +            addToken(type);
    +            break;
    +
    +          case Token.SETPROP:
    +          case Token.SETPROP_OP:
    +            {
    +                visitExpression(child, 0);
    +                child = child.getNext();
    +                String property = child.getString();
    +                child = child.getNext();
    +                if (type == Token.SETPROP_OP) {
    +                    addIcode(Icode_DUP);
    +                    stackChange(1);
    +                    addStringOp(Token.GETPROP, property);
    +                    // Compensate for the following USE_STACK
    +                    stackChange(-1);
    +                }
    +                visitExpression(child, 0);
    +                addStringOp(Token.SETPROP, property);
    +                stackChange(-1);
    +            }
    +            break;
    +
    +          case Token.SETELEM:
    +          case Token.SETELEM_OP:
    +            visitExpression(child, 0);
    +            child = child.getNext();
    +            visitExpression(child, 0);
    +            child = child.getNext();
    +            if (type == Token.SETELEM_OP) {
    +                addIcode(Icode_DUP2);
    +                stackChange(2);
    +                addToken(Token.GETELEM);
    +                stackChange(-1);
    +                // Compensate for the following USE_STACK
    +                stackChange(-1);
    +            }
    +            visitExpression(child, 0);
    +            addToken(Token.SETELEM);
    +            stackChange(-2);
    +            break;
    +
    +          case Token.SET_REF:
    +          case Token.SET_REF_OP:
    +            visitExpression(child, 0);
    +            child = child.getNext();
    +            if (type == Token.SET_REF_OP) {
    +                addIcode(Icode_DUP);
    +                stackChange(1);
    +                addToken(Token.GET_REF);
    +                // Compensate for the following USE_STACK
    +                stackChange(-1);
    +            }
    +            visitExpression(child, 0);
    +            addToken(Token.SET_REF);
    +            stackChange(-1);
    +            break;
    +
    +          case Token.SETNAME:
    +            {
    +                String name = child.getString();
    +                visitExpression(child, 0);
    +                child = child.getNext();
    +                visitExpression(child, 0);
    +                addStringOp(Token.SETNAME, name);
    +                stackChange(-1);
    +            }
    +            break;
    +
    +          case Token.SETCONST:
    +            {
    +                String name = child.getString();
    +                visitExpression(child, 0);
    +                child = child.getNext();
    +                visitExpression(child, 0);
    +                addStringOp(Icode_SETCONST, name);
    +                stackChange(-1);
    +            }
    +            break;
    +
    +          case Token.TYPEOFNAME:
    +            {
    +                int index = -1;
    +                // use typeofname if an activation frame exists
    +                // since the vars all exist there instead of in jregs
    +                if (itsInFunctionFlag && !itsData.itsNeedsActivation)
    +                    index = scriptOrFn.getIndexForNameNode(node);
    +                if (index == -1) {
    +                    addStringOp(Icode_TYPEOFNAME, node.getString());
    +                    stackChange(1);
    +                } else {
    +                    addVarOp(Token.GETVAR, index);
    +                    stackChange(1);
    +                    addToken(Token.TYPEOF);
    +                }
    +            }
    +            break;
    +
    +          case Token.BINDNAME:
    +          case Token.NAME:
    +          case Token.STRING:
    +            addStringOp(type, node.getString());
    +            stackChange(1);
    +            break;
    +
    +          case Token.INC:
    +          case Token.DEC:
    +            visitIncDec(node, child);
    +            break;
    +
    +          case Token.NUMBER:
    +            {
    +                double num = node.getDouble();
    +                int inum = (int)num;
    +                if (inum == num) {
    +                    if (inum == 0) {
    +                        addIcode(Icode_ZERO);
    +                        // Check for negative zero
    +                        if (1.0 / num < 0.0) {
    +                            addToken(Token.NEG);
    +                        }
    +                    } else if (inum == 1) {
    +                        addIcode(Icode_ONE);
    +                    } else if ((short)inum == inum) {
    +                        addIcode(Icode_SHORTNUMBER);
    +                        // write short as uin16 bit pattern
    +                        addUint16(inum & 0xFFFF);
    +                    } else {
    +                        addIcode(Icode_INTNUMBER);
    +                        addInt(inum);
    +                    }
    +                } else {
    +                    int index = getDoubleIndex(num);
    +                    addIndexOp(Token.NUMBER, index);
    +                }
    +                stackChange(1);
    +            }
    +            break;
    +
    +          case Token.GETVAR:
    +            {
    +                if (itsData.itsNeedsActivation) Kit.codeBug();
    +                int index = scriptOrFn.getIndexForNameNode(node);
    +                addVarOp(Token.GETVAR, index);
    +                stackChange(1);
    +            }
    +            break;
    +
    +          case Token.SETVAR:
    +            {
    +                if (itsData.itsNeedsActivation) Kit.codeBug();
    +                int index = scriptOrFn.getIndexForNameNode(child);
    +                child = child.getNext();
    +                visitExpression(child, 0);
    +                addVarOp(Token.SETVAR, index);
    +            }
    +            break;
    +
    +          case Token.SETCONSTVAR:
    +            {
    +                if (itsData.itsNeedsActivation) Kit.codeBug();
    +                int index = scriptOrFn.getIndexForNameNode(child);
    +                child = child.getNext();
    +                visitExpression(child, 0);
    +                addVarOp(Token.SETCONSTVAR, index);
    +            }
    +            break;
    +
    +          case Token.NULL:
    +          case Token.THIS:
    +          case Token.THISFN:
    +          case Token.FALSE:
    +          case Token.TRUE:
    +            addToken(type);
    +            stackChange(1);
    +            break;
    +
    +          case Token.ENUM_NEXT:
    +          case Token.ENUM_ID:
    +            addIndexOp(type, getLocalBlockRef(node));
    +            stackChange(1);
    +            break;
    +
    +          case Token.REGEXP:
    +            {
    +                int index = node.getExistingIntProp(Node.REGEXP_PROP);
    +                addIndexOp(Token.REGEXP, index);
    +                stackChange(1);
    +            }
    +            break;
    +
    +          case Token.ARRAYLIT:
    +          case Token.OBJECTLIT:
    +            visitLiteral(node, child);
    +            break;
    +
    +          case Token.ARRAYCOMP:
    +            visitArrayComprehension(node, child, child.getNext());
    +            break;
    +
    +          case Token.REF_SPECIAL:
    +            visitExpression(child, 0);
    +            addStringOp(type, (String)node.getProp(Node.NAME_PROP));
    +            break;
    +
    +          case Token.REF_MEMBER:
    +          case Token.REF_NS_MEMBER:
    +          case Token.REF_NAME:
    +          case Token.REF_NS_NAME:
    +            {
    +                int memberTypeFlags = node.getIntProp(Node.MEMBER_TYPE_PROP, 0);
    +                // generate possible target, possible namespace and member
    +                int childCount = 0;
    +                do {
    +                    visitExpression(child, 0);
    +                    ++childCount;
    +                    child = child.getNext();
    +                } while (child != null);
    +                addIndexOp(type, memberTypeFlags);
    +                stackChange(1 - childCount);
    +            }
    +            break;
    +
    +          case Token.DOTQUERY:
    +            {
    +                int queryPC;
    +                updateLineNumber(node);
    +                visitExpression(child, 0);
    +                addIcode(Icode_ENTERDQ);
    +                stackChange(-1);
    +                queryPC = itsICodeTop;
    +                visitExpression(child.getNext(), 0);
    +                addBackwardGoto(Icode_LEAVEDQ, queryPC);
    +            }
    +            break;
    +
    +          case Token.DEFAULTNAMESPACE :
    +          case Token.ESCXMLATTR :
    +          case Token.ESCXMLTEXT :
    +            visitExpression(child, 0);
    +            addToken(type);
    +            break;
    +
    +          case Token.YIELD:
    +            if (child != null) {
    +                visitExpression(child, 0);
    +            } else {
    +                addIcode(Icode_UNDEF);
    +                stackChange(1);
    +            }
    +            addToken(Token.YIELD);
    +            addUint16(node.getLineno() & 0xFFFF);
    +            break;
    +
    +          case Token.WITHEXPR: {
    +            Node enterWith = node.getFirstChild();
    +            Node with = enterWith.getNext();
    +            visitExpression(enterWith.getFirstChild(), 0);
    +            addToken(Token.ENTERWITH);
    +            stackChange(-1);
    +            visitExpression(with.getFirstChild(), 0);
    +            addToken(Token.LEAVEWITH);
    +            break;
    +          }
    +
    +          default:
    +            throw badTree(node);
    +        }
    +        if (savedStackDepth + 1 != itsStackDepth) {
    +            Kit.codeBug();
    +        }
    +    }
    +
    +    private void generateCallFunAndThis(Node left)
    +    {
    +        // Generate code to place on stack function and thisObj
    +        int type = left.getType();
    +        switch (type) {
    +          case Token.NAME: {
    +            String name = left.getString();
    +            // stack: ... -> ... function thisObj
    +            addStringOp(Icode_NAME_AND_THIS, name);
    +            stackChange(2);
    +            break;
    +          }
    +          case Token.GETPROP:
    +          case Token.GETELEM: {
    +            Node target = left.getFirstChild();
    +            visitExpression(target, 0);
    +            Node id = target.getNext();
    +            if (type == Token.GETPROP) {
    +                String property = id.getString();
    +                // stack: ... target -> ... function thisObj
    +                addStringOp(Icode_PROP_AND_THIS, property);
    +                stackChange(1);
    +            } else {
    +                visitExpression(id, 0);
    +                // stack: ... target id -> ... function thisObj
    +                addIcode(Icode_ELEM_AND_THIS);
    +            }
    +            break;
    +          }
    +          default:
    +            // Including Token.GETVAR
    +            visitExpression(left, 0);
    +            // stack: ... value -> ... function thisObj
    +            addIcode(Icode_VALUE_AND_THIS);
    +            stackChange(1);
    +            break;
    +        }
    +    }
    +
    +    private void visitIncDec(Node node, Node child)
    +    {
    +        int incrDecrMask = node.getExistingIntProp(Node.INCRDECR_PROP);
    +        int childType = child.getType();
    +        switch (childType) {
    +          case Token.GETVAR : {
    +            if (itsData.itsNeedsActivation) Kit.codeBug();
    +            int i = scriptOrFn.getIndexForNameNode(child);
    +            addVarOp(Icode_VAR_INC_DEC, i);
    +            addUint8(incrDecrMask);
    +            stackChange(1);
    +            break;
    +          }
    +          case Token.NAME : {
    +            String name = child.getString();
    +            addStringOp(Icode_NAME_INC_DEC, name);
    +            addUint8(incrDecrMask);
    +            stackChange(1);
    +            break;
    +          }
    +          case Token.GETPROP : {
    +            Node object = child.getFirstChild();
    +            visitExpression(object, 0);
    +            String property = object.getNext().getString();
    +            addStringOp(Icode_PROP_INC_DEC, property);
    +            addUint8(incrDecrMask);
    +            break;
    +          }
    +          case Token.GETELEM : {
    +            Node object = child.getFirstChild();
    +            visitExpression(object, 0);
    +            Node index = object.getNext();
    +            visitExpression(index, 0);
    +            addIcode(Icode_ELEM_INC_DEC);
    +            addUint8(incrDecrMask);
    +            stackChange(-1);
    +            break;
    +          }
    +          case Token.GET_REF : {
    +            Node ref = child.getFirstChild();
    +            visitExpression(ref, 0);
    +            addIcode(Icode_REF_INC_DEC);
    +            addUint8(incrDecrMask);
    +            break;
    +          }
    +          default : {
    +            throw badTree(node);
    +          }
    +        }
    +    }
    +
    +    private void visitLiteral(Node node, Node child)
    +    {
    +        int type = node.getType();
    +        int count;
    +        Object[] propertyIds = null;
    +        if (type == Token.ARRAYLIT) {
    +            count = 0;
    +            for (Node n = child; n != null; n = n.getNext()) {
    +                ++count;
    +            }
    +        } else if (type == Token.OBJECTLIT) {
    +            propertyIds = (Object[])node.getProp(Node.OBJECT_IDS_PROP);
    +            count = propertyIds.length;
    +        } else {
    +            throw badTree(node);
    +        }
    +        addIndexOp(Icode_LITERAL_NEW, count);
    +        stackChange(2);
    +        while (child != null) {
    +            int childType = child.getType();
    +            if (childType == Token.GET) {
    +                visitExpression(child.getFirstChild(), 0);
    +                addIcode(Icode_LITERAL_GETTER);
    +            } else if (childType == Token.SET) {
    +                visitExpression(child.getFirstChild(), 0);
    +                addIcode(Icode_LITERAL_SETTER);
    +            } else {
    +                visitExpression(child, 0);
    +                addIcode(Icode_LITERAL_SET);
    +            }
    +            stackChange(-1);
    +            child = child.getNext();
    +        }
    +        if (type == Token.ARRAYLIT) {
    +            int[] skipIndexes = (int[])node.getProp(Node.SKIP_INDEXES_PROP);
    +            if (skipIndexes == null) {
    +                addToken(Token.ARRAYLIT);
    +            } else {
    +                int index = itsLiteralIds.size();
    +                itsLiteralIds.add(skipIndexes);
    +                addIndexOp(Icode_SPARE_ARRAYLIT, index);
    +            }
    +        } else {
    +            int index = itsLiteralIds.size();
    +            itsLiteralIds.add(propertyIds);
    +            addIndexOp(Token.OBJECTLIT, index);
    +        }
    +        stackChange(-1);
    +    }
    +    
    +    private void visitArrayComprehension(Node node, Node initStmt, Node expr)
    +    {
    +        // A bit of a hack: array comprehensions are implemented using
    +        // statement nodes for the iteration, yet they appear in an
    +        // expression context. So we pass the current stack depth to
    +        // visitStatement so it can check that the depth is not altered
    +        // by statements.
    +        visitStatement(initStmt, itsStackDepth);
    +        visitExpression(expr, 0);
    +    }
    +
    +    private int getLocalBlockRef(Node node)
    +    {
    +        Node localBlock = (Node)node.getProp(Node.LOCAL_BLOCK_PROP);
    +        return localBlock.getExistingIntProp(Node.LOCAL_PROP);
    +    }
    +
    +    private int getTargetLabel(Node target)
    +    {
    +        int label = target.labelId();
    +        if (label != -1) {
    +            return label;
    +        }
    +        label = itsLabelTableTop;
    +        if (itsLabelTable == null || label == itsLabelTable.length) {
    +            if (itsLabelTable == null) {
    +                itsLabelTable = new int[MIN_LABEL_TABLE_SIZE];
    +            }else {
    +                int[] tmp = new int[itsLabelTable.length * 2];
    +                System.arraycopy(itsLabelTable, 0, tmp, 0, label);
    +                itsLabelTable = tmp;
    +            }
    +        }
    +        itsLabelTableTop = label + 1;
    +        itsLabelTable[label] = -1;
    +
    +        target.labelId(label);
    +        return label;
    +    }
    +
    +    private void markTargetLabel(Node target)
    +    {
    +        int label = getTargetLabel(target);
    +        if (itsLabelTable[label] != -1) {
    +            // Can mark label only once
    +            Kit.codeBug();
    +        }
    +        itsLabelTable[label] = itsICodeTop;
    +    }
    +
    +    private void addGoto(Node target, int gotoOp)
    +    {
    +        int label = getTargetLabel(target);
    +        if (!(label < itsLabelTableTop)) Kit.codeBug();
    +        int targetPC = itsLabelTable[label];
    +
    +        if (targetPC != -1) {
    +            addBackwardGoto(gotoOp, targetPC);
    +        } else {
    +            int gotoPC = itsICodeTop;
    +            addGotoOp(gotoOp);
    +            int top = itsFixupTableTop;
    +            if (itsFixupTable == null || top == itsFixupTable.length) {
    +                if (itsFixupTable == null) {
    +                    itsFixupTable = new long[MIN_FIXUP_TABLE_SIZE];
    +                } else {
    +                    long[] tmp = new long[itsFixupTable.length * 2];
    +                    System.arraycopy(itsFixupTable, 0, tmp, 0, top);
    +                    itsFixupTable = tmp;
    +                }
    +            }
    +            itsFixupTableTop = top + 1;
    +            itsFixupTable[top] = ((long)label << 32) | gotoPC;
    +        }
    +    }
    +
    +    private void fixLabelGotos()
    +    {
    +        for (int i = 0; i < itsFixupTableTop; i++) {
    +            long fixup = itsFixupTable[i];
    +            int label = (int)(fixup >> 32);
    +            int jumpSource = (int)fixup;
    +            int pc = itsLabelTable[label];
    +            if (pc == -1) {
    +                // Unlocated label
    +                throw Kit.codeBug();
    +            }
    +            resolveGoto(jumpSource, pc);
    +        }
    +        itsFixupTableTop = 0;
    +    }
    +
    +    private void addBackwardGoto(int gotoOp, int jumpPC)
    +    {
    +        int fromPC = itsICodeTop;
    +        // Ensure that this is a jump backward
    +        if (fromPC <= jumpPC) throw Kit.codeBug();
    +        addGotoOp(gotoOp);
    +        resolveGoto(fromPC, jumpPC);
    +    }
    +
    +    private void resolveForwardGoto(int fromPC)
    +    {
    +        // Ensure that forward jump skips at least self bytecode
    +        if (itsICodeTop < fromPC + 3) throw Kit.codeBug();
    +        resolveGoto(fromPC, itsICodeTop);
    +    }
    +
    +    private void resolveGoto(int fromPC, int jumpPC)
    +    {
    +        int offset = jumpPC - fromPC;
    +        // Ensure that jumps do not overlap
    +        if (0 <= offset && offset <= 2) throw Kit.codeBug();
    +        int offsetSite = fromPC + 1;
    +        if (offset != (short)offset) {
    +            if (itsData.longJumps == null) {
    +                itsData.longJumps = new UintMap();
    +            }
    +            itsData.longJumps.put(offsetSite, jumpPC);
    +            offset = 0;
    +        }
    +        byte[] array = itsData.itsICode;
    +        array[offsetSite] = (byte)(offset >> 8);
    +        array[offsetSite + 1] = (byte)offset;
    +    }
    +
    +    private void addToken(int token)
    +    {
    +        if (!validTokenCode(token)) throw Kit.codeBug();
    +        addUint8(token);
    +    }
    +
    +    private void addIcode(int icode)
    +    {
    +        if (!validIcode(icode)) throw Kit.codeBug();
    +        // Write negative icode as uint8 bits
    +        addUint8(icode & 0xFF);
    +    }
    +
    +    private void addUint8(int value)
    +    {
    +        if ((value & ~0xFF) != 0) throw Kit.codeBug();
    +        byte[] array = itsData.itsICode;
    +        int top = itsICodeTop;
    +        if (top == array.length) {
    +            array = increaseICodeCapacity(1);
    +        }
    +        array[top] = (byte)value;
    +        itsICodeTop = top + 1;
    +    }
    +
    +    private void addUint16(int value)
    +    {
    +        if ((value & ~0xFFFF) != 0) throw Kit.codeBug();
    +        byte[] array = itsData.itsICode;
    +        int top = itsICodeTop;
    +        if (top + 2 > array.length) {
    +            array = increaseICodeCapacity(2);
    +        }
    +        array[top] = (byte)(value >>> 8);
    +        array[top + 1] = (byte)value;
    +        itsICodeTop = top + 2;
    +    }
    +
    +    private void addInt(int i)
    +    {
    +        byte[] array = itsData.itsICode;
    +        int top = itsICodeTop;
    +        if (top + 4 > array.length) {
    +            array = increaseICodeCapacity(4);
    +        }
    +        array[top] = (byte)(i >>> 24);
    +        array[top + 1] = (byte)(i >>> 16);
    +        array[top + 2] = (byte)(i >>> 8);
    +        array[top + 3] = (byte)i;
    +        itsICodeTop = top + 4;
    +    }
    +
    +    private int getDoubleIndex(double num)
    +    {
    +        int index = itsDoubleTableTop;
    +        if (index == 0) {
    +            itsData.itsDoubleTable = new double[64];
    +        } else if (itsData.itsDoubleTable.length == index) {
    +            double[] na = new double[index * 2];
    +            System.arraycopy(itsData.itsDoubleTable, 0, na, 0, index);
    +            itsData.itsDoubleTable = na;
    +        }
    +        itsData.itsDoubleTable[index] = num;
    +        itsDoubleTableTop = index + 1;
    +        return index;
    +    }
    +
    +    private void addGotoOp(int gotoOp)
    +    {
    +        byte[] array = itsData.itsICode;
    +        int top = itsICodeTop;
    +        if (top + 3 > array.length) {
    +            array = increaseICodeCapacity(3);
    +        }
    +        array[top] = (byte)gotoOp;
    +        // Offset would written later
    +        itsICodeTop = top + 1 + 2;
    +    }
    +
    +    private void addVarOp(int op, int varIndex)
    +    {
    +        switch (op) {
    +          case Token.SETCONSTVAR:
    +            if (varIndex < 128) {
    +                addIcode(Icode_SETCONSTVAR1);
    +                addUint8(varIndex);
    +                return;
    +            }
    +            addIndexOp(Icode_SETCONSTVAR, varIndex);
    +            return;
    +          case Token.GETVAR:
    +          case Token.SETVAR:
    +            if (varIndex < 128) {
    +                addIcode(op == Token.GETVAR ? Icode_GETVAR1 : Icode_SETVAR1);
    +                addUint8(varIndex);
    +                return;
    +            }
    +            // fallthrough
    +          case Icode_VAR_INC_DEC:
    +            addIndexOp(op, varIndex);
    +            return;
    +        }
    +        throw Kit.codeBug();
    +    }
    +
    +    private void addStringOp(int op, String str)
    +    {
    +        addStringPrefix(str);
    +        if (validIcode(op)) {
    +            addIcode(op);
    +        } else {
    +            addToken(op);
    +        }
    +    }
    +
    +    private void addIndexOp(int op, int index)
    +    {
    +        addIndexPrefix(index);
    +        if (validIcode(op)) {
    +            addIcode(op);
    +        } else {
    +            addToken(op);
    +        }
    +    }
    +
    +    private void addStringPrefix(String str)
    +    {
    +        int index = itsStrings.get(str, -1);
    +        if (index == -1) {
    +            index = itsStrings.size();
    +            itsStrings.put(str, index);
    +        }
    +        if (index < 4) {
    +            addIcode(Icode_REG_STR_C0 - index);
    +        } else if (index <= 0xFF) {
    +            addIcode(Icode_REG_STR1);
    +            addUint8(index);
    +         } else if (index <= 0xFFFF) {
    +            addIcode(Icode_REG_STR2);
    +            addUint16(index);
    +         } else {
    +            addIcode(Icode_REG_STR4);
    +            addInt(index);
    +        }
    +    }
    +
    +    private void addIndexPrefix(int index)
    +    {
    +        if (index < 0) Kit.codeBug();
    +        if (index < 6) {
    +            addIcode(Icode_REG_IND_C0 - index);
    +        } else if (index <= 0xFF) {
    +            addIcode(Icode_REG_IND1);
    +            addUint8(index);
    +         } else if (index <= 0xFFFF) {
    +            addIcode(Icode_REG_IND2);
    +            addUint16(index);
    +         } else {
    +            addIcode(Icode_REG_IND4);
    +            addInt(index);
    +        }
    +    }
    +
    +    private void addExceptionHandler(int icodeStart, int icodeEnd,
    +                                     int handlerStart, boolean isFinally,
    +                                     int exceptionObjectLocal, int scopeLocal)
    +    {
    +        int top = itsExceptionTableTop;
    +        int[] table = itsData.itsExceptionTable;
    +        if (table == null) {
    +            if (top != 0) Kit.codeBug();
    +            table = new int[EXCEPTION_SLOT_SIZE * 2];
    +            itsData.itsExceptionTable = table;
    +        } else if (table.length == top) {
    +            table = new int[table.length * 2];
    +            System.arraycopy(itsData.itsExceptionTable, 0, table, 0, top);
    +            itsData.itsExceptionTable = table;
    +        }
    +        table[top + EXCEPTION_TRY_START_SLOT]  = icodeStart;
    +        table[top + EXCEPTION_TRY_END_SLOT]    = icodeEnd;
    +        table[top + EXCEPTION_HANDLER_SLOT]    = handlerStart;
    +        table[top + EXCEPTION_TYPE_SLOT]       = isFinally ? 1 : 0;
    +        table[top + EXCEPTION_LOCAL_SLOT]      = exceptionObjectLocal;
    +        table[top + EXCEPTION_SCOPE_SLOT]      = scopeLocal;
    +
    +        itsExceptionTableTop = top + EXCEPTION_SLOT_SIZE;
    +    }
    +
    +    private byte[] increaseICodeCapacity(int extraSize)
    +    {
    +        int capacity = itsData.itsICode.length;
    +        int top = itsICodeTop;
    +        if (top + extraSize <= capacity) throw Kit.codeBug();
    +        capacity *= 2;
    +        if (top + extraSize > capacity) {
    +            capacity = top + extraSize;
    +        }
    +        byte[] array = new byte[capacity];
    +        System.arraycopy(itsData.itsICode, 0, array, 0, top);
    +        itsData.itsICode = array;
    +        return array;
    +    }
    +
    +    private void stackChange(int change)
    +    {
    +        if (change <= 0) {
    +            itsStackDepth += change;
    +        } else {
    +            int newDepth = itsStackDepth + change;
    +            if (newDepth > itsData.itsMaxStack) {
    +                itsData.itsMaxStack = newDepth;
    +            }
    +            itsStackDepth = newDepth;
    +        }
    +    }
    +
    +    private int allocLocal()
    +    {
    +        int localSlot = itsLocalTop;
    +        ++itsLocalTop;
    +        if (itsLocalTop > itsData.itsMaxLocals) {
    +            itsData.itsMaxLocals = itsLocalTop;
    +        }
    +        return localSlot;
    +    }
    +
    +    private void releaseLocal(int localSlot)
    +    {
    +        --itsLocalTop;
    +        if (localSlot != itsLocalTop) Kit.codeBug();
    +    }
    +
    +    private static int getShort(byte[] iCode, int pc) {
    +        return (iCode[pc] << 8) | (iCode[pc + 1] & 0xFF);
    +    }
    +
    +    private static int getIndex(byte[] iCode, int pc) {
    +        return ((iCode[pc] & 0xFF) << 8) | (iCode[pc + 1] & 0xFF);
    +    }
    +
    +    private static int getInt(byte[] iCode, int pc) {
    +        return (iCode[pc] << 24) | ((iCode[pc + 1] & 0xFF) << 16)
    +               | ((iCode[pc + 2] & 0xFF) << 8) | (iCode[pc + 3] & 0xFF);
    +    }
    +
    +    private static int getExceptionHandler(CallFrame frame,
    +                                           boolean onlyFinally)
    +    {
    +        int[] exceptionTable = frame.idata.itsExceptionTable;
    +        if (exceptionTable == null) {
    +            // No exception handlers
    +            return -1;
    +        }
    +
    +        // Icode switch in the interpreter increments PC immediately
    +        // and it is necessary to subtract 1 from the saved PC
    +        // to point it before the start of the next instruction.
    +        int pc = frame.pc - 1;
    +
    +        // OPT: use binary search
    +        int best = -1, bestStart = 0, bestEnd = 0;
    +        for (int i = 0; i != exceptionTable.length; i += EXCEPTION_SLOT_SIZE) {
    +            int start = exceptionTable[i + EXCEPTION_TRY_START_SLOT];
    +            int end = exceptionTable[i + EXCEPTION_TRY_END_SLOT];
    +            if (!(start <= pc && pc < end)) {
    +                continue;
    +            }
    +            if (onlyFinally && exceptionTable[i + EXCEPTION_TYPE_SLOT] != 1) {
    +                continue;
    +            }
    +            if (best >= 0) {
    +                // Since handlers always nest and they never have shared end
    +                // although they can share start  it is sufficient to compare
    +                // handlers ends
    +                if (bestEnd < end) {
    +                    continue;
    +                }
    +                // Check the above assumption
    +                if (bestStart > start) Kit.codeBug(); // should be nested
    +                if (bestEnd == end) Kit.codeBug();  // no ens sharing
    +            }
    +            best = i;
    +            bestStart = start;
    +            bestEnd = end;
    +        }
    +        return best;
    +    }
    +
    +    private static void dumpICode(InterpreterData idata)
    +    {
    +        if (!Token.printICode) {
    +            return;
    +        }
    +
    +        byte iCode[] = idata.itsICode;
    +        int iCodeLength = iCode.length;
    +        String[] strings = idata.itsStringTable;
    +        PrintStream out = System.out;
    +        out.println("ICode dump, for " + idata.itsName
    +                    + ", length = " + iCodeLength);
    +        out.println("MaxStack = " + idata.itsMaxStack);
    +
    +        int indexReg = 0;
    +        for (int pc = 0; pc < iCodeLength; ) {
    +            out.flush();
    +            out.print(" [" + pc + "] ");
    +            int token = iCode[pc];
    +            int icodeLength = bytecodeSpan(token);
    +            String tname = bytecodeName(token);
    +            int old_pc = pc;
    +            ++pc;
    +            switch (token) {
    +              default:
    +                if (icodeLength != 1) Kit.codeBug();
    +                out.println(tname);
    +                break;
    +
    +              case Icode_GOSUB :
    +              case Token.GOTO :
    +              case Token.IFEQ :
    +              case Token.IFNE :
    +              case Icode_IFEQ_POP :
    +              case Icode_LEAVEDQ : {
    +                int newPC = pc + getShort(iCode, pc) - 1;
    +                out.println(tname + " " + newPC);
    +                pc += 2;
    +                break;
    +              }
    +              case Icode_VAR_INC_DEC :
    +              case Icode_NAME_INC_DEC :
    +              case Icode_PROP_INC_DEC :
    +              case Icode_ELEM_INC_DEC :
    +              case Icode_REF_INC_DEC: {
    +                int incrDecrType = iCode[pc];
    +                out.println(tname + " " + incrDecrType);
    +                ++pc;
    +                break;
    +              }
    +
    +              case Icode_CALLSPECIAL : {
    +                int callType = iCode[pc] & 0xFF;
    +                boolean isNew =  (iCode[pc + 1] != 0);
    +                int line = getIndex(iCode, pc+2);
    +                out.println(tname+" "+callType+" "+isNew+" "+indexReg+" "+line);
    +                pc += 4;
    +                break;
    +              }
    +
    +              case Token.CATCH_SCOPE:
    +                {
    +                    boolean afterFisrtFlag =  (iCode[pc] != 0);
    +                    out.println(tname+" "+afterFisrtFlag);
    +                    ++pc;
    +                }
    +                break;
    +              case Token.REGEXP :
    +                out.println(tname+" "+idata.itsRegExpLiterals[indexReg]);
    +                break;
    +              case Token.OBJECTLIT :
    +              case Icode_SPARE_ARRAYLIT :
    +                out.println(tname+" "+idata.literalIds[indexReg]);
    +                break;
    +              case Icode_CLOSURE_EXPR :
    +              case Icode_CLOSURE_STMT :
    +                out.println(tname+" "+idata.itsNestedFunctions[indexReg]);
    +                break;
    +              case Token.CALL :
    +              case Icode_TAIL_CALL :
    +              case Token.REF_CALL :
    +              case Token.NEW :
    +                out.println(tname+' '+indexReg);
    +                break;
    +              case Token.THROW :
    +              case Token.YIELD :
    +              case Icode_GENERATOR :
    +              case Icode_GENERATOR_END :
    +              {
    +                int line = getIndex(iCode, pc);
    +                out.println(tname + " : " + line);
    +                pc += 2;
    +                break;
    +              }
    +              case Icode_SHORTNUMBER : {
    +                int value = getShort(iCode, pc);
    +                out.println(tname + " " + value);
    +                pc += 2;
    +                break;
    +              }
    +              case Icode_INTNUMBER : {
    +                int value = getInt(iCode, pc);
    +                out.println(tname + " " + value);
    +                pc += 4;
    +                break;
    +              }
    +              case Token.NUMBER : {
    +                double value = idata.itsDoubleTable[indexReg];
    +                out.println(tname + " " + value);
    +                break;
    +              }
    +              case Icode_LINE : {
    +                int line = getIndex(iCode, pc);
    +                out.println(tname + " : " + line);
    +                pc += 2;
    +                break;
    +              }
    +              case Icode_REG_STR1: {
    +                String str = strings[0xFF & iCode[pc]];
    +                out.println(tname + " \"" + str + '"');
    +                ++pc;
    +                break;
    +              }
    +              case Icode_REG_STR2: {
    +                String str = strings[getIndex(iCode, pc)];
    +                out.println(tname + " \"" + str + '"');
    +                pc += 2;
    +                break;
    +              }
    +              case Icode_REG_STR4: {
    +                String str = strings[getInt(iCode, pc)];
    +                out.println(tname + " \"" + str + '"');
    +                pc += 4;
    +                break;
    +              }
    +              case Icode_REG_IND_C0:
    +                  indexReg = 0;
    +                  out.println(tname);
    +                  break;
    +              case Icode_REG_IND_C1:
    +                  indexReg = 1;
    +                  out.println(tname);
    +                  break;
    +              case Icode_REG_IND_C2:
    +                  indexReg = 2;
    +                  out.println(tname);
    +                  break;
    +              case Icode_REG_IND_C3:
    +                  indexReg = 3;
    +                  out.println(tname);
    +                  break;
    +              case Icode_REG_IND_C4:
    +                  indexReg = 4;
    +                  out.println(tname);
    +                  break;
    +              case Icode_REG_IND_C5:
    +                  indexReg = 5;
    +                  out.println(tname);
    +                  break;
    +              case Icode_REG_IND1: {
    +                indexReg = 0xFF & iCode[pc];
    +                out.println(tname+" "+indexReg);
    +                ++pc;
    +                break;
    +              }
    +              case Icode_REG_IND2: {
    +                indexReg = getIndex(iCode, pc);
    +                out.println(tname+" "+indexReg);
    +                pc += 2;
    +                break;
    +              }
    +              case Icode_REG_IND4: {
    +                indexReg = getInt(iCode, pc);
    +                out.println(tname+" "+indexReg);
    +                pc += 4;
    +                break;
    +              }
    +              case Icode_GETVAR1:
    +              case Icode_SETVAR1:
    +              case Icode_SETCONSTVAR1:
    +                indexReg = iCode[pc];
    +                out.println(tname+" "+indexReg);
    +                ++pc;
    +                break;
    +            }
    +            if (old_pc + icodeLength != pc) Kit.codeBug();
    +        }
    +
    +        int[] table = idata.itsExceptionTable;
    +        if (table != null) {
    +            out.println("Exception handlers: "
    +                         +table.length / EXCEPTION_SLOT_SIZE);
    +            for (int i = 0; i != table.length;
    +                 i += EXCEPTION_SLOT_SIZE)
    +            {
    +                int tryStart       = table[i + EXCEPTION_TRY_START_SLOT];
    +                int tryEnd         = table[i + EXCEPTION_TRY_END_SLOT];
    +                int handlerStart   = table[i + EXCEPTION_HANDLER_SLOT];
    +                int type           = table[i + EXCEPTION_TYPE_SLOT];
    +                int exceptionLocal = table[i + EXCEPTION_LOCAL_SLOT];
    +                int scopeLocal     = table[i + EXCEPTION_SCOPE_SLOT];
    +
    +                out.println(" tryStart="+tryStart+" tryEnd="+tryEnd
    +                            +" handlerStart="+handlerStart
    +                            +" type="+(type == 0 ? "catch" : "finally")
    +                            +" exceptionLocal="+exceptionLocal);
    +            }
    +        }
    +        out.flush();
    +    }
    +
    +    private static int bytecodeSpan(int bytecode)
    +    {
    +        switch (bytecode) {
    +            case Token.THROW :
    +            case Token.YIELD:
    +            case Icode_GENERATOR:
    +            case Icode_GENERATOR_END:
    +                // source line
    +                return 1 + 2;
    +
    +            case Icode_GOSUB :
    +            case Token.GOTO :
    +            case Token.IFEQ :
    +            case Token.IFNE :
    +            case Icode_IFEQ_POP :
    +            case Icode_LEAVEDQ :
    +                // target pc offset
    +                return 1 + 2;
    +
    +            case Icode_CALLSPECIAL :
    +                // call type
    +                // is new
    +                // line number
    +                return 1 + 1 + 1 + 2;
    +
    +            case Token.CATCH_SCOPE:
    +                // scope flag
    +                return 1 + 1;
    +
    +            case Icode_VAR_INC_DEC:
    +            case Icode_NAME_INC_DEC:
    +            case Icode_PROP_INC_DEC:
    +            case Icode_ELEM_INC_DEC:
    +            case Icode_REF_INC_DEC:
    +                // type of ++/--
    +                return 1 + 1;
    +
    +            case Icode_SHORTNUMBER :
    +                // short number
    +                return 1 + 2;
    +
    +            case Icode_INTNUMBER :
    +                // int number
    +                return 1 + 4;
    +
    +            case Icode_REG_IND1:
    +                // ubyte index
    +                return 1 + 1;
    +
    +            case Icode_REG_IND2:
    +                // ushort index
    +                return 1 + 2;
    +
    +            case Icode_REG_IND4:
    +                // int index
    +                return 1 + 4;
    +
    +            case Icode_REG_STR1:
    +                // ubyte string index
    +                return 1 + 1;
    +
    +            case Icode_REG_STR2:
    +                // ushort string index
    +                return 1 + 2;
    +
    +            case Icode_REG_STR4:
    +                // int string index
    +                return 1 + 4;
    +
    +            case Icode_GETVAR1:
    +            case Icode_SETVAR1:
    +            case Icode_SETCONSTVAR1:
    +                // byte var index
    +                return 1 + 1;
    +
    +            case Icode_LINE :
    +                // line number
    +                return 1 + 2;
    +        }
    +        if (!validBytecode(bytecode)) throw Kit.codeBug();
    +        return 1;
    +    }
    +
    +    static int[] getLineNumbers(InterpreterData data)
    +    {
    +        UintMap presentLines = new UintMap();
    +
    +        byte[] iCode = data.itsICode;
    +        int iCodeLength = iCode.length;
    +        for (int pc = 0; pc != iCodeLength;) {
    +            int bytecode = iCode[pc];
    +            int span = bytecodeSpan(bytecode);
    +            if (bytecode == Icode_LINE) {
    +                if (span != 3) Kit.codeBug();
    +                int line = getIndex(iCode, pc + 1);
    +                presentLines.put(line, 0);
    +            }
    +            pc += span;
    +        }
    +
    +        return presentLines.getKeys();
    +    }
    +
    +    public void captureStackInfo(RhinoException ex)
    +    {
    +        Context cx = Context.getCurrentContext();
    +        if (cx == null || cx.lastInterpreterFrame == null) {
    +            // No interpreter invocations
    +            ex.interpreterStackInfo = null;
    +            ex.interpreterLineData = null;
    +            return;
    +        }
    +        // has interpreter frame on the stack
    +        CallFrame[] array;
    +        if (cx.previousInterpreterInvocations == null
    +            || cx.previousInterpreterInvocations.size() == 0)
    +        {
    +            array = new CallFrame[1];
    +        } else {
    +            int previousCount = cx.previousInterpreterInvocations.size();
    +            if (cx.previousInterpreterInvocations.peek()
    +                == cx.lastInterpreterFrame)
    +            {
    +                // It can happen if exception was generated after
    +                // frame was pushed to cx.previousInterpreterInvocations
    +                // but before assignment to cx.lastInterpreterFrame.
    +                // In this case frames has to be ignored.
    +                --previousCount;
    +            }
    +            array = new CallFrame[previousCount + 1];
    +            cx.previousInterpreterInvocations.toArray(array);
    +        }
    +        array[array.length - 1]  = (CallFrame)cx.lastInterpreterFrame;
    +
    +        int interpreterFrameCount = 0;
    +        for (int i = 0; i != array.length; ++i) {
    +            interpreterFrameCount += 1 + array[i].frameIndex;
    +        }
    +
    +        int[] linePC = new int[interpreterFrameCount];
    +        // Fill linePC with pc positions from all interpreter frames.
    +        // Start from the most nested frame
    +        int linePCIndex = interpreterFrameCount;
    +        for (int i = array.length; i != 0;) {
    +            --i;
    +            CallFrame frame = array[i];
    +            while (frame != null) {
    +                --linePCIndex;
    +                linePC[linePCIndex] = frame.pcSourceLineStart;
    +                frame = frame.parentFrame;
    +            }
    +        }
    +        if (linePCIndex != 0) Kit.codeBug();
    +
    +        ex.interpreterStackInfo = array;
    +        ex.interpreterLineData = linePC;
    +    }
    +
    +    public String getSourcePositionFromStack(Context cx, int[] linep)
    +    {
    +        CallFrame frame = (CallFrame)cx.lastInterpreterFrame;
    +        InterpreterData idata = frame.idata;
    +        if (frame.pcSourceLineStart >= 0) {
    +            linep[0] = getIndex(idata.itsICode, frame.pcSourceLineStart);
    +        } else {
    +            linep[0] = 0;
    +        }
    +        return idata.itsSourceFile;
    +    }
    +
    +    public String getPatchedStack(RhinoException ex,
    +                                  String nativeStackTrace)
    +    {
    +        String tag = "org.mozilla.javascript.Interpreter.interpretLoop";
    +        StringBuffer sb = new StringBuffer(nativeStackTrace.length() + 1000);
    +        String lineSeparator = SecurityUtilities.getSystemProperty("line.separator");
    +
    +        CallFrame[] array = (CallFrame[])ex.interpreterStackInfo;
    +        int[] linePC = ex.interpreterLineData;
    +        int arrayIndex = array.length;
    +        int linePCIndex = linePC.length;
    +        int offset = 0;
    +        while (arrayIndex != 0) {
    +            --arrayIndex;
    +            int pos = nativeStackTrace.indexOf(tag, offset);
    +            if (pos < 0) {
    +                break;
    +            }
    +
    +            // Skip tag length
    +            pos += tag.length();
    +            // Skip until the end of line
    +            for (; pos != nativeStackTrace.length(); ++pos) {
    +                char c = nativeStackTrace.charAt(pos);
    +                if (c == '\n' || c == '\r') {
    +                    break;
    +                }
    +            }
    +            sb.append(nativeStackTrace.substring(offset, pos));
    +            offset = pos;
    +
    +            CallFrame frame = array[arrayIndex];
    +            while (frame != null) {
    +                if (linePCIndex == 0) Kit.codeBug();
    +                --linePCIndex;
    +                InterpreterData idata = frame.idata;
    +                sb.append(lineSeparator);
    +                sb.append("\tat script");
    +                if (idata.itsName != null && idata.itsName.length() != 0) {
    +                    sb.append('.');
    +                    sb.append(idata.itsName);
    +                }
    +                sb.append('(');
    +                sb.append(idata.itsSourceFile);
    +                int pc = linePC[linePCIndex];
    +                if (pc >= 0) {
    +                    // Include line info only if available
    +                    sb.append(':');
    +                    sb.append(getIndex(idata.itsICode, pc));
    +                }
    +                sb.append(')');
    +                frame = frame.parentFrame;
    +            }
    +        }
    +        sb.append(nativeStackTrace.substring(offset));
    +
    +        return sb.toString();
    +    }
    +
    +    public List getScriptStack(RhinoException ex)
    +    {
    +        if (ex.interpreterStackInfo == null) {
    +            return null;
    +        }
    +        
    +        List list = new ArrayList();
    +        String lineSeparator =
    +                SecurityUtilities.getSystemProperty("line.separator");
    +
    +        CallFrame[] array = (CallFrame[])ex.interpreterStackInfo;
    +        int[] linePC = ex.interpreterLineData;
    +        int arrayIndex = array.length;
    +        int linePCIndex = linePC.length;
    +        while (arrayIndex != 0) {
    +            --arrayIndex;
    +            StringBuffer sb = new StringBuffer();
    +            CallFrame frame = array[arrayIndex];
    +            while (frame != null) {
    +                if (linePCIndex == 0) Kit.codeBug();
    +                --linePCIndex;
    +                InterpreterData idata = frame.idata;
    +                sb.append("\tat ");
    +                sb.append(idata.itsSourceFile);
    +                int pc = linePC[linePCIndex];
    +                if (pc >= 0) {
    +                    // Include line info only if available
    +                    sb.append(':');
    +                    sb.append(getIndex(idata.itsICode, pc));
    +                }
    +                if (idata.itsName != null && idata.itsName.length() != 0) {
    +                    sb.append(" (");
    +                    sb.append(idata.itsName);
    +                    sb.append(')');
    +                }
    +                sb.append(lineSeparator);
    +                frame = frame.parentFrame;
    +            }
    +            list.add(sb.toString());
    +        }
    +        return list;
    +    }
    +        
    +    static String getEncodedSource(InterpreterData idata)
    +    {
    +        if (idata.encodedSource == null) {
    +            return null;
    +        }
    +        return idata.encodedSource.substring(idata.encodedSourceStart,
    +                                             idata.encodedSourceEnd);
    +    }
    +
    +    private static void initFunction(Context cx, Scriptable scope,
    +                                     InterpretedFunction parent, int index)
    +    {
    +        InterpretedFunction fn;
    +        fn = InterpretedFunction.createFunction(cx, scope, parent, index);
    +        ScriptRuntime.initFunction(cx, scope, fn, fn.idata.itsFunctionType,
    +                                   parent.idata.evalScriptFlag);
    +    }
    +
    +    static Object interpret(InterpretedFunction ifun,
    +                            Context cx, Scriptable scope,
    +                            Scriptable thisObj, Object[] args)
    +    {
    +        if (!ScriptRuntime.hasTopCall(cx)) Kit.codeBug();
    +
    +        if (cx.interpreterSecurityDomain != ifun.securityDomain) {
    +            Object savedDomain = cx.interpreterSecurityDomain;
    +            cx.interpreterSecurityDomain = ifun.securityDomain;
    +            try {
    +                return ifun.securityController.callWithDomain(
    +                    ifun.securityDomain, cx, ifun, scope, thisObj, args);
    +            } finally {
    +                cx.interpreterSecurityDomain = savedDomain;
    +            }
    +        }
    +
    +        CallFrame frame = new CallFrame();
    +        initFrame(cx, scope, thisObj, args, null, 0, args.length,
    +                  ifun, null, frame);
    +
    +        return interpretLoop(cx, frame, null);
    +    }
    +
    +    static class GeneratorState {
    +        GeneratorState(int operation, Object value) {
    +            this.operation = operation;
    +            this.value = value;
    +        }
    +        int operation;
    +        Object value;
    +        RuntimeException returnedException;
    +    }
    +
    +    public static Object resumeGenerator(Context cx,
    +                                         Scriptable scope,
    +                                         int operation,
    +                                         Object savedState,
    +                                         Object value)
    +    {
    +      CallFrame frame = (CallFrame) savedState;
    +      GeneratorState generatorState = new GeneratorState(operation, value);
    +      if (operation == NativeGenerator.GENERATOR_CLOSE) {
    +          try {
    +              return interpretLoop(cx, frame, generatorState);
    +          } catch (RuntimeException e) {
    +              // Only propagate exceptions other than closingException
    +              if (e != value)
    +                  throw e;
    +          }
    +          return Undefined.instance;
    +      }
    +      Object result = interpretLoop(cx, frame, generatorState);
    +      if (generatorState.returnedException != null)
    +          throw generatorState.returnedException;
    +      return result;
    +    }
    +
    +    public static Object restartContinuation(Continuation c, Context cx,
    +                                             Scriptable scope, Object[] args)
    +    {
    +        if (!ScriptRuntime.hasTopCall(cx)) {
    +            return ScriptRuntime.doTopCall(c, cx, scope, null, args);
    +        }
    +
    +        Object arg;
    +        if (args.length == 0) {
    +            arg = Undefined.instance;
    +        } else {
    +            arg = args[0];
    +        }
    +
    +        CallFrame capturedFrame = (CallFrame)c.getImplementation();
    +        if (capturedFrame == null) {
    +            // No frames to restart
    +            return arg;
    +        }
    +
    +        ContinuationJump cjump = new ContinuationJump(c, null);
    +
    +        cjump.result = arg;
    +        return interpretLoop(cx, null, cjump);
    +    }
    +
    +    private static Object interpretLoop(Context cx, CallFrame frame,
    +                                        Object throwable)
    +    {
    +        // throwable holds exception object to rethrow or catch
    +        // It is also used for continuation restart in which case
    +        // it holds ContinuationJump
    +
    +        final Object DBL_MRK = UniqueTag.DOUBLE_MARK;
    +        final Object undefined = Undefined.instance;
    +
    +        final boolean instructionCounting = (cx.instructionThreshold != 0);
    +        // arbitrary number to add to instructionCount when calling
    +        // other functions
    +        final int INVOCATION_COST = 100;
    +        // arbitrary exception cost for instruction counting
    +        final int EXCEPTION_COST = 100;
    +
    +        String stringReg = null;
    +        int indexReg = -1;
    +
    +        if (cx.lastInterpreterFrame != null) {
    +            // save the top frame from the previous interpretLoop
    +            // invocation on the stack
    +            if (cx.previousInterpreterInvocations == null) {
    +                cx.previousInterpreterInvocations = new ObjArray();
    +            }
    +            cx.previousInterpreterInvocations.push(cx.lastInterpreterFrame);
    +        }
    +
    +        // When restarting continuation throwable is not null and to jump
    +        // to the code that rewind continuation state indexReg should be set
    +        // to -1.
    +        // With the normal call throable == null and indexReg == -1 allows to
    +        // catch bugs with using indeReg to access array eleemnts before
    +        // initializing indexReg.
    +
    +        GeneratorState generatorState = null;
    +        if (throwable != null) {
    +            if (throwable instanceof GeneratorState) {
    +              generatorState = (GeneratorState) throwable;
    +
    +              // reestablish this call frame
    +              enterFrame(cx, frame, ScriptRuntime.emptyArgs, true);
    +              throwable = null;
    +            } else if (!(throwable instanceof ContinuationJump)) {
    +                // It should be continuation
    +                Kit.codeBug();
    +            }
    +        }
    +
    +        Object interpreterResult = null;
    +        double interpreterResultDbl = 0.0;
    +
    +        StateLoop: for (;;) {
    +            withoutExceptions: try {
    +
    +                if (throwable != null) {
    +                    // Need to return both 'frame' and 'throwable' from
    +                    // 'processThrowable', so just added a 'throwable'
    +                    // member in 'frame'.
    +                    frame = processThrowable(cx, throwable, frame, indexReg,
    +                                             instructionCounting);
    +                    throwable = frame.throwable;
    +                    frame.throwable = null;
    +                } else {
    +                    if (generatorState == null && frame.frozen) Kit.codeBug();
    +                }
    +
    +                // Use local variables for constant values in frame
    +                // for faster access
    +                Object[] stack = frame.stack;
    +                double[] sDbl = frame.sDbl;
    +                Object[] vars = frame.varSource.stack;
    +                double[] varDbls = frame.varSource.sDbl;
    +                int[] varAttributes = frame.varSource.stackAttributes;
    +                byte[] iCode = frame.idata.itsICode;
    +                String[] strings = frame.idata.itsStringTable;
    +
    +                // Use local for stackTop as well. Since execption handlers
    +                // can only exist at statement level where stack is empty,
    +                // it is necessary to save/restore stackTop only across
    +                // function calls and normal returns.
    +                int stackTop = frame.savedStackTop;
    +
    +                // Store new frame in cx which is used for error reporting etc.
    +                cx.lastInterpreterFrame = frame;
    +
    +                Loop: for (;;) {
    +
    +                    // Exception handler assumes that PC is already incremented
    +                    // pass the instruction start when it searches the
    +                    // exception handler
    +                    int op = iCode[frame.pc++];
    +                    jumplessRun: {
    +
    +    // Back indent to ease implementation reading
    +switch (op) {
    +    case Icode_GENERATOR: {
    +        if (!frame.frozen) {
    +          // First time encountering this opcode: create new generator
    +          // object and return
    +          frame.pc--; // we want to come back here when we resume
    +          CallFrame generatorFrame = captureFrameForGenerator(frame);
    +          generatorFrame.frozen = true;
    +          NativeGenerator generator = new NativeGenerator(frame.scope, 
    +              generatorFrame.fnOrScript, generatorFrame);
    +          frame.result = generator;
    +          break Loop;
    +        } else {
    +          // We are now resuming execution. Fall through to YIELD case.
    +        }
    +    }
    +    // fall through...
    +    case Token.YIELD: {
    +        if (!frame.frozen) {
    +            return freezeGenerator(cx, frame, stackTop, generatorState);
    +        } else {
    +            Object obj = thawGenerator(frame, stackTop, generatorState, op);
    +            if (obj != Scriptable.NOT_FOUND) {
    +                throwable = obj;
    +                break withoutExceptions;
    +            }
    +            continue Loop;
    +        }
    +    }
    +    case Icode_GENERATOR_END: {
    +      // throw StopIteration
    +      frame.frozen = true;
    +      int sourceLine = getIndex(iCode, frame.pc);
    +      generatorState.returnedException = new JavaScriptException(
    +          NativeIterator.getStopIterationObject(frame.scope),
    +          frame.idata.itsSourceFile, sourceLine);
    +      break Loop;
    +    }
    +    case Token.THROW: {
    +        Object value = stack[stackTop];
    +        if (value == DBL_MRK) value = ScriptRuntime.wrapNumber(sDbl[stackTop]);
    +        --stackTop;
    +
    +        int sourceLine = getIndex(iCode, frame.pc);
    +        throwable = new JavaScriptException(value,
    +                                            frame.idata.itsSourceFile,
    +                                            sourceLine);
    +        break withoutExceptions;
    +    }
    +    case Token.RETHROW: {
    +        indexReg += frame.localShift;
    +        throwable = stack[indexReg];
    +        break withoutExceptions;
    +    }
    +    case Token.GE :
    +    case Token.LE :
    +    case Token.GT :
    +    case Token.LT : {
    +        --stackTop;
    +        Object rhs = stack[stackTop + 1];
    +        Object lhs = stack[stackTop];
    +        boolean valBln;
    +      object_compare:
    +        {
    +          number_compare:
    +            {
    +                double rDbl, lDbl;
    +                if (rhs == DBL_MRK) {
    +                    rDbl = sDbl[stackTop + 1];
    +                    lDbl = stack_double(frame, stackTop);
    +                } else if (lhs == DBL_MRK) {
    +                    rDbl = ScriptRuntime.toNumber(rhs);
    +                    lDbl = sDbl[stackTop];
    +                } else {
    +                    break number_compare;
    +                }
    +                switch (op) {
    +                  case Token.GE:
    +                    valBln = (lDbl >= rDbl);
    +                    break object_compare;
    +                  case Token.LE:
    +                    valBln = (lDbl <= rDbl);
    +                    break object_compare;
    +                  case Token.GT:
    +                    valBln = (lDbl > rDbl);
    +                    break object_compare;
    +                  case Token.LT:
    +                    valBln = (lDbl < rDbl);
    +                    break object_compare;
    +                  default:
    +                    throw Kit.codeBug();
    +                }
    +            }
    +            switch (op) {
    +              case Token.GE:
    +                valBln = ScriptRuntime.cmp_LE(rhs, lhs);
    +                break;
    +              case Token.LE:
    +                valBln = ScriptRuntime.cmp_LE(lhs, rhs);
    +                break;
    +              case Token.GT:
    +                valBln = ScriptRuntime.cmp_LT(rhs, lhs);
    +                break;
    +              case Token.LT:
    +                valBln = ScriptRuntime.cmp_LT(lhs, rhs);
    +                break;
    +              default:
    +                throw Kit.codeBug();
    +            }
    +        }
    +        stack[stackTop] = ScriptRuntime.wrapBoolean(valBln);
    +        continue Loop;
    +    }
    +    case Token.IN :
    +    case Token.INSTANCEOF : {
    +        Object rhs = stack[stackTop];
    +        if (rhs == DBL_MRK) rhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
    +        --stackTop;
    +        Object lhs = stack[stackTop];
    +        if (lhs == DBL_MRK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
    +        boolean valBln;
    +        if (op == Token.IN) {
    +            valBln = ScriptRuntime.in(lhs, rhs, cx);
    +        } else {
    +            valBln = ScriptRuntime.instanceOf(lhs, rhs, cx);
    +        }
    +        stack[stackTop] = ScriptRuntime.wrapBoolean(valBln);
    +        continue Loop;
    +    }
    +    case Token.EQ :
    +    case Token.NE : {
    +        --stackTop;
    +        boolean valBln;
    +        Object rhs = stack[stackTop + 1];
    +        Object lhs = stack[stackTop];
    +        if (rhs == DBL_MRK) {
    +            if (lhs == DBL_MRK) {
    +                valBln = (sDbl[stackTop] == sDbl[stackTop + 1]);
    +            } else {
    +                valBln = ScriptRuntime.eqNumber(sDbl[stackTop + 1], lhs);
    +            }
    +        } else {
    +            if (lhs == DBL_MRK) {
    +                valBln = ScriptRuntime.eqNumber(sDbl[stackTop], rhs);
    +            } else {
    +                valBln = ScriptRuntime.eq(lhs, rhs);
    +            }
    +        }
    +        valBln ^= (op == Token.NE);
    +        stack[stackTop] = ScriptRuntime.wrapBoolean(valBln);
    +        continue Loop;
    +    }
    +    case Token.SHEQ :
    +    case Token.SHNE : {
    +        --stackTop;
    +        Object rhs = stack[stackTop + 1];
    +        Object lhs = stack[stackTop];
    +        boolean valBln;
    +      shallow_compare: {
    +            double rdbl, ldbl;
    +            if (rhs == DBL_MRK) {
    +                rdbl = sDbl[stackTop + 1];
    +                if (lhs == DBL_MRK) {
    +                    ldbl = sDbl[stackTop];
    +                } else if (lhs instanceof Number) {
    +                    ldbl = ((Number)lhs).doubleValue();
    +                } else {
    +                    valBln = false;
    +                    break shallow_compare;
    +                }
    +            } else if (lhs == DBL_MRK) {
    +                ldbl = sDbl[stackTop];
    +                if (rhs == DBL_MRK) {
    +                    rdbl = sDbl[stackTop + 1];
    +                } else if (rhs instanceof Number) {
    +                    rdbl = ((Number)rhs).doubleValue();
    +                } else {
    +                    valBln = false;
    +                    break shallow_compare;
    +                }
    +            } else {
    +                valBln = ScriptRuntime.shallowEq(lhs, rhs);
    +                break shallow_compare;
    +            }
    +            valBln = (ldbl == rdbl);
    +        }
    +        valBln ^= (op == Token.SHNE);
    +        stack[stackTop] = ScriptRuntime.wrapBoolean(valBln);
    +        continue Loop;
    +    }
    +    case Token.IFNE :
    +        if (stack_boolean(frame, stackTop--)) {
    +            frame.pc += 2;
    +            continue Loop;
    +        }
    +        break jumplessRun;
    +    case Token.IFEQ :
    +        if (!stack_boolean(frame, stackTop--)) {
    +            frame.pc += 2;
    +            continue Loop;
    +        }
    +        break jumplessRun;
    +    case Icode_IFEQ_POP :
    +        if (!stack_boolean(frame, stackTop--)) {
    +            frame.pc += 2;
    +            continue Loop;
    +        }
    +        stack[stackTop--] = null;
    +        break jumplessRun;
    +    case Token.GOTO :
    +        break jumplessRun;
    +    case Icode_GOSUB :
    +        ++stackTop;
    +        stack[stackTop] = DBL_MRK;
    +        sDbl[stackTop] = frame.pc + 2;
    +        break jumplessRun;
    +    case Icode_STARTSUB :
    +        if (stackTop == frame.emptyStackTop + 1) {
    +            // Call from Icode_GOSUB: store return PC address in the local
    +            indexReg += frame.localShift;
    +            stack[indexReg] = stack[stackTop];
    +            sDbl[indexReg] = sDbl[stackTop];
    +            --stackTop;
    +        } else {
    +            // Call from exception handler: exception object is already stored
    +            // in the local
    +            if (stackTop != frame.emptyStackTop) Kit.codeBug();
    +        }
    +        continue Loop;
    +    case Icode_RETSUB : {
    +        // indexReg: local to store return address
    +        if (instructionCounting) {
    +            addInstructionCount(cx, frame, 0);
    +        }
    +        indexReg += frame.localShift;
    +        Object value = stack[indexReg];
    +        if (value != DBL_MRK) {
    +            // Invocation from exception handler, restore object to rethrow
    +            throwable = value;
    +            break withoutExceptions;
    +        }
    +        // Normal return from GOSUB
    +        frame.pc = (int)sDbl[indexReg];
    +        if (instructionCounting) {
    +            frame.pcPrevBranch = frame.pc;
    +        }
    +        continue Loop;
    +    }
    +    case Icode_POP :
    +        stack[stackTop] = null;
    +        stackTop--;
    +        continue Loop;
    +    case Icode_POP_RESULT :
    +        frame.result = stack[stackTop];
    +        frame.resultDbl = sDbl[stackTop];
    +        stack[stackTop] = null;
    +        --stackTop;
    +        continue Loop;
    +    case Icode_DUP :
    +        stack[stackTop + 1] = stack[stackTop];
    +        sDbl[stackTop + 1] = sDbl[stackTop];
    +        stackTop++;
    +        continue Loop;
    +    case Icode_DUP2 :
    +        stack[stackTop + 1] = stack[stackTop - 1];
    +        sDbl[stackTop + 1] = sDbl[stackTop - 1];
    +        stack[stackTop + 2] = stack[stackTop];
    +        sDbl[stackTop + 2] = sDbl[stackTop];
    +        stackTop += 2;
    +        continue Loop;
    +    case Icode_SWAP : {
    +        Object o = stack[stackTop];
    +        stack[stackTop] = stack[stackTop - 1];
    +        stack[stackTop - 1] = o;
    +        double d = sDbl[stackTop];
    +        sDbl[stackTop] = sDbl[stackTop - 1];
    +        sDbl[stackTop - 1] = d;
    +        continue Loop;
    +    }
    +    case Token.RETURN :
    +        frame.result = stack[stackTop];
    +        frame.resultDbl = sDbl[stackTop];
    +        --stackTop;
    +        break Loop;
    +    case Token.RETURN_RESULT :
    +        break Loop;
    +    case Icode_RETUNDEF :
    +        frame.result = undefined;
    +        break Loop;
    +    case Token.BITNOT : {
    +        int rIntValue = stack_int32(frame, stackTop);
    +        stack[stackTop] = DBL_MRK;
    +        sDbl[stackTop] = ~rIntValue;
    +        continue Loop;
    +    }
    +    case Token.BITAND :
    +    case Token.BITOR :
    +    case Token.BITXOR :
    +    case Token.LSH :
    +    case Token.RSH : {
    +        int lIntValue = stack_int32(frame, stackTop-1);
    +        int rIntValue = stack_int32(frame, stackTop);
    +        stack[--stackTop] = DBL_MRK;
    +        switch (op) {
    +          case Token.BITAND:
    +            lIntValue &= rIntValue;
    +            break;
    +          case Token.BITOR:
    +            lIntValue |= rIntValue;
    +            break;
    +          case Token.BITXOR:
    +            lIntValue ^= rIntValue;
    +            break;
    +          case Token.LSH:
    +            lIntValue <<= rIntValue;
    +            break;
    +          case Token.RSH:
    +            lIntValue >>= rIntValue;
    +            break;
    +        }
    +        sDbl[stackTop] = lIntValue;
    +        continue Loop;
    +    }
    +    case Token.URSH : {
    +        double lDbl = stack_double(frame, stackTop-1);
    +        int rIntValue = stack_int32(frame, stackTop) & 0x1F;
    +        stack[--stackTop] = DBL_MRK;
    +        sDbl[stackTop] = ScriptRuntime.toUint32(lDbl) >>> rIntValue;
    +        continue Loop;
    +    }
    +    case Token.NEG :
    +    case Token.POS : {
    +        double rDbl = stack_double(frame, stackTop);
    +        stack[stackTop] = DBL_MRK;
    +        if (op == Token.NEG) {
    +            rDbl = -rDbl;
    +        }
    +        sDbl[stackTop] = rDbl;
    +        continue Loop;
    +    }
    +    case Token.ADD :
    +        --stackTop;
    +        do_add(stack, sDbl, stackTop, cx);
    +        continue Loop;
    +    case Token.SUB :
    +    case Token.MUL :
    +    case Token.DIV :
    +    case Token.MOD : {
    +        double rDbl = stack_double(frame, stackTop);
    +        --stackTop;
    +        double lDbl = stack_double(frame, stackTop);
    +        stack[stackTop] = DBL_MRK;
    +        switch (op) {
    +          case Token.SUB:
    +            lDbl -= rDbl;
    +            break;
    +          case Token.MUL:
    +            lDbl *= rDbl;
    +            break;
    +          case Token.DIV:
    +            lDbl /= rDbl;
    +            break;
    +          case Token.MOD:
    +            lDbl %= rDbl;
    +            break;
    +        }
    +        sDbl[stackTop] = lDbl;
    +        continue Loop;
    +    }
    +    case Token.NOT :
    +        stack[stackTop] = ScriptRuntime.wrapBoolean(
    +                              !stack_boolean(frame, stackTop));
    +        continue Loop;
    +    case Token.BINDNAME :
    +        stack[++stackTop] = ScriptRuntime.bind(cx, frame.scope, stringReg);
    +        continue Loop;
    +    case Token.SETNAME : {
    +        Object rhs = stack[stackTop];
    +        if (rhs == DBL_MRK) rhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
    +        --stackTop;
    +        Scriptable lhs = (Scriptable)stack[stackTop];
    +        stack[stackTop] = ScriptRuntime.setName(lhs, rhs, cx,
    +                                                frame.scope, stringReg);
    +        continue Loop;
    +    }
    +    case Icode_SETCONST: {
    +        Object rhs = stack[stackTop];
    +        if (rhs == DBL_MRK) rhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
    +        --stackTop;
    +        Scriptable lhs = (Scriptable)stack[stackTop];
    +        stack[stackTop] = ScriptRuntime.setConst(lhs, rhs, cx, stringReg);
    +        continue Loop;
    +    }
    +    case Token.DELPROP : {
    +        Object rhs = stack[stackTop];
    +        if (rhs == DBL_MRK) rhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
    +        --stackTop;
    +        Object lhs = stack[stackTop];
    +        if (lhs == DBL_MRK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
    +        stack[stackTop] = ScriptRuntime.delete(lhs, rhs, cx);
    +        continue Loop;
    +    }
    +    case Token.GETPROPNOWARN : {
    +        Object lhs = stack[stackTop];
    +        if (lhs == DBL_MRK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
    +        stack[stackTop] = ScriptRuntime.getObjectPropNoWarn(lhs, stringReg, cx);
    +        continue Loop;
    +    }
    +    case Token.GETPROP : {
    +        Object lhs = stack[stackTop];
    +        if (lhs == DBL_MRK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
    +        stack[stackTop] = ScriptRuntime.getObjectProp(lhs, stringReg, cx);
    +        continue Loop;
    +    }
    +    case Token.SETPROP : {
    +        Object rhs = stack[stackTop];
    +        if (rhs == DBL_MRK) rhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
    +        --stackTop;
    +        Object lhs = stack[stackTop];
    +        if (lhs == DBL_MRK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
    +        stack[stackTop] = ScriptRuntime.setObjectProp(lhs, stringReg, rhs,
    +                                                      cx);
    +        continue Loop;
    +    }
    +    case Icode_PROP_INC_DEC : {
    +        Object lhs = stack[stackTop];
    +        if (lhs == DBL_MRK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
    +        stack[stackTop] = ScriptRuntime.propIncrDecr(lhs, stringReg,
    +                                                     cx, iCode[frame.pc]);
    +        ++frame.pc;
    +        continue Loop;
    +    }
    +    case Token.GETELEM : {
    +        --stackTop;
    +        Object lhs = stack[stackTop];
    +        if (lhs == DBL_MRK) {
    +            lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
    +        }
    +        Object value;
    +        Object id = stack[stackTop + 1];
    +        if (id != DBL_MRK) {
    +            value = ScriptRuntime.getObjectElem(lhs, id, cx);
    +        } else {
    +            double d = sDbl[stackTop + 1];
    +            value = ScriptRuntime.getObjectIndex(lhs, d, cx);
    +        }
    +        stack[stackTop] = value;
    +        continue Loop;
    +    }
    +    case Token.SETELEM : {
    +        stackTop -= 2;
    +        Object rhs = stack[stackTop + 2];
    +        if (rhs == DBL_MRK) {
    +            rhs = ScriptRuntime.wrapNumber(sDbl[stackTop + 2]);
    +        }
    +        Object lhs = stack[stackTop];
    +        if (lhs == DBL_MRK) {
    +            lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
    +        }
    +        Object value;
    +        Object id = stack[stackTop + 1];
    +        if (id != DBL_MRK) {
    +            value = ScriptRuntime.setObjectElem(lhs, id, rhs, cx);
    +        } else {
    +            double d = sDbl[stackTop + 1];
    +            value = ScriptRuntime.setObjectIndex(lhs, d, rhs, cx);
    +        }
    +        stack[stackTop] = value;
    +        continue Loop;
    +    }
    +    case Icode_ELEM_INC_DEC: {
    +        Object rhs = stack[stackTop];
    +        if (rhs == DBL_MRK) rhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
    +        --stackTop;
    +        Object lhs = stack[stackTop];
    +        if (lhs == DBL_MRK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
    +        stack[stackTop] = ScriptRuntime.elemIncrDecr(lhs, rhs, cx,
    +                                                     iCode[frame.pc]);
    +        ++frame.pc;
    +        continue Loop;
    +    }
    +    case Token.GET_REF : {
    +        Ref ref = (Ref)stack[stackTop];
    +        stack[stackTop] = ScriptRuntime.refGet(ref, cx);
    +        continue Loop;
    +    }
    +    case Token.SET_REF : {
    +        Object value = stack[stackTop];
    +        if (value == DBL_MRK) value = ScriptRuntime.wrapNumber(sDbl[stackTop]);
    +        --stackTop;
    +        Ref ref = (Ref)stack[stackTop];
    +        stack[stackTop] = ScriptRuntime.refSet(ref, value, cx);
    +        continue Loop;
    +    }
    +    case Token.DEL_REF : {
    +        Ref ref = (Ref)stack[stackTop];
    +        stack[stackTop] = ScriptRuntime.refDel(ref, cx);
    +        continue Loop;
    +    }
    +    case Icode_REF_INC_DEC : {
    +        Ref ref = (Ref)stack[stackTop];
    +        stack[stackTop] = ScriptRuntime.refIncrDecr(ref, cx, iCode[frame.pc]);
    +        ++frame.pc;
    +        continue Loop;
    +    }
    +    case Token.LOCAL_LOAD :
    +        ++stackTop;
    +        indexReg += frame.localShift;
    +        stack[stackTop] = stack[indexReg];
    +        sDbl[stackTop] = sDbl[indexReg];
    +        continue Loop;
    +    case Icode_LOCAL_CLEAR :
    +        indexReg += frame.localShift;
    +        stack[indexReg] = null;
    +        continue Loop;
    +    case Icode_NAME_AND_THIS :
    +        // stringReg: name
    +        ++stackTop;
    +        stack[stackTop] = ScriptRuntime.getNameFunctionAndThis(stringReg,
    +                                                               cx, frame.scope);
    +        ++stackTop;
    +        stack[stackTop] = ScriptRuntime.lastStoredScriptable(cx);
    +        continue Loop;
    +    case Icode_PROP_AND_THIS: {
    +        Object obj = stack[stackTop];
    +        if (obj == DBL_MRK) obj = ScriptRuntime.wrapNumber(sDbl[stackTop]);
    +        // stringReg: property
    +        stack[stackTop] = ScriptRuntime.getPropFunctionAndThis(obj, stringReg,
    +                                                               cx);
    +        ++stackTop;
    +        stack[stackTop] = ScriptRuntime.lastStoredScriptable(cx);
    +        continue Loop;
    +    }
    +    case Icode_ELEM_AND_THIS: {
    +        Object obj = stack[stackTop - 1];
    +        if (obj == DBL_MRK) obj = ScriptRuntime.wrapNumber(sDbl[stackTop - 1]);
    +        Object id = stack[stackTop];
    +        if (id == DBL_MRK) id = ScriptRuntime.wrapNumber(sDbl[stackTop]);
    +        stack[stackTop - 1] = ScriptRuntime.getElemFunctionAndThis(obj, id, cx);
    +        stack[stackTop] = ScriptRuntime.lastStoredScriptable(cx);
    +        continue Loop;
    +    }
    +    case Icode_VALUE_AND_THIS : {
    +        Object value = stack[stackTop];
    +        if (value == DBL_MRK) value = ScriptRuntime.wrapNumber(sDbl[stackTop]);
    +        stack[stackTop] = ScriptRuntime.getValueFunctionAndThis(value, cx);
    +        ++stackTop;
    +        stack[stackTop] = ScriptRuntime.lastStoredScriptable(cx);
    +        continue Loop;
    +    }
    +    case Icode_CALLSPECIAL : {
    +        if (instructionCounting) {
    +            cx.instructionCount += INVOCATION_COST;
    +        }
    +        int callType = iCode[frame.pc] & 0xFF;
    +        boolean isNew =  (iCode[frame.pc + 1] != 0);
    +        int sourceLine = getIndex(iCode, frame.pc + 2);
    +
    +        // indexReg: number of arguments
    +        if (isNew) {
    +            // stack change: function arg0 .. argN -> newResult
    +            stackTop -= indexReg;
    +
    +            Object function = stack[stackTop];
    +            if (function == DBL_MRK)
    +                function = ScriptRuntime.wrapNumber(sDbl[stackTop]);
    +            Object[] outArgs = getArgsArray(
    +                                   stack, sDbl, stackTop + 1, indexReg);
    +            stack[stackTop] = ScriptRuntime.newSpecial(
    +                                  cx, function, outArgs, frame.scope, callType);
    +        } else {
    +            // stack change: function thisObj arg0 .. argN -> result
    +            stackTop -= 1 + indexReg;
    +
    +            // Call code generation ensure that stack here
    +            // is ... Callable Scriptable
    +            Scriptable functionThis = (Scriptable)stack[stackTop + 1];
    +            Callable function = (Callable)stack[stackTop];
    +            Object[] outArgs = getArgsArray(
    +                                   stack, sDbl, stackTop + 2, indexReg);
    +            stack[stackTop] = ScriptRuntime.callSpecial(
    +                                  cx, function, functionThis, outArgs,
    +                                  frame.scope, frame.thisObj, callType,
    +                                  frame.idata.itsSourceFile, sourceLine);
    +        }
    +        frame.pc += 4;
    +        continue Loop;
    +    }
    +    case Token.CALL :
    +    case Icode_TAIL_CALL :
    +    case Token.REF_CALL : {
    +        if (instructionCounting) {
    +            cx.instructionCount += INVOCATION_COST;
    +        }
    +        // stack change: function thisObj arg0 .. argN -> result
    +        // indexReg: number of arguments
    +        stackTop -= 1 + indexReg;
    +
    +        // CALL generation ensures that fun and funThisObj
    +        // are already Scriptable and Callable objects respectively
    +        Callable fun = (Callable)stack[stackTop];
    +        Scriptable funThisObj = (Scriptable)stack[stackTop + 1];
    +        if (op == Token.REF_CALL) {
    +            Object[] outArgs = getArgsArray(stack, sDbl, stackTop + 2,
    +                                            indexReg);
    +            stack[stackTop] = ScriptRuntime.callRef(fun, funThisObj,
    +                                                    outArgs, cx);
    +            continue Loop;
    +        }
    +        Scriptable calleeScope = frame.scope;
    +        if (frame.useActivation) {
    +            calleeScope = ScriptableObject.getTopLevelScope(frame.scope);
    +        }
    +        if (fun instanceof InterpretedFunction) {
    +            InterpretedFunction ifun = (InterpretedFunction)fun;
    +            if (frame.fnOrScript.securityDomain == ifun.securityDomain) {
    +                CallFrame callParentFrame = frame;
    +                CallFrame calleeFrame = new CallFrame();
    +                if (op == Icode_TAIL_CALL) {
    +                    // In principle tail call can re-use the current
    +                    // frame and its stack arrays but it is hard to
    +                    // do properly. Any exceptions that can legally
    +                    // happen during frame re-initialization including
    +                    // StackOverflowException during innocent looking
    +                    // System.arraycopy may leave the current frame
    +                    // data corrupted leading to undefined behaviour
    +                    // in the catch code bellow that unwinds JS stack
    +                    // on exceptions. Then there is issue about frame release
    +                    // end exceptions there.
    +                    // To avoid frame allocation a released frame
    +                    // can be cached for re-use which would also benefit
    +                    // non-tail calls but it is not clear that this caching
    +                    // would gain in performance due to potentially
    +                    // bad interaction with GC.
    +                    callParentFrame = frame.parentFrame;
    +                    // Release the current frame. See Bug #344501 to see why
    +                    // it is being done here.
    +                    exitFrame(cx, frame, null);
    +                }
    +                initFrame(cx, calleeScope, funThisObj, stack, sDbl,
    +                          stackTop + 2, indexReg, ifun, callParentFrame,
    +                          calleeFrame);
    +                if (op != Icode_TAIL_CALL) {
    +                    frame.savedStackTop = stackTop;
    +                    frame.savedCallOp = op;
    +                }
    +                frame = calleeFrame;
    +                continue StateLoop;
    +            }
    +        }
    +
    +        if (fun instanceof Continuation) {
    +            // Jump to the captured continuation
    +            ContinuationJump cjump;
    +            cjump = new ContinuationJump((Continuation)fun, frame);
    +
    +            // continuation result is the first argument if any
    +            // of contination call
    +            if (indexReg == 0) {
    +                cjump.result = undefined;
    +            } else {
    +                cjump.result = stack[stackTop + 2];
    +                cjump.resultDbl = sDbl[stackTop + 2];
    +            }
    +
    +            // Start the real unwind job
    +            throwable = cjump;
    +            break withoutExceptions;
    +        }
    +
    +        if (fun instanceof IdFunctionObject) {
    +            IdFunctionObject ifun = (IdFunctionObject)fun;
    +            if (Continuation.isContinuationConstructor(ifun)) {
    +                captureContinuation(cx, frame, stackTop);
    +                continue Loop;
    +            }
    +            // Bug 405654 -- make best effort to keep Function.apply and 
    +            // Function.call within this interpreter loop invocation
    +            if(BaseFunction.isApplyOrCall(ifun)) {
    +                Callable applyCallable = ScriptRuntime.getCallable(funThisObj);
    +                if(applyCallable instanceof InterpretedFunction) {
    +                    InterpretedFunction iApplyCallable = (InterpretedFunction)applyCallable;
    +                    if(frame.fnOrScript.securityDomain == iApplyCallable.securityDomain) {
    +                        frame = initFrameForApplyOrCall(cx, frame, indexReg,
    +                                stack, sDbl, stackTop, op, calleeScope, ifun,
    +                                iApplyCallable);
    +                        continue StateLoop;
    +                    }
    +                }
    +            }
    +        }
    +
    +        stack[stackTop] = fun.call(cx, calleeScope, funThisObj, 
    +                getArgsArray(stack, sDbl, stackTop + 2, indexReg));
    +
    +        continue Loop;
    +    }
    +    case Token.NEW : {
    +        if (instructionCounting) {
    +            cx.instructionCount += INVOCATION_COST;
    +        }
    +        // stack change: function arg0 .. argN -> newResult
    +        // indexReg: number of arguments
    +        stackTop -= indexReg;
    +
    +        Object lhs = stack[stackTop];
    +        if (lhs instanceof InterpretedFunction) {
    +            InterpretedFunction f = (InterpretedFunction)lhs;
    +            if (frame.fnOrScript.securityDomain == f.securityDomain) {
    +                Scriptable newInstance = f.createObject(cx, frame.scope);
    +                CallFrame calleeFrame = new CallFrame();
    +                initFrame(cx, frame.scope, newInstance, stack, sDbl,
    +                          stackTop + 1, indexReg, f, frame,
    +                          calleeFrame);
    +
    +                stack[stackTop] = newInstance;
    +                frame.savedStackTop = stackTop;
    +                frame.savedCallOp = op;
    +                frame = calleeFrame;
    +                continue StateLoop;
    +            }
    +        }
    +        if (!(lhs instanceof Function)) {
    +            if (lhs == DBL_MRK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
    +            throw ScriptRuntime.notFunctionError(lhs);
    +        }
    +        Function fun = (Function)lhs;
    +
    +        if (fun instanceof IdFunctionObject) {
    +            IdFunctionObject ifun = (IdFunctionObject)fun;
    +            if (Continuation.isContinuationConstructor(ifun)) {
    +                captureContinuation(cx, frame, stackTop);
    +                continue Loop;
    +            }
    +        }
    +
    +        Object[] outArgs = getArgsArray(stack, sDbl, stackTop + 1, indexReg);
    +        stack[stackTop] = fun.construct(cx, frame.scope, outArgs);
    +        continue Loop;
    +    }
    +    case Token.TYPEOF : {
    +        Object lhs = stack[stackTop];
    +        if (lhs == DBL_MRK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
    +        stack[stackTop] = ScriptRuntime.typeof(lhs);
    +        continue Loop;
    +    }
    +    case Icode_TYPEOFNAME :
    +        stack[++stackTop] = ScriptRuntime.typeofName(frame.scope, stringReg);
    +        continue Loop;
    +    case Token.STRING :
    +        stack[++stackTop] = stringReg;
    +        continue Loop;
    +    case Icode_SHORTNUMBER :
    +        ++stackTop;
    +        stack[stackTop] = DBL_MRK;
    +        sDbl[stackTop] = getShort(iCode, frame.pc);
    +        frame.pc += 2;
    +        continue Loop;
    +    case Icode_INTNUMBER :
    +        ++stackTop;
    +        stack[stackTop] = DBL_MRK;
    +        sDbl[stackTop] = getInt(iCode, frame.pc);
    +        frame.pc += 4;
    +        continue Loop;
    +    case Token.NUMBER :
    +        ++stackTop;
    +        stack[stackTop] = DBL_MRK;
    +        sDbl[stackTop] = frame.idata.itsDoubleTable[indexReg];
    +        continue Loop;
    +    case Token.NAME :
    +        stack[++stackTop] = ScriptRuntime.name(cx, frame.scope, stringReg);
    +        continue Loop;
    +    case Icode_NAME_INC_DEC :
    +        stack[++stackTop] = ScriptRuntime.nameIncrDecr(frame.scope, stringReg,
    +                                                       cx, iCode[frame.pc]);
    +        ++frame.pc;
    +        continue Loop;
    +    case Icode_SETCONSTVAR1:
    +        indexReg = iCode[frame.pc++];
    +        // fallthrough
    +    case Token.SETCONSTVAR :
    +        if (!frame.useActivation) {
    +            if ((varAttributes[indexReg] & ScriptableObject.READONLY) == 0) {
    +                throw Context.reportRuntimeError1("msg.var.redecl",
    +                                                  frame.idata.argNames[indexReg]);
    +            }
    +            if ((varAttributes[indexReg] & ScriptableObject.UNINITIALIZED_CONST)
    +                != 0)
    +            {
    +                vars[indexReg] = stack[stackTop];
    +                varAttributes[indexReg] &= ~ScriptableObject.UNINITIALIZED_CONST;
    +                varDbls[indexReg] = sDbl[stackTop];
    +            }
    +        } else {
    +            Object val = stack[stackTop];
    +            if (val == DBL_MRK) val = ScriptRuntime.wrapNumber(sDbl[stackTop]);
    +            stringReg = frame.idata.argNames[indexReg];
    +            if (frame.scope instanceof ConstProperties) {
    +                ConstProperties cp = (ConstProperties)frame.scope;
    +                cp.putConst(stringReg, frame.scope, val);
    +            } else
    +                throw Kit.codeBug();
    +        }
    +        continue Loop;
    +    case Icode_SETVAR1:
    +        indexReg = iCode[frame.pc++];
    +        // fallthrough
    +    case Token.SETVAR :
    +        if (!frame.useActivation) {
    +            if ((varAttributes[indexReg] & ScriptableObject.READONLY) == 0) {
    +                vars[indexReg] = stack[stackTop];
    +                varDbls[indexReg] = sDbl[stackTop];
    +            }
    +        } else {
    +            Object val = stack[stackTop];
    +            if (val == DBL_MRK) val = ScriptRuntime.wrapNumber(sDbl[stackTop]);
    +            stringReg = frame.idata.argNames[indexReg];
    +            frame.scope.put(stringReg, frame.scope, val);
    +        }
    +        continue Loop;
    +    case Icode_GETVAR1:
    +        indexReg = iCode[frame.pc++];
    +        // fallthrough
    +    case Token.GETVAR :
    +        ++stackTop;
    +        if (!frame.useActivation) {
    +            stack[stackTop] = vars[indexReg];
    +            sDbl[stackTop] = varDbls[indexReg];
    +        } else {
    +            stringReg = frame.idata.argNames[indexReg];
    +            stack[stackTop] = frame.scope.get(stringReg, frame.scope);
    +        }
    +        continue Loop;
    +    case Icode_VAR_INC_DEC : {
    +        // indexReg : varindex
    +        ++stackTop;
    +        int incrDecrMask = iCode[frame.pc];
    +        if (!frame.useActivation) {
    +            stack[stackTop] = DBL_MRK;
    +            Object varValue = vars[indexReg];
    +            double d;
    +            if (varValue == DBL_MRK) {
    +                d = varDbls[indexReg];
    +            } else {
    +                d = ScriptRuntime.toNumber(varValue);
    +                vars[indexReg] = DBL_MRK;
    +            }
    +            double d2 = ((incrDecrMask & Node.DECR_FLAG) == 0)
    +                        ? d + 1.0 : d - 1.0;
    +            varDbls[indexReg] = d2;
    +            sDbl[stackTop] = ((incrDecrMask & Node.POST_FLAG) == 0) ? d2 : d;
    +        } else {
    +            String varName = frame.idata.argNames[indexReg];
    +            stack[stackTop] = ScriptRuntime.nameIncrDecr(frame.scope, varName,
    +                                                         cx, incrDecrMask);
    +        }
    +        ++frame.pc;
    +        continue Loop;
    +    }
    +    case Icode_ZERO :
    +        ++stackTop;
    +        stack[stackTop] = DBL_MRK;
    +        sDbl[stackTop] = 0;
    +        continue Loop;
    +    case Icode_ONE :
    +        ++stackTop;
    +        stack[stackTop] = DBL_MRK;
    +        sDbl[stackTop] = 1;
    +        continue Loop;
    +    case Token.NULL :
    +        stack[++stackTop] = null;
    +        continue Loop;
    +    case Token.THIS :
    +        stack[++stackTop] = frame.thisObj;
    +        continue Loop;
    +    case Token.THISFN :
    +        stack[++stackTop] = frame.fnOrScript;
    +        continue Loop;
    +    case Token.FALSE :
    +        stack[++stackTop] = Boolean.FALSE;
    +        continue Loop;
    +    case Token.TRUE :
    +        stack[++stackTop] = Boolean.TRUE;
    +        continue Loop;
    +    case Icode_UNDEF :
    +        stack[++stackTop] = undefined;
    +        continue Loop;
    +    case Token.ENTERWITH : {
    +        Object lhs = stack[stackTop];
    +        if (lhs == DBL_MRK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
    +        --stackTop;
    +        frame.scope = ScriptRuntime.enterWith(lhs, cx, frame.scope);
    +        continue Loop;
    +    }
    +    case Token.LEAVEWITH :
    +        frame.scope = ScriptRuntime.leaveWith(frame.scope);
    +        continue Loop;
    +    case Token.CATCH_SCOPE : {
    +        // stack top: exception object
    +        // stringReg: name of exception variable
    +        // indexReg: local for exception scope
    +        --stackTop;
    +        indexReg += frame.localShift;
    +
    +        boolean afterFirstScope =  (frame.idata.itsICode[frame.pc] != 0);
    +        Throwable caughtException = (Throwable)stack[stackTop + 1];
    +        Scriptable lastCatchScope;
    +        if (!afterFirstScope) {
    +            lastCatchScope = null;
    +        } else {
    +            lastCatchScope = (Scriptable)stack[indexReg];
    +        }
    +        stack[indexReg] = ScriptRuntime.newCatchScope(caughtException,
    +                                                      lastCatchScope, stringReg,
    +                                                      cx, frame.scope);
    +        ++frame.pc;
    +        continue Loop;
    +    }
    +    case Token.ENUM_INIT_KEYS :
    +    case Token.ENUM_INIT_VALUES :
    +    case Token.ENUM_INIT_ARRAY : {
    +        Object lhs = stack[stackTop];
    +        if (lhs == DBL_MRK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
    +        --stackTop;
    +        indexReg += frame.localShift;
    +        int enumType = op == Token.ENUM_INIT_KEYS 
    +                         ? ScriptRuntime.ENUMERATE_KEYS :
    +                       op == Token.ENUM_INIT_VALUES 
    +                         ? ScriptRuntime.ENUMERATE_VALUES :
    +                       ScriptRuntime.ENUMERATE_ARRAY;
    +        stack[indexReg] = ScriptRuntime.enumInit(lhs, cx, enumType);
    +        continue Loop;
    +    }
    +    case Token.ENUM_NEXT :
    +    case Token.ENUM_ID : {
    +        indexReg += frame.localShift;
    +        Object val = stack[indexReg];
    +        ++stackTop;
    +        stack[stackTop] = (op == Token.ENUM_NEXT)
    +                          ? (Object)ScriptRuntime.enumNext(val)
    +                          : (Object)ScriptRuntime.enumId(val, cx);
    +        continue Loop;
    +    }
    +    case Token.REF_SPECIAL : {
    +        //stringReg: name of special property
    +        Object obj = stack[stackTop];
    +        if (obj == DBL_MRK) obj = ScriptRuntime.wrapNumber(sDbl[stackTop]);
    +        stack[stackTop] = ScriptRuntime.specialRef(obj, stringReg, cx);
    +        continue Loop;
    +    }
    +    case Token.REF_MEMBER: {
    +        //indexReg: flags
    +        Object elem = stack[stackTop];
    +        if (elem == DBL_MRK) elem = ScriptRuntime.wrapNumber(sDbl[stackTop]);
    +        --stackTop;
    +        Object obj = stack[stackTop];
    +        if (obj == DBL_MRK) obj = ScriptRuntime.wrapNumber(sDbl[stackTop]);
    +        stack[stackTop] = ScriptRuntime.memberRef(obj, elem, cx, indexReg);
    +        continue Loop;
    +    }
    +    case Token.REF_NS_MEMBER: {
    +        //indexReg: flags
    +        Object elem = stack[stackTop];
    +        if (elem == DBL_MRK) elem = ScriptRuntime.wrapNumber(sDbl[stackTop]);
    +        --stackTop;
    +        Object ns = stack[stackTop];
    +        if (ns == DBL_MRK) ns = ScriptRuntime.wrapNumber(sDbl[stackTop]);
    +        --stackTop;
    +        Object obj = stack[stackTop];
    +        if (obj == DBL_MRK) obj = ScriptRuntime.wrapNumber(sDbl[stackTop]);
    +        stack[stackTop] = ScriptRuntime.memberRef(obj, ns, elem, cx, indexReg);
    +        continue Loop;
    +    }
    +    case Token.REF_NAME: {
    +        //indexReg: flags
    +        Object name = stack[stackTop];
    +        if (name == DBL_MRK) name = ScriptRuntime.wrapNumber(sDbl[stackTop]);
    +        stack[stackTop] = ScriptRuntime.nameRef(name, cx, frame.scope,
    +                                                indexReg);
    +        continue Loop;
    +    }
    +    case Token.REF_NS_NAME: {
    +        //indexReg: flags
    +        Object name = stack[stackTop];
    +        if (name == DBL_MRK) name = ScriptRuntime.wrapNumber(sDbl[stackTop]);
    +        --stackTop;
    +        Object ns = stack[stackTop];
    +        if (ns == DBL_MRK) ns = ScriptRuntime.wrapNumber(sDbl[stackTop]);
    +        stack[stackTop] = ScriptRuntime.nameRef(ns, name, cx, frame.scope,
    +                                                indexReg);
    +        continue Loop;
    +    }
    +    case Icode_SCOPE_LOAD :
    +        indexReg += frame.localShift;
    +        frame.scope = (Scriptable)stack[indexReg];
    +        continue Loop;
    +    case Icode_SCOPE_SAVE :
    +        indexReg += frame.localShift;
    +        stack[indexReg] = frame.scope;
    +        continue Loop;
    +    case Icode_CLOSURE_EXPR :
    +        stack[++stackTop] = InterpretedFunction.createFunction(cx, frame.scope,
    +                                                               frame.fnOrScript,
    +                                                               indexReg);
    +        continue Loop;
    +    case Icode_CLOSURE_STMT :
    +        initFunction(cx, frame.scope, frame.fnOrScript, indexReg);
    +        continue Loop;
    +    case Token.REGEXP :
    +        stack[++stackTop] = frame.scriptRegExps[indexReg];
    +        continue Loop;
    +    case Icode_LITERAL_NEW :
    +        // indexReg: number of values in the literal
    +        ++stackTop;
    +        stack[stackTop] = new int[indexReg];
    +        ++stackTop;
    +        stack[stackTop] = new Object[indexReg];
    +        sDbl[stackTop] = 0;
    +        continue Loop;
    +    case Icode_LITERAL_SET : {
    +        Object value = stack[stackTop];
    +        if (value == DBL_MRK) value = ScriptRuntime.wrapNumber(sDbl[stackTop]);
    +        --stackTop;
    +        int i = (int)sDbl[stackTop];
    +        ((Object[])stack[stackTop])[i] = value;
    +        sDbl[stackTop] = i + 1;
    +        continue Loop;
    +    }
    +    case Icode_LITERAL_GETTER : {
    +        Object value = stack[stackTop];
    +        --stackTop;
    +        int i = (int)sDbl[stackTop];
    +        ((Object[])stack[stackTop])[i] = value;
    +        ((int[])stack[stackTop - 1])[i] = -1;
    +        sDbl[stackTop] = i + 1;
    +        continue Loop;
    +    }
    +    case Icode_LITERAL_SETTER : {
    +        Object value = stack[stackTop];
    +        --stackTop;
    +        int i = (int)sDbl[stackTop];
    +        ((Object[])stack[stackTop])[i] = value;
    +        ((int[])stack[stackTop - 1])[i] = +1;
    +        sDbl[stackTop] = i + 1;
    +        continue Loop;
    +    }
    +    case Token.ARRAYLIT :
    +    case Icode_SPARE_ARRAYLIT :
    +    case Token.OBJECTLIT : {
    +        Object[] data = (Object[])stack[stackTop];
    +        --stackTop;
    +        int[] getterSetters = (int[])stack[stackTop];
    +        Object val;
    +        if (op == Token.OBJECTLIT) {
    +            Object[] ids = (Object[])frame.idata.literalIds[indexReg];
    +            val = ScriptRuntime.newObjectLiteral(ids, data, getterSetters, cx,
    +                    frame.scope);
    +        } else {
    +            int[] skipIndexces = null;
    +            if (op == Icode_SPARE_ARRAYLIT) {
    +                skipIndexces = (int[])frame.idata.literalIds[indexReg];
    +            }
    +            val = ScriptRuntime.newArrayLiteral(data, skipIndexces, cx,
    +                                                frame.scope);
    +        }
    +        stack[stackTop] = val;
    +        continue Loop;
    +    }
    +    case Icode_ENTERDQ : {
    +        Object lhs = stack[stackTop];
    +        if (lhs == DBL_MRK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
    +        --stackTop;
    +        frame.scope = ScriptRuntime.enterDotQuery(lhs, frame.scope);
    +        continue Loop;
    +    }
    +    case Icode_LEAVEDQ : {
    +        boolean valBln = stack_boolean(frame, stackTop);
    +        Object x = ScriptRuntime.updateDotQuery(valBln, frame.scope);
    +        if (x != null) {
    +            stack[stackTop] = x;
    +            frame.scope = ScriptRuntime.leaveDotQuery(frame.scope);
    +            frame.pc += 2;
    +            continue Loop;
    +        }
    +        // reset stack and PC to code after ENTERDQ
    +        --stackTop;
    +        break jumplessRun;
    +    }
    +    case Token.DEFAULTNAMESPACE : {
    +        Object value = stack[stackTop];
    +        if (value == DBL_MRK) value = ScriptRuntime.wrapNumber(sDbl[stackTop]);
    +        stack[stackTop] = ScriptRuntime.setDefaultNamespace(value, cx);
    +        continue Loop;
    +    }
    +    case Token.ESCXMLATTR : {
    +        Object value = stack[stackTop];
    +        if (value != DBL_MRK) {
    +            stack[stackTop] = ScriptRuntime.escapeAttributeValue(value, cx);
    +        }
    +        continue Loop;
    +    }
    +    case Token.ESCXMLTEXT : {
    +        Object value = stack[stackTop];
    +        if (value != DBL_MRK) {
    +            stack[stackTop] = ScriptRuntime.escapeTextValue(value, cx);
    +        }
    +        continue Loop;
    +    }
    +    case Icode_DEBUGGER:
    +        if (frame.debuggerFrame != null) {
    +            frame.debuggerFrame.onDebuggerStatement(cx);
    +        }
    +        break Loop;
    +    case Icode_LINE :
    +        frame.pcSourceLineStart = frame.pc;
    +        if (frame.debuggerFrame != null) {
    +            int line = getIndex(iCode, frame.pc);
    +            frame.debuggerFrame.onLineChange(cx, line);
    +        }
    +        frame.pc += 2;
    +        continue Loop;
    +    case Icode_REG_IND_C0:
    +        indexReg = 0;
    +        continue Loop;
    +    case Icode_REG_IND_C1:
    +        indexReg = 1;
    +        continue Loop;
    +    case Icode_REG_IND_C2:
    +        indexReg = 2;
    +        continue Loop;
    +    case Icode_REG_IND_C3:
    +        indexReg = 3;
    +        continue Loop;
    +    case Icode_REG_IND_C4:
    +        indexReg = 4;
    +        continue Loop;
    +    case Icode_REG_IND_C5:
    +        indexReg = 5;
    +        continue Loop;
    +    case Icode_REG_IND1:
    +        indexReg = 0xFF & iCode[frame.pc];
    +        ++frame.pc;
    +        continue Loop;
    +    case Icode_REG_IND2:
    +        indexReg = getIndex(iCode, frame.pc);
    +        frame.pc += 2;
    +        continue Loop;
    +    case Icode_REG_IND4:
    +        indexReg = getInt(iCode, frame.pc);
    +        frame.pc += 4;
    +        continue Loop;
    +    case Icode_REG_STR_C0:
    +        stringReg = strings[0];
    +        continue Loop;
    +    case Icode_REG_STR_C1:
    +        stringReg = strings[1];
    +        continue Loop;
    +    case Icode_REG_STR_C2:
    +        stringReg = strings[2];
    +        continue Loop;
    +    case Icode_REG_STR_C3:
    +        stringReg = strings[3];
    +        continue Loop;
    +    case Icode_REG_STR1:
    +        stringReg = strings[0xFF & iCode[frame.pc]];
    +        ++frame.pc;
    +        continue Loop;
    +    case Icode_REG_STR2:
    +        stringReg = strings[getIndex(iCode, frame.pc)];
    +        frame.pc += 2;
    +        continue Loop;
    +    case Icode_REG_STR4:
    +        stringReg = strings[getInt(iCode, frame.pc)];
    +        frame.pc += 4;
    +        continue Loop;
    +    default :
    +        dumpICode(frame.idata);
    +        throw new RuntimeException(
    +            "Unknown icode : "+op+" @ pc : "+(frame.pc-1));
    +}  // end of interpreter switch
    +
    +                    } // end of jumplessRun label block
    +
    +                    // This should be reachable only for jump implementation
    +                    // when pc points to encoded target offset
    +                    if (instructionCounting) {
    +                        addInstructionCount(cx, frame, 2);
    +                    }
    +                    int offset = getShort(iCode, frame.pc);
    +                    if (offset != 0) {
    +                        // -1 accounts for pc pointing to jump opcode + 1
    +                        frame.pc += offset - 1;
    +                    } else {
    +                        frame.pc = frame.idata.longJumps.
    +                                       getExistingInt(frame.pc);
    +                    }
    +                    if (instructionCounting) {
    +                        frame.pcPrevBranch = frame.pc;
    +                    }
    +                    continue Loop;
    +
    +                } // end of Loop: for
    +
    +                exitFrame(cx, frame, null);
    +                interpreterResult = frame.result;
    +                interpreterResultDbl = frame.resultDbl;
    +                if (frame.parentFrame != null) {
    +                    frame = frame.parentFrame;
    +                    if (frame.frozen) {
    +                        frame = frame.cloneFrozen();
    +                    }
    +                    setCallResult(
    +                        frame, interpreterResult, interpreterResultDbl);
    +                    interpreterResult = null; // Help GC
    +                    continue StateLoop;
    +                }
    +                break StateLoop;
    +
    +            }  // end of interpreter withoutExceptions: try
    +            catch (Throwable ex) {
    +                if (throwable != null) {
    +                    // This is serious bug and it is better to track it ASAP
    +                    ex.printStackTrace(System.err);
    +                    throw new IllegalStateException();
    +                }
    +                throwable = ex;
    +            }
    +
    +            // This should be reachable only after above catch or from
    +            // finally when it needs to propagate exception or from
    +            // explicit throw
    +            if (throwable == null) Kit.codeBug();
    +
    +            // Exception type
    +            final int EX_CATCH_STATE = 2; // Can execute JS catch
    +            final int EX_FINALLY_STATE = 1; // Can execute JS finally
    +            final int EX_NO_JS_STATE = 0; // Terminate JS execution
    +
    +            int exState;
    +            ContinuationJump cjump = null;
    +
    +            if (generatorState != null &&
    +                generatorState.operation == NativeGenerator.GENERATOR_CLOSE &&
    +                throwable == generatorState.value)
    +            {
    +                exState = EX_FINALLY_STATE;            	
    +            } else if (throwable instanceof JavaScriptException) {
    +                exState = EX_CATCH_STATE;
    +            } else if (throwable instanceof EcmaError) {
    +                // an offical ECMA error object,
    +                exState = EX_CATCH_STATE;
    +            } else if (throwable instanceof EvaluatorException) {
    +                exState = EX_CATCH_STATE;
    +            } else if (throwable instanceof RuntimeException) {
    +                exState = cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS)
    +                          ? EX_CATCH_STATE
    +                          : EX_FINALLY_STATE;
    +            } else if (throwable instanceof Error) {
    +                exState = cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS)
    +                          ? EX_CATCH_STATE
    +                          : EX_NO_JS_STATE;
    +            } else if (throwable instanceof ContinuationJump) {
    +                // It must be ContinuationJump
    +                exState = EX_FINALLY_STATE;
    +                cjump = (ContinuationJump)throwable;
    +            } else {
    +                exState = cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS)
    +                          ? EX_CATCH_STATE
    +                          : EX_FINALLY_STATE;
    +            }
    +
    +            if (instructionCounting) {
    +                try {
    +                    addInstructionCount(cx, frame, EXCEPTION_COST);
    +                } catch (RuntimeException ex) {
    +                    throwable = ex;
    +                    exState = EX_FINALLY_STATE;
    +                } catch (Error ex) {
    +                    // Error from instruction counting
    +                    //     => unconditionally terminate JS
    +                    throwable = ex;
    +                    cjump = null;
    +                    exState = EX_NO_JS_STATE;
    +                }
    +            }
    +            if (frame.debuggerFrame != null
    +                && throwable instanceof RuntimeException)
    +            {
    +                // Call debugger only for RuntimeException
    +                RuntimeException rex = (RuntimeException)throwable;
    +                try {
    +                    frame.debuggerFrame.onExceptionThrown(cx, rex);
    +                } catch (Throwable ex) {
    +                    // Any exception from debugger
    +                    //     => unconditionally terminate JS
    +                    throwable = ex;
    +                    cjump = null;
    +                    exState = EX_NO_JS_STATE;
    +                }
    +            }
    +
    +            for (;;) {
    +                if (exState != EX_NO_JS_STATE) {
    +                    boolean onlyFinally = (exState != EX_CATCH_STATE);
    +                    indexReg = getExceptionHandler(frame, onlyFinally);
    +                    if (indexReg >= 0) {
    +                        // We caught an exception, restart the loop
    +                        // with exception pending the processing at the loop
    +                        // start
    +                        continue StateLoop;
    +                    }
    +                }
    +                // No allowed exception handlers in this frame, unwind
    +                // to parent and try to look there
    +
    +                exitFrame(cx, frame, throwable);
    +
    +                frame = frame.parentFrame;
    +                if (frame == null) { break; }
    +                if (cjump != null && cjump.branchFrame == frame) {
    +                    // Continuation branch point was hit,
    +                    // restart the state loop to reenter continuation
    +                    indexReg = -1;
    +                    continue StateLoop;
    +                }
    +            }
    +
    +            // No more frames, rethrow the exception or deal with continuation
    +            if (cjump != null) {
    +                if (cjump.branchFrame != null) {
    +                    // The above loop should locate the top frame
    +                    Kit.codeBug();
    +                }
    +                if (cjump.capturedFrame != null) {
    +                    // Restarting detached continuation
    +                    indexReg = -1;
    +                    continue StateLoop;
    +                }
    +                // Return continuation result to the caller
    +                interpreterResult = cjump.result;
    +                interpreterResultDbl = cjump.resultDbl;
    +                throwable = null;
    +            }
    +            break StateLoop;
    +
    +        } // end of StateLoop: for(;;)
    +
    +        // Do cleanups/restorations before the final return or throw
    +
    +        if (cx.previousInterpreterInvocations != null
    +            && cx.previousInterpreterInvocations.size() != 0)
    +        {
    +            cx.lastInterpreterFrame
    +                = cx.previousInterpreterInvocations.pop();
    +        } else {
    +            // It was the last interpreter frame on the stack
    +            cx.lastInterpreterFrame = null;
    +            // Force GC of the value cx.previousInterpreterInvocations
    +            cx.previousInterpreterInvocations = null;
    +        }
    +
    +        if (throwable != null) {
    +            if (throwable instanceof RuntimeException) {
    +                throw (RuntimeException)throwable;
    +            } else {
    +                // Must be instance of Error or code bug
    +                throw (Error)throwable;
    +            }
    +        }
    +
    +        return (interpreterResult != DBL_MRK)
    +               ? interpreterResult
    +               : ScriptRuntime.wrapNumber(interpreterResultDbl);
    +    }
    +
    +    private static CallFrame processThrowable(Context cx, Object throwable,
    +                                              CallFrame frame, int indexReg,
    +                                              boolean instructionCounting)
    +    {
    +        // Recovering from exception, indexReg contains
    +        // the index of handler
    +
    +        if (indexReg >= 0) {
    +            // Normal exception handler, transfer
    +            // control appropriately
    +
    +            if (frame.frozen) {
    +                // XXX Deal with exceptios!!!
    +                frame = frame.cloneFrozen();
    +            }
    +            
    +            int[] table = frame.idata.itsExceptionTable;
    +
    +            frame.pc = table[indexReg + EXCEPTION_HANDLER_SLOT];
    +            if (instructionCounting) {
    +                frame.pcPrevBranch = frame.pc;
    +            }
    +
    +            frame.savedStackTop = frame.emptyStackTop;
    +            int scopeLocal = frame.localShift
    +                             + table[indexReg
    +                                     + EXCEPTION_SCOPE_SLOT];
    +            int exLocal = frame.localShift
    +                             + table[indexReg
    +                                     + EXCEPTION_LOCAL_SLOT];
    +            frame.scope = (Scriptable)frame.stack[scopeLocal];
    +            frame.stack[exLocal] = throwable;
    +
    +            throwable = null;
    +        } else {
    +            // Continuation restoration
    +            ContinuationJump cjump = (ContinuationJump)throwable;
    +
    +            // Clear throwable to indicate that exceptions are OK
    +            throwable = null;
    +
    +            if (cjump.branchFrame != frame) Kit.codeBug();
    +
    +            // Check that we have at least one frozen frame
    +            // in the case of detached continuation restoration:
    +            // unwind code ensure that
    +            if (cjump.capturedFrame == null) Kit.codeBug();
    +
    +            // Need to rewind branchFrame, capturedFrame
    +            // and all frames in between
    +            int rewindCount = cjump.capturedFrame.frameIndex + 1;
    +            if (cjump.branchFrame != null) {
    +                rewindCount -= cjump.branchFrame.frameIndex;
    +            }
    +
    +            int enterCount = 0;
    +            CallFrame[] enterFrames = null;
    +
    +            CallFrame x = cjump.capturedFrame;
    +            for (int i = 0; i != rewindCount; ++i) {
    +                if (!x.frozen) Kit.codeBug();
    +                if (isFrameEnterExitRequired(x)) {
    +                    if (enterFrames == null) {
    +                        // Allocate enough space to store the rest
    +                        // of rewind frames in case all of them
    +                        // would require to enter
    +                        enterFrames = new CallFrame[rewindCount
    +                                                    - i];
    +                    }
    +                    enterFrames[enterCount] = x;
    +                    ++enterCount;
    +                }
    +                x = x.parentFrame;
    +            }
    +
    +            while (enterCount != 0) {
    +                // execute enter: walk enterFrames in the reverse
    +                // order since they were stored starting from
    +                // the capturedFrame, not branchFrame
    +                --enterCount;
    +                x = enterFrames[enterCount];
    +                enterFrame(cx, x, ScriptRuntime.emptyArgs, true);
    +            }
    +
    +            // Continuation jump is almost done: capturedFrame
    +            // points to the call to the function that captured
    +            // continuation, so clone capturedFrame and
    +            // emulate return that function with the suplied result
    +            frame = cjump.capturedFrame.cloneFrozen();
    +            setCallResult(frame, cjump.result, cjump.resultDbl);
    +            // restart the execution
    +        }
    +        frame.throwable = throwable;
    +        return frame;
    +    }
    +
    +    private static Object freezeGenerator(Context cx, CallFrame frame,
    +                                          int stackTop,
    +                                          GeneratorState generatorState)
    +    {
    +          if (generatorState.operation == NativeGenerator.GENERATOR_CLOSE) {
    +              // Error: no yields when generator is closing
    +              throw ScriptRuntime.typeError0("msg.yield.closing");
    +          }
    +          // return to our caller (which should be a method of NativeGenerator)
    +          frame.frozen = true;
    +          frame.result = frame.stack[stackTop];
    +          frame.resultDbl = frame.sDbl[stackTop];
    +          frame.savedStackTop = stackTop;
    +          frame.pc--; // we want to come back here when we resume
    +          ScriptRuntime.exitActivationFunction(cx);
    +          return (frame.result != UniqueTag.DOUBLE_MARK)
    +              ? frame.result
    +              : ScriptRuntime.wrapNumber(frame.resultDbl);
    +    }
    +
    +    private static Object thawGenerator(CallFrame frame, int stackTop,
    +                                        GeneratorState generatorState, int op)
    +    {
    +          // we are resuming execution
    +          frame.frozen = false;
    +          int sourceLine = getIndex(frame.idata.itsICode, frame.pc);
    +          frame.pc += 2; // skip line number data
    +          if (generatorState.operation == NativeGenerator.GENERATOR_THROW) {
    +              // processing a call to .throw(exception): must
    +              // act as if exception was thrown from resumption point
    +              return new JavaScriptException(generatorState.value,
    +                                                  frame.idata.itsSourceFile,
    +                                                  sourceLine);
    +          }
    +          if (generatorState.operation == NativeGenerator.GENERATOR_CLOSE) {
    +              return generatorState.value;
    +          }
    +          if (generatorState.operation != NativeGenerator.GENERATOR_SEND)
    +              throw Kit.codeBug();
    +          if (op == Token.YIELD)
    +              frame.stack[stackTop] = generatorState.value;
    +          return Scriptable.NOT_FOUND;
    +    }
    +
    +    private static CallFrame initFrameForApplyOrCall(Context cx, CallFrame frame,
    +            int indexReg, Object[] stack, double[] sDbl, int stackTop, int op,
    +            Scriptable calleeScope, IdFunctionObject ifun,
    +            InterpretedFunction iApplyCallable)
    +    {
    +        Scriptable applyThis;
    +        if (indexReg != 0) {
    +            applyThis = ScriptRuntime.toObjectOrNull(cx, stack[stackTop + 2]);
    +        }
    +        else {
    +            applyThis = null;
    +        }
    +        if (applyThis == null) {
    +            // This covers the case of args[0] == (null|undefined) as well.
    +            applyThis = ScriptRuntime.getTopCallScope(cx);
    +        }
    +        if(op == Icode_TAIL_CALL) {
    +            exitFrame(cx, frame, null);
    +            frame = frame.parentFrame;
    +        }
    +        else {
    +            frame.savedStackTop = stackTop;
    +            frame.savedCallOp = op;
    +        }
    +        CallFrame calleeFrame = new CallFrame();
    +        if(BaseFunction.isApply(ifun)) {
    +            Object[] callArgs = indexReg < 2 ? ScriptRuntime.emptyArgs : 
    +                ScriptRuntime.getApplyArguments(cx, stack[stackTop + 3]);
    +            initFrame(cx, calleeScope, applyThis, callArgs, null, 0, 
    +                    callArgs.length, iApplyCallable, frame, calleeFrame);
    +        }
    +        else {
    +            // Shift args left
    +            for(int i = 1; i < indexReg; ++i) {
    +                stack[stackTop + 1 + i] = stack[stackTop + 2 + i];
    +                sDbl[stackTop + 1 + i] = sDbl[stackTop + 2 + i];
    +            }
    +            int argCount = indexReg < 2 ? 0 : indexReg - 1;
    +            initFrame(cx, calleeScope, applyThis, stack, sDbl, stackTop + 2, 
    +                    argCount, iApplyCallable, frame, calleeFrame);
    +        }
    +        
    +        frame = calleeFrame;
    +        return frame;
    +    }
    +
    +    private static void initFrame(Context cx, Scriptable callerScope,
    +                                  Scriptable thisObj,
    +                                  Object[] args, double[] argsDbl,
    +                                  int argShift, int argCount,
    +                                  InterpretedFunction fnOrScript,
    +                                  CallFrame parentFrame, CallFrame frame)
    +    {
    +        InterpreterData idata = fnOrScript.idata;
    +
    +        boolean useActivation = idata.itsNeedsActivation;
    +        DebugFrame debuggerFrame = null;
    +        if (cx.debugger != null) {
    +            debuggerFrame = cx.debugger.getFrame(cx, idata);
    +            if (debuggerFrame != null) {
    +                useActivation = true;
    +            }
    +        }
    +
    +        if (useActivation) {
    +            // Copy args to new array to pass to enterActivationFunction
    +            // or debuggerFrame.onEnter
    +            if (argsDbl != null) {
    +                args = getArgsArray(args, argsDbl, argShift, argCount);
    +            }
    +            argShift = 0;
    +            argsDbl = null;
    +        }
    +
    +        Scriptable scope;
    +        if (idata.itsFunctionType != 0) {
    +            if (!idata.useDynamicScope) {
    +                scope = fnOrScript.getParentScope();
    +            } else {
    +                scope = callerScope;
    +            }
    +
    +            if (useActivation) {
    +                scope = ScriptRuntime.createFunctionActivation(
    +                            fnOrScript, scope, args);
    +            }
    +        } else {
    +            scope = callerScope;
    +            ScriptRuntime.initScript(fnOrScript, thisObj, cx, scope,
    +                                     fnOrScript.idata.evalScriptFlag);
    +        }
    +
    +        if (idata.itsNestedFunctions != null) {
    +            if (idata.itsFunctionType != 0 && !idata.itsNeedsActivation)
    +                Kit.codeBug();
    +            for (int i = 0; i < idata.itsNestedFunctions.length; i++) {
    +                InterpreterData fdata = idata.itsNestedFunctions[i];
    +                if (fdata.itsFunctionType == FunctionNode.FUNCTION_STATEMENT) {
    +                    initFunction(cx, scope, fnOrScript, i);
    +                }
    +            }
    +        }
    +
    +        Scriptable[] scriptRegExps = null;
    +        if (idata.itsRegExpLiterals != null) {
    +            // Wrapped regexps for functions are stored in
    +            // InterpretedFunction
    +            // but for script which should not contain references to scope
    +            // the regexps re-wrapped during each script execution
    +            if (idata.itsFunctionType != 0) {
    +                scriptRegExps = fnOrScript.functionRegExps;
    +            } else {
    +                scriptRegExps = fnOrScript.createRegExpWraps(cx, scope);
    +            }
    +        }
    +
    +        // Initialize args, vars, locals and stack
    +
    +        int emptyStackTop = idata.itsMaxVars + idata.itsMaxLocals - 1;
    +        int maxFrameArray = idata.itsMaxFrameArray;
    +        if (maxFrameArray != emptyStackTop + idata.itsMaxStack + 1)
    +            Kit.codeBug();
    +
    +        Object[] stack;
    +        int[] stackAttributes;
    +        double[] sDbl;
    +        boolean stackReuse;
    +        if (frame.stack != null && maxFrameArray <= frame.stack.length) {
    +            // Reuse stacks from old frame
    +            stackReuse = true;
    +            stack = frame.stack;
    +            stackAttributes = frame.stackAttributes;
    +            sDbl = frame.sDbl;
    +        } else {
    +            stackReuse = false;
    +            stack = new Object[maxFrameArray];
    +            stackAttributes = new int[maxFrameArray];
    +            sDbl = new double[maxFrameArray];
    +        }
    +
    +        int varCount = idata.getParamAndVarCount();
    +        for (int i = 0; i < varCount; i++) {
    +            if (idata.getParamOrVarConst(i))
    +                stackAttributes[i] = ScriptableObject.CONST;
    +        }
    +        int definedArgs = idata.argCount;
    +        if (definedArgs > argCount) { definedArgs = argCount; }
    +
    +        // Fill the frame structure
    +
    +        frame.parentFrame = parentFrame;
    +        frame.frameIndex = (parentFrame == null)
    +                           ? 0 : parentFrame.frameIndex + 1;
    +        if(frame.frameIndex > cx.getMaximumInterpreterStackDepth())
    +        {
    +            throw Context.reportRuntimeError("Exceeded maximum stack depth");
    +        }
    +        frame.frozen = false;
    +
    +        frame.fnOrScript = fnOrScript;
    +        frame.idata = idata;
    +
    +        frame.stack = stack;
    +        frame.stackAttributes = stackAttributes;
    +        frame.sDbl = sDbl;
    +        frame.varSource = frame;
    +        frame.localShift = idata.itsMaxVars;
    +        frame.emptyStackTop = emptyStackTop;
    +
    +        frame.debuggerFrame = debuggerFrame;
    +        frame.useActivation = useActivation;
    +
    +        frame.thisObj = thisObj;
    +        frame.scriptRegExps = scriptRegExps;
    +
    +        // Initialize initial values of variables that change during
    +        // interpretation.
    +        frame.result = Undefined.instance;
    +        frame.pc = 0;
    +        frame.pcPrevBranch = 0;
    +        frame.pcSourceLineStart = idata.firstLinePC;
    +        frame.scope = scope;
    +
    +        frame.savedStackTop = emptyStackTop;
    +        frame.savedCallOp = 0;
    +
    +        System.arraycopy(args, argShift, stack, 0, definedArgs);
    +        if (argsDbl != null) {
    +            System.arraycopy(argsDbl, argShift, sDbl, 0, definedArgs);
    +        }
    +        for (int i = definedArgs; i != idata.itsMaxVars; ++i) {
    +            stack[i] = Undefined.instance;
    +        }
    +        if (stackReuse) {
    +            // Clean the stack part and space beyond stack if any
    +            // of the old array to allow to GC objects there
    +            for (int i = emptyStackTop + 1; i != stack.length; ++i) {
    +                stack[i] = null;
    +            }
    +        }
    +
    +        enterFrame(cx, frame, args, false);
    +    }
    +
    +    private static boolean isFrameEnterExitRequired(CallFrame frame)
    +    {
    +        return frame.debuggerFrame != null || frame.idata.itsNeedsActivation;
    +    }
    +
    +    private static void enterFrame(Context cx, CallFrame frame, Object[] args, 
    +                                   boolean continuationRestart)
    +    {
    +        boolean usesActivation = frame.idata.itsNeedsActivation; 
    +        boolean isDebugged = frame.debuggerFrame != null;
    +        if(usesActivation || isDebugged) {
    +            Scriptable scope = frame.scope;
    +            if(scope == null) {
    +                Kit.codeBug();
    +            } else if (continuationRestart) {
    +                // Walk the parent chain of frame.scope until a NativeCall is 
    +                // found. Normally, frame.scope is a NativeCall when called 
    +                // from initFrame() for a debugged or activatable function. 
    +                // However, when called from interpretLoop() as part of
    +                // restarting a continuation, it can also be a NativeWith if 
    +                // the continuation was captured within a "with" or "catch" 
    +                // block ("catch" implicitly uses NativeWith to create a scope 
    +                // to expose the exception variable).
    +                for(;;) {
    +                    if(scope instanceof NativeWith) {
    +                        scope = scope.getParentScope();
    +                        if (scope == null || (frame.parentFrame != null && 
    +                                              frame.parentFrame.scope == scope))
    +                        {
    +                            // If we get here, we didn't find a NativeCall in 
    +                            // the call chain before reaching parent frame's 
    +                            // scope. This should not be possible.
    +                            Kit.codeBug();
    +                            break; // Never reached, but keeps the static analyzer happy about "scope" not being null 5 lines above.
    +                        }
    +                    }
    +                    else {
    +                        break;
    +                    }
    +                }
    +            }
    +            if (isDebugged) {
    +                frame.debuggerFrame.onEnter(cx, scope, frame.thisObj, args);
    +            }
    +            // Enter activation only when itsNeedsActivation true, 
    +            // since debugger should not interfere with activation 
    +            // chaining
    +            if (usesActivation) {
    +                ScriptRuntime.enterActivationFunction(cx, scope);
    +            }
    +        }
    +    }
    +
    +    private static void exitFrame(Context cx, CallFrame frame,
    +                                  Object throwable)
    +    {
    +        if (frame.idata.itsNeedsActivation) {
    +            ScriptRuntime.exitActivationFunction(cx);
    +        }
    +
    +        if (frame.debuggerFrame != null) {
    +            try {
    +                if (throwable instanceof Throwable) {
    +                    frame.debuggerFrame.onExit(cx, true, throwable);
    +                } else {
    +                    Object result;
    +                    ContinuationJump cjump = (ContinuationJump)throwable;
    +                    if (cjump == null) {
    +                        result = frame.result;
    +                    } else {
    +                        result = cjump.result;
    +                    }
    +                    if (result == UniqueTag.DOUBLE_MARK) {
    +                        double resultDbl;
    +                        if (cjump == null) {
    +                            resultDbl = frame.resultDbl;
    +                        } else {
    +                            resultDbl = cjump.resultDbl;
    +                        }
    +                        result = ScriptRuntime.wrapNumber(resultDbl);
    +                    }
    +                    frame.debuggerFrame.onExit(cx, false, result);
    +                }
    +            } catch (Throwable ex) {
    +                System.err.println(
    +"RHINO USAGE WARNING: onExit terminated with exception");
    +                ex.printStackTrace(System.err);
    +            }
    +        }
    +    }
    +
    +    private static void setCallResult(CallFrame frame,
    +                                      Object callResult,
    +                                      double callResultDbl)
    +    {
    +        if (frame.savedCallOp == Token.CALL) {
    +            frame.stack[frame.savedStackTop] = callResult;
    +            frame.sDbl[frame.savedStackTop] = callResultDbl;
    +        } else if (frame.savedCallOp == Token.NEW) {
    +            // If construct returns scriptable,
    +            // then it replaces on stack top saved original instance
    +            // of the object.
    +            if (callResult instanceof Scriptable) {
    +                frame.stack[frame.savedStackTop] = callResult;
    +            }
    +        } else {
    +            Kit.codeBug();
    +        }
    +        frame.savedCallOp = 0;
    +    }
    +
    +    private static void captureContinuation(Context cx, CallFrame frame,
    +                                            int stackTop)
    +    {
    +        Continuation c = new Continuation();
    +        ScriptRuntime.setObjectProtoAndParent(
    +            c, ScriptRuntime.getTopCallScope(cx));
    +
    +        // Make sure that all frames upstack frames are frozen
    +        CallFrame x = frame.parentFrame;
    +        while (x != null && !x.frozen) {
    +            x.frozen = true;
    +            // Allow to GC unused stack space
    +            for (int i = x.savedStackTop + 1; i != x.stack.length; ++i) {
    +                // Allow to GC unused stack space
    +                x.stack[i] = null;
    +                x.stackAttributes[i] = ScriptableObject.EMPTY;
    +            }
    +            if (x.savedCallOp == Token.CALL) {
    +                // the call will always overwrite the stack top with the result
    +                x.stack[x.savedStackTop] = null;
    +            } else {
    +                if (x.savedCallOp != Token.NEW) Kit.codeBug();
    +                // the new operator uses stack top to store the constructed
    +                // object so it shall not be cleared: see comments in
    +                // setCallResult
    +            }
    +            x = x.parentFrame;
    +        }
    +
    +        c.initImplementation(frame.parentFrame);
    +        frame.stack[stackTop] = c;
    +    }
    +
    +    private static int stack_int32(CallFrame frame, int i)
    +    {
    +        Object x = frame.stack[i];
    +        double value;
    +        if (x == UniqueTag.DOUBLE_MARK) {
    +            value = frame.sDbl[i];
    +        } else {
    +            value = ScriptRuntime.toNumber(x);
    +        }
    +        return ScriptRuntime.toInt32(value);
    +    }
    +
    +    private static double stack_double(CallFrame frame, int i)
    +    {
    +        Object x = frame.stack[i];
    +        if (x != UniqueTag.DOUBLE_MARK) {
    +            return ScriptRuntime.toNumber(x);
    +        } else {
    +            return frame.sDbl[i];
    +        }
    +    }
    +
    +    private static boolean stack_boolean(CallFrame frame, int i)
    +    {
    +        Object x = frame.stack[i];
    +        if (x == Boolean.TRUE) {
    +            return true;
    +        } else if (x == Boolean.FALSE) {
    +            return false;
    +        } else if (x == UniqueTag.DOUBLE_MARK) {
    +            double d = frame.sDbl[i];
    +            return d == d && d != 0.0;
    +        } else if (x == null || x == Undefined.instance) {
    +            return false;
    +        } else if (x instanceof Number) {
    +            double d = ((Number)x).doubleValue();
    +            return (d == d && d != 0.0);
    +        } else if (x instanceof Boolean) {
    +            return ((Boolean)x).booleanValue();
    +        } else {
    +            return ScriptRuntime.toBoolean(x);
    +        }
    +    }
    +
    +    private static void do_add(Object[] stack, double[] sDbl, int stackTop,
    +                              Context cx)
    +    {
    +        Object rhs = stack[stackTop + 1];
    +        Object lhs = stack[stackTop];
    +        double d;
    +        boolean leftRightOrder;
    +        if (rhs == UniqueTag.DOUBLE_MARK) {
    +            d = sDbl[stackTop + 1];
    +            if (lhs == UniqueTag.DOUBLE_MARK) {
    +                sDbl[stackTop] += d;
    +                return;
    +            }
    +            leftRightOrder = true;
    +            // fallthrough to object + number code
    +        } else if (lhs == UniqueTag.DOUBLE_MARK) {
    +            d = sDbl[stackTop];
    +            lhs = rhs;
    +            leftRightOrder = false;
    +            // fallthrough to object + number code
    +        } else {
    +            if (lhs instanceof Scriptable || rhs instanceof Scriptable) {
    +                stack[stackTop] = ScriptRuntime.add(lhs, rhs, cx);
    +            } else if (lhs instanceof String) {
    +                String lstr = (String)lhs;
    +                String rstr = ScriptRuntime.toString(rhs);
    +                stack[stackTop] = lstr.concat(rstr);
    +            } else if (rhs instanceof String) {
    +                String lstr = ScriptRuntime.toString(lhs);
    +                String rstr = (String)rhs;
    +                stack[stackTop] = lstr.concat(rstr);
    +            } else {
    +                double lDbl = (lhs instanceof Number)
    +                    ? ((Number)lhs).doubleValue() : ScriptRuntime.toNumber(lhs);
    +                double rDbl = (rhs instanceof Number)
    +                    ? ((Number)rhs).doubleValue() : ScriptRuntime.toNumber(rhs);
    +                stack[stackTop] = UniqueTag.DOUBLE_MARK;
    +                sDbl[stackTop] = lDbl + rDbl;
    +            }
    +            return;
    +        }
    +
    +        // handle object(lhs) + number(d) code
    +        if (lhs instanceof Scriptable) {
    +            rhs = ScriptRuntime.wrapNumber(d);
    +            if (!leftRightOrder) {
    +                Object tmp = lhs;
    +                lhs = rhs;
    +                rhs = tmp;
    +            }
    +            stack[stackTop] = ScriptRuntime.add(lhs, rhs, cx);
    +        } else if (lhs instanceof String) {
    +            String lstr = (String)lhs;
    +            String rstr = ScriptRuntime.toString(d);
    +            if (leftRightOrder) {
    +                stack[stackTop] = lstr.concat(rstr);
    +            } else {
    +                stack[stackTop] = rstr.concat(lstr);
    +            }
    +        } else {
    +            double lDbl = (lhs instanceof Number)
    +                ? ((Number)lhs).doubleValue() : ScriptRuntime.toNumber(lhs);
    +            stack[stackTop] = UniqueTag.DOUBLE_MARK;
    +            sDbl[stackTop] = lDbl + d;
    +        }
    +    }
    +
    +    private static Object[] getArgsArray(Object[] stack, double[] sDbl,
    +                                         int shift, int count)
    +    {
    +        if (count == 0) {
    +            return ScriptRuntime.emptyArgs;
    +        }
    +        Object[] args = new Object[count];
    +        for (int i = 0; i != count; ++i, ++shift) {
    +            Object val = stack[shift];
    +            if (val == UniqueTag.DOUBLE_MARK) {
    +                val = ScriptRuntime.wrapNumber(sDbl[shift]);
    +            }
    +            args[i] = val;
    +        }
    +        return args;
    +    }
    +
    +    private static void addInstructionCount(Context cx, CallFrame frame,
    +                                            int extra)
    +    {
    +        cx.instructionCount += frame.pc - frame.pcPrevBranch + extra;
    +        if (cx.instructionCount > cx.instructionThreshold) {
    +            cx.observeInstructionCount(cx.instructionCount);
    +            cx.instructionCount = 0;
    +        }
    +    }
    +}
    diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/InterpreterData.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/InterpreterData.java
    new file mode 100644
    index 0000000..7435b10
    --- /dev/null
    +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/InterpreterData.java
    @@ -0,0 +1,192 @@
    +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
    + *
    + * ***** BEGIN LICENSE BLOCK *****
    + * Version: MPL 1.1/GPL 2.0
    + *
    + * The contents of this file are subject to the Mozilla Public License Version
    + * 1.1 (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.mozilla.org/MPL/
    + *
    + * Software distributed under the License is distributed on an "AS IS" basis,
    + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
    + * for the specific language governing rights and limitations under the
    + * License.
    + *
    + * The Original Code is Rhino code, released
    + * May 6, 1999.
    + *
    + * The Initial Developer of the Original Code is
    + * Netscape Communications Corporation.
    + * Portions created by the Initial Developer are Copyright (C) 1997-2000
    + * the Initial Developer. All Rights Reserved.
    + *
    + * Contributor(s):
    + *   Norris Boyd
    + *   Bob Jervis
    + *   Roger Lawrence
    + *
    + * Alternatively, the contents of this file may be used under the terms of
    + * the GNU General Public License Version 2 or later (the "GPL"), in which
    + * case the provisions of the GPL are applicable instead of those above. If
    + * you wish to allow use of your version of this file only under the terms of
    + * the GPL and not to allow others to use your version of this file under the
    + * MPL, indicate your decision by deleting the provisions above and replacing
    + * them with the notice and other provisions required by the GPL. If you do
    + * not delete the provisions above, a recipient may use your version of this
    + * file under either the MPL or the GPL.
    + *
    + * ***** END LICENSE BLOCK ***** */
    +
    +package org.mozilla.javascript;
    +
    +import java.io.Serializable;
    +
    +import org.mozilla.javascript.debug.DebuggableScript;
    +
    +final class InterpreterData implements Serializable, DebuggableScript
    +{
    +    static final long serialVersionUID = 5067677351589230234L;
    +
    +    static final int INITIAL_MAX_ICODE_LENGTH = 1024;
    +    static final int INITIAL_STRINGTABLE_SIZE = 64;
    +    static final int INITIAL_NUMBERTABLE_SIZE = 64;
    +
    +    InterpreterData(int languageVersion,
    +                    String sourceFile, String encodedSource)
    +    {
    +        this.languageVersion = languageVersion;
    +        this.itsSourceFile = sourceFile;
    +        this.encodedSource = encodedSource;
    +
    +        init();
    +    }
    +
    +    InterpreterData(InterpreterData parent)
    +    {
    +        this.parentData = parent;
    +        this.languageVersion = parent.languageVersion;
    +        this.itsSourceFile = parent.itsSourceFile;
    +        this.encodedSource = parent.encodedSource;
    +
    +        init();
    +    }
    +
    +    private void init()
    +    {
    +        itsICode = new byte[INITIAL_MAX_ICODE_LENGTH];
    +        itsStringTable = new String[INITIAL_STRINGTABLE_SIZE];
    +    }
    +
    +    String itsName;
    +    String itsSourceFile;
    +    boolean itsNeedsActivation;
    +    int itsFunctionType;
    +
    +    String[] itsStringTable;
    +    double[] itsDoubleTable;
    +    InterpreterData[] itsNestedFunctions;
    +    Object[] itsRegExpLiterals;
    +
    +    byte[] itsICode;
    +
    +    int[] itsExceptionTable;
    +
    +    int itsMaxVars;
    +    int itsMaxLocals;
    +    int itsMaxStack;
    +    int itsMaxFrameArray;
    +
    +    // see comments in NativeFuncion for definition of argNames and argCount
    +    String[] argNames;
    +    boolean[] argIsConst;
    +    int argCount;
    +
    +    int itsMaxCalleeArgs;
    +
    +    String encodedSource;
    +    int encodedSourceStart;
    +    int encodedSourceEnd;
    +
    +    int languageVersion;
    +
    +    boolean useDynamicScope;
    +
    +    boolean topLevel;
    +
    +    Object[] literalIds;
    +
    +    UintMap longJumps;
    +
    +    int firstLinePC = -1; // PC for the first LINE icode
    +
    +    InterpreterData parentData;
    +
    +    boolean evalScriptFlag; // true if script corresponds to eval() code
    +
    +    public boolean isTopLevel()
    +    {
    +        return topLevel;
    +    }
    +
    +    public boolean isFunction()
    +    {
    +        return itsFunctionType != 0;
    +    }
    +
    +    public String getFunctionName()
    +    {
    +        return itsName;
    +    }
    +
    +    public int getParamCount()
    +    {
    +        return argCount;
    +    }
    +
    +    public int getParamAndVarCount()
    +    {
    +        return argNames.length;
    +    }
    +
    +    public String getParamOrVarName(int index)
    +    {
    +        return argNames[index];
    +    }
    +
    +    public boolean getParamOrVarConst(int index)
    +    {
    +        return argIsConst[index];
    +    }
    +
    +    public String getSourceName()
    +    {
    +        return itsSourceFile;
    +    }
    +
    +    public boolean isGeneratedScript()
    +    {
    +        return ScriptRuntime.isGeneratedScript(itsSourceFile);
    +    }
    +
    +    public int[] getLineNumbers()
    +    {
    +        return Interpreter.getLineNumbers(this);
    +    }
    +
    +    public int getFunctionCount()
    +    {
    +        return (itsNestedFunctions == null) ? 0 : itsNestedFunctions.length;
    +    }
    +
    +    public DebuggableScript getFunction(int index)
    +    {
    +        return itsNestedFunctions[index];
    +    }
    +
    +    public DebuggableScript getParent()
    +    {
    +         return parentData;
    +    }
    +
    +}
    diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/JavaAdapter.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/JavaAdapter.java
    new file mode 100644
    index 0000000..6e0a827
    --- /dev/null
    +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/JavaAdapter.java
    @@ -0,0 +1,1129 @@
    +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
    + *
    + * ***** BEGIN LICENSE BLOCK *****
    + * Version: MPL 1.1/GPL 2.0
    + *
    + * The contents of this file are subject to the Mozilla Public License Version
    + * 1.1 (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.mozilla.org/MPL/
    + *
    + * Software distributed under the License is distributed on an "AS IS" basis,
    + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
    + * for the specific language governing rights and limitations under the
    + * License.
    + *
    + * The Original Code is Rhino code, released
    + * May 6, 1999.
    + *
    + * The Initial Developer of the Original Code is
    + * Netscape Communications Corporation.
    + * Portions created by the Initial Developer are Copyright (C) 1997-1999
    + * the Initial Developer. All Rights Reserved.
    + *
    + * Contributor(s):
    + *   Patrick Beard
    + *   Norris Boyd
    + *   Igor Bukanov
    + *   Mike McCabe
    + *   Matthias Radestock
    + *   Andi Vajda
    + *   Andrew Wason
    + *   Kemal Bayram
    + *
    + * Alternatively, the contents of this file may be used under the terms of
    + * the GNU General Public License Version 2 or later (the "GPL"), in which
    + * case the provisions of the GPL are applicable instead of those above. If
    + * you wish to allow use of your version of this file only under the terms of
    + * the GPL and not to allow others to use your version of this file under the
    + * MPL, indicate your decision by deleting the provisions above and replacing
    + * them with the notice and other provisions required by the GPL. If you do
    + * not delete the provisions above, a recipient may use your version of this
    + * file under either the MPL or the GPL.
    + *
    + * ***** END LICENSE BLOCK ***** */
    +
    +package org.mozilla.javascript;
    +
    +import org.mozilla.classfile.*;
    +import java.lang.reflect.*;
    +import java.io.*;
    +import java.security.*;
    +import java.util.*;
    +
    +public final class JavaAdapter implements IdFunctionCall
    +{
    +    /**
    +     * Provides a key with which to distinguish previously generated
    +     * adapter classes stored in a hash table.
    +     */
    +    static class JavaAdapterSignature
    +    {
    +        Class superClass;
    +        Class[] interfaces;
    +        ObjToIntMap names;
    +
    +        JavaAdapterSignature(Class superClass, Class[] interfaces,
    +                             ObjToIntMap names)
    +        {
    +            this.superClass = superClass;
    +            this.interfaces = interfaces;
    +            this.names = names;
    +        }
    +
    +        public boolean equals(Object obj)
    +        {
    +            if (!(obj instanceof JavaAdapterSignature))
    +                return false;
    +            JavaAdapterSignature sig = (JavaAdapterSignature) obj;
    +            if (superClass != sig.superClass)
    +                return false;
    +            if (interfaces != sig.interfaces) {
    +                if (interfaces.length != sig.interfaces.length)
    +                    return false;
    +                for (int i=0; i < interfaces.length; i++)
    +                    if (interfaces[i] != sig.interfaces[i])
    +                        return false;
    +            }
    +            if (names.size() != sig.names.size())
    +                return false;
    +            ObjToIntMap.Iterator iter = new ObjToIntMap.Iterator(names);
    +            for (iter.start(); !iter.done(); iter.next()) {
    +                String name = (String)iter.getKey();
    +                int arity = iter.getValue();
    +                if (arity != names.get(name, arity + 1))
    +                    return false;
    +            }
    +            return true;
    +        }
    +
    +        public int hashCode()
    +        {
    +            return superClass.hashCode()
    +                | (0x9e3779b9 * (names.size() | (interfaces.length << 16)));
    +        }
    +    }
    +
    +    public static void init(Context cx, Scriptable scope, boolean sealed)
    +    {
    +        JavaAdapter obj = new JavaAdapter();
    +        IdFunctionObject ctor = new IdFunctionObject(obj, FTAG, Id_JavaAdapter,
    +                                                     "JavaAdapter", 1, scope);
    +        ctor.markAsConstructor(null);
    +        if (sealed) {
    +            ctor.sealObject();
    +        }
    +        ctor.exportAsScopeProperty();
    +    }
    +
    +    public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope,
    +                             Scriptable thisObj, Object[] args)
    +    {
    +        if (f.hasTag(FTAG)) {
    +            if (f.methodId() == Id_JavaAdapter) {
    +                return js_createAdapter(cx, scope, args);
    +            }
    +        }
    +        throw f.unknown();
    +    }
    +
    +    public static Object convertResult(Object result, Class c)
    +    {
    +        if (result == Undefined.instance &&
    +            (c != ScriptRuntime.ObjectClass &&
    +             c != ScriptRuntime.StringClass))
    +        {
    +            // Avoid an error for an undefined value; return null instead.
    +            return null;
    +        }
    +        return Context.jsToJava(result, c);
    +    }
    +
    +    public static Scriptable createAdapterWrapper(Scriptable obj,
    +                                                  Object adapter)
    +    {
    +        Scriptable scope = ScriptableObject.getTopLevelScope(obj);
    +        NativeJavaObject res = new NativeJavaObject(scope, adapter, null, true);
    +        res.setPrototype(obj);
    +        return res;
    +    }
    +
    +    public static Object getAdapterSelf(Class adapterClass, Object adapter)
    +        throws NoSuchFieldException, IllegalAccessException
    +    {
    +        Field self = adapterClass.getDeclaredField("self");
    +        return self.get(adapter);
    +    }
    +
    +    static Object js_createAdapter(Context cx, Scriptable scope, Object[] args)
    +    {
    +        int N = args.length;
    +        if (N == 0) {
    +            throw ScriptRuntime.typeError0("msg.adapter.zero.args");
    +        }
    +
    +        Class superClass = null;
    +        Class[] intfs = new Class[N - 1];
    +        int interfaceCount = 0;
    +        for (int i = 0; i != N - 1; ++i) {
    +            Object arg = args[i];
    +            if (!(arg instanceof NativeJavaClass)) {
    +                throw ScriptRuntime.typeError2("msg.not.java.class.arg",
    +                                               String.valueOf(i),
    +                                               ScriptRuntime.toString(arg));
    +            }
    +            Class c = ((NativeJavaClass) arg).getClassObject();
    +            if (!c.isInterface()) {
    +                if (superClass != null) {
    +                    throw ScriptRuntime.typeError2("msg.only.one.super",
    +                              superClass.getName(), c.getName());
    +                }
    +                superClass = c;
    +            } else {
    +                intfs[interfaceCount++] = c;
    +            }
    +        }
    +
    +        if (superClass == null)
    +            superClass = ScriptRuntime.ObjectClass;
    +
    +        Class[] interfaces = new Class[interfaceCount];
    +        System.arraycopy(intfs, 0, interfaces, 0, interfaceCount);
    +        Scriptable obj = ScriptRuntime.toObject(cx, scope, args[N - 1]);
    +
    +        Class adapterClass = getAdapterClass(scope, superClass, interfaces,
    +                                             obj);
    +
    +        Class[] ctorParms = {
    +            ScriptRuntime.ContextFactoryClass,
    +            ScriptRuntime.ScriptableClass
    +        };
    +        Object[] ctorArgs = { cx.getFactory(), obj };
    +        try {
    +            Object adapter = adapterClass.getConstructor(ctorParms).
    +                                 newInstance(ctorArgs);
    +            return getAdapterSelf(adapterClass, adapter);
    +        } catch (Exception ex) {
    +            throw Context.throwAsScriptRuntimeEx(ex);
    +        }
    +    }
    +
    +    // Needed by NativeJavaObject serializer
    +    public static void writeAdapterObject(Object javaObject,
    +                                          ObjectOutputStream out)
    +        throws IOException
    +    {
    +        Class cl = javaObject.getClass();
    +        out.writeObject(cl.getSuperclass().getName());
    +
    +        Class[] interfaces = cl.getInterfaces();
    +        String[] interfaceNames = new String[interfaces.length];
    +
    +        for (int i=0; i < interfaces.length; i++)
    +            interfaceNames[i] = interfaces[i].getName();
    +
    +        out.writeObject(interfaceNames);
    +
    +        try {
    +            Object delegee = cl.getField("delegee").get(javaObject);
    +            out.writeObject(delegee);
    +            return;
    +        } catch (IllegalAccessException e) {
    +        } catch (NoSuchFieldException e) {
    +        }
    +        throw new IOException();
    +    }
    +
    +    // Needed by NativeJavaObject de-serializer
    +    public static Object readAdapterObject(Scriptable self,
    +                                           ObjectInputStream in)
    +        throws IOException, ClassNotFoundException
    +    {
    +        ContextFactory factory;
    +        Context cx = Context.getCurrentContext();
    +        if (cx != null) {
    +            factory = cx.getFactory();
    +        } else {
    +            factory = null;
    +        }
    +
    +        Class superClass = Class.forName((String)in.readObject());
    +
    +        String[] interfaceNames = (String[])in.readObject();
    +        Class[] interfaces = new Class[interfaceNames.length];
    +
    +        for (int i=0; i < interfaceNames.length; i++)
    +            interfaces[i] = Class.forName(interfaceNames[i]);
    +
    +        Scriptable delegee = (Scriptable)in.readObject();
    +
    +        Class adapterClass = getAdapterClass(self, superClass, interfaces,
    +                                             delegee);
    +
    +        Class[] ctorParms = {
    +            ScriptRuntime.ContextFactoryClass,
    +            ScriptRuntime.ScriptableClass,
    +            ScriptRuntime.ScriptableClass
    +        };
    +        Object[] ctorArgs = { factory, delegee, self };
    +        try {
    +            return adapterClass.getConstructor(ctorParms).newInstance(ctorArgs);
    +        } catch(InstantiationException e) {
    +        } catch(IllegalAccessException e) {
    +        } catch(InvocationTargetException e) {
    +        } catch(NoSuchMethodException e) {
    +        }
    +
    +        throw new ClassNotFoundException("adapter");
    +    }
    +
    +    private static ObjToIntMap getObjectFunctionNames(Scriptable obj)
    +    {
    +        Object[] ids = ScriptableObject.getPropertyIds(obj);
    +        ObjToIntMap map = new ObjToIntMap(ids.length);
    +        for (int i = 0; i != ids.length; ++i) {
    +            if (!(ids[i] instanceof String))
    +                continue;
    +            String id = (String) ids[i];
    +            Object value = ScriptableObject.getProperty(obj, id);
    +            if (value instanceof Function) {
    +                Function f = (Function)value;
    +                int length = ScriptRuntime.toInt32(
    +                                 ScriptableObject.getProperty(f, "length"));
    +                if (length < 0) {
    +                    length = 0;
    +                }
    +                map.put(id, length);
    +            }
    +        }
    +        return map;
    +    }
    +
    +    private static Class getAdapterClass(Scriptable scope, Class superClass,
    +                                         Class[] interfaces, Scriptable obj)
    +    {
    +        ClassCache cache = ClassCache.get(scope);
    +        Map> generated
    +            = cache.getInterfaceAdapterCacheMap();
    +
    +        ObjToIntMap names = getObjectFunctionNames(obj);
    +        JavaAdapterSignature sig;
    +        sig = new JavaAdapterSignature(superClass, interfaces, names);
    +        Class adapterClass = generated.get(sig);
    +        if (adapterClass == null) {
    +            String adapterName = "adapter"
    +                                 + cache.newClassSerialNumber();
    +            byte[] code = createAdapterCode(names, adapterName,
    +                                            superClass, interfaces, null);
    +
    +            adapterClass = loadAdapterClass(adapterName, code);
    +            if (cache.isCachingEnabled()) {
    +                generated.put(sig, adapterClass);
    +            }
    +        }
    +        return adapterClass;
    +    }
    +
    +    public static byte[] createAdapterCode(ObjToIntMap functionNames,
    +                                           String adapterName,
    +                                           Class superClass,
    +                                           Class[] interfaces,
    +                                           String scriptClassName)
    +    {
    +        ClassFileWriter cfw = new ClassFileWriter(adapterName,
    +                                                  superClass.getName(),
    +                                                  "");
    +        cfw.addField("factory", "Lorg/mozilla/javascript/ContextFactory;",
    +                     (short) (ClassFileWriter.ACC_PUBLIC |
    +                              ClassFileWriter.ACC_FINAL));
    +        cfw.addField("delegee", "Lorg/mozilla/javascript/Scriptable;",
    +                     (short) (ClassFileWriter.ACC_PUBLIC |
    +                              ClassFileWriter.ACC_FINAL));
    +        cfw.addField("self", "Lorg/mozilla/javascript/Scriptable;",
    +                     (short) (ClassFileWriter.ACC_PUBLIC |
    +                              ClassFileWriter.ACC_FINAL));
    +        int interfacesCount = interfaces == null ? 0 : interfaces.length;
    +        for (int i=0; i < interfacesCount; i++) {
    +            if (interfaces[i] != null)
    +                cfw.addInterface(interfaces[i].getName());
    +        }
    +
    +        String superName = superClass.getName().replace('.', '/');
    +        generateCtor(cfw, adapterName, superName);
    +        generateSerialCtor(cfw, adapterName, superName);
    +        if (scriptClassName != null)
    +            generateEmptyCtor(cfw, adapterName, superName, scriptClassName);
    +
    +        ObjToIntMap generatedOverrides = new ObjToIntMap();
    +        ObjToIntMap generatedMethods = new ObjToIntMap();
    +
    +        // generate methods to satisfy all specified interfaces.
    +        for (int i = 0; i < interfacesCount; i++) {
    +            Method[] methods = interfaces[i].getMethods();
    +            for (int j = 0; j < methods.length; j++) {
    +                Method method = methods[j];
    +                int mods = method.getModifiers();
    +                if (Modifier.isStatic(mods) || Modifier.isFinal(mods)) {
    +                    continue;
    +                }
    +                String methodName = method.getName();
    +                Class[] argTypes = method.getParameterTypes();
    +                if (!functionNames.has(methodName)) {
    +                    try {
    +                        superClass.getMethod(methodName, argTypes);
    +                        // The class we're extending implements this method and
    +                        // the JavaScript object doesn't have an override. See
    +                        // bug 61226.
    +                        continue;
    +                    } catch (NoSuchMethodException e) {
    +                        // Not implemented by superclass; fall through
    +                    }
    +                }
    +                // make sure to generate only one instance of a particular
    +                // method/signature.
    +                String methodSignature = getMethodSignature(method, argTypes);
    +                String methodKey = methodName + methodSignature;
    +                if (! generatedOverrides.has(methodKey)) {
    +                    generateMethod(cfw, adapterName, methodName,
    +                                   argTypes, method.getReturnType());
    +                    generatedOverrides.put(methodKey, 0);
    +                    generatedMethods.put(methodName, 0);
    +                }
    +            }
    +        }
    +
    +        // Now, go through the superclass's methods, checking for abstract
    +        // methods or additional methods to override.
    +
    +        // generate any additional overrides that the object might contain.
    +        Method[] methods = getOverridableMethods(superClass);
    +        for (int j = 0; j < methods.length; j++) {
    +            Method method = methods[j];
    +            int mods = method.getModifiers();
    +            // if a method is marked abstract, must implement it or the
    +            // resulting class won't be instantiable. otherwise, if the object
    +            // has a property of the same name, then an override is intended.
    +            boolean isAbstractMethod = Modifier.isAbstract(mods);
    +            String methodName = method.getName();
    +            if (isAbstractMethod || functionNames.has(methodName)) {
    +                // make sure to generate only one instance of a particular
    +                // method/signature.
    +                Class[] argTypes = method.getParameterTypes();
    +                String methodSignature = getMethodSignature(method, argTypes);
    +                String methodKey = methodName + methodSignature;
    +                if (! generatedOverrides.has(methodKey)) {
    +                    generateMethod(cfw, adapterName, methodName,
    +                                   argTypes, method.getReturnType());
    +                    generatedOverrides.put(methodKey, 0);
    +                    generatedMethods.put(methodName, 0);
    +                    
    +                    // if a method was overridden, generate a "super$method"
    +                    // which lets the delegate call the superclass' version.
    +                    if (!isAbstractMethod) {
    +                        generateSuper(cfw, adapterName, superName,
    +                                      methodName, methodSignature,
    +                                      argTypes, method.getReturnType());
    +                    }
    +                }
    +            }
    +        }
    +
    +        // Generate Java methods for remaining properties that are not
    +        // overrides.
    +        ObjToIntMap.Iterator iter = new ObjToIntMap.Iterator(functionNames);
    +        for (iter.start(); !iter.done(); iter.next()) {
    +            String functionName = (String)iter.getKey();
    +            if (generatedMethods.has(functionName))
    +                continue;
    +            int length = iter.getValue();
    +            Class[] parms = new Class[length];
    +            for (int k=0; k < length; k++)
    +                parms[k] = ScriptRuntime.ObjectClass;
    +            generateMethod(cfw, adapterName, functionName, parms,
    +                           ScriptRuntime.ObjectClass);
    +        }
    +        return cfw.toByteArray();
    +    }
    +
    +    static Method[] getOverridableMethods(Class c)
    +    {
    +        ArrayList list = new ArrayList();
    +        HashSet skip = new HashSet();
    +        while (c != null) {
    +            Method[] methods = c.getDeclaredMethods();
    +            for (int i = 0; i < methods.length; i++) {
    +                String methodKey = methods[i].getName() + 
    +                    getMethodSignature(methods[i],
    +                            methods[i].getParameterTypes());
    +                if (skip.contains(methodKey))
    +                    continue; // skip this method
    +                int mods = methods[i].getModifiers();
    +                if (Modifier.isStatic(mods))
    +                    continue;
    +                if (Modifier.isFinal(mods)) {
    +                    // Make sure we don't add a final method to the list
    +                    // of overridable methods.
    +                    skip.add(methodKey);
    +                    continue;
    +                }
    +                if (Modifier.isPublic(mods) || Modifier.isProtected(mods)) {
    +                    list.add(methods[i]);
    +                    skip.add(methodKey);
    +                }
    +            }
    +            c = c.getSuperclass();
    +        }
    +        return list.toArray(new Method[list.size()]);
    +    }
    +
    +    static Class loadAdapterClass(String className, byte[] classBytes)
    +    {
    +        Object staticDomain;
    +        Class domainClass = SecurityController.getStaticSecurityDomainClass();
    +        if(domainClass == CodeSource.class || domainClass == ProtectionDomain.class) {
    +            ProtectionDomain protectionDomain = JavaAdapter.class.getProtectionDomain();
    +            if(domainClass == CodeSource.class) {
    +                staticDomain = protectionDomain == null ? null : protectionDomain.getCodeSource();
    +            }
    +            else {
    +                staticDomain = protectionDomain;
    +            }
    +        }
    +        else {
    +            staticDomain = null;
    +        }
    +        GeneratedClassLoader loader = SecurityController.createLoader(null, 
    +                staticDomain);
    +        Class result = loader.defineClass(className, classBytes);
    +        loader.linkClass(result);
    +        return result;
    +    }
    +
    +    public static Function getFunction(Scriptable obj, String functionName)
    +    {
    +        Object x = ScriptableObject.getProperty(obj, functionName);
    +        if (x == Scriptable.NOT_FOUND) {
    +            // This method used to swallow the exception from calling
    +            // an undefined method. People have come to depend on this
    +            // somewhat dubious behavior. It allows people to avoid
    +            // implementing listener methods that they don't care about,
    +            // for instance.
    +            return null;
    +        }
    +        if (!(x instanceof Function))
    +            throw ScriptRuntime.notFunctionError(x, functionName);
    +
    +        return (Function)x;
    +    }
    +
    +    /**
    +     * Utility method which dynamically binds a Context to the current thread,
    +     * if none already exists.
    +     */
    +    public static Object callMethod(ContextFactory factory,
    +                                    final Scriptable thisObj,
    +                                    final Function f, final Object[] args,
    +                                    final long argsToWrap)
    +    {
    +        if (f == null) {
    +            // See comments in getFunction
    +            return Undefined.instance;
    +        }
    +        if (factory == null) {
    +            factory = ContextFactory.getGlobal();
    +        }
    +
    +        final Scriptable scope = f.getParentScope();
    +        if (argsToWrap == 0) {
    +            return Context.call(factory, f, scope, thisObj, args);
    +        }
    +
    +        Context cx = Context.getCurrentContext();
    +        if (cx != null) {
    +            return doCall(cx, scope, thisObj, f, args, argsToWrap);
    +        } else {
    +            return factory.call(new ContextAction() {
    +                public Object run(Context cx)
    +                {
    +                    return doCall(cx, scope, thisObj, f, args, argsToWrap);
    +                }
    +            });
    +        }
    +    }
    +
    +    private static Object doCall(Context cx, Scriptable scope,
    +                                 Scriptable thisObj, Function f,
    +                                 Object[] args, long argsToWrap)
    +    {
    +        // Wrap the rest of objects
    +        for (int i = 0; i != args.length; ++i) {
    +            if (0 != (argsToWrap & (1 << i))) {
    +                Object arg = args[i];
    +                if (!(arg instanceof Scriptable)) {
    +                    args[i] = cx.getWrapFactory().wrap(cx, scope, arg,
    +                                                       null);
    +                }
    +            }
    +        }
    +        return f.call(cx, scope, thisObj, args);
    +    }
    +
    +    public static Scriptable runScript(final Script script)
    +    {
    +        return (Scriptable)Context.call(new ContextAction() {
    +            public Object run(Context cx)
    +            {
    +                ScriptableObject global = ScriptRuntime.getGlobal(cx);
    +                script.exec(cx, global);
    +                return global;
    +            }
    +        });
    +    }
    +
    +    private static void generateCtor(ClassFileWriter cfw, String adapterName,
    +                                     String superName)
    +    {
    +        cfw.startMethod("",
    +                        "(Lorg/mozilla/javascript/ContextFactory;"
    +                        +"Lorg/mozilla/javascript/Scriptable;)V",
    +                        ClassFileWriter.ACC_PUBLIC);
    +
    +        // Invoke base class constructor
    +        cfw.add(ByteCode.ALOAD_0);  // this
    +        cfw.addInvoke(ByteCode.INVOKESPECIAL, superName, "", "()V");
    +
    +        // Save parameter in instance variable "factory"
    +        cfw.add(ByteCode.ALOAD_0);  // this
    +        cfw.add(ByteCode.ALOAD_1);  // first arg: ContextFactory instance
    +        cfw.add(ByteCode.PUTFIELD, adapterName, "factory",
    +                "Lorg/mozilla/javascript/ContextFactory;");
    +
    +        // Save parameter in instance variable "delegee"
    +        cfw.add(ByteCode.ALOAD_0);  // this
    +        cfw.add(ByteCode.ALOAD_2);  // second arg: Scriptable delegee
    +        cfw.add(ByteCode.PUTFIELD, adapterName, "delegee",
    +                "Lorg/mozilla/javascript/Scriptable;");
    +
    +        cfw.add(ByteCode.ALOAD_0);  // this for the following PUTFIELD for self
    +        // create a wrapper object to be used as "this" in method calls
    +        cfw.add(ByteCode.ALOAD_2);  // the Scriptable delegee
    +        cfw.add(ByteCode.ALOAD_0);  // this
    +        cfw.addInvoke(ByteCode.INVOKESTATIC,
    +                      "org/mozilla/javascript/JavaAdapter",
    +                      "createAdapterWrapper",
    +                      "(Lorg/mozilla/javascript/Scriptable;"
    +                      +"Ljava/lang/Object;"
    +                      +")Lorg/mozilla/javascript/Scriptable;");
    +        cfw.add(ByteCode.PUTFIELD, adapterName, "self",
    +                "Lorg/mozilla/javascript/Scriptable;");
    +
    +        cfw.add(ByteCode.RETURN);
    +        cfw.stopMethod((short)3); // 3: this + factory + delegee
    +    }
    +
    +    private static void generateSerialCtor(ClassFileWriter cfw,
    +                                           String adapterName,
    +                                           String superName)
    +    {
    +        cfw.startMethod("",
    +                        "(Lorg/mozilla/javascript/ContextFactory;"
    +                        +"Lorg/mozilla/javascript/Scriptable;"
    +                        +"Lorg/mozilla/javascript/Scriptable;"
    +                        +")V",
    +                        ClassFileWriter.ACC_PUBLIC);
    +
    +        // Invoke base class constructor
    +        cfw.add(ByteCode.ALOAD_0);  // this
    +        cfw.addInvoke(ByteCode.INVOKESPECIAL, superName, "", "()V");
    +
    +        // Save parameter in instance variable "factory"
    +        cfw.add(ByteCode.ALOAD_0);  // this
    +        cfw.add(ByteCode.ALOAD_1);  // first arg: ContextFactory instance
    +        cfw.add(ByteCode.PUTFIELD, adapterName, "factory",
    +                "Lorg/mozilla/javascript/ContextFactory;");
    +
    +        // Save parameter in instance variable "delegee"
    +        cfw.add(ByteCode.ALOAD_0);  // this
    +        cfw.add(ByteCode.ALOAD_2);  // second arg: Scriptable delegee
    +        cfw.add(ByteCode.PUTFIELD, adapterName, "delegee",
    +                "Lorg/mozilla/javascript/Scriptable;");
    +        // save self
    +        cfw.add(ByteCode.ALOAD_0);  // this
    +        cfw.add(ByteCode.ALOAD_3);  // second arg: Scriptable self
    +        cfw.add(ByteCode.PUTFIELD, adapterName, "self",
    +                "Lorg/mozilla/javascript/Scriptable;");
    +
    +        cfw.add(ByteCode.RETURN);
    +        cfw.stopMethod((short)4); // 4: this + factory + delegee + self
    +    }
    +
    +    private static void generateEmptyCtor(ClassFileWriter cfw,
    +                                          String adapterName,
    +                                          String superName,
    +                                          String scriptClassName)
    +    {
    +        cfw.startMethod("", "()V", ClassFileWriter.ACC_PUBLIC);
    +
    +        // Invoke base class constructor
    +        cfw.add(ByteCode.ALOAD_0);  // this
    +        cfw.addInvoke(ByteCode.INVOKESPECIAL, superName, "", "()V");
    +
    +        // Set factory to null to use current global when necessary
    +        cfw.add(ByteCode.ALOAD_0);
    +        cfw.add(ByteCode.ACONST_NULL);
    +        cfw.add(ByteCode.PUTFIELD, adapterName, "factory",
    +                "Lorg/mozilla/javascript/ContextFactory;");
    +
    +        // Load script class
    +        cfw.add(ByteCode.NEW, scriptClassName);
    +        cfw.add(ByteCode.DUP);
    +        cfw.addInvoke(ByteCode.INVOKESPECIAL, scriptClassName, "", "()V");
    +
    +        // Run script and save resulting scope
    +        cfw.addInvoke(ByteCode.INVOKESTATIC,
    +                      "org/mozilla/javascript/JavaAdapter",
    +                      "runScript",
    +                      "(Lorg/mozilla/javascript/Script;"
    +                      +")Lorg/mozilla/javascript/Scriptable;");
    +        cfw.add(ByteCode.ASTORE_1);
    +
    +        // Save the Scriptable in instance variable "delegee"
    +        cfw.add(ByteCode.ALOAD_0);  // this
    +        cfw.add(ByteCode.ALOAD_1);  // the Scriptable
    +        cfw.add(ByteCode.PUTFIELD, adapterName, "delegee",
    +                "Lorg/mozilla/javascript/Scriptable;");
    +
    +        cfw.add(ByteCode.ALOAD_0);  // this for the following PUTFIELD for self
    +        // create a wrapper object to be used as "this" in method calls
    +        cfw.add(ByteCode.ALOAD_1);  // the Scriptable
    +        cfw.add(ByteCode.ALOAD_0);  // this
    +        cfw.addInvoke(ByteCode.INVOKESTATIC,
    +                      "org/mozilla/javascript/JavaAdapter",
    +                      "createAdapterWrapper",
    +                      "(Lorg/mozilla/javascript/Scriptable;"
    +                      +"Ljava/lang/Object;"
    +                      +")Lorg/mozilla/javascript/Scriptable;");
    +        cfw.add(ByteCode.PUTFIELD, adapterName, "self",
    +                "Lorg/mozilla/javascript/Scriptable;");
    +
    +        cfw.add(ByteCode.RETURN);
    +        cfw.stopMethod((short)2); // this + delegee
    +    }
    +
    +    /**
    +     * Generates code to wrap Java arguments into Object[].
    +     * Non-primitive Java types are left as-is pending conversion
    +     * in the helper method. Leaves the array object on the top of the stack.
    +     */
    +    static void generatePushWrappedArgs(ClassFileWriter cfw,
    +                                        Class[] argTypes,
    +                                        int arrayLength)
    +    {
    +        // push arguments
    +        cfw.addPush(arrayLength);
    +        cfw.add(ByteCode.ANEWARRAY, "java/lang/Object");
    +        int paramOffset = 1;
    +        for (int i = 0; i != argTypes.length; ++i) {
    +            cfw.add(ByteCode.DUP); // duplicate array reference
    +            cfw.addPush(i);
    +            paramOffset += generateWrapArg(cfw, paramOffset, argTypes[i]);
    +            cfw.add(ByteCode.AASTORE);
    +        }
    +    }
    +
    +    /**
    +     * Generates code to wrap Java argument into Object.
    +     * Non-primitive Java types are left unconverted pending conversion
    +     * in the helper method. Leaves the wrapper object on the top of the stack.
    +     */
    +    private static int generateWrapArg(ClassFileWriter cfw, int paramOffset,
    +                                       Class argType)
    +    {
    +        int size = 1;
    +        if (!argType.isPrimitive()) {
    +            cfw.add(ByteCode.ALOAD, paramOffset);
    +
    +        } else if (argType == Boolean.TYPE) {
    +            // wrap boolean values with java.lang.Boolean.
    +            cfw.add(ByteCode.NEW, "java/lang/Boolean");
    +            cfw.add(ByteCode.DUP);
    +            cfw.add(ByteCode.ILOAD, paramOffset);
    +            cfw.addInvoke(ByteCode.INVOKESPECIAL, "java/lang/Boolean",
    +                          "", "(Z)V");
    +
    +        } else if (argType == Character.TYPE) {
    +            // Create a string of length 1 using the character parameter.
    +            cfw.add(ByteCode.ILOAD, paramOffset);
    +            cfw.addInvoke(ByteCode.INVOKESTATIC, "java/lang/String",
    +                          "valueOf", "(C)Ljava/lang/String;");
    +
    +        } else {
    +            // convert all numeric values to java.lang.Double.
    +            cfw.add(ByteCode.NEW, "java/lang/Double");
    +            cfw.add(ByteCode.DUP);
    +            String typeName = argType.getName();
    +            switch (typeName.charAt(0)) {
    +            case 'b':
    +            case 's':
    +            case 'i':
    +                // load an int value, convert to double.
    +                cfw.add(ByteCode.ILOAD, paramOffset);
    +                cfw.add(ByteCode.I2D);
    +                break;
    +            case 'l':
    +                // load a long, convert to double.
    +                cfw.add(ByteCode.LLOAD, paramOffset);
    +                cfw.add(ByteCode.L2D);
    +                size = 2;
    +                break;
    +            case 'f':
    +                // load a float, convert to double.
    +                cfw.add(ByteCode.FLOAD, paramOffset);
    +                cfw.add(ByteCode.F2D);
    +                break;
    +            case 'd':
    +                cfw.add(ByteCode.DLOAD, paramOffset);
    +                size = 2;
    +                break;
    +            }
    +            cfw.addInvoke(ByteCode.INVOKESPECIAL, "java/lang/Double",
    +                          "", "(D)V");
    +        }
    +        return size;
    +    }
    +
    +    /**
    +     * Generates code to convert a wrapped value type to a primitive type.
    +     * Handles unwrapping java.lang.Boolean, and java.lang.Number types.
    +     * Generates the appropriate RETURN bytecode.
    +     */
    +    static void generateReturnResult(ClassFileWriter cfw, Class retType,
    +                                     boolean callConvertResult)
    +    {
    +        // wrap boolean values with java.lang.Boolean, convert all other
    +        // primitive values to java.lang.Double.
    +        if (retType == Void.TYPE) {
    +            cfw.add(ByteCode.POP);
    +            cfw.add(ByteCode.RETURN);
    +
    +        } else if (retType == Boolean.TYPE) {
    +            cfw.addInvoke(ByteCode.INVOKESTATIC,
    +                          "org/mozilla/javascript/Context",
    +                          "toBoolean", "(Ljava/lang/Object;)Z");
    +            cfw.add(ByteCode.IRETURN);
    +
    +        } else if (retType == Character.TYPE) {
    +            // characters are represented as strings in JavaScript.
    +            // return the first character.
    +            // first convert the value to a string if possible.
    +            cfw.addInvoke(ByteCode.INVOKESTATIC,
    +                          "org/mozilla/javascript/Context",
    +                          "toString",
    +                          "(Ljava/lang/Object;)Ljava/lang/String;");
    +            cfw.add(ByteCode.ICONST_0);
    +            cfw.addInvoke(ByteCode.INVOKEVIRTUAL, "java/lang/String",
    +                          "charAt", "(I)C");
    +            cfw.add(ByteCode.IRETURN);
    +
    +        } else if (retType.isPrimitive()) {
    +            cfw.addInvoke(ByteCode.INVOKESTATIC,
    +                          "org/mozilla/javascript/Context",
    +                          "toNumber", "(Ljava/lang/Object;)D");
    +            String typeName = retType.getName();
    +            switch (typeName.charAt(0)) {
    +            case 'b':
    +            case 's':
    +            case 'i':
    +                cfw.add(ByteCode.D2I);
    +                cfw.add(ByteCode.IRETURN);
    +                break;
    +            case 'l':
    +                cfw.add(ByteCode.D2L);
    +                cfw.add(ByteCode.LRETURN);
    +                break;
    +            case 'f':
    +                cfw.add(ByteCode.D2F);
    +                cfw.add(ByteCode.FRETURN);
    +                break;
    +            case 'd':
    +                cfw.add(ByteCode.DRETURN);
    +                break;
    +            default:
    +                throw new RuntimeException("Unexpected return type " +
    +                                           retType.toString());
    +            }
    +
    +        } else {
    +            String retTypeStr = retType.getName();
    +            if (callConvertResult) {
    +                cfw.addLoadConstant(retTypeStr);
    +                cfw.addInvoke(ByteCode.INVOKESTATIC,
    +                              "java/lang/Class",
    +                              "forName",
    +                              "(Ljava/lang/String;)Ljava/lang/Class;");
    +
    +                cfw.addInvoke(ByteCode.INVOKESTATIC,
    +                              "org/mozilla/javascript/JavaAdapter",
    +                              "convertResult",
    +                              "(Ljava/lang/Object;"
    +                              +"Ljava/lang/Class;"
    +                              +")Ljava/lang/Object;");
    +            }
    +            // Now cast to return type
    +            cfw.add(ByteCode.CHECKCAST, retTypeStr);
    +            cfw.add(ByteCode.ARETURN);
    +        }
    +    }
    +
    +    private static void generateMethod(ClassFileWriter cfw, String genName,
    +                                       String methodName, Class[] parms,
    +                                       Class returnType)
    +    {
    +        StringBuffer sb = new StringBuffer();
    +        int paramsEnd = appendMethodSignature(parms, returnType, sb);
    +        String methodSignature = sb.toString();
    +        cfw.startMethod(methodName, methodSignature,
    +                        ClassFileWriter.ACC_PUBLIC);
    +
    +        // Prepare stack to call method
    +
    +        // push factory
    +        cfw.add(ByteCode.ALOAD_0);
    +        cfw.add(ByteCode.GETFIELD, genName, "factory",
    +                "Lorg/mozilla/javascript/ContextFactory;");
    +
    +        // push self
    +        cfw.add(ByteCode.ALOAD_0);
    +        cfw.add(ByteCode.GETFIELD, genName, "self",
    +                "Lorg/mozilla/javascript/Scriptable;");
    +
    +        // push function
    +        cfw.add(ByteCode.ALOAD_0);
    +        cfw.add(ByteCode.GETFIELD, genName, "delegee",
    +                "Lorg/mozilla/javascript/Scriptable;");
    +        cfw.addPush(methodName);
    +        cfw.addInvoke(ByteCode.INVOKESTATIC,
    +                      "org/mozilla/javascript/JavaAdapter",
    +                      "getFunction",
    +                      "(Lorg/mozilla/javascript/Scriptable;"
    +                      +"Ljava/lang/String;"
    +                      +")Lorg/mozilla/javascript/Function;");
    +
    +        // push arguments
    +        generatePushWrappedArgs(cfw, parms, parms.length);
    +
    +        // push bits to indicate which parameters should be wrapped
    +        if (parms.length > 64) {
    +            // If it will be an issue, then passing a static boolean array
    +            // can be an option, but for now using simple bitmask
    +            throw Context.reportRuntimeError0(
    +                "JavaAdapter can not subclass methods with more then"
    +                +" 64 arguments.");
    +        }
    +        long convertionMask = 0;
    +        for (int i = 0; i != parms.length; ++i) {
    +            if (!parms[i].isPrimitive()) {
    +                convertionMask |= (1 << i);
    +            }
    +        }
    +        cfw.addPush(convertionMask);
    +
    +        // go through utility method, which creates a Context to run the
    +        // method in.
    +        cfw.addInvoke(ByteCode.INVOKESTATIC,
    +                      "org/mozilla/javascript/JavaAdapter",
    +                      "callMethod",
    +                      "(Lorg/mozilla/javascript/ContextFactory;"
    +                      +"Lorg/mozilla/javascript/Scriptable;"
    +                      +"Lorg/mozilla/javascript/Function;"
    +                      +"[Ljava/lang/Object;"
    +                      +"J"
    +                      +")Ljava/lang/Object;");
    +
    +        generateReturnResult(cfw, returnType, true);
    +
    +        cfw.stopMethod((short)paramsEnd);
    +    }
    +
    +    /**
    +     * Generates code to push typed parameters onto the operand stack
    +     * prior to a direct Java method call.
    +     */
    +    private static int generatePushParam(ClassFileWriter cfw, int paramOffset,
    +                                         Class paramType)
    +    {
    +        if (!paramType.isPrimitive()) {
    +            cfw.addALoad(paramOffset);
    +            return 1;
    +        }
    +        String typeName = paramType.getName();
    +        switch (typeName.charAt(0)) {
    +        case 'z':
    +        case 'b':
    +        case 'c':
    +        case 's':
    +        case 'i':
    +            // load an int value, convert to double.
    +            cfw.addILoad(paramOffset);
    +            return 1;
    +        case 'l':
    +            // load a long, convert to double.
    +            cfw.addLLoad(paramOffset);
    +            return 2;
    +        case 'f':
    +            // load a float, convert to double.
    +            cfw.addFLoad(paramOffset);
    +            return 1;
    +        case 'd':
    +            cfw.addDLoad(paramOffset);
    +            return 2;
    +        }
    +        throw Kit.codeBug();
    +    }
    +
    +    /**
    +     * Generates code to return a Java type, after calling a Java method
    +     * that returns the same type.
    +     * Generates the appropriate RETURN bytecode.
    +     */
    +    private static void generatePopResult(ClassFileWriter cfw,
    +                                          Class retType)
    +    {
    +        if (retType.isPrimitive()) {
    +            String typeName = retType.getName();
    +            switch (typeName.charAt(0)) {
    +            case 'b':
    +            case 'c':
    +            case 's':
    +            case 'i':
    +            case 'z':
    +                cfw.add(ByteCode.IRETURN);
    +                break;
    +            case 'l':
    +                cfw.add(ByteCode.LRETURN);
    +                break;
    +            case 'f':
    +                cfw.add(ByteCode.FRETURN);
    +                break;
    +            case 'd':
    +                cfw.add(ByteCode.DRETURN);
    +                break;
    +            }
    +        } else {
    +            cfw.add(ByteCode.ARETURN);
    +        }
    +    }
    +
    +    /**
    +     * Generates a method called "super$methodName()" which can be called
    +     * from JavaScript that is equivalent to calling "super.methodName()"
    +     * from Java. Eventually, this may be supported directly in JavaScript.
    +     */
    +    private static void generateSuper(ClassFileWriter cfw,
    +                                      String genName, String superName,
    +                                      String methodName, String methodSignature,
    +                                      Class[] parms, Class returnType)
    +    {
    +        cfw.startMethod("super$" + methodName, methodSignature,
    +                        ClassFileWriter.ACC_PUBLIC);
    +
    +        // push "this"
    +        cfw.add(ByteCode.ALOAD, 0);
    +
    +        // push the rest of the parameters.
    +        int paramOffset = 1;
    +        for (int i = 0; i < parms.length; i++) {
    +            paramOffset += generatePushParam(cfw, paramOffset, parms[i]);
    +        }
    +
    +        // call the superclass implementation of the method.
    +        cfw.addInvoke(ByteCode.INVOKESPECIAL,
    +                      superName,
    +                      methodName,
    +                      methodSignature);
    +
    +        // now, handle the return type appropriately.
    +        Class retType = returnType;
    +        if (!retType.equals(Void.TYPE)) {
    +            generatePopResult(cfw, retType);
    +        } else {
    +            cfw.add(ByteCode.RETURN);
    +        }
    +        cfw.stopMethod((short)(paramOffset + 1));
    +    }
    +
    +    /**
    +     * Returns a fully qualified method name concatenated with its signature.
    +     */
    +    private static String getMethodSignature(Method method, Class[] argTypes)
    +    {
    +        StringBuffer sb = new StringBuffer();
    +        appendMethodSignature(argTypes, method.getReturnType(), sb);
    +        return sb.toString();
    +    }
    +
    +    static int appendMethodSignature(Class[] argTypes,
    +                                     Class returnType,
    +                                     StringBuffer sb)
    +    {
    +        sb.append('(');
    +        int firstLocal = 1 + argTypes.length; // includes this.
    +        for (int i = 0; i < argTypes.length; i++) {
    +            Class type = argTypes[i];
    +            appendTypeString(sb, type);
    +            if (type == Long.TYPE || type == Double.TYPE) {
    +                // adjust for duble slot
    +                ++firstLocal;
    +            }
    +        }
    +        sb.append(')');
    +        appendTypeString(sb, returnType);
    +        return firstLocal;
    +    }
    +
    +    private static StringBuffer appendTypeString(StringBuffer sb, Class type)
    +    {
    +        while (type.isArray()) {
    +            sb.append('[');
    +            type = type.getComponentType();
    +        }
    +        if (type.isPrimitive()) {
    +            char typeLetter;
    +            if (type == Boolean.TYPE) {
    +                typeLetter = 'Z';
    +            } else if (type == Long.TYPE) {
    +                typeLetter = 'J';
    +            } else {
    +                String typeName = type.getName();
    +                typeLetter = Character.toUpperCase(typeName.charAt(0));
    +            }
    +            sb.append(typeLetter);
    +        } else {
    +            sb.append('L');
    +            sb.append(type.getName().replace('.', '/'));
    +            sb.append(';');
    +        }
    +        return sb;
    +    }
    +
    +    static int[] getArgsToConvert(Class[] argTypes)
    +    {
    +        int count = 0;
    +        for (int i = 0; i != argTypes.length; ++i) {
    +            if (!argTypes[i].isPrimitive())
    +                ++count;
    +        }
    +        if (count == 0)
    +            return null;
    +        int[] array = new int[count];
    +        count = 0;
    +        for (int i = 0; i != argTypes.length; ++i) {
    +            if (!argTypes[i].isPrimitive())
    +                array[count++] = i;
    +        }
    +        return array;
    +    }
    +
    +    private static final Object FTAG = new Object();
    +    private static final int Id_JavaAdapter = 1;
    +}
    diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/JavaMembers.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/JavaMembers.java
    new file mode 100644
    index 0000000..84ef2d4
    --- /dev/null
    +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/JavaMembers.java
    @@ -0,0 +1,935 @@
    +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
    + *
    + * ***** BEGIN LICENSE BLOCK *****
    + * Version: MPL 1.1/GPL 2.0
    + *
    + * The contents of this file are subject to the Mozilla Public License Version
    + * 1.1 (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.mozilla.org/MPL/
    + *
    + * Software distributed under the License is distributed on an "AS IS" basis,
    + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
    + * for the specific language governing rights and limitations under the
    + * License.
    + *
    + * The Original Code is Rhino code, released
    + * May 6, 1999.
    + *
    + * The Initial Developer of the Original Code is
    + * Netscape Communications Corporation.
    + * Portions created by the Initial Developer are Copyright (C) 1997-2000
    + * the Initial Developer. All Rights Reserved.
    + *
    + * Contributor(s):
    + *   Norris Boyd
    + *   Cameron McCormack
    + *   Frank Mitchell
    + *   Mike Shaver
    + *   Kurt Westerfeld
    + *
    + * Alternatively, the contents of this file may be used under the terms of
    + * the GNU General Public License Version 2 or later (the "GPL"), in which
    + * case the provisions of the GPL are applicable instead of those above. If
    + * you wish to allow use of your version of this file only under the terms of
    + * the GPL and not to allow others to use your version of this file under the
    + * MPL, indicate your decision by deleting the provisions above and replacing
    + * them with the notice and other provisions required by the GPL. If you do
    + * not delete the provisions above, a recipient may use your version of this
    + * file under either the MPL or the GPL.
    + *
    + * ***** END LICENSE BLOCK ***** */
    +
    +package org.mozilla.javascript;
    +
    +import java.lang.reflect.*;
    +import java.util.*;
    +
    +/**
    + *
    + * @author Mike Shaver
    + * @author Norris Boyd
    + * @see NativeJavaObject
    + * @see NativeJavaClass
    + */
    +class JavaMembers
    +{
    +    JavaMembers(Scriptable scope, Class cl)
    +    {
    +        this(scope, cl, false);
    +    }
    +
    +    JavaMembers(Scriptable scope, Class cl, boolean includeProtected)
    +    {
    +        try {
    +            Context cx = ContextFactory.getGlobal().enterContext();
    +            ClassShutter shutter = cx.getClassShutter();
    +            if (shutter != null && !shutter.visibleToScripts(cl.getName())) {
    +                throw Context.reportRuntimeError1("msg.access.prohibited",
    +                                                  cl.getName());
    +            }
    +            this.includePrivate = cx.hasFeature(
    +                Context.FEATURE_ENHANCED_JAVA_ACCESS);
    +            this.members = new Hashtable(23);
    +            this.staticMembers = new Hashtable(7);
    +            this.cl = cl;
    +            reflect(scope, includeProtected);
    +        } finally {
    +            Context.exit();
    +        }
    +    }
    +
    +    boolean has(String name, boolean isStatic)
    +    {
    +        Hashtable ht = isStatic ? staticMembers : members;
    +        Object obj = ht.get(name);
    +        if (obj != null) {
    +            return true;
    +        }
    +        return findExplicitFunction(name, isStatic) != null;
    +    }
    +
    +    Object get(Scriptable scope, String name, Object javaObject,
    +               boolean isStatic)
    +    {
    +        Hashtable ht = isStatic ? staticMembers : members;
    +        Object member = ht.get(name);
    +        if (!isStatic && member == null) {
    +            // Try to get static member from instance (LC3)
    +            member = staticMembers.get(name);
    +        }
    +        if (member == null) {
    +            member = this.getExplicitFunction(scope, name,
    +                                              javaObject, isStatic);
    +            if (member == null)
    +                return Scriptable.NOT_FOUND;
    +        }
    +        if (member instanceof Scriptable) {
    +            return member;
    +        }
    +        Context cx = Context.getContext();
    +        Object rval;
    +        Class type;
    +        try {
    +            if (member instanceof BeanProperty) {
    +                BeanProperty bp = (BeanProperty) member;
    +                if (bp.getter == null)
    +                    return Scriptable.NOT_FOUND;
    +                rval = bp.getter.invoke(javaObject, Context.emptyArgs);
    +                type = bp.getter.method().getReturnType();
    +            } else {
    +                Field field = (Field) member;
    +                rval = field.get(isStatic ? null : javaObject);
    +                type = field.getType();
    +            }
    +        } catch (Exception ex) {
    +            throw Context.throwAsScriptRuntimeEx(ex);
    +        }
    +        // Need to wrap the object before we return it.
    +        scope = ScriptableObject.getTopLevelScope(scope);
    +        return cx.getWrapFactory().wrap(cx, scope, rval, type);
    +    }
    +
    +    void put(Scriptable scope, String name, Object javaObject,
    +             Object value, boolean isStatic)
    +    {
    +        Hashtable ht = isStatic ? staticMembers : members;
    +        Object member = ht.get(name);
    +        if (!isStatic && member == null) {
    +            // Try to get static member from instance (LC3)
    +            member = staticMembers.get(name);
    +        }
    +        if (member == null)
    +            throw reportMemberNotFound(name);
    +        if (member instanceof FieldAndMethods) {
    +            FieldAndMethods fam = (FieldAndMethods) ht.get(name);
    +            member = fam.field;
    +        }
    +
    +        // Is this a bean property "set"?
    +        if (member instanceof BeanProperty) {
    +            BeanProperty bp = (BeanProperty)member;
    +            if (bp.setter == null) {
    +                throw reportMemberNotFound(name);
    +            }
    +            // If there's only one setter or if the value is null, use the
    +            // main setter. Otherwise, let the NativeJavaMethod decide which
    +            // setter to use:
    +            if (bp.setters == null || value == null) {
    +                Class setType = bp.setter.argTypes[0];
    +                Object[] args = { Context.jsToJava(value, setType) };
    +                try {
    +                    bp.setter.invoke(javaObject, args);
    +                } catch (Exception ex) {
    +                  throw Context.throwAsScriptRuntimeEx(ex);
    +                }
    +            } else {
    +                Object[] args = { value };
    +                bp.setters.call(Context.getContext(),
    +                                ScriptableObject.getTopLevelScope(scope),
    +                                scope, args);
    +            }
    +        }
    +        else {
    +            if (!(member instanceof Field)) {
    +                String str = (member == null) ? "msg.java.internal.private"
    +                                              : "msg.java.method.assign";
    +                throw Context.reportRuntimeError1(str, name);
    +            }
    +            Field field = (Field)member;
    +            Object javaValue = Context.jsToJava(value, field.getType());
    +            try {
    +                field.set(javaObject, javaValue);
    +            } catch (IllegalAccessException accessEx) {
    +                if ((field.getModifiers() & Modifier.FINAL) != 0) {
    +                    // treat Java final the same as JavaScript [[READONLY]]
    +                    return;
    +                }
    +                throw Context.throwAsScriptRuntimeEx(accessEx);
    +            } catch (IllegalArgumentException argEx) {
    +                throw Context.reportRuntimeError3(
    +                    "msg.java.internal.field.type",
    +                    value.getClass().getName(), field,
    +                    javaObject.getClass().getName());
    +            }
    +        }
    +    }
    +
    +    Object[] getIds(boolean isStatic)
    +    {
    +        Hashtable ht = isStatic ? staticMembers : members;
    +        int len = ht.size();
    +        Object[] result = new Object[len];
    +        Enumeration keys = ht.keys();
    +        for (int i=0; i < len; i++)
    +            result[i] = keys.nextElement();
    +        return result;
    +    }
    +
    +    static String javaSignature(Class type)
    +    {
    +        if (!type.isArray()) {
    +            return type.getName();
    +        } else {
    +            int arrayDimension = 0;
    +            do {
    +                ++arrayDimension;
    +                type = type.getComponentType();
    +            } while (type.isArray());
    +            String name = type.getName();
    +            String suffix = "[]";
    +            if (arrayDimension == 1) {
    +                return name.concat(suffix);
    +            } else {
    +                int length = name.length() + arrayDimension * suffix.length();
    +                StringBuffer sb = new StringBuffer(length);
    +                sb.append(name);
    +                while (arrayDimension != 0) {
    +                    --arrayDimension;
    +                    sb.append(suffix);
    +                }
    +                return sb.toString();
    +            }
    +        }
    +    }
    +
    +    static String liveConnectSignature(Class[] argTypes)
    +    {
    +        int N = argTypes.length;
    +        if (N == 0) { return "()"; }
    +        StringBuffer sb = new StringBuffer();
    +        sb.append('(');
    +        for (int i = 0; i != N; ++i) {
    +            if (i != 0) {
    +                sb.append(',');
    +            }
    +            sb.append(javaSignature(argTypes[i]));
    +        }
    +        sb.append(')');
    +        return sb.toString();
    +    }
    +
    +    private MemberBox findExplicitFunction(String name, boolean isStatic)
    +    {
    +        int sigStart = name.indexOf('(');
    +        if (sigStart < 0) { return null; }
    +
    +        Hashtable ht = isStatic ? staticMembers : members;
    +        MemberBox[] methodsOrCtors = null;
    +        boolean isCtor = (isStatic && sigStart == 0);
    +
    +        if (isCtor) {
    +            // Explicit request for an overloaded constructor
    +            methodsOrCtors = ctors;
    +        } else {
    +            // Explicit request for an overloaded method
    +            String trueName = name.substring(0,sigStart);
    +            Object obj = ht.get(trueName);
    +            if (!isStatic && obj == null) {
    +                // Try to get static member from instance (LC3)
    +                obj = staticMembers.get(trueName);
    +            }
    +            if (obj instanceof NativeJavaMethod) {
    +                NativeJavaMethod njm = (NativeJavaMethod)obj;
    +                methodsOrCtors = njm.methods;
    +            }
    +        }
    +
    +        if (methodsOrCtors != null) {
    +            for (int i = 0; i < methodsOrCtors.length; i++) {
    +                Class[] type = methodsOrCtors[i].argTypes;
    +                String sig = liveConnectSignature(type);
    +                if (sigStart + sig.length() == name.length()
    +                    && name.regionMatches(sigStart, sig, 0, sig.length()))
    +                {
    +                    return methodsOrCtors[i];
    +                }
    +            }
    +        }
    +
    +        return null;
    +    }
    +
    +    private Object getExplicitFunction(Scriptable scope, String name,
    +                                       Object javaObject, boolean isStatic)
    +    {
    +        Hashtable ht = isStatic ? staticMembers : members;
    +        Object member = null;
    +        MemberBox methodOrCtor = findExplicitFunction(name, isStatic);
    +
    +        if (methodOrCtor != null) {
    +            Scriptable prototype =
    +                ScriptableObject.getFunctionPrototype(scope);
    +
    +            if (methodOrCtor.isCtor()) {
    +                NativeJavaConstructor fun =
    +                    new NativeJavaConstructor(methodOrCtor);
    +                fun.setPrototype(prototype);
    +                member = fun;
    +                ht.put(name, fun);
    +            } else {
    +                String trueName = methodOrCtor.getName();
    +                member = ht.get(trueName);
    +
    +                if (member instanceof NativeJavaMethod &&
    +                    ((NativeJavaMethod)member).methods.length > 1 ) {
    +                    NativeJavaMethod fun =
    +                        new NativeJavaMethod(methodOrCtor, name);
    +                    fun.setPrototype(prototype);
    +                    ht.put(name, fun);
    +                    member = fun;
    +                }
    +            }
    +        }
    +
    +        return member;
    +    }
    +
    +    /**
    +     * Retrieves mapping of methods to accessible methods for a class.
    +     * In case the class is not public, retrieves methods with same 
    +     * signature as its public methods from public superclasses and 
    +     * interfaces (if they exist). Basically upcasts every method to the 
    +     * nearest accessible method.
    +     */
    +    private static Method[] discoverAccessibleMethods(Class clazz, 
    +                                                      boolean includeProtected,
    +                                                      boolean includePrivate)
    +    {
    +        Map map = new HashMap();
    +        discoverAccessibleMethods(clazz, map, includeProtected, includePrivate);
    +        return (Method[])map.values().toArray(new Method[map.size()]);
    +    }
    +    
    +    private static void discoverAccessibleMethods(Class clazz, Map map,
    +                                                  boolean includeProtected,
    +                                                  boolean includePrivate)
    +    {
    +        if (Modifier.isPublic(clazz.getModifiers()) || includePrivate) {
    +            try {
    +                if (includeProtected || includePrivate) {
    +                    while (clazz != null) {
    +                        try {
    +                            Method[] methods = clazz.getDeclaredMethods();
    +                            for (int i = 0; i < methods.length; i++) {
    +                                Method method = methods[i];
    +                                int mods = method.getModifiers();
    +    
    +                                if (Modifier.isPublic(mods) ||
    +                                    Modifier.isProtected(mods) ||
    +                                    includePrivate)
    +                                {
    +                                    if (includePrivate)
    +                                        method.setAccessible(true);
    +                                    map.put(new MethodSignature(method), method);
    +                                }
    +                            }
    +                            clazz = clazz.getSuperclass();
    +                        } catch (SecurityException e) {
    +                            // Some security settings (i.e., applets) disallow
    +                            // access to Class.getDeclaredMethods. Fall back to
    +                            // Class.getMethods.
    +                            Method[] methods = clazz.getMethods();
    +                            for (int i = 0; i < methods.length; i++) {
    +                                Method method = methods[i];
    +                                MethodSignature sig 
    +                                    = new MethodSignature(method);
    +                                if (map.get(sig) == null)
    +                                    map.put(sig, method);
    +                            }
    +                            break; // getMethods gets superclass methods, no
    +                                   // need to loop any more
    +                        }
    +                    }
    +                } else {
    +                    Method[] methods = clazz.getMethods();
    +                    for (int i = 0; i < methods.length; i++) {
    +                        Method method = methods[i];
    +                        MethodSignature sig = new MethodSignature(method);
    +                        map.put(sig, method);
    +                    }
    +                }
    +                return;
    +            } catch (SecurityException e) {
    +                Context.reportWarning(
    +                        "Could not discover accessible methods of class " +
    +                            clazz.getName() + " due to lack of privileges, " +
    +                            "attemping superclasses/interfaces.");
    +                // Fall through and attempt to discover superclass/interface
    +                // methods
    +            }
    +        }
    +
    +        Class[] interfaces = clazz.getInterfaces();
    +        for (int i = 0; i < interfaces.length; i++) {
    +            discoverAccessibleMethods(interfaces[i], map, includeProtected,
    +                    includePrivate);
    +        }
    +        Class superclass = clazz.getSuperclass();
    +        if (superclass != null) {
    +            discoverAccessibleMethods(superclass, map, includeProtected,
    +                    includePrivate);
    +        }
    +    }
    +
    +    private static final class MethodSignature
    +    {
    +        private final String name;
    +        private final Class[] args;
    +        
    +        private MethodSignature(String name, Class[] args)
    +        {
    +            this.name = name;
    +            this.args = args;
    +        }
    +        
    +        MethodSignature(Method method)
    +        {
    +            this(method.getName(), method.getParameterTypes());
    +        }
    +        
    +        public boolean equals(Object o)
    +        {
    +            if(o instanceof MethodSignature)
    +            {
    +                MethodSignature ms = (MethodSignature)o;
    +                return ms.name.equals(name) && Arrays.equals(args, ms.args);
    +            }
    +            return false;
    +        }
    +        
    +        public int hashCode()
    +        {
    +            return name.hashCode() ^ args.length;
    +        }
    +    }
    +    
    +    private void reflect(Scriptable scope, boolean includeProtected)
    +    {
    +        // We reflect methods first, because we want overloaded field/method
    +        // names to be allocated to the NativeJavaMethod before the field
    +        // gets in the way.
    +
    +        Method[] methods = discoverAccessibleMethods(cl, includeProtected,
    +                                                     includePrivate);
    +        for (int i = 0; i < methods.length; i++) {
    +            Method method = methods[i];
    +            int mods = method.getModifiers();
    +            boolean isStatic = Modifier.isStatic(mods);
    +            Hashtable ht = isStatic ? staticMembers : members;
    +            String name = method.getName();
    +            Object value = ht.get(name);
    +            if (value == null) {
    +                ht.put(name, method);
    +            } else {
    +                ObjArray overloadedMethods;
    +                if (value instanceof ObjArray) {
    +                    overloadedMethods = (ObjArray)value;
    +                } else {
    +                    if (!(value instanceof Method)) Kit.codeBug();
    +                    // value should be instance of Method as at this stage
    +                    // staticMembers and members can only contain methods
    +                    overloadedMethods = new ObjArray();
    +                    overloadedMethods.add(value);
    +                    ht.put(name, overloadedMethods);
    +                }
    +                overloadedMethods.add(method);
    +            }
    +        }
    +
    +        // replace Method instances by wrapped NativeJavaMethod objects
    +        // first in staticMembers and then in members
    +        for (int tableCursor = 0; tableCursor != 2; ++tableCursor) {
    +            boolean isStatic = (tableCursor == 0);
    +            Hashtable ht = (isStatic) ? staticMembers : members;
    +            Enumeration e = ht.keys();
    +            while (e.hasMoreElements()) {
    +                String name = (String)e.nextElement();
    +                MemberBox[] methodBoxes;
    +                Object value = ht.get(name);
    +                if (value instanceof Method) {
    +                    methodBoxes = new MemberBox[1];
    +                    methodBoxes[0] = new MemberBox((Method)value);
    +                } else {
    +                    ObjArray overloadedMethods = (ObjArray)value;
    +                    int N = overloadedMethods.size();
    +                    if (N < 2) Kit.codeBug();
    +                    methodBoxes = new MemberBox[N];
    +                    for (int i = 0; i != N; ++i) {
    +                        Method method = (Method)overloadedMethods.get(i);
    +                        methodBoxes[i] = new MemberBox(method);
    +                    }
    +                }
    +                NativeJavaMethod fun = new NativeJavaMethod(methodBoxes);
    +                if (scope != null) {
    +                    ScriptRuntime.setFunctionProtoAndParent(fun, scope);
    +                }
    +                ht.put(name, fun);
    +            }
    +        }
    +
    +        // Reflect fields.
    +        Field[] fields = getAccessibleFields();
    +        for (int i = 0; i < fields.length; i++) {
    +            Field field = fields[i];
    +            String name = field.getName();
    +            int mods = field.getModifiers();
    +            if (!includePrivate && !Modifier.isPublic(mods)) {
    +                continue;
    +            }
    +            try {
    +                boolean isStatic = Modifier.isStatic(mods);
    +                Hashtable ht = isStatic ? staticMembers : members;
    +                Object member = ht.get(name);
    +                if (member == null) {
    +                    ht.put(name, field);
    +                } else if (member instanceof NativeJavaMethod) {
    +                    NativeJavaMethod method = (NativeJavaMethod) member;
    +                    FieldAndMethods fam
    +                        = new FieldAndMethods(scope, method.methods, field);
    +                    Hashtable fmht = isStatic ? staticFieldAndMethods
    +                                              : fieldAndMethods;
    +                    if (fmht == null) {
    +                        fmht = new Hashtable(4);
    +                        if (isStatic) {
    +                            staticFieldAndMethods = fmht;
    +                        } else {
    +                            fieldAndMethods = fmht;
    +                        }
    +                    }
    +                    fmht.put(name, fam);
    +                    ht.put(name, fam);
    +                } else if (member instanceof Field) {
    +                    Field oldField = (Field) member;
    +                    // If this newly reflected field shadows an inherited field,
    +                    // then replace it. Otherwise, since access to the field
    +                    // would be ambiguous from Java, no field should be
    +                    // reflected.
    +                    // For now, the first field found wins, unless another field
    +                    // explicitly shadows it.
    +                    if (oldField.getDeclaringClass().
    +                            isAssignableFrom(field.getDeclaringClass()))
    +                    {
    +                        ht.put(name, field);
    +                    }
    +                } else {
    +                    // "unknown member type"
    +                    Kit.codeBug();
    +                }
    +            } catch (SecurityException e) {
    +                // skip this field
    +                Context.reportWarning("Could not access field "
    +                        + name + " of class " + cl.getName() +
    +                        " due to lack of privileges.");
    +            }
    +        }
    +
    +        // Create bean propeties from corresponding get/set methods first for
    +        // static members and then for instance members
    +        for (int tableCursor = 0; tableCursor != 2; ++tableCursor) {
    +            boolean isStatic = (tableCursor == 0);
    +            Hashtable ht = (isStatic) ? staticMembers : members;
    +
    +            Hashtable toAdd = new Hashtable();
    +
    +            // Now, For each member, make "bean" properties.
    +            for (Enumeration e = ht.keys(); e.hasMoreElements(); ) {
    +
    +                // Is this a getter?
    +                String name = (String) e.nextElement();
    +                boolean memberIsGetMethod = name.startsWith("get");
    +                boolean memberIsSetMethod = name.startsWith("set");
    +                boolean memberIsIsMethod = name.startsWith("is");
    +                if (memberIsGetMethod || memberIsIsMethod 
    +                        || memberIsSetMethod) {
    +                    // Double check name component.
    +                    String nameComponent
    +                        = name.substring(memberIsIsMethod ? 2 : 3);
    +                    if (nameComponent.length() == 0)
    +                        continue;
    +
    +                    // Make the bean property name.
    +                    String beanPropertyName = nameComponent;
    +                    char ch0 = nameComponent.charAt(0);
    +                    if (Character.isUpperCase(ch0)) {
    +                        if (nameComponent.length() == 1) {
    +                            beanPropertyName = nameComponent.toLowerCase();
    +                        } else {
    +                            char ch1 = nameComponent.charAt(1);
    +                            if (!Character.isUpperCase(ch1)) {
    +                                beanPropertyName = Character.toLowerCase(ch0)
    +                                                   +nameComponent.substring(1);
    +                            }
    +                        }
    +                    }
    +
    +                    // If we already have a member by this name, don't do this
    +                    // property.
    +                    if (ht.containsKey(beanPropertyName)
    +                            || toAdd.containsKey(beanPropertyName)) {
    +                        continue;
    +                    }
    +
    +                    // Find the getter method, or if there is none, the is-
    +                    // method.
    +                    MemberBox getter = null;
    +                    getter = findGetter(isStatic, ht, "get", nameComponent);
    +                    // If there was no valid getter, check for an is- method.
    +                    if (getter == null) {
    +                        getter = findGetter(isStatic, ht, "is", nameComponent);
    +                    }
    +
    +                    // setter
    +                    MemberBox setter = null;
    +                    NativeJavaMethod setters = null;
    +                    String setterName = "set".concat(nameComponent);
    +
    +                    if (ht.containsKey(setterName)) {
    +                        // Is this value a method?
    +                        Object member = ht.get(setterName);
    +                        if (member instanceof NativeJavaMethod) {
    +                            NativeJavaMethod njmSet = (NativeJavaMethod)member;
    +                            if (getter != null) {
    +                                // We have a getter. Now, do we have a matching 
    +                                // setter?
    +                                Class type = getter.method().getReturnType();
    +                                setter = extractSetMethod(type, njmSet.methods,
    +                                                            isStatic);
    +                            } else {
    +                                // No getter, find any set method
    +                                setter = extractSetMethod(njmSet.methods, 
    +                                                            isStatic);
    +                            }
    +                            if (njmSet.methods.length > 1) {
    +                                setters = njmSet;
    +                            }
    +                        }
    +                    }
    +                    // Make the property.
    +                    BeanProperty bp = new BeanProperty(getter, setter,
    +                                                       setters);
    +                    toAdd.put(beanPropertyName, bp);
    +                }
    +            }
    +
    +            // Add the new bean properties.
    +            for (Enumeration e = toAdd.keys(); e.hasMoreElements();) {
    +                Object key = e.nextElement();
    +                Object value = toAdd.get(key);
    +                ht.put(key, value);
    +            }
    +        }
    +
    +        // Reflect constructors
    +        Constructor[] constructors = getAccessibleConstructors();
    +        ctors = new MemberBox[constructors.length];
    +        for (int i = 0; i != constructors.length; ++i) {
    +            ctors[i] = new MemberBox(constructors[i]);
    +        }
    +    }
    +
    +    private Constructor[] getAccessibleConstructors()
    +    {
    +      // The JVM currently doesn't allow changing access on java.lang.Class
    +      // constructors, so don't try
    +      if (includePrivate && cl != ScriptRuntime.ClassClass) {
    +          try {
    +              Constructor[] cons = cl.getDeclaredConstructors();
    +              Constructor.setAccessible(cons, true);
    +
    +              return cons;
    +          } catch (SecurityException e) {
    +              // Fall through to !includePrivate case
    +              Context.reportWarning("Could not access constructor " +
    +                    " of class " + cl.getName() +
    +                    " due to lack of privileges.");
    +          }
    +      }
    +      return cl.getConstructors();
    +    }
    +
    +    private Field[] getAccessibleFields() {
    +        if (includePrivate) {
    +            try {
    +                ArrayList fieldsList = new ArrayList();
    +                Class currentClass = cl;
    +
    +                while (currentClass != null) {
    +                    // get all declared fields in this class, make them
    +                    // accessible, and save
    +                    Field[] declared = currentClass.getDeclaredFields();
    +                    for (int i = 0; i < declared.length; i++) {
    +                        declared[i].setAccessible(true);
    +                        fieldsList.add(declared[i]);
    +                    }
    +                    // walk up superclass chain.  no need to deal specially with
    +                    // interfaces, since they can't have fields
    +                    currentClass = currentClass.getSuperclass();
    +                }
    +
    +                return (Field[]) fieldsList.toArray(
    +                        new Field[fieldsList.size()]);
    +            } catch (SecurityException e) {
    +                // fall through to !includePrivate case
    +            }
    +        }
    +        return cl.getFields();
    +    }
    +
    +    private MemberBox findGetter(boolean isStatic, Hashtable ht, String prefix,
    +                                 String propertyName)
    +    {
    +        String getterName = prefix.concat(propertyName);
    +        if (ht.containsKey(getterName)) {
    +            // Check that the getter is a method.
    +            Object member = ht.get(getterName);
    +            if (member instanceof NativeJavaMethod) {
    +                NativeJavaMethod njmGet = (NativeJavaMethod) member;
    +                return extractGetMethod(njmGet.methods, isStatic);
    +            }
    +        }
    +        return null;
    +    }
    +
    +    private static MemberBox extractGetMethod(MemberBox[] methods,
    +                                              boolean isStatic)
    +    {
    +        // Inspect the list of all MemberBox for the only one having no
    +        // parameters
    +        for (int methodIdx = 0; methodIdx < methods.length; methodIdx++) {
    +            MemberBox method = methods[methodIdx];
    +            // Does getter method have an empty parameter list with a return
    +            // value (eg. a getSomething() or isSomething())?
    +            if (method.argTypes.length == 0
    +                && (!isStatic || method.isStatic()))
    +            {
    +                Class type = method.method().getReturnType();
    +                if (type != Void.TYPE) {
    +                    return method;
    +                }
    +                break;
    +            }
    +        }
    +        return null;
    +    }
    +
    +    private static MemberBox extractSetMethod(Class type, MemberBox[] methods,
    +                                              boolean isStatic)
    +    {
    +        //
    +        // Note: it may be preferable to allow NativeJavaMethod.findFunction()
    +        //       to find the appropriate setter; unfortunately, it requires an
    +        //       instance of the target arg to determine that.
    +        //
    +
    +        // Make two passes: one to find a method with direct type assignment,
    +        // and one to find a widening conversion.
    +        for (int pass = 1; pass <= 2; ++pass) {
    +            for (int i = 0; i < methods.length; ++i) {
    +                MemberBox method = methods[i];
    +                if (!isStatic || method.isStatic()) {
    +                    Class[] params = method.argTypes;
    +                    if (params.length == 1) {
    +                        if (pass == 1) {
    +                            if (params[0] == type) {
    +                                return method;
    +                            }
    +                        } else {
    +                            if (pass != 2) Kit.codeBug();
    +                            if (params[0].isAssignableFrom(type)) {
    +                                return method;
    +                            }
    +                        }
    +                    }
    +                }
    +            }
    +        }
    +        return null;
    +    }
    +
    +    private static MemberBox extractSetMethod(MemberBox[] methods,
    +                                              boolean isStatic)
    +    {
    +
    +        for (int i = 0; i < methods.length; ++i) {
    +            MemberBox method = methods[i];
    +            if (!isStatic || method.isStatic()) {
    +                if (method.method().getReturnType() == Void.TYPE) {
    +                    if (method.argTypes.length == 1) {
    +                        return method;
    +                    }
    +                }
    +            }
    +        }
    +        return null;
    +    }
    +
    +    Hashtable getFieldAndMethodsObjects(Scriptable scope, Object javaObject,
    +                                        boolean isStatic)
    +    {
    +        Hashtable ht = isStatic ? staticFieldAndMethods : fieldAndMethods;
    +        if (ht == null)
    +            return null;
    +        int len = ht.size();
    +        Hashtable result = new Hashtable(len);
    +        Enumeration e = ht.elements();
    +        while (len-- > 0) {
    +            FieldAndMethods fam = (FieldAndMethods) e.nextElement();
    +            FieldAndMethods famNew = new FieldAndMethods(scope, fam.methods,
    +                                                         fam.field);
    +            famNew.javaObject = javaObject;
    +            result.put(fam.field.getName(), famNew);
    +        }
    +        return result;
    +    }
    +
    +    static JavaMembers lookupClass(Scriptable scope, Class dynamicType,
    +                                   Class staticType, boolean includeProtected)
    +    {
    +        JavaMembers members;
    +        scope = ScriptableObject.getTopLevelScope(scope);
    +        ClassCache cache = ClassCache.get(scope);
    +        Map,JavaMembers> ct = cache.getClassCacheMap();
    +
    +        Class cl = dynamicType;
    +        for (;;) {
    +            members = ct.get(cl);
    +            if (members != null) {
    +                return members;
    +            }
    +            try {
    +                members = new JavaMembers(scope, cl, includeProtected);
    +                break;
    +            } catch (SecurityException e) {
    +                // Reflection may fail for objects that are in a restricted
    +                // access package (e.g. sun.*).  If we get a security
    +                // exception, try again with the static type if it is interface.
    +                // Otherwise, try superclass
    +                if (staticType != null && staticType.isInterface()) {
    +                    cl = staticType;
    +                    staticType = null; // try staticType only once
    +                } else {
    +                    Class parent = cl.getSuperclass();
    +                    if (parent == null) {
    +                        if (cl.isInterface()) {
    +                            // last resort after failed staticType interface
    +                            parent = ScriptRuntime.ObjectClass;
    +                        } else {
    +                            throw e;
    +                        }
    +                    }
    +                    cl = parent;
    +                }
    +            }
    +        }
    +
    +        if (cache.isCachingEnabled())
    +            ct.put(cl, members);
    +        return members;
    +    }
    +
    +    RuntimeException reportMemberNotFound(String memberName)
    +    {
    +        return Context.reportRuntimeError2(
    +            "msg.java.member.not.found", cl.getName(), memberName);
    +    }
    +
    +    private Class cl;
    +    private Hashtable members;
    +    private Hashtable fieldAndMethods;
    +    private Hashtable staticMembers;
    +    private Hashtable staticFieldAndMethods;
    +    MemberBox[] ctors;
    +    private boolean includePrivate;
    +}
    +
    +class BeanProperty
    +{
    +    BeanProperty(MemberBox getter, MemberBox setter, NativeJavaMethod setters)
    +    {
    +        this.getter = getter;
    +        this.setter = setter;
    +        this.setters = setters;
    +    }
    +
    +    MemberBox getter;
    +    MemberBox setter;
    +    NativeJavaMethod setters;
    +}
    +
    +class FieldAndMethods extends NativeJavaMethod
    +{
    +    static final long serialVersionUID = -9222428244284796755L;
    +
    +    FieldAndMethods(Scriptable scope, MemberBox[] methods, Field field)
    +    {
    +        super(methods);
    +        this.field = field;
    +        setParentScope(scope);
    +        setPrototype(ScriptableObject.getFunctionPrototype(scope));
    +    }
    +
    +    public Object getDefaultValue(Class hint)
    +    {
    +        if (hint == ScriptRuntime.FunctionClass)
    +            return this;
    +        Object rval;
    +        Class type;
    +        try {
    +            rval = field.get(javaObject);
    +            type = field.getType();
    +        } catch (IllegalAccessException accEx) {
    +            throw Context.reportRuntimeError1(
    +                "msg.java.internal.private", field.getName());
    +        }
    +        Context cx  = Context.getContext();
    +        rval = cx.getWrapFactory().wrap(cx, this, rval, type);
    +        if (rval instanceof Scriptable) {
    +            rval = ((Scriptable) rval).getDefaultValue(hint);
    +        }
    +        return rval;
    +    }
    +
    +    Field field;
    +    Object javaObject;
    +}
    diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/JavaScriptException.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/JavaScriptException.java
    new file mode 100644
    index 0000000..11ebedf
    --- /dev/null
    +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/JavaScriptException.java
    @@ -0,0 +1,117 @@
    +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
    + *
    + * ***** BEGIN LICENSE BLOCK *****
    + * Version: MPL 1.1/GPL 2.0
    + *
    + * The contents of this file are subject to the Mozilla Public License Version
    + * 1.1 (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.mozilla.org/MPL/
    + *
    + * Software distributed under the License is distributed on an "AS IS" basis,
    + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
    + * for the specific language governing rights and limitations under the
    + * License.
    + *
    + * The Original Code is Rhino code, released
    + * May 6, 1999.
    + *
    + * The Initial Developer of the Original Code is
    + * Netscape Communications Corporation.
    + * Portions created by the Initial Developer are Copyright (C) 1997-1999
    + * the Initial Developer. All Rights Reserved.
    + *
    + * Contributor(s):
    + *   Norris Boyd
    + *   Bojan Cekrlic
    + *   Hannes Wallnoefer
    + *
    + * Alternatively, the contents of this file may be used under the terms of
    + * the GNU General Public License Version 2 or later (the "GPL"), in which
    + * case the provisions of the GPL are applicable instead of those above. If
    + * you wish to allow use of your version of this file only under the terms of
    + * the GPL and not to allow others to use your version of this file under the
    + * MPL, indicate your decision by deleting the provisions above and replacing
    + * them with the notice and other provisions required by the GPL. If you do
    + * not delete the provisions above, a recipient may use your version of this
    + * file under either the MPL or the GPL.
    + *
    + * ***** END LICENSE BLOCK ***** */
    +
    +// API class
    +
    +package org.mozilla.javascript;
    +
    +/**
    + * Java reflection of JavaScript exceptions.
    + * Instances of this class are thrown by the JavaScript 'throw' keyword.
    + *
    + * @author Mike McCabe
    + */
    +public class JavaScriptException extends RhinoException
    +{
    +    static final long serialVersionUID = -7666130513694669293L;
    +
    +    /**
    +     * @deprecated
    +     * Use {@link WrappedException#WrappedException(Throwable)} to report
    +     * exceptions in Java code.
    +     */
    +    public JavaScriptException(Object value)
    +    {
    +        this(value, "", 0);
    +    }
    +
    +    /**
    +     * Create a JavaScript exception wrapping the given JavaScript value
    +     *
    +     * @param value the JavaScript value thrown.
    +     */
    +    public JavaScriptException(Object value, String sourceName, int lineNumber)
    +    {
    +        recordErrorOrigin(sourceName, lineNumber, null, 0);
    +        this.value = value;
    +    }
    +
    +    public String details()
    +    {
    +       try {
    +           return ScriptRuntime.toString(value);
    +       } catch (RuntimeException rte) {
    +           // ScriptRuntime.toString may throw a RuntimeException
    +           if (value == null) {
    +               return "null";
    +           } else if (value instanceof Scriptable) {
    +               return ScriptRuntime.defaultObjectToString((Scriptable)value);
    +           } else {
    +               return value.toString();
    +           }
    +       }
    +    }
    +
    +    /**
    +     * @return the value wrapped by this exception
    +     */
    +    public Object getValue()
    +    {
    +        return value;
    +    }
    +
    +    /**
    +     * @deprecated Use {@link RhinoException#sourceName()} from the super class.
    +     */
    +    public String getSourceName()
    +    {
    +        return sourceName();
    +    }
    +
    +    /**
    +     * @deprecated Use {@link RhinoException#lineNumber()} from the super class.
    +     */
    +    public int getLineNumber()
    +    {
    +        return lineNumber();
    +    }
    +
    +    private Object value;
    +}
    diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Kit.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Kit.java
    new file mode 100644
    index 0000000..f7b4cad
    --- /dev/null
    +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Kit.java
    @@ -0,0 +1,486 @@
    +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
    + *
    + * ***** BEGIN LICENSE BLOCK *****
    + * Version: MPL 1.1/GPL 2.0
    + *
    + * The contents of this file are subject to the Mozilla Public License Version
    + * 1.1 (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.mozilla.org/MPL/
    + *
    + * Software distributed under the License is distributed on an "AS IS" basis,
    + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
    + * for the specific language governing rights and limitations under the
    + * License.
    + *
    + * The Original Code is Rhino code, released
    + * May 6, 1999.
    + *
    + * The Initial Developer of the Original Code is
    + * Netscape Communications Corporation.
    + * Portions created by the Initial Developer are Copyright (C) 1997-1999
    + * the Initial Developer. All Rights Reserved.
    + *
    + * Contributor(s):
    + *   Igor Bukanov, igor@fastmail.fm
    + *
    + * Alternatively, the contents of this file may be used under the terms of
    + * the GNU General Public License Version 2 or later (the "GPL"), in which
    + * case the provisions of the GPL are applicable instead of those above. If
    + * you wish to allow use of your version of this file only under the terms of
    + * the GPL and not to allow others to use your version of this file under the
    + * MPL, indicate your decision by deleting the provisions above and replacing
    + * them with the notice and other provisions required by the GPL. If you do
    + * not delete the provisions above, a recipient may use your version of this
    + * file under either the MPL or the GPL.
    + *
    + * ***** END LICENSE BLOCK ***** */
    +
    +package org.mozilla.javascript;
    +
    +import java.io.IOException;
    +import java.io.InputStream;
    +import java.io.Reader;
    +import java.lang.reflect.Method;
    +import java.util.Hashtable;
    +
    +/**
    + * Collection of utilities
    + */
    +
    +public class Kit
    +{
    +    /**
    +     * Reflection of Throwable.initCause(Throwable) from JDK 1.4
    +     * or nul if it is not available.
    +     */
    +    private static Method Throwable_initCause = null;
    +
    +    static {
    +        // Are we running on a JDK 1.4 or later system?
    +        try {
    +            Class ThrowableClass = Kit.classOrNull("java.lang.Throwable");
    +            Class[] signature = { ThrowableClass };
    +            Throwable_initCause
    +                = ThrowableClass.getMethod("initCause", signature);
    +        } catch (Exception ex) {
    +            // Assume any exceptions means the method does not exist.
    +        }
    +    }
    +
    +    public static Class classOrNull(String className)
    +    {
    +        try {
    +            return Class.forName(className);
    +        } catch  (ClassNotFoundException ex) {
    +        } catch  (SecurityException ex) {
    +        } catch  (LinkageError ex) {
    +        } catch (IllegalArgumentException e) {
    +            // Can be thrown if name has characters that a class name
    +            // can not contain
    +        }
    +        return null;
    +    }
    +
    +    public static Class classOrNull(ClassLoader loader, String className)
    +    {
    +        try {
    +            return loader.loadClass(className);
    +        } catch (ClassNotFoundException ex) {
    +        } catch (SecurityException ex) {
    +        } catch  (LinkageError ex) {
    +        } catch (IllegalArgumentException e) {
    +            // Can be thrown if name has characters that a class name
    +            // can not contain
    +        }
    +        return null;
    +    }
    +
    +    static Object newInstanceOrNull(Class cl)
    +    {
    +        try {
    +            return cl.newInstance();
    +        } catch (SecurityException x) {
    +        } catch  (LinkageError ex) {
    +        } catch (InstantiationException x) {
    +        } catch (IllegalAccessException x) {
    +        }
    +        return null;
    +    }
    +
    +    /**
    +     * Check that testClass is accesible from the given loader.
    +     */
    +    static boolean testIfCanLoadRhinoClasses(ClassLoader loader)
    +    {
    +        Class testClass = ScriptRuntime.ContextFactoryClass;
    +        Class x = Kit.classOrNull(loader, testClass.getName());
    +        if (x != testClass) {
    +            // The check covers the case when x == null =>
    +            // loader does not know about testClass or the case
    +            // when x != null && x != testClass =>
    +            // loader loads a class unrelated to testClass
    +            return false;
    +        }
    +        return true;
    +    }
    +
    +    /**
    +     * If initCause methods exists in Throwable, call
    +     * ex.initCause(cause) or otherwise do nothing.
    +     * @return The ex argument.
    +     */
    +    public static RuntimeException initCause(RuntimeException ex,
    +                                             Throwable cause)
    +    {
    +        if (Throwable_initCause != null) {
    +            Object[] args = { cause };
    +            try {
    +                Throwable_initCause.invoke(ex, args);
    +            } catch (Exception e) {
    +                // Ignore any exceptions
    +            }
    +        }
    +        return ex;
    +    }
    +
    +    /**
    +     * Split string into array of strings using semicolon as string terminator
    +     * (; after the last string is required).
    +     */
    +    public static String[] semicolonSplit(String s)
    +    {
    +        String[] array = null;
    +        for (;;) {
    +            // loop 2 times: first to count semicolons and then to fill array
    +            int count = 0;
    +            int cursor = 0;
    +            for (;;) {
    +                int next = s.indexOf(';', cursor);
    +                if (next < 0) {
    +                    break;
    +                }
    +                if (array != null) {
    +                    array[count] = s.substring(cursor, next);
    +                }
    +                ++count;
    +                cursor = next + 1;
    +            }
    +            // after the last semicolon
    +            if (array == null) {
    +                // array size counting state:
    +                // check for required terminating ';'
    +                if (cursor != s.length())
    +                    throw new IllegalArgumentException();
    +                array = new String[count];
    +            } else {
    +                // array filling state: stop the loop
    +                break;
    +            }
    +        }
    +        return array;
    +    }
    +
    +    /**
    +     * If character c is a hexadecimal digit, return
    +     * accumulator * 16 plus corresponding
    +     * number. Otherise return -1.
    +     */
    +    public static int xDigitToInt(int c, int accumulator)
    +    {
    +        check: {
    +            // Use 0..9 < A..Z < a..z
    +            if (c <= '9') {
    +                c -= '0';
    +                if (0 <= c) { break check; }
    +            } else if (c <= 'F') {
    +                if ('A' <= c) {
    +                    c -= ('A' - 10);
    +                    break check;
    +                }
    +            } else if (c <= 'f') {
    +                if ('a' <= c) {
    +                    c -= ('a' - 10);
    +                    break check;
    +                }
    +            }
    +            return -1;
    +        }
    +        return (accumulator << 4) | c;
    +    }
    +
    +    /**
    +     * Add listener to bag of listeners.
    +     * The function does not modify bag and return a new collection
    +     * containing listener and all listeners from bag.
    +     * Bag without listeners always represented as the null value.
    +     * 

    + * Usage example: + *

    +     *     private volatile Object changeListeners;
    +     *
    +     *     public void addMyListener(PropertyChangeListener l)
    +     *     {
    +     *         synchronized (this) {
    +     *             changeListeners = Kit.addListener(changeListeners, l);
    +     *         }
    +     *     }
    +     *
    +     *     public void removeTextListener(PropertyChangeListener l)
    +     *     {
    +     *         synchronized (this) {
    +     *             changeListeners = Kit.removeListener(changeListeners, l);
    +     *         }
    +     *     }
    +     *
    +     *     public void fireChangeEvent(Object oldValue, Object newValue)
    +     *     {
    +     *     // Get immune local copy
    +     *         Object listeners = changeListeners;
    +     *         if (listeners != null) {
    +     *             PropertyChangeEvent e = new PropertyChangeEvent(
    +     *                 this, "someProperty" oldValue, newValue);
    +     *             for (int i = 0; ; ++i) {
    +     *                 Object l = Kit.getListener(listeners, i);
    +     *                 if (l == null)
    +     *                     break;
    +     *                 ((PropertyChangeListener)l).propertyChange(e);
    +     *             }
    +     *         }
    +     *     }
    +     * 
    + * + * @param listener Listener to add to bag + * @param bag Current collection of listeners. + * @return A new bag containing all listeners from bag and + * listener. + * @see #removeListener(Object bag, Object listener) + * @see #getListener(Object bag, int index) + */ + public static Object addListener(Object bag, Object listener) + { + if (listener == null) throw new IllegalArgumentException(); + if (listener instanceof Object[]) throw new IllegalArgumentException(); + + if (bag == null) { + bag = listener; + } else if (!(bag instanceof Object[])) { + bag = new Object[] { bag, listener }; + } else { + Object[] array = (Object[])bag; + int L = array.length; + // bag has at least 2 elements if it is array + if (L < 2) throw new IllegalArgumentException(); + Object[] tmp = new Object[L + 1]; + System.arraycopy(array, 0, tmp, 0, L); + tmp[L] = listener; + bag = tmp; + } + + return bag; + } + + /** + * Remove listener from bag of listeners. + * The function does not modify bag and return a new collection + * containing all listeners from bag except listener. + * If bag does not contain listener, the function returns + * bag. + *

    + * For usage example, see {@link #addListener(Object bag, Object listener)}. + * + * @param listener Listener to remove from bag + * @param bag Current collection of listeners. + * @return A new bag containing all listeners from bag except + * listener. + * @see #addListener(Object bag, Object listener) + * @see #getListener(Object bag, int index) + */ + public static Object removeListener(Object bag, Object listener) + { + if (listener == null) throw new IllegalArgumentException(); + if (listener instanceof Object[]) throw new IllegalArgumentException(); + + if (bag == listener) { + bag = null; + } else if (bag instanceof Object[]) { + Object[] array = (Object[])bag; + int L = array.length; + // bag has at least 2 elements if it is array + if (L < 2) throw new IllegalArgumentException(); + if (L == 2) { + if (array[1] == listener) { + bag = array[0]; + } else if (array[0] == listener) { + bag = array[1]; + } + } else { + int i = L; + do { + --i; + if (array[i] == listener) { + Object[] tmp = new Object[L - 1]; + System.arraycopy(array, 0, tmp, 0, i); + System.arraycopy(array, i + 1, tmp, i, L - (i + 1)); + bag = tmp; + break; + } + } while (i != 0); + } + } + + return bag; + } + + /** + * Get listener at index position in bag or null if + * index equals to number of listeners in bag. + *

    + * For usage example, see {@link #addListener(Object bag, Object listener)}. + * + * @param bag Current collection of listeners. + * @param index Index of the listener to access. + * @return Listener at the given index or null. + * @see #addListener(Object bag, Object listener) + * @see #removeListener(Object bag, Object listener) + */ + public static Object getListener(Object bag, int index) + { + if (index == 0) { + if (bag == null) + return null; + if (!(bag instanceof Object[])) + return bag; + Object[] array = (Object[])bag; + // bag has at least 2 elements if it is array + if (array.length < 2) throw new IllegalArgumentException(); + return array[0]; + } else if (index == 1) { + if (!(bag instanceof Object[])) { + if (bag == null) throw new IllegalArgumentException(); + return null; + } + Object[] array = (Object[])bag; + // the array access will check for index on its own + return array[1]; + } else { + // bag has to array + Object[] array = (Object[])bag; + int L = array.length; + if (L < 2) throw new IllegalArgumentException(); + if (index == L) + return null; + return array[index]; + } + } + + static Object initHash(Hashtable h, Object key, Object initialValue) + { + synchronized (h) { + Object current = h.get(key); + if (current == null) { + h.put(key, initialValue); + } else { + initialValue = current; + } + } + return initialValue; + } + + private final static class ComplexKey + { + private Object key1; + private Object key2; + private int hash; + + ComplexKey(Object key1, Object key2) + { + this.key1 = key1; + this.key2 = key2; + } + + public boolean equals(Object anotherObj) + { + if (!(anotherObj instanceof ComplexKey)) + return false; + ComplexKey another = (ComplexKey)anotherObj; + return key1.equals(another.key1) && key2.equals(another.key2); + } + + public int hashCode() + { + if (hash == 0) { + hash = key1.hashCode() ^ key2.hashCode(); + } + return hash; + } + } + + public static Object makeHashKeyFromPair(Object key1, Object key2) + { + if (key1 == null) throw new IllegalArgumentException(); + if (key2 == null) throw new IllegalArgumentException(); + return new ComplexKey(key1, key2); + } + + public static String readReader(Reader r) + throws IOException + { + char[] buffer = new char[512]; + int cursor = 0; + for (;;) { + int n = r.read(buffer, cursor, buffer.length - cursor); + if (n < 0) { break; } + cursor += n; + if (cursor == buffer.length) { + char[] tmp = new char[buffer.length * 2]; + System.arraycopy(buffer, 0, tmp, 0, cursor); + buffer = tmp; + } + } + return new String(buffer, 0, cursor); + } + + public static byte[] readStream(InputStream is, int initialBufferCapacity) + throws IOException + { + if (initialBufferCapacity <= 0) { + throw new IllegalArgumentException( + "Bad initialBufferCapacity: "+initialBufferCapacity); + } + byte[] buffer = new byte[initialBufferCapacity]; + int cursor = 0; + for (;;) { + int n = is.read(buffer, cursor, buffer.length - cursor); + if (n < 0) { break; } + cursor += n; + if (cursor == buffer.length) { + byte[] tmp = new byte[buffer.length * 2]; + System.arraycopy(buffer, 0, tmp, 0, cursor); + buffer = tmp; + } + } + if (cursor != buffer.length) { + byte[] tmp = new byte[cursor]; + System.arraycopy(buffer, 0, tmp, 0, cursor); + buffer = tmp; + } + return buffer; + } + + /** + * Throws RuntimeException to indicate failed assertion. + * The function never returns and its return type is RuntimeException + * only to be able to write throw Kit.codeBug() if plain + * Kit.codeBug() triggers unreachable code error. + */ + public static RuntimeException codeBug() + throws RuntimeException + { + RuntimeException ex = new IllegalStateException("FAILED ASSERTION"); + // Print stack trace ASAP + ex.printStackTrace(System.err); + throw ex; + } +} + diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/LazilyLoadedCtor.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/LazilyLoadedCtor.java new file mode 100644 index 0000000..4153372 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/LazilyLoadedCtor.java @@ -0,0 +1,136 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Norris Boyd + * Igor Bukanov + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript; + +import java.lang.reflect.*; + +/** + * Avoid loading classes unless they are used. + * + *

    This improves startup time and average memory usage. + */ +public final class LazilyLoadedCtor implements java.io.Serializable { + private static final long serialVersionUID = 1L; + + private static final int STATE_BEFORE_INIT = 0; + private static final int STATE_INITIALIZING = 1; + private static final int STATE_WITH_VALUE = 2; + + private final ScriptableObject scope; + private final String propertyName; + private final String className; + private final boolean sealed; + private Object initializedValue; + private int state; + + public LazilyLoadedCtor(ScriptableObject scope, String propertyName, + String className, boolean sealed) + { + + this.scope = scope; + this.propertyName = propertyName; + this.className = className; + this.sealed = sealed; + this.state = STATE_BEFORE_INIT; + + scope.addLazilyInitializedValue(propertyName, 0, this, + ScriptableObject.DONTENUM); + } + + void init() + { + synchronized (this) { + if (state == STATE_INITIALIZING) + throw new IllegalStateException( + "Recursive initialization for "+propertyName); + if (state == STATE_BEFORE_INIT) { + state = STATE_INITIALIZING; + // Set value now to have something to set in finally block if + // buildValue throws. + Object value = Scriptable.NOT_FOUND; + try { + value = buildValue(); + } finally { + initializedValue = value; + state = STATE_WITH_VALUE; + } + } + } + } + + Object getValue() + { + if (state != STATE_WITH_VALUE) + throw new IllegalStateException(propertyName); + return initializedValue; + } + + private Object buildValue() + { + Class cl = Kit.classOrNull(className); + if (cl != null) { + try { + Object value = ScriptableObject.buildClassCtor(scope, cl, + sealed, false); + if (value != null) { + return value; + } + else { + // cl has own static initializer which is expected + // to set the property on its own. + value = scope.get(propertyName, scope); + if (value != Scriptable.NOT_FOUND) + return value; + } + } catch (InvocationTargetException ex) { + Throwable target = ex.getTargetException(); + if (target instanceof RuntimeException) { + throw (RuntimeException)target; + } + } catch (RhinoException ex) { + } catch (InstantiationException ex) { + } catch (IllegalAccessException ex) { + } catch (SecurityException ex) { + } + } + return Scriptable.NOT_FOUND; + } + +} diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/MemberBox.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/MemberBox.java new file mode 100644 index 0000000..2d3553f --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/MemberBox.java @@ -0,0 +1,362 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Igor Bukanov + * Felix Meschberger + * Norris Boyd + * Ulrike Mueller + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript; + +import java.lang.reflect.*; +import java.io.*; + +/** + * Wrappper class for Method and Constructor instances to cache + * getParameterTypes() results, recover from IllegalAccessException + * in some cases and provide serialization support. + * + * @author Igor Bukanov + */ + +final class MemberBox implements Serializable +{ + static final long serialVersionUID = 6358550398665688245L; + + private transient Member memberObject; + transient Class[] argTypes; + transient Object delegateTo; + transient boolean vararg; + + + MemberBox(Method method) + { + init(method); + } + + MemberBox(Constructor constructor) + { + init(constructor); + } + + private void init(Method method) + { + this.memberObject = method; + this.argTypes = method.getParameterTypes(); + this.vararg = VMBridge.instance.isVarArgs(method); + } + + private void init(Constructor constructor) + { + this.memberObject = constructor; + this.argTypes = constructor.getParameterTypes(); + this.vararg = VMBridge.instance.isVarArgs(constructor); + } + + Method method() + { + return (Method)memberObject; + } + + Constructor ctor() + { + return (Constructor)memberObject; + } + + Member member() + { + return memberObject; + } + + boolean isMethod() + { + return memberObject instanceof Method; + } + + boolean isCtor() + { + return memberObject instanceof Constructor; + } + + boolean isStatic() + { + return Modifier.isStatic(memberObject.getModifiers()); + } + + String getName() + { + return memberObject.getName(); + } + + Class getDeclaringClass() + { + return memberObject.getDeclaringClass(); + } + + String toJavaDeclaration() + { + StringBuffer sb = new StringBuffer(); + if (isMethod()) { + Method method = method(); + sb.append(method.getReturnType()); + sb.append(' '); + sb.append(method.getName()); + } else { + Constructor ctor = ctor(); + String name = ctor.getDeclaringClass().getName(); + int lastDot = name.lastIndexOf('.'); + if (lastDot >= 0) { + name = name.substring(lastDot + 1); + } + sb.append(name); + } + sb.append(JavaMembers.liveConnectSignature(argTypes)); + return sb.toString(); + } + + public String toString() + { + return memberObject.toString(); + } + + Object invoke(Object target, Object[] args) + { + Method method = method(); + try { + try { + return method.invoke(target, args); + } catch (IllegalAccessException ex) { + Method accessible = searchAccessibleMethod(method, argTypes); + if (accessible != null) { + memberObject = accessible; + method = accessible; + } else { + if (!VMBridge.instance.tryToMakeAccessible(method)) { + throw Context.throwAsScriptRuntimeEx(ex); + } + } + // Retry after recovery + return method.invoke(target, args); + } + } catch (Exception ex) { + throw Context.throwAsScriptRuntimeEx(ex); + } + } + + Object newInstance(Object[] args) + { + Constructor ctor = ctor(); + try { + try { + return ctor.newInstance(args); + } catch (IllegalAccessException ex) { + if (!VMBridge.instance.tryToMakeAccessible(ctor)) { + throw Context.throwAsScriptRuntimeEx(ex); + } + } + return ctor.newInstance(args); + } catch (Exception ex) { + throw Context.throwAsScriptRuntimeEx(ex); + } + } + + private static Method searchAccessibleMethod(Method method, Class[] params) + { + int modifiers = method.getModifiers(); + if (Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers)) { + Class c = method.getDeclaringClass(); + if (!Modifier.isPublic(c.getModifiers())) { + String name = method.getName(); + Class[] intfs = c.getInterfaces(); + for (int i = 0, N = intfs.length; i != N; ++i) { + Class intf = intfs[i]; + if (Modifier.isPublic(intf.getModifiers())) { + try { + return intf.getMethod(name, params); + } catch (NoSuchMethodException ex) { + } catch (SecurityException ex) { } + } + } + for (;;) { + c = c.getSuperclass(); + if (c == null) { break; } + if (Modifier.isPublic(c.getModifiers())) { + try { + Method m = c.getMethod(name, params); + int mModifiers = m.getModifiers(); + if (Modifier.isPublic(mModifiers) + && !Modifier.isStatic(mModifiers)) + { + return m; + } + } catch (NoSuchMethodException ex) { + } catch (SecurityException ex) { } + } + } + } + } + return null; + } + + private void readObject(ObjectInputStream in) + throws IOException, ClassNotFoundException + { + in.defaultReadObject(); + Member member = readMember(in); + if (member instanceof Method) { + init((Method)member); + } else { + init((Constructor)member); + } + } + + private void writeObject(ObjectOutputStream out) + throws IOException + { + out.defaultWriteObject(); + writeMember(out, memberObject); + } + + /** + * Writes a Constructor or Method object. + * + * Methods and Constructors are not serializable, so we must serialize + * information about the class, the name, and the parameters and + * recreate upon deserialization. + */ + private static void writeMember(ObjectOutputStream out, Member member) + throws IOException + { + if (member == null) { + out.writeBoolean(false); + return; + } + out.writeBoolean(true); + if (!(member instanceof Method || member instanceof Constructor)) + throw new IllegalArgumentException("not Method or Constructor"); + out.writeBoolean(member instanceof Method); + out.writeObject(member.getName()); + out.writeObject(member.getDeclaringClass()); + if (member instanceof Method) { + writeParameters(out, ((Method) member).getParameterTypes()); + } else { + writeParameters(out, ((Constructor) member).getParameterTypes()); + } + } + + /** + * Reads a Method or a Constructor from the stream. + */ + private static Member readMember(ObjectInputStream in) + throws IOException, ClassNotFoundException + { + if (!in.readBoolean()) + return null; + boolean isMethod = in.readBoolean(); + String name = (String) in.readObject(); + Class declaring = (Class) in.readObject(); + Class[] parms = readParameters(in); + try { + if (isMethod) { + return declaring.getMethod(name, parms); + } else { + return declaring.getConstructor(parms); + } + } catch (NoSuchMethodException e) { + throw new IOException("Cannot find member: " + e); + } + } + + private static final Class[] primitives = { + Boolean.TYPE, + Byte.TYPE, + Character.TYPE, + Double.TYPE, + Float.TYPE, + Integer.TYPE, + Long.TYPE, + Short.TYPE, + Void.TYPE + }; + + /** + * Writes an array of parameter types to the stream. + * + * Requires special handling because primitive types cannot be + * found upon deserialization by the default Java implementation. + */ + private static void writeParameters(ObjectOutputStream out, Class[] parms) + throws IOException + { + out.writeShort(parms.length); + outer: + for (int i=0; i < parms.length; i++) { + Class parm = parms[i]; + boolean primitive = parm.isPrimitive(); + out.writeBoolean(primitive); + if (!primitive) { + out.writeObject(parm); + continue; + } + for (int j=0; j < primitives.length; j++) { + if (parm.equals(primitives[j])) { + out.writeByte(j); + continue outer; + } + } + throw new IllegalArgumentException("Primitive " + parm + + " not found"); + } + } + + /** + * Reads an array of parameter types from the stream. + */ + private static Class[] readParameters(ObjectInputStream in) + throws IOException, ClassNotFoundException + { + Class[] result = new Class[in.readShort()]; + for (int i=0; i < result.length; i++) { + if (!in.readBoolean()) { + result[i] = (Class) in.readObject(); + continue; + } + result[i] = primitives[in.readByte()]; + } + return result; + } +} + diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeArray.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeArray.java new file mode 100644 index 0000000..b170ff4 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeArray.java @@ -0,0 +1,1727 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Norris Boyd + * Mike McCabe + * Igor Bukanov + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript; + +import java.util.Arrays; + +/** + * This class implements the Array native object. + * @author Norris Boyd + * @author Mike McCabe + */ +public class NativeArray extends IdScriptableObject +{ + static final long serialVersionUID = 7331366857676127338L; + + /* + * Optimization possibilities and open issues: + * - Long vs. double schizophrenia. I suspect it might be better + * to use double throughout. + * + * - Functions that need a new Array call "new Array" in the + * current scope rather than using a hardwired constructor; + * "Array" could be redefined. It turns out that js calls the + * equivalent of "new Array" in the current scope, except that it + * always gets at least an object back, even when Array == null. + */ + + private static final Object ARRAY_TAG = new Object(); + private static final Integer NEGATIVE_ONE = new Integer(-1); + + static void init(Scriptable scope, boolean sealed) + { + NativeArray obj = new NativeArray(0); + obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed); + } + + static int getMaximumInitialCapacity() { + return maximumInitialCapacity; + } + + static void setMaximumInitialCapacity(int maximumInitialCapacity) { + NativeArray.maximumInitialCapacity = maximumInitialCapacity; + } + + public NativeArray(long lengthArg) + { + denseOnly = lengthArg <= maximumInitialCapacity; + if (denseOnly) { + int intLength = (int) lengthArg; + if (intLength < DEFAULT_INITIAL_CAPACITY) + intLength = DEFAULT_INITIAL_CAPACITY; + dense = new Object[intLength]; + Arrays.fill(dense, Scriptable.NOT_FOUND); + } + length = lengthArg; + } + + public NativeArray(Object[] array) + { + denseOnly = true; + dense = array; + length = array.length; + } + + public String getClassName() + { + return "Array"; + } + + private static final int + Id_length = 1, + MAX_INSTANCE_ID = 1; + + protected int getMaxInstanceId() + { + return MAX_INSTANCE_ID; + } + + protected int findInstanceIdInfo(String s) + { + if (s.equals("length")) { + return instanceIdInfo(DONTENUM | PERMANENT, Id_length); + } + return super.findInstanceIdInfo(s); + } + + protected String getInstanceIdName(int id) + { + if (id == Id_length) { return "length"; } + return super.getInstanceIdName(id); + } + + protected Object getInstanceIdValue(int id) + { + if (id == Id_length) { + return ScriptRuntime.wrapNumber(length); + } + return super.getInstanceIdValue(id); + } + + protected void setInstanceIdValue(int id, Object value) + { + if (id == Id_length) { + setLength(value); return; + } + super.setInstanceIdValue(id, value); + } + + protected void fillConstructorProperties(IdFunctionObject ctor) + { + addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_join, + "join", 2); + addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_reverse, + "reverse", 1); + addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_sort, + "sort", 2); + addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_push, + "push", 2); + addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_pop, + "pop", 2); + addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_shift, + "shift", 2); + addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_unshift, + "unshift", 2); + addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_splice, + "splice", 2); + addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_concat, + "concat", 2); + addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_slice, + "slice", 2); + addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_indexOf, + "indexOf", 2); + addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_lastIndexOf, + "lastIndexOf", 2); + addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_every, + "every", 2); + addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_filter, + "filter", 2); + addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_forEach, + "forEach", 2); + addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_map, + "map", 2); + addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_some, + "some", 2); + super.fillConstructorProperties(ctor); + } + + protected void initPrototypeId(int id) + { + String s; + int arity; + switch (id) { + case Id_constructor: arity=1; s="constructor"; break; + case Id_toString: arity=0; s="toString"; break; + case Id_toLocaleString: arity=1; s="toLocaleString"; break; + case Id_toSource: arity=0; s="toSource"; break; + case Id_join: arity=1; s="join"; break; + case Id_reverse: arity=0; s="reverse"; break; + case Id_sort: arity=1; s="sort"; break; + case Id_push: arity=1; s="push"; break; + case Id_pop: arity=1; s="pop"; break; + case Id_shift: arity=1; s="shift"; break; + case Id_unshift: arity=1; s="unshift"; break; + case Id_splice: arity=1; s="splice"; break; + case Id_concat: arity=1; s="concat"; break; + case Id_slice: arity=1; s="slice"; break; + case Id_indexOf: arity=1; s="indexOf"; break; + case Id_lastIndexOf: arity=1; s="lastIndexOf"; break; + case Id_every: arity=1; s="every"; break; + case Id_filter: arity=1; s="filter"; break; + case Id_forEach: arity=1; s="forEach"; break; + case Id_map: arity=1; s="map"; break; + case Id_some: arity=1; s="some"; break; + default: throw new IllegalArgumentException(String.valueOf(id)); + } + initPrototypeMethod(ARRAY_TAG, id, s, arity); + } + + public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, + Scriptable thisObj, Object[] args) + { + if (!f.hasTag(ARRAY_TAG)) { + return super.execIdCall(f, cx, scope, thisObj, args); + } + int id = f.methodId(); + again: + for (;;) { + switch (id) { + case ConstructorId_join: + case ConstructorId_reverse: + case ConstructorId_sort: + case ConstructorId_push: + case ConstructorId_pop: + case ConstructorId_shift: + case ConstructorId_unshift: + case ConstructorId_splice: + case ConstructorId_concat: + case ConstructorId_slice: + case ConstructorId_indexOf: + case ConstructorId_lastIndexOf: + case ConstructorId_every: + case ConstructorId_filter: + case ConstructorId_forEach: + case ConstructorId_map: + case ConstructorId_some: { + thisObj = ScriptRuntime.toObject(scope, args[0]); + Object[] newArgs = new Object[args.length-1]; + for (int i=0; i < newArgs.length; i++) + newArgs[i] = args[i+1]; + args = newArgs; + id = -id; + continue again; + } + + case Id_constructor: { + boolean inNewExpr = (thisObj == null); + if (!inNewExpr) { + // IdFunctionObject.construct will set up parent, proto + return f.construct(cx, scope, args); + } + return jsConstructor(cx, scope, args); + } + + case Id_toString: + return toStringHelper(cx, scope, thisObj, + cx.hasFeature(Context.FEATURE_TO_STRING_AS_SOURCE), false); + + case Id_toLocaleString: + return toStringHelper(cx, scope, thisObj, false, true); + + case Id_toSource: + return toStringHelper(cx, scope, thisObj, true, false); + + case Id_join: + return js_join(cx, thisObj, args); + + case Id_reverse: + return js_reverse(cx, thisObj, args); + + case Id_sort: + return js_sort(cx, scope, thisObj, args); + + case Id_push: + return js_push(cx, thisObj, args); + + case Id_pop: + return js_pop(cx, thisObj, args); + + case Id_shift: + return js_shift(cx, thisObj, args); + + case Id_unshift: + return js_unshift(cx, thisObj, args); + + case Id_splice: + return js_splice(cx, scope, thisObj, args); + + case Id_concat: + return js_concat(cx, scope, thisObj, args); + + case Id_slice: + return js_slice(cx, thisObj, args); + + case Id_indexOf: + return indexOfHelper(cx, thisObj, args, false); + + case Id_lastIndexOf: + return indexOfHelper(cx, thisObj, args, true); + + case Id_every: + case Id_filter: + case Id_forEach: + case Id_map: + case Id_some: + return iterativeMethod(cx, id, scope, thisObj, args); + } + throw new IllegalArgumentException(String.valueOf(id)); + } + } + + public Object get(int index, Scriptable start) + { + if (!denseOnly && isGetterOrSetter(null, index, false)) + return super.get(index, start); + if (dense != null && 0 <= index && index < dense.length) + return dense[index]; + return super.get(index, start); + } + + public boolean has(int index, Scriptable start) + { + if (!denseOnly && isGetterOrSetter(null, index, false)) + return super.has(index, start); + if (dense != null && 0 <= index && index < dense.length) + return dense[index] != NOT_FOUND; + return super.has(index, start); + } + + // if id is an array index (ECMA 15.4.0), return the number, + // otherwise return -1L + private static long toArrayIndex(String id) + { + double d = ScriptRuntime.toNumber(id); + if (d == d) { + long index = ScriptRuntime.toUint32(d); + if (index == d && index != 4294967295L) { + // Assume that ScriptRuntime.toString(index) is the same + // as java.lang.Long.toString(index) for long + if (Long.toString(index).equals(id)) { + return index; + } + } + } + return -1; + } + + public void put(String id, Scriptable start, Object value) + { + super.put(id, start, value); + if (start == this) { + // If the object is sealed, super will throw exception + long index = toArrayIndex(id); + if (index >= length) { + length = index + 1; + denseOnly = false; + } + } + } + + private boolean ensureCapacity(int capacity) + { + if (capacity > dense.length) { + if (capacity > MAX_PRE_GROW_SIZE) { + denseOnly = false; + return false; + } + capacity = Math.max(capacity, (int)(dense.length * GROW_FACTOR)); + Object[] newDense = new Object[capacity]; + System.arraycopy(dense, 0, newDense, 0, dense.length); + Arrays.fill(newDense, dense.length, newDense.length, + Scriptable.NOT_FOUND); + dense = newDense; + } + return true; + } + + public void put(int index, Scriptable start, Object value) + { + if (start == this && !isSealed() && dense != null && 0 <= index && + (denseOnly || !isGetterOrSetter(null, index, true))) + { + if (index < dense.length) { + dense[index] = value; + if (this.length <= index) + this.length = (long)index + 1; + return; + } else if (denseOnly && index < dense.length * GROW_FACTOR && + ensureCapacity(index+1)) + { + dense[index] = value; + this.length = (long)index + 1; + return; + } else { + denseOnly = false; + } + } + super.put(index, start, value); + if (start == this) { + // only set the array length if given an array index (ECMA 15.4.0) + if (this.length <= index) { + // avoid overflowing index! + this.length = (long)index + 1; + } + } + } + + public void delete(int index) + { + if (dense != null && 0 <= index && index < dense.length && + !isSealed() && (denseOnly || !isGetterOrSetter(null, index, true))) + { + dense[index] = NOT_FOUND; + } else { + super.delete(index); + } + } + + public Object[] getIds() + { + Object[] superIds = super.getIds(); + if (dense == null) { return superIds; } + int N = dense.length; + long currentLength = length; + if (N > currentLength) { + N = (int)currentLength; + } + if (N == 0) { return superIds; } + int superLength = superIds.length; + Object[] ids = new Object[N + superLength]; + + int presentCount = 0; + for (int i = 0; i != N; ++i) { + // Replace existing elements by their indexes + if (dense[i] != NOT_FOUND) { + ids[presentCount] = new Integer(i); + ++presentCount; + } + } + if (presentCount != N) { + // dense contains deleted elems, need to shrink the result + Object[] tmp = new Object[presentCount + superLength]; + System.arraycopy(ids, 0, tmp, 0, presentCount); + ids = tmp; + } + System.arraycopy(superIds, 0, ids, presentCount, superLength); + return ids; + } + + public Object getDefaultValue(Class hint) + { + if (hint == ScriptRuntime.NumberClass) { + Context cx = Context.getContext(); + if (cx.getLanguageVersion() == Context.VERSION_1_2) + return new Long(length); + } + return super.getDefaultValue(hint); + } + + /** + * See ECMA 15.4.1,2 + */ + private static Object jsConstructor(Context cx, Scriptable scope, + Object[] args) + { + if (args.length == 0) + return new NativeArray(0); + + // Only use 1 arg as first element for version 1.2; for + // any other version (including 1.3) follow ECMA and use it as + // a length. + if (cx.getLanguageVersion() == Context.VERSION_1_2) { + return new NativeArray(args); + } else { + Object arg0 = args[0]; + if (args.length > 1 || !(arg0 instanceof Number)) { + return new NativeArray(args); + } else { + long len = ScriptRuntime.toUint32(arg0); + if (len != ((Number)arg0).doubleValue()) + throw Context.reportRuntimeError0("msg.arraylength.bad"); + return new NativeArray(len); + } + } + } + + public long getLength() { + return length; + } + + /** @deprecated Use {@link #getLength()} instead. */ + public long jsGet_length() { + return getLength(); + } + + /** + * Change the value of the internal flag that determines whether all + * storage is handed by a dense backing array rather than an associative + * store. + * @param denseOnly new value for denseOnly flag + * @throws IllegalArgumentException if an attempt is made to enable + * denseOnly after it was disabled; NativeArray code is not written + * to handle switching back to a dense representation + */ + void setDenseOnly(boolean denseOnly) { + if (denseOnly && !this.denseOnly) + throw new IllegalArgumentException(); + this.denseOnly = denseOnly; + } + + private void setLength(Object val) { + /* XXX do we satisfy this? + * 15.4.5.1 [[Put]](P, V): + * 1. Call the [[CanPut]] method of A with name P. + * 2. If Result(1) is false, return. + * ? + */ + + double d = ScriptRuntime.toNumber(val); + long longVal = ScriptRuntime.toUint32(d); + if (longVal != d) + throw Context.reportRuntimeError0("msg.arraylength.bad"); + + if (denseOnly) { + if (longVal < length) { + // downcast okay because denseOnly + Arrays.fill(dense, (int) longVal, dense.length, NOT_FOUND); + length = longVal; + return; + } else if (longVal < MAX_PRE_GROW_SIZE && + longVal < (length * GROW_FACTOR) && + ensureCapacity((int)longVal)) + { + length = longVal; + return; + } else { + denseOnly = false; + } + } + if (longVal < length) { + // remove all properties between longVal and length + if (length - longVal > 0x1000) { + // assume that the representation is sparse + Object[] e = getIds(); // will only find in object itself + for (int i=0; i < e.length; i++) { + Object id = e[i]; + if (id instanceof String) { + // > MAXINT will appear as string + String strId = (String)id; + long index = toArrayIndex(strId); + if (index >= longVal) + delete(strId); + } else { + int index = ((Integer)id).intValue(); + if (index >= longVal) + delete(index); + } + } + } else { + // assume a dense representation + for (long i = longVal; i < length; i++) { + deleteElem(this, i); + } + } + } + length = longVal; + } + + /* Support for generic Array-ish objects. Most of the Array + * functions try to be generic; anything that has a length + * property is assumed to be an array. + * getLengthProperty returns 0 if obj does not have the length property + * or its value is not convertible to a number. + */ + static long getLengthProperty(Context cx, Scriptable obj) { + // These will both give numeric lengths within Uint32 range. + if (obj instanceof NativeString) { + return ((NativeString)obj).getLength(); + } else if (obj instanceof NativeArray) { + return ((NativeArray)obj).getLength(); + } + return ScriptRuntime.toUint32( + ScriptRuntime.getObjectProp(obj, "length", cx)); + } + + private static Object setLengthProperty(Context cx, Scriptable target, + long length) + { + return ScriptRuntime.setObjectProp( + target, "length", ScriptRuntime.wrapNumber(length), cx); + } + + /* Utility functions to encapsulate index > Integer.MAX_VALUE + * handling. Also avoids unnecessary object creation that would + * be necessary to use the general ScriptRuntime.get/setElem + * functions... though this is probably premature optimization. + */ + private static void deleteElem(Scriptable target, long index) { + int i = (int)index; + if (i == index) { target.delete(i); } + else { target.delete(Long.toString(index)); } + } + + private static Object getElem(Context cx, Scriptable target, long index) + { + if (index > Integer.MAX_VALUE) { + String id = Long.toString(index); + return ScriptRuntime.getObjectProp(target, id, cx); + } else { + return ScriptRuntime.getObjectIndex(target, (int)index, cx); + } + } + + private static void setElem(Context cx, Scriptable target, long index, + Object value) + { + if (index > Integer.MAX_VALUE) { + String id = Long.toString(index); + ScriptRuntime.setObjectProp(target, id, value, cx); + } else { + ScriptRuntime.setObjectIndex(target, (int)index, value, cx); + } + } + + private static String toStringHelper(Context cx, Scriptable scope, + Scriptable thisObj, + boolean toSource, boolean toLocale) + { + /* It's probably redundant to handle long lengths in this + * function; StringBuffers are limited to 2^31 in java. + */ + + long length = getLengthProperty(cx, thisObj); + + StringBuffer result = new StringBuffer(256); + + // whether to return '4,unquoted,5' or '[4, "quoted", 5]' + String separator; + + if (toSource) { + result.append('['); + separator = ", "; + } else { + separator = ","; + } + + boolean haslast = false; + long i = 0; + + boolean toplevel, iterating; + if (cx.iterating == null) { + toplevel = true; + iterating = false; + cx.iterating = new ObjToIntMap(31); + } else { + toplevel = false; + iterating = cx.iterating.has(thisObj); + } + + // Make sure cx.iterating is set to null when done + // so we don't leak memory + try { + if (!iterating) { + cx.iterating.put(thisObj, 0); // stop recursion. + for (i = 0; i < length; i++) { + if (i > 0) result.append(separator); + Object elem = getElem(cx, thisObj, i); + if (elem == null || elem == Undefined.instance) { + haslast = false; + continue; + } + haslast = true; + + if (toSource) { + result.append(ScriptRuntime.uneval(cx, scope, elem)); + + } else if (elem instanceof String) { + String s = (String)elem; + if (toSource) { + result.append('\"'); + result.append(ScriptRuntime.escapeString(s)); + result.append('\"'); + } else { + result.append(s); + } + + } else { + if (toLocale) + { + Callable fun; + Scriptable funThis; + fun = ScriptRuntime.getPropFunctionAndThis( + elem, "toLocaleString", cx); + funThis = ScriptRuntime.lastStoredScriptable(cx); + elem = fun.call(cx, scope, funThis, + ScriptRuntime.emptyArgs); + } + result.append(ScriptRuntime.toString(elem)); + } + } + } + } finally { + if (toplevel) { + cx.iterating = null; + } + } + + if (toSource) { + //for [,,].length behavior; we want toString to be symmetric. + if (!haslast && i > 0) + result.append(", ]"); + else + result.append(']'); + } + return result.toString(); + } + + /** + * See ECMA 15.4.4.3 + */ + private static String js_join(Context cx, Scriptable thisObj, + Object[] args) + { + long llength = getLengthProperty(cx, thisObj); + int length = (int)llength; + if (llength != length) { + throw Context.reportRuntimeError1( + "msg.arraylength.too.big", String.valueOf(llength)); + } + // if no args, use "," as separator + String separator = (args.length < 1 || args[0] == Undefined.instance) + ? "," + : ScriptRuntime.toString(args[0]); + if (thisObj instanceof NativeArray) { + NativeArray na = (NativeArray) thisObj; + if (na.denseOnly) { + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < length; i++) { + if (i != 0) { + sb.append(separator); + } + if (i < na.dense.length) { + Object temp = na.dense[i]; + if (temp != null && temp != Undefined.instance && + temp != Scriptable.NOT_FOUND) + { + sb.append(ScriptRuntime.toString(temp)); + } + } + } + return sb.toString(); + } + } + if (length == 0) { + return ""; + } + String[] buf = new String[length]; + int total_size = 0; + for (int i = 0; i != length; i++) { + Object temp = getElem(cx, thisObj, i); + if (temp != null && temp != Undefined.instance) { + String str = ScriptRuntime.toString(temp); + total_size += str.length(); + buf[i] = str; + } + } + total_size += (length - 1) * separator.length(); + StringBuffer sb = new StringBuffer(total_size); + for (int i = 0; i != length; i++) { + if (i != 0) { + sb.append(separator); + } + String str = buf[i]; + if (str != null) { + // str == null for undefined or null + sb.append(str); + } + } + return sb.toString(); + } + + /** + * See ECMA 15.4.4.4 + */ + private static Scriptable js_reverse(Context cx, Scriptable thisObj, + Object[] args) + { + if (thisObj instanceof NativeArray) { + NativeArray na = (NativeArray) thisObj; + if (na.denseOnly) { + for (int i=0, j=((int)na.length)-1; i < j; i++,j--) { + Object temp = na.dense[i]; + na.dense[i] = na.dense[j]; + na.dense[j] = temp; + } + return thisObj; + } + } + long len = getLengthProperty(cx, thisObj); + + long half = len / 2; + for(long i=0; i < half; i++) { + long j = len - i - 1; + Object temp1 = getElem(cx, thisObj, i); + Object temp2 = getElem(cx, thisObj, j); + setElem(cx, thisObj, i, temp2); + setElem(cx, thisObj, j, temp1); + } + return thisObj; + } + + /** + * See ECMA 15.4.4.5 + */ + private static Scriptable js_sort(Context cx, Scriptable scope, + Scriptable thisObj, Object[] args) + { + long length = getLengthProperty(cx, thisObj); + + if (length <= 1) { return thisObj; } + + Object compare; + Object[] cmpBuf; + + if (args.length > 0 && Undefined.instance != args[0]) { + // sort with given compare function + compare = args[0]; + cmpBuf = new Object[2]; // Buffer for cmp arguments + } else { + // sort with default compare + compare = null; + cmpBuf = null; + } + if (thisObj instanceof NativeArray) { + NativeArray na = (NativeArray) thisObj; + if (na.denseOnly) { + int ilength = (int) length; + heapsort(cx, scope, na.dense, ilength, compare, cmpBuf); + return thisObj; + } + } + + // Should we use the extended sort function, or the faster one? + if (length >= Integer.MAX_VALUE) { + heapsort_extended(cx, scope, thisObj, length, compare, cmpBuf); + } else { + int ilength = (int)length; + // copy the JS array into a working array, so it can be + // sorted cheaply. + Object[] working = new Object[ilength]; + for (int i = 0; i != ilength; ++i) { + working[i] = getElem(cx, thisObj, i); + } + + heapsort(cx, scope, working, ilength, compare, cmpBuf); + + // copy the working array back into thisObj + for (int i = 0; i != ilength; ++i) { + setElem(cx, thisObj, i, working[i]); + } + } + return thisObj; + } + + // Return true only if x > y + private static boolean isBigger(Context cx, Scriptable scope, + Object x, Object y, + Object cmp, Object[] cmpBuf) + { + if (cmp == null) { + if (cmpBuf != null) Kit.codeBug(); + } else { + if (cmpBuf == null || cmpBuf.length != 2) Kit.codeBug(); + } + + Object undef = Undefined.instance; + Object notfound = Scriptable.NOT_FOUND; + + // sort undefined to end + if (y == undef || y == notfound) { + return false; // x can not be bigger then undef + } else if (x == undef || x == notfound) { + return true; // y != undef here, so x > y + } + + if (cmp == null) { + // if no cmp function supplied, sort lexicographically + String a = ScriptRuntime.toString(x); + String b = ScriptRuntime.toString(y); + return a.compareTo(b) > 0; + } + else { + // assemble args and call supplied JS cmp function + cmpBuf[0] = x; + cmpBuf[1] = y; + Callable fun = ScriptRuntime.getValueFunctionAndThis(cmp, cx); + Scriptable funThis = ScriptRuntime.lastStoredScriptable(cx); + + Object ret = fun.call(cx, scope, funThis, cmpBuf); + double d = ScriptRuntime.toNumber(ret); + + // XXX what to do when cmp function returns NaN? ECMA states + // that it's then not a 'consistent comparison function'... but + // then what do we do? Back out and start over with the generic + // cmp function when we see a NaN? Throw an error? + + // for now, just ignore it: + + return d > 0; + } + } + +/** Heapsort implementation. + * See "Introduction to Algorithms" by Cormen, Leiserson, Rivest for details. + * Adjusted for zero based indexes. + */ + private static void heapsort(Context cx, Scriptable scope, + Object[] array, int length, + Object cmp, Object[] cmpBuf) + { + if (length <= 1) Kit.codeBug(); + + // Build heap + for (int i = length / 2; i != 0;) { + --i; + Object pivot = array[i]; + heapify(cx, scope, pivot, array, i, length, cmp, cmpBuf); + } + + // Sort heap + for (int i = length; i != 1;) { + --i; + Object pivot = array[i]; + array[i] = array[0]; + heapify(cx, scope, pivot, array, 0, i, cmp, cmpBuf); + } + } + +/** pivot and child heaps of i should be made into heap starting at i, + * original array[i] is never used to have less array access during sorting. + */ + private static void heapify(Context cx, Scriptable scope, + Object pivot, Object[] array, int i, int end, + Object cmp, Object[] cmpBuf) + { + for (;;) { + int child = i * 2 + 1; + if (child >= end) { + break; + } + Object childVal = array[child]; + if (child + 1 < end) { + Object nextVal = array[child + 1]; + if (isBigger(cx, scope, nextVal, childVal, cmp, cmpBuf)) { + ++child; childVal = nextVal; + } + } + if (!isBigger(cx, scope, childVal, pivot, cmp, cmpBuf)) { + break; + } + array[i] = childVal; + i = child; + } + array[i] = pivot; + } + +/** Version of heapsort that call getElem/setElem on target to query/assign + * array elements instead of Java array access + */ + private static void heapsort_extended(Context cx, Scriptable scope, + Scriptable target, long length, + Object cmp, Object[] cmpBuf) + { + if (length <= 1) Kit.codeBug(); + + // Build heap + for (long i = length / 2; i != 0;) { + --i; + Object pivot = getElem(cx, target, i); + heapify_extended(cx, scope, pivot, target, i, length, cmp, cmpBuf); + } + + // Sort heap + for (long i = length; i != 1;) { + --i; + Object pivot = getElem(cx, target, i); + setElem(cx, target, i, getElem(cx, target, 0)); + heapify_extended(cx, scope, pivot, target, 0, i, cmp, cmpBuf); + } + } + + private static void heapify_extended(Context cx, Scriptable scope, + Object pivot, Scriptable target, + long i, long end, + Object cmp, Object[] cmpBuf) + { + for (;;) { + long child = i * 2 + 1; + if (child >= end) { + break; + } + Object childVal = getElem(cx, target, child); + if (child + 1 < end) { + Object nextVal = getElem(cx, target, child + 1); + if (isBigger(cx, scope, nextVal, childVal, cmp, cmpBuf)) { + ++child; childVal = nextVal; + } + } + if (!isBigger(cx, scope, childVal, pivot, cmp, cmpBuf)) { + break; + } + setElem(cx, target, i, childVal); + i = child; + } + setElem(cx, target, i, pivot); + } + + /** + * Non-ECMA methods. + */ + + private static Object js_push(Context cx, Scriptable thisObj, + Object[] args) + { + if (thisObj instanceof NativeArray) { + NativeArray na = (NativeArray) thisObj; + if (na.denseOnly && + na.ensureCapacity((int) na.length + args.length)) + { + for (int i = 0; i < args.length; i++) { + na.dense[(int)na.length++] = args[i]; + } + return ScriptRuntime.wrapNumber(na.length); + } + } + long length = getLengthProperty(cx, thisObj); + for (int i = 0; i < args.length; i++) { + setElem(cx, thisObj, length + i, args[i]); + } + + length += args.length; + Object lengthObj = setLengthProperty(cx, thisObj, length); + + /* + * If JS1.2, follow Perl4 by returning the last thing pushed. + * Otherwise, return the new array length. + */ + if (cx.getLanguageVersion() == Context.VERSION_1_2) + // if JS1.2 && no arguments, return undefined. + return args.length == 0 + ? Undefined.instance + : args[args.length - 1]; + + else + return lengthObj; + } + + private static Object js_pop(Context cx, Scriptable thisObj, + Object[] args) + { + Object result; + if (thisObj instanceof NativeArray) { + NativeArray na = (NativeArray) thisObj; + if (na.denseOnly && na.length > 0) { + na.length--; + result = na.dense[(int)na.length]; + na.dense[(int)na.length] = NOT_FOUND; + return result; + } + } + long length = getLengthProperty(cx, thisObj); + if (length > 0) { + length--; + + // Get the to-be-deleted property's value. + result = getElem(cx, thisObj, length); + + // We don't need to delete the last property, because + // setLength does that for us. + } else { + result = Undefined.instance; + } + // necessary to match js even when length < 0; js pop will give a + // length property to any target it is called on. + setLengthProperty(cx, thisObj, length); + + return result; + } + + private static Object js_shift(Context cx, Scriptable thisObj, + Object[] args) + { + if (thisObj instanceof NativeArray) { + NativeArray na = (NativeArray) thisObj; + if (na.denseOnly && na.length > 0) { + na.length--; + Object result = na.dense[0]; + System.arraycopy(na.dense, 1, na.dense, 0, (int)na.length); + na.dense[(int)na.length] = NOT_FOUND; + return result; + } + } + Object result; + long length = getLengthProperty(cx, thisObj); + if (length > 0) { + long i = 0; + length--; + + // Get the to-be-deleted property's value. + result = getElem(cx, thisObj, i); + + /* + * Slide down the array above the first element. Leave i + * set to point to the last element. + */ + if (length > 0) { + for (i = 1; i <= length; i++) { + Object temp = getElem(cx, thisObj, i); + setElem(cx, thisObj, i - 1, temp); + } + } + // We don't need to delete the last property, because + // setLength does that for us. + } else { + result = Undefined.instance; + } + setLengthProperty(cx, thisObj, length); + return result; + } + + private static Object js_unshift(Context cx, Scriptable thisObj, + Object[] args) + { + if (thisObj instanceof NativeArray) { + NativeArray na = (NativeArray) thisObj; + if (na.denseOnly && + na.ensureCapacity((int)na.length + args.length)) + { + System.arraycopy(na.dense, 0, na.dense, args.length, + (int) na.length); + for (int i = 0; i < args.length; i++) { + na.dense[i] = args[i]; + } + na.length += args.length; + return ScriptRuntime.wrapNumber(na.length); + } + } + long length = getLengthProperty(cx, thisObj); + int argc = args.length; + + if (args.length > 0) { + /* Slide up the array to make room for args at the bottom */ + if (length > 0) { + for (long last = length - 1; last >= 0; last--) { + Object temp = getElem(cx, thisObj, last); + setElem(cx, thisObj, last + argc, temp); + } + } + + /* Copy from argv to the bottom of the array. */ + for (int i = 0; i < args.length; i++) { + setElem(cx, thisObj, i, args[i]); + } + + /* Follow Perl by returning the new array length. */ + length += args.length; + return setLengthProperty(cx, thisObj, length); + } + return ScriptRuntime.wrapNumber(length); + } + + private static Object js_splice(Context cx, Scriptable scope, + Scriptable thisObj, Object[] args) + { + NativeArray na = null; + boolean denseMode = false; + if (thisObj instanceof NativeArray) { + na = (NativeArray) thisObj; + denseMode = na.denseOnly; + } + + /* create an empty Array to return. */ + scope = getTopLevelScope(scope); + int argc = args.length; + if (argc == 0) + return ScriptRuntime.newObject(cx, scope, "Array", null); + long length = getLengthProperty(cx, thisObj); + + /* Convert the first argument into a starting index. */ + long begin = toSliceIndex(ScriptRuntime.toInteger(args[0]), length); + argc--; + + /* Convert the second argument into count */ + long count; + if (args.length == 1) { + count = length - begin; + } else { + double dcount = ScriptRuntime.toInteger(args[1]); + if (dcount < 0) { + count = 0; + } else if (dcount > (length - begin)) { + count = length - begin; + } else { + count = (long)dcount; + } + argc--; + } + + long end = begin + count; + + /* If there are elements to remove, put them into the return value. */ + Object result; + if (count != 0) { + if (count == 1 + && (cx.getLanguageVersion() == Context.VERSION_1_2)) + { + /* + * JS lacks "list context", whereby in Perl one turns the + * single scalar that's spliced out into an array just by + * assigning it to @single instead of $single, or by using it + * as Perl push's first argument, for instance. + * + * JS1.2 emulated Perl too closely and returned a non-Array for + * the single-splice-out case, requiring callers to test and + * wrap in [] if necessary. So JS1.3, default, and other + * versions all return an array of length 1 for uniformity. + */ + result = getElem(cx, thisObj, begin); + } else { + if (denseMode) { + int intLen = (int) (end - begin); + Object[] copy = new Object[intLen]; + System.arraycopy(na.dense, (int) begin, copy, 0, intLen); + result = cx.newArray(scope, copy); + } else { + Scriptable resultArray = ScriptRuntime.newObject(cx, scope, + "Array", null); + for (long last = begin; last != end; last++) { + Object temp = getElem(cx, thisObj, last); + setElem(cx, resultArray, last - begin, temp); + } + result = resultArray; + } + } + } else { // (count == 0) + if (cx.getLanguageVersion() == Context.VERSION_1_2) { + /* Emulate C JS1.2; if no elements are removed, return undefined. */ + result = Undefined.instance; + } else { + result = ScriptRuntime.newObject(cx, scope, "Array", null); + } + } + + /* Find the direction (up or down) to copy and make way for argv. */ + long delta = argc - count; + if (denseMode && length + delta < Integer.MAX_VALUE && + na.ensureCapacity((int) (length + delta))) + { + System.arraycopy(na.dense, (int) end, na.dense, + (int) (begin + argc), (int) (length - end)); + if (argc > 0) { + System.arraycopy(args, 2, na.dense, (int) begin, argc); + } + if (delta < 0) { + Arrays.fill(na.dense, (int) (length + delta), (int) length, + NOT_FOUND); + } + na.length = length + delta; + return result; + } + + if (delta > 0) { + for (long last = length - 1; last >= end; last--) { + Object temp = getElem(cx, thisObj, last); + setElem(cx, thisObj, last + delta, temp); + } + } else if (delta < 0) { + for (long last = end; last < length; last++) { + Object temp = getElem(cx, thisObj, last); + setElem(cx, thisObj, last + delta, temp); + } + } + + /* Copy from argv into the hole to complete the splice. */ + int argoffset = args.length - argc; + for (int i = 0; i < argc; i++) { + setElem(cx, thisObj, begin + i, args[i + argoffset]); + } + + /* Update length in case we deleted elements from the end. */ + setLengthProperty(cx, thisObj, length + delta); + return result; + } + + /* + * See Ecma 262v3 15.4.4.4 + */ + private static Scriptable js_concat(Context cx, Scriptable scope, + Scriptable thisObj, Object[] args) + { + // create an empty Array to return. + scope = getTopLevelScope(scope); + Function ctor = ScriptRuntime.getExistingCtor(cx, scope, "Array"); + Scriptable result = ctor.construct(cx, scope, ScriptRuntime.emptyArgs); + if (thisObj instanceof NativeArray && result instanceof NativeArray) { + NativeArray denseThis = (NativeArray) thisObj; + NativeArray denseResult = (NativeArray) result; + if (denseThis.denseOnly && denseResult.denseOnly) { + // First calculate length of resulting array + boolean canUseDense = true; + int length = (int) denseThis.length; + for (int i = 0; i < args.length && canUseDense; i++) { + if (ScriptRuntime.instanceOf(args[i], ctor, cx)) { + // only try to use dense approach for Array-like + // objects that are actually NativeArrays + canUseDense = args[i] instanceof NativeArray; + length += ((NativeArray) args[i]).length; + } else { + length++; + } + } + if (canUseDense && denseResult.ensureCapacity(length)) { + System.arraycopy(denseThis.dense, 0, denseResult.dense, + 0, (int) denseThis.length); + int cursor = (int) denseThis.length; + for (int i = 0; i < args.length && canUseDense; i++) { + if (args[i] instanceof NativeArray) { + NativeArray arg = (NativeArray) args[i]; + System.arraycopy(arg.dense, 0, + denseResult.dense, cursor, + (int)arg.length); + cursor += (int)arg.length; + } else { + denseResult.dense[cursor++] = args[i]; + } + } + denseResult.length = length; + return result; + } + } + } + + long length; + long slot = 0; + + /* Put the target in the result array; only add it as an array + * if it looks like one. + */ + if (ScriptRuntime.instanceOf(thisObj, ctor, cx)) { + length = getLengthProperty(cx, thisObj); + + // Copy from the target object into the result + for (slot = 0; slot < length; slot++) { + Object temp = getElem(cx, thisObj, slot); + setElem(cx, result, slot, temp); + } + } else { + setElem(cx, result, slot++, thisObj); + } + + /* Copy from the arguments into the result. If any argument + * has a numeric length property, treat it as an array and add + * elements separately; otherwise, just copy the argument. + */ + for (int i = 0; i < args.length; i++) { + if (ScriptRuntime.instanceOf(args[i], ctor, cx)) { + // ScriptRuntime.instanceOf => instanceof Scriptable + Scriptable arg = (Scriptable)args[i]; + length = getLengthProperty(cx, arg); + for (long j = 0; j < length; j++, slot++) { + Object temp = getElem(cx, arg, j); + setElem(cx, result, slot, temp); + } + } else { + setElem(cx, result, slot++, args[i]); + } + } + return result; + } + + private Scriptable js_slice(Context cx, Scriptable thisObj, + Object[] args) + { + Scriptable scope = getTopLevelScope(this); + Scriptable result = ScriptRuntime.newObject(cx, scope, "Array", null); + long length = getLengthProperty(cx, thisObj); + + long begin, end; + if (args.length == 0) { + begin = 0; + end = length; + } else { + begin = toSliceIndex(ScriptRuntime.toInteger(args[0]), length); + if (args.length == 1) { + end = length; + } else { + end = toSliceIndex(ScriptRuntime.toInteger(args[1]), length); + } + } + + for (long slot = begin; slot < end; slot++) { + Object temp = getElem(cx, thisObj, slot); + setElem(cx, result, slot - begin, temp); + } + + return result; + } + + private static long toSliceIndex(double value, long length) { + long result; + if (value < 0.0) { + if (value + length < 0.0) { + result = 0; + } else { + result = (long)(value + length); + } + } else if (value > length) { + result = length; + } else { + result = (long)value; + } + return result; + } + + /** + * Implements the methods "indexOf" and "lastIndexOf". + */ + private Object indexOfHelper(Context cx, Scriptable thisObj, + Object[] args, boolean isLast) + { + Object compareTo = args.length > 0 ? args[0] : Undefined.instance; + long length = getLengthProperty(cx, thisObj); + long start; + if (isLast) { + // lastIndexOf + /* + * From http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:lastIndexOf + * The index at which to start searching backwards. Defaults to the + * array's length, i.e. the whole array will be searched. If the + * index is greater than or equal to the length of the array, the + * whole array will be searched. If negative, it is taken as the + * offset from the end of the array. Note that even when the index + * is negative, the array is still searched from back to front. If + * the calculated index is less than 0, -1 is returned, i.e. the + * array will not be searched. + */ + if (args.length < 2) { + // default + start = length-1; + } else { + start = ScriptRuntime.toInt32(ScriptRuntime.toNumber(args[1])); + if (start >= length) + start = length-1; + else if (start < 0) + start += length; + // Note that start may be negative, but that's okay + // as the result of -1 will fall out from the code below + } + } else { + // indexOf + /* + * From http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:indexOf + * The index at which to begin the search. Defaults to 0, i.e. the + * whole array will be searched. If the index is greater than or + * equal to the length of the array, -1 is returned, i.e. the array + * will not be searched. If negative, it is taken as the offset from + * the end of the array. Note that even when the index is negative, + * the array is still searched from front to back. If the calculated + * index is less than 0, the whole array will be searched. + */ + if (args.length < 2) { + // default + start = 0; + } else { + start = ScriptRuntime.toInt32(ScriptRuntime.toNumber(args[1])); + if (start < 0) { + start += length; + if (start < 0) + start = 0; + } + // Note that start may be > length-1, but that's okay + // as the result of -1 will fall out from the code below + } + } + if (thisObj instanceof NativeArray) { + NativeArray na = (NativeArray) thisObj; + if (na.denseOnly) { + if (isLast) { + for (int i=(int)start; i >= 0; i--) { + if (na.dense[i] != Scriptable.NOT_FOUND && + ScriptRuntime.shallowEq(na.dense[i], compareTo)) + { + return new Long(i); + } + } + } else { + for (int i=(int)start; i < length; i++) { + if (na.dense[i] != Scriptable.NOT_FOUND && + ScriptRuntime.shallowEq(na.dense[i], compareTo)) + { + return new Long(i); + } + } + } + return NEGATIVE_ONE; + } + } + if (isLast) { + for (long i=start; i >= 0; i--) { + if (ScriptRuntime.shallowEq(getElem(cx, thisObj, i), compareTo)) { + return new Long(i); + } + } + } else { + for (long i=start; i < length; i++) { + if (ScriptRuntime.shallowEq(getElem(cx, thisObj, i), compareTo)) { + return new Long(i); + } + } + } + return NEGATIVE_ONE; + } + + /** + * Implements the methods "every", "filter", "forEach", "map", and "some". + */ + private Object iterativeMethod(Context cx, int id, Scriptable scope, + Scriptable thisObj, Object[] args) + { + Object callbackArg = args.length > 0 ? args[0] : Undefined.instance; + if (callbackArg == null || !(callbackArg instanceof Function)) { + throw ScriptRuntime.notFunctionError( + ScriptRuntime.toString(callbackArg)); + } + Function f = (Function) callbackArg; + Scriptable parent = ScriptableObject.getTopLevelScope(f); + Scriptable thisArg; + if (args.length < 2 || args[1] == null || args[1] == Undefined.instance) + { + thisArg = parent; + } else { + thisArg = ScriptRuntime.toObject(cx, scope, args[1]); + } + long length = getLengthProperty(cx, thisObj); + Scriptable array = ScriptRuntime.newObject(cx, scope, "Array", null); + long j=0; + for (long i=0; i < length; i++) { + Object[] innerArgs = new Object[3]; + Object elem = (i > Integer.MAX_VALUE) + ? ScriptableObject.getProperty(thisObj, Long.toString(i)) + : ScriptableObject.getProperty(thisObj, (int)i); + if (elem == Scriptable.NOT_FOUND) { + continue; + } + innerArgs[0] = elem; + innerArgs[1] = new Long(i); + innerArgs[2] = thisObj; + Object result = f.call(cx, parent, thisArg, innerArgs); + switch (id) { + case Id_every: + if (!ScriptRuntime.toBoolean(result)) + return Boolean.FALSE; + break; + case Id_filter: + if (ScriptRuntime.toBoolean(result)) + setElem(cx, array, j++, innerArgs[0]); + break; + case Id_forEach: + break; + case Id_map: + setElem(cx, array, i, result); + break; + case Id_some: + if (ScriptRuntime.toBoolean(result)) + return Boolean.TRUE; + break; + } + } + switch (id) { + case Id_every: + return Boolean.TRUE; + case Id_filter: + case Id_map: + return array; + case Id_some: + return Boolean.FALSE; + case Id_forEach: + default: + return Undefined.instance; + } + } + +// #string_id_map# + + protected int findPrototypeId(String s) + { + int id; +// #generated# Last update: 2005-09-26 15:47:42 EDT + L0: { id = 0; String X = null; int c; + L: switch (s.length()) { + case 3: c=s.charAt(0); + if (c=='m') { if (s.charAt(2)=='p' && s.charAt(1)=='a') {id=Id_map; break L0;} } + else if (c=='p') { if (s.charAt(2)=='p' && s.charAt(1)=='o') {id=Id_pop; break L0;} } + break L; + case 4: switch (s.charAt(2)) { + case 'i': X="join";id=Id_join; break L; + case 'm': X="some";id=Id_some; break L; + case 'r': X="sort";id=Id_sort; break L; + case 's': X="push";id=Id_push; break L; + } break L; + case 5: c=s.charAt(1); + if (c=='h') { X="shift";id=Id_shift; } + else if (c=='l') { X="slice";id=Id_slice; } + else if (c=='v') { X="every";id=Id_every; } + break L; + case 6: c=s.charAt(0); + if (c=='c') { X="concat";id=Id_concat; } + else if (c=='f') { X="filter";id=Id_filter; } + else if (c=='s') { X="splice";id=Id_splice; } + break L; + case 7: switch (s.charAt(0)) { + case 'f': X="forEach";id=Id_forEach; break L; + case 'i': X="indexOf";id=Id_indexOf; break L; + case 'r': X="reverse";id=Id_reverse; break L; + case 'u': X="unshift";id=Id_unshift; break L; + } break L; + case 8: c=s.charAt(3); + if (c=='o') { X="toSource";id=Id_toSource; } + else if (c=='t') { X="toString";id=Id_toString; } + break L; + case 11: c=s.charAt(0); + if (c=='c') { X="constructor";id=Id_constructor; } + else if (c=='l') { X="lastIndexOf";id=Id_lastIndexOf; } + break L; + case 14: X="toLocaleString";id=Id_toLocaleString; break L; + } + if (X!=null && X!=s && !X.equals(s)) id = 0; + } +// #/generated# + return id; + } + + private static final int + Id_constructor = 1, + Id_toString = 2, + Id_toLocaleString = 3, + Id_toSource = 4, + Id_join = 5, + Id_reverse = 6, + Id_sort = 7, + Id_push = 8, + Id_pop = 9, + Id_shift = 10, + Id_unshift = 11, + Id_splice = 12, + Id_concat = 13, + Id_slice = 14, + Id_indexOf = 15, + Id_lastIndexOf = 16, + Id_every = 17, + Id_filter = 18, + Id_forEach = 19, + Id_map = 20, + Id_some = 21, + + MAX_PROTOTYPE_ID = 21; + +// #/string_id_map# + + private static final int + ConstructorId_join = -Id_join, + ConstructorId_reverse = -Id_reverse, + ConstructorId_sort = -Id_sort, + ConstructorId_push = -Id_push, + ConstructorId_pop = -Id_pop, + ConstructorId_shift = -Id_shift, + ConstructorId_unshift = -Id_unshift, + ConstructorId_splice = -Id_splice, + ConstructorId_concat = -Id_concat, + ConstructorId_slice = -Id_slice, + ConstructorId_indexOf = -Id_indexOf, + ConstructorId_lastIndexOf = -Id_lastIndexOf, + ConstructorId_every = -Id_every, + ConstructorId_filter = -Id_filter, + ConstructorId_forEach = -Id_forEach, + ConstructorId_map = -Id_map, + ConstructorId_some = -Id_some; + + /** + * Internal representation of the JavaScript array's length property. + */ + private long length; + + /** + * Fast storage for dense arrays. Sparse arrays will use the superclass's + * hashtable storage scheme. + */ + private Object[] dense; + + /** + * True if all numeric properties are stored in dense. + */ + private boolean denseOnly; + + /** + * The maximum size of dense that will be allocated initially. + */ + private static int maximumInitialCapacity = 10000; + + /** + * The default capacity for dense. + */ + private static final int DEFAULT_INITIAL_CAPACITY = 10; + + /** + * The factor to grow dense by. + */ + private static final double GROW_FACTOR = 1.5; + private static final int MAX_PRE_GROW_SIZE = (int)(Integer.MAX_VALUE / GROW_FACTOR); +} diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeBoolean.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeBoolean.java new file mode 100644 index 0000000..b6a106a --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeBoolean.java @@ -0,0 +1,170 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Norris Boyd + * Igor Bukanov + * Mike McCabe + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript; + +/** + * This class implements the Boolean native object. + * See ECMA 15.6. + * @author Norris Boyd + */ +final class NativeBoolean extends IdScriptableObject +{ + static final long serialVersionUID = -3716996899943880933L; + + private static final Object BOOLEAN_TAG = new Object(); + + static void init(Scriptable scope, boolean sealed) + { + NativeBoolean obj = new NativeBoolean(false); + obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed); + } + + private NativeBoolean(boolean b) + { + booleanValue = b; + } + + public String getClassName() + { + return "Boolean"; + } + + public Object getDefaultValue(Class typeHint) { + // This is actually non-ECMA, but will be proposed + // as a change in round 2. + if (typeHint == ScriptRuntime.BooleanClass) + return ScriptRuntime.wrapBoolean(booleanValue); + return super.getDefaultValue(typeHint); + } + + protected void initPrototypeId(int id) + { + String s; + int arity; + switch (id) { + case Id_constructor: arity=1; s="constructor"; break; + case Id_toString: arity=0; s="toString"; break; + case Id_toSource: arity=0; s="toSource"; break; + case Id_valueOf: arity=0; s="valueOf"; break; + default: throw new IllegalArgumentException(String.valueOf(id)); + } + initPrototypeMethod(BOOLEAN_TAG, id, s, arity); + } + + public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, + Scriptable thisObj, Object[] args) + { + if (!f.hasTag(BOOLEAN_TAG)) { + return super.execIdCall(f, cx, scope, thisObj, args); + } + int id = f.methodId(); + + if (id == Id_constructor) { + boolean b; + if (args.length == 0) { + b = false; + } else { + b = args[0] instanceof ScriptableObject && + ((ScriptableObject) args[0]).avoidObjectDetection() + ? true + : ScriptRuntime.toBoolean(args[0]); + } + if (thisObj == null) { + // new Boolean(val) creates a new boolean object. + return new NativeBoolean(b); + } + // Boolean(val) converts val to a boolean. + return ScriptRuntime.wrapBoolean(b); + } + + // The rest of Boolean.prototype methods require thisObj to be Boolean + + if (!(thisObj instanceof NativeBoolean)) + throw incompatibleCallError(f); + boolean value = ((NativeBoolean)thisObj).booleanValue; + + switch (id) { + + case Id_toString: + return value ? "true" : "false"; + + case Id_toSource: + return value ? "(new Boolean(true))" : "(new Boolean(false))"; + + case Id_valueOf: + return ScriptRuntime.wrapBoolean(value); + } + throw new IllegalArgumentException(String.valueOf(id)); + } + +// #string_id_map# + + protected int findPrototypeId(String s) + { + int id; +// #generated# Last update: 2007-05-09 08:15:31 EDT + L0: { id = 0; String X = null; int c; + int s_length = s.length(); + if (s_length==7) { X="valueOf";id=Id_valueOf; } + else if (s_length==8) { + c=s.charAt(3); + if (c=='o') { X="toSource";id=Id_toSource; } + else if (c=='t') { X="toString";id=Id_toString; } + } + else if (s_length==11) { X="constructor";id=Id_constructor; } + if (X!=null && X!=s && !X.equals(s)) id = 0; + break L0; + } +// #/generated# + return id; + } + + private static final int + Id_constructor = 1, + Id_toString = 2, + Id_toSource = 3, + Id_valueOf = 4, + MAX_PROTOTYPE_ID = 4; + +// #/string_id_map# + + private boolean booleanValue; +} diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeCall.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeCall.java new file mode 100644 index 0000000..b196ac3 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeCall.java @@ -0,0 +1,154 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Norris Boyd + * Bob Jervis + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript; + +/** + * This class implements the activation object. + * + * See ECMA 10.1.6 + * + * @see org.mozilla.javascript.Arguments + * @author Norris Boyd + */ +public final class NativeCall extends IdScriptableObject +{ + static final long serialVersionUID = -7471457301304454454L; + + private static final Object CALL_TAG = new Object(); + + static void init(Scriptable scope, boolean sealed) + { + NativeCall obj = new NativeCall(); + obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed); + } + + NativeCall() { } + + NativeCall(NativeFunction function, Scriptable scope, Object[] args) + { + this.function = function; + + setParentScope(scope); + // leave prototype null + + this.originalArgs = (args == null) ? ScriptRuntime.emptyArgs : args; + + // initialize values of arguments + int paramAndVarCount = function.getParamAndVarCount(); + int paramCount = function.getParamCount(); + if (paramAndVarCount != 0) { + for (int i = 0; i < paramCount; ++i) { + String name = function.getParamOrVarName(i); + Object val = i < args.length ? args[i] + : Undefined.instance; + defineProperty(name, val, PERMANENT); + } + } + + // initialize "arguments" property but only if it was not overridden by + // the parameter with the same name + if (!super.has("arguments", this)) { + defineProperty("arguments", new Arguments(this), PERMANENT); + } + + if (paramAndVarCount != 0) { + for (int i = paramCount; i < paramAndVarCount; ++i) { + String name = function.getParamOrVarName(i); + if (!super.has(name, this)) { + if (function.getParamOrVarConst(i)) + defineProperty(name, Undefined.instance, CONST); + else + defineProperty(name, Undefined.instance, PERMANENT); + } + } + } + } + + public String getClassName() + { + return "Call"; + } + + protected int findPrototypeId(String s) + { + return s.equals("constructor") ? Id_constructor : 0; + } + + protected void initPrototypeId(int id) + { + String s; + int arity; + if (id == Id_constructor) { + arity=1; s="constructor"; + } else { + throw new IllegalArgumentException(String.valueOf(id)); + } + initPrototypeMethod(CALL_TAG, id, s, arity); + } + + public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, + Scriptable thisObj, Object[] args) + { + if (!f.hasTag(CALL_TAG)) { + return super.execIdCall(f, cx, scope, thisObj, args); + } + int id = f.methodId(); + if (id == Id_constructor) { + if (thisObj != null) { + throw Context.reportRuntimeError1("msg.only.from.new", "Call"); + } + ScriptRuntime.checkDeprecated(cx, "Call"); + NativeCall result = new NativeCall(); + result.setPrototype(getObjectPrototype(scope)); + return result; + } + throw new IllegalArgumentException(String.valueOf(id)); + } + + private static final int + Id_constructor = 1, + MAX_PROTOTYPE_ID = 1; + + NativeFunction function; + Object[] originalArgs; + + transient NativeCall parentActivationCall; +} + diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeDate.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeDate.java new file mode 100644 index 0000000..75d41ab --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeDate.java @@ -0,0 +1,1604 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Peter Annema + * Norris Boyd + * Mike McCabe + * Ilya Frank + * + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript; + +import java.util.Date; +import java.text.DateFormat; + +/** + * This class implements the Date native object. + * See ECMA 15.9. + * @author Mike McCabe + */ +final class NativeDate extends IdScriptableObject +{ + static final long serialVersionUID = -8307438915861678966L; + + private static final Object DATE_TAG = new Object(); + + private static final String js_NaN_date_str = "Invalid Date"; + + static void init(Scriptable scope, boolean sealed) + { + NativeDate obj = new NativeDate(); + // Set the value of the prototype Date to NaN ('invalid date'); + obj.date = ScriptRuntime.NaN; + obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed); + } + + private NativeDate() + { + if (thisTimeZone == null) { + // j.u.TimeZone is synchronized, so setting class statics from it + // should be OK. + thisTimeZone = java.util.TimeZone.getDefault(); + LocalTZA = thisTimeZone.getRawOffset(); + } + } + + public String getClassName() + { + return "Date"; + } + + public Object getDefaultValue(Class typeHint) + { + if (typeHint == null) + typeHint = ScriptRuntime.StringClass; + return super.getDefaultValue(typeHint); + } + + double getJSTimeValue() + { + return date; + } + + protected void fillConstructorProperties(IdFunctionObject ctor) + { + addIdFunctionProperty(ctor, DATE_TAG, ConstructorId_now, + "now", 0); + addIdFunctionProperty(ctor, DATE_TAG, ConstructorId_parse, + "parse", 1); + addIdFunctionProperty(ctor, DATE_TAG, ConstructorId_UTC, + "UTC", 1); + super.fillConstructorProperties(ctor); + } + + protected void initPrototypeId(int id) + { + String s; + int arity; + switch (id) { + case Id_constructor: arity=1; s="constructor"; break; + case Id_toString: arity=0; s="toString"; break; + case Id_toTimeString: arity=0; s="toTimeString"; break; + case Id_toDateString: arity=0; s="toDateString"; break; + case Id_toLocaleString: arity=0; s="toLocaleString"; break; + case Id_toLocaleTimeString: arity=0; s="toLocaleTimeString"; break; + case Id_toLocaleDateString: arity=0; s="toLocaleDateString"; break; + case Id_toUTCString: arity=0; s="toUTCString"; break; + case Id_toSource: arity=0; s="toSource"; break; + case Id_valueOf: arity=0; s="valueOf"; break; + case Id_getTime: arity=0; s="getTime"; break; + case Id_getYear: arity=0; s="getYear"; break; + case Id_getFullYear: arity=0; s="getFullYear"; break; + case Id_getUTCFullYear: arity=0; s="getUTCFullYear"; break; + case Id_getMonth: arity=0; s="getMonth"; break; + case Id_getUTCMonth: arity=0; s="getUTCMonth"; break; + case Id_getDate: arity=0; s="getDate"; break; + case Id_getUTCDate: arity=0; s="getUTCDate"; break; + case Id_getDay: arity=0; s="getDay"; break; + case Id_getUTCDay: arity=0; s="getUTCDay"; break; + case Id_getHours: arity=0; s="getHours"; break; + case Id_getUTCHours: arity=0; s="getUTCHours"; break; + case Id_getMinutes: arity=0; s="getMinutes"; break; + case Id_getUTCMinutes: arity=0; s="getUTCMinutes"; break; + case Id_getSeconds: arity=0; s="getSeconds"; break; + case Id_getUTCSeconds: arity=0; s="getUTCSeconds"; break; + case Id_getMilliseconds: arity=0; s="getMilliseconds"; break; + case Id_getUTCMilliseconds: arity=0; s="getUTCMilliseconds"; break; + case Id_getTimezoneOffset: arity=0; s="getTimezoneOffset"; break; + case Id_setTime: arity=1; s="setTime"; break; + case Id_setMilliseconds: arity=1; s="setMilliseconds"; break; + case Id_setUTCMilliseconds: arity=1; s="setUTCMilliseconds"; break; + case Id_setSeconds: arity=2; s="setSeconds"; break; + case Id_setUTCSeconds: arity=2; s="setUTCSeconds"; break; + case Id_setMinutes: arity=3; s="setMinutes"; break; + case Id_setUTCMinutes: arity=3; s="setUTCMinutes"; break; + case Id_setHours: arity=4; s="setHours"; break; + case Id_setUTCHours: arity=4; s="setUTCHours"; break; + case Id_setDate: arity=1; s="setDate"; break; + case Id_setUTCDate: arity=1; s="setUTCDate"; break; + case Id_setMonth: arity=2; s="setMonth"; break; + case Id_setUTCMonth: arity=2; s="setUTCMonth"; break; + case Id_setFullYear: arity=3; s="setFullYear"; break; + case Id_setUTCFullYear: arity=3; s="setUTCFullYear"; break; + case Id_setYear: arity=1; s="setYear"; break; + default: throw new IllegalArgumentException(String.valueOf(id)); + } + initPrototypeMethod(DATE_TAG, id, s, arity); + } + + public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, + Scriptable thisObj, Object[] args) + { + if (!f.hasTag(DATE_TAG)) { + return super.execIdCall(f, cx, scope, thisObj, args); + } + int id = f.methodId(); + switch (id) { + case ConstructorId_now: + return ScriptRuntime.wrapNumber(now()); + + case ConstructorId_parse: + { + String dataStr = ScriptRuntime.toString(args, 0); + return ScriptRuntime.wrapNumber(date_parseString(dataStr)); + } + + case ConstructorId_UTC: + return ScriptRuntime.wrapNumber(jsStaticFunction_UTC(args)); + + case Id_constructor: + { + // if called as a function, just return a string + // representing the current time. + if (thisObj != null) + return date_format(now(), Id_toString); + return jsConstructor(args); + } + } + + // The rest of Date.prototype methods require thisObj to be Date + + if (!(thisObj instanceof NativeDate)) + throw incompatibleCallError(f); + NativeDate realThis = (NativeDate)thisObj; + double t = realThis.date; + + switch (id) { + + case Id_toString: + case Id_toTimeString: + case Id_toDateString: + if (t == t) { + return date_format(t, id); + } + return js_NaN_date_str; + + case Id_toLocaleString: + case Id_toLocaleTimeString: + case Id_toLocaleDateString: + if (t == t) { + return toLocale_helper(t, id); + } + return js_NaN_date_str; + + case Id_toUTCString: + if (t == t) { + return js_toUTCString(t); + } + return js_NaN_date_str; + + case Id_toSource: + return "(new Date("+ScriptRuntime.toString(t)+"))"; + + case Id_valueOf: + case Id_getTime: + return ScriptRuntime.wrapNumber(t); + + case Id_getYear: + case Id_getFullYear: + case Id_getUTCFullYear: + if (t == t) { + if (id != Id_getUTCFullYear) t = LocalTime(t); + t = YearFromTime(t); + if (id == Id_getYear) { + if (cx.hasFeature(Context.FEATURE_NON_ECMA_GET_YEAR)) { + if (1900 <= t && t < 2000) { + t -= 1900; + } + } else { + t -= 1900; + } + } + } + return ScriptRuntime.wrapNumber(t); + + case Id_getMonth: + case Id_getUTCMonth: + if (t == t) { + if (id == Id_getMonth) t = LocalTime(t); + t = MonthFromTime(t); + } + return ScriptRuntime.wrapNumber(t); + + case Id_getDate: + case Id_getUTCDate: + if (t == t) { + if (id == Id_getDate) t = LocalTime(t); + t = DateFromTime(t); + } + return ScriptRuntime.wrapNumber(t); + + case Id_getDay: + case Id_getUTCDay: + if (t == t) { + if (id == Id_getDay) t = LocalTime(t); + t = WeekDay(t); + } + return ScriptRuntime.wrapNumber(t); + + case Id_getHours: + case Id_getUTCHours: + if (t == t) { + if (id == Id_getHours) t = LocalTime(t); + t = HourFromTime(t); + } + return ScriptRuntime.wrapNumber(t); + + case Id_getMinutes: + case Id_getUTCMinutes: + if (t == t) { + if (id == Id_getMinutes) t = LocalTime(t); + t = MinFromTime(t); + } + return ScriptRuntime.wrapNumber(t); + + case Id_getSeconds: + case Id_getUTCSeconds: + if (t == t) { + if (id == Id_getSeconds) t = LocalTime(t); + t = SecFromTime(t); + } + return ScriptRuntime.wrapNumber(t); + + case Id_getMilliseconds: + case Id_getUTCMilliseconds: + if (t == t) { + if (id == Id_getMilliseconds) t = LocalTime(t); + t = msFromTime(t); + } + return ScriptRuntime.wrapNumber(t); + + case Id_getTimezoneOffset: + if (t == t) { + t = (t - LocalTime(t)) / msPerMinute; + } + return ScriptRuntime.wrapNumber(t); + + case Id_setTime: + t = TimeClip(ScriptRuntime.toNumber(args, 0)); + realThis.date = t; + return ScriptRuntime.wrapNumber(t); + + case Id_setMilliseconds: + case Id_setUTCMilliseconds: + case Id_setSeconds: + case Id_setUTCSeconds: + case Id_setMinutes: + case Id_setUTCMinutes: + case Id_setHours: + case Id_setUTCHours: + t = makeTime(t, args, id); + realThis.date = t; + return ScriptRuntime.wrapNumber(t); + + case Id_setDate: + case Id_setUTCDate: + case Id_setMonth: + case Id_setUTCMonth: + case Id_setFullYear: + case Id_setUTCFullYear: + t = makeDate(t, args, id); + realThis.date = t; + return ScriptRuntime.wrapNumber(t); + + case Id_setYear: + { + double year = ScriptRuntime.toNumber(args, 0); + + if (year != year || Double.isInfinite(year)) { + t = ScriptRuntime.NaN; + } else { + if (t != t) { + t = 0; + } else { + t = LocalTime(t); + } + + if (year >= 0 && year <= 99) + year += 1900; + + double day = MakeDay(year, MonthFromTime(t), + DateFromTime(t)); + t = MakeDate(day, TimeWithinDay(t)); + t = internalUTC(t); + t = TimeClip(t); + } + } + realThis.date = t; + return ScriptRuntime.wrapNumber(t); + + default: throw new IllegalArgumentException(String.valueOf(id)); + } + + } + + /* ECMA helper functions */ + + private static final double HalfTimeDomain = 8.64e15; + private static final double HoursPerDay = 24.0; + private static final double MinutesPerHour = 60.0; + private static final double SecondsPerMinute = 60.0; + private static final double msPerSecond = 1000.0; + private static final double MinutesPerDay = (HoursPerDay * MinutesPerHour); + private static final double SecondsPerDay = (MinutesPerDay * SecondsPerMinute); + private static final double SecondsPerHour = (MinutesPerHour * SecondsPerMinute); + private static final double msPerDay = (SecondsPerDay * msPerSecond); + private static final double msPerHour = (SecondsPerHour * msPerSecond); + private static final double msPerMinute = (SecondsPerMinute * msPerSecond); + + private static double Day(double t) + { + return Math.floor(t / msPerDay); + } + + private static double TimeWithinDay(double t) + { + double result; + result = t % msPerDay; + if (result < 0) + result += msPerDay; + return result; + } + + private static boolean IsLeapYear(int year) + { + return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); + } + + /* math here has to be f.p, because we need + * floor((1968 - 1969) / 4) == -1 + */ + private static double DayFromYear(double y) + { + return ((365 * ((y)-1970) + Math.floor(((y)-1969)/4.0) + - Math.floor(((y)-1901)/100.0) + Math.floor(((y)-1601)/400.0))); + } + + private static double TimeFromYear(double y) + { + return DayFromYear(y) * msPerDay; + } + + private static int YearFromTime(double t) + { + int lo = (int) Math.floor((t / msPerDay) / 366) + 1970; + int hi = (int) Math.floor((t / msPerDay) / 365) + 1970; + int mid; + + /* above doesn't work for negative dates... */ + if (hi < lo) { + int temp = lo; + lo = hi; + hi = temp; + } + + /* Use a simple binary search algorithm to find the right + year. This seems like brute force... but the computation + of hi and lo years above lands within one year of the + correct answer for years within a thousand years of + 1970; the loop below only requires six iterations + for year 270000. */ + while (hi > lo) { + mid = (hi + lo) / 2; + if (TimeFromYear(mid) > t) { + hi = mid - 1; + } else { + lo = mid + 1; + if (TimeFromYear(lo) > t) { + return mid; + } + } + } + return lo; + } + + private static double DayFromMonth(int m, int year) + { + int day = m * 30; + + if (m >= 7) { day += m / 2 - 1; } + else if (m >= 2) { day += (m - 1) / 2 - 1; } + else { day += m; } + + if (m >= 2 && IsLeapYear(year)) { ++day; } + + return day; + } + + private static int MonthFromTime(double t) + { + int year = YearFromTime(t); + int d = (int)(Day(t) - DayFromYear(year)); + + d -= 31 + 28; + if (d < 0) { + return (d < -28) ? 0 : 1; + } + + if (IsLeapYear(year)) { + if (d == 0) + return 1; // 29 February + --d; + } + + // d: date count from 1 March + int estimate = d / 30; // approx number of month since March + int mstart; + switch (estimate) { + case 0: return 2; + case 1: mstart = 31; break; + case 2: mstart = 31+30; break; + case 3: mstart = 31+30+31; break; + case 4: mstart = 31+30+31+30; break; + case 5: mstart = 31+30+31+30+31; break; + case 6: mstart = 31+30+31+30+31+31; break; + case 7: mstart = 31+30+31+30+31+31+30; break; + case 8: mstart = 31+30+31+30+31+31+30+31; break; + case 9: mstart = 31+30+31+30+31+31+30+31+30; break; + case 10: return 11; //Late december + default: throw Kit.codeBug(); + } + // if d < mstart then real month since March == estimate - 1 + return (d >= mstart) ? estimate + 2 : estimate + 1; + } + + private static int DateFromTime(double t) + { + int year = YearFromTime(t); + int d = (int)(Day(t) - DayFromYear(year)); + + d -= 31 + 28; + if (d < 0) { + return (d < -28) ? d + 31 + 28 + 1 : d + 28 + 1; + } + + if (IsLeapYear(year)) { + if (d == 0) + return 29; // 29 February + --d; + } + + // d: date count from 1 March + int mdays, mstart; + switch (d / 30) { // approx number of month since March + case 0: return d + 1; + case 1: mdays = 31; mstart = 31; break; + case 2: mdays = 30; mstart = 31+30; break; + case 3: mdays = 31; mstart = 31+30+31; break; + case 4: mdays = 30; mstart = 31+30+31+30; break; + case 5: mdays = 31; mstart = 31+30+31+30+31; break; + case 6: mdays = 31; mstart = 31+30+31+30+31+31; break; + case 7: mdays = 30; mstart = 31+30+31+30+31+31+30; break; + case 8: mdays = 31; mstart = 31+30+31+30+31+31+30+31; break; + case 9: mdays = 30; mstart = 31+30+31+30+31+31+30+31+30; break; + case 10: + return d - (31+30+31+30+31+31+30+31+30) + 1; //Late december + default: throw Kit.codeBug(); + } + d -= mstart; + if (d < 0) { + // wrong estimate: sfhift to previous month + d += mdays; + } + return d + 1; + } + + private static int WeekDay(double t) + { + double result; + result = Day(t) + 4; + result = result % 7; + if (result < 0) + result += 7; + return (int) result; + } + + private static double now() + { + return System.currentTimeMillis(); + } + + /* Should be possible to determine the need for this dynamically + * if we go with the workaround... I'm not using it now, because I + * can't think of any clean way to make toLocaleString() and the + * time zone (comment) in toString match the generated string + * values. Currently it's wrong-but-consistent in all but the + * most recent betas of the JRE - seems to work in 1.1.7. + */ + private final static boolean TZO_WORKAROUND = false; + private static double DaylightSavingTA(double t) + { + // Another workaround! The JRE doesn't seem to know about DST + // before year 1 AD, so we map to equivalent dates for the + // purposes of finding dst. To be safe, we do this for years + // outside 1970-2038. + if (t < 0.0 || t > 2145916800000.0) { + int year = EquivalentYear(YearFromTime(t)); + double day = MakeDay(year, MonthFromTime(t), DateFromTime(t)); + t = MakeDate(day, TimeWithinDay(t)); + } + if (!TZO_WORKAROUND) { + Date date = new Date((long) t); + if (thisTimeZone.inDaylightTime(date)) + return msPerHour; + else + return 0; + } else { + /* Use getOffset if inDaylightTime() is broken, because it + * seems to work acceptably. We don't switch over to it + * entirely, because it requires (expensive) exploded date arguments, + * and the api makes it impossible to handle dst + * changeovers cleanly. + */ + + // Hardcode the assumption that the changeover always + // happens at 2:00 AM: + t += LocalTZA + (HourFromTime(t) <= 2 ? msPerHour : 0); + + int year = YearFromTime(t); + double offset = thisTimeZone.getOffset(year > 0 ? 1 : 0, + year, + MonthFromTime(t), + DateFromTime(t), + WeekDay(t), + (int)TimeWithinDay(t)); + + if ((offset - LocalTZA) != 0) + return msPerHour; + else + return 0; + // return offset - LocalTZA; + } + } + + /* + * Find a year for which any given date will fall on the same weekday. + * + * This function should be used with caution when used other than + * for determining DST; it hasn't been proven not to produce an + * incorrect year for times near year boundaries. + */ + private static int EquivalentYear(int year) + { + int day = (int) DayFromYear(year) + 4; + day = day % 7; + if (day < 0) + day += 7; + // Years and leap years on which Jan 1 is a Sunday, Monday, etc. + if (IsLeapYear(year)) { + switch (day) { + case 0: return 1984; + case 1: return 1996; + case 2: return 1980; + case 3: return 1992; + case 4: return 1976; + case 5: return 1988; + case 6: return 1972; + } + } else { + switch (day) { + case 0: return 1978; + case 1: return 1973; + case 2: return 1974; + case 3: return 1975; + case 4: return 1981; + case 5: return 1971; + case 6: return 1977; + } + } + // Unreachable + throw Kit.codeBug(); + } + + private static double LocalTime(double t) + { + return t + LocalTZA + DaylightSavingTA(t); + } + + private static double internalUTC(double t) + { + return t - LocalTZA - DaylightSavingTA(t - LocalTZA); + } + + private static int HourFromTime(double t) + { + double result; + result = Math.floor(t / msPerHour) % HoursPerDay; + if (result < 0) + result += HoursPerDay; + return (int) result; + } + + private static int MinFromTime(double t) + { + double result; + result = Math.floor(t / msPerMinute) % MinutesPerHour; + if (result < 0) + result += MinutesPerHour; + return (int) result; + } + + private static int SecFromTime(double t) + { + double result; + result = Math.floor(t / msPerSecond) % SecondsPerMinute; + if (result < 0) + result += SecondsPerMinute; + return (int) result; + } + + private static int msFromTime(double t) + { + double result; + result = t % msPerSecond; + if (result < 0) + result += msPerSecond; + return (int) result; + } + + private static double MakeTime(double hour, double min, + double sec, double ms) + { + return ((hour * MinutesPerHour + min) * SecondsPerMinute + sec) + * msPerSecond + ms; + } + + private static double MakeDay(double year, double month, double date) + { + year += Math.floor(month / 12); + + month = month % 12; + if (month < 0) + month += 12; + + double yearday = Math.floor(TimeFromYear(year) / msPerDay); + double monthday = DayFromMonth((int)month, (int)year); + + return yearday + monthday + date - 1; + } + + private static double MakeDate(double day, double time) + { + return day * msPerDay + time; + } + + private static double TimeClip(double d) + { + if (d != d || + d == Double.POSITIVE_INFINITY || + d == Double.NEGATIVE_INFINITY || + Math.abs(d) > HalfTimeDomain) + { + return ScriptRuntime.NaN; + } + if (d > 0.0) + return Math.floor(d + 0.); + else + return Math.ceil(d + 0.); + } + + /* end of ECMA helper functions */ + + /* find UTC time from given date... no 1900 correction! */ + private static double date_msecFromDate(double year, double mon, + double mday, double hour, + double min, double sec, + double msec) + { + double day; + double time; + double result; + + day = MakeDay(year, mon, mday); + time = MakeTime(hour, min, sec, msec); + result = MakeDate(day, time); + return result; + } + + /* compute the time in msec (unclipped) from the given args */ + private static final int MAXARGS = 7; + private static double date_msecFromArgs(Object[] args) + { + double array[] = new double[MAXARGS]; + int loop; + double d; + + for (loop = 0; loop < MAXARGS; loop++) { + if (loop < args.length) { + d = ScriptRuntime.toNumber(args[loop]); + if (d != d || Double.isInfinite(d)) { + return ScriptRuntime.NaN; + } + array[loop] = ScriptRuntime.toInteger(args[loop]); + } else { + if (loop == 2) { + array[loop] = 1; /* Default the date argument to 1. */ + } else { + array[loop] = 0; + } + } + } + + /* adjust 2-digit years into the 20th century */ + if (array[0] >= 0 && array[0] <= 99) + array[0] += 1900; + + return date_msecFromDate(array[0], array[1], array[2], + array[3], array[4], array[5], array[6]); + } + + private static double jsStaticFunction_UTC(Object[] args) + { + return TimeClip(date_msecFromArgs(args)); + } + + private static double date_parseString(String s) + { + int year = -1; + int mon = -1; + int mday = -1; + int hour = -1; + int min = -1; + int sec = -1; + char c = 0; + char si = 0; + int i = 0; + int n = -1; + double tzoffset = -1; + char prevc = 0; + int limit = 0; + boolean seenplusminus = false; + + limit = s.length(); + while (i < limit) { + c = s.charAt(i); + i++; + if (c <= ' ' || c == ',' || c == '-') { + if (i < limit) { + si = s.charAt(i); + if (c == '-' && '0' <= si && si <= '9') { + prevc = c; + } + } + continue; + } + if (c == '(') { /* comments) */ + int depth = 1; + while (i < limit) { + c = s.charAt(i); + i++; + if (c == '(') + depth++; + else if (c == ')') + if (--depth <= 0) + break; + } + continue; + } + if ('0' <= c && c <= '9') { + n = c - '0'; + while (i < limit && '0' <= (c = s.charAt(i)) && c <= '9') { + n = n * 10 + c - '0'; + i++; + } + + /* allow TZA before the year, so + * 'Wed Nov 05 21:49:11 GMT-0800 1997' + * works */ + + /* uses of seenplusminus allow : in TZA, so Java + * no-timezone style of GMT+4:30 works + */ + if ((prevc == '+' || prevc == '-')/* && year>=0 */) { + /* make ':' case below change tzoffset */ + seenplusminus = true; + + /* offset */ + if (n < 24) + n = n * 60; /* EG. "GMT-3" */ + else + n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */ + if (prevc == '+') /* plus means east of GMT */ + n = -n; + if (tzoffset != 0 && tzoffset != -1) + return ScriptRuntime.NaN; + tzoffset = n; + } else if (n >= 70 || + (prevc == '/' && mon >= 0 && mday >= 0 + && year < 0)) + { + if (year >= 0) + return ScriptRuntime.NaN; + else if (c <= ' ' || c == ',' || c == '/' || i >= limit) + year = n < 100 ? n + 1900 : n; + else + return ScriptRuntime.NaN; + } else if (c == ':') { + if (hour < 0) + hour = /*byte*/ n; + else if (min < 0) + min = /*byte*/ n; + else + return ScriptRuntime.NaN; + } else if (c == '/') { + if (mon < 0) + mon = /*byte*/ n-1; + else if (mday < 0) + mday = /*byte*/ n; + else + return ScriptRuntime.NaN; + } else if (i < limit && c != ',' && c > ' ' && c != '-') { + return ScriptRuntime.NaN; + } else if (seenplusminus && n < 60) { /* handle GMT-3:30 */ + if (tzoffset < 0) + tzoffset -= n; + else + tzoffset += n; + } else if (hour >= 0 && min < 0) { + min = /*byte*/ n; + } else if (min >= 0 && sec < 0) { + sec = /*byte*/ n; + } else if (mday < 0) { + mday = /*byte*/ n; + } else { + return ScriptRuntime.NaN; + } + prevc = 0; + } else if (c == '/' || c == ':' || c == '+' || c == '-') { + prevc = c; + } else { + int st = i - 1; + while (i < limit) { + c = s.charAt(i); + if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'))) + break; + i++; + } + int letterCount = i - st; + if (letterCount < 2) + return ScriptRuntime.NaN; + /* + * Use ported code from jsdate.c rather than the locale-specific + * date-parsing code from Java, to keep js and rhino consistent. + * Is this the right strategy? + */ + String wtb = "am;pm;" + +"monday;tuesday;wednesday;thursday;friday;" + +"saturday;sunday;" + +"january;february;march;april;may;june;" + +"july;august;september;october;november;december;" + +"gmt;ut;utc;est;edt;cst;cdt;mst;mdt;pst;pdt;"; + int index = 0; + for (int wtbOffset = 0; ;) { + int wtbNext = wtb.indexOf(';', wtbOffset); + if (wtbNext < 0) + return ScriptRuntime.NaN; + if (wtb.regionMatches(true, wtbOffset, s, st, letterCount)) + break; + wtbOffset = wtbNext + 1; + ++index; + } + if (index < 2) { + /* + * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as + * 12:30, instead of blindly adding 12 if PM. + */ + if (hour > 12 || hour < 0) { + return ScriptRuntime.NaN; + } else if (index == 0) { + // AM + if (hour == 12) + hour = 0; + } else { + // PM + if (hour != 12) + hour += 12; + } + } else if ((index -= 2) < 7) { + // ignore week days + } else if ((index -= 7) < 12) { + // month + if (mon < 0) { + mon = index; + } else { + return ScriptRuntime.NaN; + } + } else { + index -= 12; + // timezones + switch (index) { + case 0 /* gmt */: tzoffset = 0; break; + case 1 /* ut */: tzoffset = 0; break; + case 2 /* utc */: tzoffset = 0; break; + case 3 /* est */: tzoffset = 5 * 60; break; + case 4 /* edt */: tzoffset = 4 * 60; break; + case 5 /* cst */: tzoffset = 6 * 60; break; + case 6 /* cdt */: tzoffset = 5 * 60; break; + case 7 /* mst */: tzoffset = 7 * 60; break; + case 8 /* mdt */: tzoffset = 6 * 60; break; + case 9 /* pst */: tzoffset = 8 * 60; break; + case 10 /* pdt */:tzoffset = 7 * 60; break; + default: Kit.codeBug(); + } + } + } + } + if (year < 0 || mon < 0 || mday < 0) + return ScriptRuntime.NaN; + if (sec < 0) + sec = 0; + if (min < 0) + min = 0; + if (hour < 0) + hour = 0; + + double msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0); + if (tzoffset == -1) { /* no time zone specified, have to use local */ + return internalUTC(msec); + } else { + return msec + tzoffset * msPerMinute; + } + } + + private static String date_format(double t, int methodId) + { + StringBuffer result = new StringBuffer(60); + double local = LocalTime(t); + + /* Tue Oct 31 09:41:40 GMT-0800 (PST) 2000 */ + /* Tue Oct 31 2000 */ + /* 09:41:40 GMT-0800 (PST) */ + + if (methodId != Id_toTimeString) { + appendWeekDayName(result, WeekDay(local)); + result.append(' '); + appendMonthName(result, MonthFromTime(local)); + result.append(' '); + append0PaddedUint(result, DateFromTime(local), 2); + result.append(' '); + int year = YearFromTime(local); + if (year < 0) { + result.append('-'); + year = -year; + } + append0PaddedUint(result, year, 4); + if (methodId != Id_toDateString) + result.append(' '); + } + + if (methodId != Id_toDateString) { + append0PaddedUint(result, HourFromTime(local), 2); + result.append(':'); + append0PaddedUint(result, MinFromTime(local), 2); + result.append(':'); + append0PaddedUint(result, SecFromTime(local), 2); + + // offset from GMT in minutes. The offset includes daylight + // savings, if it applies. + int minutes = (int) Math.floor((LocalTZA + DaylightSavingTA(t)) + / msPerMinute); + // map 510 minutes to 0830 hours + int offset = (minutes / 60) * 100 + minutes % 60; + if (offset > 0) { + result.append(" GMT+"); + } else { + result.append(" GMT-"); + offset = -offset; + } + append0PaddedUint(result, offset, 4); + + if (timeZoneFormatter == null) + timeZoneFormatter = new java.text.SimpleDateFormat("zzz"); + + // Find an equivalent year before getting the timezone + // comment. See DaylightSavingTA. + if (t < 0.0 || t > 2145916800000.0) { + int equiv = EquivalentYear(YearFromTime(local)); + double day = MakeDay(equiv, MonthFromTime(t), DateFromTime(t)); + t = MakeDate(day, TimeWithinDay(t)); + } + result.append(" ("); + java.util.Date date = new Date((long) t); + synchronized (timeZoneFormatter) { + result.append(timeZoneFormatter.format(date)); + } + result.append(')'); + } + return result.toString(); + } + + /* the javascript constructor */ + private static Object jsConstructor(Object[] args) + { + NativeDate obj = new NativeDate(); + + // if called as a constructor with no args, + // return a new Date with the current time. + if (args.length == 0) { + obj.date = now(); + return obj; + } + + // if called with just one arg - + if (args.length == 1) { + Object arg0 = args[0]; + if (arg0 instanceof Scriptable) + arg0 = ((Scriptable) arg0).getDefaultValue(null); + double date; + if (arg0 instanceof String) { + // it's a string; parse it. + date = date_parseString((String)arg0); + } else { + // if it's not a string, use it as a millisecond date + date = ScriptRuntime.toNumber(arg0); + } + obj.date = TimeClip(date); + return obj; + } + + double time = date_msecFromArgs(args); + + if (!Double.isNaN(time) && !Double.isInfinite(time)) + time = TimeClip(internalUTC(time)); + + obj.date = time; + + return obj; + } + + private static String toLocale_helper(double t, int methodId) + { + java.text.DateFormat formatter; + switch (methodId) { + case Id_toLocaleString: + if (localeDateTimeFormatter == null) { + localeDateTimeFormatter + = DateFormat.getDateTimeInstance(DateFormat.LONG, + DateFormat.LONG); + } + formatter = localeDateTimeFormatter; + break; + case Id_toLocaleTimeString: + if (localeTimeFormatter == null) { + localeTimeFormatter + = DateFormat.getTimeInstance(DateFormat.LONG); + } + formatter = localeTimeFormatter; + break; + case Id_toLocaleDateString: + if (localeDateFormatter == null) { + localeDateFormatter + = DateFormat.getDateInstance(DateFormat.LONG); + } + formatter = localeDateFormatter; + break; + default: formatter = null; // unreachable + } + + synchronized (formatter) { + return formatter.format(new Date((long) t)); + } + } + + private static String js_toUTCString(double date) + { + StringBuffer result = new StringBuffer(60); + + appendWeekDayName(result, WeekDay(date)); + result.append(", "); + append0PaddedUint(result, DateFromTime(date), 2); + result.append(' '); + appendMonthName(result, MonthFromTime(date)); + result.append(' '); + int year = YearFromTime(date); + if (year < 0) { + result.append('-'); year = -year; + } + append0PaddedUint(result, year, 4); + result.append(' '); + append0PaddedUint(result, HourFromTime(date), 2); + result.append(':'); + append0PaddedUint(result, MinFromTime(date), 2); + result.append(':'); + append0PaddedUint(result, SecFromTime(date), 2); + result.append(" GMT"); + return result.toString(); + } + + private static void append0PaddedUint(StringBuffer sb, int i, int minWidth) + { + if (i < 0) Kit.codeBug(); + int scale = 1; + --minWidth; + if (i >= 10) { + if (i < 1000 * 1000 * 1000) { + for (;;) { + int newScale = scale * 10; + if (i < newScale) { break; } + --minWidth; + scale = newScale; + } + } else { + // Separated case not to check against 10 * 10^9 overflow + minWidth -= 9; + scale = 1000 * 1000 * 1000; + } + } + while (minWidth > 0) { + sb.append('0'); + --minWidth; + } + while (scale != 1) { + sb.append((char)('0' + (i / scale))); + i %= scale; + scale /= 10; + } + sb.append((char)('0' + i)); + } + + private static void appendMonthName(StringBuffer sb, int index) + { + // Take advantage of the fact that all month abbreviations + // have the same length to minimize amount of strings runtime has + // to keep in memory + String months = "Jan"+"Feb"+"Mar"+"Apr"+"May"+"Jun" + +"Jul"+"Aug"+"Sep"+"Oct"+"Nov"+"Dec"; + index *= 3; + for (int i = 0; i != 3; ++i) { + sb.append(months.charAt(index + i)); + } + } + + private static void appendWeekDayName(StringBuffer sb, int index) + { + String days = "Sun"+"Mon"+"Tue"+"Wed"+"Thu"+"Fri"+"Sat"; + index *= 3; + for (int i = 0; i != 3; ++i) { + sb.append(days.charAt(index + i)); + } + } + + private static double makeTime(double date, Object[] args, int methodId) + { + int maxargs; + boolean local = true; + switch (methodId) { + case Id_setUTCMilliseconds: + local = false; + // fallthrough + case Id_setMilliseconds: + maxargs = 1; + break; + + case Id_setUTCSeconds: + local = false; + // fallthrough + case Id_setSeconds: + maxargs = 2; + break; + + case Id_setUTCMinutes: + local = false; + // fallthrough + case Id_setMinutes: + maxargs = 3; + break; + + case Id_setUTCHours: + local = false; + // fallthrough + case Id_setHours: + maxargs = 4; + break; + + default: + Kit.codeBug(); + maxargs = 0; + } + + int i; + double conv[] = new double[4]; + double hour, min, sec, msec; + double lorutime; /* Local or UTC version of date */ + + double time; + double result; + + /* just return NaN if the date is already NaN */ + if (date != date) + return date; + + /* Satisfy the ECMA rule that if a function is called with + * fewer arguments than the specified formal arguments, the + * remaining arguments are set to undefined. Seems like all + * the Date.setWhatever functions in ECMA are only varargs + * beyond the first argument; this should be set to undefined + * if it's not given. This means that "d = new Date(); + * d.setMilliseconds()" returns NaN. Blech. + */ + if (args.length == 0) + args = ScriptRuntime.padArguments(args, 1); + + for (i = 0; i < args.length && i < maxargs; i++) { + conv[i] = ScriptRuntime.toNumber(args[i]); + + // limit checks that happen in MakeTime in ECMA. + if (conv[i] != conv[i] || Double.isInfinite(conv[i])) { + return ScriptRuntime.NaN; + } + conv[i] = ScriptRuntime.toInteger(conv[i]); + } + + if (local) + lorutime = LocalTime(date); + else + lorutime = date; + + i = 0; + int stop = args.length; + + if (maxargs >= 4 && i < stop) + hour = conv[i++]; + else + hour = HourFromTime(lorutime); + + if (maxargs >= 3 && i < stop) + min = conv[i++]; + else + min = MinFromTime(lorutime); + + if (maxargs >= 2 && i < stop) + sec = conv[i++]; + else + sec = SecFromTime(lorutime); + + if (maxargs >= 1 && i < stop) + msec = conv[i++]; + else + msec = msFromTime(lorutime); + + time = MakeTime(hour, min, sec, msec); + result = MakeDate(Day(lorutime), time); + + if (local) + result = internalUTC(result); + date = TimeClip(result); + + return date; + } + + private static double makeDate(double date, Object[] args, int methodId) + { + int maxargs; + boolean local = true; + switch (methodId) { + case Id_setUTCDate: + local = false; + // fallthrough + case Id_setDate: + maxargs = 1; + break; + + case Id_setUTCMonth: + local = false; + // fallthrough + case Id_setMonth: + maxargs = 2; + break; + + case Id_setUTCFullYear: + local = false; + // fallthrough + case Id_setFullYear: + maxargs = 3; + break; + + default: + Kit.codeBug(); + maxargs = 0; + } + + int i; + double conv[] = new double[3]; + double year, month, day; + double lorutime; /* local or UTC version of date */ + double result; + + /* See arg padding comment in makeTime.*/ + if (args.length == 0) + args = ScriptRuntime.padArguments(args, 1); + + for (i = 0; i < args.length && i < maxargs; i++) { + conv[i] = ScriptRuntime.toNumber(args[i]); + + // limit checks that happen in MakeDate in ECMA. + if (conv[i] != conv[i] || Double.isInfinite(conv[i])) { + return ScriptRuntime.NaN; + } + conv[i] = ScriptRuntime.toInteger(conv[i]); + } + + /* return NaN if date is NaN and we're not setting the year, + * If we are, use 0 as the time. */ + if (date != date) { + if (args.length < 3) { + return ScriptRuntime.NaN; + } else { + lorutime = 0; + } + } else { + if (local) + lorutime = LocalTime(date); + else + lorutime = date; + } + + i = 0; + int stop = args.length; + + if (maxargs >= 3 && i < stop) + year = conv[i++]; + else + year = YearFromTime(lorutime); + + if (maxargs >= 2 && i < stop) + month = conv[i++]; + else + month = MonthFromTime(lorutime); + + if (maxargs >= 1 && i < stop) + day = conv[i++]; + else + day = DateFromTime(lorutime); + + day = MakeDay(year, month, day); /* day within year */ + result = MakeDate(day, TimeWithinDay(lorutime)); + + if (local) + result = internalUTC(result); + + date = TimeClip(result); + + return date; + } + +// #string_id_map# + + protected int findPrototypeId(String s) + { + int id; +// #generated# Last update: 2007-05-09 08:15:38 EDT + L0: { id = 0; String X = null; int c; + L: switch (s.length()) { + case 6: X="getDay";id=Id_getDay; break L; + case 7: switch (s.charAt(3)) { + case 'D': c=s.charAt(0); + if (c=='g') { X="getDate";id=Id_getDate; } + else if (c=='s') { X="setDate";id=Id_setDate; } + break L; + case 'T': c=s.charAt(0); + if (c=='g') { X="getTime";id=Id_getTime; } + else if (c=='s') { X="setTime";id=Id_setTime; } + break L; + case 'Y': c=s.charAt(0); + if (c=='g') { X="getYear";id=Id_getYear; } + else if (c=='s') { X="setYear";id=Id_setYear; } + break L; + case 'u': X="valueOf";id=Id_valueOf; break L; + } break L; + case 8: switch (s.charAt(3)) { + case 'H': c=s.charAt(0); + if (c=='g') { X="getHours";id=Id_getHours; } + else if (c=='s') { X="setHours";id=Id_setHours; } + break L; + case 'M': c=s.charAt(0); + if (c=='g') { X="getMonth";id=Id_getMonth; } + else if (c=='s') { X="setMonth";id=Id_setMonth; } + break L; + case 'o': X="toSource";id=Id_toSource; break L; + case 't': X="toString";id=Id_toString; break L; + } break L; + case 9: X="getUTCDay";id=Id_getUTCDay; break L; + case 10: c=s.charAt(3); + if (c=='M') { + c=s.charAt(0); + if (c=='g') { X="getMinutes";id=Id_getMinutes; } + else if (c=='s') { X="setMinutes";id=Id_setMinutes; } + } + else if (c=='S') { + c=s.charAt(0); + if (c=='g') { X="getSeconds";id=Id_getSeconds; } + else if (c=='s') { X="setSeconds";id=Id_setSeconds; } + } + else if (c=='U') { + c=s.charAt(0); + if (c=='g') { X="getUTCDate";id=Id_getUTCDate; } + else if (c=='s') { X="setUTCDate";id=Id_setUTCDate; } + } + break L; + case 11: switch (s.charAt(3)) { + case 'F': c=s.charAt(0); + if (c=='g') { X="getFullYear";id=Id_getFullYear; } + else if (c=='s') { X="setFullYear";id=Id_setFullYear; } + break L; + case 'M': X="toGMTString";id=Id_toGMTString; break L; + case 'T': X="toUTCString";id=Id_toUTCString; break L; + case 'U': c=s.charAt(0); + if (c=='g') { + c=s.charAt(9); + if (c=='r') { X="getUTCHours";id=Id_getUTCHours; } + else if (c=='t') { X="getUTCMonth";id=Id_getUTCMonth; } + } + else if (c=='s') { + c=s.charAt(9); + if (c=='r') { X="setUTCHours";id=Id_setUTCHours; } + else if (c=='t') { X="setUTCMonth";id=Id_setUTCMonth; } + } + break L; + case 's': X="constructor";id=Id_constructor; break L; + } break L; + case 12: c=s.charAt(2); + if (c=='D') { X="toDateString";id=Id_toDateString; } + else if (c=='T') { X="toTimeString";id=Id_toTimeString; } + break L; + case 13: c=s.charAt(0); + if (c=='g') { + c=s.charAt(6); + if (c=='M') { X="getUTCMinutes";id=Id_getUTCMinutes; } + else if (c=='S') { X="getUTCSeconds";id=Id_getUTCSeconds; } + } + else if (c=='s') { + c=s.charAt(6); + if (c=='M') { X="setUTCMinutes";id=Id_setUTCMinutes; } + else if (c=='S') { X="setUTCSeconds";id=Id_setUTCSeconds; } + } + break L; + case 14: c=s.charAt(0); + if (c=='g') { X="getUTCFullYear";id=Id_getUTCFullYear; } + else if (c=='s') { X="setUTCFullYear";id=Id_setUTCFullYear; } + else if (c=='t') { X="toLocaleString";id=Id_toLocaleString; } + break L; + case 15: c=s.charAt(0); + if (c=='g') { X="getMilliseconds";id=Id_getMilliseconds; } + else if (c=='s') { X="setMilliseconds";id=Id_setMilliseconds; } + break L; + case 17: X="getTimezoneOffset";id=Id_getTimezoneOffset; break L; + case 18: c=s.charAt(0); + if (c=='g') { X="getUTCMilliseconds";id=Id_getUTCMilliseconds; } + else if (c=='s') { X="setUTCMilliseconds";id=Id_setUTCMilliseconds; } + else if (c=='t') { + c=s.charAt(8); + if (c=='D') { X="toLocaleDateString";id=Id_toLocaleDateString; } + else if (c=='T') { X="toLocaleTimeString";id=Id_toLocaleTimeString; } + } + break L; + } + if (X!=null && X!=s && !X.equals(s)) id = 0; + break L0; + } +// #/generated# + return id; + } + + private static final int + ConstructorId_now = -3, + ConstructorId_parse = -2, + ConstructorId_UTC = -1, + + Id_constructor = 1, + Id_toString = 2, + Id_toTimeString = 3, + Id_toDateString = 4, + Id_toLocaleString = 5, + Id_toLocaleTimeString = 6, + Id_toLocaleDateString = 7, + Id_toUTCString = 8, + Id_toSource = 9, + Id_valueOf = 10, + Id_getTime = 11, + Id_getYear = 12, + Id_getFullYear = 13, + Id_getUTCFullYear = 14, + Id_getMonth = 15, + Id_getUTCMonth = 16, + Id_getDate = 17, + Id_getUTCDate = 18, + Id_getDay = 19, + Id_getUTCDay = 20, + Id_getHours = 21, + Id_getUTCHours = 22, + Id_getMinutes = 23, + Id_getUTCMinutes = 24, + Id_getSeconds = 25, + Id_getUTCSeconds = 26, + Id_getMilliseconds = 27, + Id_getUTCMilliseconds = 28, + Id_getTimezoneOffset = 29, + Id_setTime = 30, + Id_setMilliseconds = 31, + Id_setUTCMilliseconds = 32, + Id_setSeconds = 33, + Id_setUTCSeconds = 34, + Id_setMinutes = 35, + Id_setUTCMinutes = 36, + Id_setHours = 37, + Id_setUTCHours = 38, + Id_setDate = 39, + Id_setUTCDate = 40, + Id_setMonth = 41, + Id_setUTCMonth = 42, + Id_setFullYear = 43, + Id_setUTCFullYear = 44, + Id_setYear = 45, + + MAX_PROTOTYPE_ID = 45; + + private static final int + Id_toGMTString = Id_toUTCString; // Alias, see Ecma B.2.6 +// #/string_id_map# + + /* cached values */ + private static java.util.TimeZone thisTimeZone; + private static double LocalTZA; + private static java.text.DateFormat timeZoneFormatter; + private static java.text.DateFormat localeDateTimeFormatter; + private static java.text.DateFormat localeDateFormatter; + private static java.text.DateFormat localeTimeFormatter; + + private double date; +} + + diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeError.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeError.java new file mode 100644 index 0000000..4aff10c --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeError.java @@ -0,0 +1,227 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Igor Bukanov + * Roger Lawrence + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + + +package org.mozilla.javascript; + +/** + * + * The class of error objects + * + * ECMA 15.11 + */ +final class NativeError extends IdScriptableObject +{ + static final long serialVersionUID = -5338413581437645187L; + + private static final Object ERROR_TAG = new Object(); + + static void init(Scriptable scope, boolean sealed) + { + NativeError obj = new NativeError(); + ScriptableObject.putProperty(obj, "name", "Error"); + ScriptableObject.putProperty(obj, "message", ""); + ScriptableObject.putProperty(obj, "fileName", ""); + ScriptableObject.putProperty(obj, "lineNumber", new Integer(0)); + obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed); + } + + static NativeError make(Context cx, Scriptable scope, + IdFunctionObject ctorObj, Object[] args) + { + Scriptable proto = (Scriptable)(ctorObj.get("prototype", ctorObj)); + + NativeError obj = new NativeError(); + obj.setPrototype(proto); + obj.setParentScope(scope); + + int arglen = args.length; + if (arglen >= 1) { + ScriptableObject.putProperty(obj, "message", + ScriptRuntime.toString(args[0])); + if (arglen >= 2) { + ScriptableObject.putProperty(obj, "fileName", args[1]); + if (arglen >= 3) { + int line = ScriptRuntime.toInt32(args[2]); + ScriptableObject.putProperty(obj, "lineNumber", + new Integer(line)); + } + } + } + if(arglen < 3 && cx.hasFeature(Context.FEATURE_LOCATION_INFORMATION_IN_ERROR)) { + // Fill in fileName and lineNumber automatically when not specified + // explicitly, see Bugzilla issue #342807 + int[] linep = new int[1]; + String fileName = Context.getSourcePositionFromStack(linep); + ScriptableObject.putProperty(obj, "lineNumber", + new Integer(linep[0])); + if(arglen < 2) { + ScriptableObject.putProperty(obj, "fileName", fileName); + } + } + return obj; + } + + public String getClassName() + { + return "Error"; + } + + public String toString() + { + return js_toString(this); + } + + protected void initPrototypeId(int id) + { + String s; + int arity; + switch (id) { + case Id_constructor: arity=1; s="constructor"; break; + case Id_toString: arity=0; s="toString"; break; + case Id_toSource: arity=0; s="toSource"; break; + default: throw new IllegalArgumentException(String.valueOf(id)); + } + initPrototypeMethod(ERROR_TAG, id, s, arity); + } + + public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, + Scriptable thisObj, Object[] args) + { + if (!f.hasTag(ERROR_TAG)) { + return super.execIdCall(f, cx, scope, thisObj, args); + } + int id = f.methodId(); + switch (id) { + case Id_constructor: + return make(cx, scope, f, args); + + case Id_toString: + return js_toString(thisObj); + + case Id_toSource: + return js_toSource(cx, scope, thisObj); + } + throw new IllegalArgumentException(String.valueOf(id)); + } + + private static String js_toString(Scriptable thisObj) + { + return getString(thisObj, "name")+": "+getString(thisObj, "message"); + } + + private static String js_toSource(Context cx, Scriptable scope, + Scriptable thisObj) + { + // Emulation of SpiderMonkey behavior + Object name = ScriptableObject.getProperty(thisObj, "name"); + Object message = ScriptableObject.getProperty(thisObj, "message"); + Object fileName = ScriptableObject.getProperty(thisObj, "fileName"); + Object lineNumber = ScriptableObject.getProperty(thisObj, "lineNumber"); + + StringBuffer sb = new StringBuffer(); + sb.append("(new "); + if (name == NOT_FOUND) { + name = Undefined.instance; + } + sb.append(ScriptRuntime.toString(name)); + sb.append("("); + if (message != NOT_FOUND + || fileName != NOT_FOUND + || lineNumber != NOT_FOUND) + { + if (message == NOT_FOUND) { + message = ""; + } + sb.append(ScriptRuntime.uneval(cx, scope, message)); + if (fileName != NOT_FOUND || lineNumber != NOT_FOUND) { + sb.append(", "); + if (fileName == NOT_FOUND) { + fileName = ""; + } + sb.append(ScriptRuntime.uneval(cx, scope, fileName)); + if (lineNumber != NOT_FOUND) { + int line = ScriptRuntime.toInt32(lineNumber); + if (line != 0) { + sb.append(", "); + sb.append(ScriptRuntime.toString(line)); + } + } + } + } + sb.append("))"); + return sb.toString(); + } + + private static String getString(Scriptable obj, String id) + { + Object value = ScriptableObject.getProperty(obj, id); + if (value == NOT_FOUND) return ""; + return ScriptRuntime.toString(value); + } + + protected int findPrototypeId(String s) + { + int id; +// #string_id_map# +// #generated# Last update: 2007-05-09 08:15:45 EDT + L0: { id = 0; String X = null; int c; + int s_length = s.length(); + if (s_length==8) { + c=s.charAt(3); + if (c=='o') { X="toSource";id=Id_toSource; } + else if (c=='t') { X="toString";id=Id_toString; } + } + else if (s_length==11) { X="constructor";id=Id_constructor; } + if (X!=null && X!=s && !X.equals(s)) id = 0; + break L0; + } +// #/generated# + return id; + } + + private static final int + Id_constructor = 1, + Id_toString = 2, + Id_toSource = 3, + + MAX_PROTOTYPE_ID = 3; + +// #/string_id_map# +} diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeFunction.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeFunction.java new file mode 100644 index 0000000..ac70556 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeFunction.java @@ -0,0 +1,169 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Norris Boyd + * Igor Bukanov + * Bob Jervis + * Roger Lawrence + * Mike McCabe + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript; + +import org.mozilla.javascript.debug.DebuggableScript; + +/** + * This class implements the Function native object. + * See ECMA 15.3. + * @author Norris Boyd + */ +public abstract class NativeFunction extends BaseFunction +{ + + public final void initScriptFunction(Context cx, Scriptable scope) + { + ScriptRuntime.setFunctionProtoAndParent(this, scope); + } + + /** + * @param indent How much to indent the decompiled result + * + * @param flags Flags specifying format of decompilation output + */ + final String decompile(int indent, int flags) + { + String encodedSource = getEncodedSource(); + if (encodedSource == null) { + return super.decompile(indent, flags); + } else { + UintMap properties = new UintMap(1); + properties.put(Decompiler.INITIAL_INDENT_PROP, indent); + return Decompiler.decompile(encodedSource, flags, properties); + } + } + + public int getLength() + { + int paramCount = getParamCount(); + if (getLanguageVersion() != Context.VERSION_1_2) { + return paramCount; + } + Context cx = Context.getContext(); + NativeCall activation = ScriptRuntime.findFunctionActivation(cx, this); + if (activation == null) { + return paramCount; + } + return activation.originalArgs.length; + } + + public int getArity() + { + return getParamCount(); + } + + /** + * @deprecated Use {@link BaseFunction#getFunctionName()} instead. + * For backwards compatibility keep an old method name used by + * Batik and possibly others. + */ + public String jsGet_name() + { + return getFunctionName(); + } + + /** + * Get encoded source string. + */ + public String getEncodedSource() + { + return null; + } + + public DebuggableScript getDebuggableView() + { + return null; + } + + /** + * Resume execution of a suspended generator. + * @param cx The current context + * @param scope Scope for the parent generator function + * @param operation The resumption operation (next, send, etc.. ) + * @param state The generator state (has locals, stack, etc.) + * @param value The return value of yield (if required). + * @return The next yielded value (if any) + */ + public Object resumeGenerator(Context cx, Scriptable scope, + int operation, Object state, Object value) + { + throw new EvaluatorException("resumeGenerator() not implemented"); + } + + + protected abstract int getLanguageVersion(); + + /** + * Get number of declared parameters. It should be 0 for scripts. + */ + protected abstract int getParamCount(); + + /** + * Get number of declared parameters and variables defined through var + * statements. + */ + protected abstract int getParamAndVarCount(); + + /** + * Get parameter or variable name. + * If index < {@link #getParamCount()}, then return the name of the + * corresponding parameter. Otherwise return the name of variable. + */ + protected abstract String getParamOrVarName(int index); + + /** + * Get parameter or variable const-ness. + * If index < {@link #getParamCount()}, then return the const-ness + * of the corresponding parameter. Otherwise return whether the variable is + * const. + */ + protected boolean getParamOrVarConst(int index) + { + // By default return false to preserve compatibility with existing + // classes subclassing this class, which are mostly generated by jsc + // from earlier Rhino versions. See Bugzilla #396117. + return false; + } +} + diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeGenerator.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeGenerator.java new file mode 100644 index 0000000..0a8da9f --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeGenerator.java @@ -0,0 +1,281 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Contributor(s): + * Norris Boyd + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript; + +/** + * This class implements generator objects. See + * http://developer.mozilla.org/en/docs/New_in_JavaScript_1.7#Generators + * + * @author Norris Boyd + */ +public final class NativeGenerator extends IdScriptableObject { + private static final Object GENERATOR_TAG = new Object(); + + static NativeGenerator init(ScriptableObject scope, boolean sealed) { + // Generator + // Can't use "NativeGenerator().exportAsJSClass" since we don't want + // to define "Generator" as a constructor in the top-level scope. + + NativeGenerator prototype = new NativeGenerator(); + if (scope != null) { + prototype.setParentScope(scope); + prototype.setPrototype(getObjectPrototype(scope)); + } + prototype.activatePrototypeMap(MAX_PROTOTYPE_ID); + if (sealed) { + prototype.sealObject(); + } + + // Need to access Generator prototype when constructing + // Generator instances, but don't have a generator constructor + // to use to find the prototype. Use the "associateValue" + // approach instead. + if (scope != null) { + scope.associateValue(GENERATOR_TAG, prototype); + } + + return prototype; + } + + /** + * Only for constructing the prototype object. + */ + private NativeGenerator() { } + + public NativeGenerator(Scriptable scope, NativeFunction function, + Object savedState) + { + this.function = function; + this.savedState = savedState; + // Set parent and prototype properties. Since we don't have a + // "Generator" constructor in the top scope, we stash the + // prototype in the top scope's associated value. + Scriptable top = ScriptableObject.getTopLevelScope(scope); + this.setParentScope(top); + NativeGenerator prototype = (NativeGenerator) + ScriptableObject.getTopScopeValue(top, GENERATOR_TAG); + this.setPrototype(prototype); + } + + public static final int GENERATOR_SEND = 0, + GENERATOR_THROW = 1, + GENERATOR_CLOSE = 2; + + public String getClassName() { + return "Generator"; + } + + /** + * Close the generator if it is still open. + */ + public void finalize() throws Throwable { + if (savedState != null) { + // This is a little tricky since we are most likely running in + // a different thread. We need to get a Context to run this, and + // we must call "doTopCall" since this will likely be the outermost + // JavaScript frame on this thread. + Context cx = Context.getCurrentContext(); + ContextFactory factory = cx != null ? cx.getFactory() + : ContextFactory.getGlobal(); + Scriptable scope = ScriptableObject.getTopLevelScope(this); + factory.call(new CloseGeneratorAction(this)); + } + } + + private static class CloseGeneratorAction implements ContextAction { + private NativeGenerator generator; + + CloseGeneratorAction(NativeGenerator generator) { + this.generator = generator; + } + + public Object run(Context cx) { + Scriptable scope = ScriptableObject.getTopLevelScope(generator); + Callable closeGenerator = new Callable() { + public Object call(Context cx, Scriptable scope, + Scriptable thisObj, Object[] args) { + return ((NativeGenerator)thisObj).resume(cx, scope, + GENERATOR_CLOSE, new GeneratorClosedException()); + } + }; + return ScriptRuntime.doTopCall(closeGenerator, cx, scope, + generator, null); + } + } + + protected void initPrototypeId(int id) { + String s; + int arity; + switch (id) { + case Id_close: arity=1; s="close"; break; + case Id_next: arity=1; s="next"; break; + case Id_send: arity=0; s="send"; break; + case Id_throw: arity=0; s="throw"; break; + case Id___iterator__: arity=1; s="__iterator__"; break; + default: throw new IllegalArgumentException(String.valueOf(id)); + } + initPrototypeMethod(GENERATOR_TAG, id, s, arity); + } + + public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, + Scriptable thisObj, Object[] args) + { + if (!f.hasTag(GENERATOR_TAG)) { + return super.execIdCall(f, cx, scope, thisObj, args); + } + int id = f.methodId(); + + if (!(thisObj instanceof NativeGenerator)) + throw incompatibleCallError(f); + + NativeGenerator generator = (NativeGenerator) thisObj; + + switch (id) { + + case Id_close: + // need to run any pending finally clauses + return generator.resume(cx, scope, GENERATOR_CLOSE, + new GeneratorClosedException()); + + case Id_next: + // arguments to next() are ignored + generator.firstTime = false; + return generator.resume(cx, scope, GENERATOR_SEND, + Undefined.instance); + + case Id_send: { + Object arg = args.length > 0 ? args[0] : Undefined.instance; + if (generator.firstTime && !arg.equals(Undefined.instance)) { + throw ScriptRuntime.typeError0("msg.send.newborn"); + } + return generator.resume(cx, scope, GENERATOR_SEND, arg); + } + + case Id_throw: + return generator.resume(cx, scope, GENERATOR_THROW, + args.length > 0 ? args[0] : Undefined.instance); + + case Id___iterator__: + return thisObj; + + default: + throw new IllegalArgumentException(String.valueOf(id)); + } + } + + private Object resume(Context cx, Scriptable scope, int operation, + Object value) + { + if (savedState == null) { + if (operation == GENERATOR_CLOSE) + return Undefined.instance; + Object thrown; + if (operation == GENERATOR_THROW) { + thrown = value; + } else { + thrown = NativeIterator.getStopIterationObject(scope); + } + throw new JavaScriptException(thrown, lineSource, lineNumber); + } + try { + synchronized (this) { + // generator execution is necessarily single-threaded and + // non-reentrant. + // See https://bugzilla.mozilla.org/show_bug.cgi?id=349263 + if (locked) + throw ScriptRuntime.typeError0("msg.already.exec.gen"); + locked = true; + } + return function.resumeGenerator(cx, scope, operation, savedState, + value); + } catch (GeneratorClosedException e) { + // On closing a generator in the compile path, the generator + // throws a special exception. This ensures execution of all pending + // finalizers and will not get caught by user code. + return Undefined.instance; + } catch (RhinoException e) { + lineNumber = e.lineNumber(); + lineSource = e.lineSource(); + savedState = null; + throw e; + } finally { + synchronized (this) { + locked = false; + } + if (operation == GENERATOR_CLOSE) + savedState = null; + } + } + +// #string_id_map# + + protected int findPrototypeId(String s) { + int id; +// #generated# Last update: 2007-06-14 13:13:03 EDT + L0: { id = 0; String X = null; int c; + int s_length = s.length(); + if (s_length==4) { + c=s.charAt(0); + if (c=='n') { X="next";id=Id_next; } + else if (c=='s') { X="send";id=Id_send; } + } + else if (s_length==5) { + c=s.charAt(0); + if (c=='c') { X="close";id=Id_close; } + else if (c=='t') { X="throw";id=Id_throw; } + } + else if (s_length==12) { X="__iterator__";id=Id___iterator__; } + if (X!=null && X!=s && !X.equals(s)) id = 0; + break L0; + } +// #/generated# + return id; + } + + private static final int + Id_close = 1, + Id_next = 2, + Id_send = 3, + Id_throw = 4, + Id___iterator__ = 5, + MAX_PROTOTYPE_ID = 5; + +// #/string_id_map# + private NativeFunction function; + private Object savedState; + private String lineSource; + private int lineNumber; + private boolean firstTime = true; + private boolean locked; + + public static class GeneratorClosedException extends RuntimeException { + } +} diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeGlobal.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeGlobal.java new file mode 100644 index 0000000..58faad4 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeGlobal.java @@ -0,0 +1,790 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Norris Boyd + * Igor Bukanov + * Mike McCabe + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript; + +import java.io.Serializable; + +import org.mozilla.javascript.xml.XMLLib; + +/** + * This class implements the global native object (function and value + * properties only). + * + * See ECMA 15.1.[12]. + * + * @author Mike Shaver + */ + +public class NativeGlobal implements Serializable, IdFunctionCall +{ + static final long serialVersionUID = 6080442165748707530L; + + public static void init(Context cx, Scriptable scope, boolean sealed) { + NativeGlobal obj = new NativeGlobal(); + + for (int id = 1; id <= LAST_SCOPE_FUNCTION_ID; ++id) { + String name; + int arity = 1; + switch (id) { + case Id_decodeURI: + name = "decodeURI"; + break; + case Id_decodeURIComponent: + name = "decodeURIComponent"; + break; + case Id_encodeURI: + name = "encodeURI"; + break; + case Id_encodeURIComponent: + name = "encodeURIComponent"; + break; + case Id_escape: + name = "escape"; + break; + case Id_eval: + name = "eval"; + break; + case Id_isFinite: + name = "isFinite"; + break; + case Id_isNaN: + name = "isNaN"; + break; + case Id_isXMLName: + name = "isXMLName"; + break; + case Id_parseFloat: + name = "parseFloat"; + break; + case Id_parseInt: + name = "parseInt"; + arity = 2; + break; + case Id_unescape: + name = "unescape"; + break; + case Id_uneval: + name = "uneval"; + break; + default: + throw Kit.codeBug(); + } + IdFunctionObject f = new IdFunctionObject(obj, FTAG, id, name, + arity, scope); + if (sealed) { + f.sealObject(); + } + f.exportAsScopeProperty(); + } + + ScriptableObject.defineProperty( + scope, "NaN", ScriptRuntime.NaNobj, + ScriptableObject.DONTENUM); + ScriptableObject.defineProperty( + scope, "Infinity", + ScriptRuntime.wrapNumber(Double.POSITIVE_INFINITY), + ScriptableObject.DONTENUM); + ScriptableObject.defineProperty( + scope, "undefined", Undefined.instance, + ScriptableObject.DONTENUM); + + String[] errorMethods = Kit.semicolonSplit("" + +"ConversionError;" + +"EvalError;" + +"RangeError;" + +"ReferenceError;" + +"SyntaxError;" + +"TypeError;" + +"URIError;" + +"InternalError;" + +"JavaException;" + ); + + /* + Each error constructor gets its own Error object as a prototype, + with the 'name' property set to the name of the error. + */ + for (int i = 0; i < errorMethods.length; i++) { + String name = errorMethods[i]; + Scriptable errorProto = ScriptRuntime. + newObject(cx, scope, "Error", + ScriptRuntime.emptyArgs); + errorProto.put("name", errorProto, name); + if (sealed) { + if (errorProto instanceof ScriptableObject) { + ((ScriptableObject)errorProto).sealObject(); + } + } + IdFunctionObject ctor = new IdFunctionObject(obj, FTAG, + Id_new_CommonError, + name, 1, scope); + ctor.markAsConstructor(errorProto); + if (sealed) { + ctor.sealObject(); + } + ctor.exportAsScopeProperty(); + } + } + + public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, + Scriptable thisObj, Object[] args) + { + if (f.hasTag(FTAG)) { + int methodId = f.methodId(); + switch (methodId) { + case Id_decodeURI: + case Id_decodeURIComponent: { + String str = ScriptRuntime.toString(args, 0); + return decode(str, methodId == Id_decodeURI); + } + + case Id_encodeURI: + case Id_encodeURIComponent: { + String str = ScriptRuntime.toString(args, 0); + return encode(str, methodId == Id_encodeURI); + } + + case Id_escape: + return js_escape(args); + + case Id_eval: + return js_eval(cx, scope, args); + + case Id_isFinite: { + boolean result; + if (args.length < 1) { + result = false; + } else { + double d = ScriptRuntime.toNumber(args[0]); + result = (d == d + && d != Double.POSITIVE_INFINITY + && d != Double.NEGATIVE_INFINITY); + } + return ScriptRuntime.wrapBoolean(result); + } + + case Id_isNaN: { + // The global method isNaN, as per ECMA-262 15.1.2.6. + boolean result; + if (args.length < 1) { + result = true; + } else { + double d = ScriptRuntime.toNumber(args[0]); + result = (d != d); + } + return ScriptRuntime.wrapBoolean(result); + } + + case Id_isXMLName: { + Object name = (args.length == 0) + ? Undefined.instance : args[0]; + XMLLib xmlLib = XMLLib.extractFromScope(scope); + return ScriptRuntime.wrapBoolean( + xmlLib.isXMLName(cx, name)); + } + + case Id_parseFloat: + return js_parseFloat(args); + + case Id_parseInt: + return js_parseInt(args); + + case Id_unescape: + return js_unescape(args); + + case Id_uneval: { + Object value = (args.length != 0) + ? args[0] : Undefined.instance; + return ScriptRuntime.uneval(cx, scope, value); + } + + case Id_new_CommonError: + // The implementation of all the ECMA error constructors + // (SyntaxError, TypeError, etc.) + return NativeError.make(cx, scope, f, args); + } + } + throw f.unknown(); + } + + /** + * The global method parseInt, as per ECMA-262 15.1.2.2. + */ + private Object js_parseInt(Object[] args) { + String s = ScriptRuntime.toString(args, 0); + int radix = ScriptRuntime.toInt32(args, 1); + + int len = s.length(); + if (len == 0) + return ScriptRuntime.NaNobj; + + boolean negative = false; + int start = 0; + char c; + do { + c = s.charAt(start); + if (!Character.isWhitespace(c)) + break; + start++; + } while (start < len); + + if (c == '+' || (negative = (c == '-'))) + start++; + + final int NO_RADIX = -1; + if (radix == 0) { + radix = NO_RADIX; + } else if (radix < 2 || radix > 36) { + return ScriptRuntime.NaNobj; + } else if (radix == 16 && len - start > 1 && s.charAt(start) == '0') { + c = s.charAt(start+1); + if (c == 'x' || c == 'X') + start += 2; + } + + if (radix == NO_RADIX) { + radix = 10; + if (len - start > 1 && s.charAt(start) == '0') { + c = s.charAt(start+1); + if (c == 'x' || c == 'X') { + radix = 16; + start += 2; + } else if ('0' <= c && c <= '9') { + radix = 8; + start++; + } + } + } + + double d = ScriptRuntime.stringToNumber(s, start, radix); + return ScriptRuntime.wrapNumber(negative ? -d : d); + } + + /** + * The global method parseFloat, as per ECMA-262 15.1.2.3. + * + * @param args the arguments to parseFloat, ignoring args[>=1] + */ + private Object js_parseFloat(Object[] args) + { + if (args.length < 1) + return ScriptRuntime.NaNobj; + + String s = ScriptRuntime.toString(args[0]); + int len = s.length(); + int start = 0; + // Scan forward to skip whitespace + char c; + for (;;) { + if (start == len) { + return ScriptRuntime.NaNobj; + } + c = s.charAt(start); + if (!TokenStream.isJSSpace(c)) { + break; + } + ++start; + } + + int i = start; + if (c == '+' || c == '-') { + ++i; + if (i == len) { + return ScriptRuntime.NaNobj; + } + c = s.charAt(i); + } + + if (c == 'I') { + // check for "Infinity" + if (i+8 <= len && s.regionMatches(i, "Infinity", 0, 8)) { + double d; + if (s.charAt(start) == '-') { + d = Double.NEGATIVE_INFINITY; + } else { + d = Double.POSITIVE_INFINITY; + } + return ScriptRuntime.wrapNumber(d); + } + return ScriptRuntime.NaNobj; + } + + // Find the end of the legal bit + int decimal = -1; + int exponent = -1; + for (; i < len; i++) { + switch (s.charAt(i)) { + case '.': + if (decimal != -1) // Only allow a single decimal point. + break; + decimal = i; + continue; + + case 'e': + case 'E': + if (exponent != -1) + break; + exponent = i; + continue; + + case '+': + case '-': + // Only allow '+' or '-' after 'e' or 'E' + if (exponent != i-1) + break; + continue; + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + continue; + + default: + break; + } + break; + } + s = s.substring(start, i); + try { + return Double.valueOf(s); + } + catch (NumberFormatException ex) { + return ScriptRuntime.NaNobj; + } + } + + /** + * The global method escape, as per ECMA-262 15.1.2.4. + + * Includes code for the 'mask' argument supported by the C escape + * method, which used to be part of the browser imbedding. Blame + * for the strange constant names should be directed there. + */ + + private Object js_escape(Object[] args) { + final int + URL_XALPHAS = 1, + URL_XPALPHAS = 2, + URL_PATH = 4; + + String s = ScriptRuntime.toString(args, 0); + + int mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH; + if (args.length > 1) { // the 'mask' argument. Non-ECMA. + double d = ScriptRuntime.toNumber(args[1]); + if (d != d || ((mask = (int) d) != d) || + 0 != (mask & ~(URL_XALPHAS | URL_XPALPHAS | URL_PATH))) + { + throw Context.reportRuntimeError0("msg.bad.esc.mask"); + } + } + + StringBuffer sb = null; + for (int k = 0, L = s.length(); k != L; ++k) { + int c = s.charAt(k); + if (mask != 0 + && ((c >= '0' && c <= '9') + || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') + || c == '@' || c == '*' || c == '_' || c == '-' || c == '.' + || (0 != (mask & URL_PATH) && (c == '/' || c == '+')))) + { + if (sb != null) { + sb.append((char)c); + } + } else { + if (sb == null) { + sb = new StringBuffer(L + 3); + sb.append(s); + sb.setLength(k); + } + + int hexSize; + if (c < 256) { + if (c == ' ' && mask == URL_XPALPHAS) { + sb.append('+'); + continue; + } + sb.append('%'); + hexSize = 2; + } else { + sb.append('%'); + sb.append('u'); + hexSize = 4; + } + + // append hexadecimal form of c left-padded with 0 + for (int shift = (hexSize - 1) * 4; shift >= 0; shift -= 4) { + int digit = 0xf & (c >> shift); + int hc = (digit < 10) ? '0' + digit : 'A' - 10 + digit; + sb.append((char)hc); + } + } + } + + return (sb == null) ? s : sb.toString(); + } + + /** + * The global unescape method, as per ECMA-262 15.1.2.5. + */ + + private Object js_unescape(Object[] args) + { + String s = ScriptRuntime.toString(args, 0); + int firstEscapePos = s.indexOf('%'); + if (firstEscapePos >= 0) { + int L = s.length(); + char[] buf = s.toCharArray(); + int destination = firstEscapePos; + for (int k = firstEscapePos; k != L;) { + char c = buf[k]; + ++k; + if (c == '%' && k != L) { + int end, start; + if (buf[k] == 'u') { + start = k + 1; + end = k + 5; + } else { + start = k; + end = k + 2; + } + if (end <= L) { + int x = 0; + for (int i = start; i != end; ++i) { + x = Kit.xDigitToInt(buf[i], x); + } + if (x >= 0) { + c = (char)x; + k = end; + } + } + } + buf[destination] = c; + ++destination; + } + s = new String(buf, 0, destination); + } + return s; + } + + private Object js_eval(Context cx, Scriptable scope, Object[] args) + { + String m = ScriptRuntime.getMessage1("msg.cant.call.indirect", "eval"); + throw NativeGlobal.constructError(cx, "EvalError", m, scope); + } + + static boolean isEvalFunction(Object functionObj) + { + if (functionObj instanceof IdFunctionObject) { + IdFunctionObject function = (IdFunctionObject)functionObj; + if (function.hasTag(FTAG) && function.methodId() == Id_eval) { + return true; + } + } + return false; + } + + /** + * @deprecated Use {@link ScriptRuntime#constructError(String,String)} + * instead. + */ + public static EcmaError constructError(Context cx, + String error, + String message, + Scriptable scope) + { + return ScriptRuntime.constructError(error, message); + } + + /** + * @deprecated Use + * {@link ScriptRuntime#constructError(String,String,String,int,String,int)} + * instead. + */ + public static EcmaError constructError(Context cx, + String error, + String message, + Scriptable scope, + String sourceName, + int lineNumber, + int columnNumber, + String lineSource) + { + return ScriptRuntime.constructError(error, message, + sourceName, lineNumber, + lineSource, columnNumber); + } + + /* + * ECMA 3, 15.1.3 URI Handling Function Properties + * + * The following are implementations of the algorithms + * given in the ECMA specification for the hidden functions + * 'Encode' and 'Decode'. + */ + private static String encode(String str, boolean fullUri) { + byte[] utf8buf = null; + StringBuffer sb = null; + + for (int k = 0, length = str.length(); k != length; ++k) { + char C = str.charAt(k); + if (encodeUnescaped(C, fullUri)) { + if (sb != null) { + sb.append(C); + } + } else { + if (sb == null) { + sb = new StringBuffer(length + 3); + sb.append(str); + sb.setLength(k); + utf8buf = new byte[6]; + } + if (0xDC00 <= C && C <= 0xDFFF) { + throw Context.reportRuntimeError0("msg.bad.uri"); + } + int V; + if (C < 0xD800 || 0xDBFF < C) { + V = C; + } else { + k++; + if (k == length) { + throw Context.reportRuntimeError0("msg.bad.uri"); + } + char C2 = str.charAt(k); + if (!(0xDC00 <= C2 && C2 <= 0xDFFF)) { + throw Context.reportRuntimeError0("msg.bad.uri"); + } + V = ((C - 0xD800) << 10) + (C2 - 0xDC00) + 0x10000; + } + int L = oneUcs4ToUtf8Char(utf8buf, V); + for (int j = 0; j < L; j++) { + int d = 0xff & utf8buf[j]; + sb.append('%'); + sb.append(toHexChar(d >>> 4)); + sb.append(toHexChar(d & 0xf)); + } + } + } + return (sb == null) ? str : sb.toString(); + } + + private static char toHexChar(int i) { + if (i >> 4 != 0) Kit.codeBug(); + return (char)((i < 10) ? i + '0' : i - 10 + 'a'); + } + + private static int unHex(char c) { + if ('A' <= c && c <= 'F') { + return c - 'A' + 10; + } else if ('a' <= c && c <= 'f') { + return c - 'a' + 10; + } else if ('0' <= c && c <= '9') { + return c - '0'; + } else { + return -1; + } + } + + private static int unHex(char c1, char c2) { + int i1 = unHex(c1); + int i2 = unHex(c2); + if (i1 >= 0 && i2 >= 0) { + return (i1 << 4) | i2; + } + return -1; + } + + private static String decode(String str, boolean fullUri) { + char[] buf = null; + int bufTop = 0; + + for (int k = 0, length = str.length(); k != length;) { + char C = str.charAt(k); + if (C != '%') { + if (buf != null) { + buf[bufTop++] = C; + } + ++k; + } else { + if (buf == null) { + // decode always compress so result can not be bigger then + // str.length() + buf = new char[length]; + str.getChars(0, k, buf, 0); + bufTop = k; + } + int start = k; + if (k + 3 > length) + throw Context.reportRuntimeError0("msg.bad.uri"); + int B = unHex(str.charAt(k + 1), str.charAt(k + 2)); + if (B < 0) throw Context.reportRuntimeError0("msg.bad.uri"); + k += 3; + if ((B & 0x80) == 0) { + C = (char)B; + } else { + // Decode UTF-8 sequence into ucs4Char and encode it into + // UTF-16 + int utf8Tail, ucs4Char, minUcs4Char; + if ((B & 0xC0) == 0x80) { + // First UTF-8 should be ouside 0x80..0xBF + throw Context.reportRuntimeError0("msg.bad.uri"); + } else if ((B & 0x20) == 0) { + utf8Tail = 1; ucs4Char = B & 0x1F; + minUcs4Char = 0x80; + } else if ((B & 0x10) == 0) { + utf8Tail = 2; ucs4Char = B & 0x0F; + minUcs4Char = 0x800; + } else if ((B & 0x08) == 0) { + utf8Tail = 3; ucs4Char = B & 0x07; + minUcs4Char = 0x10000; + } else if ((B & 0x04) == 0) { + utf8Tail = 4; ucs4Char = B & 0x03; + minUcs4Char = 0x200000; + } else if ((B & 0x02) == 0) { + utf8Tail = 5; ucs4Char = B & 0x01; + minUcs4Char = 0x4000000; + } else { + // First UTF-8 can not be 0xFF or 0xFE + throw Context.reportRuntimeError0("msg.bad.uri"); + } + if (k + 3 * utf8Tail > length) + throw Context.reportRuntimeError0("msg.bad.uri"); + for (int j = 0; j != utf8Tail; j++) { + if (str.charAt(k) != '%') + throw Context.reportRuntimeError0("msg.bad.uri"); + B = unHex(str.charAt(k + 1), str.charAt(k + 2)); + if (B < 0 || (B & 0xC0) != 0x80) + throw Context.reportRuntimeError0("msg.bad.uri"); + ucs4Char = (ucs4Char << 6) | (B & 0x3F); + k += 3; + } + // Check for overlongs and other should-not-present codes + if (ucs4Char < minUcs4Char + || ucs4Char == 0xFFFE || ucs4Char == 0xFFFF) + { + ucs4Char = 0xFFFD; + } + if (ucs4Char >= 0x10000) { + ucs4Char -= 0x10000; + if (ucs4Char > 0xFFFFF) + throw Context.reportRuntimeError0("msg.bad.uri"); + char H = (char)((ucs4Char >>> 10) + 0xD800); + C = (char)((ucs4Char & 0x3FF) + 0xDC00); + buf[bufTop++] = H; + } else { + C = (char)ucs4Char; + } + } + if (fullUri && URI_DECODE_RESERVED.indexOf(C) >= 0) { + for (int x = start; x != k; x++) { + buf[bufTop++] = str.charAt(x); + } + } else { + buf[bufTop++] = C; + } + } + } + return (buf == null) ? str : new String(buf, 0, bufTop); + } + + private static boolean encodeUnescaped(char c, boolean fullUri) { + if (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z') + || ('0' <= c && c <= '9')) + { + return true; + } + if ("-_.!~*'()".indexOf(c) >= 0) + return true; + if (fullUri) { + return URI_DECODE_RESERVED.indexOf(c) >= 0; + } + return false; + } + + private static final String URI_DECODE_RESERVED = ";/?:@&=+$,#"; + + /* Convert one UCS-4 char and write it into a UTF-8 buffer, which must be + * at least 6 bytes long. Return the number of UTF-8 bytes of data written. + */ + private static int oneUcs4ToUtf8Char(byte[] utf8Buffer, int ucs4Char) { + int utf8Length = 1; + + //JS_ASSERT(ucs4Char <= 0x7FFFFFFF); + if ((ucs4Char & ~0x7F) == 0) + utf8Buffer[0] = (byte)ucs4Char; + else { + int i; + int a = ucs4Char >>> 11; + utf8Length = 2; + while (a != 0) { + a >>>= 5; + utf8Length++; + } + i = utf8Length; + while (--i > 0) { + utf8Buffer[i] = (byte)((ucs4Char & 0x3F) | 0x80); + ucs4Char >>>= 6; + } + utf8Buffer[0] = (byte)(0x100 - (1 << (8-utf8Length)) + ucs4Char); + } + return utf8Length; + } + + private static final Object FTAG = new Object(); + + private static final int + Id_decodeURI = 1, + Id_decodeURIComponent = 2, + Id_encodeURI = 3, + Id_encodeURIComponent = 4, + Id_escape = 5, + Id_eval = 6, + Id_isFinite = 7, + Id_isNaN = 8, + Id_isXMLName = 9, + Id_parseFloat = 10, + Id_parseInt = 11, + Id_unescape = 12, + Id_uneval = 13, + + LAST_SCOPE_FUNCTION_ID = 13, + + Id_new_CommonError = 14; +} diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeIterator.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeIterator.java new file mode 100644 index 0000000..c61f417 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeIterator.java @@ -0,0 +1,260 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Contributor(s): + * Norris Boyd + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript; + +import java.util.Iterator; + +/** + * This class implements iterator objects. See + * http://developer.mozilla.org/en/docs/New_in_JavaScript_1.7#Iterators + * + * @author Norris Boyd + */ +public final class NativeIterator extends IdScriptableObject { + private static final Object ITERATOR_TAG = new Object(); + + static void init(ScriptableObject scope, boolean sealed) { + // Iterator + NativeIterator iterator = new NativeIterator(); + iterator.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed); + + // Generator + NativeGenerator.init(scope, sealed); + + // StopIteration + NativeObject obj = new StopIteration(); + obj.setPrototype(getObjectPrototype(scope)); + obj.setParentScope(scope); + if (sealed) { obj.sealObject(); } + ScriptableObject.defineProperty(scope, STOP_ITERATION, obj, + ScriptableObject.DONTENUM); + // Use "associateValue" so that generators can continue to + // throw StopIteration even if the property of the global + // scope is replaced or deleted. + scope.associateValue(ITERATOR_TAG, obj); + } + + /** + * Only for constructing the prototype object. + */ + private NativeIterator() { + } + + private NativeIterator(Object objectIterator) { + this.objectIterator = objectIterator; + } + + /** + * Get the value of the "StopIteration" object. Note that this value + * is stored in the top-level scope using "associateValue" so the + * value can still be found even if a script overwrites or deletes + * the global "StopIteration" property. + * @param scope a scope whose parent chain reaches a top-level scope + * @return the StopIteration object + */ + public static Object getStopIterationObject(Scriptable scope) { + Scriptable top = ScriptableObject.getTopLevelScope(scope); + return ScriptableObject.getTopScopeValue(top, ITERATOR_TAG); + } + + private static final String STOP_ITERATION = "StopIteration"; + public static final String ITERATOR_PROPERTY_NAME = "__iterator__"; + + static class StopIteration extends NativeObject { + public String getClassName() { + return STOP_ITERATION; + } + + /* StopIteration has custom instanceof behavior since it + * doesn't have a constructor. + */ + public boolean hasInstance(Scriptable instance) { + return instance instanceof StopIteration; + } + } + + public String getClassName() { + return "Iterator"; + } + + protected void initPrototypeId(int id) { + String s; + int arity; + switch (id) { + case Id_constructor: arity=2; s="constructor"; break; + case Id_next: arity=0; s="next"; break; + case Id___iterator__: arity=1; s=ITERATOR_PROPERTY_NAME; break; + default: throw new IllegalArgumentException(String.valueOf(id)); + } + initPrototypeMethod(ITERATOR_TAG, id, s, arity); + } + + public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, + Scriptable thisObj, Object[] args) + { + if (!f.hasTag(ITERATOR_TAG)) { + return super.execIdCall(f, cx, scope, thisObj, args); + } + int id = f.methodId(); + + if (id == Id_constructor) { + return jsConstructor(cx, scope, thisObj, args); + } + + if (!(thisObj instanceof NativeIterator)) + throw incompatibleCallError(f); + + NativeIterator iterator = (NativeIterator) thisObj; + + switch (id) { + + case Id_next: + return iterator.next(cx, scope); + + case Id___iterator__: + /// XXX: what about argument? SpiderMonkey apparently ignores it + return thisObj; + + default: + throw new IllegalArgumentException(String.valueOf(id)); + } + } + + /* the javascript constructor */ + private static Object jsConstructor(Context cx, Scriptable scope, + Scriptable thisObj, Object[] args) + { + if (args.length == 0 || args[0] == null || + args[0] == Undefined.instance) + { + throw ScriptRuntime.typeError1("msg.no.properties", + ScriptRuntime.toString(args[0])); + } + Scriptable obj = ScriptRuntime.toObject(scope, args[0]); + boolean keyOnly = args.length > 1 && ScriptRuntime.toBoolean(args[1]); + if (thisObj != null) { + // Called as a function. Convert to iterator if possible. + + // For objects that implement java.lang.Iterable or + // java.util.Iterator, have JavaScript Iterator call the underlying + // iteration methods + Iterator iterator = + VMBridge.instance.getJavaIterator(cx, scope, obj); + if (iterator != null) { + scope = ScriptableObject.getTopLevelScope(scope); + return cx.getWrapFactory().wrap(cx, scope, + new WrappedJavaIterator(iterator, scope), + WrappedJavaIterator.class); + } + + // Otherwise, just call the runtime routine + Scriptable jsIterator = ScriptRuntime.toIterator(cx, scope, obj, + keyOnly); + if (jsIterator != null) { + return jsIterator; + } + } + + // Otherwise, just set up to iterate over the properties of the object. + // Do not call __iterator__ method. + Object objectIterator = ScriptRuntime.enumInit(obj, cx, + keyOnly ? ScriptRuntime.ENUMERATE_KEYS_NO_ITERATOR + : ScriptRuntime.ENUMERATE_ARRAY_NO_ITERATOR); + ScriptRuntime.setEnumNumbers(objectIterator, true); + NativeIterator result = new NativeIterator(objectIterator); + result.setPrototype(NativeIterator.getClassPrototype(scope, + result.getClassName())); + result.setParentScope(scope); + return result; + } + + private Object next(Context cx, Scriptable scope) { + Boolean b = ScriptRuntime.enumNext(this.objectIterator); + if (!b.booleanValue()) { + // Out of values. Throw StopIteration. + throw new JavaScriptException( + NativeIterator.getStopIterationObject(scope), null, 0); + } + return ScriptRuntime.enumId(this.objectIterator, cx); + } + + static public class WrappedJavaIterator + { + WrappedJavaIterator(Iterator iterator, Scriptable scope) { + this.iterator = iterator; + this.scope = scope; + } + + public Object next() { + if (!iterator.hasNext()) { + // Out of values. Throw StopIteration. + throw new JavaScriptException( + NativeIterator.getStopIterationObject(scope), null, 0); + } + return iterator.next(); + } + + public Object __iterator__(boolean b) { + return this; + } + + private Iterator iterator; + private Scriptable scope; + } + +// #string_id_map# + + protected int findPrototypeId(String s) { + int id; +// #generated# Last update: 2007-06-11 09:43:19 EDT + L0: { id = 0; String X = null; + int s_length = s.length(); + if (s_length==4) { X="next";id=Id_next; } + else if (s_length==11) { X="constructor";id=Id_constructor; } + else if (s_length==12) { X="__iterator__";id=Id___iterator__; } + if (X!=null && X!=s && !X.equals(s)) id = 0; + break L0; + } +// #/generated# + return id; + } + + private static final int + Id_constructor = 1, + Id_next = 2, + Id___iterator__ = 3, + MAX_PROTOTYPE_ID = 3; + +// #/string_id_map# + + private Object objectIterator; +} + diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaArray.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaArray.java new file mode 100644 index 0000000..2f711a0 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaArray.java @@ -0,0 +1,168 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Norris Boyd + * Igor Bukanov + * Frank Mitchell + * Mike Shaver + * Kemal Bayram + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript; + +import java.lang.reflect.Array; + +/** + * This class reflects Java arrays into the JavaScript environment. + * + * @author Mike Shaver + * @see NativeJavaClass + * @see NativeJavaObject + * @see NativeJavaPackage + */ + +public class NativeJavaArray extends NativeJavaObject +{ + static final long serialVersionUID = -924022554283675333L; + + public String getClassName() { + return "JavaArray"; + } + + public static NativeJavaArray wrap(Scriptable scope, Object array) { + return new NativeJavaArray(scope, array); + } + + public Object unwrap() { + return array; + } + + public NativeJavaArray(Scriptable scope, Object array) { + super(scope, null, ScriptRuntime.ObjectClass); + Class cl = array.getClass(); + if (!cl.isArray()) { + throw new RuntimeException("Array expected"); + } + this.array = array; + this.length = Array.getLength(array); + this.cls = cl.getComponentType(); + } + + public boolean has(String id, Scriptable start) { + return id.equals("length") || super.has(id, start); + } + + public boolean has(int index, Scriptable start) { + return 0 <= index && index < length; + } + + public Object get(String id, Scriptable start) { + if (id.equals("length")) + return new Integer(length); + Object result = super.get(id, start); + if (result == NOT_FOUND && + !ScriptableObject.hasProperty(getPrototype(), id)) + { + throw Context.reportRuntimeError2( + "msg.java.member.not.found", array.getClass().getName(), id); + } + return result; + } + + public Object get(int index, Scriptable start) { + if (0 <= index && index < length) { + Context cx = Context.getContext(); + Object obj = Array.get(array, index); + return cx.getWrapFactory().wrap(cx, this, obj, cls); + } + return Undefined.instance; + } + + public void put(String id, Scriptable start, Object value) { + // Ignore assignments to "length"--it's readonly. + if (!id.equals("length")) + throw Context.reportRuntimeError1( + "msg.java.array.member.not.found", id); + } + + public void put(int index, Scriptable start, Object value) { + if (0 <= index && index < length) { + Array.set(array, index, Context.jsToJava(value, cls)); + } + else { + throw Context.reportRuntimeError2( + "msg.java.array.index.out.of.bounds", String.valueOf(index), + String.valueOf(length - 1)); + } + } + + public Object getDefaultValue(Class hint) { + if (hint == null || hint == ScriptRuntime.StringClass) + return array.toString(); + if (hint == ScriptRuntime.BooleanClass) + return Boolean.TRUE; + if (hint == ScriptRuntime.NumberClass) + return ScriptRuntime.NaNobj; + return this; + } + + public Object[] getIds() { + Object[] result = new Object[length]; + int i = length; + while (--i >= 0) + result[i] = new Integer(i); + return result; + } + + public boolean hasInstance(Scriptable value) { + if (!(value instanceof Wrapper)) + return false; + Object instance = ((Wrapper)value).unwrap(); + return cls.isInstance(instance); + } + + public Scriptable getPrototype() { + if (prototype == null) { + prototype = + ScriptableObject.getClassPrototype(this.getParentScope(), + "Array"); + } + return prototype; + } + + Object array; + int length; + Class cls; +} diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaClass.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaClass.java new file mode 100644 index 0000000..ab8af5c --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaClass.java @@ -0,0 +1,320 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Norris Boyd + * Frank Mitchell + * Mike Shaver + * Kurt Westerfeld + * Kemal Bayram + * Ulrike Mueller + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript; + +import java.lang.reflect.*; +import java.util.Hashtable; + +/** + * This class reflects Java classes into the JavaScript environment, mainly + * for constructors and static members. We lazily reflect properties, + * and currently do not guarantee that a single j.l.Class is only + * reflected once into the JS environment, although we should. + * The only known case where multiple reflections + * are possible occurs when a j.l.Class is wrapped as part of a + * method return or property access, rather than by walking the + * Packages/java tree. + * + * @author Mike Shaver + * @see NativeJavaArray + * @see NativeJavaObject + * @see NativeJavaPackage + */ + +public class NativeJavaClass extends NativeJavaObject implements Function +{ + static final long serialVersionUID = -6460763940409461664L; + + // Special property for getting the underlying Java class object. + static final String javaClassPropertyName = "__javaObject__"; + + public NativeJavaClass() { + } + + public NativeJavaClass(Scriptable scope, Class cl) { + this.parent = scope; + this.javaObject = cl; + initMembers(); + } + + protected void initMembers() { + Class cl = (Class)javaObject; + members = JavaMembers.lookupClass(parent, cl, cl, false); + staticFieldAndMethods + = members.getFieldAndMethodsObjects(this, cl, true); + } + + public String getClassName() { + return "JavaClass"; + } + + public boolean has(String name, Scriptable start) { + return members.has(name, true) || javaClassPropertyName.equals(name); + } + + public Object get(String name, Scriptable start) { + // When used as a constructor, ScriptRuntime.newObject() asks + // for our prototype to create an object of the correct type. + // We don't really care what the object is, since we're returning + // one constructed out of whole cloth, so we return null. + if (name.equals("prototype")) + return null; + + if (staticFieldAndMethods != null) { + Object result = staticFieldAndMethods.get(name); + if (result != null) + return result; + } + + if (members.has(name, true)) { + return members.get(this, name, javaObject, true); + } + + if (javaClassPropertyName.equals(name)) { + Context cx = Context.getContext(); + Scriptable scope = ScriptableObject.getTopLevelScope(start); + return cx.getWrapFactory().wrap(cx, scope, javaObject, + ScriptRuntime.ClassClass); + } + + // experimental: look for nested classes by appending $name to + // current class' name. + Class nestedClass = findNestedClass(getClassObject(), name); + if (nestedClass != null) { + NativeJavaClass nestedValue = new NativeJavaClass + (ScriptableObject.getTopLevelScope(this), nestedClass); + nestedValue.setParentScope(this); + return nestedValue; + } + + throw members.reportMemberNotFound(name); + } + + public void put(String name, Scriptable start, Object value) { + members.put(this, name, javaObject, value, true); + } + + public Object[] getIds() { + return members.getIds(true); + } + + public Class getClassObject() { + return (Class) super.unwrap(); + } + + public Object getDefaultValue(Class hint) { + if (hint == null || hint == ScriptRuntime.StringClass) + return this.toString(); + if (hint == ScriptRuntime.BooleanClass) + return Boolean.TRUE; + if (hint == ScriptRuntime.NumberClass) + return ScriptRuntime.NaNobj; + return this; + } + + public Object call(Context cx, Scriptable scope, Scriptable thisObj, + Object[] args) + { + // If it looks like a "cast" of an object to this class type, + // walk the prototype chain to see if there's a wrapper of a + // object that's an instanceof this class. + if (args.length == 1 && args[0] instanceof Scriptable) { + Class c = getClassObject(); + Scriptable p = (Scriptable) args[0]; + do { + if (p instanceof Wrapper) { + Object o = ((Wrapper) p).unwrap(); + if (c.isInstance(o)) + return p; + } + p = p.getPrototype(); + } while (p != null); + } + return construct(cx, scope, args); + } + + public Scriptable construct(Context cx, Scriptable scope, Object[] args) + { + Class classObject = getClassObject(); + int modifiers = classObject.getModifiers(); + if (! (Modifier.isInterface(modifiers) || + Modifier.isAbstract(modifiers))) + { + MemberBox[] ctors = members.ctors; + int index = NativeJavaMethod.findFunction(cx, ctors, args); + if (index < 0) { + String sig = NativeJavaMethod.scriptSignature(args); + throw Context.reportRuntimeError2( + "msg.no.java.ctor", classObject.getName(), sig); + } + + // Found the constructor, so try invoking it. + return constructSpecific(cx, scope, args, ctors[index]); + } else { + Scriptable topLevel = ScriptableObject.getTopLevelScope(this); + String msg = ""; + try { + // trying to construct an interface; use JavaAdapter to + // construct a new class on the fly that implements this + // interface. + Object v = topLevel.get("JavaAdapter", topLevel); + if (v != NOT_FOUND) { + Function f = (Function) v; + Object[] adapterArgs = { this, args[0] }; + return f.construct(cx, topLevel,adapterArgs); + } + } catch (Exception ex) { + // fall through to error + String m = ex.getMessage(); + if (m != null) + msg = m; + } + throw Context.reportRuntimeError2( + "msg.cant.instantiate", msg, classObject.getName()); + } + } + + static Scriptable constructSpecific(Context cx, Scriptable scope, + Object[] args, MemberBox ctor) + { + Scriptable topLevel = ScriptableObject.getTopLevelScope(scope); + Class[] argTypes = ctor.argTypes; + + if (ctor.vararg) { + // marshall the explicit parameter + Object[] newArgs = new Object[argTypes.length]; + for (int i = 0; i < argTypes.length-1; i++) { + newArgs[i] = Context.jsToJava(args[i], argTypes[i]); + } + + Object varArgs; + + // Handle special situation where a single variable parameter + // is given and it is a Java or ECMA array. + if (args.length == argTypes.length && + (args[args.length-1] == null || + args[args.length-1] instanceof NativeArray || + args[args.length-1] instanceof NativeJavaArray)) + { + // convert the ECMA array into a native array + varArgs = Context.jsToJava(args[args.length-1], + argTypes[argTypes.length - 1]); + } else { + // marshall the variable parameter + Class componentType = argTypes[argTypes.length - 1]. + getComponentType(); + varArgs = Array.newInstance(componentType, + args.length - argTypes.length + 1); + for (int i=0; i < Array.getLength(varArgs); i++) { + Object value = Context.jsToJava(args[argTypes.length-1 + i], + componentType); + Array.set(varArgs, i, value); + } + } + + // add varargs + newArgs[argTypes.length-1] = varArgs; + // replace the original args with the new one + args = newArgs; + } else { + Object[] origArgs = args; + for (int i = 0; i < args.length; i++) { + Object arg = args[i]; + Object x = Context.jsToJava(arg, argTypes[i]); + if (x != arg) { + if (args == origArgs) { + args = origArgs.clone(); + } + args[i] = x; + } + } + } + + Object instance = ctor.newInstance(args); + // we need to force this to be wrapped, because construct _has_ + // to return a scriptable + return cx.getWrapFactory().wrapNewObject(cx, topLevel, instance); + } + + public String toString() { + return "[JavaClass " + getClassObject().getName() + "]"; + } + + /** + * Determines if prototype is a wrapped Java object and performs + * a Java "instanceof". + * Exception: if value is an instance of NativeJavaClass, it isn't + * considered an instance of the Java class; this forestalls any + * name conflicts between java.lang.Class's methods and the + * static methods exposed by a JavaNativeClass. + */ + public boolean hasInstance(Scriptable value) { + + if (value instanceof Wrapper && + !(value instanceof NativeJavaClass)) { + Object instance = ((Wrapper)value).unwrap(); + + return getClassObject().isInstance(instance); + } + + // value wasn't something we understand + return false; + } + + private static Class findNestedClass(Class parentClass, String name) { + String nestedClassName = parentClass.getName() + '$' + name; + ClassLoader loader = parentClass.getClassLoader(); + if (loader == null) { + // ALERT: if loader is null, nested class should be loaded + // via system class loader which can be different from the + // loader that brought Rhino classes that Class.forName() would + // use, but ClassLoader.getSystemClassLoader() is Java 2 only + return Kit.classOrNull(nestedClassName); + } else { + return Kit.classOrNull(loader, nestedClassName); + } + } + + private Hashtable staticFieldAndMethods; +} diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaConstructor.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaConstructor.java new file mode 100644 index 0000000..530bf81 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaConstructor.java @@ -0,0 +1,85 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Norris Boyd + * Frank Mitchell + * Mike Shaver + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript; + +/** + * This class reflects a single Java constructor into the JavaScript + * environment. It satisfies a request for an overloaded constructor, + * as introduced in LiveConnect 3. + * All NativeJavaConstructors behave as JSRef `bound' methods, in that they + * always construct the same NativeJavaClass regardless of any reparenting + * that may occur. + * + * @author Frank Mitchell + * @see NativeJavaMethod + * @see NativeJavaPackage + * @see NativeJavaClass + */ + +public class NativeJavaConstructor extends BaseFunction +{ + static final long serialVersionUID = -8149253217482668463L; + + MemberBox ctor; + + public NativeJavaConstructor(MemberBox ctor) + { + this.ctor = ctor; + } + + public Object call(Context cx, Scriptable scope, Scriptable thisObj, + Object[] args) + { + return NativeJavaClass.constructSpecific(cx, scope, args, ctor); + } + + public String getFunctionName() + { + String sig = JavaMembers.liveConnectSignature(ctor.argTypes); + return "".concat(sig); + } + + public String toString() + { + return "[JavaConstructor " + ctor.getName() + "]"; + } +} + diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaMethod.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaMethod.java new file mode 100644 index 0000000..eb66f40 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaMethod.java @@ -0,0 +1,576 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Norris Boyd + * Frank Mitchell + * Mike Shaver + * Ulrike Mueller + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript; + +import java.lang.reflect.*; + +/** + * This class reflects Java methods into the JavaScript environment and + * handles overloading of methods. + * + * @author Mike Shaver + * @see NativeJavaArray + * @see NativeJavaPackage + * @see NativeJavaClass + */ + +public class NativeJavaMethod extends BaseFunction +{ + static final long serialVersionUID = -3440381785576412928L; + + NativeJavaMethod(MemberBox[] methods) + { + this.functionName = methods[0].getName(); + this.methods = methods; + } + + NativeJavaMethod(MemberBox method, String name) + { + this.functionName = name; + this.methods = new MemberBox[] { method }; + } + + public NativeJavaMethod(Method method, String name) + { + this(new MemberBox(method), name); + } + + public String getFunctionName() + { + return functionName; + } + + static String scriptSignature(Object[] values) + { + StringBuffer sig = new StringBuffer(); + for (int i = 0; i != values.length; ++i) { + Object value = values[i]; + + String s; + if (value == null) { + s = "null"; + } else if (value instanceof Boolean) { + s = "boolean"; + } else if (value instanceof String) { + s = "string"; + } else if (value instanceof Number) { + s = "number"; + } else if (value instanceof Scriptable) { + if (value instanceof Undefined) { + s = "undefined"; + } else if (value instanceof Wrapper) { + Object wrapped = ((Wrapper)value).unwrap(); + s = wrapped.getClass().getName(); + } else if (value instanceof Function) { + s = "function"; + } else { + s = "object"; + } + } else { + s = JavaMembers.javaSignature(value.getClass()); + } + + if (i != 0) { + sig.append(','); + } + sig.append(s); + } + return sig.toString(); + } + + String decompile(int indent, int flags) + { + StringBuffer sb = new StringBuffer(); + boolean justbody = (0 != (flags & Decompiler.ONLY_BODY_FLAG)); + if (!justbody) { + sb.append("function "); + sb.append(getFunctionName()); + sb.append("() {"); + } + sb.append("/*\n"); + sb.append(toString()); + sb.append(justbody ? "*/\n" : "*/}\n"); + return sb.toString(); + } + + public String toString() + { + StringBuffer sb = new StringBuffer(); + for (int i = 0, N = methods.length; i != N; ++i) { + Method method = methods[i].method(); + sb.append(JavaMembers.javaSignature(method.getReturnType())); + sb.append(' '); + sb.append(method.getName()); + sb.append(JavaMembers.liveConnectSignature(methods[i].argTypes)); + sb.append('\n'); + } + return sb.toString(); + } + + public Object call(Context cx, Scriptable scope, Scriptable thisObj, + Object[] args) + { + // Find a method that matches the types given. + if (methods.length == 0) { + throw new RuntimeException("No methods defined for call"); + } + + int index = findFunction(cx, methods, args); + if (index < 0) { + Class c = methods[0].method().getDeclaringClass(); + String sig = c.getName() + '.' + getFunctionName() + '(' + + scriptSignature(args) + ')'; + throw Context.reportRuntimeError1("msg.java.no_such_method", sig); + } + + MemberBox meth = methods[index]; + Class[] argTypes = meth.argTypes; + + if (meth.vararg) { + // marshall the explicit parameters + Object[] newArgs = new Object[argTypes.length]; + for (int i = 0; i < argTypes.length-1; i++) { + newArgs[i] = Context.jsToJava(args[i], argTypes[i]); + } + + Object varArgs; + + // Handle special situation where a single variable parameter + // is given and it is a Java or ECMA array or is null. + if (args.length == argTypes.length && + (args[args.length-1] == null || + args[args.length-1] instanceof NativeArray || + args[args.length-1] instanceof NativeJavaArray)) + { + // convert the ECMA array into a native array + varArgs = Context.jsToJava(args[args.length-1], + argTypes[argTypes.length - 1]); + } else { + // marshall the variable parameters + Class componentType = argTypes[argTypes.length - 1]. + getComponentType(); + varArgs = Array.newInstance(componentType, + args.length - argTypes.length + 1); + for (int i = 0; i < Array.getLength(varArgs); i++) { + Object value = Context.jsToJava(args[argTypes.length-1 + i], + componentType); + Array.set(varArgs, i, value); + } + } + + // add varargs + newArgs[argTypes.length-1] = varArgs; + // replace the original args with the new one + args = newArgs; + } else { + // First, we marshall the args. + Object[] origArgs = args; + for (int i = 0; i < args.length; i++) { + Object arg = args[i]; + Object coerced = Context.jsToJava(arg, argTypes[i]); + if (coerced != arg) { + if (origArgs == args) { + args = args.clone(); + } + args[i] = coerced; + } + } + } + Object javaObject; + if (meth.isStatic()) { + javaObject = null; // don't need an object + } else { + Scriptable o = thisObj; + Class c = meth.getDeclaringClass(); + for (;;) { + if (o == null) { + throw Context.reportRuntimeError3( + "msg.nonjava.method", getFunctionName(), + ScriptRuntime.toString(thisObj), c.getName()); + } + if (o instanceof Wrapper) { + javaObject = ((Wrapper)o).unwrap(); + if (c.isInstance(javaObject)) { + break; + } + } + o = o.getPrototype(); + } + } + if (debug) { + printDebug("Calling ", meth, args); + } + + Object retval = meth.invoke(javaObject, args); + Class staticType = meth.method().getReturnType(); + + if (debug) { + Class actualType = (retval == null) ? null + : retval.getClass(); + System.err.println(" ----- Returned " + retval + + " actual = " + actualType + + " expect = " + staticType); + } + + Object wrapped = cx.getWrapFactory().wrap(cx, scope, + retval, staticType); + if (debug) { + Class actualType = (wrapped == null) ? null + : wrapped.getClass(); + System.err.println(" ----- Wrapped as " + wrapped + + " class = " + actualType); + } + + if (wrapped == null && staticType == Void.TYPE) { + wrapped = Undefined.instance; + } + return wrapped; + } + + /** + * Find the index of the correct function to call given the set of methods + * or constructors and the arguments. + * If no function can be found to call, return -1. + */ + static int findFunction(Context cx, + MemberBox[] methodsOrCtors, Object[] args) + { + if (methodsOrCtors.length == 0) { + return -1; + } else if (methodsOrCtors.length == 1) { + MemberBox member = methodsOrCtors[0]; + Class[] argTypes = member.argTypes; + int alength = argTypes.length; + + if (member.vararg) { + alength--; + if ( alength > args.length) { + return -1; + } + } else { + if (alength != args.length) { + return -1; + } + } + for (int j = 0; j != alength; ++j) { + if (!NativeJavaObject.canConvert(args[j], argTypes[j])) { + if (debug) printDebug("Rejecting (args can't convert) ", + member, args); + return -1; + } + } + if (debug) printDebug("Found ", member, args); + return 0; + } + + int firstBestFit = -1; + int[] extraBestFits = null; + int extraBestFitsCount = 0; + + search: + for (int i = 0; i < methodsOrCtors.length; i++) { + MemberBox member = methodsOrCtors[i]; + Class[] argTypes = member.argTypes; + int alength = argTypes.length; + if (member.vararg) { + alength--; + if ( alength > args.length) { + continue search; + } + } else { + if (alength != args.length) { + continue search; + } + } + for (int j = 0; j < alength; j++) { + if (!NativeJavaObject.canConvert(args[j], argTypes[j])) { + if (debug) printDebug("Rejecting (args can't convert) ", + member, args); + continue search; + } + } + if (firstBestFit < 0) { + if (debug) printDebug("Found first applicable ", member, args); + firstBestFit = i; + } else { + // Compare with all currently fit methods. + // The loop starts from -1 denoting firstBestFit and proceed + // until extraBestFitsCount to avoid extraBestFits allocation + // in the most common case of no ambiguity + int betterCount = 0; // number of times member was prefered over + // best fits + int worseCount = 0; // number of times best fits were prefered + // over member + for (int j = -1; j != extraBestFitsCount; ++j) { + int bestFitIndex; + if (j == -1) { + bestFitIndex = firstBestFit; + } else { + bestFitIndex = extraBestFits[j]; + } + MemberBox bestFit = methodsOrCtors[bestFitIndex]; + if (cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS) && + (bestFit.member().getModifiers() & Modifier.PUBLIC) != + (member.member().getModifiers() & Modifier.PUBLIC)) + { + // When FEATURE_ENHANCED_JAVA_ACCESS gives us access + // to non-public members, continue to prefer public + // methods in overloading + if ((bestFit.member().getModifiers() & Modifier.PUBLIC) == 0) + ++betterCount; + else + ++worseCount; + } else { + int preference = preferSignature(args, argTypes, + member.vararg, + bestFit.argTypes, + bestFit.vararg ); + if (preference == PREFERENCE_AMBIGUOUS) { + break; + } else if (preference == PREFERENCE_FIRST_ARG) { + ++betterCount; + } else if (preference == PREFERENCE_SECOND_ARG) { + ++worseCount; + } else { + if (preference != PREFERENCE_EQUAL) Kit.codeBug(); + // This should not happen in theory + // but on some JVMs, Class.getMethods will return all + // static methods of the class heirarchy, even if + // a derived class's parameters match exactly. + // We want to call the dervied class's method. + if (bestFit.isStatic() + && bestFit.getDeclaringClass().isAssignableFrom( + member.getDeclaringClass())) + { + // On some JVMs, Class.getMethods will return all + // static methods of the class heirarchy, even if + // a derived class's parameters match exactly. + // We want to call the dervied class's method. + if (debug) printDebug( + "Substituting (overridden static)", + member, args); + if (j == -1) { + firstBestFit = i; + } else { + extraBestFits[j] = i; + } + } else { + if (debug) printDebug( + "Ignoring same signature member ", + member, args); + } + continue search; + } + } + } + if (betterCount == 1 + extraBestFitsCount) { + // member was prefered over all best fits + if (debug) printDebug( + "New first applicable ", member, args); + firstBestFit = i; + extraBestFitsCount = 0; + } else if (worseCount == 1 + extraBestFitsCount) { + // all best fits were prefered over member, ignore it + if (debug) printDebug( + "Rejecting (all current bests better) ", member, args); + } else { + // some ambiguity was present, add member to best fit set + if (debug) printDebug( + "Added to best fit set ", member, args); + if (extraBestFits == null) { + // Allocate maximum possible array + extraBestFits = new int[methodsOrCtors.length - 1]; + } + extraBestFits[extraBestFitsCount] = i; + ++extraBestFitsCount; + } + } + } + + if (firstBestFit < 0) { + // Nothing was found + return -1; + } else if (extraBestFitsCount == 0) { + // single best fit + return firstBestFit; + } + + // report remaining ambiguity + StringBuffer buf = new StringBuffer(); + for (int j = -1; j != extraBestFitsCount; ++j) { + int bestFitIndex; + if (j == -1) { + bestFitIndex = firstBestFit; + } else { + bestFitIndex = extraBestFits[j]; + } + buf.append("\n "); + buf.append(methodsOrCtors[bestFitIndex].toJavaDeclaration()); + } + + MemberBox firstFitMember = methodsOrCtors[firstBestFit]; + String memberName = firstFitMember.getName(); + String memberClass = firstFitMember.getDeclaringClass().getName(); + + if (methodsOrCtors[0].isMethod()) { + throw Context.reportRuntimeError3( + "msg.constructor.ambiguous", + memberName, scriptSignature(args), buf.toString()); + } else { + throw Context.reportRuntimeError4( + "msg.method.ambiguous", memberClass, + memberName, scriptSignature(args), buf.toString()); + } + } + + /** Types are equal */ + private static final int PREFERENCE_EQUAL = 0; + private static final int PREFERENCE_FIRST_ARG = 1; + private static final int PREFERENCE_SECOND_ARG = 2; + /** No clear "easy" conversion */ + private static final int PREFERENCE_AMBIGUOUS = 3; + + /** + * Determine which of two signatures is the closer fit. + * Returns one of PREFERENCE_EQUAL, PREFERENCE_FIRST_ARG, + * PREFERENCE_SECOND_ARG, or PREFERENCE_AMBIGUOUS. + */ + private static int preferSignature(Object[] args, + Class[] sig1, + boolean vararg1, + Class[] sig2, + boolean vararg2 ) + { + // TODO: This test is pretty primitive. It bascially prefers + // a matching no vararg method over a vararg method independent + // of the type conversion cost. This can lead to unexpected results. + int alength = args.length; + if (!vararg1 && vararg2) { + // prefer the no vararg signature + return PREFERENCE_FIRST_ARG; + } else if (vararg1 && !vararg2) { + // prefer the no vararg signature + return PREFERENCE_SECOND_ARG; + } else if (vararg1 && vararg2) { + if (sig1.length < sig2.length) { + // prefer the signature with more explicit types + return PREFERENCE_SECOND_ARG; + } else if (sig1.length > sig2.length) { + // prefer the signature with more explicit types + return PREFERENCE_FIRST_ARG; + } else { + // Both are varargs and have the same length, so make the + // decision with the explicit args. + alength = Math.min(args.length, sig1.length-1); + } + } + + int totalPreference = 0; + for (int j = 0; j < alength; j++) { + Class type1 = sig1[j]; + Class type2 = sig2[j]; + if (type1 == type2) { + continue; + } + Object arg = args[j]; + + // Determine which of type1, type2 is easier to convert from arg. + + int rank1 = NativeJavaObject.getConversionWeight(arg, type1); + int rank2 = NativeJavaObject.getConversionWeight(arg, type2); + + int preference; + if (rank1 < rank2) { + preference = PREFERENCE_FIRST_ARG; + } else if (rank1 > rank2) { + preference = PREFERENCE_SECOND_ARG; + } else { + // Equal ranks + if (rank1 == NativeJavaObject.CONVERSION_NONTRIVIAL) { + if (type1.isAssignableFrom(type2)) { + preference = PREFERENCE_SECOND_ARG; + } else if (type2.isAssignableFrom(type1)) { + preference = PREFERENCE_FIRST_ARG; + } else { + preference = PREFERENCE_AMBIGUOUS; + } + } else { + preference = PREFERENCE_AMBIGUOUS; + } + } + + totalPreference |= preference; + + if (totalPreference == PREFERENCE_AMBIGUOUS) { + break; + } + } + return totalPreference; + } + + + private static final boolean debug = false; + + private static void printDebug(String msg, MemberBox member, + Object[] args) + { + if (debug) { + StringBuffer sb = new StringBuffer(); + sb.append(" ----- "); + sb.append(msg); + sb.append(member.getDeclaringClass().getName()); + sb.append('.'); + if (member.isMethod()) { + sb.append(member.getName()); + } + sb.append(JavaMembers.liveConnectSignature(member.argTypes)); + sb.append(" for arguments ("); + sb.append(scriptSignature(args)); + sb.append(')'); + System.out.println(sb); + } + } + + MemberBox[] methods; + private String functionName; +} + diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaObject.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaObject.java new file mode 100644 index 0000000..3d27852 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaObject.java @@ -0,0 +1,1002 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Norris Boyd + * Igor Bukanov + * Frank Mitchell + * Mike Shaver + * Kemal Bayram + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript; + +import java.io.*; +import java.lang.reflect.*; +import java.util.Hashtable; +import java.util.Date; + +/** + * This class reflects non-Array Java objects into the JavaScript environment. It + * reflect fields directly, and uses NativeJavaMethod objects to reflect (possibly + * overloaded) methods.

    + * + * @author Mike Shaver + * @see NativeJavaArray + * @see NativeJavaPackage + * @see NativeJavaClass + */ + +public class NativeJavaObject implements Scriptable, Wrapper, Serializable +{ + static final long serialVersionUID = -6948590651130498591L; + + public NativeJavaObject() { } + + public NativeJavaObject(Scriptable scope, Object javaObject, + Class staticType) + { + this(scope, javaObject, staticType, false); + } + + public NativeJavaObject(Scriptable scope, Object javaObject, + Class staticType, boolean isAdapter) + { + this.parent = ScriptableObject.getVeryTopLevelScope(scope); // APPJET + this.javaObject = javaObject; + this.staticType = staticType; + this.isAdapter = isAdapter; + initMembers(); + } + + protected void initMembers() { + Class dynamicType; + if (javaObject != null) { + dynamicType = javaObject.getClass(); + } else { + dynamicType = staticType; + } + members = JavaMembers.lookupClass(parent, dynamicType, staticType, + isAdapter); + fieldAndMethods + = members.getFieldAndMethodsObjects(this, javaObject, false); + } + + public boolean has(String name, Scriptable start) { + return members.has(name, false); + } + + public boolean has(int index, Scriptable start) { + return false; + } + + public Object get(String name, Scriptable start) { + if (fieldAndMethods != null) { + Object result = fieldAndMethods.get(name); + if (result != null) { + return result; + } + } + // TODO: passing 'this' as the scope is bogus since it has + // no parent scope + return members.get(this, name, javaObject, false); + } + + public Object get(int index, Scriptable start) { + throw members.reportMemberNotFound(Integer.toString(index)); + } + + public void put(String name, Scriptable start, Object value) { + // We could be asked to modify the value of a property in the + // prototype. Since we can't add a property to a Java object, + // we modify it in the prototype rather than copy it down. + if (prototype == null || members.has(name, false)) + members.put(this, name, javaObject, value, false); + else + prototype.put(name, prototype, value); + } + + public void put(int index, Scriptable start, Object value) { + throw members.reportMemberNotFound(Integer.toString(index)); + } + + public boolean hasInstance(Scriptable value) { + // This is an instance of a Java class, so always return false + return false; + } + + public void delete(String name) { + } + + public void delete(int index) { + } + + public Scriptable getPrototype() { + if (prototype == null && javaObject instanceof String) { + return ScriptableObject.getClassPrototype(parent, "String"); + } + return prototype; + } + + /** + * Sets the prototype of the object. + */ + public void setPrototype(Scriptable m) { + prototype = m; + } + + /** + * Returns the parent (enclosing) scope of the object. + */ + public Scriptable getParentScope() { + return parent; + } + + /** + * Sets the parent (enclosing) scope of the object. + */ + public void setParentScope(Scriptable m) { + parent = m; + } + + public Object[] getIds() { + return members.getIds(false); + } + +/** +@deprecated Use {@link Context#getWrapFactory()} together with calling {@link +WrapFactory#wrap(Context, Scriptable, Object, Class)} +*/ + public static Object wrap(Scriptable scope, Object obj, Class staticType) { + + Context cx = Context.getContext(); + return cx.getWrapFactory().wrap(cx, scope, obj, staticType); + } + + public Object unwrap() { + return javaObject; + } + + public String getClassName() { + return "JavaObject"; + } + + public Object getDefaultValue(Class hint) + { + Object value; + if (hint == null) { + if (javaObject instanceof Boolean) { + hint = ScriptRuntime.BooleanClass; + } + } + if (hint == null || hint == ScriptRuntime.StringClass) { + value = javaObject.toString(); + } else { + String converterName; + if (hint == ScriptRuntime.BooleanClass) { + converterName = "booleanValue"; + } else if (hint == ScriptRuntime.NumberClass) { + converterName = "doubleValue"; + } else { + throw Context.reportRuntimeError0("msg.default.value"); + } + Object converterObject = get(converterName, this); + if (converterObject instanceof Function) { + Function f = (Function)converterObject; + value = f.call(Context.getContext(), f.getParentScope(), + this, ScriptRuntime.emptyArgs); + } else { + if (hint == ScriptRuntime.NumberClass + && javaObject instanceof Boolean) + { + boolean b = ((Boolean)javaObject).booleanValue(); + value = ScriptRuntime.wrapNumber(b ? 1.0 : 0.0); + } else { + value = javaObject.toString(); + } + } + } + return value; + } + + /** + * Determine whether we can/should convert between the given type and the + * desired one. This should be superceded by a conversion-cost calculation + * function, but for now I'll hide behind precedent. + */ + public static boolean canConvert(Object fromObj, Class to) { + int weight = getConversionWeight(fromObj, to); + + return (weight < CONVERSION_NONE); + } + + private static final int JSTYPE_UNDEFINED = 0; // undefined type + private static final int JSTYPE_NULL = 1; // null + private static final int JSTYPE_BOOLEAN = 2; // boolean + private static final int JSTYPE_NUMBER = 3; // number + private static final int JSTYPE_STRING = 4; // string + private static final int JSTYPE_JAVA_CLASS = 5; // JavaClass + private static final int JSTYPE_JAVA_OBJECT = 6; // JavaObject + private static final int JSTYPE_JAVA_ARRAY = 7; // JavaArray + private static final int JSTYPE_OBJECT = 8; // Scriptable + + static final byte CONVERSION_TRIVIAL = 1; + static final byte CONVERSION_NONTRIVIAL = 0; + static final byte CONVERSION_NONE = 99; + + /** + * Derive a ranking based on how "natural" the conversion is. + * The special value CONVERSION_NONE means no conversion is possible, + * and CONVERSION_NONTRIVIAL signals that more type conformance testing + * is required. + * Based on + * + * "preferred method conversions" from Live Connect 3 + */ + static int getConversionWeight(Object fromObj, Class to) { + int fromCode = getJSTypeCode(fromObj); + + switch (fromCode) { + + case JSTYPE_UNDEFINED: + if (to == ScriptRuntime.StringClass || + to == ScriptRuntime.ObjectClass) { + return 1; + } + break; + + case JSTYPE_NULL: + if (!to.isPrimitive()) { + return 1; + } + break; + + case JSTYPE_BOOLEAN: + // "boolean" is #1 + if (to == Boolean.TYPE) { + return 1; + } + else if (to == ScriptRuntime.BooleanClass) { + return 2; + } + else if (to == ScriptRuntime.ObjectClass) { + return 3; + } + else if (to == ScriptRuntime.StringClass) { + return 4; + } + break; + + case JSTYPE_NUMBER: + if (to.isPrimitive()) { + if (to == Double.TYPE) { + return 1; + } + else if (to != Boolean.TYPE) { + return 1 + getSizeRank(to); + } + } + else { + if (to == ScriptRuntime.StringClass) { + // native numbers are #1-8 + return 9; + } + else if (to == ScriptRuntime.ObjectClass) { + return 10; + } + else if (ScriptRuntime.NumberClass.isAssignableFrom(to)) { + // "double" is #1 + return 2; + } + } + break; + + case JSTYPE_STRING: + if (to == ScriptRuntime.StringClass) { + return 1; + } + else if (to.isInstance(fromObj)) { + return 2; + } + else if (to.isPrimitive()) { + if (to == Character.TYPE) { + return 3; + } else if (to != Boolean.TYPE) { + return 4; + } + } + break; + + case JSTYPE_JAVA_CLASS: + if (to == ScriptRuntime.ClassClass) { + return 1; + } + else if (to == ScriptRuntime.ObjectClass) { + return 3; + } + else if (to == ScriptRuntime.StringClass) { + return 4; + } + break; + + case JSTYPE_JAVA_OBJECT: + case JSTYPE_JAVA_ARRAY: + Object javaObj = fromObj; + if (javaObj instanceof Wrapper) { + javaObj = ((Wrapper)javaObj).unwrap(); + } + if (to.isInstance(javaObj)) { + return CONVERSION_NONTRIVIAL; + } + if (to == ScriptRuntime.StringClass) { + return 2; + } + else if (to.isPrimitive() && to != Boolean.TYPE) { + return (fromCode == JSTYPE_JAVA_ARRAY) + ? CONVERSION_NONE : 2 + getSizeRank(to); + } + break; + + case JSTYPE_OBJECT: + // Other objects takes #1-#3 spots + if (to == fromObj.getClass()) { + // No conversion required + return 1; + } + if (to.isArray()) { + if (fromObj instanceof NativeArray) { + // This is a native array conversion to a java array + // Array conversions are all equal, and preferable to object + // and string conversion, per LC3. + return 1; + } + } + else if (to == ScriptRuntime.ObjectClass) { + return 2; + } + else if (to == ScriptRuntime.StringClass) { + return 3; + } + else if (to == ScriptRuntime.DateClass) { + if (fromObj instanceof NativeDate) { + // This is a native date to java date conversion + return 1; + } + } + else if (to.isInterface()) { + if (fromObj instanceof Function) { + // See comments in coerceType + if (to.getMethods().length == 1) { + return 1; + } + } + return 11; + } + else if (to.isPrimitive() && to != Boolean.TYPE) { + return 3 + getSizeRank(to); + } + break; + } + + return CONVERSION_NONE; + } + + static int getSizeRank(Class aType) { + if (aType == Double.TYPE) { + return 1; + } + else if (aType == Float.TYPE) { + return 2; + } + else if (aType == Long.TYPE) { + return 3; + } + else if (aType == Integer.TYPE) { + return 4; + } + else if (aType == Short.TYPE) { + return 5; + } + else if (aType == Character.TYPE) { + return 6; + } + else if (aType == Byte.TYPE) { + return 7; + } + else if (aType == Boolean.TYPE) { + return CONVERSION_NONE; + } + else { + return 8; + } + } + + private static int getJSTypeCode(Object value) { + if (value == null) { + return JSTYPE_NULL; + } + else if (value == Undefined.instance) { + return JSTYPE_UNDEFINED; + } + else if (value instanceof String) { + return JSTYPE_STRING; + } + else if (value instanceof Number) { + return JSTYPE_NUMBER; + } + else if (value instanceof Boolean) { + return JSTYPE_BOOLEAN; + } + else if (value instanceof Scriptable) { + if (value instanceof NativeJavaClass) { + return JSTYPE_JAVA_CLASS; + } + else if (value instanceof NativeJavaArray) { + return JSTYPE_JAVA_ARRAY; + } + else if (value instanceof Wrapper) { + return JSTYPE_JAVA_OBJECT; + } + else { + return JSTYPE_OBJECT; + } + } + else if (value instanceof Class) { + return JSTYPE_JAVA_CLASS; + } + else { + Class valueClass = value.getClass(); + if (valueClass.isArray()) { + return JSTYPE_JAVA_ARRAY; + } + else { + return JSTYPE_JAVA_OBJECT; + } + } + } + + /** + * Not intended for public use. Callers should use the + * public API Context.toType. + * @deprecated as of 1.5 Release 4 + * @see org.mozilla.javascript.Context#jsToJava(Object, Class) + */ + public static Object coerceType(Class type, Object value) + { + return coerceTypeImpl(type, value); + } + + /** + * Type-munging for field setting and method invocation. + * Conforms to LC3 specification + */ + static Object coerceTypeImpl(Class type, Object value) + { + if (value != null && value.getClass() == type) { + return value; + } + + switch (getJSTypeCode(value)) { + + case JSTYPE_NULL: + // raise error if type.isPrimitive() + if (type.isPrimitive()) { + reportConversionError(value, type); + } + return null; + + case JSTYPE_UNDEFINED: + if (type == ScriptRuntime.StringClass || + type == ScriptRuntime.ObjectClass) { + return "undefined"; + } + else { + reportConversionError("undefined", type); + } + break; + + case JSTYPE_BOOLEAN: + // Under LC3, only JS Booleans can be coerced into a Boolean value + if (type == Boolean.TYPE || + type == ScriptRuntime.BooleanClass || + type == ScriptRuntime.ObjectClass) { + return value; + } + else if (type == ScriptRuntime.StringClass) { + return value.toString(); + } + else { + reportConversionError(value, type); + } + break; + + case JSTYPE_NUMBER: + if (type == ScriptRuntime.StringClass) { + return ScriptRuntime.toString(value); + } + else if (type == ScriptRuntime.ObjectClass) { + return coerceToNumber(Double.TYPE, value); + } + else if ((type.isPrimitive() && type != Boolean.TYPE) || + ScriptRuntime.NumberClass.isAssignableFrom(type)) { + return coerceToNumber(type, value); + } + else { + reportConversionError(value, type); + } + break; + + case JSTYPE_STRING: + if (type == ScriptRuntime.StringClass || type.isInstance(value)) { + return value; + } + else if (type == Character.TYPE + || type == ScriptRuntime.CharacterClass) + { + // Special case for converting a single char string to a + // character + // Placed here because it applies *only* to JS strings, + // not other JS objects converted to strings + if (((String)value).length() == 1) { + return new Character(((String)value).charAt(0)); + } + else { + return coerceToNumber(type, value); + } + } + else if ((type.isPrimitive() && type != Boolean.TYPE) + || ScriptRuntime.NumberClass.isAssignableFrom(type)) + { + return coerceToNumber(type, value); + } + else { + reportConversionError(value, type); + } + break; + + case JSTYPE_JAVA_CLASS: + if (value instanceof Wrapper) { + value = ((Wrapper)value).unwrap(); + } + + if (type == ScriptRuntime.ClassClass || + type == ScriptRuntime.ObjectClass) { + return value; + } + else if (type == ScriptRuntime.StringClass) { + return value.toString(); + } + else { + reportConversionError(value, type); + } + break; + + case JSTYPE_JAVA_OBJECT: + case JSTYPE_JAVA_ARRAY: + if (value instanceof Wrapper) { + value = ((Wrapper)value).unwrap(); + } + if (type.isPrimitive()) { + if (type == Boolean.TYPE) { + reportConversionError(value, type); + } + return coerceToNumber(type, value); + } + else { + if (type == ScriptRuntime.StringClass) { + return value.toString(); + } + else { + if (type.isInstance(value)) { + return value; + } + else { + reportConversionError(value, type); + } + } + } + break; + + case JSTYPE_OBJECT: + if (type == ScriptRuntime.StringClass) { + return ScriptRuntime.toString(value); + } + else if (type.isPrimitive()) { + if (type == Boolean.TYPE) { + reportConversionError(value, type); + } + return coerceToNumber(type, value); + } + else if (type.isInstance(value)) { + return value; + } + else if (type == ScriptRuntime.DateClass + && value instanceof NativeDate) + { + double time = ((NativeDate)value).getJSTimeValue(); + // XXX: This will replace NaN by 0 + return new Date((long)time); + } + else if (type.isArray() && value instanceof NativeArray) { + // Make a new java array, and coerce the JS array components + // to the target (component) type. + NativeArray array = (NativeArray) value; + long length = array.getLength(); + Class arrayType = type.getComponentType(); + Object Result = Array.newInstance(arrayType, (int)length); + for (int i = 0 ; i < length ; ++i) { + try { + Array.set(Result, i, coerceType(arrayType, + array.get(i, array))); + } + catch (EvaluatorException ee) { + reportConversionError(value, type); + } + } + + return Result; + } + else if (value instanceof Wrapper) { + value = ((Wrapper)value).unwrap(); + if (type.isInstance(value)) + return value; + reportConversionError(value, type); + } + else if (type.isInterface() && value instanceof Callable) { + // Try to use function as implementation of Java interface. + // + // XXX: Curently only instances of ScriptableObject are + // supported since the resulting interface proxies should + // be reused next time conversion is made and generic + // Callable has no storage for it. Weak references can + // address it but for now use this restriction. + if (value instanceof ScriptableObject) { + ScriptableObject so = (ScriptableObject)value; + Object key = Kit.makeHashKeyFromPair( + COERCED_INTERFACE_KEY, type); + Object old = so.getAssociatedValue(key); + if (old != null) { + // Function was already wrapped + return old; + } + Context cx = Context.getContext(); + Object glue + = InterfaceAdapter.create(cx, type, (Callable)value); + // Store for later retrival + glue = so.associateValue(key, glue); + return glue; + } + reportConversionError(value, type); + } else { + reportConversionError(value, type); + } + break; + } + + return value; + } + + private static Object coerceToNumber(Class type, Object value) + { + Class valueClass = value.getClass(); + + // Character + if (type == Character.TYPE || type == ScriptRuntime.CharacterClass) { + if (valueClass == ScriptRuntime.CharacterClass) { + return value; + } + return new Character((char)toInteger(value, + ScriptRuntime.CharacterClass, + Character.MIN_VALUE, + Character.MAX_VALUE)); + } + + // Double, Float + if (type == ScriptRuntime.ObjectClass || + type == ScriptRuntime.DoubleClass || type == Double.TYPE) { + return valueClass == ScriptRuntime.DoubleClass + ? value + : new Double(toDouble(value)); + } + + if (type == ScriptRuntime.FloatClass || type == Float.TYPE) { + if (valueClass == ScriptRuntime.FloatClass) { + return value; + } + else { + double number = toDouble(value); + if (Double.isInfinite(number) || Double.isNaN(number) + || number == 0.0) { + return new Float((float)number); + } + else { + double absNumber = Math.abs(number); + if (absNumber < Float.MIN_VALUE) { + return new Float((number > 0.0) ? +0.0 : -0.0); + } + else if (absNumber > Float.MAX_VALUE) { + return new Float((number > 0.0) ? + Float.POSITIVE_INFINITY : + Float.NEGATIVE_INFINITY); + } + else { + return new Float((float)number); + } + } + } + } + + // Integer, Long, Short, Byte + if (type == ScriptRuntime.IntegerClass || type == Integer.TYPE) { + if (valueClass == ScriptRuntime.IntegerClass) { + return value; + } + else { + return new Integer((int)toInteger(value, + ScriptRuntime.IntegerClass, + Integer.MIN_VALUE, + Integer.MAX_VALUE)); + } + } + + if (type == ScriptRuntime.LongClass || type == Long.TYPE) { + if (valueClass == ScriptRuntime.LongClass) { + return value; + } else { + /* Long values cannot be expressed exactly in doubles. + * We thus use the largest and smallest double value that + * has a value expressible as a long value. We build these + * numerical values from their hexidecimal representations + * to avoid any problems caused by attempting to parse a + * decimal representation. + */ + final double max = Double.longBitsToDouble(0x43dfffffffffffffL); + final double min = Double.longBitsToDouble(0xc3e0000000000000L); + return new Long(toInteger(value, + ScriptRuntime.LongClass, + min, + max)); + } + } + + if (type == ScriptRuntime.ShortClass || type == Short.TYPE) { + if (valueClass == ScriptRuntime.ShortClass) { + return value; + } + else { + return new Short((short)toInteger(value, + ScriptRuntime.ShortClass, + Short.MIN_VALUE, + Short.MAX_VALUE)); + } + } + + if (type == ScriptRuntime.ByteClass || type == Byte.TYPE) { + if (valueClass == ScriptRuntime.ByteClass) { + return value; + } + else { + return new Byte((byte)toInteger(value, + ScriptRuntime.ByteClass, + Byte.MIN_VALUE, + Byte.MAX_VALUE)); + } + } + + return new Double(toDouble(value)); + } + + + private static double toDouble(Object value) + { + if (value instanceof Number) { + return ((Number)value).doubleValue(); + } + else if (value instanceof String) { + return ScriptRuntime.toNumber((String)value); + } + else if (value instanceof Scriptable) { + if (value instanceof Wrapper) { + // XXX: optimize tail-recursion? + return toDouble(((Wrapper)value).unwrap()); + } + else { + return ScriptRuntime.toNumber(value); + } + } + else { + Method meth; + try { + meth = value.getClass().getMethod("doubleValue", + (Class [])null); + } + catch (NoSuchMethodException e) { + meth = null; + } + catch (SecurityException e) { + meth = null; + } + if (meth != null) { + try { + return ((Number)meth.invoke(value, + (Object [])null)).doubleValue(); + } + catch (IllegalAccessException e) { + // XXX: ignore, or error message? + reportConversionError(value, Double.TYPE); + } + catch (InvocationTargetException e) { + // XXX: ignore, or error message? + reportConversionError(value, Double.TYPE); + } + } + return ScriptRuntime.toNumber(value.toString()); + } + } + + private static long toInteger(Object value, Class type, + double min, double max) + { + double d = toDouble(value); + + if (Double.isInfinite(d) || Double.isNaN(d)) { + // Convert to string first, for more readable message + reportConversionError(ScriptRuntime.toString(value), type); + } + + if (d > 0.0) { + d = Math.floor(d); + } + else { + d = Math.ceil(d); + } + + if (d < min || d > max) { + // Convert to string first, for more readable message + reportConversionError(ScriptRuntime.toString(value), type); + } + return (long)d; + } + + static void reportConversionError(Object value, Class type) + { + // It uses String.valueOf(value), not value.toString() since + // value can be null, bug 282447. + throw Context.reportRuntimeError2( + "msg.conversion.not.allowed", + String.valueOf(value), + JavaMembers.javaSignature(type)); + } + + private void writeObject(ObjectOutputStream out) + throws IOException + { + out.defaultWriteObject(); + + out.writeBoolean(isAdapter); + if (isAdapter) { + if (adapter_writeAdapterObject == null) { + throw new IOException(); + } + Object[] args = { javaObject, out }; + try { + adapter_writeAdapterObject.invoke(null, args); + } catch (Exception ex) { + throw new IOException(); + } + } else { + out.writeObject(javaObject); + } + + if (staticType != null) { + out.writeObject(staticType.getClass().getName()); + } else { + out.writeObject(null); + } + } + + private void readObject(ObjectInputStream in) + throws IOException, ClassNotFoundException + { + in.defaultReadObject(); + + isAdapter = in.readBoolean(); + if (isAdapter) { + if (adapter_readAdapterObject == null) + throw new ClassNotFoundException(); + Object[] args = { this, in }; + try { + javaObject = adapter_readAdapterObject.invoke(null, args); + } catch (Exception ex) { + throw new IOException(); + } + } else { + javaObject = in.readObject(); + } + + String className = (String)in.readObject(); + if (className != null) { + staticType = Class.forName(className); + } else { + staticType = null; + } + + initMembers(); + } + + /** + * The prototype of this object. + */ + protected Scriptable prototype; + + /** + * The parent scope of this object. + */ + protected Scriptable parent; + + protected transient Object javaObject; + + protected transient Class staticType; + protected transient JavaMembers members; + private transient Hashtable fieldAndMethods; + private transient boolean isAdapter; + + private static final Object COERCED_INTERFACE_KEY = new Object(); + private static Method adapter_writeAdapterObject; + private static Method adapter_readAdapterObject; + + static { + // Reflection in java is verbose + Class[] sig2 = new Class[2]; + Class cl = Kit.classOrNull("org.mozilla.javascript.JavaAdapter"); + if (cl != null) { + try { + sig2[0] = ScriptRuntime.ObjectClass; + sig2[1] = Kit.classOrNull("java.io.ObjectOutputStream"); + adapter_writeAdapterObject = cl.getMethod("writeAdapterObject", + sig2); + + sig2[0] = ScriptRuntime.ScriptableClass; + sig2[1] = Kit.classOrNull("java.io.ObjectInputStream"); + adapter_readAdapterObject = cl.getMethod("readAdapterObject", + sig2); + + } catch (Exception ex) { + adapter_writeAdapterObject = null; + adapter_readAdapterObject = null; + } + } + } + +} diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaPackage.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaPackage.java new file mode 100644 index 0000000..71f09f7 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaPackage.java @@ -0,0 +1,199 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Norris Boyd + * Frank Mitchell + * Mike Shaver + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript; + +/** + * This class reflects Java packages into the JavaScript environment. We + * lazily reflect classes and subpackages, and use a caching/sharing + * system to ensure that members reflected into one JavaPackage appear + * in all other references to the same package (as with Packages.java.lang + * and java.lang). + * + * @author Mike Shaver + * @see NativeJavaArray + * @see NativeJavaObject + * @see NativeJavaClass + */ + +public class NativeJavaPackage extends ScriptableObject +{ + static final long serialVersionUID = 7445054382212031523L; + + NativeJavaPackage(boolean internalUsage, + String packageName, ClassLoader classLoader) + { + this.packageName = packageName; + this.classLoader = classLoader; + } + + /** + * @deprecated NativeJavaPackage is an internal class, do not use + * it directly. + */ + public NativeJavaPackage(String packageName, ClassLoader classLoader) { + this(false, packageName, classLoader); + } + + /** + * @deprecated NativeJavaPackage is an internal class, do not use + * it directly. + */ + public NativeJavaPackage(String packageName) { + this(false, packageName, + Context.getCurrentContext().getApplicationClassLoader()); + } + + public String getClassName() { + return "JavaPackage"; + } + + public boolean has(String id, Scriptable start) { + return true; + } + + public boolean has(int index, Scriptable start) { + return false; + } + + public void put(String id, Scriptable start, Object value) { + // Can't add properties to Java packages. Sorry. + } + + public void put(int index, Scriptable start, Object value) { + throw Context.reportRuntimeError0("msg.pkg.int"); + } + + public Object get(String id, Scriptable start) { + return getPkgProperty(id, start, true); + } + + public Object get(int index, Scriptable start) { + return NOT_FOUND; + } + + // set up a name which is known to be a package so we don't + // need to look for a class by that name + void forcePackage(String name, Scriptable scope) + { + NativeJavaPackage pkg; + int end = name.indexOf('.'); + if (end == -1) { + end = name.length(); + } + + String id = name.substring(0, end); + Object cached = super.get(id, this); + if (cached != null && cached instanceof NativeJavaPackage) { + pkg = (NativeJavaPackage) cached; + } else { + String newPackage = packageName.length() == 0 + ? id + : packageName + "." + id; + pkg = new NativeJavaPackage(true, newPackage, classLoader); + ScriptRuntime.setObjectProtoAndParent(pkg, scope); + super.put(id, this, pkg); + } + if (end < name.length()) { + pkg.forcePackage(name.substring(end+1), scope); + } + } + + synchronized Object getPkgProperty(String name, Scriptable start, + boolean createPkg) + { + Object cached = super.get(name, start); + if (cached != NOT_FOUND) + return cached; + + String className = (packageName.length() == 0) + ? name : packageName + '.' + name; + Context cx = Context.getContext(); + ClassShutter shutter = cx.getClassShutter(); + Scriptable newValue = null; + if (shutter == null || shutter.visibleToScripts(className)) { + Class cl = null; + if (classLoader != null) { + cl = Kit.classOrNull(classLoader, className); + } else { + cl = Kit.classOrNull(className); + } + if (cl != null) { + newValue = new NativeJavaClass(getTopLevelScope(this), cl); + newValue.setPrototype(getPrototype()); + } + } + if (newValue == null && createPkg) { + NativeJavaPackage pkg; + pkg = new NativeJavaPackage(true, className, classLoader); + ScriptRuntime.setObjectProtoAndParent(pkg, getParentScope()); + newValue = pkg; + } + if (newValue != null) { + // Make it available for fast lookup and sharing of + // lazily-reflected constructors and static members. + super.put(name, start, newValue); + } + return newValue; + } + + public Object getDefaultValue(Class ignored) { + return toString(); + } + + public String toString() { + return "[JavaPackage " + packageName + "]"; + } + + public boolean equals(Object obj) { + if(obj instanceof NativeJavaPackage) { + NativeJavaPackage njp = (NativeJavaPackage)obj; + return packageName.equals(njp.packageName) && classLoader == njp.classLoader; + } + return false; + } + + public int hashCode() { + return packageName.hashCode() ^ (classLoader == null ? 0 : classLoader.hashCode()); + } + + private String packageName; + private ClassLoader classLoader; +} \ No newline at end of file diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaTopPackage.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaTopPackage.java new file mode 100644 index 0000000..b5c9b49 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaTopPackage.java @@ -0,0 +1,187 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Norris Boyd + * Frank Mitchell + * Mike Shaver + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript; + +/** + * This class reflects Java packages into the JavaScript environment. We + * lazily reflect classes and subpackages, and use a caching/sharing + * system to ensure that members reflected into one JavaPackage appear + * in all other references to the same package (as with Packages.java.lang + * and java.lang). + * + * @author Mike Shaver + * @see NativeJavaArray + * @see NativeJavaObject + * @see NativeJavaClass + */ + +public class NativeJavaTopPackage + extends NativeJavaPackage implements Function, IdFunctionCall +{ + static final long serialVersionUID = -1455787259477709999L; + + // we know these are packages so we can skip the class check + // note that this is ok even if the package isn't present. + private static final String commonPackages = "" + +"java.lang;" + +"java.lang.reflect;" + +"java.io;" + +"java.math;" + +"java.net;" + +"java.util;" + +"java.util.zip;" + +"java.text;" + +"java.text.resources;" + +"java.applet;" + +"javax.swing;" + ; + + NativeJavaTopPackage(ClassLoader loader) + { + super(true, "", loader); + } + + public Object call(Context cx, Scriptable scope, Scriptable thisObj, + Object[] args) + { + return construct(cx, scope, args); + } + + public Scriptable construct(Context cx, Scriptable scope, Object[] args) + { + ClassLoader loader = null; + if (args.length != 0) { + Object arg = args[0]; + if (arg instanceof Wrapper) { + arg = ((Wrapper)arg).unwrap(); + } + if (arg instanceof ClassLoader) { + loader = (ClassLoader)arg; + } + } + if (loader == null) { + Context.reportRuntimeError0("msg.not.classloader"); + return null; + } + return new NativeJavaPackage(true, "", loader); + } + + public static void init(Context cx, Scriptable scope, boolean sealed) + { + ClassLoader loader = cx.getApplicationClassLoader(); + final NativeJavaTopPackage top = new NativeJavaTopPackage(loader); + top.setPrototype(getObjectPrototype(scope)); + top.setParentScope(scope); + + String[] names = Kit.semicolonSplit(commonPackages); + for (int i = 0; i != names.length; ++i) { + top.forcePackage(names[i], scope); + } + + // getClass implementation + IdFunctionObject getClass = new IdFunctionObject(top, FTAG, Id_getClass, + "getClass", 1, scope); + + // We want to get a real alias, and not a distinct JavaPackage + // with the same packageName, so that we share classes and top + // that are underneath. + String[] topNames = { "java", "javax", "org", "com", "edu", "net" }; + NativeJavaPackage[] topPackages = new NativeJavaPackage[topNames.length]; + for (int i=0; i < topNames.length; i++) { + topPackages[i] = (NativeJavaPackage)top.get(topNames[i], top); + } + + // It's safe to downcast here since initStandardObjects takes + // a ScriptableObject. + ScriptableObject global = (ScriptableObject) scope; + + if (sealed) { + getClass.sealObject(); + } + getClass.exportAsScopeProperty(); + global.defineProperty("Packages", top, ScriptableObject.DONTENUM); + for (int i=0; i < topNames.length; i++) { + global.defineProperty(topNames[i], topPackages[i], + ScriptableObject.DONTENUM); + } + } + + public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, + Scriptable thisObj, Object[] args) + { + if (f.hasTag(FTAG)) { + if (f.methodId() == Id_getClass) { + return js_getClass(cx, scope, args); + } + } + throw f.unknown(); + } + + private Scriptable js_getClass(Context cx, Scriptable scope, Object[] args) + { + if (args.length > 0 && args[0] instanceof Wrapper) { + Scriptable result = this; + Class cl = ((Wrapper) args[0]).unwrap().getClass(); + // Evaluate the class name by getting successive properties of + // the string to find the appropriate NativeJavaClass object + String name = cl.getName(); + int offset = 0; + for (;;) { + int index = name.indexOf('.', offset); + String propName = index == -1 + ? name.substring(offset) + : name.substring(offset, index); + Object prop = result.get(propName, result); + if (!(prop instanceof Scriptable)) + break; // fall through to error + result = (Scriptable) prop; + if (index == -1) + return result; + offset = index+1; + } + } + throw Context.reportRuntimeError0("msg.not.java.obj"); + } + + private static final Object FTAG = new Object(); + private static final int Id_getClass = 1; +} + diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeMath.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeMath.java new file mode 100644 index 0000000..36b66b4 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeMath.java @@ -0,0 +1,399 @@ +/* -*- Mode: java; tab-width: 4; indent-tabs-mode: 1; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Norris Boyd + * Igor Bukanov + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript; + +/** + * This class implements the Math native object. + * See ECMA 15.8. + * @author Norris Boyd + */ + +final class NativeMath extends IdScriptableObject +{ + static final long serialVersionUID = -8838847185801131569L; + + private static final Object MATH_TAG = new Object(); + + static void init(Scriptable scope, boolean sealed) + { + NativeMath obj = new NativeMath(); + obj.activatePrototypeMap(MAX_ID); + obj.setPrototype(getObjectPrototype(scope)); + obj.setParentScope(scope); + if (sealed) { obj.sealObject(); } + ScriptableObject.defineProperty(scope, "Math", obj, + ScriptableObject.DONTENUM); + } + + private NativeMath() + { + } + + public String getClassName() { return "Math"; } + + protected void initPrototypeId(int id) + { + if (id <= LAST_METHOD_ID) { + String name; + int arity; + switch (id) { + case Id_toSource: arity = 0; name = "toSource"; break; + case Id_abs: arity = 1; name = "abs"; break; + case Id_acos: arity = 1; name = "acos"; break; + case Id_asin: arity = 1; name = "asin"; break; + case Id_atan: arity = 1; name = "atan"; break; + case Id_atan2: arity = 2; name = "atan2"; break; + case Id_ceil: arity = 1; name = "ceil"; break; + case Id_cos: arity = 1; name = "cos"; break; + case Id_exp: arity = 1; name = "exp"; break; + case Id_floor: arity = 1; name = "floor"; break; + case Id_log: arity = 1; name = "log"; break; + case Id_max: arity = 2; name = "max"; break; + case Id_min: arity = 2; name = "min"; break; + case Id_pow: arity = 2; name = "pow"; break; + case Id_random: arity = 0; name = "random"; break; + case Id_round: arity = 1; name = "round"; break; + case Id_sin: arity = 1; name = "sin"; break; + case Id_sqrt: arity = 1; name = "sqrt"; break; + case Id_tan: arity = 1; name = "tan"; break; + default: throw new IllegalStateException(String.valueOf(id)); + } + initPrototypeMethod(MATH_TAG, id, name, arity); + } else { + String name; + double x; + switch (id) { + case Id_E: x = Math.E; name = "E"; break; + case Id_PI: x = Math.PI; name = "PI"; break; + case Id_LN10: x = 2.302585092994046; name = "LN10"; break; + case Id_LN2: x = 0.6931471805599453; name = "LN2"; break; + case Id_LOG2E: x = 1.4426950408889634; name = "LOG2E"; break; + case Id_LOG10E: x = 0.4342944819032518; name = "LOG10E"; break; + case Id_SQRT1_2: x = 0.7071067811865476; name = "SQRT1_2"; break; + case Id_SQRT2: x = 1.4142135623730951; name = "SQRT2"; break; + default: throw new IllegalStateException(String.valueOf(id)); + } + initPrototypeValue(id, name, ScriptRuntime.wrapNumber(x), + DONTENUM | READONLY | PERMANENT); + } + } + + public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, + Scriptable thisObj, Object[] args) + { + if (!f.hasTag(MATH_TAG)) { + return super.execIdCall(f, cx, scope, thisObj, args); + } + double x; + int methodId = f.methodId(); + switch (methodId) { + case Id_toSource: + return "Math"; + + case Id_abs: + x = ScriptRuntime.toNumber(args, 0); + // abs(-0.0) should be 0.0, but -0.0 < 0.0 == false + x = (x == 0.0) ? 0.0 : (x < 0.0) ? -x : x; + break; + + case Id_acos: + case Id_asin: + x = ScriptRuntime.toNumber(args, 0); + if (x == x && -1.0 <= x && x <= 1.0) { + x = (methodId == Id_acos) ? Math.acos(x) : Math.asin(x); + } else { + x = Double.NaN; + } + break; + + case Id_atan: + x = ScriptRuntime.toNumber(args, 0); + x = Math.atan(x); + break; + + case Id_atan2: + x = ScriptRuntime.toNumber(args, 0); + x = Math.atan2(x, ScriptRuntime.toNumber(args, 1)); + break; + + case Id_ceil: + x = ScriptRuntime.toNumber(args, 0); + x = Math.ceil(x); + break; + + case Id_cos: + x = ScriptRuntime.toNumber(args, 0); + x = (x == Double.POSITIVE_INFINITY + || x == Double.NEGATIVE_INFINITY) + ? Double.NaN : Math.cos(x); + break; + + case Id_exp: + x = ScriptRuntime.toNumber(args, 0); + x = (x == Double.POSITIVE_INFINITY) ? x + : (x == Double.NEGATIVE_INFINITY) ? 0.0 + : Math.exp(x); + break; + + case Id_floor: + x = ScriptRuntime.toNumber(args, 0); + x = Math.floor(x); + break; + + case Id_log: + x = ScriptRuntime.toNumber(args, 0); + // Java's log(<0) = -Infinity; we need NaN + x = (x < 0) ? Double.NaN : Math.log(x); + break; + + case Id_max: + case Id_min: + x = (methodId == Id_max) + ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY; + for (int i = 0; i != args.length; ++i) { + double d = ScriptRuntime.toNumber(args[i]); + if (d != d) { + x = d; // NaN + break; + } + if (methodId == Id_max) { + // if (x < d) x = d; does not work due to -0.0 >= +0.0 + x = Math.max(x, d); + } else { + x = Math.min(x, d); + } + } + break; + + case Id_pow: + x = ScriptRuntime.toNumber(args, 0); + x = js_pow(x, ScriptRuntime.toNumber(args, 1)); + break; + + case Id_random: + x = Math.random(); + break; + + case Id_round: + x = ScriptRuntime.toNumber(args, 0); + if (x == x && x != Double.POSITIVE_INFINITY + && x != Double.NEGATIVE_INFINITY) + { + // Round only finite x + long l = Math.round(x); + if (l != 0) { + x = l; + } else { + // We must propagate the sign of d into the result + if (x < 0.0) { + x = ScriptRuntime.negativeZero; + } else if (x != 0.0) { + x = 0.0; + } + } + } + break; + + case Id_sin: + x = ScriptRuntime.toNumber(args, 0); + x = (x == Double.POSITIVE_INFINITY + || x == Double.NEGATIVE_INFINITY) + ? Double.NaN : Math.sin(x); + break; + + case Id_sqrt: + x = ScriptRuntime.toNumber(args, 0); + x = Math.sqrt(x); + break; + + case Id_tan: + x = ScriptRuntime.toNumber(args, 0); + x = Math.tan(x); + break; + + default: throw new IllegalStateException(String.valueOf(methodId)); + } + return ScriptRuntime.wrapNumber(x); + } + + // See Ecma 15.8.2.13 + private double js_pow(double x, double y) { + double result; + if (y != y) { + // y is NaN, result is always NaN + result = y; + } else if (y == 0) { + // Java's pow(NaN, 0) = NaN; we need 1 + result = 1.0; + } else if (x == 0) { + // Many dirrerences from Java's Math.pow + if (1 / x > 0) { + result = (y > 0) ? 0 : Double.POSITIVE_INFINITY; + } else { + // x is -0, need to check if y is an odd integer + long y_long = (long)y; + if (y_long == y && (y_long & 0x1) != 0) { + result = (y > 0) ? -0.0 : Double.NEGATIVE_INFINITY; + } else { + result = (y > 0) ? 0.0 : Double.POSITIVE_INFINITY; + } + } + } else { + result = Math.pow(x, y); + if (result != result) { + // Check for broken Java implementations that gives NaN + // when they should return something else + if (y == Double.POSITIVE_INFINITY) { + if (x < -1.0 || 1.0 < x) { + result = Double.POSITIVE_INFINITY; + } else if (-1.0 < x && x < 1.0) { + result = 0; + } + } else if (y == Double.NEGATIVE_INFINITY) { + if (x < -1.0 || 1.0 < x) { + result = 0; + } else if (-1.0 < x && x < 1.0) { + result = Double.POSITIVE_INFINITY; + } + } else if (x == Double.POSITIVE_INFINITY) { + result = (y > 0) ? Double.POSITIVE_INFINITY : 0.0; + } else if (x == Double.NEGATIVE_INFINITY) { + long y_long = (long)y; + if (y_long == y && (y_long & 0x1) != 0) { + // y is odd integer + result = (y > 0) ? Double.NEGATIVE_INFINITY : -0.0; + } else { + result = (y > 0) ? Double.POSITIVE_INFINITY : 0.0; + } + } + } + } + return result; + } + +// #string_id_map# + + protected int findPrototypeId(String s) + { + int id; +// #generated# Last update: 2004-03-17 13:51:32 CET + L0: { id = 0; String X = null; int c; + L: switch (s.length()) { + case 1: if (s.charAt(0)=='E') {id=Id_E; break L0;} break L; + case 2: if (s.charAt(0)=='P' && s.charAt(1)=='I') {id=Id_PI; break L0;} break L; + case 3: switch (s.charAt(0)) { + case 'L': if (s.charAt(2)=='2' && s.charAt(1)=='N') {id=Id_LN2; break L0;} break L; + case 'a': if (s.charAt(2)=='s' && s.charAt(1)=='b') {id=Id_abs; break L0;} break L; + case 'c': if (s.charAt(2)=='s' && s.charAt(1)=='o') {id=Id_cos; break L0;} break L; + case 'e': if (s.charAt(2)=='p' && s.charAt(1)=='x') {id=Id_exp; break L0;} break L; + case 'l': if (s.charAt(2)=='g' && s.charAt(1)=='o') {id=Id_log; break L0;} break L; + case 'm': c=s.charAt(2); + if (c=='n') { if (s.charAt(1)=='i') {id=Id_min; break L0;} } + else if (c=='x') { if (s.charAt(1)=='a') {id=Id_max; break L0;} } + break L; + case 'p': if (s.charAt(2)=='w' && s.charAt(1)=='o') {id=Id_pow; break L0;} break L; + case 's': if (s.charAt(2)=='n' && s.charAt(1)=='i') {id=Id_sin; break L0;} break L; + case 't': if (s.charAt(2)=='n' && s.charAt(1)=='a') {id=Id_tan; break L0;} break L; + } break L; + case 4: switch (s.charAt(1)) { + case 'N': X="LN10";id=Id_LN10; break L; + case 'c': X="acos";id=Id_acos; break L; + case 'e': X="ceil";id=Id_ceil; break L; + case 'q': X="sqrt";id=Id_sqrt; break L; + case 's': X="asin";id=Id_asin; break L; + case 't': X="atan";id=Id_atan; break L; + } break L; + case 5: switch (s.charAt(0)) { + case 'L': X="LOG2E";id=Id_LOG2E; break L; + case 'S': X="SQRT2";id=Id_SQRT2; break L; + case 'a': X="atan2";id=Id_atan2; break L; + case 'f': X="floor";id=Id_floor; break L; + case 'r': X="round";id=Id_round; break L; + } break L; + case 6: c=s.charAt(0); + if (c=='L') { X="LOG10E";id=Id_LOG10E; } + else if (c=='r') { X="random";id=Id_random; } + break L; + case 7: X="SQRT1_2";id=Id_SQRT1_2; break L; + case 8: X="toSource";id=Id_toSource; break L; + } + if (X!=null && X!=s && !X.equals(s)) id = 0; + } +// #/generated# + return id; + } + + private static final int + Id_toSource = 1, + Id_abs = 2, + Id_acos = 3, + Id_asin = 4, + Id_atan = 5, + Id_atan2 = 6, + Id_ceil = 7, + Id_cos = 8, + Id_exp = 9, + Id_floor = 10, + Id_log = 11, + Id_max = 12, + Id_min = 13, + Id_pow = 14, + Id_random = 15, + Id_round = 16, + Id_sin = 17, + Id_sqrt = 18, + Id_tan = 19, + + LAST_METHOD_ID = 19; + + private static final int + Id_E = LAST_METHOD_ID + 1, + Id_PI = LAST_METHOD_ID + 2, + Id_LN10 = LAST_METHOD_ID + 3, + Id_LN2 = LAST_METHOD_ID + 4, + Id_LOG2E = LAST_METHOD_ID + 5, + Id_LOG10E = LAST_METHOD_ID + 6, + Id_SQRT1_2 = LAST_METHOD_ID + 7, + Id_SQRT2 = LAST_METHOD_ID + 8, + + MAX_ID = LAST_METHOD_ID + 8; + +// #/string_id_map# +} diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeNumber.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeNumber.java new file mode 100644 index 0000000..8fc9fb0 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeNumber.java @@ -0,0 +1,244 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Norris Boyd + * Igor Bukanov + * Mike McCabe + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript; + +/** + * This class implements the Number native object. + * + * See ECMA 15.7. + * + * @author Norris Boyd + */ +final class NativeNumber extends IdScriptableObject +{ + static final long serialVersionUID = 3504516769741512101L; + + private static final Object NUMBER_TAG = new Object(); + + private static final int MAX_PRECISION = 100; + + static void init(Scriptable scope, boolean sealed) + { + NativeNumber obj = new NativeNumber(0.0); + obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed); + } + + private NativeNumber(double number) + { + doubleValue = number; + } + + public String getClassName() + { + return "Number"; + } + + protected void fillConstructorProperties(IdFunctionObject ctor) + { + final int attr = ScriptableObject.DONTENUM | + ScriptableObject.PERMANENT | + ScriptableObject.READONLY; + + ctor.defineProperty("NaN", ScriptRuntime.NaNobj, attr); + ctor.defineProperty("POSITIVE_INFINITY", + ScriptRuntime.wrapNumber(Double.POSITIVE_INFINITY), + attr); + ctor.defineProperty("NEGATIVE_INFINITY", + ScriptRuntime.wrapNumber(Double.NEGATIVE_INFINITY), + attr); + ctor.defineProperty("MAX_VALUE", + ScriptRuntime.wrapNumber(Double.MAX_VALUE), + attr); + ctor.defineProperty("MIN_VALUE", + ScriptRuntime.wrapNumber(Double.MIN_VALUE), + attr); + + super.fillConstructorProperties(ctor); + } + + protected void initPrototypeId(int id) + { + String s; + int arity; + switch (id) { + case Id_constructor: arity=1; s="constructor"; break; + case Id_toString: arity=1; s="toString"; break; + case Id_toLocaleString: arity=1; s="toLocaleString"; break; + case Id_toSource: arity=0; s="toSource"; break; + case Id_valueOf: arity=0; s="valueOf"; break; + case Id_toFixed: arity=1; s="toFixed"; break; + case Id_toExponential: arity=1; s="toExponential"; break; + case Id_toPrecision: arity=1; s="toPrecision"; break; + default: throw new IllegalArgumentException(String.valueOf(id)); + } + initPrototypeMethod(NUMBER_TAG, id, s, arity); + } + + public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, + Scriptable thisObj, Object[] args) + { + if (!f.hasTag(NUMBER_TAG)) { + return super.execIdCall(f, cx, scope, thisObj, args); + } + int id = f.methodId(); + if (id == Id_constructor) { + double val = (args.length >= 1) + ? ScriptRuntime.toNumber(args[0]) : 0.0; + if (thisObj == null) { + // new Number(val) creates a new Number object. + return new NativeNumber(val); + } + // Number(val) converts val to a number value. + return ScriptRuntime.wrapNumber(val); + } + + // The rest of Number.prototype methods require thisObj to be Number + + if (!(thisObj instanceof NativeNumber)) + throw incompatibleCallError(f); + double value = ((NativeNumber)thisObj).doubleValue; + + switch (id) { + + case Id_toString: + case Id_toLocaleString: + { + // toLocaleString is just an alias for toString for now + int base = (args.length == 0) + ? 10 : ScriptRuntime.toInt32(args[0]); + return ScriptRuntime.numberToString(value, base); + } + + case Id_toSource: + return "(new Number("+ScriptRuntime.toString(value)+"))"; + + case Id_valueOf: + return ScriptRuntime.wrapNumber(value); + + case Id_toFixed: + return num_to(value, args, DToA.DTOSTR_FIXED, + DToA.DTOSTR_FIXED, -20, 0); + + case Id_toExponential: + return num_to(value, args, DToA.DTOSTR_STANDARD_EXPONENTIAL, + DToA.DTOSTR_EXPONENTIAL, 0, 1); + + case Id_toPrecision: + return num_to(value, args, DToA.DTOSTR_STANDARD, + DToA.DTOSTR_PRECISION, 1, 0); + + default: throw new IllegalArgumentException(String.valueOf(id)); + } + } + + public String toString() { + return ScriptRuntime.numberToString(doubleValue, 10); + } + + private static String num_to(double val, + Object[] args, + int zeroArgMode, int oneArgMode, + int precisionMin, int precisionOffset) + { + int precision; + if (args.length == 0) { + precision = 0; + oneArgMode = zeroArgMode; + } else { + /* We allow a larger range of precision than + ECMA requires; this is permitted by ECMA. */ + precision = ScriptRuntime.toInt32(args[0]); + if (precision < precisionMin || precision > MAX_PRECISION) { + String msg = ScriptRuntime.getMessage1( + "msg.bad.precision", ScriptRuntime.toString(args[0])); + throw ScriptRuntime.constructError("RangeError", msg); + } + } + StringBuffer sb = new StringBuffer(); + DToA.JS_dtostr(sb, oneArgMode, precision + precisionOffset, val); + return sb.toString(); + } + +// #string_id_map# + + protected int findPrototypeId(String s) + { + int id; +// #generated# Last update: 2007-05-09 08:15:50 EDT + L0: { id = 0; String X = null; int c; + L: switch (s.length()) { + case 7: c=s.charAt(0); + if (c=='t') { X="toFixed";id=Id_toFixed; } + else if (c=='v') { X="valueOf";id=Id_valueOf; } + break L; + case 8: c=s.charAt(3); + if (c=='o') { X="toSource";id=Id_toSource; } + else if (c=='t') { X="toString";id=Id_toString; } + break L; + case 11: c=s.charAt(0); + if (c=='c') { X="constructor";id=Id_constructor; } + else if (c=='t') { X="toPrecision";id=Id_toPrecision; } + break L; + case 13: X="toExponential";id=Id_toExponential; break L; + case 14: X="toLocaleString";id=Id_toLocaleString; break L; + } + if (X!=null && X!=s && !X.equals(s)) id = 0; + break L0; + } +// #/generated# + return id; + } + + private static final int + Id_constructor = 1, + Id_toString = 2, + Id_toLocaleString = 3, + Id_toSource = 4, + Id_valueOf = 5, + Id_toFixed = 6, + Id_toExponential = 7, + Id_toPrecision = 8, + MAX_PROTOTYPE_ID = 8; + +// #/string_id_map# + + private double doubleValue; +} diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeObject.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeObject.java new file mode 100644 index 0000000..19aff63 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeObject.java @@ -0,0 +1,316 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Norris Boyd + * Igor Bukanov + * Bob Jervis + * Mike McCabe + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript; + +/** + * This class implements the Object native object. + * See ECMA 15.2. + * @author Norris Boyd + */ +public class NativeObject extends IdScriptableObject +{ + static final long serialVersionUID = -6345305608474346996L; + + private static final Object OBJECT_TAG = new Object(); + + static void init(Scriptable scope, boolean sealed) + { + NativeObject obj = new NativeObject(); + obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed); + } + + public String getClassName() + { + return "Object"; + } + + public String toString() + { + return ScriptRuntime.defaultObjectToString(this); + } + + protected void initPrototypeId(int id) + { + String s; + int arity; + switch (id) { + case Id_constructor: arity=1; s="constructor"; break; + case Id_toString: arity=0; s="toString"; break; + case Id_toLocaleString: arity=0; s="toLocaleString"; break; + case Id_valueOf: arity=0; s="valueOf"; break; + case Id_hasOwnProperty: arity=1; s="hasOwnProperty"; break; + case Id_propertyIsEnumerable: + arity=1; s="propertyIsEnumerable"; break; + case Id_isPrototypeOf: arity=1; s="isPrototypeOf"; break; + case Id_toSource: arity=0; s="toSource"; break; + case Id___defineGetter__: + arity=2; s="__defineGetter__"; break; + case Id___defineSetter__: + arity=2; s="__defineSetter__"; break; + case Id___lookupGetter__: + arity=1; s="__lookupGetter__"; break; + case Id___lookupSetter__: + arity=1; s="__lookupSetter__"; break; + default: throw new IllegalArgumentException(String.valueOf(id)); + } + initPrototypeMethod(OBJECT_TAG, id, s, arity); + } + + public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, + Scriptable thisObj, Object[] args) + { + if (!f.hasTag(OBJECT_TAG)) { + return super.execIdCall(f, cx, scope, thisObj, args); + } + int id = f.methodId(); + switch (id) { + case Id_constructor: { + if (thisObj != null) { + // BaseFunction.construct will set up parent, proto + return f.construct(cx, scope, args); + } + if (args.length == 0 || args[0] == null + || args[0] == Undefined.instance) + { + return new NativeObject(); + } + return ScriptRuntime.toObject(cx, scope, args[0]); + } + + case Id_toLocaleString: // For now just alias toString + case Id_toString: { + if (cx.hasFeature(Context.FEATURE_TO_STRING_AS_SOURCE)) { + String s = ScriptRuntime.defaultObjectToSource(cx, scope, + thisObj, args); + int L = s.length(); + if (L != 0 && s.charAt(0) == '(' && s.charAt(L - 1) == ')') { + // Strip () that surrounds toSource + s = s.substring(1, L - 1); + } + return s; + } + return ScriptRuntime.defaultObjectToString(thisObj); + } + + case Id_valueOf: + return thisObj; + + case Id_hasOwnProperty: { + boolean result; + if (args.length == 0) { + result = false; + } else { + String s = ScriptRuntime.toStringIdOrIndex(cx, args[0]); + if (s == null) { + int index = ScriptRuntime.lastIndexResult(cx); + result = thisObj.has(index, thisObj); + } else { + result = thisObj.has(s, thisObj); + } + } + return ScriptRuntime.wrapBoolean(result); + } + + case Id_propertyIsEnumerable: { + boolean result; + if (args.length == 0) { + result = false; + } else { + String s = ScriptRuntime.toStringIdOrIndex(cx, args[0]); + if (s == null) { + int index = ScriptRuntime.lastIndexResult(cx); + result = thisObj.has(index, thisObj); + if (result && thisObj instanceof ScriptableObject) { + ScriptableObject so = (ScriptableObject)thisObj; + int attrs = so.getAttributes(index); + result = ((attrs & ScriptableObject.DONTENUM) == 0); + } + } else { + result = thisObj.has(s, thisObj); + if (result && thisObj instanceof ScriptableObject) { + ScriptableObject so = (ScriptableObject)thisObj; + int attrs = so.getAttributes(s); + result = ((attrs & ScriptableObject.DONTENUM) == 0); + } + } + } + return ScriptRuntime.wrapBoolean(result); + } + + case Id_isPrototypeOf: { + boolean result = false; + if (args.length != 0 && args[0] instanceof Scriptable) { + Scriptable v = (Scriptable) args[0]; + do { + v = v.getPrototype(); + if (v == thisObj) { + result = true; + break; + } + } while (v != null); + } + return ScriptRuntime.wrapBoolean(result); + } + + case Id_toSource: + return ScriptRuntime.defaultObjectToSource(cx, scope, thisObj, + args); + case Id___defineGetter__: + case Id___defineSetter__: + { + if (args.length < 2 || !(args[1] instanceof Callable)) { + Object badArg = (args.length >= 2 ? args[1] + : Undefined.instance); + throw ScriptRuntime.notFunctionError(badArg); + } + if (!(thisObj instanceof ScriptableObject)) { + throw Context.reportRuntimeError2( + "msg.extend.scriptable", + thisObj.getClass().getName(), + String.valueOf(args[0])); + } + ScriptableObject so = (ScriptableObject)thisObj; + String name = ScriptRuntime.toStringIdOrIndex(cx, args[0]); + int index = (name != null ? 0 + : ScriptRuntime.lastIndexResult(cx)); + Callable getterOrSetter = (Callable)args[1]; + boolean isSetter = (id == Id___defineSetter__); + so.setGetterOrSetter(name, index, getterOrSetter, isSetter); + if (so instanceof NativeArray) + ((NativeArray)so).setDenseOnly(false); + } + return Undefined.instance; + + case Id___lookupGetter__: + case Id___lookupSetter__: + { + if (args.length < 1 || + !(thisObj instanceof ScriptableObject)) + return Undefined.instance; + + ScriptableObject so = (ScriptableObject)thisObj; + String name = ScriptRuntime.toStringIdOrIndex(cx, args[0]); + int index = (name != null ? 0 + : ScriptRuntime.lastIndexResult(cx)); + boolean isSetter = (id == Id___lookupSetter__); + Object gs; + for (;;) { + gs = so.getGetterOrSetter(name, index, isSetter); + if (gs != null) + break; + // If there is no getter or setter for the object itself, + // how about the prototype? + Scriptable v = so.getPrototype(); + if (v == null) + break; + if (v instanceof ScriptableObject) + so = (ScriptableObject)v; + else + break; + } + if (gs != null) + return gs; + } + return Undefined.instance; + + default: + throw new IllegalArgumentException(String.valueOf(id)); + } + } + +// #string_id_map# + + protected int findPrototypeId(String s) + { + int id; +// #generated# Last update: 2007-05-09 08:15:55 EDT + L0: { id = 0; String X = null; int c; + L: switch (s.length()) { + case 7: X="valueOf";id=Id_valueOf; break L; + case 8: c=s.charAt(3); + if (c=='o') { X="toSource";id=Id_toSource; } + else if (c=='t') { X="toString";id=Id_toString; } + break L; + case 11: X="constructor";id=Id_constructor; break L; + case 13: X="isPrototypeOf";id=Id_isPrototypeOf; break L; + case 14: c=s.charAt(0); + if (c=='h') { X="hasOwnProperty";id=Id_hasOwnProperty; } + else if (c=='t') { X="toLocaleString";id=Id_toLocaleString; } + break L; + case 16: c=s.charAt(2); + if (c=='d') { + c=s.charAt(8); + if (c=='G') { X="__defineGetter__";id=Id___defineGetter__; } + else if (c=='S') { X="__defineSetter__";id=Id___defineSetter__; } + } + else if (c=='l') { + c=s.charAt(8); + if (c=='G') { X="__lookupGetter__";id=Id___lookupGetter__; } + else if (c=='S') { X="__lookupSetter__";id=Id___lookupSetter__; } + } + break L; + case 20: X="propertyIsEnumerable";id=Id_propertyIsEnumerable; break L; + } + if (X!=null && X!=s && !X.equals(s)) id = 0; + break L0; + } +// #/generated# + return id; + } + + private static final int + Id_constructor = 1, + Id_toString = 2, + Id_toLocaleString = 3, + Id_valueOf = 4, + Id_hasOwnProperty = 5, + Id_propertyIsEnumerable = 6, + Id_isPrototypeOf = 7, + Id_toSource = 8, + Id___defineGetter__ = 9, + Id___defineSetter__ = 10, + Id___lookupGetter__ = 11, + Id___lookupSetter__ = 12, + MAX_PROTOTYPE_ID = 12; + +// #/string_id_map# +} diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeScript.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeScript.java new file mode 100644 index 0000000..7b5191e --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeScript.java @@ -0,0 +1,221 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Norris Boyd + * Igor Bukanov + * Roger Lawrence + * Mike McCabe + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript; + +/** + * The JavaScript Script object. + * + * Note that the C version of the engine uses XDR as the format used + * by freeze and thaw. Since this depends on the internal format of + * structures in the C runtime, we cannot duplicate it. + * + * Since we cannot replace 'this' as a result of the compile method, + * will forward requests to execute to the nonnull 'script' field. + * + * @since 1.3 + * @author Norris Boyd + */ + +class NativeScript extends BaseFunction +{ + static final long serialVersionUID = -6795101161980121700L; + + private static final Object SCRIPT_TAG = new Object(); + + static void init(Scriptable scope, boolean sealed) + { + NativeScript obj = new NativeScript(null); + obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed); + } + + private NativeScript(Script script) + { + this.script = script; + } + + /** + * Returns the name of this JavaScript class, "Script". + */ + public String getClassName() + { + return "Script"; + } + + public Object call(Context cx, Scriptable scope, Scriptable thisObj, + Object[] args) + { + if (script != null) { + return script.exec(cx, scope); + } + return Undefined.instance; + } + + public Scriptable construct(Context cx, Scriptable scope, Object[] args) + { + throw Context.reportRuntimeError0("msg.script.is.not.constructor"); + } + + public int getLength() + { + return 0; + } + + public int getArity() + { + return 0; + } + + String decompile(int indent, int flags) + { + if (script instanceof NativeFunction) { + return ((NativeFunction)script).decompile(indent, flags); + } + return super.decompile(indent, flags); + } + + protected void initPrototypeId(int id) + { + String s; + int arity; + switch (id) { + case Id_constructor: arity=1; s="constructor"; break; + case Id_toString: arity=0; s="toString"; break; + case Id_exec: arity=0; s="exec"; break; + case Id_compile: arity=1; s="compile"; break; + default: throw new IllegalArgumentException(String.valueOf(id)); + } + initPrototypeMethod(SCRIPT_TAG, id, s, arity); + } + + public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, + Scriptable thisObj, Object[] args) + { + if (!f.hasTag(SCRIPT_TAG)) { + return super.execIdCall(f, cx, scope, thisObj, args); + } + int id = f.methodId(); + switch (id) { + case Id_constructor: { + String source = (args.length == 0) + ? "" + : ScriptRuntime.toString(args[0]); + Script script = compile(cx, source); + NativeScript nscript = new NativeScript(script); + ScriptRuntime.setObjectProtoAndParent(nscript, scope); + return nscript; + } + + case Id_toString: { + NativeScript real = realThis(thisObj, f); + Script realScript = real.script; + if (realScript == null) { return ""; } + return cx.decompileScript(realScript, 0); + } + + case Id_exec: { + throw Context.reportRuntimeError1( + "msg.cant.call.indirect", "exec"); + } + + case Id_compile: { + NativeScript real = realThis(thisObj, f); + String source = ScriptRuntime.toString(args, 0); + real.script = compile(cx, source); + return real; + } + } + throw new IllegalArgumentException(String.valueOf(id)); + } + + private static NativeScript realThis(Scriptable thisObj, IdFunctionObject f) + { + if (!(thisObj instanceof NativeScript)) + throw incompatibleCallError(f); + return (NativeScript)thisObj; + } + + private static Script compile(Context cx, String source) + { + int[] linep = { 0 }; + String filename = Context.getSourcePositionFromStack(linep); + if (filename == null) { + filename = "' + // which breaks if this code is embedded inside an HTML document. + // Since this is not the right way to do this, let's fix the code by + // transforming all " 0 && i < length - 2 && + ((JavaScriptToken) tokens.get(i - 1)).getType() == Token.NAME && + ((JavaScriptToken) tokens.get(i + 1)).getType() == Token.STRING && + ((JavaScriptToken) tokens.get(i + 2)).getType() == Token.RB) { + token = (JavaScriptToken) tokens.get(i + 1); + tv = token.getValue(); + tv = tv.substring(1, tv.length() - 1); + if (isValidIdentifier(tv)) { + tokens.set(i, new JavaScriptToken(Token.DOT, ".")); + tokens.set(i + 1, new JavaScriptToken(Token.NAME, tv)); + tokens.remove(i + 2); + i = i + 2; + length = length - 1; + } + } + } + } + + /* + * Transforms 'foo': ... into foo: ... whenever possible, saving 2 bytes. + */ + private static void optimizeObjLitMemberDecl(ArrayList tokens) { + + String tv; + int i, length; + JavaScriptToken token; + + for (i = 0, length = tokens.size(); i < length; i++) { + if (((JavaScriptToken) tokens.get(i)).getType() == Token.OBJECTLIT && + i > 0 && ((JavaScriptToken) tokens.get(i - 1)).getType() == Token.STRING) { + token = (JavaScriptToken) tokens.get(i - 1); + tv = token.getValue(); + tv = tv.substring(1, tv.length() - 1); + if (isValidIdentifier(tv)) { + tokens.set(i - 1, new JavaScriptToken(Token.NAME, tv)); + } + } + } + } + + private ErrorReporter logger; + + private boolean munge; + private boolean verbose; + + private static final int BUILDING_SYMBOL_TREE = 1; + private static final int CHECKING_SYMBOL_TREE = 2; + + private int mode; + private int offset; + private int braceNesting; + private ArrayList tokens; + private Stack scopes = new Stack(); + private ScriptOrFnScope globalScope = new ScriptOrFnScope(-1, null); + private Hashtable indexedScopes = new Hashtable(); + + public JavaScriptCompressor(Reader in, ErrorReporter reporter) + throws IOException, EvaluatorException { + + this.logger = reporter; + this.tokens = parse(in, reporter); + } + + public void compress(Writer out, int linebreak, boolean munge, boolean verbose, + boolean preserveAllSemiColons, boolean disableOptimizations) + throws IOException { + + this.munge = munge; + this.verbose = verbose; + + processStringLiterals(this.tokens, !disableOptimizations); + + if (!disableOptimizations) { + optimizeObjectMemberAccess(this.tokens); + optimizeObjLitMemberDecl(this.tokens); + } + + buildSymbolTree(); + // DO NOT TOUCH this.tokens BETWEEN THESE TWO PHASES (BECAUSE OF this.indexedScopes) + mungeSymboltree(); + StringBuffer sb = printSymbolTree(linebreak, preserveAllSemiColons); + + out.write(sb.toString()); + } + + private ScriptOrFnScope getCurrentScope() { + return (ScriptOrFnScope) scopes.peek(); + } + + private void enterScope(ScriptOrFnScope scope) { + scopes.push(scope); + } + + private void leaveCurrentScope() { + scopes.pop(); + } + + private JavaScriptToken consumeToken() { + return (JavaScriptToken) tokens.get(offset++); + } + + private JavaScriptToken getToken(int delta) { + return (JavaScriptToken) tokens.get(offset + delta); + } + + /* + * Returns the identifier for the specified symbol defined in + * the specified scope or in any scope above it. Returns null + * if this symbol does not have a corresponding identifier. + */ + private JavaScriptIdentifier getIdentifier(String symbol, ScriptOrFnScope scope) { + JavaScriptIdentifier identifier; + while (scope != null) { + identifier = scope.getIdentifier(symbol); + if (identifier != null) { + return identifier; + } + scope = scope.getParentScope(); + } + return null; + } + + /* + * If either 'eval' or 'with' is used in a local scope, we must make + * sure that all containing local scopes don't get munged. Otherwise, + * the obfuscation would potentially introduce bugs. + */ + private void protectScopeFromObfuscation(ScriptOrFnScope scope) { + assert scope != null; + + if (scope == globalScope) { + // The global scope does not get obfuscated, + // so we don't need to worry about it... + return; + } + + // Find the highest local scope containing the specified scope. + while (scope.getParentScope() != globalScope) { + scope = scope.getParentScope(); + } + + assert scope.getParentScope() == globalScope; + scope.preventMunging(); + } + + private String getDebugString(int max) { + assert max > 0; + StringBuffer result = new StringBuffer(); + int start = Math.max(offset - max, 0); + int end = Math.min(offset + max, tokens.size()); + for (int i = start; i < end; i++) { + JavaScriptToken token = (JavaScriptToken) tokens.get(i); + if (i == offset - 1) { + result.append(" ---> "); + } + result.append(token.getValue()); + if (i == offset - 1) { + result.append(" <--- "); + } + } + return result.toString(); + } + + private void warn(String message, boolean showDebugString) { + if (verbose) { + if (showDebugString) { + message = message + "\n" + getDebugString(10); + } + logger.warning(message, null, -1, null, -1); + } + } + + private void parseFunctionDeclaration() { + + String symbol; + JavaScriptToken token; + ScriptOrFnScope currentScope, fnScope; + JavaScriptIdentifier identifier; + + currentScope = getCurrentScope(); + + token = consumeToken(); + if (token.getType() == Token.NAME) { + if (mode == BUILDING_SYMBOL_TREE) { + // Get the name of the function and declare it in the current scope. + symbol = token.getValue(); + if (currentScope.getIdentifier(symbol) != null) { + warn("The function " + symbol + " has already been declared in the same scope...", true); + } + currentScope.declareIdentifier(symbol); + } + token = consumeToken(); + } + + assert token.getType() == Token.LP; + if (mode == BUILDING_SYMBOL_TREE) { + fnScope = new ScriptOrFnScope(braceNesting, currentScope); + indexedScopes.put(new Integer(offset), fnScope); + } else { + fnScope = (ScriptOrFnScope) indexedScopes.get(new Integer(offset)); + } + + // Parse function arguments. + int argpos = 0; + while ((token = consumeToken()).getType() != Token.RP) { + assert token.getType() == Token.NAME || + token.getType() == Token.COMMA; + if (token.getType() == Token.NAME && mode == BUILDING_SYMBOL_TREE) { + symbol = token.getValue(); + identifier = fnScope.declareIdentifier(symbol); + if (symbol.equals("$super") && argpos == 0) { + // Exception for Prototype 1.6... + identifier.preventMunging(); + } + argpos++; + } + } + + token = consumeToken(); + assert token.getType() == Token.LC; + braceNesting++; + + token = getToken(0); + if (token.getType() == Token.STRING && + getToken(1).getType() == Token.SEMI) { + // This is a hint. Hints are empty statements that look like + // "localvar1:nomunge, localvar2:nomunge"; They allow developers + // to prevent specific symbols from getting obfuscated (some heretic + // implementations, such as Prototype 1.6, require specific variable + // names, such as $super for example, in order to work appropriately. + // Note: right now, only "nomunge" is supported in the right hand side + // of a hint. However, in the future, the right hand side may contain + // other values. + consumeToken(); + String hints = token.getValue(); + // Remove the leading and trailing quotes... + hints = hints.substring(1, hints.length() - 1).trim(); + StringTokenizer st1 = new StringTokenizer(hints, ","); + while (st1.hasMoreTokens()) { + String hint = st1.nextToken(); + int idx = hint.indexOf(':'); + if (idx <= 0 || idx >= hint.length() - 1) { + if (mode == BUILDING_SYMBOL_TREE) { + // No need to report the error twice, hence the test... + warn("Invalid hint syntax: " + hint, true); + } + break; + } + String variableName = hint.substring(0, idx).trim(); + String variableType = hint.substring(idx + 1).trim(); + if (mode == BUILDING_SYMBOL_TREE) { + fnScope.addHint(variableName, variableType); + } else if (mode == CHECKING_SYMBOL_TREE) { + identifier = fnScope.getIdentifier(variableName); + if (identifier != null) { + if (variableType.equals("nomunge")) { + identifier.preventMunging(); + } else { + warn("Unsupported hint value: " + hint, true); + } + } else { + warn("Hint refers to an unknown identifier: " + hint, true); + } + } + } + } + + parseScope(fnScope); + } + + private void parseCatch() { + + String symbol; + JavaScriptToken token; + ScriptOrFnScope currentScope; + JavaScriptIdentifier identifier; + + token = getToken(-1); + assert token.getType() == Token.CATCH; + token = consumeToken(); + assert token.getType() == Token.LP; + token = consumeToken(); + assert token.getType() == Token.NAME; + + symbol = token.getValue(); + currentScope = getCurrentScope(); + + if (mode == BUILDING_SYMBOL_TREE) { + // We must declare the exception identifier in the containing function + // scope to avoid errors related to the obfuscation process. No need to + // display a warning if the symbol was already declared here... + currentScope.declareIdentifier(symbol); + } else { + identifier = getIdentifier(symbol, currentScope); + identifier.incrementRefcount(); + } + + token = consumeToken(); + assert token.getType() == Token.RP; + } + + private void parseExpression() { + + // Parse the expression until we encounter a comma or a semi-colon + // in the same brace nesting, bracket nesting and paren nesting. + // Parse functions if any... + + String symbol; + JavaScriptToken token; + ScriptOrFnScope currentScope; + JavaScriptIdentifier identifier; + + int expressionBraceNesting = braceNesting; + int bracketNesting = 0; + int parensNesting = 0; + + int length = tokens.size(); + + while (offset < length) { + + token = consumeToken(); + currentScope = getCurrentScope(); + + switch (token.getType()) { + + case Token.SEMI: + case Token.COMMA: + if (braceNesting == expressionBraceNesting && + bracketNesting == 0 && + parensNesting == 0) { + return; + } + break; + + case Token.FUNCTION: + parseFunctionDeclaration(); + break; + + case Token.LC: + braceNesting++; + break; + + case Token.RC: + braceNesting--; + assert braceNesting >= expressionBraceNesting; + break; + + case Token.LB: + bracketNesting++; + break; + + case Token.RB: + bracketNesting--; + break; + + case Token.LP: + parensNesting++; + break; + + case Token.RP: + parensNesting--; + break; + + case Token.SPECIALCOMMENT: + if (mode == BUILDING_SYMBOL_TREE) { + protectScopeFromObfuscation(currentScope); + warn("Using JScript conditional comments is not recommended." + (munge ? " Moreover, using JScript conditional comments reduces the level of compression!" : ""), true); + } + break; + + case Token.NAME: + symbol = token.getValue(); + + if (mode == BUILDING_SYMBOL_TREE) { + + if (symbol.equals("eval")) { + + protectScopeFromObfuscation(currentScope); + warn("Using 'eval' is not recommended." + (munge ? " Moreover, using 'eval' reduces the level of compression!" : ""), true); + + } + + } else if (mode == CHECKING_SYMBOL_TREE) { + + if ((offset < 2 || + (getToken(-2).getType() != Token.DOT && + getToken(-2).getType() != Token.GET && + getToken(-2).getType() != Token.SET)) && + getToken(0).getType() != Token.OBJECTLIT) { + + identifier = getIdentifier(symbol, currentScope); + + if (identifier == null) { + + if (symbol.length() <= 3 && !builtin.contains(symbol)) { + // Here, we found an undeclared and un-namespaced symbol that is + // 3 characters or less in length. Declare it in the global scope. + // We don't need to declare longer symbols since they won't cause + // any conflict with other munged symbols. + globalScope.declareIdentifier(symbol); + warn("Found an undeclared symbol: " + symbol, true); + } + + } else { + + identifier.incrementRefcount(); + } + } + } + break; + } + } + } + + private void parseScope(ScriptOrFnScope scope) { + + String symbol; + JavaScriptToken token; + JavaScriptIdentifier identifier; + + int length = tokens.size(); + + enterScope(scope); + + while (offset < length) { + + token = consumeToken(); + + switch (token.getType()) { + + case Token.VAR: + + if (mode == BUILDING_SYMBOL_TREE && scope.incrementVarCount() > 1) { + warn("Try to use a single 'var' statement per scope.", true); + } + + /* FALLSTHROUGH */ + + case Token.CONST: + + // The var keyword is followed by at least one symbol name. + // If several symbols follow, they are comma separated. + for (; ;) { + token = consumeToken(); + + assert token.getType() == Token.NAME; + + if (mode == BUILDING_SYMBOL_TREE) { + symbol = token.getValue(); + if (scope.getIdentifier(symbol) == null) { + scope.declareIdentifier(symbol); + } else { + warn("The variable " + symbol + " has already been declared in the same scope...", true); + } + } + + token = getToken(0); + + assert token.getType() == Token.SEMI || + token.getType() == Token.ASSIGN || + token.getType() == Token.COMMA || + token.getType() == Token.IN; + + if (token.getType() == Token.IN) { + break; + } else { + parseExpression(); + token = getToken(-1); + if (token.getType() == Token.SEMI) { + break; + } + } + } + break; + + case Token.FUNCTION: + parseFunctionDeclaration(); + break; + + case Token.LC: + braceNesting++; + break; + + case Token.RC: + braceNesting--; + assert braceNesting >= scope.getBraceNesting(); + if (braceNesting == scope.getBraceNesting()) { + leaveCurrentScope(); + return; + } + break; + + case Token.WITH: + if (mode == BUILDING_SYMBOL_TREE) { + // Inside a 'with' block, it is impossible to figure out + // statically whether a symbol is a local variable or an + // object member. As a consequence, the only thing we can + // do is turn the obfuscation off for the highest scope + // containing the 'with' block. + protectScopeFromObfuscation(scope); + warn("Using 'with' is not recommended." + (munge ? " Moreover, using 'with' reduces the level of compression!" : ""), true); + } + break; + + case Token.CATCH: + parseCatch(); + break; + + case Token.SPECIALCOMMENT: + if (mode == BUILDING_SYMBOL_TREE) { + protectScopeFromObfuscation(scope); + warn("Using JScript conditional comments is not recommended." + (munge ? " Moreover, using JScript conditional comments reduces the level of compression." : ""), true); + } + break; + + case Token.NAME: + symbol = token.getValue(); + + if (mode == BUILDING_SYMBOL_TREE) { + + if (symbol.equals("eval")) { + + protectScopeFromObfuscation(scope); + warn("Using 'eval' is not recommended." + (munge ? " Moreover, using 'eval' reduces the level of compression!" : ""), true); + + } + + } else if (mode == CHECKING_SYMBOL_TREE) { + + if ((offset < 2 || getToken(-2).getType() != Token.DOT) && + getToken(0).getType() != Token.OBJECTLIT) { + + identifier = getIdentifier(symbol, scope); + + if (identifier == null) { + + if (symbol.length() <= 3 && !builtin.contains(symbol)) { + // Here, we found an undeclared and un-namespaced symbol that is + // 3 characters or less in length. Declare it in the global scope. + // We don't need to declare longer symbols since they won't cause + // any conflict with other munged symbols. + globalScope.declareIdentifier(symbol); + warn("Found an undeclared symbol: " + symbol, true); + } + + } else { + + identifier.incrementRefcount(); + } + } + } + break; + } + } + } + + private void buildSymbolTree() { + offset = 0; + braceNesting = 0; + scopes.clear(); + indexedScopes.clear(); + indexedScopes.put(new Integer(0), globalScope); + mode = BUILDING_SYMBOL_TREE; + parseScope(globalScope); + } + + private void mungeSymboltree() { + + if (!munge) { + return; + } + + // One problem with obfuscation resides in the use of undeclared + // and un-namespaced global symbols that are 3 characters or less + // in length. Here is an example: + // + // var declaredGlobalVar; + // + // function declaredGlobalFn() { + // var localvar; + // localvar = abc; // abc is an undeclared global symbol + // } + // + // In the example above, there is a slim chance that localvar may be + // munged to 'abc', conflicting with the undeclared global symbol + // abc, creating a potential bug. The following code detects such + // global symbols. This must be done AFTER the entire file has been + // parsed, and BEFORE munging the symbol tree. Note that declaring + // extra symbols in the global scope won't hurt. + // + // Note: Since we go through all the tokens to do this, we also use + // the opportunity to count how many times each identifier is used. + + offset = 0; + braceNesting = 0; + scopes.clear(); + mode = CHECKING_SYMBOL_TREE; + parseScope(globalScope); + globalScope.munge(); + } + + private StringBuffer printSymbolTree(int linebreakpos, boolean preserveAllSemiColons) + throws IOException { + + offset = 0; + braceNesting = 0; + scopes.clear(); + + String symbol; + JavaScriptToken token; + ScriptOrFnScope currentScope; + JavaScriptIdentifier identifier; + + int length = tokens.size(); + StringBuffer result = new StringBuffer(); + + int linestartpos = 0; + + enterScope(globalScope); + + while (offset < length) { + + token = consumeToken(); + symbol = token.getValue(); + currentScope = getCurrentScope(); + + switch (token.getType()) { + + case Token.NAME: + + if (offset >= 2 && getToken(-2).getType() == Token.DOT || + getToken(0).getType() == Token.OBJECTLIT) { + + result.append(symbol); + + } else { + + identifier = getIdentifier(symbol, currentScope); + if (identifier != null) { + if (identifier.getMungedValue() != null) { + result.append(identifier.getMungedValue()); + } else { + result.append(symbol); + } + if (currentScope != globalScope && identifier.getRefcount() == 0) { + warn("The symbol " + symbol + " is declared but is apparently never used.\nThis code can probably be written in a more compact way.", true); + } + } else { + result.append(symbol); + } + } + break; + + case Token.REGEXP: + case Token.NUMBER: + case Token.STRING: + result.append(symbol); + break; + + case Token.ADD: + case Token.SUB: + result.append((String) literals.get(new Integer(token.getType()))); + if (offset < length) { + token = getToken(0); + if (token.getType() == Token.INC || + token.getType() == Token.DEC || + token.getType() == Token.ADD || + token.getType() == Token.DEC) { + // Handle the case x +/- ++/-- y + // We must keep a white space here. Otherwise, x +++ y would be + // interpreted as x ++ + y by the compiler, which is a bug (due + // to the implicit assignment being done on the wrong variable) + result.append(' '); + } else if (token.getType() == Token.POS && getToken(-1).getType() == Token.ADD || + token.getType() == Token.NEG && getToken(-1).getType() == Token.SUB) { + // Handle the case x + + y and x - - y + result.append(' '); + } + } + break; + + case Token.FUNCTION: + result.append("function"); + token = consumeToken(); + if (token.getType() == Token.NAME) { + result.append(' '); + symbol = token.getValue(); + identifier = getIdentifier(symbol, currentScope); + assert identifier != null; + if (identifier.getMungedValue() != null) { + result.append(identifier.getMungedValue()); + } else { + result.append(symbol); + } + if (currentScope != globalScope && identifier.getRefcount() == 0) { + warn("The symbol " + symbol + " is declared but is apparently never used.\nThis code can probably be written in a more compact way.", true); + } + token = consumeToken(); + } + assert token.getType() == Token.LP; + result.append('('); + currentScope = (ScriptOrFnScope) indexedScopes.get(new Integer(offset)); + enterScope(currentScope); + while ((token = consumeToken()).getType() != Token.RP) { + assert token.getType() == Token.NAME || token.getType() == Token.COMMA; + if (token.getType() == Token.NAME) { + symbol = token.getValue(); + identifier = getIdentifier(symbol, currentScope); + assert identifier != null; + if (identifier.getMungedValue() != null) { + result.append(identifier.getMungedValue()); + } else { + result.append(symbol); + } + } else if (token.getType() == Token.COMMA) { + result.append(','); + } + } + result.append(')'); + token = consumeToken(); + assert token.getType() == Token.LC; + result.append('{'); + braceNesting++; + token = getToken(0); + if (token.getType() == Token.STRING && + getToken(1).getType() == Token.SEMI) { + // This is a hint. Skip it! + consumeToken(); + consumeToken(); + } + break; + + case Token.RETURN: + case Token.TYPEOF: + result.append(literals.get(new Integer(token.getType()))); + // No space needed after 'return' and 'typeof' when followed + // by '(', '[', '{', a string or a regexp. + if (offset < length) { + token = getToken(0); + if (token.getType() != Token.LP && + token.getType() != Token.LB && + token.getType() != Token.LC && + token.getType() != Token.STRING && + token.getType() != Token.REGEXP && + token.getType() != Token.SEMI) { + result.append(' '); + } + } + break; + + case Token.CASE: + case Token.THROW: + result.append(literals.get(new Integer(token.getType()))); + // White-space needed after 'case' and 'throw' when not followed by a string. + if (offset < length && getToken(0).getType() != Token.STRING) { + result.append(' '); + } + break; + + case Token.BREAK: + case Token.CONTINUE: + result.append(literals.get(new Integer(token.getType()))); + if (offset < length && getToken(0).getType() != Token.SEMI) { + // If 'break' or 'continue' is not followed by a semi-colon, it must + // be followed by a label, hence the need for a white space. + result.append(' '); + } + break; + + case Token.LC: + result.append('{'); + braceNesting++; + break; + + case Token.RC: + result.append('}'); + braceNesting--; + assert braceNesting >= currentScope.getBraceNesting(); + if (braceNesting == currentScope.getBraceNesting()) { + leaveCurrentScope(); + } + break; + + case Token.SEMI: + // No need to output a semi-colon if the next character is a right-curly... + if (preserveAllSemiColons || offset < length && getToken(0).getType() != Token.RC) { + result.append(';'); + } + + if (linebreakpos >= 0 && result.length() - linestartpos > linebreakpos) { + // Some source control tools don't like it when files containing lines longer + // than, say 8000 characters, are checked in. The linebreak option is used in + // that case to split long lines after a specific column. + result.append('\n'); + linestartpos = result.length(); + } + break; + + case Token.SPECIALCOMMENT: + if (result.length() > 0 && result.charAt(result.length() - 1) != '\n') { + result.append("\n"); + } + result.append("/*"); + result.append(symbol); + result.append("*/\n"); + break; + + default: + String literal = (String) literals.get(new Integer(token.getType())); + if (literal != null) { + result.append(literal); + } else { + warn("This symbol cannot be printed: " + symbol, true); + } + break; + } + } + + // Append a semi-colon at the end, even if unnecessary semi-colons are + // supposed to be removed. This is especially useful when concatenating + // several minified files (the absence of an ending semi-colon at the + // end of one file may very likely cause a syntax error) + if (!preserveAllSemiColons && result.length() > 0) { + if (result.charAt(result.length() - 1) == '\n') { + result.setCharAt(result.length() - 1, ';'); + } else { + result.append(';'); + } + } + + return result; + } +} diff --git a/trunk/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/JavaScriptIdentifier.java b/trunk/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/JavaScriptIdentifier.java new file mode 100644 index 0000000..8668f49 --- /dev/null +++ b/trunk/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/JavaScriptIdentifier.java @@ -0,0 +1,55 @@ +/* + * YUI Compressor + * Author: Julien Lecomte + * Copyright (c) 2007, Yahoo! Inc. All rights reserved. + * Code licensed under the BSD License: + * http://developer.yahoo.net/yui/license.txt + */ + +package com.yahoo.platform.yui.compressor; + +import yuicompressor.org.mozilla.javascript.Token; + +/** + * JavaScriptIdentifier represents a variable/function identifier. + */ +class JavaScriptIdentifier extends JavaScriptToken { + + private int refcount = 0; + private String mungedValue; + private ScriptOrFnScope declaredScope; + private boolean markedForMunging = true; + + JavaScriptIdentifier(String value, ScriptOrFnScope declaredScope) { + super(Token.NAME, value); + this.declaredScope = declaredScope; + } + + ScriptOrFnScope getDeclaredScope() { + return declaredScope; + } + + void setMungedValue(String value) { + mungedValue = value; + } + + String getMungedValue() { + return mungedValue; + } + + void preventMunging() { + markedForMunging = false; + } + + boolean isMarkedForMunging() { + return markedForMunging; + } + + void incrementRefcount() { + refcount++; + } + + int getRefcount() { + return refcount; + } +} diff --git a/trunk/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/JavaScriptToken.java b/trunk/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/JavaScriptToken.java new file mode 100644 index 0000000..fee21d9 --- /dev/null +++ b/trunk/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/JavaScriptToken.java @@ -0,0 +1,28 @@ +/* + * YUI Compressor + * Author: Julien Lecomte + * Copyright (c) 2007, Yahoo! Inc. All rights reserved. + * Code licensed under the BSD License: + * http://developer.yahoo.net/yui/license.txt + */ + +package com.yahoo.platform.yui.compressor; + +public class JavaScriptToken { + + private int type; + private String value; + + JavaScriptToken(int type, String value) { + this.type = type; + this.value = value; + } + + int getType() { + return type; + } + + String getValue() { + return value; + } +} diff --git a/trunk/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/ScriptOrFnScope.java b/trunk/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/ScriptOrFnScope.java new file mode 100644 index 0000000..c1a2e47 --- /dev/null +++ b/trunk/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/ScriptOrFnScope.java @@ -0,0 +1,169 @@ +/* + * YUI Compressor + * Author: Julien Lecomte + * Copyright (c) 2007, Yahoo! Inc. All rights reserved. + * Code licensed under the BSD License: + * http://developer.yahoo.net/yui/license.txt + */ + +package com.yahoo.platform.yui.compressor; + +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Hashtable; + +class ScriptOrFnScope { + + private int braceNesting; + private ScriptOrFnScope parentScope; + private ArrayList subScopes; + private Hashtable identifiers = new Hashtable(); + private Hashtable hints = new Hashtable(); + private boolean markedForMunging = true; + private int varcount = 0; + + ScriptOrFnScope(int braceNesting, ScriptOrFnScope parentScope) { + this.braceNesting = braceNesting; + this.parentScope = parentScope; + this.subScopes = new ArrayList(); + if (parentScope != null) { + parentScope.subScopes.add(this); + } + } + + int getBraceNesting() { + return braceNesting; + } + + ScriptOrFnScope getParentScope() { + return parentScope; + } + + JavaScriptIdentifier declareIdentifier(String symbol) { + JavaScriptIdentifier identifier = (JavaScriptIdentifier) identifiers.get(symbol); + if (identifier == null) { + identifier = new JavaScriptIdentifier(symbol, this); + identifiers.put(symbol, identifier); + } + return identifier; + } + + JavaScriptIdentifier getIdentifier(String symbol) { + return (JavaScriptIdentifier) identifiers.get(symbol); + } + + void addHint(String variableName, String variableType) { + hints.put(variableName, variableType); + } + + void preventMunging() { + if (parentScope != null) { + // The symbols in the global scope don't get munged, + // but the sub-scopes it contains do get munged. + markedForMunging = false; + } + } + + private ArrayList getUsedSymbols() { + ArrayList result = new ArrayList(); + Enumeration elements = identifiers.elements(); + while (elements.hasMoreElements()) { + JavaScriptIdentifier identifier = (JavaScriptIdentifier) elements.nextElement(); + String mungedValue = identifier.getMungedValue(); + if (mungedValue == null) { + mungedValue = identifier.getValue(); + } + result.add(mungedValue); + } + return result; + } + + private ArrayList getAllUsedSymbols() { + ArrayList result = new ArrayList(); + ScriptOrFnScope scope = this; + while (scope != null) { + result.addAll(scope.getUsedSymbols()); + scope = scope.parentScope; + } + return result; + } + + int incrementVarCount() { + varcount++; + return varcount; + } + + void munge() { + + if (!markedForMunging) { + // Stop right here if this scope was flagged as unsafe for munging. + return; + } + + int pickFromSet = 1; + + // Do not munge symbols in the global scope! + if (parentScope != null) { + + ArrayList freeSymbols = new ArrayList(); + + freeSymbols.addAll(JavaScriptCompressor.ones); + freeSymbols.removeAll(getAllUsedSymbols()); + if (freeSymbols.size() == 0) { + pickFromSet = 2; + freeSymbols.addAll(JavaScriptCompressor.twos); + freeSymbols.removeAll(getAllUsedSymbols()); + } + if (freeSymbols.size() == 0) { + pickFromSet = 3; + freeSymbols.addAll(JavaScriptCompressor.threes); + freeSymbols.removeAll(getAllUsedSymbols()); + } + if (freeSymbols.size() == 0) { + throw new IllegalStateException("The YUI Compressor ran out of symbols. Aborting..."); + } + + // APPJET: sort identifiers by popularity + JavaScriptIdentifier idArray[] = ((Hashtable)identifiers).values().toArray(new JavaScriptIdentifier[0]); + java.util.Arrays.sort(idArray, new java.util.Comparator() { + public int compare(JavaScriptIdentifier i1, JavaScriptIdentifier i2) { + return i2.getRefcount() - i1.getRefcount(); // positive if i2 is more popular, indicating i2 should come first + } + }); + java.util.Iterator elements = java.util.Arrays.asList(idArray).iterator(); + + //Enumeration elements = identifiers.elements(); + while (elements.hasNext()) { + if (freeSymbols.size() == 0) { + pickFromSet++; + if (pickFromSet == 2) { + freeSymbols.addAll(JavaScriptCompressor.twos); + } else if (pickFromSet == 3) { + freeSymbols.addAll(JavaScriptCompressor.threes); + } else { + throw new IllegalStateException("The YUI Compressor ran out of symbols. Aborting..."); + } + // It is essential to remove the symbols already used in + // the containing scopes, or some of the variables declared + // in the containing scopes will be redeclared, which can + // lead to errors. + freeSymbols.removeAll(getAllUsedSymbols()); + } + + String mungedValue; + JavaScriptIdentifier identifier = (JavaScriptIdentifier) elements.next(); + if (identifier.isMarkedForMunging()) { + mungedValue = (String) freeSymbols.remove(0); + } else { + mungedValue = identifier.getValue(); + } + identifier.setMungedValue(mungedValue); + } + } + + for (int i = 0; i < subScopes.size(); i++) { + ScriptOrFnScope scope = (ScriptOrFnScope) subScopes.get(i); + scope.munge(); + } + } +} diff --git a/trunk/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/YUICompressor.java b/trunk/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/YUICompressor.java new file mode 100644 index 0000000..dcbaff4 --- /dev/null +++ b/trunk/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/YUICompressor.java @@ -0,0 +1,232 @@ +/* + * YUI Compressor + * Author: Julien Lecomte + * Copyright (c) 2007, Yahoo! Inc. All rights reserved. + * Code licensed under the BSD License: + * http://developer.yahoo.net/yui/license.txt + */ + +package com.yahoo.platform.yui.compressor; + +import jargs.gnu.CmdLineParser; +import yuicompressor.org.mozilla.javascript.ErrorReporter; +import yuicompressor.org.mozilla.javascript.EvaluatorException; + +import java.io.*; +import java.nio.charset.Charset; + +public class YUICompressor { + + public static void main(String args[]) { + + CmdLineParser parser = new CmdLineParser(); + CmdLineParser.Option typeOpt = parser.addStringOption("type"); + CmdLineParser.Option verboseOpt = parser.addBooleanOption('v', "verbose"); + CmdLineParser.Option nomungeOpt = parser.addBooleanOption("nomunge"); + CmdLineParser.Option linebreakOpt = parser.addStringOption("line-break"); + CmdLineParser.Option preserveSemiOpt = parser.addBooleanOption("preserve-semi"); + CmdLineParser.Option disableOptimizationsOpt = parser.addBooleanOption("disable-optimizations"); + CmdLineParser.Option helpOpt = parser.addBooleanOption('h', "help"); + CmdLineParser.Option charsetOpt = parser.addStringOption("charset"); + CmdLineParser.Option outputFilenameOpt = parser.addStringOption('o', "output"); + + Reader in = null; + Writer out = null; + + try { + + parser.parse(args); + + Boolean help = (Boolean) parser.getOptionValue(helpOpt); + if (help != null && help.booleanValue()) { + usage(); + System.exit(0); + } + + boolean verbose = parser.getOptionValue(verboseOpt) != null; + + String charset = (String) parser.getOptionValue(charsetOpt); + if (charset == null || !Charset.isSupported(charset)) { + charset = System.getProperty("file.encoding"); + if (charset == null) { + charset = "UTF-8"; + } + if (verbose) { + System.err.println("\n[INFO] Using charset " + charset); + } + } + + String[] fileArgs = parser.getRemainingArgs(); + String type = (String) parser.getOptionValue(typeOpt); + + if (fileArgs.length == 0) { + + if (type == null || !type.equalsIgnoreCase("js") && !type.equalsIgnoreCase("css")) { + usage(); + System.exit(1); + } + + in = new InputStreamReader(System.in, charset); + + } else { + + if (type != null && !type.equalsIgnoreCase("js") && !type.equalsIgnoreCase("css")) { + usage(); + System.exit(1); + } + + String inputFilename = fileArgs[0]; + + if (type == null) { + int idx = inputFilename.lastIndexOf('.'); + if (idx >= 0 && idx < inputFilename.length() - 1) { + type = inputFilename.substring(idx + 1); + } + } + + if (type == null || !type.equalsIgnoreCase("js") && !type.equalsIgnoreCase("css")) { + usage(); + System.exit(1); + } + + in = new InputStreamReader(new FileInputStream(inputFilename), charset); + } + + int linebreakpos = -1; + String linebreakstr = (String) parser.getOptionValue(linebreakOpt); + if (linebreakstr != null) { + try { + linebreakpos = Integer.parseInt(linebreakstr, 10); + } catch (NumberFormatException e) { + usage(); + System.exit(1); + } + } + + String outputFilename = (String) parser.getOptionValue(outputFilenameOpt); + + if (type.equalsIgnoreCase("js")) { + + try { + + JavaScriptCompressor compressor = new JavaScriptCompressor(in, new ErrorReporter() { + + public void warning(String message, String sourceName, + int line, String lineSource, int lineOffset) { + if (line < 0) { + System.err.println("\n[WARNING] " + message); + } else { + System.err.println("\n[WARNING] " + line + ':' + lineOffset + ':' + message); + } + } + + public void error(String message, String sourceName, + int line, String lineSource, int lineOffset) { + if (line < 0) { + System.err.println("\n[ERROR] " + message); + } else { + System.err.println("\n[ERROR] " + line + ':' + lineOffset + ':' + message); + } + } + + public EvaluatorException runtimeError(String message, String sourceName, + int line, String lineSource, int lineOffset) { + error(message, sourceName, line, lineSource, lineOffset); + return new EvaluatorException(message); + } + }); + + // Close the input stream first, and then open the output stream, + // in case the output file should override the input file. + in.close(); in = null; + + if (outputFilename == null) { + out = new OutputStreamWriter(System.out, charset); + } else { + out = new OutputStreamWriter(new FileOutputStream(outputFilename), charset); + } + + boolean munge = parser.getOptionValue(nomungeOpt) == null; + boolean preserveAllSemiColons = parser.getOptionValue(preserveSemiOpt) != null; + boolean disableOptimizations = parser.getOptionValue(disableOptimizationsOpt) != null; + + compressor.compress(out, linebreakpos, munge, verbose, + preserveAllSemiColons, disableOptimizations); + + } catch (EvaluatorException e) { + + e.printStackTrace(); + // Return a special error code used specifically by the web front-end. + System.exit(2); + + } + + } else if (type.equalsIgnoreCase("css")) { + + CssCompressor compressor = new CssCompressor(in); + + // Close the input stream first, and then open the output stream, + // in case the output file should override the input file. + in.close(); in = null; + + if (outputFilename == null) { + out = new OutputStreamWriter(System.out, charset); + } else { + out = new OutputStreamWriter(new FileOutputStream(outputFilename), charset); + } + + compressor.compress(out, linebreakpos); + } + + } catch (CmdLineParser.OptionException e) { + + usage(); + System.exit(1); + + } catch (IOException e) { + + e.printStackTrace(); + System.exit(1); + + } finally { + + if (in != null) { + try { + in.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + if (out != null) { + try { + out.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + private static void usage() { + System.out.println( + "\nUsage: java -jar yuicompressor-x.y.z.jar [options] [input file]\n\n" + + + "Global Options\n" + + " -h, --help Displays this information\n" + + " --type Specifies the type of the input file\n" + + " --charset Read the input file using \n" + + " --line-break Insert a line break after the specified column number\n" + + " -v, --verbose Display informational messages and warnings\n" + + " -o Place the output into . Defaults to stdout.\n\n" + + + "JavaScript Options\n" + + " --nomunge Minify only, do not obfuscate\n" + + " --preserve-semi Preserve all semicolons\n" + + " --disable-optimizations Disable all micro optimizations\n\n" + + + "If no input file is specified, it defaults to stdin. In this case, the 'type'\n" + + "option is required. Otherwise, the 'type' option is required only if the input\n" + + "file extension is neither 'js' nor 'css'."); + } +} diff --git a/trunk/infrastructure/yuicompressor/src/yuicompressor/org/mozilla/javascript/Decompiler.java b/trunk/infrastructure/yuicompressor/src/yuicompressor/org/mozilla/javascript/Decompiler.java new file mode 100644 index 0000000..348c568 --- /dev/null +++ b/trunk/infrastructure/yuicompressor/src/yuicompressor/org/mozilla/javascript/Decompiler.java @@ -0,0 +1,916 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mike Ang + * Igor Bukanov + * Bob Jervis + * Mike McCabe + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package yuicompressor.org.mozilla.javascript; + +/** + * The following class save decompilation information about the source. + * Source information is returned from the parser as a String + * associated with function nodes and with the toplevel script. When + * saved in the constant pool of a class, this string will be UTF-8 + * encoded, and token values will occupy a single byte. + + * Source is saved (mostly) as token numbers. The tokens saved pretty + * much correspond to the token stream of a 'canonical' representation + * of the input program, as directed by the parser. (There were a few + * cases where tokens could have been left out where decompiler could + * easily reconstruct them, but I left them in for clarity). (I also + * looked adding source collection to TokenStream instead, where I + * could have limited the changes to a few lines in getToken... but + * this wouldn't have saved any space in the resulting source + * representation, and would have meant that I'd have to duplicate + * parser logic in the decompiler to disambiguate situations where + * newlines are important.) The function decompile expands the + * tokens back into their string representations, using simple + * lookahead to correct spacing and indentation. + * + * Assignments are saved as two-token pairs (Token.ASSIGN, op). Number tokens + * are stored inline, as a NUMBER token, a character representing the type, and + * either 1 or 4 characters representing the bit-encoding of the number. String + * types NAME, STRING and OBJECT are currently stored as a token type, + * followed by a character giving the length of the string (assumed to + * be less than 2^16), followed by the characters of the string + * inlined into the source string. Changing this to some reference to + * to the string in the compiled class' constant pool would probably + * save a lot of space... but would require some method of deriving + * the final constant pool entry from information available at parse + * time. + */ +public class Decompiler +{ + /** + * Flag to indicate that the decompilation should omit the + * function header and trailing brace. + */ + public static final int ONLY_BODY_FLAG = 1 << 0; + + /** + * Flag to indicate that the decompilation generates toSource result. + */ + public static final int TO_SOURCE_FLAG = 1 << 1; + + /** + * Decompilation property to specify initial ident value. + */ + public static final int INITIAL_INDENT_PROP = 1; + + /** + * Decompilation property to specify default identation offset. + */ + public static final int INDENT_GAP_PROP = 2; + + /** + * Decompilation property to specify identation offset for case labels. + */ + public static final int CASE_GAP_PROP = 3; + + // Marker to denote the last RC of function so it can be distinguished from + // the last RC of object literals in case of function expressions + private static final int FUNCTION_END = Token.LAST_TOKEN + 1; + + String getEncodedSource() + { + return sourceToString(0); + } + + int getCurrentOffset() + { + return sourceTop; + } + + int markFunctionStart(int functionType) + { + int savedOffset = getCurrentOffset(); + addToken(Token.FUNCTION); + append((char)functionType); + return savedOffset; + } + + int markFunctionEnd(int functionStart) + { + int offset = getCurrentOffset(); + append((char)FUNCTION_END); + return offset; + } + + void addToken(int token) + { + if (!(0 <= token && token <= Token.LAST_TOKEN)) + throw new IllegalArgumentException(); + + append((char)token); + } + + void addEOL(int token) + { + if (!(0 <= token && token <= Token.LAST_TOKEN)) + throw new IllegalArgumentException(); + + append((char)token); + append((char)Token.EOL); + } + + void addName(String str) + { + addToken(Token.NAME); + appendString(str); + } + + void addString(String str) + { + addToken(Token.STRING); + appendString(str); + } + + void addRegexp(String regexp, String flags) + { + addToken(Token.REGEXP); + appendString('/' + regexp + '/' + flags); + } + + void addJScriptConditionalComment(String str) + { + addToken(Token.SPECIALCOMMENT); + appendString(str); + } + + void addNumber(double n) + { + addToken(Token.NUMBER); + + /* encode the number in the source stream. + * Save as NUMBER type (char | char char char char) + * where type is + * 'D' - double, 'S' - short, 'J' - long. + + * We need to retain float vs. integer type info to keep the + * behavior of liveconnect type-guessing the same after + * decompilation. (Liveconnect tries to present 1.0 to Java + * as a float/double) + * OPT: This is no longer true. We could compress the format. + + * This may not be the most space-efficient encoding; + * the chars created below may take up to 3 bytes in + * constant pool UTF-8 encoding, so a Double could take + * up to 12 bytes. + */ + + long lbits = (long)n; + if (lbits != n) { + // if it's floating point, save as a Double bit pattern. + // (12/15/97 our scanner only returns Double for f.p.) + lbits = Double.doubleToLongBits(n); + append('D'); + append((char)(lbits >> 48)); + append((char)(lbits >> 32)); + append((char)(lbits >> 16)); + append((char)lbits); + } + else { + // we can ignore negative values, bc they're already prefixed + // by NEG + if (lbits < 0) Kit.codeBug(); + + // will it fit in a char? + // this gives a short encoding for integer values up to 2^16. + if (lbits <= Character.MAX_VALUE) { + append('S'); + append((char)lbits); + } + else { // Integral, but won't fit in a char. Store as a long. + append('J'); + append((char)(lbits >> 48)); + append((char)(lbits >> 32)); + append((char)(lbits >> 16)); + append((char)lbits); + } + } + } + + private void appendString(String str) + { + int L = str.length(); + int lengthEncodingSize = 1; + if (L >= 0x8000) { + lengthEncodingSize = 2; + } + int nextTop = sourceTop + lengthEncodingSize + L; + if (nextTop > sourceBuffer.length) { + increaseSourceCapacity(nextTop); + } + if (L >= 0x8000) { + // Use 2 chars to encode strings exceeding 32K, were the highest + // bit in the first char indicates presence of the next byte + sourceBuffer[sourceTop] = (char)(0x8000 | (L >>> 16)); + ++sourceTop; + } + sourceBuffer[sourceTop] = (char)L; + ++sourceTop; + str.getChars(0, L, sourceBuffer, sourceTop); + sourceTop = nextTop; + } + + private void append(char c) + { + if (sourceTop == sourceBuffer.length) { + increaseSourceCapacity(sourceTop + 1); + } + sourceBuffer[sourceTop] = c; + ++sourceTop; + } + + private void increaseSourceCapacity(int minimalCapacity) + { + // Call this only when capacity increase is must + if (minimalCapacity <= sourceBuffer.length) Kit.codeBug(); + int newCapacity = sourceBuffer.length * 2; + if (newCapacity < minimalCapacity) { + newCapacity = minimalCapacity; + } + char[] tmp = new char[newCapacity]; + System.arraycopy(sourceBuffer, 0, tmp, 0, sourceTop); + sourceBuffer = tmp; + } + + private String sourceToString(int offset) + { + if (offset < 0 || sourceTop < offset) Kit.codeBug(); + return new String(sourceBuffer, offset, sourceTop - offset); + } + + /** + * Decompile the source information associated with this js + * function/script back into a string. For the most part, this + * just means translating tokens back to their string + * representations; there's a little bit of lookahead logic to + * decide the proper spacing/indentation. Most of the work in + * mapping the original source to the prettyprinted decompiled + * version is done by the parser. + * + * @param source encoded source tree presentation + * + * @param flags flags to select output format + * + * @param properties indentation properties + * + */ + public static String decompile(String source, int flags, + UintMap properties) + { + int length = source.length(); + if (length == 0) { return ""; } + + int indent = properties.getInt(INITIAL_INDENT_PROP, 0); + if (indent < 0) throw new IllegalArgumentException(); + int indentGap = properties.getInt(INDENT_GAP_PROP, 4); + if (indentGap < 0) throw new IllegalArgumentException(); + int caseGap = properties.getInt(CASE_GAP_PROP, 2); + if (caseGap < 0) throw new IllegalArgumentException(); + + StringBuffer result = new StringBuffer(); + boolean justFunctionBody = (0 != (flags & Decompiler.ONLY_BODY_FLAG)); + boolean toSource = (0 != (flags & Decompiler.TO_SOURCE_FLAG)); + + // Spew tokens in source, for debugging. + // as TYPE number char + if (printSource) { + System.err.println("length:" + length); + for (int i = 0; i < length; ++i) { + // Note that tokenToName will fail unless Context.printTrees + // is true. + String tokenname = null; + if (Token.printNames) { + tokenname = Token.name(source.charAt(i)); + } + if (tokenname == null) { + tokenname = "---"; + } + String pad = tokenname.length() > 7 + ? "\t" + : "\t\t"; + System.err.println + (tokenname + + pad + (int)source.charAt(i) + + "\t'" + ScriptRuntime.escapeString + (source.substring(i, i+1)) + + "'"); + } + System.err.println(); + } + + int braceNesting = 0; + boolean afterFirstEOL = false; + int i = 0; + int topFunctionType; + if (source.charAt(i) == Token.SCRIPT) { + ++i; + topFunctionType = -1; + } else { + topFunctionType = source.charAt(i + 1); + } + + if (!toSource) { + // add an initial newline to exactly match js. + result.append('\n'); + for (int j = 0; j < indent; j++) + result.append(' '); + } else { + if (topFunctionType == FunctionNode.FUNCTION_EXPRESSION) { + result.append('('); + } + } + + while (i < length) { + switch(source.charAt(i)) { + case Token.GET: + case Token.SET: + result.append(source.charAt(i) == Token.GET ? "get " : "set "); + ++i; + i = printSourceString(source, i + 1, false, result); + // Now increment one more to get past the FUNCTION token + ++i; + break; + + case Token.NAME: + case Token.REGEXP: // re-wrapped in '/'s in parser... + i = printSourceString(source, i + 1, false, result); + continue; + + case Token.STRING: + i = printSourceString(source, i + 1, true, result); + continue; + + case Token.NUMBER: + i = printSourceNumber(source, i + 1, result); + continue; + + case Token.TRUE: + result.append("true"); + break; + + case Token.FALSE: + result.append("false"); + break; + + case Token.NULL: + result.append("null"); + break; + + case Token.THIS: + result.append("this"); + break; + + case Token.FUNCTION: + ++i; // skip function type + result.append("function "); + break; + + case FUNCTION_END: + // Do nothing + break; + + case Token.COMMA: + result.append(", "); + break; + + case Token.LC: + ++braceNesting; + if (Token.EOL == getNext(source, length, i)) + indent += indentGap; + result.append('{'); + break; + + case Token.RC: { + --braceNesting; + /* don't print the closing RC if it closes the + * toplevel function and we're called from + * decompileFunctionBody. + */ + if (justFunctionBody && braceNesting == 0) + break; + + result.append('}'); + switch (getNext(source, length, i)) { + case Token.EOL: + case FUNCTION_END: + indent -= indentGap; + break; + case Token.WHILE: + case Token.ELSE: + indent -= indentGap; + result.append(' '); + break; + } + break; + } + case Token.LP: + result.append('('); + break; + + case Token.RP: + result.append(')'); + if (Token.LC == getNext(source, length, i)) + result.append(' '); + break; + + case Token.LB: + result.append('['); + break; + + case Token.RB: + result.append(']'); + break; + + case Token.EOL: { + if (toSource) break; + boolean newLine = true; + if (!afterFirstEOL) { + afterFirstEOL = true; + if (justFunctionBody) { + /* throw away just added 'function name(...) {' + * and restore the original indent + */ + result.setLength(0); + indent -= indentGap; + newLine = false; + } + } + if (newLine) { + result.append('\n'); + } + + /* add indent if any tokens remain, + * less setback if next token is + * a label, case or default. + */ + if (i + 1 < length) { + int less = 0; + int nextToken = source.charAt(i + 1); + if (nextToken == Token.CASE + || nextToken == Token.DEFAULT) + { + less = indentGap - caseGap; + } else if (nextToken == Token.RC) { + less = indentGap; + } + + /* elaborate check against label... skip past a + * following inlined NAME and look for a COLON. + */ + else if (nextToken == Token.NAME) { + int afterName = getSourceStringEnd(source, i + 2); + if (source.charAt(afterName) == Token.COLON) + less = indentGap; + } + + for (; less < indent; less++) + result.append(' '); + } + break; + } + case Token.DOT: + result.append('.'); + break; + + case Token.NEW: + result.append("new "); + break; + + case Token.DELPROP: + result.append("delete "); + break; + + case Token.IF: + result.append("if "); + break; + + case Token.ELSE: + result.append("else "); + break; + + case Token.FOR: + result.append("for "); + break; + + case Token.IN: + result.append(" in "); + break; + + case Token.WITH: + result.append("with "); + break; + + case Token.WHILE: + result.append("while "); + break; + + case Token.DO: + result.append("do "); + break; + + case Token.TRY: + result.append("try "); + break; + + case Token.CATCH: + result.append("catch "); + break; + + case Token.FINALLY: + result.append("finally "); + break; + + case Token.THROW: + result.append("throw "); + break; + + case Token.SWITCH: + result.append("switch "); + break; + + case Token.BREAK: + result.append("break"); + if (Token.NAME == getNext(source, length, i)) + result.append(' '); + break; + + case Token.CONTINUE: + result.append("continue"); + if (Token.NAME == getNext(source, length, i)) + result.append(' '); + break; + + case Token.CASE: + result.append("case "); + break; + + case Token.DEFAULT: + result.append("default"); + break; + + case Token.RETURN: + result.append("return"); + if (Token.SEMI != getNext(source, length, i)) + result.append(' '); + break; + + case Token.VAR: + result.append("var "); + break; + + case Token.SEMI: + result.append(';'); + if (Token.EOL != getNext(source, length, i)) { + // separators in FOR + result.append(' '); + } + break; + + case Token.ASSIGN: + result.append(" = "); + break; + + case Token.ASSIGN_ADD: + result.append(" += "); + break; + + case Token.ASSIGN_SUB: + result.append(" -= "); + break; + + case Token.ASSIGN_MUL: + result.append(" *= "); + break; + + case Token.ASSIGN_DIV: + result.append(" /= "); + break; + + case Token.ASSIGN_MOD: + result.append(" %= "); + break; + + case Token.ASSIGN_BITOR: + result.append(" |= "); + break; + + case Token.ASSIGN_BITXOR: + result.append(" ^= "); + break; + + case Token.ASSIGN_BITAND: + result.append(" &= "); + break; + + case Token.ASSIGN_LSH: + result.append(" <<= "); + break; + + case Token.ASSIGN_RSH: + result.append(" >>= "); + break; + + case Token.ASSIGN_URSH: + result.append(" >>>= "); + break; + + case Token.HOOK: + result.append(" ? "); + break; + + case Token.OBJECTLIT: + // pun OBJECTLIT to mean colon in objlit property + // initialization. + // This needs to be distinct from COLON in the general case + // to distinguish from the colon in a ternary... which needs + // different spacing. + result.append(':'); + break; + + case Token.COLON: + if (Token.EOL == getNext(source, length, i)) + // it's the end of a label + result.append(':'); + else + // it's the middle part of a ternary + result.append(" : "); + break; + + case Token.OR: + result.append(" || "); + break; + + case Token.AND: + result.append(" && "); + break; + + case Token.BITOR: + result.append(" | "); + break; + + case Token.BITXOR: + result.append(" ^ "); + break; + + case Token.BITAND: + result.append(" & "); + break; + + case Token.SHEQ: + result.append(" === "); + break; + + case Token.SHNE: + result.append(" !== "); + break; + + case Token.EQ: + result.append(" == "); + break; + + case Token.NE: + result.append(" != "); + break; + + case Token.LE: + result.append(" <= "); + break; + + case Token.LT: + result.append(" < "); + break; + + case Token.GE: + result.append(" >= "); + break; + + case Token.GT: + result.append(" > "); + break; + + case Token.INSTANCEOF: + result.append(" instanceof "); + break; + + case Token.LSH: + result.append(" << "); + break; + + case Token.RSH: + result.append(" >> "); + break; + + case Token.URSH: + result.append(" >>> "); + break; + + case Token.TYPEOF: + result.append("typeof "); + break; + + case Token.VOID: + result.append("void "); + break; + + case Token.CONST: + result.append("const "); + break; + + case Token.NOT: + result.append('!'); + break; + + case Token.BITNOT: + result.append('~'); + break; + + case Token.POS: + result.append('+'); + break; + + case Token.NEG: + result.append('-'); + break; + + case Token.INC: + result.append("++"); + break; + + case Token.DEC: + result.append("--"); + break; + + case Token.ADD: + result.append(" + "); + break; + + case Token.SUB: + result.append(" - "); + break; + + case Token.MUL: + result.append(" * "); + break; + + case Token.DIV: + result.append(" / "); + break; + + case Token.MOD: + result.append(" % "); + break; + + case Token.COLONCOLON: + result.append("::"); + break; + + case Token.DOTDOT: + result.append(".."); + break; + + case Token.DOTQUERY: + result.append(".("); + break; + + case Token.XMLATTR: + result.append('@'); + break; + + default: + // If we don't know how to decompile it, raise an exception. + throw new RuntimeException("Token: " + + Token.name(source.charAt(i))); + } + ++i; + } + + if (!toSource) { + // add that trailing newline if it's an outermost function. + if (!justFunctionBody) + result.append('\n'); + } else { + if (topFunctionType == FunctionNode.FUNCTION_EXPRESSION) { + result.append(')'); + } + } + + return result.toString(); + } + + private static int getNext(String source, int length, int i) + { + return (i + 1 < length) ? source.charAt(i + 1) : Token.EOF; + } + + private static int getSourceStringEnd(String source, int offset) + { + return printSourceString(source, offset, false, null); + } + + private static int printSourceString(String source, int offset, + boolean asQuotedString, + StringBuffer sb) + { + int length = source.charAt(offset); + ++offset; + if ((0x8000 & length) != 0) { + length = ((0x7FFF & length) << 16) | source.charAt(offset); + ++offset; + } + if (sb != null) { + String str = source.substring(offset, offset + length); + if (!asQuotedString) { + sb.append(str); + } else { + sb.append('"'); + sb.append(ScriptRuntime.escapeString(str)); + sb.append('"'); + } + } + return offset + length; + } + + private static int printSourceNumber(String source, int offset, + StringBuffer sb) + { + double number = 0.0; + char type = source.charAt(offset); + ++offset; + if (type == 'S') { + if (sb != null) { + int ival = source.charAt(offset); + number = ival; + } + ++offset; + } else if (type == 'J' || type == 'D') { + if (sb != null) { + long lbits; + lbits = (long)source.charAt(offset) << 48; + lbits |= (long)source.charAt(offset + 1) << 32; + lbits |= (long)source.charAt(offset + 2) << 16; + lbits |= source.charAt(offset + 3); + if (type == 'J') { + number = lbits; + } else { + number = Double.longBitsToDouble(lbits); + } + } + offset += 4; + } else { + // Bad source + throw new RuntimeException(); + } + if (sb != null) { + sb.append(ScriptRuntime.numberToString(number, 10)); + } + return offset; + } + + private char[] sourceBuffer = new char[128]; + +// Per script/function source buffer top: parent source does not include a +// nested functions source and uses function index as a reference instead. + private int sourceTop; + +// whether to do a debug print of the source information, when decompiling. + private static final boolean printSource = false; + +} diff --git a/trunk/infrastructure/yuicompressor/src/yuicompressor/org/mozilla/javascript/Decompiler.java.orig b/trunk/infrastructure/yuicompressor/src/yuicompressor/org/mozilla/javascript/Decompiler.java.orig new file mode 100644 index 0000000..cdb00b7 --- /dev/null +++ b/trunk/infrastructure/yuicompressor/src/yuicompressor/org/mozilla/javascript/Decompiler.java.orig @@ -0,0 +1,910 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mike Ang + * Igor Bukanov + * Bob Jervis + * Mike McCabe + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript; + +/** + * The following class save decompilation information about the source. + * Source information is returned from the parser as a String + * associated with function nodes and with the toplevel script. When + * saved in the constant pool of a class, this string will be UTF-8 + * encoded, and token values will occupy a single byte. + + * Source is saved (mostly) as token numbers. The tokens saved pretty + * much correspond to the token stream of a 'canonical' representation + * of the input program, as directed by the parser. (There were a few + * cases where tokens could have been left out where decompiler could + * easily reconstruct them, but I left them in for clarity). (I also + * looked adding source collection to TokenStream instead, where I + * could have limited the changes to a few lines in getToken... but + * this wouldn't have saved any space in the resulting source + * representation, and would have meant that I'd have to duplicate + * parser logic in the decompiler to disambiguate situations where + * newlines are important.) The function decompile expands the + * tokens back into their string representations, using simple + * lookahead to correct spacing and indentation. + * + * Assignments are saved as two-token pairs (Token.ASSIGN, op). Number tokens + * are stored inline, as a NUMBER token, a character representing the type, and + * either 1 or 4 characters representing the bit-encoding of the number. String + * types NAME, STRING and OBJECT are currently stored as a token type, + * followed by a character giving the length of the string (assumed to + * be less than 2^16), followed by the characters of the string + * inlined into the source string. Changing this to some reference to + * to the string in the compiled class' constant pool would probably + * save a lot of space... but would require some method of deriving + * the final constant pool entry from information available at parse + * time. + */ +public class Decompiler +{ + /** + * Flag to indicate that the decompilation should omit the + * function header and trailing brace. + */ + public static final int ONLY_BODY_FLAG = 1 << 0; + + /** + * Flag to indicate that the decompilation generates toSource result. + */ + public static final int TO_SOURCE_FLAG = 1 << 1; + + /** + * Decompilation property to specify initial ident value. + */ + public static final int INITIAL_INDENT_PROP = 1; + + /** + * Decompilation property to specify default identation offset. + */ + public static final int INDENT_GAP_PROP = 2; + + /** + * Decompilation property to specify identation offset for case labels. + */ + public static final int CASE_GAP_PROP = 3; + + // Marker to denote the last RC of function so it can be distinguished from + // the last RC of object literals in case of function expressions + private static final int FUNCTION_END = Token.LAST_TOKEN + 1; + + String getEncodedSource() + { + return sourceToString(0); + } + + int getCurrentOffset() + { + return sourceTop; + } + + int markFunctionStart(int functionType) + { + int savedOffset = getCurrentOffset(); + addToken(Token.FUNCTION); + append((char)functionType); + return savedOffset; + } + + int markFunctionEnd(int functionStart) + { + int offset = getCurrentOffset(); + append((char)FUNCTION_END); + return offset; + } + + void addToken(int token) + { + if (!(0 <= token && token <= Token.LAST_TOKEN)) + throw new IllegalArgumentException(); + + append((char)token); + } + + void addEOL(int token) + { + if (!(0 <= token && token <= Token.LAST_TOKEN)) + throw new IllegalArgumentException(); + + append((char)token); + append((char)Token.EOL); + } + + void addName(String str) + { + addToken(Token.NAME); + appendString(str); + } + + void addString(String str) + { + addToken(Token.STRING); + appendString(str); + } + + void addRegexp(String regexp, String flags) + { + addToken(Token.REGEXP); + appendString('/' + regexp + '/' + flags); + } + + void addNumber(double n) + { + addToken(Token.NUMBER); + + /* encode the number in the source stream. + * Save as NUMBER type (char | char char char char) + * where type is + * 'D' - double, 'S' - short, 'J' - long. + + * We need to retain float vs. integer type info to keep the + * behavior of liveconnect type-guessing the same after + * decompilation. (Liveconnect tries to present 1.0 to Java + * as a float/double) + * OPT: This is no longer true. We could compress the format. + + * This may not be the most space-efficient encoding; + * the chars created below may take up to 3 bytes in + * constant pool UTF-8 encoding, so a Double could take + * up to 12 bytes. + */ + + long lbits = (long)n; + if (lbits != n) { + // if it's floating point, save as a Double bit pattern. + // (12/15/97 our scanner only returns Double for f.p.) + lbits = Double.doubleToLongBits(n); + append('D'); + append((char)(lbits >> 48)); + append((char)(lbits >> 32)); + append((char)(lbits >> 16)); + append((char)lbits); + } + else { + // we can ignore negative values, bc they're already prefixed + // by NEG + if (lbits < 0) Kit.codeBug(); + + // will it fit in a char? + // this gives a short encoding for integer values up to 2^16. + if (lbits <= Character.MAX_VALUE) { + append('S'); + append((char)lbits); + } + else { // Integral, but won't fit in a char. Store as a long. + append('J'); + append((char)(lbits >> 48)); + append((char)(lbits >> 32)); + append((char)(lbits >> 16)); + append((char)lbits); + } + } + } + + private void appendString(String str) + { + int L = str.length(); + int lengthEncodingSize = 1; + if (L >= 0x8000) { + lengthEncodingSize = 2; + } + int nextTop = sourceTop + lengthEncodingSize + L; + if (nextTop > sourceBuffer.length) { + increaseSourceCapacity(nextTop); + } + if (L >= 0x8000) { + // Use 2 chars to encode strings exceeding 32K, were the highest + // bit in the first char indicates presence of the next byte + sourceBuffer[sourceTop] = (char)(0x8000 | (L >>> 16)); + ++sourceTop; + } + sourceBuffer[sourceTop] = (char)L; + ++sourceTop; + str.getChars(0, L, sourceBuffer, sourceTop); + sourceTop = nextTop; + } + + private void append(char c) + { + if (sourceTop == sourceBuffer.length) { + increaseSourceCapacity(sourceTop + 1); + } + sourceBuffer[sourceTop] = c; + ++sourceTop; + } + + private void increaseSourceCapacity(int minimalCapacity) + { + // Call this only when capacity increase is must + if (minimalCapacity <= sourceBuffer.length) Kit.codeBug(); + int newCapacity = sourceBuffer.length * 2; + if (newCapacity < minimalCapacity) { + newCapacity = minimalCapacity; + } + char[] tmp = new char[newCapacity]; + System.arraycopy(sourceBuffer, 0, tmp, 0, sourceTop); + sourceBuffer = tmp; + } + + private String sourceToString(int offset) + { + if (offset < 0 || sourceTop < offset) Kit.codeBug(); + return new String(sourceBuffer, offset, sourceTop - offset); + } + + /** + * Decompile the source information associated with this js + * function/script back into a string. For the most part, this + * just means translating tokens back to their string + * representations; there's a little bit of lookahead logic to + * decide the proper spacing/indentation. Most of the work in + * mapping the original source to the prettyprinted decompiled + * version is done by the parser. + * + * @param source encoded source tree presentation + * + * @param flags flags to select output format + * + * @param properties indentation properties + * + */ + public static String decompile(String source, int flags, + UintMap properties) + { + int length = source.length(); + if (length == 0) { return ""; } + + int indent = properties.getInt(INITIAL_INDENT_PROP, 0); + if (indent < 0) throw new IllegalArgumentException(); + int indentGap = properties.getInt(INDENT_GAP_PROP, 4); + if (indentGap < 0) throw new IllegalArgumentException(); + int caseGap = properties.getInt(CASE_GAP_PROP, 2); + if (caseGap < 0) throw new IllegalArgumentException(); + + StringBuffer result = new StringBuffer(); + boolean justFunctionBody = (0 != (flags & Decompiler.ONLY_BODY_FLAG)); + boolean toSource = (0 != (flags & Decompiler.TO_SOURCE_FLAG)); + + // Spew tokens in source, for debugging. + // as TYPE number char + if (printSource) { + System.err.println("length:" + length); + for (int i = 0; i < length; ++i) { + // Note that tokenToName will fail unless Context.printTrees + // is true. + String tokenname = null; + if (Token.printNames) { + tokenname = Token.name(source.charAt(i)); + } + if (tokenname == null) { + tokenname = "---"; + } + String pad = tokenname.length() > 7 + ? "\t" + : "\t\t"; + System.err.println + (tokenname + + pad + (int)source.charAt(i) + + "\t'" + ScriptRuntime.escapeString + (source.substring(i, i+1)) + + "'"); + } + System.err.println(); + } + + int braceNesting = 0; + boolean afterFirstEOL = false; + int i = 0; + int topFunctionType; + if (source.charAt(i) == Token.SCRIPT) { + ++i; + topFunctionType = -1; + } else { + topFunctionType = source.charAt(i + 1); + } + + if (!toSource) { + // add an initial newline to exactly match js. + result.append('\n'); + for (int j = 0; j < indent; j++) + result.append(' '); + } else { + if (topFunctionType == FunctionNode.FUNCTION_EXPRESSION) { + result.append('('); + } + } + + while (i < length) { + switch(source.charAt(i)) { + case Token.GET: + case Token.SET: + result.append(source.charAt(i) == Token.GET ? "get " : "set "); + ++i; + i = printSourceString(source, i + 1, false, result); + // Now increment one more to get past the FUNCTION token + ++i; + break; + + case Token.NAME: + case Token.REGEXP: // re-wrapped in '/'s in parser... + i = printSourceString(source, i + 1, false, result); + continue; + + case Token.STRING: + i = printSourceString(source, i + 1, true, result); + continue; + + case Token.NUMBER: + i = printSourceNumber(source, i + 1, result); + continue; + + case Token.TRUE: + result.append("true"); + break; + + case Token.FALSE: + result.append("false"); + break; + + case Token.NULL: + result.append("null"); + break; + + case Token.THIS: + result.append("this"); + break; + + case Token.FUNCTION: + ++i; // skip function type + result.append("function "); + break; + + case FUNCTION_END: + // Do nothing + break; + + case Token.COMMA: + result.append(", "); + break; + + case Token.LC: + ++braceNesting; + if (Token.EOL == getNext(source, length, i)) + indent += indentGap; + result.append('{'); + break; + + case Token.RC: { + --braceNesting; + /* don't print the closing RC if it closes the + * toplevel function and we're called from + * decompileFunctionBody. + */ + if (justFunctionBody && braceNesting == 0) + break; + + result.append('}'); + switch (getNext(source, length, i)) { + case Token.EOL: + case FUNCTION_END: + indent -= indentGap; + break; + case Token.WHILE: + case Token.ELSE: + indent -= indentGap; + result.append(' '); + break; + } + break; + } + case Token.LP: + result.append('('); + break; + + case Token.RP: + result.append(')'); + if (Token.LC == getNext(source, length, i)) + result.append(' '); + break; + + case Token.LB: + result.append('['); + break; + + case Token.RB: + result.append(']'); + break; + + case Token.EOL: { + if (toSource) break; + boolean newLine = true; + if (!afterFirstEOL) { + afterFirstEOL = true; + if (justFunctionBody) { + /* throw away just added 'function name(...) {' + * and restore the original indent + */ + result.setLength(0); + indent -= indentGap; + newLine = false; + } + } + if (newLine) { + result.append('\n'); + } + + /* add indent if any tokens remain, + * less setback if next token is + * a label, case or default. + */ + if (i + 1 < length) { + int less = 0; + int nextToken = source.charAt(i + 1); + if (nextToken == Token.CASE + || nextToken == Token.DEFAULT) + { + less = indentGap - caseGap; + } else if (nextToken == Token.RC) { + less = indentGap; + } + + /* elaborate check against label... skip past a + * following inlined NAME and look for a COLON. + */ + else if (nextToken == Token.NAME) { + int afterName = getSourceStringEnd(source, i + 2); + if (source.charAt(afterName) == Token.COLON) + less = indentGap; + } + + for (; less < indent; less++) + result.append(' '); + } + break; + } + case Token.DOT: + result.append('.'); + break; + + case Token.NEW: + result.append("new "); + break; + + case Token.DELPROP: + result.append("delete "); + break; + + case Token.IF: + result.append("if "); + break; + + case Token.ELSE: + result.append("else "); + break; + + case Token.FOR: + result.append("for "); + break; + + case Token.IN: + result.append(" in "); + break; + + case Token.WITH: + result.append("with "); + break; + + case Token.WHILE: + result.append("while "); + break; + + case Token.DO: + result.append("do "); + break; + + case Token.TRY: + result.append("try "); + break; + + case Token.CATCH: + result.append("catch "); + break; + + case Token.FINALLY: + result.append("finally "); + break; + + case Token.THROW: + result.append("throw "); + break; + + case Token.SWITCH: + result.append("switch "); + break; + + case Token.BREAK: + result.append("break"); + if (Token.NAME == getNext(source, length, i)) + result.append(' '); + break; + + case Token.CONTINUE: + result.append("continue"); + if (Token.NAME == getNext(source, length, i)) + result.append(' '); + break; + + case Token.CASE: + result.append("case "); + break; + + case Token.DEFAULT: + result.append("default"); + break; + + case Token.RETURN: + result.append("return"); + if (Token.SEMI != getNext(source, length, i)) + result.append(' '); + break; + + case Token.VAR: + result.append("var "); + break; + + case Token.SEMI: + result.append(';'); + if (Token.EOL != getNext(source, length, i)) { + // separators in FOR + result.append(' '); + } + break; + + case Token.ASSIGN: + result.append(" = "); + break; + + case Token.ASSIGN_ADD: + result.append(" += "); + break; + + case Token.ASSIGN_SUB: + result.append(" -= "); + break; + + case Token.ASSIGN_MUL: + result.append(" *= "); + break; + + case Token.ASSIGN_DIV: + result.append(" /= "); + break; + + case Token.ASSIGN_MOD: + result.append(" %= "); + break; + + case Token.ASSIGN_BITOR: + result.append(" |= "); + break; + + case Token.ASSIGN_BITXOR: + result.append(" ^= "); + break; + + case Token.ASSIGN_BITAND: + result.append(" &= "); + break; + + case Token.ASSIGN_LSH: + result.append(" <<= "); + break; + + case Token.ASSIGN_RSH: + result.append(" >>= "); + break; + + case Token.ASSIGN_URSH: + result.append(" >>>= "); + break; + + case Token.HOOK: + result.append(" ? "); + break; + + case Token.OBJECTLIT: + // pun OBJECTLIT to mean colon in objlit property + // initialization. + // This needs to be distinct from COLON in the general case + // to distinguish from the colon in a ternary... which needs + // different spacing. + result.append(':'); + break; + + case Token.COLON: + if (Token.EOL == getNext(source, length, i)) + // it's the end of a label + result.append(':'); + else + // it's the middle part of a ternary + result.append(" : "); + break; + + case Token.OR: + result.append(" || "); + break; + + case Token.AND: + result.append(" && "); + break; + + case Token.BITOR: + result.append(" | "); + break; + + case Token.BITXOR: + result.append(" ^ "); + break; + + case Token.BITAND: + result.append(" & "); + break; + + case Token.SHEQ: + result.append(" === "); + break; + + case Token.SHNE: + result.append(" !== "); + break; + + case Token.EQ: + result.append(" == "); + break; + + case Token.NE: + result.append(" != "); + break; + + case Token.LE: + result.append(" <= "); + break; + + case Token.LT: + result.append(" < "); + break; + + case Token.GE: + result.append(" >= "); + break; + + case Token.GT: + result.append(" > "); + break; + + case Token.INSTANCEOF: + result.append(" instanceof "); + break; + + case Token.LSH: + result.append(" << "); + break; + + case Token.RSH: + result.append(" >> "); + break; + + case Token.URSH: + result.append(" >>> "); + break; + + case Token.TYPEOF: + result.append("typeof "); + break; + + case Token.VOID: + result.append("void "); + break; + + case Token.CONST: + result.append("const "); + break; + + case Token.NOT: + result.append('!'); + break; + + case Token.BITNOT: + result.append('~'); + break; + + case Token.POS: + result.append('+'); + break; + + case Token.NEG: + result.append('-'); + break; + + case Token.INC: + result.append("++"); + break; + + case Token.DEC: + result.append("--"); + break; + + case Token.ADD: + result.append(" + "); + break; + + case Token.SUB: + result.append(" - "); + break; + + case Token.MUL: + result.append(" * "); + break; + + case Token.DIV: + result.append(" / "); + break; + + case Token.MOD: + result.append(" % "); + break; + + case Token.COLONCOLON: + result.append("::"); + break; + + case Token.DOTDOT: + result.append(".."); + break; + + case Token.DOTQUERY: + result.append(".("); + break; + + case Token.XMLATTR: + result.append('@'); + break; + + default: + // If we don't know how to decompile it, raise an exception. + throw new RuntimeException("Token: " + + Token.name(source.charAt(i))); + } + ++i; + } + + if (!toSource) { + // add that trailing newline if it's an outermost function. + if (!justFunctionBody) + result.append('\n'); + } else { + if (topFunctionType == FunctionNode.FUNCTION_EXPRESSION) { + result.append(')'); + } + } + + return result.toString(); + } + + private static int getNext(String source, int length, int i) + { + return (i + 1 < length) ? source.charAt(i + 1) : Token.EOF; + } + + private static int getSourceStringEnd(String source, int offset) + { + return printSourceString(source, offset, false, null); + } + + private static int printSourceString(String source, int offset, + boolean asQuotedString, + StringBuffer sb) + { + int length = source.charAt(offset); + ++offset; + if ((0x8000 & length) != 0) { + length = ((0x7FFF & length) << 16) | source.charAt(offset); + ++offset; + } + if (sb != null) { + String str = source.substring(offset, offset + length); + if (!asQuotedString) { + sb.append(str); + } else { + sb.append('"'); + sb.append(ScriptRuntime.escapeString(str)); + sb.append('"'); + } + } + return offset + length; + } + + private static int printSourceNumber(String source, int offset, + StringBuffer sb) + { + double number = 0.0; + char type = source.charAt(offset); + ++offset; + if (type == 'S') { + if (sb != null) { + int ival = source.charAt(offset); + number = ival; + } + ++offset; + } else if (type == 'J' || type == 'D') { + if (sb != null) { + long lbits; + lbits = (long)source.charAt(offset) << 48; + lbits |= (long)source.charAt(offset + 1) << 32; + lbits |= (long)source.charAt(offset + 2) << 16; + lbits |= source.charAt(offset + 3); + if (type == 'J') { + number = lbits; + } else { + number = Double.longBitsToDouble(lbits); + } + } + offset += 4; + } else { + // Bad source + throw new RuntimeException(); + } + if (sb != null) { + sb.append(ScriptRuntime.numberToString(number, 10)); + } + return offset; + } + + private char[] sourceBuffer = new char[128]; + +// Per script/function source buffer top: parent source does not include a +// nested functions source and uses function index as a reference instead. + private int sourceTop; + +// whether to do a debug print of the source information, when decompiling. + private static final boolean printSource = false; + +} diff --git a/trunk/infrastructure/yuicompressor/src/yuicompressor/org/mozilla/javascript/Parser.java b/trunk/infrastructure/yuicompressor/src/yuicompressor/org/mozilla/javascript/Parser.java new file mode 100644 index 0000000..bf830a8 --- /dev/null +++ b/trunk/infrastructure/yuicompressor/src/yuicompressor/org/mozilla/javascript/Parser.java @@ -0,0 +1,2170 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mike Ang + * Igor Bukanov + * Yuh-Ruey Chen + * Ethan Hugg + * Bob Jervis + * Terry Lucas + * Mike McCabe + * Milen Nankov + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package yuicompressor.org.mozilla.javascript; + +import java.io.Reader; +import java.io.IOException; +import java.util.Hashtable; + +/** + * This class implements the JavaScript parser. + * + * It is based on the C source files jsparse.c and jsparse.h + * in the jsref package. + * + * @see TokenStream + * + * @author Mike McCabe + * @author Brendan Eich + */ + +public class Parser +{ + // TokenInformation flags : currentFlaggedToken stores them together + // with token type + final static int + CLEAR_TI_MASK = 0xFFFF, // mask to clear token information bits + TI_AFTER_EOL = 1 << 16, // first token of the source line + TI_CHECK_LABEL = 1 << 17; // indicates to check for label + + CompilerEnvirons compilerEnv; + private ErrorReporter errorReporter; + private String sourceURI; + boolean calledByCompileFunction; + + private TokenStream ts; + private int currentFlaggedToken; + private int syntaxErrorCount; + + private IRFactory nf; + + private int nestingOfFunction; + + private Decompiler decompiler; + private String encodedSource; + +// The following are per function variables and should be saved/restored +// during function parsing. +// XXX Move to separated class? + ScriptOrFnNode currentScriptOrFn; + private int nestingOfWith; + private Hashtable labelSet; // map of label names into nodes + private ObjArray loopSet; + private ObjArray loopAndSwitchSet; + private boolean hasReturnValue; + private int functionEndFlags; +// end of per function variables + + // Exception to unwind + private static class ParserException extends RuntimeException + { + static final long serialVersionUID = 5882582646773765630L; + } + + public Parser(CompilerEnvirons compilerEnv, ErrorReporter errorReporter) + { + this.compilerEnv = compilerEnv; + this.errorReporter = errorReporter; + } + + protected Decompiler createDecompiler(CompilerEnvirons compilerEnv) + { + return new Decompiler(); + } + + void addStrictWarning(String messageId, String messageArg) + { + if (compilerEnv.isStrictMode()) + addWarning(messageId, messageArg); + } + + void addWarning(String messageId, String messageArg) + { + String message = ScriptRuntime.getMessage1(messageId, messageArg); + if (compilerEnv.reportWarningAsError()) { + ++syntaxErrorCount; + errorReporter.error(message, sourceURI, ts.getLineno(), + ts.getLine(), ts.getOffset()); + } else + errorReporter.warning(message, sourceURI, ts.getLineno(), + ts.getLine(), ts.getOffset()); + } + + void addError(String messageId) + { + ++syntaxErrorCount; + String message = ScriptRuntime.getMessage0(messageId); + errorReporter.error(message, sourceURI, ts.getLineno(), + ts.getLine(), ts.getOffset()); + } + + void addError(String messageId, String messageArg) + { + ++syntaxErrorCount; + String message = ScriptRuntime.getMessage1(messageId, messageArg); + errorReporter.error(message, sourceURI, ts.getLineno(), + ts.getLine(), ts.getOffset()); + } + + RuntimeException reportError(String messageId) + { + addError(messageId); + + // Throw a ParserException exception to unwind the recursive descent + // parse. + throw new ParserException(); + } + + private int peekToken() + throws IOException + { + int tt = currentFlaggedToken; + if (tt == Token.EOF) { + + while ((tt = ts.getToken()) == Token.SPECIALCOMMENT) { + /* Support for JScript conditional comments */ + decompiler.addJScriptConditionalComment(ts.getString()); + } + + if (tt == Token.EOL) { + do { + tt = ts.getToken(); + + if (tt == Token.SPECIALCOMMENT) { + /* Support for JScript conditional comments */ + decompiler.addJScriptConditionalComment(ts.getString()); + } + + } while (tt == Token.EOL || tt == Token.SPECIALCOMMENT); + tt |= TI_AFTER_EOL; + } + currentFlaggedToken = tt; + } + return tt & CLEAR_TI_MASK; + } + + private int peekFlaggedToken() + throws IOException + { + peekToken(); + return currentFlaggedToken; + } + + private void consumeToken() + { + currentFlaggedToken = Token.EOF; + } + + private int nextToken() + throws IOException + { + int tt = peekToken(); + consumeToken(); + return tt; + } + + private int nextFlaggedToken() + throws IOException + { + peekToken(); + int ttFlagged = currentFlaggedToken; + consumeToken(); + return ttFlagged; + } + + private boolean matchToken(int toMatch) + throws IOException + { + int tt = peekToken(); + if (tt != toMatch) { + return false; + } + consumeToken(); + return true; + } + + private int peekTokenOrEOL() + throws IOException + { + int tt = peekToken(); + // Check for last peeked token flags + if ((currentFlaggedToken & TI_AFTER_EOL) != 0) { + tt = Token.EOL; + } + return tt; + } + + private void setCheckForLabel() + { + if ((currentFlaggedToken & CLEAR_TI_MASK) != Token.NAME) + throw Kit.codeBug(); + currentFlaggedToken |= TI_CHECK_LABEL; + } + + private void mustMatchToken(int toMatch, String messageId) + throws IOException, ParserException + { + if (!matchToken(toMatch)) { + reportError(messageId); + } + } + + private void mustHaveXML() + { + if (!compilerEnv.isXmlAvailable()) { + reportError("msg.XML.not.available"); + } + } + + public String getEncodedSource() + { + return encodedSource; + } + + public boolean eof() + { + return ts.eof(); + } + + boolean insideFunction() + { + return nestingOfFunction != 0; + } + + private Node enterLoop(Node loopLabel) + { + Node loop = nf.createLoopNode(loopLabel, ts.getLineno()); + if (loopSet == null) { + loopSet = new ObjArray(); + if (loopAndSwitchSet == null) { + loopAndSwitchSet = new ObjArray(); + } + } + loopSet.push(loop); + loopAndSwitchSet.push(loop); + return loop; + } + + private void exitLoop() + { + loopSet.pop(); + loopAndSwitchSet.pop(); + } + + private Node enterSwitch(Node switchSelector, int lineno) + { + Node switchNode = nf.createSwitch(switchSelector, lineno); + if (loopAndSwitchSet == null) { + loopAndSwitchSet = new ObjArray(); + } + loopAndSwitchSet.push(switchNode); + return switchNode; + } + + private void exitSwitch() + { + loopAndSwitchSet.pop(); + } + + /* + * Build a parse tree from the given sourceString. + * + * @return an Object representing the parsed + * program. If the parse fails, null will be returned. (The + * parse failure will result in a call to the ErrorReporter from + * CompilerEnvirons.) + */ + public ScriptOrFnNode parse(String sourceString, + String sourceURI, int lineno) + { + this.sourceURI = sourceURI; + this.ts = new TokenStream(this, null, sourceString, lineno); + try { + return parse(); + } catch (IOException ex) { + // Should never happen + throw new IllegalStateException(); + } + } + + /* + * Build a parse tree from the given sourceString. + * + * @return an Object representing the parsed + * program. If the parse fails, null will be returned. (The + * parse failure will result in a call to the ErrorReporter from + * CompilerEnvirons.) + */ + public ScriptOrFnNode parse(Reader sourceReader, + String sourceURI, int lineno) + throws IOException + { + this.sourceURI = sourceURI; + this.ts = new TokenStream(this, sourceReader, null, lineno); + return parse(); + } + + private ScriptOrFnNode parse() + throws IOException + { + this.decompiler = createDecompiler(compilerEnv); + this.nf = new IRFactory(this); + currentScriptOrFn = nf.createScript(); + int sourceStartOffset = decompiler.getCurrentOffset(); + this.encodedSource = null; + decompiler.addToken(Token.SCRIPT); + + this.currentFlaggedToken = Token.EOF; + this.syntaxErrorCount = 0; + + int baseLineno = ts.getLineno(); // line number where source starts + + /* so we have something to add nodes to until + * we've collected all the source */ + Node pn = nf.createLeaf(Token.BLOCK); + + try { + for (;;) { + int tt = peekToken(); + + if (tt <= Token.EOF) { + break; + } + + Node n; + if (tt == Token.FUNCTION) { + consumeToken(); + try { + n = function(calledByCompileFunction + ? FunctionNode.FUNCTION_EXPRESSION + : FunctionNode.FUNCTION_STATEMENT); + } catch (ParserException e) { + break; + } + } else { + n = statement(); + } + nf.addChildToBack(pn, n); + } + } catch (StackOverflowError ex) { + String msg = ScriptRuntime.getMessage0( + "msg.too.deep.parser.recursion"); + throw Context.reportRuntimeError(msg, sourceURI, + ts.getLineno(), null, 0); + } + + if (this.syntaxErrorCount != 0) { + String msg = String.valueOf(this.syntaxErrorCount); + msg = ScriptRuntime.getMessage1("msg.got.syntax.errors", msg); + throw errorReporter.runtimeError(msg, sourceURI, baseLineno, + null, 0); + } + + currentScriptOrFn.setSourceName(sourceURI); + currentScriptOrFn.setBaseLineno(baseLineno); + currentScriptOrFn.setEndLineno(ts.getLineno()); + + int sourceEndOffset = decompiler.getCurrentOffset(); + currentScriptOrFn.setEncodedSourceBounds(sourceStartOffset, + sourceEndOffset); + + nf.initScript(currentScriptOrFn, pn); + + if (compilerEnv.isGeneratingSource()) { + encodedSource = decompiler.getEncodedSource(); + } + this.decompiler = null; // It helps GC + + return currentScriptOrFn; + } + + /* + * The C version of this function takes an argument list, + * which doesn't seem to be needed for tree generation... + * it'd only be useful for checking argument hiding, which + * I'm not doing anyway... + */ + private Node parseFunctionBody() + throws IOException + { + ++nestingOfFunction; + Node pn = nf.createBlock(ts.getLineno()); + try { + bodyLoop: for (;;) { + Node n; + int tt = peekToken(); + switch (tt) { + case Token.ERROR: + case Token.EOF: + case Token.RC: + break bodyLoop; + + case Token.FUNCTION: + consumeToken(); + n = function(FunctionNode.FUNCTION_STATEMENT); + break; + default: + n = statement(); + break; + } + nf.addChildToBack(pn, n); + } + } catch (ParserException e) { + // Ignore it + } finally { + --nestingOfFunction; + } + + return pn; + } + + private Node function(int functionType) + throws IOException, ParserException + { + int syntheticType = functionType; + int baseLineno = ts.getLineno(); // line number where source starts + + int functionSourceStart = decompiler.markFunctionStart(functionType); + String name; + Node memberExprNode = null; + if (matchToken(Token.NAME)) { + name = ts.getString(); + decompiler.addName(name); + if (!matchToken(Token.LP)) { + if (compilerEnv.isAllowMemberExprAsFunctionName()) { + // Extension to ECMA: if 'function ' does not follow + // by '(', assume starts memberExpr + Node memberExprHead = nf.createName(name); + name = ""; + memberExprNode = memberExprTail(false, memberExprHead); + } + mustMatchToken(Token.LP, "msg.no.paren.parms"); + } + } else if (matchToken(Token.LP)) { + // Anonymous function + name = ""; + } else { + name = ""; + if (compilerEnv.isAllowMemberExprAsFunctionName()) { + // Note that memberExpr can not start with '(' like + // in function (1+2).toString(), because 'function (' already + // processed as anonymous function + memberExprNode = memberExpr(false); + } + mustMatchToken(Token.LP, "msg.no.paren.parms"); + } + + if (memberExprNode != null) { + syntheticType = FunctionNode.FUNCTION_EXPRESSION; + } + + boolean nested = insideFunction(); + + FunctionNode fnNode = nf.createFunction(name); + if (nested || nestingOfWith > 0) { + // 1. Nested functions are not affected by the dynamic scope flag + // as dynamic scope is already a parent of their scope. + // 2. Functions defined under the with statement also immune to + // this setup, in which case dynamic scope is ignored in favor + // of with object. + fnNode.itsIgnoreDynamicScope = true; + } + + int functionIndex = currentScriptOrFn.addFunction(fnNode); + + int functionSourceEnd; + + ScriptOrFnNode savedScriptOrFn = currentScriptOrFn; + currentScriptOrFn = fnNode; + int savedNestingOfWith = nestingOfWith; + nestingOfWith = 0; + Hashtable savedLabelSet = labelSet; + labelSet = null; + ObjArray savedLoopSet = loopSet; + loopSet = null; + ObjArray savedLoopAndSwitchSet = loopAndSwitchSet; + loopAndSwitchSet = null; + boolean savedHasReturnValue = hasReturnValue; + int savedFunctionEndFlags = functionEndFlags; + + Node body; + try { + decompiler.addToken(Token.LP); + if (!matchToken(Token.RP)) { + boolean first = true; + do { + if (!first) + decompiler.addToken(Token.COMMA); + first = false; + mustMatchToken(Token.NAME, "msg.no.parm"); + String s = ts.getString(); + if (fnNode.hasParamOrVar(s)) { + addWarning("msg.dup.parms", s); + } + fnNode.addParam(s); + decompiler.addName(s); + } while (matchToken(Token.COMMA)); + + mustMatchToken(Token.RP, "msg.no.paren.after.parms"); + } + decompiler.addToken(Token.RP); + + mustMatchToken(Token.LC, "msg.no.brace.body"); + decompiler.addEOL(Token.LC); + body = parseFunctionBody(); + mustMatchToken(Token.RC, "msg.no.brace.after.body"); + + if (compilerEnv.isStrictMode() && !body.hasConsistentReturnUsage()) + { + String msg = name.length() > 0 ? "msg.no.return.value" + : "msg.anon.no.return.value"; + addStrictWarning(msg, name); + } + + decompiler.addToken(Token.RC); + functionSourceEnd = decompiler.markFunctionEnd(functionSourceStart); + if (functionType != FunctionNode.FUNCTION_EXPRESSION) { + // Add EOL only if function is not part of expression + // since it gets SEMI + EOL from Statement in that case + decompiler.addToken(Token.EOL); + } + } + finally { + hasReturnValue = savedHasReturnValue; + functionEndFlags = savedFunctionEndFlags; + loopAndSwitchSet = savedLoopAndSwitchSet; + loopSet = savedLoopSet; + labelSet = savedLabelSet; + nestingOfWith = savedNestingOfWith; + currentScriptOrFn = savedScriptOrFn; + } + + fnNode.setEncodedSourceBounds(functionSourceStart, functionSourceEnd); + fnNode.setSourceName(sourceURI); + fnNode.setBaseLineno(baseLineno); + fnNode.setEndLineno(ts.getLineno()); + + if (name != null) { + int index = currentScriptOrFn.getParamOrVarIndex(name); + if (index >= 0 && index < currentScriptOrFn.getParamCount()) + addStrictWarning("msg.var.hides.arg", name); + } + + Node pn = nf.initFunction(fnNode, functionIndex, body, syntheticType); + if (memberExprNode != null) { + pn = nf.createAssignment(Token.ASSIGN, memberExprNode, pn); + if (functionType != FunctionNode.FUNCTION_EXPRESSION) { + // XXX check JScript behavior: should it be createExprStatement? + pn = nf.createExprStatementNoReturn(pn, baseLineno); + } + } + return pn; + } + + private Node statements() + throws IOException + { + Node pn = nf.createBlock(ts.getLineno()); + + int tt; + while((tt = peekToken()) > Token.EOF && tt != Token.RC) { + nf.addChildToBack(pn, statement()); + } + + return pn; + } + + private Node condition() + throws IOException, ParserException + { + mustMatchToken(Token.LP, "msg.no.paren.cond"); + decompiler.addToken(Token.LP); + Node pn = expr(false); + mustMatchToken(Token.RP, "msg.no.paren.after.cond"); + decompiler.addToken(Token.RP); + + // Report strict warning on code like "if (a = 7) ...". Suppress the + // warning if the condition is parenthesized, like "if ((a = 7)) ...". + if (pn.getProp(Node.PARENTHESIZED_PROP) == null && + (pn.getType() == Token.SETNAME || pn.getType() == Token.SETPROP || + pn.getType() == Token.SETELEM)) + { + addStrictWarning("msg.equal.as.assign", ""); + } + return pn; + } + + // match a NAME; return null if no match. + private Node matchJumpLabelName() + throws IOException, ParserException + { + Node label = null; + + int tt = peekTokenOrEOL(); + if (tt == Token.NAME) { + consumeToken(); + String name = ts.getString(); + decompiler.addName(name); + if (labelSet != null) { + label = (Node)labelSet.get(name); + } + if (label == null) { + reportError("msg.undef.label"); + } + } + + return label; + } + + private Node statement() + throws IOException + { + try { + Node pn = statementHelper(null); + if (pn != null) { + if (compilerEnv.isStrictMode() && !pn.hasSideEffects()) + addStrictWarning("msg.no.side.effects", ""); + return pn; + } + } catch (ParserException e) { } + + // skip to end of statement + int lineno = ts.getLineno(); + guessingStatementEnd: for (;;) { + int tt = peekTokenOrEOL(); + consumeToken(); + switch (tt) { + case Token.ERROR: + case Token.EOF: + case Token.EOL: + case Token.SEMI: + break guessingStatementEnd; + } + } + return nf.createExprStatement(nf.createName("error"), lineno); + } + + /** + * Whether the "catch (e: e instanceof Exception) { ... }" syntax + * is implemented. + */ + + private Node statementHelper(Node statementLabel) + throws IOException, ParserException + { + Node pn = null; + + int tt; + + tt = peekToken(); + + switch(tt) { + case Token.IF: { + consumeToken(); + + decompiler.addToken(Token.IF); + int lineno = ts.getLineno(); + Node cond = condition(); + decompiler.addEOL(Token.LC); + Node ifTrue = statement(); + Node ifFalse = null; + if (matchToken(Token.ELSE)) { + decompiler.addToken(Token.RC); + decompiler.addToken(Token.ELSE); + decompiler.addEOL(Token.LC); + ifFalse = statement(); + } + decompiler.addEOL(Token.RC); + pn = nf.createIf(cond, ifTrue, ifFalse, lineno); + return pn; + } + + case Token.SWITCH: { + consumeToken(); + + decompiler.addToken(Token.SWITCH); + int lineno = ts.getLineno(); + mustMatchToken(Token.LP, "msg.no.paren.switch"); + decompiler.addToken(Token.LP); + pn = enterSwitch(expr(false), lineno); + try { + mustMatchToken(Token.RP, "msg.no.paren.after.switch"); + decompiler.addToken(Token.RP); + mustMatchToken(Token.LC, "msg.no.brace.switch"); + decompiler.addEOL(Token.LC); + + boolean hasDefault = false; + switchLoop: for (;;) { + tt = nextToken(); + Node caseExpression; + switch (tt) { + case Token.RC: + break switchLoop; + + case Token.CASE: + decompiler.addToken(Token.CASE); + caseExpression = expr(false); + mustMatchToken(Token.COLON, "msg.no.colon.case"); + decompiler.addEOL(Token.COLON); + break; + + case Token.DEFAULT: + if (hasDefault) { + reportError("msg.double.switch.default"); + } + decompiler.addToken(Token.DEFAULT); + hasDefault = true; + caseExpression = null; + mustMatchToken(Token.COLON, "msg.no.colon.case"); + decompiler.addEOL(Token.COLON); + break; + + default: + reportError("msg.bad.switch"); + break switchLoop; + } + + Node block = nf.createLeaf(Token.BLOCK); + while ((tt = peekToken()) != Token.RC + && tt != Token.CASE + && tt != Token.DEFAULT + && tt != Token.EOF) + { + nf.addChildToBack(block, statement()); + } + + // caseExpression == null => add default lable + nf.addSwitchCase(pn, caseExpression, block); + } + decompiler.addEOL(Token.RC); + nf.closeSwitch(pn); + } finally { + exitSwitch(); + } + return pn; + } + + case Token.WHILE: { + consumeToken(); + decompiler.addToken(Token.WHILE); + + Node loop = enterLoop(statementLabel); + try { + Node cond = condition(); + decompiler.addEOL(Token.LC); + Node body = statement(); + decompiler.addEOL(Token.RC); + pn = nf.createWhile(loop, cond, body); + } finally { + exitLoop(); + } + return pn; + } + + case Token.DO: { + consumeToken(); + decompiler.addToken(Token.DO); + decompiler.addEOL(Token.LC); + + Node loop = enterLoop(statementLabel); + try { + Node body = statement(); + decompiler.addToken(Token.RC); + mustMatchToken(Token.WHILE, "msg.no.while.do"); + decompiler.addToken(Token.WHILE); + Node cond = condition(); + pn = nf.createDoWhile(loop, body, cond); + } finally { + exitLoop(); + } + // Always auto-insert semicon to follow SpiderMonkey: + // It is required by EMAScript but is ignored by the rest of + // world, see bug 238945 + matchToken(Token.SEMI); + decompiler.addEOL(Token.SEMI); + return pn; + } + + case Token.FOR: { + consumeToken(); + boolean isForEach = false; + decompiler.addToken(Token.FOR); + + Node loop = enterLoop(statementLabel); + try { + + Node init; // Node init is also foo in 'foo in Object' + Node cond; // Node cond is also object in 'foo in Object' + Node incr = null; // to kill warning + Node body; + + // See if this is a for each () instead of just a for () + if (matchToken(Token.NAME)) { + decompiler.addName(ts.getString()); + if (ts.getString().equals("each")) { + isForEach = true; + } else { + reportError("msg.no.paren.for"); + } + } + + mustMatchToken(Token.LP, "msg.no.paren.for"); + decompiler.addToken(Token.LP); + tt = peekToken(); + if (tt == Token.SEMI) { + init = nf.createLeaf(Token.EMPTY); + } else { + if (tt == Token.VAR) { + // set init to a var list or initial + consumeToken(); // consume the 'var' token + init = variables(Token.FOR); + } + else { + init = expr(true); + } + } + + if (matchToken(Token.IN)) { + decompiler.addToken(Token.IN); + // 'cond' is the object over which we're iterating + cond = expr(false); + } else { // ordinary for loop + mustMatchToken(Token.SEMI, "msg.no.semi.for"); + decompiler.addToken(Token.SEMI); + if (peekToken() == Token.SEMI) { + // no loop condition + cond = nf.createLeaf(Token.EMPTY); + } else { + cond = expr(false); + } + + mustMatchToken(Token.SEMI, "msg.no.semi.for.cond"); + decompiler.addToken(Token.SEMI); + if (peekToken() == Token.RP) { + incr = nf.createLeaf(Token.EMPTY); + } else { + incr = expr(false); + } + } + + mustMatchToken(Token.RP, "msg.no.paren.for.ctrl"); + decompiler.addToken(Token.RP); + decompiler.addEOL(Token.LC); + body = statement(); + decompiler.addEOL(Token.RC); + + if (incr == null) { + // cond could be null if 'in obj' got eaten + // by the init node. + pn = nf.createForIn(loop, init, cond, body, isForEach); + } else { + pn = nf.createFor(loop, init, cond, incr, body); + } + } finally { + exitLoop(); + } + return pn; + } + + case Token.TRY: { + consumeToken(); + int lineno = ts.getLineno(); + + Node tryblock; + Node catchblocks = null; + Node finallyblock = null; + + decompiler.addToken(Token.TRY); + decompiler.addEOL(Token.LC); + tryblock = statement(); + decompiler.addEOL(Token.RC); + + catchblocks = nf.createLeaf(Token.BLOCK); + + boolean sawDefaultCatch = false; + int peek = peekToken(); + if (peek == Token.CATCH) { + while (matchToken(Token.CATCH)) { + if (sawDefaultCatch) { + reportError("msg.catch.unreachable"); + } + decompiler.addToken(Token.CATCH); + mustMatchToken(Token.LP, "msg.no.paren.catch"); + decompiler.addToken(Token.LP); + + mustMatchToken(Token.NAME, "msg.bad.catchcond"); + String varName = ts.getString(); + decompiler.addName(varName); + + Node catchCond = null; + if (matchToken(Token.IF)) { + decompiler.addToken(Token.IF); + catchCond = expr(false); + } else { + sawDefaultCatch = true; + } + + mustMatchToken(Token.RP, "msg.bad.catchcond"); + decompiler.addToken(Token.RP); + mustMatchToken(Token.LC, "msg.no.brace.catchblock"); + decompiler.addEOL(Token.LC); + + nf.addChildToBack(catchblocks, + nf.createCatch(varName, catchCond, + statements(), + ts.getLineno())); + + mustMatchToken(Token.RC, "msg.no.brace.after.body"); + decompiler.addEOL(Token.RC); + } + } else if (peek != Token.FINALLY) { + mustMatchToken(Token.FINALLY, "msg.try.no.catchfinally"); + } + + if (matchToken(Token.FINALLY)) { + decompiler.addToken(Token.FINALLY); + decompiler.addEOL(Token.LC); + finallyblock = statement(); + decompiler.addEOL(Token.RC); + } + + pn = nf.createTryCatchFinally(tryblock, catchblocks, + finallyblock, lineno); + + return pn; + } + + case Token.THROW: { + consumeToken(); + if (peekTokenOrEOL() == Token.EOL) { + // ECMAScript does not allow new lines before throw expression, + // see bug 256617 + reportError("msg.bad.throw.eol"); + } + + int lineno = ts.getLineno(); + decompiler.addToken(Token.THROW); + pn = nf.createThrow(expr(false), lineno); + break; + } + + case Token.BREAK: { + consumeToken(); + int lineno = ts.getLineno(); + + decompiler.addToken(Token.BREAK); + + // matchJumpLabelName only matches if there is one + Node breakStatement = matchJumpLabelName(); + if (breakStatement == null) { + if (loopAndSwitchSet == null || loopAndSwitchSet.size() == 0) { + reportError("msg.bad.break"); + return null; + } + breakStatement = (Node)loopAndSwitchSet.peek(); + } + pn = nf.createBreak(breakStatement, lineno); + break; + } + + case Token.CONTINUE: { + consumeToken(); + int lineno = ts.getLineno(); + + decompiler.addToken(Token.CONTINUE); + + Node loop; + // matchJumpLabelName only matches if there is one + Node label = matchJumpLabelName(); + if (label == null) { + if (loopSet == null || loopSet.size() == 0) { + reportError("msg.continue.outside"); + return null; + } + loop = (Node)loopSet.peek(); + } else { + loop = nf.getLabelLoop(label); + if (loop == null) { + reportError("msg.continue.nonloop"); + return null; + } + } + pn = nf.createContinue(loop, lineno); + break; + } + + case Token.WITH: { + consumeToken(); + + decompiler.addToken(Token.WITH); + int lineno = ts.getLineno(); + mustMatchToken(Token.LP, "msg.no.paren.with"); + decompiler.addToken(Token.LP); + Node obj = expr(false); + mustMatchToken(Token.RP, "msg.no.paren.after.with"); + decompiler.addToken(Token.RP); + decompiler.addEOL(Token.LC); + + ++nestingOfWith; + Node body; + try { + body = statement(); + } finally { + --nestingOfWith; + } + + decompiler.addEOL(Token.RC); + + pn = nf.createWith(obj, body, lineno); + return pn; + } + + case Token.CONST: + case Token.VAR: { + consumeToken(); + pn = variables(tt); + break; + } + + case Token.RETURN: { + if (!insideFunction()) { + reportError("msg.bad.return"); + } + consumeToken(); + decompiler.addToken(Token.RETURN); + int lineno = ts.getLineno(); + + Node retExpr; + /* This is ugly, but we don't want to require a semicolon. */ + tt = peekTokenOrEOL(); + switch (tt) { + case Token.SEMI: + case Token.RC: + case Token.EOF: + case Token.EOL: + case Token.ERROR: + retExpr = null; + break; + default: + retExpr = expr(false); + hasReturnValue = true; + } + pn = nf.createReturn(retExpr, lineno); + + // see if we need a strict mode warning + if (retExpr == null) { + if (functionEndFlags == Node.END_RETURNS_VALUE) + addStrictWarning("msg.return.inconsistent", ""); + + functionEndFlags |= Node.END_RETURNS; + } else { + if (functionEndFlags == Node.END_RETURNS) + addStrictWarning("msg.return.inconsistent", ""); + + functionEndFlags |= Node.END_RETURNS_VALUE; + } + + break; + } + + case Token.LC: + consumeToken(); + if (statementLabel != null) { + decompiler.addToken(Token.LC); + } + pn = statements(); + mustMatchToken(Token.RC, "msg.no.brace.block"); + if (statementLabel != null) { + decompiler.addEOL(Token.RC); + } + return pn; + + case Token.ERROR: + // Fall thru, to have a node for error recovery to work on + case Token.SEMI: + consumeToken(); + pn = nf.createLeaf(Token.EMPTY); + return pn; + + case Token.FUNCTION: { + consumeToken(); + pn = function(FunctionNode.FUNCTION_EXPRESSION_STATEMENT); + return pn; + } + + case Token.DEFAULT : + consumeToken(); + mustHaveXML(); + + decompiler.addToken(Token.DEFAULT); + int nsLine = ts.getLineno(); + + if (!(matchToken(Token.NAME) + && ts.getString().equals("xml"))) + { + reportError("msg.bad.namespace"); + } + decompiler.addName(" xml"); + + if (!(matchToken(Token.NAME) + && ts.getString().equals("namespace"))) + { + reportError("msg.bad.namespace"); + } + decompiler.addName(" namespace"); + + if (!matchToken(Token.ASSIGN)) { + reportError("msg.bad.namespace"); + } + decompiler.addToken(Token.ASSIGN); + + Node expr = expr(false); + pn = nf.createDefaultNamespace(expr, nsLine); + break; + + case Token.NAME: { + int lineno = ts.getLineno(); + String name = ts.getString(); + setCheckForLabel(); + pn = expr(false); + if (pn.getType() != Token.LABEL) { + pn = nf.createExprStatement(pn, lineno); + } else { + // Parsed the label: push back token should be + // colon that primaryExpr left untouched. + if (peekToken() != Token.COLON) Kit.codeBug(); + consumeToken(); + // depend on decompiling lookahead to guess that that + // last name was a label. + decompiler.addName(name); + decompiler.addEOL(Token.COLON); + + if (labelSet == null) { + labelSet = new Hashtable(); + } else if (labelSet.containsKey(name)) { + reportError("msg.dup.label"); + } + + boolean firstLabel; + if (statementLabel == null) { + firstLabel = true; + statementLabel = pn; + } else { + // Discard multiple label nodes and use only + // the first: it allows to simplify IRFactory + firstLabel = false; + } + labelSet.put(name, statementLabel); + try { + pn = statementHelper(statementLabel); + } finally { + labelSet.remove(name); + } + if (firstLabel) { + pn = nf.createLabeledStatement(statementLabel, pn); + } + return pn; + } + break; + } + + default: { + int lineno = ts.getLineno(); + pn = expr(false); + pn = nf.createExprStatement(pn, lineno); + break; + } + } + + int ttFlagged = peekFlaggedToken(); + switch (ttFlagged & CLEAR_TI_MASK) { + case Token.SEMI: + // Consume ';' as a part of expression + consumeToken(); + break; + case Token.ERROR: + case Token.EOF: + case Token.RC: + // Autoinsert ; + break; + default: + if ((ttFlagged & TI_AFTER_EOL) == 0) { + // Report error if no EOL or autoinsert ; otherwise + reportError("msg.no.semi.stmt"); + } + break; + } + decompiler.addEOL(Token.SEMI); + + return pn; + } + + /** + * Parse a 'var' or 'const' statement, or a 'var' init list in a for + * statement. + * @param context A token value: either VAR, CONST or FOR depending on + * context. + * @return The parsed statement + * @throws IOException + * @throws ParserException + */ + private Node variables(int context) + throws IOException, ParserException + { + Node pn; + boolean first = true; + + if (context == Token.CONST){ + pn = nf.createVariables(Token.CONST, ts.getLineno()); + decompiler.addToken(Token.CONST); + } else { + pn = nf.createVariables(Token.VAR, ts.getLineno()); + decompiler.addToken(Token.VAR); + } + + for (;;) { + Node name; + Node init; + mustMatchToken(Token.NAME, "msg.bad.var"); + String s = ts.getString(); + + if (!first) + decompiler.addToken(Token.COMMA); + first = false; + + decompiler.addName(s); + + if (context == Token.CONST) { + if (!currentScriptOrFn.addConst(s)) { + // We know it's already defined, since addConst passes if + // it's not defined at all. The addVar call just confirms + // what it is. + if (currentScriptOrFn.addVar(s) != ScriptOrFnNode.DUPLICATE_CONST) + addError("msg.var.redecl", s); + else + addError("msg.const.redecl", s); + } + } else { + int dupState = currentScriptOrFn.addVar(s); + if (dupState == ScriptOrFnNode.DUPLICATE_CONST) + addError("msg.const.redecl", s); + else if (dupState == ScriptOrFnNode.DUPLICATE_PARAMETER) + addStrictWarning("msg.var.hides.arg", s); + else if (dupState == ScriptOrFnNode.DUPLICATE_VAR) + addStrictWarning("msg.var.redecl", s); + } + name = nf.createName(s); + + // omitted check for argument hiding + + if (matchToken(Token.ASSIGN)) { + decompiler.addToken(Token.ASSIGN); + + init = assignExpr(context == Token.FOR); + nf.addChildToBack(name, init); + } + nf.addChildToBack(pn, name); + if (!matchToken(Token.COMMA)) + break; + } + return pn; + } + + private Node expr(boolean inForInit) + throws IOException, ParserException + { + Node pn = assignExpr(inForInit); + while (matchToken(Token.COMMA)) { + decompiler.addToken(Token.COMMA); + if (compilerEnv.isStrictMode() && !pn.hasSideEffects()) + addStrictWarning("msg.no.side.effects", ""); + pn = nf.createBinary(Token.COMMA, pn, assignExpr(inForInit)); + } + return pn; + } + + private Node assignExpr(boolean inForInit) + throws IOException, ParserException + { + Node pn = condExpr(inForInit); + + int tt = peekToken(); + if (Token.FIRST_ASSIGN <= tt && tt <= Token.LAST_ASSIGN) { + consumeToken(); + decompiler.addToken(tt); + pn = nf.createAssignment(tt, pn, assignExpr(inForInit)); + } + + return pn; + } + + private Node condExpr(boolean inForInit) + throws IOException, ParserException + { + Node pn = orExpr(inForInit); + + if (matchToken(Token.HOOK)) { + decompiler.addToken(Token.HOOK); + Node ifTrue = assignExpr(false); + mustMatchToken(Token.COLON, "msg.no.colon.cond"); + decompiler.addToken(Token.COLON); + Node ifFalse = assignExpr(inForInit); + return nf.createCondExpr(pn, ifTrue, ifFalse); + } + + return pn; + } + + private Node orExpr(boolean inForInit) + throws IOException, ParserException + { + Node pn = andExpr(inForInit); + if (matchToken(Token.OR)) { + decompiler.addToken(Token.OR); + pn = nf.createBinary(Token.OR, pn, orExpr(inForInit)); + } + + return pn; + } + + private Node andExpr(boolean inForInit) + throws IOException, ParserException + { + Node pn = bitOrExpr(inForInit); + if (matchToken(Token.AND)) { + decompiler.addToken(Token.AND); + pn = nf.createBinary(Token.AND, pn, andExpr(inForInit)); + } + + return pn; + } + + private Node bitOrExpr(boolean inForInit) + throws IOException, ParserException + { + Node pn = bitXorExpr(inForInit); + while (matchToken(Token.BITOR)) { + decompiler.addToken(Token.BITOR); + pn = nf.createBinary(Token.BITOR, pn, bitXorExpr(inForInit)); + } + return pn; + } + + private Node bitXorExpr(boolean inForInit) + throws IOException, ParserException + { + Node pn = bitAndExpr(inForInit); + while (matchToken(Token.BITXOR)) { + decompiler.addToken(Token.BITXOR); + pn = nf.createBinary(Token.BITXOR, pn, bitAndExpr(inForInit)); + } + return pn; + } + + private Node bitAndExpr(boolean inForInit) + throws IOException, ParserException + { + Node pn = eqExpr(inForInit); + while (matchToken(Token.BITAND)) { + decompiler.addToken(Token.BITAND); + pn = nf.createBinary(Token.BITAND, pn, eqExpr(inForInit)); + } + return pn; + } + + private Node eqExpr(boolean inForInit) + throws IOException, ParserException + { + Node pn = relExpr(inForInit); + for (;;) { + int tt = peekToken(); + switch (tt) { + case Token.EQ: + case Token.NE: + case Token.SHEQ: + case Token.SHNE: + consumeToken(); + int decompilerToken = tt; + int parseToken = tt; + if (compilerEnv.getLanguageVersion() == Context.VERSION_1_2) { + // JavaScript 1.2 uses shallow equality for == and != . + // In addition, convert === and !== for decompiler into + // == and != since the decompiler is supposed to show + // canonical source and in 1.2 ===, !== are allowed + // only as an alias to ==, !=. + switch (tt) { + case Token.EQ: + parseToken = Token.SHEQ; + break; + case Token.NE: + parseToken = Token.SHNE; + break; + case Token.SHEQ: + decompilerToken = Token.EQ; + break; + case Token.SHNE: + decompilerToken = Token.NE; + break; + } + } + decompiler.addToken(decompilerToken); + pn = nf.createBinary(parseToken, pn, relExpr(inForInit)); + continue; + } + break; + } + return pn; + } + + private Node relExpr(boolean inForInit) + throws IOException, ParserException + { + Node pn = shiftExpr(); + for (;;) { + int tt = peekToken(); + switch (tt) { + case Token.IN: + if (inForInit) + break; + // fall through + case Token.INSTANCEOF: + case Token.LE: + case Token.LT: + case Token.GE: + case Token.GT: + consumeToken(); + decompiler.addToken(tt); + pn = nf.createBinary(tt, pn, shiftExpr()); + continue; + } + break; + } + return pn; + } + + private Node shiftExpr() + throws IOException, ParserException + { + Node pn = addExpr(); + for (;;) { + int tt = peekToken(); + switch (tt) { + case Token.LSH: + case Token.URSH: + case Token.RSH: + consumeToken(); + decompiler.addToken(tt); + pn = nf.createBinary(tt, pn, addExpr()); + continue; + } + break; + } + return pn; + } + + private Node addExpr() + throws IOException, ParserException + { + Node pn = mulExpr(); + for (;;) { + int tt = peekToken(); + if (tt == Token.ADD || tt == Token.SUB) { + consumeToken(); + decompiler.addToken(tt); + // flushNewLines + pn = nf.createBinary(tt, pn, mulExpr()); + continue; + } + break; + } + + return pn; + } + + private Node mulExpr() + throws IOException, ParserException + { + Node pn = unaryExpr(); + for (;;) { + int tt = peekToken(); + switch (tt) { + case Token.MUL: + case Token.DIV: + case Token.MOD: + consumeToken(); + decompiler.addToken(tt); + pn = nf.createBinary(tt, pn, unaryExpr()); + continue; + } + break; + } + + return pn; + } + + private Node unaryExpr() + throws IOException, ParserException + { + int tt; + + tt = peekToken(); + + switch(tt) { + case Token.VOID: + case Token.NOT: + case Token.BITNOT: + case Token.TYPEOF: + consumeToken(); + decompiler.addToken(tt); + return nf.createUnary(tt, unaryExpr()); + + case Token.ADD: + consumeToken(); + // Convert to special POS token in decompiler and parse tree + decompiler.addToken(Token.POS); + return nf.createUnary(Token.POS, unaryExpr()); + + case Token.SUB: + consumeToken(); + // Convert to special NEG token in decompiler and parse tree + decompiler.addToken(Token.NEG); + return nf.createUnary(Token.NEG, unaryExpr()); + + case Token.INC: + case Token.DEC: + consumeToken(); + decompiler.addToken(tt); + return nf.createIncDec(tt, false, memberExpr(true)); + + case Token.DELPROP: + consumeToken(); + decompiler.addToken(Token.DELPROP); + return nf.createUnary(Token.DELPROP, unaryExpr()); + + case Token.ERROR: + consumeToken(); + break; + + // XML stream encountered in expression. + case Token.LT: + if (compilerEnv.isXmlAvailable()) { + consumeToken(); + Node pn = xmlInitializer(); + return memberExprTail(true, pn); + } + // Fall thru to the default handling of RELOP + + default: + Node pn = memberExpr(true); + + // Don't look across a newline boundary for a postfix incop. + tt = peekTokenOrEOL(); + if (tt == Token.INC || tt == Token.DEC) { + consumeToken(); + decompiler.addToken(tt); + return nf.createIncDec(tt, true, pn); + } + return pn; + } + return nf.createName("err"); // Only reached on error. Try to continue. + + } + + private Node xmlInitializer() throws IOException + { + int tt = ts.getFirstXMLToken(); + if (tt != Token.XML && tt != Token.XMLEND) { + reportError("msg.syntax"); + return null; + } + + /* Make a NEW node to append to. */ + Node pnXML = nf.createLeaf(Token.NEW); + + String xml = ts.getString(); + boolean fAnonymous = xml.trim().startsWith("<>"); + + Node pn = nf.createName(fAnonymous ? "XMLList" : "XML"); + nf.addChildToBack(pnXML, pn); + + pn = null; + Node expr; + for (;;tt = ts.getNextXMLToken()) { + switch (tt) { + case Token.XML: + xml = ts.getString(); + decompiler.addName(xml); + mustMatchToken(Token.LC, "msg.syntax"); + decompiler.addToken(Token.LC); + expr = (peekToken() == Token.RC) + ? nf.createString("") + : expr(false); + mustMatchToken(Token.RC, "msg.syntax"); + decompiler.addToken(Token.RC); + if (pn == null) { + pn = nf.createString(xml); + } else { + pn = nf.createBinary(Token.ADD, pn, nf.createString(xml)); + } + if (ts.isXMLAttribute()) { + /* Need to put the result in double quotes */ + expr = nf.createUnary(Token.ESCXMLATTR, expr); + Node prepend = nf.createBinary(Token.ADD, + nf.createString("\""), + expr); + expr = nf.createBinary(Token.ADD, + prepend, + nf.createString("\"")); + } else { + expr = nf.createUnary(Token.ESCXMLTEXT, expr); + } + pn = nf.createBinary(Token.ADD, pn, expr); + break; + case Token.XMLEND: + xml = ts.getString(); + decompiler.addName(xml); + if (pn == null) { + pn = nf.createString(xml); + } else { + pn = nf.createBinary(Token.ADD, pn, nf.createString(xml)); + } + + nf.addChildToBack(pnXML, pn); + return pnXML; + default: + reportError("msg.syntax"); + return null; + } + } + } + + private void argumentList(Node listNode) + throws IOException, ParserException + { + boolean matched; + matched = matchToken(Token.RP); + if (!matched) { + boolean first = true; + do { + if (!first) + decompiler.addToken(Token.COMMA); + first = false; + nf.addChildToBack(listNode, assignExpr(false)); + } while (matchToken(Token.COMMA)); + + mustMatchToken(Token.RP, "msg.no.paren.arg"); + } + decompiler.addToken(Token.RP); + } + + private Node memberExpr(boolean allowCallSyntax) + throws IOException, ParserException + { + int tt; + + Node pn; + + /* Check for new expressions. */ + tt = peekToken(); + if (tt == Token.NEW) { + /* Eat the NEW token. */ + consumeToken(); + decompiler.addToken(Token.NEW); + + /* Make a NEW node to append to. */ + pn = nf.createCallOrNew(Token.NEW, memberExpr(false)); + + if (matchToken(Token.LP)) { + decompiler.addToken(Token.LP); + /* Add the arguments to pn, if any are supplied. */ + argumentList(pn); + } + + /* XXX there's a check in the C source against + * "too many constructor arguments" - how many + * do we claim to support? + */ + + /* Experimental syntax: allow an object literal to follow a new expression, + * which will mean a kind of anonymous class built with the JavaAdapter. + * the object literal will be passed as an additional argument to the constructor. + */ + tt = peekToken(); + if (tt == Token.LC) { + nf.addChildToBack(pn, primaryExpr()); + } + } else { + pn = primaryExpr(); + } + + return memberExprTail(allowCallSyntax, pn); + } + + private Node memberExprTail(boolean allowCallSyntax, Node pn) + throws IOException, ParserException + { + tailLoop: + for (;;) { + int tt = peekToken(); + switch (tt) { + + case Token.DOT: + case Token.DOTDOT: + { + int memberTypeFlags; + String s; + + consumeToken(); + decompiler.addToken(tt); + memberTypeFlags = 0; + if (tt == Token.DOTDOT) { + mustHaveXML(); + memberTypeFlags = Node.DESCENDANTS_FLAG; + } + if (!compilerEnv.isXmlAvailable()) { + mustMatchToken(Token.NAME, "msg.no.name.after.dot"); + s = ts.getString(); + decompiler.addName(s); + pn = nf.createPropertyGet(pn, null, s, memberTypeFlags); + break; + } + + tt = nextToken(); + switch (tt) { + // handles: name, ns::name, ns::*, ns::[expr] + case Token.NAME: + s = ts.getString(); + decompiler.addName(s); + pn = propertyName(pn, s, memberTypeFlags); + break; + + // handles: *, *::name, *::*, *::[expr] + case Token.MUL: + decompiler.addName("*"); + pn = propertyName(pn, "*", memberTypeFlags); + break; + + // handles: '@attr', '@ns::attr', '@ns::*', '@ns::*', + // '@::attr', '@::*', '@*', '@*::attr', '@*::*' + case Token.XMLATTR: + decompiler.addToken(Token.XMLATTR); + pn = attributeAccess(pn, memberTypeFlags); + break; + + default: + reportError("msg.no.name.after.dot"); + } + } + break; + + case Token.DOTQUERY: + consumeToken(); + mustHaveXML(); + decompiler.addToken(Token.DOTQUERY); + pn = nf.createDotQuery(pn, expr(false), ts.getLineno()); + mustMatchToken(Token.RP, "msg.no.paren"); + decompiler.addToken(Token.RP); + break; + + case Token.LB: + consumeToken(); + decompiler.addToken(Token.LB); + pn = nf.createElementGet(pn, null, expr(false), 0); + mustMatchToken(Token.RB, "msg.no.bracket.index"); + decompiler.addToken(Token.RB); + break; + + case Token.LP: + if (!allowCallSyntax) { + break tailLoop; + } + consumeToken(); + decompiler.addToken(Token.LP); + pn = nf.createCallOrNew(Token.CALL, pn); + /* Add the arguments to pn, if any are supplied. */ + argumentList(pn); + break; + + default: + break tailLoop; + } + } + return pn; + } + + /* + * Xml attribute expression: + * '@attr', '@ns::attr', '@ns::*', '@ns::*', '@*', '@*::attr', '@*::*' + */ + private Node attributeAccess(Node pn, int memberTypeFlags) + throws IOException + { + memberTypeFlags |= Node.ATTRIBUTE_FLAG; + int tt = nextToken(); + + switch (tt) { + // handles: @name, @ns::name, @ns::*, @ns::[expr] + case Token.NAME: + { + String s = ts.getString(); + decompiler.addName(s); + pn = propertyName(pn, s, memberTypeFlags); + } + break; + + // handles: @*, @*::name, @*::*, @*::[expr] + case Token.MUL: + decompiler.addName("*"); + pn = propertyName(pn, "*", memberTypeFlags); + break; + + // handles @[expr] + case Token.LB: + decompiler.addToken(Token.LB); + pn = nf.createElementGet(pn, null, expr(false), memberTypeFlags); + mustMatchToken(Token.RB, "msg.no.bracket.index"); + decompiler.addToken(Token.RB); + break; + + default: + reportError("msg.no.name.after.xmlAttr"); + pn = nf.createPropertyGet(pn, null, "?", memberTypeFlags); + break; + } + + return pn; + } + + /** + * Check if :: follows name in which case it becomes qualified name + */ + private Node propertyName(Node pn, String name, int memberTypeFlags) + throws IOException, ParserException + { + String namespace = null; + if (matchToken(Token.COLONCOLON)) { + decompiler.addToken(Token.COLONCOLON); + namespace = name; + + int tt = nextToken(); + switch (tt) { + // handles name::name + case Token.NAME: + name = ts.getString(); + decompiler.addName(name); + break; + + // handles name::* + case Token.MUL: + decompiler.addName("*"); + name = "*"; + break; + + // handles name::[expr] + case Token.LB: + decompiler.addToken(Token.LB); + pn = nf.createElementGet(pn, namespace, expr(false), + memberTypeFlags); + mustMatchToken(Token.RB, "msg.no.bracket.index"); + decompiler.addToken(Token.RB); + return pn; + + default: + reportError("msg.no.name.after.coloncolon"); + name = "?"; + } + } + + pn = nf.createPropertyGet(pn, namespace, name, memberTypeFlags); + return pn; + } + + private Node primaryExpr() + throws IOException, ParserException + { + Node pn; + + int ttFlagged = nextFlaggedToken(); + int tt = ttFlagged & CLEAR_TI_MASK; + + switch(tt) { + + case Token.FUNCTION: + return function(FunctionNode.FUNCTION_EXPRESSION); + + case Token.LB: { + ObjArray elems = new ObjArray(); + int skipCount = 0; + decompiler.addToken(Token.LB); + boolean after_lb_or_comma = true; + for (;;) { + tt = peekToken(); + + if (tt == Token.COMMA) { + consumeToken(); + decompiler.addToken(Token.COMMA); + if (!after_lb_or_comma) { + after_lb_or_comma = true; + } else { + elems.add(null); + ++skipCount; + } + } else if (tt == Token.RB) { + consumeToken(); + decompiler.addToken(Token.RB); + break; + } else { + if (!after_lb_or_comma) { + reportError("msg.no.bracket.arg"); + } + elems.add(assignExpr(false)); + after_lb_or_comma = false; + } + } + return nf.createArrayLiteral(elems, skipCount); + } + + case Token.LC: { + ObjArray elems = new ObjArray(); + decompiler.addToken(Token.LC); + if (!matchToken(Token.RC)) { + + boolean first = true; + commaloop: + do { + Object property; + + if (!first) + decompiler.addToken(Token.COMMA); + else + first = false; + + tt = peekToken(); + switch(tt) { + case Token.NAME: + case Token.STRING: + consumeToken(); + // map NAMEs to STRINGs in object literal context + // but tell the decompiler the proper type + String s = ts.getString(); + if (tt == Token.NAME) { + if (s.equals("get") && + peekToken() == Token.NAME) { + decompiler.addToken(Token.GET); + consumeToken(); + s = ts.getString(); + decompiler.addName(s); + property = ScriptRuntime.getIndexObject(s); + if (!getterSetterProperty(elems, property, + true)) + break commaloop; + break; + } else if (s.equals("set") && + peekToken() == Token.NAME) { + decompiler.addToken(Token.SET); + consumeToken(); + s = ts.getString(); + decompiler.addName(s); + property = ScriptRuntime.getIndexObject(s); + if (!getterSetterProperty(elems, property, + false)) + break commaloop; + break; + } + decompiler.addName(s); + } else { + decompiler.addString(s); + } + property = ScriptRuntime.getIndexObject(s); + plainProperty(elems, property); + break; + + case Token.NUMBER: + consumeToken(); + double n = ts.getNumber(); + decompiler.addNumber(n); + property = ScriptRuntime.getIndexObject(n); + plainProperty(elems, property); + break; + + case Token.RC: + // trailing comma is OK. + break commaloop; + default: + reportError("msg.bad.prop"); + break commaloop; + } + } while (matchToken(Token.COMMA)); + + mustMatchToken(Token.RC, "msg.no.brace.prop"); + } + decompiler.addToken(Token.RC); + return nf.createObjectLiteral(elems); + } + + case Token.LP: + + /* Brendan's IR-jsparse.c makes a new node tagged with + * TOK_LP here... I'm not sure I understand why. Isn't + * the grouping already implicit in the structure of the + * parse tree? also TOK_LP is already overloaded (I + * think) in the C IR as 'function call.' */ + decompiler.addToken(Token.LP); + pn = expr(false); + pn.putProp(Node.PARENTHESIZED_PROP, Boolean.TRUE); + decompiler.addToken(Token.RP); + mustMatchToken(Token.RP, "msg.no.paren"); + return pn; + + case Token.XMLATTR: + mustHaveXML(); + decompiler.addToken(Token.XMLATTR); + pn = attributeAccess(null, 0); + return pn; + + case Token.NAME: { + String name = ts.getString(); + if ((ttFlagged & TI_CHECK_LABEL) != 0) { + if (peekToken() == Token.COLON) { + // Do not consume colon, it is used as unwind indicator + // to return to statementHelper. + // XXX Better way? + return nf.createLabel(ts.getLineno()); + } + } + + decompiler.addName(name); + if (compilerEnv.isXmlAvailable()) { + pn = propertyName(null, name, 0); + } else { + pn = nf.createName(name); + } + return pn; + } + + case Token.NUMBER: { + double n = ts.getNumber(); + decompiler.addNumber(n); + return nf.createNumber(n); + } + + case Token.STRING: { + String s = ts.getString(); + decompiler.addString(s); + return nf.createString(s); + } + + case Token.DIV: + case Token.ASSIGN_DIV: { + // Got / or /= which should be treated as regexp in fact + ts.readRegExp(tt); + String flags = ts.regExpFlags; + ts.regExpFlags = null; + String re = ts.getString(); + decompiler.addRegexp(re, flags); + int index = currentScriptOrFn.addRegexp(re, flags); + return nf.createRegExp(index); + } + + case Token.NULL: + case Token.THIS: + case Token.FALSE: + case Token.TRUE: + decompiler.addToken(tt); + return nf.createLeaf(tt); + + case Token.RESERVED: + reportError("msg.reserved.id"); + break; + + case Token.ERROR: + /* the scanner or one of its subroutines reported the error. */ + break; + + case Token.EOF: + reportError("msg.unexpected.eof"); + break; + + default: + reportError("msg.syntax"); + break; + } + return null; // should never reach here + } + + private void plainProperty(ObjArray elems, Object property) + throws IOException { + mustMatchToken(Token.COLON, "msg.no.colon.prop"); + + // OBJLIT is used as ':' in object literal for + // decompilation to solve spacing ambiguity. + decompiler.addToken(Token.OBJECTLIT); + elems.add(property); + elems.add(assignExpr(false)); + } + + private boolean getterSetterProperty(ObjArray elems, Object property, + boolean isGetter) throws IOException { + Node f = function(FunctionNode.FUNCTION_EXPRESSION); + if (f.getType() != Token.FUNCTION) { + reportError("msg.bad.prop"); + return false; + } + int fnIndex = f.getExistingIntProp(Node.FUNCTION_PROP); + FunctionNode fn = currentScriptOrFn.getFunctionNode(fnIndex); + if (fn.getFunctionName().length() != 0) { + reportError("msg.bad.prop"); + return false; + } + elems.add(property); + if (isGetter) { + elems.add(nf.createUnary(Token.GET, f)); + } else { + elems.add(nf.createUnary(Token.SET, f)); + } + return true; + } +} diff --git a/trunk/infrastructure/yuicompressor/src/yuicompressor/org/mozilla/javascript/Parser.java.orig b/trunk/infrastructure/yuicompressor/src/yuicompressor/org/mozilla/javascript/Parser.java.orig new file mode 100644 index 0000000..628bb42 --- /dev/null +++ b/trunk/infrastructure/yuicompressor/src/yuicompressor/org/mozilla/javascript/Parser.java.orig @@ -0,0 +1,2159 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mike Ang + * Igor Bukanov + * Yuh-Ruey Chen + * Ethan Hugg + * Bob Jervis + * Terry Lucas + * Mike McCabe + * Milen Nankov + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript; + +import java.io.Reader; +import java.io.IOException; +import java.util.Hashtable; + +/** + * This class implements the JavaScript parser. + * + * It is based on the C source files jsparse.c and jsparse.h + * in the jsref package. + * + * @see TokenStream + * + * @author Mike McCabe + * @author Brendan Eich + */ + +public class Parser +{ + // TokenInformation flags : currentFlaggedToken stores them together + // with token type + final static int + CLEAR_TI_MASK = 0xFFFF, // mask to clear token information bits + TI_AFTER_EOL = 1 << 16, // first token of the source line + TI_CHECK_LABEL = 1 << 17; // indicates to check for label + + CompilerEnvirons compilerEnv; + private ErrorReporter errorReporter; + private String sourceURI; + boolean calledByCompileFunction; + + private TokenStream ts; + private int currentFlaggedToken; + private int syntaxErrorCount; + + private IRFactory nf; + + private int nestingOfFunction; + + private Decompiler decompiler; + private String encodedSource; + +// The following are per function variables and should be saved/restored +// during function parsing. +// XXX Move to separated class? + ScriptOrFnNode currentScriptOrFn; + private int nestingOfWith; + private Hashtable labelSet; // map of label names into nodes + private ObjArray loopSet; + private ObjArray loopAndSwitchSet; + private boolean hasReturnValue; + private int functionEndFlags; +// end of per function variables + + // Exception to unwind + private static class ParserException extends RuntimeException + { + static final long serialVersionUID = 5882582646773765630L; + } + + public Parser(CompilerEnvirons compilerEnv, ErrorReporter errorReporter) + { + this.compilerEnv = compilerEnv; + this.errorReporter = errorReporter; + } + + protected Decompiler createDecompiler(CompilerEnvirons compilerEnv) + { + return new Decompiler(); + } + + void addStrictWarning(String messageId, String messageArg) + { + if (compilerEnv.isStrictMode()) + addWarning(messageId, messageArg); + } + + void addWarning(String messageId, String messageArg) + { + String message = ScriptRuntime.getMessage1(messageId, messageArg); + if (compilerEnv.reportWarningAsError()) { + ++syntaxErrorCount; + errorReporter.error(message, sourceURI, ts.getLineno(), + ts.getLine(), ts.getOffset()); + } else + errorReporter.warning(message, sourceURI, ts.getLineno(), + ts.getLine(), ts.getOffset()); + } + + void addError(String messageId) + { + ++syntaxErrorCount; + String message = ScriptRuntime.getMessage0(messageId); + errorReporter.error(message, sourceURI, ts.getLineno(), + ts.getLine(), ts.getOffset()); + } + + void addError(String messageId, String messageArg) + { + ++syntaxErrorCount; + String message = ScriptRuntime.getMessage1(messageId, messageArg); + errorReporter.error(message, sourceURI, ts.getLineno(), + ts.getLine(), ts.getOffset()); + } + + RuntimeException reportError(String messageId) + { + addError(messageId); + + // Throw a ParserException exception to unwind the recursive descent + // parse. + throw new ParserException(); + } + + private int peekToken() + throws IOException + { + int tt = currentFlaggedToken; + if (tt == Token.EOF) { + tt = ts.getToken(); + if (tt == Token.EOL) { + do { + tt = ts.getToken(); + } while (tt == Token.EOL); + tt |= TI_AFTER_EOL; + } + currentFlaggedToken = tt; + } + return tt & CLEAR_TI_MASK; + } + + private int peekFlaggedToken() + throws IOException + { + peekToken(); + return currentFlaggedToken; + } + + private void consumeToken() + { + currentFlaggedToken = Token.EOF; + } + + private int nextToken() + throws IOException + { + int tt = peekToken(); + consumeToken(); + return tt; + } + + private int nextFlaggedToken() + throws IOException + { + peekToken(); + int ttFlagged = currentFlaggedToken; + consumeToken(); + return ttFlagged; + } + + private boolean matchToken(int toMatch) + throws IOException + { + int tt = peekToken(); + if (tt != toMatch) { + return false; + } + consumeToken(); + return true; + } + + private int peekTokenOrEOL() + throws IOException + { + int tt = peekToken(); + // Check for last peeked token flags + if ((currentFlaggedToken & TI_AFTER_EOL) != 0) { + tt = Token.EOL; + } + return tt; + } + + private void setCheckForLabel() + { + if ((currentFlaggedToken & CLEAR_TI_MASK) != Token.NAME) + throw Kit.codeBug(); + currentFlaggedToken |= TI_CHECK_LABEL; + } + + private void mustMatchToken(int toMatch, String messageId) + throws IOException, ParserException + { + if (!matchToken(toMatch)) { + reportError(messageId); + } + } + + private void mustHaveXML() + { + if (!compilerEnv.isXmlAvailable()) { + reportError("msg.XML.not.available"); + } + } + + public String getEncodedSource() + { + return encodedSource; + } + + public boolean eof() + { + return ts.eof(); + } + + boolean insideFunction() + { + return nestingOfFunction != 0; + } + + private Node enterLoop(Node loopLabel) + { + Node loop = nf.createLoopNode(loopLabel, ts.getLineno()); + if (loopSet == null) { + loopSet = new ObjArray(); + if (loopAndSwitchSet == null) { + loopAndSwitchSet = new ObjArray(); + } + } + loopSet.push(loop); + loopAndSwitchSet.push(loop); + return loop; + } + + private void exitLoop() + { + loopSet.pop(); + loopAndSwitchSet.pop(); + } + + private Node enterSwitch(Node switchSelector, int lineno) + { + Node switchNode = nf.createSwitch(switchSelector, lineno); + if (loopAndSwitchSet == null) { + loopAndSwitchSet = new ObjArray(); + } + loopAndSwitchSet.push(switchNode); + return switchNode; + } + + private void exitSwitch() + { + loopAndSwitchSet.pop(); + } + + /* + * Build a parse tree from the given sourceString. + * + * @return an Object representing the parsed + * program. If the parse fails, null will be returned. (The + * parse failure will result in a call to the ErrorReporter from + * CompilerEnvirons.) + */ + public ScriptOrFnNode parse(String sourceString, + String sourceURI, int lineno) + { + this.sourceURI = sourceURI; + this.ts = new TokenStream(this, null, sourceString, lineno); + try { + return parse(); + } catch (IOException ex) { + // Should never happen + throw new IllegalStateException(); + } + } + + /* + * Build a parse tree from the given sourceString. + * + * @return an Object representing the parsed + * program. If the parse fails, null will be returned. (The + * parse failure will result in a call to the ErrorReporter from + * CompilerEnvirons.) + */ + public ScriptOrFnNode parse(Reader sourceReader, + String sourceURI, int lineno) + throws IOException + { + this.sourceURI = sourceURI; + this.ts = new TokenStream(this, sourceReader, null, lineno); + return parse(); + } + + private ScriptOrFnNode parse() + throws IOException + { + this.decompiler = createDecompiler(compilerEnv); + this.nf = new IRFactory(this); + currentScriptOrFn = nf.createScript(); + int sourceStartOffset = decompiler.getCurrentOffset(); + this.encodedSource = null; + decompiler.addToken(Token.SCRIPT); + + this.currentFlaggedToken = Token.EOF; + this.syntaxErrorCount = 0; + + int baseLineno = ts.getLineno(); // line number where source starts + + /* so we have something to add nodes to until + * we've collected all the source */ + Node pn = nf.createLeaf(Token.BLOCK); + + try { + for (;;) { + int tt = peekToken(); + + if (tt <= Token.EOF) { + break; + } + + Node n; + if (tt == Token.FUNCTION) { + consumeToken(); + try { + n = function(calledByCompileFunction + ? FunctionNode.FUNCTION_EXPRESSION + : FunctionNode.FUNCTION_STATEMENT); + } catch (ParserException e) { + break; + } + } else { + n = statement(); + } + nf.addChildToBack(pn, n); + } + } catch (StackOverflowError ex) { + String msg = ScriptRuntime.getMessage0( + "msg.too.deep.parser.recursion"); + throw Context.reportRuntimeError(msg, sourceURI, + ts.getLineno(), null, 0); + } + + if (this.syntaxErrorCount != 0) { + String msg = String.valueOf(this.syntaxErrorCount); + msg = ScriptRuntime.getMessage1("msg.got.syntax.errors", msg); + throw errorReporter.runtimeError(msg, sourceURI, baseLineno, + null, 0); + } + + currentScriptOrFn.setSourceName(sourceURI); + currentScriptOrFn.setBaseLineno(baseLineno); + currentScriptOrFn.setEndLineno(ts.getLineno()); + + int sourceEndOffset = decompiler.getCurrentOffset(); + currentScriptOrFn.setEncodedSourceBounds(sourceStartOffset, + sourceEndOffset); + + nf.initScript(currentScriptOrFn, pn); + + if (compilerEnv.isGeneratingSource()) { + encodedSource = decompiler.getEncodedSource(); + } + this.decompiler = null; // It helps GC + + return currentScriptOrFn; + } + + /* + * The C version of this function takes an argument list, + * which doesn't seem to be needed for tree generation... + * it'd only be useful for checking argument hiding, which + * I'm not doing anyway... + */ + private Node parseFunctionBody() + throws IOException + { + ++nestingOfFunction; + Node pn = nf.createBlock(ts.getLineno()); + try { + bodyLoop: for (;;) { + Node n; + int tt = peekToken(); + switch (tt) { + case Token.ERROR: + case Token.EOF: + case Token.RC: + break bodyLoop; + + case Token.FUNCTION: + consumeToken(); + n = function(FunctionNode.FUNCTION_STATEMENT); + break; + default: + n = statement(); + break; + } + nf.addChildToBack(pn, n); + } + } catch (ParserException e) { + // Ignore it + } finally { + --nestingOfFunction; + } + + return pn; + } + + private Node function(int functionType) + throws IOException, ParserException + { + int syntheticType = functionType; + int baseLineno = ts.getLineno(); // line number where source starts + + int functionSourceStart = decompiler.markFunctionStart(functionType); + String name; + Node memberExprNode = null; + if (matchToken(Token.NAME)) { + name = ts.getString(); + decompiler.addName(name); + if (!matchToken(Token.LP)) { + if (compilerEnv.isAllowMemberExprAsFunctionName()) { + // Extension to ECMA: if 'function ' does not follow + // by '(', assume starts memberExpr + Node memberExprHead = nf.createName(name); + name = ""; + memberExprNode = memberExprTail(false, memberExprHead); + } + mustMatchToken(Token.LP, "msg.no.paren.parms"); + } + } else if (matchToken(Token.LP)) { + // Anonymous function + name = ""; + } else { + name = ""; + if (compilerEnv.isAllowMemberExprAsFunctionName()) { + // Note that memberExpr can not start with '(' like + // in function (1+2).toString(), because 'function (' already + // processed as anonymous function + memberExprNode = memberExpr(false); + } + mustMatchToken(Token.LP, "msg.no.paren.parms"); + } + + if (memberExprNode != null) { + syntheticType = FunctionNode.FUNCTION_EXPRESSION; + } + + boolean nested = insideFunction(); + + FunctionNode fnNode = nf.createFunction(name); + if (nested || nestingOfWith > 0) { + // 1. Nested functions are not affected by the dynamic scope flag + // as dynamic scope is already a parent of their scope. + // 2. Functions defined under the with statement also immune to + // this setup, in which case dynamic scope is ignored in favor + // of with object. + fnNode.itsIgnoreDynamicScope = true; + } + + int functionIndex = currentScriptOrFn.addFunction(fnNode); + + int functionSourceEnd; + + ScriptOrFnNode savedScriptOrFn = currentScriptOrFn; + currentScriptOrFn = fnNode; + int savedNestingOfWith = nestingOfWith; + nestingOfWith = 0; + Hashtable savedLabelSet = labelSet; + labelSet = null; + ObjArray savedLoopSet = loopSet; + loopSet = null; + ObjArray savedLoopAndSwitchSet = loopAndSwitchSet; + loopAndSwitchSet = null; + boolean savedHasReturnValue = hasReturnValue; + int savedFunctionEndFlags = functionEndFlags; + + Node body; + try { + decompiler.addToken(Token.LP); + if (!matchToken(Token.RP)) { + boolean first = true; + do { + if (!first) + decompiler.addToken(Token.COMMA); + first = false; + mustMatchToken(Token.NAME, "msg.no.parm"); + String s = ts.getString(); + if (fnNode.hasParamOrVar(s)) { + addWarning("msg.dup.parms", s); + } + fnNode.addParam(s); + decompiler.addName(s); + } while (matchToken(Token.COMMA)); + + mustMatchToken(Token.RP, "msg.no.paren.after.parms"); + } + decompiler.addToken(Token.RP); + + mustMatchToken(Token.LC, "msg.no.brace.body"); + decompiler.addEOL(Token.LC); + body = parseFunctionBody(); + mustMatchToken(Token.RC, "msg.no.brace.after.body"); + + if (compilerEnv.isStrictMode() && !body.hasConsistentReturnUsage()) + { + String msg = name.length() > 0 ? "msg.no.return.value" + : "msg.anon.no.return.value"; + addStrictWarning(msg, name); + } + + decompiler.addToken(Token.RC); + functionSourceEnd = decompiler.markFunctionEnd(functionSourceStart); + if (functionType != FunctionNode.FUNCTION_EXPRESSION) { + // Add EOL only if function is not part of expression + // since it gets SEMI + EOL from Statement in that case + decompiler.addToken(Token.EOL); + } + } + finally { + hasReturnValue = savedHasReturnValue; + functionEndFlags = savedFunctionEndFlags; + loopAndSwitchSet = savedLoopAndSwitchSet; + loopSet = savedLoopSet; + labelSet = savedLabelSet; + nestingOfWith = savedNestingOfWith; + currentScriptOrFn = savedScriptOrFn; + } + + fnNode.setEncodedSourceBounds(functionSourceStart, functionSourceEnd); + fnNode.setSourceName(sourceURI); + fnNode.setBaseLineno(baseLineno); + fnNode.setEndLineno(ts.getLineno()); + + if (name != null) { + int index = currentScriptOrFn.getParamOrVarIndex(name); + if (index >= 0 && index < currentScriptOrFn.getParamCount()) + addStrictWarning("msg.var.hides.arg", name); + } + + Node pn = nf.initFunction(fnNode, functionIndex, body, syntheticType); + if (memberExprNode != null) { + pn = nf.createAssignment(Token.ASSIGN, memberExprNode, pn); + if (functionType != FunctionNode.FUNCTION_EXPRESSION) { + // XXX check JScript behavior: should it be createExprStatement? + pn = nf.createExprStatementNoReturn(pn, baseLineno); + } + } + return pn; + } + + private Node statements() + throws IOException + { + Node pn = nf.createBlock(ts.getLineno()); + + int tt; + while((tt = peekToken()) > Token.EOF && tt != Token.RC) { + nf.addChildToBack(pn, statement()); + } + + return pn; + } + + private Node condition() + throws IOException, ParserException + { + mustMatchToken(Token.LP, "msg.no.paren.cond"); + decompiler.addToken(Token.LP); + Node pn = expr(false); + mustMatchToken(Token.RP, "msg.no.paren.after.cond"); + decompiler.addToken(Token.RP); + + // Report strict warning on code like "if (a = 7) ...". Suppress the + // warning if the condition is parenthesized, like "if ((a = 7)) ...". + if (pn.getProp(Node.PARENTHESIZED_PROP) == null && + (pn.getType() == Token.SETNAME || pn.getType() == Token.SETPROP || + pn.getType() == Token.SETELEM)) + { + addStrictWarning("msg.equal.as.assign", ""); + } + return pn; + } + + // match a NAME; return null if no match. + private Node matchJumpLabelName() + throws IOException, ParserException + { + Node label = null; + + int tt = peekTokenOrEOL(); + if (tt == Token.NAME) { + consumeToken(); + String name = ts.getString(); + decompiler.addName(name); + if (labelSet != null) { + label = (Node)labelSet.get(name); + } + if (label == null) { + reportError("msg.undef.label"); + } + } + + return label; + } + + private Node statement() + throws IOException + { + try { + Node pn = statementHelper(null); + if (pn != null) { + if (compilerEnv.isStrictMode() && !pn.hasSideEffects()) + addStrictWarning("msg.no.side.effects", ""); + return pn; + } + } catch (ParserException e) { } + + // skip to end of statement + int lineno = ts.getLineno(); + guessingStatementEnd: for (;;) { + int tt = peekTokenOrEOL(); + consumeToken(); + switch (tt) { + case Token.ERROR: + case Token.EOF: + case Token.EOL: + case Token.SEMI: + break guessingStatementEnd; + } + } + return nf.createExprStatement(nf.createName("error"), lineno); + } + + /** + * Whether the "catch (e: e instanceof Exception) { ... }" syntax + * is implemented. + */ + + private Node statementHelper(Node statementLabel) + throws IOException, ParserException + { + Node pn = null; + + int tt; + + tt = peekToken(); + + switch(tt) { + case Token.IF: { + consumeToken(); + + decompiler.addToken(Token.IF); + int lineno = ts.getLineno(); + Node cond = condition(); + decompiler.addEOL(Token.LC); + Node ifTrue = statement(); + Node ifFalse = null; + if (matchToken(Token.ELSE)) { + decompiler.addToken(Token.RC); + decompiler.addToken(Token.ELSE); + decompiler.addEOL(Token.LC); + ifFalse = statement(); + } + decompiler.addEOL(Token.RC); + pn = nf.createIf(cond, ifTrue, ifFalse, lineno); + return pn; + } + + case Token.SWITCH: { + consumeToken(); + + decompiler.addToken(Token.SWITCH); + int lineno = ts.getLineno(); + mustMatchToken(Token.LP, "msg.no.paren.switch"); + decompiler.addToken(Token.LP); + pn = enterSwitch(expr(false), lineno); + try { + mustMatchToken(Token.RP, "msg.no.paren.after.switch"); + decompiler.addToken(Token.RP); + mustMatchToken(Token.LC, "msg.no.brace.switch"); + decompiler.addEOL(Token.LC); + + boolean hasDefault = false; + switchLoop: for (;;) { + tt = nextToken(); + Node caseExpression; + switch (tt) { + case Token.RC: + break switchLoop; + + case Token.CASE: + decompiler.addToken(Token.CASE); + caseExpression = expr(false); + mustMatchToken(Token.COLON, "msg.no.colon.case"); + decompiler.addEOL(Token.COLON); + break; + + case Token.DEFAULT: + if (hasDefault) { + reportError("msg.double.switch.default"); + } + decompiler.addToken(Token.DEFAULT); + hasDefault = true; + caseExpression = null; + mustMatchToken(Token.COLON, "msg.no.colon.case"); + decompiler.addEOL(Token.COLON); + break; + + default: + reportError("msg.bad.switch"); + break switchLoop; + } + + Node block = nf.createLeaf(Token.BLOCK); + while ((tt = peekToken()) != Token.RC + && tt != Token.CASE + && tt != Token.DEFAULT + && tt != Token.EOF) + { + nf.addChildToBack(block, statement()); + } + + // caseExpression == null => add default lable + nf.addSwitchCase(pn, caseExpression, block); + } + decompiler.addEOL(Token.RC); + nf.closeSwitch(pn); + } finally { + exitSwitch(); + } + return pn; + } + + case Token.WHILE: { + consumeToken(); + decompiler.addToken(Token.WHILE); + + Node loop = enterLoop(statementLabel); + try { + Node cond = condition(); + decompiler.addEOL(Token.LC); + Node body = statement(); + decompiler.addEOL(Token.RC); + pn = nf.createWhile(loop, cond, body); + } finally { + exitLoop(); + } + return pn; + } + + case Token.DO: { + consumeToken(); + decompiler.addToken(Token.DO); + decompiler.addEOL(Token.LC); + + Node loop = enterLoop(statementLabel); + try { + Node body = statement(); + decompiler.addToken(Token.RC); + mustMatchToken(Token.WHILE, "msg.no.while.do"); + decompiler.addToken(Token.WHILE); + Node cond = condition(); + pn = nf.createDoWhile(loop, body, cond); + } finally { + exitLoop(); + } + // Always auto-insert semicon to follow SpiderMonkey: + // It is required by EMAScript but is ignored by the rest of + // world, see bug 238945 + matchToken(Token.SEMI); + decompiler.addEOL(Token.SEMI); + return pn; + } + + case Token.FOR: { + consumeToken(); + boolean isForEach = false; + decompiler.addToken(Token.FOR); + + Node loop = enterLoop(statementLabel); + try { + + Node init; // Node init is also foo in 'foo in Object' + Node cond; // Node cond is also object in 'foo in Object' + Node incr = null; // to kill warning + Node body; + + // See if this is a for each () instead of just a for () + if (matchToken(Token.NAME)) { + decompiler.addName(ts.getString()); + if (ts.getString().equals("each")) { + isForEach = true; + } else { + reportError("msg.no.paren.for"); + } + } + + mustMatchToken(Token.LP, "msg.no.paren.for"); + decompiler.addToken(Token.LP); + tt = peekToken(); + if (tt == Token.SEMI) { + init = nf.createLeaf(Token.EMPTY); + } else { + if (tt == Token.VAR) { + // set init to a var list or initial + consumeToken(); // consume the 'var' token + init = variables(Token.FOR); + } + else { + init = expr(true); + } + } + + if (matchToken(Token.IN)) { + decompiler.addToken(Token.IN); + // 'cond' is the object over which we're iterating + cond = expr(false); + } else { // ordinary for loop + mustMatchToken(Token.SEMI, "msg.no.semi.for"); + decompiler.addToken(Token.SEMI); + if (peekToken() == Token.SEMI) { + // no loop condition + cond = nf.createLeaf(Token.EMPTY); + } else { + cond = expr(false); + } + + mustMatchToken(Token.SEMI, "msg.no.semi.for.cond"); + decompiler.addToken(Token.SEMI); + if (peekToken() == Token.RP) { + incr = nf.createLeaf(Token.EMPTY); + } else { + incr = expr(false); + } + } + + mustMatchToken(Token.RP, "msg.no.paren.for.ctrl"); + decompiler.addToken(Token.RP); + decompiler.addEOL(Token.LC); + body = statement(); + decompiler.addEOL(Token.RC); + + if (incr == null) { + // cond could be null if 'in obj' got eaten + // by the init node. + pn = nf.createForIn(loop, init, cond, body, isForEach); + } else { + pn = nf.createFor(loop, init, cond, incr, body); + } + } finally { + exitLoop(); + } + return pn; + } + + case Token.TRY: { + consumeToken(); + int lineno = ts.getLineno(); + + Node tryblock; + Node catchblocks = null; + Node finallyblock = null; + + decompiler.addToken(Token.TRY); + decompiler.addEOL(Token.LC); + tryblock = statement(); + decompiler.addEOL(Token.RC); + + catchblocks = nf.createLeaf(Token.BLOCK); + + boolean sawDefaultCatch = false; + int peek = peekToken(); + if (peek == Token.CATCH) { + while (matchToken(Token.CATCH)) { + if (sawDefaultCatch) { + reportError("msg.catch.unreachable"); + } + decompiler.addToken(Token.CATCH); + mustMatchToken(Token.LP, "msg.no.paren.catch"); + decompiler.addToken(Token.LP); + + mustMatchToken(Token.NAME, "msg.bad.catchcond"); + String varName = ts.getString(); + decompiler.addName(varName); + + Node catchCond = null; + if (matchToken(Token.IF)) { + decompiler.addToken(Token.IF); + catchCond = expr(false); + } else { + sawDefaultCatch = true; + } + + mustMatchToken(Token.RP, "msg.bad.catchcond"); + decompiler.addToken(Token.RP); + mustMatchToken(Token.LC, "msg.no.brace.catchblock"); + decompiler.addEOL(Token.LC); + + nf.addChildToBack(catchblocks, + nf.createCatch(varName, catchCond, + statements(), + ts.getLineno())); + + mustMatchToken(Token.RC, "msg.no.brace.after.body"); + decompiler.addEOL(Token.RC); + } + } else if (peek != Token.FINALLY) { + mustMatchToken(Token.FINALLY, "msg.try.no.catchfinally"); + } + + if (matchToken(Token.FINALLY)) { + decompiler.addToken(Token.FINALLY); + decompiler.addEOL(Token.LC); + finallyblock = statement(); + decompiler.addEOL(Token.RC); + } + + pn = nf.createTryCatchFinally(tryblock, catchblocks, + finallyblock, lineno); + + return pn; + } + + case Token.THROW: { + consumeToken(); + if (peekTokenOrEOL() == Token.EOL) { + // ECMAScript does not allow new lines before throw expression, + // see bug 256617 + reportError("msg.bad.throw.eol"); + } + + int lineno = ts.getLineno(); + decompiler.addToken(Token.THROW); + pn = nf.createThrow(expr(false), lineno); + break; + } + + case Token.BREAK: { + consumeToken(); + int lineno = ts.getLineno(); + + decompiler.addToken(Token.BREAK); + + // matchJumpLabelName only matches if there is one + Node breakStatement = matchJumpLabelName(); + if (breakStatement == null) { + if (loopAndSwitchSet == null || loopAndSwitchSet.size() == 0) { + reportError("msg.bad.break"); + return null; + } + breakStatement = (Node)loopAndSwitchSet.peek(); + } + pn = nf.createBreak(breakStatement, lineno); + break; + } + + case Token.CONTINUE: { + consumeToken(); + int lineno = ts.getLineno(); + + decompiler.addToken(Token.CONTINUE); + + Node loop; + // matchJumpLabelName only matches if there is one + Node label = matchJumpLabelName(); + if (label == null) { + if (loopSet == null || loopSet.size() == 0) { + reportError("msg.continue.outside"); + return null; + } + loop = (Node)loopSet.peek(); + } else { + loop = nf.getLabelLoop(label); + if (loop == null) { + reportError("msg.continue.nonloop"); + return null; + } + } + pn = nf.createContinue(loop, lineno); + break; + } + + case Token.WITH: { + consumeToken(); + + decompiler.addToken(Token.WITH); + int lineno = ts.getLineno(); + mustMatchToken(Token.LP, "msg.no.paren.with"); + decompiler.addToken(Token.LP); + Node obj = expr(false); + mustMatchToken(Token.RP, "msg.no.paren.after.with"); + decompiler.addToken(Token.RP); + decompiler.addEOL(Token.LC); + + ++nestingOfWith; + Node body; + try { + body = statement(); + } finally { + --nestingOfWith; + } + + decompiler.addEOL(Token.RC); + + pn = nf.createWith(obj, body, lineno); + return pn; + } + + case Token.CONST: + case Token.VAR: { + consumeToken(); + pn = variables(tt); + break; + } + + case Token.RETURN: { + if (!insideFunction()) { + reportError("msg.bad.return"); + } + consumeToken(); + decompiler.addToken(Token.RETURN); + int lineno = ts.getLineno(); + + Node retExpr; + /* This is ugly, but we don't want to require a semicolon. */ + tt = peekTokenOrEOL(); + switch (tt) { + case Token.SEMI: + case Token.RC: + case Token.EOF: + case Token.EOL: + case Token.ERROR: + retExpr = null; + break; + default: + retExpr = expr(false); + hasReturnValue = true; + } + pn = nf.createReturn(retExpr, lineno); + + // see if we need a strict mode warning + if (retExpr == null) { + if (functionEndFlags == Node.END_RETURNS_VALUE) + addStrictWarning("msg.return.inconsistent", ""); + + functionEndFlags |= Node.END_RETURNS; + } else { + if (functionEndFlags == Node.END_RETURNS) + addStrictWarning("msg.return.inconsistent", ""); + + functionEndFlags |= Node.END_RETURNS_VALUE; + } + + break; + } + + case Token.LC: + consumeToken(); + if (statementLabel != null) { + decompiler.addToken(Token.LC); + } + pn = statements(); + mustMatchToken(Token.RC, "msg.no.brace.block"); + if (statementLabel != null) { + decompiler.addEOL(Token.RC); + } + return pn; + + case Token.ERROR: + // Fall thru, to have a node for error recovery to work on + case Token.SEMI: + consumeToken(); + pn = nf.createLeaf(Token.EMPTY); + return pn; + + case Token.FUNCTION: { + consumeToken(); + pn = function(FunctionNode.FUNCTION_EXPRESSION_STATEMENT); + return pn; + } + + case Token.DEFAULT : + consumeToken(); + mustHaveXML(); + + decompiler.addToken(Token.DEFAULT); + int nsLine = ts.getLineno(); + + if (!(matchToken(Token.NAME) + && ts.getString().equals("xml"))) + { + reportError("msg.bad.namespace"); + } + decompiler.addName(" xml"); + + if (!(matchToken(Token.NAME) + && ts.getString().equals("namespace"))) + { + reportError("msg.bad.namespace"); + } + decompiler.addName(" namespace"); + + if (!matchToken(Token.ASSIGN)) { + reportError("msg.bad.namespace"); + } + decompiler.addToken(Token.ASSIGN); + + Node expr = expr(false); + pn = nf.createDefaultNamespace(expr, nsLine); + break; + + case Token.NAME: { + int lineno = ts.getLineno(); + String name = ts.getString(); + setCheckForLabel(); + pn = expr(false); + if (pn.getType() != Token.LABEL) { + pn = nf.createExprStatement(pn, lineno); + } else { + // Parsed the label: push back token should be + // colon that primaryExpr left untouched. + if (peekToken() != Token.COLON) Kit.codeBug(); + consumeToken(); + // depend on decompiling lookahead to guess that that + // last name was a label. + decompiler.addName(name); + decompiler.addEOL(Token.COLON); + + if (labelSet == null) { + labelSet = new Hashtable(); + } else if (labelSet.containsKey(name)) { + reportError("msg.dup.label"); + } + + boolean firstLabel; + if (statementLabel == null) { + firstLabel = true; + statementLabel = pn; + } else { + // Discard multiple label nodes and use only + // the first: it allows to simplify IRFactory + firstLabel = false; + } + labelSet.put(name, statementLabel); + try { + pn = statementHelper(statementLabel); + } finally { + labelSet.remove(name); + } + if (firstLabel) { + pn = nf.createLabeledStatement(statementLabel, pn); + } + return pn; + } + break; + } + + default: { + int lineno = ts.getLineno(); + pn = expr(false); + pn = nf.createExprStatement(pn, lineno); + break; + } + } + + int ttFlagged = peekFlaggedToken(); + switch (ttFlagged & CLEAR_TI_MASK) { + case Token.SEMI: + // Consume ';' as a part of expression + consumeToken(); + break; + case Token.ERROR: + case Token.EOF: + case Token.RC: + // Autoinsert ; + break; + default: + if ((ttFlagged & TI_AFTER_EOL) == 0) { + // Report error if no EOL or autoinsert ; otherwise + reportError("msg.no.semi.stmt"); + } + break; + } + decompiler.addEOL(Token.SEMI); + + return pn; + } + + /** + * Parse a 'var' or 'const' statement, or a 'var' init list in a for + * statement. + * @param context A token value: either VAR, CONST or FOR depending on + * context. + * @return The parsed statement + * @throws IOException + * @throws ParserException + */ + private Node variables(int context) + throws IOException, ParserException + { + Node pn; + boolean first = true; + + if (context == Token.CONST){ + pn = nf.createVariables(Token.CONST, ts.getLineno()); + decompiler.addToken(Token.CONST); + } else { + pn = nf.createVariables(Token.VAR, ts.getLineno()); + decompiler.addToken(Token.VAR); + } + + for (;;) { + Node name; + Node init; + mustMatchToken(Token.NAME, "msg.bad.var"); + String s = ts.getString(); + + if (!first) + decompiler.addToken(Token.COMMA); + first = false; + + decompiler.addName(s); + + if (context == Token.CONST) { + if (!currentScriptOrFn.addConst(s)) { + // We know it's already defined, since addConst passes if + // it's not defined at all. The addVar call just confirms + // what it is. + if (currentScriptOrFn.addVar(s) != ScriptOrFnNode.DUPLICATE_CONST) + addError("msg.var.redecl", s); + else + addError("msg.const.redecl", s); + } + } else { + int dupState = currentScriptOrFn.addVar(s); + if (dupState == ScriptOrFnNode.DUPLICATE_CONST) + addError("msg.const.redecl", s); + else if (dupState == ScriptOrFnNode.DUPLICATE_PARAMETER) + addStrictWarning("msg.var.hides.arg", s); + else if (dupState == ScriptOrFnNode.DUPLICATE_VAR) + addStrictWarning("msg.var.redecl", s); + } + name = nf.createName(s); + + // omitted check for argument hiding + + if (matchToken(Token.ASSIGN)) { + decompiler.addToken(Token.ASSIGN); + + init = assignExpr(context == Token.FOR); + nf.addChildToBack(name, init); + } + nf.addChildToBack(pn, name); + if (!matchToken(Token.COMMA)) + break; + } + return pn; + } + + private Node expr(boolean inForInit) + throws IOException, ParserException + { + Node pn = assignExpr(inForInit); + while (matchToken(Token.COMMA)) { + decompiler.addToken(Token.COMMA); + if (compilerEnv.isStrictMode() && !pn.hasSideEffects()) + addStrictWarning("msg.no.side.effects", ""); + pn = nf.createBinary(Token.COMMA, pn, assignExpr(inForInit)); + } + return pn; + } + + private Node assignExpr(boolean inForInit) + throws IOException, ParserException + { + Node pn = condExpr(inForInit); + + int tt = peekToken(); + if (Token.FIRST_ASSIGN <= tt && tt <= Token.LAST_ASSIGN) { + consumeToken(); + decompiler.addToken(tt); + pn = nf.createAssignment(tt, pn, assignExpr(inForInit)); + } + + return pn; + } + + private Node condExpr(boolean inForInit) + throws IOException, ParserException + { + Node pn = orExpr(inForInit); + + if (matchToken(Token.HOOK)) { + decompiler.addToken(Token.HOOK); + Node ifTrue = assignExpr(false); + mustMatchToken(Token.COLON, "msg.no.colon.cond"); + decompiler.addToken(Token.COLON); + Node ifFalse = assignExpr(inForInit); + return nf.createCondExpr(pn, ifTrue, ifFalse); + } + + return pn; + } + + private Node orExpr(boolean inForInit) + throws IOException, ParserException + { + Node pn = andExpr(inForInit); + if (matchToken(Token.OR)) { + decompiler.addToken(Token.OR); + pn = nf.createBinary(Token.OR, pn, orExpr(inForInit)); + } + + return pn; + } + + private Node andExpr(boolean inForInit) + throws IOException, ParserException + { + Node pn = bitOrExpr(inForInit); + if (matchToken(Token.AND)) { + decompiler.addToken(Token.AND); + pn = nf.createBinary(Token.AND, pn, andExpr(inForInit)); + } + + return pn; + } + + private Node bitOrExpr(boolean inForInit) + throws IOException, ParserException + { + Node pn = bitXorExpr(inForInit); + while (matchToken(Token.BITOR)) { + decompiler.addToken(Token.BITOR); + pn = nf.createBinary(Token.BITOR, pn, bitXorExpr(inForInit)); + } + return pn; + } + + private Node bitXorExpr(boolean inForInit) + throws IOException, ParserException + { + Node pn = bitAndExpr(inForInit); + while (matchToken(Token.BITXOR)) { + decompiler.addToken(Token.BITXOR); + pn = nf.createBinary(Token.BITXOR, pn, bitAndExpr(inForInit)); + } + return pn; + } + + private Node bitAndExpr(boolean inForInit) + throws IOException, ParserException + { + Node pn = eqExpr(inForInit); + while (matchToken(Token.BITAND)) { + decompiler.addToken(Token.BITAND); + pn = nf.createBinary(Token.BITAND, pn, eqExpr(inForInit)); + } + return pn; + } + + private Node eqExpr(boolean inForInit) + throws IOException, ParserException + { + Node pn = relExpr(inForInit); + for (;;) { + int tt = peekToken(); + switch (tt) { + case Token.EQ: + case Token.NE: + case Token.SHEQ: + case Token.SHNE: + consumeToken(); + int decompilerToken = tt; + int parseToken = tt; + if (compilerEnv.getLanguageVersion() == Context.VERSION_1_2) { + // JavaScript 1.2 uses shallow equality for == and != . + // In addition, convert === and !== for decompiler into + // == and != since the decompiler is supposed to show + // canonical source and in 1.2 ===, !== are allowed + // only as an alias to ==, !=. + switch (tt) { + case Token.EQ: + parseToken = Token.SHEQ; + break; + case Token.NE: + parseToken = Token.SHNE; + break; + case Token.SHEQ: + decompilerToken = Token.EQ; + break; + case Token.SHNE: + decompilerToken = Token.NE; + break; + } + } + decompiler.addToken(decompilerToken); + pn = nf.createBinary(parseToken, pn, relExpr(inForInit)); + continue; + } + break; + } + return pn; + } + + private Node relExpr(boolean inForInit) + throws IOException, ParserException + { + Node pn = shiftExpr(); + for (;;) { + int tt = peekToken(); + switch (tt) { + case Token.IN: + if (inForInit) + break; + // fall through + case Token.INSTANCEOF: + case Token.LE: + case Token.LT: + case Token.GE: + case Token.GT: + consumeToken(); + decompiler.addToken(tt); + pn = nf.createBinary(tt, pn, shiftExpr()); + continue; + } + break; + } + return pn; + } + + private Node shiftExpr() + throws IOException, ParserException + { + Node pn = addExpr(); + for (;;) { + int tt = peekToken(); + switch (tt) { + case Token.LSH: + case Token.URSH: + case Token.RSH: + consumeToken(); + decompiler.addToken(tt); + pn = nf.createBinary(tt, pn, addExpr()); + continue; + } + break; + } + return pn; + } + + private Node addExpr() + throws IOException, ParserException + { + Node pn = mulExpr(); + for (;;) { + int tt = peekToken(); + if (tt == Token.ADD || tt == Token.SUB) { + consumeToken(); + decompiler.addToken(tt); + // flushNewLines + pn = nf.createBinary(tt, pn, mulExpr()); + continue; + } + break; + } + + return pn; + } + + private Node mulExpr() + throws IOException, ParserException + { + Node pn = unaryExpr(); + for (;;) { + int tt = peekToken(); + switch (tt) { + case Token.MUL: + case Token.DIV: + case Token.MOD: + consumeToken(); + decompiler.addToken(tt); + pn = nf.createBinary(tt, pn, unaryExpr()); + continue; + } + break; + } + + return pn; + } + + private Node unaryExpr() + throws IOException, ParserException + { + int tt; + + tt = peekToken(); + + switch(tt) { + case Token.VOID: + case Token.NOT: + case Token.BITNOT: + case Token.TYPEOF: + consumeToken(); + decompiler.addToken(tt); + return nf.createUnary(tt, unaryExpr()); + + case Token.ADD: + consumeToken(); + // Convert to special POS token in decompiler and parse tree + decompiler.addToken(Token.POS); + return nf.createUnary(Token.POS, unaryExpr()); + + case Token.SUB: + consumeToken(); + // Convert to special NEG token in decompiler and parse tree + decompiler.addToken(Token.NEG); + return nf.createUnary(Token.NEG, unaryExpr()); + + case Token.INC: + case Token.DEC: + consumeToken(); + decompiler.addToken(tt); + return nf.createIncDec(tt, false, memberExpr(true)); + + case Token.DELPROP: + consumeToken(); + decompiler.addToken(Token.DELPROP); + return nf.createUnary(Token.DELPROP, unaryExpr()); + + case Token.ERROR: + consumeToken(); + break; + + // XML stream encountered in expression. + case Token.LT: + if (compilerEnv.isXmlAvailable()) { + consumeToken(); + Node pn = xmlInitializer(); + return memberExprTail(true, pn); + } + // Fall thru to the default handling of RELOP + + default: + Node pn = memberExpr(true); + + // Don't look across a newline boundary for a postfix incop. + tt = peekTokenOrEOL(); + if (tt == Token.INC || tt == Token.DEC) { + consumeToken(); + decompiler.addToken(tt); + return nf.createIncDec(tt, true, pn); + } + return pn; + } + return nf.createName("err"); // Only reached on error. Try to continue. + + } + + private Node xmlInitializer() throws IOException + { + int tt = ts.getFirstXMLToken(); + if (tt != Token.XML && tt != Token.XMLEND) { + reportError("msg.syntax"); + return null; + } + + /* Make a NEW node to append to. */ + Node pnXML = nf.createLeaf(Token.NEW); + + String xml = ts.getString(); + boolean fAnonymous = xml.trim().startsWith("<>"); + + Node pn = nf.createName(fAnonymous ? "XMLList" : "XML"); + nf.addChildToBack(pnXML, pn); + + pn = null; + Node expr; + for (;;tt = ts.getNextXMLToken()) { + switch (tt) { + case Token.XML: + xml = ts.getString(); + decompiler.addName(xml); + mustMatchToken(Token.LC, "msg.syntax"); + decompiler.addToken(Token.LC); + expr = (peekToken() == Token.RC) + ? nf.createString("") + : expr(false); + mustMatchToken(Token.RC, "msg.syntax"); + decompiler.addToken(Token.RC); + if (pn == null) { + pn = nf.createString(xml); + } else { + pn = nf.createBinary(Token.ADD, pn, nf.createString(xml)); + } + if (ts.isXMLAttribute()) { + /* Need to put the result in double quotes */ + expr = nf.createUnary(Token.ESCXMLATTR, expr); + Node prepend = nf.createBinary(Token.ADD, + nf.createString("\""), + expr); + expr = nf.createBinary(Token.ADD, + prepend, + nf.createString("\"")); + } else { + expr = nf.createUnary(Token.ESCXMLTEXT, expr); + } + pn = nf.createBinary(Token.ADD, pn, expr); + break; + case Token.XMLEND: + xml = ts.getString(); + decompiler.addName(xml); + if (pn == null) { + pn = nf.createString(xml); + } else { + pn = nf.createBinary(Token.ADD, pn, nf.createString(xml)); + } + + nf.addChildToBack(pnXML, pn); + return pnXML; + default: + reportError("msg.syntax"); + return null; + } + } + } + + private void argumentList(Node listNode) + throws IOException, ParserException + { + boolean matched; + matched = matchToken(Token.RP); + if (!matched) { + boolean first = true; + do { + if (!first) + decompiler.addToken(Token.COMMA); + first = false; + nf.addChildToBack(listNode, assignExpr(false)); + } while (matchToken(Token.COMMA)); + + mustMatchToken(Token.RP, "msg.no.paren.arg"); + } + decompiler.addToken(Token.RP); + } + + private Node memberExpr(boolean allowCallSyntax) + throws IOException, ParserException + { + int tt; + + Node pn; + + /* Check for new expressions. */ + tt = peekToken(); + if (tt == Token.NEW) { + /* Eat the NEW token. */ + consumeToken(); + decompiler.addToken(Token.NEW); + + /* Make a NEW node to append to. */ + pn = nf.createCallOrNew(Token.NEW, memberExpr(false)); + + if (matchToken(Token.LP)) { + decompiler.addToken(Token.LP); + /* Add the arguments to pn, if any are supplied. */ + argumentList(pn); + } + + /* XXX there's a check in the C source against + * "too many constructor arguments" - how many + * do we claim to support? + */ + + /* Experimental syntax: allow an object literal to follow a new expression, + * which will mean a kind of anonymous class built with the JavaAdapter. + * the object literal will be passed as an additional argument to the constructor. + */ + tt = peekToken(); + if (tt == Token.LC) { + nf.addChildToBack(pn, primaryExpr()); + } + } else { + pn = primaryExpr(); + } + + return memberExprTail(allowCallSyntax, pn); + } + + private Node memberExprTail(boolean allowCallSyntax, Node pn) + throws IOException, ParserException + { + tailLoop: + for (;;) { + int tt = peekToken(); + switch (tt) { + + case Token.DOT: + case Token.DOTDOT: + { + int memberTypeFlags; + String s; + + consumeToken(); + decompiler.addToken(tt); + memberTypeFlags = 0; + if (tt == Token.DOTDOT) { + mustHaveXML(); + memberTypeFlags = Node.DESCENDANTS_FLAG; + } + if (!compilerEnv.isXmlAvailable()) { + mustMatchToken(Token.NAME, "msg.no.name.after.dot"); + s = ts.getString(); + decompiler.addName(s); + pn = nf.createPropertyGet(pn, null, s, memberTypeFlags); + break; + } + + tt = nextToken(); + switch (tt) { + // handles: name, ns::name, ns::*, ns::[expr] + case Token.NAME: + s = ts.getString(); + decompiler.addName(s); + pn = propertyName(pn, s, memberTypeFlags); + break; + + // handles: *, *::name, *::*, *::[expr] + case Token.MUL: + decompiler.addName("*"); + pn = propertyName(pn, "*", memberTypeFlags); + break; + + // handles: '@attr', '@ns::attr', '@ns::*', '@ns::*', + // '@::attr', '@::*', '@*', '@*::attr', '@*::*' + case Token.XMLATTR: + decompiler.addToken(Token.XMLATTR); + pn = attributeAccess(pn, memberTypeFlags); + break; + + default: + reportError("msg.no.name.after.dot"); + } + } + break; + + case Token.DOTQUERY: + consumeToken(); + mustHaveXML(); + decompiler.addToken(Token.DOTQUERY); + pn = nf.createDotQuery(pn, expr(false), ts.getLineno()); + mustMatchToken(Token.RP, "msg.no.paren"); + decompiler.addToken(Token.RP); + break; + + case Token.LB: + consumeToken(); + decompiler.addToken(Token.LB); + pn = nf.createElementGet(pn, null, expr(false), 0); + mustMatchToken(Token.RB, "msg.no.bracket.index"); + decompiler.addToken(Token.RB); + break; + + case Token.LP: + if (!allowCallSyntax) { + break tailLoop; + } + consumeToken(); + decompiler.addToken(Token.LP); + pn = nf.createCallOrNew(Token.CALL, pn); + /* Add the arguments to pn, if any are supplied. */ + argumentList(pn); + break; + + default: + break tailLoop; + } + } + return pn; + } + + /* + * Xml attribute expression: + * '@attr', '@ns::attr', '@ns::*', '@ns::*', '@*', '@*::attr', '@*::*' + */ + private Node attributeAccess(Node pn, int memberTypeFlags) + throws IOException + { + memberTypeFlags |= Node.ATTRIBUTE_FLAG; + int tt = nextToken(); + + switch (tt) { + // handles: @name, @ns::name, @ns::*, @ns::[expr] + case Token.NAME: + { + String s = ts.getString(); + decompiler.addName(s); + pn = propertyName(pn, s, memberTypeFlags); + } + break; + + // handles: @*, @*::name, @*::*, @*::[expr] + case Token.MUL: + decompiler.addName("*"); + pn = propertyName(pn, "*", memberTypeFlags); + break; + + // handles @[expr] + case Token.LB: + decompiler.addToken(Token.LB); + pn = nf.createElementGet(pn, null, expr(false), memberTypeFlags); + mustMatchToken(Token.RB, "msg.no.bracket.index"); + decompiler.addToken(Token.RB); + break; + + default: + reportError("msg.no.name.after.xmlAttr"); + pn = nf.createPropertyGet(pn, null, "?", memberTypeFlags); + break; + } + + return pn; + } + + /** + * Check if :: follows name in which case it becomes qualified name + */ + private Node propertyName(Node pn, String name, int memberTypeFlags) + throws IOException, ParserException + { + String namespace = null; + if (matchToken(Token.COLONCOLON)) { + decompiler.addToken(Token.COLONCOLON); + namespace = name; + + int tt = nextToken(); + switch (tt) { + // handles name::name + case Token.NAME: + name = ts.getString(); + decompiler.addName(name); + break; + + // handles name::* + case Token.MUL: + decompiler.addName("*"); + name = "*"; + break; + + // handles name::[expr] + case Token.LB: + decompiler.addToken(Token.LB); + pn = nf.createElementGet(pn, namespace, expr(false), + memberTypeFlags); + mustMatchToken(Token.RB, "msg.no.bracket.index"); + decompiler.addToken(Token.RB); + return pn; + + default: + reportError("msg.no.name.after.coloncolon"); + name = "?"; + } + } + + pn = nf.createPropertyGet(pn, namespace, name, memberTypeFlags); + return pn; + } + + private Node primaryExpr() + throws IOException, ParserException + { + Node pn; + + int ttFlagged = nextFlaggedToken(); + int tt = ttFlagged & CLEAR_TI_MASK; + + switch(tt) { + + case Token.FUNCTION: + return function(FunctionNode.FUNCTION_EXPRESSION); + + case Token.LB: { + ObjArray elems = new ObjArray(); + int skipCount = 0; + decompiler.addToken(Token.LB); + boolean after_lb_or_comma = true; + for (;;) { + tt = peekToken(); + + if (tt == Token.COMMA) { + consumeToken(); + decompiler.addToken(Token.COMMA); + if (!after_lb_or_comma) { + after_lb_or_comma = true; + } else { + elems.add(null); + ++skipCount; + } + } else if (tt == Token.RB) { + consumeToken(); + decompiler.addToken(Token.RB); + break; + } else { + if (!after_lb_or_comma) { + reportError("msg.no.bracket.arg"); + } + elems.add(assignExpr(false)); + after_lb_or_comma = false; + } + } + return nf.createArrayLiteral(elems, skipCount); + } + + case Token.LC: { + ObjArray elems = new ObjArray(); + decompiler.addToken(Token.LC); + if (!matchToken(Token.RC)) { + + boolean first = true; + commaloop: + do { + Object property; + + if (!first) + decompiler.addToken(Token.COMMA); + else + first = false; + + tt = peekToken(); + switch(tt) { + case Token.NAME: + case Token.STRING: + consumeToken(); + // map NAMEs to STRINGs in object literal context + // but tell the decompiler the proper type + String s = ts.getString(); + if (tt == Token.NAME) { + if (s.equals("get") && + peekToken() == Token.NAME) { + decompiler.addToken(Token.GET); + consumeToken(); + s = ts.getString(); + decompiler.addName(s); + property = ScriptRuntime.getIndexObject(s); + if (!getterSetterProperty(elems, property, + true)) + break commaloop; + break; + } else if (s.equals("set") && + peekToken() == Token.NAME) { + decompiler.addToken(Token.SET); + consumeToken(); + s = ts.getString(); + decompiler.addName(s); + property = ScriptRuntime.getIndexObject(s); + if (!getterSetterProperty(elems, property, + false)) + break commaloop; + break; + } + decompiler.addName(s); + } else { + decompiler.addString(s); + } + property = ScriptRuntime.getIndexObject(s); + plainProperty(elems, property); + break; + + case Token.NUMBER: + consumeToken(); + double n = ts.getNumber(); + decompiler.addNumber(n); + property = ScriptRuntime.getIndexObject(n); + plainProperty(elems, property); + break; + + case Token.RC: + // trailing comma is OK. + break commaloop; + default: + reportError("msg.bad.prop"); + break commaloop; + } + } while (matchToken(Token.COMMA)); + + mustMatchToken(Token.RC, "msg.no.brace.prop"); + } + decompiler.addToken(Token.RC); + return nf.createObjectLiteral(elems); + } + + case Token.LP: + + /* Brendan's IR-jsparse.c makes a new node tagged with + * TOK_LP here... I'm not sure I understand why. Isn't + * the grouping already implicit in the structure of the + * parse tree? also TOK_LP is already overloaded (I + * think) in the C IR as 'function call.' */ + decompiler.addToken(Token.LP); + pn = expr(false); + pn.putProp(Node.PARENTHESIZED_PROP, Boolean.TRUE); + decompiler.addToken(Token.RP); + mustMatchToken(Token.RP, "msg.no.paren"); + return pn; + + case Token.XMLATTR: + mustHaveXML(); + decompiler.addToken(Token.XMLATTR); + pn = attributeAccess(null, 0); + return pn; + + case Token.NAME: { + String name = ts.getString(); + if ((ttFlagged & TI_CHECK_LABEL) != 0) { + if (peekToken() == Token.COLON) { + // Do not consume colon, it is used as unwind indicator + // to return to statementHelper. + // XXX Better way? + return nf.createLabel(ts.getLineno()); + } + } + + decompiler.addName(name); + if (compilerEnv.isXmlAvailable()) { + pn = propertyName(null, name, 0); + } else { + pn = nf.createName(name); + } + return pn; + } + + case Token.NUMBER: { + double n = ts.getNumber(); + decompiler.addNumber(n); + return nf.createNumber(n); + } + + case Token.STRING: { + String s = ts.getString(); + decompiler.addString(s); + return nf.createString(s); + } + + case Token.DIV: + case Token.ASSIGN_DIV: { + // Got / or /= which should be treated as regexp in fact + ts.readRegExp(tt); + String flags = ts.regExpFlags; + ts.regExpFlags = null; + String re = ts.getString(); + decompiler.addRegexp(re, flags); + int index = currentScriptOrFn.addRegexp(re, flags); + return nf.createRegExp(index); + } + + case Token.NULL: + case Token.THIS: + case Token.FALSE: + case Token.TRUE: + decompiler.addToken(tt); + return nf.createLeaf(tt); + + case Token.RESERVED: + reportError("msg.reserved.id"); + break; + + case Token.ERROR: + /* the scanner or one of its subroutines reported the error. */ + break; + + case Token.EOF: + reportError("msg.unexpected.eof"); + break; + + default: + reportError("msg.syntax"); + break; + } + return null; // should never reach here + } + + private void plainProperty(ObjArray elems, Object property) + throws IOException { + mustMatchToken(Token.COLON, "msg.no.colon.prop"); + + // OBJLIT is used as ':' in object literal for + // decompilation to solve spacing ambiguity. + decompiler.addToken(Token.OBJECTLIT); + elems.add(property); + elems.add(assignExpr(false)); + } + + private boolean getterSetterProperty(ObjArray elems, Object property, + boolean isGetter) throws IOException { + Node f = function(FunctionNode.FUNCTION_EXPRESSION); + if (f.getType() != Token.FUNCTION) { + reportError("msg.bad.prop"); + return false; + } + int fnIndex = f.getExistingIntProp(Node.FUNCTION_PROP); + FunctionNode fn = currentScriptOrFn.getFunctionNode(fnIndex); + if (fn.getFunctionName().length() != 0) { + reportError("msg.bad.prop"); + return false; + } + elems.add(property); + if (isGetter) { + elems.add(nf.createUnary(Token.GET, f)); + } else { + elems.add(nf.createUnary(Token.SET, f)); + } + return true; + } +} diff --git a/trunk/infrastructure/yuicompressor/src/yuicompressor/org/mozilla/javascript/Token.java b/trunk/infrastructure/yuicompressor/src/yuicompressor/org/mozilla/javascript/Token.java new file mode 100644 index 0000000..f082d68 --- /dev/null +++ b/trunk/infrastructure/yuicompressor/src/yuicompressor/org/mozilla/javascript/Token.java @@ -0,0 +1,420 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Roger Lawrence + * Mike McCabe + * Igor Bukanov + * Bob Jervis + * Milen Nankov + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package yuicompressor.org.mozilla.javascript; + +/** + * This class implements the JavaScript scanner. + * + * It is based on the C source files jsscan.c and jsscan.h + * in the jsref package. + * + * @see yuicompressor.org.mozilla.javascript.Parser + * + * @author Mike McCabe + * @author Brendan Eich + */ + +public class Token +{ + + // debug flags + public static final boolean printTrees = false; + static final boolean printICode = false; + static final boolean printNames = printTrees || printICode; + + /** + * Token types. These values correspond to JSTokenType values in + * jsscan.c. + */ + + public final static int + // start enum + ERROR = -1, // well-known as the only code < EOF + EOF = 0, // end of file token - (not EOF_CHAR) + EOL = 1, // end of line + + // Interpreter reuses the following as bytecodes + FIRST_BYTECODE_TOKEN = 2, + + ENTERWITH = 2, + LEAVEWITH = 3, + RETURN = 4, + GOTO = 5, + IFEQ = 6, + IFNE = 7, + SETNAME = 8, + BITOR = 9, + BITXOR = 10, + BITAND = 11, + EQ = 12, + NE = 13, + LT = 14, + LE = 15, + GT = 16, + GE = 17, + LSH = 18, + RSH = 19, + URSH = 20, + ADD = 21, + SUB = 22, + MUL = 23, + DIV = 24, + MOD = 25, + NOT = 26, + BITNOT = 27, + POS = 28, + NEG = 29, + NEW = 30, + DELPROP = 31, + TYPEOF = 32, + GETPROP = 33, + SETPROP = 34, + GETELEM = 35, + SETELEM = 36, + CALL = 37, + NAME = 38, + NUMBER = 39, + STRING = 40, + NULL = 41, + THIS = 42, + FALSE = 43, + TRUE = 44, + SHEQ = 45, // shallow equality (===) + SHNE = 46, // shallow inequality (!==) + REGEXP = 47, + BINDNAME = 48, + THROW = 49, + RETHROW = 50, // rethrow caught execetion: catch (e if ) use it + IN = 51, + INSTANCEOF = 52, + LOCAL_LOAD = 53, + GETVAR = 54, + SETVAR = 55, + CATCH_SCOPE = 56, + ENUM_INIT_KEYS = 57, + ENUM_INIT_VALUES = 58, + ENUM_NEXT = 59, + ENUM_ID = 60, + THISFN = 61, + RETURN_RESULT = 62, // to return prevoisly stored return result + ARRAYLIT = 63, // array literal + OBJECTLIT = 64, // object literal + GET_REF = 65, // *reference + SET_REF = 66, // *reference = something + DEL_REF = 67, // delete reference + REF_CALL = 68, // f(args) = something or f(args)++ + REF_SPECIAL = 69, // reference for special properties like __proto + + // For XML support: + DEFAULTNAMESPACE = 70, // default xml namespace = + ESCXMLATTR = 71, + ESCXMLTEXT = 72, + REF_MEMBER = 73, // Reference for x.@y, x..y etc. + REF_NS_MEMBER = 74, // Reference for x.ns::y, x..ns::y etc. + REF_NAME = 75, // Reference for @y, @[y] etc. + REF_NS_NAME = 76; // Reference for ns::y, @ns::y@[y] etc. + + // End of interpreter bytecodes + public final static int + LAST_BYTECODE_TOKEN = REF_NS_NAME, + + TRY = 77, + SEMI = 78, // semicolon + LB = 79, // left and right brackets + RB = 80, + LC = 81, // left and right curlies (braces) + RC = 82, + LP = 83, // left and right parentheses + RP = 84, + COMMA = 85, // comma operator + + ASSIGN = 86, // simple assignment (=) + ASSIGN_BITOR = 87, // |= + ASSIGN_BITXOR = 88, // ^= + ASSIGN_BITAND = 89, // |= + ASSIGN_LSH = 90, // <<= + ASSIGN_RSH = 91, // >>= + ASSIGN_URSH = 92, // >>>= + ASSIGN_ADD = 93, // += + ASSIGN_SUB = 94, // -= + ASSIGN_MUL = 95, // *= + ASSIGN_DIV = 96, // /= + ASSIGN_MOD = 97; // %= + + public final static int + FIRST_ASSIGN = ASSIGN, + LAST_ASSIGN = ASSIGN_MOD, + + HOOK = 98, // conditional (?:) + COLON = 99, + OR = 100, // logical or (||) + AND = 101, // logical and (&&) + INC = 102, // increment/decrement (++ --) + DEC = 103, + DOT = 104, // member operator (.) + FUNCTION = 105, // function keyword + EXPORT = 106, // export keyword + IMPORT = 107, // import keyword + IF = 108, // if keyword + ELSE = 109, // else keyword + SWITCH = 110, // switch keyword + CASE = 111, // case keyword + DEFAULT = 112, // default keyword + WHILE = 113, // while keyword + DO = 114, // do keyword + FOR = 115, // for keyword + BREAK = 116, // break keyword + CONTINUE = 117, // continue keyword + VAR = 118, // var keyword + WITH = 119, // with keyword + CATCH = 120, // catch keyword + FINALLY = 121, // finally keyword + VOID = 122, // void keyword + RESERVED = 123, // reserved keywords + + EMPTY = 124, + + /* types used for the parse tree - these never get returned + * by the scanner. + */ + + BLOCK = 125, // statement block + LABEL = 126, // label + TARGET = 127, + LOOP = 128, + EXPR_VOID = 129, // expression statement in functions + EXPR_RESULT = 130, // expression statement in scripts + JSR = 131, + SCRIPT = 132, // top-level node for entire script + TYPEOFNAME = 133, // for typeof(simple-name) + USE_STACK = 134, + SETPROP_OP = 135, // x.y op= something + SETELEM_OP = 136, // x[y] op= something + LOCAL_BLOCK = 137, + SET_REF_OP = 138, // *reference op= something + + // For XML support: + DOTDOT = 139, // member operator (..) + COLONCOLON = 140, // namespace::name + XML = 141, // XML type + DOTQUERY = 142, // .() -- e.g., x.emps.emp.(name == "terry") + XMLATTR = 143, // @ + XMLEND = 144, + + // Optimizer-only-tokens + TO_OBJECT = 145, + TO_DOUBLE = 146, + + GET = 147, // JS 1.5 get pseudo keyword + SET = 148, // JS 1.5 set pseudo keyword + CONST = 149, + SETCONST = 150, + SETCONSTVAR = 151, + + SPECIALCOMMENT = 152, // Internet Explorer conditional comment + + LAST_TOKEN = 153; + + public static String name(int token) + { + if (!printNames) { + return String.valueOf(token); + } + switch (token) { + case ERROR: return "ERROR"; + case EOF: return "EOF"; + case EOL: return "EOL"; + case ENTERWITH: return "ENTERWITH"; + case LEAVEWITH: return "LEAVEWITH"; + case RETURN: return "RETURN"; + case GOTO: return "GOTO"; + case IFEQ: return "IFEQ"; + case IFNE: return "IFNE"; + case SETNAME: return "SETNAME"; + case BITOR: return "BITOR"; + case BITXOR: return "BITXOR"; + case BITAND: return "BITAND"; + case EQ: return "EQ"; + case NE: return "NE"; + case LT: return "LT"; + case LE: return "LE"; + case GT: return "GT"; + case GE: return "GE"; + case LSH: return "LSH"; + case RSH: return "RSH"; + case URSH: return "URSH"; + case ADD: return "ADD"; + case SUB: return "SUB"; + case MUL: return "MUL"; + case DIV: return "DIV"; + case MOD: return "MOD"; + case NOT: return "NOT"; + case BITNOT: return "BITNOT"; + case POS: return "POS"; + case NEG: return "NEG"; + case NEW: return "NEW"; + case DELPROP: return "DELPROP"; + case TYPEOF: return "TYPEOF"; + case GETPROP: return "GETPROP"; + case SETPROP: return "SETPROP"; + case GETELEM: return "GETELEM"; + case SETELEM: return "SETELEM"; + case CALL: return "CALL"; + case NAME: return "NAME"; + case NUMBER: return "NUMBER"; + case STRING: return "STRING"; + case NULL: return "NULL"; + case THIS: return "THIS"; + case FALSE: return "FALSE"; + case TRUE: return "TRUE"; + case SHEQ: return "SHEQ"; + case SHNE: return "SHNE"; + case REGEXP: return "OBJECT"; + case BINDNAME: return "BINDNAME"; + case THROW: return "THROW"; + case RETHROW: return "RETHROW"; + case IN: return "IN"; + case INSTANCEOF: return "INSTANCEOF"; + case LOCAL_LOAD: return "LOCAL_LOAD"; + case GETVAR: return "GETVAR"; + case SETVAR: return "SETVAR"; + case CATCH_SCOPE: return "CATCH_SCOPE"; + case ENUM_INIT_KEYS: return "ENUM_INIT_KEYS"; + case ENUM_INIT_VALUES: return "ENUM_INIT_VALUES"; + case ENUM_NEXT: return "ENUM_NEXT"; + case ENUM_ID: return "ENUM_ID"; + case THISFN: return "THISFN"; + case RETURN_RESULT: return "RETURN_RESULT"; + case ARRAYLIT: return "ARRAYLIT"; + case OBJECTLIT: return "OBJECTLIT"; + case GET_REF: return "GET_REF"; + case SET_REF: return "SET_REF"; + case DEL_REF: return "DEL_REF"; + case REF_CALL: return "REF_CALL"; + case REF_SPECIAL: return "REF_SPECIAL"; + case DEFAULTNAMESPACE:return "DEFAULTNAMESPACE"; + case ESCXMLTEXT: return "ESCXMLTEXT"; + case ESCXMLATTR: return "ESCXMLATTR"; + case REF_MEMBER: return "REF_MEMBER"; + case REF_NS_MEMBER: return "REF_NS_MEMBER"; + case REF_NAME: return "REF_NAME"; + case REF_NS_NAME: return "REF_NS_NAME"; + case TRY: return "TRY"; + case SEMI: return "SEMI"; + case LB: return "LB"; + case RB: return "RB"; + case LC: return "LC"; + case RC: return "RC"; + case LP: return "LP"; + case RP: return "RP"; + case COMMA: return "COMMA"; + case ASSIGN: return "ASSIGN"; + case ASSIGN_BITOR: return "ASSIGN_BITOR"; + case ASSIGN_BITXOR: return "ASSIGN_BITXOR"; + case ASSIGN_BITAND: return "ASSIGN_BITAND"; + case ASSIGN_LSH: return "ASSIGN_LSH"; + case ASSIGN_RSH: return "ASSIGN_RSH"; + case ASSIGN_URSH: return "ASSIGN_URSH"; + case ASSIGN_ADD: return "ASSIGN_ADD"; + case ASSIGN_SUB: return "ASSIGN_SUB"; + case ASSIGN_MUL: return "ASSIGN_MUL"; + case ASSIGN_DIV: return "ASSIGN_DIV"; + case ASSIGN_MOD: return "ASSIGN_MOD"; + case HOOK: return "HOOK"; + case COLON: return "COLON"; + case OR: return "OR"; + case AND: return "AND"; + case INC: return "INC"; + case DEC: return "DEC"; + case DOT: return "DOT"; + case FUNCTION: return "FUNCTION"; + case EXPORT: return "EXPORT"; + case IMPORT: return "IMPORT"; + case IF: return "IF"; + case ELSE: return "ELSE"; + case SWITCH: return "SWITCH"; + case CASE: return "CASE"; + case DEFAULT: return "DEFAULT"; + case WHILE: return "WHILE"; + case DO: return "DO"; + case FOR: return "FOR"; + case BREAK: return "BREAK"; + case CONTINUE: return "CONTINUE"; + case VAR: return "VAR"; + case WITH: return "WITH"; + case CATCH: return "CATCH"; + case FINALLY: return "FINALLY"; + case RESERVED: return "RESERVED"; + case EMPTY: return "EMPTY"; + case BLOCK: return "BLOCK"; + case LABEL: return "LABEL"; + case TARGET: return "TARGET"; + case LOOP: return "LOOP"; + case EXPR_VOID: return "EXPR_VOID"; + case EXPR_RESULT: return "EXPR_RESULT"; + case JSR: return "JSR"; + case SCRIPT: return "SCRIPT"; + case TYPEOFNAME: return "TYPEOFNAME"; + case USE_STACK: return "USE_STACK"; + case SETPROP_OP: return "SETPROP_OP"; + case SETELEM_OP: return "SETELEM_OP"; + case LOCAL_BLOCK: return "LOCAL_BLOCK"; + case SET_REF_OP: return "SET_REF_OP"; + case DOTDOT: return "DOTDOT"; + case COLONCOLON: return "COLONCOLON"; + case XML: return "XML"; + case DOTQUERY: return "DOTQUERY"; + case XMLATTR: return "XMLATTR"; + case XMLEND: return "XMLEND"; + case TO_OBJECT: return "TO_OBJECT"; + case TO_DOUBLE: return "TO_DOUBLE"; + case GET: return "GET"; + case SET: return "SET"; + case CONST: return "CONST"; + case SETCONST: return "SETCONST"; + } + + // Token without name + throw new IllegalStateException(String.valueOf(token)); + } +} diff --git a/trunk/infrastructure/yuicompressor/src/yuicompressor/org/mozilla/javascript/Token.java.orig b/trunk/infrastructure/yuicompressor/src/yuicompressor/org/mozilla/javascript/Token.java.orig new file mode 100644 index 0000000..7f7cdc2 --- /dev/null +++ b/trunk/infrastructure/yuicompressor/src/yuicompressor/org/mozilla/javascript/Token.java.orig @@ -0,0 +1,417 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Roger Lawrence + * Mike McCabe + * Igor Bukanov + * Bob Jervis + * Milen Nankov + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript; + +/** + * This class implements the JavaScript scanner. + * + * It is based on the C source files jsscan.c and jsscan.h + * in the jsref package. + * + * @see org.mozilla.javascript.Parser + * + * @author Mike McCabe + * @author Brendan Eich + */ + +public class Token +{ + + // debug flags + public static final boolean printTrees = false; + static final boolean printICode = false; + static final boolean printNames = printTrees || printICode; + + /** + * Token types. These values correspond to JSTokenType values in + * jsscan.c. + */ + + public final static int + // start enum + ERROR = -1, // well-known as the only code < EOF + EOF = 0, // end of file token - (not EOF_CHAR) + EOL = 1, // end of line + + // Interpreter reuses the following as bytecodes + FIRST_BYTECODE_TOKEN = 2, + + ENTERWITH = 2, + LEAVEWITH = 3, + RETURN = 4, + GOTO = 5, + IFEQ = 6, + IFNE = 7, + SETNAME = 8, + BITOR = 9, + BITXOR = 10, + BITAND = 11, + EQ = 12, + NE = 13, + LT = 14, + LE = 15, + GT = 16, + GE = 17, + LSH = 18, + RSH = 19, + URSH = 20, + ADD = 21, + SUB = 22, + MUL = 23, + DIV = 24, + MOD = 25, + NOT = 26, + BITNOT = 27, + POS = 28, + NEG = 29, + NEW = 30, + DELPROP = 31, + TYPEOF = 32, + GETPROP = 33, + SETPROP = 34, + GETELEM = 35, + SETELEM = 36, + CALL = 37, + NAME = 38, + NUMBER = 39, + STRING = 40, + NULL = 41, + THIS = 42, + FALSE = 43, + TRUE = 44, + SHEQ = 45, // shallow equality (===) + SHNE = 46, // shallow inequality (!==) + REGEXP = 47, + BINDNAME = 48, + THROW = 49, + RETHROW = 50, // rethrow caught execetion: catch (e if ) use it + IN = 51, + INSTANCEOF = 52, + LOCAL_LOAD = 53, + GETVAR = 54, + SETVAR = 55, + CATCH_SCOPE = 56, + ENUM_INIT_KEYS = 57, + ENUM_INIT_VALUES = 58, + ENUM_NEXT = 59, + ENUM_ID = 60, + THISFN = 61, + RETURN_RESULT = 62, // to return prevoisly stored return result + ARRAYLIT = 63, // array literal + OBJECTLIT = 64, // object literal + GET_REF = 65, // *reference + SET_REF = 66, // *reference = something + DEL_REF = 67, // delete reference + REF_CALL = 68, // f(args) = something or f(args)++ + REF_SPECIAL = 69, // reference for special properties like __proto + + // For XML support: + DEFAULTNAMESPACE = 70, // default xml namespace = + ESCXMLATTR = 71, + ESCXMLTEXT = 72, + REF_MEMBER = 73, // Reference for x.@y, x..y etc. + REF_NS_MEMBER = 74, // Reference for x.ns::y, x..ns::y etc. + REF_NAME = 75, // Reference for @y, @[y] etc. + REF_NS_NAME = 76; // Reference for ns::y, @ns::y@[y] etc. + + // End of interpreter bytecodes + public final static int + LAST_BYTECODE_TOKEN = REF_NS_NAME, + + TRY = 77, + SEMI = 78, // semicolon + LB = 79, // left and right brackets + RB = 80, + LC = 81, // left and right curlies (braces) + RC = 82, + LP = 83, // left and right parentheses + RP = 84, + COMMA = 85, // comma operator + + ASSIGN = 86, // simple assignment (=) + ASSIGN_BITOR = 87, // |= + ASSIGN_BITXOR = 88, // ^= + ASSIGN_BITAND = 89, // |= + ASSIGN_LSH = 90, // <<= + ASSIGN_RSH = 91, // >>= + ASSIGN_URSH = 92, // >>>= + ASSIGN_ADD = 93, // += + ASSIGN_SUB = 94, // -= + ASSIGN_MUL = 95, // *= + ASSIGN_DIV = 96, // /= + ASSIGN_MOD = 97; // %= + + public final static int + FIRST_ASSIGN = ASSIGN, + LAST_ASSIGN = ASSIGN_MOD, + + HOOK = 98, // conditional (?:) + COLON = 99, + OR = 100, // logical or (||) + AND = 101, // logical and (&&) + INC = 102, // increment/decrement (++ --) + DEC = 103, + DOT = 104, // member operator (.) + FUNCTION = 105, // function keyword + EXPORT = 106, // export keyword + IMPORT = 107, // import keyword + IF = 108, // if keyword + ELSE = 109, // else keyword + SWITCH = 110, // switch keyword + CASE = 111, // case keyword + DEFAULT = 112, // default keyword + WHILE = 113, // while keyword + DO = 114, // do keyword + FOR = 115, // for keyword + BREAK = 116, // break keyword + CONTINUE = 117, // continue keyword + VAR = 118, // var keyword + WITH = 119, // with keyword + CATCH = 120, // catch keyword + FINALLY = 121, // finally keyword + VOID = 122, // void keyword + RESERVED = 123, // reserved keywords + + EMPTY = 124, + + /* types used for the parse tree - these never get returned + * by the scanner. + */ + + BLOCK = 125, // statement block + LABEL = 126, // label + TARGET = 127, + LOOP = 128, + EXPR_VOID = 129, // expression statement in functions + EXPR_RESULT = 130, // expression statement in scripts + JSR = 131, + SCRIPT = 132, // top-level node for entire script + TYPEOFNAME = 133, // for typeof(simple-name) + USE_STACK = 134, + SETPROP_OP = 135, // x.y op= something + SETELEM_OP = 136, // x[y] op= something + LOCAL_BLOCK = 137, + SET_REF_OP = 138, // *reference op= something + + // For XML support: + DOTDOT = 139, // member operator (..) + COLONCOLON = 140, // namespace::name + XML = 141, // XML type + DOTQUERY = 142, // .() -- e.g., x.emps.emp.(name == "terry") + XMLATTR = 143, // @ + XMLEND = 144, + + // Optimizer-only-tokens + TO_OBJECT = 145, + TO_DOUBLE = 146, + + GET = 147, // JS 1.5 get pseudo keyword + SET = 148, // JS 1.5 set pseudo keyword + CONST = 149, + SETCONST = 150, + SETCONSTVAR = 151, + LAST_TOKEN = 152; + + public static String name(int token) + { + if (!printNames) { + return String.valueOf(token); + } + switch (token) { + case ERROR: return "ERROR"; + case EOF: return "EOF"; + case EOL: return "EOL"; + case ENTERWITH: return "ENTERWITH"; + case LEAVEWITH: return "LEAVEWITH"; + case RETURN: return "RETURN"; + case GOTO: return "GOTO"; + case IFEQ: return "IFEQ"; + case IFNE: return "IFNE"; + case SETNAME: return "SETNAME"; + case BITOR: return "BITOR"; + case BITXOR: return "BITXOR"; + case BITAND: return "BITAND"; + case EQ: return "EQ"; + case NE: return "NE"; + case LT: return "LT"; + case LE: return "LE"; + case GT: return "GT"; + case GE: return "GE"; + case LSH: return "LSH"; + case RSH: return "RSH"; + case URSH: return "URSH"; + case ADD: return "ADD"; + case SUB: return "SUB"; + case MUL: return "MUL"; + case DIV: return "DIV"; + case MOD: return "MOD"; + case NOT: return "NOT"; + case BITNOT: return "BITNOT"; + case POS: return "POS"; + case NEG: return "NEG"; + case NEW: return "NEW"; + case DELPROP: return "DELPROP"; + case TYPEOF: return "TYPEOF"; + case GETPROP: return "GETPROP"; + case SETPROP: return "SETPROP"; + case GETELEM: return "GETELEM"; + case SETELEM: return "SETELEM"; + case CALL: return "CALL"; + case NAME: return "NAME"; + case NUMBER: return "NUMBER"; + case STRING: return "STRING"; + case NULL: return "NULL"; + case THIS: return "THIS"; + case FALSE: return "FALSE"; + case TRUE: return "TRUE"; + case SHEQ: return "SHEQ"; + case SHNE: return "SHNE"; + case REGEXP: return "OBJECT"; + case BINDNAME: return "BINDNAME"; + case THROW: return "THROW"; + case RETHROW: return "RETHROW"; + case IN: return "IN"; + case INSTANCEOF: return "INSTANCEOF"; + case LOCAL_LOAD: return "LOCAL_LOAD"; + case GETVAR: return "GETVAR"; + case SETVAR: return "SETVAR"; + case CATCH_SCOPE: return "CATCH_SCOPE"; + case ENUM_INIT_KEYS: return "ENUM_INIT_KEYS"; + case ENUM_INIT_VALUES: return "ENUM_INIT_VALUES"; + case ENUM_NEXT: return "ENUM_NEXT"; + case ENUM_ID: return "ENUM_ID"; + case THISFN: return "THISFN"; + case RETURN_RESULT: return "RETURN_RESULT"; + case ARRAYLIT: return "ARRAYLIT"; + case OBJECTLIT: return "OBJECTLIT"; + case GET_REF: return "GET_REF"; + case SET_REF: return "SET_REF"; + case DEL_REF: return "DEL_REF"; + case REF_CALL: return "REF_CALL"; + case REF_SPECIAL: return "REF_SPECIAL"; + case DEFAULTNAMESPACE:return "DEFAULTNAMESPACE"; + case ESCXMLTEXT: return "ESCXMLTEXT"; + case ESCXMLATTR: return "ESCXMLATTR"; + case REF_MEMBER: return "REF_MEMBER"; + case REF_NS_MEMBER: return "REF_NS_MEMBER"; + case REF_NAME: return "REF_NAME"; + case REF_NS_NAME: return "REF_NS_NAME"; + case TRY: return "TRY"; + case SEMI: return "SEMI"; + case LB: return "LB"; + case RB: return "RB"; + case LC: return "LC"; + case RC: return "RC"; + case LP: return "LP"; + case RP: return "RP"; + case COMMA: return "COMMA"; + case ASSIGN: return "ASSIGN"; + case ASSIGN_BITOR: return "ASSIGN_BITOR"; + case ASSIGN_BITXOR: return "ASSIGN_BITXOR"; + case ASSIGN_BITAND: return "ASSIGN_BITAND"; + case ASSIGN_LSH: return "ASSIGN_LSH"; + case ASSIGN_RSH: return "ASSIGN_RSH"; + case ASSIGN_URSH: return "ASSIGN_URSH"; + case ASSIGN_ADD: return "ASSIGN_ADD"; + case ASSIGN_SUB: return "ASSIGN_SUB"; + case ASSIGN_MUL: return "ASSIGN_MUL"; + case ASSIGN_DIV: return "ASSIGN_DIV"; + case ASSIGN_MOD: return "ASSIGN_MOD"; + case HOOK: return "HOOK"; + case COLON: return "COLON"; + case OR: return "OR"; + case AND: return "AND"; + case INC: return "INC"; + case DEC: return "DEC"; + case DOT: return "DOT"; + case FUNCTION: return "FUNCTION"; + case EXPORT: return "EXPORT"; + case IMPORT: return "IMPORT"; + case IF: return "IF"; + case ELSE: return "ELSE"; + case SWITCH: return "SWITCH"; + case CASE: return "CASE"; + case DEFAULT: return "DEFAULT"; + case WHILE: return "WHILE"; + case DO: return "DO"; + case FOR: return "FOR"; + case BREAK: return "BREAK"; + case CONTINUE: return "CONTINUE"; + case VAR: return "VAR"; + case WITH: return "WITH"; + case CATCH: return "CATCH"; + case FINALLY: return "FINALLY"; + case RESERVED: return "RESERVED"; + case EMPTY: return "EMPTY"; + case BLOCK: return "BLOCK"; + case LABEL: return "LABEL"; + case TARGET: return "TARGET"; + case LOOP: return "LOOP"; + case EXPR_VOID: return "EXPR_VOID"; + case EXPR_RESULT: return "EXPR_RESULT"; + case JSR: return "JSR"; + case SCRIPT: return "SCRIPT"; + case TYPEOFNAME: return "TYPEOFNAME"; + case USE_STACK: return "USE_STACK"; + case SETPROP_OP: return "SETPROP_OP"; + case SETELEM_OP: return "SETELEM_OP"; + case LOCAL_BLOCK: return "LOCAL_BLOCK"; + case SET_REF_OP: return "SET_REF_OP"; + case DOTDOT: return "DOTDOT"; + case COLONCOLON: return "COLONCOLON"; + case XML: return "XML"; + case DOTQUERY: return "DOTQUERY"; + case XMLATTR: return "XMLATTR"; + case XMLEND: return "XMLEND"; + case TO_OBJECT: return "TO_OBJECT"; + case TO_DOUBLE: return "TO_DOUBLE"; + case GET: return "GET"; + case SET: return "SET"; + case CONST: return "CONST"; + case SETCONST: return "SETCONST"; + } + + // Token without name + throw new IllegalStateException(String.valueOf(token)); + } +} diff --git a/trunk/infrastructure/yuicompressor/src/yuicompressor/org/mozilla/javascript/TokenStream.java b/trunk/infrastructure/yuicompressor/src/yuicompressor/org/mozilla/javascript/TokenStream.java new file mode 100644 index 0000000..5bee827 --- /dev/null +++ b/trunk/infrastructure/yuicompressor/src/yuicompressor/org/mozilla/javascript/TokenStream.java @@ -0,0 +1,1381 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Roger Lawrence + * Mike McCabe + * Igor Bukanov + * Ethan Hugg + * Bob Jervis + * Terry Lucas + * Milen Nankov + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package yuicompressor.org.mozilla.javascript; + +import java.io.*; + +/** + * This class implements the JavaScript scanner. + * + * It is based on the C source files jsscan.c and jsscan.h + * in the jsref package. + * + * @see yuicompressor.org.mozilla.javascript.Parser + * + * @author Mike McCabe + * @author Brendan Eich + */ + +class TokenStream +{ + /* + * For chars - because we need something out-of-range + * to check. (And checking EOF by exception is annoying.) + * Note distinction from EOF token type! + */ + private final static int + EOF_CHAR = -1; + + TokenStream(Parser parser, Reader sourceReader, String sourceString, + int lineno) + { + this.parser = parser; + this.lineno = lineno; + if (sourceReader != null) { + if (sourceString != null) Kit.codeBug(); + this.sourceReader = sourceReader; + this.sourceBuffer = new char[512]; + this.sourceEnd = 0; + } else { + if (sourceString == null) Kit.codeBug(); + this.sourceString = sourceString; + this.sourceEnd = sourceString.length(); + } + this.sourceCursor = 0; + } + + /* This function uses the cached op, string and number fields in + * TokenStream; if getToken has been called since the passed token + * was scanned, the op or string printed may be incorrect. + */ + String tokenToString(int token) + { + if (Token.printTrees) { + String name = Token.name(token); + + switch (token) { + case Token.STRING: + case Token.REGEXP: + case Token.NAME: + return name + " `" + this.string + "'"; + + case Token.NUMBER: + return "NUMBER " + this.number; + } + + return name; + } + return ""; + } + + static boolean isKeyword(String s) + { + return Token.EOF != stringToKeyword(s); + } + + private static int stringToKeyword(String name) + { +// #string_id_map# +// The following assumes that Token.EOF == 0 + final int + Id_break = Token.BREAK, + Id_case = Token.CASE, + Id_continue = Token.CONTINUE, + Id_default = Token.DEFAULT, + Id_delete = Token.DELPROP, + Id_do = Token.DO, + Id_else = Token.ELSE, + Id_export = Token.EXPORT, + Id_false = Token.FALSE, + Id_for = Token.FOR, + Id_function = Token.FUNCTION, + Id_if = Token.IF, + Id_in = Token.IN, + Id_new = Token.NEW, + Id_null = Token.NULL, + Id_return = Token.RETURN, + Id_switch = Token.SWITCH, + Id_this = Token.THIS, + Id_true = Token.TRUE, + Id_typeof = Token.TYPEOF, + Id_var = Token.VAR, + Id_void = Token.VOID, + Id_while = Token.WHILE, + Id_with = Token.WITH, + + // the following are #ifdef RESERVE_JAVA_KEYWORDS in jsscan.c + Id_abstract = Token.RESERVED, + Id_boolean = Token.RESERVED, + Id_byte = Token.RESERVED, + Id_catch = Token.CATCH, + Id_char = Token.RESERVED, + Id_class = Token.RESERVED, + Id_const = Token.CONST, + Id_debugger = Token.RESERVED, + Id_double = Token.RESERVED, + Id_enum = Token.RESERVED, + Id_extends = Token.RESERVED, + Id_final = Token.RESERVED, + Id_finally = Token.FINALLY, + Id_float = Token.RESERVED, + Id_goto = Token.RESERVED, + Id_implements = Token.RESERVED, + Id_import = Token.IMPORT, + Id_instanceof = Token.INSTANCEOF, + Id_int = Token.RESERVED, + Id_interface = Token.RESERVED, + Id_long = Token.RESERVED, + Id_native = Token.RESERVED, + Id_package = Token.RESERVED, + Id_private = Token.RESERVED, + Id_protected = Token.RESERVED, + Id_public = Token.RESERVED, + Id_short = Token.RESERVED, + Id_static = Token.RESERVED, + Id_super = Token.RESERVED, + Id_synchronized = Token.RESERVED, + Id_throw = Token.THROW, + Id_throws = Token.RESERVED, + Id_transient = Token.RESERVED, + Id_try = Token.TRY, + Id_volatile = Token.RESERVED; + + int id; + String s = name; +// #generated# Last update: 2001-06-01 17:45:01 CEST + L0: { id = 0; String X = null; int c; + L: switch (s.length()) { + case 2: c=s.charAt(1); + if (c=='f') { if (s.charAt(0)=='i') {id=Id_if; break L0;} } + else if (c=='n') { if (s.charAt(0)=='i') {id=Id_in; break L0;} } + else if (c=='o') { if (s.charAt(0)=='d') {id=Id_do; break L0;} } + break L; + case 3: switch (s.charAt(0)) { + case 'f': if (s.charAt(2)=='r' && s.charAt(1)=='o') {id=Id_for; break L0;} break L; + case 'i': if (s.charAt(2)=='t' && s.charAt(1)=='n') {id=Id_int; break L0;} break L; + case 'n': if (s.charAt(2)=='w' && s.charAt(1)=='e') {id=Id_new; break L0;} break L; + case 't': if (s.charAt(2)=='y' && s.charAt(1)=='r') {id=Id_try; break L0;} break L; + case 'v': if (s.charAt(2)=='r' && s.charAt(1)=='a') {id=Id_var; break L0;} break L; + } break L; + case 4: switch (s.charAt(0)) { + case 'b': X="byte";id=Id_byte; break L; + case 'c': c=s.charAt(3); + if (c=='e') { if (s.charAt(2)=='s' && s.charAt(1)=='a') {id=Id_case; break L0;} } + else if (c=='r') { if (s.charAt(2)=='a' && s.charAt(1)=='h') {id=Id_char; break L0;} } + break L; + case 'e': c=s.charAt(3); + if (c=='e') { if (s.charAt(2)=='s' && s.charAt(1)=='l') {id=Id_else; break L0;} } + else if (c=='m') { if (s.charAt(2)=='u' && s.charAt(1)=='n') {id=Id_enum; break L0;} } + break L; + case 'g': X="goto";id=Id_goto; break L; + case 'l': X="long";id=Id_long; break L; + case 'n': X="null";id=Id_null; break L; + case 't': c=s.charAt(3); + if (c=='e') { if (s.charAt(2)=='u' && s.charAt(1)=='r') {id=Id_true; break L0;} } + else if (c=='s') { if (s.charAt(2)=='i' && s.charAt(1)=='h') {id=Id_this; break L0;} } + break L; + case 'v': X="void";id=Id_void; break L; + case 'w': X="with";id=Id_with; break L; + } break L; + case 5: switch (s.charAt(2)) { + case 'a': X="class";id=Id_class; break L; + case 'e': X="break";id=Id_break; break L; + case 'i': X="while";id=Id_while; break L; + case 'l': X="false";id=Id_false; break L; + case 'n': c=s.charAt(0); + if (c=='c') { X="const";id=Id_const; } + else if (c=='f') { X="final";id=Id_final; } + break L; + case 'o': c=s.charAt(0); + if (c=='f') { X="float";id=Id_float; } + else if (c=='s') { X="short";id=Id_short; } + break L; + case 'p': X="super";id=Id_super; break L; + case 'r': X="throw";id=Id_throw; break L; + case 't': X="catch";id=Id_catch; break L; + } break L; + case 6: switch (s.charAt(1)) { + case 'a': X="native";id=Id_native; break L; + case 'e': c=s.charAt(0); + if (c=='d') { X="delete";id=Id_delete; } + else if (c=='r') { X="return";id=Id_return; } + break L; + case 'h': X="throws";id=Id_throws; break L; + case 'm': X="import";id=Id_import; break L; + case 'o': X="double";id=Id_double; break L; + case 't': X="static";id=Id_static; break L; + case 'u': X="public";id=Id_public; break L; + case 'w': X="switch";id=Id_switch; break L; + case 'x': X="export";id=Id_export; break L; + case 'y': X="typeof";id=Id_typeof; break L; + } break L; + case 7: switch (s.charAt(1)) { + case 'a': X="package";id=Id_package; break L; + case 'e': X="default";id=Id_default; break L; + case 'i': X="finally";id=Id_finally; break L; + case 'o': X="boolean";id=Id_boolean; break L; + case 'r': X="private";id=Id_private; break L; + case 'x': X="extends";id=Id_extends; break L; + } break L; + case 8: switch (s.charAt(0)) { + case 'a': X="abstract";id=Id_abstract; break L; + case 'c': X="continue";id=Id_continue; break L; + case 'd': X="debugger";id=Id_debugger; break L; + case 'f': X="function";id=Id_function; break L; + case 'v': X="volatile";id=Id_volatile; break L; + } break L; + case 9: c=s.charAt(0); + if (c=='i') { X="interface";id=Id_interface; } + else if (c=='p') { X="protected";id=Id_protected; } + else if (c=='t') { X="transient";id=Id_transient; } + break L; + case 10: c=s.charAt(1); + if (c=='m') { X="implements";id=Id_implements; } + else if (c=='n') { X="instanceof";id=Id_instanceof; } + break L; + case 12: X="synchronized";id=Id_synchronized; break L; + } + if (X!=null && X!=s && !X.equals(s)) id = 0; + } +// #/generated# +// #/string_id_map# + if (id == 0) { return Token.EOF; } + return id & 0xff; + } + + final int getLineno() { return lineno; } + + final String getString() { return string; } + + final double getNumber() { return number; } + + final boolean eof() { return hitEOF; } + + final int getToken() throws IOException + { + int c; + + retry: + for (;;) { + // Eat whitespace, possibly sensitive to newlines. + for (;;) { + c = getChar(); + if (c == EOF_CHAR) { + return Token.EOF; + } else if (c == '\n') { + dirtyLine = false; + return Token.EOL; + } else if (!isJSSpace(c)) { + if (c != '-') { + dirtyLine = true; + } + break; + } + } + + if (c == '@') return Token.XMLATTR; + + // identifier/keyword/instanceof? + // watch out for starting with a + boolean identifierStart; + boolean isUnicodeEscapeStart = false; + if (c == '\\') { + c = getChar(); + if (c == 'u') { + identifierStart = true; + isUnicodeEscapeStart = true; + stringBufferTop = 0; + } else { + identifierStart = false; + ungetChar(c); + c = '\\'; + } + } else { + identifierStart = Character.isJavaIdentifierStart((char)c); + if (identifierStart) { + stringBufferTop = 0; + addToString(c); + } + } + + if (identifierStart) { + boolean containsEscape = isUnicodeEscapeStart; + for (;;) { + if (isUnicodeEscapeStart) { + // strictly speaking we should probably push-back + // all the bad characters if the uXXXX + // sequence is malformed. But since there isn't a + // correct context(is there?) for a bad Unicode + // escape sequence in an identifier, we can report + // an error here. + int escapeVal = 0; + for (int i = 0; i != 4; ++i) { + c = getChar(); + escapeVal = Kit.xDigitToInt(c, escapeVal); + // Next check takes care about c < 0 and bad escape + if (escapeVal < 0) { break; } + } + if (escapeVal < 0) { + parser.addError("msg.invalid.escape"); + return Token.ERROR; + } + addToString(escapeVal); + isUnicodeEscapeStart = false; + } else { + c = getChar(); + if (c == '\\') { + c = getChar(); + if (c == 'u') { + isUnicodeEscapeStart = true; + containsEscape = true; + } else { + parser.addError("msg.illegal.character"); + return Token.ERROR; + } + } else { + if (c == EOF_CHAR + || !Character.isJavaIdentifierPart((char)c)) + { + break; + } + addToString(c); + } + } + } + ungetChar(c); + + String str = getStringFromBuffer(); + if (!containsEscape) { + // OPT we shouldn't have to make a string (object!) to + // check if it's a keyword. + + // Return the corresponding token if it's a keyword + int result = stringToKeyword(str); + if (result != Token.EOF) { + if (result != Token.RESERVED) { + return result; + } else if (!parser.compilerEnv. + isReservedKeywordAsIdentifier()) + { + return result; + } else { + // If implementation permits to use future reserved + // keywords in violation with the EcmaScript, + // treat it as name but issue warning + parser.addWarning("msg.reserved.keyword", str); + } + } + } + this.string = (String)allStrings.intern(str); + return Token.NAME; + } + + // is it a number? + if (isDigit(c) || (c == '.' && isDigit(peekChar()))) { + + stringBufferTop = 0; + int base = 10; + + if (c == '0') { + c = getChar(); + if (c == 'x' || c == 'X') { + base = 16; + c = getChar(); + } else if (isDigit(c)) { + base = 8; + } else { + addToString('0'); + } + } + + if (base == 16) { + while (0 <= Kit.xDigitToInt(c, 0)) { + addToString(c); + c = getChar(); + } + } else { + while ('0' <= c && c <= '9') { + /* + * We permit 08 and 09 as decimal numbers, which + * makes our behavior a superset of the ECMA + * numeric grammar. We might not always be so + * permissive, so we warn about it. + */ + if (base == 8 && c >= '8') { + parser.addWarning("msg.bad.octal.literal", + c == '8' ? "8" : "9"); + base = 10; + } + addToString(c); + c = getChar(); + } + } + + boolean isInteger = true; + + if (base == 10 && (c == '.' || c == 'e' || c == 'E')) { + isInteger = false; + if (c == '.') { + do { + addToString(c); + c = getChar(); + } while (isDigit(c)); + } + if (c == 'e' || c == 'E') { + addToString(c); + c = getChar(); + if (c == '+' || c == '-') { + addToString(c); + c = getChar(); + } + if (!isDigit(c)) { + parser.addError("msg.missing.exponent"); + return Token.ERROR; + } + do { + addToString(c); + c = getChar(); + } while (isDigit(c)); + } + } + ungetChar(c); + String numString = getStringFromBuffer(); + + double dval; + if (base == 10 && !isInteger) { + try { + // Use Java conversion to number from string... + dval = Double.valueOf(numString).doubleValue(); + } + catch (NumberFormatException ex) { + parser.addError("msg.caught.nfe"); + return Token.ERROR; + } + } else { + dval = ScriptRuntime.stringToNumber(numString, 0, base); + } + + this.number = dval; + return Token.NUMBER; + } + + // is it a string? + if (c == '"' || c == '\'') { + // We attempt to accumulate a string the fast way, by + // building it directly out of the reader. But if there + // are any escaped characters in the string, we revert to + // building it out of a StringBuffer. + + int quoteChar = c; + stringBufferTop = 0; + + c = getChar(); + while (c != quoteChar) { + if (c == '\n' || c == EOF_CHAR) { + ungetChar(c); + parser.addError("msg.unterminated.string.lit"); + return Token.ERROR; + } + + if (c == '\\') { + // We've hit an escaped character + + c = getChar(); + + switch (c) { + + case '\\': // backslash + case 'b': // backspace + case 'f': // form feed + case 'n': // line feed + case 'r': // carriage return + case 't': // horizontal tab + case 'v': // vertical tab + case 'd': // octal sequence + case 'u': // unicode sequence + case 'x': // hexadecimal sequence + // Only keep the '\' character for those + // characters that need to be escaped... + // Don't escape quoting characters... + addToString('\\'); + addToString(c); + break; + + case '\n': + // Remove line terminator after escape + break; + + default: + if (isDigit(c)) { + // Octal representation of a character. + // Preserve the escaping (see Y! bug #1637286) + addToString('\\'); + } + addToString(c); + break; + } + + } else { + + addToString(c); + } + + c = getChar(); + } + + String str = getStringFromBuffer(); + this.string = (String)allStrings.intern(str); + return Token.STRING; + } + + switch (c) { + case ';': return Token.SEMI; + case '[': return Token.LB; + case ']': return Token.RB; + case '{': return Token.LC; + case '}': return Token.RC; + case '(': return Token.LP; + case ')': return Token.RP; + case ',': return Token.COMMA; + case '?': return Token.HOOK; + case ':': + if (matchChar(':')) { + return Token.COLONCOLON; + } else { + return Token.COLON; + } + case '.': + if (matchChar('.')) { + return Token.DOTDOT; + } else if (matchChar('(')) { + return Token.DOTQUERY; + } else { + return Token.DOT; + } + + case '|': + if (matchChar('|')) { + return Token.OR; + } else if (matchChar('=')) { + return Token.ASSIGN_BITOR; + } else { + return Token.BITOR; + } + + case '^': + if (matchChar('=')) { + return Token.ASSIGN_BITXOR; + } else { + return Token.BITXOR; + } + + case '&': + if (matchChar('&')) { + return Token.AND; + } else if (matchChar('=')) { + return Token.ASSIGN_BITAND; + } else { + return Token.BITAND; + } + + case '=': + if (matchChar('=')) { + if (matchChar('=')) + return Token.SHEQ; + else + return Token.EQ; + } else { + return Token.ASSIGN; + } + + case '!': + if (matchChar('=')) { + if (matchChar('=')) + return Token.SHNE; + else + return Token.NE; + } else { + return Token.NOT; + } + + case '<': + /* NB:treat HTML begin-comment as comment-till-eol */ + if (matchChar('!')) { + if (matchChar('-')) { + if (matchChar('-')) { + skipLine(); + continue retry; + } + ungetChar('-'); + } + ungetChar('!'); + } + if (matchChar('<')) { + if (matchChar('=')) { + return Token.ASSIGN_LSH; + } else { + return Token.LSH; + } + } else { + if (matchChar('=')) { + return Token.LE; + } else { + return Token.LT; + } + } + + case '>': + if (matchChar('>')) { + if (matchChar('>')) { + if (matchChar('=')) { + return Token.ASSIGN_URSH; + } else { + return Token.URSH; + } + } else { + if (matchChar('=')) { + return Token.ASSIGN_RSH; + } else { + return Token.RSH; + } + } + } else { + if (matchChar('=')) { + return Token.GE; + } else { + return Token.GT; + } + } + + case '*': + if (matchChar('=')) { + return Token.ASSIGN_MUL; + } else { + return Token.MUL; + } + + case '/': + // is it a // comment? + if (matchChar('/')) { + skipLine(); + continue retry; + } + if (matchChar('*')) { + boolean lookForSlash = false; + StringBuffer sb = new StringBuffer(); + for (;;) { + c = getChar(); + if (c == EOF_CHAR) { + parser.addError("msg.unterminated.comment"); + return Token.ERROR; + } + sb.append((char) c); + if (c == '*') { + lookForSlash = true; + } else if (c == '/') { + if (lookForSlash) { + sb.delete(sb.length()-2, sb.length()); + String s = sb.toString(); + if (s.startsWith("!") || + s.startsWith("@cc_on") || + s.startsWith("@if") || + s.startsWith("@elif") || + s.startsWith("@else") || + s.startsWith("@end")) { + if (s.startsWith("!")) { + // Remove the leading '!' + this.string = s.substring(1); + } else { + this.string = s; + } + return Token.SPECIALCOMMENT; + } else { + continue retry; + } + } + } else { + lookForSlash = false; + } + } + } + + if (matchChar('=')) { + return Token.ASSIGN_DIV; + } else { + return Token.DIV; + } + + case '%': + if (matchChar('=')) { + return Token.ASSIGN_MOD; + } else { + return Token.MOD; + } + + case '~': + return Token.BITNOT; + + case '+': + if (matchChar('=')) { + return Token.ASSIGN_ADD; + } else if (matchChar('+')) { + return Token.INC; + } else { + return Token.ADD; + } + + case '-': + if (matchChar('=')) { + c = Token.ASSIGN_SUB; + } else if (matchChar('-')) { + if (!dirtyLine) { + // treat HTML end-comment after possible whitespace + // after line start as comment-utill-eol + if (matchChar('>')) { + skipLine(); + continue retry; + } + } + c = Token.DEC; + } else { + c = Token.SUB; + } + dirtyLine = true; + return c; + + default: + parser.addError("msg.illegal.character"); + return Token.ERROR; + } + } + } + + private static boolean isAlpha(int c) + { + // Use 'Z' < 'a' + if (c <= 'Z') { + return 'A' <= c; + } else { + return 'a' <= c && c <= 'z'; + } + } + + static boolean isDigit(int c) + { + return '0' <= c && c <= '9'; + } + + /* As defined in ECMA. jsscan.c uses C isspace() (which allows + * \v, I think.) note that code in getChar() implicitly accepts + * '\r' == \u000D as well. + */ + static boolean isJSSpace(int c) + { + if (c <= 127) { + return c == 0x20 || c == 0x9 || c == 0xC || c == 0xB; + } else { + return c == 0xA0 + || Character.getType((char)c) == Character.SPACE_SEPARATOR; + } + } + + private static boolean isJSFormatChar(int c) + { + return c > 127 && Character.getType((char)c) == Character.FORMAT; + } + + /** + * Parser calls the method when it gets / or /= in literal context. + */ + void readRegExp(int startToken) + throws IOException + { + stringBufferTop = 0; + if (startToken == Token.ASSIGN_DIV) { + // Miss-scanned /= + addToString('='); + } else { + if (startToken != Token.DIV) Kit.codeBug(); + } + + int c; + boolean inClass = false; + while ((c = getChar()) != '/' || inClass) { + if (c == '\n' || c == EOF_CHAR) { + ungetChar(c); + throw parser.reportError("msg.unterminated.re.lit"); + } + if (c == '\\') { + addToString(c); + c = getChar(); + } else if (c == '[') { + inClass = true; + } else if (c == ']') { + inClass = false; + } + + addToString(c); + } + int reEnd = stringBufferTop; + + while (true) { + if (matchChar('g')) + addToString('g'); + else if (matchChar('i')) + addToString('i'); + else if (matchChar('m')) + addToString('m'); + else + break; + } + + if (isAlpha(peekChar())) { + throw parser.reportError("msg.invalid.re.flag"); + } + + this.string = new String(stringBuffer, 0, reEnd); + this.regExpFlags = new String(stringBuffer, reEnd, + stringBufferTop - reEnd); + } + + boolean isXMLAttribute() + { + return xmlIsAttribute; + } + + int getFirstXMLToken() throws IOException + { + xmlOpenTagsCount = 0; + xmlIsAttribute = false; + xmlIsTagContent = false; + ungetChar('<'); + return getNextXMLToken(); + } + + int getNextXMLToken() throws IOException + { + stringBufferTop = 0; // remember the XML + + for (int c = getChar(); c != EOF_CHAR; c = getChar()) { + if (xmlIsTagContent) { + switch (c) { + case '>': + addToString(c); + xmlIsTagContent = false; + xmlIsAttribute = false; + break; + case '/': + addToString(c); + if (peekChar() == '>') { + c = getChar(); + addToString(c); + xmlIsTagContent = false; + xmlOpenTagsCount--; + } + break; + case '{': + ungetChar(c); + this.string = getStringFromBuffer(); + return Token.XML; + case '\'': + case '"': + addToString(c); + if (!readQuotedString(c)) return Token.ERROR; + break; + case '=': + addToString(c); + xmlIsAttribute = true; + break; + case ' ': + case '\t': + case '\r': + case '\n': + addToString(c); + break; + default: + addToString(c); + xmlIsAttribute = false; + break; + } + + if (!xmlIsTagContent && xmlOpenTagsCount == 0) { + this.string = getStringFromBuffer(); + return Token.XMLEND; + } + } else { + switch (c) { + case '<': + addToString(c); + c = peekChar(); + switch (c) { + case '!': + c = getChar(); // Skip ! + addToString(c); + c = peekChar(); + switch (c) { + case '-': + c = getChar(); // Skip - + addToString(c); + c = getChar(); + if (c == '-') { + addToString(c); + if(!readXmlComment()) return Token.ERROR; + } else { + // throw away the string in progress + stringBufferTop = 0; + this.string = null; + parser.addError("msg.XML.bad.form"); + return Token.ERROR; + } + break; + case '[': + c = getChar(); // Skip [ + addToString(c); + if (getChar() == 'C' && + getChar() == 'D' && + getChar() == 'A' && + getChar() == 'T' && + getChar() == 'A' && + getChar() == '[') + { + addToString('C'); + addToString('D'); + addToString('A'); + addToString('T'); + addToString('A'); + addToString('['); + if (!readCDATA()) return Token.ERROR; + + } else { + // throw away the string in progress + stringBufferTop = 0; + this.string = null; + parser.addError("msg.XML.bad.form"); + return Token.ERROR; + } + break; + default: + if(!readEntity()) return Token.ERROR; + break; + } + break; + case '?': + c = getChar(); // Skip ? + addToString(c); + if (!readPI()) return Token.ERROR; + break; + case '/': + // End tag + c = getChar(); // Skip / + addToString(c); + if (xmlOpenTagsCount == 0) { + // throw away the string in progress + stringBufferTop = 0; + this.string = null; + parser.addError("msg.XML.bad.form"); + return Token.ERROR; + } + xmlIsTagContent = true; + xmlOpenTagsCount--; + break; + default: + // Start tag + xmlIsTagContent = true; + xmlOpenTagsCount++; + break; + } + break; + case '{': + ungetChar(c); + this.string = getStringFromBuffer(); + return Token.XML; + default: + addToString(c); + break; + } + } + } + + stringBufferTop = 0; // throw away the string in progress + this.string = null; + parser.addError("msg.XML.bad.form"); + return Token.ERROR; + } + + /** + * + */ + private boolean readQuotedString(int quote) throws IOException + { + for (int c = getChar(); c != EOF_CHAR; c = getChar()) { + addToString(c); + if (c == quote) return true; + } + + stringBufferTop = 0; // throw away the string in progress + this.string = null; + parser.addError("msg.XML.bad.form"); + return false; + } + + /** + * + */ + private boolean readXmlComment() throws IOException + { + for (int c = getChar(); c != EOF_CHAR;) { + addToString(c); + if (c == '-' && peekChar() == '-') { + c = getChar(); + addToString(c); + if (peekChar() == '>') { + c = getChar(); // Skip > + addToString(c); + return true; + } else { + continue; + } + } + c = getChar(); + } + + stringBufferTop = 0; // throw away the string in progress + this.string = null; + parser.addError("msg.XML.bad.form"); + return false; + } + + /** + * + */ + private boolean readCDATA() throws IOException + { + for (int c = getChar(); c != EOF_CHAR;) { + addToString(c); + if (c == ']' && peekChar() == ']') { + c = getChar(); + addToString(c); + if (peekChar() == '>') { + c = getChar(); // Skip > + addToString(c); + return true; + } else { + continue; + } + } + c = getChar(); + } + + stringBufferTop = 0; // throw away the string in progress + this.string = null; + parser.addError("msg.XML.bad.form"); + return false; + } + + /** + * + */ + private boolean readEntity() throws IOException + { + int declTags = 1; + for (int c = getChar(); c != EOF_CHAR; c = getChar()) { + addToString(c); + switch (c) { + case '<': + declTags++; + break; + case '>': + declTags--; + if (declTags == 0) return true; + break; + } + } + + stringBufferTop = 0; // throw away the string in progress + this.string = null; + parser.addError("msg.XML.bad.form"); + return false; + } + + /** + * + */ + private boolean readPI() throws IOException + { + for (int c = getChar(); c != EOF_CHAR; c = getChar()) { + addToString(c); + if (c == '?' && peekChar() == '>') { + c = getChar(); // Skip > + addToString(c); + return true; + } + } + + stringBufferTop = 0; // throw away the string in progress + this.string = null; + parser.addError("msg.XML.bad.form"); + return false; + } + + private String getStringFromBuffer() + { + return new String(stringBuffer, 0, stringBufferTop); + } + + private void addToString(int c) + { + int N = stringBufferTop; + if (N == stringBuffer.length) { + char[] tmp = new char[stringBuffer.length * 2]; + System.arraycopy(stringBuffer, 0, tmp, 0, N); + stringBuffer = tmp; + } + stringBuffer[N] = (char)c; + stringBufferTop = N + 1; + } + + private void ungetChar(int c) + { + // can not unread past across line boundary + if (ungetCursor != 0 && ungetBuffer[ungetCursor - 1] == '\n') + Kit.codeBug(); + ungetBuffer[ungetCursor++] = c; + } + + private boolean matchChar(int test) throws IOException + { + int c = getChar(); + if (c == test) { + return true; + } else { + ungetChar(c); + return false; + } + } + + private int peekChar() throws IOException + { + int c = getChar(); + ungetChar(c); + return c; + } + + private int getChar() throws IOException + { + if (ungetCursor != 0) { + return ungetBuffer[--ungetCursor]; + } + + for(;;) { + int c; + if (sourceString != null) { + if (sourceCursor == sourceEnd) { + hitEOF = true; + return EOF_CHAR; + } + c = sourceString.charAt(sourceCursor++); + } else { + if (sourceCursor == sourceEnd) { + if (!fillSourceBuffer()) { + hitEOF = true; + return EOF_CHAR; + } + } + c = sourceBuffer[sourceCursor++]; + } + + if (lineEndChar >= 0) { + if (lineEndChar == '\r' && c == '\n') { + lineEndChar = '\n'; + continue; + } + lineEndChar = -1; + lineStart = sourceCursor - 1; + lineno++; + } + + if (c <= 127) { + if (c == '\n' || c == '\r') { + lineEndChar = c; + c = '\n'; + } + } else { + if (isJSFormatChar(c)) { + continue; + } + if (ScriptRuntime.isJSLineTerminator(c)) { + lineEndChar = c; + c = '\n'; + } + } + return c; + } + } + + private void skipLine() throws IOException + { + // skip to end of line + int c; + while ((c = getChar()) != EOF_CHAR && c != '\n') { } + ungetChar(c); + } + + final int getOffset() + { + int n = sourceCursor - lineStart; + if (lineEndChar >= 0) { --n; } + return n; + } + + final String getLine() + { + if (sourceString != null) { + // String case + int lineEnd = sourceCursor; + if (lineEndChar >= 0) { + --lineEnd; + } else { + for (; lineEnd != sourceEnd; ++lineEnd) { + int c = sourceString.charAt(lineEnd); + if (ScriptRuntime.isJSLineTerminator(c)) { + break; + } + } + } + return sourceString.substring(lineStart, lineEnd); + } else { + // Reader case + int lineLength = sourceCursor - lineStart; + if (lineEndChar >= 0) { + --lineLength; + } else { + // Read until the end of line + for (;; ++lineLength) { + int i = lineStart + lineLength; + if (i == sourceEnd) { + try { + if (!fillSourceBuffer()) { break; } + } catch (IOException ioe) { + // ignore it, we're already displaying an error... + break; + } + // i recalculuation as fillSourceBuffer can move saved + // line buffer and change lineStart + i = lineStart + lineLength; + } + int c = sourceBuffer[i]; + if (ScriptRuntime.isJSLineTerminator(c)) { + break; + } + } + } + return new String(sourceBuffer, lineStart, lineLength); + } + } + + private boolean fillSourceBuffer() throws IOException + { + if (sourceString != null) Kit.codeBug(); + if (sourceEnd == sourceBuffer.length) { + if (lineStart != 0) { + System.arraycopy(sourceBuffer, lineStart, sourceBuffer, 0, + sourceEnd - lineStart); + sourceEnd -= lineStart; + sourceCursor -= lineStart; + lineStart = 0; + } else { + char[] tmp = new char[sourceBuffer.length * 2]; + System.arraycopy(sourceBuffer, 0, tmp, 0, sourceEnd); + sourceBuffer = tmp; + } + } + int n = sourceReader.read(sourceBuffer, sourceEnd, + sourceBuffer.length - sourceEnd); + if (n < 0) { + return false; + } + sourceEnd += n; + return true; + } + + // stuff other than whitespace since start of line + private boolean dirtyLine; + + String regExpFlags; + + // Set this to an inital non-null value so that the Parser has + // something to retrieve even if an error has occured and no + // string is found. Fosters one class of error, but saves lots of + // code. + private String string = ""; + private double number; + + private char[] stringBuffer = new char[128]; + private int stringBufferTop; + private ObjToIntMap allStrings = new ObjToIntMap(50); + + // Room to backtrace from to < on failed match of the last - in