aboutsummaryrefslogtreecommitdiff
path: root/node_modules/selenium-webdriver
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2016-11-03 01:33:53 +0100
committerFlorian Dold <florian.dold@gmail.com>2016-11-03 01:33:53 +0100
commitd1291f67551c58168af43698a359cb5ddfd266b0 (patch)
tree55a13ed29fe1915e3f42f1b1b7038dafa2e975a7 /node_modules/selenium-webdriver
parentd0a0695fb5d34996850723f7d4b1b59c3df909c2 (diff)
downloadwallet-core-d1291f67551c58168af43698a359cb5ddfd266b0.tar.xz
node_modules
Diffstat (limited to 'node_modules/selenium-webdriver')
-rw-r--r--node_modules/selenium-webdriver/.npmignore2
-rw-r--r--node_modules/selenium-webdriver/CHANGES.md660
-rw-r--r--node_modules/selenium-webdriver/LICENSE202
-rw-r--r--node_modules/selenium-webdriver/NOTICE2
-rw-r--r--node_modules/selenium-webdriver/README.md239
-rw-r--r--node_modules/selenium-webdriver/chrome.js749
-rw-r--r--node_modules/selenium-webdriver/edge.js305
-rw-r--r--node_modules/selenium-webdriver/example/chrome_android.js38
-rw-r--r--node_modules/selenium-webdriver/example/chrome_mobile_emulation.js39
-rw-r--r--node_modules/selenium-webdriver/example/google_search.js50
-rw-r--r--node_modules/selenium-webdriver/example/google_search_generator.js45
-rw-r--r--node_modules/selenium-webdriver/example/google_search_test.js47
-rw-r--r--node_modules/selenium-webdriver/example/logging.js43
-rw-r--r--node_modules/selenium-webdriver/example/parallel_flows.js51
-rw-r--r--node_modules/selenium-webdriver/firefox/binary.js270
-rw-r--r--node_modules/selenium-webdriver/firefox/extension.js187
-rw-r--r--node_modules/selenium-webdriver/firefox/index.js679
-rw-r--r--node_modules/selenium-webdriver/firefox/profile.js409
-rw-r--r--node_modules/selenium-webdriver/http/index.js255
-rw-r--r--node_modules/selenium-webdriver/http/util.js139
-rw-r--r--node_modules/selenium-webdriver/ie.js445
-rw-r--r--node_modules/selenium-webdriver/index.js622
-rw-r--r--node_modules/selenium-webdriver/io/exec.js153
-rw-r--r--node_modules/selenium-webdriver/io/index.js300
-rw-r--r--node_modules/selenium-webdriver/lib/README6
-rw-r--r--node_modules/selenium-webdriver/lib/actions.js596
-rw-r--r--node_modules/selenium-webdriver/lib/by.js277
-rw-r--r--node_modules/selenium-webdriver/lib/capabilities.js450
-rw-r--r--node_modules/selenium-webdriver/lib/command.js243
-rw-r--r--node_modules/selenium-webdriver/lib/devmode.js34
-rw-r--r--node_modules/selenium-webdriver/lib/error.js555
-rw-r--r--node_modules/selenium-webdriver/lib/events.js210
-rw-r--r--node_modules/selenium-webdriver/lib/firefox/amd64/libnoblur64.sobin0 -> 41262 bytes
-rw-r--r--node_modules/selenium-webdriver/lib/firefox/i386/libnoblur.sobin0 -> 30887 bytes
-rw-r--r--node_modules/selenium-webdriver/lib/firefox/webdriver.json69
-rw-r--r--node_modules/selenium-webdriver/lib/firefox/webdriver.xpibin0 -> 706947 bytes
-rw-r--r--node_modules/selenium-webdriver/lib/http.js456
-rw-r--r--node_modules/selenium-webdriver/lib/input.js171
-rw-r--r--node_modules/selenium-webdriver/lib/logging.js670
-rw-r--r--node_modules/selenium-webdriver/lib/promise.js3039
-rw-r--r--node_modules/selenium-webdriver/lib/proxy.js127
-rw-r--r--node_modules/selenium-webdriver/lib/safari/client.js56
-rw-r--r--node_modules/selenium-webdriver/lib/session.js80
-rw-r--r--node_modules/selenium-webdriver/lib/symbols.js38
-rw-r--r--node_modules/selenium-webdriver/lib/test/build.js153
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/ClickTest_testClicksASurroundingStrongTag.html11
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/Page.aspx17
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/Page.aspx.cs22
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/Redirect.aspx11
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/Redirect.aspx.cs9
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/Settings.StyleCop759
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/Web.Config59
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/actualXhtmlPage.xhtml14
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/ajaxy_page.html81
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/alerts.html85
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/banner.gifbin0 -> 2109 bytes
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/beach.jpgbin0 -> 14085 bytes
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/blank.html1
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/bodyTypingTest.html41
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/booleanAttributes.html19
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/child/childPage.html8
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/child/grandchild/grandchildPage.html8
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/clickEventPage.html26
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/click_frames.html10
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/click_jacker.html38
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/click_out_of_bounds.html23
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/click_out_of_bounds_overflow.html85
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/click_rtl.html19
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/click_source.html18
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/click_tests/click_iframe.html6
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/click_tests/click_in_iframe.html8
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/click_tests/disappearing_element.html62
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/click_tests/google_map.html15
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/click_tests/google_map.pngbin0 -> 26209 bytes
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/click_tests/html5_submit_buttons.html16
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/click_tests/issue5237.html9
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/click_tests/issue5237_frame.html1
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/click_tests/issue5237_target.html10
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/click_tests/link_that_wraps.html11
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/click_tests/mapped_page1.html9
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/click_tests/mapped_page2.html9
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/click_tests/mapped_page3.html9
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/click_tests/overlapping_elements.html70
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/click_tests/partially_overlapping_elements.html124
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/click_tests/span_that_wraps.html11
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/click_tests/submitted_page.html9
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/click_tests/wrapped_overlapping_elements.html13
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/click_too_big.html10
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/click_too_big_in_frame.html11
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/clicks.html35
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/closeable_window.html8
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/cn-test.html156
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/colorPage.html20
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/cookies.html30
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/coordinates_tests/element_in_frame.html9
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/coordinates_tests/element_in_nested_frame.html9
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/coordinates_tests/page_with_element_out_of_view.html11
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/coordinates_tests/page_with_empty_element.html10
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/coordinates_tests/page_with_fixed_element.html12
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/coordinates_tests/page_with_hidden_element.html10
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/coordinates_tests/page_with_invisible_element.html10
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/coordinates_tests/page_with_transparent_element.html10
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/coordinates_tests/simple_page.html10
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.pngbin0 -> 260 bytes
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.pngbin0 -> 251 bytes
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-bg_flat_10_000000_40x100.pngbin0 -> 178 bytes
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.pngbin0 -> 104 bytes
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.pngbin0 -> 125 bytes
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.pngbin0 -> 105 bytes
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-bg_gloss-wave_35_f6a828_500x100.pngbin0 -> 3762 bytes
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.pngbin0 -> 90 bytes
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.pngbin0 -> 129 bytes
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-icons_222222_256x240.pngbin0 -> 4369 bytes
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-icons_228ef1_256x240.pngbin0 -> 4369 bytes
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-icons_ef8c08_256x240.pngbin0 -> 4369 bytes
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-icons_ffd27a_256x240.pngbin0 -> 4369 bytes
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-icons_ffffff_256x240.pngbin0 -> 4369 bytes
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/jquery-ui-1.8.10.custom.css573
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/cssTransform.html61
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/cssTransform2.html20
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/document_write_in_onload.html13
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/dragAndDropInsideScrolledDiv.html67
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/dragAndDropTest.html102
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/dragDropOverflow.html104
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/draggableLists.html67
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/droppableItems.html65
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/dynamic.html39
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/dynamicallyModifiedPage.html42
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/errors.html15
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/firefox/jetpack-sample.xpibin0 -> 7289 bytes
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/firefox/sample.xpibin0 -> 1551 bytes
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/fixedFooterNoScroll.html13
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/fixedFooterNoScrollQuirksMode.html12
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/formPage.html174
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/formSelectionPage.html46
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/form_handling_js_submit.html30
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/framePage3.html7
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/frameScrollChild.html26
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/frameScrollPage.html14
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/frameScrollParent.html11
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/frameWithAnimals.html11
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/frame_switching_tests/bug4876.html9
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/frame_switching_tests/bug4876_iframe.html9
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/frame_switching_tests/deletingFrame.html29
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/frame_switching_tests/deletingFrame_iframe.html8
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/frame_switching_tests/deletingFrame_iframe2.html7
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/frameset.html14
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/framesetPage2.html7
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/framesetPage3.html4
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/globalscope.html15
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/hidden.html5
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/hidden_partially.html45
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/html5/blue.jpgbin0 -> 92 bytes
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/html5/database.js84
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/html5/geolocation.js18
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/html5/green.jpgbin0 -> 92 bytes
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/html5/red.jpgbin0 -> 92 bytes
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/html5/status.html1
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/html5/test.appcache11
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/html5/yellow.jpgbin0 -> 92 bytes
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/html5Page.html32
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/icon.gifbin0 -> 127 bytes
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/idElements.html2
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/iframeAtBottom.html15
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/iframeWithAlert.html1
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/iframeWithIframe.html1
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/iframes.html11
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/injectableContent.html22
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/javascriptEnhancedForm.html30
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/javascriptPage.html285
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/jquery-1.3.2.js4376
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/js/jquery-1.4.4.min.js167
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/js/jquery-ui-1.8.10.custom.min.js782
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/content.inline.min.css1
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/content.min.css1
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/readme.md1
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/tinymce-small.dev.svg175
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/tinymce-small.eotbin0 -> 10316 bytes
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/tinymce-small.svg62
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/tinymce-small.ttfbin0 -> 10128 bytes
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/tinymce-small.woffbin0 -> 7848 bytes
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/tinymce.dev.svg153
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/tinymce.eotbin0 -> 10024 bytes
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/tinymce.svg63
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/tinymce.ttfbin0 -> 9860 bytes
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/tinymce.woffbin0 -> 7664 bytes
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/img/anchor.gifbin0 -> 53 bytes
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/img/loader.gifbin0 -> 2608 bytes
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/img/object.gifbin0 -> 152 bytes
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/img/trans.gifbin0 -> 43 bytes
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/skin.ie7.min.css1
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/skin.min.css1
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/js/themes/modern/theme.min.js1
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/js/tinymce.min.js10
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/key_tests/remove_on_keypress.html36
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/keyboard_shortcut.html36
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/linked_image.html16
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/locators_tests/boolean_attribute_selected.html13
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/locators_tests/boolean_attribute_selected_html4.html13
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/longContentPage.html55
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/macbeth.html5255
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/map.pngbin0 -> 26209 bytes
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/map_visibility.html8
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/markerTransparent.pngbin0 -> 260 bytes
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/messages.html15
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/meta-redirect.html11
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/missedJsReference.html11
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/modal_dialogs/modal_1.html21
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/modal_dialogs/modal_2.html21
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/modal_dialogs/modal_3.html15
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/modal_dialogs/modalindex.html21
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/mouseOver.html17
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/mousePositionTracker.html33
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/nestedElements.html155
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/overflow-body.html15
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/overflow/x_auto_y_auto.html30
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/overflow/x_auto_y_hidden.html30
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/overflow/x_auto_y_scroll.html30
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/overflow/x_hidden_y_auto.html30
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/overflow/x_hidden_y_hidden.html30
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/overflow/x_hidden_y_scroll.html30
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/overflow/x_scroll_y_auto.html30
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/overflow/x_scroll_y_hidden.html30
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/overflow/x_scroll_y_scroll.html30
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/pageWithOnBeforeUnloadMessage.html20
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/pageWithOnLoad.html6
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/pageWithOnUnload.html6
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/page_with_link_to_slow_loading_page.html6
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/plain.txt1
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/proxy/page1.html20
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/proxy/page2.html24
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/proxy/page3.html5
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/readOnlyPage.html24
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/rectangles.html40
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/resultPage.html25
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/rich_text.html161
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/safari/frames_benchmark.html31
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/screen/screen.css19
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/screen/screen.html72
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/screen/screen.js7
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/screen/screen_frame1.html72
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/screen/screen_frame2.html72
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/screen/screen_frames.html11
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/screen/screen_iframes.html12
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/screen/screen_too_long.html68
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/screen/screen_x_long.html72
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/screen/screen_x_too_long.html72
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/screen/screen_y_long.html72
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/screen/screen_y_too_long.html72
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/scroll.html27
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/scroll2.html21
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/scroll3.html8
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/scroll4.html7
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/scroll5.html18
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/scrolling_tests/frame_with_height_above_200.html26
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/scrolling_tests/frame_with_height_above_2000.html26
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/scrolling_tests/frame_with_nested_scrolling_frame.html11
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/scrolling_tests/frame_with_nested_scrolling_frame_out_of_view.html12
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/scrolling_tests/frame_with_small_height.html10
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/scrolling_tests/page_with_double_overflow_auto.html19
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/scrolling_tests/page_with_frame_out_of_view.html12
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/scrolling_tests/page_with_nested_scrolling_frames.html11
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/scrolling_tests/page_with_nested_scrolling_frames_out_of_view.html12
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/scrolling_tests/page_with_non_scrolling_frame.html11
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/scrolling_tests/page_with_scrolling_frame.html11
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/scrolling_tests/page_with_scrolling_frame_out_of_view.html12
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/scrolling_tests/page_with_tall_frame.html11
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/scrolling_tests/page_with_y_overflow_auto.html14
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/scrolling_tests/target_page.html9
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/selectPage.html58
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/selectableItems.html65
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/sessionCookie.html21
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/sessionCookieDest.html34
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/simple.xml5
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/simpleTest.html98
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/slowLoadingAlert.html10
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/slowLoadingResourcePage.html12
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/slow_loading_iframes.html14
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/styledPage.html28
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/svgPiechart.xhtml81
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/svgTest.svg4
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/tables.html36
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/tinymce.html10
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/transformable.xml11
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/transformable.xsl37
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/transparentUpload.html70
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/underscore.html9
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/unicode_ltr.html8
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/upload.html45
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/userDefinedProperty.html8
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/veryLargeCanvas.html81
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/visibility-css.html21
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/win32frameset.html8
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/window_switching_tests/page_with_frame.html12
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/window_switching_tests/simple_page.html9
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/xhtmlFormPage.xhtml17
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/xhtmlTest.html76
-rw-r--r--node_modules/selenium-webdriver/lib/test/fileserver.js319
-rw-r--r--node_modules/selenium-webdriver/lib/test/httpserver.js122
-rw-r--r--node_modules/selenium-webdriver/lib/test/index.js301
-rw-r--r--node_modules/selenium-webdriver/lib/test/resources.js44
-rw-r--r--node_modules/selenium-webdriver/lib/until.js427
-rw-r--r--node_modules/selenium-webdriver/lib/webdriver.js2459
-rw-r--r--node_modules/selenium-webdriver/net/index.js117
-rw-r--r--node_modules/selenium-webdriver/net/portprober.js205
l---------node_modules/selenium-webdriver/node_modules/.bin/rimraf1
-rw-r--r--node_modules/selenium-webdriver/opera.js402
-rw-r--r--node_modules/selenium-webdriver/package.json43
-rw-r--r--node_modules/selenium-webdriver/phantomjs.js289
-rw-r--r--node_modules/selenium-webdriver/proxy.js32
-rw-r--r--node_modules/selenium-webdriver/remote/index.js593
-rw-r--r--node_modules/selenium-webdriver/safari.js700
-rw-r--r--node_modules/selenium-webdriver/test/actions_test.js54
-rw-r--r--node_modules/selenium-webdriver/test/chrome/options_test.js227
-rw-r--r--node_modules/selenium-webdriver/test/chrome/service_test.js45
-rw-r--r--node_modules/selenium-webdriver/test/cookie_test.js214
-rw-r--r--node_modules/selenium-webdriver/test/element_finding_test.js410
-rw-r--r--node_modules/selenium-webdriver/test/execute_script_test.js352
-rw-r--r--node_modules/selenium-webdriver/test/fingerprint_test.js57
-rw-r--r--node_modules/selenium-webdriver/test/firefox/extension_test.js96
-rw-r--r--node_modules/selenium-webdriver/test/firefox/firefox_test.js188
-rw-r--r--node_modules/selenium-webdriver/test/firefox/profile_test.js186
-rw-r--r--node_modules/selenium-webdriver/test/http/http_test.js205
-rw-r--r--node_modules/selenium-webdriver/test/http/util_test.js184
-rw-r--r--node_modules/selenium-webdriver/test/io_test.js321
-rw-r--r--node_modules/selenium-webdriver/test/lib/by_test.js139
-rw-r--r--node_modules/selenium-webdriver/test/lib/capabilities_test.js111
-rw-r--r--node_modules/selenium-webdriver/test/lib/error_test.js286
-rw-r--r--node_modules/selenium-webdriver/test/lib/events_test.js177
-rw-r--r--node_modules/selenium-webdriver/test/lib/http_test.js655
-rw-r--r--node_modules/selenium-webdriver/test/lib/logging_test.js272
-rw-r--r--node_modules/selenium-webdriver/test/lib/promise_aplus_test.js74
-rw-r--r--node_modules/selenium-webdriver/test/lib/promise_error_test.js880
-rw-r--r--node_modules/selenium-webdriver/test/lib/promise_flow_test.js2284
-rw-r--r--node_modules/selenium-webdriver/test/lib/promise_generator_test.js308
-rw-r--r--node_modules/selenium-webdriver/test/lib/promise_test.js1067
-rw-r--r--node_modules/selenium-webdriver/test/lib/testutil.js90
-rw-r--r--node_modules/selenium-webdriver/test/lib/until_test.js463
-rw-r--r--node_modules/selenium-webdriver/test/lib/webdriver_test.js2177
-rw-r--r--node_modules/selenium-webdriver/test/logging_test.js169
-rw-r--r--node_modules/selenium-webdriver/test/net/index_test.js60
-rw-r--r--node_modules/selenium-webdriver/test/net/portprober_test.js129
-rw-r--r--node_modules/selenium-webdriver/test/page_loading_test.js166
-rw-r--r--node_modules/selenium-webdriver/test/phantomjs/execute_phantomjs_test.js73
-rw-r--r--node_modules/selenium-webdriver/test/proxy_test.js180
-rw-r--r--node_modules/selenium-webdriver/test/remote_test.js117
-rw-r--r--node_modules/selenium-webdriver/test/safari_test.js112
-rw-r--r--node_modules/selenium-webdriver/test/session_test.js53
-rw-r--r--node_modules/selenium-webdriver/test/stale_element_test.js61
-rw-r--r--node_modules/selenium-webdriver/test/tag_name_test.js34
-rw-r--r--node_modules/selenium-webdriver/test/testing/assert_test.js374
-rw-r--r--node_modules/selenium-webdriver/test/testing/index_test.js178
-rw-r--r--node_modules/selenium-webdriver/test/upload_test.js86
-rw-r--r--node_modules/selenium-webdriver/test/window_test.js175
-rw-r--r--node_modules/selenium-webdriver/testing/assert.js378
-rw-r--r--node_modules/selenium-webdriver/testing/index.js277
356 files changed, 51650 insertions, 0 deletions
diff --git a/node_modules/selenium-webdriver/.npmignore b/node_modules/selenium-webdriver/.npmignore
new file mode 100644
index 000000000..d5700888a
--- /dev/null
+++ b/node_modules/selenium-webdriver/.npmignore
@@ -0,0 +1,2 @@
+node_modules/
+
diff --git a/node_modules/selenium-webdriver/CHANGES.md b/node_modules/selenium-webdriver/CHANGES.md
new file mode 100644
index 000000000..614f905b4
--- /dev/null
+++ b/node_modules/selenium-webdriver/CHANGES.md
@@ -0,0 +1,660 @@
+## v3.0.0-beta-3
+
+* Fixed a bug where the promise manager would silently drop callbacks after
+ recovering from an unhandled promise rejection.
+* Added the `firefox.ServiceBuilder` class, which may be used to customize the
+ geckodriver used for `firefox.Driver` instances.
+* Added support for Safari 10 safaridriver. safaridriver may be disabled
+ via tha API, `safari.Options#useLegacyDriver`, to use the safari
+ extension driver.
+* Updated the `lib/proxy` module to support configuring a SOCKS proxy.
+* For the `promise.ControlFlow`, fire the "uncaughtException" event in a new
+ turn of the JS event loop. As a result of this change, any errors thrown by
+ an event listener will propagate to the global error handler. Previously,
+ this event was fired with in the context of a (native) promise callback,
+ causing errors to be silently suppressed in the promise chain.
+
+### API Changes
+
+* Added `remote.DriverService.Builder` as a base class for configuring
+ DriverService instances that run in a child-process. The
+ `chrome.ServiceBuilder`, `edge.ServiceBuilder`, and `opera.ServiceBuilder`
+ classes now all extend this base class with browser-specific options.
+* For each of the ServiceBuilder clases, renamed `usingPort` and
+ `withEnvironment` to `setPort` and `setEnvironment`, respectively.
+* Renamed `chrome.ServiceBuilder#setUrlBasePath` to `#setPath`
+* Changed the signature of the `firefox.Driver` from `(config, flow, executor)`
+ to `(config, executor, flow)`.
+* Exposed the `Condition` and `WebElementCondition` classes from the top-level
+ `selenium-webdriver` module (these were previously only available from
+ `lib/webdriver`).
+
+
+### Changes for W3C WebDriver Spec Compliance
+
+* Updated command mappings for [getting](https://w3c.github.io/webdriver/webdriver-spec.html#get-window-position)
+ and [setting](https://w3c.github.io/webdriver/webdriver-spec.html#set-window-position)
+ the window position.
+
+
+## v3.0.0-beta-2
+
+### API Changes
+
+* Moved the `builder.Builder` class into the main module (`selenium-webdriver`).
+* Removed the `builder` module.
+* Fix `webdriver.WebDriver#setFileDetector` when driving Chrome or Firefox on a
+ remote machine.
+
+
+## v3.0.0-beta-1
+
+* Allow users to set the agent used for HTTP connections through
+ `builder.Builder#usingHttpAgent()`
+* Added new wait conditions: `until.urlIs()`, `until.urlContains()`,
+ `until.urlMatches()`
+* Added work around for [GeckoDriver bug](https://bugzilla.mozilla.org/show_bug.cgi?id=1274924)
+ raising a type conversion error
+* Internal cleanup replacing uses of managed promises with native promises
+* Removed the mandatory use of Firefox Dev Edition, when using Marionette driver
+* Fixed timeouts' URL
+* Properly send HTTP requests when using a WebDriver server proxy
+* Properly configure proxies when using the geckodriver
+* `http.Executor` now accepts a promised client. The `builder.Builder` class
+ will now use this instead of a `command.DeferredExecutor` when creating
+ WebDriver instances.
+* For Chrome and Firefox, the `builder.Builder` class will always return an
+ instanceof `chrome.Driver` and `firefox.Driver`, respectively, even when
+ configured to use a remote server (from `builder.Builder#usingServer(url)`,
+ `SELENIUM_REMOTE_URL`, etc).
+
+### API Changes
+
+* `promise.Deferred` is no longer a thenable object.
+* `Options#addCookie()` now takes a record object instead of 7 individual
+ parameters. A TypeError will be thrown if addCookie() is called with invalid
+ arguments.
+* When adding cookies, the desired expiry must be provided as a Date or in
+ _seconds_ since epoch. When retrieving cookies, the expiration is always
+ returned in seconds.
+* Renamed `firefox.Options#useMarionette` to `firefox.Options#useGeckoDriver`
+* Removed deprecated modules:
+ - `selenium-webdriver/error` (use `selenium-webdriver/lib/error`,\
+ or the `error` property exported by `selenium-webdriver`)
+ - `selenium-webdriver/executors` — this was not previously deprecated, but
+ is no longer used.
+* Removed deprecated types:
+ - `command.DeferredExecutor` — this was not previously deprecated, but is no
+ longer used. It can be trivially implemented by clients should it be
+ needed.
+ - `error.InvalidSessionIdError` (use `error.NoSuchSessionError`)
+ - `executors.DeferredExecutor`
+ - `until.Condition` (use `webdriver.Condition`)
+ - `until.WebElementCondition` (use `webdriver.WebElementCondition`)
+ - `webdriver.UnhandledAlertError` (use `error.UnexpectedAlertOpenError`)
+* Removed deprecated functions:
+ - `Deferred#cancel()`
+ - `Deferred#catch()`
+ - `Deferred#finally()`
+ - `Deferred#isPending()`
+ - `Deferred#then()`
+ - `Promise#thenCatch()`
+ - `Promise#thenFinally()`
+ - `WebDriver#isElementPresent()`
+ - `WebElement#getInnerHtml()`
+ - `WebElement#getOuterHtml()`
+ - `WebElement#getRawId()`
+ - `WebElement#isElementPresent()`
+* Removed deprecated properties:
+ - `WebDriverError#code`
+
+
+## v2.53.2
+
+* Changed `io.exists()` to return a rejected promise if the input path is not
+ a string
+* Deprecated `Promise#thenFinally()` - use `Promise#finally()`. The thenFinally
+ shim added to the promise module in v2.53.0 will be removed in v3.0
+ Sorry for the churn!
+* FIXED: capabilities serialization now properly handles undefined vs.
+ false-like values.
+* FIXED: properly handle responses from the remote end in
+ `WebDriver.attachToSession`
+
+## v2.53.1
+
+* FIXED: for consistency with the other language bindings, `remote.FileDetector`
+ will ignore paths that refer to a directory.
+
+## v2.53.0
+
+### Change Summary
+
+* Added preliminary support for Marionette, Mozilla's WebDriver implementation
+ for Firefox. Marionette may be enabled via the API,
+ `firefox.Options#useMarionette`, or by setting the `SELENIUM_MARIONETTE`
+ environment variable.
+* Moved all logic for parsing and interpreting responses from the remote end
+ into the individual `command.Executor` implementations.
+* For consistency with the other Selenium language bindings,
+ `WebDriver#isElementPresent()` and `WebElement#isElementPresent()` have
+ been deprecated. These methods will be removed in v3.0. Use the findElements
+ command to test for the presence of an element:
+
+ driver.findElements(By.css('.foo')).then(found => !!found.length);
+* Added support for W3C-spec compliant servers.
+* For consistent naming, deprecating `error.InvalidSessionIdError` in favor of
+ `error.NoSuchSessionError`.
+* Moved the `error` module to `lib/error` so all core modules are co-located.
+ The top-level `error` module will be removed in v3.0.
+* Moved `until.Condition` and `until.WebElementCondition` to the webdriver
+ module to break a circular dependency.
+* Added support for setting the username and password in basic auth pop-up
+ dialogs (currently IE only).
+* Deprecated `WebElement#getInnerHtml()` and `WebEleemnt#getOuterHtml()`
+* Deprecated `Promise#thenCatch()` - use `Promise#catch()` instead
+* Deprecated `Promise#thenFinally()` - use `promise.thenFinally()` instead
+* FIXED: `io.findInPath()` will no longer match against directories that have
+ the same basename as the target file.
+* FIXED: `phantomjs.Driver` now takes a third argument that defines the path to
+ a log file to use for the phantomjs executable's output. This may be quickly
+ set at runtime with the `SELENIUM_PHANTOMJS_LOG` environment variable.
+
+### Changes for W3C WebDriver Spec Compliance
+
+* Changed `element.sendKeys(...)` to send the key sequence as an array where
+ each element defines a single key. The legacy wire protocol permits arrays
+ where each element is a string of arbitrary length. This change is solely
+ at the protocol level and should have no user-visible effect.
+
+
+## v2.52.0
+
+### Notice
+
+Starting with v2.52.0, each release of selenium-webdriver will support the
+latest _minor_ LTS and stable Node releases. All releases between the LTS and
+stable release will have best effort support. Further details are available in
+the selenium-webdriver package README.
+
+### Change Summary
+
+* Add support for Microsoft's Edge web browser
+* Added `webdriver.Builder#buildAsync()`, which returns a promise that will be
+ fulfilled with the newly created WebDriver instance once the associated
+ browser has been full initialized. This is purely a convenient alternative
+ to the existing build() method as the WebDriver class will always defer
+ commands until it has a fully created browser.
+* Added `firefox.Profile#setHost()` which may be used to set the host that
+ the FirefoxDriver's server listens for commands on. The server uses
+ "localhost" by default.
+* Added `promise.Promise#catch()` for API compatibility with native Promises.
+ `promise.Promise#thenCatch()` is not yet deprecated, but it simply
+ delegates to `catch`.
+* Changed some `io` operations to use native promises.
+* Changed `command.Executor#execute()` and `HttpClient#send()` to return
+ promises instead of using callback passing.
+* Replaced the `Serializable` class with an internal, Symbol-defined method.
+* Changed the `Capabilities` class to extend the native `Map` type.
+* Changed the `Capabilities.has(key)` to only test if a capability has been set
+ (Map semantics). To check whether the value is true, use `get(key)`.
+* Deprecated `executors.DeferredExecutor` in favor of
+ `lib/command.DeferredExecutor`.
+* API documentation is no longer distributed with the npm package, but remains
+ available at <http://seleniumhq.github.io/selenium/docs/api/javascript/>
+* Rewrote the `error` module to export an Error subtype for each type of error
+ defined in the [W3C WebDriver spec](https://w3c.github.io/webdriver/webdriver-spec.html#handling-errors).
+* Changed the `http.Request` and `http.Response` classes to store headers in
+ maps instead of object literals.
+* Updated `ws` dependency to version `1.0.1`.
+* Removed fluent predicates "is" and "not" from the experimental
+ `testing/assert` module.
+* Wait conditions that locate an element, or that wait on an element's state,
+ will return a WebElementPromise.
+* Lots of internal clean-up to break selenium-webdriver's long standing
+ dependency on Google's Closure library.
+
+### Changes for W3C WebDriver Spec Compliance
+
+* Updated the `By` locators that are not in the W3C spec to delegated to using
+ CSS selectors: `By.className`, `By.id`, `By.name`, and `By.tagName`.
+
+
+## v2.49-51
+
+* _Releases skipped to stay in sync with the rest of the Selenium project_
+
+
+## v2.48.2
+
+* Added `WebElement#takeScreenshot()`.
+* More adjustments to promise callback tracking.
+
+## v2.48.1
+
+* FIXED: Adjusted how the control flow tracks promise callbacks to avoid a
+ potential deadlock.
+
+## v2.48.0
+
+* Node v0.12.x users must run with --harmony. _This is the last release that
+ will support v0.12.x_
+* FIXED: (Promise/A+ compliance) When a promise is rejected with a thenable,
+ the promise adopts the thenable as its rejection reason instead of waiting
+ for it to settle. The previous (incorrect) behavior was hidden by bugs in
+ the `promises-aplus-tests` compliance test suite that were fixed in version
+ `2.1.1`.
+* FIXED: the `webdriver.promise.ControlFlow` now has a consistent execution
+ order for tasks/callbacks scheduled in different turns of the JS event loop.
+ Refer to the `webdriver.promise` documentation for more details.
+* FIXED: do not drop user auth from the WebDriver server URL.
+* FIXED: a single `firefox.Binary` instance may be used to configure and
+ launch multiple FirefoxDriver sessions.
+
+ var binary = new firefox.Binary();
+ var options = new firefox.Options().setBinary(binary);
+ var builder = new Builder().setFirefoxOptions(options);
+
+ var driver1 = builder.build();
+ var driver2 = builder.build();
+
+* FIXED: zip files created for transfer to a remote WebDriver server are no
+ longer compressed. If the zip contained a file that was already compressed,
+ the server would return an "invalid code lengths set" error.
+* FIXED: Surfaced the `loopback` option to `remote/SeleniumServer`. When set,
+ the server will be accessed using the current host's loopback address.
+
+## v2.47.0
+
+### Notice
+
+This is the last release for `selenium-webdriver` that will support ES5.
+Subsequent releases will depend on ES6 features that are enabled by
+[default](https://nodejs.org/en/docs/es6/) in Node v4.0.0. Node v0.12.x will
+continue to be supported, but will require setting the `--harmony` flag.
+
+### Change Summary
+
+* Add support for [Node v4.0.0](https://nodejs.org/en/blog/release/v4.0.0/)
+ * Updated `ws` dependency from `0.7.1` to `0.8.0`
+* Bumped the minimum supported version of Node from `0.10.x` to `0.12.x`. This
+ is in accordance with the Node support policy established in `v2.45.0`.
+
+## v2.46.1
+
+* Fixed internal module loading on Windows.
+* Fixed error message format on timeouts for `until.elementLocated()`
+ and `until.elementsLocated()`.
+
+## v2.46.0
+
+* Exposed a new logging API via the `webdriver.logging` module. For usage, see
+ `example/logging.js`.
+* Added support for using a proxy server for WebDriver commands.
+ See `Builder#usingWebDriverProxy()` for more info.
+* Removed deprecated functions:
+ * Capabilities#toJSON()
+ * UnhandledAlertError#getAlert()
+ * chrome.createDriver()
+ * phantomjs.createDriver()
+ * promise.ControlFlow#annotateError()
+ * promise.ControlFlow#await()
+ * promise.ControlFlow#clearHistory()
+ * promise.ControlFlow#getHistory()
+* Removed deprecated enum values: `ErrorCode.NO_MODAL_DIALOG_OPEN` and
+ `ErrorCode.MODAL_DIALOG_OPENED`. Use `ErrorCode.NO_SUCH_ALERT` and
+ `ErrorCode.UNEXPECTED_ALERT_OPEN`, respectively.
+* FIXED: The `promise.ControlFlow` will maintain state for promise chains
+ generated in a loop.
+* FIXED: Correct serialize target elements used in an action sequence.
+* FIXED: `promise.ControlFlow#wait()` now has consistent semantics for an
+ omitted or 0-timeout: it will wait indefinitely.
+* FIXED: `remote.DriverService#start()` will now fail if the child process dies
+ while waiting for the server to start accepting requests. Previously, start
+ would continue to poll the server address until the timeout expired.
+* FIXED: Skip launching Firefox with the `-silent` flag to preheat the profile.
+ Starting with Firefox 38, this would cause the browser to crash. This step,
+ which was first introduced for Selenium's java client back with Firefox 2,
+ no longer appears to be required.
+* FIXED: 8564: `firefox.Driver#quit()` will wait for the Firefox process to
+ terminate before deleting the temporary webdriver profile. This eliminates a
+ race condition where Firefox would write profile data during shutdown,
+ causing the `rm -rf` operation on the profile directory to fail.
+
+## v2.45.1
+
+* FIXED: 8548: Task callbacks are once again dropped if the task was cancelled
+ due to a previously uncaught error within the frame.
+* FIXED: 8496: Extended the `chrome.Options` API to cover all configuration
+ options (e.g. mobile emulation and performance logging) documented on the
+ ChromeDriver [project site](https://sites.google.com/a/chromium.org/chromedriver/capabilities).
+
+## v2.45.0
+
+### Important Policy Change
+
+Starting with the 2.45.0 release, selenium-webdriver will support the last
+two stable minor releases for Node. For 2.45.0, this means Selenium will
+support Node 0.10.x and 0.12.x. Support for the intermediate, un-stable release
+(0.11.x) is "best-effort". This policy will be re-evaluated once Node has a
+major version release (i.e. 1.0.0).
+
+### Change Summary
+
+* Added native browser support for Internet Explorer, Opera 26+, and Safari
+* With the release of [Node 0.12.0](http://blog.nodejs.org/2015/02/06/node-v0-12-0-stable/)
+ (finally!), the minimum supported version of Node is now `0.10.x`.
+* The `promise` module is now [Promises/A+](https://promisesaplus.com/)
+ compliant. The biggest compliance change is that promise callbacks are now
+ invoked in a future turn of the JS event loop. For example:
+
+ var promise = require('selenium-webdriver').promise;
+ console.log('start');
+ promise.fulfilled().then(function() {
+ console.log('middle');
+ });
+ console.log('end');
+
+ // Output in selenium-webdriver@2.44.0
+ // start
+ // middle
+ // end
+ //
+ // Output in selenium-webdriver@2.45.0
+ // start
+ // end
+ // middle
+
+ The `promise.ControlFlow` class has been updated to track the asynchronous
+ breaks required by Promises/A+, so there are no changes to task execution
+ order.
+* Updated how errors are annotated on failures. When a task fails, the
+ stacktrace from when that task was scheduled is appended to the rejection
+ reason with a `From: ` prefix (if it is an Error object). For example:
+
+ var driver = new webdriver.Builder().forBrowser('chrome').build();
+ driver.get('http://www.google.com/ncr');
+ driver.call(function() {
+ driver.wait(function() {
+ return driver.isElementPresent(webdriver.By.id('not-there'));
+ }, 2000, 'element not found');
+ });
+
+ This code will fail an error like:
+
+ Error: element not found
+ Wait timed out after 2002ms
+ at <stack trace>
+ From: Task: element not found
+ at <stack trace>
+ From: Task: WebDriver.call(function)
+ at <stack trace>
+
+* Changed the format of strings returned by `promise.ControlFlow#getSchedule`.
+ This function now accepts a boolean to control whether the returned string
+ should include the stacktraces for when each task was scheduled.
+* Deprecating `promise.ControlFlow#getHistory`,
+ `promise.ControlFlow#clearHistory`, and `promise.ControlFlow#annotateError`.
+ These functions were all intended for internal use and are no longer
+ necessary, so they have been made no-ops.
+* `WebDriver.wait()` may now be used to wait for a promise to resolve, with
+ an optional timeout. Refer to the API documentation for more information.
+* Added support for copying files to a remote Selenium via `sendKeys` to test
+ file uploads. Refer to the API documentation for more information. Sample
+ usage included in `test/upload_test.js`
+* Expanded the interactions API to include touch actions.
+ See `WebDriver.touchActions()`.
+* FIXED: 8380: `firefox.Driver` will delete its temporary profile on `quit`.
+* FIXED: 8306: Stack overflow in promise callbacks eliminated.
+* FIXED: 8221: Added support for defining custom command mappings. Includes
+ support for PhantomJS's `executePhantomJS` (requires PhantomJS 1.9.7 or
+ GhostDriver 1.1.0).
+* FIXED: 8128: When the FirefoxDriver marshals an object to the page for
+ `executeScript`, it defines additional properties (required by the driver's
+ implementation). These properties will no longer be enumerable and should
+ be omitted (i.e. they won't show up in JSON.stringify output).
+* FIXED: 8094: The control flow will no longer deadlock when a task returns
+ a promise that depends on the completion of sub-tasks.
+
+## v2.44.0
+
+* Added the `until` module, which defines common explicit wait conditions.
+ Sample usage:
+
+ var firefox = require('selenium-webdriver/firefox'),
+ until = require('selenium-webdriver/until');
+
+ var driver = new firefox.Driver();
+ driver.get('http://www.google.com/ncr');
+ driver.wait(until.titleIs('Google Search'), 1000);
+
+* FIXED: 8000: `Builder.forBrowser()` now accepts an empty string since some
+ WebDriver implementations ignore the value. A value must still be specified,
+ however, since it is a required field in WebDriver's wire protocol.
+* FIXED: 7994: The `stacktrace` module will not modify stack traces if the
+ initial parse fails (e.g. the user defined `Error.prepareStackTrace`)
+* FIXED: 5855: Added a module (`until`) that defines several common conditions
+ for use with explicit waits. See updated examples for usage.
+
+## v2.43.5
+
+* FIXED: 7905: `Builder.usingServer(url)` once again returns `this` for
+ chaining.
+
+## v2.43.2-4
+
+* No changes; version bumps while attempting to work around an issue with
+ publishing to npm (a version string may only be used once).
+
+## v2.43.1
+
+* Fixed an issue with flakiness when setting up the Firefox profile that could
+ prevent the driver from initializing properly.
+
+## v2.43.0
+
+* Added native support for Firefox - the Java Selenium server is no longer
+ required.
+* Added support for generator functions to `ControlFlow#execute` and
+ `ControlFlow#wait`. For more information, see documentation on
+ `webdriver.promise.consume`. Requires harmony support (run with
+ `node --harmony-generators` in `v0.11.x`).
+* Various improvements to the `Builder` API. Notably, the `build()` function
+ will no longer default to attempting to use a server at
+ `http://localhost:4444/wd/hub` if it cannot start a browser directly -
+ you must specify the WebDriver server with `usingServer(url)`. You can
+ also set the target browser and WebDriver server through a pair of
+ environment variables. See the documentation on the `Builder` constructor
+ for more information.
+* For consistency with the other language bindings, added browser specific
+ classes that can be used to start a browser without the builder.
+
+ var webdriver = require('selenium-webdriver')
+ chrome = require('selenium-webdriver/chrome');
+
+ // The following are equivalent.
+ var driver1 = new webdriver.Builder().forBrowser('chrome').build();
+ var driver2 = new chrome.Driver();
+
+* Promise A+ compliance: a promise may no longer resolve to itself.
+* For consistency with other language bindings, deprecated
+ `UnhandledAlertError#getAlert` and added `#getAlertText`.
+ `getAlert` will be removed in `2.45.0`.
+* FIXED: 7641: Deprecated `ErrorCode.NO_MODAL_DIALOG_OPEN` and
+ `ErrorCode.MODAL_DIALOG_OPENED` in favor of the new
+ `ErrorCode.NO_SUCH_ALERT` and `ErrorCode.UNEXPECTED_ALERT_OPEN`,
+ respectively.
+* FIXED: 7563: Mocha integration no longer disables timeouts. Default Mocha
+ timeouts apply (2000 ms) and may be changed using `this.timeout(ms)`.
+* FIXED: 7470: Make it easier to create WebDriver instances in custom flows for
+ parallel execution.
+
+## v2.42.1
+
+* FIXED: 7465: Fixed `net.getLoopbackAddress` on Windows
+* FIXED: 7277: Support `done` callback in Mocha's BDD interface
+* FIXED: 7156: `Promise#thenFinally` should not suppress original error
+
+## v2.42.0
+
+* Removed deprecated functions `Promise#addCallback()`,
+ `Promise#addCallbacks()`, `Promise#addErrback()`, and `Promise#addBoth()`.
+* Fail with a more descriptive error if the server returns a malformed redirect
+* FIXED: 7300: Connect to ChromeDriver using the loopback address since
+ ChromeDriver 2.10.267517 binds to localhost by default.
+* FIXED: 7339: Preserve wrapped test function's string representation for
+ Mocha's BDD interface.
+
+## v2.41.0
+
+* FIXED: 7138: export logging API from webdriver module.
+* FIXED: 7105: beforeEach/it/afterEach properly bind `this` for Mocha tests.
+
+## v2.40.0
+
+* API documentation is now included in the docs directory.
+* Added utility functions for working with an array of promises:
+ `promise.all`, `promise.map`, and `promise.filter`
+* Introduced `Promise#thenCatch()` and `Promise#thenFinally()`.
+* Deprecated `Promise#addCallback()`, `Promise#addCallbacks()`,
+ `Promise#addErrback()`, and `Promise#addBoth()`.
+* Removed deprecated function `webdriver.WebDriver#getCapability`.
+* FIXED: 6826: Added support for custom locators.
+
+## v2.39.0
+
+* Version bump to stay in sync with the Selenium project.
+
+## v2.38.1
+
+* FIXED: 6686: Changed `webdriver.promise.Deferred#cancel()` to silently no-op
+ if the deferred has already been resolved.
+
+## v2.38.0
+
+* When a promise is rejected, always annotate the stacktrace with the parent
+ flow state so users can identify the source of an error.
+* Updated tests to reflect features not working correctly in the SafariDriver
+ (cookie management and proxy support; see issues 5051, 5212, and 5503)
+* FIXED: 6284: For mouse moves, correctly omit the x/y offsets if not
+ specified as a function argument (instead of passing (0,0)).
+* FIXED: 6471: Updated documentation on `webdriver.WebElement#getAttribute`
+* FIXED: 6612: On Unix, use the default IANA ephemeral port range if unable to
+ retrieve the current system's port range.
+* FIXED: 6617: Avoid triggering the node debugger when initializing the
+ stacktrace module.
+* FIXED: 6627: Safely rebuild chrome.Options from a partial JSON spec.
+
+## v2.37.0
+
+* FIXED: 6346: The remote.SeleniumServer class now accepts JVM arguments using
+ the `jvmArgs` option.
+
+## v2.36.0
+
+* _Release skipped to stay in sync with main Selenium project._
+
+## v2.35.2
+
+* FIXED: 6200: Pass arguments to the Selenium server instead of to the JVM.
+
+## v2.35.1
+
+* FIXED: 6090: Changed example scripts to use chromedriver.
+
+## v2.35.0
+
+* Version bump to stay in sync with the Selenium project.
+
+## v2.34.1
+
+* FIXED: 6079: The parent process should not wait for spawn driver service
+ processes (chromedriver, phantomjs, etc.)
+
+## v2.34.0
+
+* Added the `selenium-webdriver/testing/assert` module. This module
+ simplifies writing assertions against promised values (see
+ example in module documentation).
+* Added the `webdriver.Capabilities` class.
+* Added native support for the ChromeDriver. When using the `Builder`,
+ requesting chrome without specifying a remote server URL will default to
+ the native ChromeDriver implementation. The
+ [ChromeDriver server](https://code.google.com/p/chromedriver/downloads/list)
+ must be downloaded separately.
+
+ // Will start ChromeDriver locally.
+ var driver = new webdriver.Builder().
+ withCapabilities(webdriver.Capabilities.chrome()).
+ build();
+
+ // Will start ChromeDriver using the remote server.
+ var driver = new webdriver.Builder().
+ withCapabilities(webdriver.Capabilities.chrome()).
+ usingServer('http://server:1234/wd/hub').
+ build();
+
+* Added support for configuring proxies through the builder. For examples, see
+ `selenium-webdriver/test/proxy_test`.
+* Added native support for PhantomJS.
+* Changed signature of `SeleniumServer` to `SeleniumServer(jar, options)`.
+* Tests are now included in the npm published package. See `README.md` for
+ execution instructions
+* Removed the deprecated `webdriver.Deferred#resolve` and
+ `webdriver.promise.resolved` functions.
+* Removed the ability to connect to an existing session from the Builder. This
+ feature is intended for use with the browser-based client.
+
+## v2.33.0
+
+* Added support for WebDriver's logging API
+* FIXED: 5511: Added webdriver.manage().timeouts().pageLoadTimeout(ms)
+
+## v2.32.1
+
+* FIXED: 5541: Added missing return statement for windows in
+ `portprober.findFreePort()`
+
+## v2.32.0
+
+* Added the `selenium-webdriver/testing` package, which provides a basic
+ framework for writing tests using Mocha. See
+ `selenium-webdriver/example/google_search_test.js` for usage.
+* For Promises/A+ compatibility, backing out the change in 2.30.0 that ensured
+ rejections were always Error objects. Rejection reasons are now left as is.
+* Removed deprecated functions originally scheduled for removal in 2.31.0
+ * promise.Application.getInstance()
+ * promise.ControlFlow#schedule()
+ * promise.ControlFlow#scheduleTimeout()
+ * promise.ControlFlow#scheduleWait()
+* Renamed some functions for consistency with Promises/A+ terminology. The
+ original functions have been deprecated and will be removed in 2.34.0:
+ * promise.resolved() -> promise.fulfilled()
+ * promise.Deferred#resolve() -> promise.Deferred#fulfill()
+* FIXED: remote.SeleniumServer#stop now shuts down within the active control
+ flow, allowing scripts to finish. Use #kill to shutdown immediately.
+* FIXED: 5321: cookie deletion commands
+
+## v2.31.0
+
+* Added an example script.
+* Added a class for controlling the standalone Selenium server (server
+available separately)
+* Added a portprober for finding free ports
+* FIXED: WebElements now belong to the same flow as their parent driver.
+
+## v2.30.0
+
+* Ensures promise rejections are always Error values.
+* Version bump to keep in sync with the Selenium project.
+
+## v2.29.1
+
+* Fixed a bug that could lead to an infinite loop.
+* Added a README.md
+
+## v2.29.0
+
+* Initial release for npm:
+
+ npm install selenium-webdriver
diff --git a/node_modules/selenium-webdriver/LICENSE b/node_modules/selenium-webdriver/LICENSE
new file mode 100644
index 000000000..d64569567
--- /dev/null
+++ b/node_modules/selenium-webdriver/LICENSE
@@ -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/node_modules/selenium-webdriver/NOTICE b/node_modules/selenium-webdriver/NOTICE
new file mode 100644
index 000000000..274450355
--- /dev/null
+++ b/node_modules/selenium-webdriver/NOTICE
@@ -0,0 +1,2 @@
+Copyright 2011-2016 Software Freedom Conservancy
+Copyright 2004-2011 Selenium committers
diff --git a/node_modules/selenium-webdriver/README.md b/node_modules/selenium-webdriver/README.md
new file mode 100644
index 000000000..0b70aeddc
--- /dev/null
+++ b/node_modules/selenium-webdriver/README.md
@@ -0,0 +1,239 @@
+# selenium-webdriver
+
+Selenium is a browser automation library. Most often used for testing
+web-applications, Selenium may be used for any task that requires automating
+interaction with the browser.
+
+## Installation
+
+Selenium may be installed via npm with
+
+ npm install selenium-webdriver
+
+You will need to download additional components to work with each of the major
+browsers. The drivers for Chrome, Firefox, Safari, PhantomJS, Opera, and
+Microsoft's IE and Edge web browsers are all standalone executables that should
+be placed on your system [PATH]. Apple's safaridriver is shipped with
+Safari 10 in macOS Sierra. You will need to enable Remote Automation in
+the Develop menu of Safari 10 before testing.
+
+> **NOTE:** Mozilla's [geckodriver] is only required for Firefox 47+.
+> Everything you need for Firefox 38-46 is included with this package.
+
+> **NOTE:** Apple's [safaridriver] is preferred for testing Safari 10+.
+> To test versions of Safari prior to Safari 10, The
+> [SafariDriver.safariextz][release] browser extension should be
+> installed in your browser before using Selenium. We recommend
+> disabling the extension when using the browser without Selenium
+> or installing the extension in a profile only used for testing.
+
+
+| Browser | Component |
+| ----------------- | ---------------------------------- |
+| Chrome | [chromedriver(.exe)][chrome] |
+| Internet Explorer | [IEDriverServer.exe][release] |
+| Edge | [MicrosoftWebDriver.msi][edge] |
+| Firefox 47+ | [geckodriver(.exe)][geckodriver] |
+| PhantomJS | [phantomjs(.exe)][phantomjs] |
+| Opera | [operadriver(.exe)][opera] |
+| Safari | [safaridriver] |
+
+## Usage
+
+The sample below and others are included in the `example` directory. You may
+also find the tests for selenium-webdriver informative.
+
+ var webdriver = require('selenium-webdriver'),
+ By = webdriver.By,
+ until = webdriver.until;
+
+ var driver = new webdriver.Builder()
+ .forBrowser('firefox')
+ .build();
+
+ driver.get('http://www.google.com/ncr');
+ driver.findElement(By.name('q')).sendKeys('webdriver');
+ driver.findElement(By.name('btnG')).click();
+ driver.wait(until.titleIs('webdriver - Google Search'), 1000);
+ driver.quit();
+
+### Using the Builder API
+
+The `Builder` class is your one-stop shop for configuring new WebDriver
+instances. Rather than clutter your code with branches for the various browsers,
+the builder lets you set all options in one flow. When you call
+`Builder#build()`, all options irrelevant to the selected browser are dropped:
+
+ var webdriver = require('selenium-webdriver'),
+ chrome = require('selenium-webdriver/chrome'),
+ firefox = require('selenium-webdriver/firefox');
+
+ var driver = new webdriver.Builder()
+ .forBrowser('firefox')
+ .setChromeOptions(/* ... */)
+ .setFirefoxOptions(/* ... */)
+ .build();
+
+Why would you want to configure options irrelevant to the target browser? The
+`Builder`'s API defines your _default_ configuration. You can change the target
+browser at runtime through the `SELENIUM_BROWSER` environment variable. For
+example, the `example/google_search.js` script is configured to run against
+Firefox. You can run the example against other browsers just by changing the
+runtime environment
+
+ # cd node_modules/selenium-webdriver
+ node example/google_search
+ SELENIUM_BROWSER=chrome node example/google_search
+ SELENIUM_BROWSER=safari node example/google_search
+
+### The Standalone Selenium Server
+
+The standalone Selenium Server acts as a proxy between your script and the
+browser-specific drivers. The server may be used when running locally, but it's
+not recommend as it introduces an extra hop for each request and will slow
+things down. The server is required, however, to use a browser on a remote host
+(most browser drivers, like the IEDriverServer, do not accept remote
+connections).
+
+To use the Selenium Server, you will need to install the
+[JDK](http://www.oracle.com/technetwork/java/javase/downloads/index.html) and
+download the latest server from [Selenium][release]. Once downloaded, run the
+server with
+
+ java -jar selenium-server-standalone-2.45.0.jar
+
+You may configure your tests to run against a remote server through the Builder
+API:
+
+ var driver = new webdriver.Builder()
+ .forBrowser('firefox')
+ .usingServer('http://localhost:4444/wd/hub')
+ .build();
+
+Or change the Builder's configuration at runtime with the `SELENIUM_REMOTE_URL`
+environment variable:
+
+ SELENIUM_REMOTE_URL="http://localhost:4444/wd/hub" node script.js
+
+You can experiment with these options using the `example/google_search.js`
+script provided with `selenium-webdriver`.
+
+## Documentation
+
+API documentation is available online from the [Selenium project][api].
+Additional resources include
+
+- the #selenium channel on freenode IRC
+- the [selenium-users@googlegroups.com][users] list
+- [SeleniumHQ](http://www.seleniumhq.org/docs/) documentation
+
+## Contributing
+
+Contributions are accepted either through [GitHub][gh] pull requests or patches
+via the [Selenium issue tracker][issues]. You must sign our
+[Contributor License Agreement][cla] before your changes will be accepted.
+
+## Node Support Policy
+
+Each version of selenium-webdriver will support the latest _semver-minor_
+version of the [LTS] and stable Node releases. All _semver-major_ &
+_semver-minor_ versions between the LTS and stable release will have "best
+effort" support. Following a Selenium release, any _semver-minor_ Node releases
+will also have "best effort" support. Releases older than the latest LTS,
+_semver-major_ releases, and all unstable release branches (e.g. "v.Next")
+are considered strictly unsupported.
+
+For example, suppose the current LTS and stable releases are v4.2.4 and v5.4.1,
+respectively. Then a Selenium release would have the following support levels:
+
+| Version | Support |
+| ------- | ------------- |
+| <= 4.1 | _unsupported_ |
+| 4.2 | supported |
+| 5.0-3 | best effort |
+| 5.4 | supported |
+| >= 5.5 | best effort |
+| v.Next | _unsupported_ |
+
+### Support Level Definitions
+
+- _supported:_ A selenium-webdriver release will be API compatible with the
+ platform API, without the use of runtime flags.
+
+- _best effort:_ Bugs will be investigated as time permits. API compatibility is
+ only guaranteed where required by a _supported_ release. This effectively
+ means the adoption of new JS features, such as ES2015 modules, will depend
+ on what is supported in Node's LTS.
+
+- _unsupported:_ Bug submissions will be closed as will-not-fix and API
+ compatibility is not guaranteed.
+
+### Projected Support Schedule
+
+If Node releases a new [LTS] each October and a new major version every 6
+months, the support window for selenium-webdriver will be roughly:
+
+| Date | LTS | Stable |
+| --------- | ---: | -----: |
+| (current) | 4.2 | 5.0 |
+| 2016-04 | 4.2 | 6.0 |
+| 2016-10 | 6.0 | 7.0 |
+| 2017-04 | 6.0 | 8.0 |
+| 2017-10 | 8.0 | 9.0 |
+
+## Issues
+
+Please report any issues using the [Selenium issue tracker][issues]. When using
+the issue tracker
+
+- __Do__ include a detailed description of the problem.
+- __Do__ include a link to a [gist](http://gist.github.com/) with any
+ interesting stack traces/logs (you may also attach these directly to the bug
+ report).
+- __Do__ include a [reduced test case][reduction]. Reporting "unable to find
+ element on the page" is _not_ a valid report - there's nothing for us to
+ look into. Expect your bug report to be closed if you do not provide enough
+ information for us to investigate.
+- __Do not__ use the issue tracker to submit basic help requests. All help
+ inquiries should be directed to the [user forum][users] or #selenium IRC
+ channel.
+- __Do not__ post empty "I see this too" or "Any updates?" comments. These
+ provide no additional information and clutter the log.
+- __Do not__ report regressions on closed bugs as they are not actively
+ monitored for upates (especially bugs that are >6 months old). Please open a
+ new issue and reference the original bug in your report.
+
+## License
+
+Licensed to the Software Freedom Conservancy (SFC) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The SFC licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+
+[LTS]: https://github.com/nodejs/LTS
+[PATH]: http://en.wikipedia.org/wiki/PATH_%28variable%29
+[api]: http://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/
+[cla]: http://goo.gl/qC50R
+[chrome]: http://chromedriver.storage.googleapis.com/index.html
+[gh]: https://github.com/SeleniumHQ/selenium/
+[issues]: https://github.com/SeleniumHQ/selenium/issues
+[opera]: https://github.com/operasoftware/operachromiumdriver/releases
+[phantomjs]: http://phantomjs.org/
+[edge]: http://go.microsoft.com/fwlink/?LinkId=619687
+[geckodriver]: https://github.com/mozilla/geckodriver/releases/
+[reduction]: http://www.webkit.org/quality/reduction.html
+[release]: http://selenium-release.storage.googleapis.com/index.html
+[users]: https://groups.google.com/forum/#!forum/selenium-users
+[safaridriver]: https://developer.apple.com/library/prerelease/content/releasenotes/General/WhatsNewInSafari/Articles/Safari_10_0.html#//apple_ref/doc/uid/TP40014305-CH11-DontLinkElementID_28
diff --git a/node_modules/selenium-webdriver/chrome.js b/node_modules/selenium-webdriver/chrome.js
new file mode 100644
index 000000000..13d7b7bbf
--- /dev/null
+++ b/node_modules/selenium-webdriver/chrome.js
@@ -0,0 +1,749 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+/**
+ * @fileoverview Defines a {@linkplain Driver WebDriver} client for the Chrome
+ * web browser. Before using this module, you must download the latest
+ * [ChromeDriver release] and ensure it can be found on your system [PATH].
+ *
+ * There are three primary classes exported by this module:
+ *
+ * 1. {@linkplain ServiceBuilder}: configures the
+ * {@link selenium-webdriver/remote.DriverService remote.DriverService}
+ * that manages the [ChromeDriver] child process.
+ *
+ * 2. {@linkplain Options}: defines configuration options for each new Chrome
+ * session, such as which {@linkplain Options#setProxy proxy} to use,
+ * what {@linkplain Options#addExtensions extensions} to install, or
+ * what {@linkplain Options#addArguments command-line switches} to use when
+ * starting the browser.
+ *
+ * 3. {@linkplain Driver}: the WebDriver client; each new instance will control
+ * a unique browser session with a clean user profile (unless otherwise
+ * configured through the {@link Options} class).
+ *
+ * __Customizing the ChromeDriver Server__ <a id="custom-server"></a>
+ *
+ * By default, every Chrome session will use a single driver service, which is
+ * started the first time a {@link Driver} instance is created and terminated
+ * when this process exits. The default service will inherit its environment
+ * from the current process and direct all output to /dev/null. You may obtain
+ * a handle to this default service using
+ * {@link #getDefaultService getDefaultService()} and change its configuration
+ * with {@link #setDefaultService setDefaultService()}.
+ *
+ * You may also create a {@link Driver} with its own driver service. This is
+ * useful if you need to capture the server's log output for a specific session:
+ *
+ * let chrome = require('selenium-webdriver/chrome');
+ *
+ * let service = new chrome.ServiceBuilder()
+ * .loggingTo('/my/log/file.txt')
+ * .enableVerboseLogging()
+ * .build();
+ *
+ * let options = new chrome.Options();
+ * // configure browser options ...
+ *
+ * let driver = new chrome.Driver(options, service);
+ *
+ * Users should only instantiate the {@link Driver} class directly when they
+ * need a custom driver service configuration (as shown above). For normal
+ * operation, users should start Chrome using the
+ * {@link selenium-webdriver.Builder}.
+ *
+ * __Working with Android__ <a id="android"></a>
+ *
+ * The [ChromeDriver][android] supports running tests on the Chrome browser as
+ * well as [WebView apps][webview] starting in Android 4.4 (KitKat). In order to
+ * work with Android, you must first start the adb
+ *
+ * adb start-server
+ *
+ * By default, adb will start on port 5037. You may change this port, but this
+ * will require configuring a [custom server](#custom-server) that will connect
+ * to adb on the {@linkplain ServiceBuilder#setAdbPort correct port}:
+ *
+ * let service = new chrome.ServiceBuilder()
+ * .setAdbPort(1234)
+ * build();
+ * // etc.
+ *
+ * The ChromeDriver may be configured to launch Chrome on Android using
+ * {@link Options#androidChrome()}:
+ *
+ * let driver = new Builder()
+ * .forBrowser('chrome')
+ * .setChromeOptions(new chrome.Options().androidChrome())
+ * .build();
+ *
+ * Alternatively, you can configure the ChromeDriver to launch an app with a
+ * Chrome-WebView by setting the {@linkplain Options#androidActivity
+ * androidActivity} option:
+ *
+ * let driver = new Builder()
+ * .forBrowser('chrome')
+ * .setChromeOptions(new chrome.Options()
+ * .androidPackage('com.example')
+ * .androidActivity('com.example.Activity'))
+ * .build();
+ *
+ * [Refer to the ChromeDriver site] for more information on using the
+ * [ChromeDriver with Android][android].
+ *
+ * [ChromeDriver]: https://sites.google.com/a/chromium.org/chromedriver/
+ * [ChromeDriver release]: http://chromedriver.storage.googleapis.com/index.html
+ * [PATH]: http://en.wikipedia.org/wiki/PATH_%28variable%29
+ * [android]: https://sites.google.com/a/chromium.org/chromedriver/getting-started/getting-started---android
+ * [webview]: https://developer.chrome.com/multidevice/webview/overview
+ */
+
+'use strict';
+
+const fs = require('fs'),
+ util = require('util');
+
+const http = require('./http'),
+ io = require('./io'),
+ Capabilities = require('./lib/capabilities').Capabilities,
+ Capability = require('./lib/capabilities').Capability,
+ command = require('./lib/command'),
+ logging = require('./lib/logging'),
+ promise = require('./lib/promise'),
+ Symbols = require('./lib/symbols'),
+ webdriver = require('./lib/webdriver'),
+ portprober = require('./net/portprober'),
+ remote = require('./remote');
+
+
+/**
+ * Name of the ChromeDriver executable.
+ * @type {string}
+ * @const
+ */
+const CHROMEDRIVER_EXE =
+ process.platform === 'win32' ? 'chromedriver.exe' : 'chromedriver';
+
+
+/**
+ * Custom command names supported by ChromeDriver.
+ * @enum {string}
+ */
+const Command = {
+ LAUNCH_APP: 'launchApp'
+};
+
+
+/**
+ * Creates a command executor with support for ChromeDriver's custom commands.
+ * @param {!Promise<string>} url The server's URL.
+ * @return {!command.Executor} The new command executor.
+ */
+function createExecutor(url) {
+ let client = url.then(url => new http.HttpClient(url));
+ let executor = new http.Executor(client);
+ configureExecutor(executor);
+ return executor;
+}
+
+
+/**
+ * Configures the given executor with Chrome-specific commands.
+ * @param {!http.Executor} executor the executor to configure.
+ */
+function configureExecutor(executor) {
+ executor.defineCommand(
+ Command.LAUNCH_APP,
+ 'POST',
+ '/session/:sessionId/chromium/launch_app');
+}
+
+
+/**
+ * Creates {@link selenium-webdriver/remote.DriverService} instances that manage
+ * a [ChromeDriver](https://sites.google.com/a/chromium.org/chromedriver/)
+ * server in a child process.
+ */
+class ServiceBuilder extends remote.DriverService.Builder {
+ /**
+ * @param {string=} opt_exe Path to the server executable to use. If omitted,
+ * the builder will attempt to locate the chromedriver on the current
+ * PATH.
+ * @throws {Error} If provided executable does not exist, or the chromedriver
+ * cannot be found on the PATH.
+ */
+ constructor(opt_exe) {
+ let exe = opt_exe || io.findInPath(CHROMEDRIVER_EXE, true);
+ if (!exe) {
+ throw Error(
+ 'The ChromeDriver could not be found on the current PATH. Please ' +
+ 'download the latest version of the ChromeDriver from ' +
+ 'http://chromedriver.storage.googleapis.com/index.html and ensure ' +
+ 'it can be found on your PATH.');
+ }
+
+ super(exe);
+ this.setLoopback(true); // Required
+ }
+
+ /**
+ * Sets which port adb is listening to. _The ChromeDriver will connect to adb
+ * if an {@linkplain Options#androidPackage Android session} is requested, but
+ * adb **must** be started beforehand._
+ *
+ * @param {number} port Which port adb is running on.
+ * @return {!ServiceBuilder} A self reference.
+ */
+ setAdbPort(port) {
+ return this.addArguments('--adb-port=' + port);
+ }
+
+ /**
+ * Sets the path of the log file the driver should log to. If a log file is
+ * not specified, the driver will log to stderr.
+ * @param {string} path Path of the log file to use.
+ * @return {!ServiceBuilder} A self reference.
+ */
+ loggingTo(path) {
+ return this.addArguments('--log-path=' + path);
+ }
+
+ /**
+ * Enables verbose logging.
+ * @return {!ServiceBuilder} A self reference.
+ */
+ enableVerboseLogging() {
+ return this.addArguments('--verbose');
+ }
+
+ /**
+ * Sets the number of threads the driver should use to manage HTTP requests.
+ * By default, the driver will use 4 threads.
+ * @param {number} n The number of threads to use.
+ * @return {!ServiceBuilder} A self reference.
+ */
+ setNumHttpThreads(n) {
+ return this.addArguments('--http-threads=' + n);
+ }
+
+ /**
+ * @override
+ */
+ setPath(path) {
+ super.setPath(path);
+ return this.addArguments('--url-base=' + path);
+ }
+}
+
+
+
+/** @type {remote.DriverService} */
+let defaultService = null;
+
+
+/**
+ * Sets the default service to use for new ChromeDriver instances.
+ * @param {!remote.DriverService} service The service to use.
+ * @throws {Error} If the default service is currently running.
+ */
+function setDefaultService(service) {
+ if (defaultService && defaultService.isRunning()) {
+ throw Error(
+ 'The previously configured ChromeDriver service is still running. ' +
+ 'You must shut it down before you may adjust its configuration.');
+ }
+ defaultService = service;
+}
+
+
+/**
+ * Returns the default ChromeDriver service. If such a service has not been
+ * configured, one will be constructed using the default configuration for
+ * a ChromeDriver executable found on the system PATH.
+ * @return {!remote.DriverService} The default ChromeDriver service.
+ */
+function getDefaultService() {
+ if (!defaultService) {
+ defaultService = new ServiceBuilder().build();
+ }
+ return defaultService;
+}
+
+
+/**
+ * @type {string}
+ * @const
+ */
+let OPTIONS_CAPABILITY_KEY = 'chromeOptions';
+
+
+/**
+ * Class for managing ChromeDriver specific options.
+ */
+class Options {
+ constructor() {
+ /** @private {!Object} */
+ this.options_ = {};
+
+ /** @private {!Array<(string|!Buffer)>} */
+ this.extensions_ = [];
+
+ /** @private {?logging.Preferences} */
+ this.logPrefs_ = null;
+
+ /** @private {?./lib/capabilities.ProxyConfig} */
+ this.proxy_ = null;
+ }
+
+ /**
+ * Extracts the ChromeDriver specific options from the given capabilities
+ * object.
+ * @param {!Capabilities} caps The capabilities object.
+ * @return {!Options} The ChromeDriver options.
+ */
+ static fromCapabilities(caps) {
+ let options = new Options();
+
+ let o = caps.get(OPTIONS_CAPABILITY_KEY);
+ if (o instanceof Options) {
+ options = o;
+ } else if (o) {
+ options.
+ addArguments(o.args || []).
+ addExtensions(o.extensions || []).
+ detachDriver(o.detach).
+ excludeSwitches(o.excludeSwitches || []).
+ setChromeBinaryPath(o.binary).
+ setChromeLogFile(o.logPath).
+ setChromeMinidumpPath(o.minidumpPath).
+ setLocalState(o.localState).
+ setMobileEmulation(o.mobileEmulation).
+ setUserPreferences(o.prefs).
+ setPerfLoggingPrefs(o.perfLoggingPrefs);
+ }
+
+ if (caps.has(Capability.PROXY)) {
+ options.setProxy(caps.get(Capability.PROXY));
+ }
+
+ if (caps.has(Capability.LOGGING_PREFS)) {
+ options.setLoggingPrefs(
+ caps.get(Capability.LOGGING_PREFS));
+ }
+
+ return options;
+ }
+
+ /**
+ * Add additional command line arguments to use when launching the Chrome
+ * browser. Each argument may be specified with or without the "--" prefix
+ * (e.g. "--foo" and "foo"). Arguments with an associated value should be
+ * delimited by an "=": "foo=bar".
+ * @param {...(string|!Array<string>)} var_args The arguments to add.
+ * @return {!Options} A self reference.
+ */
+ addArguments(var_args) {
+ let args = this.options_.args || [];
+ args = args.concat.apply(args, arguments);
+ if (args.length) {
+ this.options_.args = args;
+ }
+ return this;
+ }
+
+ /**
+ * List of Chrome command line switches to exclude that ChromeDriver by default
+ * passes when starting Chrome. Do not prefix switches with "--".
+ *
+ * @param {...(string|!Array<string>)} var_args The switches to exclude.
+ * @return {!Options} A self reference.
+ */
+ excludeSwitches(var_args) {
+ let switches = this.options_.excludeSwitches || [];
+ switches = switches.concat.apply(switches, arguments);
+ if (switches.length) {
+ this.options_.excludeSwitches = switches;
+ }
+ return this;
+ }
+
+ /**
+ * Add additional extensions to install when launching Chrome. Each extension
+ * should be specified as the path to the packed CRX file, or a Buffer for an
+ * extension.
+ * @param {...(string|!Buffer|!Array<(string|!Buffer)>)} var_args The
+ * extensions to add.
+ * @return {!Options} A self reference.
+ */
+ addExtensions(var_args) {
+ this.extensions_ =
+ this.extensions_.concat.apply(this.extensions_, arguments);
+ return this;
+ }
+
+ /**
+ * Sets the path to the Chrome binary to use. On Mac OS X, this path should
+ * reference the actual Chrome executable, not just the application binary
+ * (e.g. "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome").
+ *
+ * The binary path be absolute or relative to the chromedriver server
+ * executable, but it must exist on the machine that will launch Chrome.
+ *
+ * @param {string} path The path to the Chrome binary to use.
+ * @return {!Options} A self reference.
+ */
+ setChromeBinaryPath(path) {
+ this.options_.binary = path;
+ return this;
+ }
+
+ /**
+ * Sets whether to leave the started Chrome browser running if the controlling
+ * ChromeDriver service is killed before {@link webdriver.WebDriver#quit()} is
+ * called.
+ * @param {boolean} detach Whether to leave the browser running if the
+ * chromedriver service is killed before the session.
+ * @return {!Options} A self reference.
+ */
+ detachDriver(detach) {
+ this.options_.detach = detach;
+ return this;
+ }
+
+ /**
+ * Sets the user preferences for Chrome's user profile. See the "Preferences"
+ * file in Chrome's user data directory for examples.
+ * @param {!Object} prefs Dictionary of user preferences to use.
+ * @return {!Options} A self reference.
+ */
+ setUserPreferences(prefs) {
+ this.options_.prefs = prefs;
+ return this;
+ }
+
+ /**
+ * Sets the logging preferences for the new session.
+ * @param {!logging.Preferences} prefs The logging preferences.
+ * @return {!Options} A self reference.
+ */
+ setLoggingPrefs(prefs) {
+ this.logPrefs_ = prefs;
+ return this;
+ }
+
+ /**
+ * Sets the performance logging preferences. Options include:
+ *
+ * - `enableNetwork`: Whether or not to collect events from Network domain.
+ * - `enablePage`: Whether or not to collect events from Page domain.
+ * - `enableTimeline`: Whether or not to collect events from Timeline domain.
+ * Note: when tracing is enabled, Timeline domain is implicitly disabled,
+ * unless `enableTimeline` is explicitly set to true.
+ * - `tracingCategories`: A comma-separated string of Chrome tracing
+ * categories for which trace events should be collected. An unspecified
+ * or empty string disables tracing.
+ * - `bufferUsageReportingInterval`: The requested number of milliseconds
+ * between DevTools trace buffer usage events. For example, if 1000, then
+ * once per second, DevTools will report how full the trace buffer is. If
+ * a report indicates the buffer usage is 100%, a warning will be issued.
+ *
+ * @param {{enableNetwork: boolean,
+ * enablePage: boolean,
+ * enableTimeline: boolean,
+ * tracingCategories: string,
+ * bufferUsageReportingInterval: number}} prefs The performance
+ * logging preferences.
+ * @return {!Options} A self reference.
+ */
+ setPerfLoggingPrefs(prefs) {
+ this.options_.perfLoggingPrefs = prefs;
+ return this;
+ }
+
+ /**
+ * Sets preferences for the "Local State" file in Chrome's user data
+ * directory.
+ * @param {!Object} state Dictionary of local state preferences.
+ * @return {!Options} A self reference.
+ */
+ setLocalState(state) {
+ this.options_.localState = state;
+ return this;
+ }
+
+ /**
+ * Sets the name of the activity hosting a Chrome-based Android WebView. This
+ * option must be set to connect to an [Android WebView](
+ * https://sites.google.com/a/chromium.org/chromedriver/getting-started/getting-started---android)
+ *
+ * @param {string} name The activity name.
+ * @return {!Options} A self reference.
+ */
+ androidActivity(name) {
+ this.options_.androidActivity = name;
+ return this;
+ }
+
+ /**
+ * Sets the device serial number to connect to via ADB. If not specified, the
+ * ChromeDriver will select an unused device at random. An error will be
+ * returned if all devices already have active sessions.
+ *
+ * @param {string} serial The device serial number to connect to.
+ * @return {!Options} A self reference.
+ */
+ androidDeviceSerial(serial) {
+ this.options_.androidDeviceSerial = serial;
+ return this;
+ }
+
+ /**
+ * Configures the ChromeDriver to launch Chrome on Android via adb. This
+ * function is shorthand for
+ * {@link #androidPackage options.androidPackage('com.android.chrome')}.
+ * @return {!Options} A self reference.
+ */
+ androidChrome() {
+ return this.androidPackage('com.android.chrome');
+ }
+
+ /**
+ * Sets the package name of the Chrome or WebView app.
+ *
+ * @param {?string} pkg The package to connect to, or `null` to disable Android
+ * and switch back to using desktop Chrome.
+ * @return {!Options} A self reference.
+ */
+ androidPackage(pkg) {
+ this.options_.androidPackage = pkg;
+ return this;
+ }
+
+ /**
+ * Sets the process name of the Activity hosting the WebView (as given by
+ * `ps`). If not specified, the process name is assumed to be the same as
+ * {@link #androidPackage}.
+ *
+ * @param {string} processName The main activity name.
+ * @return {!Options} A self reference.
+ */
+ androidProcess(processName) {
+ this.options_.androidProcess = processName;
+ return this;
+ }
+
+ /**
+ * Sets whether to connect to an already-running instead of the specified
+ * {@linkplain #androidProcess app} instead of launching the app with a clean
+ * data directory.
+ *
+ * @param {boolean} useRunning Whether to connect to a running instance.
+ * @return {!Options} A self reference.
+ */
+ androidUseRunningApp(useRunning) {
+ this.options_.androidUseRunningApp = useRunning;
+ return this;
+ }
+
+ /**
+ * Sets the path to Chrome's log file. This path should exist on the machine
+ * that will launch Chrome.
+ * @param {string} path Path to the log file to use.
+ * @return {!Options} A self reference.
+ */
+ setChromeLogFile(path) {
+ this.options_.logPath = path;
+ return this;
+ }
+
+ /**
+ * Sets the directory to store Chrome minidumps in. This option is only
+ * supported when ChromeDriver is running on Linux.
+ * @param {string} path The directory path.
+ * @return {!Options} A self reference.
+ */
+ setChromeMinidumpPath(path) {
+ this.options_.minidumpPath = path;
+ return this;
+ }
+
+ /**
+ * Configures Chrome to emulate a mobile device. For more information, refer
+ * to the ChromeDriver project page on [mobile emulation][em]. Configuration
+ * options include:
+ *
+ * - `deviceName`: The name of a pre-configured [emulated device][devem]
+ * - `width`: screen width, in pixels
+ * - `height`: screen height, in pixels
+ * - `pixelRatio`: screen pixel ratio
+ *
+ * __Example 1: Using a Pre-configured Device__
+ *
+ * let options = new chrome.Options().setMobileEmulation(
+ * {deviceName: 'Google Nexus 5'});
+ *
+ * let driver = new chrome.Driver(options);
+ *
+ * __Example 2: Using Custom Screen Configuration__
+ *
+ * let options = new chrome.Options().setMobileEmulation({
+ * width: 360,
+ * height: 640,
+ * pixelRatio: 3.0
+ * });
+ *
+ * let driver = new chrome.Driver(options);
+ *
+ *
+ * [em]: https://sites.google.com/a/chromium.org/chromedriver/mobile-emulation
+ * [devem]: https://developer.chrome.com/devtools/docs/device-mode
+ *
+ * @param {?({deviceName: string}|
+ * {width: number, height: number, pixelRatio: number})} config The
+ * mobile emulation configuration, or `null` to disable emulation.
+ * @return {!Options} A self reference.
+ */
+ setMobileEmulation(config) {
+ this.options_.mobileEmulation = config;
+ return this;
+ }
+
+ /**
+ * Sets the proxy settings for the new session.
+ * @param {./lib/capabilities.ProxyConfig} proxy The proxy configuration to
+ * use.
+ * @return {!Options} A self reference.
+ */
+ setProxy(proxy) {
+ this.proxy_ = proxy;
+ return this;
+ }
+
+ /**
+ * Converts this options instance to a {@link Capabilities} object.
+ * @param {Capabilities=} opt_capabilities The capabilities to merge
+ * these options into, if any.
+ * @return {!Capabilities} The capabilities.
+ */
+ toCapabilities(opt_capabilities) {
+ let caps = opt_capabilities || Capabilities.chrome();
+ caps.
+ set(Capability.PROXY, this.proxy_).
+ set(Capability.LOGGING_PREFS, this.logPrefs_).
+ set(OPTIONS_CAPABILITY_KEY, this);
+ return caps;
+ }
+
+ /**
+ * Converts this instance to its JSON wire protocol representation. Note this
+ * function is an implementation not intended for general use.
+ * @return {!Object} The JSON wire protocol representation of this instance.
+ */
+ [Symbols.serialize]() {
+ let json = {};
+ for (let key in this.options_) {
+ if (this.options_[key] != null) {
+ json[key] = this.options_[key];
+ }
+ }
+ if (this.extensions_.length) {
+ json.extensions = this.extensions_.map(function(extension) {
+ if (Buffer.isBuffer(extension)) {
+ return extension.toString('base64');
+ }
+ return io.read(/** @type {string} */(extension))
+ .then(buffer => buffer.toString('base64'));
+ });
+ }
+ return json;
+ }
+}
+
+
+/**
+ * Creates a new WebDriver client for Chrome.
+ */
+class Driver extends webdriver.WebDriver {
+ /**
+ * @param {(Capabilities|Options)=} opt_config The configuration
+ * options.
+ * @param {remote.DriverService=} opt_service The session to use; will use
+ * the {@linkplain #getDefaultService default service} by default.
+ * @param {promise.ControlFlow=} opt_flow The control flow to use,
+ * or {@code null} to use the currently active flow.
+ * @param {http.Executor=} opt_executor A pre-configured command executor that
+ * should be used to send commands to the remote end. The provided
+ * executor should not be reused with other clients as its internal
+ * command mappings will be updated to support Chrome-specific commands.
+ *
+ * You may provide either a custom executor or a driver service, but not both.
+ *
+ * @throws {Error} if both `opt_service` and `opt_executor` are provided.
+ */
+ constructor(opt_config, opt_service, opt_flow, opt_executor) {
+ if (opt_service && opt_executor) {
+ throw Error(
+ 'Either a DriverService or Executor may be provided, but not both');
+ }
+
+ let executor;
+ if (opt_executor) {
+ executor = opt_executor;
+ configureExecutor(executor);
+ } else {
+ let service = opt_service || getDefaultService();
+ executor = createExecutor(service.start());
+ }
+
+ let caps =
+ opt_config instanceof Options ? opt_config.toCapabilities() :
+ (opt_config || Capabilities.chrome());
+
+ let driver = webdriver.WebDriver.createSession(executor, caps, opt_flow);
+
+ super(driver.getSession(), executor, driver.controlFlow());
+ }
+
+ /**
+ * This function is a no-op as file detectors are not supported by this
+ * implementation.
+ * @override
+ */
+ setFileDetector() {}
+
+ /**
+ * Schedules a command to launch Chrome App with given ID.
+ * @param {string} id ID of the App to launch.
+ * @return {!promise.Promise<void>} A promise that will be resolved
+ * when app is launched.
+ */
+ launchApp(id) {
+ return this.schedule(
+ new command.Command(Command.LAUNCH_APP).setParameter('id', id),
+ 'Driver.launchApp()');
+ }
+}
+
+
+// PUBLIC API
+
+
+exports.Driver = Driver;
+exports.Options = Options;
+exports.ServiceBuilder = ServiceBuilder;
+exports.getDefaultService = getDefaultService;
+exports.setDefaultService = setDefaultService;
diff --git a/node_modules/selenium-webdriver/edge.js b/node_modules/selenium-webdriver/edge.js
new file mode 100644
index 000000000..9685a2c21
--- /dev/null
+++ b/node_modules/selenium-webdriver/edge.js
@@ -0,0 +1,305 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+/**
+ * @fileoverview Defines a {@linkplain Driver WebDriver} client for
+ * Microsoft's Edge web browser. Before using this module,
+ * you must download and install the latest
+ * [MicrosoftEdgeDriver](http://go.microsoft.com/fwlink/?LinkId=619687) server.
+ * Ensure that the MicrosoftEdgeDriver is on your
+ * [PATH](http://en.wikipedia.org/wiki/PATH_%28variable%29).
+ *
+ * There are three primary classes exported by this module:
+ *
+ * 1. {@linkplain ServiceBuilder}: configures the
+ * {@link ./remote.DriverService remote.DriverService}
+ * that manages the [MicrosoftEdgeDriver] child process.
+ *
+ * 2. {@linkplain Options}: defines configuration options for each new
+ * MicrosoftEdgeDriver session, such as which
+ * {@linkplain Options#setProxy proxy} to use when starting the browser.
+ *
+ * 3. {@linkplain Driver}: the WebDriver client; each new instance will control
+ * a unique browser session.
+ *
+ * __Customizing the MicrosoftEdgeDriver Server__ <a id="custom-server"></a>
+ *
+ * By default, every MicrosoftEdge session will use a single driver service,
+ * which is started the first time a {@link Driver} instance is created and
+ * terminated when this process exits. The default service will inherit its
+ * environment from the current process.
+ * You may obtain a handle to this default service using
+ * {@link #getDefaultService getDefaultService()} and change its configuration
+ * with {@link #setDefaultService setDefaultService()}.
+ *
+ * You may also create a {@link Driver} with its own driver service. This is
+ * useful if you need to capture the server's log output for a specific session:
+ *
+ * var edge = require('selenium-webdriver/edge');
+ *
+ * var service = new edge.ServiceBuilder()
+ * .setPort(55555)
+ * .build();
+ *
+ * var options = new edge.Options();
+ * // configure browser options ...
+ *
+ * var driver = new edge.Driver(options, service);
+ *
+ * Users should only instantiate the {@link Driver} class directly when they
+ * need a custom driver service configuration (as shown above). For normal
+ * operation, users should start MicrosoftEdge using the
+ * {@link ./builder.Builder selenium-webdriver.Builder}.
+ *
+ * [MicrosoftEdgeDriver]: https://msdn.microsoft.com/en-us/library/mt188085(v=vs.85).aspx
+ */
+
+'use strict';
+
+const fs = require('fs'),
+ util = require('util');
+
+const http = require('./http'),
+ io = require('./io'),
+ capabilities = require('./lib/capabilities'),
+ promise = require('./lib/promise'),
+ Symbols = require('./lib/symbols'),
+ webdriver = require('./lib/webdriver'),
+ portprober = require('./net/portprober'),
+ remote = require('./remote');
+
+const EDGEDRIVER_EXE = 'MicrosoftWebDriver.exe';
+
+
+/**
+ * Option keys.
+ * @enum {string}
+ */
+const CAPABILITY_KEY = {
+ PAGE_LOAD_STRATEGY: 'pageLoadStrategy'
+};
+
+
+/**
+ * Class for managing MicrosoftEdgeDriver specific options.
+ */
+class Options {
+ constructor() {
+ /** @private {!Object} */
+ this.options_ = {};
+
+ /** @private {?capabilities.ProxyConfig} */
+ this.proxy_ = null;
+ }
+
+ /**
+ * Extracts the MicrosoftEdgeDriver specific options from the given
+ * capabilities object.
+ * @param {!capabilities.Capabilities} caps The capabilities object.
+ * @return {!Options} The MicrosoftEdgeDriver options.
+ */
+ static fromCapabilities(caps) {
+ var options = new Options();
+ var map = options.options_;
+
+ Object.keys(CAPABILITY_KEY).forEach(function(key) {
+ key = CAPABILITY_KEY[key];
+ if (caps.has(key)) {
+ map[key] = caps.get(key);
+ }
+ });
+
+ if (caps.has(capabilities.Capability.PROXY)) {
+ options.setProxy(caps.get(capabilities.Capability.PROXY));
+ }
+
+ return options;
+ }
+
+ /**
+ * Sets the proxy settings for the new session.
+ * @param {capabilities.ProxyConfig} proxy The proxy configuration to use.
+ * @return {!Options} A self reference.
+ */
+ setProxy(proxy) {
+ this.proxy_ = proxy;
+ return this;
+ }
+
+ /**
+ * Sets the page load strategy for Edge.
+ * Supported values are "normal", "eager", and "none";
+ *
+ * @param {string} pageLoadStrategy The page load strategy to use.
+ * @return {!Options} A self reference.
+ */
+ setPageLoadStrategy(pageLoadStrategy) {
+ this.options_[CAPABILITY_KEY.PAGE_LOAD_STRATEGY] =
+ pageLoadStrategy.toLowerCase();
+ return this;
+ }
+
+ /**
+ * Converts this options instance to a {@link capabilities.Capabilities}
+ * object.
+ * @param {capabilities.Capabilities=} opt_capabilities The capabilities to
+ * merge these options into, if any.
+ * @return {!capabilities.Capabilities} The capabilities.
+ */
+ toCapabilities(opt_capabilities) {
+ var caps = opt_capabilities || capabilities.Capabilities.edge();
+ if (this.proxy_) {
+ caps.set(capabilities.Capability.PROXY, this.proxy_);
+ }
+ Object.keys(this.options_).forEach(function(key) {
+ caps.set(key, this.options_[key]);
+ }, this);
+ return caps;
+ }
+
+ /**
+ * Converts this instance to its JSON wire protocol representation. Note this
+ * function is an implementation not intended for general use.
+ * @return {{pageLoadStrategy: (string|undefined)}}
+ * The JSON wire protocol representation of this instance.
+ */
+ [Symbols.serialize]() {
+ var json = {};
+ for (var key in this.options_) {
+ if (this.options_[key] != null) {
+ json[key] = this.options_[key];
+ }
+ }
+ return json;
+ }
+}
+
+
+/**
+ * Creates {@link remote.DriverService} instances that manage a
+ * MicrosoftEdgeDriver server in a child process.
+ */
+class ServiceBuilder extends remote.DriverService.Builder {
+ /**
+ * @param {string=} opt_exe Path to the server executable to use. If omitted,
+ * the builder will attempt to locate the MicrosoftEdgeDriver on the current
+ * PATH.
+ * @throws {Error} If provided executable does not exist, or the
+ * MicrosoftEdgeDriver cannot be found on the PATH.
+ */
+ constructor(opt_exe) {
+ let exe = opt_exe || io.findInPath(EDGEDRIVER_EXE, true);
+ if (!exe) {
+ throw Error(
+ 'The ' + EDGEDRIVER_EXE + ' could not be found on the current PATH. ' +
+ 'Please download the latest version of the MicrosoftEdgeDriver from ' +
+ 'https://www.microsoft.com/en-us/download/details.aspx?id=48212 and ' +
+ 'ensure it can be found on your PATH.');
+ }
+
+ super(exe);
+
+ // Binding to the loopback address will fail if not running with
+ // administrator privileges. Since we cannot test for that in script
+ // (or can we?), force the DriverService to use "localhost".
+ this.setHostname('localhost');
+ }
+}
+
+
+/** @type {remote.DriverService} */
+var defaultService = null;
+
+
+/**
+ * Sets the default service to use for new MicrosoftEdgeDriver instances.
+ * @param {!remote.DriverService} service The service to use.
+ * @throws {Error} If the default service is currently running.
+ */
+function setDefaultService(service) {
+ if (defaultService && defaultService.isRunning()) {
+ throw Error(
+ 'The previously configured EdgeDriver service is still running. ' +
+ 'You must shut it down before you may adjust its configuration.');
+ }
+ defaultService = service;
+}
+
+
+/**
+ * Returns the default MicrosoftEdgeDriver service. If such a service has
+ * not been configured, one will be constructed using the default configuration
+ * for an MicrosoftEdgeDriver executable found on the system PATH.
+ * @return {!remote.DriverService} The default MicrosoftEdgeDriver service.
+ */
+function getDefaultService() {
+ if (!defaultService) {
+ defaultService = new ServiceBuilder().build();
+ }
+ return defaultService;
+}
+
+
+/**
+ * Creates a new WebDriver client for Microsoft's Edge.
+ */
+class Driver extends webdriver.WebDriver {
+ /**
+ * @param {(capabilities.Capabilities|Options)=} opt_config The configuration
+ * options.
+ * @param {remote.DriverService=} opt_service The session to use; will use
+ * the {@linkplain #getDefaultService default service} by default.
+ * @param {promise.ControlFlow=} opt_flow The control flow to use, or
+ * {@code null} to use the currently active flow.
+ */
+ constructor(opt_config, opt_service, opt_flow) {
+ var service = opt_service || getDefaultService();
+ var client = service.start().then(url => new http.HttpClient(url));
+ var executor = new http.Executor(client);
+
+ var caps =
+ opt_config instanceof Options ? opt_config.toCapabilities() :
+ (opt_config || capabilities.Capabilities.edge());
+
+ var driver = webdriver.WebDriver.createSession(executor, caps, opt_flow);
+ super(driver.getSession(), executor, driver.controlFlow());
+
+ var boundQuit = this.quit.bind(this);
+
+ /** @override */
+ this.quit = function() {
+ return boundQuit().finally(service.kill.bind(service));
+ };
+ }
+
+ /**
+ * This function is a no-op as file detectors are not supported by this
+ * implementation.
+ * @override
+ */
+ setFileDetector() {}
+}
+
+
+// PUBLIC API
+
+
+exports.Driver = Driver;
+exports.Options = Options;
+exports.ServiceBuilder = ServiceBuilder;
+exports.getDefaultService = getDefaultService;
+exports.setDefaultService = setDefaultService;
diff --git a/node_modules/selenium-webdriver/example/chrome_android.js b/node_modules/selenium-webdriver/example/chrome_android.js
new file mode 100644
index 000000000..990a4c445
--- /dev/null
+++ b/node_modules/selenium-webdriver/example/chrome_android.js
@@ -0,0 +1,38 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+/**
+ * @fileoverview A basic example of working with Chrome on Android. Before
+ * running this example, you must start adb and connect a device (or start an
+ * AVD).
+ */
+
+var webdriver = require('..'),
+ By = webdriver.By,
+ until = webdriver.until,
+ chrome = require('../chrome');
+
+var driver = new webdriver.Builder()
+ .forBrowser('chrome')
+ .setChromeOptions(new chrome.Options().androidChrome())
+ .build();
+
+driver.get('http://www.google.com/ncr');
+driver.findElement(By.name('q')).sendKeys('webdriver');
+driver.findElement(By.name('btnG')).click();
+driver.wait(until.titleIs('webdriver - Google Search'), 1000);
+driver.quit();
diff --git a/node_modules/selenium-webdriver/example/chrome_mobile_emulation.js b/node_modules/selenium-webdriver/example/chrome_mobile_emulation.js
new file mode 100644
index 000000000..d3081127d
--- /dev/null
+++ b/node_modules/selenium-webdriver/example/chrome_mobile_emulation.js
@@ -0,0 +1,39 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+/**
+ * @fileoverview This is an example of emulating a mobile device using the
+ * ChromeDriver.
+ */
+
+var webdriver = require('..'),
+ By = webdriver.By,
+ until = webdriver.until,
+ chrome = require('../chrome');
+
+
+var driver = new webdriver.Builder()
+ .forBrowser('chrome')
+ .setChromeOptions(new chrome.Options()
+ .setMobileEmulation({deviceName: 'Google Nexus 5'}))
+ .build();
+
+driver.get('http://www.google.com/ncr');
+driver.findElement(By.name('q')).sendKeys('webdriver');
+driver.findElement(By.name('btnG')).click();
+driver.wait(until.titleIs('webdriver - Google Search'), 1000);
+driver.quit();
diff --git a/node_modules/selenium-webdriver/example/google_search.js b/node_modules/selenium-webdriver/example/google_search.js
new file mode 100644
index 000000000..22d0d21ce
--- /dev/null
+++ b/node_modules/selenium-webdriver/example/google_search.js
@@ -0,0 +1,50 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+/**
+ * @fileoverview An example WebDriver script. This requires the chromedriver
+ * to be present on the system PATH.
+ *
+ * Usage:
+ * // Default behavior
+ * node selenium-webdriver/example/google_search.js
+ *
+ * // Target Chrome locally; the chromedriver must be on your PATH
+ * SELENIUM_BROWSER=chrome node selenium-webdriver/example/google_search.js
+ *
+ * // Use a local copy of the standalone Selenium server
+ * SELENIUM_SERVER_JAR=/path/to/selenium-server-standalone.jar \
+ * node selenium-webdriver/example/google_search.js
+ *
+ * // Target a remote Selenium server
+ * SELENIUM_REMOTE_URL=http://www.example.com:4444/wd/hub \
+ * node selenium-webdriver/example/google_search.js
+ */
+
+var webdriver = require('..'),
+ By = webdriver.By,
+ until = webdriver.until;
+
+var driver = new webdriver.Builder()
+ .forBrowser('firefox')
+ .build();
+
+driver.get('http://www.google.com/ncr');
+driver.findElement(By.name('q')).sendKeys('webdriver');
+driver.findElement(By.name('btnG')).click();
+driver.wait(until.titleIs('webdriver - Google Search'), 1000);
+driver.quit(); \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/example/google_search_generator.js b/node_modules/selenium-webdriver/example/google_search_generator.js
new file mode 100644
index 000000000..983c8d84f
--- /dev/null
+++ b/node_modules/selenium-webdriver/example/google_search_generator.js
@@ -0,0 +1,45 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+/**
+ * @fileoverview An example WebDriver script using generator functions.
+ *
+ * Usage: node selenium-webdriver/example/google_search_generator.js
+ */
+
+var webdriver = require('..'),
+ By = webdriver.By;
+
+var driver = new webdriver.Builder()
+ .forBrowser('firefox')
+ .build();
+
+driver.get('http://www.google.com/ncr');
+driver.call(function* () {
+ var query = yield driver.findElement(By.name('q'));
+ query.sendKeys('webdriver');
+
+ var submit = yield driver.findElement(By.name('btnG'));
+ submit.click();
+});
+
+driver.wait(function* () {
+ var title = yield driver.getTitle();
+ return 'webdriver - Google Search' === title;
+}, 1000);
+
+driver.quit();
diff --git a/node_modules/selenium-webdriver/example/google_search_test.js b/node_modules/selenium-webdriver/example/google_search_test.js
new file mode 100644
index 000000000..823e2c578
--- /dev/null
+++ b/node_modules/selenium-webdriver/example/google_search_test.js
@@ -0,0 +1,47 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+/**
+ * @fileoverview An example test that may be run using Mocha.
+ * Usage: mocha -t 10000 selenium-webdriver/example/google_search_test.js
+ */
+
+var webdriver = require('..'),
+ By = webdriver.By,
+ until = webdriver.until,
+ test = require('../testing');
+
+test.describe('Google Search', function() {
+ var driver;
+
+ test.before(function() {
+ driver = new webdriver.Builder()
+ .forBrowser('firefox')
+ .build();
+ });
+
+ test.it('should append query to title', function() {
+ driver.get('http://www.google.com');
+ driver.findElement(By.name('q')).sendKeys('webdriver');
+ driver.findElement(By.name('btnG')).click();
+ driver.wait(until.titleIs('webdriver - Google Search'), 1000);
+ });
+
+ test.after(function() {
+ driver.quit();
+ });
+});
diff --git a/node_modules/selenium-webdriver/example/logging.js b/node_modules/selenium-webdriver/example/logging.js
new file mode 100644
index 000000000..ae1d4cc2a
--- /dev/null
+++ b/node_modules/selenium-webdriver/example/logging.js
@@ -0,0 +1,43 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+/**
+ * @fileoverview Demonstrates how to use WebDriver's logging sysem.
+ */
+
+'use strict';
+
+var webdriver = require('..'),
+ By = webdriver.By,
+ until = webdriver.until;
+
+webdriver.logging.installConsoleHandler();
+webdriver.logging.getLogger('webdriver.http')
+ .setLevel(webdriver.logging.Level.ALL);
+
+var driver = new webdriver.Builder()
+ .forBrowser('firefox')
+ .build();
+
+driver.get('http://www.google.com/ncr');
+
+var searchBox = driver.wait(until.elementLocated(By.name('q')), 3000);
+searchBox.sendKeys('webdriver');
+
+driver.findElement(By.name('btnG')).click();
+driver.wait(until.titleIs('webdriver - Google Search'), 1000);
+driver.quit();
diff --git a/node_modules/selenium-webdriver/example/parallel_flows.js b/node_modules/selenium-webdriver/example/parallel_flows.js
new file mode 100644
index 000000000..f41692234
--- /dev/null
+++ b/node_modules/selenium-webdriver/example/parallel_flows.js
@@ -0,0 +1,51 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+/**
+ * @fileoverview An example of starting multiple WebDriver clients that run
+ * in parallel in separate control flows.
+ */
+
+var webdriver = require('..'),
+ By = webdriver.By,
+ until = webdriver.until;
+
+for (var i = 0; i < 3; i++) {
+ (function(n) {
+ var flow = new webdriver.promise.ControlFlow()
+ .on('uncaughtException', function(e) {
+ console.log('uncaughtException in flow %d: %s', n, e);
+ });
+
+ var driver = new webdriver.Builder().
+ forBrowser('firefox').
+ setControlFlow(flow). // Comment out this line to see the difference.
+ build();
+
+ // Position and resize window so it's easy to see them running together.
+ driver.manage().window().setSize(600, 400);
+ driver.manage().window().setPosition(300 * i, 400 * i);
+
+ driver.get('http://www.google.com');
+ driver.findElement(By.name('q')).sendKeys('webdriver');
+ driver.findElement(By.name('btnG')).click();
+ driver.wait(until.titleIs('webdriver - Google Search'), 1000);
+
+ driver.quit();
+ })(i);
+}
+
diff --git a/node_modules/selenium-webdriver/firefox/binary.js b/node_modules/selenium-webdriver/firefox/binary.js
new file mode 100644
index 000000000..f31440b4e
--- /dev/null
+++ b/node_modules/selenium-webdriver/firefox/binary.js
@@ -0,0 +1,270 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+/**
+ * @fileoverview Manages Firefox binaries. This module is considered internal;
+ * users should use {@link ./firefox selenium-webdriver/firefox}.
+ */
+
+'use strict';
+
+const child = require('child_process'),
+ fs = require('fs'),
+ path = require('path'),
+ util = require('util');
+
+const isDevMode = require('../lib/devmode'),
+ Symbols = require('../lib/symbols'),
+ io = require('../io'),
+ exec = require('../io/exec');
+
+
+
+/** @const */
+const NO_FOCUS_LIB_X86 = isDevMode ?
+ path.join(__dirname, '../../../../cpp/prebuilt/i386/libnoblur.so') :
+ path.join(__dirname, '../lib/firefox/i386/libnoblur.so') ;
+
+/** @const */
+const NO_FOCUS_LIB_AMD64 = isDevMode ?
+ path.join(__dirname, '../../../../cpp/prebuilt/amd64/libnoblur64.so') :
+ path.join(__dirname, '../lib/firefox/amd64/libnoblur64.so') ;
+
+const X_IGNORE_NO_FOCUS_LIB = 'x_ignore_nofocus.so';
+
+
+let foundBinary = null;
+let foundDevBinary = null;
+
+
+/**
+ * Checks the default Windows Firefox locations in Program Files.
+ *
+ * @param {boolean=} opt_dev Whether to find the Developer Edition.
+ * @return {!Promise<?string>} A promise for the located executable.
+ * The promise will resolve to {@code null} if Firefox was not found.
+ */
+function defaultWindowsLocation(opt_dev) {
+ var files = [
+ process.env['PROGRAMFILES'] || 'C:\\Program Files',
+ process.env['PROGRAMFILES(X86)'] || 'C:\\Program Files (x86)'
+ ].map(function(prefix) {
+ if (opt_dev) {
+ return path.join(prefix, 'Firefox Developer Edition\\firefox.exe');
+ }
+ return path.join(prefix, 'Mozilla Firefox\\firefox.exe');
+ });
+ return io.exists(files[0]).then(function(exists) {
+ return exists ? files[0] : io.exists(files[1]).then(function(exists) {
+ return exists ? files[1] : null;
+ });
+ });
+}
+
+
+/**
+ * Locates the Firefox binary for the current system.
+ *
+ * @param {boolean=} opt_dev Whether to find the Developer Edition. This only
+ * used on Windows and OSX.
+ * @return {!Promise<string>} A promise for the located binary. The promise will
+ * be rejected if Firefox cannot be located.
+ */
+function findFirefox(opt_dev) {
+ if (opt_dev && foundDevBinary) {
+ return foundDevBinary;
+ }
+
+ if (!opt_dev && foundBinary) {
+ return foundBinary;
+ }
+
+ let found;
+ if (process.platform === 'darwin') {
+ let exe = opt_dev
+ ? '/Applications/FirefoxDeveloperEdition.app/Contents/MacOS/firefox-bin'
+ : '/Applications/Firefox.app/Contents/MacOS/firefox-bin';
+ found = io.exists(exe).then(exists => exists ? exe : null);
+
+ } else if (process.platform === 'win32') {
+ found = defaultWindowsLocation(opt_dev);
+
+ } else {
+ found = Promise.resolve(io.findInPath('firefox'));
+ }
+
+ found = found.then(found => {
+ if (found) {
+ return found;
+ }
+ throw Error('Could not locate Firefox on the current system');
+ });
+
+ if (opt_dev) {
+ return foundDevBinary = found;
+ } else {
+ return foundBinary = found;
+ }
+}
+
+
+/**
+ * Copies the no focus libs into the given profile directory.
+ * @param {string} profileDir Path to the profile directory to install into.
+ * @return {!Promise<string>} The LD_LIBRARY_PATH prefix string to use
+ * for the installed libs.
+ */
+function installNoFocusLibs(profileDir) {
+ var x86 = path.join(profileDir, 'x86');
+ var amd64 = path.join(profileDir, 'amd64');
+
+ return io.mkdir(x86)
+ .then(() => copyLib(NO_FOCUS_LIB_X86, x86))
+ .then(() => io.mkdir(amd64))
+ .then(() => copyLib(NO_FOCUS_LIB_AMD64, amd64))
+ .then(function() {
+ return x86 + ':' + amd64;
+ });
+
+ function copyLib(src, dir) {
+ return io.copy(src, path.join(dir, X_IGNORE_NO_FOCUS_LIB));
+ }
+}
+
+
+/**
+ * Provides a mechanism to configure and launch Firefox in a subprocess for
+ * use with WebDriver.
+ *
+ * If created _without_ a path for the Firefox binary to use, this class will
+ * attempt to find Firefox when {@link #launch()} is called. For OSX and
+ * Windows, this class will look for Firefox in the current platform's default
+ * installation location (e.g. /Applications/Firefox.app on OSX). For all other
+ * platforms, the Firefox executable must be available on your system `PATH`.
+ *
+ * @final
+ */
+class Binary {
+ /**
+ * @param {string=} opt_exe Path to the Firefox binary to use.
+ */
+ constructor(opt_exe) {
+ /** @private {(string|undefined)} */
+ this.exe_ = opt_exe;
+
+ /** @private {!Array.<string>} */
+ this.args_ = [];
+
+ /** @private {!Object<string, string>} */
+ this.env_ = {};
+ Object.assign(this.env_, process.env, {
+ MOZ_CRASHREPORTER_DISABLE: '1',
+ MOZ_NO_REMOTE: '1',
+ NO_EM_RESTART: '1'
+ });
+
+ /** @private {boolean} */
+ this.devEdition_ = false;
+ }
+
+ /**
+ * Add arguments to the command line used to start Firefox.
+ * @param {...(string|!Array.<string>)} var_args Either the arguments to add
+ * as varargs, or the arguments as an array.
+ */
+ addArguments(var_args) {
+ for (var i = 0; i < arguments.length; i++) {
+ if (Array.isArray(arguments[i])) {
+ this.args_ = this.args_.concat(arguments[i]);
+ } else {
+ this.args_.push(arguments[i]);
+ }
+ }
+ }
+
+ /**
+ * Specifies whether to use Firefox Developer Edition instead of the normal
+ * stable channel. Setting this option has no effect if this instance was
+ * created with a path to a specific Firefox binary.
+ *
+ * This method has no effect on Unix systems where the Firefox application
+ * has the same (default) name regardless of version.
+ *
+ * @param {boolean=} opt_use Whether to use the developer edition. Defaults to
+ * true.
+ */
+ useDevEdition(opt_use) {
+ this.devEdition_ = opt_use === undefined || !!opt_use;
+ }
+
+ /**
+ * Returns a promise for the Firefox executable used by this instance. The
+ * returned promise will be immediately resolved if the user supplied an
+ * executable path when this instance was created. Otherwise, an attempt will
+ * be made to find Firefox on the current system.
+ *
+ * @return {!Promise<string>} a promise for the path to the Firefox executable
+ * used by this instance.
+ */
+ locate() {
+ return Promise.resolve(this.exe_ || findFirefox(this.devEdition_));
+ }
+
+ /**
+ * Launches Firefox and returns a promise that will be fulfilled when the
+ * process terminates.
+ * @param {string} profile Path to the profile directory to use.
+ * @return {!Promise<!exec.Command>} A promise for the handle to the started
+ * subprocess.
+ */
+ launch(profile) {
+ let env = {};
+ Object.assign(env, this.env_, {XRE_PROFILE_PATH: profile});
+
+ let args = ['-foreground'].concat(this.args_);
+
+ return this.locate().then(function(firefox) {
+ if (process.platform === 'win32' || process.platform === 'darwin') {
+ return exec(firefox, {args: args, env: env});
+ }
+ return installNoFocusLibs(profile).then(function(ldLibraryPath) {
+ env['LD_LIBRARY_PATH'] = ldLibraryPath + ':' + env['LD_LIBRARY_PATH'];
+ env['LD_PRELOAD'] = X_IGNORE_NO_FOCUS_LIB;
+ return exec(firefox, {args: args, env: env});
+ });
+ });
+ }
+
+ /**
+ * Returns a promise for the wire representation of this binary. Note: the
+ * FirefoxDriver only supports passing the path to the binary executable over
+ * the wire; all command line arguments and environment variables will be
+ * discarded.
+ *
+ * @return {!Promise<string>} A promise for this binary's wire representation.
+ */
+ [Symbols.serialize]() {
+ return this.locate();
+ }
+}
+
+
+// PUBLIC API
+
+
+exports.Binary = Binary;
+
diff --git a/node_modules/selenium-webdriver/firefox/extension.js b/node_modules/selenium-webdriver/firefox/extension.js
new file mode 100644
index 000000000..60abb06b8
--- /dev/null
+++ b/node_modules/selenium-webdriver/firefox/extension.js
@@ -0,0 +1,187 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+/** @fileoverview Utilities for working with Firefox extensions. */
+
+'use strict';
+
+const AdmZip = require('adm-zip'),
+ fs = require('fs'),
+ path = require('path'),
+ xml = require('xml2js');
+
+const io = require('../io');
+
+
+/**
+ * Thrown when there an add-on is malformed.
+ */
+class AddonFormatError extends Error {
+ /** @param {string} msg The error message. */
+ constructor(msg) {
+ super(msg);
+ /** @override */
+ this.name = this.constructor.name;
+ }
+}
+
+
+
+/**
+ * Installs an extension to the given directory.
+ * @param {string} extension Path to the extension to install, as either a xpi
+ * file or a directory.
+ * @param {string} dir Path to the directory to install the extension in.
+ * @return {!Promise<string>} A promise for the add-on ID once
+ * installed.
+ */
+function install(extension, dir) {
+ return getDetails(extension).then(function(details) {
+ var dst = path.join(dir, details.id);
+ if (extension.slice(-4) === '.xpi') {
+ if (!details.unpack) {
+ return io.copy(extension, dst + '.xpi').then(() => details.id);
+ } else {
+ return Promise.resolve().then(function() {
+ // TODO: find an async library for inflating a zip archive.
+ new AdmZip(extension).extractAllTo(dst, true);
+ return details.id;
+ });
+ }
+ } else {
+ return io.copyDir(extension, dst).then(() => details.id);
+ }
+ });
+}
+
+
+/**
+ * Describes a Firefox add-on.
+ * @typedef {{id: string, name: string, version: string, unpack: boolean}}
+ */
+var AddonDetails;
+
+/** @typedef {{$: !Object<string, string>}} */
+var RdfRoot;
+
+
+
+/**
+ * Extracts the details needed to install an add-on.
+ * @param {string} addonPath Path to the extension directory.
+ * @return {!Promise<!AddonDetails>} A promise for the add-on details.
+ */
+function getDetails(addonPath) {
+ return readManifest(addonPath).then(function(doc) {
+ var em = getNamespaceId(doc, 'http://www.mozilla.org/2004/em-rdf#');
+ var rdf = getNamespaceId(
+ doc, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#');
+
+ var description = doc[rdf + 'RDF'][rdf + 'Description'][0];
+ var details = {
+ id: getNodeText(description, em + 'id'),
+ name: getNodeText(description, em + 'name'),
+ version: getNodeText(description, em + 'version'),
+ unpack: getNodeText(description, em + 'unpack') || false
+ };
+
+ if (typeof details.unpack === 'string') {
+ details.unpack = details.unpack.toLowerCase() === 'true';
+ }
+
+ if (!details.id) {
+ throw new AddonFormatError('Could not find add-on ID for ' + addonPath);
+ }
+
+ return details;
+ });
+
+ function getNodeText(node, name) {
+ return node[name] && node[name][0] || '';
+ }
+
+ function getNamespaceId(doc, url) {
+ var keys = Object.keys(doc);
+ if (keys.length !== 1) {
+ throw new AddonFormatError('Malformed manifest for add-on ' + addonPath);
+ }
+
+ var namespaces = /** @type {!RdfRoot} */(doc[keys[0]]).$;
+ var id = '';
+ Object.keys(namespaces).some(function(ns) {
+ if (namespaces[ns] !== url) {
+ return false;
+ }
+
+ if (ns.indexOf(':') != -1) {
+ id = ns.split(':')[1] + ':';
+ }
+ return true;
+ });
+ return id;
+ }
+}
+
+
+/**
+ * Reads the manifest for a Firefox add-on.
+ * @param {string} addonPath Path to a Firefox add-on as a xpi or an extension.
+ * @return {!Promise<!Object>} A promise for the parsed manifest.
+ */
+function readManifest(addonPath) {
+ var manifest;
+
+ if (addonPath.slice(-4) === '.xpi') {
+ manifest = new Promise((resolve, reject) => {
+ let zip = new AdmZip(addonPath);
+
+ if (!zip.getEntry('install.rdf')) {
+ reject(new AddonFormatError(
+ 'Could not find install.rdf in ' + addonPath));
+ return;
+ }
+
+ zip.readAsTextAsync('install.rdf', resolve);
+ });
+ } else {
+ manifest = io.stat(addonPath).then(function(stats) {
+ if (!stats.isDirectory()) {
+ throw Error(
+ 'Add-on path is niether a xpi nor a directory: ' + addonPath);
+ }
+ return io.read(path.join(addonPath, 'install.rdf'));
+ });
+ }
+
+ return manifest.then(function(content) {
+ return new Promise((resolve, reject) => {
+ xml.parseString(content, (err, data) => {
+ if (err) {
+ reject(err);
+ } else {
+ resolve(data);
+ }
+ });
+ });
+ });
+}
+
+
+// PUBLIC API
+
+
+exports.install = install;
diff --git a/node_modules/selenium-webdriver/firefox/index.js b/node_modules/selenium-webdriver/firefox/index.js
new file mode 100644
index 000000000..0adc97093
--- /dev/null
+++ b/node_modules/selenium-webdriver/firefox/index.js
@@ -0,0 +1,679 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+/**
+ * @fileoverview Defines the {@linkplain Driver WebDriver} client for Firefox.
+ * Before using this module, you must download the latest
+ * [geckodriver release] and ensure it can be found on your system [PATH].
+ *
+ * Each FirefoxDriver instance will be created with an anonymous profile,
+ * ensuring browser historys do not share session data (cookies, history, cache,
+ * offline storage, etc.)
+ *
+ * __Customizing the Firefox Profile__
+ *
+ * The {@link Profile} class may be used to configure the browser profile used
+ * with WebDriver, with functions to install additional
+ * {@linkplain Profile#addExtension extensions}, configure browser
+ * {@linkplain Profile#setPreference preferences}, and more. For example, you
+ * may wish to include Firebug:
+ *
+ * var firefox = require('selenium-webdriver/firefox');
+ *
+ * var profile = new firefox.Profile();
+ * profile.addExtension('/path/to/firebug.xpi');
+ * profile.setPreference('extensions.firebug.showChromeErrors', true);
+ *
+ * var options = new firefox.Options().setProfile(profile);
+ * var driver = new firefox.Driver(options);
+ *
+ * The {@link Profile} class may also be used to configure WebDriver based on a
+ * pre-existing browser profile:
+ *
+ * var profile = new firefox.Profile(
+ * '/usr/local/home/bob/.mozilla/firefox/3fgog75h.testing');
+ * var options = new firefox.Options().setProfile(profile);
+ * var driver = new firefox.Driver(options);
+ *
+ * The FirefoxDriver will _never_ modify a pre-existing profile; instead it will
+ * create a copy for it to modify. By extension, there are certain browser
+ * preferences that are required for WebDriver to function properly and they
+ * will always be overwritten.
+ *
+ * __Using a Custom Firefox Binary__
+ *
+ * On Windows and OSX, the FirefoxDriver will search for Firefox in its
+ * default installation location:
+ *
+ * * Windows: C:\Program Files and C:\Program Files (x86).
+ * * Mac OS X: /Applications/Firefox.app
+ *
+ * For Linux, Firefox will be located on the PATH: `$(where firefox)`.
+ *
+ * You can configure WebDriver to start use a custom Firefox installation with
+ * the {@link Binary} class:
+ *
+ * var firefox = require('selenium-webdriver/firefox');
+ * var binary = new firefox.Binary('/my/firefox/install/dir/firefox-bin');
+ * var options = new firefox.Options().setBinary(binary);
+ * var driver = new firefox.Driver(options);
+ *
+ * __Remote Testing__
+ *
+ * You may customize the Firefox binary and profile when running against a
+ * remote Selenium server. Your custom profile will be packaged as a zip and
+ * transfered to the remote host for use. The profile will be transferred
+ * _once for each new session_. The performance impact should be minimal if
+ * you've only configured a few extra browser preferences. If you have a large
+ * profile with several extensions, you should consider installing it on the
+ * remote host and defining its path via the {@link Options} class. Custom
+ * binaries are never copied to remote machines and must be referenced by
+ * installation path.
+ *
+ * var options = new firefox.Options()
+ * .setProfile('/profile/path/on/remote/host')
+ * .setBinary('/install/dir/on/remote/host/firefox-bin');
+ *
+ * var driver = new (require('selenium-webdriver')).Builder()
+ * .forBrowser('firefox')
+ * .usingServer('http://127.0.0.1:4444/wd/hub')
+ * .setFirefoxOptions(options)
+ * .build();
+ *
+ * __Testing Older Versions of Firefox__
+ *
+ * To test versions of Firefox prior to Firefox 47, you must disable the use of
+ * the geckodriver using the {@link Options} class.
+ *
+ * var options = new firefox.Options().useGeckoDriver(false);
+ * var driver = new firefox.Driver(options);
+ *
+ * Alternatively, you may disable the geckodriver at runtime by setting the
+ * environment variable `SELENIUM_MARIONETTE=false`.
+ *
+ * [geckodriver release]: https://github.com/mozilla/geckodriver/releases/
+ * [PATH]: http://en.wikipedia.org/wiki/PATH_%28variable%29
+ */
+
+'use strict';
+
+const url = require('url');
+
+const Binary = require('./binary').Binary,
+ Profile = require('./profile').Profile,
+ decodeProfile = require('./profile').decode,
+ http = require('../http'),
+ httpUtil = require('../http/util'),
+ io = require('../io'),
+ capabilities = require('../lib/capabilities'),
+ command = require('../lib/command'),
+ logging = require('../lib/logging'),
+ promise = require('../lib/promise'),
+ webdriver = require('../lib/webdriver'),
+ net = require('../net'),
+ portprober = require('../net/portprober'),
+ remote = require('../remote');
+
+
+/**
+ * Firefox-specific capability keys. Users should use the {@linkplain Options}
+ * class instead of referencing these keys directly. _These keys are considered
+ * implementation details and may be removed or changed at any time._
+ *
+ * @enum {string}
+ */
+const Capability = {
+ /**
+ * Defines the Firefox binary to use. May be set to either a
+ * {@linkplain Binary} instance, or a string path to the Firefox executable.
+ */
+ BINARY: 'firefox_binary',
+
+ /**
+ * Specifies whether to use Mozilla's Marionette, or the legacy FirefoxDriver
+ * from the Selenium project. Defaults to false.
+ */
+ MARIONETTE: 'marionette',
+
+ /**
+ * Defines the Firefox profile to use. May be set to either a
+ * {@linkplain Profile} instance, or to a base-64 encoded zip of a profile
+ * directory.
+ */
+ PROFILE: 'firefox_profile'
+};
+
+
+/**
+ * Configuration options for the FirefoxDriver.
+ */
+class Options {
+ constructor() {
+ /** @private {Profile} */
+ this.profile_ = null;
+
+ /** @private {Binary} */
+ this.binary_ = null;
+
+ /** @private {logging.Preferences} */
+ this.logPrefs_ = null;
+
+ /** @private {?capabilities.ProxyConfig} */
+ this.proxy_ = null;
+
+ /** @private {boolean} */
+ this.marionette_ = true;
+ }
+
+ /**
+ * Sets the profile to use. The profile may be specified as a
+ * {@link Profile} object or as the path to an existing Firefox profile to use
+ * as a template.
+ *
+ * @param {(string|!Profile)} profile The profile to use.
+ * @return {!Options} A self reference.
+ */
+ setProfile(profile) {
+ if (typeof profile === 'string') {
+ profile = new Profile(profile);
+ }
+ this.profile_ = profile;
+ return this;
+ }
+
+ /**
+ * Sets the binary to use. The binary may be specified as the path to a Firefox
+ * executable, or as a {@link Binary} object.
+ *
+ * @param {(string|!Binary)} binary The binary to use.
+ * @return {!Options} A self reference.
+ */
+ setBinary(binary) {
+ if (typeof binary === 'string') {
+ binary = new Binary(binary);
+ }
+ this.binary_ = binary;
+ return this;
+ }
+
+ /**
+ * Sets the logging preferences for the new session.
+ * @param {logging.Preferences} prefs The logging preferences.
+ * @return {!Options} A self reference.
+ */
+ setLoggingPreferences(prefs) {
+ this.logPrefs_ = prefs;
+ return this;
+ }
+
+ /**
+ * Sets the proxy to use.
+ *
+ * @param {capabilities.ProxyConfig} proxy The proxy configuration to use.
+ * @return {!Options} A self reference.
+ */
+ setProxy(proxy) {
+ this.proxy_ = proxy;
+ return this;
+ }
+
+ /**
+ * Sets whether to use Mozilla's geckodriver to drive the browser. This option
+ * is enabled by default and required for Firefox 47+.
+ *
+ * @param {boolean} enable Whether to enable the geckodriver.
+ * @see https://github.com/mozilla/geckodriver
+ */
+ useGeckoDriver(enable) {
+ this.marionette_ = enable;
+ return this;
+ }
+
+ /**
+ * Converts these options to a {@link capabilities.Capabilities} instance.
+ *
+ * @return {!capabilities.Capabilities} A new capabilities object.
+ */
+ toCapabilities() {
+ var caps = capabilities.Capabilities.firefox();
+ if (this.logPrefs_) {
+ caps.set(capabilities.Capability.LOGGING_PREFS, this.logPrefs_);
+ }
+ if (this.proxy_) {
+ caps.set(capabilities.Capability.PROXY, this.proxy_);
+ }
+ if (this.binary_) {
+ caps.set(Capability.BINARY, this.binary_);
+ }
+ if (this.profile_) {
+ caps.set(Capability.PROFILE, this.profile_);
+ }
+ caps.set(Capability.MARIONETTE, this.marionette_);
+ return caps;
+ }
+}
+
+
+/**
+ * Enum of available command contexts.
+ *
+ * Command contexts are specific to Marionette, and may be used with the
+ * {@link #context=} method. Contexts allow you to direct all subsequent
+ * commands to either "content" (default) or "chrome". The latter gives
+ * you elevated security permissions.
+ *
+ * @enum {string}
+ */
+const Context = {
+ CONTENT: "content",
+ CHROME: "chrome",
+};
+
+
+const GECKO_DRIVER_EXE =
+ process.platform === 'win32' ? 'geckodriver.exe' : 'geckodriver';
+
+
+/**
+ * @return {string} .
+ * @throws {Error}
+ */
+function findGeckoDriver() {
+ let exe = io.findInPath(GECKO_DRIVER_EXE, true);
+ if (!exe) {
+ throw Error(
+ 'The ' + GECKO_DRIVER_EXE + ' executable could not be found on the current ' +
+ 'PATH. Please download the latest version from ' +
+ 'https://github.com/mozilla/geckodriver/releases/' +
+ 'WebDriver and ensure it can be found on your PATH.');
+ }
+ return exe;
+}
+
+
+/**
+ * @param {(Profile|string)} profile The profile to prepare.
+ * @param {number} port The port the FirefoxDriver should listen on.
+ * @return {!Promise<string>} a promise for the path to the profile directory.
+ */
+function prepareProfile(profile, port) {
+ if (typeof profile === 'string') {
+ return decodeProfile(/** @type {string} */(profile)).then(dir => {
+ profile = new Profile(dir);
+ profile.setPreference('webdriver_firefox_port', port);
+ return profile.writeToDisk();
+ });
+ }
+
+ profile = profile || new Profile;
+ profile.setPreference('webdriver_firefox_port', port);
+ return profile.writeToDisk();
+}
+
+
+function normalizeProxyConfiguration(config) {
+ if ('manual' === config.proxyType) {
+ if (config.ftpProxy && !config.ftpProxyPort) {
+ let hostAndPort = net.splitHostAndPort(config.ftpProxy);
+ config.ftpProxy = hostAndPort.host;
+ config.ftpProxyPort = hostAndPort.port;
+ }
+
+ if (config.httpProxy && !config.httpProxyPort) {
+ let hostAndPort = net.splitHostAndPort(config.httpProxy);
+ config.httpProxy = hostAndPort.host;
+ config.httpProxyPort = hostAndPort.port;
+ }
+
+ if (config.sslProxy && !config.sslProxyPort) {
+ let hostAndPort = net.splitHostAndPort(config.sslProxy);
+ config.sslProxy = hostAndPort.host;
+ config.sslProxyPort = hostAndPort.port;
+ }
+
+ if (config.socksProxy && !config.socksProxyPort) {
+ let hostAndPort = net.splitHostAndPort(config.socksProxy);
+ config.socksProxy = hostAndPort.host;
+ config.socksProxyPort = hostAndPort.port;
+ }
+ } else if ('pac' === config.proxyType) {
+ if (config.proxyAutoconfigUrl && !config.pacUrl) {
+ config.pacUrl = config.proxyAutoconfigUrl;
+ }
+ }
+ return config;
+}
+
+
+/** @enum {string} */
+const ExtensionCommand = {
+ GET_CONTEXT: 'getContext',
+ SET_CONTEXT: 'setContext',
+};
+
+
+/**
+ * Creates a command executor with support for Marionette's custom commands.
+ * @param {!Promise<string>} serverUrl The server's URL.
+ * @return {!command.Executor} The new command executor.
+ */
+function createExecutor(serverUrl) {
+ let client = serverUrl.then(url => new http.HttpClient(url));
+ let executor = new http.Executor(client);
+ configureExecutor(executor);
+ return executor;
+}
+
+
+/**
+ * Configures the given executor with Firefox-specific commands.
+ * @param {!http.Executor} executor the executor to configure.
+ */
+function configureExecutor(executor) {
+ executor.defineCommand(
+ ExtensionCommand.GET_CONTEXT,
+ 'GET',
+ '/session/:sessionId/moz/context');
+
+ executor.defineCommand(
+ ExtensionCommand.SET_CONTEXT,
+ 'POST',
+ '/session/:sessionId/moz/context');
+}
+
+
+/**
+ * Creates {@link selenium-webdriver/remote.DriverService} instances that manage
+ * a [geckodriver](https://github.com/mozilla/geckodriver) server in a child
+ * process.
+ */
+class ServiceBuilder extends remote.DriverService.Builder {
+ /**
+ * @param {string=} opt_exe Path to the server executable to use. If omitted,
+ * the builder will attempt to locate the geckodriver on the system PATH.
+ */
+ constructor(opt_exe) {
+ super(opt_exe || findGeckoDriver());
+ this.setLoopback(true); // Required.
+ }
+
+ /**
+ * Enables verbose logging.
+ *
+ * @param {boolean=} opt_trace Whether to enable trace-level logging. By
+ * default, only debug logging is enabled.
+ * @return {!ServiceBuilder} A self reference.
+ */
+ enableVerboseLogging(opt_trace) {
+ return this.addArguments(opt_trace ? '-vv' : '-v');
+ }
+
+ /**
+ * Sets the path to the executable Firefox binary that the geckodriver should
+ * use. If this method is not called, this builder will attempt to locate
+ * Firefox in the default installation location for the current platform.
+ *
+ * @param {(string|!Binary)} binary Path to the executable Firefox binary to use.
+ * @return {!ServiceBuilder} A self reference.
+ * @see Binary#locate()
+ */
+ setFirefoxBinary(binary) {
+ let exe = typeof binary === 'string'
+ ? Promise.resolve(binary) : binary.locate();
+ return this.addArguments('-b', exe);
+ }
+}
+
+
+/**
+ * @typedef {{driver: !webdriver.WebDriver, onQuit: function()}}
+ */
+var DriverSpec;
+
+
+/**
+ * @param {(http.Executor|remote.DriverService|undefined)} executor
+ * @param {!capabilities.Capabilities} caps
+ * @param {Profile} profile
+ * @param {Binary} binary
+ * @param {(promise.ControlFlow|undefined)} flow
+ * @return {DriverSpec}
+ */
+function createGeckoDriver(
+ executor, caps, profile, binary, flow) {
+ if (profile) {
+ caps.set(Capability.PROFILE, profile.encode());
+ }
+
+ let sessionCaps = caps;
+ if (caps.has(capabilities.Capability.PROXY)) {
+ let proxy = normalizeProxyConfiguration(
+ caps.get(capabilities.Capability.PROXY));
+
+ // Marionette requires proxy settings to be specified as required
+ // capabilities. See mozilla/geckodriver#97
+ let required = new capabilities.Capabilities()
+ .set(capabilities.Capability.PROXY, proxy);
+
+ caps.delete(capabilities.Capability.PROXY);
+ sessionCaps = {required, desired: caps};
+ }
+
+ /** @type {(command.Executor|undefined)} */
+ let cmdExecutor;
+ let onQuit = function() {};
+
+ if (executor instanceof http.Executor) {
+ configureExecutor(executor);
+ cmdExecutor = executor;
+ } else if (executor instanceof remote.DriverService) {
+ cmdExecutor = createExecutor(executor.start());
+ onQuit = () => executor.kill();
+ } else {
+ let builder = new ServiceBuilder();
+ if (binary) {
+ builder.setFirefoxBinary(binary);
+ }
+ let service = builder.build();
+ cmdExecutor = createExecutor(service.start());
+ onQuit = () => service.kill();
+ }
+
+ let driver =
+ webdriver.WebDriver.createSession(
+ /** @type {!http.Executor} */(cmdExecutor),
+ sessionCaps,
+ flow);
+ return {driver, onQuit};
+}
+
+
+/**
+ * @param {!capabilities.Capabilities} caps
+ * @param {Profile} profile
+ * @param {!Binary} binary
+ * @param {(promise.ControlFlow|undefined)} flow
+ * @return {DriverSpec}
+ */
+function createLegacyDriver(caps, profile, binary, flow) {
+ profile = profile || new Profile;
+
+ let freePort = portprober.findFreePort();
+ let preparedProfile =
+ freePort.then(port => prepareProfile(profile, port));
+ let command = preparedProfile.then(dir => binary.launch(dir));
+
+ let serverUrl = command.then(() => freePort)
+ .then(function(/** number */port) {
+ let serverUrl = url.format({
+ protocol: 'http',
+ hostname: net.getLoopbackAddress(),
+ port: port + '',
+ pathname: '/hub'
+ });
+ let ready = httpUtil.waitForServer(serverUrl, 45 * 1000);
+ return ready.then(() => serverUrl);
+ });
+
+ let onQuit = function() {
+ return command.then(command => {
+ command.kill();
+ return preparedProfile.then(io.rmDir)
+ .then(() => command.result(),
+ () => command.result());
+ });
+ };
+
+ let executor = createExecutor(serverUrl);
+ let driver = webdriver.WebDriver.createSession(executor, caps, flow);
+ return {driver, onQuit};
+}
+
+
+/**
+ * A WebDriver client for Firefox.
+ */
+class Driver extends webdriver.WebDriver {
+ /**
+ * @param {(Options|capabilities.Capabilities|Object)=} opt_config The
+ * configuration options for this driver, specified as either an
+ * {@link Options} or {@link capabilities.Capabilities}, or as a raw hash
+ * object.
+ * @param {(http.Executor|remote.DriverService)=} opt_executor Either a
+ * pre-configured command executor to use for communicating with an
+ * externally managed remote end (which is assumed to already be running),
+ * or the `DriverService` to use to start the geckodriver in a child
+ * process.
+ *
+ * If an executor is provided, care should e taken not to use reuse it with
+ * other clients as its internal command mappings will be updated to support
+ * Firefox-specific commands.
+ *
+ * _This parameter may only be used with Mozilla's GeckoDriver._
+ *
+ * @param {promise.ControlFlow=} opt_flow The flow to
+ * schedule commands through. Defaults to the active flow object.
+ * @throws {Error} If a custom command executor is provided and the driver is
+ * configured to use the legacy FirefoxDriver from the Selenium project.
+ */
+ constructor(opt_config, opt_executor, opt_flow) {
+ let caps;
+ if (opt_config instanceof Options) {
+ caps = opt_config.toCapabilities();
+ } else {
+ caps = new capabilities.Capabilities(opt_config);
+ }
+
+ let hasBinary = caps.has(Capability.BINARY);
+ let binary = caps.get(Capability.BINARY) || new Binary();
+ caps.delete(Capability.BINARY);
+ if (typeof binary === 'string') {
+ binary = new Binary(binary);
+ }
+
+ let profile;
+ if (caps.has(Capability.PROFILE)) {
+ profile = caps.get(Capability.PROFILE);
+ caps.delete(Capability.PROFILE);
+ }
+
+ let serverUrl, onQuit;
+
+ // Users must now explicitly disable marionette to use the legacy
+ // FirefoxDriver.
+ let noMarionette =
+ caps.get(Capability.MARIONETTE) === false
+ || /^0|false$/i.test(process.env['SELENIUM_MARIONETTE']);
+ let useMarionette = !noMarionette;
+
+ let spec;
+ if (useMarionette) {
+ spec = createGeckoDriver(
+ opt_executor,
+ caps,
+ profile,
+ hasBinary ? binary : null,
+ opt_flow);
+ } else {
+ if (opt_executor) {
+ throw Error('You may not use a custom command executor with the legacy'
+ + ' FirefoxDriver');
+ }
+ spec = createLegacyDriver(caps, profile, binary, opt_flow);
+ }
+
+ super(spec.driver.getSession(),
+ spec.driver.getExecutor(),
+ spec.driver.controlFlow());
+
+ /** @override */
+ this.quit = () => {
+ return super.quit().finally(spec.onQuit);
+ };
+ }
+
+ /**
+ * This function is a no-op as file detectors are not supported by this
+ * implementation.
+ * @override
+ */
+ setFileDetector() {
+ }
+
+ /**
+ * Get the context that is currently in effect.
+ *
+ * @return {!promise.Promise<Context>} Current context.
+ */
+ getContext() {
+ return this.schedule(
+ new command.Command(ExtensionCommand.GET_CONTEXT),
+ 'get WebDriver.context');
+ }
+
+ /**
+ * Changes target context for commands between chrome- and content.
+ *
+ * Changing the current context has a stateful impact on all subsequent
+ * commands. The {@link Context.CONTENT} context has normal web
+ * platform document permissions, as if you would evaluate arbitrary
+ * JavaScript. The {@link Context.CHROME} context gets elevated
+ * permissions that lets you manipulate the browser chrome itself,
+ * with full access to the XUL toolkit.
+ *
+ * Use your powers wisely.
+ *
+ * @param {!promise.Promise<void>} ctx The context to switch to.
+ */
+ setContext(ctx) {
+ return this.schedule(
+ new command.Command(ExtensionCommand.SET_CONTEXT)
+ .setParameter("context", ctx),
+ 'set WebDriver.context');
+ }
+}
+
+
+// PUBLIC API
+
+
+exports.Binary = Binary;
+exports.Context = Context;
+exports.Driver = Driver;
+exports.Options = Options;
+exports.Profile = Profile;
+exports.ServiceBuilder = ServiceBuilder;
diff --git a/node_modules/selenium-webdriver/firefox/profile.js b/node_modules/selenium-webdriver/firefox/profile.js
new file mode 100644
index 000000000..b6b39086c
--- /dev/null
+++ b/node_modules/selenium-webdriver/firefox/profile.js
@@ -0,0 +1,409 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+/**
+ * @fileoverview Profile management module. This module is considered internal;
+ * users should use {@link selenium-webdriver/firefox}.
+ */
+
+'use strict';
+
+const AdmZip = require('adm-zip'),
+ fs = require('fs'),
+ path = require('path'),
+ vm = require('vm');
+
+const isDevMode = require('../lib/devmode'),
+ Symbols = require('../lib/symbols'),
+ io = require('../io'),
+ extension = require('./extension');
+
+
+/** @const */
+const WEBDRIVER_PREFERENCES_PATH = isDevMode
+ ? path.join(__dirname, '../../../firefox-driver/webdriver.json')
+ : path.join(__dirname, '../lib/firefox/webdriver.json');
+
+/** @const */
+const WEBDRIVER_EXTENSION_PATH = isDevMode
+ ? path.join(__dirname,
+ '../../../../build/javascript/firefox-driver/webdriver.xpi')
+ : path.join(__dirname, '../lib/firefox/webdriver.xpi');
+
+/** @const */
+const WEBDRIVER_EXTENSION_NAME = 'fxdriver@googlecode.com';
+
+
+
+/** @type {Object} */
+var defaultPreferences = null;
+
+/**
+ * Synchronously loads the default preferences used for the FirefoxDriver.
+ * @return {!Object} The default preferences JSON object.
+ */
+function getDefaultPreferences() {
+ if (!defaultPreferences) {
+ var contents = /** @type {string} */(
+ fs.readFileSync(WEBDRIVER_PREFERENCES_PATH, 'utf8'));
+ defaultPreferences = /** @type {!Object} */(JSON.parse(contents));
+ }
+ return defaultPreferences;
+}
+
+
+/**
+ * Parses a user.js file in a Firefox profile directory.
+ * @param {string} f Path to the file to parse.
+ * @return {!Promise<!Object>} A promise for the parsed preferences as
+ * a JSON object. If the file does not exist, an empty object will be
+ * returned.
+ */
+function loadUserPrefs(f) {
+ return io.read(f).then(
+ function onSuccess(contents) {
+ var prefs = {};
+ var context = vm.createContext({
+ 'user_pref': function(key, value) {
+ prefs[key] = value;
+ }
+ });
+ vm.runInContext(contents.toString(), context, f);
+ return prefs;
+ },
+ function onError(err) {
+ if (err && err.code === 'ENOENT') {
+ return {};
+ }
+ throw err;
+ });
+}
+
+
+
+/**
+ * @param {!Object} prefs The default preferences to write. Will be
+ * overridden by user.js preferences in the template directory and the
+ * frozen preferences required by WebDriver.
+ * @param {string} dir Path to the directory write the file to.
+ * @return {!Promise<string>} A promise for the profile directory,
+ * to be fulfilled when user preferences have been written.
+ */
+function writeUserPrefs(prefs, dir) {
+ var userPrefs = path.join(dir, 'user.js');
+ return loadUserPrefs(userPrefs).then(function(overrides) {
+ Object.assign(prefs, overrides);
+ Object.assign(prefs, getDefaultPreferences()['frozen']);
+
+ var contents = Object.keys(prefs).map(function(key) {
+ return 'user_pref(' + JSON.stringify(key) + ', ' +
+ JSON.stringify(prefs[key]) + ');';
+ }).join('\n');
+
+ return new Promise((resolve, reject) => {
+ fs.writeFile(userPrefs, contents, function(err) {
+ err && reject(err) || resolve(dir);
+ });
+ });
+ });
+};
+
+
+/**
+ * Installs a group of extensions in the given profile directory. If the
+ * WebDriver extension is not included in this set, the default version
+ * bundled with this package will be installed.
+ * @param {!Array.<string>} extensions The extensions to install, as a
+ * path to an unpacked extension directory or a path to a xpi file.
+ * @param {string} dir The profile directory to install to.
+ * @param {boolean=} opt_excludeWebDriverExt Whether to skip installation of
+ * the default WebDriver extension.
+ * @return {!Promise<string>} A promise for the main profile directory
+ * once all extensions have been installed.
+ */
+function installExtensions(extensions, dir, opt_excludeWebDriverExt) {
+ var hasWebDriver = !!opt_excludeWebDriverExt;
+ var next = 0;
+ var extensionDir = path.join(dir, 'extensions');
+
+ return new Promise(function(fulfill, reject) {
+ io.mkdir(extensionDir).then(installNext, reject);
+
+ function installNext() {
+ if (next >= extensions.length) {
+ if (hasWebDriver) {
+ fulfill(dir);
+ } else {
+ install(WEBDRIVER_EXTENSION_PATH);
+ }
+ } else {
+ install(extensions[next++]);
+ }
+ }
+
+ function install(ext) {
+ extension.install(ext, extensionDir).then(function(id) {
+ hasWebDriver = hasWebDriver || (id === WEBDRIVER_EXTENSION_NAME);
+ installNext();
+ }, reject);
+ }
+ });
+}
+
+
+/**
+ * Decodes a base64 encoded profile.
+ * @param {string} data The base64 encoded string.
+ * @return {!Promise<string>} A promise for the path to the decoded profile
+ * directory.
+ */
+function decode(data) {
+ return io.tmpFile().then(function(file) {
+ var buf = new Buffer(data, 'base64');
+ return io.write(file, buf)
+ .then(io.tmpDir)
+ .then(function(dir) {
+ var zip = new AdmZip(file);
+ zip.extractAllTo(dir); // Sync only? Why?? :-(
+ return dir;
+ });
+ });
+}
+
+
+
+/**
+ * Models a Firefox profile directory for use with the FirefoxDriver. The
+ * {@code Profile} directory uses an in-memory model until
+ * {@link #writeToDisk} or {@link #encode} is called.
+ */
+class Profile {
+ /**
+ * @param {string=} opt_dir Path to an existing Firefox profile directory to
+ * use a template for this profile. If not specified, a blank profile will
+ * be used.
+ */
+ constructor(opt_dir) {
+ /** @private {!Object} */
+ this.preferences_ = {};
+
+ Object.assign(this.preferences_, getDefaultPreferences()['mutable']);
+ Object.assign(this.preferences_, getDefaultPreferences()['frozen']);
+
+ /** @private {boolean} */
+ this.nativeEventsEnabled_ = true;
+
+ /** @private {(string|undefined)} */
+ this.template_ = opt_dir;
+
+ /** @private {number} */
+ this.port_ = 0;
+
+ /** @private {!Array<string>} */
+ this.extensions_ = [];
+ }
+
+ /**
+ * Registers an extension to be included with this profile.
+ * @param {string} extension Path to the extension to include, as either an
+ * unpacked extension directory or the path to a xpi file.
+ */
+ addExtension(extension) {
+ this.extensions_.push(extension);
+ }
+
+ /**
+ * Sets a desired preference for this profile.
+ * @param {string} key The preference key.
+ * @param {(string|number|boolean)} value The preference value.
+ * @throws {Error} If attempting to set a frozen preference.
+ */
+ setPreference(key, value) {
+ var frozen = getDefaultPreferences()['frozen'];
+ if (frozen.hasOwnProperty(key) && frozen[key] !== value) {
+ throw Error('You may not set ' + key + '=' + JSON.stringify(value)
+ + '; value is frozen for proper WebDriver functionality ('
+ + key + '=' + JSON.stringify(frozen[key]) + ')');
+ }
+ this.preferences_[key] = value;
+ }
+
+ /**
+ * Returns the currently configured value of a profile preference. This does
+ * not include any defaults defined in the profile's template directory user.js
+ * file (if a template were specified on construction).
+ * @param {string} key The desired preference.
+ * @return {(string|number|boolean|undefined)} The current value of the
+ * requested preference.
+ */
+ getPreference(key) {
+ return this.preferences_[key];
+ }
+
+ /**
+ * Specifies which host the driver should listen for commands on. If not
+ * specified, the driver will default to "localhost". This option should be
+ * specified when "localhost" is not mapped to the loopback address
+ * (127.0.0.1) in `/etc/hosts`.
+ *
+ * @param {string} host the host the driver should listen for commands on
+ */
+ setHost(host) {
+ this.preferences_['webdriver_firefox_allowed_hosts'] = host;
+ }
+
+ /**
+ * @return {number} The port this profile is currently configured to use, or
+ * 0 if the port will be selected at random when the profile is written
+ * to disk.
+ */
+ getPort() {
+ return this.port_;
+ }
+
+ /**
+ * Sets the port to use for the WebDriver extension loaded by this profile.
+ * @param {number} port The desired port, or 0 to use any free port.
+ */
+ setPort(port) {
+ this.port_ = port;
+ }
+
+ /**
+ * @return {boolean} Whether the FirefoxDriver is configured to automatically
+ * accept untrusted SSL certificates.
+ */
+ acceptUntrustedCerts() {
+ return !!this.preferences_['webdriver_accept_untrusted_certs'];
+ }
+
+ /**
+ * Sets whether the FirefoxDriver should automatically accept untrusted SSL
+ * certificates.
+ * @param {boolean} value .
+ */
+ setAcceptUntrustedCerts(value) {
+ this.preferences_['webdriver_accept_untrusted_certs'] = !!value;
+ }
+
+ /**
+ * Sets whether to assume untrusted certificates come from untrusted issuers.
+ * @param {boolean} value .
+ */
+ setAssumeUntrustedCertIssuer(value) {
+ this.preferences_['webdriver_assume_untrusted_issuer'] = !!value;
+ }
+
+ /**
+ * @return {boolean} Whether to assume untrusted certs come from untrusted
+ * issuers.
+ */
+ assumeUntrustedCertIssuer() {
+ return !!this.preferences_['webdriver_assume_untrusted_issuer'];
+ }
+
+ /**
+ * Sets whether to use native events with this profile.
+ * @param {boolean} enabled .
+ */
+ setNativeEventsEnabled(enabled) {
+ this.nativeEventsEnabled_ = enabled;
+ }
+
+ /**
+ * Returns whether native events are enabled in this profile.
+ * @return {boolean} .
+ */
+ nativeEventsEnabled() {
+ return this.nativeEventsEnabled_;
+ }
+
+ /**
+ * Writes this profile to disk.
+ * @param {boolean=} opt_excludeWebDriverExt Whether to exclude the WebDriver
+ * extension from the generated profile. Used to reduce the size of an
+ * {@link #encode() encoded profile} since the server will always install
+ * the extension itself.
+ * @return {!Promise<string>} A promise for the path to the new profile
+ * directory.
+ */
+ writeToDisk(opt_excludeWebDriverExt) {
+ var profileDir = io.tmpDir();
+ if (this.template_) {
+ profileDir = profileDir.then(function(dir) {
+ return io.copyDir(
+ /** @type {string} */(this.template_),
+ dir, /(parent\.lock|lock|\.parentlock)/);
+ }.bind(this));
+ }
+
+ // Freeze preferences for async operations.
+ var prefs = {};
+ Object.assign(prefs, this.preferences_);
+
+ // Freeze extensions for async operations.
+ var extensions = this.extensions_.concat();
+
+ return profileDir.then(function(dir) {
+ return writeUserPrefs(prefs, dir);
+ }).then(function(dir) {
+ return installExtensions(extensions, dir, !!opt_excludeWebDriverExt);
+ });
+ }
+
+ /**
+ * Write profile to disk, compress its containing directory, and return
+ * it as a Base64 encoded string.
+ *
+ * @return {!Promise<string>} A promise for the encoded profile as
+ * Base64 string.
+ *
+ */
+ encode() {
+ return this.writeToDisk(true).then(function(dir) {
+ var zip = new AdmZip();
+ zip.addLocalFolder(dir, '');
+ // Stored compression, see https://en.wikipedia.org/wiki/Zip_(file_format)
+ zip.getEntries().forEach(function(entry) {
+ entry.header.method = 0;
+ });
+
+ return io.tmpFile().then(function(file) {
+ zip.writeZip(file); // Sync! Why oh why :-(
+ return io.read(file);
+ });
+ }).then(function(data) {
+ return data.toString('base64');
+ });
+ }
+
+ /**
+ * Encodes this profile as a zipped, base64 encoded directory.
+ * @return {!Promise<string>} A promise for the encoded profile.
+ */
+ [Symbols.serialize]() {
+ return this.encode();
+ }
+}
+
+
+// PUBLIC API
+
+
+exports.Profile = Profile;
+exports.decode = decode;
+exports.loadUserPrefs = loadUserPrefs;
diff --git a/node_modules/selenium-webdriver/http/index.js b/node_modules/selenium-webdriver/http/index.js
new file mode 100644
index 000000000..fa98557a0
--- /dev/null
+++ b/node_modules/selenium-webdriver/http/index.js
@@ -0,0 +1,255 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+/**
+ * @fileoverview Defines an {@linkplain cmd.Executor command executor} that
+ * communicates with a remote end using HTTP + JSON.
+ */
+
+'use strict';
+
+const http = require('http');
+const https = require('https');
+const url = require('url');
+
+const httpLib = require('../lib/http');
+
+
+/**
+ * @typedef {{protocol: (?string|undefined),
+ * auth: (?string|undefined),
+ * hostname: (?string|undefined),
+ * host: (?string|undefined),
+ * port: (?string|undefined),
+ * path: (?string|undefined),
+ * pathname: (?string|undefined)}}
+ */
+var RequestOptions;
+
+
+/**
+ * @param {string} aUrl The request URL to parse.
+ * @return {RequestOptions} The request options.
+ * @throws {Error} if the URL does not include a hostname.
+ */
+function getRequestOptions(aUrl) {
+ let options = url.parse(aUrl);
+ if (!options.hostname) {
+ throw new Error('Invalid URL: ' + aUrl);
+ }
+ // Delete the search and has portions as they are not used.
+ options.search = null;
+ options.hash = null;
+ options.path = options.pathname;
+ return options;
+}
+
+
+/**
+ * A basic HTTP client used to send messages to a remote end.
+ *
+ * @implements {httpLib.Client}
+ */
+class HttpClient {
+ /**
+ * @param {string} serverUrl URL for the WebDriver server to send commands to.
+ * @param {http.Agent=} opt_agent The agent to use for each request.
+ * Defaults to `http.globalAgent`.
+ * @param {?string=} opt_proxy The proxy to use for the connection to the
+ * server. Default is to use no proxy.
+ */
+ constructor(serverUrl, opt_agent, opt_proxy) {
+ /** @private {http.Agent} */
+ this.agent_ = opt_agent || null;
+
+ /**
+ * Base options for each request.
+ * @private {RequestOptions}
+ */
+ this.options_ = getRequestOptions(serverUrl);
+
+ /**
+ * @private {?RequestOptions}
+ */
+ this.proxyOptions_ = opt_proxy ? getRequestOptions(opt_proxy) : null;
+ }
+
+ /** @override */
+ send(httpRequest) {
+ let data;
+
+ let headers = {};
+ httpRequest.headers.forEach(function(value, name) {
+ headers[name] = value;
+ });
+
+ headers['Content-Length'] = 0;
+ if (httpRequest.method == 'POST' || httpRequest.method == 'PUT') {
+ data = JSON.stringify(httpRequest.data);
+ headers['Content-Length'] = Buffer.byteLength(data, 'utf8');
+ headers['Content-Type'] = 'application/json;charset=UTF-8';
+ }
+
+ let path = this.options_.path;
+ if (path.endsWith('/') && httpRequest.path.startsWith('/')) {
+ path += httpRequest.path.substring(1);
+ } else {
+ path += httpRequest.path;
+ }
+ let parsedPath = url.parse(path);
+
+ let options = {
+ agent: this.agent_ || null,
+ method: httpRequest.method,
+
+ auth: this.options_.auth,
+ hostname: this.options_.hostname,
+ port: this.options_.port,
+ protocol: this.options_.protocol,
+
+ path: parsedPath.path,
+ pathname: parsedPath.pathname,
+ search: parsedPath.search,
+ hash: parsedPath.hash,
+
+ headers,
+ };
+
+ return new Promise((fulfill, reject) => {
+ sendRequest(options, fulfill, reject, data, this.proxyOptions_);
+ });
+ }
+}
+
+
+/**
+ * Sends a single HTTP request.
+ * @param {!Object} options The request options.
+ * @param {function(!httpLib.Response)} onOk The function to call if the
+ * request succeeds.
+ * @param {function(!Error)} onError The function to call if the request fails.
+ * @param {?string=} opt_data The data to send with the request.
+ * @param {?RequestOptions=} opt_proxy The proxy server to use for the request.
+ */
+function sendRequest(options, onOk, onError, opt_data, opt_proxy) {
+ var hostname = options.hostname;
+ var port = options.port;
+
+ if (opt_proxy) {
+ let proxy = /** @type {RequestOptions} */(opt_proxy);
+
+ // RFC 2616, section 5.1.2:
+ // The absoluteURI form is REQUIRED when the request is being made to a
+ // proxy.
+ let absoluteUri = url.format(options);
+
+ // RFC 2616, section 14.23:
+ // An HTTP/1.1 proxy MUST ensure that any request message it forwards does
+ // contain an appropriate Host header field that identifies the service
+ // being requested by the proxy.
+ let targetHost = options.hostname
+ if (options.port) {
+ targetHost += ':' + options.port;
+ }
+
+ // Update the request options with our proxy info.
+ options.headers['Host'] = targetHost;
+ options.path = absoluteUri;
+ options.host = proxy.host;
+ options.hostname = proxy.hostname;
+ options.port = proxy.port;
+
+ if (proxy.auth) {
+ options.headers['Proxy-Authorization'] =
+ 'Basic ' + new Buffer(proxy.auth).toString('base64');
+ }
+ }
+
+ let requestFn = options.protocol === 'https:' ? https.request : http.request;
+ var request = requestFn(options, function onResponse(response) {
+ if (response.statusCode == 302 || response.statusCode == 303) {
+ try {
+ var location = url.parse(response.headers['location']);
+ } catch (ex) {
+ onError(Error(
+ 'Failed to parse "Location" header for server redirect: ' +
+ ex.message + '\nResponse was: \n' +
+ new httpLib.Response(response.statusCode, response.headers, '')));
+ return;
+ }
+
+ if (!location.hostname) {
+ location.hostname = hostname;
+ location.port = port;
+ }
+
+ request.abort();
+ sendRequest({
+ method: 'GET',
+ protocol: location.protocol || options.protocol,
+ hostname: location.hostname,
+ port: location.port,
+ path: location.path,
+ pathname: location.pathname,
+ search: location.search,
+ hash: location.hash,
+ headers: {
+ 'Accept': 'application/json; charset=utf-8'
+ }
+ }, onOk, onError, undefined, opt_proxy);
+ return;
+ }
+
+ var body = [];
+ response.on('data', body.push.bind(body));
+ response.on('end', function() {
+ var resp = new httpLib.Response(
+ /** @type {number} */(response.statusCode),
+ /** @type {!Object<string>} */(response.headers),
+ body.join('').replace(/\0/g, ''));
+ onOk(resp);
+ });
+ });
+
+ request.on('error', function(e) {
+ if (e.code === 'ECONNRESET') {
+ setTimeout(function() {
+ sendRequest(options, onOk, onError, opt_data, opt_proxy);
+ }, 15);
+ } else {
+ var message = e.message;
+ if (e.code) {
+ message = e.code + ' ' + message;
+ }
+ onError(new Error(message));
+ }
+ });
+
+ if (opt_data) {
+ request.write(opt_data);
+ }
+
+ request.end();
+}
+
+
+// PUBLIC API
+
+exports.Executor = httpLib.Executor;
+exports.HttpClient = HttpClient;
+exports.Request = httpLib.Request;
+exports.Response = httpLib.Response;
diff --git a/node_modules/selenium-webdriver/http/util.js b/node_modules/selenium-webdriver/http/util.js
new file mode 100644
index 000000000..7564ba85e
--- /dev/null
+++ b/node_modules/selenium-webdriver/http/util.js
@@ -0,0 +1,139 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+/**
+ * @fileoverview Various HTTP utilities.
+ */
+
+'use strict';
+
+const Executor = require('./index').Executor,
+ HttpClient = require('./index').HttpClient,
+ HttpRequest = require('./index').Request,
+ Command = require('../lib/command').Command,
+ CommandName = require('../lib/command').Name,
+ error = require('../lib/error'),
+ promise = require('../lib/promise');
+
+
+
+/**
+ * Queries a WebDriver server for its current status.
+ * @param {string} url Base URL of the server to query.
+ * @return {!Promise<!Object>} A promise that resolves with
+ * a hash of the server status.
+ */
+function getStatus(url) {
+ var client = new HttpClient(url);
+ var executor = new Executor(client);
+ var command = new Command(CommandName.GET_SERVER_STATUS);
+ return executor.execute(command);
+}
+
+
+// PUBLIC API
+
+
+/**
+ * Queries a WebDriver server for its current status.
+ * @param {string} url Base URL of the server to query.
+ * @return {!Promise<!Object>} A promise that resolves with
+ * a hash of the server status.
+ */
+exports.getStatus = getStatus;
+
+
+/**
+ * Waits for a WebDriver server to be healthy and accepting requests.
+ * @param {string} url Base URL of the server to query.
+ * @param {number} timeout How long to wait for the server.
+ * @return {!promise.Promise} A promise that will resolve when the
+ * server is ready.
+ */
+exports.waitForServer = function(url, timeout) {
+ var ready = promise.defer(),
+ start = Date.now();
+ checkServerStatus();
+ return ready.promise;
+
+ function checkServerStatus() {
+ return getStatus(url).then(status => ready.fulfill(status), onError);
+ }
+
+ function onError(e) {
+ // Some servers don't support the status command. If they are able to
+ // response with an error, then can consider the server ready.
+ if (e instanceof error.UnsupportedOperationError) {
+ ready.fulfill();
+ return;
+ }
+
+ if (Date.now() - start > timeout) {
+ ready.reject(
+ Error('Timed out waiting for the WebDriver server at ' + url));
+ } else {
+ setTimeout(function() {
+ if (ready.promise.isPending()) {
+ checkServerStatus();
+ }
+ }, 50);
+ }
+ }
+};
+
+
+/**
+ * Polls a URL with GET requests until it returns a 2xx response or the
+ * timeout expires.
+ * @param {string} url The URL to poll.
+ * @param {number} timeout How long to wait, in milliseconds.
+ * @return {!promise.Promise} A promise that will resolve when the
+ * URL responds with 2xx.
+ */
+exports.waitForUrl = function(url, timeout) {
+ var client = new HttpClient(url),
+ request = new HttpRequest('GET', ''),
+ ready = promise.defer(),
+ start = Date.now();
+ testUrl();
+ return ready.promise;
+
+ function testUrl() {
+ client.send(request).then(onResponse, onError);
+ }
+
+ function onError() {
+ if (Date.now() - start > timeout) {
+ ready.reject(Error(
+ 'Timed out waiting for the URL to return 2xx: ' + url));
+ } else {
+ setTimeout(function() {
+ if (ready.promise.isPending()) {
+ testUrl();
+ }
+ }, 50);
+ }
+ }
+
+ function onResponse(response) {
+ if (!ready.promise.isPending()) return;
+ if (response.status > 199 && response.status < 300) {
+ return ready.fulfill();
+ }
+ onError();
+ }
+};
diff --git a/node_modules/selenium-webdriver/ie.js b/node_modules/selenium-webdriver/ie.js
new file mode 100644
index 000000000..40095a0bf
--- /dev/null
+++ b/node_modules/selenium-webdriver/ie.js
@@ -0,0 +1,445 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+/**
+ * @fileoverview Defines a {@linkplain Driver WebDriver} client for Microsoft's
+ * Internet Explorer. Before using the IEDriver, you must download the latest
+ * [IEDriverServer](http://selenium-release.storage.googleapis.com/index.html)
+ * and place it on your
+ * [PATH](http://en.wikipedia.org/wiki/PATH_%28variable%29). You must also apply
+ * the system configuration outlined on the Selenium project
+ * [wiki](https://github.com/SeleniumHQ/selenium/wiki/InternetExplorerDriver)
+ */
+
+'use strict';
+
+const fs = require('fs'),
+ util = require('util');
+
+const http = require('./http'),
+ io = require('./io'),
+ capabilities = require('./lib/capabilities'),
+ promise = require('./lib/promise'),
+ webdriver = require('./lib/webdriver'),
+ portprober = require('./net/portprober'),
+ remote = require('./remote');
+
+
+const IEDRIVER_EXE = 'IEDriverServer.exe';
+
+
+
+/**
+ * IEDriverServer logging levels.
+ * @enum {string}
+ */
+const Level = {
+ FATAL: 'FATAL',
+ ERROR: 'ERROR',
+ WARN: 'WARN',
+ INFO: 'INFO',
+ DEBUG: 'DEBUG',
+ TRACE: 'TRACE'
+};
+
+
+
+/**
+ * Option keys:
+ * https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities#ie-specific
+ * @enum {string}
+ */
+const Key = {
+ IGNORE_PROTECTED_MODE_SETTINGS: 'ignoreProtectedModeSettings',
+ IGNORE_ZOOM_SETTING: 'ignoreZoomSetting',
+ INITIAL_BROWSER_URL: 'initialBrowserUrl',
+ ENABLE_PERSISTENT_HOVER: 'enablePersistentHover',
+ ENABLE_ELEMENT_CACHE_CLEANUP: 'enableElementCacheCleanup',
+ REQUIRE_WINDOW_FOCUS: 'requireWindowFocus',
+ BROWSER_ATTACH_TIMEOUT: 'browserAttachTimeout',
+ FORCE_CREATE_PROCESS: 'ie.forceCreateProcessApi',
+ BROWSER_COMMAND_LINE_SWITCHES: 'ie.browserCommandLineSwitches',
+ USE_PER_PROCESS_PROXY: 'ie.usePerProcessProxy',
+ ENSURE_CLEAN_SESSION: 'ie.ensureCleanSession',
+ LOG_FILE: 'logFile',
+ LOG_LEVEL: 'logLevel',
+ HOST: 'host',
+ EXTRACT_PATH: 'extractPath',
+ SILENT: 'silent'
+};
+
+
+/**
+ * Class for managing IEDriver specific options.
+ */
+class Options {
+ constructor() {
+ /** @private {!Object<(boolean|number|string|!Array<string>)>} */
+ this.options_ = {};
+
+ /** @private {(capabilities.ProxyConfig|null)} */
+ this.proxy_ = null;
+ }
+
+ /**
+ * Extracts the IEDriver specific options from the given capabilities
+ * object.
+ * @param {!capabilities.Capabilities} caps The capabilities object.
+ * @return {!Options} The IEDriver options.
+ */
+ static fromCapabilities(caps) {
+ var options = new Options();
+ var map = options.options_;
+
+ Object.keys(Key).forEach(function(key) {
+ key = Key[key];
+ if (caps.has(key)) {
+ map[key] = caps.get(key);
+ }
+ });
+
+ if (caps.has(capabilities.Capability.PROXY)) {
+ options.setProxy(caps.get(capabilities.Capability.PROXY));
+ }
+
+ return options;
+ }
+
+ /**
+ * Whether to disable the protected mode settings check when the session is
+ * created. Disbling this setting may lead to significant instability as the
+ * browser may become unresponsive/hang. Only "best effort" support is provided
+ * when using this capability.
+ *
+ * For more information, refer to the IEDriver's
+ * [required system configuration](http://goo.gl/eH0Yi3).
+ *
+ * @param {boolean} ignoreSettings Whether to ignore protected mode settings.
+ * @return {!Options} A self reference.
+ */
+ introduceFlakinessByIgnoringProtectedModeSettings(ignoreSettings) {
+ this.options_[Key.IGNORE_PROTECTED_MODE_SETTINGS] = !!ignoreSettings;
+ return this;
+ }
+
+ /**
+ * Indicates whether to skip the check that the browser's zoom level is set to
+ * 100%.
+ *
+ * @param {boolean} ignore Whether to ignore the browser's zoom level settings.
+ * @return {!Options} A self reference.
+ */
+ ignoreZoomSetting(ignore) {
+ this.options_[Key.IGNORE_ZOOM_SETTING] = !!ignore;
+ return this;
+ }
+
+ /**
+ * Sets the initial URL loaded when IE starts. This is intended to be used with
+ * {@link #ignoreProtectedModeSettings} to allow the user to initialize IE in
+ * the proper Protected Mode zone. Setting this option may cause browser
+ * instability or flaky and unresponsive code. Only "best effort" support is
+ * provided when using this option.
+ *
+ * @param {string} url The initial browser URL.
+ * @return {!Options} A self reference.
+ */
+ initialBrowserUrl(url) {
+ this.options_[Key.INITIAL_BROWSER_URL] = url;
+ return this;
+ }
+
+ /**
+ * Configures whether to enable persistent mouse hovering (true by default).
+ * Persistent hovering is achieved by continuously firing mouse over events at
+ * the last location the mouse cursor has been moved to.
+ *
+ * @param {boolean} enable Whether to enable persistent hovering.
+ * @return {!Options} A self reference.
+ */
+ enablePersistentHover(enable) {
+ this.options_[Key.ENABLE_PERSISTENT_HOVER] = !!enable;
+ return this;
+ }
+
+ /**
+ * Configures whether the driver should attempt to remove obsolete
+ * {@linkplain webdriver.WebElement WebElements} from its internal cache on
+ * page navigation (true by default). Disabling this option will cause the
+ * driver to run with a larger memory footprint.
+ *
+ * @param {boolean} enable Whether to enable element reference cleanup.
+ * @return {!Options} A self reference.
+ */
+ enableElementCacheCleanup(enable) {
+ this.options_[Key.ENABLE_ELEMENT_CACHE_CLEANUP] = !!enable;
+ return this;
+ }
+
+ /**
+ * Configures whether to require the IE window to have input focus before
+ * performing any user interactions (i.e. mouse or keyboard events). This
+ * option is disabled by default, but delivers much more accurate interaction
+ * events when enabled.
+ *
+ * @param {boolean} require Whether to require window focus.
+ * @return {!Options} A self reference.
+ */
+ requireWindowFocus(require) {
+ this.options_[Key.REQUIRE_WINDOW_FOCUS] = !!require;
+ return this;
+ }
+
+ /**
+ * Configures the timeout, in milliseconds, that the driver will attempt to
+ * located and attach to a newly opened instance of Internet Explorer. The
+ * default is zero, which indicates waiting indefinitely.
+ *
+ * @param {number} timeout How long to wait for IE.
+ * @return {!Options} A self reference.
+ */
+ browserAttachTimeout(timeout) {
+ this.options_[Key.BROWSER_ATTACH_TIMEOUT] = Math.max(timeout, 0);
+ return this;
+ }
+
+ /**
+ * Configures whether to launch Internet Explorer using the CreateProcess API.
+ * If this option is not specified, IE is launched using IELaunchURL, if
+ * available. For IE 8 and above, this option requires the TabProcGrowth
+ * registry value to be set to 0.
+ *
+ * @param {boolean} force Whether to use the CreateProcess API.
+ * @return {!Options} A self reference.
+ */
+ forceCreateProcessApi(force) {
+ this.options_[Key.FORCE_CREATE_PROCESS] = !!force;
+ return this;
+ }
+
+ /**
+ * Specifies command-line switches to use when launching Internet Explorer.
+ * This is only valid when used with {@link #forceCreateProcessApi}.
+ *
+ * @param {...(string|!Array.<string>)} var_args The arguments to add.
+ * @return {!Options} A self reference.
+ */
+ addArguments(var_args) {
+ var args = this.options_[Key.BROWSER_COMMAND_LINE_SWITCHES] || [];
+ args = args.concat.apply(args, arguments);
+ this.options_[Key.BROWSER_COMMAND_LINE_SWITCHES] = args;
+ return this;
+ }
+
+ /**
+ * Configures whether proxies should be configured on a per-process basis. If
+ * not set, setting a {@linkplain #setProxy proxy} will configure the system
+ * proxy. The default behavior is to use the system proxy.
+ *
+ * @param {boolean} enable Whether to enable per-process proxy settings.
+ * @return {!Options} A self reference.
+ */
+ usePerProcessProxy(enable) {
+ this.options_[Key.USE_PER_PROCESS_PROXY] = !!enable;
+ return this;
+ }
+
+ /**
+ * Configures whether to clear the cache, cookies, history, and saved form data
+ * before starting the browser. _Using this capability will clear session data
+ * for all running instances of Internet Explorer, including those started
+ * manually._
+ *
+ * @param {boolean} cleanSession Whether to clear all session data on startup.
+ * @return {!Options} A self reference.
+ */
+ ensureCleanSession(cleanSession) {
+ this.options_[Key.ENSURE_CLEAN_SESSION] = !!cleanSession;
+ return this;
+ }
+
+ /**
+ * Sets the path to the log file the driver should log to.
+ * @param {string} file The log file path.
+ * @return {!Options} A self reference.
+ */
+ setLogFile(file) {
+ this.options_[Key.LOG_FILE] = file;
+ return this;
+ }
+
+ /**
+ * Sets the IEDriverServer's logging {@linkplain Level level}.
+ * @param {Level} level The logging level.
+ * @return {!Options} A self reference.
+ */
+ setLogLevel(level) {
+ this.options_[Key.LOG_LEVEL] = level;
+ return this;
+ }
+
+ /**
+ * Sets the IP address of the driver's host adapter.
+ * @param {string} host The IP address to use.
+ * @return {!Options} A self reference.
+ */
+ setHost(host) {
+ this.options_[Key.HOST] = host;
+ return this;
+ }
+
+ /**
+ * Sets the path of the temporary data directory to use.
+ * @param {string} path The log file path.
+ * @return {!Options} A self reference.
+ */
+ setExtractPath(path) {
+ this.options_[Key.EXTRACT_PATH] = path;
+ return this;
+ }
+
+ /**
+ * Sets whether the driver should start in silent mode.
+ * @param {boolean} silent Whether to run in silent mode.
+ * @return {!Options} A self reference.
+ */
+ silent(silent) {
+ this.options_[Key.SILENT] = silent;
+ return this;
+ }
+
+ /**
+ * Sets the proxy settings for the new session.
+ * @param {capabilities.ProxyConfig} proxy The proxy configuration to use.
+ * @return {!Options} A self reference.
+ */
+ setProxy(proxy) {
+ this.proxy_ = proxy;
+ return this;
+ }
+
+ /**
+ * Converts this options instance to a {@link capabilities.Capabilities}
+ * object.
+ * @param {capabilities.Capabilities=} opt_capabilities The capabilities to
+ * merge these options into, if any.
+ * @return {!capabilities.Capabilities} The capabilities.
+ */
+ toCapabilities(opt_capabilities) {
+ var caps = opt_capabilities || capabilities.Capabilities.ie();
+ if (this.proxy_) {
+ caps.set(capabilities.Capability.PROXY, this.proxy_);
+ }
+ Object.keys(this.options_).forEach(function(key) {
+ caps.set(key, this.options_[key]);
+ }, this);
+ return caps;
+ }
+}
+
+
+function createServiceFromCapabilities(capabilities) {
+ if (process.platform !== 'win32') {
+ throw Error(
+ 'The IEDriver may only be used on Windows, but you appear to be on ' +
+ process.platform + '. Did you mean to run against a remote ' +
+ 'WebDriver server?');
+ }
+
+ let exe = io.findInPath(IEDRIVER_EXE, true);
+ if (!exe || !fs.existsSync(exe)) {
+ throw Error(
+ `${IEDRIVER_EXE} could not be found on the current PATH. Please ` +
+ `download the latest version of ${IEDRIVER_EXE} from ` +
+ 'http://selenium-release.storage.googleapis.com/index.html and ' +
+ 'ensure it can be found on your system PATH.');
+ }
+
+ var args = [];
+ if (capabilities.has(Key.HOST)) {
+ args.push('--host=' + capabilities.get(Key.HOST));
+ }
+ if (capabilities.has(Key.LOG_FILE)) {
+ args.push('--log-file=' + capabilities.get(Key.LOG_FILE));
+ }
+ if (capabilities.has(Key.LOG_LEVEL)) {
+ args.push('--log-level=' + capabilities.get(Key.LOG_LEVEL));
+ }
+ if (capabilities.has(Key.EXTRACT_PATH)) {
+ args.push('--extract-path=' + capabilities.get(Key.EXTRACT_PATH));
+ }
+ if (capabilities.get(Key.SILENT)) {
+ args.push('--silent');
+ }
+
+ var port = portprober.findFreePort();
+ return new remote.DriverService(exe, {
+ loopback: true,
+ port: port,
+ args: port.then(function(port) {
+ return args.concat('--port=' + port);
+ }),
+ stdio: 'ignore'
+ });
+}
+
+
+/**
+ * A WebDriver client for Microsoft's Internet Explorer.
+ */
+class Driver extends webdriver.WebDriver {
+ /**
+ * @param {(capabilities.Capabilities|Options)=} opt_config The configuration
+ * options.
+ * @param {promise.ControlFlow=} opt_flow The control flow to use,
+ * or {@code null} to use the currently active flow.
+ */
+ constructor(opt_config, opt_flow) {
+ var caps = opt_config instanceof Options ?
+ opt_config.toCapabilities() :
+ (opt_config || capabilities.Capabilities.ie());
+
+ var service = createServiceFromCapabilities(caps);
+ var client = service.start().then(url => new http.HttpClient(url));
+ var executor = new http.Executor(client);
+ var driver = webdriver.WebDriver.createSession(executor, caps, opt_flow);
+
+ super(driver.getSession(), executor, driver.controlFlow());
+
+ let boundQuit = this.quit.bind(this);
+
+ /** @override */
+ this.quit = function() {
+ return boundQuit().finally(service.kill.bind(service));
+ };
+ }
+
+ /**
+ * This function is a no-op as file detectors are not supported by this
+ * implementation.
+ * @override
+ */
+ setFileDetector() {}
+}
+
+
+// PUBLIC API
+
+
+exports.Driver = Driver;
+exports.Options = Options;
+exports.Level = Level;
diff --git a/node_modules/selenium-webdriver/index.js b/node_modules/selenium-webdriver/index.js
new file mode 100644
index 000000000..47f825738
--- /dev/null
+++ b/node_modules/selenium-webdriver/index.js
@@ -0,0 +1,622 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+/**
+ * @fileoverview The main user facing module. Exports WebDriver's primary
+ * public API and provides convenience assessors to certain sub-modules.
+ */
+
+'use strict';
+
+const chrome = require('./chrome');
+const edge = require('./edge');
+const firefox = require('./firefox');
+const _http = require('./http');
+const ie = require('./ie');
+const actions = require('./lib/actions');
+const by = require('./lib/by');
+const capabilities = require('./lib/capabilities');
+const command = require('./lib/command');
+const error = require('./lib/error');
+const events = require('./lib/events');
+const input = require('./lib/input');
+const logging = require('./lib/logging');
+const promise = require('./lib/promise');
+const session = require('./lib/session');
+const until = require('./lib/until');
+const webdriver = require('./lib/webdriver');
+const opera = require('./opera');
+const phantomjs = require('./phantomjs');
+const remote = require('./remote');
+const safari = require('./safari');
+
+const Browser = capabilities.Browser;
+const Capabilities = capabilities.Capabilities;
+const Capability = capabilities.Capability;
+const WebDriver = webdriver.WebDriver;
+
+
+
+var seleniumServer;
+
+/**
+ * Starts an instance of the Selenium server if not yet running.
+ * @param {string} jar Path to the server jar to use.
+ * @return {!Promise<string>} A promise for the server's
+ * addrss once started.
+ */
+function startSeleniumServer(jar) {
+ if (!seleniumServer) {
+ seleniumServer = new remote.SeleniumServer(jar);
+ }
+ return seleniumServer.start();
+}
+
+
+/**
+ * {@linkplain webdriver.WebDriver#setFileDetector WebDriver's setFileDetector}
+ * method uses a non-standard command to transfer files from the local client
+ * to the remote end hosting the browser. Many of the WebDriver sub-types, like
+ * the {@link chrome.Driver} and {@link firefox.Driver}, do not support this
+ * command. Thus, these classes override the `setFileDetector` to no-op.
+ *
+ * This function uses a mixin to re-enable `setFileDetector` by calling the
+ * original method on the WebDriver prototype directly. This is used only when
+ * the builder creates a Chrome or Firefox instance that communicates with a
+ * remote end (and thus, support for remote file detectors is unknown).
+ *
+ * @param {function(new: webdriver.WebDriver, ...?)} ctor
+ * @return {function(new: webdriver.WebDriver, ...?)}
+ */
+function ensureFileDetectorsAreEnabled(ctor) {
+ const mixin = class extends ctor {
+ /** @param {input.FileDetector} detector */
+ setFileDetector(detector) {
+ webdriver.WebDriver.prototype.setFileDetector.call(this, detector);
+ }
+ };
+ return mixin;
+}
+
+
+/**
+ * Creates new {@link webdriver.WebDriver WebDriver} instances. The environment
+ * variables listed below may be used to override a builder's configuration,
+ * allowing quick runtime changes.
+ *
+ * - {@code SELENIUM_BROWSER}: defines the target browser in the form
+ * {@code browser[:version][:platform]}.
+ *
+ * - {@code SELENIUM_REMOTE_URL}: defines the remote URL for all builder
+ * instances. This environment variable should be set to a fully qualified
+ * URL for a WebDriver server (e.g. http://localhost:4444/wd/hub). This
+ * option always takes precedence over {@code SELENIUM_SERVER_JAR}.
+ *
+ * - {@code SELENIUM_SERVER_JAR}: defines the path to the
+ * <a href="http://selenium-release.storage.googleapis.com/index.html">
+ * standalone Selenium server</a> jar to use. The server will be started the
+ * first time a WebDriver instance and be killed when the process exits.
+ *
+ * Suppose you had mytest.js that created WebDriver with
+ *
+ * var driver = new webdriver.Builder()
+ * .forBrowser('chrome')
+ * .build();
+ *
+ * This test could be made to use Firefox on the local machine by running with
+ * `SELENIUM_BROWSER=firefox node mytest.js`. Rather than change the code to
+ * target Google Chrome on a remote machine, you can simply set the
+ * `SELENIUM_BROWSER` and `SELENIUM_REMOTE_URL` environment variables:
+ *
+ * SELENIUM_BROWSER=chrome:36:LINUX \
+ * SELENIUM_REMOTE_URL=http://www.example.com:4444/wd/hub \
+ * node mytest.js
+ *
+ * You could also use a local copy of the standalone Selenium server:
+ *
+ * SELENIUM_BROWSER=chrome:36:LINUX \
+ * SELENIUM_SERVER_JAR=/path/to/selenium-server-standalone.jar \
+ * node mytest.js
+ */
+class Builder {
+ constructor() {
+ /** @private {promise.ControlFlow} */
+ this.flow_ = null;
+
+ /** @private {string} */
+ this.url_ = '';
+
+ /** @private {?string} */
+ this.proxy_ = null;
+
+ /** @private {!Capabilities} */
+ this.capabilities_ = new Capabilities();
+
+ /** @private {chrome.Options} */
+ this.chromeOptions_ = null;
+
+ /** @private {firefox.Options} */
+ this.firefoxOptions_ = null;
+
+ /** @private {opera.Options} */
+ this.operaOptions_ = null;
+
+ /** @private {ie.Options} */
+ this.ieOptions_ = null;
+
+ /** @private {safari.Options} */
+ this.safariOptions_ = null;
+
+ /** @private {edge.Options} */
+ this.edgeOptions_ = null;
+
+ /** @private {boolean} */
+ this.ignoreEnv_ = false;
+
+ /** @private {http.Agent} */
+ this.agent_ = null;
+ }
+
+ /**
+ * Configures this builder to ignore any environment variable overrides and to
+ * only use the configuration specified through this instance's API.
+ *
+ * @return {!Builder} A self reference.
+ */
+ disableEnvironmentOverrides() {
+ this.ignoreEnv_ = true;
+ return this;
+ }
+
+ /**
+ * Sets the URL of a remote WebDriver server to use. Once a remote URL has
+ * been specified, the builder direct all new clients to that server. If this
+ * method is never called, the Builder will attempt to create all clients
+ * locally.
+ *
+ * As an alternative to this method, you may also set the
+ * `SELENIUM_REMOTE_URL` environment variable.
+ *
+ * @param {string} url The URL of a remote server to use.
+ * @return {!Builder} A self reference.
+ */
+ usingServer(url) {
+ this.url_ = url;
+ return this;
+ }
+
+ /**
+ * @return {string} The URL of the WebDriver server this instance is
+ * configured to use.
+ */
+ getServerUrl() {
+ return this.url_;
+ }
+
+ /**
+ * Sets the URL of the proxy to use for the WebDriver's HTTP connections.
+ * If this method is never called, the Builder will create a connection
+ * without a proxy.
+ *
+ * @param {string} proxy The URL of a proxy to use.
+ * @return {!Builder} A self reference.
+ */
+ usingWebDriverProxy(proxy) {
+ this.proxy_ = proxy;
+ return this;
+ }
+
+ /**
+ * @return {?string} The URL of the proxy server to use for the WebDriver's
+ * HTTP connections, or `null` if not set.
+ */
+ getWebDriverProxy() {
+ return this.proxy_;
+ }
+
+ /**
+ * Sets the http agent to use for each request.
+ * If this method is not called, the Builder will use http.globalAgent by default.
+ *
+ * @param {http.Agent} agent The agent to use for each request.
+ * @return {!Builder} A self reference.
+ */
+ usingHttpAgent(agent) {
+ this.agent_ = agent;
+ return this;
+ }
+
+ /**
+ * @return {http.Agent} The http agent used for each request
+ */
+ getHttpAgent() {
+ return this.agent_;
+ }
+
+ /**
+ * Sets the desired capabilities when requesting a new session. This will
+ * overwrite any previously set capabilities.
+ * @param {!(Object|Capabilities)} capabilities The desired capabilities for
+ * a new session.
+ * @return {!Builder} A self reference.
+ */
+ withCapabilities(capabilities) {
+ this.capabilities_ = new Capabilities(capabilities);
+ return this;
+ }
+
+ /**
+ * Returns the base set of capabilities this instance is currently configured
+ * to use.
+ * @return {!Capabilities} The current capabilities for this builder.
+ */
+ getCapabilities() {
+ return this.capabilities_;
+ }
+
+ /**
+ * Configures the target browser for clients created by this instance.
+ * Any calls to {@link #withCapabilities} after this function will
+ * overwrite these settings.
+ *
+ * You may also define the target browser using the {@code SELENIUM_BROWSER}
+ * environment variable. If set, this environment variable should be of the
+ * form `browser[:[version][:platform]]`.
+ *
+ * @param {(string|Browser)} name The name of the target browser;
+ * common defaults are available on the {@link webdriver.Browser} enum.
+ * @param {string=} opt_version A desired version; may be omitted if any
+ * version should be used.
+ * @param {string=} opt_platform The desired platform; may be omitted if any
+ * version may be used.
+ * @return {!Builder} A self reference.
+ */
+ forBrowser(name, opt_version, opt_platform) {
+ this.capabilities_.set(Capability.BROWSER_NAME, name);
+ this.capabilities_.set(Capability.VERSION, opt_version || null);
+ this.capabilities_.set(Capability.PLATFORM, opt_platform || null);
+ return this;
+ }
+
+ /**
+ * Sets the proxy configuration for the target browser.
+ * Any calls to {@link #withCapabilities} after this function will
+ * overwrite these settings.
+ *
+ * @param {!capabilities.ProxyConfig} config The configuration to use.
+ * @return {!Builder} A self reference.
+ */
+ setProxy(config) {
+ this.capabilities_.setProxy(config);
+ return this;
+ }
+
+ /**
+ * Sets the logging preferences for the created session. Preferences may be
+ * changed by repeated calls, or by calling {@link #withCapabilities}.
+ * @param {!(./lib/logging.Preferences|Object<string, string>)} prefs The
+ * desired logging preferences.
+ * @return {!Builder} A self reference.
+ */
+ setLoggingPrefs(prefs) {
+ this.capabilities_.setLoggingPrefs(prefs);
+ return this;
+ }
+
+ /**
+ * Sets whether native events should be used.
+ * @param {boolean} enabled Whether to enable native events.
+ * @return {!Builder} A self reference.
+ */
+ setEnableNativeEvents(enabled) {
+ this.capabilities_.setEnableNativeEvents(enabled);
+ return this;
+ }
+
+ /**
+ * Sets how elements should be scrolled into view for interaction.
+ * @param {number} behavior The desired scroll behavior: either 0 to align
+ * with the top of the viewport or 1 to align with the bottom.
+ * @return {!Builder} A self reference.
+ */
+ setScrollBehavior(behavior) {
+ this.capabilities_.setScrollBehavior(behavior);
+ return this;
+ }
+
+ /**
+ * Sets the default action to take with an unexpected alert before returning
+ * an error.
+ * @param {string} behavior The desired behavior; should be "accept",
+ * "dismiss", or "ignore". Defaults to "dismiss".
+ * @return {!Builder} A self reference.
+ */
+ setAlertBehavior(behavior) {
+ this.capabilities_.setAlertBehavior(behavior);
+ return this;
+ }
+
+ /**
+ * Sets Chrome specific {@linkplain chrome.Options options} for drivers
+ * created by this builder. Any logging or proxy settings defined on the given
+ * options will take precedence over those set through
+ * {@link #setLoggingPrefs} and {@link #setProxy}, respectively.
+ *
+ * @param {!chrome.Options} options The ChromeDriver options to use.
+ * @return {!Builder} A self reference.
+ */
+ setChromeOptions(options) {
+ this.chromeOptions_ = options;
+ return this;
+ }
+
+ /**
+ * Sets Firefox specific {@linkplain firefox.Options options} for drivers
+ * created by this builder. Any logging or proxy settings defined on the given
+ * options will take precedence over those set through
+ * {@link #setLoggingPrefs} and {@link #setProxy}, respectively.
+ *
+ * @param {!firefox.Options} options The FirefoxDriver options to use.
+ * @return {!Builder} A self reference.
+ */
+ setFirefoxOptions(options) {
+ this.firefoxOptions_ = options;
+ return this;
+ }
+
+ /**
+ * @return {firefox.Options} the Firefox specific options currently configured
+ * for this instance.
+ */
+ getFirefoxOptions() {
+ return this.firefoxOptions_;
+ }
+
+ /**
+ * Sets Opera specific {@linkplain opera.Options options} for drivers created
+ * by this builder. Any logging or proxy settings defined on the given options
+ * will take precedence over those set through {@link #setLoggingPrefs} and
+ * {@link #setProxy}, respectively.
+ *
+ * @param {!opera.Options} options The OperaDriver options to use.
+ * @return {!Builder} A self reference.
+ */
+ setOperaOptions(options) {
+ this.operaOptions_ = options;
+ return this;
+ }
+
+ /**
+ * Set Internet Explorer specific {@linkplain ie.Options options} for drivers
+ * created by this builder. Any proxy settings defined on the given options
+ * will take precedence over those set through {@link #setProxy}.
+ *
+ * @param {!ie.Options} options The IEDriver options to use.
+ * @return {!Builder} A self reference.
+ */
+ setIeOptions(options) {
+ this.ieOptions_ = options;
+ return this;
+ }
+
+ /**
+ * Set {@linkplain edge.Options options} specific to Microsoft's Edge browser
+ * for drivers created by this builder. Any proxy settings defined on the
+ * given options will take precedence over those set through
+ * {@link #setProxy}.
+ *
+ * @param {!edge.Options} options The MicrosoftEdgeDriver options to use.
+ * @return {!Builder} A self reference.
+ */
+ setEdgeOptions(options) {
+ this.edgeOptions_ = options;
+ return this;
+ }
+
+ /**
+ * Sets Safari specific {@linkplain safari.Options options} for drivers
+ * created by this builder. Any logging settings defined on the given options
+ * will take precedence over those set through {@link #setLoggingPrefs}.
+ *
+ * @param {!safari.Options} options The Safari options to use.
+ * @return {!Builder} A self reference.
+ */
+ setSafariOptions(options) {
+ this.safariOptions_ = options;
+ return this;
+ }
+
+ /**
+ * @return {safari.Options} the Safari specific options currently configured
+ * for this instance.
+ */
+ getSafariOptions() {
+ return this.safariOptions_;
+ }
+
+ /**
+ * Sets the control flow that created drivers should execute actions in. If
+ * the flow is never set, or is set to {@code null}, it will use the active
+ * flow at the time {@link #build()} is called.
+ * @param {promise.ControlFlow} flow The control flow to use, or
+ * {@code null} to
+ * @return {!Builder} A self reference.
+ */
+ setControlFlow(flow) {
+ this.flow_ = flow;
+ return this;
+ }
+
+ /**
+ * Creates a new WebDriver client based on this builder's current
+ * configuration.
+ *
+ * While this method will immediately return a new WebDriver instance, any
+ * commands issued against it will be deferred until the associated browser
+ * has been fully initialized. Users may call {@link #buildAsync()} to obtain
+ * a promise that will not be fulfilled until the browser has been created
+ * (the difference is purely in style).
+ *
+ * @return {!webdriver.WebDriver} A new WebDriver instance.
+ * @throws {Error} If the current configuration is invalid.
+ * @see #buildAsync()
+ */
+ build() {
+ // Create a copy for any changes we may need to make based on the current
+ // environment.
+ var capabilities = new Capabilities(this.capabilities_);
+
+ var browser;
+ if (!this.ignoreEnv_ && process.env.SELENIUM_BROWSER) {
+ browser = process.env.SELENIUM_BROWSER.split(/:/, 3);
+ capabilities.set(Capability.BROWSER_NAME, browser[0]);
+ capabilities.set(Capability.VERSION, browser[1] || null);
+ capabilities.set(Capability.PLATFORM, browser[2] || null);
+ }
+
+ browser = capabilities.get(Capability.BROWSER_NAME);
+
+ if (typeof browser !== 'string') {
+ throw TypeError(
+ `Target browser must be a string, but is <${typeof browser}>;` +
+ ' did you forget to call forBrowser()?');
+ }
+
+ if (browser === 'ie') {
+ browser = Browser.INTERNET_EXPLORER;
+ }
+
+ // Apply browser specific overrides.
+ if (browser === Browser.CHROME && this.chromeOptions_) {
+ capabilities.merge(this.chromeOptions_.toCapabilities());
+
+ } else if (browser === Browser.FIREFOX && this.firefoxOptions_) {
+ capabilities.merge(this.firefoxOptions_.toCapabilities());
+
+ } else if (browser === Browser.INTERNET_EXPLORER && this.ieOptions_) {
+ capabilities.merge(this.ieOptions_.toCapabilities());
+
+ } else if (browser === Browser.OPERA && this.operaOptions_) {
+ capabilities.merge(this.operaOptions_.toCapabilities());
+
+ } else if (browser === Browser.SAFARI && this.safariOptions_) {
+ capabilities.merge(this.safariOptions_.toCapabilities());
+
+ } else if (browser === Browser.EDGE && this.edgeOptions_) {
+ capabilities.merge(this.edgeOptions_.toCapabilities());
+ }
+
+ // Check for a remote browser.
+ let url = this.url_;
+ if (!this.ignoreEnv_) {
+ if (process.env.SELENIUM_REMOTE_URL) {
+ url = process.env.SELENIUM_REMOTE_URL;
+ } else if (process.env.SELENIUM_SERVER_JAR) {
+ url = startSeleniumServer(process.env.SELENIUM_SERVER_JAR);
+ }
+ }
+
+ if (url) {
+ let client = Promise.resolve(url)
+ .then(url => new _http.HttpClient(url, this.agent_, this.proxy_));
+ let executor = new _http.Executor(client);
+
+ if (browser === Browser.CHROME) {
+ const driver = ensureFileDetectorsAreEnabled(chrome.Driver);
+ return new driver(capabilities, null, this.flow_, executor);
+ }
+
+ if (browser === Browser.FIREFOX) {
+ const driver = ensureFileDetectorsAreEnabled(firefox.Driver);
+ return new driver(capabilities, executor, this.flow_);
+ }
+
+ return WebDriver.createSession(executor, capabilities, this.flow_);
+ }
+
+ // Check for a native browser.
+ switch (browser) {
+ case Browser.CHROME:
+ return new chrome.Driver(capabilities, null, this.flow_);
+
+ case Browser.FIREFOX:
+ return new firefox.Driver(capabilities, null, this.flow_);
+
+ case Browser.INTERNET_EXPLORER:
+ return new ie.Driver(capabilities, this.flow_);
+
+ case Browser.EDGE:
+ return new edge.Driver(capabilities, null, this.flow_);
+
+ case Browser.OPERA:
+ return new opera.Driver(capabilities, null, this.flow_);
+
+ case Browser.PHANTOM_JS:
+ return new phantomjs.Driver(capabilities, this.flow_);
+
+ case Browser.SAFARI:
+ return new safari.Driver(capabilities, this.flow_);
+
+ default:
+ throw new Error('Do not know how to build driver: ' + browser
+ + '; did you forget to call usingServer(url)?');
+ }
+ }
+
+ /**
+ * Creates a new WebDriver client based on this builder's current
+ * configuration. This method returns a promise that will not be fulfilled
+ * until the new browser session has been fully initialized.
+ *
+ * __Note:__ this method is purely a convenience wrapper around
+ * {@link #build()}.
+ *
+ * @return {!promise.Promise<!webdriver.WebDriver>} A promise that will be
+ * fulfilled with the newly created WebDriver instance once the browser
+ * has been fully initialized.
+ * @see #build()
+ */
+ buildAsync() {
+ let driver = this.build();
+ return driver.getSession().then(() => driver);
+ }
+}
+
+
+// PUBLIC API
+
+
+exports.ActionSequence = actions.ActionSequence;
+exports.Browser = capabilities.Browser;
+exports.Builder = Builder;
+exports.Button = input.Button;
+exports.By = by.By;
+exports.Capabilities = capabilities.Capabilities;
+exports.Capability = capabilities.Capability;
+exports.Condition = webdriver.Condition;
+exports.EventEmitter = events.EventEmitter;
+exports.FileDetector = input.FileDetector;
+exports.Key = input.Key;
+exports.Session = session.Session;
+exports.WebDriver = webdriver.WebDriver;
+exports.WebElement = webdriver.WebElement;
+exports.WebElementCondition = webdriver.WebElementCondition;
+exports.WebElementPromise = webdriver.WebElementPromise;
+exports.error = error;
+exports.logging = logging;
+exports.promise = promise;
+exports.until = until;
diff --git a/node_modules/selenium-webdriver/io/exec.js b/node_modules/selenium-webdriver/io/exec.js
new file mode 100644
index 000000000..948749e83
--- /dev/null
+++ b/node_modules/selenium-webdriver/io/exec.js
@@ -0,0 +1,153 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+const childProcess = require('child_process');
+
+
+/**
+ * A hash with configuration options for an executed command.
+ *
+ * - `args` - Command line arguments.
+ * - `env` - Command environment; will inherit from the current process if
+ * missing.
+ * - `stdio` - IO configuration for the spawned server process. For more
+ * information, refer to the documentation of `child_process.spawn`.
+ *
+ * @typedef {{
+ * args: (!Array<string>|undefined),
+ * env: (!Object<string, string>|undefined),
+ * stdio: (string|!Array<string|number|!stream.Stream|null|undefined>|
+ * undefined)
+ * }}
+ */
+var Options;
+
+
+/**
+ * Describes a command's termination conditions.
+ */
+class Result {
+ /**
+ * @param {?number} code The exit code, or {@code null} if the command did not
+ * exit normally.
+ * @param {?string} signal The signal used to kill the command, or
+ * {@code null}.
+ */
+ constructor(code, signal) {
+ /** @type {?number} */
+ this.code = code;
+
+ /** @type {?string} */
+ this.signal = signal;
+ }
+
+ /** @override */
+ toString() {
+ return `Result(code=${this.code}, signal=${this.signal})`;
+ }
+}
+
+
+const COMMAND_RESULT = /** !WeakMap<!Command, !Promise<!Result>> */new WeakMap;
+const KILL_HOOK = /** !WeakMap<!Command, function(string)> */new WeakMap;
+
+/**
+ * Represents a command running in a sub-process.
+ */
+class Command {
+ /**
+ * @param {!Promise<!Result>} result The command result.
+ * @param {function(string)} onKill The function to call when {@link #kill()}
+ * is called.
+ */
+ constructor(result, onKill) {
+ COMMAND_RESULT.set(this, result);
+ KILL_HOOK.set(this, onKill);
+ }
+
+ /**
+ * @return {!Promise<!Result>} A promise for the result of this
+ * command.
+ */
+ result() {
+ return /** @type {!Promise<!Result>} */(COMMAND_RESULT.get(this));
+ }
+
+ /**
+ * Sends a signal to the underlying process.
+ * @param {string=} opt_signal The signal to send; defaults to `SIGTERM`.
+ */
+ kill(opt_signal) {
+ KILL_HOOK.get(this)(opt_signal || 'SIGTERM');
+ }
+}
+
+
+// PUBLIC API
+
+
+/**
+ * Spawns a child process. The returned {@link Command} may be used to wait
+ * for the process result or to send signals to the process.
+ *
+ * @param {string} command The executable to spawn.
+ * @param {Options=} opt_options The command options.
+ * @return {!Command} The launched command.
+ */
+module.exports = function exec(command, opt_options) {
+ var options = opt_options || {};
+
+ var proc = childProcess.spawn(command, options.args || [], {
+ env: options.env || process.env,
+ stdio: options.stdio || 'ignore'
+ });
+
+ // This process should not wait on the spawned child, however, we do
+ // want to ensure the child is killed when this process exits.
+ proc.unref();
+ process.once('exit', onProcessExit);
+
+ let result = new Promise(resolve => {
+ proc.once('exit', (code, signal) => {
+ proc = null;
+ process.removeListener('exit', onProcessExit);
+ resolve(new Result(code, signal));
+ });
+ });
+ return new Command(result, killCommand);
+
+ function onProcessExit() {
+ killCommand('SIGTERM');
+ }
+
+ function killCommand(signal) {
+ process.removeListener('exit', onProcessExit);
+ if (proc) {
+ proc.kill(signal);
+ proc = null;
+ }
+ }
+};
+
+// Exported to improve generated API documentation.
+
+module.exports.Command = Command;
+/** @typedef {!Options} */
+module.exports.Options = Options;
+module.exports.Result = Result;
diff --git a/node_modules/selenium-webdriver/io/index.js b/node_modules/selenium-webdriver/io/index.js
new file mode 100644
index 000000000..17aceac47
--- /dev/null
+++ b/node_modules/selenium-webdriver/io/index.js
@@ -0,0 +1,300 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+var fs = require('fs'),
+ path = require('path'),
+ rimraf = require('rimraf'),
+ tmp = require('tmp');
+
+
+/**
+ * @param {!Function} fn .
+ * @return {!Promise<T>} .
+ * @template T
+ */
+function checkedCall(fn) {
+ return new Promise((resolve, reject) => {
+ try {
+ fn((err, value) => {
+ if (err) {
+ reject(err);
+ } else {
+ resolve(value);
+ }
+ });
+ } catch (e) {
+ reject(e);
+ }
+ });
+}
+
+
+
+// PUBLIC API
+
+
+
+/**
+ * Recursively removes a directory and all of its contents. This is equivalent
+ * to {@code rm -rf} on a POSIX system.
+ * @param {string} dirPath Path to the directory to remove.
+ * @return {!Promise} A promise to be resolved when the operation has
+ * completed.
+ */
+exports.rmDir = function(dirPath) {
+ return new Promise(function(fulfill, reject) {
+ var numAttempts = 0;
+ attemptRm();
+ function attemptRm() {
+ numAttempts += 1;
+ rimraf(dirPath, function(err) {
+ if (err) {
+ if (err.code && err.code === 'ENOTEMPTY' && numAttempts < 2) {
+ attemptRm();
+ return;
+ }
+ reject(err);
+ } else {
+ fulfill();
+ }
+ });
+ }
+ });
+};
+
+
+/**
+ * Copies one file to another.
+ * @param {string} src The source file.
+ * @param {string} dst The destination file.
+ * @return {!Promise<string>} A promise for the copied file's path.
+ */
+exports.copy = function(src, dst) {
+ return new Promise(function(fulfill, reject) {
+ var rs = fs.createReadStream(src);
+ rs.on('error', reject);
+ rs.on('end', () => fulfill(dst));
+
+ var ws = fs.createWriteStream(dst);
+ ws.on('error', reject);
+
+ rs.pipe(ws);
+ });
+};
+
+
+/**
+ * Recursively copies the contents of one directory to another.
+ * @param {string} src The source directory to copy.
+ * @param {string} dst The directory to copy into.
+ * @param {(RegExp|function(string): boolean)=} opt_exclude An exclusion filter
+ * as either a regex or predicate function. All files matching this filter
+ * will not be copied.
+ * @return {!Promise<string>} A promise for the destination
+ * directory's path once all files have been copied.
+ */
+exports.copyDir = function(src, dst, opt_exclude) {
+ var predicate = opt_exclude;
+ if (opt_exclude && typeof opt_exclude !== 'function') {
+ predicate = function(p) {
+ return !opt_exclude.test(p);
+ };
+ }
+
+ // TODO(jleyba): Make this function completely async.
+ if (!fs.existsSync(dst)) {
+ fs.mkdirSync(dst);
+ }
+
+ var files = fs.readdirSync(src);
+ files = files.map(function(file) {
+ return path.join(src, file);
+ });
+
+ if (predicate) {
+ files = files.filter(/** @type {function(string): boolean} */(predicate));
+ }
+
+ var results = [];
+ files.forEach(function(file) {
+ var stats = fs.statSync(file);
+ var target = path.join(dst, path.basename(file));
+
+ if (stats.isDirectory()) {
+ if (!fs.existsSync(target)) {
+ fs.mkdirSync(target, stats.mode);
+ }
+ results.push(exports.copyDir(file, target, predicate));
+ } else {
+ results.push(exports.copy(file, target));
+ }
+ });
+
+ return Promise.all(results).then(() => dst);
+};
+
+
+/**
+ * Tests if a file path exists.
+ * @param {string} aPath The path to test.
+ * @return {!Promise<boolean>} A promise for whether the file exists.
+ */
+exports.exists = function(aPath) {
+ return new Promise(function(fulfill, reject) {
+ let type = typeof aPath;
+ if (type !== 'string') {
+ reject(TypeError(`expected string path, but got ${type}`));
+ } else {
+ fs.exists(aPath, fulfill);
+ }
+ });
+};
+
+
+/**
+ * Calls `stat(2)`.
+ * @param {string} aPath The path to stat.
+ * @return {!Promise<!fs.Stats>} A promise for the file stats.
+ */
+exports.stat = function stat(aPath) {
+ return checkedCall(callback => fs.stat(aPath, callback));
+};
+
+
+/**
+ * Deletes a name from the filesystem and possibly the file it refers to. Has
+ * no effect if the file does not exist.
+ * @param {string} aPath The path to remove.
+ * @return {!Promise} A promise for when the file has been removed.
+ */
+exports.unlink = function(aPath) {
+ return new Promise(function(fulfill, reject) {
+ fs.exists(aPath, function(exists) {
+ if (exists) {
+ fs.unlink(aPath, function(err) {
+ err && reject(err) || fulfill();
+ });
+ } else {
+ fulfill();
+ }
+ });
+ });
+};
+
+
+/**
+ * @return {!Promise<string>} A promise for the path to a temporary directory.
+ * @see https://www.npmjs.org/package/tmp
+ */
+exports.tmpDir = function() {
+ return checkedCall(tmp.dir);
+};
+
+
+/**
+ * @param {{postfix: string}=} opt_options Temporary file options.
+ * @return {!Promise<string>} A promise for the path to a temporary file.
+ * @see https://www.npmjs.org/package/tmp
+ */
+exports.tmpFile = function(opt_options) {
+ return checkedCall(callback => {
+ // |tmp.file| checks arguments length to detect options rather than doing a
+ // truthy check, so we must only pass options if there are some to pass.
+ if (opt_options) {
+ tmp.file(opt_options, callback);
+ } else {
+ tmp.file(callback);
+ }
+ });
+};
+
+
+/**
+ * Searches the {@code PATH} environment variable for the given file.
+ * @param {string} file The file to locate on the PATH.
+ * @param {boolean=} opt_checkCwd Whether to always start with the search with
+ * the current working directory, regardless of whether it is explicitly
+ * listed on the PATH.
+ * @return {?string} Path to the located file, or {@code null} if it could
+ * not be found.
+ */
+exports.findInPath = function(file, opt_checkCwd) {
+ let dirs = [];
+ if (opt_checkCwd) {
+ dirs.push(process.cwd());
+ }
+ dirs.push.apply(dirs, process.env['PATH'].split(path.delimiter));
+
+ let foundInDir = dirs.find(dir => {
+ let tmp = path.join(dir, file);
+ try {
+ let stats = fs.statSync(tmp);
+ return stats.isFile() && !stats.isDirectory();
+ } catch (ex) {
+ return false;
+ }
+ });
+
+ return foundInDir ? path.join(foundInDir, file) : null;
+};
+
+
+/**
+ * Reads the contents of the given file.
+ *
+ * @param {string} aPath Path to the file to read.
+ * @return {!Promise<!Buffer>} A promise that will resolve with a buffer of the
+ * file contents.
+ */
+exports.read = function(aPath) {
+ return checkedCall(callback => fs.readFile(aPath, callback));
+};
+
+
+/**
+ * Writes to a file.
+ *
+ * @param {string} aPath Path to the file to write to.
+ * @param {(string|!Buffer)} data The data to write.
+ * @return {!Promise} A promise that will resolve when the operation has
+ * completed.
+ */
+exports.write = function(aPath, data) {
+ return checkedCall(callback => fs.writeFile(aPath, data, callback));
+};
+
+
+/**
+ * Creates a directory.
+ *
+ * @param {string} aPath The directory path.
+ * @return {!Promise<string>} A promise that will resolve with the path of the
+ * created directory.
+ */
+exports.mkdir = function(aPath) {
+ return checkedCall(callback => {
+ fs.mkdir(aPath, undefined, err => {
+ if (err && err.code !== 'EEXIST') {
+ callback(err);
+ } else {
+ callback(null, aPath);
+ }
+ });
+ });
+};
diff --git a/node_modules/selenium-webdriver/lib/README b/node_modules/selenium-webdriver/lib/README
new file mode 100644
index 000000000..583293864
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/README
@@ -0,0 +1,6 @@
+This directory contains modules internal to selenium-webdriver that are not
+intended for general consumption. They may change at any time.
+
+With the exception of the test/ directory, all files under this directory
+may only depend on built-in JavaScript features and other modules in the
+directory.
diff --git a/node_modules/selenium-webdriver/lib/actions.js b/node_modules/selenium-webdriver/lib/actions.js
new file mode 100644
index 000000000..7200b08d6
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/actions.js
@@ -0,0 +1,596 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+const command = require('./command');
+const error = require('./error');
+const input = require('./input');
+
+
+/**
+ * @param {!IArrayLike} args .
+ * @return {!Array} .
+ */
+function flatten(args) {
+ let result = [];
+ for (let i = 0; i < args.length; i++) {
+ let element = args[i];
+ if (Array.isArray(element)) {
+ result.push.apply(result, flatten(element));
+ } else {
+ result.push(element);
+ }
+ }
+ return result;
+}
+
+
+const MODIFIER_KEYS = new Set([
+ input.Key.ALT,
+ input.Key.CONTROL,
+ input.Key.SHIFT,
+ input.Key.COMMAND
+]);
+
+
+/**
+ * Checks that a key is a modifier key.
+ * @param {!input.Key} key The key to check.
+ * @throws {error.InvalidArgumentError} If the key is not a modifier key.
+ * @private
+ */
+function checkModifierKey(key) {
+ if (!MODIFIER_KEYS.has(key)) {
+ throw new error.InvalidArgumentError('Not a modifier key');
+ }
+}
+
+
+/**
+ * Class for defining sequences of complex user interactions. Each sequence
+ * will not be executed until {@link #perform} is called.
+ *
+ * Example:
+ *
+ * new ActionSequence(driver).
+ * keyDown(Key.SHIFT).
+ * click(element1).
+ * click(element2).
+ * dragAndDrop(element3, element4).
+ * keyUp(Key.SHIFT).
+ * perform();
+ *
+ */
+class ActionSequence {
+ /**
+ * @param {!./webdriver.WebDriver} driver The driver that should be used to
+ * perform this action sequence.
+ */
+ constructor(driver) {
+ /** @private {!./webdriver.WebDriver} */
+ this.driver_ = driver;
+
+ /** @private {!Array<{description: string, command: !command.Command}>} */
+ this.actions_ = [];
+ }
+
+ /**
+ * Schedules an action to be executed each time {@link #perform} is called on
+ * this instance.
+ *
+ * @param {string} description A description of the command.
+ * @param {!command.Command} command The command.
+ * @private
+ */
+ schedule_(description, command) {
+ this.actions_.push({
+ description: description,
+ command: command
+ });
+ }
+
+ /**
+ * Executes this action sequence.
+ *
+ * @return {!./promise.Promise} A promise that will be resolved once
+ * this sequence has completed.
+ */
+ perform() {
+ // Make a protected copy of the scheduled actions. This will protect against
+ // users defining additional commands before this sequence is actually
+ // executed.
+ let actions = this.actions_.concat();
+ let driver = this.driver_;
+ return driver.controlFlow().execute(function() {
+ actions.forEach(function(action) {
+ driver.schedule(action.command, action.description);
+ });
+ }, 'ActionSequence.perform');
+ }
+
+ /**
+ * Moves the mouse. The location to move to may be specified in terms of the
+ * mouse's current location, an offset relative to the top-left corner of an
+ * element, or an element (in which case the middle of the element is used).
+ *
+ * @param {(!./webdriver.WebElement|{x: number, y: number})} location The
+ * location to drag to, as either another WebElement or an offset in
+ * pixels.
+ * @param {{x: number, y: number}=} opt_offset If the target {@code location}
+ * is defined as a {@link ./webdriver.WebElement}, this parameter defines
+ * an offset within that element. The offset should be specified in pixels
+ * relative to the top-left corner of the element's bounding box. If
+ * omitted, the element's center will be used as the target offset.
+ * @return {!ActionSequence} A self reference.
+ */
+ mouseMove(location, opt_offset) {
+ let cmd = new command.Command(command.Name.MOVE_TO);
+
+ if (typeof location.x === 'number') {
+ setOffset(/** @type {{x: number, y: number}} */(location));
+ } else {
+ cmd.setParameter('element', location.getId());
+ if (opt_offset) {
+ setOffset(opt_offset);
+ }
+ }
+
+ this.schedule_('mouseMove', cmd);
+ return this;
+
+ /** @param {{x: number, y: number}} offset The offset to use. */
+ function setOffset(offset) {
+ cmd.setParameter('xoffset', offset.x || 0);
+ cmd.setParameter('yoffset', offset.y || 0);
+ }
+ }
+
+ /**
+ * Schedules a mouse action.
+ * @param {string} description A simple descriptive label for the scheduled
+ * action.
+ * @param {!command.Name} commandName The name of the command.
+ * @param {(./webdriver.WebElement|input.Button)=} opt_elementOrButton Either
+ * the element to interact with or the button to click with.
+ * Defaults to {@link input.Button.LEFT} if neither an element nor
+ * button is specified.
+ * @param {input.Button=} opt_button The button to use. Defaults to
+ * {@link input.Button.LEFT}. Ignored if the previous argument is
+ * provided as a button.
+ * @return {!ActionSequence} A self reference.
+ * @private
+ */
+ scheduleMouseAction_(
+ description, commandName, opt_elementOrButton, opt_button) {
+ let button;
+ if (typeof opt_elementOrButton === 'number') {
+ button = opt_elementOrButton;
+ } else {
+ if (opt_elementOrButton) {
+ this.mouseMove(
+ /** @type {!./webdriver.WebElement} */ (opt_elementOrButton));
+ }
+ button = opt_button !== void(0) ? opt_button : input.Button.LEFT;
+ }
+
+ let cmd = new command.Command(commandName).
+ setParameter('button', button);
+ this.schedule_(description, cmd);
+ return this;
+ }
+
+ /**
+ * Presses a mouse button. The mouse button will not be released until
+ * {@link #mouseUp} is called, regardless of whether that call is made in this
+ * sequence or another. The behavior for out-of-order events (e.g. mouseDown,
+ * click) is undefined.
+ *
+ * If an element is provided, the mouse will first be moved to the center
+ * of that element. This is equivalent to:
+ *
+ * sequence.mouseMove(element).mouseDown()
+ *
+ * Warning: this method currently only supports the left mouse button. See
+ * [issue 4047](http://code.google.com/p/selenium/issues/detail?id=4047).
+ *
+ * @param {(./webdriver.WebElement|input.Button)=} opt_elementOrButton Either
+ * the element to interact with or the button to click with.
+ * Defaults to {@link input.Button.LEFT} if neither an element nor
+ * button is specified.
+ * @param {input.Button=} opt_button The button to use. Defaults to
+ * {@link input.Button.LEFT}. Ignored if a button is provided as the
+ * first argument.
+ * @return {!ActionSequence} A self reference.
+ */
+ mouseDown(opt_elementOrButton, opt_button) {
+ return this.scheduleMouseAction_('mouseDown',
+ command.Name.MOUSE_DOWN, opt_elementOrButton, opt_button);
+ }
+
+ /**
+ * Releases a mouse button. Behavior is undefined for calling this function
+ * without a previous call to {@link #mouseDown}.
+ *
+ * If an element is provided, the mouse will first be moved to the center
+ * of that element. This is equivalent to:
+ *
+ * sequence.mouseMove(element).mouseUp()
+ *
+ * Warning: this method currently only supports the left mouse button. See
+ * [issue 4047](http://code.google.com/p/selenium/issues/detail?id=4047).
+ *
+ * @param {(./webdriver.WebElement|input.Button)=} opt_elementOrButton Either
+ * the element to interact with or the button to click with.
+ * Defaults to {@link input.Button.LEFT} if neither an element nor
+ * button is specified.
+ * @param {input.Button=} opt_button The button to use. Defaults to
+ * {@link input.Button.LEFT}. Ignored if a button is provided as the
+ * first argument.
+ * @return {!ActionSequence} A self reference.
+ */
+ mouseUp(opt_elementOrButton, opt_button) {
+ return this.scheduleMouseAction_('mouseUp',
+ command.Name.MOUSE_UP, opt_elementOrButton, opt_button);
+ }
+
+ /**
+ * Convenience function for performing a "drag and drop" manuever. The target
+ * element may be moved to the location of another element, or by an offset (in
+ * pixels).
+ *
+ * @param {!./webdriver.WebElement} element The element to drag.
+ * @param {(!./webdriver.WebElement|{x: number, y: number})} location The
+ * location to drag to, either as another WebElement or an offset in
+ * pixels.
+ * @return {!ActionSequence} A self reference.
+ */
+ dragAndDrop(element, location) {
+ return this.mouseDown(element).mouseMove(location).mouseUp();
+ }
+
+ /**
+ * Clicks a mouse button.
+ *
+ * If an element is provided, the mouse will first be moved to the center
+ * of that element. This is equivalent to:
+ *
+ * sequence.mouseMove(element).click()
+ *
+ * @param {(./webdriver.WebElement|input.Button)=} opt_elementOrButton Either
+ * the element to interact with or the button to click with.
+ * Defaults to {@link input.Button.LEFT} if neither an element nor
+ * button is specified.
+ * @param {input.Button=} opt_button The button to use. Defaults to
+ * {@link input.Button.LEFT}. Ignored if a button is provided as the
+ * first argument.
+ * @return {!ActionSequence} A self reference.
+ */
+ click(opt_elementOrButton, opt_button) {
+ return this.scheduleMouseAction_('click',
+ command.Name.CLICK, opt_elementOrButton, opt_button);
+ }
+
+ /**
+ * Double-clicks a mouse button.
+ *
+ * If an element is provided, the mouse will first be moved to the center of
+ * that element. This is equivalent to:
+ *
+ * sequence.mouseMove(element).doubleClick()
+ *
+ * Warning: this method currently only supports the left mouse button. See
+ * [issue 4047](http://code.google.com/p/selenium/issues/detail?id=4047).
+ *
+ * @param {(./webdriver.WebElement|input.Button)=} opt_elementOrButton Either
+ * the element to interact with or the button to click with.
+ * Defaults to {@link input.Button.LEFT} if neither an element nor
+ * button is specified.
+ * @param {input.Button=} opt_button The button to use. Defaults to
+ * {@link input.Button.LEFT}. Ignored if a button is provided as the
+ * first argument.
+ * @return {!ActionSequence} A self reference.
+ */
+ doubleClick(opt_elementOrButton, opt_button) {
+ return this.scheduleMouseAction_('doubleClick',
+ command.Name.DOUBLE_CLICK, opt_elementOrButton, opt_button);
+ }
+
+ /**
+ * Schedules a keyboard action.
+ *
+ * @param {string} description A simple descriptive label for the scheduled
+ * action.
+ * @param {!Array<(string|!input.Key)>} keys The keys to send.
+ * @return {!ActionSequence} A self reference.
+ * @private
+ */
+ scheduleKeyboardAction_(description, keys) {
+ let cmd = new command.Command(command.Name.SEND_KEYS_TO_ACTIVE_ELEMENT)
+ .setParameter('value', keys);
+ this.schedule_(description, cmd);
+ return this;
+ }
+
+ /**
+ * Performs a modifier key press. The modifier key is <em>not released</em>
+ * until {@link #keyUp} or {@link #sendKeys} is called. The key press will be
+ * targetted at the currently focused element.
+ *
+ * @param {!input.Key} key The modifier key to push. Must be one of
+ * {ALT, CONTROL, SHIFT, COMMAND, META}.
+ * @return {!ActionSequence} A self reference.
+ * @throws {error.InvalidArgumentError} If the key is not a valid modifier
+ * key.
+ */
+ keyDown(key) {
+ checkModifierKey(key);
+ return this.scheduleKeyboardAction_('keyDown', [key]);
+ }
+
+ /**
+ * Performs a modifier key release. The release is targetted at the currently
+ * focused element.
+ * @param {!input.Key} key The modifier key to release. Must be one of
+ * {ALT, CONTROL, SHIFT, COMMAND, META}.
+ * @return {!ActionSequence} A self reference.
+ * @throws {error.InvalidArgumentError} If the key is not a valid modifier
+ * key.
+ */
+ keyUp(key) {
+ checkModifierKey(key);
+ return this.scheduleKeyboardAction_('keyUp', [key]);
+ }
+
+ /**
+ * Simulates typing multiple keys. Each modifier key encountered in the
+ * sequence will not be released until it is encountered again. All key events
+ * will be targetted at the currently focused element.
+ *
+ * @param {...(string|!input.Key|!Array<(string|!input.Key)>)} var_args
+ * The keys to type.
+ * @return {!ActionSequence} A self reference.
+ * @throws {Error} If the key is not a valid modifier key.
+ */
+ sendKeys(var_args) {
+ let keys = flatten(arguments);
+ return this.scheduleKeyboardAction_('sendKeys', keys);
+ }
+}
+
+
+/**
+ * Class for defining sequences of user touch interactions. Each sequence
+ * will not be executed until {@link #perform} is called.
+ *
+ * Example:
+ *
+ * new TouchSequence(driver).
+ * tapAndHold({x: 0, y: 0}).
+ * move({x: 3, y: 4}).
+ * release({x: 10, y: 10}).
+ * perform();
+ *
+ */
+class TouchSequence {
+ /**
+ * @param {!./webdriver.WebDriver} driver The driver that should be used to
+ * perform this action sequence.
+ */
+ constructor(driver) {
+ /** @private {!./webdriver.WebDriver} */
+ this.driver_ = driver;
+
+ /** @private {!Array<{description: string, command: !command.Command}>} */
+ this.actions_ = [];
+ }
+
+ /**
+ * Schedules an action to be executed each time {@link #perform} is called on
+ * this instance.
+ * @param {string} description A description of the command.
+ * @param {!command.Command} command The command.
+ * @private
+ */
+ schedule_(description, command) {
+ this.actions_.push({
+ description: description,
+ command: command
+ });
+ }
+
+ /**
+ * Executes this action sequence.
+ * @return {!./promise.Promise} A promise that will be resolved once
+ * this sequence has completed.
+ */
+ perform() {
+ // Make a protected copy of the scheduled actions. This will protect against
+ // users defining additional commands before this sequence is actually
+ // executed.
+ let actions = this.actions_.concat();
+ let driver = this.driver_;
+ return driver.controlFlow().execute(function() {
+ actions.forEach(function(action) {
+ driver.schedule(action.command, action.description);
+ });
+ }, 'TouchSequence.perform');
+ }
+
+ /**
+ * Taps an element.
+ *
+ * @param {!./webdriver.WebElement} elem The element to tap.
+ * @return {!TouchSequence} A self reference.
+ */
+ tap(elem) {
+ let cmd = new command.Command(command.Name.TOUCH_SINGLE_TAP).
+ setParameter('element', elem.getId());
+
+ this.schedule_('tap', cmd);
+ return this;
+ }
+
+ /**
+ * Double taps an element.
+ *
+ * @param {!./webdriver.WebElement} elem The element to double tap.
+ * @return {!TouchSequence} A self reference.
+ */
+ doubleTap(elem) {
+ let cmd = new command.Command(command.Name.TOUCH_DOUBLE_TAP).
+ setParameter('element', elem.getId());
+
+ this.schedule_('doubleTap', cmd);
+ return this;
+ }
+
+ /**
+ * Long press on an element.
+ *
+ * @param {!./webdriver.WebElement} elem The element to long press.
+ * @return {!TouchSequence} A self reference.
+ */
+ longPress(elem) {
+ let cmd = new command.Command(command.Name.TOUCH_LONG_PRESS).
+ setParameter('element', elem.getId());
+
+ this.schedule_('longPress', cmd);
+ return this;
+ }
+
+ /**
+ * Touch down at the given location.
+ *
+ * @param {{x: number, y: number}} location The location to touch down at.
+ * @return {!TouchSequence} A self reference.
+ */
+ tapAndHold(location) {
+ let cmd = new command.Command(command.Name.TOUCH_DOWN).
+ setParameter('x', location.x).
+ setParameter('y', location.y);
+
+ this.schedule_('tapAndHold', cmd);
+ return this;
+ }
+
+ /**
+ * Move a held {@linkplain #tapAndHold touch} to the specified location.
+ *
+ * @param {{x: number, y: number}} location The location to move to.
+ * @return {!TouchSequence} A self reference.
+ */
+ move(location) {
+ let cmd = new command.Command(command.Name.TOUCH_MOVE).
+ setParameter('x', location.x).
+ setParameter('y', location.y);
+
+ this.schedule_('move', cmd);
+ return this;
+ }
+
+ /**
+ * Release a held {@linkplain #tapAndHold touch} at the specified location.
+ *
+ * @param {{x: number, y: number}} location The location to release at.
+ * @return {!TouchSequence} A self reference.
+ */
+ release(location) {
+ let cmd = new command.Command(command.Name.TOUCH_UP).
+ setParameter('x', location.x).
+ setParameter('y', location.y);
+
+ this.schedule_('release', cmd);
+ return this;
+ }
+
+ /**
+ * Scrolls the touch screen by the given offset.
+ *
+ * @param {{x: number, y: number}} offset The offset to scroll to.
+ * @return {!TouchSequence} A self reference.
+ */
+ scroll(offset) {
+ let cmd = new command.Command(command.Name.TOUCH_SCROLL).
+ setParameter('xoffset', offset.x).
+ setParameter('yoffset', offset.y);
+
+ this.schedule_('scroll', cmd);
+ return this;
+ }
+
+ /**
+ * Scrolls the touch screen, starting on `elem` and moving by the specified
+ * offset.
+ *
+ * @param {!./webdriver.WebElement} elem The element where scroll starts.
+ * @param {{x: number, y: number}} offset The offset to scroll to.
+ * @return {!TouchSequence} A self reference.
+ */
+ scrollFromElement(elem, offset) {
+ let cmd = new command.Command(command.Name.TOUCH_SCROLL).
+ setParameter('element', elem.getId()).
+ setParameter('xoffset', offset.x).
+ setParameter('yoffset', offset.y);
+
+ this.schedule_('scrollFromElement', cmd);
+ return this;
+ }
+
+ /**
+ * Flick, starting anywhere on the screen, at speed xspeed and yspeed.
+ *
+ * @param {{xspeed: number, yspeed: number}} speed The speed to flick in each
+ direction, in pixels per second.
+ * @return {!TouchSequence} A self reference.
+ */
+ flick(speed) {
+ let cmd = new command.Command(command.Name.TOUCH_FLICK).
+ setParameter('xspeed', speed.xspeed).
+ setParameter('yspeed', speed.yspeed);
+
+ this.schedule_('flick', cmd);
+ return this;
+ }
+
+ /**
+ * Flick starting at elem and moving by x and y at specified speed.
+ *
+ * @param {!./webdriver.WebElement} elem The element where flick starts.
+ * @param {{x: number, y: number}} offset The offset to flick to.
+ * @param {number} speed The speed to flick at in pixels per second.
+ * @return {!TouchSequence} A self reference.
+ */
+ flickElement(elem, offset, speed) {
+ let cmd = new command.Command(command.Name.TOUCH_FLICK).
+ setParameter('element', elem.getId()).
+ setParameter('xoffset', offset.x).
+ setParameter('yoffset', offset.y).
+ setParameter('speed', speed);
+
+ this.schedule_('flickElement', cmd);
+ return this;
+ }
+}
+
+
+// PUBLIC API
+
+module.exports = {
+ ActionSequence: ActionSequence,
+ TouchSequence: TouchSequence,
+};
diff --git a/node_modules/selenium-webdriver/lib/by.js b/node_modules/selenium-webdriver/lib/by.js
new file mode 100644
index 000000000..ac448e683
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/by.js
@@ -0,0 +1,277 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+/**
+ * @fileoverview Factory methods for the supported locator strategies.
+ */
+
+/**
+ * Short-hand expressions for the primary element locator strategies.
+ * For example the following two statements are equivalent:
+ *
+ * var e1 = driver.findElement(By.id('foo'));
+ * var e2 = driver.findElement({id: 'foo'});
+ *
+ * Care should be taken when using JavaScript minifiers (such as the
+ * Closure compiler), as locator hashes will always be parsed using
+ * the un-obfuscated properties listed.
+ *
+ * @typedef {(
+ * {className: string}|
+ * {css: string}|
+ * {id: string}|
+ * {js: string}|
+ * {linkText: string}|
+ * {name: string}|
+ * {partialLinkText: string}|
+ * {tagName: string}|
+ * {xpath: string})}
+ */
+var ByHash;
+
+
+/**
+ * Error thrown if an invalid character is encountered while escaping a CSS
+ * identifier.
+ * @see https://drafts.csswg.org/cssom/#serialize-an-identifier
+ */
+class InvalidCharacterError extends Error {
+ constructor() {
+ super();
+ this.name = this.constructor.name;
+ }
+}
+
+
+/**
+ * Escapes a CSS string.
+ * @param {string} css the string to escape.
+ * @return {string} the escaped string.
+ * @throws {TypeError} if the input value is not a string.
+ * @throws {InvalidCharacterError} if the string contains an invalid character.
+ * @see https://drafts.csswg.org/cssom/#serialize-an-identifier
+ */
+function escapeCss(css) {
+ if (typeof css !== 'string') {
+ throw new TypeError('input must be a string');
+ }
+ let ret = '';
+ const n = css.length;
+ for (let i = 0; i < n; i++) {
+ const c = css.charCodeAt(i);
+ if (c == 0x0) {
+ throw new InvalidCharacterError();
+ }
+
+ if ((c >= 0x0001 && c <= 0x001F)
+ || c == 0x007F
+ || (i == 0 && c >= 0x0030 && c <= 0x0039)
+ || (i == 1 && c >= 0x0030 && c <= 0x0039
+ && css.charCodeAt(0) == 0x002D)) {
+ ret += '\\' + c.toString(16) + ' ';
+ continue;
+ }
+
+ if (i == 0 && c == 0x002D && n == 1) {
+ ret += '\\' + css.charAt(i);
+ continue;
+ }
+
+ if (c >= 0x0080
+ || c == 0x002D // -
+ || c == 0x005F // _
+ || (c >= 0x0030 && c <= 0x0039) // [0-9]
+ || (c >= 0x0041 && c <= 0x005A) // [A-Z]
+ || (c >= 0x0061 && c <= 0x007A)) { // [a-z]
+ ret += css.charAt(i);
+ continue;
+ }
+
+ ret += '\\' + css.charAt(i);
+ }
+ return ret;
+}
+
+
+/**
+ * Describes a mechanism for locating an element on the page.
+ * @final
+ */
+class By {
+ /**
+ * @param {string} using the name of the location strategy to use.
+ * @param {string} value the value to search for.
+ */
+ constructor(using, value) {
+ /** @type {string} */
+ this.using = using;
+
+ /** @type {string} */
+ this.value = value;
+ }
+
+ /**
+ * Locates elements that have a specific class name.
+ *
+ * @param {string} name The class name to search for.
+ * @return {!By} The new locator.
+ * @see http://www.w3.org/TR/2011/WD-html5-20110525/elements.html#classes
+ * @see http://www.w3.org/TR/CSS2/selector.html#class-html
+ */
+ static className(name) {
+ let names = name.split(/\s+/g)
+ .filter(s => s.length > 0)
+ .map(s => escapeCss(s));
+ return By.css('.' + names.join('.'));
+ }
+
+ /**
+ * Locates elements using a CSS selector.
+ *
+ * @param {string} selector The CSS selector to use.
+ * @return {!By} The new locator.
+ * @see http://www.w3.org/TR/CSS2/selector.html
+ */
+ static css(selector) {
+ return new By('css selector', selector);
+ }
+
+ /**
+ * Locates eleemnts by the ID attribute. This locator uses the CSS selector
+ * `*[id="$ID"]`, _not_ `document.getElementById`.
+ *
+ * @param {string} id The ID to search for.
+ * @return {!By} The new locator.
+ */
+ static id(id) {
+ return By.css('*[id="' + escapeCss(id) + '"]');
+ }
+
+ /**
+ * Locates link elements whose
+ * {@linkplain webdriver.WebElement#getText visible text} matches the given
+ * string.
+ *
+ * @param {string} text The link text to search for.
+ * @return {!By} The new locator.
+ */
+ static linkText(text) {
+ return new By('link text', text);
+ }
+
+ /**
+ * Locates an elements by evaluating a
+ * {@linkplain webdriver.WebDriver#executeScript JavaScript expression}.
+ * The result of this expression must be an element or list of elements.
+ *
+ * @param {!(string|Function)} script The script to execute.
+ * @param {...*} var_args The arguments to pass to the script.
+ * @return {function(!./webdriver.WebDriver): !./promise.Promise}
+ * A new JavaScript-based locator function.
+ */
+ static js(script, var_args) {
+ let args = Array.prototype.slice.call(arguments, 0);
+ return function(driver) {
+ return driver.executeScript.apply(driver, args);
+ };
+ }
+
+ /**
+ * Locates elements whose `name` attribute has the given value.
+ *
+ * @param {string} name The name attribute to search for.
+ * @return {!By} The new locator.
+ */
+ static name(name) {
+ return By.css('*[name="' + escapeCss(name) + '"]');
+ }
+
+ /**
+ * Locates link elements whose
+ * {@linkplain webdriver.WebElement#getText visible text} contains the given
+ * substring.
+ *
+ * @param {string} text The substring to check for in a link's visible text.
+ * @return {!By} The new locator.
+ */
+ static partialLinkText(text) {
+ return new By('partial link text', text);
+ }
+
+ /**
+ * Locates elements with a given tag name.
+ *
+ * @param {string} name The tag name to search for.
+ * @return {!By} The new locator.
+ * @deprecated Use {@link By.css() By.css(tagName)} instead.
+ */
+ static tagName(name) {
+ return By.css(name);
+ }
+
+ /**
+ * Locates elements matching a XPath selector. Care should be taken when
+ * using an XPath selector with a {@link webdriver.WebElement} as WebDriver
+ * will respect the context in the specified in the selector. For example,
+ * given the selector `//div`, WebDriver will search from the document root
+ * regardless of whether the locator was used with a WebElement.
+ *
+ * @param {string} xpath The XPath selector to use.
+ * @return {!By} The new locator.
+ * @see http://www.w3.org/TR/xpath/
+ */
+ static xpath(xpath) {
+ return new By('xpath', xpath);
+ }
+
+ /** @override */
+ toString() {
+ // The static By.name() overrides this.constructor.name. Shame...
+ return `By(${this.using}, ${this.value})`;
+ }
+}
+
+
+/**
+ * Checks if a value is a valid locator.
+ * @param {!(By|Function|ByHash)} locator The value to check.
+ * @return {!(By|Function)} The valid locator.
+ * @throws {TypeError} If the given value does not define a valid locator
+ * strategy.
+ */
+function check(locator) {
+ if (locator instanceof By || typeof locator === 'function') {
+ return locator;
+ }
+ for (let key in locator) {
+ if (locator.hasOwnProperty(key) && By.hasOwnProperty(key)) {
+ return By[key](locator[key]);
+ }
+ }
+ throw new TypeError('Invalid locator');
+}
+
+
+
+// PUBLIC API
+
+module.exports = {
+ By: By,
+ checkedLocator: check,
+};
diff --git a/node_modules/selenium-webdriver/lib/capabilities.js b/node_modules/selenium-webdriver/lib/capabilities.js
new file mode 100644
index 000000000..61396d54d
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/capabilities.js
@@ -0,0 +1,450 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+/**
+ * @fileoverview Defines types related to describing the capabilities of a
+ * WebDriver session.
+ */
+
+const Symbols = require('./symbols');
+
+
+/**
+ * Recognized browser names.
+ * @enum {string}
+ */
+const Browser = {
+ ANDROID: 'android',
+ CHROME: 'chrome',
+ EDGE: 'MicrosoftEdge',
+ FIREFOX: 'firefox',
+ IE: 'internet explorer',
+ INTERNET_EXPLORER: 'internet explorer',
+ IPAD: 'iPad',
+ IPHONE: 'iPhone',
+ OPERA: 'opera',
+ PHANTOM_JS: 'phantomjs',
+ SAFARI: 'safari',
+ HTMLUNIT: 'htmlunit'
+};
+
+
+/**
+ * Common Capability keys.
+ * @enum {string}
+ */
+const Capability = {
+
+ /**
+ * Indicates whether a driver should accept all SSL certs by default. This
+ * capability only applies when requesting a new session. To query whether
+ * a driver can handle insecure SSL certs, see {@link #SECURE_SSL}.
+ */
+ ACCEPT_SSL_CERTS: 'acceptSslCerts',
+
+
+ /**
+ * The browser name. Common browser names are defined in the {@link Browser}
+ * enum.
+ */
+ BROWSER_NAME: 'browserName',
+
+ /**
+ * Defines how elements should be scrolled into the viewport for interaction.
+ * This capability will be set to zero (0) if elements are aligned with the
+ * top of the viewport, or one (1) if aligned with the bottom. The default
+ * behavior is to align with the top of the viewport.
+ */
+ ELEMENT_SCROLL_BEHAVIOR: 'elementScrollBehavior',
+
+ /**
+ * Whether the driver is capable of handling modal alerts (e.g. alert,
+ * confirm, prompt). To define how a driver <i>should</i> handle alerts,
+ * use {@link #UNEXPECTED_ALERT_BEHAVIOR}.
+ */
+ HANDLES_ALERTS: 'handlesAlerts',
+
+ /**
+ * Key for the logging driver logging preferences.
+ */
+ LOGGING_PREFS: 'loggingPrefs',
+
+ /**
+ * Whether this session generates native events when simulating user input.
+ */
+ NATIVE_EVENTS: 'nativeEvents',
+
+ /**
+ * Describes the platform the browser is running on. Will be one of
+ * ANDROID, IOS, LINUX, MAC, UNIX, or WINDOWS. When <i>requesting</i> a
+ * session, ANY may be used to indicate no platform preference (this is
+ * semantically equivalent to omitting the platform capability).
+ */
+ PLATFORM: 'platform',
+
+ /**
+ * Describes the proxy configuration to use for a new WebDriver session.
+ */
+ PROXY: 'proxy',
+
+ /** Whether the driver supports changing the brower's orientation. */
+ ROTATABLE: 'rotatable',
+
+ /**
+ * Whether a driver is only capable of handling secure SSL certs. To request
+ * that a driver accept insecure SSL certs by default, use
+ * {@link #ACCEPT_SSL_CERTS}.
+ */
+ SECURE_SSL: 'secureSsl',
+
+ /** Whether the driver supports manipulating the app cache. */
+ SUPPORTS_APPLICATION_CACHE: 'applicationCacheEnabled',
+
+ /** Whether the driver supports locating elements with CSS selectors. */
+ SUPPORTS_CSS_SELECTORS: 'cssSelectorsEnabled',
+
+ /** Whether the browser supports JavaScript. */
+ SUPPORTS_JAVASCRIPT: 'javascriptEnabled',
+
+ /** Whether the driver supports controlling the browser's location info. */
+ SUPPORTS_LOCATION_CONTEXT: 'locationContextEnabled',
+
+ /** Whether the driver supports taking screenshots. */
+ TAKES_SCREENSHOT: 'takesScreenshot',
+
+ /**
+ * Defines how the driver should handle unexpected alerts. The value should
+ * be one of "accept", "dismiss", or "ignore.
+ */
+ UNEXPECTED_ALERT_BEHAVIOR: 'unexpectedAlertBehavior',
+
+ /** Defines the browser version. */
+ VERSION: 'version'
+};
+
+
+/**
+ * Describes how a proxy should be configured for a WebDriver session.
+ * @record
+ */
+function ProxyConfig() {}
+
+/**
+ * The proxy type. Must be one of {"manual", "pac", "system"}.
+ * @type {string}
+ */
+ProxyConfig.prototype.proxyType;
+
+/**
+ * URL for the PAC file to use. Only used if {@link #proxyType} is "pac".
+ * @type {(string|undefined)}
+ */
+ProxyConfig.prototype.proxyAutoconfigUrl;
+
+/**
+ * The proxy host for FTP requests. Only used if {@link #proxyType} is "manual".
+ * @type {(string|undefined)}
+ */
+ProxyConfig.prototype.ftpProxy;
+
+/**
+ * The proxy host for HTTP requests. Only used if {@link #proxyType} is
+ * "manual".
+ * @type {(string|undefined)}
+ */
+ProxyConfig.prototype.httpProxy;
+
+/**
+ * The proxy host for HTTPS requests. Only used if {@link #proxyType} is
+ * "manual".
+ * @type {(string|undefined)}
+ */
+ProxyConfig.prototype.sslProxy;
+
+/**
+ * A comma delimited list of hosts which should bypass all proxies. Only used if
+ * {@link #proxyType} is "manual".
+ * @type {(string|undefined)}
+ */
+ProxyConfig.prototype.noProxy;
+
+
+/**
+ * Converts a generic hash object to a map.
+ * @param {!Object<string, ?>} hash The hash object.
+ * @return {!Map<string, ?>} The converted map.
+ */
+function toMap(hash) {
+ let m = new Map;
+ for (let key in hash) {
+ if (hash.hasOwnProperty(key)) {
+ m.set(key, hash[key]);
+ }
+ }
+ return m;
+}
+
+
+/**
+ * Describes a set of capabilities for a WebDriver session.
+ */
+class Capabilities extends Map {
+ /**
+ * @param {(Capabilities|Map<string, ?>|Object)=} opt_other Another set of
+ * capabilities to initialize this instance from.
+ */
+ constructor(opt_other) {
+ if (opt_other && !(opt_other instanceof Map)) {
+ opt_other = toMap(opt_other);
+ }
+ super(opt_other);
+ }
+
+ /**
+ * @return {!Capabilities} A basic set of capabilities for Android.
+ */
+ static android() {
+ return new Capabilities()
+ .set(Capability.BROWSER_NAME, Browser.ANDROID)
+ .set(Capability.PLATFORM, 'ANDROID');
+ }
+
+ /**
+ * @return {!Capabilities} A basic set of capabilities for Chrome.
+ */
+ static chrome() {
+ return new Capabilities().set(Capability.BROWSER_NAME, Browser.CHROME);
+ }
+
+ /**
+ * @return {!Capabilities} A basic set of capabilities for Microsoft Edge.
+ */
+ static edge() {
+ return new Capabilities()
+ .set(Capability.BROWSER_NAME, Browser.EDGE)
+ .set(Capability.PLATFORM, 'WINDOWS');
+ }
+
+ /**
+ * @return {!Capabilities} A basic set of capabilities for Firefox.
+ */
+ static firefox() {
+ return new Capabilities().set(Capability.BROWSER_NAME, Browser.FIREFOX);
+ }
+
+ /**
+ * @return {!Capabilities} A basic set of capabilities for Internet Explorer.
+ */
+ static ie() {
+ return new Capabilities().
+ set(Capability.BROWSER_NAME, Browser.INTERNET_EXPLORER).
+ set(Capability.PLATFORM, 'WINDOWS');
+ }
+
+ /**
+ * @return {!Capabilities} A basic set of capabilities for iPad.
+ */
+ static ipad() {
+ return new Capabilities().
+ set(Capability.BROWSER_NAME, Browser.IPAD).
+ set(Capability.PLATFORM, 'MAC');
+ }
+
+ /**
+ * @return {!Capabilities} A basic set of capabilities for iPhone.
+ */
+ static iphone() {
+ return new Capabilities().
+ set(Capability.BROWSER_NAME, Browser.IPHONE).
+ set(Capability.PLATFORM, 'MAC');
+ }
+
+ /**
+ * @return {!Capabilities} A basic set of capabilities for Opera.
+ */
+ static opera() {
+ return new Capabilities().
+ set(Capability.BROWSER_NAME, Browser.OPERA);
+ }
+
+ /**
+ * @return {!Capabilities} A basic set of capabilities for PhantomJS.
+ */
+ static phantomjs() {
+ return new Capabilities().
+ set(Capability.BROWSER_NAME, Browser.PHANTOM_JS);
+ }
+
+ /**
+ * @return {!Capabilities} A basic set of capabilities for Safari.
+ */
+ static safari() {
+ return new Capabilities().
+ set(Capability.BROWSER_NAME, Browser.SAFARI).
+ set(Capability.PLATFORM, 'MAC');
+ }
+
+ /**
+ * @return {!Capabilities} A basic set of capabilities for HTMLUnit.
+ */
+ static htmlunit() {
+ return new Capabilities().
+ set(Capability.BROWSER_NAME, Browser.HTMLUNIT);
+ }
+
+ /**
+ * @return {!Capabilities} A basic set of capabilities for HTMLUnit
+ * with enabled Javascript.
+ */
+ static htmlunitwithjs() {
+ return new Capabilities().
+ set(Capability.BROWSER_NAME, Browser.HTMLUNIT).
+ set(Capability.SUPPORTS_JAVASCRIPT, true);
+ }
+
+ /**
+ * @return {!Object<string, ?>} The JSON representation of this instance.
+ * Note, the returned object may contain nested promised values.
+ * @suppress {checkTypes} Suppress [] access on a struct (state inherited from
+ * Map).
+ */
+ [Symbols.serialize]() {
+ return serialize(this);
+ }
+
+ /**
+ * Merges another set of capabilities into this instance.
+ * @param {!(Capabilities|Map<String, ?>|Object<string, ?>)} other The other
+ * set of capabilities to merge.
+ * @return {!Capabilities} A self reference.
+ */
+ merge(other) {
+ if (!other) {
+ throw new TypeError('no capabilities provided for merge');
+ }
+
+ if (!(other instanceof Map)) {
+ other = toMap(other);
+ }
+
+ for (let key of other.keys()) {
+ this.set(key, other.get(key));
+ }
+
+ return this;
+ }
+
+ /**
+ * @param {string} key The capability key.
+ * @param {*} value The capability value.
+ * @return {!Capabilities} A self reference.
+ * @throws {TypeError} If the `key` is not a string.
+ * @override
+ */
+ set(key, value) {
+ if (typeof key !== 'string') {
+ throw new TypeError('Capability keys must be strings: ' + typeof key);
+ }
+ super.set(key, value);
+ return this;
+ }
+
+ /**
+ * Sets the logging preferences. Preferences may be specified as a
+ * {@link ./logging.Preferences} instance, or a as a map of log-type to
+ * log-level.
+ * @param {!(./logging.Preferences|Object<string>)} prefs The logging
+ * preferences.
+ * @return {!Capabilities} A self reference.
+ */
+ setLoggingPrefs(prefs) {
+ return this.set(Capability.LOGGING_PREFS, prefs);
+ }
+
+ /**
+ * Sets the proxy configuration for this instance.
+ * @param {ProxyConfig} proxy The desired proxy configuration.
+ * @return {!Capabilities} A self reference.
+ */
+ setProxy(proxy) {
+ return this.set(Capability.PROXY, proxy);
+ }
+
+ /**
+ * Sets whether native events should be used.
+ * @param {boolean} enabled Whether to enable native events.
+ * @return {!Capabilities} A self reference.
+ */
+ setEnableNativeEvents(enabled) {
+ return this.set(Capability.NATIVE_EVENTS, enabled);
+ }
+
+ /**
+ * Sets how elements should be scrolled into view for interaction.
+ * @param {number} behavior The desired scroll behavior: either 0 to align
+ * with the top of the viewport or 1 to align with the bottom.
+ * @return {!Capabilities} A self reference.
+ */
+ setScrollBehavior(behavior) {
+ return this.set(Capability.ELEMENT_SCROLL_BEHAVIOR, behavior);
+ }
+
+ /**
+ * Sets the default action to take with an unexpected alert before returning
+ * an error.
+ * @param {string} behavior The desired behavior; should be "accept",
+ * "dismiss", or "ignore". Defaults to "dismiss".
+ * @return {!Capabilities} A self reference.
+ */
+ setAlertBehavior(behavior) {
+ return this.set(Capability.UNEXPECTED_ALERT_BEHAVIOR, behavior);
+ }
+}
+
+
+/**
+ * Serializes a capabilities object. This is defined as a standalone function
+ * so it may be type checked (where Capabilities[Symbols.serialize] has type
+ * checking disabled since it is defined with [] access on a struct).
+ *
+ * @param {!Capabilities} caps The capabilities to serialize.
+ * @return {!Object<string, ?>} The JSON representation of this instance.
+ * Note, the returned object may contain nested promised values.
+ */
+function serialize(caps) {
+ let ret = {};
+ for (let key of caps.keys()) {
+ let cap = caps.get(key);
+ if (cap !== undefined && cap !== null) {
+ ret[key] = cap;
+ }
+ }
+ return ret;
+}
+
+
+// PUBLIC API
+
+
+module.exports = {
+ Browser: Browser,
+ Capabilities: Capabilities,
+ Capability: Capability,
+ ProxyConfig: ProxyConfig
+};
diff --git a/node_modules/selenium-webdriver/lib/command.js b/node_modules/selenium-webdriver/lib/command.js
new file mode 100644
index 000000000..c9a366e4e
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/command.js
@@ -0,0 +1,243 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+/**
+ * @fileoverview Contains several classes for handling commands.
+ */
+
+'use strict';
+
+/**
+ * Describes a command to execute.
+ * @final
+ */
+class Command {
+ /** @param {string} name The name of this command. */
+ constructor(name) {
+ /** @private {string} */
+ this.name_ = name;
+
+ /** @private {!Object<*>} */
+ this.parameters_ = {};
+ }
+
+ /** @return {string} This command's name. */
+ getName() {
+ return this.name_;
+ }
+
+ /**
+ * Sets a parameter to send with this command.
+ * @param {string} name The parameter name.
+ * @param {*} value The parameter value.
+ * @return {!Command} A self reference.
+ */
+ setParameter(name, value) {
+ this.parameters_[name] = value;
+ return this;
+ }
+
+ /**
+ * Sets the parameters for this command.
+ * @param {!Object<*>} parameters The command parameters.
+ * @return {!Command} A self reference.
+ */
+ setParameters(parameters) {
+ this.parameters_ = parameters;
+ return this;
+ }
+
+ /**
+ * Returns a named command parameter.
+ * @param {string} key The parameter key to look up.
+ * @return {*} The parameter value, or undefined if it has not been set.
+ */
+ getParameter(key) {
+ return this.parameters_[key];
+ }
+
+ /**
+ * @return {!Object<*>} The parameters to send with this command.
+ */
+ getParameters() {
+ return this.parameters_;
+ }
+}
+
+
+/**
+ * Enumeration of predefined names command names that all command processors
+ * will support.
+ * @enum {string}
+ */
+// TODO: Delete obsolete command names.
+const Name = {
+ GET_SERVER_STATUS: 'getStatus',
+
+ NEW_SESSION: 'newSession',
+ GET_SESSIONS: 'getSessions',
+ DESCRIBE_SESSION: 'getSessionCapabilities',
+
+ CLOSE: 'close',
+ QUIT: 'quit',
+
+ GET_CURRENT_URL: 'getCurrentUrl',
+ GET: 'get',
+ GO_BACK: 'goBack',
+ GO_FORWARD: 'goForward',
+ REFRESH: 'refresh',
+
+ ADD_COOKIE: 'addCookie',
+ GET_COOKIE: 'getCookie',
+ GET_ALL_COOKIES: 'getCookies',
+ DELETE_COOKIE: 'deleteCookie',
+ DELETE_ALL_COOKIES: 'deleteAllCookies',
+
+ GET_ACTIVE_ELEMENT: 'getActiveElement',
+ FIND_ELEMENT: 'findElement',
+ FIND_ELEMENTS: 'findElements',
+ FIND_CHILD_ELEMENT: 'findChildElement',
+ FIND_CHILD_ELEMENTS: 'findChildElements',
+
+ CLEAR_ELEMENT: 'clearElement',
+ CLICK_ELEMENT: 'clickElement',
+ SEND_KEYS_TO_ELEMENT: 'sendKeysToElement',
+ SUBMIT_ELEMENT: 'submitElement',
+
+ GET_CURRENT_WINDOW_HANDLE: 'getCurrentWindowHandle',
+ GET_WINDOW_HANDLES: 'getWindowHandles',
+ GET_WINDOW_POSITION: 'getWindowPosition',
+ SET_WINDOW_POSITION: 'setWindowPosition',
+ GET_WINDOW_SIZE: 'getWindowSize',
+ SET_WINDOW_SIZE: 'setWindowSize',
+ MAXIMIZE_WINDOW: 'maximizeWindow',
+
+ SWITCH_TO_WINDOW: 'switchToWindow',
+ SWITCH_TO_FRAME: 'switchToFrame',
+ GET_PAGE_SOURCE: 'getPageSource',
+ GET_TITLE: 'getTitle',
+
+ EXECUTE_SCRIPT: 'executeScript',
+ EXECUTE_ASYNC_SCRIPT: 'executeAsyncScript',
+
+ GET_ELEMENT_TEXT: 'getElementText',
+ GET_ELEMENT_TAG_NAME: 'getElementTagName',
+ IS_ELEMENT_SELECTED: 'isElementSelected',
+ IS_ELEMENT_ENABLED: 'isElementEnabled',
+ IS_ELEMENT_DISPLAYED: 'isElementDisplayed',
+ GET_ELEMENT_LOCATION: 'getElementLocation',
+ GET_ELEMENT_LOCATION_IN_VIEW: 'getElementLocationOnceScrolledIntoView',
+ GET_ELEMENT_SIZE: 'getElementSize',
+ GET_ELEMENT_ATTRIBUTE: 'getElementAttribute',
+ GET_ELEMENT_VALUE_OF_CSS_PROPERTY: 'getElementValueOfCssProperty',
+ ELEMENT_EQUALS: 'elementEquals',
+
+ SCREENSHOT: 'screenshot',
+ TAKE_ELEMENT_SCREENSHOT: 'takeElementScreenshot',
+ IMPLICITLY_WAIT: 'implicitlyWait',
+ SET_SCRIPT_TIMEOUT: 'setScriptTimeout',
+ SET_TIMEOUT: 'setTimeout',
+
+ ACCEPT_ALERT: 'acceptAlert',
+ DISMISS_ALERT: 'dismissAlert',
+ GET_ALERT_TEXT: 'getAlertText',
+ SET_ALERT_TEXT: 'setAlertValue',
+ SET_ALERT_CREDENTIALS: 'setAlertCredentials',
+
+ EXECUTE_SQL: 'executeSQL',
+ GET_LOCATION: 'getLocation',
+ SET_LOCATION: 'setLocation',
+ GET_APP_CACHE: 'getAppCache',
+ GET_APP_CACHE_STATUS: 'getStatus',
+ CLEAR_APP_CACHE: 'clearAppCache',
+ IS_BROWSER_ONLINE: 'isBrowserOnline',
+ SET_BROWSER_ONLINE: 'setBrowserOnline',
+
+ GET_LOCAL_STORAGE_ITEM: 'getLocalStorageItem',
+ GET_LOCAL_STORAGE_KEYS: 'getLocalStorageKeys',
+ SET_LOCAL_STORAGE_ITEM: 'setLocalStorageItem',
+ REMOVE_LOCAL_STORAGE_ITEM: 'removeLocalStorageItem',
+ CLEAR_LOCAL_STORAGE: 'clearLocalStorage',
+ GET_LOCAL_STORAGE_SIZE: 'getLocalStorageSize',
+
+ GET_SESSION_STORAGE_ITEM: 'getSessionStorageItem',
+ GET_SESSION_STORAGE_KEYS: 'getSessionStorageKey',
+ SET_SESSION_STORAGE_ITEM: 'setSessionStorageItem',
+ REMOVE_SESSION_STORAGE_ITEM: 'removeSessionStorageItem',
+ CLEAR_SESSION_STORAGE: 'clearSessionStorage',
+ GET_SESSION_STORAGE_SIZE: 'getSessionStorageSize',
+
+ SET_SCREEN_ORIENTATION: 'setScreenOrientation',
+ GET_SCREEN_ORIENTATION: 'getScreenOrientation',
+
+ // These belong to the Advanced user interactions - an element is
+ // optional for these commands.
+ CLICK: 'mouseClick',
+ DOUBLE_CLICK: 'mouseDoubleClick',
+ MOUSE_DOWN: 'mouseButtonDown',
+ MOUSE_UP: 'mouseButtonUp',
+ MOVE_TO: 'mouseMoveTo',
+ SEND_KEYS_TO_ACTIVE_ELEMENT: 'sendKeysToActiveElement',
+
+ // These belong to the Advanced Touch API
+ TOUCH_SINGLE_TAP: 'touchSingleTap',
+ TOUCH_DOWN: 'touchDown',
+ TOUCH_UP: 'touchUp',
+ TOUCH_MOVE: 'touchMove',
+ TOUCH_SCROLL: 'touchScroll',
+ TOUCH_DOUBLE_TAP: 'touchDoubleTap',
+ TOUCH_LONG_PRESS: 'touchLongPress',
+ TOUCH_FLICK: 'touchFlick',
+
+ GET_AVAILABLE_LOG_TYPES: 'getAvailableLogTypes',
+ GET_LOG: 'getLog',
+ GET_SESSION_LOGS: 'getSessionLogs',
+
+ // Non-standard commands used by the standalone Selenium server.
+ UPLOAD_FILE: 'uploadFile'
+};
+
+
+
+/**
+ * Handles the execution of WebDriver {@link Command commands}.
+ * @interface
+ */
+class Executor {
+ /**
+ * Executes the given {@code command}. If there is an error executing the
+ * command, the provided callback will be invoked with the offending error.
+ * Otherwise, the callback will be invoked with a null Error and non-null
+ * response object.
+ *
+ * @param {!Command} command The command to execute.
+ * @return {!Promise<?>} A promise that will be fulfilled with the command
+ * result.
+ */
+ execute(command) {}
+}
+
+
+
+// PUBLIC API
+
+
+module.exports = {
+ Command: Command,
+ Name: Name,
+ Executor: Executor
+};
diff --git a/node_modules/selenium-webdriver/lib/devmode.js b/node_modules/selenium-webdriver/lib/devmode.js
new file mode 100644
index 000000000..4862c6a70
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/devmode.js
@@ -0,0 +1,34 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+/**
+ * @fileoverview Module used to detect if scripts are loaded from the Selenium
+ * project repo instead of from a deployed package.
+ */
+
+'use strict';
+
+const fs = require('fs');
+const path = require('path');
+
+/**
+ * @const {boolean}
+ */
+module.exports = (function() {
+ let buildDescFile = path.join(__dirname, '..', '..', 'build.desc');
+ return fs.existsSync(buildDescFile);
+})();
diff --git a/node_modules/selenium-webdriver/lib/error.js b/node_modules/selenium-webdriver/lib/error.js
new file mode 100644
index 000000000..555e5cbc5
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/error.js
@@ -0,0 +1,555 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+/**
+ * The base WebDriver error type. This error type is only used directly when a
+ * more appropriate category is not defined for the offending error.
+ */
+class WebDriverError extends Error {
+ /** @param {string=} opt_error the error message, if any. */
+ constructor(opt_error) {
+ super(opt_error);
+
+ /** @override */
+ this.name = this.constructor.name;
+ }
+}
+
+
+/**
+ * An attempt was made to select an element that cannot be selected.
+ */
+class ElementNotSelectableError extends WebDriverError {
+ /** @param {string=} opt_error the error message, if any. */
+ constructor(opt_error) {
+ super(opt_error);
+ }
+}
+
+
+/**
+ * An element command could not be completed because the element is not visible
+ * on the page.
+ */
+class ElementNotVisibleError extends WebDriverError {
+ /** @param {string=} opt_error the error message, if any. */
+ constructor(opt_error) {
+ super(opt_error);
+ }
+}
+
+
+/**
+ * The arguments passed to a command are either invalid or malformed.
+ */
+class InvalidArgumentError extends WebDriverError {
+ /** @param {string=} opt_error the error message, if any. */
+ constructor(opt_error) {
+ super(opt_error);
+ }
+}
+
+
+/**
+ * An illegal attempt was made to set a cookie under a different domain than
+ * the current page.
+ */
+class InvalidCookieDomainError extends WebDriverError {
+ /** @param {string=} opt_error the error message, if any. */
+ constructor(opt_error) {
+ super(opt_error);
+ }
+}
+
+
+/**
+ * The coordinates provided to an interactions operation are invalid.
+ */
+class InvalidElementCoordinatesError extends WebDriverError {
+ /** @param {string=} opt_error the error message, if any. */
+ constructor(opt_error) {
+ super(opt_error);
+ }
+}
+
+
+/**
+ * An element command could not be completed because the element is in an
+ * invalid state, e.g. attempting to click an element that is no longer attached
+ * to the document.
+ */
+class InvalidElementStateError extends WebDriverError {
+ /** @param {string=} opt_error the error message, if any. */
+ constructor(opt_error) {
+ super(opt_error);
+ }
+}
+
+
+/**
+ * Argument was an invalid selector.
+ */
+class InvalidSelectorError extends WebDriverError {
+ /** @param {string=} opt_error the error message, if any. */
+ constructor(opt_error) {
+ super(opt_error);
+ }
+}
+
+
+/**
+ * Occurs when a command is directed to a session that does not exist.
+ */
+class NoSuchSessionError extends WebDriverError {
+ /** @param {string=} opt_error the error message, if any. */
+ constructor(opt_error) {
+ super(opt_error);
+ }
+}
+
+
+/**
+ * An error occurred while executing JavaScript supplied by the user.
+ */
+class JavascriptError extends WebDriverError {
+ /** @param {string=} opt_error the error message, if any. */
+ constructor(opt_error) {
+ super(opt_error);
+ }
+}
+
+
+/**
+ * The target for mouse interaction is not in the browser’s viewport and cannot
+ * be brought into that viewport.
+ */
+class MoveTargetOutOfBoundsError extends WebDriverError {
+ /** @param {string=} opt_error the error message, if any. */
+ constructor(opt_error) {
+ super(opt_error);
+ }
+}
+
+
+/**
+ * An attempt was made to operate on a modal dialog when one was not open.
+ */
+class NoSuchAlertError extends WebDriverError {
+ /** @param {string=} opt_error the error message, if any. */
+ constructor(opt_error) {
+ super(opt_error);
+ }
+}
+
+
+/**
+ * An element could not be located on the page using the given search
+ * parameters.
+ */
+class NoSuchElementError extends WebDriverError {
+ /** @param {string=} opt_error the error message, if any. */
+ constructor(opt_error) {
+ super(opt_error);
+ }
+}
+
+
+/**
+ * A request to switch to a frame could not be satisfied because the frame
+ * could not be found.
+ */
+class NoSuchFrameError extends WebDriverError {
+ /** @param {string=} opt_error the error message, if any. */
+ constructor(opt_error) {
+ super(opt_error);
+ }
+}
+
+
+/**
+ * A request to switch to a window could not be satisfied because the window
+ * could not be found.
+ */
+class NoSuchWindowError extends WebDriverError {
+ /** @param {string=} opt_error the error message, if any. */
+ constructor(opt_error) {
+ super(opt_error);
+ }
+}
+
+
+/**
+ * A script did not complete before its timeout expired.
+ */
+class ScriptTimeoutError extends WebDriverError {
+ /** @param {string=} opt_error the error message, if any. */
+ constructor(opt_error) {
+ super(opt_error);
+ }
+}
+
+
+/**
+ * A new session could not be created.
+ */
+class SessionNotCreatedError extends WebDriverError {
+ /** @param {string=} opt_error the error message, if any. */
+ constructor(opt_error) {
+ super(opt_error);
+ }
+}
+
+
+
+/**
+ * An element command failed because the referenced element is no longer
+ * attached to the DOM.
+ */
+class StaleElementReferenceError extends WebDriverError {
+ /** @param {string=} opt_error the error message, if any. */
+ constructor(opt_error) {
+ super(opt_error);
+ }
+}
+
+
+/**
+ * An operation did not complete before its timeout expired.
+ */
+class TimeoutError extends WebDriverError {
+ /** @param {string=} opt_error the error message, if any. */
+ constructor(opt_error) {
+ super(opt_error);
+ }
+}
+
+
+/**
+ * A request to set a cookie’s value could not be satisfied.
+ */
+class UnableToSetCookieError extends WebDriverError {
+ /** @param {string=} opt_error the error message, if any. */
+ constructor(opt_error) {
+ super(opt_error);
+ }
+}
+
+
+/**
+ * A screen capture operation was not possible.
+ */
+class UnableToCaptureScreenError extends WebDriverError {
+ /** @param {string=} opt_error the error message, if any. */
+ constructor(opt_error) {
+ super(opt_error);
+ }
+}
+
+
+/**
+ * A modal dialog was open, blocking this operation.
+ */
+class UnexpectedAlertOpenError extends WebDriverError {
+ /**
+ * @param {string=} opt_error the error message, if any.
+ * @param {string=} opt_text the text of the open dialog, if available.
+ */
+ constructor(opt_error, opt_text) {
+ super(opt_error);
+
+ /** @private {(string|undefined)} */
+ this.text_ = opt_text;
+ }
+
+ /**
+ * @return {(string|undefined)} The text displayed with the unhandled alert,
+ * if available.
+ */
+ getAlertText() {
+ return this.text_;
+ }
+}
+
+
+/**
+ * A command could not be executed because the remote end is not aware of it.
+ */
+class UnknownCommandError extends WebDriverError {
+ /** @param {string=} opt_error the error message, if any. */
+ constructor(opt_error) {
+ super(opt_error);
+ }
+}
+
+
+/**
+ * The requested command matched a known URL but did not match an method for
+ * that URL.
+ */
+class UnknownMethodError extends WebDriverError {
+ /** @param {string=} opt_error the error message, if any. */
+ constructor(opt_error) {
+ super(opt_error);
+ }
+}
+
+
+/**
+ * Reports an unsupport operation.
+ */
+class UnsupportedOperationError extends WebDriverError {
+ /** @param {string=} opt_error the error message, if any. */
+ constructor(opt_error) {
+ super(opt_error);
+ }
+}
+
+// TODO(jleyba): Define UnknownError as an alias of WebDriverError?
+
+
+/**
+ * Enum of legacy error codes.
+ * TODO: remove this when all code paths have been switched to the new error
+ * types.
+ * @deprecated
+ * @enum {number}
+ */
+const ErrorCode = {
+ SUCCESS: 0,
+ NO_SUCH_ELEMENT: 7,
+ NO_SUCH_FRAME: 8,
+ UNKNOWN_COMMAND: 9,
+ UNSUPPORTED_OPERATION: 9,
+ STALE_ELEMENT_REFERENCE: 10,
+ ELEMENT_NOT_VISIBLE: 11,
+ INVALID_ELEMENT_STATE: 12,
+ UNKNOWN_ERROR: 13,
+ ELEMENT_NOT_SELECTABLE: 15,
+ JAVASCRIPT_ERROR: 17,
+ XPATH_LOOKUP_ERROR: 19,
+ TIMEOUT: 21,
+ NO_SUCH_WINDOW: 23,
+ INVALID_COOKIE_DOMAIN: 24,
+ UNABLE_TO_SET_COOKIE: 25,
+ UNEXPECTED_ALERT_OPEN: 26,
+ NO_SUCH_ALERT: 27,
+ SCRIPT_TIMEOUT: 28,
+ INVALID_ELEMENT_COORDINATES: 29,
+ IME_NOT_AVAILABLE: 30,
+ IME_ENGINE_ACTIVATION_FAILED: 31,
+ INVALID_SELECTOR_ERROR: 32,
+ SESSION_NOT_CREATED: 33,
+ MOVE_TARGET_OUT_OF_BOUNDS: 34,
+ SQL_DATABASE_ERROR: 35,
+ INVALID_XPATH_SELECTOR: 51,
+ INVALID_XPATH_SELECTOR_RETURN_TYPE: 52,
+ METHOD_NOT_ALLOWED: 405
+};
+
+
+const LEGACY_ERROR_CODE_TO_TYPE = new Map([
+ [ErrorCode.NO_SUCH_ELEMENT, NoSuchElementError],
+ [ErrorCode.NO_SUCH_FRAME, NoSuchFrameError],
+ [ErrorCode.UNSUPPORTED_OPERATION, UnsupportedOperationError],
+ [ErrorCode.STALE_ELEMENT_REFERENCE, StaleElementReferenceError],
+ [ErrorCode.ELEMENT_NOT_VISIBLE, ElementNotVisibleError],
+ [ErrorCode.INVALID_ELEMENT_STATE, InvalidElementStateError],
+ [ErrorCode.UNKNOWN_ERROR, WebDriverError],
+ [ErrorCode.ELEMENT_NOT_SELECTABLE, ElementNotSelectableError],
+ [ErrorCode.JAVASCRIPT_ERROR, JavascriptError],
+ [ErrorCode.XPATH_LOOKUP_ERROR, InvalidSelectorError],
+ [ErrorCode.TIMEOUT, TimeoutError],
+ [ErrorCode.NO_SUCH_WINDOW, NoSuchWindowError],
+ [ErrorCode.INVALID_COOKIE_DOMAIN, InvalidCookieDomainError],
+ [ErrorCode.UNABLE_TO_SET_COOKIE, UnableToSetCookieError],
+ [ErrorCode.UNEXPECTED_ALERT_OPEN, UnexpectedAlertOpenError],
+ [ErrorCode.NO_SUCH_ALERT, NoSuchAlertError],
+ [ErrorCode.SCRIPT_TIMEOUT, ScriptTimeoutError],
+ [ErrorCode.INVALID_ELEMENT_COORDINATES, InvalidElementCoordinatesError],
+ [ErrorCode.INVALID_SELECTOR_ERROR, InvalidSelectorError],
+ [ErrorCode.SESSION_NOT_CREATED, SessionNotCreatedError],
+ [ErrorCode.MOVE_TARGET_OUT_OF_BOUNDS, MoveTargetOutOfBoundsError],
+ [ErrorCode.INVALID_XPATH_SELECTOR, InvalidSelectorError],
+ [ErrorCode.INVALID_XPATH_SELECTOR_RETURN_TYPE, InvalidSelectorError],
+ [ErrorCode.METHOD_NOT_ALLOWED, UnsupportedOperationError]]);
+
+
+const ERROR_CODE_TO_TYPE = new Map([
+ ['unknown error', WebDriverError],
+ ['element not selectable', ElementNotSelectableError],
+ ['element not visible', ElementNotVisibleError],
+ ['invalid argument', InvalidArgumentError],
+ ['invalid cookie domain', InvalidCookieDomainError],
+ ['invalid element coordinates', InvalidElementCoordinatesError],
+ ['invalid element state', InvalidElementStateError],
+ ['invalid selector', InvalidSelectorError],
+ ['invalid session id', NoSuchSessionError],
+ ['javascript error', JavascriptError],
+ ['move target out of bounds', MoveTargetOutOfBoundsError],
+ ['no such alert', NoSuchAlertError],
+ ['no such element', NoSuchElementError],
+ ['no such frame', NoSuchFrameError],
+ ['no such window', NoSuchWindowError],
+ ['script timeout', ScriptTimeoutError],
+ ['session not created', SessionNotCreatedError],
+ ['stale element reference', StaleElementReferenceError],
+ ['timeout', TimeoutError],
+ ['unable to set cookie', UnableToSetCookieError],
+ ['unable to capture screen', UnableToCaptureScreenError],
+ ['unexpected alert open', UnexpectedAlertOpenError],
+ ['unknown command', UnknownCommandError],
+ ['unknown method', UnknownMethodError],
+ ['unsupported operation', UnsupportedOperationError]]);
+
+
+const TYPE_TO_ERROR_CODE = new Map;
+ERROR_CODE_TO_TYPE.forEach((value, key) => {
+ TYPE_TO_ERROR_CODE.set(value, key);
+});
+
+
+
+/**
+ * @param {*} err The error to encode.
+ * @return {{error: string, message: string}} the encoded error.
+ */
+function encodeError(err) {
+ let type = WebDriverError;
+ if (err instanceof WebDriverError
+ && TYPE_TO_ERROR_CODE.has(err.constructor)) {
+ type = err.constructor;
+ }
+
+ let message = err instanceof Error
+ ? err.message
+ : err + '';
+
+ let code = /** @type {string} */(TYPE_TO_ERROR_CODE.get(type));
+ return {'error': code, 'message': message};
+}
+
+
+/**
+ * Checks a response object from a server that adheres to the W3C WebDriver
+ * protocol.
+ * @param {*} data The response data to check.
+ * @return {*} The response data if it was not an encoded error.
+ * @throws {WebDriverError} the decoded error, if present in the data object.
+ * @deprecated Use {@link #throwDecodedError(data)} instead.
+ * @see https://w3c.github.io/webdriver/webdriver-spec.html#protocol
+ */
+function checkResponse(data) {
+ if (data && typeof data.error === 'string') {
+ let ctor = ERROR_CODE_TO_TYPE.get(data.error) || WebDriverError;
+ throw new ctor(data.message);
+ }
+ return data;
+}
+
+
+/**
+ * Throws an error coded from the W3C protocol. A generic error will be thrown
+ * if the privded `data` is not a valid encoded error.
+ *
+ * @param {{error: string, message: string}} data The error data to decode.
+ * @throws {WebDriverError} the decoded error.
+ * @see https://w3c.github.io/webdriver/webdriver-spec.html#protocol
+ */
+function throwDecodedError(data) {
+ if (data && typeof data === 'object' && typeof data.error === 'string') {
+ let ctor = ERROR_CODE_TO_TYPE.get(data.error) || WebDriverError;
+ throw new ctor(data.message);
+ }
+ throw new WebDriverError('Unknown error: ' + JSON.stringify(data));
+}
+
+
+/**
+ * Checks a legacy response from the Selenium 2.0 wire protocol for an error.
+ * @param {*} responseObj the response object to check.
+ * @return {*} responseObj the original response if it does not define an error.
+ * @throws {WebDriverError} if the response object defines an error.
+ */
+function checkLegacyResponse(responseObj) {
+ // Handle the legacy Selenium error response format.
+ if (responseObj
+ && typeof responseObj === 'object'
+ && typeof responseObj['status'] === 'number'
+ && responseObj['status'] !== 0) {
+ let status = responseObj['status'];
+ let ctor = LEGACY_ERROR_CODE_TO_TYPE.get(status) || WebDriverError;
+
+ let value = responseObj['value'];
+
+ if (!value || typeof value !== 'object') {
+ throw new ctor(value + '');
+ } else {
+ let message = value['message'] + '';
+ if (ctor !== UnexpectedAlertOpenError) {
+ throw new ctor(message);
+ }
+
+ let text = '';
+ if (value['alert'] && typeof value['alert']['text'] === 'string') {
+ text = value['alert']['text'];
+ }
+ throw new UnexpectedAlertOpenError(message, text);
+ }
+ }
+ return responseObj;
+}
+
+
+// PUBLIC API
+
+
+module.exports = {
+ ErrorCode: ErrorCode,
+
+ WebDriverError: WebDriverError,
+ ElementNotSelectableError: ElementNotSelectableError,
+ ElementNotVisibleError: ElementNotVisibleError,
+ InvalidArgumentError: InvalidArgumentError,
+ InvalidCookieDomainError: InvalidCookieDomainError,
+ InvalidElementCoordinatesError: InvalidElementCoordinatesError,
+ InvalidElementStateError: InvalidElementStateError,
+ InvalidSelectorError: InvalidSelectorError,
+ JavascriptError: JavascriptError,
+ MoveTargetOutOfBoundsError: MoveTargetOutOfBoundsError,
+ NoSuchAlertError: NoSuchAlertError,
+ NoSuchElementError: NoSuchElementError,
+ NoSuchFrameError: NoSuchFrameError,
+ NoSuchSessionError: NoSuchSessionError,
+ NoSuchWindowError: NoSuchWindowError,
+ ScriptTimeoutError: ScriptTimeoutError,
+ SessionNotCreatedError: SessionNotCreatedError,
+ StaleElementReferenceError: StaleElementReferenceError,
+ TimeoutError: TimeoutError,
+ UnableToSetCookieError: UnableToSetCookieError,
+ UnableToCaptureScreenError: UnableToCaptureScreenError,
+ UnexpectedAlertOpenError: UnexpectedAlertOpenError,
+ UnknownCommandError: UnknownCommandError,
+ UnknownMethodError: UnknownMethodError,
+ UnsupportedOperationError: UnsupportedOperationError,
+
+ checkResponse: checkResponse,
+ checkLegacyResponse: checkLegacyResponse,
+ encodeError: encodeError,
+ throwDecodedError: throwDecodedError,
+};
diff --git a/node_modules/selenium-webdriver/lib/events.js b/node_modules/selenium-webdriver/lib/events.js
new file mode 100644
index 000000000..65e63de8c
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/events.js
@@ -0,0 +1,210 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+/**
+ * Describes an event listener registered on an {@linkplain EventEmitter}.
+ */
+class Listener {
+ /**
+ * @param {!Function} fn The acutal listener function.
+ * @param {(Object|undefined)} scope The object in whose scope to invoke the
+ * listener.
+ * @param {boolean} oneshot Whether this listener should only be used once.
+ */
+ constructor(fn, scope, oneshot) {
+ this.fn = fn;
+ this.scope = scope;
+ this.oneshot = oneshot;
+ }
+}
+
+
+/** @type {!WeakMap<!EventEmitter, !Map<string, !Set<!Listener>>>} */
+const EVENTS = new WeakMap;
+
+
+/**
+ * Object that can emit events for others to listen for.
+ */
+class EventEmitter {
+ /**
+ * Fires an event and calls all listeners.
+ * @param {string} type The type of event to emit.
+ * @param {...*} var_args Any arguments to pass to each listener.
+ */
+ emit(type, var_args) {
+ let events = EVENTS.get(this);
+ if (!events) {
+ return;
+ }
+
+ let args = Array.prototype.slice.call(arguments, 1);
+
+ let listeners = events.get(type);
+ if (listeners) {
+ for (let listener of listeners) {
+ listener.fn.apply(listener.scope, args);
+ if (listener.oneshot) {
+ listeners.delete(listener);
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns a mutable list of listeners for a specific type of event.
+ * @param {string} type The type of event to retrieve the listeners for.
+ * @return {!Set<!Listener>} The registered listeners for the given event
+ * type.
+ */
+ listeners(type) {
+ let events = EVENTS.get(this);
+ if (!events) {
+ events = new Map;
+ EVENTS.set(this, events);
+ }
+
+ let listeners = events.get(type);
+ if (!listeners) {
+ listeners = new Set;
+ events.set(type, listeners);
+ }
+ return listeners;
+ }
+
+ /**
+ * Registers a listener.
+ * @param {string} type The type of event to listen for.
+ * @param {!Function} fn The function to invoke when the event is fired.
+ * @param {Object=} opt_self The object in whose scope to invoke the listener.
+ * @param {boolean=} opt_oneshot Whether the listener should b (e removed after
+ * the first event is fired.
+ * @return {!EventEmitter} A self reference.
+ * @private
+ */
+ addListener_(type, fn, opt_self, opt_oneshot) {
+ let listeners = this.listeners(type);
+ for (let listener of listeners) {
+ if (listener.fn === fn) {
+ return this;
+ }
+ }
+ listeners.add(new Listener(fn, opt_self || undefined, !!opt_oneshot));
+ return this;
+ }
+
+ /**
+ * Registers a listener.
+ * @param {string} type The type of event to listen for.
+ * @param {!Function} fn The function to invoke when the event is fired.
+ * @param {Object=} opt_self The object in whose scope to invoke the listener.
+ * @return {!EventEmitter} A self reference.
+ */
+ addListener(type, fn, opt_self) {
+ return this.addListener_(type, fn, opt_self, false);
+ }
+
+ /**
+ * Registers a one-time listener which will be called only the first time an
+ * event is emitted, after which it will be removed.
+ * @param {string} type The type of event to listen for.
+ * @param {!Function} fn The function to invoke when the event is fired.
+ * @param {Object=} opt_self The object in whose scope to invoke the listener.
+ * @return {!EventEmitter} A self reference.
+ */
+ once(type, fn, opt_self) {
+ return this.addListener_(type, fn, opt_self, true);
+ }
+
+ /**
+ * An alias for {@link #addListener() addListener()}.
+ * @param {string} type The type of event to listen for.
+ * @param {!Function} fn The function to invoke when the event is fired.
+ * @param {Object=} opt_self The object in whose scope to invoke the listener.
+ * @return {!EventEmitter} A self reference.
+ */
+ on(type, fn, opt_self) {
+ return this.addListener(type, fn, opt_self);
+ }
+
+ /**
+ * Removes a previously registered event listener.
+ * @param {string} type The type of event to unregister.
+ * @param {!Function} listenerFn The handler function to remove.
+ * @return {!EventEmitter} A self reference.
+ */
+ removeListener(type, listenerFn) {
+ if (typeof type !== 'string' || typeof listenerFn !== 'function') {
+ throw TypeError('invalid args: expected (string, function), got ('
+ + (typeof type) + ', ' + (typeof listenerFn) + ')');
+ }
+
+ let events = EVENTS.get(this);
+ if (!events) {
+ return this;
+ }
+
+ let listeners = events.get(type);
+ if (!listeners) {
+ return this;
+ }
+
+ let match;
+ for (let listener of listeners) {
+ if (listener.fn === listenerFn) {
+ match = listener;
+ break;
+ }
+ }
+ if (match) {
+ listeners.delete(match);
+ if (!listeners.size) {
+ events.delete(type);
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Removes all listeners for a specific type of event. If no event is
+ * specified, all listeners across all types will be removed.
+ * @param {string=} opt_type The type of event to remove listeners from.
+ * @return {!EventEmitter} A self reference.
+ */
+ removeAllListeners(opt_type) {
+ let events = EVENTS.get(this);
+ if (events) {
+ if (typeof opt_type === 'string') {
+ events.delete(opt_type);
+ } else {
+ EVENTS.delete(this);
+ }
+ }
+ return this;
+ }
+}
+
+
+// PUBLIC API
+
+
+module.exports = {
+ EventEmitter: EventEmitter,
+ Listener: Listener
+};
diff --git a/node_modules/selenium-webdriver/lib/firefox/amd64/libnoblur64.so b/node_modules/selenium-webdriver/lib/firefox/amd64/libnoblur64.so
new file mode 100644
index 000000000..916e530f3
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/firefox/amd64/libnoblur64.so
Binary files differ
diff --git a/node_modules/selenium-webdriver/lib/firefox/i386/libnoblur.so b/node_modules/selenium-webdriver/lib/firefox/i386/libnoblur.so
new file mode 100644
index 000000000..8e7db8de3
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/firefox/i386/libnoblur.so
Binary files differ
diff --git a/node_modules/selenium-webdriver/lib/firefox/webdriver.json b/node_modules/selenium-webdriver/lib/firefox/webdriver.json
new file mode 100644
index 000000000..38601a28c
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/firefox/webdriver.json
@@ -0,0 +1,69 @@
+{
+ "frozen": {
+ "app.update.auto": false,
+ "app.update.enabled": false,
+ "browser.displayedE10SNotice": 4,
+ "browser.download.manager.showWhenStarting": false,
+ "browser.EULA.override": true,
+ "browser.EULA.3.accepted": true,
+ "browser.link.open_external": 2,
+ "browser.link.open_newwindow": 2,
+ "browser.offline": false,
+ "browser.reader.detectedFirstArticle": true,
+ "browser.safebrowsing.enabled": false,
+ "browser.safebrowsing.malware.enabled": false,
+ "browser.search.update": false,
+ "browser.selfsupport.url" : "",
+ "browser.sessionstore.resume_from_crash": false,
+ "browser.shell.checkDefaultBrowser": false,
+ "browser.tabs.warnOnClose": false,
+ "browser.tabs.warnOnOpen": false,
+ "datareporting.healthreport.service.enabled": false,
+ "datareporting.healthreport.uploadEnabled": false,
+ "datareporting.healthreport.service.firstRun": false,
+ "datareporting.healthreport.logging.consoleEnabled": false,
+ "datareporting.policy.dataSubmissionEnabled": false,
+ "datareporting.policy.dataSubmissionPolicyAccepted": false,
+ "devtools.errorconsole.enabled": true,
+ "dom.disable_open_during_load": false,
+ "extensions.autoDisableScopes": 10,
+ "extensions.blocklist.enabled": false,
+ "extensions.checkCompatibility.nightly": false,
+ "extensions.logging.enabled": true,
+ "extensions.update.enabled": false,
+ "extensions.update.notifyUser": false,
+ "javascript.enabled": true,
+ "network.manage-offline-status": false,
+ "network.http.phishy-userpass-length": 255,
+ "offline-apps.allow_by_default": true,
+ "prompts.tab_modal.enabled": false,
+ "security.csp.enable": false,
+ "security.fileuri.origin_policy": 3,
+ "security.fileuri.strict_origin_policy": false,
+ "signon.rememberSignons": false,
+ "toolkit.networkmanager.disable": true,
+ "toolkit.telemetry.prompted": 2,
+ "toolkit.telemetry.enabled": false,
+ "toolkit.telemetry.rejected": true,
+ "xpinstall.signatures.required": false,
+ "xpinstall.whitelist.required": false
+ },
+ "mutable": {
+ "browser.dom.window.dump.enabled": true,
+ "browser.laterrun.enabled": false,
+ "browser.newtab.url": "about:blank",
+ "browser.newtabpage.enabled": false,
+ "browser.startup.page": 0,
+ "browser.startup.homepage": "about:blank",
+ "browser.startup.homepage_override.mstone": "ignore",
+ "browser.usedOnWindows10.introURL": "about:blank",
+ "dom.max_chrome_script_run_time": 30,
+ "dom.max_script_run_time": 30,
+ "dom.report_all_js_exceptions": true,
+ "javascript.options.showInConsole": true,
+ "startup.homepage_welcome_url": "about:blank",
+ "startup.homepage_welcome_url.additional": "about:blank",
+ "webdriver_accept_untrusted_certs": true,
+ "webdriver_assume_untrusted_issuer": true
+ }
+}
diff --git a/node_modules/selenium-webdriver/lib/firefox/webdriver.xpi b/node_modules/selenium-webdriver/lib/firefox/webdriver.xpi
new file mode 100644
index 000000000..39aca6b62
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/firefox/webdriver.xpi
Binary files differ
diff --git a/node_modules/selenium-webdriver/lib/http.js b/node_modules/selenium-webdriver/lib/http.js
new file mode 100644
index 000000000..a5675f81f
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/http.js
@@ -0,0 +1,456 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+/**
+ * @fileoverview Defines an environment agnostic {@linkplain cmd.Executor
+ * command executor} that communicates with a remote end using JSON over HTTP.
+ *
+ * Clients should implement the {@link Client} interface, which is used by
+ * the {@link Executor} to send commands to the remote end.
+ */
+
+'use strict';
+
+const cmd = require('./command');
+const error = require('./error');
+const logging = require('./logging');
+const promise = require('./promise');
+const Session = require('./session').Session;
+const WebElement = require('./webdriver').WebElement;
+
+
+/**
+ * Converts a headers map to a HTTP header block string.
+ * @param {!Map<string, string>} headers The map to convert.
+ * @return {string} The headers as a string.
+ */
+function headersToString(headers) {
+ let ret = [];
+ headers.forEach(function(value, name) {
+ ret.push(`${name.toLowerCase()}: ${value}`);
+ });
+ return ret.join('\n');
+}
+
+
+/**
+ * Represents a HTTP request message. This class is a "partial" request and only
+ * defines the path on the server to send a request to. It is each client's
+ * responsibility to build the full URL for the final request.
+ * @final
+ */
+class Request {
+ /**
+ * @param {string} method The HTTP method to use for the request.
+ * @param {string} path The path on the server to send the request to.
+ * @param {Object=} opt_data This request's non-serialized JSON payload data.
+ */
+ constructor(method, path, opt_data) {
+ this.method = /** string */method;
+ this.path = /** string */path;
+ this.data = /** Object */opt_data;
+ this.headers = /** !Map<string, string> */new Map(
+ [['Accept', 'application/json; charset=utf-8']]);
+ }
+
+ /** @override */
+ toString() {
+ let ret = `${this.method} ${this.path} HTTP/1.1\n`;
+ ret += headersToString(this.headers) + '\n\n';
+ if (this.data) {
+ ret += JSON.stringify(this.data);
+ }
+ return ret;
+ }
+}
+
+
+/**
+ * Represents a HTTP response message.
+ * @final
+ */
+class Response {
+ /**
+ * @param {number} status The response code.
+ * @param {!Object<string>} headers The response headers. All header names
+ * will be converted to lowercase strings for consistent lookups.
+ * @param {string} body The response body.
+ */
+ constructor(status, headers, body) {
+ this.status = /** number */status;
+ this.body = /** string */body;
+ this.headers = /** !Map<string, string>*/new Map;
+ for (let header in headers) {
+ this.headers.set(header.toLowerCase(), headers[header]);
+ }
+ }
+
+ /** @override */
+ toString() {
+ let ret = `HTTP/1.1 ${this.status}\n${headersToString(this.headers)}\n\n`;
+ if (this.body) {
+ ret += this.body;
+ }
+ return ret;
+ }
+}
+
+
+function post(path) { return resource('POST', path); }
+function del(path) { return resource('DELETE', path); }
+function get(path) { return resource('GET', path); }
+function resource(method, path) { return {method: method, path: path}; }
+
+
+/** @const {!Map<string, {method: string, path: string}>} */
+const COMMAND_MAP = new Map([
+ [cmd.Name.GET_SERVER_STATUS, get('/status')],
+ [cmd.Name.NEW_SESSION, post('/session')],
+ [cmd.Name.GET_SESSIONS, get('/sessions')],
+ [cmd.Name.DESCRIBE_SESSION, get('/session/:sessionId')],
+ [cmd.Name.QUIT, del('/session/:sessionId')],
+ [cmd.Name.CLOSE, del('/session/:sessionId/window')],
+ [cmd.Name.GET_CURRENT_WINDOW_HANDLE, get('/session/:sessionId/window_handle')],
+ [cmd.Name.GET_WINDOW_HANDLES, get('/session/:sessionId/window_handles')],
+ [cmd.Name.GET_CURRENT_URL, get('/session/:sessionId/url')],
+ [cmd.Name.GET, post('/session/:sessionId/url')],
+ [cmd.Name.GO_BACK, post('/session/:sessionId/back')],
+ [cmd.Name.GO_FORWARD, post('/session/:sessionId/forward')],
+ [cmd.Name.REFRESH, post('/session/:sessionId/refresh')],
+ [cmd.Name.ADD_COOKIE, post('/session/:sessionId/cookie')],
+ [cmd.Name.GET_ALL_COOKIES, get('/session/:sessionId/cookie')],
+ [cmd.Name.DELETE_ALL_COOKIES, del('/session/:sessionId/cookie')],
+ [cmd.Name.DELETE_COOKIE, del('/session/:sessionId/cookie/:name')],
+ [cmd.Name.FIND_ELEMENT, post('/session/:sessionId/element')],
+ [cmd.Name.FIND_ELEMENTS, post('/session/:sessionId/elements')],
+ [cmd.Name.GET_ACTIVE_ELEMENT, post('/session/:sessionId/element/active')],
+ [cmd.Name.FIND_CHILD_ELEMENT, post('/session/:sessionId/element/:id/element')],
+ [cmd.Name.FIND_CHILD_ELEMENTS, post('/session/:sessionId/element/:id/elements')],
+ [cmd.Name.CLEAR_ELEMENT, post('/session/:sessionId/element/:id/clear')],
+ [cmd.Name.CLICK_ELEMENT, post('/session/:sessionId/element/:id/click')],
+ [cmd.Name.SEND_KEYS_TO_ELEMENT, post('/session/:sessionId/element/:id/value')],
+ [cmd.Name.SUBMIT_ELEMENT, post('/session/:sessionId/element/:id/submit')],
+ [cmd.Name.GET_ELEMENT_TEXT, get('/session/:sessionId/element/:id/text')],
+ [cmd.Name.GET_ELEMENT_TAG_NAME, get('/session/:sessionId/element/:id/name')],
+ [cmd.Name.IS_ELEMENT_SELECTED, get('/session/:sessionId/element/:id/selected')],
+ [cmd.Name.IS_ELEMENT_ENABLED, get('/session/:sessionId/element/:id/enabled')],
+ [cmd.Name.IS_ELEMENT_DISPLAYED, get('/session/:sessionId/element/:id/displayed')],
+ [cmd.Name.GET_ELEMENT_LOCATION, get('/session/:sessionId/element/:id/location')],
+ [cmd.Name.GET_ELEMENT_SIZE, get('/session/:sessionId/element/:id/size')],
+ [cmd.Name.GET_ELEMENT_ATTRIBUTE, get('/session/:sessionId/element/:id/attribute/:name')],
+ [cmd.Name.GET_ELEMENT_VALUE_OF_CSS_PROPERTY, get('/session/:sessionId/element/:id/css/:propertyName')],
+ [cmd.Name.ELEMENT_EQUALS, get('/session/:sessionId/element/:id/equals/:other')],
+ [cmd.Name.TAKE_ELEMENT_SCREENSHOT, get('/session/:sessionId/element/:id/screenshot')],
+ [cmd.Name.SWITCH_TO_WINDOW, post('/session/:sessionId/window')],
+ [cmd.Name.MAXIMIZE_WINDOW, post('/session/:sessionId/window/current/maximize')],
+ [cmd.Name.GET_WINDOW_POSITION, get('/session/:sessionId/window/current/position')],
+ [cmd.Name.SET_WINDOW_POSITION, post('/session/:sessionId/window/current/position')],
+ [cmd.Name.GET_WINDOW_SIZE, get('/session/:sessionId/window/current/size')],
+ [cmd.Name.SET_WINDOW_SIZE, post('/session/:sessionId/window/current/size')],
+ [cmd.Name.SWITCH_TO_FRAME, post('/session/:sessionId/frame')],
+ [cmd.Name.GET_PAGE_SOURCE, get('/session/:sessionId/source')],
+ [cmd.Name.GET_TITLE, get('/session/:sessionId/title')],
+ [cmd.Name.EXECUTE_SCRIPT, post('/session/:sessionId/execute')],
+ [cmd.Name.EXECUTE_ASYNC_SCRIPT, post('/session/:sessionId/execute_async')],
+ [cmd.Name.SCREENSHOT, get('/session/:sessionId/screenshot')],
+ [cmd.Name.SET_TIMEOUT, post('/session/:sessionId/timeouts')],
+ [cmd.Name.MOVE_TO, post('/session/:sessionId/moveto')],
+ [cmd.Name.CLICK, post('/session/:sessionId/click')],
+ [cmd.Name.DOUBLE_CLICK, post('/session/:sessionId/doubleclick')],
+ [cmd.Name.MOUSE_DOWN, post('/session/:sessionId/buttondown')],
+ [cmd.Name.MOUSE_UP, post('/session/:sessionId/buttonup')],
+ [cmd.Name.MOVE_TO, post('/session/:sessionId/moveto')],
+ [cmd.Name.SEND_KEYS_TO_ACTIVE_ELEMENT, post('/session/:sessionId/keys')],
+ [cmd.Name.TOUCH_SINGLE_TAP, post('/session/:sessionId/touch/click')],
+ [cmd.Name.TOUCH_DOUBLE_TAP, post('/session/:sessionId/touch/doubleclick')],
+ [cmd.Name.TOUCH_DOWN, post('/session/:sessionId/touch/down')],
+ [cmd.Name.TOUCH_UP, post('/session/:sessionId/touch/up')],
+ [cmd.Name.TOUCH_MOVE, post('/session/:sessionId/touch/move')],
+ [cmd.Name.TOUCH_SCROLL, post('/session/:sessionId/touch/scroll')],
+ [cmd.Name.TOUCH_LONG_PRESS, post('/session/:sessionId/touch/longclick')],
+ [cmd.Name.TOUCH_FLICK, post('/session/:sessionId/touch/flick')],
+ [cmd.Name.ACCEPT_ALERT, post('/session/:sessionId/accept_alert')],
+ [cmd.Name.DISMISS_ALERT, post('/session/:sessionId/dismiss_alert')],
+ [cmd.Name.GET_ALERT_TEXT, get('/session/:sessionId/alert_text')],
+ [cmd.Name.SET_ALERT_TEXT, post('/session/:sessionId/alert_text')],
+ [cmd.Name.SET_ALERT_CREDENTIALS, post('/session/:sessionId/alert/credentials')],
+ [cmd.Name.GET_LOG, post('/session/:sessionId/log')],
+ [cmd.Name.GET_AVAILABLE_LOG_TYPES, get('/session/:sessionId/log/types')],
+ [cmd.Name.GET_SESSION_LOGS, post('/logs')],
+ [cmd.Name.UPLOAD_FILE, post('/session/:sessionId/file')],
+]);
+
+
+/** @const {!Map<string, {method: string, path: string}>} */
+const W3C_COMMAND_MAP = new Map([
+ [cmd.Name.MAXIMIZE_WINDOW, post('/session/:sessionId/window/maximize')],
+ [cmd.Name.GET_WINDOW_POSITION, get('/session/:sessionId/window/position')],
+ [cmd.Name.SET_WINDOW_POSITION, post('/session/:sessionId/window/position')],
+ [cmd.Name.GET_WINDOW_SIZE, get('/session/:sessionId/window/size')],
+ [cmd.Name.SET_WINDOW_SIZE, post('/session/:sessionId/window/size')],
+]);
+
+
+/**
+ * Handles sending HTTP messages to a remote end.
+ *
+ * @interface
+ */
+class Client {
+
+ /**
+ * Sends a request to the server. The client will automatically follow any
+ * redirects returned by the server, fulfilling the returned promise with the
+ * final response.
+ *
+ * @param {!Request} httpRequest The request to send.
+ * @return {!Promise<Response>} A promise that will be fulfilled with the
+ * server's response.
+ */
+ send(httpRequest) {}
+}
+
+
+const CLIENTS =
+ /** !WeakMap<!Executor, !(Client|IThenable<!Client>)> */new WeakMap;
+
+
+/**
+ * Sends a request using the given executor.
+ * @param {!Executor} executor
+ * @param {!Request} request
+ * @return {!Promise<Response>}
+ */
+function doSend(executor, request) {
+ const client = CLIENTS.get(executor);
+ if (promise.isPromise(client)) {
+ return client.then(client => {
+ CLIENTS.set(executor, client);
+ return client.send(request);
+ });
+ } else {
+ return client.send(request);
+ }
+}
+
+
+/**
+ * A command executor that communicates with the server using JSON over HTTP.
+ *
+ * By default, each instance of this class will use the legacy wire protocol
+ * from [Selenium project][json]. The executor will automatically switch to the
+ * [W3C wire protocol][w3c] if the remote end returns a compliant response to
+ * a new session command.
+ *
+ * [json]: https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol
+ * [w3c]: https://w3c.github.io/webdriver/webdriver-spec.html
+ *
+ * @implements {cmd.Executor}
+ */
+class Executor {
+ /**
+ * @param {!(Client|IThenable<!Client>)} client The client to use for sending
+ * requests to the server, or a promise-like object that will resolve to
+ * to the client.
+ */
+ constructor(client) {
+ CLIENTS.set(this, client);
+
+ /**
+ * Whether this executor should use the W3C wire protocol. The executor
+ * will automatically switch if the remote end sends a compliant response
+ * to a new session command, however, this property may be directly set to
+ * `true` to force the executor into W3C mode.
+ * @type {boolean}
+ */
+ this.w3c = false;
+
+ /** @private {Map<string, {method: string, path: string}>} */
+ this.customCommands_ = null;
+
+ /** @private {!logging.Logger} */
+ this.log_ = logging.getLogger('webdriver.http.Executor');
+ }
+
+ /**
+ * Defines a new command for use with this executor. When a command is sent,
+ * the {@code path} will be preprocessed using the command's parameters; any
+ * path segments prefixed with ":" will be replaced by the parameter of the
+ * same name. For example, given "/person/:name" and the parameters
+ * "{name: 'Bob'}", the final command path will be "/person/Bob".
+ *
+ * @param {string} name The command name.
+ * @param {string} method The HTTP method to use when sending this command.
+ * @param {string} path The path to send the command to, relative to
+ * the WebDriver server's command root and of the form
+ * "/path/:variable/segment".
+ */
+ defineCommand(name, method, path) {
+ if (!this.customCommands_) {
+ this.customCommands_ = new Map;
+ }
+ this.customCommands_.set(name, {method, path});
+ }
+
+ /** @override */
+ execute(command) {
+ let resource =
+ (this.customCommands_ && this.customCommands_.get(command.getName()))
+ || (this.w3c && W3C_COMMAND_MAP.get(command.getName()))
+ || COMMAND_MAP.get(command.getName());
+ if (!resource) {
+ throw new error.UnknownCommandError(
+ 'Unrecognized command: ' + command.getName());
+ }
+
+ let parameters = command.getParameters();
+ let path = buildPath(resource.path, parameters);
+ let request = new Request(resource.method, path, parameters);
+
+ let log = this.log_;
+ log.finer(() => '>>>\n' + request);
+ return doSend(this, request).then(response => {
+ log.finer(() => '<<<\n' + response);
+
+ let parsed =
+ parseHttpResponse(/** @type {!Response} */ (response), this.w3c);
+
+ if (command.getName() === cmd.Name.NEW_SESSION
+ || command.getName() === cmd.Name.DESCRIBE_SESSION) {
+ if (!parsed || !parsed['sessionId']) {
+ throw new error.WebDriverError(
+ 'Unable to parse new session response: ' + response.body);
+ }
+
+ // The remote end is a W3C compliant server if there is no `status`
+ // field in the response. This is not appliable for the DESCRIBE_SESSION
+ // command, which is not defined in the W3C spec.
+ if (command.getName() === cmd.Name.NEW_SESSION) {
+ this.w3c = this.w3c || !('status' in parsed);
+ }
+
+ return new Session(parsed['sessionId'], parsed['value']);
+ }
+
+ if (parsed
+ && typeof parsed === 'object'
+ && 'value' in parsed) {
+ let value = parsed['value'];
+ return typeof value === 'undefined' ? null : value;
+ }
+ return parsed;
+ });
+ }
+}
+
+
+/**
+ * @param {string} str .
+ * @return {?} .
+ */
+function tryParse(str) {
+ try {
+ return JSON.parse(str);
+ } catch (ignored) {
+ // Do nothing.
+ }
+}
+
+
+/**
+ * Callback used to parse {@link Response} objects from a
+ * {@link HttpClient}.
+ * @param {!Response} httpResponse The HTTP response to parse.
+ * @param {boolean} w3c Whether the response should be processed using the
+ * W3C wire protocol.
+ * @return {?} The parsed response.
+ * @throws {WebDriverError} If the HTTP response is an error.
+ */
+function parseHttpResponse(httpResponse, w3c) {
+ let parsed = tryParse(httpResponse.body);
+ if (parsed !== undefined) {
+ if (w3c) {
+ if (httpResponse.status > 399) {
+ error.throwDecodedError(parsed);
+ }
+
+ if (httpResponse.status < 200) {
+ // This should never happen, but throw the raw response so
+ // users report it.
+ throw new error.WebDriverError(
+ `Unexpected HTTP response:\n${httpResponse}`);
+ }
+ } else {
+ error.checkLegacyResponse(parsed);
+ }
+ return parsed;
+ }
+
+ let value = httpResponse.body.replace(/\r\n/g, '\n');
+
+ // 404 represents an unknown command; anything else > 399 is a generic unknown
+ // error.
+ if (httpResponse.status == 404) {
+ throw new error.UnsupportedOperationError(value);
+ } else if (httpResponse.status >= 400) {
+ throw new error.WebDriverError(value);
+ }
+
+ return value || null;
+}
+
+
+/**
+ * Builds a fully qualified path using the given set of command parameters. Each
+ * path segment prefixed with ':' will be replaced by the value of the
+ * corresponding parameter. All parameters spliced into the path will be
+ * removed from the parameter map.
+ * @param {string} path The original resource path.
+ * @param {!Object<*>} parameters The parameters object to splice into the path.
+ * @return {string} The modified path.
+ */
+function buildPath(path, parameters) {
+ let pathParameters = path.match(/\/:(\w+)\b/g);
+ if (pathParameters) {
+ for (let i = 0; i < pathParameters.length; ++i) {
+ let key = pathParameters[i].substring(2); // Trim the /:
+ if (key in parameters) {
+ let value = parameters[key];
+ if (WebElement.isId(value)) {
+ // When inserting a WebElement into the URL, only use its ID value,
+ // not the full JSON.
+ value = WebElement.extractId(value);
+ }
+ path = path.replace(pathParameters[i], '/' + value);
+ delete parameters[key];
+ } else {
+ throw new error.InvalidArgumentError(
+ 'Missing required parameter: ' + key);
+ }
+ }
+ }
+ return path;
+}
+
+
+// PUBLIC API
+
+exports.Executor = Executor;
+exports.Client = Client;
+exports.Request = Request;
+exports.Response = Response;
+exports.buildPath = buildPath; // Exported for testing.
diff --git a/node_modules/selenium-webdriver/lib/input.js b/node_modules/selenium-webdriver/lib/input.js
new file mode 100644
index 000000000..058530ebc
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/input.js
@@ -0,0 +1,171 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+/**
+ * @fileoverview Defines types related to user input with the WebDriver API.
+ */
+
+
+/**
+ * Enumeration of the buttons used in the advanced interactions API.
+ * @enum {number}
+ */
+const Button = {
+ LEFT: 0,
+ MIDDLE: 1,
+ RIGHT: 2
+};
+
+
+
+/**
+ * Representations of pressable keys that aren't text. These are stored in
+ * the Unicode PUA (Private Use Area) code points, 0xE000-0xF8FF. Refer to
+ * http://www.google.com.au/search?&q=unicode+pua&btnG=Search
+ *
+ * @enum {string}
+ */
+const Key = {
+ NULL: '\uE000',
+ CANCEL: '\uE001', // ^break
+ HELP: '\uE002',
+ BACK_SPACE: '\uE003',
+ TAB: '\uE004',
+ CLEAR: '\uE005',
+ RETURN: '\uE006',
+ ENTER: '\uE007',
+ SHIFT: '\uE008',
+ CONTROL: '\uE009',
+ ALT: '\uE00A',
+ PAUSE: '\uE00B',
+ ESCAPE: '\uE00C',
+ SPACE: '\uE00D',
+ PAGE_UP: '\uE00E',
+ PAGE_DOWN: '\uE00F',
+ END: '\uE010',
+ HOME: '\uE011',
+ ARROW_LEFT: '\uE012',
+ LEFT: '\uE012',
+ ARROW_UP: '\uE013',
+ UP: '\uE013',
+ ARROW_RIGHT: '\uE014',
+ RIGHT: '\uE014',
+ ARROW_DOWN: '\uE015',
+ DOWN: '\uE015',
+ INSERT: '\uE016',
+ DELETE: '\uE017',
+ SEMICOLON: '\uE018',
+ EQUALS: '\uE019',
+
+ NUMPAD0: '\uE01A', // number pad keys
+ NUMPAD1: '\uE01B',
+ NUMPAD2: '\uE01C',
+ NUMPAD3: '\uE01D',
+ NUMPAD4: '\uE01E',
+ NUMPAD5: '\uE01F',
+ NUMPAD6: '\uE020',
+ NUMPAD7: '\uE021',
+ NUMPAD8: '\uE022',
+ NUMPAD9: '\uE023',
+ MULTIPLY: '\uE024',
+ ADD: '\uE025',
+ SEPARATOR: '\uE026',
+ SUBTRACT: '\uE027',
+ DECIMAL: '\uE028',
+ DIVIDE: '\uE029',
+
+ F1: '\uE031', // function keys
+ F2: '\uE032',
+ F3: '\uE033',
+ F4: '\uE034',
+ F5: '\uE035',
+ F6: '\uE036',
+ F7: '\uE037',
+ F8: '\uE038',
+ F9: '\uE039',
+ F10: '\uE03A',
+ F11: '\uE03B',
+ F12: '\uE03C',
+
+ COMMAND: '\uE03D', // Apple command key
+ META: '\uE03D' // alias for Windows key
+};
+
+
+/**
+ * Simulate pressing many keys at once in a "chord". Takes a sequence of
+ * {@linkplain Key keys} or strings, appends each of the values to a string,
+ * adds the chord termination key ({@link Key.NULL}) and returns the resulting
+ * string.
+ *
+ * Note: when the low-level webdriver key handlers see Keys.NULL, active
+ * modifier keys (CTRL/ALT/SHIFT/etc) release via a keyup event.
+ *
+ * @param {...string} var_args The key sequence to concatenate.
+ * @return {string} The null-terminated key sequence.
+ */
+Key.chord = function(var_args) {
+ return Array.prototype.slice.call(arguments, 0).join('') + Key.NULL;
+};
+
+
+/**
+ * Used with {@link ./webelement.WebElement#sendKeys WebElement#sendKeys} on
+ * file input elements (`<input type="file">`) to detect when the entered key
+ * sequence defines the path to a file.
+ *
+ * By default, {@linkplain ./webelement.WebElement WebElement's} will enter all
+ * key sequences exactly as entered. You may set a
+ * {@linkplain ./webdriver.WebDriver#setFileDetector file detector} on the
+ * parent WebDriver instance to define custom behavior for handling file
+ * elements. Of particular note is the
+ * {@link selenium-webdriver/remote.FileDetector}, which should be used when
+ * running against a remote
+ * [Selenium Server](http://docs.seleniumhq.org/download/).
+ */
+class FileDetector {
+
+ /**
+ * Handles the file specified by the given path, preparing it for use with
+ * the current browser. If the path does not refer to a valid file, it will
+ * be returned unchanged, otherwisee a path suitable for use with the current
+ * browser will be returned.
+ *
+ * This default implementation is a no-op. Subtypes may override this function
+ * for custom tailored file handling.
+ *
+ * @param {!./webdriver.WebDriver} driver The driver for the current browser.
+ * @param {string} path The path to process.
+ * @return {!Promise<string>} A promise for the processed file path.
+ * @package
+ */
+ handleFile(driver, path) {
+ return Promise.resolve(path);
+ }
+}
+
+
+// PUBLIC API
+
+
+module.exports = {
+ Button: Button,
+ Key: Key,
+ FileDetector: FileDetector
+};
diff --git a/node_modules/selenium-webdriver/lib/logging.js b/node_modules/selenium-webdriver/lib/logging.js
new file mode 100644
index 000000000..97f54924c
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/logging.js
@@ -0,0 +1,670 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+/**
+ * @fileoverview Defines WebDriver's logging system. The logging system is
+ * broken into major components: local and remote logging.
+ *
+ * The local logging API, which is anchored by the {@linkplain Logger} class is
+ * similar to Java's logging API. Loggers, retrieved by
+ * {@linkplain #getLogger getLogger(name)}, use hierarchical, dot-delimited
+ * namespaces (e.g. "" > "webdriver" > "webdriver.logging"). Recorded log
+ * messages are represented by the {@linkplain Entry} class. You can capture log
+ * records by {@linkplain Logger#addHandler attaching} a handler function to the
+ * desired logger. For convenience, you can quickly enable logging to the
+ * console by simply calling {@linkplain #installConsoleHandler
+ * installConsoleHandler}.
+ *
+ * The [remote logging API](https://github.com/SeleniumHQ/selenium/wiki/Logging)
+ * allows you to retrieve logs from a remote WebDriver server. This API uses the
+ * {@link Preferences} class to define desired log levels prior to creating
+ * a WebDriver session:
+ *
+ * var prefs = new logging.Preferences();
+ * prefs.setLevel(logging.Type.BROWSER, logging.Level.DEBUG);
+ *
+ * var caps = Capabilities.chrome();
+ * caps.setLoggingPrefs(prefs);
+ * // ...
+ *
+ * Remote log entries, also represented by the {@link Entry} class, may be
+ * retrieved via {@link webdriver.WebDriver.Logs}:
+ *
+ * driver.manage().logs().get(logging.Type.BROWSER)
+ * .then(function(entries) {
+ * entries.forEach(function(entry) {
+ * console.log('[%s] %s', entry.level.name, entry.message);
+ * });
+ * });
+ *
+ * **NOTE:** Only a few browsers support the remote logging API (notably
+ * Firefox and Chrome). Firefox supports basic logging functionality, while
+ * Chrome exposes robust
+ * [performance logging](https://sites.google.com/a/chromium.org/chromedriver/logging)
+ * options. Remote logging is still considered a non-standard feature, and the
+ * APIs exposed by this module for it are non-frozen. This module will be
+ * updated, possibly breaking backwards-compatibility, once logging is
+ * officially defined by the
+ * [W3C WebDriver spec](http://www.w3.org/TR/webdriver/).
+ */
+
+/**
+ * Defines a message level that may be used to control logging output.
+ *
+ * @final
+ */
+class Level {
+ /**
+ * @param {string} name the level's name.
+ * @param {number} level the level's numeric value.
+ */
+ constructor(name, level) {
+ if (level < 0) {
+ throw new TypeError('Level must be >= 0');
+ }
+
+ /** @private {string} */
+ this.name_ = name;
+
+ /** @private {number} */
+ this.value_ = level;
+ }
+
+ /** This logger's name. */
+ get name() {
+ return this.name_;
+ }
+
+ /** The numeric log level. */
+ get value() {
+ return this.value_;
+ }
+
+ /** @override */
+ toString() {
+ return this.name;
+ }
+}
+
+/**
+ * Indicates no log messages should be recorded.
+ * @const
+ */
+Level.OFF = new Level('OFF', Infinity);
+
+
+/**
+ * Log messages with a level of `1000` or higher.
+ * @const
+ */
+Level.SEVERE = new Level('SEVERE', 1000);
+
+
+/**
+ * Log messages with a level of `900` or higher.
+ * @const
+ */
+Level.WARNING = new Level('WARNING', 900);
+
+
+/**
+ * Log messages with a level of `800` or higher.
+ * @const
+ */
+Level.INFO = new Level('INFO', 800);
+
+
+/**
+ * Log messages with a level of `700` or higher.
+ * @const
+ */
+Level.DEBUG = new Level('DEBUG', 700);
+
+
+/**
+ * Log messages with a level of `500` or higher.
+ * @const
+ */
+Level.FINE = new Level('FINE', 500);
+
+
+/**
+ * Log messages with a level of `400` or higher.
+ * @const
+ */
+Level.FINER = new Level('FINER', 400);
+
+
+/**
+ * Log messages with a level of `300` or higher.
+ * @const
+ */
+Level.FINEST = new Level('FINEST', 300);
+
+
+/**
+ * Indicates all log messages should be recorded.
+ * @const
+ */
+Level.ALL = new Level('ALL', 0);
+
+
+const ALL_LEVELS = /** !Set<Level> */new Set([
+ Level.OFF,
+ Level.SEVERE,
+ Level.WARNING,
+ Level.INFO,
+ Level.DEBUG,
+ Level.FINE,
+ Level.FINER,
+ Level.FINEST,
+ Level.ALL
+]);
+
+
+const LEVELS_BY_NAME = /** !Map<string, !Level> */ new Map([
+ [Level.OFF.name, Level.OFF],
+ [Level.SEVERE.name, Level.SEVERE],
+ [Level.WARNING.name, Level.WARNING],
+ [Level.INFO.name, Level.INFO],
+ [Level.DEBUG.name, Level.DEBUG],
+ [Level.FINE.name, Level.FINE],
+ [Level.FINER.name, Level.FINER],
+ [Level.FINEST.name, Level.FINEST],
+ [Level.ALL.name, Level.ALL]
+]);
+
+
+/**
+ * Converts a level name or value to a {@link Level} value. If the name/value
+ * is not recognized, {@link Level.ALL} will be returned.
+ *
+ * @param {(number|string)} nameOrValue The log level name, or value, to
+ * convert.
+ * @return {!Level} The converted level.
+ */
+function getLevel(nameOrValue) {
+ if (typeof nameOrValue === 'string') {
+ return LEVELS_BY_NAME.get(nameOrValue) || Level.ALL;
+ }
+ if (typeof nameOrValue !== 'number') {
+ throw new TypeError('not a string or number');
+ }
+ for (let level of ALL_LEVELS) {
+ if (nameOrValue >= level.value) {
+ return level;
+ }
+ }
+ return Level.ALL;
+}
+
+
+/**
+ * Describes a single log entry.
+ *
+ * @final
+ */
+class Entry {
+ /**
+ * @param {(!Level|string|number)} level The entry level.
+ * @param {string} message The log message.
+ * @param {number=} opt_timestamp The time this entry was generated, in
+ * milliseconds since 0:00:00, January 1, 1970 UTC. If omitted, the
+ * current time will be used.
+ * @param {string=} opt_type The log type, if known.
+ */
+ constructor(level, message, opt_timestamp, opt_type) {
+ this.level = level instanceof Level ? level : getLevel(level);
+ this.message = message;
+ this.timestamp =
+ typeof opt_timestamp === 'number' ? opt_timestamp : Date.now();
+ this.type = opt_type || '';
+ }
+
+ /**
+ * @return {{level: string, message: string, timestamp: number,
+ * type: string}} The JSON representation of this entry.
+ */
+ toJSON() {
+ return {
+ 'level': this.level.name,
+ 'message': this.message,
+ 'timestamp': this.timestamp,
+ 'type': this.type
+ };
+ }
+}
+
+
+/** @typedef {(string|function(): string)} */
+let Loggable;
+
+
+/**
+ * An object used to log debugging messages. Loggers use a hierarchical,
+ * dot-separated naming scheme. For instance, "foo" is considered the parent of
+ * the "foo.bar" and an ancestor of "foo.bar.baz".
+ *
+ * Each logger may be assigned a {@linkplain #setLevel log level}, which
+ * controls which level of messages will be reported to the
+ * {@linkplain #addHandler handlers} attached to this instance. If a log level
+ * is not explicitly set on a logger, it will inherit its parent.
+ *
+ * This class should never be directly instantiated. Instead, users should
+ * obtain logger references using the {@linkplain ./logging.getLogger()
+ * getLogger()} function.
+ *
+ * @final
+ */
+class Logger {
+ /**
+ * @param {string} name the name of this logger.
+ * @param {Level=} opt_level the initial level for this logger.
+ */
+ constructor(name, opt_level) {
+ /** @private {string} */
+ this.name_ = name;
+
+ /** @private {Level} */
+ this.level_ = opt_level || null;
+
+ /** @private {Logger} */
+ this.parent_ = null;
+
+ /** @private {Set<function(!Entry)>} */
+ this.handlers_ = null;
+ }
+
+ /** @return {string} the name of this logger. */
+ getName() {
+ return this.name_;
+ }
+
+ /**
+ * @param {Level} level the new level for this logger, or `null` if the logger
+ * should inherit its level from its parent logger.
+ */
+ setLevel(level) {
+ this.level_ = level;
+ }
+
+ /** @return {Level} the log level for this logger. */
+ getLevel() {
+ return this.level_;
+ }
+
+ /**
+ * @return {!Level} the effective level for this logger.
+ */
+ getEffectiveLevel() {
+ let logger = this;
+ let level;
+ do {
+ level = logger.level_;
+ logger = logger.parent_;
+ } while (logger && !level);
+ return level || Level.OFF;
+ }
+
+ /**
+ * @param {!Level} level the level to check.
+ * @return {boolean} whether messages recorded at the given level are loggable
+ * by this instance.
+ */
+ isLoggable(level) {
+ return level.value !== Level.OFF.value
+ && level.value >= this.getEffectiveLevel().value;
+ }
+
+ /**
+ * Adds a handler to this logger. The handler will be invoked for each message
+ * logged with this instance, or any of its descendants.
+ *
+ * @param {function(!Entry)} handler the handler to add.
+ */
+ addHandler(handler) {
+ if (!this.handlers_) {
+ this.handlers_ = new Set;
+ }
+ this.handlers_.add(handler);
+ }
+
+ /**
+ * Removes a handler from this logger.
+ *
+ * @param {function(!Entry)} handler the handler to remove.
+ * @return {boolean} whether a handler was successfully removed.
+ */
+ removeHandler(handler) {
+ if (!this.handlers_) {
+ return false;
+ }
+ return this.handlers_.delete(handler);
+ }
+
+ /**
+ * Logs a message at the given level. The message may be defined as a string
+ * or as a function that will return the message. If a function is provided,
+ * it will only be invoked if this logger's
+ * {@linkplain #getEffectiveLevel() effective log level} includes the given
+ * `level`.
+ *
+ * @param {!Level} level the level at which to log the message.
+ * @param {(string|function(): string)} loggable the message to log, or a
+ * function that will return the message.
+ */
+ log(level, loggable) {
+ if (!this.isLoggable(level)) {
+ return;
+ }
+ let message = '[' + this.name_ + '] '
+ + (typeof loggable === 'function' ? loggable() : loggable);
+ let entry = new Entry(level, message, Date.now());
+ for (let logger = this; !!logger; logger = logger.parent_) {
+ if (logger.handlers_) {
+ for (let handler of logger.handlers_) {
+ handler(entry);
+ }
+ }
+ }
+ }
+
+ /**
+ * Logs a message at the {@link Level.SEVERE} log level.
+ * @param {(string|function(): string)} loggable the message to log, or a
+ * function that will return the message.
+ */
+ severe(loggable) {
+ this.log(Level.SEVERE, loggable);
+ }
+
+ /**
+ * Logs a message at the {@link Level.WARNING} log level.
+ * @param {(string|function(): string)} loggable the message to log, or a
+ * function that will return the message.
+ */
+ warning(loggable) {
+ this.log(Level.WARNING, loggable);
+ }
+
+ /**
+ * Logs a message at the {@link Level.INFO} log level.
+ * @param {(string|function(): string)} loggable the message to log, or a
+ * function that will return the message.
+ */
+ info(loggable) {
+ this.log(Level.INFO, loggable);
+ }
+
+ /**
+ * Logs a message at the {@link Level.DEBUG} log level.
+ * @param {(string|function(): string)} loggable the message to log, or a
+ * function that will return the message.
+ */
+ debug(loggable) {
+ this.log(Level.DEBUG, loggable);
+ }
+
+ /**
+ * Logs a message at the {@link Level.FINE} log level.
+ * @param {(string|function(): string)} loggable the message to log, or a
+ * function that will return the message.
+ */
+ fine(loggable) {
+ this.log(Level.FINE, loggable);
+ }
+
+ /**
+ * Logs a message at the {@link Level.FINER} log level.
+ * @param {(string|function(): string)} loggable the message to log, or a
+ * function that will return the message.
+ */
+ finer(loggable) {
+ this.log(Level.FINER, loggable);
+ }
+
+ /**
+ * Logs a message at the {@link Level.FINEST} log level.
+ * @param {(string|function(): string)} loggable the message to log, or a
+ * function that will return the message.
+ */
+ finest(loggable) {
+ this.log(Level.FINEST, loggable);
+ }
+}
+
+
+/**
+ * Maintains a collection of loggers.
+ *
+ * @final
+ */
+class LogManager {
+ constructor() {
+ /** @private {!Map<string, !Logger>} */
+ this.loggers_ = new Map;
+ this.root_ = new Logger('', Level.OFF);
+ }
+
+ /**
+ * Retrieves a named logger, creating it in the process. This function will
+ * implicitly create the requested logger, and any of its parents, if they
+ * do not yet exist.
+ *
+ * @param {string} name the logger's name.
+ * @return {!Logger} the requested logger.
+ */
+ getLogger(name) {
+ if (!name) {
+ return this.root_;
+ }
+ let parent = this.root_;
+ for (let i = name.indexOf('.'); i != -1; i = name.indexOf('.', i + 1)) {
+ let parentName = name.substr(0, i);
+ parent = this.createLogger_(parentName, parent);
+ }
+ return this.createLogger_(name, parent);
+ }
+
+ /**
+ * Creates a new logger.
+ *
+ * @param {string} name the logger's name.
+ * @param {!Logger} parent the logger's parent.
+ * @return {!Logger} the new logger.
+ * @private
+ */
+ createLogger_(name, parent) {
+ if (this.loggers_.has(name)) {
+ return /** @type {!Logger} */(this.loggers_.get(name));
+ }
+ let logger = new Logger(name, null);
+ logger.parent_ = parent;
+ this.loggers_.set(name, logger);
+ return logger;
+ }
+}
+
+
+const logManager = new LogManager;
+
+
+/**
+ * Retrieves a named logger, creating it in the process. This function will
+ * implicitly create the requested logger, and any of its parents, if they
+ * do not yet exist.
+ *
+ * The log level will be unspecified for newly created loggers. Use
+ * {@link Logger#setLevel(level)} to explicitly set a level.
+ *
+ * @param {string} name the logger's name.
+ * @return {!Logger} the requested logger.
+ */
+function getLogger(name) {
+ return logManager.getLogger(name);
+}
+
+
+function pad(n) {
+ if (n > 10) {
+ return '' + n;
+ } else {
+ return '0' + n;
+ }
+}
+
+
+/**
+ * Logs all messages to the Console API.
+ * @param {!Entry} entry the entry to log.
+ */
+function consoleHandler(entry) {
+ if (typeof console === 'undefined' || !console) {
+ return;
+ }
+
+ var timestamp = new Date(entry.timestamp);
+ var msg =
+ '[' + timestamp.getUTCFullYear() + '-' +
+ pad(timestamp.getUTCMonth() + 1) + '-' +
+ pad(timestamp.getUTCDate()) + 'T' +
+ pad(timestamp.getUTCHours()) + ':' +
+ pad(timestamp.getUTCMinutes()) + ':' +
+ pad(timestamp.getUTCSeconds()) + 'Z] ' +
+ '[' + entry.level.name + '] ' +
+ entry.message;
+
+ var level = entry.level.value;
+ if (level >= Level.SEVERE.value) {
+ console.error(msg);
+ } else if (level >= Level.WARNING.value) {
+ console.warn(msg);
+ } else {
+ console.log(msg);
+ }
+}
+
+
+/**
+ * Adds the console handler to the given logger. The console handler will log
+ * all messages using the JavaScript Console API.
+ *
+ * @param {Logger=} opt_logger The logger to add the handler to; defaults
+ * to the root logger.
+ */
+function addConsoleHandler(opt_logger) {
+ let logger = opt_logger || logManager.root_;
+ logger.addHandler(consoleHandler);
+}
+
+
+/**
+ * Removes the console log handler from the given logger.
+ *
+ * @param {Logger=} opt_logger The logger to remove the handler from; defaults
+ * to the root logger.
+ * @see exports.addConsoleHandler
+ */
+function removeConsoleHandler(opt_logger) {
+ let logger = opt_logger || logManager.root_;
+ logger.removeHandler(consoleHandler);
+}
+
+
+/**
+ * Installs the console log handler on the root logger.
+ */
+function installConsoleHandler() {
+ addConsoleHandler(logManager.root_);
+}
+
+
+/**
+ * Common log types.
+ * @enum {string}
+ */
+const Type = {
+ /** Logs originating from the browser. */
+ BROWSER: 'browser',
+ /** Logs from a WebDriver client. */
+ CLIENT: 'client',
+ /** Logs from a WebDriver implementation. */
+ DRIVER: 'driver',
+ /** Logs related to performance. */
+ PERFORMANCE: 'performance',
+ /** Logs from the remote server. */
+ SERVER: 'server'
+};
+
+
+/**
+ * Describes the log preferences for a WebDriver session.
+ *
+ * @final
+ */
+class Preferences {
+ constructor() {
+ /** @private {!Map<string, !Level>} */
+ this.prefs_ = new Map;
+ }
+
+ /**
+ * Sets the desired logging level for a particular log type.
+ * @param {(string|Type)} type The log type.
+ * @param {(!Level|string|number)} level The desired log level.
+ * @throws {TypeError} if `type` is not a `string`.
+ */
+ setLevel(type, level) {
+ if (typeof type !== 'string') {
+ throw TypeError('specified log type is not a string: ' + typeof type);
+ }
+ this.prefs_.set(type, level instanceof Level ? level : getLevel(level));
+ }
+
+ /**
+ * Converts this instance to its JSON representation.
+ * @return {!Object<string, string>} The JSON representation of this set of
+ * preferences.
+ */
+ toJSON() {
+ let json = {};
+ for (let key of this.prefs_.keys()) {
+ json[key] = this.prefs_.get(key).name;
+ }
+ return json;
+ }
+}
+
+
+// PUBLIC API
+
+
+module.exports = {
+ Entry: Entry,
+ Level: Level,
+ LogManager: LogManager,
+ Logger: Logger,
+ Preferences: Preferences,
+ Type: Type,
+ addConsoleHandler: addConsoleHandler,
+ getLevel: getLevel,
+ getLogger: getLogger,
+ installConsoleHandler: installConsoleHandler,
+ removeConsoleHandler: removeConsoleHandler
+};
diff --git a/node_modules/selenium-webdriver/lib/promise.js b/node_modules/selenium-webdriver/lib/promise.js
new file mode 100644
index 000000000..b98e7cf2e
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/promise.js
@@ -0,0 +1,3039 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+/**
+ * @fileoverview
+ * The promise module is centered around the {@linkplain ControlFlow}, a class
+ * that coordinates the execution of asynchronous tasks. The ControlFlow allows
+ * users to focus on the imperative commands for their script without worrying
+ * about chaining together every single asynchronous action, which can be
+ * tedious and verbose. APIs may be layered on top of the control flow to read
+ * as if they were synchronous. For instance, the core
+ * {@linkplain ./webdriver.WebDriver WebDriver} API is built on top of the
+ * control flow, allowing users to write
+ *
+ * driver.get('http://www.google.com/ncr');
+ * driver.findElement({name: 'q'}).sendKeys('webdriver');
+ * driver.findElement({name: 'btnGn'}).click();
+ *
+ * instead of
+ *
+ * driver.get('http://www.google.com/ncr')
+ * .then(function() {
+ * return driver.findElement({name: 'q'});
+ * })
+ * .then(function(q) {
+ * return q.sendKeys('webdriver');
+ * })
+ * .then(function() {
+ * return driver.findElement({name: 'btnG'});
+ * })
+ * .then(function(btnG) {
+ * return btnG.click();
+ * });
+ *
+ * ## Tasks and Task Queues
+ *
+ * The control flow is based on the concept of tasks and task queues. Tasks are
+ * functions that define the basic unit of work for the control flow to execute.
+ * Each task is scheduled via {@link ControlFlow#execute()}, which will return
+ * a {@link ManagedPromise ManagedPromise} that will be resolved with the task's
+ * result.
+ *
+ * A task queue contains all of the tasks scheduled within a single turn of the
+ * [JavaScript event loop][JSEL]. The control flow will create a new task queue
+ * the first time a task is scheduled within an event loop.
+ *
+ * var flow = promise.controlFlow();
+ * flow.execute(foo); // Creates a new task queue and inserts foo.
+ * flow.execute(bar); // Inserts bar into the same queue as foo.
+ * setTimeout(function() {
+ * flow.execute(baz); // Creates a new task queue and inserts baz.
+ * }, 0);
+ *
+ * Whenever the control flow creates a new task queue, it will automatically
+ * begin executing tasks in the next available turn of the event loop. This
+ * execution is scheduled using a "micro-task" timer, such as a (native)
+ * `ManagedPromise.then()` callback.
+ *
+ * setTimeout(() => console.log('a'));
+ * ManagedPromise.resolve().then(() => console.log('b')); // A native promise.
+ * flow.execute(() => console.log('c'));
+ * ManagedPromise.resolve().then(() => console.log('d'));
+ * setTimeout(() => console.log('fin'));
+ * // b
+ * // c
+ * // d
+ * // a
+ * // fin
+ *
+ * In the example above, b/c/d is logged before a/fin because native promises
+ * and this module use "micro-task" timers, which have a higher priority than
+ * "macro-tasks" like `setTimeout`.
+ *
+ * ## Task Execution
+ *
+ * Upon creating a task queue, and whenever an exisiting queue completes a task,
+ * the control flow will schedule a micro-task timer to process any scheduled
+ * tasks. This ensures no task is ever started within the same turn of the
+ * JavaScript event loop in which it was scheduled, nor is a task ever started
+ * within the same turn that another finishes.
+ *
+ * When the execution timer fires, a single task will be dequeued and executed.
+ * There are several important events that may occur while executing a task
+ * function:
+ *
+ * 1. A new task queue is created by a call to {@link ControlFlow#execute()}.
+ * Any tasks scheduled within this task queue are considered subtasks of the
+ * current task.
+ * 2. The task function throws an error. Any scheduled tasks are immediately
+ * discarded and the task's promised result (previously returned by
+ * {@link ControlFlow#execute()}) is immediately rejected with the thrown
+ * error.
+ * 3. The task function returns sucessfully.
+ *
+ * If a task function created a new task queue, the control flow will wait for
+ * that queue to complete before processing the task result. If the queue
+ * completes without error, the flow will settle the task's promise with the
+ * value originaly returned by the task function. On the other hand, if the task
+ * queue termintes with an error, the task's promise will be rejected with that
+ * error.
+ *
+ * flow.execute(function() {
+ * flow.execute(() => console.log('a'));
+ * flow.execute(() => console.log('b'));
+ * });
+ * flow.execute(() => console.log('c'));
+ * // a
+ * // b
+ * // c
+ *
+ * ## ManagedPromise Integration
+ *
+ * In addition to the {@link ControlFlow} class, the promise module also exports
+ * a [ManagedPromise/A+] {@linkplain ManagedPromise implementation} that is deeply
+ * integrated with the ControlFlow. First and foremost, each promise
+ * {@linkplain ManagedPromise#then() callback} is scheduled with the
+ * control flow as a task. As a result, each callback is invoked in its own turn
+ * of the JavaScript event loop with its own task queue. If any tasks are
+ * scheduled within a callback, the callback's promised result will not be
+ * settled until the task queue has completed.
+ *
+ * promise.fulfilled().then(function() {
+ * flow.execute(function() {
+ * console.log('b');
+ * });
+ * }).then(() => console.log('a'));
+ * // b
+ * // a
+ *
+ * ### Scheduling ManagedPromise Callbacks <a id="scheduling_callbacks"></a>
+ *
+ * How callbacks are scheduled in the control flow depends on when they are
+ * attached to the promise. Callbacks attached to a _previously_ resolved
+ * promise are immediately enqueued as subtasks of the currently running task.
+ *
+ * var p = promise.fulfilled();
+ * flow.execute(function() {
+ * flow.execute(() => console.log('A'));
+ * p.then( () => console.log('B'));
+ * flow.execute(() => console.log('C'));
+ * p.then( () => console.log('D'));
+ * }).then(function() {
+ * console.log('fin');
+ * });
+ * // A
+ * // B
+ * // C
+ * // D
+ * // fin
+ *
+ * When a promise is resolved while a task function is on the call stack, any
+ * callbacks also registered in that stack frame are scheduled as if the promise
+ * were already resolved:
+ *
+ * var d = promise.defer();
+ * flow.execute(function() {
+ * flow.execute( () => console.log('A'));
+ * d.promise.then(() => console.log('B'));
+ * flow.execute( () => console.log('C'));
+ * d.promise.then(() => console.log('D'));
+ *
+ * d.fulfill();
+ * }).then(function() {
+ * console.log('fin');
+ * });
+ * // A
+ * // B
+ * // C
+ * // D
+ * // fin
+ *
+ * Callbacks attached to an _unresolved_ promise within a task function are
+ * only weakly scheduled as subtasks and will be dropped if they reach the
+ * front of the queue before the promise is resolved. In the example below, the
+ * callbacks for `B` & `D` are dropped as sub-tasks since they are attached to
+ * an unresolved promise when they reach the front of the task queue.
+ *
+ * var d = promise.defer();
+ * flow.execute(function() {
+ * flow.execute( () => console.log('A'));
+ * d.promise.then(() => console.log('B'));
+ * flow.execute( () => console.log('C'));
+ * d.promise.then(() => console.log('D'));
+ *
+ * setTimeout(d.fulfill, 20);
+ * }).then(function() {
+ * console.log('fin')
+ * });
+ * // A
+ * // C
+ * // fin
+ * // B
+ * // D
+ *
+ * If a promise is resolved while a task function is on the call stack, any
+ * previously registered and unqueued callbacks (i.e. either attached while no
+ * task was on the call stack, or previously dropped as described above) act as
+ * _interrupts_ and are inserted at the front of the task queue. If multiple
+ * promises are fulfilled, their interrupts are enqueued in the order the
+ * promises are resolved.
+ *
+ * var d1 = promise.defer();
+ * d1.promise.then(() => console.log('A'));
+ *
+ * var d2 = promise.defer();
+ * d2.promise.then(() => console.log('B'));
+ *
+ * flow.execute(function() {
+ * d1.promise.then(() => console.log('C'));
+ * flow.execute(() => console.log('D'));
+ * });
+ * flow.execute(function() {
+ * flow.execute(() => console.log('E'));
+ * flow.execute(() => console.log('F'));
+ * d1.fulfill();
+ * d2.fulfill();
+ * }).then(function() {
+ * console.log('fin');
+ * });
+ * // D
+ * // A
+ * // C
+ * // B
+ * // E
+ * // F
+ * // fin
+ *
+ * Within a task function (or callback), each step of a promise chain acts as
+ * an interrupt on the task queue:
+ *
+ * var d = promise.defer();
+ * flow.execute(function() {
+ * d.promise.
+ * then(() => console.log('A')).
+ * then(() => console.log('B')).
+ * then(() => console.log('C')).
+ * then(() => console.log('D'));
+ *
+ * flow.execute(() => console.log('E'));
+ * d.fulfill();
+ * }).then(function() {
+ * console.log('fin');
+ * });
+ * // A
+ * // B
+ * // C
+ * // D
+ * // E
+ * // fin
+ *
+ * If there are multiple promise chains derived from a single promise, they are
+ * processed in the order created:
+ *
+ * var d = promise.defer();
+ * flow.execute(function() {
+ * var chain = d.promise.then(() => console.log('A'));
+ *
+ * chain.then(() => console.log('B')).
+ * then(() => console.log('C'));
+ *
+ * chain.then(() => console.log('D')).
+ * then(() => console.log('E'));
+ *
+ * flow.execute(() => console.log('F'));
+ *
+ * d.fulfill();
+ * }).then(function() {
+ * console.log('fin');
+ * });
+ * // A
+ * // B
+ * // C
+ * // D
+ * // E
+ * // F
+ * // fin
+ *
+ * Even though a subtask's promised result will never resolve while the task
+ * function is on the stack, it will be treated as a promise resolved within the
+ * task. In all other scenarios, a task's promise behaves just like a normal
+ * promise. In the sample below, `C/D` is loggged before `B` because the
+ * resolution of `subtask1` interrupts the flow of the enclosing task. Within
+ * the final subtask, `E/F` is logged in order because `subtask1` is a resolved
+ * promise when that task runs.
+ *
+ * flow.execute(function() {
+ * var subtask1 = flow.execute(() => console.log('A'));
+ * var subtask2 = flow.execute(() => console.log('B'));
+ *
+ * subtask1.then(() => console.log('C'));
+ * subtask1.then(() => console.log('D'));
+ *
+ * flow.execute(function() {
+ * flow.execute(() => console.log('E'));
+ * subtask1.then(() => console.log('F'));
+ * });
+ * }).then(function() {
+ * console.log('fin');
+ * });
+ * // A
+ * // C
+ * // D
+ * // B
+ * // E
+ * // F
+ * // fin
+ *
+ * Finally, consider the following:
+ *
+ * var d = promise.defer();
+ * d.promise.then(() => console.log('A'));
+ * d.promise.then(() => console.log('B'));
+ *
+ * flow.execute(function() {
+ * flow.execute( () => console.log('C'));
+ * d.promise.then(() => console.log('D'));
+ *
+ * flow.execute( () => console.log('E'));
+ * d.promise.then(() => console.log('F'));
+ *
+ * d.fulfill();
+ *
+ * flow.execute( () => console.log('G'));
+ * d.promise.then(() => console.log('H'));
+ * }).then(function() {
+ * console.log('fin');
+ * });
+ * // A
+ * // B
+ * // C
+ * // D
+ * // E
+ * // F
+ * // G
+ * // H
+ * // fin
+ *
+ * In this example, callbacks are registered on `d.promise` both before and
+ * during the invocation of the task function. When `d.fulfill()` is called,
+ * the callbacks registered before the task (`A` & `B`) are registered as
+ * interrupts. The remaining callbacks were all attached within the task and
+ * are scheduled in the flow as standard tasks.
+ *
+ * ## Generator Support
+ *
+ * [Generators][GF] may be scheduled as tasks within a control flow or attached
+ * as callbacks to a promise. Each time the generator yields a promise, the
+ * control flow will wait for that promise to settle before executing the next
+ * iteration of the generator. The yielded promise's fulfilled value will be
+ * passed back into the generator:
+ *
+ * flow.execute(function* () {
+ * var d = promise.defer();
+ *
+ * setTimeout(() => console.log('...waiting...'), 25);
+ * setTimeout(() => d.fulfill(123), 50);
+ *
+ * console.log('start: ' + Date.now());
+ *
+ * var value = yield d.promise;
+ * console.log('mid: %d; value = %d', Date.now(), value);
+ *
+ * yield promise.delayed(10);
+ * console.log('end: ' + Date.now());
+ * }).then(function() {
+ * console.log('fin');
+ * });
+ * // start: 0
+ * // ...waiting...
+ * // mid: 50; value = 123
+ * // end: 60
+ * // fin
+ *
+ * Yielding the result of a promise chain will wait for the entire chain to
+ * complete:
+ *
+ * promise.fulfilled().then(function* () {
+ * console.log('start: ' + Date.now());
+ *
+ * var value = yield flow.
+ * execute(() => console.log('A')).
+ * then( () => console.log('B')).
+ * then( () => 123);
+ *
+ * console.log('mid: %s; value = %d', Date.now(), value);
+ *
+ * yield flow.execute(() => console.log('C'));
+ * }).then(function() {
+ * console.log('fin');
+ * });
+ * // start: 0
+ * // A
+ * // B
+ * // mid: 2; value = 123
+ * // C
+ * // fin
+ *
+ * Yielding a _rejected_ promise will cause the rejected value to be thrown
+ * within the generator function:
+ *
+ * flow.execute(function* () {
+ * console.log('start: ' + Date.now());
+ * try {
+ * yield promise.delayed(10).then(function() {
+ * throw Error('boom');
+ * });
+ * } catch (ex) {
+ * console.log('caught time: ' + Date.now());
+ * console.log(ex.message);
+ * }
+ * });
+ * // start: 0
+ * // caught time: 10
+ * // boom
+ *
+ * # Error Handling
+ *
+ * ES6 promises do not require users to handle a promise rejections. This can
+ * result in subtle bugs as the rejections are silently "swallowed" by the
+ * ManagedPromise class.
+ *
+ * ManagedPromise.reject(Error('boom'));
+ * // ... *crickets* ...
+ *
+ * Selenium's promise module, on the other hand, requires that every rejection
+ * be explicitly handled. When a {@linkplain ManagedPromise ManagedPromise} is
+ * rejected and no callbacks are defined on that promise, it is considered an
+ * _unhandled rejection_ and reproted to the active task queue. If the rejection
+ * remains unhandled after a single turn of the [event loop][JSEL] (scheduled
+ * with a micro-task), it will propagate up the stack.
+ *
+ * ## Error Propagation
+ *
+ * If an unhandled rejection occurs within a task function, that task's promised
+ * result is rejected and all remaining subtasks are discarded:
+ *
+ * flow.execute(function() {
+ * // No callbacks registered on promise -> unhandled rejection
+ * promise.rejected(Error('boom'));
+ * flow.execute(function() { console.log('this will never run'); });
+ * }).catch(function(e) {
+ * console.log(e.message);
+ * });
+ * // boom
+ *
+ * The promised results for discarded tasks are silently rejected with a
+ * cancellation error and existing callback chains will never fire.
+ *
+ * flow.execute(function() {
+ * promise.rejected(Error('boom'));
+ * flow.execute(function() { console.log('a'); }).
+ * then(function() { console.log('b'); });
+ * }).catch(function(e) {
+ * console.log(e.message);
+ * });
+ * // boom
+ *
+ * An unhandled rejection takes precedence over a task function's returned
+ * result, even if that value is another promise:
+ *
+ * flow.execute(function() {
+ * promise.rejected(Error('boom'));
+ * return flow.execute(someOtherTask);
+ * }).catch(function(e) {
+ * console.log(e.message);
+ * });
+ * // boom
+ *
+ * If there are multiple unhandled rejections within a task, they are packaged
+ * in a {@link MultipleUnhandledRejectionError}, which has an `errors` property
+ * that is a `Set` of the recorded unhandled rejections:
+ *
+ * flow.execute(function() {
+ * promise.rejected(Error('boom1'));
+ * promise.rejected(Error('boom2'));
+ * }).catch(function(ex) {
+ * console.log(ex instanceof MultipleUnhandledRejectionError);
+ * for (var e of ex.errors) {
+ * console.log(e.message);
+ * }
+ * });
+ * // boom1
+ * // boom2
+ *
+ * When a subtask is discarded due to an unreported rejection in its parent
+ * frame, the existing callbacks on that task will never settle and the
+ * callbacks will not be invoked. If a new callback is attached ot the subtask
+ * _after_ it has been discarded, it is handled the same as adding a callback
+ * to a cancelled promise: the error-callback path is invoked. This behavior is
+ * intended to handle cases where the user saves a reference to a task promise,
+ * as illustrated below.
+ *
+ * var subTask;
+ * flow.execute(function() {
+ * promise.rejected(Error('boom'));
+ * subTask = flow.execute(function() {});
+ * }).catch(function(e) {
+ * console.log(e.message);
+ * }).then(function() {
+ * return subTask.then(
+ * () => console.log('subtask success!'),
+ * (e) => console.log('subtask failed:\n' + e));
+ * });
+ * // boom
+ * // subtask failed:
+ * // DiscardedTaskError: Task was discarded due to a previous failure: boom
+ *
+ * When a subtask fails, its promised result is treated the same as any other
+ * promise: it must be handled within one turn of the rejection or the unhandled
+ * rejection is propagated to the parent task. This means users can catch errors
+ * from complex flows from the top level task:
+ *
+ * flow.execute(function() {
+ * flow.execute(function() {
+ * flow.execute(function() {
+ * throw Error('fail!');
+ * });
+ * });
+ * }).catch(function(e) {
+ * console.log(e.message);
+ * });
+ * // fail!
+ *
+ * ## Unhandled Rejection Events
+ *
+ * When an unhandled rejection propagates to the root of the control flow, the
+ * flow will emit an __uncaughtException__ event. If no listeners are registered
+ * on the flow, the error will be rethrown to the global error handler: an
+ * __uncaughtException__ event from the
+ * [`process`](https://nodejs.org/api/process.html) object in node, or
+ * `window.onerror` when running in a browser.
+ *
+ * Bottom line: you __*must*__ handle rejected promises.
+ *
+ * # ManagedPromise/A+ Compatibility
+ *
+ * This `promise` module is compliant with the [ManagedPromise/A+][] specification
+ * except for sections `2.2.6.1` and `2.2.6.2`:
+ *
+ * >
+ * > - `then` may be called multiple times on the same promise.
+ * > - If/when `promise` is fulfilled, all respective `onFulfilled` callbacks
+ * > must execute in the order of their originating calls to `then`.
+ * > - If/when `promise` is rejected, all respective `onRejected` callbacks
+ * > must execute in the order of their originating calls to `then`.
+ * >
+ *
+ * Specifically, the conformance tests contains the following scenario (for
+ * brevity, only the fulfillment version is shown):
+ *
+ * var p1 = ManagedPromise.resolve();
+ * p1.then(function() {
+ * console.log('A');
+ * p1.then(() => console.log('B'));
+ * });
+ * p1.then(() => console.log('C'));
+ * // A
+ * // C
+ * // B
+ *
+ * Since the [ControlFlow](#scheduling_callbacks) executes promise callbacks as
+ * tasks, with this module, the result would be
+ *
+ * var p2 = promise.fulfilled();
+ * p2.then(function() {
+ * console.log('A');
+ * p2.then(() => console.log('B');
+ * });
+ * p2.then(() => console.log('C'));
+ * // A
+ * // B
+ * // C
+ *
+ * [JSEL]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop
+ * [GF]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*
+ * [ManagedPromise/A+]: https://promisesaplus.com/
+ */
+
+'use strict';
+
+const events = require('./events');
+const logging = require('./logging');
+
+
+/**
+ * Alias to help with readability and differentiate types.
+ * @const
+ */
+const NativePromise = Promise;
+
+
+/**
+ * Whether to append traces of `then` to rejection errors.
+ * @type {boolean}
+ */
+var LONG_STACK_TRACES = false; // TODO: this should not be CONSTANT_CASE
+
+
+/** @const */
+const LOG = logging.getLogger('promise');
+
+
+const UNIQUE_IDS = new WeakMap;
+let nextId = 1;
+
+
+function getUid(obj) {
+ let id = UNIQUE_IDS.get(obj);
+ if (!id) {
+ id = nextId;
+ nextId += 1;
+ UNIQUE_IDS.set(obj, id);
+ }
+ return id;
+}
+
+
+/**
+ * Runs the given function after a micro-task yield.
+ * @param {function()} fn The function to run.
+ */
+function asyncRun(fn) {
+ NativePromise.resolve().then(function() {
+ try {
+ fn();
+ } catch (ignored) {
+ // Do nothing.
+ }
+ });
+}
+
+
+/**
+ * @param {number} level What level of verbosity to log with.
+ * @param {(string|function(this: T): string)} loggable The message to log.
+ * @param {T=} opt_self The object in whose context to run the loggable
+ * function.
+ * @template T
+ */
+function vlog(level, loggable, opt_self) {
+ var logLevel = logging.Level.FINE;
+ if (level > 1) {
+ logLevel = logging.Level.FINEST;
+ } else if (level > 0) {
+ logLevel = logging.Level.FINER;
+ }
+
+ if (typeof loggable === 'function') {
+ loggable = loggable.bind(opt_self);
+ }
+
+ LOG.log(logLevel, loggable);
+}
+
+
+/**
+ * Generates an error to capture the current stack trace.
+ * @param {string} name Error name for this stack trace.
+ * @param {string} msg Message to record.
+ * @param {Function=} opt_topFn The function that should appear at the top of
+ * the stack; only applicable in V8.
+ * @return {!Error} The generated error.
+ */
+function captureStackTrace(name, msg, opt_topFn) {
+ var e = Error(msg);
+ e.name = name;
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(e, opt_topFn);
+ } else {
+ var stack = Error().stack;
+ if (stack) {
+ e.stack = e.toString();
+ e.stack += '\n' + stack;
+ }
+ }
+ return e;
+}
+
+
+/**
+ * Error used when the computation of a promise is cancelled.
+ */
+class CancellationError extends Error {
+ /**
+ * @param {string=} opt_msg The cancellation message.
+ */
+ constructor(opt_msg) {
+ super(opt_msg);
+
+ /** @override */
+ this.name = this.constructor.name;
+
+ /** @private {boolean} */
+ this.silent_ = false;
+ }
+
+ /**
+ * Wraps the given error in a CancellationError.
+ *
+ * @param {*} error The error to wrap.
+ * @param {string=} opt_msg The prefix message to use.
+ * @return {!CancellationError} A cancellation error.
+ */
+ static wrap(error, opt_msg) {
+ var message;
+ if (error instanceof CancellationError) {
+ return new CancellationError(
+ opt_msg ? (opt_msg + ': ' + error.message) : error.message);
+ } else if (opt_msg) {
+ message = opt_msg;
+ if (error) {
+ message += ': ' + error;
+ }
+ return new CancellationError(message);
+ }
+ if (error) {
+ message = error + '';
+ }
+ return new CancellationError(message);
+ }
+}
+
+
+/**
+ * Error used to cancel tasks when a control flow is reset.
+ * @final
+ */
+class FlowResetError extends CancellationError {
+ constructor() {
+ super('ControlFlow was reset');
+ this.silent_ = true;
+ }
+}
+
+
+/**
+ * Error used to cancel tasks that have been discarded due to an uncaught error
+ * reported earlier in the control flow.
+ * @final
+ */
+class DiscardedTaskError extends CancellationError {
+ /** @param {*} error The original error. */
+ constructor(error) {
+ if (error instanceof DiscardedTaskError) {
+ return /** @type {!DiscardedTaskError} */(error);
+ }
+
+ var msg = '';
+ if (error) {
+ msg = ': ' + (
+ typeof error.message === 'string' ? error.message : error);
+ }
+
+ super('Task was discarded due to a previous failure' + msg);
+ this.silent_ = true;
+ }
+}
+
+
+/**
+ * Error used when there are multiple unhandled promise rejections detected
+ * within a task or callback.
+ *
+ * @final
+ */
+class MultipleUnhandledRejectionError extends Error {
+ /**
+ * @param {!(Set<*>)} errors The errors to report.
+ */
+ constructor(errors) {
+ super('Multiple unhandled promise rejections reported');
+
+ /** @override */
+ this.name = this.constructor.name;
+
+ /** @type {!Set<*>} */
+ this.errors = errors;
+ }
+}
+
+
+/**
+ * Property used to flag constructor's as implementing the Thenable interface
+ * for runtime type checking.
+ * @const
+ */
+const IMPLEMENTED_BY_SYMBOL = Symbol('promise.Thenable');
+
+
+/**
+ * Thenable is a promise-like object with a {@code then} method which may be
+ * used to schedule callbacks on a promised value.
+ *
+ * @interface
+ * @extends {IThenable<T>}
+ * @template T
+ */
+class Thenable {
+ /**
+ * Adds a property to a class prototype to allow runtime checks of whether
+ * instances of that class implement the Thenable interface. This function
+ * will also ensure the prototype's {@code then} function is exported from
+ * compiled code.
+ * @param {function(new: Thenable, ...?)} ctor The
+ * constructor whose prototype to modify.
+ */
+ static addImplementation(ctor) {
+ ctor.prototype['then'] = ctor.prototype.then;
+ try {
+ ctor.prototype[IMPLEMENTED_BY_SYMBOL] = true;
+ } catch (ignored) {
+ // Property access denied?
+ }
+ }
+
+ /**
+ * Checks if an object has been tagged for implementing the Thenable
+ * interface as defined by {@link Thenable.addImplementation}.
+ * @param {*} object The object to test.
+ * @return {boolean} Whether the object is an implementation of the Thenable
+ * interface.
+ */
+ static isImplementation(object) {
+ if (!object) {
+ return false;
+ }
+ try {
+ return !!object[IMPLEMENTED_BY_SYMBOL];
+ } catch (e) {
+ return false; // Property access seems to be forbidden.
+ }
+ }
+
+ /**
+ * Cancels the computation of this promise's value, rejecting the promise in
+ * the process. This method is a no-op if the promise has already been
+ * resolved.
+ *
+ * @param {(string|Error)=} opt_reason The reason this promise is being
+ * cancelled. This value will be wrapped in a {@link CancellationError}.
+ */
+ cancel(opt_reason) {}
+
+ /** @return {boolean} Whether this promise's value is still being computed. */
+ isPending() {}
+
+ /**
+ * Registers listeners for when this instance is resolved.
+ *
+ * @param {?(function(T): (R|IThenable<R>))=} opt_callback The
+ * function to call if this promise is successfully resolved. The function
+ * should expect a single argument: the promise's resolved value.
+ * @param {?(function(*): (R|IThenable<R>))=} opt_errback
+ * The function to call if this promise is rejected. The function should
+ * expect a single argument: the rejection reason.
+ * @return {!ManagedPromise<R>} A new promise which will be
+ * resolved with the result of the invoked callback.
+ * @template R
+ */
+ then(opt_callback, opt_errback) {}
+
+ /**
+ * Registers a listener for when this promise is rejected. This is synonymous
+ * with the {@code catch} clause in a synchronous API:
+ *
+ * // Synchronous API:
+ * try {
+ * doSynchronousWork();
+ * } catch (ex) {
+ * console.error(ex);
+ * }
+ *
+ * // Asynchronous promise API:
+ * doAsynchronousWork().catch(function(ex) {
+ * console.error(ex);
+ * });
+ *
+ * @param {function(*): (R|IThenable<R>)} errback The
+ * function to call if this promise is rejected. The function should
+ * expect a single argument: the rejection reason.
+ * @return {!ManagedPromise<R>} A new promise which will be
+ * resolved with the result of the invoked callback.
+ * @template R
+ */
+ catch(errback) {}
+
+ /**
+ * Registers a listener to invoke when this promise is resolved, regardless
+ * of whether the promise's value was successfully computed. This function
+ * is synonymous with the {@code finally} clause in a synchronous API:
+ *
+ * // Synchronous API:
+ * try {
+ * doSynchronousWork();
+ * } finally {
+ * cleanUp();
+ * }
+ *
+ * // Asynchronous promise API:
+ * doAsynchronousWork().finally(cleanUp);
+ *
+ * __Note:__ similar to the {@code finally} clause, if the registered
+ * callback returns a rejected promise or throws an error, it will silently
+ * replace the rejection error (if any) from this promise:
+ *
+ * try {
+ * throw Error('one');
+ * } finally {
+ * throw Error('two'); // Hides Error: one
+ * }
+ *
+ * promise.rejected(Error('one'))
+ * .finally(function() {
+ * throw Error('two'); // Hides Error: one
+ * });
+ *
+ * @param {function(): (R|IThenable<R>)} callback The function to call when
+ * this promise is resolved.
+ * @return {!ManagedPromise<R>} A promise that will be fulfilled
+ * with the callback result.
+ * @template R
+ */
+ finally(callback) {}
+}
+
+
+/**
+ * @enum {string}
+ */
+const PromiseState = {
+ PENDING: 'pending',
+ BLOCKED: 'blocked',
+ REJECTED: 'rejected',
+ FULFILLED: 'fulfilled'
+};
+
+
+/**
+ * Internal map used to store cancellation handlers for {@link ManagedPromise}
+ * objects. This is an internal implementation detail used by the
+ * {@link TaskQueue} class to monitor for when a promise is cancelled without
+ * generating an extra promise via then().
+ *
+ * @const {!WeakMap<!ManagedPromise, function(!CancellationError)>}
+ */
+const ON_CANCEL_HANDLER = new WeakMap;
+
+
+/**
+ * Represents the eventual value of a completed operation. Each promise may be
+ * in one of three states: pending, fulfilled, or rejected. Each promise starts
+ * in the pending state and may make a single transition to either a
+ * fulfilled or rejected state, at which point the promise is considered
+ * resolved.
+ *
+ * @implements {Thenable<T>}
+ * @template T
+ * @see http://promises-aplus.github.io/promises-spec/
+ */
+class ManagedPromise {
+ /**
+ * @param {function(
+ * function((T|IThenable<T>|Thenable)=),
+ * function(*=))} resolver
+ * Function that is invoked immediately to begin computation of this
+ * promise's value. The function should accept a pair of callback
+ * functions, one for fulfilling the promise and another for rejecting it.
+ * @param {ControlFlow=} opt_flow The control flow
+ * this instance was created under. Defaults to the currently active flow.
+ */
+ constructor(resolver, opt_flow) {
+ getUid(this);
+
+ /** @private {!ControlFlow} */
+ this.flow_ = opt_flow || controlFlow();
+
+ /** @private {Error} */
+ this.stack_ = null;
+ if (LONG_STACK_TRACES) {
+ this.stack_ = captureStackTrace('ManagedPromise', 'new', this.constructor);
+ }
+
+ /** @private {Thenable<?>} */
+ this.parent_ = null;
+
+ /** @private {Array<!Task>} */
+ this.callbacks_ = null;
+
+ /** @private {PromiseState} */
+ this.state_ = PromiseState.PENDING;
+
+ /** @private {boolean} */
+ this.handled_ = false;
+
+ /** @private {*} */
+ this.value_ = undefined;
+
+ /** @private {TaskQueue} */
+ this.queue_ = null;
+
+ try {
+ var self = this;
+ resolver(function(value) {
+ self.resolve_(PromiseState.FULFILLED, value);
+ }, function(reason) {
+ self.resolve_(PromiseState.REJECTED, reason);
+ });
+ } catch (ex) {
+ this.resolve_(PromiseState.REJECTED, ex);
+ }
+ }
+
+ /** @override */
+ toString() {
+ return 'ManagedPromise::' + getUid(this) +
+ ' {[[PromiseStatus]]: "' + this.state_ + '"}';
+ }
+
+ /**
+ * Resolves this promise. If the new value is itself a promise, this function
+ * will wait for it to be resolved before notifying the registered listeners.
+ * @param {PromiseState} newState The promise's new state.
+ * @param {*} newValue The promise's new value.
+ * @throws {TypeError} If {@code newValue === this}.
+ * @private
+ */
+ resolve_(newState, newValue) {
+ if (PromiseState.PENDING !== this.state_) {
+ return;
+ }
+
+ if (newValue === this) {
+ // See promise a+, 2.3.1
+ // http://promises-aplus.github.io/promises-spec/#point-48
+ newValue = new TypeError('A promise may not resolve to itself');
+ newState = PromiseState.REJECTED;
+ }
+
+ this.parent_ = null;
+ this.state_ = PromiseState.BLOCKED;
+
+ if (newState !== PromiseState.REJECTED) {
+ if (Thenable.isImplementation(newValue)) {
+ // 2.3.2
+ newValue = /** @type {!Thenable} */(newValue);
+ this.parent_ = newValue;
+ newValue.then(
+ this.unblockAndResolve_.bind(this, PromiseState.FULFILLED),
+ this.unblockAndResolve_.bind(this, PromiseState.REJECTED));
+ return;
+
+ } else if (newValue
+ && (typeof newValue === 'object' || typeof newValue === 'function')) {
+ // 2.3.3
+
+ try {
+ // 2.3.3.1
+ var then = newValue['then'];
+ } catch (e) {
+ // 2.3.3.2
+ this.state_ = PromiseState.REJECTED;
+ this.value_ = e;
+ this.scheduleNotifications_();
+ return;
+ }
+
+ if (typeof then === 'function') {
+ // 2.3.3.3
+ this.invokeThen_(/** @type {!Object} */(newValue), then);
+ return;
+ }
+ }
+ }
+
+ if (newState === PromiseState.REJECTED &&
+ isError(newValue) && newValue.stack && this.stack_) {
+ newValue.stack += '\nFrom: ' + (this.stack_.stack || this.stack_);
+ }
+
+ // 2.3.3.4 and 2.3.4
+ this.state_ = newState;
+ this.value_ = newValue;
+ this.scheduleNotifications_();
+ }
+
+ /**
+ * Invokes a thenable's "then" method according to 2.3.3.3 of the promise
+ * A+ spec.
+ * @param {!Object} x The thenable object.
+ * @param {!Function} then The "then" function to invoke.
+ * @private
+ */
+ invokeThen_(x, then) {
+ var called = false;
+ var self = this;
+
+ var resolvePromise = function(value) {
+ if (!called) { // 2.3.3.3.3
+ called = true;
+ // 2.3.3.3.1
+ self.unblockAndResolve_(PromiseState.FULFILLED, value);
+ }
+ };
+
+ var rejectPromise = function(reason) {
+ if (!called) { // 2.3.3.3.3
+ called = true;
+ // 2.3.3.3.2
+ self.unblockAndResolve_(PromiseState.REJECTED, reason);
+ }
+ };
+
+ try {
+ // 2.3.3.3
+ then.call(x, resolvePromise, rejectPromise);
+ } catch (e) {
+ // 2.3.3.3.4.2
+ rejectPromise(e);
+ }
+ }
+
+ /**
+ * @param {PromiseState} newState The promise's new state.
+ * @param {*} newValue The promise's new value.
+ * @private
+ */
+ unblockAndResolve_(newState, newValue) {
+ if (this.state_ === PromiseState.BLOCKED) {
+ this.state_ = PromiseState.PENDING;
+ this.resolve_(newState, newValue);
+ }
+ }
+
+ /**
+ * @private
+ */
+ scheduleNotifications_() {
+ vlog(2, () => this + ' scheduling notifications', this);
+
+ ON_CANCEL_HANDLER.delete(this);
+ if (this.value_ instanceof CancellationError
+ && this.value_.silent_) {
+ this.callbacks_ = null;
+ }
+
+ if (!this.queue_) {
+ this.queue_ = this.flow_.getActiveQueue_();
+ }
+
+ if (!this.handled_ &&
+ this.state_ === PromiseState.REJECTED &&
+ !(this.value_ instanceof CancellationError)) {
+ this.queue_.addUnhandledRejection(this);
+ }
+ this.queue_.scheduleCallbacks(this);
+ }
+
+ /** @override */
+ cancel(opt_reason) {
+ if (!canCancel(this)) {
+ return;
+ }
+
+ if (this.parent_ && canCancel(this.parent_)) {
+ this.parent_.cancel(opt_reason);
+ } else {
+ var reason = CancellationError.wrap(opt_reason);
+ let onCancel = ON_CANCEL_HANDLER.get(this);
+ if (onCancel) {
+ onCancel(reason);
+ ON_CANCEL_HANDLER.delete(this);
+ }
+
+ if (this.state_ === PromiseState.BLOCKED) {
+ this.unblockAndResolve_(PromiseState.REJECTED, reason);
+ } else {
+ this.resolve_(PromiseState.REJECTED, reason);
+ }
+ }
+
+ function canCancel(promise) {
+ if (!(promise instanceof ManagedPromise)) {
+ return Thenable.isImplementation(promise);
+ }
+ return promise.state_ === PromiseState.PENDING
+ || promise.state_ === PromiseState.BLOCKED;
+ }
+ }
+
+ /** @override */
+ isPending() {
+ return this.state_ === PromiseState.PENDING;
+ }
+
+ /** @override */
+ then(opt_callback, opt_errback) {
+ return this.addCallback_(
+ opt_callback, opt_errback, 'then', ManagedPromise.prototype.then);
+ }
+
+ /** @override */
+ catch(errback) {
+ return this.addCallback_(
+ null, errback, 'catch', ManagedPromise.prototype.catch);
+ }
+
+ /** @override */
+ finally(callback) {
+ var error;
+ var mustThrow = false;
+ return this.then(function() {
+ return callback();
+ }, function(err) {
+ error = err;
+ mustThrow = true;
+ return callback();
+ }).then(function() {
+ if (mustThrow) {
+ throw error;
+ }
+ });
+ }
+
+ /**
+ * Registers a new callback with this promise
+ * @param {(function(T): (R|IThenable<R>)|null|undefined)} callback The
+ * fulfillment callback.
+ * @param {(function(*): (R|IThenable<R>)|null|undefined)} errback The
+ * rejection callback.
+ * @param {string} name The callback name.
+ * @param {!Function} fn The function to use as the top of the stack when
+ * recording the callback's creation point.
+ * @return {!ManagedPromise<R>} A new promise which will be resolved with the
+ * esult of the invoked callback.
+ * @template R
+ * @private
+ */
+ addCallback_(callback, errback, name, fn) {
+ if (typeof callback !== 'function' && typeof errback !== 'function') {
+ return this;
+ }
+
+ this.handled_ = true;
+ if (this.queue_) {
+ this.queue_.clearUnhandledRejection(this);
+ }
+
+ var cb = new Task(
+ this.flow_,
+ this.invokeCallback_.bind(this, callback, errback),
+ name,
+ LONG_STACK_TRACES ? {name: 'Promise', top: fn} : undefined);
+ cb.promise.parent_ = this;
+
+ if (this.state_ !== PromiseState.PENDING &&
+ this.state_ !== PromiseState.BLOCKED) {
+ this.flow_.getActiveQueue_().enqueue(cb);
+ } else {
+ if (!this.callbacks_) {
+ this.callbacks_ = [];
+ }
+ this.callbacks_.push(cb);
+ cb.blocked = true;
+ this.flow_.getActiveQueue_().enqueue(cb);
+ }
+
+ return cb.promise;
+ }
+
+ /**
+ * Invokes a callback function attached to this promise.
+ * @param {(function(T): (R|IThenable<R>)|null|undefined)} callback The
+ * fulfillment callback.
+ * @param {(function(*): (R|IThenable<R>)|null|undefined)} errback The
+ * rejection callback.
+ * @template R
+ * @private
+ */
+ invokeCallback_(callback, errback) {
+ var callbackFn = callback;
+ if (this.state_ === PromiseState.REJECTED) {
+ callbackFn = errback;
+ }
+
+ if (typeof callbackFn === 'function') {
+ if (isGenerator(callbackFn)) {
+ return consume(callbackFn, null, this.value_);
+ }
+ return callbackFn(this.value_);
+ } else if (this.state_ === PromiseState.REJECTED) {
+ throw this.value_;
+ } else {
+ return this.value_;
+ }
+ }
+}
+Thenable.addImplementation(ManagedPromise);
+
+
+/**
+ * Represents a value that will be resolved at some point in the future. This
+ * class represents the protected "producer" half of a ManagedPromise - each Deferred
+ * has a {@code promise} property that may be returned to consumers for
+ * registering callbacks, reserving the ability to resolve the deferred to the
+ * producer.
+ *
+ * If this Deferred is rejected and there are no listeners registered before
+ * the next turn of the event loop, the rejection will be passed to the
+ * {@link ControlFlow} as an unhandled failure.
+ *
+ * @template T
+ */
+class Deferred {
+ /**
+ * @param {ControlFlow=} opt_flow The control flow this instance was
+ * created under. This should only be provided during unit tests.
+ */
+ constructor(opt_flow) {
+ var fulfill, reject;
+
+ /** @type {!ManagedPromise<T>} */
+ this.promise = new ManagedPromise(function(f, r) {
+ fulfill = f;
+ reject = r;
+ }, opt_flow);
+
+ var self = this;
+ var checkNotSelf = function(value) {
+ if (value === self) {
+ throw new TypeError('May not resolve a Deferred with itself');
+ }
+ };
+
+ /**
+ * Resolves this deferred with the given value. It is safe to call this as a
+ * normal function (with no bound "this").
+ * @param {(T|IThenable<T>|Thenable)=} opt_value The fulfilled value.
+ */
+ this.fulfill = function(opt_value) {
+ checkNotSelf(opt_value);
+ fulfill(opt_value);
+ };
+
+ /**
+ * Rejects this promise with the given reason. It is safe to call this as a
+ * normal function (with no bound "this").
+ * @param {*=} opt_reason The rejection reason.
+ */
+ this.reject = function(opt_reason) {
+ checkNotSelf(opt_reason);
+ reject(opt_reason);
+ };
+ }
+}
+
+
+/**
+ * Tests if a value is an Error-like object. This is more than an straight
+ * instanceof check since the value may originate from another context.
+ * @param {*} value The value to test.
+ * @return {boolean} Whether the value is an error.
+ */
+function isError(value) {
+ return value instanceof Error ||
+ (!!value && typeof value === 'object'
+ && typeof value.message === 'string');
+}
+
+
+/**
+ * Determines whether a {@code value} should be treated as a promise.
+ * Any object whose "then" property is a function will be considered a promise.
+ *
+ * @param {?} value The value to test.
+ * @return {boolean} Whether the value is a promise.
+ */
+function isPromise(value) {
+ try {
+ // Use array notation so the Closure compiler does not obfuscate away our
+ // contract.
+ return value
+ && (typeof value === 'object' || typeof value === 'function')
+ && typeof value['then'] === 'function';
+ } catch (ex) {
+ return false;
+ }
+}
+
+
+/**
+ * Creates a promise that will be resolved at a set time in the future.
+ * @param {number} ms The amount of time, in milliseconds, to wait before
+ * resolving the promise.
+ * @return {!ManagedPromise} The promise.
+ */
+function delayed(ms) {
+ var key;
+ return new ManagedPromise(function(fulfill) {
+ key = setTimeout(function() {
+ key = null;
+ fulfill();
+ }, ms);
+ }).catch(function(e) {
+ clearTimeout(key);
+ key = null;
+ throw e;
+ });
+}
+
+
+/**
+ * Creates a new deferred object.
+ * @return {!Deferred<T>} The new deferred object.
+ * @template T
+ */
+function defer() {
+ return new Deferred();
+}
+
+
+/**
+ * Creates a promise that has been resolved with the given value.
+ * @param {T=} opt_value The resolved value.
+ * @return {!ManagedPromise<T>} The resolved promise.
+ * @template T
+ */
+function fulfilled(opt_value) {
+ if (opt_value instanceof ManagedPromise) {
+ return opt_value;
+ }
+ return new ManagedPromise(function(fulfill) {
+ fulfill(opt_value);
+ });
+}
+
+
+/**
+ * Creates a promise that has been rejected with the given reason.
+ * @param {*=} opt_reason The rejection reason; may be any value, but is
+ * usually an Error or a string.
+ * @return {!ManagedPromise<T>} The rejected promise.
+ * @template T
+ */
+function rejected(opt_reason) {
+ if (opt_reason instanceof ManagedPromise) {
+ return opt_reason;
+ }
+ return new ManagedPromise(function(_, reject) {
+ reject(opt_reason);
+ });
+}
+
+
+/**
+ * Wraps a function that expects a node-style callback as its final
+ * argument. This callback expects two arguments: an error value (which will be
+ * null if the call succeeded), and the success value as the second argument.
+ * The callback will the resolve or reject the returned promise, based on its
+ * arguments.
+ * @param {!Function} fn The function to wrap.
+ * @param {...?} var_args The arguments to apply to the function, excluding the
+ * final callback.
+ * @return {!ManagedPromise} A promise that will be resolved with the
+ * result of the provided function's callback.
+ */
+function checkedNodeCall(fn, var_args) {
+ let args = Array.prototype.slice.call(arguments, 1);
+ return new ManagedPromise(function(fulfill, reject) {
+ try {
+ args.push(function(error, value) {
+ error ? reject(error) : fulfill(value);
+ });
+ fn.apply(undefined, args);
+ } catch (ex) {
+ reject(ex);
+ }
+ });
+}
+
+
+/**
+ * Registers an observer on a promised {@code value}, returning a new promise
+ * that will be resolved when the value is. If {@code value} is not a promise,
+ * then the return promise will be immediately resolved.
+ * @param {*} value The value to observe.
+ * @param {Function=} opt_callback The function to call when the value is
+ * resolved successfully.
+ * @param {Function=} opt_errback The function to call when the value is
+ * rejected.
+ * @return {!ManagedPromise} A new promise.
+ */
+function when(value, opt_callback, opt_errback) {
+ if (Thenable.isImplementation(value)) {
+ return value.then(opt_callback, opt_errback);
+ }
+
+ return new ManagedPromise(function(fulfill) {
+ fulfill(value);
+ }).then(opt_callback, opt_errback);
+}
+
+
+/**
+ * Invokes the appropriate callback function as soon as a promised `value` is
+ * resolved. This function is similar to `when()`, except it does not return
+ * a new promise.
+ * @param {*} value The value to observe.
+ * @param {Function} callback The function to call when the value is
+ * resolved successfully.
+ * @param {Function=} opt_errback The function to call when the value is
+ * rejected.
+ */
+function asap(value, callback, opt_errback) {
+ if (isPromise(value)) {
+ value.then(callback, opt_errback);
+
+ } else if (callback) {
+ callback(value);
+ }
+}
+
+
+/**
+ * Given an array of promises, will return a promise that will be fulfilled
+ * with the fulfillment values of the input array's values. If any of the
+ * input array's promises are rejected, the returned promise will be rejected
+ * with the same reason.
+ *
+ * @param {!Array<(T|!ManagedPromise<T>)>} arr An array of
+ * promises to wait on.
+ * @return {!ManagedPromise<!Array<T>>} A promise that is
+ * fulfilled with an array containing the fulfilled values of the
+ * input array, or rejected with the same reason as the first
+ * rejected value.
+ * @template T
+ */
+function all(arr) {
+ return new ManagedPromise(function(fulfill, reject) {
+ var n = arr.length;
+ var values = [];
+
+ if (!n) {
+ fulfill(values);
+ return;
+ }
+
+ var toFulfill = n;
+ var onFulfilled = function(index, value) {
+ values[index] = value;
+ toFulfill--;
+ if (toFulfill == 0) {
+ fulfill(values);
+ }
+ };
+
+ function processPromise(index) {
+ asap(arr[index], function(value) {
+ onFulfilled(index, value);
+ }, reject);
+ }
+
+ for (var i = 0; i < n; ++i) {
+ processPromise(i);
+ }
+ });
+}
+
+
+/**
+ * Calls a function for each element in an array and inserts the result into a
+ * new array, which is used as the fulfillment value of the promise returned
+ * by this function.
+ *
+ * If the return value of the mapping function is a promise, this function
+ * will wait for it to be fulfilled before inserting it into the new array.
+ *
+ * If the mapping function throws or returns a rejected promise, the
+ * promise returned by this function will be rejected with the same reason.
+ * Only the first failure will be reported; all subsequent errors will be
+ * silently ignored.
+ *
+ * @param {!(Array<TYPE>|ManagedPromise<!Array<TYPE>>)} arr The
+ * array to iterator over, or a promise that will resolve to said array.
+ * @param {function(this: SELF, TYPE, number, !Array<TYPE>): ?} fn The
+ * function to call for each element in the array. This function should
+ * expect three arguments (the element, the index, and the array itself.
+ * @param {SELF=} opt_self The object to be used as the value of 'this' within
+ * {@code fn}.
+ * @template TYPE, SELF
+ */
+function map(arr, fn, opt_self) {
+ return fulfilled(arr).then(function(v) {
+ if (!Array.isArray(v)) {
+ throw TypeError('not an array');
+ }
+ var arr = /** @type {!Array} */(v);
+ return new ManagedPromise(function(fulfill, reject) {
+ var n = arr.length;
+ var values = new Array(n);
+ (function processNext(i) {
+ for (; i < n; i++) {
+ if (i in arr) {
+ break;
+ }
+ }
+ if (i >= n) {
+ fulfill(values);
+ return;
+ }
+ try {
+ asap(
+ fn.call(opt_self, arr[i], i, /** @type {!Array} */(arr)),
+ function(value) {
+ values[i] = value;
+ processNext(i + 1);
+ },
+ reject);
+ } catch (ex) {
+ reject(ex);
+ }
+ })(0);
+ });
+ });
+}
+
+
+/**
+ * Calls a function for each element in an array, and if the function returns
+ * true adds the element to a new array.
+ *
+ * If the return value of the filter function is a promise, this function
+ * will wait for it to be fulfilled before determining whether to insert the
+ * element into the new array.
+ *
+ * If the filter function throws or returns a rejected promise, the promise
+ * returned by this function will be rejected with the same reason. Only the
+ * first failure will be reported; all subsequent errors will be silently
+ * ignored.
+ *
+ * @param {!(Array<TYPE>|ManagedPromise<!Array<TYPE>>)} arr The
+ * array to iterator over, or a promise that will resolve to said array.
+ * @param {function(this: SELF, TYPE, number, !Array<TYPE>): (
+ * boolean|ManagedPromise<boolean>)} fn The function
+ * to call for each element in the array.
+ * @param {SELF=} opt_self The object to be used as the value of 'this' within
+ * {@code fn}.
+ * @template TYPE, SELF
+ */
+function filter(arr, fn, opt_self) {
+ return fulfilled(arr).then(function(v) {
+ if (!Array.isArray(v)) {
+ throw TypeError('not an array');
+ }
+ var arr = /** @type {!Array} */(v);
+ return new ManagedPromise(function(fulfill, reject) {
+ var n = arr.length;
+ var values = [];
+ var valuesLength = 0;
+ (function processNext(i) {
+ for (; i < n; i++) {
+ if (i in arr) {
+ break;
+ }
+ }
+ if (i >= n) {
+ fulfill(values);
+ return;
+ }
+ try {
+ var value = arr[i];
+ var include = fn.call(opt_self, value, i, /** @type {!Array} */(arr));
+ asap(include, function(include) {
+ if (include) {
+ values[valuesLength++] = value;
+ }
+ processNext(i + 1);
+ }, reject);
+ } catch (ex) {
+ reject(ex);
+ }
+ })(0);
+ });
+ });
+}
+
+
+/**
+ * Returns a promise that will be resolved with the input value in a
+ * fully-resolved state. If the value is an array, each element will be fully
+ * resolved. Likewise, if the value is an object, all keys will be fully
+ * resolved. In both cases, all nested arrays and objects will also be
+ * fully resolved. All fields are resolved in place; the returned promise will
+ * resolve on {@code value} and not a copy.
+ *
+ * Warning: This function makes no checks against objects that contain
+ * cyclical references:
+ *
+ * var value = {};
+ * value['self'] = value;
+ * promise.fullyResolved(value); // Stack overflow.
+ *
+ * @param {*} value The value to fully resolve.
+ * @return {!ManagedPromise} A promise for a fully resolved version
+ * of the input value.
+ */
+function fullyResolved(value) {
+ if (isPromise(value)) {
+ return when(value, fullyResolveValue);
+ }
+ return fullyResolveValue(value);
+}
+
+
+/**
+ * @param {*} value The value to fully resolve. If a promise, assumed to
+ * already be resolved.
+ * @return {!ManagedPromise} A promise for a fully resolved version
+ * of the input value.
+ */
+function fullyResolveValue(value) {
+ if (Array.isArray(value)) {
+ return fullyResolveKeys(/** @type {!Array} */ (value));
+ }
+
+ if (isPromise(value)) {
+ if (isPromise(value)) {
+ // We get here when the original input value is a promise that
+ // resolves to itself. When the user provides us with such a promise,
+ // trust that it counts as a "fully resolved" value and return it.
+ // Of course, since it's already a promise, we can just return it
+ // to the user instead of wrapping it in another promise.
+ return /** @type {!ManagedPromise} */ (value);
+ }
+ }
+
+ if (value && typeof value === 'object') {
+ return fullyResolveKeys(/** @type {!Object} */ (value));
+ }
+
+ if (typeof value === 'function') {
+ return fullyResolveKeys(/** @type {!Object} */ (value));
+ }
+
+ return fulfilled(value);
+}
+
+
+/**
+ * @param {!(Array|Object)} obj the object to resolve.
+ * @return {!ManagedPromise} A promise that will be resolved with the
+ * input object once all of its values have been fully resolved.
+ */
+function fullyResolveKeys(obj) {
+ var isArray = Array.isArray(obj);
+ var numKeys = isArray ? obj.length : (function() {
+ let n = 0;
+ for (let key in obj) {
+ n += 1;
+ }
+ return n;
+ })();
+ if (!numKeys) {
+ return fulfilled(obj);
+ }
+
+ function forEachProperty(obj, fn) {
+ for (let key in obj) {
+ fn.call(null, obj[key], key, obj);
+ }
+ }
+
+ function forEachElement(arr, fn) {
+ arr.forEach(fn);
+ }
+
+ var numResolved = 0;
+ return new ManagedPromise(function(fulfill, reject) {
+ var forEachKey = isArray ? forEachElement: forEachProperty;
+
+ forEachKey(obj, function(partialValue, key) {
+ if (!Array.isArray(partialValue)
+ && (!partialValue || typeof partialValue !== 'object')) {
+ maybeResolveValue();
+ return;
+ }
+
+ fullyResolved(partialValue).then(
+ function(resolvedValue) {
+ obj[key] = resolvedValue;
+ maybeResolveValue();
+ },
+ reject);
+ });
+
+ function maybeResolveValue() {
+ if (++numResolved == numKeys) {
+ fulfill(obj);
+ }
+ }
+ });
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// ControlFlow
+//
+//////////////////////////////////////////////////////////////////////////////
+
+
+
+/**
+ * Handles the execution of scheduled tasks, each of which may be an
+ * asynchronous operation. The control flow will ensure tasks are executed in
+ * the ordered scheduled, starting each task only once those before it have
+ * completed.
+ *
+ * Each task scheduled within this flow may return a {@link ManagedPromise} to
+ * indicate it is an asynchronous operation. The ControlFlow will wait for such
+ * promises to be resolved before marking the task as completed.
+ *
+ * Tasks and each callback registered on a {@link ManagedPromise} will be run
+ * in their own ControlFlow frame. Any tasks scheduled within a frame will take
+ * priority over previously scheduled tasks. Furthermore, if any of the tasks in
+ * the frame fail, the remainder of the tasks in that frame will be discarded
+ * and the failure will be propagated to the user through the callback/task's
+ * promised result.
+ *
+ * Each time a ControlFlow empties its task queue, it will fire an
+ * {@link ControlFlow.EventType.IDLE IDLE} event. Conversely,
+ * whenever the flow terminates due to an unhandled error, it will remove all
+ * remaining tasks in its queue and fire an
+ * {@link ControlFlow.EventType.UNCAUGHT_EXCEPTION UNCAUGHT_EXCEPTION} event.
+ * If there are no listeners registered with the flow, the error will be
+ * rethrown to the global error handler.
+ *
+ * Refer to the {@link ./promise} module documentation fora detailed
+ * explanation of how the ControlFlow coordinates task execution.
+ *
+ * @final
+ */
+class ControlFlow extends events.EventEmitter {
+ constructor() {
+ super();
+
+ /** @private {boolean} */
+ this.propagateUnhandledRejections_ = true;
+
+ /** @private {TaskQueue} */
+ this.activeQueue_ = null;
+
+ /** @private {Set<TaskQueue>} */
+ this.taskQueues_ = null;
+
+ /**
+ * Micro task that controls shutting down the control flow. Upon shut down,
+ * the flow will emit an
+ * {@link ControlFlow.EventType.IDLE} event. Idle events
+ * always follow a brief timeout in order to catch latent errors from the
+ * last completed task. If this task had a callback registered, but no
+ * errback, and the task fails, the unhandled failure would not be reported
+ * by the promise system until the next turn of the event loop:
+ *
+ * // Schedule 1 task that fails.
+ * var result = promise.controlFlow().schedule('example',
+ * function() { return promise.rejected('failed'); });
+ * // Set a callback on the result. This delays reporting the unhandled
+ * // failure for 1 turn of the event loop.
+ * result.then(function() {});
+ *
+ * @private {MicroTask}
+ */
+ this.shutdownTask_ = null;
+
+ /**
+ * ID for a long running interval used to keep a Node.js process running
+ * while a control flow's event loop is still working. This is a cheap hack
+ * required since JS events are only scheduled to run when there is
+ * _actually_ something to run. When a control flow is waiting on a task,
+ * there will be nothing in the JS event loop and the process would
+ * terminate without this.
+ * @private
+ */
+ this.hold_ = null;
+ }
+
+ /**
+ * Returns a string representation of this control flow, which is its current
+ * {@linkplain #getSchedule() schedule}, sans task stack traces.
+ * @return {string} The string representation of this contorl flow.
+ * @override
+ */
+ toString() {
+ return this.getSchedule();
+ }
+
+ /**
+ * Sets whether any unhandled rejections should propagate up through the
+ * control flow stack and cause rejections within parent tasks. If error
+ * propagation is disabled, tasks will not be aborted when an unhandled
+ * promise rejection is detected, but the rejection _will_ trigger an
+ * {@link ControlFlow.EventType.UNCAUGHT_EXCEPTION}
+ * event.
+ *
+ * The default behavior is to propagate all unhandled rejections. _The use
+ * of this option is highly discouraged._
+ *
+ * @param {boolean} propagate whether to propagate errors.
+ */
+ setPropagateUnhandledRejections(propagate) {
+ this.propagateUnhandledRejections_ = propagate;
+ }
+
+ /**
+ * @return {boolean} Whether this flow is currently idle.
+ */
+ isIdle() {
+ return !this.shutdownTask_ && (!this.taskQueues_ || !this.taskQueues_.size);
+ }
+
+ /**
+ * Resets this instance, clearing its queue and removing all event listeners.
+ */
+ reset() {
+ this.cancelQueues_(new FlowResetError);
+ this.emit(ControlFlow.EventType.RESET);
+ this.removeAllListeners();
+ this.cancelShutdown_();
+ }
+
+ /**
+ * Generates an annotated string describing the internal state of this control
+ * flow, including the currently executing as well as pending tasks. If
+ * {@code opt_includeStackTraces === true}, the string will include the
+ * stack trace from when each task was scheduled.
+ * @param {string=} opt_includeStackTraces Whether to include the stack traces
+ * from when each task was scheduled. Defaults to false.
+ * @return {string} String representation of this flow's internal state.
+ */
+ getSchedule(opt_includeStackTraces) {
+ var ret = 'ControlFlow::' + getUid(this);
+ var activeQueue = this.activeQueue_;
+ if (!this.taskQueues_ || !this.taskQueues_.size) {
+ return ret;
+ }
+ var childIndent = '| ';
+ for (var q of this.taskQueues_) {
+ ret += '\n' + printQ(q, childIndent);
+ }
+ return ret;
+
+ function printQ(q, indent) {
+ var ret = q.toString();
+ if (q === activeQueue) {
+ ret = '(active) ' + ret;
+ }
+ var prefix = indent + childIndent;
+ if (q.pending_) {
+ if (q.pending_.q.state_ !== TaskQueueState.FINISHED) {
+ ret += '\n' + prefix + '(pending) ' + q.pending_.task;
+ ret += '\n' + printQ(q.pending_.q, prefix + childIndent);
+ } else {
+ ret += '\n' + prefix + '(blocked) ' + q.pending_.task;
+ }
+ }
+ if (q.interrupts_) {
+ q.interrupts_.forEach((task) => {
+ ret += '\n' + prefix + task;
+ });
+ }
+ if (q.tasks_) {
+ q.tasks_.forEach((task) => ret += printTask(task, '\n' + prefix));
+ }
+ return indent + ret;
+ }
+
+ function printTask(task, prefix) {
+ var ret = prefix + task;
+ if (opt_includeStackTraces && task.promise.stack_) {
+ ret += prefix + childIndent
+ + (task.promise.stack_.stack || task.promise.stack_)
+ .replace(/\n/g, prefix);
+ }
+ return ret;
+ }
+ }
+
+ /**
+ * Returns the currently actively task queue for this flow. If there is no
+ * active queue, one will be created.
+ * @return {!TaskQueue} the currently active task queue for this flow.
+ * @private
+ */
+ getActiveQueue_() {
+ if (this.activeQueue_) {
+ return this.activeQueue_;
+ }
+
+ this.activeQueue_ = new TaskQueue(this);
+ if (!this.taskQueues_) {
+ this.taskQueues_ = new Set();
+ }
+ this.taskQueues_.add(this.activeQueue_);
+ this.activeQueue_
+ .once('end', this.onQueueEnd_, this)
+ .once('error', this.onQueueError_, this);
+
+ asyncRun(() => this.activeQueue_ = null);
+ this.activeQueue_.start();
+ return this.activeQueue_;
+ }
+
+ /**
+ * Schedules a task for execution. If there is nothing currently in the
+ * queue, the task will be executed in the next turn of the event loop. If
+ * the task function is a generator, the task will be executed using
+ * {@link ./promise.consume consume()}.
+ *
+ * @param {function(): (T|ManagedPromise<T>)} fn The function to
+ * call to start the task. If the function returns a
+ * {@link ManagedPromise}, this instance will wait for it to be
+ * resolved before starting the next task.
+ * @param {string=} opt_description A description of the task.
+ * @return {!ManagedPromise<T>} A promise that will be resolved
+ * with the result of the action.
+ * @template T
+ */
+ execute(fn, opt_description) {
+ if (isGenerator(fn)) {
+ let original = fn;
+ fn = () => consume(original);
+ }
+
+ if (!this.hold_) {
+ var holdIntervalMs = 2147483647; // 2^31-1; max timer length for Node.js
+ this.hold_ = setInterval(function() {}, holdIntervalMs);
+ }
+
+ var task = new Task(
+ this, fn, opt_description || '<anonymous>',
+ {name: 'Task', top: ControlFlow.prototype.execute});
+
+ var q = this.getActiveQueue_();
+ q.enqueue(task);
+ this.emit(ControlFlow.EventType.SCHEDULE_TASK, task.description);
+ return task.promise;
+ }
+
+ /**
+ * Inserts a {@code setTimeout} into the command queue. This is equivalent to
+ * a thread sleep in a synchronous programming language.
+ *
+ * @param {number} ms The timeout delay, in milliseconds.
+ * @param {string=} opt_description A description to accompany the timeout.
+ * @return {!ManagedPromise} A promise that will be resolved with
+ * the result of the action.
+ */
+ timeout(ms, opt_description) {
+ return this.execute(function() {
+ return delayed(ms);
+ }, opt_description);
+ }
+
+ /**
+ * Schedules a task that shall wait for a condition to hold. Each condition
+ * function may return any value, but it will always be evaluated as a
+ * boolean.
+ *
+ * Condition functions may schedule sub-tasks with this instance, however,
+ * their execution time will be factored into whether a wait has timed out.
+ *
+ * In the event a condition returns a ManagedPromise, the polling loop will wait for
+ * it to be resolved before evaluating whether the condition has been
+ * satisfied. The resolution time for a promise is factored into whether a
+ * wait has timed out.
+ *
+ * If the condition function throws, or returns a rejected promise, the
+ * wait task will fail.
+ *
+ * If the condition is defined as a promise, the flow will wait for it to
+ * settle. If the timeout expires before the promise settles, the promise
+ * returned by this function will be rejected.
+ *
+ * If this function is invoked with `timeout === 0`, or the timeout is
+ * omitted, the flow will wait indefinitely for the condition to be satisfied.
+ *
+ * @param {(!ManagedPromise<T>|function())} condition The condition to poll,
+ * or a promise to wait on.
+ * @param {number=} opt_timeout How long to wait, in milliseconds, for the
+ * condition to hold before timing out. If omitted, the flow will wait
+ * indefinitely.
+ * @param {string=} opt_message An optional error message to include if the
+ * wait times out; defaults to the empty string.
+ * @return {!ManagedPromise<T>} A promise that will be fulfilled
+ * when the condition has been satisified. The promise shall be rejected
+ * if the wait times out waiting for the condition.
+ * @throws {TypeError} If condition is not a function or promise or if timeout
+ * is not a number >= 0.
+ * @template T
+ */
+ wait(condition, opt_timeout, opt_message) {
+ var timeout = opt_timeout || 0;
+ if (typeof timeout !== 'number' || timeout < 0) {
+ throw TypeError('timeout must be a number >= 0: ' + timeout);
+ }
+
+ if (isPromise(condition)) {
+ return this.execute(function() {
+ if (!timeout) {
+ return condition;
+ }
+ return new ManagedPromise(function(fulfill, reject) {
+ var start = Date.now();
+ var timer = setTimeout(function() {
+ timer = null;
+ reject(Error((opt_message ? opt_message + '\n' : '') +
+ 'Timed out waiting for promise to resolve after ' +
+ (Date.now() - start) + 'ms'));
+ }, timeout);
+
+ /** @type {Thenable} */(condition).then(
+ function(value) {
+ timer && clearTimeout(timer);
+ fulfill(value);
+ },
+ function(error) {
+ timer && clearTimeout(timer);
+ reject(error);
+ });
+ });
+ }, opt_message || '<anonymous wait: promise resolution>');
+ }
+
+ if (typeof condition !== 'function') {
+ throw TypeError('Invalid condition; must be a function or promise: ' +
+ typeof condition);
+ }
+
+ if (isGenerator(condition)) {
+ let original = condition;
+ condition = () => consume(original);
+ }
+
+ var self = this;
+ return this.execute(function() {
+ var startTime = Date.now();
+ return new ManagedPromise(function(fulfill, reject) {
+ pollCondition();
+
+ function pollCondition() {
+ var conditionFn = /** @type {function()} */(condition);
+ self.execute(conditionFn).then(function(value) {
+ var elapsed = Date.now() - startTime;
+ if (!!value) {
+ fulfill(value);
+ } else if (timeout && elapsed >= timeout) {
+ reject(new Error((opt_message ? opt_message + '\n' : '') +
+ 'Wait timed out after ' + elapsed + 'ms'));
+ } else {
+ // Do not use asyncRun here because we need a non-micro yield
+ // here so the UI thread is given a chance when running in a
+ // browser.
+ setTimeout(pollCondition, 0);
+ }
+ }, reject);
+ }
+ });
+ }, opt_message || '<anonymous wait>');
+ }
+
+ /**
+ * Executes a function in the next available turn of the JavaScript event
+ * loop. This ensures the function runs with its own task queue and any
+ * scheduled tasks will run in "parallel" to those scheduled in the current
+ * function.
+ *
+ * flow.execute(() => console.log('a'));
+ * flow.execute(() => console.log('b'));
+ * flow.execute(() => console.log('c'));
+ * flow.async(() => {
+ * flow.execute(() => console.log('d'));
+ * flow.execute(() => console.log('e'));
+ * });
+ * flow.async(() => {
+ * flow.execute(() => console.log('f'));
+ * flow.execute(() => console.log('g'));
+ * });
+ * flow.once('idle', () => console.log('fin'));
+ * // a
+ * // d
+ * // f
+ * // b
+ * // e
+ * // g
+ * // c
+ * // fin
+ *
+ * If the function itself throws, the error will be treated the same as an
+ * unhandled rejection within the control flow.
+ *
+ * __NOTE__: This function is considered _unstable_.
+ *
+ * @param {!Function} fn The function to execute.
+ * @param {Object=} opt_self The object in whose context to run the function.
+ * @param {...*} var_args Any arguments to pass to the function.
+ */
+ async(fn, opt_self, var_args) {
+ asyncRun(() => {
+ // Clear any lingering queues, forces getActiveQueue_ to create a new one.
+ this.activeQueue_ = null;
+ var q = this.getActiveQueue_();
+ try {
+ q.execute_(fn.bind(opt_self, var_args));
+ } catch (ex) {
+ var cancellationError = CancellationError.wrap(ex,
+ 'Function passed to ControlFlow.async() threw');
+ cancellationError.silent_ = true;
+ q.abort_(cancellationError);
+ } finally {
+ this.activeQueue_ = null;
+ }
+ });
+ }
+
+ /**
+ * Event handler for when a task queue is exhausted. This starts the shutdown
+ * sequence for this instance if there are no remaining task queues: after
+ * one turn of the event loop, this object will emit the
+ * {@link ControlFlow.EventType.IDLE IDLE} event to signal
+ * listeners that it has completed. During this wait, if another task is
+ * scheduled, the shutdown will be aborted.
+ *
+ * @param {!TaskQueue} q the completed task queue.
+ * @private
+ */
+ onQueueEnd_(q) {
+ if (!this.taskQueues_) {
+ return;
+ }
+ this.taskQueues_.delete(q);
+
+ vlog(1, () => q + ' has finished');
+ vlog(1, () => this.taskQueues_.size + ' queues remain\n' + this, this);
+
+ if (!this.taskQueues_.size) {
+ if (this.shutdownTask_) {
+ throw Error('Already have a shutdown task??');
+ }
+ vlog(1, () => 'Scheduling shutdown\n' + this);
+ this.shutdownTask_ = new MicroTask(() => this.shutdown_());
+ }
+ }
+
+ /**
+ * Event handler for when a task queue terminates with an error. This triggers
+ * the cancellation of all other task queues and a
+ * {@link ControlFlow.EventType.UNCAUGHT_EXCEPTION} event.
+ * If there are no error event listeners registered with this instance, the
+ * error will be rethrown to the global error handler.
+ *
+ * @param {*} error the error that caused the task queue to terminate.
+ * @param {!TaskQueue} q the task queue.
+ * @private
+ */
+ onQueueError_(error, q) {
+ if (this.taskQueues_) {
+ this.taskQueues_.delete(q);
+ }
+ this.cancelQueues_(CancellationError.wrap(
+ error, 'There was an uncaught error in the control flow'));
+ this.cancelShutdown_();
+ this.cancelHold_();
+
+ setTimeout(() => {
+ let listeners = this.listeners(ControlFlow.EventType.UNCAUGHT_EXCEPTION);
+ if (!listeners.size) {
+ throw error;
+ } else {
+ this.reportUncaughtException_(error);
+ }
+ }, 0);
+ }
+
+ /**
+ * Cancels all remaining task queues.
+ * @param {!CancellationError} reason The cancellation reason.
+ * @private
+ */
+ cancelQueues_(reason) {
+ reason.silent_ = true;
+ if (this.taskQueues_) {
+ for (var q of this.taskQueues_) {
+ q.removeAllListeners();
+ q.abort_(reason);
+ }
+ this.taskQueues_.clear();
+ this.taskQueues_ = null;
+ }
+ }
+
+ /**
+ * Reports an uncaught exception using a
+ * {@link ControlFlow.EventType.UNCAUGHT_EXCEPTION} event.
+ *
+ * @param {*} e the error to report.
+ * @private
+ */
+ reportUncaughtException_(e) {
+ this.emit(ControlFlow.EventType.UNCAUGHT_EXCEPTION, e);
+ }
+
+ /** @private */
+ cancelHold_() {
+ if (this.hold_) {
+ clearInterval(this.hold_);
+ this.hold_ = null;
+ }
+ }
+
+ /** @private */
+ shutdown_() {
+ vlog(1, () => 'Going idle: ' + this);
+ this.cancelHold_();
+ this.shutdownTask_ = null;
+ this.emit(ControlFlow.EventType.IDLE);
+ }
+
+ /**
+ * Cancels the shutdown sequence if it is currently scheduled.
+ * @private
+ */
+ cancelShutdown_() {
+ if (this.shutdownTask_) {
+ this.shutdownTask_.cancel();
+ this.shutdownTask_ = null;
+ }
+ }
+}
+
+
+/**
+ * Events that may be emitted by an {@link ControlFlow}.
+ * @enum {string}
+ */
+ControlFlow.EventType = {
+
+ /** Emitted when all tasks have been successfully executed. */
+ IDLE: 'idle',
+
+ /** Emitted when a ControlFlow has been reset. */
+ RESET: 'reset',
+
+ /** Emitted whenever a new task has been scheduled. */
+ SCHEDULE_TASK: 'scheduleTask',
+
+ /**
+ * Emitted whenever a control flow aborts due to an unhandled promise
+ * rejection. This event will be emitted along with the offending rejection
+ * reason. Upon emitting this event, the control flow will empty its task
+ * queue and revert to its initial state.
+ */
+ UNCAUGHT_EXCEPTION: 'uncaughtException'
+};
+
+
+/**
+ * Wraps a function to execute as a cancellable micro task.
+ * @final
+ */
+class MicroTask {
+ /**
+ * @param {function()} fn The function to run as a micro task.
+ */
+ constructor(fn) {
+ /** @private {boolean} */
+ this.cancelled_ = false;
+ asyncRun(() => {
+ if (!this.cancelled_) {
+ fn();
+ }
+ });
+ }
+
+ /**
+ * Runs the given function after a micro-task yield.
+ * @param {function()} fn The function to run.
+ */
+ static run(fn) {
+ NativePromise.resolve().then(function() {
+ try {
+ fn();
+ } catch (ignored) {
+ // Do nothing.
+ }
+ });
+ }
+
+ /**
+ * Cancels the execution of this task. Note: this will not prevent the task
+ * timer from firing, just the invocation of the wrapped function.
+ */
+ cancel() {
+ this.cancelled_ = true;
+ }
+}
+
+
+/**
+ * A task to be executed by a {@link ControlFlow}.
+ *
+ * @template T
+ * @final
+ */
+class Task extends Deferred {
+ /**
+ * @param {!ControlFlow} flow The flow this instances belongs
+ * to.
+ * @param {function(): (T|!ManagedPromise<T>)} fn The function to
+ * call when the task executes. If it returns a
+ * {@link ManagedPromise}, the flow will wait for it to be
+ * resolved before starting the next task.
+ * @param {string} description A description of the task for debugging.
+ * @param {{name: string, top: !Function}=} opt_stackOptions Options to use
+ * when capturing the stacktrace for when this task was created.
+ */
+ constructor(flow, fn, description, opt_stackOptions) {
+ super(flow);
+ getUid(this);
+
+ /** @type {function(): (T|!ManagedPromise<T>)} */
+ this.execute = fn;
+
+ /** @type {string} */
+ this.description = description;
+
+ /** @type {TaskQueue} */
+ this.queue = null;
+
+ /**
+ * Whether this task is considered block. A blocked task may be registered
+ * in a task queue, but will be dropped if it is still blocked when it
+ * reaches the front of the queue. A dropped task may always be rescheduled.
+ *
+ * Blocked tasks are used when a callback is attached to an unsettled
+ * promise to reserve a spot in line (in a manner of speaking). If the
+ * promise is not settled before the callback reaches the front of the
+ * of the queue, it will be dropped. Once the promise is settled, the
+ * dropped task will be rescheduled as an interrupt on the currently task
+ * queue.
+ *
+ * @type {boolean}
+ */
+ this.blocked = false;
+
+ if (opt_stackOptions) {
+ this.promise.stack_ = captureStackTrace(
+ opt_stackOptions.name, this.description, opt_stackOptions.top);
+ }
+ }
+
+ /** @override */
+ toString() {
+ return 'Task::' + getUid(this) + '<' + this.description + '>';
+ }
+}
+
+
+/** @enum {string} */
+const TaskQueueState = {
+ NEW: 'new',
+ STARTED: 'started',
+ FINISHED: 'finished'
+};
+
+
+/**
+ * @final
+ */
+class TaskQueue extends events.EventEmitter {
+ /** @param {!ControlFlow} flow . */
+ constructor(flow) {
+ super();
+
+ /** @private {string} */
+ this.name_ = 'TaskQueue::' + getUid(this);
+
+ /** @private {!ControlFlow} */
+ this.flow_ = flow;
+
+ /** @private {!Array<!Task>} */
+ this.tasks_ = [];
+
+ /** @private {Array<!Task>} */
+ this.interrupts_ = null;
+
+ /** @private {({task: !Task, q: !TaskQueue}|null)} */
+ this.pending_ = null;
+
+ /** @private {TaskQueueState} */
+ this.state_ = TaskQueueState.NEW;
+
+ /** @private {!Set<!ManagedPromise>} */
+ this.unhandledRejections_ = new Set();
+ }
+
+ /** @override */
+ toString() {
+ return 'TaskQueue::' + getUid(this);
+ }
+
+ /**
+ * @param {!ManagedPromise} promise .
+ */
+ addUnhandledRejection(promise) {
+ // TODO: node 4.0.0+
+ vlog(2, () => this + ' registering unhandled rejection: ' + promise, this);
+ this.unhandledRejections_.add(promise);
+ }
+
+ /**
+ * @param {!ManagedPromise} promise .
+ */
+ clearUnhandledRejection(promise) {
+ var deleted = this.unhandledRejections_.delete(promise);
+ if (deleted) {
+ // TODO: node 4.0.0+
+ vlog(2, () => this + ' clearing unhandled rejection: ' + promise, this);
+ }
+ }
+
+ /**
+ * Enqueues a new task for execution.
+ * @param {!Task} task The task to enqueue.
+ * @throws {Error} If this instance has already started execution.
+ */
+ enqueue(task) {
+ if (this.state_ !== TaskQueueState.NEW) {
+ throw Error('TaskQueue has started: ' + this);
+ }
+
+ if (task.queue) {
+ throw Error('Task is already scheduled in another queue');
+ }
+
+ this.tasks_.push(task);
+ task.queue = this;
+ ON_CANCEL_HANDLER.set(
+ task.promise,
+ (e) => this.onTaskCancelled_(task, e));
+
+ vlog(1, () => this + '.enqueue(' + task + ')', this);
+ vlog(2, () => this.flow_.toString(), this);
+ }
+
+ /**
+ * Schedules the callbacks registered on the given promise in this queue.
+ *
+ * @param {!ManagedPromise} promise the promise whose callbacks should be
+ * registered as interrupts in this task queue.
+ * @throws {Error} if this queue has already finished.
+ */
+ scheduleCallbacks(promise) {
+ if (this.state_ === TaskQueueState.FINISHED) {
+ throw new Error('cannot interrupt a finished q(' + this + ')');
+ }
+
+ if (this.pending_ && this.pending_.task.promise === promise) {
+ this.pending_.task.promise.queue_ = null;
+ this.pending_ = null;
+ asyncRun(() => this.executeNext_());
+ }
+
+ if (!promise.callbacks_) {
+ return;
+ }
+ promise.callbacks_.forEach(function(cb) {
+ cb.blocked = false;
+ if (cb.queue) {
+ return;
+ }
+
+ ON_CANCEL_HANDLER.set(
+ cb.promise,
+ (e) => this.onTaskCancelled_(cb, e));
+
+ if (cb.queue === this && this.tasks_.indexOf(cb) !== -1) {
+ return;
+ }
+
+ if (cb.queue) {
+ cb.queue.dropTask_(cb);
+ }
+
+ cb.queue = this;
+ if (!this.interrupts_) {
+ this.interrupts_ = [];
+ }
+ this.interrupts_.push(cb);
+ }, this);
+ promise.callbacks_ = null;
+ vlog(2, () => this + ' interrupted\n' + this.flow_, this);
+ }
+
+ /**
+ * Starts executing tasks in this queue. Once called, no further tasks may
+ * be {@linkplain #enqueue() enqueued} with this instance.
+ *
+ * @throws {Error} if this queue has already been started.
+ */
+ start() {
+ if (this.state_ !== TaskQueueState.NEW) {
+ throw new Error('TaskQueue has already started');
+ }
+ // Always asynchronously execute next, even if there doesn't look like
+ // there is anything in the queue. This will catch pending unhandled
+ // rejections that were registered before start was called.
+ asyncRun(() => this.executeNext_());
+ }
+
+ /**
+ * Aborts this task queue. If there are any scheduled tasks, they are silently
+ * cancelled and discarded (their callbacks will never fire). If this queue
+ * has a _pending_ task, the abortion error is used to cancel that task.
+ * Otherwise, this queue will emit an error event.
+ *
+ * @param {*} error The abortion reason.
+ * @private
+ */
+ abort_(error) {
+ var cancellation;
+
+ if (error instanceof FlowResetError) {
+ cancellation = error;
+ } else {
+ cancellation = new DiscardedTaskError(error);
+ }
+
+ if (this.interrupts_ && this.interrupts_.length) {
+ this.interrupts_.forEach((t) => t.reject(cancellation));
+ this.interrupts_ = [];
+ }
+
+ if (this.tasks_ && this.tasks_.length) {
+ this.tasks_.forEach((t) => t.reject(cancellation));
+ this.tasks_ = [];
+ }
+
+ // Now that all of the remaining tasks have been silently cancelled (e.g. no
+ // exisitng callbacks on those tasks will fire), clear the silence bit on
+ // the cancellation error. This ensures additional callbacks registered in
+ // the future will actually execute.
+ cancellation.silent_ = false;
+
+ if (this.pending_) {
+ vlog(2, () => this + '.abort(); cancelling pending task', this);
+ this.pending_.task.promise.cancel(
+ /** @type {!CancellationError} */(error));
+
+ } else {
+ vlog(2, () => this + '.abort(); emitting error event', this);
+ this.emit('error', error, this);
+ }
+ }
+
+ /** @private */
+ executeNext_() {
+ if (this.state_ === TaskQueueState.FINISHED) {
+ return;
+ }
+ this.state_ = TaskQueueState.STARTED;
+
+ if (this.pending_ !== null || this.processUnhandledRejections_()) {
+ return;
+ }
+
+ var task;
+ do {
+ task = this.getNextTask_();
+ } while (task && !task.promise.isPending());
+
+ if (!task) {
+ this.state_ = TaskQueueState.FINISHED;
+ this.tasks_ = [];
+ this.interrupts_ = null;
+ vlog(2, () => this + '.emit(end)', this);
+ this.emit('end', this);
+ return;
+ }
+
+ var self = this;
+ var subQ = new TaskQueue(this.flow_);
+ subQ.once('end', () => self.onTaskComplete_(result))
+ .once('error', (e) => self.onTaskFailure_(result, e));
+ vlog(2, () => self + ' created ' + subQ + ' for ' + task);
+
+ var result = undefined;
+ try {
+ this.pending_ = {task: task, q: subQ};
+ task.promise.queue_ = this;
+ result = subQ.execute_(task.execute);
+ subQ.start();
+ } catch (ex) {
+ subQ.abort_(ex);
+ }
+ }
+
+ /**
+ * @param {!Function} fn .
+ * @return {T} .
+ * @template T
+ * @private
+ */
+ execute_(fn) {
+ try {
+ activeFlows.push(this.flow_);
+ this.flow_.activeQueue_ = this;
+ return fn();
+ } finally {
+ this.flow_.activeQueue_ = null;
+ activeFlows.pop();
+ }
+ }
+
+ /**
+ * Process any unhandled rejections registered with this task queue. If there
+ * is a rejection, this queue will be aborted with the rejection error. If
+ * there are multiple rejections registered, this queue will be aborted with
+ * a {@link MultipleUnhandledRejectionError}.
+ * @return {boolean} whether there was an unhandled rejection.
+ * @private
+ */
+ processUnhandledRejections_() {
+ if (!this.unhandledRejections_.size) {
+ return false;
+ }
+
+ var errors = new Set();
+ for (var rejection of this.unhandledRejections_) {
+ errors.add(rejection.value_);
+ }
+ this.unhandledRejections_.clear();
+
+ var errorToReport = errors.size === 1
+ ? errors.values().next().value
+ : new MultipleUnhandledRejectionError(errors);
+
+ vlog(1, () => this + ' aborting due to unhandled rejections', this);
+ if (this.flow_.propagateUnhandledRejections_) {
+ this.abort_(errorToReport);
+ return true;
+ } else {
+ vlog(1, 'error propagation disabled; reporting to control flow');
+ this.flow_.reportUncaughtException_(errorToReport);
+ return false;
+ }
+ }
+
+ /**
+ * @param {!Task} task The task to drop.
+ * @private
+ */
+ dropTask_(task) {
+ var index;
+ if (this.interrupts_) {
+ index = this.interrupts_.indexOf(task);
+ if (index != -1) {
+ task.queue = null;
+ this.interrupts_.splice(index, 1);
+ return;
+ }
+ }
+
+ index = this.tasks_.indexOf(task);
+ if (index != -1) {
+ task.queue = null;
+ this.tasks_.splice(index, 1);
+ }
+ }
+
+ /**
+ * @param {!Task} task The task that was cancelled.
+ * @param {!CancellationError} reason The cancellation reason.
+ * @private
+ */
+ onTaskCancelled_(task, reason) {
+ if (this.pending_ && this.pending_.task === task) {
+ this.pending_.q.abort_(reason);
+ } else {
+ this.dropTask_(task);
+ }
+ }
+
+ /**
+ * @param {*} value the value originally returned by the task function.
+ * @private
+ */
+ onTaskComplete_(value) {
+ if (this.pending_) {
+ this.pending_.task.fulfill(value);
+ }
+ }
+
+ /**
+ * @param {*} taskFnResult the value originally returned by the task function.
+ * @param {*} error the error that caused the task function to terminate.
+ * @private
+ */
+ onTaskFailure_(taskFnResult, error) {
+ if (Thenable.isImplementation(taskFnResult)) {
+ taskFnResult.cancel(CancellationError.wrap(error));
+ }
+ this.pending_.task.reject(error);
+ }
+
+ /**
+ * @return {(Task|undefined)} the next task scheduled within this queue,
+ * if any.
+ * @private
+ */
+ getNextTask_() {
+ var task = undefined;
+ while (true) {
+ if (this.interrupts_) {
+ task = this.interrupts_.shift();
+ }
+ if (!task && this.tasks_) {
+ task = this.tasks_.shift();
+ }
+ if (task && task.blocked) {
+ vlog(2, () => this + ' skipping blocked task ' + task, this);
+ task.queue = null;
+ task = null;
+ // TODO: recurse when tail-call optimization is available in node.
+ } else {
+ break;
+ }
+ }
+ return task;
+ }
+};
+
+
+
+/**
+ * The default flow to use if no others are active.
+ * @type {!ControlFlow}
+ */
+var defaultFlow = new ControlFlow();
+
+
+/**
+ * A stack of active control flows, with the top of the stack used to schedule
+ * commands. When there are multiple flows on the stack, the flow at index N
+ * represents a callback triggered within a task owned by the flow at index
+ * N-1.
+ * @type {!Array<!ControlFlow>}
+ */
+var activeFlows = [];
+
+
+/**
+ * Changes the default flow to use when no others are active.
+ * @param {!ControlFlow} flow The new default flow.
+ * @throws {Error} If the default flow is not currently active.
+ */
+function setDefaultFlow(flow) {
+ if (activeFlows.length) {
+ throw Error('You may only change the default flow while it is active');
+ }
+ defaultFlow = flow;
+}
+
+
+/**
+ * @return {!ControlFlow} The currently active control flow.
+ */
+function controlFlow() {
+ return /** @type {!ControlFlow} */ (
+ activeFlows.length ? activeFlows[activeFlows.length - 1] : defaultFlow);
+}
+
+
+/**
+ * Creates a new control flow. The provided callback will be invoked as the
+ * first task within the new flow, with the flow as its sole argument. Returns
+ * a promise that resolves to the callback result.
+ * @param {function(!ControlFlow)} callback The entry point
+ * to the newly created flow.
+ * @return {!ManagedPromise} A promise that resolves to the callback
+ * result.
+ */
+function createFlow(callback) {
+ var flow = new ControlFlow;
+ return flow.execute(function() {
+ return callback(flow);
+ });
+}
+
+
+/**
+ * Tests is a function is a generator.
+ * @param {!Function} fn The function to test.
+ * @return {boolean} Whether the function is a generator.
+ */
+function isGenerator(fn) {
+ return fn.constructor.name === 'GeneratorFunction';
+}
+
+
+/**
+ * Consumes a {@code GeneratorFunction}. Each time the generator yields a
+ * promise, this function will wait for it to be fulfilled before feeding the
+ * fulfilled value back into {@code next}. Likewise, if a yielded promise is
+ * rejected, the rejection error will be passed to {@code throw}.
+ *
+ * __Example 1:__ the Fibonacci Sequence.
+ *
+ * promise.consume(function* fibonacci() {
+ * var n1 = 1, n2 = 1;
+ * for (var i = 0; i < 4; ++i) {
+ * var tmp = yield n1 + n2;
+ * n1 = n2;
+ * n2 = tmp;
+ * }
+ * return n1 + n2;
+ * }).then(function(result) {
+ * console.log(result); // 13
+ * });
+ *
+ * __Example 2:__ a generator that throws.
+ *
+ * promise.consume(function* () {
+ * yield promise.delayed(250).then(function() {
+ * throw Error('boom');
+ * });
+ * }).catch(function(e) {
+ * console.log(e.toString()); // Error: boom
+ * });
+ *
+ * @param {!Function} generatorFn The generator function to execute.
+ * @param {Object=} opt_self The object to use as "this" when invoking the
+ * initial generator.
+ * @param {...*} var_args Any arguments to pass to the initial generator.
+ * @return {!ManagedPromise<?>} A promise that will resolve to the
+ * generator's final result.
+ * @throws {TypeError} If the given function is not a generator.
+ */
+function consume(generatorFn, opt_self, var_args) {
+ if (!isGenerator(generatorFn)) {
+ throw new TypeError('Input is not a GeneratorFunction: ' +
+ generatorFn.constructor.name);
+ }
+
+ var deferred = defer();
+ var generator = generatorFn.apply(
+ opt_self, Array.prototype.slice.call(arguments, 2));
+ callNext();
+ return deferred.promise;
+
+ /** @param {*=} opt_value . */
+ function callNext(opt_value) {
+ pump(generator.next, opt_value);
+ }
+
+ /** @param {*=} opt_error . */
+ function callThrow(opt_error) {
+ // Dictionary lookup required because Closure compiler's built-in
+ // externs does not include GeneratorFunction.prototype.throw.
+ pump(generator['throw'], opt_error);
+ }
+
+ function pump(fn, opt_arg) {
+ if (!deferred.promise.isPending()) {
+ return; // Defererd was cancelled; silently abort.
+ }
+
+ try {
+ var result = fn.call(generator, opt_arg);
+ } catch (ex) {
+ deferred.reject(ex);
+ return;
+ }
+
+ if (result.done) {
+ deferred.fulfill(result.value);
+ return;
+ }
+
+ asap(result.value, callNext, callThrow);
+ }
+}
+
+
+// PUBLIC API
+
+
+module.exports = {
+ CancellationError: CancellationError,
+ ControlFlow: ControlFlow,
+ Deferred: Deferred,
+ MultipleUnhandledRejectionError: MultipleUnhandledRejectionError,
+ Thenable: Thenable,
+ Promise: ManagedPromise,
+ all: all,
+ asap: asap,
+ captureStackTrace: captureStackTrace,
+ checkedNodeCall: checkedNodeCall,
+ consume: consume,
+ controlFlow: controlFlow,
+ createFlow: createFlow,
+ defer: defer,
+ delayed: delayed,
+ filter: filter,
+ fulfilled: fulfilled,
+ fullyResolved: fullyResolved,
+ isGenerator: isGenerator,
+ isPromise: isPromise,
+ map: map,
+ rejected: rejected,
+ setDefaultFlow: setDefaultFlow,
+ when: when,
+
+ get LONG_STACK_TRACES() { return LONG_STACK_TRACES; },
+ set LONG_STACK_TRACES(v) { LONG_STACK_TRACES = v; },
+};
diff --git a/node_modules/selenium-webdriver/lib/proxy.js b/node_modules/selenium-webdriver/lib/proxy.js
new file mode 100644
index 000000000..8632912f5
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/proxy.js
@@ -0,0 +1,127 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+/**
+ * @fileoverview Defines functions for configuring a webdriver proxy:
+ *
+ * const Capabilities = require('./capabilities').Capabilities;
+ *
+ * var capabilities = new Capabilities();
+ * capabilities.setProxy(proxy.manual({http: 'host:1234'});
+ */
+
+'use strict';
+
+var ProxyConfig = require('./capabilities').ProxyConfig;
+
+
+// PUBLIC API
+
+
+/**
+ * Configures WebDriver to bypass all browser proxies.
+ * @return {!ProxyConfig} A new proxy configuration object.
+ */
+exports.direct = function() {
+ return {proxyType: 'direct'};
+};
+
+
+/**
+ * Manually configures the browser proxy. The following options are
+ * supported:
+ *
+ * - `ftp`: Proxy host to use for FTP requests
+ * - `http`: Proxy host to use for HTTP requests
+ * - `https`: Proxy host to use for HTTPS requests
+ * - `bypass`: A list of hosts requests should directly connect to,
+ * bypassing any other proxies for that request. May be specified as a
+ * comma separated string, or a list of strings.
+ *
+ * Behavior is undefined for FTP, HTTP, and HTTPS requests if the
+ * corresponding key is omitted from the configuration options.
+ *
+ * @param {{ftp: (string|undefined),
+ * http: (string|undefined),
+ * https: (string|undefined),
+ * bypass: (string|!Array.<string>|undefined)}} options Proxy
+ * configuration options.
+ * @return {!ProxyConfig} A new proxy configuration object.
+ */
+exports.manual = function(options) {
+ // TODO(jleyba): Figure out why the Closure compiler does not think this is
+ // a ProxyConfig record without the cast.
+ return /** @type {!ProxyConfig} */({
+ proxyType: 'manual',
+ ftpProxy: options.ftp,
+ httpProxy: options.http,
+ sslProxy: options.https,
+ noProxy: Array.isArray(options.bypass) ?
+ options.bypass.join(',') : options.bypass
+ });
+};
+
+
+/**
+ * Creates a proxy configuration for a socks proxy.
+ *
+ * __Example:__
+ *
+ * const {Capabilities} = require('selenium-webdriver');
+ * const proxy = require('selenium-webdriver/lib/proxy');
+ *
+ * let capabilities = new Capabilities();
+ * capabilities.setProxy(proxy.socks('localhost:1234', 'bob', 'password'));
+ *
+ *
+ * @param {string} host The proxy host, in the form `hostname:port`.
+ * @param {string} username The user name to authenticate as.
+ * @param {string} password The password to authenticate with.
+ * @return {!ProxyConfig} A new proxy configuration object.
+ * @see https://en.wikipedia.org/wiki/SOCKS
+ */
+exports.socks = function(host, username, password) {
+ return /** @type {!ProxyConfig} */({
+ proxyType: 'manual',
+ socksProxy: host,
+ socksUsername: username,
+ socksPassword: password
+ });
+};
+
+
+/**
+ * Configures WebDriver to configure the browser proxy using the PAC file at
+ * the given URL.
+ * @param {string} url URL for the PAC proxy to use.
+ * @return {!ProxyConfig} A new proxy configuration object.
+ */
+exports.pac = function(url) {
+ return {
+ proxyType: 'pac',
+ proxyAutoconfigUrl: url
+ };
+};
+
+
+/**
+ * Configures WebDriver to use the current system's proxy.
+ * @return {!ProxyConfig} A new proxy configuration object.
+ */
+exports.system = function() {
+ return {proxyType: 'system'};
+};
diff --git a/node_modules/selenium-webdriver/lib/safari/client.js b/node_modules/selenium-webdriver/lib/safari/client.js
new file mode 100644
index 000000000..482c820fc
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/safari/client.js
@@ -0,0 +1,56 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';var n=this;
+function q(a){var b=typeof a;if("object"==b)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==c||"undefined"!=typeof a.call&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("call"))return"function"}else return"null";else if("function"==
+b&&"undefined"==typeof a.call)return"object";return b}function aa(a){var b=q(a);return"array"==b||"object"==b&&"number"==typeof a.length}function r(a){return"string"==typeof a}function ba(a){var b=typeof a;return"object"==b&&null!=a||"function"==b}function ca(a,b,c){return a.call.apply(a.bind,arguments)}
+function da(a,b,c){if(!a)throw Error();if(2<arguments.length){var d=Array.prototype.slice.call(arguments,2);return function(){var c=Array.prototype.slice.call(arguments);Array.prototype.unshift.apply(c,d);return a.apply(b,c)}}return function(){return a.apply(b,arguments)}}function ea(a,b,c){ea=Function.prototype.bind&&-1!=Function.prototype.bind.toString().indexOf("native code")?ca:da;return ea.apply(null,arguments)}var fa=Date.now||function(){return+new Date};
+function ga(a,b){function c(){}c.prototype=b.prototype;a.B=b.prototype;a.prototype=new c;a.prototype.constructor=a;a.w=function(a,c,g){for(var e=Array(arguments.length-2),l=2;l<arguments.length;l++)e[l-2]=arguments[l];return b.prototype[c].apply(a,e)}};function t(a){if(Error.captureStackTrace)Error.captureStackTrace(this,t);else{var b=Error().stack;b&&(this.stack=b)}a&&(this.message=String(a))}ga(t,Error);t.prototype.name="CustomError";var ia;function ja(a,b){for(var c=a.split("%s"),d="",f=Array.prototype.slice.call(arguments,1);f.length&&1<c.length;)d+=c.shift()+f.shift();return d+c.join("%s")}var ka=String.prototype.trim?function(a){return a.trim()}:function(a){return a.replace(/^[\s\xa0]+|[\s\xa0]+$/g,"")};
+function la(a){if(!ma.test(a))return a;-1!=a.indexOf("&")&&(a=a.replace(na,"&amp;"));-1!=a.indexOf("<")&&(a=a.replace(oa,"&lt;"));-1!=a.indexOf(">")&&(a=a.replace(pa,"&gt;"));-1!=a.indexOf('"')&&(a=a.replace(qa,"&quot;"));-1!=a.indexOf("'")&&(a=a.replace(ra,"&#39;"));-1!=a.indexOf("\x00")&&(a=a.replace(sa,"&#0;"));return a}var na=/&/g,oa=/</g,pa=/>/g,qa=/"/g,ra=/'/g,sa=/\x00/g,ma=/[\x00&<>"']/;function ta(a,b){return a<b?-1:a>b?1:0};function ua(a,b){b.unshift(a);t.call(this,ja.apply(null,b));b.shift()}ga(ua,t);ua.prototype.name="AssertionError";function u(a,b){throw new ua("Failure"+(a?": "+a:""),Array.prototype.slice.call(arguments,1));};var va=Array.prototype.indexOf?function(a,b,c){return Array.prototype.indexOf.call(a,b,c)}:function(a,b,c){c=null==c?0:0>c?Math.max(0,a.length+c):c;if(r(a))return r(b)&&1==b.length?a.indexOf(b,c):-1;for(;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1},wa=Array.prototype.forEach?function(a,b,c){Array.prototype.forEach.call(a,b,c)}:function(a,b,c){for(var d=a.length,f=r(a)?a.split(""):a,g=0;g<d;g++)g in f&&b.call(c,f[g],g,a)};
+function xa(a){return Array.prototype.concat.apply(Array.prototype,arguments)}function Ba(a){var b=a.length;if(0<b){for(var c=Array(b),d=0;d<b;d++)c[d]=a[d];return c}return[]}function Ca(a,b,c){return 2>=arguments.length?Array.prototype.slice.call(a,b):Array.prototype.slice.call(a,b,c)};function Da(a,b){for(var c in a)b.call(void 0,a[c],c,a)};function v(a,b){this.b={};this.a=[];this.f=this.c=0;var c=arguments.length;if(1<c){if(c%2)throw Error("Uneven number of arguments");for(var d=0;d<c;d+=2)Ea(this,arguments[d],arguments[d+1])}else if(a){var f;if(a instanceof v)f=a.o(),d=a.l();else{var c=[],g=0;for(f in a)c[g++]=f;f=c;c=[];g=0;for(d in a)c[g++]=a[d];d=c}for(c=0;c<f.length;c++)Ea(this,f[c],d[c])}}v.prototype.l=function(){Fa(this);for(var a=[],b=0;b<this.a.length;b++)a.push(this.b[this.a[b]]);return a};
+v.prototype.o=function(){Fa(this);return this.a.concat()};v.prototype.clear=function(){this.b={};this.f=this.c=this.a.length=0};function Fa(a){if(a.c!=a.a.length){for(var b=0,c=0;b<a.a.length;){var d=a.a[b];y(a.b,d)&&(a.a[c++]=d);b++}a.a.length=c}if(a.c!=a.a.length){for(var f={},c=b=0;b<a.a.length;)d=a.a[b],y(f,d)||(a.a[c++]=d,f[d]=1),b++;a.a.length=c}}function Ga(a,b){return y(a.b,b)?a.b[b]:void 0}function Ea(a,b,c){y(a.b,b)||(a.c++,a.a.push(b),a.f++);a.b[b]=c}
+v.prototype.forEach=function(a,b){for(var c=this.o(),d=0;d<c.length;d++){var f=c[d];a.call(b,Ga(this,f),f,this)}};v.prototype.clone=function(){return new v(this)};function y(a,b){return Object.prototype.hasOwnProperty.call(a,b)};var Ha=/^(?:([^:/?#.]+):)?(?:\/\/(?:([^/?#]*)@)?([^/#?]*?)(?::([0-9]+))?(?=[/#?]|$))?([^?#]+)?(?:\?([^#]*))?(?:#(.*))?$/;function Ia(a,b){if(a)for(var c=a.split("&"),d=0;d<c.length;d++){var f=c[d].indexOf("="),g=null,e=null;0<=f?(g=c[d].substring(0,f),e=c[d].substring(f+1)):g=c[d];b(g,e?decodeURIComponent(e.replace(/\+/g," ")):"")}};function z(a,b){this.f=this.u=this.c="";this.s=null;this.g=this.m="";this.a=!1;var c;a instanceof z?(this.a=void 0!==b?b:a.a,Ja(this,a.c),this.u=a.u,this.f=a.f,Ka(this,a.s),this.m=a.m,La(this,a.b.clone()),this.g=a.g):a&&(c=String(a).match(Ha))?(this.a=!!b,Ja(this,c[1]||"",!0),this.u=A(c[2]||""),this.f=A(c[3]||"",!0),Ka(this,c[4]),this.m=A(c[5]||"",!0),La(this,c[6]||"",!0),this.g=A(c[7]||"")):(this.a=!!b,this.b=new B(null,0,this.a))}
+z.prototype.toString=function(){var a=[],b=this.c;b&&a.push(C(b,Ma,!0),":");var c=this.f;if(c||"file"==b)a.push("//"),(b=this.u)&&a.push(C(b,Ma,!0),"@"),a.push(encodeURIComponent(String(c)).replace(/%25([0-9a-fA-F]{2})/g,"%$1")),c=this.s,null!=c&&a.push(":",String(c));if(c=this.m)this.f&&"/"!=c.charAt(0)&&a.push("/"),a.push(C(c,"/"==c.charAt(0)?Na:Oa,!0));(c=this.b.toString())&&a.push("?",c);(c=this.g)&&a.push("#",C(c,Pa));return a.join("")};z.prototype.clone=function(){return new z(this)};
+function Ja(a,b,c){a.c=c?A(b,!0):b;a.c&&(a.c=a.c.replace(/:$/,""))}function Ka(a,b){if(b){b=Number(b);if(isNaN(b)||0>b)throw Error("Bad port number "+b);a.s=b}else a.s=null}function La(a,b,c){b instanceof B?(a.b=b,Qa(a.b,a.a)):(c||(b=C(b,Ra)),a.b=new B(b,0,a.a))}function A(a,b){return a?b?decodeURI(a.replace(/%25/g,"%2525")):decodeURIComponent(a):""}function C(a,b,c){return r(a)?(a=encodeURI(a).replace(b,Sa),c&&(a=a.replace(/%25([0-9a-fA-F]{2})/g,"%$1")),a):null}
+function Sa(a){a=a.charCodeAt(0);return"%"+(a>>4&15).toString(16)+(a&15).toString(16)}var Ma=/[#\/\?@]/g,Oa=/[\#\?:]/g,Na=/[\#\?]/g,Ra=/[\#\?@]/g,Pa=/#/g;function B(a,b,c){this.c=this.a=null;this.b=a||null;this.f=!!c}function D(a){a.a||(a.a=new v,a.c=0,a.b&&Ia(a.b,function(b,c){var d=decodeURIComponent(b.replace(/\+/g," "));D(a);a.b=null;var d=E(a,d),f=Ga(a.a,d);f||Ea(a.a,d,f=[]);f.push(c);a.c=a.c+1}))}
+function Ta(a,b){D(a);b=E(a,b);if(y(a.a.b,b)){a.b=null;a.c=a.c-Ga(a.a,b).length;var c=a.a;y(c.b,b)&&(delete c.b[b],c.c--,c.f++,c.a.length>2*c.c&&Fa(c))}}B.prototype.clear=function(){this.a=this.b=null;this.c=0};B.prototype.o=function(){D(this);for(var a=this.a.l(),b=this.a.o(),c=[],d=0;d<b.length;d++)for(var f=a[d],g=0;g<f.length;g++)c.push(b[d]);return c};
+B.prototype.l=function(a){D(this);var b=[];if(r(a)){var c=a;D(this);c=E(this,c);y(this.a.b,c)&&(b=xa(b,Ga(this.a,E(this,a))))}else for(a=this.a.l(),c=0;c<a.length;c++)b=xa(b,a[c]);return b};function Ua(){var a=(new z(window.location)).b.l("url");return 0<a.length?String(a[0]):void 0}
+B.prototype.toString=function(){if(this.b)return this.b;if(!this.a)return"";for(var a=[],b=this.a.o(),c=0;c<b.length;c++)for(var d=b[c],f=encodeURIComponent(String(d)),d=this.l(d),g=0;g<d.length;g++){var e=f;""!==d[g]&&(e+="="+encodeURIComponent(String(d[g])));a.push(e)}return this.b=a.join("&")};B.prototype.clone=function(){var a=new B;a.b=this.b;this.a&&(a.a=this.a.clone(),a.c=this.c);return a};function E(a,b){var c=String(b);a.f&&(c=c.toLowerCase());return c}
+function Qa(a,b){b&&!a.f&&(D(a),a.b=null,a.a.forEach(function(a,b){var f=b.toLowerCase();b!=f&&(Ta(this,b),Ta(this,f),0<a.length&&(this.b=null,Ea(this.a,E(this,f),Ba(a)),this.c=this.c+a.length))},a));a.f=b};var Va={area:!0,base:!0,br:!0,col:!0,command:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0};function F(){this.a="";this.b=Wa}F.prototype.i=!0;F.prototype.h=function(){return this.a};F.prototype.toString=function(){return"Const{"+this.a+"}"};function Xa(a){if(a instanceof F&&a.constructor===F&&a.b===Wa)return a.a;u("expected object of type Const, got '"+a+"'");return"type_error:Const"}var Wa={};function Ya(a){var b=new F;b.a=a;return b};function G(){this.a="";this.b=Za}G.prototype.i=!0;var Za={};G.prototype.h=function(){return this.a};G.prototype.toString=function(){return"SafeStyle{"+this.a+"}"};function $a(a){var b=new G;b.a=a;return b}var ab=$a(""),bb=/^([-,."'%_!# a-zA-Z0-9]+|(?:rgb|hsl)a?\([0-9.%, ]+\))$/;function H(){this.a="";this.b=cb}H.prototype.i=!0;H.prototype.h=function(){return this.a};H.prototype.v=!0;H.prototype.j=function(){return 1};H.prototype.toString=function(){return"SafeUrl{"+this.a+"}"};function db(a){if(a instanceof H&&a.constructor===H&&a.b===cb)return a.a;u("expected object of type SafeUrl, got '"+a+"' of type "+q(a));return"type_error:SafeUrl"}var eb=/^(?:(?:https?|mailto|ftp):|[^&:/?#]*(?:[/?#]|$))/i;
+function fb(a){if(a instanceof H)return a;a=a.i?a.h():String(a);eb.test(a)||(a="about:invalid#zClosurez");return gb(a)}var cb={};function gb(a){var b=new H;b.a=a;return b}gb("about:blank");function I(){this.a=hb}I.prototype.i=!0;I.prototype.h=function(){return""};I.prototype.v=!0;I.prototype.j=function(){return 1};I.prototype.toString=function(){return"TrustedResourceUrl{}"};var hb={};var J;a:{var ib=n.navigator;if(ib){var jb=ib.userAgent;if(jb){J=jb;break a}}J=""};function L(){this.a="";this.c=kb;this.b=null}L.prototype.v=!0;L.prototype.j=function(){return this.b};L.prototype.i=!0;L.prototype.h=function(){return this.a};L.prototype.toString=function(){return"SafeHtml{"+this.a+"}"};function M(a){if(a instanceof L&&a.constructor===L&&a.c===kb)return a.a;u("expected object of type SafeHtml, got '"+a+"' of type "+q(a));return"type_error:SafeHtml"}function lb(a){if(a instanceof L)return a;var b=null;a.v&&(b=a.j());a=la(a.i?a.h():String(a));return N(a,b)}
+function O(a){if(a instanceof L)return a;a=lb(a);var b;b=M(a).replace(/ /g," &#160;").replace(/(\r\n|\r|\n)/g,"<br>");return N(b,a.j())}var mb=/^[a-zA-Z0-9-]+$/,nb={action:!0,cite:!0,data:!0,formaction:!0,href:!0,manifest:!0,poster:!0,src:!0},ob={APPLET:!0,BASE:!0,EMBED:!0,IFRAME:!0,LINK:!0,MATH:!0,META:!0,OBJECT:!0,SCRIPT:!0,STYLE:!0,SVG:!0,TEMPLATE:!0};
+function pb(a,b,c){if(!mb.test(a))throw Error("Invalid tag name <"+a+">.");if(a.toUpperCase()in ob)throw Error("Tag name <"+a+"> is not allowed for SafeHtml.");var d=null,f="<"+a;if(b)for(var g in b){if(!mb.test(g))throw Error('Invalid attribute name "'+g+'".');var e=b[g];if(null!=e){var l,m=a;l=g;if(e instanceof F)e=Xa(e);else if("style"==l.toLowerCase()){if(!ba(e))throw Error('The "style" attribute requires goog.html.SafeStyle or map of style properties, '+typeof e+" given: "+e);if(!(e instanceof
+G)){var m="",w=void 0;for(w in e){if(!/^[-_a-zA-Z0-9]+$/.test(w))throw Error("Name allows only [-_a-zA-Z0-9], got: "+w);var k=e[w];if(null!=k){if(k instanceof F)k=Xa(k);else if(bb.test(k)){for(var h=!0,x=!0,p=0;p<k.length;p++){var K=k.charAt(p);"'"==K&&x?h=!h:'"'==K&&h&&(x=!x)}h&&x||(u("String value requires balanced quotes, got: "+k),k="zClosurez")}else u("String value allows only [-,.\"'%_!# a-zA-Z0-9], rgb() and rgba(), got: "+k),k="zClosurez";m+=w+":"+k+";"}}e=m?$a(m):ab}m=void 0;e instanceof
+G&&e.constructor===G&&e.b===Za?m=e.a:(u("expected object of type SafeStyle, got '"+e+"' of type "+q(e)),m="type_error:SafeStyle");e=m}else{if(/^on/i.test(l))throw Error('Attribute "'+l+'" requires goog.string.Const value, "'+e+'" given.');if(l.toLowerCase()in nb)if(e instanceof I)e instanceof I&&e.constructor===I&&e.a===hb?e="":(u("expected object of type TrustedResourceUrl, got '"+e+"' of type "+q(e)),e="type_error:TrustedResourceUrl");else if(e instanceof H)e=db(e);else if(r(e))e=fb(e).h();else throw Error('Attribute "'+
+l+'" on tag "'+m+'" requires goog.html.SafeUrl, goog.string.Const, or string, value "'+e+'" given.');}e.i&&(e=e.h());l=l+'="'+la(String(e))+'"';f=f+(" "+l)}}null!=c?"array"==q(c)||(c=[c]):c=[];!0===Va[a.toLowerCase()]?f+=">":(d=P(c),f+=">"+M(d)+"</"+a+">",d=d.j());(a=b&&b.dir)&&(/^(ltr|rtl|auto)$/i.test(a)?d=0:d=null);return N(f,d)}function P(a){function b(a){"array"==q(a)?wa(a,b):(a=lb(a),d+=M(a),a=a.j(),0==c?c=a:0!=a&&c!=a&&(c=null))}var c=0,d="";wa(arguments,b);return N(d,c)}var kb={};
+function N(a,b){var c=new L;c.a=a;c.b=b;return c}N("<!DOCTYPE html>",0);var qb=N("",0),rb=N("<br>",0);function sb(a){var b;b=Error();if(Error.captureStackTrace)Error.captureStackTrace(b,a||sb),b=String(b.stack);else{try{throw b;}catch(c){b=c}b=(b=b.stack)?String(b):null}b||(b=tb(a||arguments.callee.caller,[]));return b}
+function tb(a,b){var c=[];if(0<=va(b,a))c.push("[...circular reference...]");else if(a&&50>b.length){c.push(ub(a)+"(");for(var d=a.arguments,f=0;d&&f<d.length;f++){0<f&&c.push(", ");var g;g=d[f];switch(typeof g){case "object":g=g?"object":"null";break;case "string":break;case "number":g=String(g);break;case "boolean":g=g?"true":"false";break;case "function":g=(g=ub(g))?g:"[fn]";break;default:g=typeof g}40<g.length&&(g=g.substr(0,40)+"...");c.push(g)}b.push(a);c.push(")\n");try{c.push(tb(a.caller,
+b))}catch(e){c.push("[exception trying to get caller]\n")}}else a?c.push("[...long stack...]"):c.push("[end]");return c.join("")}function ub(a){if(Q[a])return Q[a];a=String(a);if(!Q[a]){var b=/function ([^\(]+)/.exec(a);Q[a]=b?b[1]:"[Anonymous]"}return Q[a]}var Q={};function vb(a,b,c,d,f){"number"==typeof f||wb++;this.c=d||fa();this.f=a;this.b=b;this.g=c;delete this.a}vb.prototype.a=null;var wb=0;function xb(a){this.g=a;this.a=this.c=this.f=this.b=null}function R(a,b){this.name=a;this.value=b}R.prototype.toString=function(){return this.name};var yb=new R("SHOUT",1200),zb=new R("SEVERE",1E3),Ab=new R("WARNING",900),Bb=new R("INFO",800),Cb=new R("CONFIG",700);function Db(a){if(a.f)return a.f;if(a.b)return Db(a.b);u("Root logger has no level set.");return null}
+xb.prototype.log=function(a,b,c){if(a.value>=Db(this).value)for("function"==q(b)&&(b=b()),a=new vb(a,String(b),this.g),c&&(a.a=c),c="log:"+a.b,n.console&&(n.console.timeStamp?n.console.timeStamp(c):n.console.markTimeline&&n.console.markTimeline(c)),n.msWriteProfilerMark&&n.msWriteProfilerMark(c),c=this;c;){b=c;var d=a;if(b.a)for(var f=0,g=void 0;g=b.a[f];f++)g(d);c=c.b}};var Eb={},S=null;function Fb(){S||(S=new xb(""),Eb[""]=S,S.f=Cb)}
+function Gb(a){Fb();var b;if(!(b=Eb[a])){b=new xb(a);var c=a.lastIndexOf("."),d=a.substr(c+1),c=Gb(a.substr(0,c));c.c||(c.c={});c.c[d]=b;b.b=c;Eb[a]=b}return b};var Hb=new function(){this.a=fa()};function Ib(a){this.c=a||"";this.f=Hb}Ib.prototype.a=!0;Ib.prototype.b=!1;function T(a){return 10>a?"0"+a:String(a)}function Jb(a){Ib.call(this,a)}ga(Jb,Ib);Jb.prototype.b=!0;function Kb(a,b){Da(b,function(b,d){"style"==d?a.style.cssText=b:"class"==d?a.className=b:"for"==d?a.htmlFor=b:Lb.hasOwnProperty(d)?a.setAttribute(Lb[d],b):0==d.lastIndexOf("aria-",0)||0==d.lastIndexOf("data-",0)?a.setAttribute(d,b):a[d]=b})}var Lb={cellpadding:"cellPadding",cellspacing:"cellSpacing",colspan:"colSpan",frameborder:"frameBorder",height:"height",maxlength:"maxLength",role:"role",rowspan:"rowSpan",type:"type",usemap:"useMap",valign:"vAlign",width:"width"};
+function Mb(a,b,c){function d(c){c&&b.appendChild(r(c)?a.createTextNode(c):c)}for(var f=2;f<c.length;f++){var g=c[f];!aa(g)||ba(g)&&0<g.nodeType?d(g):wa(Nb(g)?Ba(g):g,d)}}function Nb(a){if(a&&"number"==typeof a.length){if(ba(a))return"function"==typeof a.item||"string"==typeof a.item;if("function"==q(a))return"function"==typeof a.item}return!1}function Ob(a){this.b=a||n.document||document}
+function Pb(a,b){var c;c=a.b;var d=b&&"*"!=b?b.toUpperCase():"";c.querySelectorAll&&c.querySelector&&d?c=c.querySelectorAll(d+""):c=c.getElementsByTagName(d||"*");return c}Ob.prototype.a=function(a,b,c){var d=this.b,f=arguments,g=f[1],e=d.createElement(f[0]);g&&(r(g)?e.className=g:"array"==q(g)?e.className=g.join(" "):Kb(e,g));2<f.length&&Mb(d,e,f);return e};
+Ob.prototype.contains=function(a,b){if(!a||!b)return!1;if(a.contains&&1==b.nodeType)return a==b||a.contains(b);if("undefined"!=typeof a.compareDocumentPosition)return a==b||!!(a.compareDocumentPosition(b)&16);for(;b&&a!=b;)b=b.parentNode;return b==a};function Qb(a){this.m=ea(this.f,this);this.b=new Jb;this.c=this.b.a=!1;this.a=a;this.g=this.a.ownerDocument||this.a.document;a=(a=this.a)?new Ob(9==a.nodeType?a:a.ownerDocument||a.document):ia||(ia=new Ob);var b=null,c=Pb(a,"HEAD")[0];c||(b=Pb(a,"BODY")[0],c=a.a("HEAD"),b.parentNode.insertBefore(c,b));b=a.a("STYLE");b.innerHTML=".dbg-sev{color:#F00}.dbg-w{color:#C40}.dbg-sh{font-weight:bold;color:#000}.dbg-i{color:#444}.dbg-f{color:#999}.dbg-ev{color:#0A0}.dbg-m{color:#990}.logmsg{border-bottom:1px solid #CCC;padding:2px}.logsep{background-color: #8C8;}.logdiv{border:1px solid #CCC;background-color:#FCFCFC;font:medium monospace}";
+c.appendChild(b);this.a.className+=" logdiv"}function Rb(a){if(1!=a.c){var b;Fb();b=S;var c=a.m;b.a||(b.a=[]);b.a.push(c);a.c=!0}}
+Qb.prototype.f=function(a){if(a){var b=100>=this.a.scrollHeight-this.a.scrollTop-this.a.clientHeight,c=this.g.createElement("DIV");c.className="logmsg";var d;var f=this.b;if(a){switch(a.f.value){case yb.value:d="dbg-sh";break;case zb.value:d="dbg-sev";break;case Ab.value:d="dbg-w";break;case Bb.value:d="dbg-i";break;default:d="dbg-f"}var g=[];g.push(f.c," ");if(f.a){var e=new Date(a.c);g.push("[",T(e.getFullYear()-2E3)+T(e.getMonth()+1)+T(e.getDate())+" "+T(e.getHours())+":"+T(e.getMinutes())+":"+
+T(e.getSeconds())+"."+T(Math.floor(e.getMilliseconds()/10)),"] ")}var e=(a.c-f.f.a)/1E3,l=e.toFixed(3),m=0;if(1>e)m=2;else for(;100>e;)m++,e*=10;for(;0<m--;)l=" "+l;g.push("[",l,"s] ");g.push("[",a.g,"] ");g=O(g.join(""));e=qb;if(f.b&&a.a){var w;try{var k;var h=a.a,x;d:{for(var f=["window","location","href"],e=n,p;p=f.shift();)if(null!=e[p])e=e[p];else{x=null;break d}x=e}if(r(h))k={message:h,name:"Unknown error",lineNumber:"Not available",fileName:x,stack:"Not available"};else{var K,ya;p=!1;try{K=
+h.lineNumber||h.A||"Not available"}catch(za){K="Not available",p=!0}try{ya=h.fileName||h.filename||h.sourceURL||n.$googDebugFname||x}catch(za){ya="Not available",p=!0}k=!p&&h.lineNumber&&h.fileName&&h.stack&&h.message&&h.name?h:{message:h.message||"Not available",name:h.name||"UnknownError",lineNumber:K,fileName:ya,stack:h.stack||"Not available"}}var Aa;var ha=k.fileName;null!=ha||(ha="");if(/^https?:\/\//i.test(ha)){var cc=fb(ha);Ya("view-source scheme plus HTTP/HTTPS URL");var dc="view-source:"+
+db(cc);Aa=gb(dc)}else{var ec=Ya("sanitizedviewsrc");Aa=gb(Xa(ec))}w=P(O("Message: "+k.message+"\nUrl: "),pb("a",{href:Aa,target:"_new"},k.fileName),O("\nLine: "+k.lineNumber+"\n\nBrowser stack:\n"+k.stack+"-> [end]\n\nJS stack traversal:\n"+sb(void 0)+"-> "))}catch(za){w=O("Exception trying to expose exception! You win, we lose. "+za)}e=P(rb,w)}a=O(a.b);d=pb("span",{"class":d},P(a,e));d=P(g,d,rb)}else d=qb;c.innerHTML=M(d);this.a.appendChild(c);b&&(this.a.scrollTop=this.a.scrollHeight)}};
+Qb.prototype.clear=function(){this.a&&(this.a.innerHTML=M(qb))};function U(a,b){a&&a.log(Bb,b,void 0)};var Sb;if(-1!=J.indexOf("iPhone")&&-1==J.indexOf("iPod")&&-1==J.indexOf("iPad")||-1!=J.indexOf("iPad")||-1!=J.indexOf("iPod"))Sb="";else{var Tb=/Version\/([0-9.]+)/.exec(J);Sb=Tb?Tb[1]:""};for(var Ub=0,Vb=ka(String(Sb)).split("."),Wb=ka("6").split("."),Xb=Math.max(Vb.length,Wb.length),Yb=0;0==Ub&&Yb<Xb;Yb++){var Zb=Vb[Yb]||"",$b=Wb[Yb]||"",ac=/(\d*)(\D*)/g,bc=/(\d*)(\D*)/g;do{var V=ac.exec(Zb)||["","",""],W=bc.exec($b)||["","",""];if(0==V[0].length&&0==W[0].length)break;Ub=ta(0==V[1].length?0:parseInt(V[1],10),0==W[1].length?0:parseInt(W[1],10))||ta(0==V[2].length,0==W[2].length)||ta(V[2],W[2])}while(0==Ub)};var fc=JSON.stringify;function X(a,b,c){var d=a.constructor;if(a.window===a)try{var f=a.constructor;delete a.constructor;d=a.constructor;a.constructor=f}catch(g){}return d.prototype[b].apply(a,Ca(arguments,2))};Gb("safaridriver.message");function gc(a){this.a={};this.a[hc]="webdriver";this.a[ic]=a}var hc="origin",ic="type";function jc(a){var b=window;a.a[hc]="webdriver";if(b.postMessage){var c,b=function(a){c=a.data};X(window,"addEventListener","safaridriver.message.response",b,!1);kc(a.a);X(window,"removeEventListener","safaridriver.message.response",b,!1);return c}var d=X(document,"createEvent","Events");d.initEvent("beforeload",!1,!1);return b.canLoad(d,a.a)}
+function kc(a){var b=X(document,"createEvent","MessageEvent");b.initMessageEvent("safaridriver.message",!1,!1,a,window.location.origin,"0",window,null);X(window,"dispatchEvent",b)}gc.prototype.toJSON=function(){return this.a};gc.prototype.toString=function(){return fc(this.toJSON())};function lc(a){gc.call(this,"connect");this.a.url=a}ga(lc,gc);function mc(){function a(){f+=1;jc(g)?(U(c,"Connected to extension"),U(c,"Requesting extension connect to client at "+d)):5>f?setTimeout(a,250*f):c&&c.log(zb,"Unable to establish a connection with the SafariDriver extension",void 0)}var b=document.createElement("h2");b.innerHTML="SafariDriver Launcher";document.body.appendChild(b);b=document.createElement("div");document.body.appendChild(b);Rb(new Qb(b));var c=Gb("safaridriver.client"),d=Ua();if(d){d=new z(d);U(c,"Connecting to SafariDriver browser extension...");
+U(c,"This will fail if you have not installed the latest SafariDriver extension from\nhttp://selenium-release.storage.googleapis.com/index.html");U(c,"Extension logs may be viewed by clicking the Selenium [\u2713] button on the Safari toolbar");var f=0,g=new lc(d.toString());a()}else c&&c.log(zb,"No url specified. Please reload this page with the url parameter set",void 0)}var Y=["init"],Z=n;Y[0]in Z||!Z.execScript||Z.execScript("var "+Y[0]);
+for(var nc;Y.length&&(nc=Y.shift());)Y.length||void 0===mc?Z[nc]?Z=Z[nc]:Z=Z[nc]={}:Z[nc]=mc;;window.onload = init;
diff --git a/node_modules/selenium-webdriver/lib/session.js b/node_modules/selenium-webdriver/lib/session.js
new file mode 100644
index 000000000..296291d4c
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/session.js
@@ -0,0 +1,80 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+const Capabilities = require('./capabilities').Capabilities;
+
+
+/**
+ * Contains information about a single WebDriver session.
+ */
+class Session {
+
+ /**
+ * @param {string} id The session ID.
+ * @param {!(Object|Capabilities)} capabilities The session
+ * capabilities.
+ */
+ constructor(id, capabilities) {
+ /** @private {string} */
+ this.id_ = id;
+
+ /** @private {!Capabilities} */
+ this.caps_ = capabilities instanceof Capabilities
+ ? /** @type {!Capabilities} */(capabilities)
+ : new Capabilities(capabilities);
+ }
+
+ /**
+ * @return {string} This session's ID.
+ */
+ getId() {
+ return this.id_;
+ }
+
+ /**
+ * @return {!Capabilities} This session's capabilities.
+ */
+ getCapabilities() {
+ return this.caps_;
+ }
+
+ /**
+ * Retrieves the value of a specific capability.
+ * @param {string} key The capability to retrieve.
+ * @return {*} The capability value.
+ */
+ getCapability(key) {
+ return this.caps_.get(key);
+ }
+
+ /**
+ * Returns the JSON representation of this object, which is just the string
+ * session ID.
+ * @return {string} The JSON representation of this Session.
+ */
+ toJSON() {
+ return this.getId();
+ }
+}
+
+
+// PUBLIC API
+
+
+module.exports = {Session: Session};
diff --git a/node_modules/selenium-webdriver/lib/symbols.js b/node_modules/selenium-webdriver/lib/symbols.js
new file mode 100644
index 000000000..d5c62504e
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/symbols.js
@@ -0,0 +1,38 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+/**
+ * @fileoverview Defines well-known symbols used within the selenium-webdriver
+ * library.
+ */
+
+
+module.exports = {
+ /**
+ * The serialize symbol specifies a method that returns an object's serialized
+ * representation. If an object's serialized form is not immediately
+ * available, the serialize method will return a promise that will be resolved
+ * with the serialized form.
+ *
+ * Note that the described method is analgous to objects that define a
+ * `toJSON()` method, except the serialized result may be a promise, or
+ * another object with a promised property.
+ */
+ serialize: Symbol('serialize')
+};
diff --git a/node_modules/selenium-webdriver/lib/test/build.js b/node_modules/selenium-webdriver/lib/test/build.js
new file mode 100644
index 000000000..59f0b1357
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/build.js
@@ -0,0 +1,153 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+const spawn = require('child_process').spawn,
+ fs = require('fs'),
+ path = require('path');
+
+const isDevMode = require('../devmode'),
+ promise = require('../promise');
+
+var projectRoot = path.normalize(path.join(__dirname, '../../../../..'));
+
+
+function checkIsDevMode() {
+ if (!isDevMode) {
+ throw Error('Cannot execute build; not running in dev mode');
+ }
+}
+
+
+/**
+ * Targets that have been previously built.
+ * @type {!Object}
+ */
+var builtTargets = {};
+
+
+/**
+ * @param {!Array.<string>} targets The targets to build.
+ * @throws {Error} If not running in dev mode.
+ * @constructor
+ */
+var Build = function(targets) {
+ checkIsDevMode();
+ this.targets_ = targets;
+};
+
+
+/** @private {boolean} */
+Build.prototype.cacheResults_ = false;
+
+
+/**
+ * Configures this build to only execute if it has not previously been
+ * run during the life of the current process.
+ * @return {!Build} A self reference.
+ */
+Build.prototype.onlyOnce = function() {
+ this.cacheResults_ = true;
+ return this;
+};
+
+
+/**
+ * Executes the build.
+ * @return {!webdriver.promise.Promise} A promise that will be resolved when
+ * the build has completed.
+ * @throws {Error} If no targets were specified.
+ */
+Build.prototype.go = function() {
+ var targets = this.targets_;
+ if (!targets.length) {
+ throw Error('No targets specified');
+ }
+
+ // Filter out cached results.
+ if (this.cacheResults_) {
+ targets = targets.filter(function(target) {
+ return !builtTargets.hasOwnProperty(target);
+ });
+
+ if (!targets.length) {
+ return promise.fulfilled();
+ }
+ }
+
+ console.log('\nBuilding', targets.join(' '), '...');
+
+ var cmd, args = targets;
+ if (process.platform === 'win32') {
+ cmd = 'cmd.exe';
+ args.unshift('/c', path.join(projectRoot, 'go.bat'));
+ } else {
+ cmd = path.join(projectRoot, 'go');
+ }
+
+ var result = promise.defer();
+ spawn(cmd, args, {
+ cwd: projectRoot,
+ env: process.env,
+ stdio: ['ignore', process.stdout, process.stderr]
+ }).on('exit', function(code, signal) {
+ if (code === 0) {
+ targets.forEach(function(target) {
+ builtTargets[target] = 1;
+ });
+ return result.fulfill();
+ }
+
+ var msg = 'Unable to build artifacts';
+ if (code) { // May be null.
+ msg += '; code=' + code;
+ }
+ if (signal) {
+ msg += '; signal=' + signal;
+ }
+
+ result.reject(Error(msg));
+ });
+
+ return result.promise;
+};
+
+
+// PUBLIC API
+
+
+/**
+ * Creates a build of the listed targets.
+ * @param {...string} var_args The targets to build.
+ * @return {!Build} The new build.
+ * @throws {Error} If not running in dev mode.
+ */
+exports.of = function(var_args) {
+ var targets = Array.prototype.slice.call(arguments, 0);
+ return new Build(targets);
+};
+
+
+/**
+ * @return {string} Absolute path of the project's root directory.
+ * @throws {Error} If not running in dev mode.
+ */
+exports.projectRoot = function() {
+ checkIsDevMode();
+ return projectRoot;
+};
diff --git a/node_modules/selenium-webdriver/lib/test/data/ClickTest_testClicksASurroundingStrongTag.html b/node_modules/selenium-webdriver/lib/test/data/ClickTest_testClicksASurroundingStrongTag.html
new file mode 100644
index 000000000..3d117df3b
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/ClickTest_testClicksASurroundingStrongTag.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>ClickTest_testClicksASurroundingStrongTag</title>
+</head>
+<body>
+ <div>
+ <a href="xhtmlTest.html"><strong>Click</strong></a>
+ </div>
+</body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/Page.aspx b/node_modules/selenium-webdriver/lib/test/data/Page.aspx
new file mode 100644
index 000000000..8e3a0d4dc
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/Page.aspx
@@ -0,0 +1,17 @@
+<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Page.aspx.cs" Inherits="Page" %>
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" >
+<head runat="server">
+ <title>Untitled Page</title>
+</head>
+<body>
+ <a href="../xhtmlTest.html" target="_top">top</a>
+ <form id="form1" runat="server">
+ <div>
+
+ </div>
+ </form>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/Page.aspx.cs b/node_modules/selenium-webdriver/lib/test/data/Page.aspx.cs
new file mode 100644
index 000000000..1a8f7fe3e
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/Page.aspx.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Threading;
+
+public partial class Page : System.Web.UI.Page
+{
+ protected void Page_Load(object sender, EventArgs e)
+ {
+ Response.ContentType = "text/html";
+
+ int lastIndex = Request.PathInfo.LastIndexOf("/");
+ string pageNumber = (lastIndex == -1 ? "Unknown" : Request.PathInfo.Substring(lastIndex + 1));
+ if (!string.IsNullOrEmpty(Request.QueryString["pageNumber"]))
+ {
+ pageNumber = Request.QueryString["pageNumber"];
+ }
+ Response.Output.Write("<html><head><title>Page" + pageNumber + "</title></head>");
+ Response.Output.Write("<body>Page number <span id=\"pageNumber\">");
+ Response.Output.Write(pageNumber);
+ //Response.Output.Write("<script>var s=''; for (var i in window) {s += i + ' -> ' + window[i] + '<p>';} document.write(s);</script>")'
+ Response.Output.Write("</span></body></html>");
+ }
+}
diff --git a/node_modules/selenium-webdriver/lib/test/data/Redirect.aspx b/node_modules/selenium-webdriver/lib/test/data/Redirect.aspx
new file mode 100644
index 000000000..52d2e6786
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/Redirect.aspx
@@ -0,0 +1,11 @@
+<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Redirect.aspx.cs" Inherits="Redirect" %>
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" >
+<head runat="server">
+ <title>Untitled Page</title>
+</head>
+<body>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/Redirect.aspx.cs b/node_modules/selenium-webdriver/lib/test/data/Redirect.aspx.cs
new file mode 100644
index 000000000..9e0650bfb
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/Redirect.aspx.cs
@@ -0,0 +1,9 @@
+using System;
+
+public partial class Redirect : Page
+{
+ protected new void Page_Load(object sender, EventArgs e)
+ {
+ Response.Redirect("resultPage.html");
+ }
+}
diff --git a/node_modules/selenium-webdriver/lib/test/data/Settings.StyleCop b/node_modules/selenium-webdriver/lib/test/data/Settings.StyleCop
new file mode 100644
index 000000000..fc955f815
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/Settings.StyleCop
@@ -0,0 +1,759 @@
+<StyleCopSettings Version="4.3">
+ <Analyzers>
+ <Analyzer AnalyzerId="Microsoft.StyleCop.CSharp.DocumentationRules">
+ <Rules>
+ <Rule Name="ElementsMustBeDocumented">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="PartialElementsMustBeDocumented">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="EnumerationItemsMustBeDocumented">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DocumentationMustContainValidXml">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementDocumentationMustHaveSummary">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="PartialElementDocumentationMustHaveSummary">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementDocumentationMustHaveSummaryText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="PartialElementDocumentationMustHaveSummaryText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementDocumentationMustNotHaveDefaultSummary">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementParametersMustBeDocumented">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementParameterDocumentationMustMatchElementParameters">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementParameterDocumentationMustDeclareParameterName">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementParameterDocumentationMustHaveText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementReturnValueMustBeDocumented">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementReturnValueDocumentationMustHaveText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="VoidReturnValueMustNotBeDocumented">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="GenericTypeParametersMustBeDocumented">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="GenericTypeParametersMustBeDocumentedPartialClass">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="GenericTypeParameterDocumentationMustMatchTypeParameters">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="GenericTypeParameterDocumentationMustDeclareParameterName">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="GenericTypeParameterDocumentationMustHaveText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="PropertySummaryDocumentationMustMatchAccessors">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="PropertySummaryDocumentationMustOmitSetAccessorWithRestrictedAccess">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementDocumentationMustNotBeCopiedAndPasted">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="SingleLineCommentsMustNotUseDocumentationStyleSlashes">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DocumentationTextMustNotBeEmpty">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DocumentationTextMustContainWhitespace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DocumentationMustMeetCharacterPercentage">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DocumentationTextMustMeetMinimumCharacterLength">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ConstructorSummaryDocumentationMustBeginWithStandardText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DestructorSummaryDocumentationMustBeginWithStandardText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DocumentationHeadersMustNotContainBlankLines">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="IncludedDocumentationXPathDoesNotExist">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="IncludeNodeDoesNotContainValidFileAndPath">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileMustHaveHeader">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileHeaderMustShowCopyright">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileHeaderMustHaveCopyrightText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileHeaderMustContainFileName">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileHeaderFileNameDocumentationMustMatchFileName">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileHeaderMustHaveValidCompanyText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ </Rules>
+ <AnalyzerSettings />
+ </Analyzer>
+ <Analyzer AnalyzerId="Microsoft.StyleCop.CSharp.LayoutRules">
+ <Rules>
+ <Rule Name="CurlyBracketsForMultiLineStatementsMustNotShareLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="StatementMustNotBeOnSingleLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementMustNotBeOnSingleLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CurlyBracketsMustNotBeOmitted">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="AllAccessorsMustBeMultiLineOrSingleLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="OpeningCurlyBracketsMustNotBeFollowedByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementDocumentationHeadersMustNotBeFollowedByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CodeMustNotContainMultipleBlankLinesInARow">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ClosingCurlyBracketsMustNotBePrecededByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="OpeningCurlyBracketsMustNotBePrecededByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ChainedStatementBlocksMustNotBePrecededByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="WhileDoFooterMustNotBePrecededByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="SingleLineCommentsMustNotBeFollowedByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ClosingCurlyBracketMustBeFollowedByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementDocumentationHeaderMustBePrecededByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="SingleLineCommentMustBePrecededByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementsMustBeSeparatedByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ </Rules>
+ <AnalyzerSettings />
+ </Analyzer>
+ <Analyzer AnalyzerId="Microsoft.StyleCop.CSharp.MaintainabilityRules">
+ <Rules>
+ <Rule Name="AccessModifierMustBeDeclared">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FieldsMustBePrivate">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CodeAnalysisSuppressionMustHaveJustification">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DebugAssertMustProvideMessageText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DebugFailMustProvideMessageText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileMayOnlyContainASingleClass">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileMayOnlyContainASingleNamespace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="StatementMustNotUseUnnecessaryParenthesis">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ArithmeticExpressionsMustDeclarePrecedence">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ConditionalExpressionsMustDeclarePrecedence">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="RemoveDelegateParenthesisWhenPossible">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="RemoveUnnecessaryCode">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ </Rules>
+ <AnalyzerSettings />
+ </Analyzer>
+ <Analyzer AnalyzerId="Microsoft.StyleCop.CSharp.NamingRules">
+ <Rules>
+ <Rule Name="ElementMustBeginWithUpperCaseLetter">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementMustBeginWithLowerCaseLetter">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="InterfaceNamesMustBeginWithI">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ConstFieldNamesMustBeginWithUpperCaseLetter">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="NonPrivateReadonlyFieldsMustBeginWithUpperCaseLetter">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FieldNamesMustNotUseHungarianNotation">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FieldNamesMustBeginWithLowerCaseLetter">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="AccessibleFieldsMustBeginWithUpperCaseLetter">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="VariableNamesMustNotBePrefixed">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FieldNamesMustNotBeginWithUnderscore">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FieldNamesMustNotContainUnderscore">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ </Rules>
+ <AnalyzerSettings />
+ </Analyzer>
+ <Analyzer AnalyzerId="Microsoft.StyleCop.CSharp.OrderingRules">
+ <Rules>
+ <Rule Name="UsingDirectivesMustBePlacedWithinNamespace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementsMustAppearInTheCorrectOrder">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementsMustBeOrderedByAccess">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ConstantsMustAppearBeforeFields">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="StaticElementsMustAppearBeforeInstanceElements">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DeclarationKeywordsMustFollowOrder">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ProtectedMustComeBeforeInternal">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="PropertyAccessorsMustFollowOrder">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="EventAccessorsMustFollowOrder">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="SystemUsingDirectivesMustBePlacedBeforeOtherUsingDirectives">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="UsingAliasDirectivesMustBePlacedAfterOtherUsingDirectives">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="UsingDirectivesMustBeOrderedAlphabeticallyByNamespace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="UsingAliasDirectivesMustBeOrderedAlphabeticallyByAliasName">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ </Rules>
+ <AnalyzerSettings />
+ </Analyzer>
+ <Analyzer AnalyzerId="Microsoft.StyleCop.CSharp.ReadabilityRules">
+ <Rules>
+ <Rule Name="CommentsMustContainText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DoNotPrefixCallsWithBaseUnlessLocalImplementationExists">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="PrefixLocalCallsWithThis">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="OpeningParenthesisMustBeOnDeclarationLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ClosingParenthesisMustBeOnLineOfLastParameter">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ClosingParenthesisMustBeOnLineOfOpeningParenthesis">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CommaMustBeOnSameLineAsPreviousParameter">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ParameterListMustFollowDeclaration">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ParameterMustFollowComma">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="SplitParametersMustStartOnLineAfterDeclaration">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ParametersMustBeOnSameLineOrSeparateLines">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ParameterMustNotSpanMultipleLines">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="QueryClauseMustFollowPreviousClause">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="QueryClausesMustBeOnSeparateLinesOrAllOnOneLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="QueryClauseMustBeginOnNewLineWhenPreviousClauseSpansMultipleLines">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="QueryClausesSpanningMultipleLinesMustBeginOnOwnLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DoNotPlaceRegionsWithinElements">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CodeMustNotContainEmptyStatements">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CodeMustNotContainMultipleStatementsOnOneLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="BlockStatementsMustNotContainEmbeddedComments">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="BlockStatementsMustNotContainEmbeddedRegions">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="UseStringEmptyForEmptyStrings">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="UseBuiltInTypeAlias">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ </Rules>
+ <AnalyzerSettings />
+ </Analyzer>
+ <Analyzer AnalyzerId="Microsoft.StyleCop.CSharp.SpacingRules">
+ <Rules>
+ <Rule Name="KeywordsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CommasMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="SemicolonsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="SymbolsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DocumentationLinesMustBeginWithSingleSpace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="SingleLineCommentsMustBeginWithSingleSpace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="PreprocessorKeywordsMustNotBePrecededBySpace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="OperatorKeywordMustBeFollowedBySpace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="OpeningParenthesisMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ClosingParenthesisMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="OpeningSquareBracketsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ClosingSquareBracketsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="OpeningCurlyBracketsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ClosingCurlyBracketsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="OpeningGenericBracketsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ClosingGenericBracketsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="OpeningAttributeBracketsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ClosingAttributeBracketsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="NullableTypeSymbolsMustNotBePrecededBySpace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="MemberAccessSymbolsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="IncrementDecrementSymbolsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="NegativeSignsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="PositiveSignsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DereferenceAndAccessOfSymbolsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ColonsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CodeMustNotContainMultipleWhitespaceInARow">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CodeMustNotContainSpaceAfterNewKeywordInImplicitlyTypedArrayAllocation">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="TabsMustNotBeUsed">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ </Rules>
+ <AnalyzerSettings />
+ </Analyzer>
+ </Analyzers>
+</StyleCopSettings> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/Web.Config b/node_modules/selenium-webdriver/lib/test/data/Web.Config
new file mode 100644
index 000000000..68b648f81
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/Web.Config
@@ -0,0 +1,59 @@
+<?xml version="1.0"?>
+<!--
+ Note: As an alternative to hand editing this file you can use the
+ web admin tool to configure settings for your application. Use
+ the Website->Asp.Net Configuration option in Visual Studio.
+ A full list of settings and comments can be found in
+ machine.config.comments usually located in
+ \Windows\Microsoft.Net\Framework\v2.x\Config
+-->
+<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
+ <configSections>
+ <section name="rewriter" requirePermission="false" type="Intelligencia.UrlRewriter.Configuration.RewriterConfigurationSectionHandler, Intelligencia.UrlRewriter"/>
+ </configSections>
+ <appSettings/>
+ <connectionStrings/>
+ <system.web>
+ <!--
+ Set compilation debug="true" to insert debugging
+ symbols into the compiled page. Because this
+ affects performance, set this value to true only
+ during development.
+ -->
+ <compilation debug="true" defaultLanguage="c#" targetFramework="4.0"/>
+ <!--
+ The <authentication> section enables configuration
+ of the security authentication mode used by
+ ASP.NET to identify an incoming user.
+ -->
+ <authentication mode="Windows"/>
+ <!--
+ The <customErrors> section enables configuration
+ of what to do if/when an unhandled error occurs
+ during the execution of a request. Specifically,
+ it enables developers to configure html error pages
+ to be displayed in place of a error stack trace.
+
+ <customErrors mode="RemoteOnly" defaultRedirect="GenericErrorPage.htm">
+ <error statusCode="403" redirect="NoAccess.htm" />
+ <error statusCode="404" redirect="FileNotFound.htm" />
+ </customErrors>
+ -->
+ <httpModules>
+ <add name="UrlRewriter" type="Intelligencia.UrlRewriter.RewriterHttpModule, Intelligencia.UrlRewriter"/>
+ </httpModules>
+ <!--urlMappings enabled="true">
+ <add url="~/redirect" mappedUrl="~/Redirect.aspx" />
+ </urlMappings-->
+ <pages controlRenderingCompatibilityVersion="3.5" clientIDMode="AutoID"/></system.web>
+ <system.webServer>
+ <modules runAllManagedModulesForAllRequests="true">
+ <add name="UrlRewriter" type="Intelligencia.UrlRewriter.RewriterHttpModule"/>
+ </modules>
+ </system.webServer>
+ <rewriter>
+ <rewrite url="~/redirect" to="~/Redirect.aspx"/>
+ <rewrite url="~/page/([0-9]+)$" to="~/Page.aspx?pageNumber=$1"/>
+ <rewrite url="~/page/([0-9]+)(\?)(.*)" to="~/Page.aspx?pageNumber=$1&amp;$3"/>
+ </rewriter>
+</configuration>
diff --git a/node_modules/selenium-webdriver/lib/test/data/actualXhtmlPage.xhtml b/node_modules/selenium-webdriver/lib/test/data/actualXhtmlPage.xhtml
new file mode 100644
index 000000000..a0f54703c
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/actualXhtmlPage.xhtml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>
+ Title
+ </title>
+ </head>
+ <body>
+ <p>
+ <a href="/foo">Foo</a>
+ </p>
+ </body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/ajaxy_page.html b/node_modules/selenium-webdriver/lib/test/data/ajaxy_page.html
new file mode 100644
index 000000000..4b34031d5
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/ajaxy_page.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<html>
+<body>
+<form action="javascript:updateDom()">
+ <label for="typer">New label text:</label>
+ <input name="typer" type="text"/>
+ <br/>
+ <label for="color">Select label color:</label>
+ <input name="color" id="red" value="red" type="radio"/>Red
+ <input name="color" id="green" value="green" type="radio"/>Green
+ <br/>
+ <input name="submit" type="submit" value="Add Label"/>
+</form>
+<div id="update_butter" style="display:none"></div>
+<script>
+ var butter = document.getElementById('update_butter');
+
+ var textProperty = butter['innerText'] ? 'innerText' : 'textContent';
+
+ var listeners = [];
+ function registerListener(fn) {
+ listeners.push(fn);
+ }
+
+ function updateDom() {
+ butter.style.display = 'block';
+ butter[textProperty] = 'Updating';
+ disableForm();
+ tick();
+ }
+
+ function disableForm() {
+ var inputs = document.getElementsByTagName('input');
+ for (var i = 0, input; input = inputs[i]; ++i) {
+ input.disabled = true;
+ }
+ }
+
+ function enableForm() {
+ var inputs = document.getElementsByTagName('input');
+ for (var i = 0, input; input = inputs[i]; ++i) {
+ input.disabled = false;
+ }
+ }
+
+ function tick() {
+ var length = butter[textProperty].substring('Updating'.length).length;
+ if (length != 10) {
+ butter[textProperty] += '.';
+ window.setTimeout(tick, 500);
+ } else {
+ enableForm();
+ var div = document.createElement('div');
+ var colors = document.forms[0].color;
+ for (var i = 0, color; color = colors[i]; ++i) {
+ if (color.checked) {
+ div.style.backgroundColor = color.value;
+ break;
+ }
+ }
+ div[textProperty] = document.forms[0].typer.value;
+ div.className = 'label';
+ document.forms[0].typer.value = '';
+ document.body.appendChild(div);
+
+ butter[textProperty] = 'Done!';
+
+ window.setTimeout(function() {
+ while (listeners.length) {
+ try {
+ listeners.shift()(div[textProperty]);
+ } catch (e) {
+ butter[textProperty] = "Exception seen: " + e;
+ }
+ }
+ }, 500);
+ }
+ }
+</script>
+</body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/alerts.html b/node_modules/selenium-webdriver/lib/test/data/alerts.html
new file mode 100644
index 000000000..1add0db99
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/alerts.html
@@ -0,0 +1,85 @@
+<html>
+<!-- Padding to account for small screens of mobile devices -->
+<style>
+ p {margin-top:48px;}
+</style>
+<head>
+ <title>Testing Alerts</title>
+
+ <script type="text/javascript">
+ function setInnerText(id, value) {
+ document.getElementById(id).innerHTML = '<p>' + value + '</p>';
+ }
+
+ function displayPrompt() {
+ setInnerText('text', prompt('Enter something'));
+ }
+
+ function displayPromptWithDefault() {
+ setInnerText('text', prompt('Enter something', 'This is a default value'));
+ }
+
+ function displayTwoPrompts() {
+ setInnerText('text1', prompt('First'));
+ setInnerText('text2', prompt('Second'));
+ }
+
+ function slowAlert() {
+ window.setTimeout(function() {
+ alert('Slow');
+ }, 200);
+ }
+ </script>
+</head>
+<body>
+
+<h1>Testing Alerts and Stuff</h1>
+
+<p>This tests alerts: <a href="#" id="alert" onclick="alert('cheese');">click me</a></p>
+
+<p>This tests alerts: <a href="#" id="empty-alert" onclick="alert('');">click me</a></p>
+
+<p>Let's make the <a href="#" id="prompt" onclick="displayPrompt();">prompt happen</a></p>
+
+<p>Let's make the <a href="#" id="prompt-with-default" onclick="displayPromptWithDefault();">prompt with default happen</a></p>
+
+<p>Let's make TWO <a href="#" id="double-prompt" onclick="displayTwoPrompts();">prompts happen</a></p>
+
+<p>A <a href="#" id="slow-alert" onclick="slowAlert();">SLOW</a> alert</p>
+
+<p>This is a test of a confirm:
+ <a href="simpleTest.html" id="confirm" onclick="return confirm('Are you sure?');">test confirm</a></p>
+
+<p>This is a test of showModalDialog: <a href="#" id="dialog" onclick="showModalDialog('javascriptPage.html')">test dialog</a></p>
+
+<p>This is a test of an alert in an iframe:
+ <iframe src="iframeWithAlert.html" name="iframeWithAlert"></iframe>
+</p>
+
+<p>This is a test of an alert in a nested iframe:
+ <iframe src="iframeWithIframe.html" name="iframeWithIframe"></iframe>
+</p>
+
+<p>This is a test of an alert open from onload event handler: <a id="open-page-with-onload-alert" href="pageWithOnLoad.html">open new page</a></p>
+
+<p>This is a test of an alert open from onload event handler: <a id="open-window-with-onload-alert" href="pageWithOnLoad.html" target="onload">open new window</a></p>
+
+<p>This is a test of an alert open from onunload event handler: <a id="open-page-with-onunload-alert" href="pageWithOnUnload.html">open new page</a></p>
+
+<p>This is a test of an alert open from onclose event handler: <a id="open-window-with-onclose-alert" href="pageWithOnUnload.html" target="onclose">open new window</a></p>
+
+<p>This is a test of an alert open from onclose event handler: <a id="open-new-window" href="blank.html" target="newwindow">open new window</a></p>
+
+<div id="text"></div>
+<div id="text1"></div>
+<div id="text2"></div>
+
+<p><select id="select" onchange="alert('changed');">
+ <option id="novalue" value="">Nothing selected</option>
+ <option id="value1" value="1">One</option>
+ <option id="value2" value="2">Two</option>
+ <option id="value3" value="3">Three</option>
+</select></p>
+<p><input id="input" onchange="alert('change fired');" value="onchange"/></p>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/banner.gif b/node_modules/selenium-webdriver/lib/test/data/banner.gif
new file mode 100644
index 000000000..3f3435401
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/banner.gif
Binary files differ
diff --git a/node_modules/selenium-webdriver/lib/test/data/beach.jpg b/node_modules/selenium-webdriver/lib/test/data/beach.jpg
new file mode 100644
index 000000000..402237cbd
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/beach.jpg
Binary files differ
diff --git a/node_modules/selenium-webdriver/lib/test/data/blank.html b/node_modules/selenium-webdriver/lib/test/data/blank.html
new file mode 100644
index 000000000..c3f376e76
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/blank.html
@@ -0,0 +1 @@
+<html><head><title>blank</title></head><body></body></html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/bodyTypingTest.html b/node_modules/selenium-webdriver/lib/test/data/bodyTypingTest.html
new file mode 100644
index 000000000..f2b1939f1
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/bodyTypingTest.html
@@ -0,0 +1,41 @@
+<?xml version="1.0"?>
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title>Testing Typing into body</title>
+ <script type="text/javascript">
+ function appendMessage(message) {
+ document.getElementById('result').innerHTML += message + " ";
+ }
+
+ function appendMessageFromBody(message) {
+ document.getElementById('body_result').innerHTML += message + " ";
+ }
+ </script>
+</head>
+
+<body onkeypress="appendMessageFromBody('keypress')">
+ <h1>Type Stuff</h1>
+
+ <div id="result">
+ &nbsp;
+ </div>
+
+ <div id="body_result">
+ &nbsp;
+ </div>
+
+
+ <!-- Textbox - to confuse some browsers. -->
+
+ <form action="resultPage.html" id="on-form">
+ <input id="intext"
+ onfocus="appendMessage('focus')"
+ onkeydown="appendMessage('keydown')"
+ onkeypress="appendMessage('keypress')"
+ onkeyup="appendMessage('keyup')"
+ onblur="appendMessage('blur')"
+ onchange="appendMessage('change')"
+ />
+ </form>
+</body>
+
diff --git a/node_modules/selenium-webdriver/lib/test/data/booleanAttributes.html b/node_modules/selenium-webdriver/lib/test/data/booleanAttributes.html
new file mode 100644
index 000000000..16fbbe9f8
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/booleanAttributes.html
@@ -0,0 +1,19 @@
+<html>
+<head>
+ <title>Elements with boolean attributes</title>
+</head>
+<body>
+<form method="get" action="resultPage.html" name="required">
+ <input type="text" id="working"/>
+ <input type="email" id="emailRequired" required/>
+ <input type="text" id="inputRequired" value="Example text" required=""/>
+ <textarea id="textAreaRequired" rows="5" cols="5" required="false">Example text</textarea>
+ <textarea id="emptyTextAreaRequired" rows="5" cols="5" required="required"></textarea>
+</form>
+
+<!-- Empty div to test GetAttribute -->
+<div id="wallace" class="gromit"></div>
+<!-- Div to test boolean attributes -->
+<div id="unwrappable" nowrap>Unwrappable text</div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/child/childPage.html b/node_modules/selenium-webdriver/lib/test/data/child/childPage.html
new file mode 100644
index 000000000..9192b54a4
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/child/childPage.html
@@ -0,0 +1,8 @@
+<html>
+<head>
+ <title>Depth one child page</title>
+</head>
+<body>
+ <p>I'm a page in a child directory</p>
+</body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/child/grandchild/grandchildPage.html b/node_modules/selenium-webdriver/lib/test/data/child/grandchild/grandchildPage.html
new file mode 100644
index 000000000..f52685e06
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/child/grandchild/grandchildPage.html
@@ -0,0 +1,8 @@
+<html>
+<head>
+ <title>Depth two child page</title>
+</head>
+<body>
+ <p>I'm a page in a grandchild directory! How cute!</p>
+</body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/clickEventPage.html b/node_modules/selenium-webdriver/lib/test/data/clickEventPage.html
new file mode 100644
index 000000000..8e0355d94
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/clickEventPage.html
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title>Testing click events</title>
+ <script type="text/javascript">
+ function displayEvent(event) {
+ var keys = ['x', 'y', 'clientX', 'clientY', 'pageX', 'pageY', 'screenX', 'screenY', 'shiftKey', 'ctrlKey'];
+ var message = "<ul>";
+ for (var i = 0; i < keys.length; i++) {
+ message += "<li>" + keys[i] + ": <span id='" + keys[i] + "'>" + event[keys[i]] + "</span></li>";
+ }
+ message += "</ul>";
+ document.getElementById('result').innerHTML = message;
+ }
+ </script>
+</head>
+<body>
+ <div id="eventish" onclick="displayEvent(event);">
+ Click me to view my coordinates
+ </div>
+
+ <div id="result" style="width:300;height:60">
+ <p>&nbsp;</p>
+ </div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/click_frames.html b/node_modules/selenium-webdriver/lib/test/data/click_frames.html
new file mode 100644
index 000000000..bd055c7c7
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/click_frames.html
@@ -0,0 +1,10 @@
+<html>
+<head>
+ <title>click frames</title>
+</head>
+<frameset rows="*" border="1">
+ <frame src="clicks.html" name="source" scrolling="NO" noresize>
+ <frame src="xhtmlTest.html" name="target">
+</frameset>
+</frameset>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/click_jacker.html b/node_modules/selenium-webdriver/lib/test/data/click_jacker.html
new file mode 100644
index 000000000..0ff3900ec
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/click_jacker.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>click-jacking</title>
+ <script>
+ var clickJacker;
+
+ function setOpacity(opacity) {
+ var matches = window.navigator.userAgent.match(/MSIE\s*(\d*)/);
+ if (matches && matches.length > 1 && parseInt(matches[1]) <= 8) {
+ clickJacker.style.filter = 'alpha(opacity=' + (opacity * 100) + ')';
+ } else {
+ clickJacker.style.opacity = opacity;
+ }
+ }
+
+ function init() {
+ clickJacker = document.getElementById('clickJacker');
+ setOpacity(0);
+ }
+ </script>
+</head>
+<body onload="init()">
+<div>
+ <div id="clickJacker"
+ onclick="setOpacity(1);"
+ style="position:absolute;float:left;
+ width:200px;height:100px; padding:10px;
+ background-color:darkred;
+ border:1px solid darkred;">Click jacked!</div>
+ <div style="width:200px; height:100px;
+ border:1px solid black; padding:10px">Click Me</div>
+ <script>
+ clickJacker = document.getElementById('clickJacker');
+ </script>
+</div>
+</body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/click_out_of_bounds.html b/node_modules/selenium-webdriver/lib/test/data/click_out_of_bounds.html
new file mode 100644
index 000000000..8a51659b2
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/click_out_of_bounds.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<body>
+
+<table>
+ <tr>
+
+ <td>
+ <div style="width:752px;"></div>
+ </td>
+
+ <td>
+ <div style="width:350px; text-align: center;">
+ <form onsubmit="return false;" method="get">
+ <input id="button" value="Click me" type="submit">
+ </form>
+ </div>
+ </td>
+ </tr>
+</table>
+
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/click_out_of_bounds_overflow.html b/node_modules/selenium-webdriver/lib/test/data/click_out_of_bounds_overflow.html
new file mode 100644
index 000000000..15ac17f92
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/click_out_of_bounds_overflow.html
@@ -0,0 +1,85 @@
+<!DOCTYPE html>
+<html><head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<body>
+<div style="height: 100px; overflow: auto;">
+ <table>
+ <tbody>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td>data</td></tr>
+ <tr><td><a href="#clicked" id="link">click me</a></td></tr>
+ </tbody>
+</table>
+</div>
+</body></html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/click_rtl.html b/node_modules/selenium-webdriver/lib/test/data/click_rtl.html
new file mode 100644
index 000000000..e84fffa99
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/click_rtl.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<title>RTL test</title>
+<style type="text/css">
+ .test { font-size: 150%; }
+ table td { border: 1px solid #CCC; }
+ img { margin: 10px; }
+</style>
+</head>
+<body>
+<div dir="rtl">
+
+<div class="test">مفتاح<a href="clicks.html" id="ar_link"> معايير</a> الويب</div>
+
+<div class="test">פעילות<a id="hb_link" href="clicks.html"> הבינאום </a></div>
+
+</div>
+
+</body></html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/click_source.html b/node_modules/selenium-webdriver/lib/test/data/click_source.html
new file mode 100644
index 000000000..22e9319a5
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/click_source.html
@@ -0,0 +1,18 @@
+<html>
+<head>
+ <title>Click Source</title>
+</head>
+<body>
+ <a href="simpleTest.html" target="target" id="otherframe">I go to a target</a>
+</body>
+</html>
+
+<html>
+<head>
+ <title>Click Source</title>
+</head>
+<body>
+ <a href="simpleTest.html" target="target" id="otherframe">I go to a target</a>
+</body>
+</html>
+ \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/click_tests/click_iframe.html b/node_modules/selenium-webdriver/lib/test/data/click_tests/click_iframe.html
new file mode 100644
index 000000000..7b749bc04
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/click_tests/click_iframe.html
@@ -0,0 +1,6 @@
+<html>
+<head>
+ <title>click iframe</title>
+</head>
+<body><a id="link" href="submitted_page.html" target="_top">Click me</a></body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/click_tests/click_in_iframe.html b/node_modules/selenium-webdriver/lib/test/data/click_tests/click_in_iframe.html
new file mode 100644
index 000000000..60b1ccab1
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/click_tests/click_in_iframe.html
@@ -0,0 +1,8 @@
+<html>
+<head>
+ <title>click in iframe</title>
+</head>
+<body>
+<iframe id="ifr" src="click_iframe.html" style="margin: 200px; width: 100px; height: 50px;" />
+</body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/click_tests/disappearing_element.html b/node_modules/selenium-webdriver/lib/test/data/click_tests/disappearing_element.html
new file mode 100644
index 000000000..4386d5d4b
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/click_tests/disappearing_element.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>An element that disappears on click</title>
+ <style>
+#under {
+ position: absolute;
+ top: 20;
+ left: 20;
+ width: 100px;
+ height: 100px;
+ background-color: white;
+}
+
+#over {
+ position: absolute;
+ top: 20;
+ left: 20;
+ width: 100px;
+ height: 100px;
+ background-color: red;
+ opacity: 0.5;
+}
+
+#log {
+ position: absolute;
+ top: 120px;
+}
+ </style>
+</head>
+<body id="body">
+ <div id="under"><p id="contents">Hello</p></div>
+ <div id="over"></div>
+ <div id="log">
+ <p>Log:<p>
+ </div>
+<script>
+var byId = document.getElementById.bind(document);
+
+var log = byId("log");
+
+function handler(ev) {
+ log.innerHTML += "<p></p>";
+ log.lastElementChild.textContent = ev.type + " in " + ev.target.id + " (handled by " + ev.currentTarget.id + ")\n";
+}
+
+var under = byId("under");
+var over = byId("over");
+var body = document.body;
+
+var types = ["click", "mousedown", "mouseup"];
+for (var i = 0, type; (type = types[i]); ++i) {
+ under.addEventListener(type, handler);
+ over.addEventListener(type, handler);
+ body.addEventListener(type, handler);
+}
+over.addEventListener("mousedown", function () {
+ over.style.display = "none";
+})
+</script>
+</body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/click_tests/google_map.html b/node_modules/selenium-webdriver/lib/test/data/click_tests/google_map.html
new file mode 100644
index 000000000..eb2e556c9
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/click_tests/google_map.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Google Image Map</title>
+</head>
+<body>
+<h1>Google Image Map</h1>
+<img id="google_logo" src="google_map.png" usemap="#google_map" border="0" width="364" height="126"/>
+<map id="google_map" name="google_map">
+<area id="rectG" shape="rect" coords="0,0,90,100" href="mapped_page1.html" alt="area 1"/>
+<area id="circleO" shape="circle" coords="120,60,30" href="mapped_page2.html" alt="area 2"/>
+<area id="polyLE" shape="poly" coords="280,0,310,0,360,30,360,90,280,90" href="mapped_page3.html" alt="area 3"/>
+</map>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/click_tests/google_map.png b/node_modules/selenium-webdriver/lib/test/data/click_tests/google_map.png
new file mode 100644
index 000000000..763f56279
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/click_tests/google_map.png
Binary files differ
diff --git a/node_modules/selenium-webdriver/lib/test/data/click_tests/html5_submit_buttons.html b/node_modules/selenium-webdriver/lib/test/data/click_tests/html5_submit_buttons.html
new file mode 100644
index 000000000..a8fc6ca54
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/click_tests/html5_submit_buttons.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>HTML5 Submit Buttons</title>
+</head>
+<body>
+<form id="form1" action="submitted_page.html">
+<label for="name">Enter your name: </label><input type="text" name="name" id="name"/>
+<button id="internal_explicit_submit" type="submit">Explicit Submit</button>
+<button id="internal_implicit_submit">Implicit Submit</button>
+<button type="submit"><span id="internal_span_submit">Spanned Submit</span></button>
+</form>
+<button id="external_explicit_submit" type="submit" form="form1">Explicit Submit</button>
+<button id="external_implicit_submit" form="form1">Implicit Submit</button>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/click_tests/issue5237.html b/node_modules/selenium-webdriver/lib/test/data/click_tests/issue5237.html
new file mode 100644
index 000000000..464fa1138
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/click_tests/issue5237.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Sample page for issue 5237</title>
+</head>
+<body>
+ <iframe id="search" src="issue5237_frame.html"></iframe>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/click_tests/issue5237_frame.html b/node_modules/selenium-webdriver/lib/test/data/click_tests/issue5237_frame.html
new file mode 100644
index 000000000..d6f4caf12
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/click_tests/issue5237_frame.html
@@ -0,0 +1 @@
+<a id="submit" href="#" onclick="window.top.location = 'issue5237_target.html'; return false;">Continue</a> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/click_tests/issue5237_target.html b/node_modules/selenium-webdriver/lib/test/data/click_tests/issue5237_target.html
new file mode 100644
index 000000000..cbc16e851
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/click_tests/issue5237_target.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Target page for issue 5237</title>
+</head>
+<body>
+<h1>Test passed</h1>
+</map>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/click_tests/link_that_wraps.html b/node_modules/selenium-webdriver/lib/test/data/click_tests/link_that_wraps.html
new file mode 100644
index 000000000..04434364f
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/click_tests/link_that_wraps.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>Link that continues on next line</title>
+</head>
+<body>
+ <div style="width:300px">
+ <div style="float:left;width:200px;background:red">placeholder</div><a id="link" href="submitted_page.html">Link that continues on next line</a>
+ </div>
+</body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/click_tests/mapped_page1.html b/node_modules/selenium-webdriver/lib/test/data/click_tests/mapped_page1.html
new file mode 100644
index 000000000..245f0385b
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/click_tests/mapped_page1.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Target Page 1</title>
+</head>
+<body>
+ <div>Target Page 1</div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/click_tests/mapped_page2.html b/node_modules/selenium-webdriver/lib/test/data/click_tests/mapped_page2.html
new file mode 100644
index 000000000..6f9636c5c
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/click_tests/mapped_page2.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Target Page 2</title>
+</head>
+<body>
+ <div>Target Page 2</div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/click_tests/mapped_page3.html b/node_modules/selenium-webdriver/lib/test/data/click_tests/mapped_page3.html
new file mode 100644
index 000000000..87a35f388
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/click_tests/mapped_page3.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Target Page 3</title>
+</head>
+<body>
+ <div>Target Page 3</div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/click_tests/overlapping_elements.html b/node_modules/selenium-webdriver/lib/test/data/click_tests/overlapping_elements.html
new file mode 100644
index 000000000..6cfa56ade
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/click_tests/overlapping_elements.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>An element that disappears on click</title>
+ <style>
+#under {
+ position: absolute;
+ top: 20px;
+ left: 20px;
+ width: 100px;
+ height: 100px;
+ background-color: white;
+}
+
+#partially_under {
+ position: absolute;
+ top: 20px;
+ left: 10px;
+ width: 100px;
+ height: 100px;
+ background-color: blue;
+ opacity: 0.5;
+}
+
+#over {
+ position: absolute;
+ top: 20px;
+ left: 20px;
+ width: 100px;
+ height: 100px;
+ background-color: red;
+ opacity: 0.5;
+}
+
+#log {
+ position: absolute;
+ top: 120px;
+}
+ </style>
+</head>
+<body id="body">
+ <div id="under"><p id="contents">Hello</p></div>
+ <div id="partially_under"><p id="other_contents">Hello</p></div>
+ <div id="over"></div>
+ <div id="log">
+ <p>Log:<p>
+ </div>
+<script>
+var byId = document.getElementById.bind(document);
+
+var log = byId("log");
+
+function handler(ev) {
+ log.innerHTML += "<p></p>";
+ log.lastElementChild.textContent = ev.type + " in " + ev.target.id + " (handled by " + ev.currentTarget.id + ")\n";
+}
+
+var under = byId("under");
+var over = byId("over");
+var body = document.body;
+
+var types = ["click", "mousedown", "mouseup"];
+for (var i = 0, type; (type = types[i]); ++i) {
+ under.addEventListener(type, handler);
+ over.addEventListener(type, handler);
+ body.addEventListener(type, handler);
+}
+</script>
+</body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/click_tests/partially_overlapping_elements.html b/node_modules/selenium-webdriver/lib/test/data/click_tests/partially_overlapping_elements.html
new file mode 100644
index 000000000..2af62526e
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/click_tests/partially_overlapping_elements.html
@@ -0,0 +1,124 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>An element that disappears on click</title>
+ <style>
+#under_under {
+ position: absolute;
+ top: 20px;
+ left: 20px;
+ width: 100px;
+ height: 100px;
+ background-color: white;
+}
+
+#under {
+ position: absolute;
+ top: 20px;
+ left: 20px;
+ width: 100px;
+ height: 100px;
+ background-color: blue;
+ opacity: 0.1;
+}
+
+#over1 {
+ position: absolute;
+ top: 10px;
+ left: 10px;
+ width: 50px;
+ height: 50px;
+ background-color: red;
+ opacity: 0.2;
+}
+
+#over2 {
+ position: absolute;
+ top: 80px;
+ left: 10px;
+ width: 50px;
+ height: 50px;
+ background-color: red;
+ opacity: 0.2;
+}
+
+#over3 {
+ position: absolute;
+ top: 10px;
+ left: 80px;
+ width: 50px;
+ height: 50px;
+ background-color: red;
+ opacity: 0.2;
+}
+
+#over4 {
+ position: absolute;
+ top: 80px;
+ left: 80px;
+ width: 50px;
+ height: 50px;
+ background-color: red;
+ opacity: 0.2;
+}
+
+#over5 {
+ position: absolute;
+ top: 45px;
+ left: 45px;
+ width: 50px;
+ height: 50px;
+ background-color: red;
+ opacity: 0.2;
+}
+
+#log {
+ position: absolute;
+ top: 120px;
+}
+ </style>
+</head>
+<body id="body">
+ <div id="under_under"><p id="contents">Hello</p></div>
+ <div id="under"><p id="other_contents">Hello</p></div>
+ <div id="over1"></div>
+ <div id="over2"></div>
+ <div id="over3"></div>
+ <div id="over4"></div>
+ <div id="over5"></div>
+ <div id="log">
+ <p>Log:<p>
+ </div>
+<script>
+var byId = document.getElementById.bind(document);
+
+var log = byId("log");
+
+function handler(ev) {
+ log.innerHTML += "<p></p>";
+ log.lastElementChild.textContent = ev.type + " in " + ev.target.id + " (handled by " + ev.currentTarget.id + ")\n";
+}
+
+var under_under = byId("under_under");
+var under = byId("under");
+var over1 = byId("over1");
+var over2 = byId("over2");
+var over3 = byId("over3");
+var over4 = byId("over4");
+var over5 = byId("over5");
+var body = document.body;
+
+var types = ["click", "mousedown", "mouseup"];
+for (var i = 0, type; (type = types[i]); ++i) {
+ under_under.addEventListener(type, handler);
+ under.addEventListener(type, handler);
+ over1.addEventListener(type, handler);
+ over2.addEventListener(type, handler);
+ over3.addEventListener(type, handler);
+ over4.addEventListener(type, handler);
+ over5.addEventListener(type, handler);
+ body.addEventListener(type, handler);
+}
+</script>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/click_tests/span_that_wraps.html b/node_modules/selenium-webdriver/lib/test/data/click_tests/span_that_wraps.html
new file mode 100644
index 000000000..77a9d6d50
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/click_tests/span_that_wraps.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>Link that continues on next line</title>
+</head>
+<body>
+ <div style="width:300px">
+ <div style="float:left;width:200px;background:red">placeholder</div><span id="span" onclick="document.location='submitted_page.html'">Span that continues on next line</span>
+ </div>
+</body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/click_tests/submitted_page.html b/node_modules/selenium-webdriver/lib/test/data/click_tests/submitted_page.html
new file mode 100644
index 000000000..0ed2cbacb
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/click_tests/submitted_page.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Submitted Successfully!</title>
+</head>
+<body>
+<h1>Submitted Successfully!</h1>
+</body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/click_tests/wrapped_overlapping_elements.html b/node_modules/selenium-webdriver/lib/test/data/click_tests/wrapped_overlapping_elements.html
new file mode 100644
index 000000000..1c0c3d03e
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/click_tests/wrapped_overlapping_elements.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>A wrapped element with overlapped first part</title>
+</head>
+<body id="body">
+<div style="width:300px">
+ <div style="float:left;width:200px;background:green;">placeholder</div>
+ <div style="float:left; position:absolute;width:100px;margin-left:200px;background:red;opacity:0.5;">Over</div>
+ <a id="link" href="submitted_page.html">Link that continues on next line</a>
+</div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/click_too_big.html b/node_modules/selenium-webdriver/lib/test/data/click_too_big.html
new file mode 100644
index 000000000..568ee77eb
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/click_too_big.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<body>
+ <a id="click" href="clicks.html" target="_parent">
+ <div style="width: 10001px; height: 10001px; background-color: green;">
+ &nbsp; &nbsp; &nbsp;
+ </div>
+ </a>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/click_too_big_in_frame.html b/node_modules/selenium-webdriver/lib/test/data/click_too_big_in_frame.html
new file mode 100644
index 000000000..cda990ed8
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/click_too_big_in_frame.html
@@ -0,0 +1,11 @@
+<html>
+<head>
+ <title>This page has iframes</title>
+</head>
+<body>
+<h1 id="iframe_page_heading">This is the heading</h1>
+
+<iframe src="click_too_big.html" marginheight="0" marginwidth="0" topmargin="0" leftmargin="0" allowtransparency="true"
+ frameborder="0" height="10001" scrolling="no" width="10001" id="iframe1" name="iframe1-name" />
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/clicks.html b/node_modules/selenium-webdriver/lib/test/data/clicks.html
new file mode 100644
index 000000000..dc07fee6a
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/clicks.html
@@ -0,0 +1,35 @@
+<html>
+<head>
+ <title>clicks</title>
+</head>
+<body>
+<h1>Testing Clicks</h1>
+
+<iframe id="source" src="click_source.html">
+
+</iframe>
+
+<iframe id="target" name="target">
+
+</iframe>
+<br>
+<a href="xhtmlTest.html" id="normal">I'm a normal link</a>
+<br/>
+<a href="#" id="anchor">I go to an anchor</a>
+<br>
+<a href="javascript:window.open('xhtmlTest.html', '_blank')" id="new-window">I open a window with javascript</a>
+<br/>
+<a href="xhtmlTest.html" id="twoClientRects"><span></span><span>Click me</span></a>
+<br/>
+<a href="xhtmlTest.html" id="link-with-enclosed-image"><img id="enclosed-image" src="./icon.gif"/></a>
+<br/>
+<a href="xhtmlTest.html" id="link-with-enclosed-span"><span id="enclosed-span" style="color: rgb(0, 255, 0)">I'm a green link</span></a>
+<p style="background: none repeat scroll 0% 0% rgb(0, 255, 0); width: 5em;"><a id="overflowLink" href="xhtmlTest.html">looooooooooong short looooooooooong</a>
+ </p>
+<div id="bubblesTo" onclick="window.bubbledClick = true;">
+ <a id="bubblesFrom">I bubble</a>
+</div>
+<a href="xhtmlTest.html">333333</a>
+<p><a href="xhtmlTest.html" id="embeddedBlock"><span style="display: block;">I have a span</span><div>And a div</div><span>And another span</span></a></p>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/closeable_window.html b/node_modules/selenium-webdriver/lib/test/data/closeable_window.html
new file mode 100644
index 000000000..e64c599c9
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/closeable_window.html
@@ -0,0 +1,8 @@
+<html>
+<head>
+<title>closeable window</title>
+</head>
+<body>
+This window can be closed by clicking on <a id="close" onclick="window.setTimeout(function() { window.close();}, 0);" href="#">this</a>.
+</body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/cn-test.html b/node_modules/selenium-webdriver/lib/test/data/cn-test.html
new file mode 100644
index 000000000..df846ad46
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/cn-test.html
@@ -0,0 +1,156 @@
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
+<style type="text/css">
+
+
+
+ /* default css */
+
+ table {
+ font-size: 1em;
+ line-height: inherit;
+ }
+
+
+ div, address, ol, ul, li, option, select {
+ margin-top: 0px;
+ margin-bottom: 0px;
+ }
+
+ p {
+ margin: 0px;
+ }
+
+ body {
+
+
+ margin: 0px;
+
+
+
+ padding-left: 50px;
+ padding-right: 50px;
+ padding-top: 40px;
+
+
+ }
+
+ h6 { font-size: 10pt }
+ h5 { font-size: 11pt }
+ h4 { font-size: 12pt }
+ h3 { font-size: 13pt }
+ h2 { font-size: 14pt }
+ h1 { font-size: 16pt }
+
+ blockquote {padding: 10px; border: 1px #DDD dashed }
+
+ a img {border: 0}
+
+ div.google_header, div.google_footer {
+ position: relative;
+ margin-top: 1em;
+ margin-bottom: 1em;
+ }
+ /* end default css */
+
+
+ /* default print css */
+
+ @media print {
+ body {
+ padding: 0;
+ margin: 0;
+ }
+
+ div.google_header, div.google_footer {
+ display: block;
+ min-height: 0;
+ border: none;
+ }
+
+ div.google_header {
+ flow: static(header);
+ }
+
+ /* used to insert page numbers */
+ div.google_header::before, div.google_footer::before {
+ position: absolute;
+ top: 0;
+ }
+
+ div.google_footer {
+ flow: static(footer);
+ }
+
+ /* always consider this element at the start of the doc */
+ div#google_footer {
+ flow: static(footer, start);
+ }
+
+ span.google_pagenumber {
+ content: counter(page);
+ }
+
+ span.google_pagecount {
+ content: counter(pages);
+ }
+ }
+
+ @page {
+ @top {
+ content: flow(header);
+ }
+ @bottom {
+ content: flow(footer);
+ }
+ }
+ /* end default print css */
+
+
+ /* custom css */
+
+
+ /* end custom css */
+
+ /* ui edited css */
+
+ body {
+ font-family: Verdana;
+
+ font-size: 10.0pt;
+ line-height: normal;
+ background-color: #ffffff;
+ }
+ /* end ui edited css */
+
+
+
+
+</style>
+
+
+</head>
+
+<body revision="cczv65wb_54f62cc9f2:3">
+
+<div id="result">
+</div>
+
+
+<h1>չ2008ƣӿ </h1><br>
+<br>
+ 882008˻ᵹʱһף찲Ź㳡СͼΪףеݳ »
+
+ ӿġҪһԤ⣬ҪԽһʶ<br>
+<br>
+<a href=simpleTest.html id=b7v9 title=й֮>й֮</a><br>
+<br>
+<br>
+
+<form action="simpleTest.html">
+ <input name="i18n" onchange="document.getElementById('result').innerHTML = '<p>' + this.value + '</p>';" />
+</form>
+
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/colorPage.html b/node_modules/selenium-webdriver/lib/test/data/colorPage.html
new file mode 100644
index 000000000..0d1bfc0af
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/colorPage.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Color Page</title>
+</head>
+<body>
+<div id="namedColor" style="background-color: green;">namedColor</div>
+<div id="rgb" style="background-color: rgb(0,128,0);">rgb</div>
+<div id="rgbpct" style="background-color: rgb(0%,50%,0%);">rgbpct</div>
+<div id="hex" style="background-color: #008000;">hex</div>
+<div id="hexShort" style="background-color: #eee;">hex</div>
+<div id="hsl" style="background-color: hsl(120,100%,25%);">hsl</div>
+<div id="rgba" style="background-color: rgba(0,128,0, 0.5);">rgba</div>
+<div id="rgbapct" style="background-color: rgba(0%,50%,0%, 0.5);">rgba</div>
+<div id="hsla" style="background-color: hsla(120,100%,25%,0.5);">hsla</div>
+</body>
+</html>
+
+
+
diff --git a/node_modules/selenium-webdriver/lib/test/data/cookies.html b/node_modules/selenium-webdriver/lib/test/data/cookies.html
new file mode 100644
index 000000000..7db5b4931
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/cookies.html
@@ -0,0 +1,30 @@
+<html>
+<head>
+ <title>Testing cookies</title>
+
+ <script type="text/javascript">
+ function setCookie(domain, name) {
+ document.cookie = name + "=ok;path=/;domain=" + domain;
+ }
+
+ function showCookie() {
+ document.getElementById("result").innerHTML = "<p>" + document.cookie + "</p>";
+ }
+ </script>
+</head>
+<body onload="showCookie();">
+<h2>Cookie Mashing</h2>
+ .com <a href="#" onclick="setCookie('.com', 'the.com_one'); showCookie(); return false;">Click</a></br />
+ . <a href="#" onclick="setCookie('.', 'the.one'); showCookie(); return false;">Click</a></br />
+ google.com <a href="#" onclick="setCookie('google.com', 'google'); showCookie(); return false;">Click</a></br />
+ .google.com <a href="#" onclick="setCookie('.google.com', '.google'); showCookie(); return false;">Click</a></br />
+ 127.0.0.1 <a href="#" onclick="setCookie('127.0.0.1', 'localhost'); showCookie(); return false;">Click</a></br />
+ localhost:3001 <a href="#" onclick="setCookie('mency.ad.corp.google.com:62210', 'with_port'); showCookie(); return false;">Click</a></br />
+ .google:3001 <a href="#" onclick="setCookie('.google.com:62210', 'with_domain_and_port'); showCookie(); return false;">Click</a></br />
+ 172.16.12.225 <a href="#" onclick="setCookie('172.16.12.225', 'raw_IP'); showCookie(); return false;">Click</a></br />
+ 172.16.12.225:port <a href="#" onclick="setCookie('172.16.12.225:62210', 'raw_IP_and_port'); showCookie(); return false;">Click</a></br />
+ <a href="#" onclick="document.cookie = 'foo=bar;path=/common/galaxy';">Set on a different path</a>
+
+<div id="result"></div>
+</body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/coordinates_tests/element_in_frame.html b/node_modules/selenium-webdriver/lib/test/data/coordinates_tests/element_in_frame.html
new file mode 100644
index 000000000..7714a48ad
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/coordinates_tests/element_in_frame.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Welcome Page</title>
+</head>
+<body style="margin: 10px; padding: 0px;">
+ <iframe name="ifr" src="simple_page.html" width="400" height="300" style="border: 5px solid;"></iframe>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/coordinates_tests/element_in_nested_frame.html b/node_modules/selenium-webdriver/lib/test/data/coordinates_tests/element_in_nested_frame.html
new file mode 100644
index 000000000..b3143b0d6
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/coordinates_tests/element_in_nested_frame.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Welcome Page</title>
+</head>
+<body style="margin: 10px; padding: 0px;">
+ <iframe name="ifr" src="element_in_frame.html" width="500" height="400" style="border: 5px solid;"></iframe>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/coordinates_tests/page_with_element_out_of_view.html b/node_modules/selenium-webdriver/lib/test/data/coordinates_tests/page_with_element_out_of_view.html
new file mode 100644
index 000000000..6f2bcd4f2
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/coordinates_tests/page_with_element_out_of_view.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Page With Element Out Of View</title>
+</head>
+<body style="margin: 10px; padding: 0px;">
+ <div style="height: 5000px;">Placeholder</div>
+ <div id="box" style="width: 100px; height: 100px; background-color: red;">Red box</div>
+ <div>Tex after box</div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/coordinates_tests/page_with_empty_element.html b/node_modules/selenium-webdriver/lib/test/data/coordinates_tests/page_with_empty_element.html
new file mode 100644
index 000000000..b07972abd
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/coordinates_tests/page_with_empty_element.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Page With Empty Element</title>
+</head>
+<body style="margin: 10px; padding: 0px;">
+ <div id="box"></div>
+ <div>Tex after box</div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/coordinates_tests/page_with_fixed_element.html b/node_modules/selenium-webdriver/lib/test/data/coordinates_tests/page_with_fixed_element.html
new file mode 100644
index 000000000..6cbb2738e
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/coordinates_tests/page_with_fixed_element.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Page With Fixed Element</title>
+</head>
+<body>
+ <div id="fixed" style="position:fixed; top:0px; left:100px; background-color:red">fixed red box</div>
+ <div id="placeholder" style="height: 5000px; background-color:green">Placeholder</div>
+ <div id="bottom">Element at the bottom</div>
+ <div>Tex after box</div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/coordinates_tests/page_with_hidden_element.html b/node_modules/selenium-webdriver/lib/test/data/coordinates_tests/page_with_hidden_element.html
new file mode 100644
index 000000000..286b04b17
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/coordinates_tests/page_with_hidden_element.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Page With Hidden Element</title>
+</head>
+<body style="margin: 10px; padding: 0px;">
+ <div id="box" style="width: 100px; height: 100px; background-color: red; visibility: hidden;">Hidden box</div>
+ <div>Tex after box</div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/coordinates_tests/page_with_invisible_element.html b/node_modules/selenium-webdriver/lib/test/data/coordinates_tests/page_with_invisible_element.html
new file mode 100644
index 000000000..dc33c7185
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/coordinates_tests/page_with_invisible_element.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Page With Invisible Element</title>
+</head>
+<body style="margin: 10px; padding: 0px;">
+ <div id="box" style="width: 100px; height: 100px; background-color: red; display: none;">Invisible box</div>
+ <div>Tex after box</div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/coordinates_tests/page_with_transparent_element.html b/node_modules/selenium-webdriver/lib/test/data/coordinates_tests/page_with_transparent_element.html
new file mode 100644
index 000000000..d0090d921
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/coordinates_tests/page_with_transparent_element.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Page With Transparent Element</title>
+</head>
+<body style="margin: 10px; padding: 0px;">
+ <div id="box" style="width: 100px; height: 100px; background-color: red; opacity: 0;">Hidden box</div>
+ <div>Tex after box</div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/coordinates_tests/simple_page.html b/node_modules/selenium-webdriver/lib/test/data/coordinates_tests/simple_page.html
new file mode 100644
index 000000000..7b857b9df
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/coordinates_tests/simple_page.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Simple Page</title>
+</head>
+<body style="margin: 10px; padding: 0px;">
+ <div id="box" style="width: 100px; height: 100px; background-color: red;">Red box</div>
+ <div>Tex after box</div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png b/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png
new file mode 100644
index 000000000..954e22dbd
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png
Binary files differ
diff --git a/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png b/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png
new file mode 100644
index 000000000..64ece5707
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png
Binary files differ
diff --git a/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-bg_flat_10_000000_40x100.png b/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-bg_flat_10_000000_40x100.png
new file mode 100644
index 000000000..abdc01082
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-bg_flat_10_000000_40x100.png
Binary files differ
diff --git a/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png b/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png
new file mode 100644
index 000000000..9b383f4d2
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png
Binary files differ
diff --git a/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png b/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png
new file mode 100644
index 000000000..a23baad25
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png
Binary files differ
diff --git a/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png b/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png
new file mode 100644
index 000000000..42ccba269
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png
Binary files differ
diff --git a/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-bg_gloss-wave_35_f6a828_500x100.png b/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-bg_gloss-wave_35_f6a828_500x100.png
new file mode 100644
index 000000000..39d5824d6
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-bg_gloss-wave_35_f6a828_500x100.png
Binary files differ
diff --git a/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png b/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png
new file mode 100644
index 000000000..f1273672d
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png
Binary files differ
diff --git a/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.png b/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.png
new file mode 100644
index 000000000..359397acf
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.png
Binary files differ
diff --git a/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-icons_222222_256x240.png b/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-icons_222222_256x240.png
new file mode 100644
index 000000000..b273ff111
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-icons_222222_256x240.png
Binary files differ
diff --git a/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-icons_228ef1_256x240.png b/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-icons_228ef1_256x240.png
new file mode 100644
index 000000000..a641a371a
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-icons_228ef1_256x240.png
Binary files differ
diff --git a/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-icons_ef8c08_256x240.png b/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-icons_ef8c08_256x240.png
new file mode 100644
index 000000000..85e63e9f6
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-icons_ef8c08_256x240.png
Binary files differ
diff --git a/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-icons_ffd27a_256x240.png b/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-icons_ffd27a_256x240.png
new file mode 100644
index 000000000..e117effa3
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-icons_ffd27a_256x240.png
Binary files differ
diff --git a/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-icons_ffffff_256x240.png b/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-icons_ffffff_256x240.png
new file mode 100644
index 000000000..42f8f992c
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/images/ui-icons_ffffff_256x240.png
Binary files differ
diff --git a/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/jquery-ui-1.8.10.custom.css b/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/jquery-ui-1.8.10.custom.css
new file mode 100644
index 000000000..1706e2207
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/css/ui-lightness/jquery-ui-1.8.10.custom.css
@@ -0,0 +1,573 @@
+/*
+ * jQuery UI CSS Framework 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Theming/API
+ */
+
+/* Layout helpers
+----------------------------------*/
+.ui-helper-hidden { display: none; }
+.ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); }
+.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
+.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+.ui-helper-clearfix { display: inline-block; }
+/* required comment for clearfix to work in Opera \*/
+* html .ui-helper-clearfix { height:1%; }
+.ui-helper-clearfix { display:block; }
+/* end clearfix */
+.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
+
+
+/* Interaction Cues
+----------------------------------*/
+.ui-state-disabled { cursor: default !important; }
+
+
+/* Icons
+----------------------------------*/
+
+/* states and images */
+.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
+
+
+/* Misc visuals
+----------------------------------*/
+
+/* Overlays */
+.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
+
+
+/*
+ * jQuery UI CSS Framework 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Theming/API
+ *
+ * To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Trebuchet%20MS,%20Tahoma,%20Verdana,%20Arial,%20sans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=f6a828&bgTextureHeader=12_gloss_wave.png&bgImgOpacityHeader=35&borderColorHeader=e78f08&fcHeader=ffffff&iconColorHeader=ffffff&bgColorContent=eeeeee&bgTextureContent=03_highlight_soft.png&bgImgOpacityContent=100&borderColorContent=dddddd&fcContent=333333&iconColorContent=222222&bgColorDefault=f6f6f6&bgTextureDefault=02_glass.png&bgImgOpacityDefault=100&borderColorDefault=cccccc&fcDefault=1c94c4&iconColorDefault=ef8c08&bgColorHover=fdf5ce&bgTextureHover=02_glass.png&bgImgOpacityHover=100&borderColorHover=fbcb09&fcHover=c77405&iconColorHover=ef8c08&bgColorActive=ffffff&bgTextureActive=02_glass.png&bgImgOpacityActive=65&borderColorActive=fbd850&fcActive=eb8f00&iconColorActive=ef8c08&bgColorHighlight=ffe45c&bgTextureHighlight=03_highlight_soft.png&bgImgOpacityHighlight=75&borderColorHighlight=fed22f&fcHighlight=363636&iconColorHighlight=228ef1&bgColorError=b81900&bgTextureError=08_diagonals_thick.png&bgImgOpacityError=18&borderColorError=cd0a0a&fcError=ffffff&iconColorError=ffd27a&bgColorOverlay=666666&bgTextureOverlay=08_diagonals_thick.png&bgImgOpacityOverlay=20&opacityOverlay=50&bgColorShadow=000000&bgTextureShadow=01_flat.png&bgImgOpacityShadow=10&opacityShadow=20&thicknessShadow=5px&offsetTopShadow=-5px&offsetLeftShadow=-5px&cornerRadiusShadow=5px
+ */
+
+
+/* Component containers
+----------------------------------*/
+.ui-widget { font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif; font-size: 1.1em; }
+.ui-widget .ui-widget { font-size: 1em; }
+.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif; font-size: 1em; }
+.ui-widget-content { border: 1px solid #dddddd; background: #eeeeee url(images/ui-bg_highlight-soft_100_eeeeee_1x100.png) 50% top repeat-x; color: #333333; }
+.ui-widget-content a { color: #333333; }
+.ui-widget-header { border: 1px solid #e78f08; background: #f6a828 url(images/ui-bg_gloss-wave_35_f6a828_500x100.png) 50% 50% repeat-x; color: #ffffff; font-weight: bold; }
+.ui-widget-header a { color: #ffffff; }
+
+/* Interaction states
+----------------------------------*/
+.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #cccccc; background: #f6f6f6 url(images/ui-bg_glass_100_f6f6f6_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #1c94c4; }
+.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #1c94c4; text-decoration: none; }
+.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #fbcb09; background: #fdf5ce url(images/ui-bg_glass_100_fdf5ce_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #c77405; }
+.ui-state-hover a, .ui-state-hover a:hover { color: #c77405; text-decoration: none; }
+.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #fbd850; background: #ffffff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #eb8f00; }
+.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #eb8f00; text-decoration: none; }
+.ui-widget :active { outline: none; }
+
+/* Interaction Cues
+----------------------------------*/
+.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #fed22f; background: #ffe45c url(images/ui-bg_highlight-soft_75_ffe45c_1x100.png) 50% top repeat-x; color: #363636; }
+.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636; }
+.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cd0a0a; background: #b81900 url(images/ui-bg_diagonals-thick_18_b81900_40x40.png) 50% 50% repeat; color: #ffffff; }
+.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #ffffff; }
+.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #ffffff; }
+.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; }
+.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
+.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
+
+/* Icons
+----------------------------------*/
+
+/* states and images */
+.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png); }
+.ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); }
+.ui-widget-header .ui-icon {background-image: url(images/ui-icons_ffffff_256x240.png); }
+.ui-state-default .ui-icon { background-image: url(images/ui-icons_ef8c08_256x240.png); }
+.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_ef8c08_256x240.png); }
+.ui-state-active .ui-icon {background-image: url(images/ui-icons_ef8c08_256x240.png); }
+.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_228ef1_256x240.png); }
+.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_ffd27a_256x240.png); }
+
+/* positioning */
+.ui-icon-carat-1-n { background-position: 0 0; }
+.ui-icon-carat-1-ne { background-position: -16px 0; }
+.ui-icon-carat-1-e { background-position: -32px 0; }
+.ui-icon-carat-1-se { background-position: -48px 0; }
+.ui-icon-carat-1-s { background-position: -64px 0; }
+.ui-icon-carat-1-sw { background-position: -80px 0; }
+.ui-icon-carat-1-w { background-position: -96px 0; }
+.ui-icon-carat-1-nw { background-position: -112px 0; }
+.ui-icon-carat-2-n-s { background-position: -128px 0; }
+.ui-icon-carat-2-e-w { background-position: -144px 0; }
+.ui-icon-triangle-1-n { background-position: 0 -16px; }
+.ui-icon-triangle-1-ne { background-position: -16px -16px; }
+.ui-icon-triangle-1-e { background-position: -32px -16px; }
+.ui-icon-triangle-1-se { background-position: -48px -16px; }
+.ui-icon-triangle-1-s { background-position: -64px -16px; }
+.ui-icon-triangle-1-sw { background-position: -80px -16px; }
+.ui-icon-triangle-1-w { background-position: -96px -16px; }
+.ui-icon-triangle-1-nw { background-position: -112px -16px; }
+.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
+.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
+.ui-icon-arrow-1-n { background-position: 0 -32px; }
+.ui-icon-arrow-1-ne { background-position: -16px -32px; }
+.ui-icon-arrow-1-e { background-position: -32px -32px; }
+.ui-icon-arrow-1-se { background-position: -48px -32px; }
+.ui-icon-arrow-1-s { background-position: -64px -32px; }
+.ui-icon-arrow-1-sw { background-position: -80px -32px; }
+.ui-icon-arrow-1-w { background-position: -96px -32px; }
+.ui-icon-arrow-1-nw { background-position: -112px -32px; }
+.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
+.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
+.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
+.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
+.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
+.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
+.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
+.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
+.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
+.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
+.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
+.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
+.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
+.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
+.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
+.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
+.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
+.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
+.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
+.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
+.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
+.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
+.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
+.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
+.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
+.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
+.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
+.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
+.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
+.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
+.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
+.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
+.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
+.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
+.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
+.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
+.ui-icon-arrow-4 { background-position: 0 -80px; }
+.ui-icon-arrow-4-diag { background-position: -16px -80px; }
+.ui-icon-extlink { background-position: -32px -80px; }
+.ui-icon-newwin { background-position: -48px -80px; }
+.ui-icon-refresh { background-position: -64px -80px; }
+.ui-icon-shuffle { background-position: -80px -80px; }
+.ui-icon-transfer-e-w { background-position: -96px -80px; }
+.ui-icon-transferthick-e-w { background-position: -112px -80px; }
+.ui-icon-folder-collapsed { background-position: 0 -96px; }
+.ui-icon-folder-open { background-position: -16px -96px; }
+.ui-icon-document { background-position: -32px -96px; }
+.ui-icon-document-b { background-position: -48px -96px; }
+.ui-icon-note { background-position: -64px -96px; }
+.ui-icon-mail-closed { background-position: -80px -96px; }
+.ui-icon-mail-open { background-position: -96px -96px; }
+.ui-icon-suitcase { background-position: -112px -96px; }
+.ui-icon-comment { background-position: -128px -96px; }
+.ui-icon-person { background-position: -144px -96px; }
+.ui-icon-print { background-position: -160px -96px; }
+.ui-icon-trash { background-position: -176px -96px; }
+.ui-icon-locked { background-position: -192px -96px; }
+.ui-icon-unlocked { background-position: -208px -96px; }
+.ui-icon-bookmark { background-position: -224px -96px; }
+.ui-icon-tag { background-position: -240px -96px; }
+.ui-icon-home { background-position: 0 -112px; }
+.ui-icon-flag { background-position: -16px -112px; }
+.ui-icon-calendar { background-position: -32px -112px; }
+.ui-icon-cart { background-position: -48px -112px; }
+.ui-icon-pencil { background-position: -64px -112px; }
+.ui-icon-clock { background-position: -80px -112px; }
+.ui-icon-disk { background-position: -96px -112px; }
+.ui-icon-calculator { background-position: -112px -112px; }
+.ui-icon-zoomin { background-position: -128px -112px; }
+.ui-icon-zoomout { background-position: -144px -112px; }
+.ui-icon-search { background-position: -160px -112px; }
+.ui-icon-wrench { background-position: -176px -112px; }
+.ui-icon-gear { background-position: -192px -112px; }
+.ui-icon-heart { background-position: -208px -112px; }
+.ui-icon-star { background-position: -224px -112px; }
+.ui-icon-link { background-position: -240px -112px; }
+.ui-icon-cancel { background-position: 0 -128px; }
+.ui-icon-plus { background-position: -16px -128px; }
+.ui-icon-plusthick { background-position: -32px -128px; }
+.ui-icon-minus { background-position: -48px -128px; }
+.ui-icon-minusthick { background-position: -64px -128px; }
+.ui-icon-close { background-position: -80px -128px; }
+.ui-icon-closethick { background-position: -96px -128px; }
+.ui-icon-key { background-position: -112px -128px; }
+.ui-icon-lightbulb { background-position: -128px -128px; }
+.ui-icon-scissors { background-position: -144px -128px; }
+.ui-icon-clipboard { background-position: -160px -128px; }
+.ui-icon-copy { background-position: -176px -128px; }
+.ui-icon-contact { background-position: -192px -128px; }
+.ui-icon-image { background-position: -208px -128px; }
+.ui-icon-video { background-position: -224px -128px; }
+.ui-icon-script { background-position: -240px -128px; }
+.ui-icon-alert { background-position: 0 -144px; }
+.ui-icon-info { background-position: -16px -144px; }
+.ui-icon-notice { background-position: -32px -144px; }
+.ui-icon-help { background-position: -48px -144px; }
+.ui-icon-check { background-position: -64px -144px; }
+.ui-icon-bullet { background-position: -80px -144px; }
+.ui-icon-radio-off { background-position: -96px -144px; }
+.ui-icon-radio-on { background-position: -112px -144px; }
+.ui-icon-pin-w { background-position: -128px -144px; }
+.ui-icon-pin-s { background-position: -144px -144px; }
+.ui-icon-play { background-position: 0 -160px; }
+.ui-icon-pause { background-position: -16px -160px; }
+.ui-icon-seek-next { background-position: -32px -160px; }
+.ui-icon-seek-prev { background-position: -48px -160px; }
+.ui-icon-seek-end { background-position: -64px -160px; }
+.ui-icon-seek-start { background-position: -80px -160px; }
+/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
+.ui-icon-seek-first { background-position: -80px -160px; }
+.ui-icon-stop { background-position: -96px -160px; }
+.ui-icon-eject { background-position: -112px -160px; }
+.ui-icon-volume-off { background-position: -128px -160px; }
+.ui-icon-volume-on { background-position: -144px -160px; }
+.ui-icon-power { background-position: 0 -176px; }
+.ui-icon-signal-diag { background-position: -16px -176px; }
+.ui-icon-signal { background-position: -32px -176px; }
+.ui-icon-battery-0 { background-position: -48px -176px; }
+.ui-icon-battery-1 { background-position: -64px -176px; }
+.ui-icon-battery-2 { background-position: -80px -176px; }
+.ui-icon-battery-3 { background-position: -96px -176px; }
+.ui-icon-circle-plus { background-position: 0 -192px; }
+.ui-icon-circle-minus { background-position: -16px -192px; }
+.ui-icon-circle-close { background-position: -32px -192px; }
+.ui-icon-circle-triangle-e { background-position: -48px -192px; }
+.ui-icon-circle-triangle-s { background-position: -64px -192px; }
+.ui-icon-circle-triangle-w { background-position: -80px -192px; }
+.ui-icon-circle-triangle-n { background-position: -96px -192px; }
+.ui-icon-circle-arrow-e { background-position: -112px -192px; }
+.ui-icon-circle-arrow-s { background-position: -128px -192px; }
+.ui-icon-circle-arrow-w { background-position: -144px -192px; }
+.ui-icon-circle-arrow-n { background-position: -160px -192px; }
+.ui-icon-circle-zoomin { background-position: -176px -192px; }
+.ui-icon-circle-zoomout { background-position: -192px -192px; }
+.ui-icon-circle-check { background-position: -208px -192px; }
+.ui-icon-circlesmall-plus { background-position: 0 -208px; }
+.ui-icon-circlesmall-minus { background-position: -16px -208px; }
+.ui-icon-circlesmall-close { background-position: -32px -208px; }
+.ui-icon-squaresmall-plus { background-position: -48px -208px; }
+.ui-icon-squaresmall-minus { background-position: -64px -208px; }
+.ui-icon-squaresmall-close { background-position: -80px -208px; }
+.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
+.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
+.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
+.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
+.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
+.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
+
+
+/* Misc visuals
+----------------------------------*/
+
+/* Corner radius */
+.ui-corner-tl { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; }
+.ui-corner-tr { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; }
+.ui-corner-bl { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; }
+.ui-corner-br { -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; }
+.ui-corner-top { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; }
+.ui-corner-bottom { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; }
+.ui-corner-right { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; }
+.ui-corner-left { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; }
+.ui-corner-all { -moz-border-radius: 4px; -webkit-border-radius: 4px; border-radius: 4px; }
+
+/* Overlays */
+.ui-widget-overlay { background: #666666 url(images/ui-bg_diagonals-thick_20_666666_40x40.png) 50% 50% repeat; opacity: .50;filter:Alpha(Opacity=50); }
+.ui-widget-shadow { margin: -5px 0 0 -5px; padding: 5px; background: #000000 url(images/ui-bg_flat_10_000000_40x100.png) 50% 50% repeat-x; opacity: .20;filter:Alpha(Opacity=20); -moz-border-radius: 5px; -webkit-border-radius: 5px; border-radius: 5px; }/*
+ * jQuery UI Resizable 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Resizable#theming
+ */
+.ui-resizable { position: relative;}
+.ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block;}
+.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; }
+.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; }
+.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; }
+.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; }
+.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; }
+.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; }
+.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; }
+.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; }
+.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/*
+ * jQuery UI Selectable 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Selectable#theming
+ */
+.ui-selectable-helper { position: absolute; z-index: 100; border:1px dotted black; }
+/*
+ * jQuery UI Accordion 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Accordion#theming
+ */
+/* IE/Win - Fix animation bug - #4615 */
+.ui-accordion { width: 100%; }
+.ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; }
+.ui-accordion .ui-accordion-li-fix { display: inline; }
+.ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; }
+.ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em .7em; }
+.ui-accordion-icons .ui-accordion-header a { padding-left: 2.2em; }
+.ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; }
+.ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; }
+.ui-accordion .ui-accordion-content-active { display: block; }
+/*
+ * jQuery UI Autocomplete 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Autocomplete#theming
+ */
+.ui-autocomplete { position: absolute; cursor: default; }
+
+/* workarounds */
+* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */
+
+/*
+ * jQuery UI Menu 1.8.10
+ *
+ * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Menu#theming
+ */
+.ui-menu {
+ list-style:none;
+ padding: 2px;
+ margin: 0;
+ display:block;
+ float: left;
+}
+.ui-menu .ui-menu {
+ margin-top: -3px;
+}
+.ui-menu .ui-menu-item {
+ margin:0;
+ padding: 0;
+ zoom: 1;
+ float: left;
+ clear: left;
+ width: 100%;
+}
+.ui-menu .ui-menu-item a {
+ text-decoration:none;
+ display:block;
+ padding:.2em .4em;
+ line-height:1.5;
+ zoom:1;
+}
+.ui-menu .ui-menu-item a.ui-state-hover,
+.ui-menu .ui-menu-item a.ui-state-active {
+ font-weight: normal;
+ margin: -1px;
+}
+/*
+ * jQuery UI Button 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Button#theming
+ */
+.ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: visible; } /* the overflow property removes extra width in IE */
+.ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */
+button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */
+.ui-button-icons-only { width: 3.4em; }
+button.ui-button-icons-only { width: 3.7em; }
+
+/*button text element */
+.ui-button .ui-button-text { display: block; line-height: 1.4; }
+.ui-button-text-only .ui-button-text { padding: .4em 1em; }
+.ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: .4em; text-indent: -9999999px; }
+.ui-button-text-icon-primary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 1em .4em 2.1em; }
+.ui-button-text-icon-secondary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 2.1em .4em 1em; }
+.ui-button-text-icons .ui-button-text { padding-left: 2.1em; padding-right: 2.1em; }
+/* no icon support for input elements, provide padding by default */
+input.ui-button { padding: .4em 1em; }
+
+/*button icon element(s) */
+.ui-button-icon-only .ui-icon, .ui-button-text-icon-primary .ui-icon, .ui-button-text-icon-secondary .ui-icon, .ui-button-text-icons .ui-icon, .ui-button-icons-only .ui-icon { position: absolute; top: 50%; margin-top: -8px; }
+.ui-button-icon-only .ui-icon { left: 50%; margin-left: -8px; }
+.ui-button-text-icon-primary .ui-button-icon-primary, .ui-button-text-icons .ui-button-icon-primary, .ui-button-icons-only .ui-button-icon-primary { left: .5em; }
+.ui-button-text-icon-secondary .ui-button-icon-secondary, .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
+.ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
+
+/*button sets*/
+.ui-buttonset { margin-right: 7px; }
+.ui-buttonset .ui-button { margin-left: 0; margin-right: -.3em; }
+
+/* workarounds */
+button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */
+/*
+ * jQuery UI Dialog 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Dialog#theming
+ */
+.ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; }
+.ui-dialog .ui-dialog-titlebar { padding: .4em 1em; position: relative; }
+.ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .1em 0; }
+.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; }
+.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; }
+.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; }
+.ui-dialog .ui-dialog-content { position: relative; border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; }
+.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; }
+.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { float: right; }
+.ui-dialog .ui-dialog-buttonpane button { margin: .5em .4em .5em 0; cursor: pointer; }
+.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; }
+.ui-draggable .ui-dialog-titlebar { cursor: move; }
+/*
+ * jQuery UI Slider 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Slider#theming
+ */
+.ui-slider { position: relative; text-align: left; }
+.ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; }
+.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; }
+
+.ui-slider-horizontal { height: .8em; }
+.ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; }
+.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; }
+.ui-slider-horizontal .ui-slider-range-min { left: 0; }
+.ui-slider-horizontal .ui-slider-range-max { right: 0; }
+
+.ui-slider-vertical { width: .8em; height: 100px; }
+.ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; }
+.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; }
+.ui-slider-vertical .ui-slider-range-min { bottom: 0; }
+.ui-slider-vertical .ui-slider-range-max { top: 0; }/*
+ * jQuery UI Tabs 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Tabs#theming
+ */
+.ui-tabs { position: relative; padding: .2em; zoom: 1; } /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */
+.ui-tabs .ui-tabs-nav { margin: 0; padding: .2em .2em 0; }
+.ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 1px; margin: 0 .2em 1px 0; border-bottom: 0 !important; padding: 0; white-space: nowrap; }
+.ui-tabs .ui-tabs-nav li a { float: left; padding: .5em 1em; text-decoration: none; }
+.ui-tabs .ui-tabs-nav li.ui-tabs-selected { margin-bottom: 0; padding-bottom: 1px; }
+.ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; }
+.ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */
+.ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 1.4em; background: none; }
+.ui-tabs .ui-tabs-hide { display: none !important; }
+/*
+ * jQuery UI Datepicker 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Datepicker#theming
+ */
+.ui-datepicker { width: 17em; padding: .2em .2em 0; display: none; }
+.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; }
+.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; }
+.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; }
+.ui-datepicker .ui-datepicker-prev { left:2px; }
+.ui-datepicker .ui-datepicker-next { right:2px; }
+.ui-datepicker .ui-datepicker-prev-hover { left:1px; }
+.ui-datepicker .ui-datepicker-next-hover { right:1px; }
+.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; }
+.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; }
+.ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; }
+.ui-datepicker select.ui-datepicker-month-year {width: 100%;}
+.ui-datepicker select.ui-datepicker-month,
+.ui-datepicker select.ui-datepicker-year { width: 49%;}
+.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; }
+.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; }
+.ui-datepicker td { border: 0; padding: 1px; }
+.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; }
+.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; }
+.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
+.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; }
+
+/* with multiple calendars */
+.ui-datepicker.ui-datepicker-multi { width:auto; }
+.ui-datepicker-multi .ui-datepicker-group { float:left; }
+.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; }
+.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; }
+.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; }
+.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; }
+.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; }
+.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; }
+.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; }
+.ui-datepicker-row-break { clear:both; width:100%; }
+
+/* RTL support */
+.ui-datepicker-rtl { direction: rtl; }
+.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; }
+.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; }
+.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; }
+.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; }
+.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; }
+.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; }
+.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; }
+.ui-datepicker-rtl .ui-datepicker-group { float:right; }
+.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
+.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
+
+/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */
+.ui-datepicker-cover {
+ display: none; /*sorry for IE5*/
+ display/**/: block; /*sorry for IE5*/
+ position: absolute; /*must have*/
+ z-index: -1; /*must have*/
+ filter: mask(); /*must have*/
+ top: -4px; /*must have*/
+ left: -4px; /*must have*/
+ width: 200px; /*must have*/
+ height: 200px; /*must have*/
+}/*
+ * jQuery UI Progressbar 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Progressbar#theming
+ */
+.ui-progressbar { height:2em; text-align: left; }
+.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; } \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/cssTransform.html b/node_modules/selenium-webdriver/lib/test/data/cssTransform.html
new file mode 100644
index 000000000..c3b99648a
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/cssTransform.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<style>
+#parentY {
+ transform: translateY(-10000px);
+ -webkit-transform: translateY(-10000px);
+ -o-transform: translateY(-10000px);
+ -ms-transform: translateY(-10000px);
+ -moz-transform: translateY(-10000px);
+}
+#parentX {
+ transform: translateX(-10000px);
+ -webkit-transform: translateX(-10000px);
+ -o-transform: translateX(-10000px);
+ -ms-transform: translateX(-10000px);
+ -moz-transform: translateX(-10000px);
+}
+#transformX {
+ transform: translateX(-10000px);
+ -webkit-transform: translateX(-10000px);
+ -o-transform: translateX(-10000px);
+ -ms-transform: translateX(-10000px);
+ -moz-transform: translateX(-10000px);
+}
+#transformY {
+ transform: translateY(-10000px);
+ -webkit-transform: translateY(-10000px);
+ -o-transform: translateY(-10000px);
+ -ms-transform: translateY(-10000px);
+ -moz-transform: translateY(-10000px);
+}
+
+#zero-transform {
+ transform: translateY(0px);
+ -webkit-transform: translateY(0px);
+ -o-transform: translateY(0px);
+ -ms-transform: translateY(0px);
+ -moz-transform: translateY(0px);
+ transform: translateX(0px);
+ -webkit-transform: translateX(0px);
+ -o-transform: translateX(0px);
+ -ms-transform: translateX(0px);
+ -moz-transform: translateX(0px);
+}
+</style>
+<div id='zero-tranform'>
+You shouldn't see anything other than this sentence on the page
+</div>
+<div id='parentY'>
+ I have a hidden child
+ <div id='childY'>
+ I am a hidden child
+ </div>
+</div>
+<div id='parentX'>
+ I have a hidden child
+ <div id='childX'>
+ I am a hidden child
+ </div>
+</div>
+<div id='transformX'>I am a hidden element </div>
+<div id='transformY'>I am a hidden element </div>
diff --git a/node_modules/selenium-webdriver/lib/test/data/cssTransform2.html b/node_modules/selenium-webdriver/lib/test/data/cssTransform2.html
new file mode 100644
index 000000000..602924bfb
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/cssTransform2.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<style>
+#negative-percentage-transformY{
+ transform: translateY(-75px);
+ -webkit-transform: translateY(-75%);
+ -o-transform: translateY(-75%);
+ -ms-transform: translateY(-75%);
+ -moz-transform: translateY(-75%);
+}
+.block {
+ display = block;
+}
+</style>
+<div class='block'>
+ <br/>
+</div>
+ <br/>
+<div class='block'>
+</div>
+<div id='negative-percentage-transformY'>I am not a hidden element </div>
diff --git a/node_modules/selenium-webdriver/lib/test/data/document_write_in_onload.html b/node_modules/selenium-webdriver/lib/test/data/document_write_in_onload.html
new file mode 100644
index 000000000..a15fc479e
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/document_write_in_onload.html
@@ -0,0 +1,13 @@
+<html>
+<head>
+ <title>Document Write In Onload</title>
+ <script>
+ function init() {
+ document.writeln('goodbye, world!');
+ }
+ </script>
+</head>
+<body onload="init();">
+<p>hello world</p>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/dragAndDropInsideScrolledDiv.html b/node_modules/selenium-webdriver/lib/test/data/dragAndDropInsideScrolledDiv.html
new file mode 100644
index 000000000..0b2ee9a24
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/dragAndDropInsideScrolledDiv.html
@@ -0,0 +1,67 @@
+<html>
+<head>
+<style>
+<!--
+.dragme{position:relative;}
+-->
+</style>
+<script language="JavaScript1.2">
+<!--
+
+var ie=document.all;
+var nn6=document.getElementById&&!document.all;
+
+var isdrag=false;
+var x,y;
+var dobj;
+
+function movemouse(e)
+{
+
+ if (isdrag)
+ {
+ dobj.style.left = nn6 ? tx + e.clientX - x : tx + event.clientX - x;
+ dobj.style.top = nn6 ? ty + e.clientY - y : ty + event.clientY - y;
+ return false;
+ }
+}
+
+function selectmouse(e)
+{
+ var fobj = nn6 ? e.target : event.srcElement;
+ var topelement = nn6 ? "HTML" : "BODY";
+
+ while (fobj.tagName != topelement && fobj.className != "dragme")
+ {
+ fobj = nn6 ? fobj.parentNode : fobj.parentElement;
+ }
+
+ if (fobj.className=="dragme")
+ {
+ isdrag = true;
+ dobj = fobj;
+ tx = parseInt(dobj.style.left+0);
+ ty = parseInt(dobj.style.top+0);
+ x = nn6 ? e.clientX : event.clientX;
+ y = nn6 ? e.clientY : event.clientY;
+ document.onmousemove=movemouse;
+ return false;
+ }
+}
+
+document.onmousedown=selectmouse;
+document.onmouseup=new Function("isdrag=false");
+
+//-->
+</script>
+
+</head>
+<body>
+ <div style="overflow: scroll; margin: 20px; height: 90%; width: 90%">
+ <div style="height: 4000px; width: 4000px;">
+ <div id="test1" class="dragme" style="width: 100px; height: 100px;
+ background-color: black;" />
+ </div>
+ </div>
+</body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/dragAndDropTest.html b/node_modules/selenium-webdriver/lib/test/data/dragAndDropTest.html
new file mode 100644
index 000000000..fdee16b0b
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/dragAndDropTest.html
@@ -0,0 +1,102 @@
+<html>
+<body>
+
+<style>
+<!--
+.dragme{position:relative;}
+-->
+</style>
+<script language="JavaScript1.2">
+<!--
+
+var ie=document.all;
+var nn6=document.getElementById&&!document.all;
+
+var isdrag=false;
+var x,y;
+var dobj;
+
+function movemouse(e)
+{
+ if (isdrag)
+ {
+ if (e && e.clientX)
+ {
+ dobj.style.left = tx + e.clientX - x;
+ dobj.style.top = ty + e.clientY - y
+ }
+ else
+ {
+ dobj.style.left = tx + event.clientX - x;
+ dobj.style.top = ty + event.clientY - y;
+ }
+ return false;
+ }
+}
+
+function selectmouse(e)
+{
+ var fobj;
+ var topelement;
+ if (e && e.target)
+ {
+ fobj = e.target;
+ topelement = "HTML";
+ }
+ else
+ {
+ fobj = event.srcElement;
+ topelement = "BODY";
+ }
+
+ while (fobj.tagName != topelement && fobj.className != "dragme")
+ {
+ if (fobj.parentNode)
+ {
+ fobj = fobj.parentNode;
+ }
+ else
+ {
+ fobj = fobj.parentElement;
+ }
+ }
+
+ if (fobj.className=="dragme")
+ {
+ isdrag = true;
+ dobj = fobj;
+ tx = parseInt(dobj.style.left+0);
+ ty = parseInt(dobj.style.top+0);
+ if (e && e.clientX)
+ {
+ x = e.clientX;
+ y = e.clientY;
+ }
+ else
+ {
+ x = event.clientX;
+ y = event.clientY;
+ }
+
+ document.onmousemove=movemouse;
+ return false;
+ }
+}
+
+document.onmousedown=selectmouse;
+document.onmouseup=new Function("isdrag=false");
+
+//-->
+</script>
+
+
+
+<img src="icon.gif" class="dragme" id="test1"><br>
+<img src="icon.gif" class="dragme" id="test2"><br>
+<b>"Hi there</b>
+<div style="position: absolute; left: 210px; top: 80px; height: 400px; width: 100px; padding: 10em;">
+<img src="icon.gif" class="dragme" id="test3"><br>
+<img src="icon.gif" class="dragme" id="test4"><br>
+</div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/dragDropOverflow.html b/node_modules/selenium-webdriver/lib/test/data/dragDropOverflow.html
new file mode 100644
index 000000000..ecb25625d
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/dragDropOverflow.html
@@ -0,0 +1,104 @@
+<html>
+<head>
+ <meta content="text/html; charset=UTF-8" http-equiv="content-type">
+ <style type="text/css">
+ body {
+ color: #222;
+ font-size: 13px;
+ }
+
+ .time-slot {
+ height: 47px;
+ vertical-align: top;
+ border-top: 1px solid #ddd;
+ padding-right: 2px;
+ z-index: 2;
+ }
+
+ #time-marker {
+ border-top: 3px solid #4d90fe;
+ height: 6px;
+ position: absolute;
+ width: 100%;
+ z-index: 4;
+ cursor: row-resize;
+ }
+ </style>
+</head>
+<body>
+<div style="position: absolute; overflow: hidden; width: 250px; height: 470px;">
+ <div style="position: relative;">
+ <div style="overflow: auto; position: relative; height: 85%; border: 1px solid #ebebeb">
+ <div id="time-marker" style="top: 432;"></div>
+ <div class="time-slot">12am</div>
+ <div class="time-slot">1am</div>
+ <div class="time-slot">2am</div>
+ <div class="time-slot">3am</div>
+ <div class="time-slot">4am</div>
+ <div class="time-slot">5am</div>
+ <div class="time-slot">6am</div>
+ <div class="time-slot">7am</div>
+ <div class="time-slot">8am</div>
+ <div class="time-slot">9am</div>
+ <div class="time-slot">10am</div>
+ <div id="11am" class="time-slot">11am</div>
+ <div class="time-slot">12pm</div>
+ <div class="time-slot">1pm</div>
+ <div class="time-slot">2pm</div>
+ <div class="time-slot">3pm</div>
+ <div class="time-slot">4pm</div>
+ <div class="time-slot">5pm</div>
+ <div class="time-slot">6pm</div>
+ <div class="time-slot">7pm</div>
+ <div class="time-slot">8pm</div>
+ <div class="time-slot">9pm</div>
+ <div class="time-slot">10pm</div>
+ <div class="time-slot">11pm</div>
+ </div>
+ </div>
+</div>
+<script type="text/javascript">
+ var startTime = document.getElementById('time-marker');
+
+ var ie = document.all;
+ var nn6 = document.getElementById && !document.all;
+
+ var isDrag = false;
+ var x, y, tx, ty;
+ var dragEl;
+
+ function movemouse(e) {
+ if (isDrag) {
+ var dy = (nn6 ? e.clientY - y : event.clientY - y);
+ if (dy % 12 == 0) {
+ dragEl.style.top = ty + dy;
+ }
+ return false;
+ }
+ }
+
+ function selectmouse(e) {
+ var srcEl = nn6 ? e.target : event.srcElement;
+ var topElement = nn6 ? "HTML" : "BODY";
+
+ while (srcEl.tagName != topElement && srcEl != startTime) {
+ srcEl = nn6 ? srcEl.parentNode : srcEl.parentElement;
+ }
+
+ if (srcEl === startTime) {
+ isDrag = true;
+ dragEl = srcEl;
+ tx = parseInt(dragEl.style.left + 0);
+ ty = parseInt(dragEl.style.top + 0);
+ x = nn6 ? e.clientX : event.clientX;
+ y = nn6 ? e.clientY : event.clientY;
+ document.onmousemove = movemouse;
+ return false;
+ }
+ }
+
+ document.onmousedown = selectmouse;
+ document.onmouseup = function() { isDrag = false; };
+</script>
+</body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/draggableLists.html b/node_modules/selenium-webdriver/lib/test/data/draggableLists.html
new file mode 100644
index 000000000..f7e0dcace
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/draggableLists.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8" />
+ <title>jQuery UI Sortable - Connect lists</title>
+ <!--link type="text/css" href="development-bundle/themes/base/jquery.ui.all.css" rel="stylesheet" /-->
+ <!--link type="text/css" href="development-bundle/demos/demos.css" rel="stylesheet" /-->
+ <link type="text/css" href="css/ui-lightness/jquery-ui-1.8.10.custom.css" rel="stylesheet" />
+ <script type="text/javascript" src="js/jquery-1.4.4.min.js"></script>
+ <script type="text/javascript" src="js/jquery-ui-1.8.10.custom.min.js"></script>
+ <style type="text/css">
+ #sortable1, #sortable2 { list-style-type: none; margin: 0; padding: 0; float: left; margin-right: 10px; }
+ #sortable1 li, #sortable2 li { margin: 0 5px 5px 5px; padding: 5px; font-size: 1.2em; width: 120px; }
+ </style>
+ <script type="text/javascript">
+ $(function() {
+ $("#sortable1, #sortable2").sortable({
+ connectWith: '.connectedSortable'
+ }).disableSelection();
+
+ var report_event = function(report_text) {
+ var reportElement = $("#dragging_reports");
+ var origText = reportElement.text();
+ reportElement.text(origText + " " + report_text);
+ }
+
+ $("#sortable2").droppable({
+ out: function(event, ui) {
+ report_event("DragOut");
+ }
+ });
+
+ $("#sortable1").droppable({
+ drop: function(event, ui) {
+ report_event("DropIn " + ui.draggable.text());
+ }
+ });
+ });
+ </script>
+ </head>
+ <body>
+ <div class="demo">
+ <ul id="sortable1" class="connectedSortable">
+ <li id="leftitem-1" class="ui-state-default">LeftItem 1</li>
+ <li id="leftitem-2" class="ui-state-default">LeftItem 2</li>
+ <li id="leftitem-3" class="ui-state-default">LeftItem 3</li>
+ <li id="leftitem-4" class="ui-state-default">LeftItem 4</li>
+ <li id="leftitem-5" class="ui-state-default">LeftItem 5</li>
+ </ul>
+
+ <ul id="sortable2" class="connectedSortable">
+ <li id="rightitem-1" class="ui-state-highlight">RightItem 1</li>
+ <li id="rightitem-2" class="ui-state-highlight">RightItem 2</li>
+ <li id="rightitem-3" class="ui-state-highlight">RightItem 3</li>
+ <li id="rightitem-4" class="ui-state-highlight">RightItem 4</li>
+ <li id="rightitem-5" class="ui-state-highlight">RightItem 5</li>
+ </ul>
+
+ </div>
+
+ <br/>
+ <div class="test-data">
+ <p id="dragging_reports">Nothing happened.</p>
+ </div>
+
+ </body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/droppableItems.html b/node_modules/selenium-webdriver/lib/test/data/droppableItems.html
new file mode 100644
index 000000000..fc850ac96
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/droppableItems.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8" />
+ <title>jQuery UI Droppable - Default Demo</title>
+ <link type="text/css" href="css/ui-lightness/jquery-ui-1.8.10.custom.css" rel="stylesheet" />
+ <script type="text/javascript" src="js/jquery-1.4.4.min.js"></script>
+ <script type="text/javascript" src="js/jquery-ui-1.8.10.custom.min.js"></script>
+ <style type="text/css">
+ #draggable { width: 100px; height: 100px; padding: 0.5em; float: left; margin: 10px 10px 10px 0; }
+ #droppable { width: 150px; height: 150px; padding: 0.5em; float: left; margin: 10px; }
+ </style>
+ <script type="text/javascript">
+ $(function() {
+ $("#draggable").draggable();
+ $("#droppable").droppable({
+ drop: function(event, ui) {
+ $(this).addClass('ui-state-highlight').find('p').html('Dropped!');
+ }
+ });
+
+ var report_event = function(report_text) {
+ var reportElement = $("#drop_reports");
+ var origText = reportElement.text();
+ reportElement.text(origText + " " + report_text);
+ }
+
+ $('body').mousedown(function() {
+ report_event('down');
+ });
+
+ $('body').mousemove(function() {
+ report_event('move');
+ });
+
+ $('body').mouseup(function() {
+ report_event('up');
+ });
+ });
+ </script>
+</head>
+<body>
+<div class="demo">
+
+<div id="draggable" class="ui-widget-content">
+ <p>Drag me to my target</p>
+</div>
+
+<div id="droppable" class="ui-widget-header">
+ <p>Drop here</p>
+</div>
+
+<div class="test-data">
+ <p id="drop_reports">start</p>
+</div>
+
+</div><!-- End demo -->
+
+<div class="demo-description">
+
+<p>Taken from the JQuery demo.</p>
+
+</div><!-- End demo-description -->
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/dynamic.html b/node_modules/selenium-webdriver/lib/test/data/dynamic.html
new file mode 100644
index 000000000..b9e60678d
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/dynamic.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+ <head>
+ <title></title>
+ <script type="text/javascript">
+ var next = 0;
+
+ function addMore() {
+ var box = document.createElement('DIV');
+ box.id = 'box' + next++;
+ box.className = 'redbox';
+ box.style.width = '150px';
+ box.style.height = '150px';
+ box.style.backgroundColor = 'red';
+ box.style.border = '1px solid black';
+ box.style.margin = '5px';
+
+ window.setTimeout(function() {
+ document.body.appendChild(box);
+ }, 1000);
+ }
+
+ function reveal() {
+ var elem = document.getElementById('revealed');
+ window.setTimeout(function() {
+ elem.style.display = '';
+ }, 1000);
+ }
+ </script>
+ </head>
+ <body>
+ <input id="adder" type="button" value="Add a box!" onclick="addMore()"/>
+
+ <input id="reveal" type="button" value="Reveal a new input" onclick="reveal();" />
+
+ <input id="revealed" style="display:none;" />
+ </body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/dynamicallyModifiedPage.html b/node_modules/selenium-webdriver/lib/test/data/dynamicallyModifiedPage.html
new file mode 100644
index 000000000..ed7c7ed2b
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/dynamicallyModifiedPage.html
@@ -0,0 +1,42 @@
+<?xml version="1.0">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+ <head>
+ <title>Delayed remove of an element</title>
+
+ <script type="text/javascript">
+
+ var parentNode;
+ var elementId;
+
+ function timedRemove()
+ {
+ var t = setTimeout('removeElement()', 500);
+ t = setTimeout('insertElement()', 2000);
+ }
+
+ function removeElement()
+ {
+ var element = document.getElementById('element-to-remove');
+ elementId = element.id;
+ parentNode = element.parentNode;
+ parentNode.removeChild(element);
+ }
+
+ function insertElement()
+ {
+ var newElement = document.createElement('p');
+ newElement.setAttribute('id', elementId);
+ newElement.innerHTML = 'new element';
+ parentNode.appendChild(newElement);
+ }
+ </script>
+
+ </head>
+ <body>
+ <form>
+ <input id="buttonDelete" type="button" value="element will be removed in half a second"
+ onclick="timedRemove()"/>
+ </form>
+ <p id="element-to-remove">element</p>
+ </body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/errors.html b/node_modules/selenium-webdriver/lib/test/data/errors.html
new file mode 100644
index 000000000..78fb90207
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/errors.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <script type="text/javascript">
+ window.ERRORS = [];
+
+ window.onerror = function(e) {
+ window.ERRORS.push(e);
+ };
+ </script>
+ </head>
+ <body>
+ <input type="button" value="Throw!" onclick="throw Error('boom!');"/>
+ </body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/firefox/jetpack-sample.xpi b/node_modules/selenium-webdriver/lib/test/data/firefox/jetpack-sample.xpi
new file mode 100644
index 000000000..84d6493dd
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/firefox/jetpack-sample.xpi
Binary files differ
diff --git a/node_modules/selenium-webdriver/lib/test/data/firefox/sample.xpi b/node_modules/selenium-webdriver/lib/test/data/firefox/sample.xpi
new file mode 100644
index 000000000..062f9a172
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/firefox/sample.xpi
Binary files differ
diff --git a/node_modules/selenium-webdriver/lib/test/data/fixedFooterNoScroll.html b/node_modules/selenium-webdriver/lib/test/data/fixedFooterNoScroll.html
new file mode 100644
index 000000000..ca65d1fee
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/fixedFooterNoScroll.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Fixed footer with no scrollbar</title>
+</head>
+<body>
+ <div style="width: 100%; min-height: 100%;">
+ <div style="position: absolute; bottom: 0px;">
+ <a id="link" href="xhtmlTest.html">Click me</a>
+ </div>
+ </div>
+</body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/fixedFooterNoScrollQuirksMode.html b/node_modules/selenium-webdriver/lib/test/data/fixedFooterNoScrollQuirksMode.html
new file mode 100644
index 000000000..2593bf35c
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/fixedFooterNoScrollQuirksMode.html
@@ -0,0 +1,12 @@
+<html>
+<head>
+ <title>Fixed footer with no scrollbar</title>
+</head>
+<body>
+ <div style="width: 100%; min-height: 100%;">
+ <div style="position: absolute; bottom: 0px;">
+ <a id="link" href="xhtmlTest.html">Click me</a>
+ </div>
+ </div>
+</body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/formPage.html b/node_modules/selenium-webdriver/lib/test/data/formPage.html
new file mode 100644
index 000000000..7bcfea00f
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/formPage.html
@@ -0,0 +1,174 @@
+<html>
+<head>
+ <title>We Leave From Here</title>
+
+ <script type="text/javascript">
+ function changePage() {
+ var newLocation = '/common/page/3';
+ window.location = newLocation;
+ }
+ </script>
+</head>
+<body>
+There should be a form here:
+
+<form method="get" action="resultPage.html" name="login">
+ <input type="email" id="email"/>
+ <input type="number" id="age"/>
+ <input type="submit" id="submitButton" value="Hello there"/>
+</form>
+
+<form method="get" action="resultPage.html" name="image">
+ <input type="image" id="imageButton" alt="click me!" src="images/button.gif"/>
+</form>
+
+<form method="get" action="resultPage.html" name="optional" style="display: block">
+ Here's a checkbox:
+ <input type="checkbox" id="checky" name="checky" value="furrfu"/>
+ <input type="checkbox" id="checkedchecky" name="checkedchecky" checked="checked" />
+ <input type="checkbox" id="disabledchecky" disabled="disabled" name="disabledchecky" />
+ <input type="checkbox" id="randomly_disabled_checky" disabled="somerandomstring" checked="checked" name="randomlydisabledchecky" />
+ <br/>
+ <select name="selectomatic">
+ <option selected="selected" id="non_multi_option" value="one">One</option>
+ <option value="two">Two</option>
+ <option value="four">Four</option>
+ <option value="still learning how to count, apparently">Still learning how to count, apparently</option>
+ </select>
+
+ <select name="multi" id="multi" multiple="multiple">
+ <option selected="selected" value="eggs">Eggs</option>
+ <option value="ham">Ham</option>
+ <option selected="selected" value="sausages">Sausages</option>
+ <option value="onion gravy">Onion gravy</option>
+ </select>
+
+ <select name="no-select" disabled="disabled">
+ <option value="foo">Foo</option>
+ </select>
+
+ <select name="select_empty_multiple" multiple>
+ <option id="multi_1" value="select_1">select_1</option>
+ <option id="multi_2" value="select_2">select_2</option>
+ <option id="multi_3" value="select_3">select_3</option>
+ <option id="multi_4" value="select_4">select_4</option>
+ </select>
+
+ <select name="multi_true" multiple="true">
+ <option id="multi_true_1" value="select_1">select_1</option>
+ <option id="multi_true_2" value="select_2">select_2</option>
+ </select>
+
+ <select name="multi_false" multiple="false">
+ <option id="multi_false_1" value="select_1">select_1</option>
+ <option id="multi_false_2" value="select_2">select_2</option>
+ </select>
+
+ <select id="invisi_select" style="opacity:0;">
+ <option selected value="apples">Apples</option>
+ <option value="oranges">Oranges</option>
+ </select>
+
+ <select name="select-default">
+ <option>One</option>
+ <option>Two</option>
+ <option>Four</option>
+ <option>Still learning how to count, apparently</option>
+ </select>
+
+ <select name="select_with_spaces">
+ <option>One</option>
+ <option> Two </option>
+ <option>
+ Four
+ </option>
+ <option>
+ Still learning how to count,
+ apparently
+ </option>
+ </select>
+
+ <select>
+ <option id="blankOption"></option>
+ <option id="optionEmptyValueSet" value="">nothing</option>
+ </select>
+
+ <br/>
+
+ <input type="radio" id="cheese" name="snack" value="cheese"/>Cheese<br/>
+ <input type="radio" id="peas" name="snack" value="peas"/>Peas<br/>
+ <input type="radio" id="cheese_and_peas" name="snack" value="cheese and peas" checked/>Cheese and peas<br/>
+ <input type="radio" id="nothing" name="snack" value="nowt" disabled="disabled"/>Not a sausage<br/>
+ <input type="radio" id="randomly_disabled_nothing" name="snack" value="funny nowt" disabled="somedisablingstring"/>Not another sausage
+
+ <input type="hidden" name="hidden" value="fromage" />
+
+ <p id="cheeseLiker">I like cheese</p>
+ <input type="submit" value="Click!"/>
+
+ <input type="radio" id="lone_disabled_selected_radio" name="not_a_snack" value="cumberland" checked="checked" disabled="disabled" />Cumberland sausage
+</form>
+
+<form method="get" action="resultPage.html" name="disable">
+ <input type="text" id="working"/>
+ <input type="text" id="notWorking" disabled="true"/>
+
+ <textarea id="notWorkingArea" disabled="disabled" cols="5" rows="5"></textarea>
+
+ <input type="text" id="inputWithText" value="Example text"/>
+ <textarea id="withText" rows="5" cols="5">Example text</textarea>
+ <textarea id="emptyTextArea" rows="5" cols="5"></textarea>
+</form>
+
+<form method="post" action="resultPage.html">
+ <select id="redirect" name="redirect" onchange="javascript:changePage();">
+ <option selected="selected" value="one">One</option>
+ <option id="changeme" value="two">Two</option>
+ </select>
+
+ <input id="no-type" />
+ <input type="file" id="upload" onchange="document.getElementById('fileResults').innerHTML = 'changed';" />
+ <span id="fileResults"></span>
+
+ <input type="submit" />
+</form>
+
+<form method="get" action="resultPage.html">
+ <input type="text" value="name" name="id-name1"/>
+ <input type="text" value="id" id="id-name1"/>
+
+ <!-- Reverse the ordering -->
+ <input type="text" value="id" id="id-name2"/>
+ <input type="text" value="name" name="id-name2"/>
+ <input name="readonly" readonly="readonly" />
+</form>
+
+<!-- form with nested children -->
+<form method="get" action="resultPage.html" id="nested_form">
+ <div>
+ <input type="text" value="name" name="x"/>
+ </div>
+ <input type="submit" />
+</form>
+
+<!-- Form with disabled form elements -->
+<form method="get" action="xhtmlTest.html">
+ <p>
+ <input type="text" id="disabledTextElement1" disabled="foo" />
+ <input type="text" id="disabledTextElement2" disabled="" />
+ <input type="submit" id="disabledSubmitElement" disabled="qwerty" value="Submit" />
+ </p>
+</form>
+<!-- Empty div to test GetAttribute -->
+<div id="wallace" class="gromit"></div>
+
+<input type='button' id='killIframe' onclick='top.remove();' value="Kill containing iframe" />
+
+<form method="get" action="formPage.html">
+ <p>
+ <label for="checkbox-with-label" id="label-for-checkbox-with-label">Label</label><input type="checkbox" id="checkbox-with-label" />
+ </p>
+</form>
+<input id="vsearchGadget" name="SearchableText" type="text" size="18" value="" title="Hvad søger du?" accesskey="4" class="inputLabel" />
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/formSelectionPage.html b/node_modules/selenium-webdriver/lib/test/data/formSelectionPage.html
new file mode 100644
index 000000000..4890c08e8
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/formSelectionPage.html
@@ -0,0 +1,46 @@
+<?xml version="1.0"?>
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title>Testing Typing into body</title>
+ <script type="text/javascript">
+ function setMessage(message) {
+ document.getElementById('result').innerHTML = message;
+ }
+
+ function showSelected() {
+ var selectElement = document.getElementById('multi');
+ if (selectElement == null) {
+ appendMessage("null!");
+ }
+
+ var options_array = selectElement.getElementsByTagName('option');
+ var selected_cheese = "";
+ for (var i = 0; i < options_array.length; i++) {
+ if (options_array[i].selected) {
+ selected_cheese = selected_cheese + options_array[i].label + " ";
+ }
+ }
+ setMessage(selected_cheese);
+ }
+ </script>
+</head>
+
+<body>
+ <h1>Type Stuff</h1>
+
+ <div id="result">
+ &nbsp;
+ </div>
+
+ <form action="" id="on-form" name="multichoice">
+ <select name="multi" id="multi" multiple="multiple">
+ <option selected="selected" label="emmental">Emmental</option>
+ <option label="roquefort" >Roquefort</option>
+ <option label="parmigiano">Parmigiano</option>
+ <option label="cheddar">Cheddar</option>
+ </select>
+ </form>
+
+ <input type="button" name="showselected" onclick="showSelected()" value="Show selected"/>
+</body>
+
diff --git a/node_modules/selenium-webdriver/lib/test/data/form_handling_js_submit.html b/node_modules/selenium-webdriver/lib/test/data/form_handling_js_submit.html
new file mode 100644
index 000000000..302314392
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/form_handling_js_submit.html
@@ -0,0 +1,30 @@
+<!--
+ ~ Copyright 2012 Selenium committers
+ ~ Copyright 2012 Software Freedom Conservancy
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Form with JS action</title>
+</head>
+<body>
+ <form id="theForm" method="get" action="javascript:alert('Tasty cheese');">
+ <input name="unused" type="submit">
+ </form>
+
+ <p id="result"></p>
+</body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/framePage3.html b/node_modules/selenium-webdriver/lib/test/data/framePage3.html
new file mode 100644
index 000000000..3e62e455c
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/framePage3.html
@@ -0,0 +1,7 @@
+<html>
+ <head>
+ <title>inner</title>
+ </head>
+ <body>
+ </body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/frameScrollChild.html b/node_modules/selenium-webdriver/lib/test/data/frameScrollChild.html
new file mode 100644
index 000000000..3eb3bf47d
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/frameScrollChild.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Child frame</title>
+</head>
+<body>
+ <h1>This is a scrolling frame test</h1>
+ <div>
+ <table>
+ <tr height="50px">
+ <td>First row</td>
+ </tr>
+ <tr height="50px">
+ <td>Second row</td>
+ </tr>
+ <tr height="50px">
+ <td>Third row</td>
+ </tr>
+ <tr height="50px">
+ <td>Fourth row</td>
+ </tr>
+ </table>
+ </div>
+ <input type='checkbox' name='scroll_checkbox' />
+</body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/frameScrollPage.html b/node_modules/selenium-webdriver/lib/test/data/frameScrollPage.html
new file mode 100644
index 000000000..b7fb8f242
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/frameScrollPage.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Welcome Page</title>
+</head>
+<body>
+ <div>
+ <iframe name="scrolling_child_frame" src="frameScrollParent.html" width="800" height="200"></iframe>
+ </div>
+ <div>
+ <iframe name="scrolling_frame" src="frameScrollChild.html" width="800" height="200" ></iframe>
+ </div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/frameScrollParent.html b/node_modules/selenium-webdriver/lib/test/data/frameScrollParent.html
new file mode 100644
index 000000000..8fccb6d36
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/frameScrollParent.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Welcome Page</title>
+</head>
+<body>
+ <div>
+ <iframe name="scrolling_frame" src="frameScrollChild.html" width="600" height="500"></iframe>
+ </div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/frameWithAnimals.html b/node_modules/selenium-webdriver/lib/test/data/frameWithAnimals.html
new file mode 100644
index 000000000..1e0dc8704
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/frameWithAnimals.html
@@ -0,0 +1,11 @@
+<html>
+<head>
+ <title>This page has iframes</title>
+</head>
+<body>
+<h1 id="iframe_page_heading">This is the heading</h1>
+
+<iframe src="animals" marginheight="0" marginwidth="0" topmargin="0" leftmargin="0" allowtransparency="true"
+ frameborder="0" height="600" scrolling="no" width="120" id="iframe1" name="iframe1-name" />
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/frame_switching_tests/bug4876.html b/node_modules/selenium-webdriver/lib/test/data/frame_switching_tests/bug4876.html
new file mode 100644
index 000000000..4ed597d37
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/frame_switching_tests/bug4876.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Test issue 4876</title>
+</head>
+<body>
+<iframe id="iframe" src="bug4876_iframe.html"></iframe>
+</body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/frame_switching_tests/bug4876_iframe.html b/node_modules/selenium-webdriver/lib/test/data/frame_switching_tests/bug4876_iframe.html
new file mode 100644
index 000000000..57d47d845
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/frame_switching_tests/bug4876_iframe.html
@@ -0,0 +1,9 @@
+<html>
+ <head></head>
+ <body>
+ <form >
+ <input type="text" id="inputText" />
+ <input type="submit" id="submitButton" value="submit" onclick="window.location=window.location;" />
+ <form>
+ </body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/frame_switching_tests/deletingFrame.html b/node_modules/selenium-webdriver/lib/test/data/frame_switching_tests/deletingFrame.html
new file mode 100644
index 000000000..9c27e04c4
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/frame_switching_tests/deletingFrame.html
@@ -0,0 +1,29 @@
+<html>
+<head>
+ <title>Deleting frame: main page</title>
+</head>
+ <script type="text/javascript">
+ function remove() {
+ var iframe = document.getElementById("iframe1");
+ var myDiv = document.getElementById("myDiv");
+ myDiv.removeChild(iframe);
+ }
+ function addBack() {
+ var iframe = '<iframe src="deletingFrame_iframe2.html" marginheight="0" marginwidth="0" topmargin="0" leftmargin="0" allowtransparency="true" frameborder="0" height="200" scrolling="no" width="400" id="iframe1"></iframe>';
+ var myDiv2 = document.getElementById("myDiv2");
+ myDiv2.innerHTML = iframe;
+ }
+ </script>
+<body>
+
+<div id='myDiv'>
+<iframe src="deletingFrame_iframe.html" marginheight="0" marginwidth="0" topmargin="0" leftmargin="0" allowtransparency="true"
+ frameborder="1" height="200" scrolling="no" width="400" id="iframe1"></iframe>
+
+ </div>
+<div id='myDiv2'>
+
+</div>
+<input type='button' id='addBackFrame' value='Add back frame' onclick='addBack();' />
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/frame_switching_tests/deletingFrame_iframe.html b/node_modules/selenium-webdriver/lib/test/data/frame_switching_tests/deletingFrame_iframe.html
new file mode 100644
index 000000000..e4b9723e9
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/frame_switching_tests/deletingFrame_iframe.html
@@ -0,0 +1,8 @@
+<html>
+<head>
+ <title>Deleting frame: iframe</title>
+</head>
+<body>
+<input type='button' id='killIframe' onclick='top.remove();' value="Kill containing iframe" />
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/frame_switching_tests/deletingFrame_iframe2.html b/node_modules/selenium-webdriver/lib/test/data/frame_switching_tests/deletingFrame_iframe2.html
new file mode 100644
index 000000000..47764eb3e
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/frame_switching_tests/deletingFrame_iframe2.html
@@ -0,0 +1,7 @@
+<html>
+<head>
+ <title>Deleting frame: iframe 2</title>
+</head>
+<body>
+<div id="success">Added back</div></body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/frameset.html b/node_modules/selenium-webdriver/lib/test/data/frameset.html
new file mode 100644
index 000000000..039c5f217
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/frameset.html
@@ -0,0 +1,14 @@
+<html>
+ <head>
+ <title>Unique title</title>
+ </head>
+<frameset cols="*, *, *, *, *, *, *">
+ <frame name="first" src="page/1"/>
+ <frame name="second" src="page/2?title=Fish"/>
+ <frame name="third" src="formPage.html"/>
+ <frame name="fourth" src="framesetPage2.html"/>
+ <frame id="fifth" src="xhtmlTest.html"/>
+ <frame id="sixth" src="iframes.html"/>
+ <frame id="sixth.iframe1" src="page/3"/>
+</frameset>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/framesetPage2.html b/node_modules/selenium-webdriver/lib/test/data/framesetPage2.html
new file mode 100644
index 000000000..4ea35ff71
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/framesetPage2.html
@@ -0,0 +1,7 @@
+<html>
+<head></head>
+<frameset cols="*, *">
+ <frame name="child1" src="page/10"/>
+ <frame name="child2" src="page/11"/>
+</frameset>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/framesetPage3.html b/node_modules/selenium-webdriver/lib/test/data/framesetPage3.html
new file mode 100644
index 000000000..42a93007f
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/framesetPage3.html
@@ -0,0 +1,4 @@
+<frameset>
+ <frame src="framePage3.html" id="first"></frame>
+ <frame src="framePage3.html" id="second"></frame>
+</frameset> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/globalscope.html b/node_modules/selenium-webdriver/lib/test/data/globalscope.html
new file mode 100644
index 000000000..e4ca97ab7
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/globalscope.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Global scope</title>
+ <script type="text/javascript">
+ window.w = "w";
+ window.h = "h";
+ window.__webdriver_w = "__webdriver_w";
+ window.__webdriver_h = "__webdriver_h";
+ </script>
+ </head>
+ <body>
+ <div style="position: absolute; left: 10px; top: 10px; height: 10px; width: 10px; background-color: blue;" id="toclick"></div>
+ </body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/hidden.html b/node_modules/selenium-webdriver/lib/test/data/hidden.html
new file mode 100644
index 000000000..0e8097e97
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/hidden.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<div id='singleHidden' hidden>This will not be visible</div>
+<div id='parent' hidden>
+ <div id='child'>My parent is hidden so you can't see me</div>
+</div> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/hidden_partially.html b/node_modules/selenium-webdriver/lib/test/data/hidden_partially.html
new file mode 100644
index 000000000..f0f9fe5b8
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/hidden_partially.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title></title>
+ <script type="text/javascript">
+ var next = 0;
+
+ function addVisibleBox() {
+ var box = document.createElement('DIV');
+ box.id = 'box' + next++;
+ box.className = 'redbox';
+ box.style.width = '150px';
+ box.style.height = '150px';
+ box.style.backgroundColor = 'red';
+ box.style.border = '1px solid black';
+ box.style.margin = '5px';
+ box.style.visibility = 'visible'
+
+ window.setTimeout(function() {
+ document.body.appendChild(box);
+ }, 1000);
+ }
+
+ function addHiddenBox() {
+ var box = document.createElement('DIV');
+ box.id = 'box' + next++;
+ box.className = 'redbox';
+ box.style.width = '150px';
+ box.style.height = '150px';
+ box.style.backgroundColor = 'red';
+ box.style.border = '1px solid black';
+ box.style.margin = '5px';
+ box.style.visibility = 'hidden';
+
+ window.setTimeout(function() {
+ document.body.appendChild(box);
+ }, 1000);
+ }
+ </script>
+ </head>
+ <body>
+ <input id="addVisible" type="button" value="Add a visible box!" onclick="addVisibleBox()"/>
+ <input id="addHidden" type="button" value="Add a hidden box!" onclick="addHiddenBox();" />
+ </body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/html5/blue.jpg b/node_modules/selenium-webdriver/lib/test/data/html5/blue.jpg
new file mode 100644
index 000000000..8ea27c42f
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/html5/blue.jpg
Binary files differ
diff --git a/node_modules/selenium-webdriver/lib/test/data/html5/database.js b/node_modules/selenium-webdriver/lib/test/data/html5/database.js
new file mode 100644
index 000000000..c6333be8c
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/html5/database.js
@@ -0,0 +1,84 @@
+var database={};
+database.db={};
+
+database.onError = function(tx, e) {
+ var log = document.createElement('div');
+ log.setAttribute('name','error');
+ log.setAttribute('style','background-color:red');
+ log.innerText = e.message;
+ document.getElementById('logs').appendChild(log);
+}
+
+database.onSuccess = function(tx, r) {
+ if (r.rows.length) {
+ var ol;
+ for (var i = 0; i < r.rows.length; i++) {
+ ol = document.createElement('ol');
+ ol.innerHTML = r.rows.item(i).ID + ": " + r.rows.item(i).docname + " (" + r.rows.item(i).created + ")";
+ document.getElementById('logs').appendChild(ol);
+ }
+
+ }
+}
+
+database.open=function(){
+ database.db=openDatabase('HTML5', '1.0', 'Offline document storage', 100*1024);
+}
+
+database.create=function(){
+ database.db.transaction(function(tx) {
+ tx.executeSql("CREATE TABLE IF NOT EXISTS docs(ID INTEGER PRIMARY KEY ASC, docname TEXT, created TIMESTAMP DEFAULT CURRENT_TIMESTAMP)",
+ [],
+ database.onSuccess,
+ database.onError);
+ });}
+
+database.add = function(message) {
+ database.db.transaction(function(tx){
+ tx.executeSql("INSERT INTO docs(docname) VALUES (?)",
+ [message], database.onSuccess, database.onError);
+ });
+}
+
+database.selectAll = function() {
+ database.db.transaction(function(tx) {
+ tx.executeSql("SELECT * FROM docs", [], database.onSuccess,
+ database.onError);
+ });
+}
+
+database.onDeleteAllSuccess = function(tx, r) {
+ var doc = document.documentElement;
+ var db_completed = document.createElement("div");
+ db_completed.setAttribute("id", "db_completed");
+ db_completed.innerText = "db operation completed";
+ doc.appendChild(db_completed);
+}
+
+database.deleteAll = function() {
+ database.db.transaction(function(tx) {
+ tx.executeSql("delete from docs", [], database.onDeleteAllSuccess,
+ database.onError);
+ });
+}
+
+var log = document.createElement('div');
+log.setAttribute('name','notice');
+log.setAttribute('style','background-color:yellow');
+log.innerText = typeof window.openDatabase == "function" ? "Web Database is supported." : "Web Database is not supported.";
+document.getElementById('logs').appendChild(log);
+
+try {
+ database.open();
+ database.create();
+ database.add('Doc 1');
+ database.add('Doc 2');
+ database.selectAll();
+ database.deleteAll();
+} catch(error) {
+ var log = document.createElement('div');
+ log.setAttribute('name','critical');
+ log.setAttribute('style','background-color:pink');
+ log.innerText = error;
+ document.getElementById('logs').appendChild(log);
+}
diff --git a/node_modules/selenium-webdriver/lib/test/data/html5/geolocation.js b/node_modules/selenium-webdriver/lib/test/data/html5/geolocation.js
new file mode 100644
index 000000000..f07af148e
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/html5/geolocation.js
@@ -0,0 +1,18 @@
+function success(position) {
+ var message = document.getElementById("status");
+ message.innerHTML ="<img src='http://maps.google.com/maps/api/staticmap?center=" + position.coords.latitude + "," + position.coords.longitude + "&size=300x200&maptype=roadmap&zoom=12&&markers=size:mid|color:red|" + position.coords.latitude + "," + position.coords.longitude + "&sensor=false' />";
+ message.innerHTML += "<p>Longitude: " + position.coords.longitude + "</p>";
+ message.innerHTML += "<p>Latitude: " + position.coords.latitude + "</p>";
+ message.innerHTML += "<p>Altitude: " + position.coords.altitude + "</p>";
+}
+
+function error(msg) {
+ var message = document.getElementById("status");
+ message.innerHTML = "Failed to get geolocation.";
+}
+
+if (navigator.geolocation) {
+ navigator.geolocation.getCurrentPosition(success, error);
+} else {
+ error('Geolocation is not supported.');
+} \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/html5/green.jpg b/node_modules/selenium-webdriver/lib/test/data/html5/green.jpg
new file mode 100644
index 000000000..6a0d3bea4
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/html5/green.jpg
Binary files differ
diff --git a/node_modules/selenium-webdriver/lib/test/data/html5/red.jpg b/node_modules/selenium-webdriver/lib/test/data/html5/red.jpg
new file mode 100644
index 000000000..f296e2719
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/html5/red.jpg
Binary files differ
diff --git a/node_modules/selenium-webdriver/lib/test/data/html5/status.html b/node_modules/selenium-webdriver/lib/test/data/html5/status.html
new file mode 100644
index 000000000..394116a52
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/html5/status.html
@@ -0,0 +1 @@
+<html><head><title>Online</title></head><body></body></html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/html5/test.appcache b/node_modules/selenium-webdriver/lib/test/data/html5/test.appcache
new file mode 100644
index 000000000..3bc4e0025
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/html5/test.appcache
@@ -0,0 +1,11 @@
+CACHE MANIFEST
+
+CACHE:
+# Additional items to cache.
+yellow.jpg
+red.jpg
+blue.jpg
+green.jpg
+
+FALLBACK:
+status.html offline.html
diff --git a/node_modules/selenium-webdriver/lib/test/data/html5/yellow.jpg b/node_modules/selenium-webdriver/lib/test/data/html5/yellow.jpg
new file mode 100644
index 000000000..7c609b371
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/html5/yellow.jpg
Binary files differ
diff --git a/node_modules/selenium-webdriver/lib/test/data/html5Page.html b/node_modules/selenium-webdriver/lib/test/data/html5Page.html
new file mode 100644
index 000000000..355ddc3a1
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/html5Page.html
@@ -0,0 +1,32 @@
+<html manifest="html5/test.appcache">
+<head>
+<title>HTML5</title>
+</head>
+<body>
+
+<h3>Geolocation Test</h3>
+<div id="status">Location unknown</div>
+<script language="javascript" type="text/javascript" src="html5/geolocation.js"></script>
+
+<h3>Web SQL Database Test</h3>
+<div id="logs"></div>
+<script language="javascript" type="text/javascript" src="html5/database.js"></script>
+
+<h3>Application Cache Test</h3>
+<div id="images">
+ <p>Current network status: <span id="state"></span></p>
+ <script>
+ var state = document.getElementById('state')
+ setInterval(function () {
+ state.className = navigator.onLine ? 'online' : 'offline';
+ state.innerHTML = navigator.onLine ? 'online' : 'offline';
+ }, 250);
+ </script>
+ <img id="red" src="html5/red.jpg">
+ <img id="blue" src="html5/blue.jpg">
+ <img id="green" src="html5/green.jpg">
+ <img id="yellow" src="html5/yellow.jpg">
+</div>
+
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/icon.gif b/node_modules/selenium-webdriver/lib/test/data/icon.gif
new file mode 100644
index 000000000..bb9946192
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/icon.gif
Binary files differ
diff --git a/node_modules/selenium-webdriver/lib/test/data/idElements.html b/node_modules/selenium-webdriver/lib/test/data/idElements.html
new file mode 100644
index 000000000..47f0834ca
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/idElements.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<div id="with.dots">Element with a dot in the id</div>
diff --git a/node_modules/selenium-webdriver/lib/test/data/iframeAtBottom.html b/node_modules/selenium-webdriver/lib/test/data/iframeAtBottom.html
new file mode 100644
index 000000000..a686ba312
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/iframeAtBottom.html
@@ -0,0 +1,15 @@
+<html>
+<head>
+ <title>This page has iframes</title>
+</head>
+<body>
+<h1 id="iframe_page_heading">This is the heading</h1>
+
+<div style="position: fixed; bottom: 0px; right: 0px; width: 150px; height: 150px;">
+ <iframe src="dragAndDropTest.html" marginheight="0" marginwidth="0" topmargin="0" leftmargin="0" allowtransparency="true" frameborder="1" height="100" scrolling="no" width="100" id="iframe1" name="iframe1-name" />
+</div>
+
+</body>
+</html>
+
+
diff --git a/node_modules/selenium-webdriver/lib/test/data/iframeWithAlert.html b/node_modules/selenium-webdriver/lib/test/data/iframeWithAlert.html
new file mode 100644
index 000000000..bc54ddd58
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/iframeWithAlert.html
@@ -0,0 +1 @@
+<a href="#" id="alertInFrame" onclick="alert('framed cheese');">click me</a>
diff --git a/node_modules/selenium-webdriver/lib/test/data/iframeWithIframe.html b/node_modules/selenium-webdriver/lib/test/data/iframeWithIframe.html
new file mode 100644
index 000000000..b29c54f3c
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/iframeWithIframe.html
@@ -0,0 +1 @@
+<iframe src="iframeWithAlert.html" name="iframeWithAlert"></iframe>
diff --git a/node_modules/selenium-webdriver/lib/test/data/iframes.html b/node_modules/selenium-webdriver/lib/test/data/iframes.html
new file mode 100644
index 000000000..e00b482aa
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/iframes.html
@@ -0,0 +1,11 @@
+<html>
+<head>
+ <title>This page has iframes</title>
+</head>
+<body>
+<h1 id="iframe_page_heading">This is the heading</h1>
+
+<iframe src="formPage.html" marginheight="0" marginwidth="0" topmargin="0" leftmargin="0" allowtransparency="true"
+ frameborder="0" height="600" scrolling="no" width="120" id="iframe1" name="iframe1-name" />
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/injectableContent.html b/node_modules/selenium-webdriver/lib/test/data/injectableContent.html
new file mode 100644
index 000000000..7248dccf8
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/injectableContent.html
@@ -0,0 +1,22 @@
+<html>
+ <head>
+ <title>random numbers</title>
+ <script>
+ function display() {
+ document.getElementById('numbers').textContent = numbers.join(' ');
+ }
+ </script>
+ </head>
+ <body onload="display()">
+ <p>Some random numbers between 0 and 100:</p>
+
+ <!-- Placeholder for the list of random numbers -->
+ <p id="numbers"></p>
+ <script>
+ numbers = [];
+ while (numbers.length < 100) {
+ numbers.push(Math.round(Math.random() * 100));
+ }
+ </script>
+ </body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/javascriptEnhancedForm.html b/node_modules/selenium-webdriver/lib/test/data/javascriptEnhancedForm.html
new file mode 100644
index 000000000..cbcff4335
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/javascriptEnhancedForm.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+ <head>
+ <title>Javascript Enhanced Page</title>
+ </head>
+ <body>
+ <form id="form1" action="javascriptEnhancedForm.html" method="post">
+ <p>
+ <label id="labelforusername" for="username">
+ Username: <input id="username" type="text" name="username" />
+ <script type="text/javascript">
+ document.getElementById('username').value = 'Michael';
+ </script>
+ </label>
+ </p>
+ <p>
+ <label for="password">
+ Password: <input id="password" type="password" name="password" />
+ <script type="text/javascript">
+ var z = document.getElementById('password'); setTimeout('try{z.focus();z.select();} catch(e) {}', 1);
+ </script>
+ </label>
+ </p>
+ <p>
+ <input type="submit" value="Submit" />
+ </p>
+ </form>
+ </body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/javascriptPage.html b/node_modules/selenium-webdriver/lib/test/data/javascriptPage.html
new file mode 100644
index 000000000..fcac252e8
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/javascriptPage.html
@@ -0,0 +1,285 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title>Testing Javascript</title>
+ <script type="text/javascript">
+ var seen = {};
+
+ function updateContent(input) {
+ document.getElementById('resultParagraph').innerHTML = input.value;
+ }
+
+ function displayMessage(message) {
+ document.getElementById('resultParagraph').innerHTML = message;
+ }
+
+ function appendMessage(message) {
+ document.getElementById('resultParagraph').innerHTML += message + " ";
+ }
+
+ function register(message) {
+ if (!seen[message]) {
+ appendMessage(message);
+ seen[message] = true;
+ }
+ }
+
+ function delayedShowHide(delay, show) {
+ var blackBox = document.getElementById('clickToHide');
+ window.setTimeout(function() {
+ blackBox.style.display = show ? '' : 'none';
+ }, delay);
+ }
+ </script>
+ <script type="text/javascript">
+ var startList = function() {
+ // Ugh. Let's hope no-one is faking their user agent when running the tests
+ if (navigator.userAgent.indexOf("MSIE") != -1) {
+ var navRoot = document.getElementById("nav");
+ for (var i = 0; i < navRoot.childNodes.length; i++) {
+ var node = navRoot.childNodes[i];
+ if (node.nodeName == "LI") {
+ node.onmouseover = function() {
+ this.className += " over";
+ };
+ node.onmouseout = function() {
+ this.className = this.className.replace(" over", "");
+ };
+ }
+ }
+ }
+ };
+ window.onload=startList;
+ </script>
+ <style type="text/css">
+ #nav {
+ padding: 0; margin: 0; list-style: none;
+ }
+ #nav li {
+ float: left; position: relative; width: 10em;
+ }
+ #nav li ul {
+ display: none; position: absolute; top: 1em; left: 0;
+ }
+ #nav li > ul { top: auto; left: auto; }
+ #nav li:hover ul, #nav li.over ul{ display: block; background: white; }
+ </style>
+</head>
+<body>
+<h1>Type Stuff</h1>
+
+<div>
+ <ul id="nav">
+ <li id="menu1">Menu 1
+ <ul>
+ <li id="item1" onclick="displayMessage('item 1');">Item 1</li>
+ <li>Item 2</li>
+ </ul>
+ </li>
+ </ul>
+</div>
+
+<div id="resultContainer" height="30">&nbsp;
+ <div id="result" style="width:300;height:60">
+ <p id="resultParagraph">&nbsp;</p>
+ </div>
+
+</div>
+
+<div id="formageddon">
+ <form action="#">
+ Key Up: <input type="text" id="keyUp" onkeyup="javascript:updateContent(this)"/><br/>
+ Key Down: <input type="text" id="keyDown" onkeydown="javascript:updateContent(this)"/><br/>
+ Key Press: <input type="text" id="keyPress" onkeypress="javascript:updateContent(this)"/><br/>
+ Change: <input type="text" id="change" onkeypress="javascript:displayMessage('change')"/><br/>
+ <textarea id="keyDownArea" onkeydown="javascript:updateContent(this)" rows="2" cols="15"></textarea>
+ <textarea id="keyPressArea" onkeypress="javascript:updateContent(this)" rows="2" cols="15"></textarea>
+ <textarea id="keyUpArea" onkeyup="javascript:updateContent(this)" rows="2" cols="15"></textarea>
+ <select id="selector" onchange="javascript:updateContent(this)">
+ <option value="foo">Foo</option>
+ <option value="bar">Bar</option>
+ </select>
+ <select id="selector2" onclick="javascript:updateContent(this)">
+ <option value="foo">Foo</option>
+ <option value="bar">Bar</option>
+ </select>
+ <input type="checkbox" id="checkbox" value="checkbox thing" onchange="javascript:updateContent(this)"/>
+ <input id="clickField" type="text" onclick="document.getElementById('clickField').value='Clicked';" value="Hello"/>
+ <input id="doubleClickField" type="text" onclick="document.getElementById('doubleClickField').value='Clicked';" ondblclick="document.getElementById('doubleClickField').value='DoubleClicked';" oncontextmenu="document.getElementById('doubleClickField').value='ContextClicked'; return false;" value="DoubleHello"/>
+ <input id="clearMe" value="Something" onchange="displayMessage('Cleared')"/>
+ </form>
+</div>
+
+<div>
+ <p><a href="#" onclick="javascript:document.title='Changed'">Change the page title!</a></p>
+
+ <p><a onclick="javascript:document.title='Changed'" id="nohref">No href</a></p>
+
+ <p><a id="updatediv" href="#" onclick="javascript:document.getElementById('dynamo').innerHTML = 'Fish and chips!';">Update a
+ div</a></p>
+</div>
+
+<div id="dynamo">What's for dinner?</div>
+
+<div id="mousedown" onmousedown="javascript:displayMessage('mouse down');">
+ <p>Click for the mouse down event</p>
+ <span><p id="child">Here's some text</p></span>
+</div>
+
+<div id="mouseup" onmouseup="javascript:displayMessage('mouse up');">
+ <p>Click for the mouse up event</p>
+</div>
+
+<div id="mouseclick" onclick="javascript:displayMessage('mouse click');">
+ <p>Click for the mouse click event</p>
+</div>
+
+<div id="error" onclick="document.getElementById('doesnotexist').innerHTML = 'cheese';">
+ Clicking this causes a JS exception in the click handler
+</div>
+
+<div>
+ <form action="resultPage.html" id="on-form">
+ <input id="theworks"
+ onfocus="appendMessage('focus')"
+ onkeydown="appendMessage('keydown')"
+ onkeypress="appendMessage('keypress')"
+ onkeyup="appendMessage('keyup')"
+ onblur="appendMessage('blur')"
+ onchange="appendMessage('change')"
+ />
+
+ <input id="changeable" name="changeable" onfocus="appendMessage('focus')" onchange="appendMessage('change')" onblur="appendMessage('blur')"/>
+
+ <button type="button" id="plainButton"
+ onfocus="appendMessage('focus')"
+ onkeydown="appendMessage('keydown')"
+ onkeypress="appendMessage('keypress')"
+ onkeyup="appendMessage('keyup')"
+ onblur="appendMessage('blur')"
+ onclick="appendMessage('click')"
+ onmousedown="appendMessage('mousedown ')"
+ onmouseup="appendMessage('mouseup ')"
+ onmouseover="register('mouseover ')"
+ onmousemove="register('mousemove ')"
+ >
+ <b>Go somewhere</b>
+ </button>
+ <button type="submit" id="submittingButton"><emph>submit</emph></button>
+ <button type="button" id="jsSubmitButton" onclick="javascript:document.getElementById('on-form').submit();">Submitomatic</button>
+
+ <button type="button" id="switchFocus" onclick="document.getElementById('theworks').focus();">Switch focus</button>
+ <button type="button" onclick="var element = document.getElementById('switchFocus'); var clickEvent = document.createEvent('MouseEvents'); clickEvent.initMouseEvent('click', true, true, null, 0, 0, 0, 0, 0,false, false, false, false, 0, element);element.dispatchEvent(clickEvent);">Do magic</button><br/>
+ <label id="labelForCheckbox" for="labeledCheckbox" onclick="appendMessage('labelclick')">Toggle checkbox</label><input type="checkbox" id="labeledCheckbox" onclick="appendMessage('chboxclick')"/>
+ </form>
+
+ <form action="javascriptPage.html" id="submitListeningForm" onsubmit="appendMessage('form-onsubmit '); return false;">
+ <p>
+ <input id="submitListeningForm-text" type="text" onsubmit="appendMessage('text-onsubmit ')" onclick="appendMessage('text-onclick ');" />
+ <input id="submitListeningForm-submit" type="submit" onsubmit="appendMessage('submit-onsubmit ')" onclick="appendMessage('submit-onclick ');" />
+ </p>
+ </form>
+</div>
+
+<p id="suppressedParagraph" style="display: none">A paragraph suppressed using CSS display=none</p>
+
+<div>
+ <p id="displayed">Displayed</p>
+
+ <form action="#"><input type="hidden" name="hidden" /> </form>
+
+ <p id="none" style="display: none;">Display set to none</p>
+
+ <p id="hidden" style="visibility: hidden;">Hidden</p>
+
+ <div id="hiddenparent" style="height: 2em; display: none;">
+ <div id="hiddenchild">
+ <a href="#" id="hiddenlink">ok</a>
+ </div>
+ </div>
+
+ <div style="visibility: hidden;">
+ <span>
+ <input id="unclickable" />
+ <input type="checkbox" id="untogglable" checked="checked" />Check box you can't see
+ </span>
+ </div>
+
+ <p id="outer" style="visibility: hidden">A <b id="visibleSubElement" style="visibility: visible">sub-element that is explicitly visible</b> using CSS visibility=visible</p>
+</div>
+
+<div>
+ <form>
+ <input type="text" id="keyReporter" size="80"
+ onkeyup="appendMessage('up: ' + event.keyCode)"
+ onkeypress="appendMessage('press: ' + event.keyCode)"
+ onkeydown="displayMessage(''); appendMessage('down: ' + event.keyCode)" />
+ <input name="suppress" onkeydown="if (event.preventDefault) event.preventDefault(); event.returnValue = false; return false;" onkeypress="appendMessage('press');"/>
+ </form>
+</div>
+
+<!-- Used for testing styles -->
+<div style="background-color: green;" id="green-parent">
+ <p id="style1">This should be greenish</p>
+ <ul>
+ <li id="green-item">So should this</li>
+ <li id="red-item" style="background-color: red;">But this is red</li>
+ </ul>
+</div>
+
+<a href="#" id="close" onclick="window.close();">Close window</a>
+
+<div id="delete" onclick="var d = document.getElementById('deleted'); this.removeChild(d);">
+ <p id="deleted">I should be deleted when you click my containing div</p>
+ <p>Whereas, I should not</p>
+</div>
+
+<div>
+ <span id="hideMe" onclick="this.style.display = 'none';">Click to hide me.</span>
+</div>
+
+<div id="hideOnBlur" tabindex="0"
+ style="width: 100px; height: 100px; border: 1px solid red"
+ onblur="appendMessage('blur'); this.style.display = 'none';">
+ <div id="hideOnBlurChild"
+ style="width: 30px; height: 30px; border: 1px solid blue">
+ x
+ </div>
+ Focusable. Will hide when focus is lost.
+</div>
+
+<div style="margin-top: 10px;">
+ Click actions delayed by 3000ms:
+ <div id="clickToShow" onclick="delayedShowHide(3000, true);"
+ style="float: left;width: 100px;height:100px;border: 1px solid black;">
+ Click to show black box
+ </div>
+ <div id="clickToHide" onclick="delayedShowHide(3000, false);"
+ style="float: left;width: 100px;height:100px;border: 1px solid black;
+ background-color: black; color: white; display: none;">
+ Click to hide black box
+ </div>
+ <div style="clear: both"></div>
+</div>
+
+<a id="new_window" onmouseup="window.open('closeable_window.html', 'close_me')" href="#">Click me to open a new window</a>
+
+<a id="throwing-mouseover" onmouseover="throw new Error()" href="#throwing-mouseover">Mouse over me will throw a JS error</a>
+
+<div id="parent">
+ <span id="movable" onmouseover="var p = document.getElementById('movable'); displayMessage('parent matches? ' + (p != event.relatedTarget));">
+ Click on me to show the related target
+ </span>
+</div>
+
+<div id="zero" style="width:0;height:0">
+ <div>
+ <img src="map.png">
+ </div>
+</div>
+
+</body>
+</html>
+
+
diff --git a/node_modules/selenium-webdriver/lib/test/data/jquery-1.3.2.js b/node_modules/selenium-webdriver/lib/test/data/jquery-1.3.2.js
new file mode 100644
index 000000000..462cde56c
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/jquery-1.3.2.js
@@ -0,0 +1,4376 @@
+/*!
+ * jQuery JavaScript Library v1.3.2
+ * http://jquery.com/
+ *
+ * Copyright (c) 2009 John Resig
+ * Dual licensed under the MIT and GPL licenses.
+ * http://docs.jquery.com/License
+ *
+ * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009)
+ * Revision: 6246
+ */
+(function(){
+
+var
+ // Will speed up references to window, and allows munging its name.
+ window = this,
+ // Will speed up references to undefined, and allows munging its name.
+ undefined,
+ // Map over jQuery in case of overwrite
+ _jQuery = window.jQuery,
+ // Map over the $ in case of overwrite
+ _$ = window.$,
+
+ jQuery = window.jQuery = window.$ = function( selector, context ) {
+ // The jQuery object is actually just the init constructor 'enhanced'
+ return new jQuery.fn.init( selector, context );
+ },
+
+ // A simple way to check for HTML strings or ID strings
+ // (both of which we optimize for)
+ quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,
+ // Is it a simple selector
+ isSimple = /^.[^:#\[\.,]*$/;
+
+jQuery.fn = jQuery.prototype = {
+ init: function( selector, context ) {
+ // Make sure that a selection was provided
+ selector = selector || document;
+
+ // Handle $(DOMElement)
+ if ( selector.nodeType ) {
+ this[0] = selector;
+ this.length = 1;
+ this.context = selector;
+ return this;
+ }
+ // Handle HTML strings
+ if ( typeof selector === "string" ) {
+ // Are we dealing with HTML string or an ID?
+ var match = quickExpr.exec( selector );
+
+ // Verify a match, and that no context was specified for #id
+ if ( match && (match[1] || !context) ) {
+
+ // HANDLE: $(html) -> $(array)
+ if ( match[1] )
+ selector = jQuery.clean( [ match[1] ], context );
+
+ // HANDLE: $("#id")
+ else {
+ var elem = document.getElementById( match[3] );
+
+ // Handle the case where IE and Opera return items
+ // by name instead of ID
+ if ( elem && elem.id != match[3] )
+ return jQuery().find( selector );
+
+ // Otherwise, we inject the element directly into the jQuery object
+ var ret = jQuery( elem || [] );
+ ret.context = document;
+ ret.selector = selector;
+ return ret;
+ }
+
+ // HANDLE: $(expr, [context])
+ // (which is just equivalent to: $(content).find(expr)
+ } else
+ return jQuery( context ).find( selector );
+
+ // HANDLE: $(function)
+ // Shortcut for document ready
+ } else if ( jQuery.isFunction( selector ) )
+ return jQuery( document ).ready( selector );
+
+ // Make sure that old selector state is passed along
+ if ( selector.selector && selector.context ) {
+ this.selector = selector.selector;
+ this.context = selector.context;
+ }
+
+ return this.setArray(jQuery.isArray( selector ) ?
+ selector :
+ jQuery.makeArray(selector));
+ },
+
+ // Start with an empty selector
+ selector: "",
+
+ // The current version of jQuery being used
+ jquery: "1.3.2",
+
+ // The number of elements contained in the matched element set
+ size: function() {
+ return this.length;
+ },
+
+ // Get the Nth element in the matched element set OR
+ // Get the whole matched element set as a clean array
+ get: function( num ) {
+ return num === undefined ?
+
+ // Return a 'clean' array
+ Array.prototype.slice.call( this ) :
+
+ // Return just the object
+ this[ num ];
+ },
+
+ // Take an array of elements and push it onto the stack
+ // (returning the new matched element set)
+ pushStack: function( elems, name, selector ) {
+ // Build a new jQuery matched element set
+ var ret = jQuery( elems );
+
+ // Add the old object onto the stack (as a reference)
+ ret.prevObject = this;
+
+ ret.context = this.context;
+
+ if ( name === "find" )
+ ret.selector = this.selector + (this.selector ? " " : "") + selector;
+ else if ( name )
+ ret.selector = this.selector + "." + name + "(" + selector + ")";
+
+ // Return the newly-formed element set
+ return ret;
+ },
+
+ // Force the current matched set of elements to become
+ // the specified array of elements (destroying the stack in the process)
+ // You should use pushStack() in order to do this, but maintain the stack
+ setArray: function( elems ) {
+ // Resetting the length to 0, then using the native Array push
+ // is a super-fast way to populate an object with array-like properties
+ this.length = 0;
+ Array.prototype.push.apply( this, elems );
+
+ return this;
+ },
+
+ // Execute a callback for every element in the matched set.
+ // (You can seed the arguments with an array of args, but this is
+ // only used internally.)
+ each: function( callback, args ) {
+ return jQuery.each( this, callback, args );
+ },
+
+ // Determine the position of an element within
+ // the matched set of elements
+ index: function( elem ) {
+ // Locate the position of the desired element
+ return jQuery.inArray(
+ // If it receives a jQuery object, the first element is used
+ elem && elem.jquery ? elem[0] : elem
+ , this );
+ },
+
+ attr: function( name, value, type ) {
+ var options = name;
+
+ // Look for the case where we're accessing a style value
+ if ( typeof name === "string" )
+ if ( value === undefined )
+ return this[0] && jQuery[ type || "attr" ]( this[0], name );
+
+ else {
+ options = {};
+ options[ name ] = value;
+ }
+
+ // Check to see if we're setting style values
+ return this.each(function(i){
+ // Set all the styles
+ for ( name in options )
+ jQuery.attr(
+ type ?
+ this.style :
+ this,
+ name, jQuery.prop( this, options[ name ], type, i, name )
+ );
+ });
+ },
+
+ css: function( key, value ) {
+ // ignore negative width and height values
+ if ( (key == 'width' || key == 'height') && parseFloat(value) < 0 )
+ value = undefined;
+ return this.attr( key, value, "curCSS" );
+ },
+
+ text: function( text ) {
+ if ( typeof text !== "object" && text != null )
+ return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );
+
+ var ret = "";
+
+ jQuery.each( text || this, function(){
+ jQuery.each( this.childNodes, function(){
+ if ( this.nodeType != 8 )
+ ret += this.nodeType != 1 ?
+ this.nodeValue :
+ jQuery.fn.text( [ this ] );
+ });
+ });
+
+ return ret;
+ },
+
+ wrapAll: function( html ) {
+ if ( this[0] ) {
+ // The elements to wrap the target around
+ var wrap = jQuery( html, this[0].ownerDocument ).clone();
+
+ if ( this[0].parentNode )
+ wrap.insertBefore( this[0] );
+
+ wrap.map(function(){
+ var elem = this;
+
+ while ( elem.firstChild )
+ elem = elem.firstChild;
+
+ return elem;
+ }).append(this);
+ }
+
+ return this;
+ },
+
+ wrapInner: function( html ) {
+ return this.each(function(){
+ jQuery( this ).contents().wrapAll( html );
+ });
+ },
+
+ wrap: function( html ) {
+ return this.each(function(){
+ jQuery( this ).wrapAll( html );
+ });
+ },
+
+ append: function() {
+ return this.domManip(arguments, true, function(elem){
+ if (this.nodeType == 1)
+ this.appendChild( elem );
+ });
+ },
+
+ prepend: function() {
+ return this.domManip(arguments, true, function(elem){
+ if (this.nodeType == 1)
+ this.insertBefore( elem, this.firstChild );
+ });
+ },
+
+ before: function() {
+ return this.domManip(arguments, false, function(elem){
+ this.parentNode.insertBefore( elem, this );
+ });
+ },
+
+ after: function() {
+ return this.domManip(arguments, false, function(elem){
+ this.parentNode.insertBefore( elem, this.nextSibling );
+ });
+ },
+
+ end: function() {
+ return this.prevObject || jQuery( [] );
+ },
+
+ // For internal use only.
+ // Behaves like an Array's method, not like a jQuery method.
+ push: [].push,
+ sort: [].sort,
+ splice: [].splice,
+
+ find: function( selector ) {
+ if ( this.length === 1 ) {
+ var ret = this.pushStack( [], "find", selector );
+ ret.length = 0;
+ jQuery.find( selector, this[0], ret );
+ return ret;
+ } else {
+ return this.pushStack( jQuery.unique(jQuery.map(this, function(elem){
+ return jQuery.find( selector, elem );
+ })), "find", selector );
+ }
+ },
+
+ clone: function( events ) {
+ // Do the clone
+ var ret = this.map(function(){
+ if ( !jQuery.support.noCloneEvent && !jQuery.isXMLDoc(this) ) {
+ // IE copies events bound via attachEvent when
+ // using cloneNode. Calling detachEvent on the
+ // clone will also remove the events from the orignal
+ // In order to get around this, we use innerHTML.
+ // Unfortunately, this means some modifications to
+ // attributes in IE that are actually only stored
+ // as properties will not be copied (such as the
+ // the name attribute on an input).
+ var html = this.outerHTML;
+ if ( !html ) {
+ var div = this.ownerDocument.createElement("div");
+ div.appendChild( this.cloneNode(true) );
+ html = div.innerHTML;
+ }
+
+ return jQuery.clean([html.replace(/ jQuery\d+="(?:\d+|null)"/g, "").replace(/^\s*/, "")])[0];
+ } else
+ return this.cloneNode(true);
+ });
+
+ // Copy the events from the original to the clone
+ if ( events === true ) {
+ var orig = this.find("*").andSelf(), i = 0;
+
+ ret.find("*").andSelf().each(function(){
+ if ( this.nodeName !== orig[i].nodeName )
+ return;
+
+ var events = jQuery.data( orig[i], "events" );
+
+ for ( var type in events ) {
+ for ( var handler in events[ type ] ) {
+ jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data );
+ }
+ }
+
+ i++;
+ });
+ }
+
+ // Return the cloned set
+ return ret;
+ },
+
+ filter: function( selector ) {
+ return this.pushStack(
+ jQuery.isFunction( selector ) &&
+ jQuery.grep(this, function(elem, i){
+ return selector.call( elem, i );
+ }) ||
+
+ jQuery.multiFilter( selector, jQuery.grep(this, function(elem){
+ return elem.nodeType === 1;
+ }) ), "filter", selector );
+ },
+
+ closest: function( selector ) {
+ var pos = jQuery.expr.match.POS.test( selector ) ? jQuery(selector) : null,
+ closer = 0;
+
+ return this.map(function(){
+ var cur = this;
+ while ( cur && cur.ownerDocument ) {
+ if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selector) ) {
+ jQuery.data(cur, "closest", closer);
+ return cur;
+ }
+ cur = cur.parentNode;
+ closer++;
+ }
+ });
+ },
+
+ not: function( selector ) {
+ if ( typeof selector === "string" )
+ // test special case where just one selector is passed in
+ if ( isSimple.test( selector ) )
+ return this.pushStack( jQuery.multiFilter( selector, this, true ), "not", selector );
+ else
+ selector = jQuery.multiFilter( selector, this );
+
+ var isArrayLike = selector.length && selector[selector.length - 1] !== undefined && !selector.nodeType;
+ return this.filter(function() {
+ return isArrayLike ? jQuery.inArray( this, selector ) < 0 : this != selector;
+ });
+ },
+
+ add: function( selector ) {
+ return this.pushStack( jQuery.unique( jQuery.merge(
+ this.get(),
+ typeof selector === "string" ?
+ jQuery( selector ) :
+ jQuery.makeArray( selector )
+ )));
+ },
+
+ is: function( selector ) {
+ return !!selector && jQuery.multiFilter( selector, this ).length > 0;
+ },
+
+ hasClass: function( selector ) {
+ return !!selector && this.is( "." + selector );
+ },
+
+ val: function( value ) {
+ if ( value === undefined ) {
+ var elem = this[0];
+
+ if ( elem ) {
+ if( jQuery.nodeName( elem, 'option' ) )
+ return (elem.attributes.value || {}).specified ? elem.value : elem.text;
+
+ // We need to handle select boxes special
+ if ( jQuery.nodeName( elem, "select" ) ) {
+ var index = elem.selectedIndex,
+ values = [],
+ options = elem.options,
+ one = elem.type == "select-one";
+
+ // Nothing was selected
+ if ( index < 0 )
+ return null;
+
+ // Loop through all the selected options
+ for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) {
+ var option = options[ i ];
+
+ if ( option.selected ) {
+ // Get the specifc value for the option
+ value = jQuery(option).val();
+
+ // We don't need an array for one selects
+ if ( one )
+ return value;
+
+ // Multi-Selects return an array
+ values.push( value );
+ }
+ }
+
+ return values;
+ }
+
+ // Everything else, we just grab the value
+ return (elem.value || "").replace(/\r/g, "");
+
+ }
+
+ return undefined;
+ }
+
+ if ( typeof value === "number" )
+ value += '';
+
+ return this.each(function(){
+ if ( this.nodeType != 1 )
+ return;
+
+ if ( jQuery.isArray(value) && /radio|checkbox/.test( this.type ) )
+ this.checked = (jQuery.inArray(this.value, value) >= 0 ||
+ jQuery.inArray(this.name, value) >= 0);
+
+ else if ( jQuery.nodeName( this, "select" ) ) {
+ var values = jQuery.makeArray(value);
+
+ jQuery( "option", this ).each(function(){
+ this.selected = (jQuery.inArray( this.value, values ) >= 0 ||
+ jQuery.inArray( this.text, values ) >= 0);
+ });
+
+ if ( !values.length )
+ this.selectedIndex = -1;
+
+ } else
+ this.value = value;
+ });
+ },
+
+ html: function( value ) {
+ return value === undefined ?
+ (this[0] ?
+ this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g, "") :
+ null) :
+ this.empty().append( value );
+ },
+
+ replaceWith: function( value ) {
+ return this.after( value ).remove();
+ },
+
+ eq: function( i ) {
+ return this.slice( i, +i + 1 );
+ },
+
+ slice: function() {
+ return this.pushStack( Array.prototype.slice.apply( this, arguments ),
+ "slice", Array.prototype.slice.call(arguments).join(",") );
+ },
+
+ map: function( callback ) {
+ return this.pushStack( jQuery.map(this, function(elem, i){
+ return callback.call( elem, i, elem );
+ }));
+ },
+
+ andSelf: function() {
+ return this.add( this.prevObject );
+ },
+
+ domManip: function( args, table, callback ) {
+ if ( this[0] ) {
+ var fragment = (this[0].ownerDocument || this[0]).createDocumentFragment(),
+ scripts = jQuery.clean( args, (this[0].ownerDocument || this[0]), fragment ),
+ first = fragment.firstChild;
+
+ if ( first )
+ for ( var i = 0, l = this.length; i < l; i++ )
+ callback.call( root(this[i], first), this.length > 1 || i > 0 ?
+ fragment.cloneNode(true) : fragment );
+
+ if ( scripts )
+ jQuery.each( scripts, evalScript );
+ }
+
+ return this;
+
+ function root( elem, cur ) {
+ return table && jQuery.nodeName(elem, "table") && jQuery.nodeName(cur, "tr") ?
+ (elem.getElementsByTagName("tbody")[0] ||
+ elem.appendChild(elem.ownerDocument.createElement("tbody"))) :
+ elem;
+ }
+ }
+};
+
+// Give the init function the jQuery prototype for later instantiation
+jQuery.fn.init.prototype = jQuery.fn;
+
+function evalScript( i, elem ) {
+ if ( elem.src )
+ jQuery.ajax({
+ url: elem.src,
+ async: false,
+ dataType: "script"
+ });
+
+ else
+ jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" );
+
+ if ( elem.parentNode )
+ elem.parentNode.removeChild( elem );
+}
+
+function now(){
+ return +new Date;
+}
+
+jQuery.extend = jQuery.fn.extend = function() {
+ // copy reference to target object
+ var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options;
+
+ // Handle a deep copy situation
+ if ( typeof target === "boolean" ) {
+ deep = target;
+ target = arguments[1] || {};
+ // skip the boolean and the target
+ i = 2;
+ }
+
+ // Handle case when target is a string or something (possible in deep copy)
+ if ( typeof target !== "object" && !jQuery.isFunction(target) )
+ target = {};
+
+ // extend jQuery itself if only one argument is passed
+ if ( length == i ) {
+ target = this;
+ --i;
+ }
+
+ for ( ; i < length; i++ )
+ // Only deal with non-null/undefined values
+ if ( (options = arguments[ i ]) != null )
+ // Extend the base object
+ for ( var name in options ) {
+ var src = target[ name ], copy = options[ name ];
+
+ // Prevent never-ending loop
+ if ( target === copy )
+ continue;
+
+ // Recurse if we're merging object values
+ if ( deep && copy && typeof copy === "object" && !copy.nodeType )
+ target[ name ] = jQuery.extend( deep,
+ // Never move original objects, clone them
+ src || ( copy.length != null ? [ ] : { } )
+ , copy );
+
+ // Don't bring in undefined values
+ else if ( copy !== undefined )
+ target[ name ] = copy;
+
+ }
+
+ // Return the modified object
+ return target;
+};
+
+// exclude the following css properties to add px
+var exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i,
+ // cache defaultView
+ defaultView = document.defaultView || {},
+ toString = Object.prototype.toString;
+
+jQuery.extend({
+ noConflict: function( deep ) {
+ window.$ = _$;
+
+ if ( deep )
+ window.jQuery = _jQuery;
+
+ return jQuery;
+ },
+
+ // See test/unit/core.js for details concerning isFunction.
+ // Since version 1.3, DOM methods and functions like alert
+ // aren't supported. They return false on IE (#2968).
+ isFunction: function( obj ) {
+ return toString.call(obj) === "[object Function]";
+ },
+
+ isArray: function( obj ) {
+ return toString.call(obj) === "[object Array]";
+ },
+
+ // check if an element is in a (or is an) XML document
+ isXMLDoc: function( elem ) {
+ return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
+ !!elem.ownerDocument && jQuery.isXMLDoc( elem.ownerDocument );
+ },
+
+ // Evalulates a script in a global context
+ globalEval: function( data ) {
+ if ( data && /\S/.test(data) ) {
+ // Inspired by code by Andrea Giammarchi
+ // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html
+ var head = document.getElementsByTagName("head")[0] || document.documentElement,
+ script = document.createElement("script");
+
+ script.type = "text/javascript";
+ if ( jQuery.support.scriptEval )
+ script.appendChild( document.createTextNode( data ) );
+ else
+ script.text = data;
+
+ // Use insertBefore instead of appendChild to circumvent an IE6 bug.
+ // This arises when a base node is used (#2709).
+ head.insertBefore( script, head.firstChild );
+ head.removeChild( script );
+ }
+ },
+
+ nodeName: function( elem, name ) {
+ return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase();
+ },
+
+ // args is for internal usage only
+ each: function( object, callback, args ) {
+ var name, i = 0, length = object.length;
+
+ if ( args ) {
+ if ( length === undefined ) {
+ for ( name in object )
+ if ( callback.apply( object[ name ], args ) === false )
+ break;
+ } else
+ for ( ; i < length; )
+ if ( callback.apply( object[ i++ ], args ) === false )
+ break;
+
+ // A special, fast, case for the most common use of each
+ } else {
+ if ( length === undefined ) {
+ for ( name in object )
+ if ( callback.call( object[ name ], name, object[ name ] ) === false )
+ break;
+ } else
+ for ( var value = object[0];
+ i < length && callback.call( value, i, value ) !== false; value = object[++i] ){}
+ }
+
+ return object;
+ },
+
+ prop: function( elem, value, type, i, name ) {
+ // Handle executable functions
+ if ( jQuery.isFunction( value ) )
+ value = value.call( elem, i );
+
+ // Handle passing in a number to a CSS property
+ return typeof value === "number" && type == "curCSS" && !exclude.test( name ) ?
+ value + "px" :
+ value;
+ },
+
+ className: {
+ // internal only, use addClass("class")
+ add: function( elem, classNames ) {
+ jQuery.each((classNames || "").split(/\s+/), function(i, className){
+ if ( elem.nodeType == 1 && !jQuery.className.has( elem.className, className ) )
+ elem.className += (elem.className ? " " : "") + className;
+ });
+ },
+
+ // internal only, use removeClass("class")
+ remove: function( elem, classNames ) {
+ if (elem.nodeType == 1)
+ elem.className = classNames !== undefined ?
+ jQuery.grep(elem.className.split(/\s+/), function(className){
+ return !jQuery.className.has( classNames, className );
+ }).join(" ") :
+ "";
+ },
+
+ // internal only, use hasClass("class")
+ has: function( elem, className ) {
+ return elem && jQuery.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1;
+ }
+ },
+
+ // A method for quickly swapping in/out CSS properties to get correct calculations
+ swap: function( elem, options, callback ) {
+ var old = {};
+ // Remember the old values, and insert the new ones
+ for ( var name in options ) {
+ old[ name ] = elem.style[ name ];
+ elem.style[ name ] = options[ name ];
+ }
+
+ callback.call( elem );
+
+ // Revert the old values
+ for ( var name in options )
+ elem.style[ name ] = old[ name ];
+ },
+
+ css: function( elem, name, force, extra ) {
+ if ( name == "width" || name == "height" ) {
+ var val, props = { position: "absolute", visibility: "hidden", display:"block" }, which = name == "width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ];
+
+ function getWH() {
+ val = name == "width" ? elem.offsetWidth : elem.offsetHeight;
+
+ if ( extra === "border" )
+ return;
+
+ jQuery.each( which, function() {
+ if ( !extra )
+ val -= parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0;
+ if ( extra === "margin" )
+ val += parseFloat(jQuery.curCSS( elem, "margin" + this, true)) || 0;
+ else
+ val -= parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0;
+ });
+ }
+
+ if ( elem.offsetWidth !== 0 )
+ getWH();
+ else
+ jQuery.swap( elem, props, getWH );
+
+ return Math.max(0, Math.round(val));
+ }
+
+ return jQuery.curCSS( elem, name, force );
+ },
+
+ curCSS: function( elem, name, force ) {
+ var ret, style = elem.style;
+
+ // We need to handle opacity special in IE
+ if ( name == "opacity" && !jQuery.support.opacity ) {
+ ret = jQuery.attr( style, "opacity" );
+
+ return ret == "" ?
+ "1" :
+ ret;
+ }
+
+ // Make sure we're using the right name for getting the float value
+ if ( name.match( /float/i ) )
+ name = styleFloat;
+
+ if ( !force && style && style[ name ] )
+ ret = style[ name ];
+
+ else if ( defaultView.getComputedStyle ) {
+
+ // Only "float" is needed here
+ if ( name.match( /float/i ) )
+ name = "float";
+
+ name = name.replace( /([A-Z])/g, "-$1" ).toLowerCase();
+
+ var computedStyle = defaultView.getComputedStyle( elem, null );
+
+ if ( computedStyle )
+ ret = computedStyle.getPropertyValue( name );
+
+ // We should always get a number back from opacity
+ if ( name == "opacity" && ret == "" )
+ ret = "1";
+
+ } else if ( elem.currentStyle ) {
+ var camelCase = name.replace(/\-(\w)/g, function(all, letter){
+ return letter.toUpperCase();
+ });
+
+ ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ];
+
+ // From the awesome hack by Dean Edwards
+ // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
+
+ // If we're not dealing with a regular pixel number
+ // but a number that has a weird ending, we need to convert it to pixels
+ if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) {
+ // Remember the original values
+ var left = style.left, rsLeft = elem.runtimeStyle.left;
+
+ // Put in the new values to get a computed value out
+ elem.runtimeStyle.left = elem.currentStyle.left;
+ style.left = ret || 0;
+ ret = style.pixelLeft + "px";
+
+ // Revert the changed values
+ style.left = left;
+ elem.runtimeStyle.left = rsLeft;
+ }
+ }
+
+ return ret;
+ },
+
+ clean: function( elems, context, fragment ) {
+ context = context || document;
+
+ // !context.createElement fails in IE with an error but returns typeof 'object'
+ if ( typeof context.createElement === "undefined" )
+ context = context.ownerDocument || context[0] && context[0].ownerDocument || document;
+
+ // If a single string is passed in and it's a single tag
+ // just do a createElement and skip the rest
+ if ( !fragment && elems.length === 1 && typeof elems[0] === "string" ) {
+ var match = /^<(\w+)\s*\/?>$/.exec(elems[0]);
+ if ( match )
+ return [ context.createElement( match[1] ) ];
+ }
+
+ var ret = [], scripts = [], div = context.createElement("div");
+
+ jQuery.each(elems, function(i, elem){
+ if ( typeof elem === "number" )
+ elem += '';
+
+ if ( !elem )
+ return;
+
+ // Convert html string into DOM nodes
+ if ( typeof elem === "string" ) {
+ // Fix "XHTML"-style tags in all browsers
+ elem = elem.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag){
+ return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ?
+ all :
+ front + "></" + tag + ">";
+ });
+
+ // Trim whitespace, otherwise indexOf won't work as expected
+ var tags = elem.replace(/^\s+/, "").substring(0, 10).toLowerCase();
+
+ var wrap =
+ // option or optgroup
+ !tags.indexOf("<opt") &&
+ [ 1, "<select multiple='multiple'>", "</select>" ] ||
+
+ !tags.indexOf("<leg") &&
+ [ 1, "<fieldset>", "</fieldset>" ] ||
+
+ tags.match(/^<(thead|tbody|tfoot|colg|cap)/) &&
+ [ 1, "<table>", "</table>" ] ||
+
+ !tags.indexOf("<tr") &&
+ [ 2, "<table><tbody>", "</tbody></table>" ] ||
+
+ // <thead> matched above
+ (!tags.indexOf("<td") || !tags.indexOf("<th")) &&
+ [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ] ||
+
+ !tags.indexOf("<col") &&
+ [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ] ||
+
+ // IE can't serialize <link> and <script> tags normally
+ !jQuery.support.htmlSerialize &&
+ [ 1, "div<div>", "</div>" ] ||
+
+ [ 0, "", "" ];
+
+ // Go to html and back, then peel off extra wrappers
+ div.innerHTML = wrap[1] + elem + wrap[2];
+
+ // Move to the right depth
+ while ( wrap[0]-- )
+ div = div.lastChild;
+
+ // Remove IE's autoinserted <tbody> from table fragments
+ if ( !jQuery.support.tbody ) {
+
+ // String was a <table>, *may* have spurious <tbody>
+ var hasBody = /<tbody/i.test(elem),
+ tbody = !tags.indexOf("<table") && !hasBody ?
+ div.firstChild && div.firstChild.childNodes :
+
+ // String was a bare <thead> or <tfoot>
+ wrap[1] == "<table>" && !hasBody ?
+ div.childNodes :
+ [];
+
+ for ( var j = tbody.length - 1; j >= 0 ; --j )
+ if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length )
+ tbody[ j ].parentNode.removeChild( tbody[ j ] );
+
+ }
+
+ // IE completely kills leading whitespace when innerHTML is used
+ if ( !jQuery.support.leadingWhitespace && /^\s/.test( elem ) )
+ div.insertBefore( context.createTextNode( elem.match(/^\s*/)[0] ), div.firstChild );
+
+ elem = jQuery.makeArray( div.childNodes );
+ }
+
+ if ( elem.nodeType )
+ ret.push( elem );
+ else
+ ret = jQuery.merge( ret, elem );
+
+ });
+
+ if ( fragment ) {
+ for ( var i = 0; ret[i]; i++ ) {
+ if ( jQuery.nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) {
+ scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] );
+ } else {
+ if ( ret[i].nodeType === 1 )
+ ret.splice.apply( ret, [i + 1, 0].concat(jQuery.makeArray(ret[i].getElementsByTagName("script"))) );
+ fragment.appendChild( ret[i] );
+ }
+ }
+
+ return scripts;
+ }
+
+ return ret;
+ },
+
+ attr: function( elem, name, value ) {
+ // don't set attributes on text and comment nodes
+ if (!elem || elem.nodeType == 3 || elem.nodeType == 8)
+ return undefined;
+
+ var notxml = !jQuery.isXMLDoc( elem ),
+ // Whether we are setting (or getting)
+ set = value !== undefined;
+
+ // Try to normalize/fix the name
+ name = notxml && jQuery.props[ name ] || name;
+
+ // Only do all the following if this is a node (faster for style)
+ // IE elem.getAttribute passes even for style
+ if ( elem.tagName ) {
+
+ // These attributes require special treatment
+ var special = /href|src|style/.test( name );
+
+ // Safari mis-reports the default selected property of a hidden option
+ // Accessing the parent's selectedIndex property fixes it
+ if ( name == "selected" && elem.parentNode )
+ elem.parentNode.selectedIndex;
+
+ // If applicable, access the attribute via the DOM 0 way
+ if ( name in elem && notxml && !special ) {
+ if ( set ){
+ // We can't allow the type property to be changed (since it causes problems in IE)
+ if ( name == "type" && jQuery.nodeName( elem, "input" ) && elem.parentNode )
+ throw "type property can't be changed";
+
+ elem[ name ] = value;
+ }
+
+ // browsers index elements by id/name on forms, give priority to attributes.
+ if( jQuery.nodeName( elem, "form" ) && elem.getAttributeNode(name) )
+ return elem.getAttributeNode( name ).nodeValue;
+
+ // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
+ // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
+ if ( name == "tabIndex" ) {
+ var attributeNode = elem.getAttributeNode( "tabIndex" );
+ return attributeNode && attributeNode.specified
+ ? attributeNode.value
+ : elem.nodeName.match(/(button|input|object|select|textarea)/i)
+ ? 0
+ : elem.nodeName.match(/^(a|area)$/i) && elem.href
+ ? 0
+ : undefined;
+ }
+
+ return elem[ name ];
+ }
+
+ if ( !jQuery.support.style && notxml && name == "style" )
+ return jQuery.attr( elem.style, "cssText", value );
+
+ if ( set )
+ // convert the value to a string (all browsers do this but IE) see #1070
+ elem.setAttribute( name, "" + value );
+
+ var attr = !jQuery.support.hrefNormalized && notxml && special
+ // Some attributes require a special call on IE
+ ? elem.getAttribute( name, 2 )
+ : elem.getAttribute( name );
+
+ // Non-existent attributes return null, we normalize to undefined
+ return attr === null ? undefined : attr;
+ }
+
+ // elem is actually elem.style ... set the style
+
+ // IE uses filters for opacity
+ if ( !jQuery.support.opacity && name == "opacity" ) {
+ if ( set ) {
+ // IE has trouble with opacity if it does not have layout
+ // Force it by setting the zoom level
+ elem.zoom = 1;
+
+ // Set the alpha filter to set the opacity
+ elem.filter = (elem.filter || "").replace( /alpha\([^)]*\)/, "" ) +
+ (parseInt( value ) + '' == "NaN" ? "" : "alpha(opacity=" + value * 100 + ")");
+ }
+
+ return elem.filter && elem.filter.indexOf("opacity=") >= 0 ?
+ (parseFloat( elem.filter.match(/opacity=([^)]*)/)[1] ) / 100) + '':
+ "";
+ }
+
+ name = name.replace(/-([a-z])/ig, function(all, letter){
+ return letter.toUpperCase();
+ });
+
+ if ( set )
+ elem[ name ] = value;
+
+ return elem[ name ];
+ },
+
+ trim: function( text ) {
+ return (text || "").replace( /^\s+|\s+$/g, "" );
+ },
+
+ makeArray: function( array ) {
+ var ret = [];
+
+ if( array != null ){
+ var i = array.length;
+ // The window, strings (and functions) also have 'length'
+ if( i == null || typeof array === "string" || jQuery.isFunction(array) || array.setInterval )
+ ret[0] = array;
+ else
+ while( i )
+ ret[--i] = array[i];
+ }
+
+ return ret;
+ },
+
+ inArray: function( elem, array ) {
+ for ( var i = 0, length = array.length; i < length; i++ )
+ // Use === because on IE, window == document
+ if ( array[ i ] === elem )
+ return i;
+
+ return -1;
+ },
+
+ merge: function( first, second ) {
+ // We have to loop this way because IE & Opera overwrite the length
+ // expando of getElementsByTagName
+ var i = 0, elem, pos = first.length;
+ // Also, we need to make sure that the correct elements are being returned
+ // (IE returns comment nodes in a '*' query)
+ if ( !jQuery.support.getAll ) {
+ while ( (elem = second[ i++ ]) != null )
+ if ( elem.nodeType != 8 )
+ first[ pos++ ] = elem;
+
+ } else
+ while ( (elem = second[ i++ ]) != null )
+ first[ pos++ ] = elem;
+
+ return first;
+ },
+
+ unique: function( array ) {
+ var ret = [], done = {};
+
+ try {
+
+ for ( var i = 0, length = array.length; i < length; i++ ) {
+ var id = jQuery.data( array[ i ] );
+
+ if ( !done[ id ] ) {
+ done[ id ] = true;
+ ret.push( array[ i ] );
+ }
+ }
+
+ } catch( e ) {
+ ret = array;
+ }
+
+ return ret;
+ },
+
+ grep: function( elems, callback, inv ) {
+ var ret = [];
+
+ // Go through the array, only saving the items
+ // that pass the validator function
+ for ( var i = 0, length = elems.length; i < length; i++ )
+ if ( !inv != !callback( elems[ i ], i ) )
+ ret.push( elems[ i ] );
+
+ return ret;
+ },
+
+ map: function( elems, callback ) {
+ var ret = [];
+
+ // Go through the array, translating each of the items to their
+ // new value (or values).
+ for ( var i = 0, length = elems.length; i < length; i++ ) {
+ var value = callback( elems[ i ], i );
+
+ if ( value != null )
+ ret[ ret.length ] = value;
+ }
+
+ return ret.concat.apply( [], ret );
+ }
+});
+
+// Use of jQuery.browser is deprecated.
+// It's included for backwards compatibility and plugins,
+// although they should work to migrate away.
+
+var userAgent = navigator.userAgent.toLowerCase();
+
+// Figure out what browser is being used
+jQuery.browser = {
+ version: (userAgent.match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [0,'0'])[1],
+ safari: /webkit/.test( userAgent ),
+ opera: /opera/.test( userAgent ),
+ msie: /msie/.test( userAgent ) && !/opera/.test( userAgent ),
+ mozilla: /mozilla/.test( userAgent ) && !/(compatible|webkit)/.test( userAgent )
+};
+
+jQuery.each({
+ parent: function(elem){return elem.parentNode;},
+ parents: function(elem){return jQuery.dir(elem,"parentNode");},
+ next: function(elem){return jQuery.nth(elem,2,"nextSibling");},
+ prev: function(elem){return jQuery.nth(elem,2,"previousSibling");},
+ nextAll: function(elem){return jQuery.dir(elem,"nextSibling");},
+ prevAll: function(elem){return jQuery.dir(elem,"previousSibling");},
+ siblings: function(elem){return jQuery.sibling(elem.parentNode.firstChild,elem);},
+ children: function(elem){return jQuery.sibling(elem.firstChild);},
+ contents: function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.makeArray(elem.childNodes);}
+}, function(name, fn){
+ jQuery.fn[ name ] = function( selector ) {
+ var ret = jQuery.map( this, fn );
+
+ if ( selector && typeof selector == "string" )
+ ret = jQuery.multiFilter( selector, ret );
+
+ return this.pushStack( jQuery.unique( ret ), name, selector );
+ };
+});
+
+jQuery.each({
+ appendTo: "append",
+ prependTo: "prepend",
+ insertBefore: "before",
+ insertAfter: "after",
+ replaceAll: "replaceWith"
+}, function(name, original){
+ jQuery.fn[ name ] = function( selector ) {
+ var ret = [], insert = jQuery( selector );
+
+ for ( var i = 0, l = insert.length; i < l; i++ ) {
+ var elems = (i > 0 ? this.clone(true) : this).get();
+ jQuery.fn[ original ].apply( jQuery(insert[i]), elems );
+ ret = ret.concat( elems );
+ }
+
+ return this.pushStack( ret, name, selector );
+ };
+});
+
+jQuery.each({
+ removeAttr: function( name ) {
+ jQuery.attr( this, name, "" );
+ if (this.nodeType == 1)
+ this.removeAttribute( name );
+ },
+
+ addClass: function( classNames ) {
+ jQuery.className.add( this, classNames );
+ },
+
+ removeClass: function( classNames ) {
+ jQuery.className.remove( this, classNames );
+ },
+
+ toggleClass: function( classNames, state ) {
+ if( typeof state !== "boolean" )
+ state = !jQuery.className.has( this, classNames );
+ jQuery.className[ state ? "add" : "remove" ]( this, classNames );
+ },
+
+ remove: function( selector ) {
+ if ( !selector || jQuery.filter( selector, [ this ] ).length ) {
+ // Prevent memory leaks
+ jQuery( "*", this ).add([this]).each(function(){
+ jQuery.event.remove(this);
+ jQuery.removeData(this);
+ });
+ if (this.parentNode)
+ this.parentNode.removeChild( this );
+ }
+ },
+
+ empty: function() {
+ // Remove element nodes and prevent memory leaks
+ jQuery(this).children().remove();
+
+ // Remove any remaining nodes
+ while ( this.firstChild )
+ this.removeChild( this.firstChild );
+ }
+}, function(name, fn){
+ jQuery.fn[ name ] = function(){
+ return this.each( fn, arguments );
+ };
+});
+
+// Helper function used by the dimensions and offset modules
+function num(elem, prop) {
+ return elem[0] && parseInt( jQuery.curCSS(elem[0], prop, true), 10 ) || 0;
+}
+var expando = "jQuery" + now(), uuid = 0, windowData = {};
+
+jQuery.extend({
+ cache: {},
+
+ data: function( elem, name, data ) {
+ elem = elem == window ?
+ windowData :
+ elem;
+
+ var id = elem[ expando ];
+
+ // Compute a unique ID for the element
+ if ( !id )
+ id = elem[ expando ] = ++uuid;
+
+ // Only generate the data cache if we're
+ // trying to access or manipulate it
+ if ( name && !jQuery.cache[ id ] )
+ jQuery.cache[ id ] = {};
+
+ // Prevent overriding the named cache with undefined values
+ if ( data !== undefined )
+ jQuery.cache[ id ][ name ] = data;
+
+ // Return the named cache data, or the ID for the element
+ return name ?
+ jQuery.cache[ id ][ name ] :
+ id;
+ },
+
+ removeData: function( elem, name ) {
+ elem = elem == window ?
+ windowData :
+ elem;
+
+ var id = elem[ expando ];
+
+ // If we want to remove a specific section of the element's data
+ if ( name ) {
+ if ( jQuery.cache[ id ] ) {
+ // Remove the section of cache data
+ delete jQuery.cache[ id ][ name ];
+
+ // If we've removed all the data, remove the element's cache
+ name = "";
+
+ for ( name in jQuery.cache[ id ] )
+ break;
+
+ if ( !name )
+ jQuery.removeData( elem );
+ }
+
+ // Otherwise, we want to remove all of the element's data
+ } else {
+ // Clean up the element expando
+ try {
+ delete elem[ expando ];
+ } catch(e){
+ // IE has trouble directly removing the expando
+ // but it's ok with using removeAttribute
+ if ( elem.removeAttribute )
+ elem.removeAttribute( expando );
+ }
+
+ // Completely remove the data cache
+ delete jQuery.cache[ id ];
+ }
+ },
+ queue: function( elem, type, data ) {
+ if ( elem ){
+
+ type = (type || "fx") + "queue";
+
+ var q = jQuery.data( elem, type );
+
+ if ( !q || jQuery.isArray(data) )
+ q = jQuery.data( elem, type, jQuery.makeArray(data) );
+ else if( data )
+ q.push( data );
+
+ }
+ return q;
+ },
+
+ dequeue: function( elem, type ){
+ var queue = jQuery.queue( elem, type ),
+ fn = queue.shift();
+
+ if( !type || type === "fx" )
+ fn = queue[0];
+
+ if( fn !== undefined )
+ fn.call(elem);
+ }
+});
+
+jQuery.fn.extend({
+ data: function( key, value ){
+ var parts = key.split(".");
+ parts[1] = parts[1] ? "." + parts[1] : "";
+
+ if ( value === undefined ) {
+ var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);
+
+ if ( data === undefined && this.length )
+ data = jQuery.data( this[0], key );
+
+ return data === undefined && parts[1] ?
+ this.data( parts[0] ) :
+ data;
+ } else
+ return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function(){
+ jQuery.data( this, key, value );
+ });
+ },
+
+ removeData: function( key ){
+ return this.each(function(){
+ jQuery.removeData( this, key );
+ });
+ },
+ queue: function(type, data){
+ if ( typeof type !== "string" ) {
+ data = type;
+ type = "fx";
+ }
+
+ if ( data === undefined )
+ return jQuery.queue( this[0], type );
+
+ return this.each(function(){
+ var queue = jQuery.queue( this, type, data );
+
+ if( type == "fx" && queue.length == 1 )
+ queue[0].call(this);
+ });
+ },
+ dequeue: function(type){
+ return this.each(function(){
+ jQuery.dequeue( this, type );
+ });
+ }
+});/*!
+ * Sizzle CSS Selector Engine - v0.9.3
+ * Copyright 2009, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ * More information: http://sizzlejs.com/
+ */
+(function(){
+
+var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,
+ done = 0,
+ toString = Object.prototype.toString;
+
+var Sizzle = function(selector, context, results, seed) {
+ results = results || [];
+ context = context || document;
+
+ if ( context.nodeType !== 1 && context.nodeType !== 9 )
+ return [];
+
+ if ( !selector || typeof selector !== "string" ) {
+ return results;
+ }
+
+ var parts = [], m, set, checkSet, check, mode, extra, prune = true;
+
+ // Reset the position of the chunker regexp (start from head)
+ chunker.lastIndex = 0;
+
+ while ( (m = chunker.exec(selector)) !== null ) {
+ parts.push( m[1] );
+
+ if ( m[2] ) {
+ extra = RegExp.rightContext;
+ break;
+ }
+ }
+
+ if ( parts.length > 1 && origPOS.exec( selector ) ) {
+ if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
+ set = posProcess( parts[0] + parts[1], context );
+ } else {
+ set = Expr.relative[ parts[0] ] ?
+ [ context ] :
+ Sizzle( parts.shift(), context );
+
+ while ( parts.length ) {
+ selector = parts.shift();
+
+ if ( Expr.relative[ selector ] )
+ selector += parts.shift();
+
+ set = posProcess( selector, set );
+ }
+ }
+ } else {
+ var ret = seed ?
+ { expr: parts.pop(), set: makeArray(seed) } :
+ Sizzle.find( parts.pop(), parts.length === 1 && context.parentNode ? context.parentNode : context, isXML(context) );
+ set = Sizzle.filter( ret.expr, ret.set );
+
+ if ( parts.length > 0 ) {
+ checkSet = makeArray(set);
+ } else {
+ prune = false;
+ }
+
+ while ( parts.length ) {
+ var cur = parts.pop(), pop = cur;
+
+ if ( !Expr.relative[ cur ] ) {
+ cur = "";
+ } else {
+ pop = parts.pop();
+ }
+
+ if ( pop == null ) {
+ pop = context;
+ }
+
+ Expr.relative[ cur ]( checkSet, pop, isXML(context) );
+ }
+ }
+
+ if ( !checkSet ) {
+ checkSet = set;
+ }
+
+ if ( !checkSet ) {
+ throw "Syntax error, unrecognized expression: " + (cur || selector);
+ }
+
+ if ( toString.call(checkSet) === "[object Array]" ) {
+ if ( !prune ) {
+ results.push.apply( results, checkSet );
+ } else if ( context.nodeType === 1 ) {
+ for ( var i = 0; checkSet[i] != null; i++ ) {
+ if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {
+ results.push( set[i] );
+ }
+ }
+ } else {
+ for ( var i = 0; checkSet[i] != null; i++ ) {
+ if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
+ results.push( set[i] );
+ }
+ }
+ }
+ } else {
+ makeArray( checkSet, results );
+ }
+
+ if ( extra ) {
+ Sizzle( extra, context, results, seed );
+
+ if ( sortOrder ) {
+ hasDuplicate = false;
+ results.sort(sortOrder);
+
+ if ( hasDuplicate ) {
+ for ( var i = 1; i < results.length; i++ ) {
+ if ( results[i] === results[i-1] ) {
+ results.splice(i--, 1);
+ }
+ }
+ }
+ }
+ }
+
+ return results;
+};
+
+Sizzle.matches = function(expr, set){
+ return Sizzle(expr, null, null, set);
+};
+
+Sizzle.find = function(expr, context, isXML){
+ var set, match;
+
+ if ( !expr ) {
+ return [];
+ }
+
+ for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
+ var type = Expr.order[i], match;
+
+ if ( (match = Expr.match[ type ].exec( expr )) ) {
+ var left = RegExp.leftContext;
+
+ if ( left.substr( left.length - 1 ) !== "\\" ) {
+ match[1] = (match[1] || "").replace(/\\/g, "");
+ set = Expr.find[ type ]( match, context, isXML );
+ if ( set != null ) {
+ expr = expr.replace( Expr.match[ type ], "" );
+ break;
+ }
+ }
+ }
+ }
+
+ if ( !set ) {
+ set = context.getElementsByTagName("*");
+ }
+
+ return {set: set, expr: expr};
+};
+
+Sizzle.filter = function(expr, set, inplace, not){
+ var old = expr, result = [], curLoop = set, match, anyFound,
+ isXMLFilter = set && set[0] && isXML(set[0]);
+
+ while ( expr && set.length ) {
+ for ( var type in Expr.filter ) {
+ if ( (match = Expr.match[ type ].exec( expr )) != null ) {
+ var filter = Expr.filter[ type ], found, item;
+ anyFound = false;
+
+ if ( curLoop == result ) {
+ result = [];
+ }
+
+ if ( Expr.preFilter[ type ] ) {
+ match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
+
+ if ( !match ) {
+ anyFound = found = true;
+ } else if ( match === true ) {
+ continue;
+ }
+ }
+
+ if ( match ) {
+ for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
+ if ( item ) {
+ found = filter( item, match, i, curLoop );
+ var pass = not ^ !!found;
+
+ if ( inplace && found != null ) {
+ if ( pass ) {
+ anyFound = true;
+ } else {
+ curLoop[i] = false;
+ }
+ } else if ( pass ) {
+ result.push( item );
+ anyFound = true;
+ }
+ }
+ }
+ }
+
+ if ( found !== undefined ) {
+ if ( !inplace ) {
+ curLoop = result;
+ }
+
+ expr = expr.replace( Expr.match[ type ], "" );
+
+ if ( !anyFound ) {
+ return [];
+ }
+
+ break;
+ }
+ }
+ }
+
+ // Improper expression
+ if ( expr == old ) {
+ if ( anyFound == null ) {
+ throw "Syntax error, unrecognized expression: " + expr;
+ } else {
+ break;
+ }
+ }
+
+ old = expr;
+ }
+
+ return curLoop;
+};
+
+var Expr = Sizzle.selectors = {
+ order: [ "ID", "NAME", "TAG" ],
+ match: {
+ ID: /#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
+ CLASS: /\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
+ NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)['"]*\]/,
+ ATTR: /\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
+ TAG: /^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/,
+ CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
+ POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
+ PSEUDO: /:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/
+ },
+ attrMap: {
+ "class": "className",
+ "for": "htmlFor"
+ },
+ attrHandle: {
+ href: function(elem){
+ return elem.getAttribute("href");
+ }
+ },
+ relative: {
+ "+": function(checkSet, part, isXML){
+ var isPartStr = typeof part === "string",
+ isTag = isPartStr && !/\W/.test(part),
+ isPartStrNotTag = isPartStr && !isTag;
+
+ if ( isTag && !isXML ) {
+ part = part.toUpperCase();
+ }
+
+ for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
+ if ( (elem = checkSet[i]) ) {
+ while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
+
+ checkSet[i] = isPartStrNotTag || elem && elem.nodeName === part ?
+ elem || false :
+ elem === part;
+ }
+ }
+
+ if ( isPartStrNotTag ) {
+ Sizzle.filter( part, checkSet, true );
+ }
+ },
+ ">": function(checkSet, part, isXML){
+ var isPartStr = typeof part === "string";
+
+ if ( isPartStr && !/\W/.test(part) ) {
+ part = isXML ? part : part.toUpperCase();
+
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+ var elem = checkSet[i];
+ if ( elem ) {
+ var parent = elem.parentNode;
+ checkSet[i] = parent.nodeName === part ? parent : false;
+ }
+ }
+ } else {
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+ var elem = checkSet[i];
+ if ( elem ) {
+ checkSet[i] = isPartStr ?
+ elem.parentNode :
+ elem.parentNode === part;
+ }
+ }
+
+ if ( isPartStr ) {
+ Sizzle.filter( part, checkSet, true );
+ }
+ }
+ },
+ "": function(checkSet, part, isXML){
+ var doneName = done++, checkFn = dirCheck;
+
+ if ( !part.match(/\W/) ) {
+ var nodeCheck = part = isXML ? part : part.toUpperCase();
+ checkFn = dirNodeCheck;
+ }
+
+ checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
+ },
+ "~": function(checkSet, part, isXML){
+ var doneName = done++, checkFn = dirCheck;
+
+ if ( typeof part === "string" && !part.match(/\W/) ) {
+ var nodeCheck = part = isXML ? part : part.toUpperCase();
+ checkFn = dirNodeCheck;
+ }
+
+ checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
+ }
+ },
+ find: {
+ ID: function(match, context, isXML){
+ if ( typeof context.getElementById !== "undefined" && !isXML ) {
+ var m = context.getElementById(match[1]);
+ return m ? [m] : [];
+ }
+ },
+ NAME: function(match, context, isXML){
+ if ( typeof context.getElementsByName !== "undefined" ) {
+ var ret = [], results = context.getElementsByName(match[1]);
+
+ for ( var i = 0, l = results.length; i < l; i++ ) {
+ if ( results[i].getAttribute("name") === match[1] ) {
+ ret.push( results[i] );
+ }
+ }
+
+ return ret.length === 0 ? null : ret;
+ }
+ },
+ TAG: function(match, context){
+ return context.getElementsByTagName(match[1]);
+ }
+ },
+ preFilter: {
+ CLASS: function(match, curLoop, inplace, result, not, isXML){
+ match = " " + match[1].replace(/\\/g, "") + " ";
+
+ if ( isXML ) {
+ return match;
+ }
+
+ for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
+ if ( elem ) {
+ if ( not ^ (elem.className && (" " + elem.className + " ").indexOf(match) >= 0) ) {
+ if ( !inplace )
+ result.push( elem );
+ } else if ( inplace ) {
+ curLoop[i] = false;
+ }
+ }
+ }
+
+ return false;
+ },
+ ID: function(match){
+ return match[1].replace(/\\/g, "");
+ },
+ TAG: function(match, curLoop){
+ for ( var i = 0; curLoop[i] === false; i++ ){}
+ return curLoop[i] && isXML(curLoop[i]) ? match[1] : match[1].toUpperCase();
+ },
+ CHILD: function(match){
+ if ( match[1] == "nth" ) {
+ // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
+ var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
+ match[2] == "even" && "2n" || match[2] == "odd" && "2n+1" ||
+ !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
+
+ // calculate the numbers (first)n+(last) including if they are negative
+ match[2] = (test[1] + (test[2] || 1)) - 0;
+ match[3] = test[3] - 0;
+ }
+
+ // TODO: Move to normal caching system
+ match[0] = done++;
+
+ return match;
+ },
+ ATTR: function(match, curLoop, inplace, result, not, isXML){
+ var name = match[1].replace(/\\/g, "");
+
+ if ( !isXML && Expr.attrMap[name] ) {
+ match[1] = Expr.attrMap[name];
+ }
+
+ if ( match[2] === "~=" ) {
+ match[4] = " " + match[4] + " ";
+ }
+
+ return match;
+ },
+ PSEUDO: function(match, curLoop, inplace, result, not){
+ if ( match[1] === "not" ) {
+ // If we're dealing with a complex expression, or a simple one
+ if ( match[3].match(chunker).length > 1 || /^\w/.test(match[3]) ) {
+ match[3] = Sizzle(match[3], null, null, curLoop);
+ } else {
+ var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
+ if ( !inplace ) {
+ result.push.apply( result, ret );
+ }
+ return false;
+ }
+ } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
+ return true;
+ }
+
+ return match;
+ },
+ POS: function(match){
+ match.unshift( true );
+ return match;
+ }
+ },
+ filters: {
+ enabled: function(elem){
+ return elem.disabled === false && elem.type !== "hidden";
+ },
+ disabled: function(elem){
+ return elem.disabled === true;
+ },
+ checked: function(elem){
+ return elem.checked === true;
+ },
+ selected: function(elem){
+ // Accessing this property makes selected-by-default
+ // options in Safari work properly
+ elem.parentNode.selectedIndex;
+ return elem.selected === true;
+ },
+ parent: function(elem){
+ return !!elem.firstChild;
+ },
+ empty: function(elem){
+ return !elem.firstChild;
+ },
+ has: function(elem, i, match){
+ return !!Sizzle( match[3], elem ).length;
+ },
+ header: function(elem){
+ return /h\d/i.test( elem.nodeName );
+ },
+ text: function(elem){
+ return "text" === elem.type;
+ },
+ radio: function(elem){
+ return "radio" === elem.type;
+ },
+ checkbox: function(elem){
+ return "checkbox" === elem.type;
+ },
+ file: function(elem){
+ return "file" === elem.type;
+ },
+ password: function(elem){
+ return "password" === elem.type;
+ },
+ submit: function(elem){
+ return "submit" === elem.type;
+ },
+ image: function(elem){
+ return "image" === elem.type;
+ },
+ reset: function(elem){
+ return "reset" === elem.type;
+ },
+ button: function(elem){
+ return "button" === elem.type || elem.nodeName.toUpperCase() === "BUTTON";
+ },
+ input: function(elem){
+ return /input|select|textarea|button/i.test(elem.nodeName);
+ }
+ },
+ setFilters: {
+ first: function(elem, i){
+ return i === 0;
+ },
+ last: function(elem, i, match, array){
+ return i === array.length - 1;
+ },
+ even: function(elem, i){
+ return i % 2 === 0;
+ },
+ odd: function(elem, i){
+ return i % 2 === 1;
+ },
+ lt: function(elem, i, match){
+ return i < match[3] - 0;
+ },
+ gt: function(elem, i, match){
+ return i > match[3] - 0;
+ },
+ nth: function(elem, i, match){
+ return match[3] - 0 == i;
+ },
+ eq: function(elem, i, match){
+ return match[3] - 0 == i;
+ }
+ },
+ filter: {
+ PSEUDO: function(elem, match, i, array){
+ var name = match[1], filter = Expr.filters[ name ];
+
+ if ( filter ) {
+ return filter( elem, i, match, array );
+ } else if ( name === "contains" ) {
+ return (elem.textContent || elem.innerText || "").indexOf(match[3]) >= 0;
+ } else if ( name === "not" ) {
+ var not = match[3];
+
+ for ( var i = 0, l = not.length; i < l; i++ ) {
+ if ( not[i] === elem ) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ },
+ CHILD: function(elem, match){
+ var type = match[1], node = elem;
+ switch (type) {
+ case 'only':
+ case 'first':
+ while (node = node.previousSibling) {
+ if ( node.nodeType === 1 ) return false;
+ }
+ if ( type == 'first') return true;
+ node = elem;
+ case 'last':
+ while (node = node.nextSibling) {
+ if ( node.nodeType === 1 ) return false;
+ }
+ return true;
+ case 'nth':
+ var first = match[2], last = match[3];
+
+ if ( first == 1 && last == 0 ) {
+ return true;
+ }
+
+ var doneName = match[0],
+ parent = elem.parentNode;
+
+ if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
+ var count = 0;
+ for ( node = parent.firstChild; node; node = node.nextSibling ) {
+ if ( node.nodeType === 1 ) {
+ node.nodeIndex = ++count;
+ }
+ }
+ parent.sizcache = doneName;
+ }
+
+ var diff = elem.nodeIndex - last;
+ if ( first == 0 ) {
+ return diff == 0;
+ } else {
+ return ( diff % first == 0 && diff / first >= 0 );
+ }
+ }
+ },
+ ID: function(elem, match){
+ return elem.nodeType === 1 && elem.getAttribute("id") === match;
+ },
+ TAG: function(elem, match){
+ return (match === "*" && elem.nodeType === 1) || elem.nodeName === match;
+ },
+ CLASS: function(elem, match){
+ return (" " + (elem.className || elem.getAttribute("class")) + " ")
+ .indexOf( match ) > -1;
+ },
+ ATTR: function(elem, match){
+ var name = match[1],
+ result = Expr.attrHandle[ name ] ?
+ Expr.attrHandle[ name ]( elem ) :
+ elem[ name ] != null ?
+ elem[ name ] :
+ elem.getAttribute( name ),
+ value = result + "",
+ type = match[2],
+ check = match[4];
+
+ return result == null ?
+ type === "!=" :
+ type === "=" ?
+ value === check :
+ type === "*=" ?
+ value.indexOf(check) >= 0 :
+ type === "~=" ?
+ (" " + value + " ").indexOf(check) >= 0 :
+ !check ?
+ value && result !== false :
+ type === "!=" ?
+ value != check :
+ type === "^=" ?
+ value.indexOf(check) === 0 :
+ type === "$=" ?
+ value.substr(value.length - check.length) === check :
+ type === "|=" ?
+ value === check || value.substr(0, check.length + 1) === check + "-" :
+ false;
+ },
+ POS: function(elem, match, i, array){
+ var name = match[2], filter = Expr.setFilters[ name ];
+
+ if ( filter ) {
+ return filter( elem, i, match, array );
+ }
+ }
+ }
+};
+
+var origPOS = Expr.match.POS;
+
+for ( var type in Expr.match ) {
+ Expr.match[ type ] = RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
+}
+
+var makeArray = function(array, results) {
+ array = Array.prototype.slice.call( array );
+
+ if ( results ) {
+ results.push.apply( results, array );
+ return results;
+ }
+
+ return array;
+};
+
+// Perform a simple check to determine if the browser is capable of
+// converting a NodeList to an array using builtin methods.
+try {
+ Array.prototype.slice.call( document.documentElement.childNodes );
+
+// Provide a fallback method if it does not work
+} catch(e){
+ makeArray = function(array, results) {
+ var ret = results || [];
+
+ if ( toString.call(array) === "[object Array]" ) {
+ Array.prototype.push.apply( ret, array );
+ } else {
+ if ( typeof array.length === "number" ) {
+ for ( var i = 0, l = array.length; i < l; i++ ) {
+ ret.push( array[i] );
+ }
+ } else {
+ for ( var i = 0; array[i]; i++ ) {
+ ret.push( array[i] );
+ }
+ }
+ }
+
+ return ret;
+ };
+}
+
+var sortOrder;
+
+if ( document.documentElement.compareDocumentPosition ) {
+ sortOrder = function( a, b ) {
+ var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
+ if ( ret === 0 ) {
+ hasDuplicate = true;
+ }
+ return ret;
+ };
+} else if ( "sourceIndex" in document.documentElement ) {
+ sortOrder = function( a, b ) {
+ var ret = a.sourceIndex - b.sourceIndex;
+ if ( ret === 0 ) {
+ hasDuplicate = true;
+ }
+ return ret;
+ };
+} else if ( document.createRange ) {
+ sortOrder = function( a, b ) {
+ var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
+ aRange.selectNode(a);
+ aRange.collapse(true);
+ bRange.selectNode(b);
+ bRange.collapse(true);
+ var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
+ if ( ret === 0 ) {
+ hasDuplicate = true;
+ }
+ return ret;
+ };
+}
+
+// Check to see if the browser returns elements by name when
+// querying by getElementById (and provide a workaround)
+(function(){
+ // We're going to inject a fake input element with a specified name
+ var form = document.createElement("form"),
+ id = "script" + (new Date).getTime();
+ form.innerHTML = "<input name='" + id + "'/>";
+
+ // Inject it into the root element, check its status, and remove it quickly
+ var root = document.documentElement;
+ root.insertBefore( form, root.firstChild );
+
+ // The workaround has to do additional checks after a getElementById
+ // Which slows things down for other browsers (hence the branching)
+ if ( !!document.getElementById( id ) ) {
+ Expr.find.ID = function(match, context, isXML){
+ if ( typeof context.getElementById !== "undefined" && !isXML ) {
+ var m = context.getElementById(match[1]);
+ return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
+ }
+ };
+
+ Expr.filter.ID = function(elem, match){
+ var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
+ return elem.nodeType === 1 && node && node.nodeValue === match;
+ };
+ }
+
+ root.removeChild( form );
+})();
+
+(function(){
+ // Check to see if the browser returns only elements
+ // when doing getElementsByTagName("*")
+
+ // Create a fake element
+ var div = document.createElement("div");
+ div.appendChild( document.createComment("") );
+
+ // Make sure no comments are found
+ if ( div.getElementsByTagName("*").length > 0 ) {
+ Expr.find.TAG = function(match, context){
+ var results = context.getElementsByTagName(match[1]);
+
+ // Filter out possible comments
+ if ( match[1] === "*" ) {
+ var tmp = [];
+
+ for ( var i = 0; results[i]; i++ ) {
+ if ( results[i].nodeType === 1 ) {
+ tmp.push( results[i] );
+ }
+ }
+
+ results = tmp;
+ }
+
+ return results;
+ };
+ }
+
+ // Check to see if an attribute returns normalized href attributes
+ div.innerHTML = "<a href='#'></a>";
+ if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
+ div.firstChild.getAttribute("href") !== "#" ) {
+ Expr.attrHandle.href = function(elem){
+ return elem.getAttribute("href", 2);
+ };
+ }
+})();
+
+if ( document.querySelectorAll ) (function(){
+ var oldSizzle = Sizzle, div = document.createElement("div");
+ div.innerHTML = "<p class='TEST'></p>";
+
+ // Safari can't handle uppercase or unicode characters when
+ // in quirks mode.
+ if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
+ return;
+ }
+
+ Sizzle = function(query, context, extra, seed){
+ context = context || document;
+
+ // Only use querySelectorAll on non-XML documents
+ // (ID selectors don't work in non-HTML documents)
+ if ( !seed && context.nodeType === 9 && !isXML(context) ) {
+ try {
+ return makeArray( context.querySelectorAll(query), extra );
+ } catch(e){}
+ }
+
+ return oldSizzle(query, context, extra, seed);
+ };
+
+ Sizzle.find = oldSizzle.find;
+ Sizzle.filter = oldSizzle.filter;
+ Sizzle.selectors = oldSizzle.selectors;
+ Sizzle.matches = oldSizzle.matches;
+})();
+
+if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){
+ var div = document.createElement("div");
+ div.innerHTML = "<div class='test e'></div><div class='test'></div>";
+
+ // Opera can't find a second classname (in 9.6)
+ if ( div.getElementsByClassName("e").length === 0 )
+ return;
+
+ // Safari caches class attributes, doesn't catch changes (in 3.2)
+ div.lastChild.className = "e";
+
+ if ( div.getElementsByClassName("e").length === 1 )
+ return;
+
+ Expr.order.splice(1, 0, "CLASS");
+ Expr.find.CLASS = function(match, context, isXML) {
+ if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
+ return context.getElementsByClassName(match[1]);
+ }
+ };
+})();
+
+function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+ var sibDir = dir == "previousSibling" && !isXML;
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+ var elem = checkSet[i];
+ if ( elem ) {
+ if ( sibDir && elem.nodeType === 1 ){
+ elem.sizcache = doneName;
+ elem.sizset = i;
+ }
+ elem = elem[dir];
+ var match = false;
+
+ while ( elem ) {
+ if ( elem.sizcache === doneName ) {
+ match = checkSet[elem.sizset];
+ break;
+ }
+
+ if ( elem.nodeType === 1 && !isXML ){
+ elem.sizcache = doneName;
+ elem.sizset = i;
+ }
+
+ if ( elem.nodeName === cur ) {
+ match = elem;
+ break;
+ }
+
+ elem = elem[dir];
+ }
+
+ checkSet[i] = match;
+ }
+ }
+}
+
+function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+ var sibDir = dir == "previousSibling" && !isXML;
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+ var elem = checkSet[i];
+ if ( elem ) {
+ if ( sibDir && elem.nodeType === 1 ) {
+ elem.sizcache = doneName;
+ elem.sizset = i;
+ }
+ elem = elem[dir];
+ var match = false;
+
+ while ( elem ) {
+ if ( elem.sizcache === doneName ) {
+ match = checkSet[elem.sizset];
+ break;
+ }
+
+ if ( elem.nodeType === 1 ) {
+ if ( !isXML ) {
+ elem.sizcache = doneName;
+ elem.sizset = i;
+ }
+ if ( typeof cur !== "string" ) {
+ if ( elem === cur ) {
+ match = true;
+ break;
+ }
+
+ } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
+ match = elem;
+ break;
+ }
+ }
+
+ elem = elem[dir];
+ }
+
+ checkSet[i] = match;
+ }
+ }
+}
+
+var contains = document.compareDocumentPosition ? function(a, b){
+ return a.compareDocumentPosition(b) & 16;
+} : function(a, b){
+ return a !== b && (a.contains ? a.contains(b) : true);
+};
+
+var isXML = function(elem){
+ return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
+ !!elem.ownerDocument && isXML( elem.ownerDocument );
+};
+
+var posProcess = function(selector, context){
+ var tmpSet = [], later = "", match,
+ root = context.nodeType ? [context] : context;
+
+ // Position selectors must be done after the filter
+ // And so must :not(positional) so we move all PSEUDOs to the end
+ while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
+ later += match[0];
+ selector = selector.replace( Expr.match.PSEUDO, "" );
+ }
+
+ selector = Expr.relative[selector] ? selector + "*" : selector;
+
+ for ( var i = 0, l = root.length; i < l; i++ ) {
+ Sizzle( selector, root[i], tmpSet );
+ }
+
+ return Sizzle.filter( later, tmpSet );
+};
+
+// EXPOSE
+jQuery.find = Sizzle;
+jQuery.filter = Sizzle.filter;
+jQuery.expr = Sizzle.selectors;
+jQuery.expr[":"] = jQuery.expr.filters;
+
+Sizzle.selectors.filters.hidden = function(elem){
+ return elem.offsetWidth === 0 || elem.offsetHeight === 0;
+};
+
+Sizzle.selectors.filters.visible = function(elem){
+ return elem.offsetWidth > 0 || elem.offsetHeight > 0;
+};
+
+Sizzle.selectors.filters.animated = function(elem){
+ return jQuery.grep(jQuery.timers, function(fn){
+ return elem === fn.elem;
+ }).length;
+};
+
+jQuery.multiFilter = function( expr, elems, not ) {
+ if ( not ) {
+ expr = ":not(" + expr + ")";
+ }
+
+ return Sizzle.matches(expr, elems);
+};
+
+jQuery.dir = function( elem, dir ){
+ var matched = [], cur = elem[dir];
+ while ( cur && cur != document ) {
+ if ( cur.nodeType == 1 )
+ matched.push( cur );
+ cur = cur[dir];
+ }
+ return matched;
+};
+
+jQuery.nth = function(cur, result, dir, elem){
+ result = result || 1;
+ var num = 0;
+
+ for ( ; cur; cur = cur[dir] )
+ if ( cur.nodeType == 1 && ++num == result )
+ break;
+
+ return cur;
+};
+
+jQuery.sibling = function(n, elem){
+ var r = [];
+
+ for ( ; n; n = n.nextSibling ) {
+ if ( n.nodeType == 1 && n != elem )
+ r.push( n );
+ }
+
+ return r;
+};
+
+return;
+
+window.Sizzle = Sizzle;
+
+})();
+/*
+ * A number of helper functions used for managing events.
+ * Many of the ideas behind this code originated from
+ * Dean Edwards' addEvent library.
+ */
+jQuery.event = {
+
+ // Bind an event to an element
+ // Original by Dean Edwards
+ add: function(elem, types, handler, data) {
+ if ( elem.nodeType == 3 || elem.nodeType == 8 )
+ return;
+
+ // For whatever reason, IE has trouble passing the window object
+ // around, causing it to be cloned in the process
+ if ( elem.setInterval && elem != window )
+ elem = window;
+
+ // Make sure that the function being executed has a unique ID
+ if ( !handler.guid )
+ handler.guid = this.guid++;
+
+ // if data is passed, bind to handler
+ if ( data !== undefined ) {
+ // Create temporary function pointer to original handler
+ var fn = handler;
+
+ // Create unique handler function, wrapped around original handler
+ handler = this.proxy( fn );
+
+ // Store data in unique handler
+ handler.data = data;
+ }
+
+ // Init the element's event structure
+ var events = jQuery.data(elem, "events") || jQuery.data(elem, "events", {}),
+ handle = jQuery.data(elem, "handle") || jQuery.data(elem, "handle", function(){
+ // Handle the second event of a trigger and when
+ // an event is called after a page has unloaded
+ return typeof jQuery !== "undefined" && !jQuery.event.triggered ?
+ jQuery.event.handle.apply(arguments.callee.elem, arguments) :
+ undefined;
+ });
+ // Add elem as a property of the handle function
+ // This is to prevent a memory leak with non-native
+ // event in IE.
+ handle.elem = elem;
+
+ // Handle multiple events separated by a space
+ // jQuery(...).bind("mouseover mouseout", fn);
+ jQuery.each(types.split(/\s+/), function(index, type) {
+ // Namespaced event handlers
+ var namespaces = type.split(".");
+ type = namespaces.shift();
+ handler.type = namespaces.slice().sort().join(".");
+
+ // Get the current list of functions bound to this event
+ var handlers = events[type];
+
+ if ( jQuery.event.specialAll[type] )
+ jQuery.event.specialAll[type].setup.call(elem, data, namespaces);
+
+ // Init the event handler queue
+ if (!handlers) {
+ handlers = events[type] = {};
+
+ // Check for a special event handler
+ // Only use addEventListener/attachEvent if the special
+ // events handler returns false
+ if ( !jQuery.event.special[type] || jQuery.event.special[type].setup.call(elem, data, namespaces) === false ) {
+ // Bind the global event handler to the element
+ if (elem.addEventListener)
+ elem.addEventListener(type, handle, false);
+ else if (elem.attachEvent)
+ elem.attachEvent("on" + type, handle);
+ }
+ }
+
+ // Add the function to the element's handler list
+ handlers[handler.guid] = handler;
+
+ // Keep track of which events have been used, for global triggering
+ jQuery.event.global[type] = true;
+ });
+
+ // Nullify elem to prevent memory leaks in IE
+ elem = null;
+ },
+
+ guid: 1,
+ global: {},
+
+ // Detach an event or set of events from an element
+ remove: function(elem, types, handler) {
+ // don't do events on text and comment nodes
+ if ( elem.nodeType == 3 || elem.nodeType == 8 )
+ return;
+
+ var events = jQuery.data(elem, "events"), ret, index;
+
+ if ( events ) {
+ // Unbind all events for the element
+ if ( types === undefined || (typeof types === "string" && types.charAt(0) == ".") )
+ for ( var type in events )
+ this.remove( elem, type + (types || "") );
+ else {
+ // types is actually an event object here
+ if ( types.type ) {
+ handler = types.handler;
+ types = types.type;
+ }
+
+ // Handle multiple events seperated by a space
+ // jQuery(...).unbind("mouseover mouseout", fn);
+ jQuery.each(types.split(/\s+/), function(index, type){
+ // Namespaced event handlers
+ var namespaces = type.split(".");
+ type = namespaces.shift();
+ var namespace = RegExp("(^|\\.)" + namespaces.slice().sort().join(".*\\.") + "(\\.|$)");
+
+ if ( events[type] ) {
+ // remove the given handler for the given type
+ if ( handler )
+ delete events[type][handler.guid];
+
+ // remove all handlers for the given type
+ else
+ for ( var handle in events[type] )
+ // Handle the removal of namespaced events
+ if ( namespace.test(events[type][handle].type) )
+ delete events[type][handle];
+
+ if ( jQuery.event.specialAll[type] )
+ jQuery.event.specialAll[type].teardown.call(elem, namespaces);
+
+ // remove generic event handler if no more handlers exist
+ for ( ret in events[type] ) break;
+ if ( !ret ) {
+ if ( !jQuery.event.special[type] || jQuery.event.special[type].teardown.call(elem, namespaces) === false ) {
+ if (elem.removeEventListener)
+ elem.removeEventListener(type, jQuery.data(elem, "handle"), false);
+ else if (elem.detachEvent)
+ elem.detachEvent("on" + type, jQuery.data(elem, "handle"));
+ }
+ ret = null;
+ delete events[type];
+ }
+ }
+ });
+ }
+
+ // Remove the expando if it's no longer used
+ for ( ret in events ) break;
+ if ( !ret ) {
+ var handle = jQuery.data( elem, "handle" );
+ if ( handle ) handle.elem = null;
+ jQuery.removeData( elem, "events" );
+ jQuery.removeData( elem, "handle" );
+ }
+ }
+ },
+
+ // bubbling is internal
+ trigger: function( event, data, elem, bubbling ) {
+ // Event object or event type
+ var type = event.type || event;
+
+ if( !bubbling ){
+ event = typeof event === "object" ?
+ // jQuery.Event object
+ event[expando] ? event :
+ // Object literal
+ jQuery.extend( jQuery.Event(type), event ) :
+ // Just the event type (string)
+ jQuery.Event(type);
+
+ if ( type.indexOf("!") >= 0 ) {
+ event.type = type = type.slice(0, -1);
+ event.exclusive = true;
+ }
+
+ // Handle a global trigger
+ if ( !elem ) {
+ // Don't bubble custom events when global (to avoid too much overhead)
+ event.stopPropagation();
+ // Only trigger if we've ever bound an event for it
+ if ( this.global[type] )
+ jQuery.each( jQuery.cache, function(){
+ if ( this.events && this.events[type] )
+ jQuery.event.trigger( event, data, this.handle.elem );
+ });
+ }
+
+ // Handle triggering a single element
+
+ // don't do events on text and comment nodes
+ if ( !elem || elem.nodeType == 3 || elem.nodeType == 8 )
+ return undefined;
+
+ // Clean up in case it is reused
+ event.result = undefined;
+ event.target = elem;
+
+ // Clone the incoming data, if any
+ data = jQuery.makeArray(data);
+ data.unshift( event );
+ }
+
+ event.currentTarget = elem;
+
+ // Trigger the event, it is assumed that "handle" is a function
+ var handle = jQuery.data(elem, "handle");
+ if ( handle )
+ handle.apply( elem, data );
+
+ // Handle triggering native .onfoo handlers (and on links since we don't call .click() for links)
+ if ( (!elem[type] || (jQuery.nodeName(elem, 'a') && type == "click")) && elem["on"+type] && elem["on"+type].apply( elem, data ) === false )
+ event.result = false;
+
+ // Trigger the native events (except for clicks on links)
+ if ( !bubbling && elem[type] && !event.isDefaultPrevented() && !(jQuery.nodeName(elem, 'a') && type == "click") ) {
+ this.triggered = true;
+ try {
+ elem[ type ]();
+ // prevent IE from throwing an error for some hidden elements
+ } catch (e) {}
+ }
+
+ this.triggered = false;
+
+ if ( !event.isPropagationStopped() ) {
+ var parent = elem.parentNode || elem.ownerDocument;
+ if ( parent )
+ jQuery.event.trigger(event, data, parent, true);
+ }
+ },
+
+ handle: function(event) {
+ // returned undefined or false
+ var all, handlers;
+
+ event = arguments[0] = jQuery.event.fix( event || window.event );
+ event.currentTarget = this;
+
+ // Namespaced event handlers
+ var namespaces = event.type.split(".");
+ event.type = namespaces.shift();
+
+ // Cache this now, all = true means, any handler
+ all = !namespaces.length && !event.exclusive;
+
+ var namespace = RegExp("(^|\\.)" + namespaces.slice().sort().join(".*\\.") + "(\\.|$)");
+
+ handlers = ( jQuery.data(this, "events") || {} )[event.type];
+
+ for ( var j in handlers ) {
+ var handler = handlers[j];
+
+ // Filter the functions by class
+ if ( all || namespace.test(handler.type) ) {
+ // Pass in a reference to the handler function itself
+ // So that we can later remove it
+ event.handler = handler;
+ event.data = handler.data;
+
+ var ret = handler.apply(this, arguments);
+
+ if( ret !== undefined ){
+ event.result = ret;
+ if ( ret === false ) {
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ }
+
+ if( event.isImmediatePropagationStopped() )
+ break;
+
+ }
+ }
+ },
+
+ props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
+
+ fix: function(event) {
+ if ( event[expando] )
+ return event;
+
+ // store a copy of the original event object
+ // and "clone" to set read-only properties
+ var originalEvent = event;
+ event = jQuery.Event( originalEvent );
+
+ for ( var i = this.props.length, prop; i; ){
+ prop = this.props[ --i ];
+ event[ prop ] = originalEvent[ prop ];
+ }
+
+ // Fix target property, if necessary
+ if ( !event.target )
+ event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either
+
+ // check if target is a textnode (safari)
+ if ( event.target.nodeType == 3 )
+ event.target = event.target.parentNode;
+
+ // Add relatedTarget, if necessary
+ if ( !event.relatedTarget && event.fromElement )
+ event.relatedTarget = event.fromElement == event.target ? event.toElement : event.fromElement;
+
+ // Calculate pageX/Y if missing and clientX/Y available
+ if ( event.pageX == null && event.clientX != null ) {
+ var doc = document.documentElement, body = document.body;
+ event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc.clientLeft || 0);
+ event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc.clientTop || 0);
+ }
+
+ // Add which for key events
+ if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) )
+ event.which = event.charCode || event.keyCode;
+
+ // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
+ if ( !event.metaKey && event.ctrlKey )
+ event.metaKey = event.ctrlKey;
+
+ // Add which for click: 1 == left; 2 == middle; 3 == right
+ // Note: button is not normalized, so don't use it
+ if ( !event.which && event.button )
+ event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
+
+ return event;
+ },
+
+ proxy: function( fn, proxy ){
+ proxy = proxy || function(){ return fn.apply(this, arguments); };
+ // Set the guid of unique handler to the same of original handler, so it can be removed
+ proxy.guid = fn.guid = fn.guid || proxy.guid || this.guid++;
+ // So proxy can be declared as an argument
+ return proxy;
+ },
+
+ special: {
+ ready: {
+ // Make sure the ready event is setup
+ setup: bindReady,
+ teardown: function() {}
+ }
+ },
+
+ specialAll: {
+ live: {
+ setup: function( selector, namespaces ){
+ jQuery.event.add( this, namespaces[0], liveHandler );
+ },
+ teardown: function( namespaces ){
+ if ( namespaces.length ) {
+ var remove = 0, name = RegExp("(^|\\.)" + namespaces[0] + "(\\.|$)");
+
+ jQuery.each( (jQuery.data(this, "events").live || {}), function(){
+ if ( name.test(this.type) )
+ remove++;
+ });
+
+ if ( remove < 1 )
+ jQuery.event.remove( this, namespaces[0], liveHandler );
+ }
+ }
+ }
+ }
+};
+
+jQuery.Event = function( src ){
+ // Allow instantiation without the 'new' keyword
+ if( !this.preventDefault )
+ return new jQuery.Event(src);
+
+ // Event object
+ if( src && src.type ){
+ this.originalEvent = src;
+ this.type = src.type;
+ // Event type
+ }else
+ this.type = src;
+
+ // timeStamp is buggy for some events on Firefox(#3843)
+ // So we won't rely on the native value
+ this.timeStamp = now();
+
+ // Mark it as fixed
+ this[expando] = true;
+};
+
+function returnFalse(){
+ return false;
+}
+function returnTrue(){
+ return true;
+}
+
+// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
+// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
+jQuery.Event.prototype = {
+ preventDefault: function() {
+ this.isDefaultPrevented = returnTrue;
+
+ var e = this.originalEvent;
+ if( !e )
+ return;
+ // if preventDefault exists run it on the original event
+ if (e.preventDefault)
+ e.preventDefault();
+ // otherwise set the returnValue property of the original event to false (IE)
+ e.returnValue = false;
+ },
+ stopPropagation: function() {
+ this.isPropagationStopped = returnTrue;
+
+ var e = this.originalEvent;
+ if( !e )
+ return;
+ // if stopPropagation exists run it on the original event
+ if (e.stopPropagation)
+ e.stopPropagation();
+ // otherwise set the cancelBubble property of the original event to true (IE)
+ e.cancelBubble = true;
+ },
+ stopImmediatePropagation:function(){
+ this.isImmediatePropagationStopped = returnTrue;
+ this.stopPropagation();
+ },
+ isDefaultPrevented: returnFalse,
+ isPropagationStopped: returnFalse,
+ isImmediatePropagationStopped: returnFalse
+};
+// Checks if an event happened on an element within another element
+// Used in jQuery.event.special.mouseenter and mouseleave handlers
+var withinElement = function(event) {
+ // Check if mouse(over|out) are still within the same parent element
+ var parent = event.relatedTarget;
+ // Traverse up the tree
+ while ( parent && parent != this )
+ try { parent = parent.parentNode; }
+ catch(e) { parent = this; }
+
+ if( parent != this ){
+ // set the correct event type
+ event.type = event.data;
+ // handle event if we actually just moused on to a non sub-element
+ jQuery.event.handle.apply( this, arguments );
+ }
+};
+
+jQuery.each({
+ mouseover: 'mouseenter',
+ mouseout: 'mouseleave'
+}, function( orig, fix ){
+ jQuery.event.special[ fix ] = {
+ setup: function(){
+ jQuery.event.add( this, orig, withinElement, fix );
+ },
+ teardown: function(){
+ jQuery.event.remove( this, orig, withinElement );
+ }
+ };
+});
+
+jQuery.fn.extend({
+ bind: function( type, data, fn ) {
+ return type == "unload" ? this.one(type, data, fn) : this.each(function(){
+ jQuery.event.add( this, type, fn || data, fn && data );
+ });
+ },
+
+ one: function( type, data, fn ) {
+ var one = jQuery.event.proxy( fn || data, function(event) {
+ jQuery(this).unbind(event, one);
+ return (fn || data).apply( this, arguments );
+ });
+ return this.each(function(){
+ jQuery.event.add( this, type, one, fn && data);
+ });
+ },
+
+ unbind: function( type, fn ) {
+ return this.each(function(){
+ jQuery.event.remove( this, type, fn );
+ });
+ },
+
+ trigger: function( type, data ) {
+ return this.each(function(){
+ jQuery.event.trigger( type, data, this );
+ });
+ },
+
+ triggerHandler: function( type, data ) {
+ if( this[0] ){
+ var event = jQuery.Event(type);
+ event.preventDefault();
+ event.stopPropagation();
+ jQuery.event.trigger( event, data, this[0] );
+ return event.result;
+ }
+ },
+
+ toggle: function( fn ) {
+ // Save reference to arguments for access in closure
+ var args = arguments, i = 1;
+
+ // link all the functions, so any of them can unbind this click handler
+ while( i < args.length )
+ jQuery.event.proxy( fn, args[i++] );
+
+ return this.click( jQuery.event.proxy( fn, function(event) {
+ // Figure out which function to execute
+ this.lastToggle = ( this.lastToggle || 0 ) % i;
+
+ // Make sure that clicks stop
+ event.preventDefault();
+
+ // and execute the function
+ return args[ this.lastToggle++ ].apply( this, arguments ) || false;
+ }));
+ },
+
+ hover: function(fnOver, fnOut) {
+ return this.mouseenter(fnOver).mouseleave(fnOut);
+ },
+
+ ready: function(fn) {
+ // Attach the listeners
+ bindReady();
+
+ // If the DOM is already ready
+ if ( jQuery.isReady )
+ // Execute the function immediately
+ fn.call( document, jQuery );
+
+ // Otherwise, remember the function for later
+ else
+ // Add the function to the wait list
+ jQuery.readyList.push( fn );
+
+ return this;
+ },
+
+ live: function( type, fn ){
+ var proxy = jQuery.event.proxy( fn );
+ proxy.guid += this.selector + type;
+
+ jQuery(document).bind( liveConvert(type, this.selector), this.selector, proxy );
+
+ return this;
+ },
+
+ die: function( type, fn ){
+ jQuery(document).unbind( liveConvert(type, this.selector), fn ? { guid: fn.guid + this.selector + type } : null );
+ return this;
+ }
+});
+
+function liveHandler( event ){
+ var check = RegExp("(^|\\.)" + event.type + "(\\.|$)"),
+ stop = true,
+ elems = [];
+
+ jQuery.each(jQuery.data(this, "events").live || [], function(i, fn){
+ if ( check.test(fn.type) ) {
+ var elem = jQuery(event.target).closest(fn.data)[0];
+ if ( elem )
+ elems.push({ elem: elem, fn: fn });
+ }
+ });
+
+ elems.sort(function(a,b) {
+ return jQuery.data(a.elem, "closest") - jQuery.data(b.elem, "closest");
+ });
+
+ jQuery.each(elems, function(){
+ if ( this.fn.call(this.elem, event, this.fn.data) === false )
+ return (stop = false);
+ });
+
+ return stop;
+}
+
+function liveConvert(type, selector){
+ return ["live", type, selector.replace(/\./g, "`").replace(/ /g, "|")].join(".");
+}
+
+jQuery.extend({
+ isReady: false,
+ readyList: [],
+ // Handle when the DOM is ready
+ ready: function() {
+ // Make sure that the DOM is not already loaded
+ if ( !jQuery.isReady ) {
+ // Remember that the DOM is ready
+ jQuery.isReady = true;
+
+ // If there are functions bound, to execute
+ if ( jQuery.readyList ) {
+ // Execute all of them
+ jQuery.each( jQuery.readyList, function(){
+ this.call( document, jQuery );
+ });
+
+ // Reset the list of functions
+ jQuery.readyList = null;
+ }
+
+ // Trigger any bound ready events
+ jQuery(document).triggerHandler("ready");
+ }
+ }
+});
+
+var readyBound = false;
+
+function bindReady(){
+ if ( readyBound ) return;
+ readyBound = true;
+
+ // Mozilla, Opera and webkit nightlies currently support this event
+ if ( document.addEventListener ) {
+ // Use the handy event callback
+ document.addEventListener( "DOMContentLoaded", function(){
+ document.removeEventListener( "DOMContentLoaded", arguments.callee, false );
+ jQuery.ready();
+ }, false );
+
+ // If IE event model is used
+ } else if ( document.attachEvent ) {
+ // ensure firing before onload,
+ // maybe late but safe also for iframes
+ document.attachEvent("onreadystatechange", function(){
+ if ( document.readyState === "complete" ) {
+ document.detachEvent( "onreadystatechange", arguments.callee );
+ jQuery.ready();
+ }
+ });
+
+ // If IE and not an iframe
+ // continually check to see if the document is ready
+ if ( document.documentElement.doScroll && window == window.top ) (function(){
+ if ( jQuery.isReady ) return;
+
+ try {
+ // If IE is used, use the trick by Diego Perini
+ // http://javascript.nwbox.com/IEContentLoaded/
+ document.documentElement.doScroll("left");
+ } catch( error ) {
+ setTimeout( arguments.callee, 0 );
+ return;
+ }
+
+ // and execute any waiting functions
+ jQuery.ready();
+ })();
+ }
+
+ // A fallback to window.onload, that will always work
+ jQuery.event.add( window, "load", jQuery.ready );
+}
+
+jQuery.each( ("blur,focus,load,resize,scroll,unload,click,dblclick," +
+ "mousedown,mouseup,mousemove,mouseover,mouseout,mouseenter,mouseleave," +
+ "change,select,submit,keydown,keypress,keyup,error").split(","), function(i, name){
+
+ // Handle event binding
+ jQuery.fn[name] = function(fn){
+ return fn ? this.bind(name, fn) : this.trigger(name);
+ };
+});
+
+// Prevent memory leaks in IE
+// And prevent errors on refresh with events like mouseover in other browsers
+// Window isn't included so as not to unbind existing unload events
+jQuery( window ).bind( 'unload', function(){
+ for ( var id in jQuery.cache )
+ // Skip the window
+ if ( id != 1 && jQuery.cache[ id ].handle )
+ jQuery.event.remove( jQuery.cache[ id ].handle.elem );
+});
+(function(){
+
+ jQuery.support = {};
+
+ var root = document.documentElement,
+ script = document.createElement("script"),
+ div = document.createElement("div"),
+ id = "script" + (new Date).getTime();
+
+ div.style.display = "none";
+ div.innerHTML = ' <link/><table></table><a href="/a" style="color:red;float:left;opacity:.5;">a</a><select><option>text</option></select><object><param/></object>';
+
+ var all = div.getElementsByTagName("*"),
+ a = div.getElementsByTagName("a")[0];
+
+ // Can't get basic test support
+ if ( !all || !all.length || !a ) {
+ return;
+ }
+
+ jQuery.support = {
+ // IE strips leading whitespace when .innerHTML is used
+ leadingWhitespace: div.firstChild.nodeType == 3,
+
+ // Make sure that tbody elements aren't automatically inserted
+ // IE will insert them into empty tables
+ tbody: !div.getElementsByTagName("tbody").length,
+
+ // Make sure that you can get all elements in an <object> element
+ // IE 7 always returns no results
+ objectAll: !!div.getElementsByTagName("object")[0]
+ .getElementsByTagName("*").length,
+
+ // Make sure that link elements get serialized correctly by innerHTML
+ // This requires a wrapper element in IE
+ htmlSerialize: !!div.getElementsByTagName("link").length,
+
+ // Get the style information from getAttribute
+ // (IE uses .cssText insted)
+ style: /red/.test( a.getAttribute("style") ),
+
+ // Make sure that URLs aren't manipulated
+ // (IE normalizes it by default)
+ hrefNormalized: a.getAttribute("href") === "/a",
+
+ // Make sure that element opacity exists
+ // (IE uses filter instead)
+ opacity: a.style.opacity === "0.5",
+
+ // Verify style float existence
+ // (IE uses styleFloat instead of cssFloat)
+ cssFloat: !!a.style.cssFloat,
+
+ // Will be defined later
+ scriptEval: false,
+ noCloneEvent: true,
+ boxModel: null
+ };
+
+ script.type = "text/javascript";
+ try {
+ script.appendChild( document.createTextNode( "window." + id + "=1;" ) );
+ } catch(e){}
+
+ root.insertBefore( script, root.firstChild );
+
+ // Make sure that the execution of code works by injecting a script
+ // tag with appendChild/createTextNode
+ // (IE doesn't support this, fails, and uses .text instead)
+ if ( window[ id ] ) {
+ jQuery.support.scriptEval = true;
+ delete window[ id ];
+ }
+
+ root.removeChild( script );
+
+ if ( div.attachEvent && div.fireEvent ) {
+ div.attachEvent("onclick", function(){
+ // Cloning a node shouldn't copy over any
+ // bound event handlers (IE does this)
+ jQuery.support.noCloneEvent = false;
+ div.detachEvent("onclick", arguments.callee);
+ });
+ div.cloneNode(true).fireEvent("onclick");
+ }
+
+ // Figure out if the W3C box model works as expected
+ // document.body must exist before we can do this
+ jQuery(function(){
+ var div = document.createElement("div");
+ div.style.width = div.style.paddingLeft = "1px";
+
+ document.body.appendChild( div );
+ jQuery.boxModel = jQuery.support.boxModel = div.offsetWidth === 2;
+ document.body.removeChild( div ).style.display = 'none';
+ });
+})();
+
+var styleFloat = jQuery.support.cssFloat ? "cssFloat" : "styleFloat";
+
+jQuery.props = {
+ "for": "htmlFor",
+ "class": "className",
+ "float": styleFloat,
+ cssFloat: styleFloat,
+ styleFloat: styleFloat,
+ readonly: "readOnly",
+ maxlength: "maxLength",
+ cellspacing: "cellSpacing",
+ rowspan: "rowSpan",
+ tabindex: "tabIndex"
+};
+jQuery.fn.extend({
+ // Keep a copy of the old load
+ _load: jQuery.fn.load,
+
+ load: function( url, params, callback ) {
+ if ( typeof url !== "string" )
+ return this._load( url );
+
+ var off = url.indexOf(" ");
+ if ( off >= 0 ) {
+ var selector = url.slice(off, url.length);
+ url = url.slice(0, off);
+ }
+
+ // Default to a GET request
+ var type = "GET";
+
+ // If the second parameter was provided
+ if ( params )
+ // If it's a function
+ if ( jQuery.isFunction( params ) ) {
+ // We assume that it's the callback
+ callback = params;
+ params = null;
+
+ // Otherwise, build a param string
+ } else if( typeof params === "object" ) {
+ params = jQuery.param( params );
+ type = "POST";
+ }
+
+ var self = this;
+
+ // Request the remote document
+ jQuery.ajax({
+ url: url,
+ type: type,
+ dataType: "html",
+ data: params,
+ complete: function(res, status){
+ // If successful, inject the HTML into all the matched elements
+ if ( status == "success" || status == "notmodified" )
+ // See if a selector was specified
+ self.html( selector ?
+ // Create a dummy div to hold the results
+ jQuery("<div/>")
+ // inject the contents of the document in, removing the scripts
+ // to avoid any 'Permission Denied' errors in IE
+ .append(res.responseText.replace(/<script(.|\s)*?\/script>/g, ""))
+
+ // Locate the specified elements
+ .find(selector) :
+
+ // If not, just inject the full result
+ res.responseText );
+
+ if( callback )
+ self.each( callback, [res.responseText, status, res] );
+ }
+ });
+ return this;
+ },
+
+ serialize: function() {
+ return jQuery.param(this.serializeArray());
+ },
+ serializeArray: function() {
+ return this.map(function(){
+ return this.elements ? jQuery.makeArray(this.elements) : this;
+ })
+ .filter(function(){
+ return this.name && !this.disabled &&
+ (this.checked || /select|textarea/i.test(this.nodeName) ||
+ /text|hidden|password|search/i.test(this.type));
+ })
+ .map(function(i, elem){
+ var val = jQuery(this).val();
+ return val == null ? null :
+ jQuery.isArray(val) ?
+ jQuery.map( val, function(val, i){
+ return {name: elem.name, value: val};
+ }) :
+ {name: elem.name, value: val};
+ }).get();
+ }
+});
+
+// Attach a bunch of functions for handling common AJAX events
+jQuery.each( "ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","), function(i,o){
+ jQuery.fn[o] = function(f){
+ return this.bind(o, f);
+ };
+});
+
+var jsc = now();
+
+jQuery.extend({
+
+ get: function( url, data, callback, type ) {
+ // shift arguments if data argument was ommited
+ if ( jQuery.isFunction( data ) ) {
+ callback = data;
+ data = null;
+ }
+
+ return jQuery.ajax({
+ type: "GET",
+ url: url,
+ data: data,
+ success: callback,
+ dataType: type
+ });
+ },
+
+ getScript: function( url, callback ) {
+ return jQuery.get(url, null, callback, "script");
+ },
+
+ getJSON: function( url, data, callback ) {
+ return jQuery.get(url, data, callback, "json");
+ },
+
+ post: function( url, data, callback, type ) {
+ if ( jQuery.isFunction( data ) ) {
+ callback = data;
+ data = {};
+ }
+
+ return jQuery.ajax({
+ type: "POST",
+ url: url,
+ data: data,
+ success: callback,
+ dataType: type
+ });
+ },
+
+ ajaxSetup: function( settings ) {
+ jQuery.extend( jQuery.ajaxSettings, settings );
+ },
+
+ ajaxSettings: {
+ url: location.href,
+ global: true,
+ type: "GET",
+ contentType: "application/x-www-form-urlencoded",
+ processData: true,
+ async: true,
+ /*
+ timeout: 0,
+ data: null,
+ username: null,
+ password: null,
+ */
+ // Create the request object; Microsoft failed to properly
+ // implement the XMLHttpRequest in IE7, so we use the ActiveXObject when it is available
+ // This function can be overriden by calling jQuery.ajaxSetup
+ xhr:function(){
+ return window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest();
+ },
+ accepts: {
+ xml: "application/xml, text/xml",
+ html: "text/html",
+ script: "text/javascript, application/javascript",
+ json: "application/json, text/javascript",
+ text: "text/plain",
+ _default: "*/*"
+ }
+ },
+
+ // Last-Modified header cache for next request
+ lastModified: {},
+
+ ajax: function( s ) {
+ // Extend the settings, but re-extend 's' so that it can be
+ // checked again later (in the test suite, specifically)
+ s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s));
+
+ var jsonp, jsre = /=\?(&|$)/g, status, data,
+ type = s.type.toUpperCase();
+
+ // convert data if not already a string
+ if ( s.data && s.processData && typeof s.data !== "string" )
+ s.data = jQuery.param(s.data);
+
+ // Handle JSONP Parameter Callbacks
+ if ( s.dataType == "jsonp" ) {
+ if ( type == "GET" ) {
+ if ( !s.url.match(jsre) )
+ s.url += (s.url.match(/\?/) ? "&" : "?") + (s.jsonp || "callback") + "=?";
+ } else if ( !s.data || !s.data.match(jsre) )
+ s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?";
+ s.dataType = "json";
+ }
+
+ // Build temporary JSONP function
+ if ( s.dataType == "json" && (s.data && s.data.match(jsre) || s.url.match(jsre)) ) {
+ jsonp = "jsonp" + jsc++;
+
+ // Replace the =? sequence both in the query string and the data
+ if ( s.data )
+ s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");
+ s.url = s.url.replace(jsre, "=" + jsonp + "$1");
+
+ // We need to make sure
+ // that a JSONP style response is executed properly
+ s.dataType = "script";
+
+ // Handle JSONP-style loading
+ window[ jsonp ] = function(tmp){
+ data = tmp;
+ success();
+ complete();
+ // Garbage collect
+ window[ jsonp ] = undefined;
+ try{ delete window[ jsonp ]; } catch(e){}
+ if ( head )
+ head.removeChild( script );
+ };
+ }
+
+ if ( s.dataType == "script" && s.cache == null )
+ s.cache = false;
+
+ if ( s.cache === false && type == "GET" ) {
+ var ts = now();
+ // try replacing _= if it is there
+ var ret = s.url.replace(/(\?|&)_=.*?(&|$)/, "$1_=" + ts + "$2");
+ // if nothing was replaced, add timestamp to the end
+ s.url = ret + ((ret == s.url) ? (s.url.match(/\?/) ? "&" : "?") + "_=" + ts : "");
+ }
+
+ // If data is available, append data to url for get requests
+ if ( s.data && type == "GET" ) {
+ s.url += (s.url.match(/\?/) ? "&" : "?") + s.data;
+
+ // IE likes to send both get and post data, prevent this
+ s.data = null;
+ }
+
+ // Watch for a new set of requests
+ if ( s.global && ! jQuery.active++ )
+ jQuery.event.trigger( "ajaxStart" );
+
+ // Matches an absolute URL, and saves the domain
+ var parts = /^(\w+:)?\/\/([^\/?#]+)/.exec( s.url );
+
+ // If we're requesting a remote document
+ // and trying to load JSON or Script with a GET
+ if ( s.dataType == "script" && type == "GET" && parts
+ && ( parts[1] && parts[1] != location.protocol || parts[2] != location.host )){
+
+ var head = document.getElementsByTagName("head")[0];
+ var script = document.createElement("script");
+ script.src = s.url;
+ if (s.scriptCharset)
+ script.charset = s.scriptCharset;
+
+ // Handle Script loading
+ if ( !jsonp ) {
+ var done = false;
+
+ // Attach handlers for all browsers
+ script.onload = script.onreadystatechange = function(){
+ if ( !done && (!this.readyState ||
+ this.readyState == "loaded" || this.readyState == "complete") ) {
+ done = true;
+ success();
+ complete();
+
+ // Handle memory leak in IE
+ script.onload = script.onreadystatechange = null;
+ head.removeChild( script );
+ }
+ };
+ }
+
+ head.appendChild(script);
+
+ // We handle everything using the script element injection
+ return undefined;
+ }
+
+ var requestDone = false;
+
+ // Create the request object
+ var xhr = s.xhr();
+
+ // Open the socket
+ // Passing null username, generates a login popup on Opera (#2865)
+ if( s.username )
+ xhr.open(type, s.url, s.async, s.username, s.password);
+ else
+ xhr.open(type, s.url, s.async);
+
+ // Need an extra try/catch for cross domain requests in Firefox 3
+ try {
+ // Set the correct header, if data is being sent
+ if ( s.data )
+ xhr.setRequestHeader("Content-Type", s.contentType);
+
+ // Set the If-Modified-Since header, if ifModified mode.
+ if ( s.ifModified )
+ xhr.setRequestHeader("If-Modified-Since",
+ jQuery.lastModified[s.url] || "Thu, 01 Jan 1970 00:00:00 GMT" );
+
+ // Set header so the called script knows that it's an XMLHttpRequest
+ xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
+
+ // Set the Accepts header for the server, depending on the dataType
+ xhr.setRequestHeader("Accept", s.dataType && s.accepts[ s.dataType ] ?
+ s.accepts[ s.dataType ] + ", */*" :
+ s.accepts._default );
+ } catch(e){}
+
+ // Allow custom headers/mimetypes and early abort
+ if ( s.beforeSend && s.beforeSend(xhr, s) === false ) {
+ // Handle the global AJAX counter
+ if ( s.global && ! --jQuery.active )
+ jQuery.event.trigger( "ajaxStop" );
+ // close opended socket
+ xhr.abort();
+ return false;
+ }
+
+ if ( s.global )
+ jQuery.event.trigger("ajaxSend", [xhr, s]);
+
+ // Wait for a response to come back
+ var onreadystatechange = function(isTimeout){
+ // The request was aborted, clear the interval and decrement jQuery.active
+ if (xhr.readyState == 0) {
+ if (ival) {
+ // clear poll interval
+ clearInterval(ival);
+ ival = null;
+ // Handle the global AJAX counter
+ if ( s.global && ! --jQuery.active )
+ jQuery.event.trigger( "ajaxStop" );
+ }
+ // The transfer is complete and the data is available, or the request timed out
+ } else if ( !requestDone && xhr && (xhr.readyState == 4 || isTimeout == "timeout") ) {
+ requestDone = true;
+
+ // clear poll interval
+ if (ival) {
+ clearInterval(ival);
+ ival = null;
+ }
+
+ status = isTimeout == "timeout" ? "timeout" :
+ !jQuery.httpSuccess( xhr ) ? "error" :
+ s.ifModified && jQuery.httpNotModified( xhr, s.url ) ? "notmodified" :
+ "success";
+
+ if ( status == "success" ) {
+ // Watch for, and catch, XML document parse errors
+ try {
+ // process the data (runs the xml through httpData regardless of callback)
+ data = jQuery.httpData( xhr, s.dataType, s );
+ } catch(e) {
+ status = "parsererror";
+ }
+ }
+
+ // Make sure that the request was successful or notmodified
+ if ( status == "success" ) {
+ // Cache Last-Modified header, if ifModified mode.
+ var modRes;
+ try {
+ modRes = xhr.getResponseHeader("Last-Modified");
+ } catch(e) {} // swallow exception thrown by FF if header is not available
+
+ if ( s.ifModified && modRes )
+ jQuery.lastModified[s.url] = modRes;
+
+ // JSONP handles its own success callback
+ if ( !jsonp )
+ success();
+ } else
+ jQuery.handleError(s, xhr, status);
+
+ // Fire the complete handlers
+ complete();
+
+ if ( isTimeout )
+ xhr.abort();
+
+ // Stop memory leaks
+ if ( s.async )
+ xhr = null;
+ }
+ };
+
+ if ( s.async ) {
+ // don't attach the handler to the request, just poll it instead
+ var ival = setInterval(onreadystatechange, 13);
+
+ // Timeout checker
+ if ( s.timeout > 0 )
+ setTimeout(function(){
+ // Check to see if the request is still happening
+ if ( xhr && !requestDone )
+ onreadystatechange( "timeout" );
+ }, s.timeout);
+ }
+
+ // Send the data
+ try {
+ xhr.send(s.data);
+ } catch(e) {
+ jQuery.handleError(s, xhr, null, e);
+ }
+
+ // firefox 1.5 doesn't fire statechange for sync requests
+ if ( !s.async )
+ onreadystatechange();
+
+ function success(){
+ // If a local callback was specified, fire it and pass it the data
+ if ( s.success )
+ s.success( data, status );
+
+ // Fire the global callback
+ if ( s.global )
+ jQuery.event.trigger( "ajaxSuccess", [xhr, s] );
+ }
+
+ function complete(){
+ // Process result
+ if ( s.complete )
+ s.complete(xhr, status);
+
+ // The request was completed
+ if ( s.global )
+ jQuery.event.trigger( "ajaxComplete", [xhr, s] );
+
+ // Handle the global AJAX counter
+ if ( s.global && ! --jQuery.active )
+ jQuery.event.trigger( "ajaxStop" );
+ }
+
+ // return XMLHttpRequest to allow aborting the request etc.
+ return xhr;
+ },
+
+ handleError: function( s, xhr, status, e ) {
+ // If a local callback was specified, fire it
+ if ( s.error ) s.error( xhr, status, e );
+
+ // Fire the global callback
+ if ( s.global )
+ jQuery.event.trigger( "ajaxError", [xhr, s, e] );
+ },
+
+ // Counter for holding the number of active queries
+ active: 0,
+
+ // Determines if an XMLHttpRequest was successful or not
+ httpSuccess: function( xhr ) {
+ try {
+ // IE error sometimes returns 1223 when it should be 204 so treat it as success, see #1450
+ return !xhr.status && location.protocol == "file:" ||
+ ( xhr.status >= 200 && xhr.status < 300 ) || xhr.status == 304 || xhr.status == 1223;
+ } catch(e){}
+ return false;
+ },
+
+ // Determines if an XMLHttpRequest returns NotModified
+ httpNotModified: function( xhr, url ) {
+ try {
+ var xhrRes = xhr.getResponseHeader("Last-Modified");
+
+ // Firefox always returns 200. check Last-Modified date
+ return xhr.status == 304 || xhrRes == jQuery.lastModified[url];
+ } catch(e){}
+ return false;
+ },
+
+ httpData: function( xhr, type, s ) {
+ var ct = xhr.getResponseHeader("content-type"),
+ xml = type == "xml" || !type && ct && ct.indexOf("xml") >= 0,
+ data = xml ? xhr.responseXML : xhr.responseText;
+
+ if ( xml && data.documentElement.tagName == "parsererror" )
+ throw "parsererror";
+
+ // Allow a pre-filtering function to sanitize the response
+ // s != null is checked to keep backwards compatibility
+ if( s && s.dataFilter )
+ data = s.dataFilter( data, type );
+
+ // The filter can actually parse the response
+ if( typeof data === "string" ){
+
+ // If the type is "script", eval it in global context
+ if ( type == "script" )
+ jQuery.globalEval( data );
+
+ // Get the JavaScript object, if JSON is used.
+ if ( type == "json" )
+ data = window["eval"]("(" + data + ")");
+ }
+
+ return data;
+ },
+
+ // Serialize an array of form elements or a set of
+ // key/values into a query string
+ param: function( a ) {
+ var s = [ ];
+
+ function add( key, value ){
+ s[ s.length ] = encodeURIComponent(key) + '=' + encodeURIComponent(value);
+ };
+
+ // If an array was passed in, assume that it is an array
+ // of form elements
+ if ( jQuery.isArray(a) || a.jquery )
+ // Serialize the form elements
+ jQuery.each( a, function(){
+ add( this.name, this.value );
+ });
+
+ // Otherwise, assume that it's an object of key/value pairs
+ else
+ // Serialize the key/values
+ for ( var j in a )
+ // If the value is an array then the key names need to be repeated
+ if ( jQuery.isArray(a[j]) )
+ jQuery.each( a[j], function(){
+ add( j, this );
+ });
+ else
+ add( j, jQuery.isFunction(a[j]) ? a[j]() : a[j] );
+
+ // Return the resulting serialization
+ return s.join("&").replace(/%20/g, "+");
+ }
+
+});
+var elemdisplay = {},
+ timerId,
+ fxAttrs = [
+ // height animations
+ [ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ],
+ // width animations
+ [ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ],
+ // opacity animations
+ [ "opacity" ]
+ ];
+
+function genFx( type, num ){
+ var obj = {};
+ jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice(0,num)), function(){
+ obj[ this ] = type;
+ });
+ return obj;
+}
+
+jQuery.fn.extend({
+ show: function(speed,callback){
+ if ( speed ) {
+ return this.animate( genFx("show", 3), speed, callback);
+ } else {
+ for ( var i = 0, l = this.length; i < l; i++ ){
+ var old = jQuery.data(this[i], "olddisplay");
+
+ this[i].style.display = old || "";
+
+ if ( jQuery.css(this[i], "display") === "none" ) {
+ var tagName = this[i].tagName, display;
+
+ if ( elemdisplay[ tagName ] ) {
+ display = elemdisplay[ tagName ];
+ } else {
+ var elem = jQuery("<" + tagName + " />").appendTo("body");
+
+ display = elem.css("display");
+ if ( display === "none" )
+ display = "block";
+
+ elem.remove();
+
+ elemdisplay[ tagName ] = display;
+ }
+
+ jQuery.data(this[i], "olddisplay", display);
+ }
+ }
+
+ // Set the display of the elements in a second loop
+ // to avoid the constant reflow
+ for ( var i = 0, l = this.length; i < l; i++ ){
+ this[i].style.display = jQuery.data(this[i], "olddisplay") || "";
+ }
+
+ return this;
+ }
+ },
+
+ hide: function(speed,callback){
+ if ( speed ) {
+ return this.animate( genFx("hide", 3), speed, callback);
+ } else {
+ for ( var i = 0, l = this.length; i < l; i++ ){
+ var old = jQuery.data(this[i], "olddisplay");
+ if ( !old && old !== "none" )
+ jQuery.data(this[i], "olddisplay", jQuery.css(this[i], "display"));
+ }
+
+ // Set the display of the elements in a second loop
+ // to avoid the constant reflow
+ for ( var i = 0, l = this.length; i < l; i++ ){
+ this[i].style.display = "none";
+ }
+
+ return this;
+ }
+ },
+
+ // Save the old toggle function
+ _toggle: jQuery.fn.toggle,
+
+ toggle: function( fn, fn2 ){
+ var bool = typeof fn === "boolean";
+
+ return jQuery.isFunction(fn) && jQuery.isFunction(fn2) ?
+ this._toggle.apply( this, arguments ) :
+ fn == null || bool ?
+ this.each(function(){
+ var state = bool ? fn : jQuery(this).is(":hidden");
+ jQuery(this)[ state ? "show" : "hide" ]();
+ }) :
+ this.animate(genFx("toggle", 3), fn, fn2);
+ },
+
+ fadeTo: function(speed,to,callback){
+ return this.animate({opacity: to}, speed, callback);
+ },
+
+ animate: function( prop, speed, easing, callback ) {
+ var optall = jQuery.speed(speed, easing, callback);
+
+ return this[ optall.queue === false ? "each" : "queue" ](function(){
+
+ var opt = jQuery.extend({}, optall), p,
+ hidden = this.nodeType == 1 && jQuery(this).is(":hidden"),
+ self = this;
+
+ for ( p in prop ) {
+ if ( prop[p] == "hide" && hidden || prop[p] == "show" && !hidden )
+ return opt.complete.call(this);
+
+ if ( ( p == "height" || p == "width" ) && this.style ) {
+ // Store display property
+ opt.display = jQuery.css(this, "display");
+
+ // Make sure that nothing sneaks out
+ opt.overflow = this.style.overflow;
+ }
+ }
+
+ if ( opt.overflow != null )
+ this.style.overflow = "hidden";
+
+ opt.curAnim = jQuery.extend({}, prop);
+
+ jQuery.each( prop, function(name, val){
+ var e = new jQuery.fx( self, opt, name );
+
+ if ( /toggle|show|hide/.test(val) )
+ e[ val == "toggle" ? hidden ? "show" : "hide" : val ]( prop );
+ else {
+ var parts = val.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),
+ start = e.cur(true) || 0;
+
+ if ( parts ) {
+ var end = parseFloat(parts[2]),
+ unit = parts[3] || "px";
+
+ // We need to compute starting value
+ if ( unit != "px" ) {
+ self.style[ name ] = (end || 1) + unit;
+ start = ((end || 1) / e.cur(true)) * start;
+ self.style[ name ] = start + unit;
+ }
+
+ // If a +=/-= token was provided, we're doing a relative animation
+ if ( parts[1] )
+ end = ((parts[1] == "-=" ? -1 : 1) * end) + start;
+
+ e.custom( start, end, unit );
+ } else
+ e.custom( start, val, "" );
+ }
+ });
+
+ // For JS strict compliance
+ return true;
+ });
+ },
+
+ stop: function(clearQueue, gotoEnd){
+ var timers = jQuery.timers;
+
+ if (clearQueue)
+ this.queue([]);
+
+ this.each(function(){
+ // go in reverse order so anything added to the queue during the loop is ignored
+ for ( var i = timers.length - 1; i >= 0; i-- )
+ if ( timers[i].elem == this ) {
+ if (gotoEnd)
+ // force the next step to be the last
+ timers[i](true);
+ timers.splice(i, 1);
+ }
+ });
+
+ // start the next in the queue if the last step wasn't forced
+ if (!gotoEnd)
+ this.dequeue();
+
+ return this;
+ }
+
+});
+
+// Generate shortcuts for custom animations
+jQuery.each({
+ slideDown: genFx("show", 1),
+ slideUp: genFx("hide", 1),
+ slideToggle: genFx("toggle", 1),
+ fadeIn: { opacity: "show" },
+ fadeOut: { opacity: "hide" }
+}, function( name, props ){
+ jQuery.fn[ name ] = function( speed, callback ){
+ return this.animate( props, speed, callback );
+ };
+});
+
+jQuery.extend({
+
+ speed: function(speed, easing, fn) {
+ var opt = typeof speed === "object" ? speed : {
+ complete: fn || !fn && easing ||
+ jQuery.isFunction( speed ) && speed,
+ duration: speed,
+ easing: fn && easing || easing && !jQuery.isFunction(easing) && easing
+ };
+
+ opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
+ jQuery.fx.speeds[opt.duration] || jQuery.fx.speeds._default;
+
+ // Queueing
+ opt.old = opt.complete;
+ opt.complete = function(){
+ if ( opt.queue !== false )
+ jQuery(this).dequeue();
+ if ( jQuery.isFunction( opt.old ) )
+ opt.old.call( this );
+ };
+
+ return opt;
+ },
+
+ easing: {
+ linear: function( p, n, firstNum, diff ) {
+ return firstNum + diff * p;
+ },
+ swing: function( p, n, firstNum, diff ) {
+ return ((-Math.cos(p*Math.PI)/2) + 0.5) * diff + firstNum;
+ }
+ },
+
+ timers: [],
+
+ fx: function( elem, options, prop ){
+ this.options = options;
+ this.elem = elem;
+ this.prop = prop;
+
+ if ( !options.orig )
+ options.orig = {};
+ }
+
+});
+
+jQuery.fx.prototype = {
+
+ // Simple function for setting a style value
+ update: function(){
+ if ( this.options.step )
+ this.options.step.call( this.elem, this.now, this );
+
+ (jQuery.fx.step[this.prop] || jQuery.fx.step._default)( this );
+
+ // Set display property to block for height/width animations
+ if ( ( this.prop == "height" || this.prop == "width" ) && this.elem.style )
+ this.elem.style.display = "block";
+ },
+
+ // Get the current size
+ cur: function(force){
+ if ( this.elem[this.prop] != null && (!this.elem.style || this.elem.style[this.prop] == null) )
+ return this.elem[ this.prop ];
+
+ var r = parseFloat(jQuery.css(this.elem, this.prop, force));
+ return r && r > -10000 ? r : parseFloat(jQuery.curCSS(this.elem, this.prop)) || 0;
+ },
+
+ // Start an animation from one number to another
+ custom: function(from, to, unit){
+ this.startTime = now();
+ this.start = from;
+ this.end = to;
+ this.unit = unit || this.unit || "px";
+ this.now = this.start;
+ this.pos = this.state = 0;
+
+ var self = this;
+ function t(gotoEnd){
+ return self.step(gotoEnd);
+ }
+
+ t.elem = this.elem;
+
+ if ( t() && jQuery.timers.push(t) && !timerId ) {
+ timerId = setInterval(function(){
+ var timers = jQuery.timers;
+
+ for ( var i = 0; i < timers.length; i++ )
+ if ( !timers[i]() )
+ timers.splice(i--, 1);
+
+ if ( !timers.length ) {
+ clearInterval( timerId );
+ timerId = undefined;
+ }
+ }, 13);
+ }
+ },
+
+ // Simple 'show' function
+ show: function(){
+ // Remember where we started, so that we can go back to it later
+ this.options.orig[this.prop] = jQuery.attr( this.elem.style, this.prop );
+ this.options.show = true;
+
+ // Begin the animation
+ // Make sure that we start at a small width/height to avoid any
+ // flash of content
+ this.custom(this.prop == "width" || this.prop == "height" ? 1 : 0, this.cur());
+
+ // Start by showing the element
+ jQuery(this.elem).show();
+ },
+
+ // Simple 'hide' function
+ hide: function(){
+ // Remember where we started, so that we can go back to it later
+ this.options.orig[this.prop] = jQuery.attr( this.elem.style, this.prop );
+ this.options.hide = true;
+
+ // Begin the animation
+ this.custom(this.cur(), 0);
+ },
+
+ // Each step of an animation
+ step: function(gotoEnd){
+ var t = now();
+
+ if ( gotoEnd || t >= this.options.duration + this.startTime ) {
+ this.now = this.end;
+ this.pos = this.state = 1;
+ this.update();
+
+ this.options.curAnim[ this.prop ] = true;
+
+ var done = true;
+ for ( var i in this.options.curAnim )
+ if ( this.options.curAnim[i] !== true )
+ done = false;
+
+ if ( done ) {
+ if ( this.options.display != null ) {
+ // Reset the overflow
+ this.elem.style.overflow = this.options.overflow;
+
+ // Reset the display
+ this.elem.style.display = this.options.display;
+ if ( jQuery.css(this.elem, "display") == "none" )
+ this.elem.style.display = "block";
+ }
+
+ // Hide the element if the "hide" operation was done
+ if ( this.options.hide )
+ jQuery(this.elem).hide();
+
+ // Reset the properties, if the item has been hidden or shown
+ if ( this.options.hide || this.options.show )
+ for ( var p in this.options.curAnim )
+ jQuery.attr(this.elem.style, p, this.options.orig[p]);
+
+ // Execute the complete function
+ this.options.complete.call( this.elem );
+ }
+
+ return false;
+ } else {
+ var n = t - this.startTime;
+ this.state = n / this.options.duration;
+
+ // Perform the easing function, defaults to swing
+ this.pos = jQuery.easing[this.options.easing || (jQuery.easing.swing ? "swing" : "linear")](this.state, n, 0, 1, this.options.duration);
+ this.now = this.start + ((this.end - this.start) * this.pos);
+
+ // Perform the next step of the animation
+ this.update();
+ }
+
+ return true;
+ }
+
+};
+
+jQuery.extend( jQuery.fx, {
+ speeds:{
+ slow: 600,
+ fast: 200,
+ // Default speed
+ _default: 400
+ },
+ step: {
+
+ opacity: function(fx){
+ jQuery.attr(fx.elem.style, "opacity", fx.now);
+ },
+
+ _default: function(fx){
+ if ( fx.elem.style && fx.elem.style[ fx.prop ] != null )
+ fx.elem.style[ fx.prop ] = fx.now + fx.unit;
+ else
+ fx.elem[ fx.prop ] = fx.now;
+ }
+ }
+});
+if ( document.documentElement["getBoundingClientRect"] )
+ jQuery.fn.offset = function() {
+ if ( !this[0] ) return { top: 0, left: 0 };
+ if ( this[0] === this[0].ownerDocument.body ) return jQuery.offset.bodyOffset( this[0] );
+ var box = this[0].getBoundingClientRect(), doc = this[0].ownerDocument, body = doc.body, docElem = doc.documentElement,
+ clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0,
+ top = box.top + (self.pageYOffset || jQuery.boxModel && docElem.scrollTop || body.scrollTop ) - clientTop,
+ left = box.left + (self.pageXOffset || jQuery.boxModel && docElem.scrollLeft || body.scrollLeft) - clientLeft;
+ return { top: top, left: left };
+ };
+else
+ jQuery.fn.offset = function() {
+ if ( !this[0] ) return { top: 0, left: 0 };
+ if ( this[0] === this[0].ownerDocument.body ) return jQuery.offset.bodyOffset( this[0] );
+ jQuery.offset.initialized || jQuery.offset.initialize();
+
+ var elem = this[0], offsetParent = elem.offsetParent, prevOffsetParent = elem,
+ doc = elem.ownerDocument, computedStyle, docElem = doc.documentElement,
+ body = doc.body, defaultView = doc.defaultView,
+ prevComputedStyle = defaultView.getComputedStyle(elem, null),
+ top = elem.offsetTop, left = elem.offsetLeft;
+
+ while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) {
+ computedStyle = defaultView.getComputedStyle(elem, null);
+ top -= elem.scrollTop, left -= elem.scrollLeft;
+ if ( elem === offsetParent ) {
+ top += elem.offsetTop, left += elem.offsetLeft;
+ if ( jQuery.offset.doesNotAddBorder && !(jQuery.offset.doesAddBorderForTableAndCells && /^t(able|d|h)$/i.test(elem.tagName)) )
+ top += parseInt( computedStyle.borderTopWidth, 10) || 0,
+ left += parseInt( computedStyle.borderLeftWidth, 10) || 0;
+ prevOffsetParent = offsetParent, offsetParent = elem.offsetParent;
+ }
+ if ( jQuery.offset.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" )
+ top += parseInt( computedStyle.borderTopWidth, 10) || 0,
+ left += parseInt( computedStyle.borderLeftWidth, 10) || 0;
+ prevComputedStyle = computedStyle;
+ }
+
+ if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" )
+ top += body.offsetTop,
+ left += body.offsetLeft;
+
+ if ( prevComputedStyle.position === "fixed" )
+ top += Math.max(docElem.scrollTop, body.scrollTop),
+ left += Math.max(docElem.scrollLeft, body.scrollLeft);
+
+ return { top: top, left: left };
+ };
+
+jQuery.offset = {
+ initialize: function() {
+ if ( this.initialized ) return;
+ var body = document.body, container = document.createElement('div'), innerDiv, checkDiv, table, td, rules, prop, bodyMarginTop = body.style.marginTop,
+ html = '<div style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;"><div></div></div><table style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;" cellpadding="0" cellspacing="0"><tr><td></td></tr></table>';
+
+ rules = { position: 'absolute', top: 0, left: 0, margin: 0, border: 0, width: '1px', height: '1px', visibility: 'hidden' };
+ for ( prop in rules ) container.style[prop] = rules[prop];
+
+ container.innerHTML = html;
+ body.insertBefore(container, body.firstChild);
+ innerDiv = container.firstChild, checkDiv = innerDiv.firstChild, td = innerDiv.nextSibling.firstChild.firstChild;
+
+ this.doesNotAddBorder = (checkDiv.offsetTop !== 5);
+ this.doesAddBorderForTableAndCells = (td.offsetTop === 5);
+
+ innerDiv.style.overflow = 'hidden', innerDiv.style.position = 'relative';
+ this.subtractsBorderForOverflowNotVisible = (checkDiv.offsetTop === -5);
+
+ body.style.marginTop = '1px';
+ this.doesNotIncludeMarginInBodyOffset = (body.offsetTop === 0);
+ body.style.marginTop = bodyMarginTop;
+
+ body.removeChild(container);
+ this.initialized = true;
+ },
+
+ bodyOffset: function(body) {
+ jQuery.offset.initialized || jQuery.offset.initialize();
+ var top = body.offsetTop, left = body.offsetLeft;
+ if ( jQuery.offset.doesNotIncludeMarginInBodyOffset )
+ top += parseInt( jQuery.curCSS(body, 'marginTop', true), 10 ) || 0,
+ left += parseInt( jQuery.curCSS(body, 'marginLeft', true), 10 ) || 0;
+ return { top: top, left: left };
+ }
+};
+
+
+jQuery.fn.extend({
+ position: function() {
+ var left = 0, top = 0, results;
+
+ if ( this[0] ) {
+ // Get *real* offsetParent
+ var offsetParent = this.offsetParent(),
+
+ // Get correct offsets
+ offset = this.offset(),
+ parentOffset = /^body|html$/i.test(offsetParent[0].tagName) ? { top: 0, left: 0 } : offsetParent.offset();
+
+ // Subtract element margins
+ // note: when an element has margin: auto the offsetLeft and marginLeft
+ // are the same in Safari causing offset.left to incorrectly be 0
+ offset.top -= num( this, 'marginTop' );
+ offset.left -= num( this, 'marginLeft' );
+
+ // Add offsetParent borders
+ parentOffset.top += num( offsetParent, 'borderTopWidth' );
+ parentOffset.left += num( offsetParent, 'borderLeftWidth' );
+
+ // Subtract the two offsets
+ results = {
+ top: offset.top - parentOffset.top,
+ left: offset.left - parentOffset.left
+ };
+ }
+
+ return results;
+ },
+
+ offsetParent: function() {
+ var offsetParent = this[0].offsetParent || document.body;
+ while ( offsetParent && (!/^body|html$/i.test(offsetParent.tagName) && jQuery.css(offsetParent, 'position') == 'static') )
+ offsetParent = offsetParent.offsetParent;
+ return jQuery(offsetParent);
+ }
+});
+
+
+// Create scrollLeft and scrollTop methods
+jQuery.each( ['Left', 'Top'], function(i, name) {
+ var method = 'scroll' + name;
+
+ jQuery.fn[ method ] = function(val) {
+ if (!this[0]) return null;
+
+ return val !== undefined ?
+
+ // Set the scroll offset
+ this.each(function() {
+ this == window || this == document ?
+ window.scrollTo(
+ !i ? val : jQuery(window).scrollLeft(),
+ i ? val : jQuery(window).scrollTop()
+ ) :
+ this[ method ] = val;
+ }) :
+
+ // Return the scroll offset
+ this[0] == window || this[0] == document ?
+ self[ i ? 'pageYOffset' : 'pageXOffset' ] ||
+ jQuery.boxModel && document.documentElement[ method ] ||
+ document.body[ method ] :
+ this[0][ method ];
+ };
+});
+// Create innerHeight, innerWidth, outerHeight and outerWidth methods
+jQuery.each([ "Height", "Width" ], function(i, name){
+
+ var tl = i ? "Left" : "Top", // top or left
+ br = i ? "Right" : "Bottom", // bottom or right
+ lower = name.toLowerCase();
+
+ // innerHeight and innerWidth
+ jQuery.fn["inner" + name] = function(){
+ return this[0] ?
+ jQuery.css( this[0], lower, false, "padding" ) :
+ null;
+ };
+
+ // outerHeight and outerWidth
+ jQuery.fn["outer" + name] = function(margin) {
+ return this[0] ?
+ jQuery.css( this[0], lower, false, margin ? "margin" : "border" ) :
+ null;
+ };
+
+ var type = name.toLowerCase();
+
+ jQuery.fn[ type ] = function( size ) {
+ // Get window width or height
+ return this[0] == window ?
+ // Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode
+ document.compatMode == "CSS1Compat" && document.documentElement[ "client" + name ] ||
+ document.body[ "client" + name ] :
+
+ // Get document width or height
+ this[0] == document ?
+ // Either scroll[Width/Height] or offset[Width/Height], whichever is greater
+ Math.max(
+ document.documentElement["client" + name],
+ document.body["scroll" + name], document.documentElement["scroll" + name],
+ document.body["offset" + name], document.documentElement["offset" + name]
+ ) :
+
+ // Get or set width or height on the element
+ size === undefined ?
+ // Get width or height on the element
+ (this.length ? jQuery.css( this[0], type ) : null) :
+
+ // Set the width or height on the element (default to pixels if value is unitless)
+ this.css( type, typeof size === "string" ? size : size + "px" );
+ };
+
+});
+})();
diff --git a/node_modules/selenium-webdriver/lib/test/data/js/jquery-1.4.4.min.js b/node_modules/selenium-webdriver/lib/test/data/js/jquery-1.4.4.min.js
new file mode 100644
index 000000000..8f3ca2e2d
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/js/jquery-1.4.4.min.js
@@ -0,0 +1,167 @@
+/*!
+ * jQuery JavaScript Library v1.4.4
+ * http://jquery.com/
+ *
+ * Copyright 2010, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ * Copyright 2010, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ *
+ * Date: Thu Nov 11 19:04:53 2010 -0500
+ */
+(function(E,B){function ka(a,b,d){if(d===B&&a.nodeType===1){d=a.getAttribute("data-"+b);if(typeof d==="string"){try{d=d==="true"?true:d==="false"?false:d==="null"?null:!c.isNaN(d)?parseFloat(d):Ja.test(d)?c.parseJSON(d):d}catch(e){}c.data(a,b,d)}else d=B}return d}function U(){return false}function ca(){return true}function la(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function Ka(a){var b,d,e,f,h,l,k,o,x,r,A,C=[];f=[];h=c.data(this,this.nodeType?"events":"__events__");if(typeof h==="function")h=
+h.events;if(!(a.liveFired===this||!h||!h.live||a.button&&a.type==="click")){if(a.namespace)A=RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)");a.liveFired=this;var J=h.live.slice(0);for(k=0;k<J.length;k++){h=J[k];h.origType.replace(X,"")===a.type?f.push(h.selector):J.splice(k--,1)}f=c(a.target).closest(f,a.currentTarget);o=0;for(x=f.length;o<x;o++){r=f[o];for(k=0;k<J.length;k++){h=J[k];if(r.selector===h.selector&&(!A||A.test(h.namespace))){l=r.elem;e=null;if(h.preType==="mouseenter"||
+h.preType==="mouseleave"){a.type=h.preType;e=c(a.relatedTarget).closest(h.selector)[0]}if(!e||e!==l)C.push({elem:l,handleObj:h,level:r.level})}}}o=0;for(x=C.length;o<x;o++){f=C[o];if(d&&f.level>d)break;a.currentTarget=f.elem;a.data=f.handleObj.data;a.handleObj=f.handleObj;A=f.handleObj.origHandler.apply(f.elem,arguments);if(A===false||a.isPropagationStopped()){d=f.level;if(A===false)b=false;if(a.isImmediatePropagationStopped())break}}return b}}function Y(a,b){return(a&&a!=="*"?a+".":"")+b.replace(La,
+"`").replace(Ma,"&")}function ma(a,b,d){if(c.isFunction(b))return c.grep(a,function(f,h){return!!b.call(f,h,f)===d});else if(b.nodeType)return c.grep(a,function(f){return f===b===d});else if(typeof b==="string"){var e=c.grep(a,function(f){return f.nodeType===1});if(Na.test(b))return c.filter(b,e,!d);else b=c.filter(b,e)}return c.grep(a,function(f){return c.inArray(f,b)>=0===d})}function na(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var e=c.data(a[d++]),f=c.data(this,
+e);if(e=e&&e.events){delete f.handle;f.events={};for(var h in e)for(var l in e[h])c.event.add(this,h,e[h][l],e[h][l].data)}}})}function Oa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function oa(a,b,d){var e=b==="width"?a.offsetWidth:a.offsetHeight;if(d==="border")return e;c.each(b==="width"?Pa:Qa,function(){d||(e-=parseFloat(c.css(a,"padding"+this))||0);if(d==="margin")e+=parseFloat(c.css(a,
+"margin"+this))||0;else e-=parseFloat(c.css(a,"border"+this+"Width"))||0});return e}function da(a,b,d,e){if(c.isArray(b)&&b.length)c.each(b,function(f,h){d||Ra.test(a)?e(a,h):da(a+"["+(typeof h==="object"||c.isArray(h)?f:"")+"]",h,d,e)});else if(!d&&b!=null&&typeof b==="object")c.isEmptyObject(b)?e(a,""):c.each(b,function(f,h){da(a+"["+f+"]",h,d,e)});else e(a,b)}function S(a,b){var d={};c.each(pa.concat.apply([],pa.slice(0,b)),function(){d[this]=a});return d}function qa(a){if(!ea[a]){var b=c("<"+
+a+">").appendTo("body"),d=b.css("display");b.remove();if(d==="none"||d==="")d="block";ea[a]=d}return ea[a]}function fa(a){return c.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var t=E.document,c=function(){function a(){if(!b.isReady){try{t.documentElement.doScroll("left")}catch(j){setTimeout(a,1);return}b.ready()}}var b=function(j,s){return new b.fn.init(j,s)},d=E.jQuery,e=E.$,f,h=/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]+)$)/,l=/\S/,k=/^\s+/,o=/\s+$/,x=/\W/,r=/\d/,A=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,
+C=/^[\],:{}\s]*$/,J=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,w=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,I=/(?:^|:|,)(?:\s*\[)+/g,L=/(webkit)[ \/]([\w.]+)/,g=/(opera)(?:.*version)?[ \/]([\w.]+)/,i=/(msie) ([\w.]+)/,n=/(mozilla)(?:.*? rv:([\w.]+))?/,m=navigator.userAgent,p=false,q=[],u,y=Object.prototype.toString,F=Object.prototype.hasOwnProperty,M=Array.prototype.push,N=Array.prototype.slice,O=String.prototype.trim,D=Array.prototype.indexOf,R={};b.fn=b.prototype={init:function(j,
+s){var v,z,H;if(!j)return this;if(j.nodeType){this.context=this[0]=j;this.length=1;return this}if(j==="body"&&!s&&t.body){this.context=t;this[0]=t.body;this.selector="body";this.length=1;return this}if(typeof j==="string")if((v=h.exec(j))&&(v[1]||!s))if(v[1]){H=s?s.ownerDocument||s:t;if(z=A.exec(j))if(b.isPlainObject(s)){j=[t.createElement(z[1])];b.fn.attr.call(j,s,true)}else j=[H.createElement(z[1])];else{z=b.buildFragment([v[1]],[H]);j=(z.cacheable?z.fragment.cloneNode(true):z.fragment).childNodes}return b.merge(this,
+j)}else{if((z=t.getElementById(v[2]))&&z.parentNode){if(z.id!==v[2])return f.find(j);this.length=1;this[0]=z}this.context=t;this.selector=j;return this}else if(!s&&!x.test(j)){this.selector=j;this.context=t;j=t.getElementsByTagName(j);return b.merge(this,j)}else return!s||s.jquery?(s||f).find(j):b(s).find(j);else if(b.isFunction(j))return f.ready(j);if(j.selector!==B){this.selector=j.selector;this.context=j.context}return b.makeArray(j,this)},selector:"",jquery:"1.4.4",length:0,size:function(){return this.length},
+toArray:function(){return N.call(this,0)},get:function(j){return j==null?this.toArray():j<0?this.slice(j)[0]:this[j]},pushStack:function(j,s,v){var z=b();b.isArray(j)?M.apply(z,j):b.merge(z,j);z.prevObject=this;z.context=this.context;if(s==="find")z.selector=this.selector+(this.selector?" ":"")+v;else if(s)z.selector=this.selector+"."+s+"("+v+")";return z},each:function(j,s){return b.each(this,j,s)},ready:function(j){b.bindReady();if(b.isReady)j.call(t,b);else q&&q.push(j);return this},eq:function(j){return j===
+-1?this.slice(j):this.slice(j,+j+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(N.apply(this,arguments),"slice",N.call(arguments).join(","))},map:function(j){return this.pushStack(b.map(this,function(s,v){return j.call(s,v,s)}))},end:function(){return this.prevObject||b(null)},push:M,sort:[].sort,splice:[].splice};b.fn.init.prototype=b.fn;b.extend=b.fn.extend=function(){var j,s,v,z,H,G=arguments[0]||{},K=1,Q=arguments.length,ga=false;
+if(typeof G==="boolean"){ga=G;G=arguments[1]||{};K=2}if(typeof G!=="object"&&!b.isFunction(G))G={};if(Q===K){G=this;--K}for(;K<Q;K++)if((j=arguments[K])!=null)for(s in j){v=G[s];z=j[s];if(G!==z)if(ga&&z&&(b.isPlainObject(z)||(H=b.isArray(z)))){if(H){H=false;v=v&&b.isArray(v)?v:[]}else v=v&&b.isPlainObject(v)?v:{};G[s]=b.extend(ga,v,z)}else if(z!==B)G[s]=z}return G};b.extend({noConflict:function(j){E.$=e;if(j)E.jQuery=d;return b},isReady:false,readyWait:1,ready:function(j){j===true&&b.readyWait--;
+if(!b.readyWait||j!==true&&!b.isReady){if(!t.body)return setTimeout(b.ready,1);b.isReady=true;if(!(j!==true&&--b.readyWait>0))if(q){var s=0,v=q;for(q=null;j=v[s++];)j.call(t,b);b.fn.trigger&&b(t).trigger("ready").unbind("ready")}}},bindReady:function(){if(!p){p=true;if(t.readyState==="complete")return setTimeout(b.ready,1);if(t.addEventListener){t.addEventListener("DOMContentLoaded",u,false);E.addEventListener("load",b.ready,false)}else if(t.attachEvent){t.attachEvent("onreadystatechange",u);E.attachEvent("onload",
+b.ready);var j=false;try{j=E.frameElement==null}catch(s){}t.documentElement.doScroll&&j&&a()}}},isFunction:function(j){return b.type(j)==="function"},isArray:Array.isArray||function(j){return b.type(j)==="array"},isWindow:function(j){return j&&typeof j==="object"&&"setInterval"in j},isNaN:function(j){return j==null||!r.test(j)||isNaN(j)},type:function(j){return j==null?String(j):R[y.call(j)]||"object"},isPlainObject:function(j){if(!j||b.type(j)!=="object"||j.nodeType||b.isWindow(j))return false;if(j.constructor&&
+!F.call(j,"constructor")&&!F.call(j.constructor.prototype,"isPrototypeOf"))return false;for(var s in j);return s===B||F.call(j,s)},isEmptyObject:function(j){for(var s in j)return false;return true},error:function(j){throw j;},parseJSON:function(j){if(typeof j!=="string"||!j)return null;j=b.trim(j);if(C.test(j.replace(J,"@").replace(w,"]").replace(I,"")))return E.JSON&&E.JSON.parse?E.JSON.parse(j):(new Function("return "+j))();else b.error("Invalid JSON: "+j)},noop:function(){},globalEval:function(j){if(j&&
+l.test(j)){var s=t.getElementsByTagName("head")[0]||t.documentElement,v=t.createElement("script");v.type="text/javascript";if(b.support.scriptEval)v.appendChild(t.createTextNode(j));else v.text=j;s.insertBefore(v,s.firstChild);s.removeChild(v)}},nodeName:function(j,s){return j.nodeName&&j.nodeName.toUpperCase()===s.toUpperCase()},each:function(j,s,v){var z,H=0,G=j.length,K=G===B||b.isFunction(j);if(v)if(K)for(z in j){if(s.apply(j[z],v)===false)break}else for(;H<G;){if(s.apply(j[H++],v)===false)break}else if(K)for(z in j){if(s.call(j[z],
+z,j[z])===false)break}else for(v=j[0];H<G&&s.call(v,H,v)!==false;v=j[++H]);return j},trim:O?function(j){return j==null?"":O.call(j)}:function(j){return j==null?"":j.toString().replace(k,"").replace(o,"")},makeArray:function(j,s){var v=s||[];if(j!=null){var z=b.type(j);j.length==null||z==="string"||z==="function"||z==="regexp"||b.isWindow(j)?M.call(v,j):b.merge(v,j)}return v},inArray:function(j,s){if(s.indexOf)return s.indexOf(j);for(var v=0,z=s.length;v<z;v++)if(s[v]===j)return v;return-1},merge:function(j,
+s){var v=j.length,z=0;if(typeof s.length==="number")for(var H=s.length;z<H;z++)j[v++]=s[z];else for(;s[z]!==B;)j[v++]=s[z++];j.length=v;return j},grep:function(j,s,v){var z=[],H;v=!!v;for(var G=0,K=j.length;G<K;G++){H=!!s(j[G],G);v!==H&&z.push(j[G])}return z},map:function(j,s,v){for(var z=[],H,G=0,K=j.length;G<K;G++){H=s(j[G],G,v);if(H!=null)z[z.length]=H}return z.concat.apply([],z)},guid:1,proxy:function(j,s,v){if(arguments.length===2)if(typeof s==="string"){v=j;j=v[s];s=B}else if(s&&!b.isFunction(s)){v=
+s;s=B}if(!s&&j)s=function(){return j.apply(v||this,arguments)};if(j)s.guid=j.guid=j.guid||s.guid||b.guid++;return s},access:function(j,s,v,z,H,G){var K=j.length;if(typeof s==="object"){for(var Q in s)b.access(j,Q,s[Q],z,H,v);return j}if(v!==B){z=!G&&z&&b.isFunction(v);for(Q=0;Q<K;Q++)H(j[Q],s,z?v.call(j[Q],Q,H(j[Q],s)):v,G);return j}return K?H(j[0],s):B},now:function(){return(new Date).getTime()},uaMatch:function(j){j=j.toLowerCase();j=L.exec(j)||g.exec(j)||i.exec(j)||j.indexOf("compatible")<0&&n.exec(j)||
+[];return{browser:j[1]||"",version:j[2]||"0"}},browser:{}});b.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(j,s){R["[object "+s+"]"]=s.toLowerCase()});m=b.uaMatch(m);if(m.browser){b.browser[m.browser]=true;b.browser.version=m.version}if(b.browser.webkit)b.browser.safari=true;if(D)b.inArray=function(j,s){return D.call(s,j)};if(!/\s/.test("\u00a0")){k=/^[\s\xA0]+/;o=/[\s\xA0]+$/}f=b(t);if(t.addEventListener)u=function(){t.removeEventListener("DOMContentLoaded",u,
+false);b.ready()};else if(t.attachEvent)u=function(){if(t.readyState==="complete"){t.detachEvent("onreadystatechange",u);b.ready()}};return E.jQuery=E.$=b}();(function(){c.support={};var a=t.documentElement,b=t.createElement("script"),d=t.createElement("div"),e="script"+c.now();d.style.display="none";d.innerHTML=" <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";var f=d.getElementsByTagName("*"),h=d.getElementsByTagName("a")[0],l=t.createElement("select"),
+k=l.appendChild(t.createElement("option"));if(!(!f||!f.length||!h)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(h.getAttribute("style")),hrefNormalized:h.getAttribute("href")==="/a",opacity:/^0.55$/.test(h.style.opacity),cssFloat:!!h.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:k.selected,deleteExpando:true,optDisabled:false,checkClone:false,
+scriptEval:false,noCloneEvent:true,boxModel:null,inlineBlockNeedsLayout:false,shrinkWrapBlocks:false,reliableHiddenOffsets:true};l.disabled=true;c.support.optDisabled=!k.disabled;b.type="text/javascript";try{b.appendChild(t.createTextNode("window."+e+"=1;"))}catch(o){}a.insertBefore(b,a.firstChild);if(E[e]){c.support.scriptEval=true;delete E[e]}try{delete b.test}catch(x){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function r(){c.support.noCloneEvent=
+false;d.detachEvent("onclick",r)});d.cloneNode(true).fireEvent("onclick")}d=t.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=t.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var r=t.createElement("div");r.style.width=r.style.paddingLeft="1px";t.body.appendChild(r);c.boxModel=c.support.boxModel=r.offsetWidth===2;if("zoom"in r.style){r.style.display="inline";r.style.zoom=
+1;c.support.inlineBlockNeedsLayout=r.offsetWidth===2;r.style.display="";r.innerHTML="<div style='width:4px;'></div>";c.support.shrinkWrapBlocks=r.offsetWidth!==2}r.innerHTML="<table><tr><td style='padding:0;display:none'></td><td>t</td></tr></table>";var A=r.getElementsByTagName("td");c.support.reliableHiddenOffsets=A[0].offsetHeight===0;A[0].style.display="";A[1].style.display="none";c.support.reliableHiddenOffsets=c.support.reliableHiddenOffsets&&A[0].offsetHeight===0;r.innerHTML="";t.body.removeChild(r).style.display=
+"none"});a=function(r){var A=t.createElement("div");r="on"+r;var C=r in A;if(!C){A.setAttribute(r,"return;");C=typeof A[r]==="function"}return C};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=f=h=null}})();var ra={},Ja=/^(?:\{.*\}|\[.*\])$/;c.extend({cache:{},uuid:0,expando:"jQuery"+c.now(),noData:{embed:true,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:true},data:function(a,b,d){if(c.acceptData(a)){a=a==E?ra:a;var e=a.nodeType,f=e?a[c.expando]:null,h=
+c.cache;if(!(e&&!f&&typeof b==="string"&&d===B)){if(e)f||(a[c.expando]=f=++c.uuid);else h=a;if(typeof b==="object")if(e)h[f]=c.extend(h[f],b);else c.extend(h,b);else if(e&&!h[f])h[f]={};a=e?h[f]:h;if(d!==B)a[b]=d;return typeof b==="string"?a[b]:a}}},removeData:function(a,b){if(c.acceptData(a)){a=a==E?ra:a;var d=a.nodeType,e=d?a[c.expando]:a,f=c.cache,h=d?f[e]:e;if(b){if(h){delete h[b];d&&c.isEmptyObject(h)&&c.removeData(a)}}else if(d&&c.support.deleteExpando)delete a[c.expando];else if(a.removeAttribute)a.removeAttribute(c.expando);
+else if(d)delete f[e];else for(var l in a)delete a[l]}},acceptData:function(a){if(a.nodeName){var b=c.noData[a.nodeName.toLowerCase()];if(b)return!(b===true||a.getAttribute("classid")!==b)}return true}});c.fn.extend({data:function(a,b){var d=null;if(typeof a==="undefined"){if(this.length){var e=this[0].attributes,f;d=c.data(this[0]);for(var h=0,l=e.length;h<l;h++){f=e[h].name;if(f.indexOf("data-")===0){f=f.substr(5);ka(this[0],f,d[f])}}}return d}else if(typeof a==="object")return this.each(function(){c.data(this,
+a)});var k=a.split(".");k[1]=k[1]?"."+k[1]:"";if(b===B){d=this.triggerHandler("getData"+k[1]+"!",[k[0]]);if(d===B&&this.length){d=c.data(this[0],a);d=ka(this[0],a,d)}return d===B&&k[1]?this.data(k[0]):d}else return this.each(function(){var o=c(this),x=[k[0],b];o.triggerHandler("setData"+k[1]+"!",x);c.data(this,a,b);o.triggerHandler("changeData"+k[1]+"!",x)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var e=
+c.data(a,b);if(!d)return e||[];if(!e||c.isArray(d))e=c.data(a,b,c.makeArray(d));else e.push(d);return e}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),e=d.shift();if(e==="inprogress")e=d.shift();if(e){b==="fx"&&d.unshift("inprogress");e.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===B)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,
+a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var sa=/[\n\t]/g,ha=/\s+/,Sa=/\r/g,Ta=/^(?:href|src|style)$/,Ua=/^(?:button|input)$/i,Va=/^(?:button|input|object|select|textarea)$/i,Wa=/^a(?:rea)?$/i,ta=/^(?:radio|checkbox)$/i;c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",
+colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};c.fn.extend({attr:function(a,b){return c.access(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(x){var r=c(this);r.addClass(a.call(this,x,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ha),d=0,e=this.length;d<e;d++){var f=this[d];if(f.nodeType===
+1)if(f.className){for(var h=" "+f.className+" ",l=f.className,k=0,o=b.length;k<o;k++)if(h.indexOf(" "+b[k]+" ")<0)l+=" "+b[k];f.className=c.trim(l)}else f.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(o){var x=c(this);x.removeClass(a.call(this,o,x.attr("class")))});if(a&&typeof a==="string"||a===B)for(var b=(a||"").split(ha),d=0,e=this.length;d<e;d++){var f=this[d];if(f.nodeType===1&&f.className)if(a){for(var h=(" "+f.className+" ").replace(sa," "),
+l=0,k=b.length;l<k;l++)h=h.replace(" "+b[l]+" "," ");f.className=c.trim(h)}else f.className=""}return this},toggleClass:function(a,b){var d=typeof a,e=typeof b==="boolean";if(c.isFunction(a))return this.each(function(f){var h=c(this);h.toggleClass(a.call(this,f,h.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var f,h=0,l=c(this),k=b,o=a.split(ha);f=o[h++];){k=e?k:!l.hasClass(f);l[k?"addClass":"removeClass"](f)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,
+"__className__",this.className);this.className=this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(sa," ").indexOf(a)>-1)return true;return false},val:function(a){if(!arguments.length){var b=this[0];if(b){if(c.nodeName(b,"option")){var d=b.attributes.value;return!d||d.specified?b.value:b.text}if(c.nodeName(b,"select")){var e=b.selectedIndex;d=[];var f=b.options;b=b.type==="select-one";
+if(e<0)return null;var h=b?e:0;for(e=b?e+1:f.length;h<e;h++){var l=f[h];if(l.selected&&(c.support.optDisabled?!l.disabled:l.getAttribute("disabled")===null)&&(!l.parentNode.disabled||!c.nodeName(l.parentNode,"optgroup"))){a=c(l).val();if(b)return a;d.push(a)}}return d}if(ta.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Sa,"")}return B}var k=c.isFunction(a);return this.each(function(o){var x=c(this),r=a;if(this.nodeType===1){if(k)r=
+a.call(this,o,x.val());if(r==null)r="";else if(typeof r==="number")r+="";else if(c.isArray(r))r=c.map(r,function(C){return C==null?"":C+""});if(c.isArray(r)&&ta.test(this.type))this.checked=c.inArray(x.val(),r)>=0;else if(c.nodeName(this,"select")){var A=c.makeArray(r);c("option",this).each(function(){this.selected=c.inArray(c(this).val(),A)>=0});if(!A.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},
+attr:function(a,b,d,e){if(!a||a.nodeType===3||a.nodeType===8)return B;if(e&&b in c.attrFn)return c(a)[b](d);e=a.nodeType!==1||!c.isXMLDoc(a);var f=d!==B;b=e&&c.props[b]||b;var h=Ta.test(b);if((b in a||a[b]!==B)&&e&&!h){if(f){b==="type"&&Ua.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");if(d===null)a.nodeType===1&&a.removeAttribute(b);else a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&
+b.specified?b.value:Va.test(a.nodeName)||Wa.test(a.nodeName)&&a.href?0:B;return a[b]}if(!c.support.style&&e&&b==="style"){if(f)a.style.cssText=""+d;return a.style.cssText}f&&a.setAttribute(b,""+d);if(!a.attributes[b]&&a.hasAttribute&&!a.hasAttribute(b))return B;a=!c.support.hrefNormalized&&e&&h?a.getAttribute(b,2):a.getAttribute(b);return a===null?B:a}});var X=/\.(.*)$/,ia=/^(?:textarea|input|select)$/i,La=/\./g,Ma=/ /g,Xa=/[^\w\s.|`]/g,Ya=function(a){return a.replace(Xa,"\\$&")},ua={focusin:0,focusout:0};
+c.event={add:function(a,b,d,e){if(!(a.nodeType===3||a.nodeType===8)){if(c.isWindow(a)&&a!==E&&!a.frameElement)a=E;if(d===false)d=U;else if(!d)return;var f,h;if(d.handler){f=d;d=f.handler}if(!d.guid)d.guid=c.guid++;if(h=c.data(a)){var l=a.nodeType?"events":"__events__",k=h[l],o=h.handle;if(typeof k==="function"){o=k.handle;k=k.events}else if(!k){a.nodeType||(h[l]=h=function(){});h.events=k={}}if(!o)h.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,
+arguments):B};o.elem=a;b=b.split(" ");for(var x=0,r;l=b[x++];){h=f?c.extend({},f):{handler:d,data:e};if(l.indexOf(".")>-1){r=l.split(".");l=r.shift();h.namespace=r.slice(0).sort().join(".")}else{r=[];h.namespace=""}h.type=l;if(!h.guid)h.guid=d.guid;var A=k[l],C=c.event.special[l]||{};if(!A){A=k[l]=[];if(!C.setup||C.setup.call(a,e,r,o)===false)if(a.addEventListener)a.addEventListener(l,o,false);else a.attachEvent&&a.attachEvent("on"+l,o)}if(C.add){C.add.call(a,h);if(!h.handler.guid)h.handler.guid=
+d.guid}A.push(h);c.event.global[l]=true}a=null}}},global:{},remove:function(a,b,d,e){if(!(a.nodeType===3||a.nodeType===8)){if(d===false)d=U;var f,h,l=0,k,o,x,r,A,C,J=a.nodeType?"events":"__events__",w=c.data(a),I=w&&w[J];if(w&&I){if(typeof I==="function"){w=I;I=I.events}if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(f in I)c.event.remove(a,f+b)}else{for(b=b.split(" ");f=b[l++];){r=f;k=f.indexOf(".")<0;o=[];if(!k){o=f.split(".");f=o.shift();x=RegExp("(^|\\.)"+
+c.map(o.slice(0).sort(),Ya).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(A=I[f])if(d){r=c.event.special[f]||{};for(h=e||0;h<A.length;h++){C=A[h];if(d.guid===C.guid){if(k||x.test(C.namespace)){e==null&&A.splice(h--,1);r.remove&&r.remove.call(a,C)}if(e!=null)break}}if(A.length===0||e!=null&&A.length===1){if(!r.teardown||r.teardown.call(a,o)===false)c.removeEvent(a,f,w.handle);delete I[f]}}else for(h=0;h<A.length;h++){C=A[h];if(k||x.test(C.namespace)){c.event.remove(a,r,C.handler,h);A.splice(h--,1)}}}if(c.isEmptyObject(I)){if(b=
+w.handle)b.elem=null;delete w.events;delete w.handle;if(typeof w==="function")c.removeData(a,J);else c.isEmptyObject(w)&&c.removeData(a)}}}}},trigger:function(a,b,d,e){var f=a.type||a;if(!e){a=typeof a==="object"?a[c.expando]?a:c.extend(c.Event(f),a):c.Event(f);if(f.indexOf("!")>=0){a.type=f=f.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[f]&&c.each(c.cache,function(){this.events&&this.events[f]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===
+8)return B;a.result=B;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(e=d.nodeType?c.data(d,"handle"):(c.data(d,"__events__")||{}).handle)&&e.apply(d,b);e=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+f]&&d["on"+f].apply(d,b)===false){a.result=false;a.preventDefault()}}catch(h){}if(!a.isPropagationStopped()&&e)c.event.trigger(a,b,e,true);else if(!a.isDefaultPrevented()){var l;e=a.target;var k=f.replace(X,""),o=c.nodeName(e,"a")&&k===
+"click",x=c.event.special[k]||{};if((!x._default||x._default.call(d,a)===false)&&!o&&!(e&&e.nodeName&&c.noData[e.nodeName.toLowerCase()])){try{if(e[k]){if(l=e["on"+k])e["on"+k]=null;c.event.triggered=true;e[k]()}}catch(r){}if(l)e["on"+k]=l;c.event.triggered=false}}},handle:function(a){var b,d,e,f;d=[];var h=c.makeArray(arguments);a=h[0]=c.event.fix(a||E.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;if(!b){e=a.type.split(".");a.type=e.shift();d=e.slice(0).sort();e=RegExp("(^|\\.)"+
+d.join("\\.(?:.*\\.)?")+"(\\.|$)")}a.namespace=a.namespace||d.join(".");f=c.data(this,this.nodeType?"events":"__events__");if(typeof f==="function")f=f.events;d=(f||{})[a.type];if(f&&d){d=d.slice(0);f=0;for(var l=d.length;f<l;f++){var k=d[f];if(b||e.test(k.namespace)){a.handler=k.handler;a.data=k.data;a.handleObj=k;k=k.handler.apply(this,h);if(k!==B){a.result=k;if(k===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
+fix:function(a){if(a[c.expando])return a;var b=a;a=c.Event(b);for(var d=this.props.length,e;d;){e=this.props[--d];a[e]=b[e]}if(!a.target)a.target=a.srcElement||t;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=t.documentElement;d=t.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop||
+d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(a.which==null&&(a.charCode!=null||a.keyCode!=null))a.which=a.charCode!=null?a.charCode:a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==B)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a){c.event.add(this,Y(a.origType,a.selector),c.extend({},a,{handler:Ka,guid:a.handler.guid}))},remove:function(a){c.event.remove(this,
+Y(a.origType,a.selector),a)}},beforeunload:{setup:function(a,b,d){if(c.isWindow(this))this.onbeforeunload=d},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};c.removeEvent=t.removeEventListener?function(a,b,d){a.removeEventListener&&a.removeEventListener(b,d,false)}:function(a,b,d){a.detachEvent&&a.detachEvent("on"+b,d)};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=a;this.type=a.type}else this.type=a;this.timeStamp=
+c.now();this[c.expando]=true};c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=ca;var a=this.originalEvent;if(a)if(a.preventDefault)a.preventDefault();else a.returnValue=false},stopPropagation:function(){this.isPropagationStopped=ca;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=ca;this.stopPropagation()},isDefaultPrevented:U,isPropagationStopped:U,isImmediatePropagationStopped:U};
+var va=function(a){var b=a.relatedTarget;try{for(;b&&b!==this;)b=b.parentNode;if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}}catch(d){}},wa=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?wa:va,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?wa:va)}}});if(!c.support.submitBubbles)c.event.special.submit={setup:function(){if(this.nodeName.toLowerCase()!==
+"form"){c.event.add(this,"click.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="submit"||d==="image")&&c(b).closest("form").length){a.liveFired=B;return la("submit",this,arguments)}});c.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="text"||d==="password")&&c(b).closest("form").length&&a.keyCode===13){a.liveFired=B;return la("submit",this,arguments)}})}else return false},teardown:function(){c.event.remove(this,".specialSubmit")}};if(!c.support.changeBubbles){var V,
+xa=function(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(e){return e.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},Z=function(a,b){var d=a.target,e,f;if(!(!ia.test(d.nodeName)||d.readOnly)){e=c.data(d,"_change_data");f=xa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",f);if(!(e===B||f===e))if(e!=null||f){a.type="change";a.liveFired=
+B;return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:Z,beforedeactivate:Z,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return Z.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return Z.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,"_change_data",xa(a))}},setup:function(){if(this.type===
+"file")return false;for(var a in V)c.event.add(this,a+".specialChange",V[a]);return ia.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return ia.test(this.nodeName)}};V=c.event.special.change.filters;V.focus=V.beforeactivate}t.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(e){e=c.event.fix(e);e.type=b;return c.event.trigger(e,null,e.target)}c.event.special[b]={setup:function(){ua[b]++===0&&t.addEventListener(a,d,true)},teardown:function(){--ua[b]===
+0&&t.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,e,f){if(typeof d==="object"){for(var h in d)this[b](h,e,d[h],f);return this}if(c.isFunction(e)||e===false){f=e;e=B}var l=b==="one"?c.proxy(f,function(o){c(this).unbind(o,l);return f.apply(this,arguments)}):f;if(d==="unload"&&b!=="one")this.one(d,e,f);else{h=0;for(var k=this.length;h<k;h++)c.event.add(this[h],d,l,e)}return this}});c.fn.extend({unbind:function(a,b){if(typeof a==="object"&&!a.preventDefault)for(var d in a)this.unbind(d,
+a[d]);else{d=0;for(var e=this.length;d<e;d++)c.event.remove(this[d],a,b)}return this},delegate:function(a,b,d,e){return this.live(b,d,e,a)},undelegate:function(a,b,d){return arguments.length===0?this.unbind("live"):this.die(b,null,d,a)},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){var d=c.Event(a);d.preventDefault();d.stopPropagation();c.event.trigger(d,b,this[0]);return d.result}},toggle:function(a){for(var b=arguments,d=
+1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(e){var f=(c.data(this,"lastToggle"+a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,f+1);e.preventDefault();return b[f].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var ya={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};c.each(["live","die"],function(a,b){c.fn[b]=function(d,e,f,h){var l,k=0,o,x,r=h||this.selector;h=h?this:c(this.context);if(typeof d===
+"object"&&!d.preventDefault){for(l in d)h[b](l,e,d[l],r);return this}if(c.isFunction(e)){f=e;e=B}for(d=(d||"").split(" ");(l=d[k++])!=null;){o=X.exec(l);x="";if(o){x=o[0];l=l.replace(X,"")}if(l==="hover")d.push("mouseenter"+x,"mouseleave"+x);else{o=l;if(l==="focus"||l==="blur"){d.push(ya[l]+x);l+=x}else l=(ya[l]||l)+x;if(b==="live"){x=0;for(var A=h.length;x<A;x++)c.event.add(h[x],"live."+Y(l,r),{data:e,selector:r,handler:f,origType:l,origHandler:f,preType:o})}else h.unbind("live."+Y(l,r),f)}}return this}});
+c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),function(a,b){c.fn[b]=function(d,e){if(e==null){e=d;d=null}return arguments.length>0?this.bind(b,d,e):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});E.attachEvent&&!E.addEventListener&&c(E).bind("unload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});
+(function(){function a(g,i,n,m,p,q){p=0;for(var u=m.length;p<u;p++){var y=m[p];if(y){var F=false;for(y=y[g];y;){if(y.sizcache===n){F=m[y.sizset];break}if(y.nodeType===1&&!q){y.sizcache=n;y.sizset=p}if(y.nodeName.toLowerCase()===i){F=y;break}y=y[g]}m[p]=F}}}function b(g,i,n,m,p,q){p=0;for(var u=m.length;p<u;p++){var y=m[p];if(y){var F=false;for(y=y[g];y;){if(y.sizcache===n){F=m[y.sizset];break}if(y.nodeType===1){if(!q){y.sizcache=n;y.sizset=p}if(typeof i!=="string"){if(y===i){F=true;break}}else if(k.filter(i,
+[y]).length>0){F=y;break}}y=y[g]}m[p]=F}}}var d=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,e=0,f=Object.prototype.toString,h=false,l=true;[0,0].sort(function(){l=false;return 0});var k=function(g,i,n,m){n=n||[];var p=i=i||t;if(i.nodeType!==1&&i.nodeType!==9)return[];if(!g||typeof g!=="string")return n;var q,u,y,F,M,N=true,O=k.isXML(i),D=[],R=g;do{d.exec("");if(q=d.exec(R)){R=q[3];D.push(q[1]);if(q[2]){F=q[3];
+break}}}while(q);if(D.length>1&&x.exec(g))if(D.length===2&&o.relative[D[0]])u=L(D[0]+D[1],i);else for(u=o.relative[D[0]]?[i]:k(D.shift(),i);D.length;){g=D.shift();if(o.relative[g])g+=D.shift();u=L(g,u)}else{if(!m&&D.length>1&&i.nodeType===9&&!O&&o.match.ID.test(D[0])&&!o.match.ID.test(D[D.length-1])){q=k.find(D.shift(),i,O);i=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]}if(i){q=m?{expr:D.pop(),set:C(m)}:k.find(D.pop(),D.length===1&&(D[0]==="~"||D[0]==="+")&&i.parentNode?i.parentNode:i,O);u=q.expr?k.filter(q.expr,
+q.set):q.set;if(D.length>0)y=C(u);else N=false;for(;D.length;){q=M=D.pop();if(o.relative[M])q=D.pop();else M="";if(q==null)q=i;o.relative[M](y,q,O)}}else y=[]}y||(y=u);y||k.error(M||g);if(f.call(y)==="[object Array]")if(N)if(i&&i.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&k.contains(i,y[g])))n.push(u[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&n.push(u[g]);else n.push.apply(n,y);else C(y,n);if(F){k(F,p,n,m);k.uniqueSort(n)}return n};k.uniqueSort=function(g){if(w){h=
+l;g.sort(w);if(h)for(var i=1;i<g.length;i++)g[i]===g[i-1]&&g.splice(i--,1)}return g};k.matches=function(g,i){return k(g,null,null,i)};k.matchesSelector=function(g,i){return k(i,null,null,[g]).length>0};k.find=function(g,i,n){var m;if(!g)return[];for(var p=0,q=o.order.length;p<q;p++){var u,y=o.order[p];if(u=o.leftMatch[y].exec(g)){var F=u[1];u.splice(1,1);if(F.substr(F.length-1)!=="\\"){u[1]=(u[1]||"").replace(/\\/g,"");m=o.find[y](u,i,n);if(m!=null){g=g.replace(o.match[y],"");break}}}}m||(m=i.getElementsByTagName("*"));
+return{set:m,expr:g}};k.filter=function(g,i,n,m){for(var p,q,u=g,y=[],F=i,M=i&&i[0]&&k.isXML(i[0]);g&&i.length;){for(var N in o.filter)if((p=o.leftMatch[N].exec(g))!=null&&p[2]){var O,D,R=o.filter[N];D=p[1];q=false;p.splice(1,1);if(D.substr(D.length-1)!=="\\"){if(F===y)y=[];if(o.preFilter[N])if(p=o.preFilter[N](p,F,n,y,m,M)){if(p===true)continue}else q=O=true;if(p)for(var j=0;(D=F[j])!=null;j++)if(D){O=R(D,p,j,F);var s=m^!!O;if(n&&O!=null)if(s)q=true;else F[j]=false;else if(s){y.push(D);q=true}}if(O!==
+B){n||(F=y);g=g.replace(o.match[N],"");if(!q)return[];break}}}if(g===u)if(q==null)k.error(g);else break;u=g}return F};k.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var o=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+\-]*)\))?/,
+POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}},relative:{"+":function(g,i){var n=typeof i==="string",m=n&&!/\W/.test(i);n=n&&!m;if(m)i=i.toLowerCase();m=0;for(var p=g.length,q;m<p;m++)if(q=g[m]){for(;(q=q.previousSibling)&&q.nodeType!==1;);g[m]=n||q&&q.nodeName.toLowerCase()===
+i?q||false:q===i}n&&k.filter(i,g,true)},">":function(g,i){var n,m=typeof i==="string",p=0,q=g.length;if(m&&!/\W/.test(i))for(i=i.toLowerCase();p<q;p++){if(n=g[p]){n=n.parentNode;g[p]=n.nodeName.toLowerCase()===i?n:false}}else{for(;p<q;p++)if(n=g[p])g[p]=m?n.parentNode:n.parentNode===i;m&&k.filter(i,g,true)}},"":function(g,i,n){var m,p=e++,q=b;if(typeof i==="string"&&!/\W/.test(i)){m=i=i.toLowerCase();q=a}q("parentNode",i,p,g,m,n)},"~":function(g,i,n){var m,p=e++,q=b;if(typeof i==="string"&&!/\W/.test(i)){m=
+i=i.toLowerCase();q=a}q("previousSibling",i,p,g,m,n)}},find:{ID:function(g,i,n){if(typeof i.getElementById!=="undefined"&&!n)return(g=i.getElementById(g[1]))&&g.parentNode?[g]:[]},NAME:function(g,i){if(typeof i.getElementsByName!=="undefined"){for(var n=[],m=i.getElementsByName(g[1]),p=0,q=m.length;p<q;p++)m[p].getAttribute("name")===g[1]&&n.push(m[p]);return n.length===0?null:n}},TAG:function(g,i){return i.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,i,n,m,p,q){g=" "+g[1].replace(/\\/g,
+"")+" ";if(q)return g;q=0;for(var u;(u=i[q])!=null;q++)if(u)if(p^(u.className&&(" "+u.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))n||m.push(u);else if(n)i[q]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},CHILD:function(g){if(g[1]==="nth"){var i=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=i[1]+(i[2]||1)-0;g[3]=i[3]-0}g[0]=e++;return g},ATTR:function(g,i,n,
+m,p,q){i=g[1].replace(/\\/g,"");if(!q&&o.attrMap[i])g[1]=o.attrMap[i];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,i,n,m,p){if(g[1]==="not")if((d.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,i);else{g=k.filter(g[3],i,n,true^p);n||m.push.apply(m,g);return false}else if(o.match.POS.test(g[0])||o.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===
+true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,i,n){return!!k(n[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===
+g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},setFilters:{first:function(g,i){return i===0},last:function(g,i,n,m){return i===m.length-1},even:function(g,i){return i%2===0},odd:function(g,i){return i%2===1},lt:function(g,i,n){return i<n[3]-0},gt:function(g,i,n){return i>n[3]-0},nth:function(g,i,n){return n[3]-
+0===i},eq:function(g,i,n){return n[3]-0===i}},filter:{PSEUDO:function(g,i,n,m){var p=i[1],q=o.filters[p];if(q)return q(g,n,i,m);else if(p==="contains")return(g.textContent||g.innerText||k.getText([g])||"").indexOf(i[3])>=0;else if(p==="not"){i=i[3];n=0;for(m=i.length;n<m;n++)if(i[n]===g)return false;return true}else k.error("Syntax error, unrecognized expression: "+p)},CHILD:function(g,i){var n=i[1],m=g;switch(n){case "only":case "first":for(;m=m.previousSibling;)if(m.nodeType===1)return false;if(n===
+"first")return true;m=g;case "last":for(;m=m.nextSibling;)if(m.nodeType===1)return false;return true;case "nth":n=i[2];var p=i[3];if(n===1&&p===0)return true;var q=i[0],u=g.parentNode;if(u&&(u.sizcache!==q||!g.nodeIndex)){var y=0;for(m=u.firstChild;m;m=m.nextSibling)if(m.nodeType===1)m.nodeIndex=++y;u.sizcache=q}m=g.nodeIndex-p;return n===0?m===0:m%n===0&&m/n>=0}},ID:function(g,i){return g.nodeType===1&&g.getAttribute("id")===i},TAG:function(g,i){return i==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===
+i},CLASS:function(g,i){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(i)>-1},ATTR:function(g,i){var n=i[1];n=o.attrHandle[n]?o.attrHandle[n](g):g[n]!=null?g[n]:g.getAttribute(n);var m=n+"",p=i[2],q=i[4];return n==null?p==="!=":p==="="?m===q:p==="*="?m.indexOf(q)>=0:p==="~="?(" "+m+" ").indexOf(q)>=0:!q?m&&n!==false:p==="!="?m!==q:p==="^="?m.indexOf(q)===0:p==="$="?m.substr(m.length-q.length)===q:p==="|="?m===q||m.substr(0,q.length+1)===q+"-":false},POS:function(g,i,n,m){var p=o.setFilters[i[2]];
+if(p)return p(g,n,i,m)}}},x=o.match.POS,r=function(g,i){return"\\"+(i-0+1)},A;for(A in o.match){o.match[A]=RegExp(o.match[A].source+/(?![^\[]*\])(?![^\(]*\))/.source);o.leftMatch[A]=RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[A].source.replace(/\\(\d+)/g,r))}var C=function(g,i){g=Array.prototype.slice.call(g,0);if(i){i.push.apply(i,g);return i}return g};try{Array.prototype.slice.call(t.documentElement.childNodes,0)}catch(J){C=function(g,i){var n=0,m=i||[];if(f.call(g)==="[object Array]")Array.prototype.push.apply(m,
+g);else if(typeof g.length==="number")for(var p=g.length;n<p;n++)m.push(g[n]);else for(;g[n];n++)m.push(g[n]);return m}}var w,I;if(t.documentElement.compareDocumentPosition)w=function(g,i){if(g===i){h=true;return 0}if(!g.compareDocumentPosition||!i.compareDocumentPosition)return g.compareDocumentPosition?-1:1;return g.compareDocumentPosition(i)&4?-1:1};else{w=function(g,i){var n,m,p=[],q=[];n=g.parentNode;m=i.parentNode;var u=n;if(g===i){h=true;return 0}else if(n===m)return I(g,i);else if(n){if(!m)return 1}else return-1;
+for(;u;){p.unshift(u);u=u.parentNode}for(u=m;u;){q.unshift(u);u=u.parentNode}n=p.length;m=q.length;for(u=0;u<n&&u<m;u++)if(p[u]!==q[u])return I(p[u],q[u]);return u===n?I(g,q[u],-1):I(p[u],i,1)};I=function(g,i,n){if(g===i)return n;for(g=g.nextSibling;g;){if(g===i)return-1;g=g.nextSibling}return 1}}k.getText=function(g){for(var i="",n,m=0;g[m];m++){n=g[m];if(n.nodeType===3||n.nodeType===4)i+=n.nodeValue;else if(n.nodeType!==8)i+=k.getText(n.childNodes)}return i};(function(){var g=t.createElement("div"),
+i="script"+(new Date).getTime(),n=t.documentElement;g.innerHTML="<a name='"+i+"'/>";n.insertBefore(g,n.firstChild);if(t.getElementById(i)){o.find.ID=function(m,p,q){if(typeof p.getElementById!=="undefined"&&!q)return(p=p.getElementById(m[1]))?p.id===m[1]||typeof p.getAttributeNode!=="undefined"&&p.getAttributeNode("id").nodeValue===m[1]?[p]:B:[]};o.filter.ID=function(m,p){var q=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&q&&q.nodeValue===p}}n.removeChild(g);
+n=g=null})();(function(){var g=t.createElement("div");g.appendChild(t.createComment(""));if(g.getElementsByTagName("*").length>0)o.find.TAG=function(i,n){var m=n.getElementsByTagName(i[1]);if(i[1]==="*"){for(var p=[],q=0;m[q];q++)m[q].nodeType===1&&p.push(m[q]);m=p}return m};g.innerHTML="<a href='#'></a>";if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")o.attrHandle.href=function(i){return i.getAttribute("href",2)};g=null})();t.querySelectorAll&&
+function(){var g=k,i=t.createElement("div");i.innerHTML="<p class='TEST'></p>";if(!(i.querySelectorAll&&i.querySelectorAll(".TEST").length===0)){k=function(m,p,q,u){p=p||t;m=m.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!u&&!k.isXML(p))if(p.nodeType===9)try{return C(p.querySelectorAll(m),q)}catch(y){}else if(p.nodeType===1&&p.nodeName.toLowerCase()!=="object"){var F=p.getAttribute("id"),M=F||"__sizzle__";F||p.setAttribute("id",M);try{return C(p.querySelectorAll("#"+M+" "+m),q)}catch(N){}finally{F||
+p.removeAttribute("id")}}return g(m,p,q,u)};for(var n in g)k[n]=g[n];i=null}}();(function(){var g=t.documentElement,i=g.matchesSelector||g.mozMatchesSelector||g.webkitMatchesSelector||g.msMatchesSelector,n=false;try{i.call(t.documentElement,"[test!='']:sizzle")}catch(m){n=true}if(i)k.matchesSelector=function(p,q){q=q.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!k.isXML(p))try{if(n||!o.match.PSEUDO.test(q)&&!/!=/.test(q))return i.call(p,q)}catch(u){}return k(q,null,null,[p]).length>0}})();(function(){var g=
+t.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){o.order.splice(1,0,"CLASS");o.find.CLASS=function(i,n,m){if(typeof n.getElementsByClassName!=="undefined"&&!m)return n.getElementsByClassName(i[1])};g=null}}})();k.contains=t.documentElement.contains?function(g,i){return g!==i&&(g.contains?g.contains(i):true)}:t.documentElement.compareDocumentPosition?
+function(g,i){return!!(g.compareDocumentPosition(i)&16)}:function(){return false};k.isXML=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false};var L=function(g,i){for(var n,m=[],p="",q=i.nodeType?[i]:i;n=o.match.PSEUDO.exec(g);){p+=n[0];g=g.replace(o.match.PSEUDO,"")}g=o.relative[g]?g+"*":g;n=0;for(var u=q.length;n<u;n++)k(g,q[n],m);return k.filter(p,m)};c.find=k;c.expr=k.selectors;c.expr[":"]=c.expr.filters;c.unique=k.uniqueSort;c.text=k.getText;c.isXMLDoc=k.isXML;
+c.contains=k.contains})();var Za=/Until$/,$a=/^(?:parents|prevUntil|prevAll)/,ab=/,/,Na=/^.[^:#\[\.,]*$/,bb=Array.prototype.slice,cb=c.expr.match.POS;c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,e=0,f=this.length;e<f;e++){d=b.length;c.find(a,this[e],b);if(e>0)for(var h=d;h<b.length;h++)for(var l=0;l<d;l++)if(b[l]===b[h]){b.splice(h--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=0,e=b.length;d<e;d++)if(c.contains(this,b[d]))return true})},
+not:function(a){return this.pushStack(ma(this,a,false),"not",a)},filter:function(a){return this.pushStack(ma(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){var d=[],e,f,h=this[0];if(c.isArray(a)){var l,k={},o=1;if(h&&a.length){e=0;for(f=a.length;e<f;e++){l=a[e];k[l]||(k[l]=c.expr.match.POS.test(l)?c(l,b||this.context):l)}for(;h&&h.ownerDocument&&h!==b;){for(l in k){e=k[l];if(e.jquery?e.index(h)>-1:c(h).is(e))d.push({selector:l,elem:h,level:o})}h=
+h.parentNode;o++}}return d}l=cb.test(a)?c(a,b||this.context):null;e=0;for(f=this.length;e<f;e++)for(h=this[e];h;)if(l?l.index(h)>-1:c.find.matchesSelector(h,a)){d.push(h);break}else{h=h.parentNode;if(!h||!h.ownerDocument||h===b)break}d=d.length>1?c.unique(d):d;return this.pushStack(d,"closest",a)},index:function(a){if(!a||typeof a==="string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var d=typeof a==="string"?c(a,b||this.context):
+c.makeArray(a),e=c.merge(this.get(),d);return this.pushStack(!d[0]||!d[0].parentNode||d[0].parentNode.nodeType===11||!e[0]||!e[0].parentNode||e[0].parentNode.nodeType===11?e:c.unique(e))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,
+2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,
+b){c.fn[a]=function(d,e){var f=c.map(this,b,d);Za.test(a)||(e=d);if(e&&typeof e==="string")f=c.filter(e,f);f=this.length>1?c.unique(f):f;if((this.length>1||ab.test(e))&&$a.test(a))f=f.reverse();return this.pushStack(f,a,bb.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return b.length===1?c.find.matchesSelector(b[0],a)?[b[0]]:[]:c.find.matches(a,b)},dir:function(a,b,d){var e=[];for(a=a[b];a&&a.nodeType!==9&&(d===B||a.nodeType!==1||!c(a).is(d));){a.nodeType===1&&
+e.push(a);a=a[b]}return e},nth:function(a,b,d){b=b||1;for(var e=0;a;a=a[d])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var za=/ jQuery\d+="(?:\d+|null)"/g,$=/^\s+/,Aa=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Ba=/<([\w:]+)/,db=/<tbody/i,eb=/<|&#?\w+;/,Ca=/<(?:script|object|embed|option|style)/i,Da=/checked\s*(?:[^=]|=\s*.checked.)/i,fb=/\=([^="'>\s]+\/)>/g,P={option:[1,
+"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};P.optgroup=P.option;P.tbody=P.tfoot=P.colgroup=P.caption=P.thead;P.th=P.td;if(!c.support.htmlSerialize)P._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=
+c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==B)return this.empty().append((this[0]&&this[0].ownerDocument||t).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},
+wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},
+prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,
+this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,e;(e=this[d])!=null;d++)if(!a||c.filter(a,[e]).length){if(!b&&e.nodeType===1){c.cleanData(e.getElementsByTagName("*"));c.cleanData([e])}e.parentNode&&e.parentNode.removeChild(e)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild);
+return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,e=this.ownerDocument;if(!d){d=e.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(za,"").replace(fb,'="$1">').replace($,"")],e)[0]}else return this.cloneNode(true)});if(a===true){na(this,b);na(this.find("*"),b.find("*"))}return b},html:function(a){if(a===B)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(za,""):null;
+else if(typeof a==="string"&&!Ca.test(a)&&(c.support.leadingWhitespace||!$.test(a))&&!P[(Ba.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Aa,"<$1></$2>");try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(e){this.empty().append(a)}}else c.isFunction(a)?this.each(function(f){var h=c(this);h.html(a.call(this,f,h.html()))}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=
+c(this),e=d.html();d.replaceWith(a.call(this,b,e))});if(typeof a!=="string")a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){var e,f,h,l=a[0],k=[];if(!c.support.checkClone&&arguments.length===3&&typeof l==="string"&&Da.test(l))return this.each(function(){c(this).domManip(a,
+b,d,true)});if(c.isFunction(l))return this.each(function(x){var r=c(this);a[0]=l.call(this,x,b?r.html():B);r.domManip(a,b,d)});if(this[0]){e=l&&l.parentNode;e=c.support.parentNode&&e&&e.nodeType===11&&e.childNodes.length===this.length?{fragment:e}:c.buildFragment(a,this,k);h=e.fragment;if(f=h.childNodes.length===1?h=h.firstChild:h.firstChild){b=b&&c.nodeName(f,"tr");f=0;for(var o=this.length;f<o;f++)d.call(b?c.nodeName(this[f],"table")?this[f].getElementsByTagName("tbody")[0]||this[f].appendChild(this[f].ownerDocument.createElement("tbody")):
+this[f]:this[f],f>0||e.cacheable||this.length>1?h.cloneNode(true):h)}k.length&&c.each(k,Oa)}return this}});c.buildFragment=function(a,b,d){var e,f,h;b=b&&b[0]?b[0].ownerDocument||b[0]:t;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===t&&!Ca.test(a[0])&&(c.support.checkClone||!Da.test(a[0]))){f=true;if(h=c.fragments[a[0]])if(h!==1)e=h}if(!e){e=b.createDocumentFragment();c.clean(a,b,e,d)}if(f)c.fragments[a[0]]=h?e:1;return{fragment:e,cacheable:f}};c.fragments={};c.each({appendTo:"append",
+prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var e=[];d=c(d);var f=this.length===1&&this[0].parentNode;if(f&&f.nodeType===11&&f.childNodes.length===1&&d.length===1){d[b](this[0]);return this}else{f=0;for(var h=d.length;f<h;f++){var l=(f>0?this.clone(true):this).get();c(d[f])[b](l);e=e.concat(l)}return this.pushStack(e,a,d.selector)}}});c.extend({clean:function(a,b,d,e){b=b||t;if(typeof b.createElement==="undefined")b=b.ownerDocument||
+b[0]&&b[0].ownerDocument||t;for(var f=[],h=0,l;(l=a[h])!=null;h++){if(typeof l==="number")l+="";if(l){if(typeof l==="string"&&!eb.test(l))l=b.createTextNode(l);else if(typeof l==="string"){l=l.replace(Aa,"<$1></$2>");var k=(Ba.exec(l)||["",""])[1].toLowerCase(),o=P[k]||P._default,x=o[0],r=b.createElement("div");for(r.innerHTML=o[1]+l+o[2];x--;)r=r.lastChild;if(!c.support.tbody){x=db.test(l);k=k==="table"&&!x?r.firstChild&&r.firstChild.childNodes:o[1]==="<table>"&&!x?r.childNodes:[];for(o=k.length-
+1;o>=0;--o)c.nodeName(k[o],"tbody")&&!k[o].childNodes.length&&k[o].parentNode.removeChild(k[o])}!c.support.leadingWhitespace&&$.test(l)&&r.insertBefore(b.createTextNode($.exec(l)[0]),r.firstChild);l=r.childNodes}if(l.nodeType)f.push(l);else f=c.merge(f,l)}}if(d)for(h=0;f[h];h++)if(e&&c.nodeName(f[h],"script")&&(!f[h].type||f[h].type.toLowerCase()==="text/javascript"))e.push(f[h].parentNode?f[h].parentNode.removeChild(f[h]):f[h]);else{f[h].nodeType===1&&f.splice.apply(f,[h+1,0].concat(c.makeArray(f[h].getElementsByTagName("script"))));
+d.appendChild(f[h])}return f},cleanData:function(a){for(var b,d,e=c.cache,f=c.event.special,h=c.support.deleteExpando,l=0,k;(k=a[l])!=null;l++)if(!(k.nodeName&&c.noData[k.nodeName.toLowerCase()]))if(d=k[c.expando]){if((b=e[d])&&b.events)for(var o in b.events)f[o]?c.event.remove(k,o):c.removeEvent(k,o,b.handle);if(h)delete k[c.expando];else k.removeAttribute&&k.removeAttribute(c.expando);delete e[d]}}});var Ea=/alpha\([^)]*\)/i,gb=/opacity=([^)]*)/,hb=/-([a-z])/ig,ib=/([A-Z])/g,Fa=/^-?\d+(?:px)?$/i,
+jb=/^-?\d/,kb={position:"absolute",visibility:"hidden",display:"block"},Pa=["Left","Right"],Qa=["Top","Bottom"],W,Ga,aa,lb=function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){if(arguments.length===2&&b===B)return this;return c.access(this,a,b,true,function(d,e,f){return f!==B?c.style(d,e,f):c.css(d,e)})};c.extend({cssHooks:{opacity:{get:function(a,b){if(b){var d=W(a,"opacity","opacity");return d===""?"1":d}else return a.style.opacity}}},cssNumber:{zIndex:true,fontWeight:true,opacity:true,
+zoom:true,lineHeight:true},cssProps:{"float":c.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,b,d,e){if(!(!a||a.nodeType===3||a.nodeType===8||!a.style)){var f,h=c.camelCase(b),l=a.style,k=c.cssHooks[h];b=c.cssProps[h]||h;if(d!==B){if(!(typeof d==="number"&&isNaN(d)||d==null)){if(typeof d==="number"&&!c.cssNumber[h])d+="px";if(!k||!("set"in k)||(d=k.set(a,d))!==B)try{l[b]=d}catch(o){}}}else{if(k&&"get"in k&&(f=k.get(a,false,e))!==B)return f;return l[b]}}},css:function(a,b,d){var e,f=c.camelCase(b),
+h=c.cssHooks[f];b=c.cssProps[f]||f;if(h&&"get"in h&&(e=h.get(a,true,d))!==B)return e;else if(W)return W(a,b,f)},swap:function(a,b,d){var e={},f;for(f in b){e[f]=a.style[f];a.style[f]=b[f]}d.call(a);for(f in b)a.style[f]=e[f]},camelCase:function(a){return a.replace(hb,lb)}});c.curCSS=c.css;c.each(["height","width"],function(a,b){c.cssHooks[b]={get:function(d,e,f){var h;if(e){if(d.offsetWidth!==0)h=oa(d,b,f);else c.swap(d,kb,function(){h=oa(d,b,f)});if(h<=0){h=W(d,b,b);if(h==="0px"&&aa)h=aa(d,b,b);
+if(h!=null)return h===""||h==="auto"?"0px":h}if(h<0||h==null){h=d.style[b];return h===""||h==="auto"?"0px":h}return typeof h==="string"?h:h+"px"}},set:function(d,e){if(Fa.test(e)){e=parseFloat(e);if(e>=0)return e+"px"}else return e}}});if(!c.support.opacity)c.cssHooks.opacity={get:function(a,b){return gb.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var d=a.style;d.zoom=1;var e=c.isNaN(b)?"":"alpha(opacity="+b*100+")",f=
+d.filter||"";d.filter=Ea.test(f)?f.replace(Ea,e):d.filter+" "+e}};if(t.defaultView&&t.defaultView.getComputedStyle)Ga=function(a,b,d){var e;d=d.replace(ib,"-$1").toLowerCase();if(!(b=a.ownerDocument.defaultView))return B;if(b=b.getComputedStyle(a,null)){e=b.getPropertyValue(d);if(e===""&&!c.contains(a.ownerDocument.documentElement,a))e=c.style(a,d)}return e};if(t.documentElement.currentStyle)aa=function(a,b){var d,e,f=a.currentStyle&&a.currentStyle[b],h=a.style;if(!Fa.test(f)&&jb.test(f)){d=h.left;
+e=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;h.left=b==="fontSize"?"1em":f||0;f=h.pixelLeft+"px";h.left=d;a.runtimeStyle.left=e}return f===""?"auto":f};W=Ga||aa;if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=a.offsetHeight;return a.offsetWidth===0&&b===0||!c.support.reliableHiddenOffsets&&(a.style.display||c.css(a,"display"))==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var mb=c.now(),nb=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
+ob=/^(?:select|textarea)/i,pb=/^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,qb=/^(?:GET|HEAD)$/,Ra=/\[\]$/,T=/\=\?(&|$)/,ja=/\?/,rb=/([?&])_=[^&]*/,sb=/^(\w+:)?\/\/([^\/?#]+)/,tb=/%20/g,ub=/#.*$/,Ha=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!=="string"&&Ha)return Ha.apply(this,arguments);else if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var f=a.slice(e,a.length);a=a.slice(0,e)}e="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b===
+"object"){b=c.param(b,c.ajaxSettings.traditional);e="POST"}var h=this;c.ajax({url:a,type:e,dataType:"html",data:b,complete:function(l,k){if(k==="success"||k==="notmodified")h.html(f?c("<div>").append(l.responseText.replace(nb,"")).find(f):l.responseText);d&&h.each(d,[l.responseText,k,l])}});return this},serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&
+!this.disabled&&(this.checked||ob.test(this.nodeName)||pb.test(this.type))}).map(function(a,b){var d=c(this).val();return d==null?null:c.isArray(d)?c.map(d,function(e){return{name:b.name,value:e}}):{name:b.name,value:d}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,e){if(c.isFunction(b)){e=e||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:e})},
+getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,e){if(c.isFunction(b)){e=e||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:e})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return new E.XMLHttpRequest},accepts:{xml:"application/xml, text/xml",html:"text/html",
+script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},ajax:function(a){var b=c.extend(true,{},c.ajaxSettings,a),d,e,f,h=b.type.toUpperCase(),l=qb.test(h);b.url=b.url.replace(ub,"");b.context=a&&a.context!=null?a.context:b;if(b.data&&b.processData&&typeof b.data!=="string")b.data=c.param(b.data,b.traditional);if(b.dataType==="jsonp"){if(h==="GET")T.test(b.url)||(b.url+=(ja.test(b.url)?"&":"?")+(b.jsonp||"callback")+"=?");else if(!b.data||
+!T.test(b.data))b.data=(b.data?b.data+"&":"")+(b.jsonp||"callback")+"=?";b.dataType="json"}if(b.dataType==="json"&&(b.data&&T.test(b.data)||T.test(b.url))){d=b.jsonpCallback||"jsonp"+mb++;if(b.data)b.data=(b.data+"").replace(T,"="+d+"$1");b.url=b.url.replace(T,"="+d+"$1");b.dataType="script";var k=E[d];E[d]=function(m){if(c.isFunction(k))k(m);else{E[d]=B;try{delete E[d]}catch(p){}}f=m;c.handleSuccess(b,w,e,f);c.handleComplete(b,w,e,f);r&&r.removeChild(A)}}if(b.dataType==="script"&&b.cache===null)b.cache=
+false;if(b.cache===false&&l){var o=c.now(),x=b.url.replace(rb,"$1_="+o);b.url=x+(x===b.url?(ja.test(b.url)?"&":"?")+"_="+o:"")}if(b.data&&l)b.url+=(ja.test(b.url)?"&":"?")+b.data;b.global&&c.active++===0&&c.event.trigger("ajaxStart");o=(o=sb.exec(b.url))&&(o[1]&&o[1].toLowerCase()!==location.protocol||o[2].toLowerCase()!==location.host);if(b.dataType==="script"&&h==="GET"&&o){var r=t.getElementsByTagName("head")[0]||t.documentElement,A=t.createElement("script");if(b.scriptCharset)A.charset=b.scriptCharset;
+A.src=b.url;if(!d){var C=false;A.onload=A.onreadystatechange=function(){if(!C&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){C=true;c.handleSuccess(b,w,e,f);c.handleComplete(b,w,e,f);A.onload=A.onreadystatechange=null;r&&A.parentNode&&r.removeChild(A)}}}r.insertBefore(A,r.firstChild);return B}var J=false,w=b.xhr();if(w){b.username?w.open(h,b.url,b.async,b.username,b.password):w.open(h,b.url,b.async);try{if(b.data!=null&&!l||a&&a.contentType)w.setRequestHeader("Content-Type",
+b.contentType);if(b.ifModified){c.lastModified[b.url]&&w.setRequestHeader("If-Modified-Since",c.lastModified[b.url]);c.etag[b.url]&&w.setRequestHeader("If-None-Match",c.etag[b.url])}o||w.setRequestHeader("X-Requested-With","XMLHttpRequest");w.setRequestHeader("Accept",b.dataType&&b.accepts[b.dataType]?b.accepts[b.dataType]+", */*; q=0.01":b.accepts._default)}catch(I){}if(b.beforeSend&&b.beforeSend.call(b.context,w,b)===false){b.global&&c.active--===1&&c.event.trigger("ajaxStop");w.abort();return false}b.global&&
+c.triggerGlobal(b,"ajaxSend",[w,b]);var L=w.onreadystatechange=function(m){if(!w||w.readyState===0||m==="abort"){J||c.handleComplete(b,w,e,f);J=true;if(w)w.onreadystatechange=c.noop}else if(!J&&w&&(w.readyState===4||m==="timeout")){J=true;w.onreadystatechange=c.noop;e=m==="timeout"?"timeout":!c.httpSuccess(w)?"error":b.ifModified&&c.httpNotModified(w,b.url)?"notmodified":"success";var p;if(e==="success")try{f=c.httpData(w,b.dataType,b)}catch(q){e="parsererror";p=q}if(e==="success"||e==="notmodified")d||
+c.handleSuccess(b,w,e,f);else c.handleError(b,w,e,p);d||c.handleComplete(b,w,e,f);m==="timeout"&&w.abort();if(b.async)w=null}};try{var g=w.abort;w.abort=function(){w&&Function.prototype.call.call(g,w);L("abort")}}catch(i){}b.async&&b.timeout>0&&setTimeout(function(){w&&!J&&L("timeout")},b.timeout);try{w.send(l||b.data==null?null:b.data)}catch(n){c.handleError(b,w,null,n);c.handleComplete(b,w,e,f)}b.async||L();return w}},param:function(a,b){var d=[],e=function(h,l){l=c.isFunction(l)?l():l;d[d.length]=
+encodeURIComponent(h)+"="+encodeURIComponent(l)};if(b===B)b=c.ajaxSettings.traditional;if(c.isArray(a)||a.jquery)c.each(a,function(){e(this.name,this.value)});else for(var f in a)da(f,a[f],b,e);return d.join("&").replace(tb,"+")}});c.extend({active:0,lastModified:{},etag:{},handleError:function(a,b,d,e){a.error&&a.error.call(a.context,b,d,e);a.global&&c.triggerGlobal(a,"ajaxError",[b,a,e])},handleSuccess:function(a,b,d,e){a.success&&a.success.call(a.context,e,d,b);a.global&&c.triggerGlobal(a,"ajaxSuccess",
+[b,a])},handleComplete:function(a,b,d){a.complete&&a.complete.call(a.context,b,d);a.global&&c.triggerGlobal(a,"ajaxComplete",[b,a]);a.global&&c.active--===1&&c.event.trigger("ajaxStop")},triggerGlobal:function(a,b,d){(a.context&&a.context.url==null?c(a.context):c.event).trigger(b,d)},httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===1223}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),
+e=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(e)c.etag[b]=e;return a.status===304},httpData:function(a,b,d){var e=a.getResponseHeader("content-type")||"",f=b==="xml"||!b&&e.indexOf("xml")>=0;a=f?a.responseXML:a.responseText;f&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b==="json"||!b&&e.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&e.indexOf("javascript")>=0)c.globalEval(a);return a}});
+if(E.ActiveXObject)c.ajaxSettings.xhr=function(){if(E.location.protocol!=="file:")try{return new E.XMLHttpRequest}catch(a){}try{return new E.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}};c.support.ajax=!!c.ajaxSettings.xhr();var ea={},vb=/^(?:toggle|show|hide)$/,wb=/^([+\-]=)?([\d+.\-]+)(.*)$/,ba,pa=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b,d){if(a||a===0)return this.animate(S("show",
+3),a,b,d);else{d=0;for(var e=this.length;d<e;d++){a=this[d];b=a.style.display;if(!c.data(a,"olddisplay")&&b==="none")b=a.style.display="";b===""&&c.css(a,"display")==="none"&&c.data(a,"olddisplay",qa(a.nodeName))}for(d=0;d<e;d++){a=this[d];b=a.style.display;if(b===""||b==="none")a.style.display=c.data(a,"olddisplay")||""}return this}},hide:function(a,b,d){if(a||a===0)return this.animate(S("hide",3),a,b,d);else{a=0;for(b=this.length;a<b;a++){d=c.css(this[a],"display");d!=="none"&&c.data(this[a],"olddisplay",
+d)}for(a=0;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b,d){var e=typeof a==="boolean";if(c.isFunction(a)&&c.isFunction(b))this._toggle.apply(this,arguments);else a==null||e?this.each(function(){var f=e?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(S("toggle",3),a,b,d);return this},fadeTo:function(a,b,d,e){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d,e)},animate:function(a,b,d,e){var f=c.speed(b,
+d,e);if(c.isEmptyObject(a))return this.each(f.complete);return this[f.queue===false?"each":"queue"](function(){var h=c.extend({},f),l,k=this.nodeType===1,o=k&&c(this).is(":hidden"),x=this;for(l in a){var r=c.camelCase(l);if(l!==r){a[r]=a[l];delete a[l];l=r}if(a[l]==="hide"&&o||a[l]==="show"&&!o)return h.complete.call(this);if(k&&(l==="height"||l==="width")){h.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY];if(c.css(this,"display")==="inline"&&c.css(this,"float")==="none")if(c.support.inlineBlockNeedsLayout)if(qa(this.nodeName)===
+"inline")this.style.display="inline-block";else{this.style.display="inline";this.style.zoom=1}else this.style.display="inline-block"}if(c.isArray(a[l])){(h.specialEasing=h.specialEasing||{})[l]=a[l][1];a[l]=a[l][0]}}if(h.overflow!=null)this.style.overflow="hidden";h.curAnim=c.extend({},a);c.each(a,function(A,C){var J=new c.fx(x,h,A);if(vb.test(C))J[C==="toggle"?o?"show":"hide":C](a);else{var w=wb.exec(C),I=J.cur()||0;if(w){var L=parseFloat(w[2]),g=w[3]||"px";if(g!=="px"){c.style(x,A,(L||1)+g);I=(L||
+1)/J.cur()*I;c.style(x,A,I+g)}if(w[1])L=(w[1]==="-="?-1:1)*L+I;J.custom(I,L,g)}else J.custom(I,C,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);this.each(function(){for(var e=d.length-1;e>=0;e--)if(d[e].elem===this){b&&d[e](true);d.splice(e,1)}});b||this.dequeue();return this}});c.each({slideDown:S("show",1),slideUp:S("hide",1),slideToggle:S("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){c.fn[a]=function(d,e,f){return this.animate(b,
+d,e,f)}});c.extend({speed:function(a,b,d){var e=a&&typeof a==="object"?c.extend({},a):{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};e.duration=c.fx.off?0:typeof e.duration==="number"?e.duration:e.duration in c.fx.speeds?c.fx.speeds[e.duration]:c.fx.speeds._default;e.old=e.complete;e.complete=function(){e.queue!==false&&c(this).dequeue();c.isFunction(e.old)&&e.old.call(this)};return e},easing:{linear:function(a,b,d,e){return d+e*a},swing:function(a,b,d,e){return(-Math.cos(a*
+Math.PI)/2+0.5)*e+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||c.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a=parseFloat(c.css(this.elem,this.prop));return a&&a>-1E4?a:0},custom:function(a,b,d){function e(l){return f.step(l)}
+var f=this,h=c.fx;this.startTime=c.now();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;this.pos=this.state=0;e.elem=this.elem;if(e()&&c.timers.push(e)&&!ba)ba=setInterval(h.tick,h.interval)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;
+this.custom(this.cur(),0)},step:function(a){var b=c.now(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var e in this.options.curAnim)if(this.options.curAnim[e]!==true)d=false;if(d){if(this.options.overflow!=null&&!c.support.shrinkWrapBlocks){var f=this.elem,h=this.options;c.each(["","X","Y"],function(k,o){f.style["overflow"+o]=h.overflow[k]})}this.options.hide&&c(this.elem).hide();if(this.options.hide||
+this.options.show)for(var l in this.options.curAnim)c.style(this.elem,l,this.options.orig[l]);this.options.complete.call(this.elem)}return false}else{a=b-this.startTime;this.state=a/this.options.duration;b=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||b](this.state,a,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=
+c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||c.fx.stop()},interval:13,stop:function(){clearInterval(ba);ba=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===
+b.elem}).length};var xb=/^t(?:able|d|h)$/i,Ia=/^(?:body|html)$/i;c.fn.offset="getBoundingClientRect"in t.documentElement?function(a){var b=this[0],d;if(a)return this.each(function(l){c.offset.setOffset(this,a,l)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);try{d=b.getBoundingClientRect()}catch(e){}var f=b.ownerDocument,h=f.documentElement;if(!d||!c.contains(h,b))return d||{top:0,left:0};b=f.body;f=fa(f);return{top:d.top+(f.pageYOffset||c.support.boxModel&&
+h.scrollTop||b.scrollTop)-(h.clientTop||b.clientTop||0),left:d.left+(f.pageXOffset||c.support.boxModel&&h.scrollLeft||b.scrollLeft)-(h.clientLeft||b.clientLeft||0)}}:function(a){var b=this[0];if(a)return this.each(function(x){c.offset.setOffset(this,a,x)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d,e=b.offsetParent,f=b.ownerDocument,h=f.documentElement,l=f.body;d=(f=f.defaultView)?f.getComputedStyle(b,null):b.currentStyle;
+for(var k=b.offsetTop,o=b.offsetLeft;(b=b.parentNode)&&b!==l&&b!==h;){if(c.offset.supportsFixedPosition&&d.position==="fixed")break;d=f?f.getComputedStyle(b,null):b.currentStyle;k-=b.scrollTop;o-=b.scrollLeft;if(b===e){k+=b.offsetTop;o+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&xb.test(b.nodeName))){k+=parseFloat(d.borderTopWidth)||0;o+=parseFloat(d.borderLeftWidth)||0}e=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&d.overflow!=="visible"){k+=
+parseFloat(d.borderTopWidth)||0;o+=parseFloat(d.borderLeftWidth)||0}d=d}if(d.position==="relative"||d.position==="static"){k+=l.offsetTop;o+=l.offsetLeft}if(c.offset.supportsFixedPosition&&d.position==="fixed"){k+=Math.max(h.scrollTop,l.scrollTop);o+=Math.max(h.scrollLeft,l.scrollLeft)}return{top:k,left:o}};c.offset={initialize:function(){var a=t.body,b=t.createElement("div"),d,e,f,h=parseFloat(c.css(a,"marginTop"))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",
+height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";a.insertBefore(b,a.firstChild);d=b.firstChild;e=d.firstChild;f=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=e.offsetTop!==5;this.doesAddBorderForTableAndCells=
+f.offsetTop===5;e.style.position="fixed";e.style.top="20px";this.supportsFixedPosition=e.offsetTop===20||e.offsetTop===15;e.style.position=e.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=e.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==h;a.removeChild(b);c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.css(a,
+"marginTop"))||0;d+=parseFloat(c.css(a,"marginLeft"))||0}return{top:b,left:d}},setOffset:function(a,b,d){var e=c.css(a,"position");if(e==="static")a.style.position="relative";var f=c(a),h=f.offset(),l=c.css(a,"top"),k=c.css(a,"left"),o=e==="absolute"&&c.inArray("auto",[l,k])>-1;e={};var x={};if(o)x=f.position();l=o?x.top:parseInt(l,10)||0;k=o?x.left:parseInt(k,10)||0;if(c.isFunction(b))b=b.call(a,d,h);if(b.top!=null)e.top=b.top-h.top+l;if(b.left!=null)e.left=b.left-h.left+k;"using"in b?b.using.call(a,
+e):f.css(e)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),e=Ia.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.css(a,"marginTop"))||0;d.left-=parseFloat(c.css(a,"marginLeft"))||0;e.top+=parseFloat(c.css(b[0],"borderTopWidth"))||0;e.left+=parseFloat(c.css(b[0],"borderLeftWidth"))||0;return{top:d.top-e.top,left:d.left-e.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||t.body;a&&!Ia.test(a.nodeName)&&
+c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(e){var f=this[0],h;if(!f)return null;if(e!==B)return this.each(function(){if(h=fa(this))h.scrollTo(!a?e:c(h).scrollLeft(),a?e:c(h).scrollTop());else this[d]=e});else return(h=fa(f))?"pageXOffset"in h?h[a?"pageYOffset":"pageXOffset"]:c.support.boxModel&&h.document.documentElement[d]||h.document.body[d]:f[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();
+c.fn["inner"+b]=function(){return this[0]?parseFloat(c.css(this[0],d,"padding")):null};c.fn["outer"+b]=function(e){return this[0]?parseFloat(c.css(this[0],d,e?"margin":"border")):null};c.fn[d]=function(e){var f=this[0];if(!f)return e==null?null:this;if(c.isFunction(e))return this.each(function(l){var k=c(this);k[d](e.call(this,l,k[d]()))});if(c.isWindow(f))return f.document.compatMode==="CSS1Compat"&&f.document.documentElement["client"+b]||f.document.body["client"+b];else if(f.nodeType===9)return Math.max(f.documentElement["client"+
+b],f.body["scroll"+b],f.documentElement["scroll"+b],f.body["offset"+b],f.documentElement["offset"+b]);else if(e===B){f=c.css(f,d);var h=parseFloat(f);return c.isNaN(h)?f:h}else return this.css(d,typeof e==="string"?e:e+"px")}})})(window);
diff --git a/node_modules/selenium-webdriver/lib/test/data/js/jquery-ui-1.8.10.custom.min.js b/node_modules/selenium-webdriver/lib/test/data/js/jquery-ui-1.8.10.custom.min.js
new file mode 100644
index 000000000..7d4ff1cec
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/js/jquery-ui-1.8.10.custom.min.js
@@ -0,0 +1,782 @@
+/*!
+ * jQuery UI 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI
+ */
+(function(c,j){function k(a){return!c(a).parents().andSelf().filter(function(){return c.curCSS(this,"visibility")==="hidden"||c.expr.filters.hidden(this)}).length}c.ui=c.ui||{};if(!c.ui.version){c.extend(c.ui,{version:"1.8.10",keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,
+NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}});c.fn.extend({_focus:c.fn.focus,focus:function(a,b){return typeof a==="number"?this.each(function(){var d=this;setTimeout(function(){c(d).focus();b&&b.call(d)},a)}):this._focus.apply(this,arguments)},scrollParent:function(){var a;a=c.browser.msie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?this.parents().filter(function(){return/(relative|absolute|fixed)/.test(c.curCSS(this,
+"position",1))&&/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0):this.parents().filter(function(){return/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0);return/fixed/.test(this.css("position"))||!a.length?c(document):a},zIndex:function(a){if(a!==j)return this.css("zIndex",a);if(this.length){a=c(this[0]);for(var b;a.length&&a[0]!==document;){b=a.css("position");
+if(b==="absolute"||b==="relative"||b==="fixed"){b=parseInt(a.css("zIndex"),10);if(!isNaN(b)&&b!==0)return b}a=a.parent()}}return 0},disableSelection:function(){return this.bind((c.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection",function(a){a.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}});c.each(["Width","Height"],function(a,b){function d(f,g,l,m){c.each(e,function(){g-=parseFloat(c.curCSS(f,"padding"+this,true))||0;if(l)g-=parseFloat(c.curCSS(f,
+"border"+this+"Width",true))||0;if(m)g-=parseFloat(c.curCSS(f,"margin"+this,true))||0});return g}var e=b==="Width"?["Left","Right"]:["Top","Bottom"],h=b.toLowerCase(),i={innerWidth:c.fn.innerWidth,innerHeight:c.fn.innerHeight,outerWidth:c.fn.outerWidth,outerHeight:c.fn.outerHeight};c.fn["inner"+b]=function(f){if(f===j)return i["inner"+b].call(this);return this.each(function(){c(this).css(h,d(this,f)+"px")})};c.fn["outer"+b]=function(f,g){if(typeof f!=="number")return i["outer"+b].call(this,f);return this.each(function(){c(this).css(h,
+d(this,f,true,g)+"px")})}});c.extend(c.expr[":"],{data:function(a,b,d){return!!c.data(a,d[3])},focusable:function(a){var b=a.nodeName.toLowerCase(),d=c.attr(a,"tabindex");if("area"===b){b=a.parentNode;d=b.name;if(!a.href||!d||b.nodeName.toLowerCase()!=="map")return false;a=c("img[usemap=#"+d+"]")[0];return!!a&&k(a)}return(/input|select|textarea|button|object/.test(b)?!a.disabled:"a"==b?a.href||!isNaN(d):!isNaN(d))&&k(a)},tabbable:function(a){var b=c.attr(a,"tabindex");return(isNaN(b)||b>=0)&&c(a).is(":focusable")}});
+c(function(){var a=document.body,b=a.appendChild(b=document.createElement("div"));c.extend(b.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0});c.support.minHeight=b.offsetHeight===100;c.support.selectstart="onselectstart"in b;a.removeChild(b).style.display="none"});c.extend(c.ui,{plugin:{add:function(a,b,d){a=c.ui[a].prototype;for(var e in d){a.plugins[e]=a.plugins[e]||[];a.plugins[e].push([b,d[e]])}},call:function(a,b,d){if((b=a.plugins[b])&&a.element[0].parentNode)for(var e=0;e<b.length;e++)a.options[b[e][0]]&&
+b[e][1].apply(a.element,d)}},contains:function(a,b){return document.compareDocumentPosition?a.compareDocumentPosition(b)&16:a!==b&&a.contains(b)},hasScroll:function(a,b){if(c(a).css("overflow")==="hidden")return false;b=b&&b==="left"?"scrollLeft":"scrollTop";var d=false;if(a[b]>0)return true;a[b]=1;d=a[b]>0;a[b]=0;return d},isOverAxis:function(a,b,d){return a>b&&a<b+d},isOver:function(a,b,d,e,h,i){return c.ui.isOverAxis(a,d,h)&&c.ui.isOverAxis(b,e,i)}})}})(jQuery);
+;/*!
+ * jQuery UI Widget 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Widget
+ */
+(function(b,j){if(b.cleanData){var k=b.cleanData;b.cleanData=function(a){for(var c=0,d;(d=a[c])!=null;c++)b(d).triggerHandler("remove");k(a)}}else{var l=b.fn.remove;b.fn.remove=function(a,c){return this.each(function(){if(!c)if(!a||b.filter(a,[this]).length)b("*",this).add([this]).each(function(){b(this).triggerHandler("remove")});return l.call(b(this),a,c)})}}b.widget=function(a,c,d){var e=a.split(".")[0],f;a=a.split(".")[1];f=e+"-"+a;if(!d){d=c;c=b.Widget}b.expr[":"][f]=function(h){return!!b.data(h,
+a)};b[e]=b[e]||{};b[e][a]=function(h,g){arguments.length&&this._createWidget(h,g)};c=new c;c.options=b.extend(true,{},c.options);b[e][a].prototype=b.extend(true,c,{namespace:e,widgetName:a,widgetEventPrefix:b[e][a].prototype.widgetEventPrefix||a,widgetBaseClass:f},d);b.widget.bridge(a,b[e][a])};b.widget.bridge=function(a,c){b.fn[a]=function(d){var e=typeof d==="string",f=Array.prototype.slice.call(arguments,1),h=this;d=!e&&f.length?b.extend.apply(null,[true,d].concat(f)):d;if(e&&d.charAt(0)==="_")return h;
+e?this.each(function(){var g=b.data(this,a),i=g&&b.isFunction(g[d])?g[d].apply(g,f):g;if(i!==g&&i!==j){h=i;return false}}):this.each(function(){var g=b.data(this,a);g?g.option(d||{})._init():b.data(this,a,new c(d,this))});return h}};b.Widget=function(a,c){arguments.length&&this._createWidget(a,c)};b.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",options:{disabled:false},_createWidget:function(a,c){b.data(c,this.widgetName,this);this.element=b(c);this.options=b.extend(true,{},this.options,
+this._getCreateOptions(),a);var d=this;this.element.bind("remove."+this.widgetName,function(){d.destroy()});this._create();this._trigger("create");this._init()},_getCreateOptions:function(){return b.metadata&&b.metadata.get(this.element[0])[this.widgetName]},_create:function(){},_init:function(){},destroy:function(){this.element.unbind("."+this.widgetName).removeData(this.widgetName);this.widget().unbind("."+this.widgetName).removeAttr("aria-disabled").removeClass(this.widgetBaseClass+"-disabled ui-state-disabled")},
+widget:function(){return this.element},option:function(a,c){var d=a;if(arguments.length===0)return b.extend({},this.options);if(typeof a==="string"){if(c===j)return this.options[a];d={};d[a]=c}this._setOptions(d);return this},_setOptions:function(a){var c=this;b.each(a,function(d,e){c._setOption(d,e)});return this},_setOption:function(a,c){this.options[a]=c;if(a==="disabled")this.widget()[c?"addClass":"removeClass"](this.widgetBaseClass+"-disabled ui-state-disabled").attr("aria-disabled",c);return this},
+enable:function(){return this._setOption("disabled",false)},disable:function(){return this._setOption("disabled",true)},_trigger:function(a,c,d){var e=this.options[a];c=b.Event(c);c.type=(a===this.widgetEventPrefix?a:this.widgetEventPrefix+a).toLowerCase();d=d||{};if(c.originalEvent){a=b.event.props.length;for(var f;a;){f=b.event.props[--a];c[f]=c.originalEvent[f]}}this.element.trigger(c,d);return!(b.isFunction(e)&&e.call(this.element[0],c,d)===false||c.isDefaultPrevented())}}})(jQuery);
+;/*!
+ * jQuery UI Mouse 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Mouse
+ *
+ * Depends:
+ * jquery.ui.widget.js
+ */
+(function(c){c.widget("ui.mouse",{options:{cancel:":input,option",distance:1,delay:0},_mouseInit:function(){var a=this;this.element.bind("mousedown."+this.widgetName,function(b){return a._mouseDown(b)}).bind("click."+this.widgetName,function(b){if(true===c.data(b.target,a.widgetName+".preventClickEvent")){c.removeData(b.target,a.widgetName+".preventClickEvent");b.stopImmediatePropagation();return false}});this.started=false},_mouseDestroy:function(){this.element.unbind("."+this.widgetName)},_mouseDown:function(a){a.originalEvent=
+a.originalEvent||{};if(!a.originalEvent.mouseHandled){this._mouseStarted&&this._mouseUp(a);this._mouseDownEvent=a;var b=this,e=a.which==1,f=typeof this.options.cancel=="string"?c(a.target).parents().add(a.target).filter(this.options.cancel).length:false;if(!e||f||!this._mouseCapture(a))return true;this.mouseDelayMet=!this.options.delay;if(!this.mouseDelayMet)this._mouseDelayTimer=setTimeout(function(){b.mouseDelayMet=true},this.options.delay);if(this._mouseDistanceMet(a)&&this._mouseDelayMet(a)){this._mouseStarted=
+this._mouseStart(a)!==false;if(!this._mouseStarted){a.preventDefault();return true}}this._mouseMoveDelegate=function(d){return b._mouseMove(d)};this._mouseUpDelegate=function(d){return b._mouseUp(d)};c(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate);a.preventDefault();return a.originalEvent.mouseHandled=true}},_mouseMove:function(a){if(c.browser.msie&&!(document.documentMode>=9)&&!a.button)return this._mouseUp(a);if(this._mouseStarted){this._mouseDrag(a);
+return a.preventDefault()}if(this._mouseDistanceMet(a)&&this._mouseDelayMet(a))(this._mouseStarted=this._mouseStart(this._mouseDownEvent,a)!==false)?this._mouseDrag(a):this._mouseUp(a);return!this._mouseStarted},_mouseUp:function(a){c(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted=false;a.target==this._mouseDownEvent.target&&c.data(a.target,this.widgetName+".preventClickEvent",
+true);this._mouseStop(a)}return false},_mouseDistanceMet:function(a){return Math.max(Math.abs(this._mouseDownEvent.pageX-a.pageX),Math.abs(this._mouseDownEvent.pageY-a.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return true}})})(jQuery);
+;/*
+ * jQuery UI Position 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Position
+ */
+(function(c){c.ui=c.ui||{};var n=/left|center|right/,o=/top|center|bottom/,t=c.fn.position,u=c.fn.offset;c.fn.position=function(b){if(!b||!b.of)return t.apply(this,arguments);b=c.extend({},b);var a=c(b.of),d=a[0],g=(b.collision||"flip").split(" "),e=b.offset?b.offset.split(" "):[0,0],h,k,j;if(d.nodeType===9){h=a.width();k=a.height();j={top:0,left:0}}else if(d.setTimeout){h=a.width();k=a.height();j={top:a.scrollTop(),left:a.scrollLeft()}}else if(d.preventDefault){b.at="left top";h=k=0;j={top:b.of.pageY,
+left:b.of.pageX}}else{h=a.outerWidth();k=a.outerHeight();j=a.offset()}c.each(["my","at"],function(){var f=(b[this]||"").split(" ");if(f.length===1)f=n.test(f[0])?f.concat(["center"]):o.test(f[0])?["center"].concat(f):["center","center"];f[0]=n.test(f[0])?f[0]:"center";f[1]=o.test(f[1])?f[1]:"center";b[this]=f});if(g.length===1)g[1]=g[0];e[0]=parseInt(e[0],10)||0;if(e.length===1)e[1]=e[0];e[1]=parseInt(e[1],10)||0;if(b.at[0]==="right")j.left+=h;else if(b.at[0]==="center")j.left+=h/2;if(b.at[1]==="bottom")j.top+=
+k;else if(b.at[1]==="center")j.top+=k/2;j.left+=e[0];j.top+=e[1];return this.each(function(){var f=c(this),l=f.outerWidth(),m=f.outerHeight(),p=parseInt(c.curCSS(this,"marginLeft",true))||0,q=parseInt(c.curCSS(this,"marginTop",true))||0,v=l+p+(parseInt(c.curCSS(this,"marginRight",true))||0),w=m+q+(parseInt(c.curCSS(this,"marginBottom",true))||0),i=c.extend({},j),r;if(b.my[0]==="right")i.left-=l;else if(b.my[0]==="center")i.left-=l/2;if(b.my[1]==="bottom")i.top-=m;else if(b.my[1]==="center")i.top-=
+m/2;i.left=Math.round(i.left);i.top=Math.round(i.top);r={left:i.left-p,top:i.top-q};c.each(["left","top"],function(s,x){c.ui.position[g[s]]&&c.ui.position[g[s]][x](i,{targetWidth:h,targetHeight:k,elemWidth:l,elemHeight:m,collisionPosition:r,collisionWidth:v,collisionHeight:w,offset:e,my:b.my,at:b.at})});c.fn.bgiframe&&f.bgiframe();f.offset(c.extend(i,{using:b.using}))})};c.ui.position={fit:{left:function(b,a){var d=c(window);d=a.collisionPosition.left+a.collisionWidth-d.width()-d.scrollLeft();b.left=
+d>0?b.left-d:Math.max(b.left-a.collisionPosition.left,b.left)},top:function(b,a){var d=c(window);d=a.collisionPosition.top+a.collisionHeight-d.height()-d.scrollTop();b.top=d>0?b.top-d:Math.max(b.top-a.collisionPosition.top,b.top)}},flip:{left:function(b,a){if(a.at[0]!=="center"){var d=c(window);d=a.collisionPosition.left+a.collisionWidth-d.width()-d.scrollLeft();var g=a.my[0]==="left"?-a.elemWidth:a.my[0]==="right"?a.elemWidth:0,e=a.at[0]==="left"?a.targetWidth:-a.targetWidth,h=-2*a.offset[0];b.left+=
+a.collisionPosition.left<0?g+e+h:d>0?g+e+h:0}},top:function(b,a){if(a.at[1]!=="center"){var d=c(window);d=a.collisionPosition.top+a.collisionHeight-d.height()-d.scrollTop();var g=a.my[1]==="top"?-a.elemHeight:a.my[1]==="bottom"?a.elemHeight:0,e=a.at[1]==="top"?a.targetHeight:-a.targetHeight,h=-2*a.offset[1];b.top+=a.collisionPosition.top<0?g+e+h:d>0?g+e+h:0}}}};if(!c.offset.setOffset){c.offset.setOffset=function(b,a){if(/static/.test(c.curCSS(b,"position")))b.style.position="relative";var d=c(b),
+g=d.offset(),e=parseInt(c.curCSS(b,"top",true),10)||0,h=parseInt(c.curCSS(b,"left",true),10)||0;g={top:a.top-g.top+e,left:a.left-g.left+h};"using"in a?a.using.call(b,g):d.css(g)};c.fn.offset=function(b){var a=this[0];if(!a||!a.ownerDocument)return null;if(b)return this.each(function(){c.offset.setOffset(this,b)});return u.call(this)}}})(jQuery);
+;/*
+ * jQuery UI Draggable 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Draggables
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.mouse.js
+ * jquery.ui.widget.js
+ */
+(function(d){d.widget("ui.draggable",d.ui.mouse,{widgetEventPrefix:"drag",options:{addClasses:true,appendTo:"parent",axis:false,connectToSortable:false,containment:false,cursor:"auto",cursorAt:false,grid:false,handle:false,helper:"original",iframeFix:false,opacity:false,refreshPositions:false,revert:false,revertDuration:500,scope:"default",scroll:true,scrollSensitivity:20,scrollSpeed:20,snap:false,snapMode:"both",snapTolerance:20,stack:false,zIndex:false},_create:function(){if(this.options.helper==
+"original"&&!/^(?:r|a|f)/.test(this.element.css("position")))this.element[0].style.position="relative";this.options.addClasses&&this.element.addClass("ui-draggable");this.options.disabled&&this.element.addClass("ui-draggable-disabled");this._mouseInit()},destroy:function(){if(this.element.data("draggable")){this.element.removeData("draggable").unbind(".draggable").removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled");this._mouseDestroy();return this}},_mouseCapture:function(a){var b=
+this.options;if(this.helper||b.disabled||d(a.target).is(".ui-resizable-handle"))return false;this.handle=this._getHandle(a);if(!this.handle)return false;return true},_mouseStart:function(a){var b=this.options;this.helper=this._createHelper(a);this._cacheHelperProportions();if(d.ui.ddmanager)d.ui.ddmanager.current=this;this._cacheMargins();this.cssPosition=this.helper.css("position");this.scrollParent=this.helper.scrollParent();this.offset=this.positionAbs=this.element.offset();this.offset={top:this.offset.top-
+this.margins.top,left:this.offset.left-this.margins.left};d.extend(this.offset,{click:{left:a.pageX-this.offset.left,top:a.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()});this.originalPosition=this.position=this._generatePosition(a);this.originalPageX=a.pageX;this.originalPageY=a.pageY;b.cursorAt&&this._adjustOffsetFromHelper(b.cursorAt);b.containment&&this._setContainment();if(this._trigger("start",a)===false){this._clear();return false}this._cacheHelperProportions();
+d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a);this.helper.addClass("ui-draggable-dragging");this._mouseDrag(a,true);return true},_mouseDrag:function(a,b){this.position=this._generatePosition(a);this.positionAbs=this._convertPositionTo("absolute");if(!b){b=this._uiHash();if(this._trigger("drag",a,b)===false){this._mouseUp({});return false}this.position=b.position}if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis||
+this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";d.ui.ddmanager&&d.ui.ddmanager.drag(this,a);return false},_mouseStop:function(a){var b=false;if(d.ui.ddmanager&&!this.options.dropBehaviour)b=d.ui.ddmanager.drop(this,a);if(this.dropped){b=this.dropped;this.dropped=false}if((!this.element[0]||!this.element[0].parentNode)&&this.options.helper=="original")return false;if(this.options.revert=="invalid"&&!b||this.options.revert=="valid"&&b||this.options.revert===true||d.isFunction(this.options.revert)&&
+this.options.revert.call(this.element,b)){var c=this;d(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){c._trigger("stop",a)!==false&&c._clear()})}else this._trigger("stop",a)!==false&&this._clear();return false},cancel:function(){this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear();return this},_getHandle:function(a){var b=!this.options.handle||!d(this.options.handle,this.element).length?true:false;d(this.options.handle,this.element).find("*").andSelf().each(function(){if(this==
+a.target)b=true});return b},_createHelper:function(a){var b=this.options;a=d.isFunction(b.helper)?d(b.helper.apply(this.element[0],[a])):b.helper=="clone"?this.element.clone():this.element;a.parents("body").length||a.appendTo(b.appendTo=="parent"?this.element[0].parentNode:b.appendTo);a[0]!=this.element[0]&&!/(fixed|absolute)/.test(a.css("position"))&&a.css("position","absolute");return a},_adjustOffsetFromHelper:function(a){if(typeof a=="string")a=a.split(" ");if(d.isArray(a))a={left:+a[0],top:+a[1]||
+0};if("left"in a)this.offset.click.left=a.left+this.margins.left;if("right"in a)this.offset.click.left=this.helperProportions.width-a.right+this.margins.left;if("top"in a)this.offset.click.top=a.top+this.margins.top;if("bottom"in a)this.offset.click.top=this.helperProportions.height-a.bottom+this.margins.top},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var a=this.offsetParent.offset();if(this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],
+this.offsetParent[0])){a.left+=this.scrollParent.scrollLeft();a.top+=this.scrollParent.scrollTop()}if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&d.browser.msie)a={top:0,left:0};return{top:a.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:a.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var a=this.element.position();return{top:a.top-
+(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}else return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var a=this.options;if(a.containment==
+"parent")a.containment=this.helper[0].parentNode;if(a.containment=="document"||a.containment=="window")this.containment=[(a.containment=="document"?0:d(window).scrollLeft())-this.offset.relative.left-this.offset.parent.left,(a.containment=="document"?0:d(window).scrollTop())-this.offset.relative.top-this.offset.parent.top,(a.containment=="document"?0:d(window).scrollLeft())+d(a.containment=="document"?document:window).width()-this.helperProportions.width-this.margins.left,(a.containment=="document"?
+0:d(window).scrollTop())+(d(a.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(a.containment)&&a.containment.constructor!=Array){var b=d(a.containment)[0];if(b){a=d(a.containment).offset();var c=d(b).css("overflow")!="hidden";this.containment=[a.left+(parseInt(d(b).css("borderLeftWidth"),10)||0)+(parseInt(d(b).css("paddingLeft"),10)||0)-this.margins.left,a.top+(parseInt(d(b).css("borderTopWidth"),
+10)||0)+(parseInt(d(b).css("paddingTop"),10)||0)-this.margins.top,a.left+(c?Math.max(b.scrollWidth,b.offsetWidth):b.offsetWidth)-(parseInt(d(b).css("borderLeftWidth"),10)||0)-(parseInt(d(b).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,a.top+(c?Math.max(b.scrollHeight,b.offsetHeight):b.offsetHeight)-(parseInt(d(b).css("borderTopWidth"),10)||0)-(parseInt(d(b).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top]}}else if(a.containment.constructor==
+Array)this.containment=a.containment},_convertPositionTo:function(a,b){if(!b)b=this.position;a=a=="absolute"?1:-1;var c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName);return{top:b.top+this.offset.relative.top*a+this.offset.parent.top*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():
+f?0:c.scrollTop())*a),left:b.left+this.offset.relative.left*a+this.offset.parent.left*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():f?0:c.scrollLeft())*a)}},_generatePosition:function(a){var b=this.options,c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName),e=a.pageX,g=a.pageY;
+if(this.originalPosition){if(this.containment){if(a.pageX-this.offset.click.left<this.containment[0])e=this.containment[0]+this.offset.click.left;if(a.pageY-this.offset.click.top<this.containment[1])g=this.containment[1]+this.offset.click.top;if(a.pageX-this.offset.click.left>this.containment[2])e=this.containment[2]+this.offset.click.left;if(a.pageY-this.offset.click.top>this.containment[3])g=this.containment[3]+this.offset.click.top}if(b.grid){g=this.originalPageY+Math.round((g-this.originalPageY)/
+b.grid[1])*b.grid[1];g=this.containment?!(g-this.offset.click.top<this.containment[1]||g-this.offset.click.top>this.containment[3])?g:!(g-this.offset.click.top<this.containment[1])?g-b.grid[1]:g+b.grid[1]:g;e=this.originalPageX+Math.round((e-this.originalPageX)/b.grid[0])*b.grid[0];e=this.containment?!(e-this.offset.click.left<this.containment[0]||e-this.offset.click.left>this.containment[2])?e:!(e-this.offset.click.left<this.containment[0])?e-b.grid[0]:e+b.grid[0]:e}}return{top:g-this.offset.click.top-
+this.offset.relative.top-this.offset.parent.top+(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollTop():f?0:c.scrollTop()),left:e-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():f?0:c.scrollLeft())}},_clear:function(){this.helper.removeClass("ui-draggable-dragging");this.helper[0]!=
+this.element[0]&&!this.cancelHelperRemoval&&this.helper.remove();this.helper=null;this.cancelHelperRemoval=false},_trigger:function(a,b,c){c=c||this._uiHash();d.ui.plugin.call(this,a,[b,c]);if(a=="drag")this.positionAbs=this._convertPositionTo("absolute");return d.Widget.prototype._trigger.call(this,a,b,c)},plugins:{},_uiHash:function(){return{helper:this.helper,position:this.position,originalPosition:this.originalPosition,offset:this.positionAbs}}});d.extend(d.ui.draggable,{version:"1.8.10"});
+d.ui.plugin.add("draggable","connectToSortable",{start:function(a,b){var c=d(this).data("draggable"),f=c.options,e=d.extend({},b,{item:c.element});c.sortables=[];d(f.connectToSortable).each(function(){var g=d.data(this,"sortable");if(g&&!g.options.disabled){c.sortables.push({instance:g,shouldRevert:g.options.revert});g._refreshItems();g._trigger("activate",a,e)}})},stop:function(a,b){var c=d(this).data("draggable"),f=d.extend({},b,{item:c.element});d.each(c.sortables,function(){if(this.instance.isOver){this.instance.isOver=
+0;c.cancelHelperRemoval=true;this.instance.cancelHelperRemoval=false;if(this.shouldRevert)this.instance.options.revert=true;this.instance._mouseStop(a);this.instance.options.helper=this.instance.options._helper;c.options.helper=="original"&&this.instance.currentItem.css({top:"auto",left:"auto"})}else{this.instance.cancelHelperRemoval=false;this.instance._trigger("deactivate",a,f)}})},drag:function(a,b){var c=d(this).data("draggable"),f=this;d.each(c.sortables,function(){this.instance.positionAbs=
+c.positionAbs;this.instance.helperProportions=c.helperProportions;this.instance.offset.click=c.offset.click;if(this.instance._intersectsWith(this.instance.containerCache)){if(!this.instance.isOver){this.instance.isOver=1;this.instance.currentItem=d(f).clone().appendTo(this.instance.element).data("sortable-item",true);this.instance.options._helper=this.instance.options.helper;this.instance.options.helper=function(){return b.helper[0]};a.target=this.instance.currentItem[0];this.instance._mouseCapture(a,
+true);this.instance._mouseStart(a,true,true);this.instance.offset.click.top=c.offset.click.top;this.instance.offset.click.left=c.offset.click.left;this.instance.offset.parent.left-=c.offset.parent.left-this.instance.offset.parent.left;this.instance.offset.parent.top-=c.offset.parent.top-this.instance.offset.parent.top;c._trigger("toSortable",a);c.dropped=this.instance.element;c.currentItem=c.element;this.instance.fromOutside=c}this.instance.currentItem&&this.instance._mouseDrag(a)}else if(this.instance.isOver){this.instance.isOver=
+0;this.instance.cancelHelperRemoval=true;this.instance.options.revert=false;this.instance._trigger("out",a,this.instance._uiHash(this.instance));this.instance._mouseStop(a,true);this.instance.options.helper=this.instance.options._helper;this.instance.currentItem.remove();this.instance.placeholder&&this.instance.placeholder.remove();c._trigger("fromSortable",a);c.dropped=false}})}});d.ui.plugin.add("draggable","cursor",{start:function(){var a=d("body"),b=d(this).data("draggable").options;if(a.css("cursor"))b._cursor=
+a.css("cursor");a.css("cursor",b.cursor)},stop:function(){var a=d(this).data("draggable").options;a._cursor&&d("body").css("cursor",a._cursor)}});d.ui.plugin.add("draggable","iframeFix",{start:function(){var a=d(this).data("draggable").options;d(a.iframeFix===true?"iframe":a.iframeFix).each(function(){d('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>').css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1E3}).css(d(this).offset()).appendTo("body")})},
+stop:function(){d("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)})}});d.ui.plugin.add("draggable","opacity",{start:function(a,b){a=d(b.helper);b=d(this).data("draggable").options;if(a.css("opacity"))b._opacity=a.css("opacity");a.css("opacity",b.opacity)},stop:function(a,b){a=d(this).data("draggable").options;a._opacity&&d(b.helper).css("opacity",a._opacity)}});d.ui.plugin.add("draggable","scroll",{start:function(){var a=d(this).data("draggable");if(a.scrollParent[0]!=
+document&&a.scrollParent[0].tagName!="HTML")a.overflowOffset=a.scrollParent.offset()},drag:function(a){var b=d(this).data("draggable"),c=b.options,f=false;if(b.scrollParent[0]!=document&&b.scrollParent[0].tagName!="HTML"){if(!c.axis||c.axis!="x")if(b.overflowOffset.top+b.scrollParent[0].offsetHeight-a.pageY<c.scrollSensitivity)b.scrollParent[0].scrollTop=f=b.scrollParent[0].scrollTop+c.scrollSpeed;else if(a.pageY-b.overflowOffset.top<c.scrollSensitivity)b.scrollParent[0].scrollTop=f=b.scrollParent[0].scrollTop-
+c.scrollSpeed;if(!c.axis||c.axis!="y")if(b.overflowOffset.left+b.scrollParent[0].offsetWidth-a.pageX<c.scrollSensitivity)b.scrollParent[0].scrollLeft=f=b.scrollParent[0].scrollLeft+c.scrollSpeed;else if(a.pageX-b.overflowOffset.left<c.scrollSensitivity)b.scrollParent[0].scrollLeft=f=b.scrollParent[0].scrollLeft-c.scrollSpeed}else{if(!c.axis||c.axis!="x")if(a.pageY-d(document).scrollTop()<c.scrollSensitivity)f=d(document).scrollTop(d(document).scrollTop()-c.scrollSpeed);else if(d(window).height()-
+(a.pageY-d(document).scrollTop())<c.scrollSensitivity)f=d(document).scrollTop(d(document).scrollTop()+c.scrollSpeed);if(!c.axis||c.axis!="y")if(a.pageX-d(document).scrollLeft()<c.scrollSensitivity)f=d(document).scrollLeft(d(document).scrollLeft()-c.scrollSpeed);else if(d(window).width()-(a.pageX-d(document).scrollLeft())<c.scrollSensitivity)f=d(document).scrollLeft(d(document).scrollLeft()+c.scrollSpeed)}f!==false&&d.ui.ddmanager&&!c.dropBehaviour&&d.ui.ddmanager.prepareOffsets(b,a)}});d.ui.plugin.add("draggable",
+"snap",{start:function(){var a=d(this).data("draggable"),b=a.options;a.snapElements=[];d(b.snap.constructor!=String?b.snap.items||":data(draggable)":b.snap).each(function(){var c=d(this),f=c.offset();this!=a.element[0]&&a.snapElements.push({item:this,width:c.outerWidth(),height:c.outerHeight(),top:f.top,left:f.left})})},drag:function(a,b){for(var c=d(this).data("draggable"),f=c.options,e=f.snapTolerance,g=b.offset.left,n=g+c.helperProportions.width,m=b.offset.top,o=m+c.helperProportions.height,h=
+c.snapElements.length-1;h>=0;h--){var i=c.snapElements[h].left,k=i+c.snapElements[h].width,j=c.snapElements[h].top,l=j+c.snapElements[h].height;if(i-e<g&&g<k+e&&j-e<m&&m<l+e||i-e<g&&g<k+e&&j-e<o&&o<l+e||i-e<n&&n<k+e&&j-e<m&&m<l+e||i-e<n&&n<k+e&&j-e<o&&o<l+e){if(f.snapMode!="inner"){var p=Math.abs(j-o)<=e,q=Math.abs(l-m)<=e,r=Math.abs(i-n)<=e,s=Math.abs(k-g)<=e;if(p)b.position.top=c._convertPositionTo("relative",{top:j-c.helperProportions.height,left:0}).top-c.margins.top;if(q)b.position.top=c._convertPositionTo("relative",
+{top:l,left:0}).top-c.margins.top;if(r)b.position.left=c._convertPositionTo("relative",{top:0,left:i-c.helperProportions.width}).left-c.margins.left;if(s)b.position.left=c._convertPositionTo("relative",{top:0,left:k}).left-c.margins.left}var t=p||q||r||s;if(f.snapMode!="outer"){p=Math.abs(j-m)<=e;q=Math.abs(l-o)<=e;r=Math.abs(i-g)<=e;s=Math.abs(k-n)<=e;if(p)b.position.top=c._convertPositionTo("relative",{top:j,left:0}).top-c.margins.top;if(q)b.position.top=c._convertPositionTo("relative",{top:l-c.helperProportions.height,
+left:0}).top-c.margins.top;if(r)b.position.left=c._convertPositionTo("relative",{top:0,left:i}).left-c.margins.left;if(s)b.position.left=c._convertPositionTo("relative",{top:0,left:k-c.helperProportions.width}).left-c.margins.left}if(!c.snapElements[h].snapping&&(p||q||r||s||t))c.options.snap.snap&&c.options.snap.snap.call(c.element,a,d.extend(c._uiHash(),{snapItem:c.snapElements[h].item}));c.snapElements[h].snapping=p||q||r||s||t}else{c.snapElements[h].snapping&&c.options.snap.release&&c.options.snap.release.call(c.element,
+a,d.extend(c._uiHash(),{snapItem:c.snapElements[h].item}));c.snapElements[h].snapping=false}}}});d.ui.plugin.add("draggable","stack",{start:function(){var a=d(this).data("draggable").options;a=d.makeArray(d(a.stack)).sort(function(c,f){return(parseInt(d(c).css("zIndex"),10)||0)-(parseInt(d(f).css("zIndex"),10)||0)});if(a.length){var b=parseInt(a[0].style.zIndex)||0;d(a).each(function(c){this.style.zIndex=b+c});this[0].style.zIndex=b+a.length}}});d.ui.plugin.add("draggable","zIndex",{start:function(a,
+b){a=d(b.helper);b=d(this).data("draggable").options;if(a.css("zIndex"))b._zIndex=a.css("zIndex");a.css("zIndex",b.zIndex)},stop:function(a,b){a=d(this).data("draggable").options;a._zIndex&&d(b.helper).css("zIndex",a._zIndex)}})})(jQuery);
+;/*
+ * jQuery UI Droppable 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Droppables
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ * jquery.ui.mouse.js
+ * jquery.ui.draggable.js
+ */
+(function(d){d.widget("ui.droppable",{widgetEventPrefix:"drop",options:{accept:"*",activeClass:false,addClasses:true,greedy:false,hoverClass:false,scope:"default",tolerance:"intersect"},_create:function(){var a=this.options,b=a.accept;this.isover=0;this.isout=1;this.accept=d.isFunction(b)?b:function(c){return c.is(b)};this.proportions={width:this.element[0].offsetWidth,height:this.element[0].offsetHeight};d.ui.ddmanager.droppables[a.scope]=d.ui.ddmanager.droppables[a.scope]||[];d.ui.ddmanager.droppables[a.scope].push(this);
+a.addClasses&&this.element.addClass("ui-droppable")},destroy:function(){for(var a=d.ui.ddmanager.droppables[this.options.scope],b=0;b<a.length;b++)a[b]==this&&a.splice(b,1);this.element.removeClass("ui-droppable ui-droppable-disabled").removeData("droppable").unbind(".droppable");return this},_setOption:function(a,b){if(a=="accept")this.accept=d.isFunction(b)?b:function(c){return c.is(b)};d.Widget.prototype._setOption.apply(this,arguments)},_activate:function(a){var b=d.ui.ddmanager.current;this.options.activeClass&&
+this.element.addClass(this.options.activeClass);b&&this._trigger("activate",a,this.ui(b))},_deactivate:function(a){var b=d.ui.ddmanager.current;this.options.activeClass&&this.element.removeClass(this.options.activeClass);b&&this._trigger("deactivate",a,this.ui(b))},_over:function(a){var b=d.ui.ddmanager.current;if(!(!b||(b.currentItem||b.element)[0]==this.element[0]))if(this.accept.call(this.element[0],b.currentItem||b.element)){this.options.hoverClass&&this.element.addClass(this.options.hoverClass);
+this._trigger("over",a,this.ui(b))}},_out:function(a){var b=d.ui.ddmanager.current;if(!(!b||(b.currentItem||b.element)[0]==this.element[0]))if(this.accept.call(this.element[0],b.currentItem||b.element)){this.options.hoverClass&&this.element.removeClass(this.options.hoverClass);this._trigger("out",a,this.ui(b))}},_drop:function(a,b){var c=b||d.ui.ddmanager.current;if(!c||(c.currentItem||c.element)[0]==this.element[0])return false;var e=false;this.element.find(":data(droppable)").not(".ui-draggable-dragging").each(function(){var g=
+d.data(this,"droppable");if(g.options.greedy&&!g.options.disabled&&g.options.scope==c.options.scope&&g.accept.call(g.element[0],c.currentItem||c.element)&&d.ui.intersect(c,d.extend(g,{offset:g.element.offset()}),g.options.tolerance)){e=true;return false}});if(e)return false;if(this.accept.call(this.element[0],c.currentItem||c.element)){this.options.activeClass&&this.element.removeClass(this.options.activeClass);this.options.hoverClass&&this.element.removeClass(this.options.hoverClass);this._trigger("drop",
+a,this.ui(c));return this.element}return false},ui:function(a){return{draggable:a.currentItem||a.element,helper:a.helper,position:a.position,offset:a.positionAbs}}});d.extend(d.ui.droppable,{version:"1.8.10"});d.ui.intersect=function(a,b,c){if(!b.offset)return false;var e=(a.positionAbs||a.position.absolute).left,g=e+a.helperProportions.width,f=(a.positionAbs||a.position.absolute).top,h=f+a.helperProportions.height,i=b.offset.left,k=i+b.proportions.width,j=b.offset.top,l=j+b.proportions.height;
+switch(c){case "fit":return i<=e&&g<=k&&j<=f&&h<=l;case "intersect":return i<e+a.helperProportions.width/2&&g-a.helperProportions.width/2<k&&j<f+a.helperProportions.height/2&&h-a.helperProportions.height/2<l;case "pointer":return d.ui.isOver((a.positionAbs||a.position.absolute).top+(a.clickOffset||a.offset.click).top,(a.positionAbs||a.position.absolute).left+(a.clickOffset||a.offset.click).left,j,i,b.proportions.height,b.proportions.width);case "touch":return(f>=j&&f<=l||h>=j&&h<=l||f<j&&h>l)&&(e>=
+i&&e<=k||g>=i&&g<=k||e<i&&g>k);default:return false}};d.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(a,b){var c=d.ui.ddmanager.droppables[a.options.scope]||[],e=b?b.type:null,g=(a.currentItem||a.element).find(":data(droppable)").andSelf(),f=0;a:for(;f<c.length;f++)if(!(c[f].options.disabled||a&&!c[f].accept.call(c[f].element[0],a.currentItem||a.element))){for(var h=0;h<g.length;h++)if(g[h]==c[f].element[0]){c[f].proportions.height=0;continue a}c[f].visible=c[f].element.css("display")!=
+"none";if(c[f].visible){c[f].offset=c[f].element.offset();c[f].proportions={width:c[f].element[0].offsetWidth,height:c[f].element[0].offsetHeight};e=="mousedown"&&c[f]._activate.call(c[f],b)}}},drop:function(a,b){var c=false;d.each(d.ui.ddmanager.droppables[a.options.scope]||[],function(){if(this.options){if(!this.options.disabled&&this.visible&&d.ui.intersect(a,this,this.options.tolerance))c=c||this._drop.call(this,b);if(!this.options.disabled&&this.visible&&this.accept.call(this.element[0],a.currentItem||
+a.element)){this.isout=1;this.isover=0;this._deactivate.call(this,b)}}});return c},drag:function(a,b){a.options.refreshPositions&&d.ui.ddmanager.prepareOffsets(a,b);d.each(d.ui.ddmanager.droppables[a.options.scope]||[],function(){if(!(this.options.disabled||this.greedyChild||!this.visible)){var c=d.ui.intersect(a,this,this.options.tolerance);if(c=!c&&this.isover==1?"isout":c&&this.isover==0?"isover":null){var e;if(this.options.greedy){var g=this.element.parents(":data(droppable):eq(0)");if(g.length){e=
+d.data(g[0],"droppable");e.greedyChild=c=="isover"?1:0}}if(e&&c=="isover"){e.isover=0;e.isout=1;e._out.call(e,b)}this[c]=1;this[c=="isout"?"isover":"isout"]=0;this[c=="isover"?"_over":"_out"].call(this,b);if(e&&c=="isout"){e.isout=0;e.isover=1;e._over.call(e,b)}}}})}}})(jQuery);
+;/*
+ * jQuery UI Resizable 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Resizables
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.mouse.js
+ * jquery.ui.widget.js
+ */
+(function(e){e.widget("ui.resizable",e.ui.mouse,{widgetEventPrefix:"resize",options:{alsoResize:false,animate:false,animateDuration:"slow",animateEasing:"swing",aspectRatio:false,autoHide:false,containment:false,ghost:false,grid:false,handles:"e,s,se",helper:false,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:1E3},_create:function(){var b=this,a=this.options;this.element.addClass("ui-resizable");e.extend(this,{_aspectRatio:!!a.aspectRatio,aspectRatio:a.aspectRatio,originalElement:this.element,
+_proportionallyResizeElements:[],_helper:a.helper||a.ghost||a.animate?a.helper||"ui-resizable-helper":null});if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)){/relative/.test(this.element.css("position"))&&e.browser.opera&&this.element.css({position:"relative",top:"auto",left:"auto"});this.element.wrap(e('<div class="ui-wrapper" style="overflow: hidden;"></div>').css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),
+top:this.element.css("top"),left:this.element.css("left")}));this.element=this.element.parent().data("resizable",this.element.data("resizable"));this.elementIsWrapper=true;this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")});this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0});this.originalResizeStyle=
+this.originalElement.css("resize");this.originalElement.css("resize","none");this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"}));this.originalElement.css({margin:this.originalElement.css("margin")});this._proportionallyResize()}this.handles=a.handles||(!e(".ui-resizable-handle",this.element).length?"e,s,se":{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",
+nw:".ui-resizable-nw"});if(this.handles.constructor==String){if(this.handles=="all")this.handles="n,e,s,w,se,sw,ne,nw";var c=this.handles.split(",");this.handles={};for(var d=0;d<c.length;d++){var f=e.trim(c[d]),g=e('<div class="ui-resizable-handle '+("ui-resizable-"+f)+'"></div>');/sw|se|ne|nw/.test(f)&&g.css({zIndex:++a.zIndex});"se"==f&&g.addClass("ui-icon ui-icon-gripsmall-diagonal-se");this.handles[f]=".ui-resizable-"+f;this.element.append(g)}}this._renderAxis=function(h){h=h||this.element;for(var i in this.handles){if(this.handles[i].constructor==
+String)this.handles[i]=e(this.handles[i],this.element).show();if(this.elementIsWrapper&&this.originalElement[0].nodeName.match(/textarea|input|select|button/i)){var j=e(this.handles[i],this.element),k=0;k=/sw|ne|nw|se|n|s/.test(i)?j.outerHeight():j.outerWidth();j=["padding",/ne|nw|n/.test(i)?"Top":/se|sw|s/.test(i)?"Bottom":/^e$/.test(i)?"Right":"Left"].join("");h.css(j,k);this._proportionallyResize()}e(this.handles[i])}};this._renderAxis(this.element);this._handles=e(".ui-resizable-handle",this.element).disableSelection();
+this._handles.mouseover(function(){if(!b.resizing){if(this.className)var h=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);b.axis=h&&h[1]?h[1]:"se"}});if(a.autoHide){this._handles.hide();e(this.element).addClass("ui-resizable-autohide").hover(function(){e(this).removeClass("ui-resizable-autohide");b._handles.show()},function(){if(!b.resizing){e(this).addClass("ui-resizable-autohide");b._handles.hide()}})}this._mouseInit()},destroy:function(){this._mouseDestroy();var b=function(c){e(c).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").unbind(".resizable").find(".ui-resizable-handle").remove()};
+if(this.elementIsWrapper){b(this.element);var a=this.element;a.after(this.originalElement.css({position:a.css("position"),width:a.outerWidth(),height:a.outerHeight(),top:a.css("top"),left:a.css("left")})).remove()}this.originalElement.css("resize",this.originalResizeStyle);b(this.originalElement);return this},_mouseCapture:function(b){var a=false;for(var c in this.handles)if(e(this.handles[c])[0]==b.target)a=true;return!this.options.disabled&&a},_mouseStart:function(b){var a=this.options,c=this.element.position(),
+d=this.element;this.resizing=true;this.documentScroll={top:e(document).scrollTop(),left:e(document).scrollLeft()};if(d.is(".ui-draggable")||/absolute/.test(d.css("position")))d.css({position:"absolute",top:c.top,left:c.left});e.browser.opera&&/relative/.test(d.css("position"))&&d.css({position:"relative",top:"auto",left:"auto"});this._renderProxy();c=m(this.helper.css("left"));var f=m(this.helper.css("top"));if(a.containment){c+=e(a.containment).scrollLeft()||0;f+=e(a.containment).scrollTop()||0}this.offset=
+this.helper.offset();this.position={left:c,top:f};this.size=this._helper?{width:d.outerWidth(),height:d.outerHeight()}:{width:d.width(),height:d.height()};this.originalSize=this._helper?{width:d.outerWidth(),height:d.outerHeight()}:{width:d.width(),height:d.height()};this.originalPosition={left:c,top:f};this.sizeDiff={width:d.outerWidth()-d.width(),height:d.outerHeight()-d.height()};this.originalMousePosition={left:b.pageX,top:b.pageY};this.aspectRatio=typeof a.aspectRatio=="number"?a.aspectRatio:
+this.originalSize.width/this.originalSize.height||1;a=e(".ui-resizable-"+this.axis).css("cursor");e("body").css("cursor",a=="auto"?this.axis+"-resize":a);d.addClass("ui-resizable-resizing");this._propagate("start",b);return true},_mouseDrag:function(b){var a=this.helper,c=this.originalMousePosition,d=this._change[this.axis];if(!d)return false;c=d.apply(this,[b,b.pageX-c.left||0,b.pageY-c.top||0]);if(this._aspectRatio||b.shiftKey)c=this._updateRatio(c,b);c=this._respectSize(c,b);this._propagate("resize",
+b);a.css({top:this.position.top+"px",left:this.position.left+"px",width:this.size.width+"px",height:this.size.height+"px"});!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize();this._updateCache(c);this._trigger("resize",b,this.ui());return false},_mouseStop:function(b){this.resizing=false;var a=this.options,c=this;if(this._helper){var d=this._proportionallyResizeElements,f=d.length&&/textarea/i.test(d[0].nodeName);d=f&&e.ui.hasScroll(d[0],"left")?0:c.sizeDiff.height;
+f=f?0:c.sizeDiff.width;f={width:c.helper.width()-f,height:c.helper.height()-d};d=parseInt(c.element.css("left"),10)+(c.position.left-c.originalPosition.left)||null;var g=parseInt(c.element.css("top"),10)+(c.position.top-c.originalPosition.top)||null;a.animate||this.element.css(e.extend(f,{top:g,left:d}));c.helper.height(c.size.height);c.helper.width(c.size.width);this._helper&&!a.animate&&this._proportionallyResize()}e("body").css("cursor","auto");this.element.removeClass("ui-resizable-resizing");
+this._propagate("stop",b);this._helper&&this.helper.remove();return false},_updateCache:function(b){this.offset=this.helper.offset();if(l(b.left))this.position.left=b.left;if(l(b.top))this.position.top=b.top;if(l(b.height))this.size.height=b.height;if(l(b.width))this.size.width=b.width},_updateRatio:function(b){var a=this.position,c=this.size,d=this.axis;if(b.height)b.width=c.height*this.aspectRatio;else if(b.width)b.height=c.width/this.aspectRatio;if(d=="sw"){b.left=a.left+(c.width-b.width);b.top=
+null}if(d=="nw"){b.top=a.top+(c.height-b.height);b.left=a.left+(c.width-b.width)}return b},_respectSize:function(b){var a=this.options,c=this.axis,d=l(b.width)&&a.maxWidth&&a.maxWidth<b.width,f=l(b.height)&&a.maxHeight&&a.maxHeight<b.height,g=l(b.width)&&a.minWidth&&a.minWidth>b.width,h=l(b.height)&&a.minHeight&&a.minHeight>b.height;if(g)b.width=a.minWidth;if(h)b.height=a.minHeight;if(d)b.width=a.maxWidth;if(f)b.height=a.maxHeight;var i=this.originalPosition.left+this.originalSize.width,j=this.position.top+
+this.size.height,k=/sw|nw|w/.test(c);c=/nw|ne|n/.test(c);if(g&&k)b.left=i-a.minWidth;if(d&&k)b.left=i-a.maxWidth;if(h&&c)b.top=j-a.minHeight;if(f&&c)b.top=j-a.maxHeight;if((a=!b.width&&!b.height)&&!b.left&&b.top)b.top=null;else if(a&&!b.top&&b.left)b.left=null;return b},_proportionallyResize:function(){if(this._proportionallyResizeElements.length)for(var b=this.helper||this.element,a=0;a<this._proportionallyResizeElements.length;a++){var c=this._proportionallyResizeElements[a];if(!this.borderDif){var d=
+[c.css("borderTopWidth"),c.css("borderRightWidth"),c.css("borderBottomWidth"),c.css("borderLeftWidth")],f=[c.css("paddingTop"),c.css("paddingRight"),c.css("paddingBottom"),c.css("paddingLeft")];this.borderDif=e.map(d,function(g,h){g=parseInt(g,10)||0;h=parseInt(f[h],10)||0;return g+h})}e.browser.msie&&(e(b).is(":hidden")||e(b).parents(":hidden").length)||c.css({height:b.height()-this.borderDif[0]-this.borderDif[2]||0,width:b.width()-this.borderDif[1]-this.borderDif[3]||0})}},_renderProxy:function(){var b=
+this.options;this.elementOffset=this.element.offset();if(this._helper){this.helper=this.helper||e('<div style="overflow:hidden;"></div>');var a=e.browser.msie&&e.browser.version<7,c=a?1:0;a=a?2:-1;this.helper.addClass(this._helper).css({width:this.element.outerWidth()+a,height:this.element.outerHeight()+a,position:"absolute",left:this.elementOffset.left-c+"px",top:this.elementOffset.top-c+"px",zIndex:++b.zIndex});this.helper.appendTo("body").disableSelection()}else this.helper=this.element},_change:{e:function(b,
+a){return{width:this.originalSize.width+a}},w:function(b,a){return{left:this.originalPosition.left+a,width:this.originalSize.width-a}},n:function(b,a,c){return{top:this.originalPosition.top+c,height:this.originalSize.height-c}},s:function(b,a,c){return{height:this.originalSize.height+c}},se:function(b,a,c){return e.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[b,a,c]))},sw:function(b,a,c){return e.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[b,a,
+c]))},ne:function(b,a,c){return e.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[b,a,c]))},nw:function(b,a,c){return e.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[b,a,c]))}},_propagate:function(b,a){e.ui.plugin.call(this,b,[a,this.ui()]);b!="resize"&&this._trigger(b,a,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,
+originalPosition:this.originalPosition}}});e.extend(e.ui.resizable,{version:"1.8.10"});e.ui.plugin.add("resizable","alsoResize",{start:function(){var b=e(this).data("resizable").options,a=function(c){e(c).each(function(){var d=e(this);d.data("resizable-alsoresize",{width:parseInt(d.width(),10),height:parseInt(d.height(),10),left:parseInt(d.css("left"),10),top:parseInt(d.css("top"),10),position:d.css("position")})})};if(typeof b.alsoResize=="object"&&!b.alsoResize.parentNode)if(b.alsoResize.length){b.alsoResize=
+b.alsoResize[0];a(b.alsoResize)}else e.each(b.alsoResize,function(c){a(c)});else a(b.alsoResize)},resize:function(b,a){var c=e(this).data("resizable");b=c.options;var d=c.originalSize,f=c.originalPosition,g={height:c.size.height-d.height||0,width:c.size.width-d.width||0,top:c.position.top-f.top||0,left:c.position.left-f.left||0},h=function(i,j){e(i).each(function(){var k=e(this),q=e(this).data("resizable-alsoresize"),p={},r=j&&j.length?j:k.parents(a.originalElement[0]).length?["width","height"]:["width",
+"height","top","left"];e.each(r,function(n,o){if((n=(q[o]||0)+(g[o]||0))&&n>=0)p[o]=n||null});if(e.browser.opera&&/relative/.test(k.css("position"))){c._revertToRelativePosition=true;k.css({position:"absolute",top:"auto",left:"auto"})}k.css(p)})};typeof b.alsoResize=="object"&&!b.alsoResize.nodeType?e.each(b.alsoResize,function(i,j){h(i,j)}):h(b.alsoResize)},stop:function(){var b=e(this).data("resizable"),a=b.options,c=function(d){e(d).each(function(){var f=e(this);f.css({position:f.data("resizable-alsoresize").position})})};
+if(b._revertToRelativePosition){b._revertToRelativePosition=false;typeof a.alsoResize=="object"&&!a.alsoResize.nodeType?e.each(a.alsoResize,function(d){c(d)}):c(a.alsoResize)}e(this).removeData("resizable-alsoresize")}});e.ui.plugin.add("resizable","animate",{stop:function(b){var a=e(this).data("resizable"),c=a.options,d=a._proportionallyResizeElements,f=d.length&&/textarea/i.test(d[0].nodeName),g=f&&e.ui.hasScroll(d[0],"left")?0:a.sizeDiff.height;f={width:a.size.width-(f?0:a.sizeDiff.width),height:a.size.height-
+g};g=parseInt(a.element.css("left"),10)+(a.position.left-a.originalPosition.left)||null;var h=parseInt(a.element.css("top"),10)+(a.position.top-a.originalPosition.top)||null;a.element.animate(e.extend(f,h&&g?{top:h,left:g}:{}),{duration:c.animateDuration,easing:c.animateEasing,step:function(){var i={width:parseInt(a.element.css("width"),10),height:parseInt(a.element.css("height"),10),top:parseInt(a.element.css("top"),10),left:parseInt(a.element.css("left"),10)};d&&d.length&&e(d[0]).css({width:i.width,
+height:i.height});a._updateCache(i);a._propagate("resize",b)}})}});e.ui.plugin.add("resizable","containment",{start:function(){var b=e(this).data("resizable"),a=b.element,c=b.options.containment;if(a=c instanceof e?c.get(0):/parent/.test(c)?a.parent().get(0):c){b.containerElement=e(a);if(/document/.test(c)||c==document){b.containerOffset={left:0,top:0};b.containerPosition={left:0,top:0};b.parentData={element:e(document),left:0,top:0,width:e(document).width(),height:e(document).height()||document.body.parentNode.scrollHeight}}else{var d=
+e(a),f=[];e(["Top","Right","Left","Bottom"]).each(function(i,j){f[i]=m(d.css("padding"+j))});b.containerOffset=d.offset();b.containerPosition=d.position();b.containerSize={height:d.innerHeight()-f[3],width:d.innerWidth()-f[1]};c=b.containerOffset;var g=b.containerSize.height,h=b.containerSize.width;h=e.ui.hasScroll(a,"left")?a.scrollWidth:h;g=e.ui.hasScroll(a)?a.scrollHeight:g;b.parentData={element:a,left:c.left,top:c.top,width:h,height:g}}}},resize:function(b){var a=e(this).data("resizable"),c=a.options,
+d=a.containerOffset,f=a.position;b=a._aspectRatio||b.shiftKey;var g={top:0,left:0},h=a.containerElement;if(h[0]!=document&&/static/.test(h.css("position")))g=d;if(f.left<(a._helper?d.left:0)){a.size.width+=a._helper?a.position.left-d.left:a.position.left-g.left;if(b)a.size.height=a.size.width/c.aspectRatio;a.position.left=c.helper?d.left:0}if(f.top<(a._helper?d.top:0)){a.size.height+=a._helper?a.position.top-d.top:a.position.top;if(b)a.size.width=a.size.height*c.aspectRatio;a.position.top=a._helper?
+d.top:0}a.offset.left=a.parentData.left+a.position.left;a.offset.top=a.parentData.top+a.position.top;c=Math.abs((a._helper?a.offset.left-g.left:a.offset.left-g.left)+a.sizeDiff.width);d=Math.abs((a._helper?a.offset.top-g.top:a.offset.top-d.top)+a.sizeDiff.height);f=a.containerElement.get(0)==a.element.parent().get(0);g=/relative|absolute/.test(a.containerElement.css("position"));if(f&&g)c-=a.parentData.left;if(c+a.size.width>=a.parentData.width){a.size.width=a.parentData.width-c;if(b)a.size.height=
+a.size.width/a.aspectRatio}if(d+a.size.height>=a.parentData.height){a.size.height=a.parentData.height-d;if(b)a.size.width=a.size.height*a.aspectRatio}},stop:function(){var b=e(this).data("resizable"),a=b.options,c=b.containerOffset,d=b.containerPosition,f=b.containerElement,g=e(b.helper),h=g.offset(),i=g.outerWidth()-b.sizeDiff.width;g=g.outerHeight()-b.sizeDiff.height;b._helper&&!a.animate&&/relative/.test(f.css("position"))&&e(this).css({left:h.left-d.left-c.left,width:i,height:g});b._helper&&!a.animate&&
+/static/.test(f.css("position"))&&e(this).css({left:h.left-d.left-c.left,width:i,height:g})}});e.ui.plugin.add("resizable","ghost",{start:function(){var b=e(this).data("resizable"),a=b.options,c=b.size;b.ghost=b.originalElement.clone();b.ghost.css({opacity:0.25,display:"block",position:"relative",height:c.height,width:c.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass(typeof a.ghost=="string"?a.ghost:"");b.ghost.appendTo(b.helper)},resize:function(){var b=e(this).data("resizable");
+b.ghost&&b.ghost.css({position:"relative",height:b.size.height,width:b.size.width})},stop:function(){var b=e(this).data("resizable");b.ghost&&b.helper&&b.helper.get(0).removeChild(b.ghost.get(0))}});e.ui.plugin.add("resizable","grid",{resize:function(){var b=e(this).data("resizable"),a=b.options,c=b.size,d=b.originalSize,f=b.originalPosition,g=b.axis;a.grid=typeof a.grid=="number"?[a.grid,a.grid]:a.grid;var h=Math.round((c.width-d.width)/(a.grid[0]||1))*(a.grid[0]||1);a=Math.round((c.height-d.height)/
+(a.grid[1]||1))*(a.grid[1]||1);if(/^(se|s|e)$/.test(g)){b.size.width=d.width+h;b.size.height=d.height+a}else if(/^(ne)$/.test(g)){b.size.width=d.width+h;b.size.height=d.height+a;b.position.top=f.top-a}else{if(/^(sw)$/.test(g)){b.size.width=d.width+h;b.size.height=d.height+a}else{b.size.width=d.width+h;b.size.height=d.height+a;b.position.top=f.top-a}b.position.left=f.left-h}}});var m=function(b){return parseInt(b,10)||0},l=function(b){return!isNaN(parseInt(b,10))}})(jQuery);
+;/*
+ * jQuery UI Selectable 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Selectables
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.mouse.js
+ * jquery.ui.widget.js
+ */
+(function(e){e.widget("ui.selectable",e.ui.mouse,{options:{appendTo:"body",autoRefresh:true,distance:0,filter:"*",tolerance:"touch"},_create:function(){var c=this;this.element.addClass("ui-selectable");this.dragged=false;var f;this.refresh=function(){f=e(c.options.filter,c.element[0]);f.each(function(){var d=e(this),b=d.offset();e.data(this,"selectable-item",{element:this,$element:d,left:b.left,top:b.top,right:b.left+d.outerWidth(),bottom:b.top+d.outerHeight(),startselected:false,selected:d.hasClass("ui-selected"),
+selecting:d.hasClass("ui-selecting"),unselecting:d.hasClass("ui-unselecting")})})};this.refresh();this.selectees=f.addClass("ui-selectee");this._mouseInit();this.helper=e("<div class='ui-selectable-helper'></div>")},destroy:function(){this.selectees.removeClass("ui-selectee").removeData("selectable-item");this.element.removeClass("ui-selectable ui-selectable-disabled").removeData("selectable").unbind(".selectable");this._mouseDestroy();return this},_mouseStart:function(c){var f=this;this.opos=[c.pageX,
+c.pageY];if(!this.options.disabled){var d=this.options;this.selectees=e(d.filter,this.element[0]);this._trigger("start",c);e(d.appendTo).append(this.helper);this.helper.css({left:c.clientX,top:c.clientY,width:0,height:0});d.autoRefresh&&this.refresh();this.selectees.filter(".ui-selected").each(function(){var b=e.data(this,"selectable-item");b.startselected=true;if(!c.metaKey){b.$element.removeClass("ui-selected");b.selected=false;b.$element.addClass("ui-unselecting");b.unselecting=true;f._trigger("unselecting",
+c,{unselecting:b.element})}});e(c.target).parents().andSelf().each(function(){var b=e.data(this,"selectable-item");if(b){var g=!c.metaKey||!b.$element.hasClass("ui-selected");b.$element.removeClass(g?"ui-unselecting":"ui-selected").addClass(g?"ui-selecting":"ui-unselecting");b.unselecting=!g;b.selecting=g;(b.selected=g)?f._trigger("selecting",c,{selecting:b.element}):f._trigger("unselecting",c,{unselecting:b.element});return false}})}},_mouseDrag:function(c){var f=this;this.dragged=true;if(!this.options.disabled){var d=
+this.options,b=this.opos[0],g=this.opos[1],h=c.pageX,i=c.pageY;if(b>h){var j=h;h=b;b=j}if(g>i){j=i;i=g;g=j}this.helper.css({left:b,top:g,width:h-b,height:i-g});this.selectees.each(function(){var a=e.data(this,"selectable-item");if(!(!a||a.element==f.element[0])){var k=false;if(d.tolerance=="touch")k=!(a.left>h||a.right<b||a.top>i||a.bottom<g);else if(d.tolerance=="fit")k=a.left>b&&a.right<h&&a.top>g&&a.bottom<i;if(k){if(a.selected){a.$element.removeClass("ui-selected");a.selected=false}if(a.unselecting){a.$element.removeClass("ui-unselecting");
+a.unselecting=false}if(!a.selecting){a.$element.addClass("ui-selecting");a.selecting=true;f._trigger("selecting",c,{selecting:a.element})}}else{if(a.selecting)if(c.metaKey&&a.startselected){a.$element.removeClass("ui-selecting");a.selecting=false;a.$element.addClass("ui-selected");a.selected=true}else{a.$element.removeClass("ui-selecting");a.selecting=false;if(a.startselected){a.$element.addClass("ui-unselecting");a.unselecting=true}f._trigger("unselecting",c,{unselecting:a.element})}if(a.selected)if(!c.metaKey&&
+!a.startselected){a.$element.removeClass("ui-selected");a.selected=false;a.$element.addClass("ui-unselecting");a.unselecting=true;f._trigger("unselecting",c,{unselecting:a.element})}}}});return false}},_mouseStop:function(c){var f=this;this.dragged=false;e(".ui-unselecting",this.element[0]).each(function(){var d=e.data(this,"selectable-item");d.$element.removeClass("ui-unselecting");d.unselecting=false;d.startselected=false;f._trigger("unselected",c,{unselected:d.element})});e(".ui-selecting",this.element[0]).each(function(){var d=
+e.data(this,"selectable-item");d.$element.removeClass("ui-selecting").addClass("ui-selected");d.selecting=false;d.selected=true;d.startselected=true;f._trigger("selected",c,{selected:d.element})});this._trigger("stop",c);this.helper.remove();return false}});e.extend(e.ui.selectable,{version:"1.8.10"})})(jQuery);
+;/*
+ * jQuery UI Sortable 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Sortables
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.mouse.js
+ * jquery.ui.widget.js
+ */
+(function(d){d.widget("ui.sortable",d.ui.mouse,{widgetEventPrefix:"sort",options:{appendTo:"parent",axis:false,connectWith:false,containment:false,cursor:"auto",cursorAt:false,dropOnEmpty:true,forcePlaceholderSize:false,forceHelperSize:false,grid:false,handle:false,helper:"original",items:"> *",opacity:false,placeholder:false,revert:false,scroll:true,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1E3},_create:function(){this.containerCache={};this.element.addClass("ui-sortable");
+this.refresh();this.floating=this.items.length?/left|right/.test(this.items[0].item.css("float")):false;this.offset=this.element.offset();this._mouseInit()},destroy:function(){this.element.removeClass("ui-sortable ui-sortable-disabled").removeData("sortable").unbind(".sortable");this._mouseDestroy();for(var a=this.items.length-1;a>=0;a--)this.items[a].item.removeData("sortable-item");return this},_setOption:function(a,b){if(a==="disabled"){this.options[a]=b;this.widget()[b?"addClass":"removeClass"]("ui-sortable-disabled")}else d.Widget.prototype._setOption.apply(this,
+arguments)},_mouseCapture:function(a,b){if(this.reverting)return false;if(this.options.disabled||this.options.type=="static")return false;this._refreshItems(a);var c=null,e=this;d(a.target).parents().each(function(){if(d.data(this,"sortable-item")==e){c=d(this);return false}});if(d.data(a.target,"sortable-item")==e)c=d(a.target);if(!c)return false;if(this.options.handle&&!b){var f=false;d(this.options.handle,c).find("*").andSelf().each(function(){if(this==a.target)f=true});if(!f)return false}this.currentItem=
+c;this._removeCurrentsFromItems();return true},_mouseStart:function(a,b,c){b=this.options;var e=this;this.currentContainer=this;this.refreshPositions();this.helper=this._createHelper(a);this._cacheHelperProportions();this._cacheMargins();this.scrollParent=this.helper.scrollParent();this.offset=this.currentItem.offset();this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left};this.helper.css("position","absolute");this.cssPosition=this.helper.css("position");d.extend(this.offset,
+{click:{left:a.pageX-this.offset.left,top:a.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()});this.originalPosition=this._generatePosition(a);this.originalPageX=a.pageX;this.originalPageY=a.pageY;b.cursorAt&&this._adjustOffsetFromHelper(b.cursorAt);this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]};this.helper[0]!=this.currentItem[0]&&this.currentItem.hide();this._createPlaceholder();b.containment&&this._setContainment();
+if(b.cursor){if(d("body").css("cursor"))this._storedCursor=d("body").css("cursor");d("body").css("cursor",b.cursor)}if(b.opacity){if(this.helper.css("opacity"))this._storedOpacity=this.helper.css("opacity");this.helper.css("opacity",b.opacity)}if(b.zIndex){if(this.helper.css("zIndex"))this._storedZIndex=this.helper.css("zIndex");this.helper.css("zIndex",b.zIndex)}if(this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML")this.overflowOffset=this.scrollParent.offset();this._trigger("start",
+a,this._uiHash());this._preserveHelperProportions||this._cacheHelperProportions();if(!c)for(c=this.containers.length-1;c>=0;c--)this.containers[c]._trigger("activate",a,e._uiHash(this));if(d.ui.ddmanager)d.ui.ddmanager.current=this;d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a);this.dragging=true;this.helper.addClass("ui-sortable-helper");this._mouseDrag(a);return true},_mouseDrag:function(a){this.position=this._generatePosition(a);this.positionAbs=this._convertPositionTo("absolute");
+if(!this.lastPositionAbs)this.lastPositionAbs=this.positionAbs;if(this.options.scroll){var b=this.options,c=false;if(this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML"){if(this.overflowOffset.top+this.scrollParent[0].offsetHeight-a.pageY<b.scrollSensitivity)this.scrollParent[0].scrollTop=c=this.scrollParent[0].scrollTop+b.scrollSpeed;else if(a.pageY-this.overflowOffset.top<b.scrollSensitivity)this.scrollParent[0].scrollTop=c=this.scrollParent[0].scrollTop-b.scrollSpeed;if(this.overflowOffset.left+
+this.scrollParent[0].offsetWidth-a.pageX<b.scrollSensitivity)this.scrollParent[0].scrollLeft=c=this.scrollParent[0].scrollLeft+b.scrollSpeed;else if(a.pageX-this.overflowOffset.left<b.scrollSensitivity)this.scrollParent[0].scrollLeft=c=this.scrollParent[0].scrollLeft-b.scrollSpeed}else{if(a.pageY-d(document).scrollTop()<b.scrollSensitivity)c=d(document).scrollTop(d(document).scrollTop()-b.scrollSpeed);else if(d(window).height()-(a.pageY-d(document).scrollTop())<b.scrollSensitivity)c=d(document).scrollTop(d(document).scrollTop()+
+b.scrollSpeed);if(a.pageX-d(document).scrollLeft()<b.scrollSensitivity)c=d(document).scrollLeft(d(document).scrollLeft()-b.scrollSpeed);else if(d(window).width()-(a.pageX-d(document).scrollLeft())<b.scrollSensitivity)c=d(document).scrollLeft(d(document).scrollLeft()+b.scrollSpeed)}c!==false&&d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a)}this.positionAbs=this._convertPositionTo("absolute");if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+
+"px";if(!this.options.axis||this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";for(b=this.items.length-1;b>=0;b--){c=this.items[b];var e=c.item[0],f=this._intersectsWithPointer(c);if(f)if(e!=this.currentItem[0]&&this.placeholder[f==1?"next":"prev"]()[0]!=e&&!d.ui.contains(this.placeholder[0],e)&&(this.options.type=="semi-dynamic"?!d.ui.contains(this.element[0],e):true)){this.direction=f==1?"down":"up";if(this.options.tolerance=="pointer"||this._intersectsWithSides(c))this._rearrange(a,
+c);else break;this._trigger("change",a,this._uiHash());break}}this._contactContainers(a);d.ui.ddmanager&&d.ui.ddmanager.drag(this,a);this._trigger("sort",a,this._uiHash());this.lastPositionAbs=this.positionAbs;return false},_mouseStop:function(a,b){if(a){d.ui.ddmanager&&!this.options.dropBehaviour&&d.ui.ddmanager.drop(this,a);if(this.options.revert){var c=this;b=c.placeholder.offset();c.reverting=true;d(this.helper).animate({left:b.left-this.offset.parent.left-c.margins.left+(this.offsetParent[0]==
+document.body?0:this.offsetParent[0].scrollLeft),top:b.top-this.offset.parent.top-c.margins.top+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollTop)},parseInt(this.options.revert,10)||500,function(){c._clear(a)})}else this._clear(a,b);return false}},cancel:function(){var a=this;if(this.dragging){this._mouseUp({target:null});this.options.helper=="original"?this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"):this.currentItem.show();for(var b=this.containers.length-
+1;b>=0;b--){this.containers[b]._trigger("deactivate",null,a._uiHash(this));if(this.containers[b].containerCache.over){this.containers[b]._trigger("out",null,a._uiHash(this));this.containers[b].containerCache.over=0}}}if(this.placeholder){this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]);this.options.helper!="original"&&this.helper&&this.helper[0].parentNode&&this.helper.remove();d.extend(this,{helper:null,dragging:false,reverting:false,_noFinalSort:null});
+this.domPosition.prev?d(this.domPosition.prev).after(this.currentItem):d(this.domPosition.parent).prepend(this.currentItem)}return this},serialize:function(a){var b=this._getItemsAsjQuery(a&&a.connected),c=[];a=a||{};d(b).each(function(){var e=(d(a.item||this).attr(a.attribute||"id")||"").match(a.expression||/(.+)[-=_](.+)/);if(e)c.push((a.key||e[1]+"[]")+"="+(a.key&&a.expression?e[1]:e[2]))});!c.length&&a.key&&c.push(a.key+"=");return c.join("&")},toArray:function(a){var b=this._getItemsAsjQuery(a&&
+a.connected),c=[];a=a||{};b.each(function(){c.push(d(a.item||this).attr(a.attribute||"id")||"")});return c},_intersectsWith:function(a){var b=this.positionAbs.left,c=b+this.helperProportions.width,e=this.positionAbs.top,f=e+this.helperProportions.height,g=a.left,h=g+a.width,i=a.top,k=i+a.height,j=this.offset.click.top,l=this.offset.click.left;j=e+j>i&&e+j<k&&b+l>g&&b+l<h;return this.options.tolerance=="pointer"||this.options.forcePointerForContainers||this.options.tolerance!="pointer"&&this.helperProportions[this.floating?
+"width":"height"]>a[this.floating?"width":"height"]?j:g<b+this.helperProportions.width/2&&c-this.helperProportions.width/2<h&&i<e+this.helperProportions.height/2&&f-this.helperProportions.height/2<k},_intersectsWithPointer:function(a){var b=d.ui.isOverAxis(this.positionAbs.top+this.offset.click.top,a.top,a.height);a=d.ui.isOverAxis(this.positionAbs.left+this.offset.click.left,a.left,a.width);b=b&&a;a=this._getDragVerticalDirection();var c=this._getDragHorizontalDirection();if(!b)return false;return this.floating?
+c&&c=="right"||a=="down"?2:1:a&&(a=="down"?2:1)},_intersectsWithSides:function(a){var b=d.ui.isOverAxis(this.positionAbs.top+this.offset.click.top,a.top+a.height/2,a.height);a=d.ui.isOverAxis(this.positionAbs.left+this.offset.click.left,a.left+a.width/2,a.width);var c=this._getDragVerticalDirection(),e=this._getDragHorizontalDirection();return this.floating&&e?e=="right"&&a||e=="left"&&!a:c&&(c=="down"&&b||c=="up"&&!b)},_getDragVerticalDirection:function(){var a=this.positionAbs.top-this.lastPositionAbs.top;
+return a!=0&&(a>0?"down":"up")},_getDragHorizontalDirection:function(){var a=this.positionAbs.left-this.lastPositionAbs.left;return a!=0&&(a>0?"right":"left")},refresh:function(a){this._refreshItems(a);this.refreshPositions();return this},_connectWith:function(){var a=this.options;return a.connectWith.constructor==String?[a.connectWith]:a.connectWith},_getItemsAsjQuery:function(a){var b=[],c=[],e=this._connectWith();if(e&&a)for(a=e.length-1;a>=0;a--)for(var f=d(e[a]),g=f.length-1;g>=0;g--){var h=
+d.data(f[g],"sortable");if(h&&h!=this&&!h.options.disabled)c.push([d.isFunction(h.options.items)?h.options.items.call(h.element):d(h.options.items,h.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),h])}c.push([d.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):d(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),this]);for(a=c.length-1;a>=0;a--)c[a][0].each(function(){b.push(this)});
+return d(b)},_removeCurrentsFromItems:function(){for(var a=this.currentItem.find(":data(sortable-item)"),b=0;b<this.items.length;b++)for(var c=0;c<a.length;c++)a[c]==this.items[b].item[0]&&this.items.splice(b,1)},_refreshItems:function(a){this.items=[];this.containers=[this];var b=this.items,c=[[d.isFunction(this.options.items)?this.options.items.call(this.element[0],a,{item:this.currentItem}):d(this.options.items,this.element),this]],e=this._connectWith();if(e)for(var f=e.length-1;f>=0;f--)for(var g=
+d(e[f]),h=g.length-1;h>=0;h--){var i=d.data(g[h],"sortable");if(i&&i!=this&&!i.options.disabled){c.push([d.isFunction(i.options.items)?i.options.items.call(i.element[0],a,{item:this.currentItem}):d(i.options.items,i.element),i]);this.containers.push(i)}}for(f=c.length-1;f>=0;f--){a=c[f][1];e=c[f][0];h=0;for(g=e.length;h<g;h++){i=d(e[h]);i.data("sortable-item",a);b.push({item:i,instance:a,width:0,height:0,left:0,top:0})}}},refreshPositions:function(a){if(this.offsetParent&&this.helper)this.offset.parent=
+this._getParentOffset();for(var b=this.items.length-1;b>=0;b--){var c=this.items[b],e=this.options.toleranceElement?d(this.options.toleranceElement,c.item):c.item;if(!a){c.width=e.outerWidth();c.height=e.outerHeight()}e=e.offset();c.left=e.left;c.top=e.top}if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(b=this.containers.length-1;b>=0;b--){e=this.containers[b].element.offset();this.containers[b].containerCache.left=e.left;this.containers[b].containerCache.top=
+e.top;this.containers[b].containerCache.width=this.containers[b].element.outerWidth();this.containers[b].containerCache.height=this.containers[b].element.outerHeight()}return this},_createPlaceholder:function(a){var b=a||this,c=b.options;if(!c.placeholder||c.placeholder.constructor==String){var e=c.placeholder;c.placeholder={element:function(){var f=d(document.createElement(b.currentItem[0].nodeName)).addClass(e||b.currentItem[0].className+" ui-sortable-placeholder").removeClass("ui-sortable-helper")[0];
+if(!e)f.style.visibility="hidden";return f},update:function(f,g){if(!(e&&!c.forcePlaceholderSize)){g.height()||g.height(b.currentItem.innerHeight()-parseInt(b.currentItem.css("paddingTop")||0,10)-parseInt(b.currentItem.css("paddingBottom")||0,10));g.width()||g.width(b.currentItem.innerWidth()-parseInt(b.currentItem.css("paddingLeft")||0,10)-parseInt(b.currentItem.css("paddingRight")||0,10))}}}}b.placeholder=d(c.placeholder.element.call(b.element,b.currentItem));b.currentItem.after(b.placeholder);
+c.placeholder.update(b,b.placeholder)},_contactContainers:function(a){for(var b=null,c=null,e=this.containers.length-1;e>=0;e--)if(!d.ui.contains(this.currentItem[0],this.containers[e].element[0]))if(this._intersectsWith(this.containers[e].containerCache)){if(!(b&&d.ui.contains(this.containers[e].element[0],b.element[0]))){b=this.containers[e];c=e}}else if(this.containers[e].containerCache.over){this.containers[e]._trigger("out",a,this._uiHash(this));this.containers[e].containerCache.over=0}if(b)if(this.containers.length===
+1){this.containers[c]._trigger("over",a,this._uiHash(this));this.containers[c].containerCache.over=1}else if(this.currentContainer!=this.containers[c]){b=1E4;e=null;for(var f=this.positionAbs[this.containers[c].floating?"left":"top"],g=this.items.length-1;g>=0;g--)if(d.ui.contains(this.containers[c].element[0],this.items[g].item[0])){var h=this.items[g][this.containers[c].floating?"left":"top"];if(Math.abs(h-f)<b){b=Math.abs(h-f);e=this.items[g]}}if(e||this.options.dropOnEmpty){this.currentContainer=
+this.containers[c];e?this._rearrange(a,e,null,true):this._rearrange(a,null,this.containers[c].element,true);this._trigger("change",a,this._uiHash());this.containers[c]._trigger("change",a,this._uiHash(this));this.options.placeholder.update(this.currentContainer,this.placeholder);this.containers[c]._trigger("over",a,this._uiHash(this));this.containers[c].containerCache.over=1}}},_createHelper:function(a){var b=this.options;a=d.isFunction(b.helper)?d(b.helper.apply(this.element[0],[a,this.currentItem])):
+b.helper=="clone"?this.currentItem.clone():this.currentItem;a.parents("body").length||d(b.appendTo!="parent"?b.appendTo:this.currentItem[0].parentNode)[0].appendChild(a[0]);if(a[0]==this.currentItem[0])this._storedCSS={width:this.currentItem[0].style.width,height:this.currentItem[0].style.height,position:this.currentItem.css("position"),top:this.currentItem.css("top"),left:this.currentItem.css("left")};if(a[0].style.width==""||b.forceHelperSize)a.width(this.currentItem.width());if(a[0].style.height==
+""||b.forceHelperSize)a.height(this.currentItem.height());return a},_adjustOffsetFromHelper:function(a){if(typeof a=="string")a=a.split(" ");if(d.isArray(a))a={left:+a[0],top:+a[1]||0};if("left"in a)this.offset.click.left=a.left+this.margins.left;if("right"in a)this.offset.click.left=this.helperProportions.width-a.right+this.margins.left;if("top"in a)this.offset.click.top=a.top+this.margins.top;if("bottom"in a)this.offset.click.top=this.helperProportions.height-a.bottom+this.margins.top},_getParentOffset:function(){this.offsetParent=
+this.helper.offsetParent();var a=this.offsetParent.offset();if(this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0])){a.left+=this.scrollParent.scrollLeft();a.top+=this.scrollParent.scrollTop()}if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&d.browser.msie)a={top:0,left:0};return{top:a.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:a.left+(parseInt(this.offsetParent.css("borderLeftWidth"),
+10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var a=this.currentItem.position();return{top:a.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}else return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.currentItem.css("marginLeft"),10)||0,top:parseInt(this.currentItem.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions=
+{width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var a=this.options;if(a.containment=="parent")a.containment=this.helper[0].parentNode;if(a.containment=="document"||a.containment=="window")this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,d(a.containment=="document"?document:window).width()-this.helperProportions.width-this.margins.left,(d(a.containment=="document"?document:window).height()||
+document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(a.containment)){var b=d(a.containment)[0];a=d(a.containment).offset();var c=d(b).css("overflow")!="hidden";this.containment=[a.left+(parseInt(d(b).css("borderLeftWidth"),10)||0)+(parseInt(d(b).css("paddingLeft"),10)||0)-this.margins.left,a.top+(parseInt(d(b).css("borderTopWidth"),10)||0)+(parseInt(d(b).css("paddingTop"),10)||0)-this.margins.top,a.left+(c?Math.max(b.scrollWidth,
+b.offsetWidth):b.offsetWidth)-(parseInt(d(b).css("borderLeftWidth"),10)||0)-(parseInt(d(b).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,a.top+(c?Math.max(b.scrollHeight,b.offsetHeight):b.offsetHeight)-(parseInt(d(b).css("borderTopWidth"),10)||0)-(parseInt(d(b).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top]}},_convertPositionTo:function(a,b){if(!b)b=this.position;a=a=="absolute"?1:-1;var c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=
+document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,e=/(html|body)/i.test(c[0].tagName);return{top:b.top+this.offset.relative.top*a+this.offset.parent.top*a-(d.browser.safari&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():e?0:c.scrollTop())*a),left:b.left+this.offset.relative.left*a+this.offset.parent.left*a-(d.browser.safari&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():
+e?0:c.scrollLeft())*a)}},_generatePosition:function(a){var b=this.options,c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,e=/(html|body)/i.test(c[0].tagName);if(this.cssPosition=="relative"&&!(this.scrollParent[0]!=document&&this.scrollParent[0]!=this.offsetParent[0]))this.offset.relative=this._getRelativeOffset();var f=a.pageX,g=a.pageY;if(this.originalPosition){if(this.containment){if(a.pageX-
+this.offset.click.left<this.containment[0])f=this.containment[0]+this.offset.click.left;if(a.pageY-this.offset.click.top<this.containment[1])g=this.containment[1]+this.offset.click.top;if(a.pageX-this.offset.click.left>this.containment[2])f=this.containment[2]+this.offset.click.left;if(a.pageY-this.offset.click.top>this.containment[3])g=this.containment[3]+this.offset.click.top}if(b.grid){g=this.originalPageY+Math.round((g-this.originalPageY)/b.grid[1])*b.grid[1];g=this.containment?!(g-this.offset.click.top<
+this.containment[1]||g-this.offset.click.top>this.containment[3])?g:!(g-this.offset.click.top<this.containment[1])?g-b.grid[1]:g+b.grid[1]:g;f=this.originalPageX+Math.round((f-this.originalPageX)/b.grid[0])*b.grid[0];f=this.containment?!(f-this.offset.click.left<this.containment[0]||f-this.offset.click.left>this.containment[2])?f:!(f-this.offset.click.left<this.containment[0])?f-b.grid[0]:f+b.grid[0]:f}}return{top:g-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+(d.browser.safari&&
+this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollTop():e?0:c.scrollTop()),left:f-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+(d.browser.safari&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():e?0:c.scrollLeft())}},_rearrange:function(a,b,c,e){c?c[0].appendChild(this.placeholder[0]):b.item[0].parentNode.insertBefore(this.placeholder[0],this.direction=="down"?b.item[0]:b.item[0].nextSibling);this.counter=
+this.counter?++this.counter:1;var f=this,g=this.counter;window.setTimeout(function(){g==f.counter&&f.refreshPositions(!e)},0)},_clear:function(a,b){this.reverting=false;var c=[];!this._noFinalSort&&this.currentItem[0].parentNode&&this.placeholder.before(this.currentItem);this._noFinalSort=null;if(this.helper[0]==this.currentItem[0]){for(var e in this._storedCSS)if(this._storedCSS[e]=="auto"||this._storedCSS[e]=="static")this._storedCSS[e]="";this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper")}else this.currentItem.show();
+this.fromOutside&&!b&&c.push(function(f){this._trigger("receive",f,this._uiHash(this.fromOutside))});if((this.fromOutside||this.domPosition.prev!=this.currentItem.prev().not(".ui-sortable-helper")[0]||this.domPosition.parent!=this.currentItem.parent()[0])&&!b)c.push(function(f){this._trigger("update",f,this._uiHash())});if(!d.ui.contains(this.element[0],this.currentItem[0])){b||c.push(function(f){this._trigger("remove",f,this._uiHash())});for(e=this.containers.length-1;e>=0;e--)if(d.ui.contains(this.containers[e].element[0],
+this.currentItem[0])&&!b){c.push(function(f){return function(g){f._trigger("receive",g,this._uiHash(this))}}.call(this,this.containers[e]));c.push(function(f){return function(g){f._trigger("update",g,this._uiHash(this))}}.call(this,this.containers[e]))}}for(e=this.containers.length-1;e>=0;e--){b||c.push(function(f){return function(g){f._trigger("deactivate",g,this._uiHash(this))}}.call(this,this.containers[e]));if(this.containers[e].containerCache.over){c.push(function(f){return function(g){f._trigger("out",
+g,this._uiHash(this))}}.call(this,this.containers[e]));this.containers[e].containerCache.over=0}}this._storedCursor&&d("body").css("cursor",this._storedCursor);this._storedOpacity&&this.helper.css("opacity",this._storedOpacity);if(this._storedZIndex)this.helper.css("zIndex",this._storedZIndex=="auto"?"":this._storedZIndex);this.dragging=false;if(this.cancelHelperRemoval){if(!b){this._trigger("beforeStop",a,this._uiHash());for(e=0;e<c.length;e++)c[e].call(this,a);this._trigger("stop",a,this._uiHash())}return false}b||
+this._trigger("beforeStop",a,this._uiHash());this.placeholder[0].parentNode.removeChild(this.placeholder[0]);this.helper[0]!=this.currentItem[0]&&this.helper.remove();this.helper=null;if(!b){for(e=0;e<c.length;e++)c[e].call(this,a);this._trigger("stop",a,this._uiHash())}this.fromOutside=false;return true},_trigger:function(){d.Widget.prototype._trigger.apply(this,arguments)===false&&this.cancel()},_uiHash:function(a){var b=a||this;return{helper:b.helper,placeholder:b.placeholder||d([]),position:b.position,
+originalPosition:b.originalPosition,offset:b.positionAbs,item:b.currentItem,sender:a?a.element:null}}});d.extend(d.ui.sortable,{version:"1.8.10"})})(jQuery);
+;/*
+ * jQuery UI Accordion 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Accordion
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ */
+(function(c){c.widget("ui.accordion",{options:{active:0,animated:"slide",autoHeight:true,clearStyle:false,collapsible:false,event:"click",fillSpace:false,header:"> li > :first-child,> :not(li):even",icons:{header:"ui-icon-triangle-1-e",headerSelected:"ui-icon-triangle-1-s"},navigation:false,navigationFilter:function(){return this.href.toLowerCase()===location.href.toLowerCase()}},_create:function(){var a=this,b=a.options;a.running=0;a.element.addClass("ui-accordion ui-widget ui-helper-reset").children("li").addClass("ui-accordion-li-fix");
+a.headers=a.element.find(b.header).addClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all").bind("mouseenter.accordion",function(){b.disabled||c(this).addClass("ui-state-hover")}).bind("mouseleave.accordion",function(){b.disabled||c(this).removeClass("ui-state-hover")}).bind("focus.accordion",function(){b.disabled||c(this).addClass("ui-state-focus")}).bind("blur.accordion",function(){b.disabled||c(this).removeClass("ui-state-focus")});a.headers.next().addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom");
+if(b.navigation){var d=a.element.find("a").filter(b.navigationFilter).eq(0);if(d.length){var h=d.closest(".ui-accordion-header");a.active=h.length?h:d.closest(".ui-accordion-content").prev()}}a.active=a._findActive(a.active||b.active).addClass("ui-state-default ui-state-active").toggleClass("ui-corner-all").toggleClass("ui-corner-top");a.active.next().addClass("ui-accordion-content-active");a._createIcons();a.resize();a.element.attr("role","tablist");a.headers.attr("role","tab").bind("keydown.accordion",
+function(f){return a._keydown(f)}).next().attr("role","tabpanel");a.headers.not(a.active||"").attr({"aria-expanded":"false",tabIndex:-1}).next().hide();a.active.length?a.active.attr({"aria-expanded":"true",tabIndex:0}):a.headers.eq(0).attr("tabIndex",0);c.browser.safari||a.headers.find("a").attr("tabIndex",-1);b.event&&a.headers.bind(b.event.split(" ").join(".accordion ")+".accordion",function(f){a._clickHandler.call(a,f,this);f.preventDefault()})},_createIcons:function(){var a=this.options;if(a.icons){c("<span></span>").addClass("ui-icon "+
+a.icons.header).prependTo(this.headers);this.active.children(".ui-icon").toggleClass(a.icons.header).toggleClass(a.icons.headerSelected);this.element.addClass("ui-accordion-icons")}},_destroyIcons:function(){this.headers.children(".ui-icon").remove();this.element.removeClass("ui-accordion-icons")},destroy:function(){var a=this.options;this.element.removeClass("ui-accordion ui-widget ui-helper-reset").removeAttr("role");this.headers.unbind(".accordion").removeClass("ui-accordion-header ui-accordion-disabled ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top").removeAttr("role").removeAttr("aria-expanded").removeAttr("tabIndex");
+this.headers.find("a").removeAttr("tabIndex");this._destroyIcons();var b=this.headers.next().css("display","").removeAttr("role").removeClass("ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-accordion-disabled ui-state-disabled");if(a.autoHeight||a.fillHeight)b.css("height","");return c.Widget.prototype.destroy.call(this)},_setOption:function(a,b){c.Widget.prototype._setOption.apply(this,arguments);a=="active"&&this.activate(b);if(a=="icons"){this._destroyIcons();
+b&&this._createIcons()}if(a=="disabled")this.headers.add(this.headers.next())[b?"addClass":"removeClass"]("ui-accordion-disabled ui-state-disabled")},_keydown:function(a){if(!(this.options.disabled||a.altKey||a.ctrlKey)){var b=c.ui.keyCode,d=this.headers.length,h=this.headers.index(a.target),f=false;switch(a.keyCode){case b.RIGHT:case b.DOWN:f=this.headers[(h+1)%d];break;case b.LEFT:case b.UP:f=this.headers[(h-1+d)%d];break;case b.SPACE:case b.ENTER:this._clickHandler({target:a.target},a.target);
+a.preventDefault()}if(f){c(a.target).attr("tabIndex",-1);c(f).attr("tabIndex",0);f.focus();return false}return true}},resize:function(){var a=this.options,b;if(a.fillSpace){if(c.browser.msie){var d=this.element.parent().css("overflow");this.element.parent().css("overflow","hidden")}b=this.element.parent().height();c.browser.msie&&this.element.parent().css("overflow",d);this.headers.each(function(){b-=c(this).outerHeight(true)});this.headers.next().each(function(){c(this).height(Math.max(0,b-c(this).innerHeight()+
+c(this).height()))}).css("overflow","auto")}else if(a.autoHeight){b=0;this.headers.next().each(function(){b=Math.max(b,c(this).height("").height())}).height(b)}return this},activate:function(a){this.options.active=a;a=this._findActive(a)[0];this._clickHandler({target:a},a);return this},_findActive:function(a){return a?typeof a==="number"?this.headers.filter(":eq("+a+")"):this.headers.not(this.headers.not(a)):a===false?c([]):this.headers.filter(":eq(0)")},_clickHandler:function(a,b){var d=this.options;
+if(!d.disabled)if(a.target){a=c(a.currentTarget||b);b=a[0]===this.active[0];d.active=d.collapsible&&b?false:this.headers.index(a);if(!(this.running||!d.collapsible&&b)){var h=this.active;j=a.next();g=this.active.next();e={options:d,newHeader:b&&d.collapsible?c([]):a,oldHeader:this.active,newContent:b&&d.collapsible?c([]):j,oldContent:g};var f=this.headers.index(this.active[0])>this.headers.index(a[0]);this.active=b?c([]):a;this._toggle(j,g,e,b,f);h.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").children(".ui-icon").removeClass(d.icons.headerSelected).addClass(d.icons.header);
+if(!b){a.removeClass("ui-state-default ui-corner-all").addClass("ui-state-active ui-corner-top").children(".ui-icon").removeClass(d.icons.header).addClass(d.icons.headerSelected);a.next().addClass("ui-accordion-content-active")}}}else if(d.collapsible){this.active.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").children(".ui-icon").removeClass(d.icons.headerSelected).addClass(d.icons.header);this.active.next().addClass("ui-accordion-content-active");var g=this.active.next(),
+e={options:d,newHeader:c([]),oldHeader:d.active,newContent:c([]),oldContent:g},j=this.active=c([]);this._toggle(j,g,e)}},_toggle:function(a,b,d,h,f){var g=this,e=g.options;g.toShow=a;g.toHide=b;g.data=d;var j=function(){if(g)return g._completed.apply(g,arguments)};g._trigger("changestart",null,g.data);g.running=b.size()===0?a.size():b.size();if(e.animated){d={};d=e.collapsible&&h?{toShow:c([]),toHide:b,complete:j,down:f,autoHeight:e.autoHeight||e.fillSpace}:{toShow:a,toHide:b,complete:j,down:f,autoHeight:e.autoHeight||
+e.fillSpace};if(!e.proxied)e.proxied=e.animated;if(!e.proxiedDuration)e.proxiedDuration=e.duration;e.animated=c.isFunction(e.proxied)?e.proxied(d):e.proxied;e.duration=c.isFunction(e.proxiedDuration)?e.proxiedDuration(d):e.proxiedDuration;h=c.ui.accordion.animations;var i=e.duration,k=e.animated;if(k&&!h[k]&&!c.easing[k])k="slide";h[k]||(h[k]=function(l){this.slide(l,{easing:k,duration:i||700})});h[k](d)}else{if(e.collapsible&&h)a.toggle();else{b.hide();a.show()}j(true)}b.prev().attr({"aria-expanded":"false",
+tabIndex:-1}).blur();a.prev().attr({"aria-expanded":"true",tabIndex:0}).focus()},_completed:function(a){this.running=a?0:--this.running;if(!this.running){this.options.clearStyle&&this.toShow.add(this.toHide).css({height:"",overflow:""});this.toHide.removeClass("ui-accordion-content-active");if(this.toHide.length)this.toHide.parent()[0].className=this.toHide.parent()[0].className;this._trigger("change",null,this.data)}}});c.extend(c.ui.accordion,{version:"1.8.10",animations:{slide:function(a,b){a=
+c.extend({easing:"swing",duration:300},a,b);if(a.toHide.size())if(a.toShow.size()){var d=a.toShow.css("overflow"),h=0,f={},g={},e;b=a.toShow;e=b[0].style.width;b.width(parseInt(b.parent().width(),10)-parseInt(b.css("paddingLeft"),10)-parseInt(b.css("paddingRight"),10)-(parseInt(b.css("borderLeftWidth"),10)||0)-(parseInt(b.css("borderRightWidth"),10)||0));c.each(["height","paddingTop","paddingBottom"],function(j,i){g[i]="hide";j=(""+c.css(a.toShow[0],i)).match(/^([\d+-.]+)(.*)$/);f[i]={value:j[1],
+unit:j[2]||"px"}});a.toShow.css({height:0,overflow:"hidden"}).show();a.toHide.filter(":hidden").each(a.complete).end().filter(":visible").animate(g,{step:function(j,i){if(i.prop=="height")h=i.end-i.start===0?0:(i.now-i.start)/(i.end-i.start);a.toShow[0].style[i.prop]=h*f[i.prop].value+f[i.prop].unit},duration:a.duration,easing:a.easing,complete:function(){a.autoHeight||a.toShow.css("height","");a.toShow.css({width:e,overflow:d});a.complete()}})}else a.toHide.animate({height:"hide",paddingTop:"hide",
+paddingBottom:"hide"},a);else a.toShow.animate({height:"show",paddingTop:"show",paddingBottom:"show"},a)},bounceslide:function(a){this.slide(a,{easing:a.down?"easeOutBounce":"swing",duration:a.down?1E3:200})}}})})(jQuery);
+;/*
+ * jQuery UI Autocomplete 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Autocomplete
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ * jquery.ui.position.js
+ */
+(function(d){var e=0;d.widget("ui.autocomplete",{options:{appendTo:"body",delay:300,minLength:1,position:{my:"left top",at:"left bottom",collision:"none"},source:null},pending:0,_create:function(){var a=this,b=this.element[0].ownerDocument,g;this.element.addClass("ui-autocomplete-input").attr("autocomplete","off").attr({role:"textbox","aria-autocomplete":"list","aria-haspopup":"true"}).bind("keydown.autocomplete",function(c){if(!(a.options.disabled||a.element.attr("readonly"))){g=false;var f=d.ui.keyCode;
+switch(c.keyCode){case f.PAGE_UP:a._move("previousPage",c);break;case f.PAGE_DOWN:a._move("nextPage",c);break;case f.UP:a._move("previous",c);c.preventDefault();break;case f.DOWN:a._move("next",c);c.preventDefault();break;case f.ENTER:case f.NUMPAD_ENTER:if(a.menu.active){g=true;c.preventDefault()}case f.TAB:if(!a.menu.active)return;a.menu.select(c);break;case f.ESCAPE:a.element.val(a.term);a.close(c);break;default:clearTimeout(a.searching);a.searching=setTimeout(function(){if(a.term!=a.element.val()){a.selectedItem=
+null;a.search(null,c)}},a.options.delay);break}}}).bind("keypress.autocomplete",function(c){if(g){g=false;c.preventDefault()}}).bind("focus.autocomplete",function(){if(!a.options.disabled){a.selectedItem=null;a.previous=a.element.val()}}).bind("blur.autocomplete",function(c){if(!a.options.disabled){clearTimeout(a.searching);a.closing=setTimeout(function(){a.close(c);a._change(c)},150)}});this._initSource();this.response=function(){return a._response.apply(a,arguments)};this.menu=d("<ul></ul>").addClass("ui-autocomplete").appendTo(d(this.options.appendTo||
+"body",b)[0]).mousedown(function(c){var f=a.menu.element[0];d(c.target).closest(".ui-menu-item").length||setTimeout(function(){d(document).one("mousedown",function(h){h.target!==a.element[0]&&h.target!==f&&!d.ui.contains(f,h.target)&&a.close()})},1);setTimeout(function(){clearTimeout(a.closing)},13)}).menu({focus:function(c,f){f=f.item.data("item.autocomplete");false!==a._trigger("focus",c,{item:f})&&/^key/.test(c.originalEvent.type)&&a.element.val(f.value)},selected:function(c,f){var h=f.item.data("item.autocomplete"),
+i=a.previous;if(a.element[0]!==b.activeElement){a.element.focus();a.previous=i;setTimeout(function(){a.previous=i;a.selectedItem=h},1)}false!==a._trigger("select",c,{item:h})&&a.element.val(h.value);a.term=a.element.val();a.close(c);a.selectedItem=h},blur:function(){a.menu.element.is(":visible")&&a.element.val()!==a.term&&a.element.val(a.term)}}).zIndex(this.element.zIndex()+1).css({top:0,left:0}).hide().data("menu");d.fn.bgiframe&&this.menu.element.bgiframe()},destroy:function(){this.element.removeClass("ui-autocomplete-input").removeAttr("autocomplete").removeAttr("role").removeAttr("aria-autocomplete").removeAttr("aria-haspopup");
+this.menu.element.remove();d.Widget.prototype.destroy.call(this)},_setOption:function(a,b){d.Widget.prototype._setOption.apply(this,arguments);a==="source"&&this._initSource();if(a==="appendTo")this.menu.element.appendTo(d(b||"body",this.element[0].ownerDocument)[0]);a==="disabled"&&b&&this.xhr&&this.xhr.abort()},_initSource:function(){var a=this,b,g;if(d.isArray(this.options.source)){b=this.options.source;this.source=function(c,f){f(d.ui.autocomplete.filter(b,c.term))}}else if(typeof this.options.source===
+"string"){g=this.options.source;this.source=function(c,f){a.xhr&&a.xhr.abort();a.xhr=d.ajax({url:g,data:c,dataType:"json",autocompleteRequest:++e,success:function(h){this.autocompleteRequest===e&&f(h)},error:function(){this.autocompleteRequest===e&&f([])}})}}else this.source=this.options.source},search:function(a,b){a=a!=null?a:this.element.val();this.term=this.element.val();if(a.length<this.options.minLength)return this.close(b);clearTimeout(this.closing);if(this._trigger("search",b)!==false)return this._search(a)},
+_search:function(a){this.pending++;this.element.addClass("ui-autocomplete-loading");this.source({term:a},this.response)},_response:function(a){if(!this.options.disabled&&a&&a.length){a=this._normalize(a);this._suggest(a);this._trigger("open")}else this.close();this.pending--;this.pending||this.element.removeClass("ui-autocomplete-loading")},close:function(a){clearTimeout(this.closing);if(this.menu.element.is(":visible")){this.menu.element.hide();this.menu.deactivate();this._trigger("close",a)}},_change:function(a){this.previous!==
+this.element.val()&&this._trigger("change",a,{item:this.selectedItem})},_normalize:function(a){if(a.length&&a[0].label&&a[0].value)return a;return d.map(a,function(b){if(typeof b==="string")return{label:b,value:b};return d.extend({label:b.label||b.value,value:b.value||b.label},b)})},_suggest:function(a){var b=this.menu.element.empty().zIndex(this.element.zIndex()+1);this._renderMenu(b,a);this.menu.deactivate();this.menu.refresh();b.show();this._resizeMenu();b.position(d.extend({of:this.element},this.options.position))},
+_resizeMenu:function(){var a=this.menu.element;a.outerWidth(Math.max(a.width("").outerWidth(),this.element.outerWidth()))},_renderMenu:function(a,b){var g=this;d.each(b,function(c,f){g._renderItem(a,f)})},_renderItem:function(a,b){return d("<li></li>").data("item.autocomplete",b).append(d("<a></a>").text(b.label)).appendTo(a)},_move:function(a,b){if(this.menu.element.is(":visible"))if(this.menu.first()&&/^previous/.test(a)||this.menu.last()&&/^next/.test(a)){this.element.val(this.term);this.menu.deactivate()}else this.menu[a](b);
+else this.search(null,b)},widget:function(){return this.menu.element}});d.extend(d.ui.autocomplete,{escapeRegex:function(a){return a.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&")},filter:function(a,b){var g=new RegExp(d.ui.autocomplete.escapeRegex(b),"i");return d.grep(a,function(c){return g.test(c.label||c.value||c)})}})})(jQuery);
+(function(d){d.widget("ui.menu",{_create:function(){var e=this;this.element.addClass("ui-menu ui-widget ui-widget-content ui-corner-all").attr({role:"listbox","aria-activedescendant":"ui-active-menuitem"}).click(function(a){if(d(a.target).closest(".ui-menu-item a").length){a.preventDefault();e.select(a)}});this.refresh()},refresh:function(){var e=this;this.element.children("li:not(.ui-menu-item):has(a)").addClass("ui-menu-item").attr("role","menuitem").children("a").addClass("ui-corner-all").attr("tabindex",
+-1).mouseenter(function(a){e.activate(a,d(this).parent())}).mouseleave(function(){e.deactivate()})},activate:function(e,a){this.deactivate();if(this.hasScroll()){var b=a.offset().top-this.element.offset().top,g=this.element.attr("scrollTop"),c=this.element.height();if(b<0)this.element.attr("scrollTop",g+b);else b>=c&&this.element.attr("scrollTop",g+b-c+a.height())}this.active=a.eq(0).children("a").addClass("ui-state-hover").attr("id","ui-active-menuitem").end();this._trigger("focus",e,{item:a})},
+deactivate:function(){if(this.active){this.active.children("a").removeClass("ui-state-hover").removeAttr("id");this._trigger("blur");this.active=null}},next:function(e){this.move("next",".ui-menu-item:first",e)},previous:function(e){this.move("prev",".ui-menu-item:last",e)},first:function(){return this.active&&!this.active.prevAll(".ui-menu-item").length},last:function(){return this.active&&!this.active.nextAll(".ui-menu-item").length},move:function(e,a,b){if(this.active){e=this.active[e+"All"](".ui-menu-item").eq(0);
+e.length?this.activate(b,e):this.activate(b,this.element.children(a))}else this.activate(b,this.element.children(a))},nextPage:function(e){if(this.hasScroll())if(!this.active||this.last())this.activate(e,this.element.children(".ui-menu-item:first"));else{var a=this.active.offset().top,b=this.element.height(),g=this.element.children(".ui-menu-item").filter(function(){var c=d(this).offset().top-a-b+d(this).height();return c<10&&c>-10});g.length||(g=this.element.children(".ui-menu-item:last"));this.activate(e,
+g)}else this.activate(e,this.element.children(".ui-menu-item").filter(!this.active||this.last()?":first":":last"))},previousPage:function(e){if(this.hasScroll())if(!this.active||this.first())this.activate(e,this.element.children(".ui-menu-item:last"));else{var a=this.active.offset().top,b=this.element.height();result=this.element.children(".ui-menu-item").filter(function(){var g=d(this).offset().top-a+b-d(this).height();return g<10&&g>-10});result.length||(result=this.element.children(".ui-menu-item:first"));
+this.activate(e,result)}else this.activate(e,this.element.children(".ui-menu-item").filter(!this.active||this.first()?":last":":first"))},hasScroll:function(){return this.element.height()<this.element.attr("scrollHeight")},select:function(e){this._trigger("selected",e,{item:this.active})}})})(jQuery);
+;/*
+ * jQuery UI Button 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Button
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ */
+(function(a){var g,i=function(b){a(":ui-button",b.target.form).each(function(){var c=a(this).data("button");setTimeout(function(){c.refresh()},1)})},h=function(b){var c=b.name,d=b.form,f=a([]);if(c)f=d?a(d).find("[name='"+c+"']"):a("[name='"+c+"']",b.ownerDocument).filter(function(){return!this.form});return f};a.widget("ui.button",{options:{disabled:null,text:true,label:null,icons:{primary:null,secondary:null}},_create:function(){this.element.closest("form").unbind("reset.button").bind("reset.button",
+i);if(typeof this.options.disabled!=="boolean")this.options.disabled=this.element.attr("disabled");this._determineButtonType();this.hasTitle=!!this.buttonElement.attr("title");var b=this,c=this.options,d=this.type==="checkbox"||this.type==="radio",f="ui-state-hover"+(!d?" ui-state-active":"");if(c.label===null)c.label=this.buttonElement.html();if(this.element.is(":disabled"))c.disabled=true;this.buttonElement.addClass("ui-button ui-widget ui-state-default ui-corner-all").attr("role","button").bind("mouseenter.button",
+function(){if(!c.disabled){a(this).addClass("ui-state-hover");this===g&&a(this).addClass("ui-state-active")}}).bind("mouseleave.button",function(){c.disabled||a(this).removeClass(f)}).bind("focus.button",function(){a(this).addClass("ui-state-focus")}).bind("blur.button",function(){a(this).removeClass("ui-state-focus")});d&&this.element.bind("change.button",function(){b.refresh()});if(this.type==="checkbox")this.buttonElement.bind("click.button",function(){if(c.disabled)return false;a(this).toggleClass("ui-state-active");
+b.buttonElement.attr("aria-pressed",b.element[0].checked)});else if(this.type==="radio")this.buttonElement.bind("click.button",function(){if(c.disabled)return false;a(this).addClass("ui-state-active");b.buttonElement.attr("aria-pressed",true);var e=b.element[0];h(e).not(e).map(function(){return a(this).button("widget")[0]}).removeClass("ui-state-active").attr("aria-pressed",false)});else{this.buttonElement.bind("mousedown.button",function(){if(c.disabled)return false;a(this).addClass("ui-state-active");
+g=this;a(document).one("mouseup",function(){g=null})}).bind("mouseup.button",function(){if(c.disabled)return false;a(this).removeClass("ui-state-active")}).bind("keydown.button",function(e){if(c.disabled)return false;if(e.keyCode==a.ui.keyCode.SPACE||e.keyCode==a.ui.keyCode.ENTER)a(this).addClass("ui-state-active")}).bind("keyup.button",function(){a(this).removeClass("ui-state-active")});this.buttonElement.is("a")&&this.buttonElement.keyup(function(e){e.keyCode===a.ui.keyCode.SPACE&&a(this).click()})}this._setOption("disabled",
+c.disabled)},_determineButtonType:function(){this.type=this.element.is(":checkbox")?"checkbox":this.element.is(":radio")?"radio":this.element.is("input")?"input":"button";if(this.type==="checkbox"||this.type==="radio"){this.buttonElement=this.element.parents().last().find("label[for="+this.element.attr("id")+"]");this.element.addClass("ui-helper-hidden-accessible");var b=this.element.is(":checked");b&&this.buttonElement.addClass("ui-state-active");this.buttonElement.attr("aria-pressed",b)}else this.buttonElement=
+this.element},widget:function(){return this.buttonElement},destroy:function(){this.element.removeClass("ui-helper-hidden-accessible");this.buttonElement.removeClass("ui-button ui-widget ui-state-default ui-corner-all ui-state-hover ui-state-active ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only").removeAttr("role").removeAttr("aria-pressed").html(this.buttonElement.find(".ui-button-text").html());this.hasTitle||
+this.buttonElement.removeAttr("title");a.Widget.prototype.destroy.call(this)},_setOption:function(b,c){a.Widget.prototype._setOption.apply(this,arguments);if(b==="disabled")c?this.element.attr("disabled",true):this.element.removeAttr("disabled");this._resetButton()},refresh:function(){var b=this.element.is(":disabled");b!==this.options.disabled&&this._setOption("disabled",b);if(this.type==="radio")h(this.element[0]).each(function(){a(this).is(":checked")?a(this).button("widget").addClass("ui-state-active").attr("aria-pressed",
+true):a(this).button("widget").removeClass("ui-state-active").attr("aria-pressed",false)});else if(this.type==="checkbox")this.element.is(":checked")?this.buttonElement.addClass("ui-state-active").attr("aria-pressed",true):this.buttonElement.removeClass("ui-state-active").attr("aria-pressed",false)},_resetButton:function(){if(this.type==="input")this.options.label&&this.element.val(this.options.label);else{var b=this.buttonElement.removeClass("ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only"),
+c=a("<span></span>").addClass("ui-button-text").html(this.options.label).appendTo(b.empty()).text(),d=this.options.icons,f=d.primary&&d.secondary,e=[];if(d.primary||d.secondary){e.push("ui-button-text-icon"+(f?"s":d.primary?"-primary":"-secondary"));d.primary&&b.prepend("<span class='ui-button-icon-primary ui-icon "+d.primary+"'></span>");d.secondary&&b.append("<span class='ui-button-icon-secondary ui-icon "+d.secondary+"'></span>");if(!this.options.text){e.push(f?"ui-button-icons-only":"ui-button-icon-only");
+b.removeClass("ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary");this.hasTitle||b.attr("title",c)}}else e.push("ui-button-text-only");b.addClass(e.join(" "))}}});a.widget("ui.buttonset",{options:{items:":button, :submit, :reset, :checkbox, :radio, a, :data(button)"},_create:function(){this.element.addClass("ui-buttonset")},_init:function(){this.refresh()},_setOption:function(b,c){b==="disabled"&&this.buttons.button("option",b,c);a.Widget.prototype._setOption.apply(this,
+arguments)},refresh:function(){this.buttons=this.element.find(this.options.items).filter(":ui-button").button("refresh").end().not(":ui-button").button().end().map(function(){return a(this).button("widget")[0]}).removeClass("ui-corner-all ui-corner-left ui-corner-right").filter(":first").addClass("ui-corner-left").end().filter(":last").addClass("ui-corner-right").end().end()},destroy:function(){this.element.removeClass("ui-buttonset");this.buttons.map(function(){return a(this).button("widget")[0]}).removeClass("ui-corner-left ui-corner-right").end().button("destroy");
+a.Widget.prototype.destroy.call(this)}})})(jQuery);
+;/*
+ * jQuery UI Dialog 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Dialog
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ * jquery.ui.button.js
+ * jquery.ui.draggable.js
+ * jquery.ui.mouse.js
+ * jquery.ui.position.js
+ * jquery.ui.resizable.js
+ */
+(function(c,j){var k={buttons:true,height:true,maxHeight:true,maxWidth:true,minHeight:true,minWidth:true,width:true},l={maxHeight:true,maxWidth:true,minHeight:true,minWidth:true};c.widget("ui.dialog",{options:{autoOpen:true,buttons:{},closeOnEscape:true,closeText:"close",dialogClass:"",draggable:true,hide:null,height:"auto",maxHeight:false,maxWidth:false,minHeight:150,minWidth:150,modal:false,position:{my:"center",at:"center",collision:"fit",using:function(a){var b=c(this).css(a).offset().top;b<0&&
+c(this).css("top",a.top-b)}},resizable:true,show:null,stack:true,title:"",width:300,zIndex:1E3},_create:function(){this.originalTitle=this.element.attr("title");if(typeof this.originalTitle!=="string")this.originalTitle="";this.options.title=this.options.title||this.originalTitle;var a=this,b=a.options,d=b.title||"&#160;",e=c.ui.dialog.getTitleId(a.element),g=(a.uiDialog=c("<div></div>")).appendTo(document.body).hide().addClass("ui-dialog ui-widget ui-widget-content ui-corner-all "+b.dialogClass).css({zIndex:b.zIndex}).attr("tabIndex",
+-1).css("outline",0).keydown(function(i){if(b.closeOnEscape&&i.keyCode&&i.keyCode===c.ui.keyCode.ESCAPE){a.close(i);i.preventDefault()}}).attr({role:"dialog","aria-labelledby":e}).mousedown(function(i){a.moveToTop(false,i)});a.element.show().removeAttr("title").addClass("ui-dialog-content ui-widget-content").appendTo(g);var f=(a.uiDialogTitlebar=c("<div></div>")).addClass("ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix").prependTo(g),h=c('<a href="#"></a>').addClass("ui-dialog-titlebar-close ui-corner-all").attr("role",
+"button").hover(function(){h.addClass("ui-state-hover")},function(){h.removeClass("ui-state-hover")}).focus(function(){h.addClass("ui-state-focus")}).blur(function(){h.removeClass("ui-state-focus")}).click(function(i){a.close(i);return false}).appendTo(f);(a.uiDialogTitlebarCloseText=c("<span></span>")).addClass("ui-icon ui-icon-closethick").text(b.closeText).appendTo(h);c("<span></span>").addClass("ui-dialog-title").attr("id",e).html(d).prependTo(f);if(c.isFunction(b.beforeclose)&&!c.isFunction(b.beforeClose))b.beforeClose=
+b.beforeclose;f.find("*").add(f).disableSelection();b.draggable&&c.fn.draggable&&a._makeDraggable();b.resizable&&c.fn.resizable&&a._makeResizable();a._createButtons(b.buttons);a._isOpen=false;c.fn.bgiframe&&g.bgiframe()},_init:function(){this.options.autoOpen&&this.open()},destroy:function(){var a=this;a.overlay&&a.overlay.destroy();a.uiDialog.hide();a.element.unbind(".dialog").removeData("dialog").removeClass("ui-dialog-content ui-widget-content").hide().appendTo("body");a.uiDialog.remove();a.originalTitle&&
+a.element.attr("title",a.originalTitle);return a},widget:function(){return this.uiDialog},close:function(a){var b=this,d,e;if(false!==b._trigger("beforeClose",a)){b.overlay&&b.overlay.destroy();b.uiDialog.unbind("keypress.ui-dialog");b._isOpen=false;if(b.options.hide)b.uiDialog.hide(b.options.hide,function(){b._trigger("close",a)});else{b.uiDialog.hide();b._trigger("close",a)}c.ui.dialog.overlay.resize();if(b.options.modal){d=0;c(".ui-dialog").each(function(){if(this!==b.uiDialog[0]){e=c(this).css("z-index");
+isNaN(e)||(d=Math.max(d,e))}});c.ui.dialog.maxZ=d}return b}},isOpen:function(){return this._isOpen},moveToTop:function(a,b){var d=this,e=d.options;if(e.modal&&!a||!e.stack&&!e.modal)return d._trigger("focus",b);if(e.zIndex>c.ui.dialog.maxZ)c.ui.dialog.maxZ=e.zIndex;if(d.overlay){c.ui.dialog.maxZ+=1;d.overlay.$el.css("z-index",c.ui.dialog.overlay.maxZ=c.ui.dialog.maxZ)}a={scrollTop:d.element.attr("scrollTop"),scrollLeft:d.element.attr("scrollLeft")};c.ui.dialog.maxZ+=1;d.uiDialog.css("z-index",c.ui.dialog.maxZ);
+d.element.attr(a);d._trigger("focus",b);return d},open:function(){if(!this._isOpen){var a=this,b=a.options,d=a.uiDialog;a.overlay=b.modal?new c.ui.dialog.overlay(a):null;a._size();a._position(b.position);d.show(b.show);a.moveToTop(true);b.modal&&d.bind("keypress.ui-dialog",function(e){if(e.keyCode===c.ui.keyCode.TAB){var g=c(":tabbable",this),f=g.filter(":first");g=g.filter(":last");if(e.target===g[0]&&!e.shiftKey){f.focus(1);return false}else if(e.target===f[0]&&e.shiftKey){g.focus(1);return false}}});
+c(a.element.find(":tabbable").get().concat(d.find(".ui-dialog-buttonpane :tabbable").get().concat(d.get()))).eq(0).focus();a._isOpen=true;a._trigger("open");return a}},_createButtons:function(a){var b=this,d=false,e=c("<div></div>").addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix"),g=c("<div></div>").addClass("ui-dialog-buttonset").appendTo(e);b.uiDialog.find(".ui-dialog-buttonpane").remove();typeof a==="object"&&a!==null&&c.each(a,function(){return!(d=true)});if(d){c.each(a,function(f,
+h){h=c.isFunction(h)?{click:h,text:f}:h;f=c('<button type="button"></button>').attr(h,true).unbind("click").click(function(){h.click.apply(b.element[0],arguments)}).appendTo(g);c.fn.button&&f.button()});e.appendTo(b.uiDialog)}},_makeDraggable:function(){function a(f){return{position:f.position,offset:f.offset}}var b=this,d=b.options,e=c(document),g;b.uiDialog.draggable({cancel:".ui-dialog-content, .ui-dialog-titlebar-close",handle:".ui-dialog-titlebar",containment:"document",start:function(f,h){g=
+d.height==="auto"?"auto":c(this).height();c(this).height(c(this).height()).addClass("ui-dialog-dragging");b._trigger("dragStart",f,a(h))},drag:function(f,h){b._trigger("drag",f,a(h))},stop:function(f,h){d.position=[h.position.left-e.scrollLeft(),h.position.top-e.scrollTop()];c(this).removeClass("ui-dialog-dragging").height(g);b._trigger("dragStop",f,a(h));c.ui.dialog.overlay.resize()}})},_makeResizable:function(a){function b(f){return{originalPosition:f.originalPosition,originalSize:f.originalSize,
+position:f.position,size:f.size}}a=a===j?this.options.resizable:a;var d=this,e=d.options,g=d.uiDialog.css("position");a=typeof a==="string"?a:"n,e,s,w,se,sw,ne,nw";d.uiDialog.resizable({cancel:".ui-dialog-content",containment:"document",alsoResize:d.element,maxWidth:e.maxWidth,maxHeight:e.maxHeight,minWidth:e.minWidth,minHeight:d._minHeight(),handles:a,start:function(f,h){c(this).addClass("ui-dialog-resizing");d._trigger("resizeStart",f,b(h))},resize:function(f,h){d._trigger("resize",f,b(h))},stop:function(f,
+h){c(this).removeClass("ui-dialog-resizing");e.height=c(this).height();e.width=c(this).width();d._trigger("resizeStop",f,b(h));c.ui.dialog.overlay.resize()}}).css("position",g).find(".ui-resizable-se").addClass("ui-icon ui-icon-grip-diagonal-se")},_minHeight:function(){var a=this.options;return a.height==="auto"?a.minHeight:Math.min(a.minHeight,a.height)},_position:function(a){var b=[],d=[0,0],e;if(a){if(typeof a==="string"||typeof a==="object"&&"0"in a){b=a.split?a.split(" "):[a[0],a[1]];if(b.length===
+1)b[1]=b[0];c.each(["left","top"],function(g,f){if(+b[g]===b[g]){d[g]=b[g];b[g]=f}});a={my:b.join(" "),at:b.join(" "),offset:d.join(" ")}}a=c.extend({},c.ui.dialog.prototype.options.position,a)}else a=c.ui.dialog.prototype.options.position;(e=this.uiDialog.is(":visible"))||this.uiDialog.show();this.uiDialog.css({top:0,left:0}).position(c.extend({of:window},a));e||this.uiDialog.hide()},_setOptions:function(a){var b=this,d={},e=false;c.each(a,function(g,f){b._setOption(g,f);if(g in k)e=true;if(g in
+l)d[g]=f});e&&this._size();this.uiDialog.is(":data(resizable)")&&this.uiDialog.resizable("option",d)},_setOption:function(a,b){var d=this,e=d.uiDialog;switch(a){case "beforeclose":a="beforeClose";break;case "buttons":d._createButtons(b);break;case "closeText":d.uiDialogTitlebarCloseText.text(""+b);break;case "dialogClass":e.removeClass(d.options.dialogClass).addClass("ui-dialog ui-widget ui-widget-content ui-corner-all "+b);break;case "disabled":b?e.addClass("ui-dialog-disabled"):e.removeClass("ui-dialog-disabled");
+break;case "draggable":var g=e.is(":data(draggable)");g&&!b&&e.draggable("destroy");!g&&b&&d._makeDraggable();break;case "position":d._position(b);break;case "resizable":(g=e.is(":data(resizable)"))&&!b&&e.resizable("destroy");g&&typeof b==="string"&&e.resizable("option","handles",b);!g&&b!==false&&d._makeResizable(b);break;case "title":c(".ui-dialog-title",d.uiDialogTitlebar).html(""+(b||"&#160;"));break}c.Widget.prototype._setOption.apply(d,arguments)},_size:function(){var a=this.options,b,d,e=
+this.uiDialog.is(":visible");this.element.show().css({width:"auto",minHeight:0,height:0});if(a.minWidth>a.width)a.width=a.minWidth;b=this.uiDialog.css({height:"auto",width:a.width}).height();d=Math.max(0,a.minHeight-b);if(a.height==="auto")if(c.support.minHeight)this.element.css({minHeight:d,height:"auto"});else{this.uiDialog.show();a=this.element.css("height","auto").height();e||this.uiDialog.hide();this.element.height(Math.max(a,d))}else this.element.height(Math.max(a.height-b,0));this.uiDialog.is(":data(resizable)")&&
+this.uiDialog.resizable("option","minHeight",this._minHeight())}});c.extend(c.ui.dialog,{version:"1.8.10",uuid:0,maxZ:0,getTitleId:function(a){a=a.attr("id");if(!a){this.uuid+=1;a=this.uuid}return"ui-dialog-title-"+a},overlay:function(a){this.$el=c.ui.dialog.overlay.create(a)}});c.extend(c.ui.dialog.overlay,{instances:[],oldInstances:[],maxZ:0,events:c.map("focus,mousedown,mouseup,keydown,keypress,click".split(","),function(a){return a+".dialog-overlay"}).join(" "),create:function(a){if(this.instances.length===
+0){setTimeout(function(){c.ui.dialog.overlay.instances.length&&c(document).bind(c.ui.dialog.overlay.events,function(d){if(c(d.target).zIndex()<c.ui.dialog.overlay.maxZ)return false})},1);c(document).bind("keydown.dialog-overlay",function(d){if(a.options.closeOnEscape&&d.keyCode&&d.keyCode===c.ui.keyCode.ESCAPE){a.close(d);d.preventDefault()}});c(window).bind("resize.dialog-overlay",c.ui.dialog.overlay.resize)}var b=(this.oldInstances.pop()||c("<div></div>").addClass("ui-widget-overlay")).appendTo(document.body).css({width:this.width(),
+height:this.height()});c.fn.bgiframe&&b.bgiframe();this.instances.push(b);return b},destroy:function(a){var b=c.inArray(a,this.instances);b!=-1&&this.oldInstances.push(this.instances.splice(b,1)[0]);this.instances.length===0&&c([document,window]).unbind(".dialog-overlay");a.remove();var d=0;c.each(this.instances,function(){d=Math.max(d,this.css("z-index"))});this.maxZ=d},height:function(){var a,b;if(c.browser.msie&&c.browser.version<7){a=Math.max(document.documentElement.scrollHeight,document.body.scrollHeight);
+b=Math.max(document.documentElement.offsetHeight,document.body.offsetHeight);return a<b?c(window).height()+"px":a+"px"}else return c(document).height()+"px"},width:function(){var a,b;if(c.browser.msie&&c.browser.version<7){a=Math.max(document.documentElement.scrollWidth,document.body.scrollWidth);b=Math.max(document.documentElement.offsetWidth,document.body.offsetWidth);return a<b?c(window).width()+"px":a+"px"}else return c(document).width()+"px"},resize:function(){var a=c([]);c.each(c.ui.dialog.overlay.instances,
+function(){a=a.add(this)});a.css({width:0,height:0}).css({width:c.ui.dialog.overlay.width(),height:c.ui.dialog.overlay.height()})}});c.extend(c.ui.dialog.overlay.prototype,{destroy:function(){c.ui.dialog.overlay.destroy(this.$el)}})})(jQuery);
+;/*
+ * jQuery UI Slider 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Slider
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.mouse.js
+ * jquery.ui.widget.js
+ */
+(function(d){d.widget("ui.slider",d.ui.mouse,{widgetEventPrefix:"slide",options:{animate:false,distance:0,max:100,min:0,orientation:"horizontal",range:false,step:1,value:0,values:null},_create:function(){var b=this,a=this.options;this._mouseSliding=this._keySliding=false;this._animateOff=true;this._handleIndex=null;this._detectOrientation();this._mouseInit();this.element.addClass("ui-slider ui-slider-"+this.orientation+" ui-widget ui-widget-content ui-corner-all");a.disabled&&this.element.addClass("ui-slider-disabled ui-disabled");
+this.range=d([]);if(a.range){if(a.range===true){this.range=d("<div></div>");if(!a.values)a.values=[this._valueMin(),this._valueMin()];if(a.values.length&&a.values.length!==2)a.values=[a.values[0],a.values[0]]}else this.range=d("<div></div>");this.range.appendTo(this.element).addClass("ui-slider-range");if(a.range==="min"||a.range==="max")this.range.addClass("ui-slider-range-"+a.range);this.range.addClass("ui-widget-header")}d(".ui-slider-handle",this.element).length===0&&d("<a href='#'></a>").appendTo(this.element).addClass("ui-slider-handle");
+if(a.values&&a.values.length)for(;d(".ui-slider-handle",this.element).length<a.values.length;)d("<a href='#'></a>").appendTo(this.element).addClass("ui-slider-handle");this.handles=d(".ui-slider-handle",this.element).addClass("ui-state-default ui-corner-all");this.handle=this.handles.eq(0);this.handles.add(this.range).filter("a").click(function(c){c.preventDefault()}).hover(function(){a.disabled||d(this).addClass("ui-state-hover")},function(){d(this).removeClass("ui-state-hover")}).focus(function(){if(a.disabled)d(this).blur();
+else{d(".ui-slider .ui-state-focus").removeClass("ui-state-focus");d(this).addClass("ui-state-focus")}}).blur(function(){d(this).removeClass("ui-state-focus")});this.handles.each(function(c){d(this).data("index.ui-slider-handle",c)});this.handles.keydown(function(c){var e=true,f=d(this).data("index.ui-slider-handle"),h,g,i;if(!b.options.disabled){switch(c.keyCode){case d.ui.keyCode.HOME:case d.ui.keyCode.END:case d.ui.keyCode.PAGE_UP:case d.ui.keyCode.PAGE_DOWN:case d.ui.keyCode.UP:case d.ui.keyCode.RIGHT:case d.ui.keyCode.DOWN:case d.ui.keyCode.LEFT:e=
+false;if(!b._keySliding){b._keySliding=true;d(this).addClass("ui-state-active");h=b._start(c,f);if(h===false)return}break}i=b.options.step;h=b.options.values&&b.options.values.length?(g=b.values(f)):(g=b.value());switch(c.keyCode){case d.ui.keyCode.HOME:g=b._valueMin();break;case d.ui.keyCode.END:g=b._valueMax();break;case d.ui.keyCode.PAGE_UP:g=b._trimAlignValue(h+(b._valueMax()-b._valueMin())/5);break;case d.ui.keyCode.PAGE_DOWN:g=b._trimAlignValue(h-(b._valueMax()-b._valueMin())/5);break;case d.ui.keyCode.UP:case d.ui.keyCode.RIGHT:if(h===
+b._valueMax())return;g=b._trimAlignValue(h+i);break;case d.ui.keyCode.DOWN:case d.ui.keyCode.LEFT:if(h===b._valueMin())return;g=b._trimAlignValue(h-i);break}b._slide(c,f,g);return e}}).keyup(function(c){var e=d(this).data("index.ui-slider-handle");if(b._keySliding){b._keySliding=false;b._stop(c,e);b._change(c,e);d(this).removeClass("ui-state-active")}});this._refreshValue();this._animateOff=false},destroy:function(){this.handles.remove();this.range.remove();this.element.removeClass("ui-slider ui-slider-horizontal ui-slider-vertical ui-slider-disabled ui-widget ui-widget-content ui-corner-all").removeData("slider").unbind(".slider");
+this._mouseDestroy();return this},_mouseCapture:function(b){var a=this.options,c,e,f,h,g;if(a.disabled)return false;this.elementSize={width:this.element.outerWidth(),height:this.element.outerHeight()};this.elementOffset=this.element.offset();c=this._normValueFromMouse({x:b.pageX,y:b.pageY});e=this._valueMax()-this._valueMin()+1;h=this;this.handles.each(function(i){var j=Math.abs(c-h.values(i));if(e>j){e=j;f=d(this);g=i}});if(a.range===true&&this.values(1)===a.min){g+=1;f=d(this.handles[g])}if(this._start(b,
+g)===false)return false;this._mouseSliding=true;h._handleIndex=g;f.addClass("ui-state-active").focus();a=f.offset();this._clickOffset=!d(b.target).parents().andSelf().is(".ui-slider-handle")?{left:0,top:0}:{left:b.pageX-a.left-f.width()/2,top:b.pageY-a.top-f.height()/2-(parseInt(f.css("borderTopWidth"),10)||0)-(parseInt(f.css("borderBottomWidth"),10)||0)+(parseInt(f.css("marginTop"),10)||0)};this.handles.hasClass("ui-state-hover")||this._slide(b,g,c);return this._animateOff=true},_mouseStart:function(){return true},
+_mouseDrag:function(b){var a=this._normValueFromMouse({x:b.pageX,y:b.pageY});this._slide(b,this._handleIndex,a);return false},_mouseStop:function(b){this.handles.removeClass("ui-state-active");this._mouseSliding=false;this._stop(b,this._handleIndex);this._change(b,this._handleIndex);this._clickOffset=this._handleIndex=null;return this._animateOff=false},_detectOrientation:function(){this.orientation=this.options.orientation==="vertical"?"vertical":"horizontal"},_normValueFromMouse:function(b){var a;
+if(this.orientation==="horizontal"){a=this.elementSize.width;b=b.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)}else{a=this.elementSize.height;b=b.y-this.elementOffset.top-(this._clickOffset?this._clickOffset.top:0)}a=b/a;if(a>1)a=1;if(a<0)a=0;if(this.orientation==="vertical")a=1-a;b=this._valueMax()-this._valueMin();return this._trimAlignValue(this._valueMin()+a*b)},_start:function(b,a){var c={handle:this.handles[a],value:this.value()};if(this.options.values&&this.options.values.length){c.value=
+this.values(a);c.values=this.values()}return this._trigger("start",b,c)},_slide:function(b,a,c){var e;if(this.options.values&&this.options.values.length){e=this.values(a?0:1);if(this.options.values.length===2&&this.options.range===true&&(a===0&&c>e||a===1&&c<e))c=e;if(c!==this.values(a)){e=this.values();e[a]=c;b=this._trigger("slide",b,{handle:this.handles[a],value:c,values:e});this.values(a?0:1);b!==false&&this.values(a,c,true)}}else if(c!==this.value()){b=this._trigger("slide",b,{handle:this.handles[a],
+value:c});b!==false&&this.value(c)}},_stop:function(b,a){var c={handle:this.handles[a],value:this.value()};if(this.options.values&&this.options.values.length){c.value=this.values(a);c.values=this.values()}this._trigger("stop",b,c)},_change:function(b,a){if(!this._keySliding&&!this._mouseSliding){var c={handle:this.handles[a],value:this.value()};if(this.options.values&&this.options.values.length){c.value=this.values(a);c.values=this.values()}this._trigger("change",b,c)}},value:function(b){if(arguments.length){this.options.value=
+this._trimAlignValue(b);this._refreshValue();this._change(null,0)}return this._value()},values:function(b,a){var c,e,f;if(arguments.length>1){this.options.values[b]=this._trimAlignValue(a);this._refreshValue();this._change(null,b)}if(arguments.length)if(d.isArray(arguments[0])){c=this.options.values;e=arguments[0];for(f=0;f<c.length;f+=1){c[f]=this._trimAlignValue(e[f]);this._change(null,f)}this._refreshValue()}else return this.options.values&&this.options.values.length?this._values(b):this.value();
+else return this._values()},_setOption:function(b,a){var c,e=0;if(d.isArray(this.options.values))e=this.options.values.length;d.Widget.prototype._setOption.apply(this,arguments);switch(b){case "disabled":if(a){this.handles.filter(".ui-state-focus").blur();this.handles.removeClass("ui-state-hover");this.handles.attr("disabled","disabled");this.element.addClass("ui-disabled")}else{this.handles.removeAttr("disabled");this.element.removeClass("ui-disabled")}break;case "orientation":this._detectOrientation();
+this.element.removeClass("ui-slider-horizontal ui-slider-vertical").addClass("ui-slider-"+this.orientation);this._refreshValue();break;case "value":this._animateOff=true;this._refreshValue();this._change(null,0);this._animateOff=false;break;case "values":this._animateOff=true;this._refreshValue();for(c=0;c<e;c+=1)this._change(null,c);this._animateOff=false;break}},_value:function(){var b=this.options.value;return b=this._trimAlignValue(b)},_values:function(b){var a,c;if(arguments.length){a=this.options.values[b];
+return a=this._trimAlignValue(a)}else{a=this.options.values.slice();for(c=0;c<a.length;c+=1)a[c]=this._trimAlignValue(a[c]);return a}},_trimAlignValue:function(b){if(b<=this._valueMin())return this._valueMin();if(b>=this._valueMax())return this._valueMax();var a=this.options.step>0?this.options.step:1,c=(b-this._valueMin())%a;alignValue=b-c;if(Math.abs(c)*2>=a)alignValue+=c>0?a:-a;return parseFloat(alignValue.toFixed(5))},_valueMin:function(){return this.options.min},_valueMax:function(){return this.options.max},
+_refreshValue:function(){var b=this.options.range,a=this.options,c=this,e=!this._animateOff?a.animate:false,f,h={},g,i,j,l;if(this.options.values&&this.options.values.length)this.handles.each(function(k){f=(c.values(k)-c._valueMin())/(c._valueMax()-c._valueMin())*100;h[c.orientation==="horizontal"?"left":"bottom"]=f+"%";d(this).stop(1,1)[e?"animate":"css"](h,a.animate);if(c.options.range===true)if(c.orientation==="horizontal"){if(k===0)c.range.stop(1,1)[e?"animate":"css"]({left:f+"%"},a.animate);
+if(k===1)c.range[e?"animate":"css"]({width:f-g+"%"},{queue:false,duration:a.animate})}else{if(k===0)c.range.stop(1,1)[e?"animate":"css"]({bottom:f+"%"},a.animate);if(k===1)c.range[e?"animate":"css"]({height:f-g+"%"},{queue:false,duration:a.animate})}g=f});else{i=this.value();j=this._valueMin();l=this._valueMax();f=l!==j?(i-j)/(l-j)*100:0;h[c.orientation==="horizontal"?"left":"bottom"]=f+"%";this.handle.stop(1,1)[e?"animate":"css"](h,a.animate);if(b==="min"&&this.orientation==="horizontal")this.range.stop(1,
+1)[e?"animate":"css"]({width:f+"%"},a.animate);if(b==="max"&&this.orientation==="horizontal")this.range[e?"animate":"css"]({width:100-f+"%"},{queue:false,duration:a.animate});if(b==="min"&&this.orientation==="vertical")this.range.stop(1,1)[e?"animate":"css"]({height:f+"%"},a.animate);if(b==="max"&&this.orientation==="vertical")this.range[e?"animate":"css"]({height:100-f+"%"},{queue:false,duration:a.animate})}}});d.extend(d.ui.slider,{version:"1.8.10"})})(jQuery);
+;/*
+ * jQuery UI Tabs 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Tabs
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ */
+(function(d,p){function u(){return++v}function w(){return++x}var v=0,x=0;d.widget("ui.tabs",{options:{add:null,ajaxOptions:null,cache:false,cookie:null,collapsible:false,disable:null,disabled:[],enable:null,event:"click",fx:null,idPrefix:"ui-tabs-",load:null,panelTemplate:"<div></div>",remove:null,select:null,show:null,spinner:"<em>Loading&#8230;</em>",tabTemplate:"<li><a href='#{href}'><span>#{label}</span></a></li>"},_create:function(){this._tabify(true)},_setOption:function(b,e){if(b=="selected")this.options.collapsible&&
+e==this.options.selected||this.select(e);else{this.options[b]=e;this._tabify()}},_tabId:function(b){return b.title&&b.title.replace(/\s/g,"_").replace(/[^\w\u00c0-\uFFFF-]/g,"")||this.options.idPrefix+u()},_sanitizeSelector:function(b){return b.replace(/:/g,"\\:")},_cookie:function(){var b=this.cookie||(this.cookie=this.options.cookie.name||"ui-tabs-"+w());return d.cookie.apply(null,[b].concat(d.makeArray(arguments)))},_ui:function(b,e){return{tab:b,panel:e,index:this.anchors.index(b)}},_cleanup:function(){this.lis.filter(".ui-state-processing").removeClass("ui-state-processing").find("span:data(label.tabs)").each(function(){var b=
+d(this);b.html(b.data("label.tabs")).removeData("label.tabs")})},_tabify:function(b){function e(g,f){g.css("display","");!d.support.opacity&&f.opacity&&g[0].style.removeAttribute("filter")}var a=this,c=this.options,h=/^#.+/;this.list=this.element.find("ol,ul").eq(0);this.lis=d(" > li:has(a[href])",this.list);this.anchors=this.lis.map(function(){return d("a",this)[0]});this.panels=d([]);this.anchors.each(function(g,f){var i=d(f).attr("href"),l=i.split("#")[0],q;if(l&&(l===location.toString().split("#")[0]||
+(q=d("base")[0])&&l===q.href)){i=f.hash;f.href=i}if(h.test(i))a.panels=a.panels.add(a.element.find(a._sanitizeSelector(i)));else if(i&&i!=="#"){d.data(f,"href.tabs",i);d.data(f,"load.tabs",i.replace(/#.*$/,""));i=a._tabId(f);f.href="#"+i;f=a.element.find("#"+i);if(!f.length){f=d(c.panelTemplate).attr("id",i).addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").insertAfter(a.panels[g-1]||a.list);f.data("destroy.tabs",true)}a.panels=a.panels.add(f)}else c.disabled.push(g)});if(b){this.element.addClass("ui-tabs ui-widget ui-widget-content ui-corner-all");
+this.list.addClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all");this.lis.addClass("ui-state-default ui-corner-top");this.panels.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom");if(c.selected===p){location.hash&&this.anchors.each(function(g,f){if(f.hash==location.hash){c.selected=g;return false}});if(typeof c.selected!=="number"&&c.cookie)c.selected=parseInt(a._cookie(),10);if(typeof c.selected!=="number"&&this.lis.filter(".ui-tabs-selected").length)c.selected=
+this.lis.index(this.lis.filter(".ui-tabs-selected"));c.selected=c.selected||(this.lis.length?0:-1)}else if(c.selected===null)c.selected=-1;c.selected=c.selected>=0&&this.anchors[c.selected]||c.selected<0?c.selected:0;c.disabled=d.unique(c.disabled.concat(d.map(this.lis.filter(".ui-state-disabled"),function(g){return a.lis.index(g)}))).sort();d.inArray(c.selected,c.disabled)!=-1&&c.disabled.splice(d.inArray(c.selected,c.disabled),1);this.panels.addClass("ui-tabs-hide");this.lis.removeClass("ui-tabs-selected ui-state-active");
+if(c.selected>=0&&this.anchors.length){a.element.find(a._sanitizeSelector(a.anchors[c.selected].hash)).removeClass("ui-tabs-hide");this.lis.eq(c.selected).addClass("ui-tabs-selected ui-state-active");a.element.queue("tabs",function(){a._trigger("show",null,a._ui(a.anchors[c.selected],a.element.find(a._sanitizeSelector(a.anchors[c.selected].hash))[0]))});this.load(c.selected)}d(window).bind("unload",function(){a.lis.add(a.anchors).unbind(".tabs");a.lis=a.anchors=a.panels=null})}else c.selected=this.lis.index(this.lis.filter(".ui-tabs-selected"));
+this.element[c.collapsible?"addClass":"removeClass"]("ui-tabs-collapsible");c.cookie&&this._cookie(c.selected,c.cookie);b=0;for(var j;j=this.lis[b];b++)d(j)[d.inArray(b,c.disabled)!=-1&&!d(j).hasClass("ui-tabs-selected")?"addClass":"removeClass"]("ui-state-disabled");c.cache===false&&this.anchors.removeData("cache.tabs");this.lis.add(this.anchors).unbind(".tabs");if(c.event!=="mouseover"){var k=function(g,f){f.is(":not(.ui-state-disabled)")&&f.addClass("ui-state-"+g)},n=function(g,f){f.removeClass("ui-state-"+
+g)};this.lis.bind("mouseover.tabs",function(){k("hover",d(this))});this.lis.bind("mouseout.tabs",function(){n("hover",d(this))});this.anchors.bind("focus.tabs",function(){k("focus",d(this).closest("li"))});this.anchors.bind("blur.tabs",function(){n("focus",d(this).closest("li"))})}var m,o;if(c.fx)if(d.isArray(c.fx)){m=c.fx[0];o=c.fx[1]}else m=o=c.fx;var r=o?function(g,f){d(g).closest("li").addClass("ui-tabs-selected ui-state-active");f.hide().removeClass("ui-tabs-hide").animate(o,o.duration||"normal",
+function(){e(f,o);a._trigger("show",null,a._ui(g,f[0]))})}:function(g,f){d(g).closest("li").addClass("ui-tabs-selected ui-state-active");f.removeClass("ui-tabs-hide");a._trigger("show",null,a._ui(g,f[0]))},s=m?function(g,f){f.animate(m,m.duration||"normal",function(){a.lis.removeClass("ui-tabs-selected ui-state-active");f.addClass("ui-tabs-hide");e(f,m);a.element.dequeue("tabs")})}:function(g,f){a.lis.removeClass("ui-tabs-selected ui-state-active");f.addClass("ui-tabs-hide");a.element.dequeue("tabs")};
+this.anchors.bind(c.event+".tabs",function(){var g=this,f=d(g).closest("li"),i=a.panels.filter(":not(.ui-tabs-hide)"),l=a.element.find(a._sanitizeSelector(g.hash));if(f.hasClass("ui-tabs-selected")&&!c.collapsible||f.hasClass("ui-state-disabled")||f.hasClass("ui-state-processing")||a.panels.filter(":animated").length||a._trigger("select",null,a._ui(this,l[0]))===false){this.blur();return false}c.selected=a.anchors.index(this);a.abort();if(c.collapsible)if(f.hasClass("ui-tabs-selected")){c.selected=
+-1;c.cookie&&a._cookie(c.selected,c.cookie);a.element.queue("tabs",function(){s(g,i)}).dequeue("tabs");this.blur();return false}else if(!i.length){c.cookie&&a._cookie(c.selected,c.cookie);a.element.queue("tabs",function(){r(g,l)});a.load(a.anchors.index(this));this.blur();return false}c.cookie&&a._cookie(c.selected,c.cookie);if(l.length){i.length&&a.element.queue("tabs",function(){s(g,i)});a.element.queue("tabs",function(){r(g,l)});a.load(a.anchors.index(this))}else throw"jQuery UI Tabs: Mismatching fragment identifier.";
+d.browser.msie&&this.blur()});this.anchors.bind("click.tabs",function(){return false})},_getIndex:function(b){if(typeof b=="string")b=this.anchors.index(this.anchors.filter("[href$="+b+"]"));return b},destroy:function(){var b=this.options;this.abort();this.element.unbind(".tabs").removeClass("ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible").removeData("tabs");this.list.removeClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all");this.anchors.each(function(){var e=
+d.data(this,"href.tabs");if(e)this.href=e;var a=d(this).unbind(".tabs");d.each(["href","load","cache"],function(c,h){a.removeData(h+".tabs")})});this.lis.unbind(".tabs").add(this.panels).each(function(){d.data(this,"destroy.tabs")?d(this).remove():d(this).removeClass("ui-state-default ui-corner-top ui-tabs-selected ui-state-active ui-state-hover ui-state-focus ui-state-disabled ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide")});b.cookie&&this._cookie(null,b.cookie);return this},add:function(b,
+e,a){if(a===p)a=this.anchors.length;var c=this,h=this.options;e=d(h.tabTemplate.replace(/#\{href\}/g,b).replace(/#\{label\}/g,e));b=!b.indexOf("#")?b.replace("#",""):this._tabId(d("a",e)[0]);e.addClass("ui-state-default ui-corner-top").data("destroy.tabs",true);var j=c.element.find("#"+b);j.length||(j=d(h.panelTemplate).attr("id",b).data("destroy.tabs",true));j.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide");if(a>=this.lis.length){e.appendTo(this.list);j.appendTo(this.list[0].parentNode)}else{e.insertBefore(this.lis[a]);
+j.insertBefore(this.panels[a])}h.disabled=d.map(h.disabled,function(k){return k>=a?++k:k});this._tabify();if(this.anchors.length==1){h.selected=0;e.addClass("ui-tabs-selected ui-state-active");j.removeClass("ui-tabs-hide");this.element.queue("tabs",function(){c._trigger("show",null,c._ui(c.anchors[0],c.panels[0]))});this.load(0)}this._trigger("add",null,this._ui(this.anchors[a],this.panels[a]));return this},remove:function(b){b=this._getIndex(b);var e=this.options,a=this.lis.eq(b).remove(),c=this.panels.eq(b).remove();
+if(a.hasClass("ui-tabs-selected")&&this.anchors.length>1)this.select(b+(b+1<this.anchors.length?1:-1));e.disabled=d.map(d.grep(e.disabled,function(h){return h!=b}),function(h){return h>=b?--h:h});this._tabify();this._trigger("remove",null,this._ui(a.find("a")[0],c[0]));return this},enable:function(b){b=this._getIndex(b);var e=this.options;if(d.inArray(b,e.disabled)!=-1){this.lis.eq(b).removeClass("ui-state-disabled");e.disabled=d.grep(e.disabled,function(a){return a!=b});this._trigger("enable",null,
+this._ui(this.anchors[b],this.panels[b]));return this}},disable:function(b){b=this._getIndex(b);var e=this.options;if(b!=e.selected){this.lis.eq(b).addClass("ui-state-disabled");e.disabled.push(b);e.disabled.sort();this._trigger("disable",null,this._ui(this.anchors[b],this.panels[b]))}return this},select:function(b){b=this._getIndex(b);if(b==-1)if(this.options.collapsible&&this.options.selected!=-1)b=this.options.selected;else return this;this.anchors.eq(b).trigger(this.options.event+".tabs");return this},
+load:function(b){b=this._getIndex(b);var e=this,a=this.options,c=this.anchors.eq(b)[0],h=d.data(c,"load.tabs");this.abort();if(!h||this.element.queue("tabs").length!==0&&d.data(c,"cache.tabs"))this.element.dequeue("tabs");else{this.lis.eq(b).addClass("ui-state-processing");if(a.spinner){var j=d("span",c);j.data("label.tabs",j.html()).html(a.spinner)}this.xhr=d.ajax(d.extend({},a.ajaxOptions,{url:h,success:function(k,n){e.element.find(e._sanitizeSelector(c.hash)).html(k);e._cleanup();a.cache&&d.data(c,
+"cache.tabs",true);e._trigger("load",null,e._ui(e.anchors[b],e.panels[b]));try{a.ajaxOptions.success(k,n)}catch(m){}},error:function(k,n){e._cleanup();e._trigger("load",null,e._ui(e.anchors[b],e.panels[b]));try{a.ajaxOptions.error(k,n,b,c)}catch(m){}}}));e.element.dequeue("tabs");return this}},abort:function(){this.element.queue([]);this.panels.stop(false,true);this.element.queue("tabs",this.element.queue("tabs").splice(-2,2));if(this.xhr){this.xhr.abort();delete this.xhr}this._cleanup();return this},
+url:function(b,e){this.anchors.eq(b).removeData("cache.tabs").data("load.tabs",e);return this},length:function(){return this.anchors.length}});d.extend(d.ui.tabs,{version:"1.8.10"});d.extend(d.ui.tabs.prototype,{rotation:null,rotate:function(b,e){var a=this,c=this.options,h=a._rotate||(a._rotate=function(j){clearTimeout(a.rotation);a.rotation=setTimeout(function(){var k=c.selected;a.select(++k<a.anchors.length?k:0)},b);j&&j.stopPropagation()});e=a._unrotate||(a._unrotate=!e?function(j){j.clientX&&
+a.rotate(null)}:function(){t=c.selected;h()});if(b){this.element.bind("tabsshow",h);this.anchors.bind(c.event+".tabs",e);h()}else{clearTimeout(a.rotation);this.element.unbind("tabsshow",h);this.anchors.unbind(c.event+".tabs",e);delete this._rotate;delete this._unrotate}return this}})})(jQuery);
+;/*
+ * jQuery UI Datepicker 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Datepicker
+ *
+ * Depends:
+ * jquery.ui.core.js
+ */
+(function(d,G){function K(){this.debug=false;this._curInst=null;this._keyEvent=false;this._disabledInputs=[];this._inDialog=this._datepickerShowing=false;this._mainDivId="ui-datepicker-div";this._inlineClass="ui-datepicker-inline";this._appendClass="ui-datepicker-append";this._triggerClass="ui-datepicker-trigger";this._dialogClass="ui-datepicker-dialog";this._disableClass="ui-datepicker-disabled";this._unselectableClass="ui-datepicker-unselectable";this._currentClass="ui-datepicker-current-day";this._dayOverClass=
+"ui-datepicker-days-cell-over";this.regional=[];this.regional[""]={closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su",
+"Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"mm/dd/yy",firstDay:0,isRTL:false,showMonthAfterYear:false,yearSuffix:""};this._defaults={showOn:"focus",showAnim:"fadeIn",showOptions:{},defaultDate:null,appendText:"",buttonText:"...",buttonImage:"",buttonImageOnly:false,hideIfNoPrevNext:false,navigationAsDateFormat:false,gotoCurrent:false,changeMonth:false,changeYear:false,yearRange:"c-10:c+10",showOtherMonths:false,selectOtherMonths:false,showWeek:false,calculateWeek:this.iso8601Week,shortYearCutoff:"+10",
+minDate:null,maxDate:null,duration:"fast",beforeShowDay:null,beforeShow:null,onSelect:null,onChangeMonthYear:null,onClose:null,numberOfMonths:1,showCurrentAtPos:0,stepMonths:1,stepBigMonths:12,altField:"",altFormat:"",constrainInput:true,showButtonPanel:false,autoSize:false};d.extend(this._defaults,this.regional[""]);this.dpDiv=d('<div id="'+this._mainDivId+'" class="ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>')}function E(a,b){d.extend(a,b);for(var c in b)if(b[c]==
+null||b[c]==G)a[c]=b[c];return a}d.extend(d.ui,{datepicker:{version:"1.8.10"}});var y=(new Date).getTime();d.extend(K.prototype,{markerClassName:"hasDatepicker",log:function(){this.debug&&console.log.apply("",arguments)},_widgetDatepicker:function(){return this.dpDiv},setDefaults:function(a){E(this._defaults,a||{});return this},_attachDatepicker:function(a,b){var c=null;for(var e in this._defaults){var f=a.getAttribute("date:"+e);if(f){c=c||{};try{c[e]=eval(f)}catch(h){c[e]=f}}}e=a.nodeName.toLowerCase();
+f=e=="div"||e=="span";if(!a.id){this.uuid+=1;a.id="dp"+this.uuid}var i=this._newInst(d(a),f);i.settings=d.extend({},b||{},c||{});if(e=="input")this._connectDatepicker(a,i);else f&&this._inlineDatepicker(a,i)},_newInst:function(a,b){return{id:a[0].id.replace(/([^A-Za-z0-9_-])/g,"\\\\$1"),input:a,selectedDay:0,selectedMonth:0,selectedYear:0,drawMonth:0,drawYear:0,inline:b,dpDiv:!b?this.dpDiv:d('<div class="'+this._inlineClass+' ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>')}},
+_connectDatepicker:function(a,b){var c=d(a);b.append=d([]);b.trigger=d([]);if(!c.hasClass(this.markerClassName)){this._attachments(c,b);c.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).keyup(this._doKeyUp).bind("setData.datepicker",function(e,f,h){b.settings[f]=h}).bind("getData.datepicker",function(e,f){return this._get(b,f)});this._autoSize(b);d.data(a,"datepicker",b)}},_attachments:function(a,b){var c=this._get(b,"appendText"),e=this._get(b,"isRTL");b.append&&
+b.append.remove();if(c){b.append=d('<span class="'+this._appendClass+'">'+c+"</span>");a[e?"before":"after"](b.append)}a.unbind("focus",this._showDatepicker);b.trigger&&b.trigger.remove();c=this._get(b,"showOn");if(c=="focus"||c=="both")a.focus(this._showDatepicker);if(c=="button"||c=="both"){c=this._get(b,"buttonText");var f=this._get(b,"buttonImage");b.trigger=d(this._get(b,"buttonImageOnly")?d("<img/>").addClass(this._triggerClass).attr({src:f,alt:c,title:c}):d('<button type="button"></button>').addClass(this._triggerClass).html(f==
+""?c:d("<img/>").attr({src:f,alt:c,title:c})));a[e?"before":"after"](b.trigger);b.trigger.click(function(){d.datepicker._datepickerShowing&&d.datepicker._lastInput==a[0]?d.datepicker._hideDatepicker():d.datepicker._showDatepicker(a[0]);return false})}},_autoSize:function(a){if(this._get(a,"autoSize")&&!a.inline){var b=new Date(2009,11,20),c=this._get(a,"dateFormat");if(c.match(/[DM]/)){var e=function(f){for(var h=0,i=0,g=0;g<f.length;g++)if(f[g].length>h){h=f[g].length;i=g}return i};b.setMonth(e(this._get(a,
+c.match(/MM/)?"monthNames":"monthNamesShort")));b.setDate(e(this._get(a,c.match(/DD/)?"dayNames":"dayNamesShort"))+20-b.getDay())}a.input.attr("size",this._formatDate(a,b).length)}},_inlineDatepicker:function(a,b){var c=d(a);if(!c.hasClass(this.markerClassName)){c.addClass(this.markerClassName).append(b.dpDiv).bind("setData.datepicker",function(e,f,h){b.settings[f]=h}).bind("getData.datepicker",function(e,f){return this._get(b,f)});d.data(a,"datepicker",b);this._setDate(b,this._getDefaultDate(b),
+true);this._updateDatepicker(b);this._updateAlternate(b);b.dpDiv.show()}},_dialogDatepicker:function(a,b,c,e,f){a=this._dialogInst;if(!a){this.uuid+=1;this._dialogInput=d('<input type="text" id="'+("dp"+this.uuid)+'" style="position: absolute; top: -100px; width: 0px; z-index: -10;"/>');this._dialogInput.keydown(this._doKeyDown);d("body").append(this._dialogInput);a=this._dialogInst=this._newInst(this._dialogInput,false);a.settings={};d.data(this._dialogInput[0],"datepicker",a)}E(a.settings,e||{});
+b=b&&b.constructor==Date?this._formatDate(a,b):b;this._dialogInput.val(b);this._pos=f?f.length?f:[f.pageX,f.pageY]:null;if(!this._pos)this._pos=[document.documentElement.clientWidth/2-100+(document.documentElement.scrollLeft||document.body.scrollLeft),document.documentElement.clientHeight/2-150+(document.documentElement.scrollTop||document.body.scrollTop)];this._dialogInput.css("left",this._pos[0]+20+"px").css("top",this._pos[1]+"px");a.settings.onSelect=c;this._inDialog=true;this.dpDiv.addClass(this._dialogClass);
+this._showDatepicker(this._dialogInput[0]);d.blockUI&&d.blockUI(this.dpDiv);d.data(this._dialogInput[0],"datepicker",a);return this},_destroyDatepicker:function(a){var b=d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();d.removeData(a,"datepicker");if(e=="input"){c.append.remove();c.trigger.remove();b.removeClass(this.markerClassName).unbind("focus",this._showDatepicker).unbind("keydown",this._doKeyDown).unbind("keypress",this._doKeyPress).unbind("keyup",
+this._doKeyUp)}else if(e=="div"||e=="span")b.removeClass(this.markerClassName).empty()}},_enableDatepicker:function(a){var b=d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();if(e=="input"){a.disabled=false;c.trigger.filter("button").each(function(){this.disabled=false}).end().filter("img").css({opacity:"1.0",cursor:""})}else if(e=="div"||e=="span")b.children("."+this._inlineClass).children().removeClass("ui-state-disabled");this._disabledInputs=d.map(this._disabledInputs,
+function(f){return f==a?null:f})}},_disableDatepicker:function(a){var b=d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();if(e=="input"){a.disabled=true;c.trigger.filter("button").each(function(){this.disabled=true}).end().filter("img").css({opacity:"0.5",cursor:"default"})}else if(e=="div"||e=="span")b.children("."+this._inlineClass).children().addClass("ui-state-disabled");this._disabledInputs=d.map(this._disabledInputs,function(f){return f==a?null:
+f});this._disabledInputs[this._disabledInputs.length]=a}},_isDisabledDatepicker:function(a){if(!a)return false;for(var b=0;b<this._disabledInputs.length;b++)if(this._disabledInputs[b]==a)return true;return false},_getInst:function(a){try{return d.data(a,"datepicker")}catch(b){throw"Missing instance data for this datepicker";}},_optionDatepicker:function(a,b,c){var e=this._getInst(a);if(arguments.length==2&&typeof b=="string")return b=="defaults"?d.extend({},d.datepicker._defaults):e?b=="all"?d.extend({},
+e.settings):this._get(e,b):null;var f=b||{};if(typeof b=="string"){f={};f[b]=c}if(e){this._curInst==e&&this._hideDatepicker();var h=this._getDateDatepicker(a,true);E(e.settings,f);this._attachments(d(a),e);this._autoSize(e);this._setDateDatepicker(a,h);this._updateDatepicker(e)}},_changeDatepicker:function(a,b,c){this._optionDatepicker(a,b,c)},_refreshDatepicker:function(a){(a=this._getInst(a))&&this._updateDatepicker(a)},_setDateDatepicker:function(a,b){if(a=this._getInst(a)){this._setDate(a,b);
+this._updateDatepicker(a);this._updateAlternate(a)}},_getDateDatepicker:function(a,b){(a=this._getInst(a))&&!a.inline&&this._setDateFromField(a,b);return a?this._getDate(a):null},_doKeyDown:function(a){var b=d.datepicker._getInst(a.target),c=true,e=b.dpDiv.is(".ui-datepicker-rtl");b._keyEvent=true;if(d.datepicker._datepickerShowing)switch(a.keyCode){case 9:d.datepicker._hideDatepicker();c=false;break;case 13:c=d("td."+d.datepicker._dayOverClass+":not(."+d.datepicker._currentClass+")",b.dpDiv);c[0]?
+d.datepicker._selectDay(a.target,b.selectedMonth,b.selectedYear,c[0]):d.datepicker._hideDatepicker();return false;case 27:d.datepicker._hideDatepicker();break;case 33:d.datepicker._adjustDate(a.target,a.ctrlKey?-d.datepicker._get(b,"stepBigMonths"):-d.datepicker._get(b,"stepMonths"),"M");break;case 34:d.datepicker._adjustDate(a.target,a.ctrlKey?+d.datepicker._get(b,"stepBigMonths"):+d.datepicker._get(b,"stepMonths"),"M");break;case 35:if(a.ctrlKey||a.metaKey)d.datepicker._clearDate(a.target);c=a.ctrlKey||
+a.metaKey;break;case 36:if(a.ctrlKey||a.metaKey)d.datepicker._gotoToday(a.target);c=a.ctrlKey||a.metaKey;break;case 37:if(a.ctrlKey||a.metaKey)d.datepicker._adjustDate(a.target,e?+1:-1,"D");c=a.ctrlKey||a.metaKey;if(a.originalEvent.altKey)d.datepicker._adjustDate(a.target,a.ctrlKey?-d.datepicker._get(b,"stepBigMonths"):-d.datepicker._get(b,"stepMonths"),"M");break;case 38:if(a.ctrlKey||a.metaKey)d.datepicker._adjustDate(a.target,-7,"D");c=a.ctrlKey||a.metaKey;break;case 39:if(a.ctrlKey||a.metaKey)d.datepicker._adjustDate(a.target,
+e?-1:+1,"D");c=a.ctrlKey||a.metaKey;if(a.originalEvent.altKey)d.datepicker._adjustDate(a.target,a.ctrlKey?+d.datepicker._get(b,"stepBigMonths"):+d.datepicker._get(b,"stepMonths"),"M");break;case 40:if(a.ctrlKey||a.metaKey)d.datepicker._adjustDate(a.target,+7,"D");c=a.ctrlKey||a.metaKey;break;default:c=false}else if(a.keyCode==36&&a.ctrlKey)d.datepicker._showDatepicker(this);else c=false;if(c){a.preventDefault();a.stopPropagation()}},_doKeyPress:function(a){var b=d.datepicker._getInst(a.target);if(d.datepicker._get(b,
+"constrainInput")){b=d.datepicker._possibleChars(d.datepicker._get(b,"dateFormat"));var c=String.fromCharCode(a.charCode==G?a.keyCode:a.charCode);return a.ctrlKey||a.metaKey||c<" "||!b||b.indexOf(c)>-1}},_doKeyUp:function(a){a=d.datepicker._getInst(a.target);if(a.input.val()!=a.lastVal)try{if(d.datepicker.parseDate(d.datepicker._get(a,"dateFormat"),a.input?a.input.val():null,d.datepicker._getFormatConfig(a))){d.datepicker._setDateFromField(a);d.datepicker._updateAlternate(a);d.datepicker._updateDatepicker(a)}}catch(b){d.datepicker.log(b)}return true},
+_showDatepicker:function(a){a=a.target||a;if(a.nodeName.toLowerCase()!="input")a=d("input",a.parentNode)[0];if(!(d.datepicker._isDisabledDatepicker(a)||d.datepicker._lastInput==a)){var b=d.datepicker._getInst(a);d.datepicker._curInst&&d.datepicker._curInst!=b&&d.datepicker._curInst.dpDiv.stop(true,true);var c=d.datepicker._get(b,"beforeShow");E(b.settings,c?c.apply(a,[a,b]):{});b.lastVal=null;d.datepicker._lastInput=a;d.datepicker._setDateFromField(b);if(d.datepicker._inDialog)a.value="";if(!d.datepicker._pos){d.datepicker._pos=
+d.datepicker._findPos(a);d.datepicker._pos[1]+=a.offsetHeight}var e=false;d(a).parents().each(function(){e|=d(this).css("position")=="fixed";return!e});if(e&&d.browser.opera){d.datepicker._pos[0]-=document.documentElement.scrollLeft;d.datepicker._pos[1]-=document.documentElement.scrollTop}c={left:d.datepicker._pos[0],top:d.datepicker._pos[1]};d.datepicker._pos=null;b.dpDiv.empty();b.dpDiv.css({position:"absolute",display:"block",top:"-1000px"});d.datepicker._updateDatepicker(b);c=d.datepicker._checkOffset(b,
+c,e);b.dpDiv.css({position:d.datepicker._inDialog&&d.blockUI?"static":e?"fixed":"absolute",display:"none",left:c.left+"px",top:c.top+"px"});if(!b.inline){c=d.datepicker._get(b,"showAnim");var f=d.datepicker._get(b,"duration"),h=function(){d.datepicker._datepickerShowing=true;var i=b.dpDiv.find("iframe.ui-datepicker-cover");if(i.length){var g=d.datepicker._getBorders(b.dpDiv);i.css({left:-g[0],top:-g[1],width:b.dpDiv.outerWidth(),height:b.dpDiv.outerHeight()})}};b.dpDiv.zIndex(d(a).zIndex()+1);d.effects&&
+d.effects[c]?b.dpDiv.show(c,d.datepicker._get(b,"showOptions"),f,h):b.dpDiv[c||"show"](c?f:null,h);if(!c||!f)h();b.input.is(":visible")&&!b.input.is(":disabled")&&b.input.focus();d.datepicker._curInst=b}}},_updateDatepicker:function(a){var b=this,c=d.datepicker._getBorders(a.dpDiv);a.dpDiv.empty().append(this._generateHTML(a));var e=a.dpDiv.find("iframe.ui-datepicker-cover");e.length&&e.css({left:-c[0],top:-c[1],width:a.dpDiv.outerWidth(),height:a.dpDiv.outerHeight()});a.dpDiv.find("button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a").bind("mouseout",
+function(){d(this).removeClass("ui-state-hover");this.className.indexOf("ui-datepicker-prev")!=-1&&d(this).removeClass("ui-datepicker-prev-hover");this.className.indexOf("ui-datepicker-next")!=-1&&d(this).removeClass("ui-datepicker-next-hover")}).bind("mouseover",function(){if(!b._isDisabledDatepicker(a.inline?a.dpDiv.parent()[0]:a.input[0])){d(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover");d(this).addClass("ui-state-hover");this.className.indexOf("ui-datepicker-prev")!=
+-1&&d(this).addClass("ui-datepicker-prev-hover");this.className.indexOf("ui-datepicker-next")!=-1&&d(this).addClass("ui-datepicker-next-hover")}}).end().find("."+this._dayOverClass+" a").trigger("mouseover").end();c=this._getNumberOfMonths(a);e=c[1];e>1?a.dpDiv.addClass("ui-datepicker-multi-"+e).css("width",17*e+"em"):a.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width("");a.dpDiv[(c[0]!=1||c[1]!=1?"add":"remove")+"Class"]("ui-datepicker-multi");a.dpDiv[(this._get(a,
+"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl");a==d.datepicker._curInst&&d.datepicker._datepickerShowing&&a.input&&a.input.is(":visible")&&!a.input.is(":disabled")&&a.input[0]!=document.activeElement&&a.input.focus();if(a.yearshtml){var f=a.yearshtml;setTimeout(function(){f===a.yearshtml&&a.dpDiv.find("select.ui-datepicker-year:first").replaceWith(a.yearshtml);f=a.yearshtml=null},0)}},_getBorders:function(a){var b=function(c){return{thin:1,medium:2,thick:3}[c]||c};return[parseFloat(b(a.css("border-left-width"))),
+parseFloat(b(a.css("border-top-width")))]},_checkOffset:function(a,b,c){var e=a.dpDiv.outerWidth(),f=a.dpDiv.outerHeight(),h=a.input?a.input.outerWidth():0,i=a.input?a.input.outerHeight():0,g=document.documentElement.clientWidth+d(document).scrollLeft(),j=document.documentElement.clientHeight+d(document).scrollTop();b.left-=this._get(a,"isRTL")?e-h:0;b.left-=c&&b.left==a.input.offset().left?d(document).scrollLeft():0;b.top-=c&&b.top==a.input.offset().top+i?d(document).scrollTop():0;b.left-=Math.min(b.left,
+b.left+e>g&&g>e?Math.abs(b.left+e-g):0);b.top-=Math.min(b.top,b.top+f>j&&j>f?Math.abs(f+i):0);return b},_findPos:function(a){for(var b=this._get(this._getInst(a),"isRTL");a&&(a.type=="hidden"||a.nodeType!=1||d.expr.filters.hidden(a));)a=a[b?"previousSibling":"nextSibling"];a=d(a).offset();return[a.left,a.top]},_hideDatepicker:function(a){var b=this._curInst;if(!(!b||a&&b!=d.data(a,"datepicker")))if(this._datepickerShowing){a=this._get(b,"showAnim");var c=this._get(b,"duration"),e=function(){d.datepicker._tidyDialog(b);
+this._curInst=null};d.effects&&d.effects[a]?b.dpDiv.hide(a,d.datepicker._get(b,"showOptions"),c,e):b.dpDiv[a=="slideDown"?"slideUp":a=="fadeIn"?"fadeOut":"hide"](a?c:null,e);a||e();if(a=this._get(b,"onClose"))a.apply(b.input?b.input[0]:null,[b.input?b.input.val():"",b]);this._datepickerShowing=false;this._lastInput=null;if(this._inDialog){this._dialogInput.css({position:"absolute",left:"0",top:"-100px"});if(d.blockUI){d.unblockUI();d("body").append(this.dpDiv)}}this._inDialog=false}},_tidyDialog:function(a){a.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar")},
+_checkExternalClick:function(a){if(d.datepicker._curInst){a=d(a.target);a[0].id!=d.datepicker._mainDivId&&a.parents("#"+d.datepicker._mainDivId).length==0&&!a.hasClass(d.datepicker.markerClassName)&&!a.hasClass(d.datepicker._triggerClass)&&d.datepicker._datepickerShowing&&!(d.datepicker._inDialog&&d.blockUI)&&d.datepicker._hideDatepicker()}},_adjustDate:function(a,b,c){a=d(a);var e=this._getInst(a[0]);if(!this._isDisabledDatepicker(a[0])){this._adjustInstDate(e,b+(c=="M"?this._get(e,"showCurrentAtPos"):
+0),c);this._updateDatepicker(e)}},_gotoToday:function(a){a=d(a);var b=this._getInst(a[0]);if(this._get(b,"gotoCurrent")&&b.currentDay){b.selectedDay=b.currentDay;b.drawMonth=b.selectedMonth=b.currentMonth;b.drawYear=b.selectedYear=b.currentYear}else{var c=new Date;b.selectedDay=c.getDate();b.drawMonth=b.selectedMonth=c.getMonth();b.drawYear=b.selectedYear=c.getFullYear()}this._notifyChange(b);this._adjustDate(a)},_selectMonthYear:function(a,b,c){a=d(a);var e=this._getInst(a[0]);e._selectingMonthYear=
+false;e["selected"+(c=="M"?"Month":"Year")]=e["draw"+(c=="M"?"Month":"Year")]=parseInt(b.options[b.selectedIndex].value,10);this._notifyChange(e);this._adjustDate(a)},_clickMonthYear:function(a){var b=this._getInst(d(a)[0]);b.input&&b._selectingMonthYear&&setTimeout(function(){b.input.focus()},0);b._selectingMonthYear=!b._selectingMonthYear},_selectDay:function(a,b,c,e){var f=d(a);if(!(d(e).hasClass(this._unselectableClass)||this._isDisabledDatepicker(f[0]))){f=this._getInst(f[0]);f.selectedDay=f.currentDay=
+d("a",e).html();f.selectedMonth=f.currentMonth=b;f.selectedYear=f.currentYear=c;this._selectDate(a,this._formatDate(f,f.currentDay,f.currentMonth,f.currentYear))}},_clearDate:function(a){a=d(a);this._getInst(a[0]);this._selectDate(a,"")},_selectDate:function(a,b){a=this._getInst(d(a)[0]);b=b!=null?b:this._formatDate(a);a.input&&a.input.val(b);this._updateAlternate(a);var c=this._get(a,"onSelect");if(c)c.apply(a.input?a.input[0]:null,[b,a]);else a.input&&a.input.trigger("change");if(a.inline)this._updateDatepicker(a);
+else{this._hideDatepicker();this._lastInput=a.input[0];typeof a.input[0]!="object"&&a.input.focus();this._lastInput=null}},_updateAlternate:function(a){var b=this._get(a,"altField");if(b){var c=this._get(a,"altFormat")||this._get(a,"dateFormat"),e=this._getDate(a),f=this.formatDate(c,e,this._getFormatConfig(a));d(b).each(function(){d(this).val(f)})}},noWeekends:function(a){a=a.getDay();return[a>0&&a<6,""]},iso8601Week:function(a){a=new Date(a.getTime());a.setDate(a.getDate()+4-(a.getDay()||7));var b=
+a.getTime();a.setMonth(0);a.setDate(1);return Math.floor(Math.round((b-a)/864E5)/7)+1},parseDate:function(a,b,c){if(a==null||b==null)throw"Invalid arguments";b=typeof b=="object"?b.toString():b+"";if(b=="")return null;var e=(c?c.shortYearCutoff:null)||this._defaults.shortYearCutoff;e=typeof e!="string"?e:(new Date).getFullYear()%100+parseInt(e,10);for(var f=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,h=(c?c.dayNames:null)||this._defaults.dayNames,i=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort,
+g=(c?c.monthNames:null)||this._defaults.monthNames,j=c=-1,l=-1,u=-1,k=false,o=function(p){(p=z+1<a.length&&a.charAt(z+1)==p)&&z++;return p},m=function(p){var v=o(p);p=new RegExp("^\\d{1,"+(p=="@"?14:p=="!"?20:p=="y"&&v?4:p=="o"?3:2)+"}");p=b.substring(s).match(p);if(!p)throw"Missing number at position "+s;s+=p[0].length;return parseInt(p[0],10)},n=function(p,v,H){p=o(p)?H:v;for(v=0;v<p.length;v++)if(b.substr(s,p[v].length).toLowerCase()==p[v].toLowerCase()){s+=p[v].length;return v+1}throw"Unknown name at position "+
+s;},r=function(){if(b.charAt(s)!=a.charAt(z))throw"Unexpected literal at position "+s;s++},s=0,z=0;z<a.length;z++)if(k)if(a.charAt(z)=="'"&&!o("'"))k=false;else r();else switch(a.charAt(z)){case "d":l=m("d");break;case "D":n("D",f,h);break;case "o":u=m("o");break;case "m":j=m("m");break;case "M":j=n("M",i,g);break;case "y":c=m("y");break;case "@":var w=new Date(m("@"));c=w.getFullYear();j=w.getMonth()+1;l=w.getDate();break;case "!":w=new Date((m("!")-this._ticksTo1970)/1E4);c=w.getFullYear();j=w.getMonth()+
+1;l=w.getDate();break;case "'":if(o("'"))r();else k=true;break;default:r()}if(c==-1)c=(new Date).getFullYear();else if(c<100)c+=(new Date).getFullYear()-(new Date).getFullYear()%100+(c<=e?0:-100);if(u>-1){j=1;l=u;do{e=this._getDaysInMonth(c,j-1);if(l<=e)break;j++;l-=e}while(1)}w=this._daylightSavingAdjust(new Date(c,j-1,l));if(w.getFullYear()!=c||w.getMonth()+1!=j||w.getDate()!=l)throw"Invalid date";return w},ATOM:"yy-mm-dd",COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y",
+RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y",TICKS:"!",TIMESTAMP:"@",W3C:"yy-mm-dd",_ticksTo1970:(718685+Math.floor(492.5)-Math.floor(19.7)+Math.floor(4.925))*24*60*60*1E7,formatDate:function(a,b,c){if(!b)return"";var e=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,f=(c?c.dayNames:null)||this._defaults.dayNames,h=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort;c=(c?c.monthNames:null)||this._defaults.monthNames;var i=function(o){(o=k+1<a.length&&
+a.charAt(k+1)==o)&&k++;return o},g=function(o,m,n){m=""+m;if(i(o))for(;m.length<n;)m="0"+m;return m},j=function(o,m,n,r){return i(o)?r[m]:n[m]},l="",u=false;if(b)for(var k=0;k<a.length;k++)if(u)if(a.charAt(k)=="'"&&!i("'"))u=false;else l+=a.charAt(k);else switch(a.charAt(k)){case "d":l+=g("d",b.getDate(),2);break;case "D":l+=j("D",b.getDay(),e,f);break;case "o":l+=g("o",(b.getTime()-(new Date(b.getFullYear(),0,0)).getTime())/864E5,3);break;case "m":l+=g("m",b.getMonth()+1,2);break;case "M":l+=j("M",
+b.getMonth(),h,c);break;case "y":l+=i("y")?b.getFullYear():(b.getYear()%100<10?"0":"")+b.getYear()%100;break;case "@":l+=b.getTime();break;case "!":l+=b.getTime()*1E4+this._ticksTo1970;break;case "'":if(i("'"))l+="'";else u=true;break;default:l+=a.charAt(k)}return l},_possibleChars:function(a){for(var b="",c=false,e=function(h){(h=f+1<a.length&&a.charAt(f+1)==h)&&f++;return h},f=0;f<a.length;f++)if(c)if(a.charAt(f)=="'"&&!e("'"))c=false;else b+=a.charAt(f);else switch(a.charAt(f)){case "d":case "m":case "y":case "@":b+=
+"0123456789";break;case "D":case "M":return null;case "'":if(e("'"))b+="'";else c=true;break;default:b+=a.charAt(f)}return b},_get:function(a,b){return a.settings[b]!==G?a.settings[b]:this._defaults[b]},_setDateFromField:function(a,b){if(a.input.val()!=a.lastVal){var c=this._get(a,"dateFormat"),e=a.lastVal=a.input?a.input.val():null,f,h;f=h=this._getDefaultDate(a);var i=this._getFormatConfig(a);try{f=this.parseDate(c,e,i)||h}catch(g){this.log(g);e=b?"":e}a.selectedDay=f.getDate();a.drawMonth=a.selectedMonth=
+f.getMonth();a.drawYear=a.selectedYear=f.getFullYear();a.currentDay=e?f.getDate():0;a.currentMonth=e?f.getMonth():0;a.currentYear=e?f.getFullYear():0;this._adjustInstDate(a)}},_getDefaultDate:function(a){return this._restrictMinMax(a,this._determineDate(a,this._get(a,"defaultDate"),new Date))},_determineDate:function(a,b,c){var e=function(h){var i=new Date;i.setDate(i.getDate()+h);return i},f=function(h){try{return d.datepicker.parseDate(d.datepicker._get(a,"dateFormat"),h,d.datepicker._getFormatConfig(a))}catch(i){}var g=
+(h.toLowerCase().match(/^c/)?d.datepicker._getDate(a):null)||new Date,j=g.getFullYear(),l=g.getMonth();g=g.getDate();for(var u=/([+-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,k=u.exec(h);k;){switch(k[2]||"d"){case "d":case "D":g+=parseInt(k[1],10);break;case "w":case "W":g+=parseInt(k[1],10)*7;break;case "m":case "M":l+=parseInt(k[1],10);g=Math.min(g,d.datepicker._getDaysInMonth(j,l));break;case "y":case "Y":j+=parseInt(k[1],10);g=Math.min(g,d.datepicker._getDaysInMonth(j,l));break}k=u.exec(h)}return new Date(j,
+l,g)};if(b=(b=b==null||b===""?c:typeof b=="string"?f(b):typeof b=="number"?isNaN(b)?c:e(b):new Date(b.getTime()))&&b.toString()=="Invalid Date"?c:b){b.setHours(0);b.setMinutes(0);b.setSeconds(0);b.setMilliseconds(0)}return this._daylightSavingAdjust(b)},_daylightSavingAdjust:function(a){if(!a)return null;a.setHours(a.getHours()>12?a.getHours()+2:0);return a},_setDate:function(a,b,c){var e=!b,f=a.selectedMonth,h=a.selectedYear;b=this._restrictMinMax(a,this._determineDate(a,b,new Date));a.selectedDay=
+a.currentDay=b.getDate();a.drawMonth=a.selectedMonth=a.currentMonth=b.getMonth();a.drawYear=a.selectedYear=a.currentYear=b.getFullYear();if((f!=a.selectedMonth||h!=a.selectedYear)&&!c)this._notifyChange(a);this._adjustInstDate(a);if(a.input)a.input.val(e?"":this._formatDate(a))},_getDate:function(a){return!a.currentYear||a.input&&a.input.val()==""?null:this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay))},_generateHTML:function(a){var b=new Date;b=this._daylightSavingAdjust(new Date(b.getFullYear(),
+b.getMonth(),b.getDate()));var c=this._get(a,"isRTL"),e=this._get(a,"showButtonPanel"),f=this._get(a,"hideIfNoPrevNext"),h=this._get(a,"navigationAsDateFormat"),i=this._getNumberOfMonths(a),g=this._get(a,"showCurrentAtPos"),j=this._get(a,"stepMonths"),l=i[0]!=1||i[1]!=1,u=this._daylightSavingAdjust(!a.currentDay?new Date(9999,9,9):new Date(a.currentYear,a.currentMonth,a.currentDay)),k=this._getMinMaxDate(a,"min"),o=this._getMinMaxDate(a,"max");g=a.drawMonth-g;var m=a.drawYear;if(g<0){g+=12;m--}if(o){var n=
+this._daylightSavingAdjust(new Date(o.getFullYear(),o.getMonth()-i[0]*i[1]+1,o.getDate()));for(n=k&&n<k?k:n;this._daylightSavingAdjust(new Date(m,g,1))>n;){g--;if(g<0){g=11;m--}}}a.drawMonth=g;a.drawYear=m;n=this._get(a,"prevText");n=!h?n:this.formatDate(n,this._daylightSavingAdjust(new Date(m,g-j,1)),this._getFormatConfig(a));n=this._canAdjustMonth(a,-1,m,g)?'<a class="ui-datepicker-prev ui-corner-all" onclick="DP_jQuery_'+y+".datepicker._adjustDate('#"+a.id+"', -"+j+", 'M');\" title=\""+n+'"><span class="ui-icon ui-icon-circle-triangle-'+
+(c?"e":"w")+'">'+n+"</span></a>":f?"":'<a class="ui-datepicker-prev ui-corner-all ui-state-disabled" title="'+n+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"e":"w")+'">'+n+"</span></a>";var r=this._get(a,"nextText");r=!h?r:this.formatDate(r,this._daylightSavingAdjust(new Date(m,g+j,1)),this._getFormatConfig(a));f=this._canAdjustMonth(a,+1,m,g)?'<a class="ui-datepicker-next ui-corner-all" onclick="DP_jQuery_'+y+".datepicker._adjustDate('#"+a.id+"', +"+j+", 'M');\" title=\""+r+'"><span class="ui-icon ui-icon-circle-triangle-'+
+(c?"w":"e")+'">'+r+"</span></a>":f?"":'<a class="ui-datepicker-next ui-corner-all ui-state-disabled" title="'+r+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"w":"e")+'">'+r+"</span></a>";j=this._get(a,"currentText");r=this._get(a,"gotoCurrent")&&a.currentDay?u:b;j=!h?j:this.formatDate(j,r,this._getFormatConfig(a));h=!a.inline?'<button type="button" class="ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all" onclick="DP_jQuery_'+y+'.datepicker._hideDatepicker();">'+this._get(a,
+"closeText")+"</button>":"";e=e?'<div class="ui-datepicker-buttonpane ui-widget-content">'+(c?h:"")+(this._isInRange(a,r)?'<button type="button" class="ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all" onclick="DP_jQuery_'+y+".datepicker._gotoToday('#"+a.id+"');\">"+j+"</button>":"")+(c?"":h)+"</div>":"";h=parseInt(this._get(a,"firstDay"),10);h=isNaN(h)?0:h;j=this._get(a,"showWeek");r=this._get(a,"dayNames");this._get(a,"dayNamesShort");var s=this._get(a,"dayNamesMin"),z=
+this._get(a,"monthNames"),w=this._get(a,"monthNamesShort"),p=this._get(a,"beforeShowDay"),v=this._get(a,"showOtherMonths"),H=this._get(a,"selectOtherMonths");this._get(a,"calculateWeek");for(var L=this._getDefaultDate(a),I="",C=0;C<i[0];C++){for(var M="",D=0;D<i[1];D++){var N=this._daylightSavingAdjust(new Date(m,g,a.selectedDay)),t=" ui-corner-all",x="";if(l){x+='<div class="ui-datepicker-group';if(i[1]>1)switch(D){case 0:x+=" ui-datepicker-group-first";t=" ui-corner-"+(c?"right":"left");break;case i[1]-
+1:x+=" ui-datepicker-group-last";t=" ui-corner-"+(c?"left":"right");break;default:x+=" ui-datepicker-group-middle";t="";break}x+='">'}x+='<div class="ui-datepicker-header ui-widget-header ui-helper-clearfix'+t+'">'+(/all|left/.test(t)&&C==0?c?f:n:"")+(/all|right/.test(t)&&C==0?c?n:f:"")+this._generateMonthYearHeader(a,g,m,k,o,C>0||D>0,z,w)+'</div><table class="ui-datepicker-calendar"><thead><tr>';var A=j?'<th class="ui-datepicker-week-col">'+this._get(a,"weekHeader")+"</th>":"";for(t=0;t<7;t++){var q=
+(t+h)%7;A+="<th"+((t+h+6)%7>=5?' class="ui-datepicker-week-end"':"")+'><span title="'+r[q]+'">'+s[q]+"</span></th>"}x+=A+"</tr></thead><tbody>";A=this._getDaysInMonth(m,g);if(m==a.selectedYear&&g==a.selectedMonth)a.selectedDay=Math.min(a.selectedDay,A);t=(this._getFirstDayOfMonth(m,g)-h+7)%7;A=l?6:Math.ceil((t+A)/7);q=this._daylightSavingAdjust(new Date(m,g,1-t));for(var O=0;O<A;O++){x+="<tr>";var P=!j?"":'<td class="ui-datepicker-week-col">'+this._get(a,"calculateWeek")(q)+"</td>";for(t=0;t<7;t++){var F=
+p?p.apply(a.input?a.input[0]:null,[q]):[true,""],B=q.getMonth()!=g,J=B&&!H||!F[0]||k&&q<k||o&&q>o;P+='<td class="'+((t+h+6)%7>=5?" ui-datepicker-week-end":"")+(B?" ui-datepicker-other-month":"")+(q.getTime()==N.getTime()&&g==a.selectedMonth&&a._keyEvent||L.getTime()==q.getTime()&&L.getTime()==N.getTime()?" "+this._dayOverClass:"")+(J?" "+this._unselectableClass+" ui-state-disabled":"")+(B&&!v?"":" "+F[1]+(q.getTime()==u.getTime()?" "+this._currentClass:"")+(q.getTime()==b.getTime()?" ui-datepicker-today":
+""))+'"'+((!B||v)&&F[2]?' title="'+F[2]+'"':"")+(J?"":' onclick="DP_jQuery_'+y+".datepicker._selectDay('#"+a.id+"',"+q.getMonth()+","+q.getFullYear()+', this);return false;"')+">"+(B&&!v?"&#xa0;":J?'<span class="ui-state-default">'+q.getDate()+"</span>":'<a class="ui-state-default'+(q.getTime()==b.getTime()?" ui-state-highlight":"")+(q.getTime()==u.getTime()?" ui-state-active":"")+(B?" ui-priority-secondary":"")+'" href="#">'+q.getDate()+"</a>")+"</td>";q.setDate(q.getDate()+1);q=this._daylightSavingAdjust(q)}x+=
+P+"</tr>"}g++;if(g>11){g=0;m++}x+="</tbody></table>"+(l?"</div>"+(i[0]>0&&D==i[1]-1?'<div class="ui-datepicker-row-break"></div>':""):"");M+=x}I+=M}I+=e+(d.browser.msie&&parseInt(d.browser.version,10)<7&&!a.inline?'<iframe src="javascript:false;" class="ui-datepicker-cover" frameborder="0"></iframe>':"");a._keyEvent=false;return I},_generateMonthYearHeader:function(a,b,c,e,f,h,i,g){var j=this._get(a,"changeMonth"),l=this._get(a,"changeYear"),u=this._get(a,"showMonthAfterYear"),k='<div class="ui-datepicker-title">',
+o="";if(h||!j)o+='<span class="ui-datepicker-month">'+i[b]+"</span>";else{i=e&&e.getFullYear()==c;var m=f&&f.getFullYear()==c;o+='<select class="ui-datepicker-month" onchange="DP_jQuery_'+y+".datepicker._selectMonthYear('#"+a.id+"', this, 'M');\" onclick=\"DP_jQuery_"+y+".datepicker._clickMonthYear('#"+a.id+"');\">";for(var n=0;n<12;n++)if((!i||n>=e.getMonth())&&(!m||n<=f.getMonth()))o+='<option value="'+n+'"'+(n==b?' selected="selected"':"")+">"+g[n]+"</option>";o+="</select>"}u||(k+=o+(h||!(j&&
+l)?"&#xa0;":""));a.yearshtml="";if(h||!l)k+='<span class="ui-datepicker-year">'+c+"</span>";else{g=this._get(a,"yearRange").split(":");var r=(new Date).getFullYear();i=function(s){s=s.match(/c[+-].*/)?c+parseInt(s.substring(1),10):s.match(/[+-].*/)?r+parseInt(s,10):parseInt(s,10);return isNaN(s)?r:s};b=i(g[0]);g=Math.max(b,i(g[1]||""));b=e?Math.max(b,e.getFullYear()):b;g=f?Math.min(g,f.getFullYear()):g;for(a.yearshtml+='<select class="ui-datepicker-year" onchange="DP_jQuery_'+y+".datepicker._selectMonthYear('#"+
+a.id+"', this, 'Y');\" onclick=\"DP_jQuery_"+y+".datepicker._clickMonthYear('#"+a.id+"');\">";b<=g;b++)a.yearshtml+='<option value="'+b+'"'+(b==c?' selected="selected"':"")+">"+b+"</option>";a.yearshtml+="</select>";if(d.browser.mozilla)k+='<select class="ui-datepicker-year"><option value="'+c+'" selected="selected">'+c+"</option></select>";else{k+=a.yearshtml;a.yearshtml=null}}k+=this._get(a,"yearSuffix");if(u)k+=(h||!(j&&l)?"&#xa0;":"")+o;k+="</div>";return k},_adjustInstDate:function(a,b,c){var e=
+a.drawYear+(c=="Y"?b:0),f=a.drawMonth+(c=="M"?b:0);b=Math.min(a.selectedDay,this._getDaysInMonth(e,f))+(c=="D"?b:0);e=this._restrictMinMax(a,this._daylightSavingAdjust(new Date(e,f,b)));a.selectedDay=e.getDate();a.drawMonth=a.selectedMonth=e.getMonth();a.drawYear=a.selectedYear=e.getFullYear();if(c=="M"||c=="Y")this._notifyChange(a)},_restrictMinMax:function(a,b){var c=this._getMinMaxDate(a,"min");a=this._getMinMaxDate(a,"max");b=c&&b<c?c:b;return b=a&&b>a?a:b},_notifyChange:function(a){var b=this._get(a,
+"onChangeMonthYear");if(b)b.apply(a.input?a.input[0]:null,[a.selectedYear,a.selectedMonth+1,a])},_getNumberOfMonths:function(a){a=this._get(a,"numberOfMonths");return a==null?[1,1]:typeof a=="number"?[1,a]:a},_getMinMaxDate:function(a,b){return this._determineDate(a,this._get(a,b+"Date"),null)},_getDaysInMonth:function(a,b){return 32-this._daylightSavingAdjust(new Date(a,b,32)).getDate()},_getFirstDayOfMonth:function(a,b){return(new Date(a,b,1)).getDay()},_canAdjustMonth:function(a,b,c,e){var f=this._getNumberOfMonths(a);
+c=this._daylightSavingAdjust(new Date(c,e+(b<0?b:f[0]*f[1]),1));b<0&&c.setDate(this._getDaysInMonth(c.getFullYear(),c.getMonth()));return this._isInRange(a,c)},_isInRange:function(a,b){var c=this._getMinMaxDate(a,"min");a=this._getMinMaxDate(a,"max");return(!c||b.getTime()>=c.getTime())&&(!a||b.getTime()<=a.getTime())},_getFormatConfig:function(a){var b=this._get(a,"shortYearCutoff");b=typeof b!="string"?b:(new Date).getFullYear()%100+parseInt(b,10);return{shortYearCutoff:b,dayNamesShort:this._get(a,
+"dayNamesShort"),dayNames:this._get(a,"dayNames"),monthNamesShort:this._get(a,"monthNamesShort"),monthNames:this._get(a,"monthNames")}},_formatDate:function(a,b,c,e){if(!b){a.currentDay=a.selectedDay;a.currentMonth=a.selectedMonth;a.currentYear=a.selectedYear}b=b?typeof b=="object"?b:this._daylightSavingAdjust(new Date(e,c,b)):this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay));return this.formatDate(this._get(a,"dateFormat"),b,this._getFormatConfig(a))}});d.fn.datepicker=
+function(a){if(!this.length)return this;if(!d.datepicker.initialized){d(document).mousedown(d.datepicker._checkExternalClick).find("body").append(d.datepicker.dpDiv);d.datepicker.initialized=true}var b=Array.prototype.slice.call(arguments,1);if(typeof a=="string"&&(a=="isDisabled"||a=="getDate"||a=="widget"))return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this[0]].concat(b));if(a=="option"&&arguments.length==2&&typeof arguments[1]=="string")return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,
+[this[0]].concat(b));return this.each(function(){typeof a=="string"?d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this].concat(b)):d.datepicker._attachDatepicker(this,a)})};d.datepicker=new K;d.datepicker.initialized=false;d.datepicker.uuid=(new Date).getTime();d.datepicker.version="1.8.10";window["DP_jQuery_"+y]=d})(jQuery);
+;/*
+ * jQuery UI Progressbar 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Progressbar
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ */
+(function(b,d){b.widget("ui.progressbar",{options:{value:0,max:100},min:0,_create:function(){this.element.addClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").attr({role:"progressbar","aria-valuemin":this.min,"aria-valuemax":this.options.max,"aria-valuenow":this._value()});this.valueDiv=b("<div class='ui-progressbar-value ui-widget-header ui-corner-left'></div>").appendTo(this.element);this.oldValue=this._value();this._refreshValue()},destroy:function(){this.element.removeClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow");
+this.valueDiv.remove();b.Widget.prototype.destroy.apply(this,arguments)},value:function(a){if(a===d)return this._value();this._setOption("value",a);return this},_setOption:function(a,c){if(a==="value"){this.options.value=c;this._refreshValue();this._value()===this.options.max&&this._trigger("complete")}b.Widget.prototype._setOption.apply(this,arguments)},_value:function(){var a=this.options.value;if(typeof a!=="number")a=0;return Math.min(this.options.max,Math.max(this.min,a))},_percentage:function(){return 100*
+this._value()/this.options.max},_refreshValue:function(){var a=this.value(),c=this._percentage();if(this.oldValue!==a){this.oldValue=a;this._trigger("change")}this.valueDiv.toggleClass("ui-corner-right",a===this.options.max).width(c.toFixed(0)+"%");this.element.attr("aria-valuenow",a)}});b.extend(b.ui.progressbar,{version:"1.8.10"})})(jQuery);
+;/*
+ * jQuery UI Effects 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/
+ */
+jQuery.effects||function(f,j){function n(c){var a;if(c&&c.constructor==Array&&c.length==3)return c;if(a=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(c))return[parseInt(a[1],10),parseInt(a[2],10),parseInt(a[3],10)];if(a=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(c))return[parseFloat(a[1])*2.55,parseFloat(a[2])*2.55,parseFloat(a[3])*2.55];if(a=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(c))return[parseInt(a[1],
+16),parseInt(a[2],16),parseInt(a[3],16)];if(a=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(c))return[parseInt(a[1]+a[1],16),parseInt(a[2]+a[2],16),parseInt(a[3]+a[3],16)];if(/rgba\(0, 0, 0, 0\)/.exec(c))return o.transparent;return o[f.trim(c).toLowerCase()]}function s(c,a){var b;do{b=f.curCSS(c,a);if(b!=""&&b!="transparent"||f.nodeName(c,"body"))break;a="backgroundColor"}while(c=c.parentNode);return n(b)}function p(){var c=document.defaultView?document.defaultView.getComputedStyle(this,null):this.currentStyle,
+a={},b,d;if(c&&c.length&&c[0]&&c[c[0]])for(var e=c.length;e--;){b=c[e];if(typeof c[b]=="string"){d=b.replace(/\-(\w)/g,function(g,h){return h.toUpperCase()});a[d]=c[b]}}else for(b in c)if(typeof c[b]==="string")a[b]=c[b];return a}function q(c){var a,b;for(a in c){b=c[a];if(b==null||f.isFunction(b)||a in t||/scrollbar/.test(a)||!/color/i.test(a)&&isNaN(parseFloat(b)))delete c[a]}return c}function u(c,a){var b={_:0},d;for(d in a)if(c[d]!=a[d])b[d]=a[d];return b}function k(c,a,b,d){if(typeof c=="object"){d=
+a;b=null;a=c;c=a.effect}if(f.isFunction(a)){d=a;b=null;a={}}if(typeof a=="number"||f.fx.speeds[a]){d=b;b=a;a={}}if(f.isFunction(b)){d=b;b=null}a=a||{};b=b||a.duration;b=f.fx.off?0:typeof b=="number"?b:b in f.fx.speeds?f.fx.speeds[b]:f.fx.speeds._default;d=d||a.complete;return[c,a,b,d]}function m(c){if(!c||typeof c==="number"||f.fx.speeds[c])return true;if(typeof c==="string"&&!f.effects[c])return true;return false}f.effects={};f.each(["backgroundColor","borderBottomColor","borderLeftColor","borderRightColor",
+"borderTopColor","borderColor","color","outlineColor"],function(c,a){f.fx.step[a]=function(b){if(!b.colorInit){b.start=s(b.elem,a);b.end=n(b.end);b.colorInit=true}b.elem.style[a]="rgb("+Math.max(Math.min(parseInt(b.pos*(b.end[0]-b.start[0])+b.start[0],10),255),0)+","+Math.max(Math.min(parseInt(b.pos*(b.end[1]-b.start[1])+b.start[1],10),255),0)+","+Math.max(Math.min(parseInt(b.pos*(b.end[2]-b.start[2])+b.start[2],10),255),0)+")"}});var o={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,
+0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,
+211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0],transparent:[255,255,255]},r=["add","remove","toggle"],t={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};f.effects.animateClass=function(c,a,b,
+d){if(f.isFunction(b)){d=b;b=null}return this.queue("fx",function(){var e=f(this),g=e.attr("style")||" ",h=q(p.call(this)),l,v=e.attr("className");f.each(r,function(w,i){c[i]&&e[i+"Class"](c[i])});l=q(p.call(this));e.attr("className",v);e.animate(u(h,l),a,b,function(){f.each(r,function(w,i){c[i]&&e[i+"Class"](c[i])});if(typeof e.attr("style")=="object"){e.attr("style").cssText="";e.attr("style").cssText=g}else e.attr("style",g);d&&d.apply(this,arguments)});h=f.queue(this);l=h.splice(h.length-1,1)[0];
+h.splice(1,0,l);f.dequeue(this)})};f.fn.extend({_addClass:f.fn.addClass,addClass:function(c,a,b,d){return a?f.effects.animateClass.apply(this,[{add:c},a,b,d]):this._addClass(c)},_removeClass:f.fn.removeClass,removeClass:function(c,a,b,d){return a?f.effects.animateClass.apply(this,[{remove:c},a,b,d]):this._removeClass(c)},_toggleClass:f.fn.toggleClass,toggleClass:function(c,a,b,d,e){return typeof a=="boolean"||a===j?b?f.effects.animateClass.apply(this,[a?{add:c}:{remove:c},b,d,e]):this._toggleClass(c,
+a):f.effects.animateClass.apply(this,[{toggle:c},a,b,d])},switchClass:function(c,a,b,d,e){return f.effects.animateClass.apply(this,[{add:a,remove:c},b,d,e])}});f.extend(f.effects,{version:"1.8.10",save:function(c,a){for(var b=0;b<a.length;b++)a[b]!==null&&c.data("ec.storage."+a[b],c[0].style[a[b]])},restore:function(c,a){for(var b=0;b<a.length;b++)a[b]!==null&&c.css(a[b],c.data("ec.storage."+a[b]))},setMode:function(c,a){if(a=="toggle")a=c.is(":hidden")?"show":"hide";return a},getBaseline:function(c,
+a){var b;switch(c[0]){case "top":b=0;break;case "middle":b=0.5;break;case "bottom":b=1;break;default:b=c[0]/a.height}switch(c[1]){case "left":c=0;break;case "center":c=0.5;break;case "right":c=1;break;default:c=c[1]/a.width}return{x:c,y:b}},createWrapper:function(c){if(c.parent().is(".ui-effects-wrapper"))return c.parent();var a={width:c.outerWidth(true),height:c.outerHeight(true),"float":c.css("float")},b=f("<div></div>").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",
+border:"none",margin:0,padding:0});c.wrap(b);b=c.parent();if(c.css("position")=="static"){b.css({position:"relative"});c.css({position:"relative"})}else{f.extend(a,{position:c.css("position"),zIndex:c.css("z-index")});f.each(["top","left","bottom","right"],function(d,e){a[e]=c.css(e);if(isNaN(parseInt(a[e],10)))a[e]="auto"});c.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"})}return b.css(a).show()},removeWrapper:function(c){if(c.parent().is(".ui-effects-wrapper"))return c.parent().replaceWith(c);
+return c},setTransition:function(c,a,b,d){d=d||{};f.each(a,function(e,g){unit=c.cssUnit(g);if(unit[0]>0)d[g]=unit[0]*b+unit[1]});return d}});f.fn.extend({effect:function(c){var a=k.apply(this,arguments),b={options:a[1],duration:a[2],callback:a[3]};a=b.options.mode;var d=f.effects[c];if(f.fx.off||!d)return a?this[a](b.duration,b.callback):this.each(function(){b.callback&&b.callback.call(this)});return d.call(this,b)},_show:f.fn.show,show:function(c){if(m(c))return this._show.apply(this,arguments);
+else{var a=k.apply(this,arguments);a[1].mode="show";return this.effect.apply(this,a)}},_hide:f.fn.hide,hide:function(c){if(m(c))return this._hide.apply(this,arguments);else{var a=k.apply(this,arguments);a[1].mode="hide";return this.effect.apply(this,a)}},__toggle:f.fn.toggle,toggle:function(c){if(m(c)||typeof c==="boolean"||f.isFunction(c))return this.__toggle.apply(this,arguments);else{var a=k.apply(this,arguments);a[1].mode="toggle";return this.effect.apply(this,a)}},cssUnit:function(c){var a=this.css(c),
+b=[];f.each(["em","px","%","pt"],function(d,e){if(a.indexOf(e)>0)b=[parseFloat(a),e]});return b}});f.easing.jswing=f.easing.swing;f.extend(f.easing,{def:"easeOutQuad",swing:function(c,a,b,d,e){return f.easing[f.easing.def](c,a,b,d,e)},easeInQuad:function(c,a,b,d,e){return d*(a/=e)*a+b},easeOutQuad:function(c,a,b,d,e){return-d*(a/=e)*(a-2)+b},easeInOutQuad:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a+b;return-d/2*(--a*(a-2)-1)+b},easeInCubic:function(c,a,b,d,e){return d*(a/=e)*a*a+b},easeOutCubic:function(c,
+a,b,d,e){return d*((a=a/e-1)*a*a+1)+b},easeInOutCubic:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a+b;return d/2*((a-=2)*a*a+2)+b},easeInQuart:function(c,a,b,d,e){return d*(a/=e)*a*a*a+b},easeOutQuart:function(c,a,b,d,e){return-d*((a=a/e-1)*a*a*a-1)+b},easeInOutQuart:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a*a+b;return-d/2*((a-=2)*a*a*a-2)+b},easeInQuint:function(c,a,b,d,e){return d*(a/=e)*a*a*a*a+b},easeOutQuint:function(c,a,b,d,e){return d*((a=a/e-1)*a*a*a*a+1)+b},easeInOutQuint:function(c,
+a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a*a*a+b;return d/2*((a-=2)*a*a*a*a+2)+b},easeInSine:function(c,a,b,d,e){return-d*Math.cos(a/e*(Math.PI/2))+d+b},easeOutSine:function(c,a,b,d,e){return d*Math.sin(a/e*(Math.PI/2))+b},easeInOutSine:function(c,a,b,d,e){return-d/2*(Math.cos(Math.PI*a/e)-1)+b},easeInExpo:function(c,a,b,d,e){return a==0?b:d*Math.pow(2,10*(a/e-1))+b},easeOutExpo:function(c,a,b,d,e){return a==e?b+d:d*(-Math.pow(2,-10*a/e)+1)+b},easeInOutExpo:function(c,a,b,d,e){if(a==0)return b;if(a==
+e)return b+d;if((a/=e/2)<1)return d/2*Math.pow(2,10*(a-1))+b;return d/2*(-Math.pow(2,-10*--a)+2)+b},easeInCirc:function(c,a,b,d,e){return-d*(Math.sqrt(1-(a/=e)*a)-1)+b},easeOutCirc:function(c,a,b,d,e){return d*Math.sqrt(1-(a=a/e-1)*a)+b},easeInOutCirc:function(c,a,b,d,e){if((a/=e/2)<1)return-d/2*(Math.sqrt(1-a*a)-1)+b;return d/2*(Math.sqrt(1-(a-=2)*a)+1)+b},easeInElastic:function(c,a,b,d,e){c=1.70158;var g=0,h=d;if(a==0)return b;if((a/=e)==1)return b+d;g||(g=e*0.3);if(h<Math.abs(d)){h=d;c=g/4}else c=
+g/(2*Math.PI)*Math.asin(d/h);return-(h*Math.pow(2,10*(a-=1))*Math.sin((a*e-c)*2*Math.PI/g))+b},easeOutElastic:function(c,a,b,d,e){c=1.70158;var g=0,h=d;if(a==0)return b;if((a/=e)==1)return b+d;g||(g=e*0.3);if(h<Math.abs(d)){h=d;c=g/4}else c=g/(2*Math.PI)*Math.asin(d/h);return h*Math.pow(2,-10*a)*Math.sin((a*e-c)*2*Math.PI/g)+d+b},easeInOutElastic:function(c,a,b,d,e){c=1.70158;var g=0,h=d;if(a==0)return b;if((a/=e/2)==2)return b+d;g||(g=e*0.3*1.5);if(h<Math.abs(d)){h=d;c=g/4}else c=g/(2*Math.PI)*Math.asin(d/
+h);if(a<1)return-0.5*h*Math.pow(2,10*(a-=1))*Math.sin((a*e-c)*2*Math.PI/g)+b;return h*Math.pow(2,-10*(a-=1))*Math.sin((a*e-c)*2*Math.PI/g)*0.5+d+b},easeInBack:function(c,a,b,d,e,g){if(g==j)g=1.70158;return d*(a/=e)*a*((g+1)*a-g)+b},easeOutBack:function(c,a,b,d,e,g){if(g==j)g=1.70158;return d*((a=a/e-1)*a*((g+1)*a+g)+1)+b},easeInOutBack:function(c,a,b,d,e,g){if(g==j)g=1.70158;if((a/=e/2)<1)return d/2*a*a*(((g*=1.525)+1)*a-g)+b;return d/2*((a-=2)*a*(((g*=1.525)+1)*a+g)+2)+b},easeInBounce:function(c,
+a,b,d,e){return d-f.easing.easeOutBounce(c,e-a,0,d,e)+b},easeOutBounce:function(c,a,b,d,e){return(a/=e)<1/2.75?d*7.5625*a*a+b:a<2/2.75?d*(7.5625*(a-=1.5/2.75)*a+0.75)+b:a<2.5/2.75?d*(7.5625*(a-=2.25/2.75)*a+0.9375)+b:d*(7.5625*(a-=2.625/2.75)*a+0.984375)+b},easeInOutBounce:function(c,a,b,d,e){if(a<e/2)return f.easing.easeInBounce(c,a*2,0,d,e)*0.5+b;return f.easing.easeOutBounce(c,a*2-e,0,d,e)*0.5+d*0.5+b}})}(jQuery);
+;/*
+ * jQuery UI Effects Blind 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Blind
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function(b){b.effects.blind=function(c){return this.queue(function(){var a=b(this),g=["position","top","bottom","left","right"],f=b.effects.setMode(a,c.options.mode||"hide"),d=c.options.direction||"vertical";b.effects.save(a,g);a.show();var e=b.effects.createWrapper(a).css({overflow:"hidden"}),h=d=="vertical"?"height":"width";d=d=="vertical"?e.height():e.width();f=="show"&&e.css(h,0);var i={};i[h]=f=="show"?d:0;e.animate(i,c.duration,c.options.easing,function(){f=="hide"&&a.hide();b.effects.restore(a,
+g);b.effects.removeWrapper(a);c.callback&&c.callback.apply(a[0],arguments);a.dequeue()})})}})(jQuery);
+;/*
+ * jQuery UI Effects Bounce 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Bounce
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function(e){e.effects.bounce=function(b){return this.queue(function(){var a=e(this),l=["position","top","bottom","left","right"],h=e.effects.setMode(a,b.options.mode||"effect"),d=b.options.direction||"up",c=b.options.distance||20,m=b.options.times||5,i=b.duration||250;/show|hide/.test(h)&&l.push("opacity");e.effects.save(a,l);a.show();e.effects.createWrapper(a);var f=d=="up"||d=="down"?"top":"left";d=d=="up"||d=="left"?"pos":"neg";c=b.options.distance||(f=="top"?a.outerHeight({margin:true})/3:a.outerWidth({margin:true})/
+3);if(h=="show")a.css("opacity",0).css(f,d=="pos"?-c:c);if(h=="hide")c/=m*2;h!="hide"&&m--;if(h=="show"){var g={opacity:1};g[f]=(d=="pos"?"+=":"-=")+c;a.animate(g,i/2,b.options.easing);c/=2;m--}for(g=0;g<m;g++){var j={},k={};j[f]=(d=="pos"?"-=":"+=")+c;k[f]=(d=="pos"?"+=":"-=")+c;a.animate(j,i/2,b.options.easing).animate(k,i/2,b.options.easing);c=h=="hide"?c*2:c/2}if(h=="hide"){g={opacity:0};g[f]=(d=="pos"?"-=":"+=")+c;a.animate(g,i/2,b.options.easing,function(){a.hide();e.effects.restore(a,l);e.effects.removeWrapper(a);
+b.callback&&b.callback.apply(this,arguments)})}else{j={};k={};j[f]=(d=="pos"?"-=":"+=")+c;k[f]=(d=="pos"?"+=":"-=")+c;a.animate(j,i/2,b.options.easing).animate(k,i/2,b.options.easing,function(){e.effects.restore(a,l);e.effects.removeWrapper(a);b.callback&&b.callback.apply(this,arguments)})}a.queue("fx",function(){a.dequeue()});a.dequeue()})}})(jQuery);
+;/*
+ * jQuery UI Effects Clip 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Clip
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function(b){b.effects.clip=function(e){return this.queue(function(){var a=b(this),i=["position","top","bottom","left","right","height","width"],f=b.effects.setMode(a,e.options.mode||"hide"),c=e.options.direction||"vertical";b.effects.save(a,i);a.show();var d=b.effects.createWrapper(a).css({overflow:"hidden"});d=a[0].tagName=="IMG"?d:a;var g={size:c=="vertical"?"height":"width",position:c=="vertical"?"top":"left"};c=c=="vertical"?d.height():d.width();if(f=="show"){d.css(g.size,0);d.css(g.position,
+c/2)}var h={};h[g.size]=f=="show"?c:0;h[g.position]=f=="show"?0:c/2;d.animate(h,{queue:false,duration:e.duration,easing:e.options.easing,complete:function(){f=="hide"&&a.hide();b.effects.restore(a,i);b.effects.removeWrapper(a);e.callback&&e.callback.apply(a[0],arguments);a.dequeue()}})})}})(jQuery);
+;/*
+ * jQuery UI Effects Drop 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Drop
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function(c){c.effects.drop=function(d){return this.queue(function(){var a=c(this),h=["position","top","bottom","left","right","opacity"],e=c.effects.setMode(a,d.options.mode||"hide"),b=d.options.direction||"left";c.effects.save(a,h);a.show();c.effects.createWrapper(a);var f=b=="up"||b=="down"?"top":"left";b=b=="up"||b=="left"?"pos":"neg";var g=d.options.distance||(f=="top"?a.outerHeight({margin:true})/2:a.outerWidth({margin:true})/2);if(e=="show")a.css("opacity",0).css(f,b=="pos"?-g:g);var i={opacity:e==
+"show"?1:0};i[f]=(e=="show"?b=="pos"?"+=":"-=":b=="pos"?"-=":"+=")+g;a.animate(i,{queue:false,duration:d.duration,easing:d.options.easing,complete:function(){e=="hide"&&a.hide();c.effects.restore(a,h);c.effects.removeWrapper(a);d.callback&&d.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery);
+;/*
+ * jQuery UI Effects Explode 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Explode
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function(j){j.effects.explode=function(a){return this.queue(function(){var c=a.options.pieces?Math.round(Math.sqrt(a.options.pieces)):3,d=a.options.pieces?Math.round(Math.sqrt(a.options.pieces)):3;a.options.mode=a.options.mode=="toggle"?j(this).is(":visible")?"hide":"show":a.options.mode;var b=j(this).show().css("visibility","hidden"),g=b.offset();g.top-=parseInt(b.css("marginTop"),10)||0;g.left-=parseInt(b.css("marginLeft"),10)||0;for(var h=b.outerWidth(true),i=b.outerHeight(true),e=0;e<c;e++)for(var f=
+0;f<d;f++)b.clone().appendTo("body").wrap("<div></div>").css({position:"absolute",visibility:"visible",left:-f*(h/d),top:-e*(i/c)}).parent().addClass("ui-effects-explode").css({position:"absolute",overflow:"hidden",width:h/d,height:i/c,left:g.left+f*(h/d)+(a.options.mode=="show"?(f-Math.floor(d/2))*(h/d):0),top:g.top+e*(i/c)+(a.options.mode=="show"?(e-Math.floor(c/2))*(i/c):0),opacity:a.options.mode=="show"?0:1}).animate({left:g.left+f*(h/d)+(a.options.mode=="show"?0:(f-Math.floor(d/2))*(h/d)),top:g.top+
+e*(i/c)+(a.options.mode=="show"?0:(e-Math.floor(c/2))*(i/c)),opacity:a.options.mode=="show"?1:0},a.duration||500);setTimeout(function(){a.options.mode=="show"?b.css({visibility:"visible"}):b.css({visibility:"visible"}).hide();a.callback&&a.callback.apply(b[0]);b.dequeue();j("div.ui-effects-explode").remove()},a.duration||500)})}})(jQuery);
+;/*
+ * jQuery UI Effects Fade 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Fade
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function(b){b.effects.fade=function(a){return this.queue(function(){var c=b(this),d=b.effects.setMode(c,a.options.mode||"hide");c.animate({opacity:d},{queue:false,duration:a.duration,easing:a.options.easing,complete:function(){a.callback&&a.callback.apply(this,arguments);c.dequeue()}})})}})(jQuery);
+;/*
+ * jQuery UI Effects Fold 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Fold
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function(c){c.effects.fold=function(a){return this.queue(function(){var b=c(this),j=["position","top","bottom","left","right"],d=c.effects.setMode(b,a.options.mode||"hide"),g=a.options.size||15,h=!!a.options.horizFirst,k=a.duration?a.duration/2:c.fx.speeds._default/2;c.effects.save(b,j);b.show();var e=c.effects.createWrapper(b).css({overflow:"hidden"}),f=d=="show"!=h,l=f?["width","height"]:["height","width"];f=f?[e.width(),e.height()]:[e.height(),e.width()];var i=/([0-9]+)%/.exec(g);if(i)g=parseInt(i[1],
+10)/100*f[d=="hide"?0:1];if(d=="show")e.css(h?{height:0,width:g}:{height:g,width:0});h={};i={};h[l[0]]=d=="show"?f[0]:g;i[l[1]]=d=="show"?f[1]:0;e.animate(h,k,a.options.easing).animate(i,k,a.options.easing,function(){d=="hide"&&b.hide();c.effects.restore(b,j);c.effects.removeWrapper(b);a.callback&&a.callback.apply(b[0],arguments);b.dequeue()})})}})(jQuery);
+;/*
+ * jQuery UI Effects Highlight 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Highlight
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function(b){b.effects.highlight=function(c){return this.queue(function(){var a=b(this),e=["backgroundImage","backgroundColor","opacity"],d=b.effects.setMode(a,c.options.mode||"show"),f={backgroundColor:a.css("backgroundColor")};if(d=="hide")f.opacity=0;b.effects.save(a,e);a.show().css({backgroundImage:"none",backgroundColor:c.options.color||"#ffff99"}).animate(f,{queue:false,duration:c.duration,easing:c.options.easing,complete:function(){d=="hide"&&a.hide();b.effects.restore(a,e);d=="show"&&!b.support.opacity&&
+this.style.removeAttribute("filter");c.callback&&c.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery);
+;/*
+ * jQuery UI Effects Pulsate 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Pulsate
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function(d){d.effects.pulsate=function(a){return this.queue(function(){var b=d(this),c=d.effects.setMode(b,a.options.mode||"show");times=(a.options.times||5)*2-1;duration=a.duration?a.duration/2:d.fx.speeds._default/2;isVisible=b.is(":visible");animateTo=0;if(!isVisible){b.css("opacity",0).show();animateTo=1}if(c=="hide"&&isVisible||c=="show"&&!isVisible)times--;for(c=0;c<times;c++){b.animate({opacity:animateTo},duration,a.options.easing);animateTo=(animateTo+1)%2}b.animate({opacity:animateTo},duration,
+a.options.easing,function(){animateTo==0&&b.hide();a.callback&&a.callback.apply(this,arguments)});b.queue("fx",function(){b.dequeue()}).dequeue()})}})(jQuery);
+;/*
+ * jQuery UI Effects Scale 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Scale
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function(c){c.effects.puff=function(b){return this.queue(function(){var a=c(this),e=c.effects.setMode(a,b.options.mode||"hide"),g=parseInt(b.options.percent,10)||150,h=g/100,i={height:a.height(),width:a.width()};c.extend(b.options,{fade:true,mode:e,percent:e=="hide"?g:100,from:e=="hide"?i:{height:i.height*h,width:i.width*h}});a.effect("scale",b.options,b.duration,b.callback);a.dequeue()})};c.effects.scale=function(b){return this.queue(function(){var a=c(this),e=c.extend(true,{},b.options),g=c.effects.setMode(a,
+b.options.mode||"effect"),h=parseInt(b.options.percent,10)||(parseInt(b.options.percent,10)==0?0:g=="hide"?0:100),i=b.options.direction||"both",f=b.options.origin;if(g!="effect"){e.origin=f||["middle","center"];e.restore=true}f={height:a.height(),width:a.width()};a.from=b.options.from||(g=="show"?{height:0,width:0}:f);h={y:i!="horizontal"?h/100:1,x:i!="vertical"?h/100:1};a.to={height:f.height*h.y,width:f.width*h.x};if(b.options.fade){if(g=="show"){a.from.opacity=0;a.to.opacity=1}if(g=="hide"){a.from.opacity=
+1;a.to.opacity=0}}e.from=a.from;e.to=a.to;e.mode=g;a.effect("size",e,b.duration,b.callback);a.dequeue()})};c.effects.size=function(b){return this.queue(function(){var a=c(this),e=["position","top","bottom","left","right","width","height","overflow","opacity"],g=["position","top","bottom","left","right","overflow","opacity"],h=["width","height","overflow"],i=["fontSize"],f=["borderTopWidth","borderBottomWidth","paddingTop","paddingBottom"],k=["borderLeftWidth","borderRightWidth","paddingLeft","paddingRight"],
+p=c.effects.setMode(a,b.options.mode||"effect"),n=b.options.restore||false,m=b.options.scale||"both",l=b.options.origin,j={height:a.height(),width:a.width()};a.from=b.options.from||j;a.to=b.options.to||j;if(l){l=c.effects.getBaseline(l,j);a.from.top=(j.height-a.from.height)*l.y;a.from.left=(j.width-a.from.width)*l.x;a.to.top=(j.height-a.to.height)*l.y;a.to.left=(j.width-a.to.width)*l.x}var d={from:{y:a.from.height/j.height,x:a.from.width/j.width},to:{y:a.to.height/j.height,x:a.to.width/j.width}};
+if(m=="box"||m=="both"){if(d.from.y!=d.to.y){e=e.concat(f);a.from=c.effects.setTransition(a,f,d.from.y,a.from);a.to=c.effects.setTransition(a,f,d.to.y,a.to)}if(d.from.x!=d.to.x){e=e.concat(k);a.from=c.effects.setTransition(a,k,d.from.x,a.from);a.to=c.effects.setTransition(a,k,d.to.x,a.to)}}if(m=="content"||m=="both")if(d.from.y!=d.to.y){e=e.concat(i);a.from=c.effects.setTransition(a,i,d.from.y,a.from);a.to=c.effects.setTransition(a,i,d.to.y,a.to)}c.effects.save(a,n?e:g);a.show();c.effects.createWrapper(a);
+a.css("overflow","hidden").css(a.from);if(m=="content"||m=="both"){f=f.concat(["marginTop","marginBottom"]).concat(i);k=k.concat(["marginLeft","marginRight"]);h=e.concat(f).concat(k);a.find("*[width]").each(function(){child=c(this);n&&c.effects.save(child,h);var o={height:child.height(),width:child.width()};child.from={height:o.height*d.from.y,width:o.width*d.from.x};child.to={height:o.height*d.to.y,width:o.width*d.to.x};if(d.from.y!=d.to.y){child.from=c.effects.setTransition(child,f,d.from.y,child.from);
+child.to=c.effects.setTransition(child,f,d.to.y,child.to)}if(d.from.x!=d.to.x){child.from=c.effects.setTransition(child,k,d.from.x,child.from);child.to=c.effects.setTransition(child,k,d.to.x,child.to)}child.css(child.from);child.animate(child.to,b.duration,b.options.easing,function(){n&&c.effects.restore(child,h)})})}a.animate(a.to,{queue:false,duration:b.duration,easing:b.options.easing,complete:function(){a.to.opacity===0&&a.css("opacity",a.from.opacity);p=="hide"&&a.hide();c.effects.restore(a,
+n?e:g);c.effects.removeWrapper(a);b.callback&&b.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery);
+;/*
+ * jQuery UI Effects Shake 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Shake
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function(d){d.effects.shake=function(a){return this.queue(function(){var b=d(this),j=["position","top","bottom","left","right"];d.effects.setMode(b,a.options.mode||"effect");var c=a.options.direction||"left",e=a.options.distance||20,l=a.options.times||3,f=a.duration||a.options.duration||140;d.effects.save(b,j);b.show();d.effects.createWrapper(b);var g=c=="up"||c=="down"?"top":"left",h=c=="up"||c=="left"?"pos":"neg";c={};var i={},k={};c[g]=(h=="pos"?"-=":"+=")+e;i[g]=(h=="pos"?"+=":"-=")+e*2;k[g]=
+(h=="pos"?"-=":"+=")+e*2;b.animate(c,f,a.options.easing);for(e=1;e<l;e++)b.animate(i,f,a.options.easing).animate(k,f,a.options.easing);b.animate(i,f,a.options.easing).animate(c,f/2,a.options.easing,function(){d.effects.restore(b,j);d.effects.removeWrapper(b);a.callback&&a.callback.apply(this,arguments)});b.queue("fx",function(){b.dequeue()});b.dequeue()})}})(jQuery);
+;/*
+ * jQuery UI Effects Slide 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Slide
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function(c){c.effects.slide=function(d){return this.queue(function(){var a=c(this),h=["position","top","bottom","left","right"],f=c.effects.setMode(a,d.options.mode||"show"),b=d.options.direction||"left";c.effects.save(a,h);a.show();c.effects.createWrapper(a).css({overflow:"hidden"});var g=b=="up"||b=="down"?"top":"left";b=b=="up"||b=="left"?"pos":"neg";var e=d.options.distance||(g=="top"?a.outerHeight({margin:true}):a.outerWidth({margin:true}));if(f=="show")a.css(g,b=="pos"?isNaN(e)?"-"+e:-e:e);
+var i={};i[g]=(f=="show"?b=="pos"?"+=":"-=":b=="pos"?"-=":"+=")+e;a.animate(i,{queue:false,duration:d.duration,easing:d.options.easing,complete:function(){f=="hide"&&a.hide();c.effects.restore(a,h);c.effects.removeWrapper(a);d.callback&&d.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery);
+;/*
+ * jQuery UI Effects Transfer 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Transfer
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function(e){e.effects.transfer=function(a){return this.queue(function(){var b=e(this),c=e(a.options.to),d=c.offset();c={top:d.top,left:d.left,height:c.innerHeight(),width:c.innerWidth()};d=b.offset();var f=e('<div class="ui-effects-transfer"></div>').appendTo(document.body).addClass(a.options.className).css({top:d.top,left:d.left,height:b.innerHeight(),width:b.innerWidth(),position:"absolute"}).animate(c,a.duration,a.options.easing,function(){f.remove();a.callback&&a.callback.apply(b[0],arguments);
+b.dequeue()})})}})(jQuery);
+; \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/content.inline.min.css b/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/content.inline.min.css
new file mode 100644
index 000000000..9f194f6a6
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/content.inline.min.css
@@ -0,0 +1 @@
+.mce-object{border:1px dotted #3A3A3A;background:#d5d5d5 url(img/object.gif) no-repeat center}.mce-pagebreak{cursor:default;display:block;border:0;width:100%;height:5px;border:1px dashed #666;margin-top:15px;page-break-before:always}@media print{.mce-pagebreak{border:0px}}.mce-item-anchor{cursor:default;display:inline-block;-webkit-user-select:all;-webkit-user-modify:read-only;-moz-user-select:all;-moz-user-modify:read-only;user-select:all;user-modify:read-only;width:9px !important;height:9px !important;border:1px dotted #3A3A3A;background:#d5d5d5 url(img/anchor.gif) no-repeat center}.mce-nbsp{background:#AAA}hr{cursor:default}.mce-match-marker{background:#AAA;color:#fff}.mce-match-marker-selected{background:#3399ff;color:#fff}.mce-spellchecker-word{border-bottom:2px solid #F00;cursor:default}.mce-spellchecker-grammar{border-bottom:2px solid #008000;cursor:default}.mce-item-table,.mce-item-table td,.mce-item-table th,.mce-item-table caption{border:1px dashed #BBB}td.mce-item-selected,th.mce-item-selected{background-color:#3399ff !important}.mce-edit-focus{outline:1px dotted #333} \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/content.min.css b/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/content.min.css
new file mode 100644
index 000000000..ea08c6896
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/content.min.css
@@ -0,0 +1 @@
+body{background-color:#FFFFFF;color:#000000;font-family:Verdana,Arial,Helvetica,sans-serif;font-size:11px;scrollbar-3dlight-color:#F0F0EE;scrollbar-arrow-color:#676662;scrollbar-base-color:#F0F0EE;scrollbar-darkshadow-color:#DDDDDD;scrollbar-face-color:#E0E0DD;scrollbar-highlight-color:#F0F0EE;scrollbar-shadow-color:#F0F0EE;scrollbar-track-color:#F5F5F5}td,th{font-family:Verdana,Arial,Helvetica,sans-serif;font-size:11px}.mce-object{border:1px dotted #3A3A3A;background:#d5d5d5 url(img/object.gif) no-repeat center}.mce-pagebreak{cursor:default;display:block;border:0;width:100%;height:5px;border:1px dashed #666;margin-top:15px;page-break-before:always}@media print{.mce-pagebreak{border:0px}}.mce-item-anchor{cursor:default;display:inline-block;-webkit-user-select:all;-webkit-user-modify:read-only;-moz-user-select:all;-moz-user-modify:read-only;user-select:all;user-modify:read-only;width:9px !important;height:9px !important;border:1px dotted #3A3A3A;background:#d5d5d5 url(img/anchor.gif) no-repeat center}.mce-nbsp{background:#AAA}hr{cursor:default}.mce-match-marker{background:#AAA;color:#fff}.mce-match-marker-selected{background:#3399ff;color:#fff}.mce-spellchecker-word{border-bottom:2px solid #F00;cursor:default}.mce-spellchecker-grammar{border-bottom:2px solid #008000;cursor:default}.mce-item-table,.mce-item-table td,.mce-item-table th,.mce-item-table caption{border:1px dashed #BBB}td.mce-item-selected,th.mce-item-selected{background-color:#3399ff !important}.mce-edit-focus{outline:1px dotted #333} \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/readme.md b/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/readme.md
new file mode 100644
index 000000000..fa5d63946
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/readme.md
@@ -0,0 +1 @@
+Icons are generated and provided by the http://icomoon.io service.
diff --git a/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/tinymce-small.dev.svg b/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/tinymce-small.dev.svg
new file mode 100644
index 000000000..578b86954
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/tinymce-small.dev.svg
@@ -0,0 +1,175 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
+<svg xmlns="http://www.w3.org/2000/svg">
+<metadata>
+This is a custom SVG font generated by IcoMoon.
+<iconset grid="16"></iconset>
+</metadata>
+<defs>
+<font id="tinymce-small" horiz-adv-x="512" >
+<font-face units-per-em="512" ascent="480" descent="-32" />
+<missing-glyph horiz-adv-x="512" />
+<glyph class="hidden" unicode="&#xf000;" d="M0,480L 512 -32L0 -32 z" horiz-adv-x="0" />
+<glyph unicode="&#xe020;" d="M 352,64l0,18.502 c 75.674,30.814, 128,96.91, 128,173.498c0,106.039-100.288,192-224,192S 32,362.039, 32,256
+ c0-76.588, 52.327-142.684, 128-173.498L 160,64 L 64,64 l-32,48l0-112 l 160,0 L 192,111.406 c-50.45,25.681-85.333,80.77-85.333,144.594
+ c0,88.366, 66.859,160, 149.333,160c 82.474,0, 149.333-71.634, 149.333-160c0-63.824-34.883-118.913-85.333-144.594L 320,0 l 160,0 L 480,112 l-32-48
+ L 352,64 z" />
+<glyph unicode="&#xe013;" d="M 128,448l0-448 l 128,128l 128-128L 384,448 L 128,448 z M 352,85.255l-96,96l-96-96L 160,416 l 192,0 L 352,85.255 z" />
+<glyph unicode="&#xe012;" d="M 463.637,364.892l-66.745,66.744C 386.34,442.188, 372.276,448, 357.293,448s-29.047-5.812-39.598-16.363l-82.746-82.745
+ c-21.834-21.834-21.834-57.362,0-79.196l 1.373-1.373l 33.941,33.941l-1.373,1.373c-3.066,3.066-3.066,8.247,0,11.313l 82.746,82.746
+ C 353.641,399.7, 356.040,400, 357.292,400s 3.651-0.299, 5.656-2.305l 66.745-66.744c 3.066-3.067, 3.066-8.249, 0.001-11.314l-82.747-82.747
+ c-2.004-2.004-4.403-2.304-5.655-2.304s-3.651,0.3-5.656,2.306l-1.373,1.373l-33.94-33.942l 1.371-1.371
+ c 10.553-10.554, 24.615-16.364, 39.6-16.364s 29.047,5.812, 39.598,16.363l 82.747,82.746C 485.47,307.53, 485.47,343.057, 463.637,364.892
+ zM 275.678,179.678l-33.941-33.941l 1.373-1.373c 2.004-2.004, 2.305-4.403, 2.305-5.655c0-1.253-0.299-3.651-2.303-5.657
+ l-82.747-82.745c-2.005-2.005-4.405-2.305-5.657-2.305s-3.652,0.3-5.657,2.305L 82.305,117.050C 80.3,119.055, 80,121.455, 80,122.707
+ s 0.299,3.65, 2.305,5.656l 82.745,82.744c 2.005,2.006, 4.405,2.306, 5.657,2.306s 3.652-0.3, 5.657-2.306l 1.373-1.371l 33.941,33.94
+ l-1.373,1.373c-10.552,10.552-24.615,16.363-39.598,16.363s-29.046-5.812-39.598-16.363l-82.744-82.743
+ C 37.812,151.754, 32,137.689, 32,122.707s 5.812-29.047, 16.363-39.599l 66.745-66.745C 125.661,5.812, 139.724,0, 154.707,0
+ s 29.046,5.812, 39.598,16.363l 82.747,82.746c 10.552,10.552, 16.361,24.615, 16.361,39.598s-5.812,29.047-16.363,39.598
+ L 275.678,179.678zM 400,61c-4.862,0-9.725,1.854-13.435,5.565l-64,63.999c-7.422,7.42-7.422,19.449,0,26.869
+ c 7.42,7.422, 19.448,7.422, 26.868,0l 64-64c 7.422-7.42, 7.422-19.448,0-26.868C 409.725,62.854, 404.862,61, 400,61zM 304,0c-8.837,0-16,7.163-16,16l0,64 c0,8.837, 7.163,16, 16,16s 16-7.163, 16-16l0-64 C 320,7.163, 312.837,0, 304,0zM 464,160l-64,0 c-8.837,0-16,7.163-16,16s 7.163,16, 16,16l 64,0 c 8.837,0, 16-7.163, 16-16S 472.837,160, 464,160zM 112,387c 4.862,0, 9.725-1.854, 13.435-5.565l 64-64c 7.421-7.42, 7.421-19.449,0-26.869c-7.42-7.422-19.449-7.422-26.869,0
+ l-64,64c-7.421,7.42-7.421,19.449,0,26.869C 102.275,385.146, 107.138,387, 112,387zM 208,448c 8.837,0, 16-7.163, 16-16l0-64 c0-8.837-7.163-16-16-16s-16,7.163-16,16L 192,432 C 192,440.837, 199.163,448, 208,448zM 48,288l 64,0 c 8.837,0, 16-7.163, 16-16s-7.163-16-16-16L 48,256 c-8.837,0-16,7.163-16,16S 39.163,288, 48,288z" />
+<glyph unicode="&#xe011;" d="M 463.637,364.892l-66.745,66.744C 386.34,442.188, 372.276,448, 357.293,448s-29.047-5.812-39.598-16.363l-82.746-82.745
+ c-21.834-21.834-21.834-57.362,0-79.196l 1.373-1.373l 33.941,33.941l-1.373,1.373c-3.066,3.066-3.066,8.247,0,11.313l 82.746,82.746
+ C 353.641,399.7, 356.040,400, 357.292,400s 3.651-0.299, 5.656-2.305l 66.745-66.744c 3.066-3.067, 3.066-8.249, 0.001-11.314l-82.747-82.747
+ c-2.004-2.004-4.403-2.304-5.655-2.304s-3.651,0.3-5.656,2.306l-1.373,1.373l-33.94-33.942l 1.371-1.371
+ c 10.553-10.554, 24.615-16.364, 39.6-16.364s 29.047,5.812, 39.598,16.363l 82.747,82.746C 485.47,307.53, 485.47,343.057, 463.637,364.892
+ zM 275.678,179.678l-33.941-33.941l 1.373-1.373c 2.004-2.004, 2.305-4.403, 2.305-5.655c0-1.253-0.299-3.651-2.303-5.657
+ l-82.747-82.745c-2.005-2.005-4.405-2.305-5.657-2.305s-3.652,0.3-5.657,2.305L 82.305,117.050C 80.3,119.055, 80,121.455, 80,122.707
+ s 0.299,3.65, 2.305,5.656l 82.745,82.744c 2.005,2.006, 4.405,2.306, 5.657,2.306s 3.652-0.3, 5.657-2.306l 1.373-1.371l 33.941,33.94
+ l-1.373,1.373c-10.552,10.552-24.615,16.363-39.598,16.363s-29.046-5.812-39.598-16.363l-82.744-82.743
+ C 37.812,151.754, 32,137.689, 32,122.707s 5.812-29.047, 16.363-39.599l 66.745-66.745C 125.661,5.812, 139.724,0, 154.707,0
+ s 29.046,5.812, 39.598,16.363l 82.747,82.746c 10.552,10.552, 16.361,24.615, 16.361,39.598s-5.812,29.047-16.363,39.598
+ L 275.678,179.678zM 176,125c-4.862,0-9.725,1.855-13.435,5.564c-7.42,7.42-7.42,19.449,0,26.869l 160,160c 7.42,7.42, 19.448,7.42, 26.868,0
+ c 7.422-7.42, 7.422-19.45,0-26.87l-160-160C 185.725,126.855, 180.862,125, 176,125z" />
+<glyph unicode="&#xe010;" d="M 288,339.337L 288,448 l 168.001-168L 288,112L 288,223.048 C 92.547,227.633, 130.5,99.5, 160,0C 16,160, 53.954,345.437, 288,339.337z" />
+<glyph unicode="&#xe00f;" d="M 352,0c 29.5,99.5, 67.453,227.633-128,223.048L 224,112 L 55.999,280L 224,448l0-108.663 C 458.046,345.437, 496,160, 352,0z" />
+<glyph unicode="&#xe00e;" d="M 128.214,267.637c 52.9,0, 95.786-45.585, 95.786-101.819C 224,109.586, 181.114,64, 128.214,64
+ c-52.901,0-95.786,45.585-95.786,101.818L 32,180.364C 32,292.829, 117.77,384, 223.572,384l0-58.182 c-36.55,0-70.913-15.13-96.758-42.602
+ c-4.977-5.289-9.517-10.917-13.612-16.828C 118.094,267.208, 123.105,267.637, 128.214,267.637zM 384.214,267.637c 52.9,0, 95.786-45.585, 95.786-101.819C 480,109.586, 437.114,64, 384.214,64
+ c-52.901,0-95.786,45.585-95.786,101.818L 288,180.364C 288,292.829, 373.77,384, 479.572,384l0-58.182 c-36.55,0-70.913-15.13-96.758-42.602
+ c-4.978-5.289-9.518-10.917-13.612-16.828C 374.094,267.208, 379.105,267.637, 384.214,267.637z" />
+<glyph unicode="&#xe00c;" d="M 32,384L 480,384L 480,320L 32,320zM 192,192L 480,192L 480,128L 192,128zM 192,288L 480,288L 480,224L 192,224zM 32,96L 480,96L 480,32L 32,32zM 32,288L 144,208L 32,128 z" />
+<glyph unicode="&#xe00d;" d="M 32,384L 480,384L 480,320L 32,320zM 32,192L 320,192L 320,128L 32,128zM 32,288L 320,288L 320,224L 32,224zM 32,96L 480,96L 480,32L 32,32zM 480,288L 368,208L 480,128 z" />
+<glyph unicode="&#xe00b;" d="M 192,416L 480,416L 480,352L 192,352zM 192,256L 480,256L 480,192L 192,192zM 192,96L 480,96L 480,32L 192,32zM 160,215L 160,288L 128,288L 128,448L 64,448L 64,416L 96,416L 96,288L 64,288L 64,256L 128,256L 128,231L 64,201L 64,128L 128,128L 128,96L 64,96L 64,64L 128,64L 128,32L 64,32L 64,0L 160,0L 160,160L 96,160L 96,185 z" />
+<glyph unicode="&#xe00a;" d="M 192,416L 480,416L 480,352L 192,352zM 192,256L 480,256L 480,192L 192,192zM 192,96L 480,96L 480,32L 192,32zM 64,384A32,32 2700 1 1 128,384A32,32 2700 1 1 64,384zM 64,224A32,32 2700 1 1 128,224A32,32 2700 1 1 64,224zM 64,64A32,32 2700 1 1 128,64A32,32 2700 1 1 64,64z" />
+<glyph unicode="&#xe009;" d="M 444,288l-28,0 L 416,416 l 32,0 L 448,448 L 288,448 l0-32 l 32,0 l0-128 L 192,288 L 192,416 l 32,0 L 224,448 L 64,448 l0-32 l 32,0 l0-128 L 68,288 c-19.8,0-36-16.2-36-36l0-216 c0-19.8, 16.2-36, 36-36l 120,0
+ c 19.8,0, 36,16.2, 36,36L 224,192 l 64,0 l0-156 c0-19.8, 16.2-36, 36-36l 120,0 c 19.8,0, 36,16.2, 36,36L 480,252 C 480,271.8, 463.8,288, 444,288z M 174,32L 82,32
+ c-9.9,0-18,7.2-18,16s 8.1,16, 18,16l 92,0 c 9.9,0, 18-7.2, 18-16S 183.9,32, 174,32z M 272,224l-32,0 c-8.8,0-16,7.2-16,16s 7.2,16, 16,16l 32,0
+ c 8.8,0, 16-7.2, 16-16S 280.8,224, 272,224z M 430,32l-92,0 c-9.9,0-18,7.2-18,16s 8.1,16, 18,16l 92,0 c 9.9,0, 18-7.2, 18-16S 439.9,32, 430,32z" />
+<glyph unicode="&#xe008;" d="M 352,288l0,80 c0,8.8-7.2,16-16,16l-80,0 L 256,416 c0,17.6-14.4,32-32,32l-64,0 c-17.602,0-32-14.4-32-32l0-32 L 48,384 c-8.801,0-16-7.2-16-16l0-256
+ c0-8.8, 7.199-16, 16-16l 112,0 l0-96 l 192,0 l 96,96L 448,288 L 352,288 z M 160,415.943c 0.017,0.019, 0.036,0.039, 0.057,0.057l 63.884,0
+ c 0.021-0.018, 0.041-0.038, 0.059-0.057L 224,384 l-64,0 L 160,415.943 L 160,415.943z M 96,320l0,32 l 192,0 l0-32 L 96,320 z M 352,45.255L 352,96 l 50.745,0 L 352,45.255z
+ M 416,128l-96,0 l0-96 L 192,32 L 192,256 l 224,0 L 416,128 z" />
+<glyph unicode="&#xe031;" d="M 416,320l-96,0 l0,32 l-96,96L 32,448 l0-352 l 192,0 l0-96 l 288,0 L 512,224 L 416,320z M 416,274.745L 466.745,224L 416,224 L 416,274.745 z M 224,402.745L 274.745,352
+ L 224,352 L 224,402.745 z M 64,416l 128,0 l0-96 l 96,0 l0-192 L 64,128 L 64,416 z M 480,32L 256,32 l0,64 l 64,0 L 320,288 l 64,0 l0-96 l 96,0 L 480,32 z" />
+<glyph unicode="&#xe007;" d="M 432.204,144.934c-23.235,23.235-53.469,34.002-80.541,31.403L 320,208l 96,96c0,0, 64,64,0,128L 256,272L 96,432
+ c-64-64,0-128,0-128l 96-96l-31.663-31.663c-27.072,2.599-57.305-8.169-80.54-31.403c-37.49-37.49-42.556-93.209-11.313-124.45
+ c 31.241-31.241, 86.96-26.177, 124.45,11.313c 23.235,23.234, 34.001,53.469, 31.403,80.54L 256,144l 31.664-31.664
+ c-2.598-27.072, 8.168-57.305, 31.403-80.539c 37.489-37.49, 93.209-42.556, 124.449-11.313
+ C 474.76,51.725, 469.694,107.443, 432.204,144.934z M 176.562,100.711c-1.106-12.166-7.51-24.913-17.57-34.973
+ C 147.886,54.631, 133.452,48, 120.383,48c-5.262,0-12.649,1.114-17.958,6.424c-10.703,10.702-8.688,36.566, 11.313,56.568
+ c 11.106,11.107, 25.54,17.738, 38.609,17.738c 5.262,0, 12.649-1.114, 17.958-6.424C 176.861,115.751, 177.040,105.962, 176.562,100.711z
+ M 256,176c-17.673,0-32,14.327-32,32s 14.327,32, 32,32s 32-14.327, 32-32S 273.673,176, 256,176z M 409.576,54.424
+ c-5.31-5.31-12.696-6.424-17.958-6.424c-13.069,0-27.503,6.631-38.609,17.738c-10.061,10.060-16.464,22.807-17.569,34.973
+ c-0.479,5.251-0.3,15.040, 6.257,21.596c 5.309,5.311, 12.695,6.424, 17.958,6.424c 13.068,0, 27.503-6.631, 38.608-17.737
+ C 418.265,90.99, 420.279,65.126, 409.576,54.424z" />
+<glyph unicode="&#xe006;" d="M 32,384L 480,384L 480,320L 32,320zM 32,192L 480,192L 480,128L 32,128zM 32,288L 480,288L 480,224L 32,224zM 32,96L 480,96L 480,32L 32,32z" />
+<glyph unicode="&#xe004;" d="M 32,384L 480,384L 480,320L 32,320zM 32,192L 480,192L 480,128L 32,128zM 128,288L 384,288L 384,224L 128,224zM 128,96L 384,96L 384,32L 128,32z" />
+<glyph unicode="&#xe005;" d="M 32,384L 480,384L 480,320L 32,320zM 32,192L 480,192L 480,128L 32,128zM 192,288L 480,288L 480,224L 192,224zM 192,96L 480,96L 480,32L 192,32z" />
+<glyph unicode="&#xe003;" d="M 32,384L 480,384L 480,320L 32,320zM 32,192L 480,192L 480,128L 32,128zM 32,288L 320,288L 320,224L 32,224zM 32,96L 320,96L 320,32L 32,32z" />
+<glyph unicode="&#xe02d;" d="M 480,224l-4.571,0 L 347.062,224 c-25.039,17.71-57.215,27.43-91.062,27.43c-44.603,0-82.286,25.121-82.286,54.856
+ c0,29.735, 37.683,54.857, 82.286,54.857c 37.529,0, 70.154-17.788, 79.56-41.143l 56.508,0 c-3.965,25.322-18.79,48.984-42.029,66.413
+ C 324.599,405.493, 291.201,416, 256,416c-35.202,0-68.598-10.507-94.037-29.587c-27.394-20.545-43.106-49.751-43.106-80.127
+ s 15.712-59.582, 43.106-80.127c 0.978-0.733, 1.971-1.449, 2.973-2.158L 36.571,224.001 L 32,224.001 l0-32 l 256.266,0 c 29.104-8.553, 50.021-28.135, 50.021-50.286
+ c0-29.734-37.684-54.855-82.286-54.855c-37.53,0-70.154,17.787-79.559,41.143l-56.508,0 c 3.965-25.32, 18.791-48.984, 42.030-66.413
+ C 187.402,42.508, 220.798,32, 256,32c 35.201,0, 68.599,10.508, 94.037,29.587c 27.395,20.545, 43.104,49.751, 43.104,80.127
+ c0,17.649-5.327,34.896-15.147,50.286L 480,192 L 480,224 z" />
+<glyph unicode="&#xe02c;" d="M 96,64l 288,0 l0-32 L 96,32 L 96,64 zM 320,416l0-192 c0-15.656-7.35-30.812-20.695-42.676C 283.834,167.573, 262.771,160, 240,160c-22.772,0-43.834,7.573-59.304,21.324
+ C 167.35,193.188, 160,208.344, 160,224L 160,416 L 96,416 l0-192 c0-70.691, 64.471-128, 144-128c 79.529,0, 144,57.309, 144,128L 384,416 L 320,416 z" />
+<glyph unicode="&#xe02b;" d="M 416,416l0-32 l-72,0 L 216,64l 72,0 l0-32 L 64,32 l0,32 l 72,0 L 264,384l-72,0 L 192,416 L 416,416 z" />
+<glyph unicode="&#xe02a;" d="M 312.721,232.909C 336.758,251.984, 352,280.337, 352,312c0,57.438-50.145,104-112,104L 128,416 l0-384 l 144,0
+ c 61.856,0, 112,46.562, 112,104C 384,180.098, 354.441,217.781, 312.721,232.909z M 192,328c0,13.255, 10.745,24, 24,24l 33.602,0
+ C 270.809,352, 288,330.51, 288,304s-17.191-48-38.398-48L 192,256 L 192,328 z M 273.6,96L 216,96 c-13.255,0-24,10.745-24,24l0,72 l 81.6,0
+ c 21.209,0, 38.4-21.49, 38.4-48S 294.809,96, 273.6,96z" />
+<glyph unicode="&#xe001;" d="M 425.373,358.627l-66.746,66.745C 346.183,437.818, 321.6,448, 304,448L 96,448 c-17.6,0-32-14.4-32-32l0-384 c0-17.6, 14.4-32, 32-32l 320,0
+ c 17.6,0, 32,14.4, 32,32L 448,304 C 448,321.6, 437.817,346.182, 425.373,358.627z M 402.745,336.001c 3.396-3.398, 6.896-9.581, 9.447-16.001L 320,320
+ L 320,412.193 c 6.42-2.55, 12.602-6.050, 16-9.448L 402.745,336.001z M 415.942,32L 96.057,32 c-0.020,0.017-0.041,0.038-0.057,0.058L 96,415.943
+ c 0.017,0.020, 0.038,0.041, 0.057,0.057L 288,416 l0-128 l 128,0 l0-255.942 C 415.983,32.038, 415.962,32.017, 415.942,32z" />
+<glyph unicode="&#xe000;" d="M 480,40L 480,335.969 L 368.031,448L 72,448 c-22.091,0-40-17.908-40-40l0-368 c0-22.092, 17.909-40, 40-40l 368,0
+ C 462.092,0, 480,17.908, 480,40z M 288,384l 32,0 l0-96 l-32,0 L 288,384 z M 352,64L 160,64 L 160,191.941 c 0.017,0.021, 0.038,0.041, 0.058,0.059l 191.885,0
+ c 0.020-0.018, 0.041-0.038, 0.058-0.059L 352,64L 352,64z M 416,64l-32,0 L 384,192 c0,17.6-14.4,32-32,32L 160,224 c-17.6,0-32-14.4-32-32l0-128 L 96,64 L 96,384
+ l 32,0 l0-96 c0-17.6, 14.4-32, 32-32l 160,0 c 17.6,0, 32,14.4, 32,32l0,85.505 l 64-64.036L 416,64 z" />
+<glyph unicode="&#xe01b;" d="M 32,384l0-352 l 448,0 L 480,384 L 32,384 z M 192,160l0,64 l 128,0 l0-64 L 192,160 z M 320,128l0-64 L 192,64 l0,64 L 320,128 z M 320,320l0-64 L 192,256 l0,64 L 320,320 z M 160,320l0-64 L 64,256 l0,64 L 160,320
+ z M 64,224l 96,0 l0-64 L 64,160 L 64,224 z M 352,224l 96,0 l0-64 l-96,0 L 352,224 z M 352,256l0,64 l 96,0 l0-64 L 352,256 z M 64,128l 96,0 l0-64 L 64,64 L 64,128 z M 352,64l0,64 l 96,0 l0-64 L 352,64 z" />
+<glyph unicode="&#xe021;" d="M 256,410c 49.683,0, 96.391-19.347, 131.521-54.478S 442,273.683, 442,224s-19.348-96.391-54.479-131.521S 305.683,38, 256,38
+ s-96.391,19.348-131.522,54.479S 70,174.317, 70,224s 19.347,96.391, 54.478,131.522S 206.317,410, 256,410 M 256,448
+ C 132.288,448, 32,347.712, 32,224s 100.288-224, 224-224s 224,100.288, 224,224S 379.712,448, 256,448L 256,448zM 160,288A32,32 2700 1 1 224,288A32,32 2700 1 1 160,288zM 288,288A32,32 2700 1 1 352,288A32,32 2700 1 1 288,288zM 256,152c-50.92,0-96.28,18.437-125.583,47.164C 141.98,140.36, 193.806,96, 256,96c 62.194,0, 114.020,44.36, 125.584,103.164
+ C 352.28,170.437, 306.92,152, 256,152z" />
+<glyph unicode="&#xe023;" d="M 240,288L 144,384L 208,448L 32,448L 32,272L 96,336L 192,240 zM 320,240L 416,336L 480,272L 480,448L 304,448L 368,384L 272,288 zM 272,160L 368,64L 304,0L 480,0L 480,176L 416,112L 320,208 zM 192,208L 96,112L 32,176L 32,0L 208,0L 144,64L 240,160 z" />
+<glyph unicode="&#xe01c;" d="M 32,256L 480,256L 480,192L 32,192z" />
+<glyph unicode="&#xe01d;" d="M 32,96l 256,0 l0-64 L 32,32 L 32,96 z M 384,384L 273.721,384 l-91.883-256l-66.144,0 l 91.881,256L 96,384 L 96,448 l 288,0 L 384,384 z M 464.887,32L 400,96.887
+ L 335.113,32L 304,63.113L 368.887,128L 304,192.887L 335.113,224L 400,159.113L 464.887,224L 496,192.887L 431.113,128L 496,63.113
+ L 464.887,32z" />
+<glyph unicode="&#xe022;" d="M 128,416l 256,0 l0-64 L 128,352 L 128,416 z M 448,320L 64,320 c-17.6,0-32-14.4-32-32l0-128 c0-17.6, 14.398-32, 32-32l 64,0 l0-96 l 256,0 l0,96 l 64,0
+ c 17.6,0, 32,14.4, 32,32L 480,288 C 480,305.6, 465.6,320, 448,320z M 352,64L 160,64 L 160,192 l 192,0 L 352,64 z M 455.2,272c0-12.813-10.387-23.2-23.199-23.2
+ S 408.8,259.187, 408.8,272s 10.389,23.2, 23.201,23.2C 444.814,295.2, 455.2,284.813, 455.2,272z" />
+<glyph unicode="&#xe02e;" d="M 192,416c-61.856,0-112-50.144-112-112s 50.144-112, 112-112l0-160 l 64,0 L 256,352 l 32,0 l0-320 l 64,0 L 352,352 l 64,0 L 416,416 L 192,416 z" />
+<glyph unicode="&#xe02f;" d="M 224,416c-61.856,0-112-50.144-112-112s 50.144-112, 112-112l0-160 l 64,0 L 288,352 l 32,0 l0-320 l 64,0 L 384,352 l 64,0 L 448,416 L 224,416 zM 32,32L 144,128L 32,224 z" />
+<glyph unicode="&#xe030;" d="M 160,416C 98.144,416, 48,365.856, 48,304s 50.144-112, 112-112l0-160 l 64,0 L 224,352 l 32,0 l0-320 l 64,0 L 320,352 l 64,0 L 384,416 L 160,416 zM 480,224L 368,128L 480,32 z" />
+<glyph unicode="&#xe026;" d="M 256,288L 320,288L 320,256L 256,256zM 256,96L 320,96L 320,64L 256,64zM 288,192L 352,192L 352,160L 288,160zM 384,192L 384,96L 352,96L 352,64L 416,64L 416,192 zM 192,192L 256,192L 256,160L 192,160zM 160,96L 224,96L 224,64L 160,64zM 160,288L 224,288L 224,256L 160,256zM 96,384L 96,256L 128,256L 128,352L 160,352L 160,384 zM 352,256L 416,256L 416,384L 384,384L 384,288L 352,288 zM 32,448l0-448 l 448,0 L 480,448 L 32,448 z M 448,32L 64,32 L 64,416 l 384,0 L 448,32 zM 96,192L 96,64L 128,64L 128,160L 160,160L 160,192 zM 288,384L 352,384L 352,352L 288,352zM 192,384L 256,384L 256,352L 192,352z" />
+<glyph unicode="&#xe027;" d="M 408,448l 8-192L 96,256 l 8,192l 16,0 l 8-160l 256,0 l 8,160L 408,448 z M 104,0l-8,160l 320,0 l-8-160l-16,0 l-8,128L 128,128 l-8-128L 104,0 zM 32,224L 96,224L 96,192L 32,192zM 128,224L 192,224L 192,192L 128,192zM 224,224L 288,224L 288,192L 224,192zM 320,224L 384,224L 384,192L 320,192zM 416,224L 480,224L 480,192L 416,192z" />
+<glyph unicode="&#xe024;" d="M 480,416L 480,448 l-96,0 c-17.601,0-32-14.4-32-32l0-160 c0-7.928, 2.929-15.201, 7.748-20.807L 208,105l-71,74l-41-35l 112-144l 208,224l 64,0
+ l0,32 l-96,0 L 384,416 L 480,416 zM 128,224l 32,0 L 160,416 c0,17.6-14.4,32-32,32L 64,448 c-17.6,0-32-14.4-32-32l0-192 l 32,0 l0,96 l 64,0 L 128,224 z M 64,352L 64,416 l 64,0 l0-64 L 64,352 zM 320,256l0,48 c0,17.6-4.4,32-22,32c 17.6,0, 22,14.4, 22,32L 320,416 c0,17.6-14.4,32-32,32l-96,0 l0-224 l 96,0 C 305.6,224, 320,238.4, 320,256z
+ M 224,416l 64,0 l0-64 l-64,0 L 224,416 z M 224,320l 64,0 l0-64 l-64,0 L 224,320 z" />
+<glyph unicode="&#xe025;" d="M 224,224l-64,0 l0,64 l 64,0 l0,64 l 64,0 l0-64 l 64,0 l0-64 l-64,0 l0-64 l-64,0 L 224,224 z M 480,192l0-160 L 32,32 L 32,192 l 64,0 l0-96 l 320,0 l0,96 L 480,192 z" />
+<glyph unicode="&#xe017;" d="M 208,128L 112,224L 208,320L 176,352L 48,224L 176,96 zM 336,352L 304,320L 400,224L 304,128L 336,96L 464,224 z" />
+<glyph unicode="&#xe016;" d="M 224,128l 64,0 l0-64 l-64,0 L 224,128 z M 352,352c 17.673,0, 32-14.327, 32-32l0-83 l-114-77l-46,0 l0,32 l 96,64l0,32 L 160,288 l0,64 L 352,352 z M 256,448
+ c-59.833,0-116.083-23.3-158.392-65.608C 55.301,340.083, 32,283.833, 32,224c0-59.832, 23.301-116.084, 65.608-158.392
+ C 139.917,23.3, 196.167,0, 256,0c 59.832,0, 116.084,23.3, 158.392,65.608C 456.7,107.916, 480,164.168, 480,224
+ c0,59.833-23.3,116.083-65.608,158.392C 372.084,424.7, 315.832,448, 256,448z" />
+<glyph unicode="&#xe014;" d="M 448,416L 64,416 c-17.6,0-32-14.4-32-32l0-320 c0-17.6, 14.4-32, 32-32l 384,0 c 17.6,0, 32,14.4, 32,32L 480,384 C 480,401.6, 465.6,416, 448,416z
+ M 448,64.058c-0.006-0.007-0.015-0.014-0.021-0.021L 352,224l-80-64L 160,304L 64.016,64.042c-0.005,0.005-0.011,0.011-0.016,0.016
+ L 64,383.943 c 0.017,0.020, 0.038,0.041, 0.057,0.057l 383.885,0 c 0.020-0.017, 0.041-0.038, 0.058-0.058L 448,64.058 zM 320,304A48,48 2700 1 1 416,304A48,48 2700 1 1 320,304z" />
+<glyph unicode="&#xe015;" d="M 448,416L 64,416 c-17.6,0-32-14.4-32-32l0-320 c0-17.6, 14.4-32, 32-32l 384,0 c 17.6,0, 32,14.4, 32,32L 480,384 C 480,401.6, 465.6,416, 448,416z
+ M 128,64L 64,64 l0,64 l 64,0 L 128,64 z M 128,192L 64,192 l0,64 l 64,0 L 128,192 z M 128,320L 64,320 L 64,384 l 64,0 L 128,320 z M 352,64L 160,64 L 160,384 l 192,0 L 352,64 z M 448,64l-64,0 l0,64 l 64,0 L 448,64 z
+ M 448,192l-64,0 l0,64 l 64,0 L 448,192 z M 448,320l-64,0 L 384,384 l 64,0 L 448,320 zM 192,320L 192,128L 336,224 z" />
+<glyph unicode="&#xe018;" d="M 38.899,327.688l 40.707-25.441C 105.007,342.804, 144,373.974, 190.21,389.37l-15.183,45.547
+ C 118.153,415.968, 70.163,377.604, 38.899,327.688zM 336.973,434.917L 321.79,389.37c 46.211-15.396, 85.202-46.566, 110.604-87.124l 40.706,25.441
+ C 441.837,377.604, 393.847,415.968, 336.973,434.917zM 303.987,127.996c-2.404,0-4.846,0.545-7.143,1.693L 224,166.111L 224,272 c0,8.836, 7.164,16, 16,16s 16-7.164, 16-16l0-86.111
+ l 55.155-27.578c 7.903-3.951, 11.107-13.562, 7.155-21.466C 315.508,131.238, 309.856,127.997, 303.987,127.996zM 256,384C 149.961,384, 64,298.039, 64,192c0-106.039, 85.961-192, 192-192c 106.039,0, 192,85.961, 192,192
+ C 448,298.039, 362.039,384, 256,384z M 256,48c-79.529,0-144,64.471-144,144c0,79.529, 64.471,144, 144,144c 79.529,0, 144-64.471, 144-144
+ C 400,112.471, 335.529,48, 256,48z" />
+<glyph unicode="&#xe019;" d="M 32,252.127c 22.659,24.96, 48.581,46.18, 76.636,62.562C 153.802,341.061, 204.759,355, 256,355
+ c 51.24,0, 102.198-13.939, 147.363-40.312c 28.056-16.382, 53.978-37.602, 76.637-62.562l0,58.716
+ c-16.505,14.059-34.062,26.57-52.434,37.297C 375.063,378.796, 315.737,395, 256,395s-119.064-16.204-171.567-46.86
+ C 66.062,337.413, 48.505,324.901, 32,310.842L 32,252.127 zM 256,320c-91.598,0-172.919-50.278-224-128c 51.081-77.724, 132.402-128, 224-128c 91.598,0, 172.919,50.276, 224,128
+ C 428.919,269.722, 347.598,320, 256,320z M 256,224c0-17.673-14.327-32-32-32s-32,14.327-32,32c0,17.674, 14.327,32, 32,32
+ S 256,241.674, 256,224z M 364.033,131.669C 330.316,111.982, 293.969,102, 256,102s-74.316,9.982-108.033,29.669
+ C 122.19,146.721, 98.659,167.324, 78.91,192c 19.749,24.675, 43.28,45.279, 69.058,60.33c 6.638,3.876, 13.379,7.37, 20.213,10.491
+ C 162.925,250.95, 160,237.817, 160,224c0-53.020, 42.981-96, 96-96c 53.020,0, 96,42.98, 96,96c0,13.817-2.925,26.95-8.18,38.821
+ c 6.834-3.122, 13.575-6.615, 20.213-10.491c 25.777-15.051, 49.308-35.655, 69.058-60.33
+ C 413.342,167.324, 389.811,146.721, 364.033,131.669z" />
+<glyph unicode="&#xe01a;" d="M 325.584,338.083C 313.278,379.064, 311.146,384, 272,384l-32,0 c-39.809,0-41.332-5.076-54.209-48c0-0.001,0-0.001-0.001-0.002
+ L 113.791,96l 56.818,0 l 28.8,96l 113.183,0 l 28.8-96l 56.815,0 L 325.584,338.083z M 218.609,256l 19.2,68c 5.043,16.809, 18.19,15, 18.19,15
+ s 13.147,1.809, 18.19-15l 0.002,0 l 19.2-68L 218.609,256 z" />
+<glyph unicode="&#xe028;" d="M 288,448 C 411.712,448 512,347.712 512,224 C 512,100.288 411.712,0 288,0 L 288,48 C 335.012,48 379.209,66.307 412.451,99.549 C 445.693,132.791 464,176.988 464,224 C 464,271.011 445.693,315.209 412.451,348.451 C 379.209,381.693 335.012,400 288,400 C 240.989,400 196.791,381.693 163.549,348.451 C 137.979,322.882 121.258,290.828 114.896,256 L 208,256 L 96,128 L -16,256 L 66.285,256 C 81.815,364.551 175.154,448 288,448 ZM 384,256 L 384,192 L 256,192 L 256,352 L 320,352 L 320,256 Z" />
+<glyph unicode="&#xe002;" d="M 512,183.771l0,80.458 l-79.572,7.957c-4.093,15.021-10.044,29.274-17.605,42.49l 52.298,63.919L 410.595,435.12l-63.918-52.298
+ c-13.217,7.562-27.471,13.513-42.491,17.604L 296.229,480l-80.458,0 l-7.957-79.573c-15.021-4.093-29.274-10.043-42.49-17.604
+ L 101.405,435.12L 44.88,378.595l 52.298-63.918c-7.562-13.216-13.513-27.47-17.605-42.49L0,264.229l0-80.458 l 79.573-7.957
+ c 4.093-15.021, 10.043-29.274, 17.605-42.491L 44.88,69.405l 56.524-56.524l 63.919,52.298c 13.216-7.562, 27.47-13.514, 42.49-17.605
+ L 215.771-32l 80.458,0 l 7.957,79.572c 15.021,4.093, 29.274,10.044, 42.491,17.605l 63.918-52.298l 56.524,56.524l-52.298,63.918
+ c 7.562,13.217, 13.514,27.471, 17.605,42.49L 512,183.771z M 352,192l-64-64l-64,0 l-64,64l0,64 l 64,64l 64,0 l 64-64L 352,192 z" />
+<glyph unicode="&#xe01f;" d="M 384,377 L 384,352 L 448,352 L 448,320 L 352,320 L 352,393 L 416,423 L 416,448 L 352,448 L 352,480 L 448,480 L 448,407 ZM 338,352L 270,352L 176,258L 82,352L 14,352L 142,224L 14,96L 82,96L 176,190L 270,96L 338,96L 210,224 z" />
+<glyph unicode="&#xe01e;" d="M 384,25 L 384,0 L 448,0 L 448-32 L 352-32 L 352,41 L 416,71 L 416,96 L 352,96 L 352,128 L 448,128 L 448,55 ZM 338,352L 270,352L 176,258L 82,352L 14,352L 142,224L 14,96L 82,96L 176,190L 270,96L 338,96L 210,224 z" />
+<glyph unicode="&#xe035;" d="M 352,288l0,80 c0,8.8-7.2,16-16,16l-80,0 L 256,416 c0,17.6-14.4,32-32,32l-64,0 c-17.602,0-32-14.4-32-32l0-32 L 48,384 c-8.801,0-16-7.2-16-16
+ l0-256 c0-8.8, 7.199-16, 16-16l 112,0 l0-96 l 288,0 L 448,288 L 352,288 z M 160,415.943c 0.017,0.019, 0.036,0.039, 0.057,0.057l 63.884,0
+ c 0.021-0.018, 0.041-0.038, 0.059-0.057L 224,384 l-64,0 L 160,415.943 z M 96,320l0,32 l 192,0 l0-32 L 96,320 z M 416,32L 192,32 L 192,256 l 224,0 L 416,32 zM 224,224L 224,160L 240,160L 256,192L 288,192L 288,96L 264,96L 264,64L 344,64L 344,96L 320,96L 320,192L 352,192L 368,160L 384,160L 384,224 z" data-tags="pastetext" />
+<glyph unicode="&#xe032;" d="M 384,352L 416,352L 416,320L 384,320zM 320,288L 352,288L 352,256L 320,256zM 320,224L 352,224L 352,192L 320,192zM 320,160L 352,160L 352,128L 320,128zM 256,224L 288,224L 288,192L 256,192zM 256,160L 288,160L 288,128L 256,128zM 192,160L 224,160L 224,128L 192,128zM 384,288L 416,288L 416,256L 384,256zM 384,224L 416,224L 416,192L 384,192zM 384,160L 416,160L 416,128L 384,128zM 384,96L 416,96L 416,64L 384,64zM 320,96L 352,96L 352,64L 320,64zM 256,96L 288,96L 288,64L 256,64zM 192,96L 224,96L 224,64L 192,64zM 128,96L 160,96L 160,64L 128,64z" data-tags="resize, dots" />
+<glyph unicode="&#xe034;" d="M 464,416L 256,416L 240,448L 64,448L 32,384L 480,384 zM 420.17,128L 464,128 l 16,224L 32,352 l 32-320l 178.040,0 C 189.599,50.888, 152,101.133, 152,160c0,74.991, 61.009,136, 136,136
+ c 74.99,0, 136-61.009, 136-136C 424,149.161, 422.689,138.425, 420.17,128zM 437.498,55.125l-67.248,55.346C 378.977,124.932, 384,141.878, 384,160c0,53.020-42.98,96-96,96s-96-42.98-96-96
+ s 42.98-96, 96-96c 18.122,0, 35.069,5.023, 49.529,13.75l 55.346-67.248c 11.481-13.339, 31.059-14.070, 43.503-1.626l 2.746,2.746
+ C 451.568,24.066, 450.837,43.644, 437.498,55.125z M 288,98c-34.242,0-62,27.758-62,62s 27.758,62, 62,62s 62-27.758, 62-62
+ S 322.242,98, 288,98z" data-tags="browse" />
+<glyph unicode="&#x20;" horiz-adv-x="256" />
+</font></defs></svg> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/tinymce-small.eot b/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/tinymce-small.eot
new file mode 100644
index 000000000..60e2d2e5c
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/tinymce-small.eot
Binary files differ
diff --git a/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/tinymce-small.svg b/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/tinymce-small.svg
new file mode 100644
index 000000000..930c48dce
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/tinymce-small.svg
@@ -0,0 +1,62 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
+<svg xmlns="http://www.w3.org/2000/svg">
+<metadata>Generated by IcoMoon</metadata>
+<defs>
+<font id="tinymce-small" horiz-adv-x="512">
+<font-face units-per-em="512" ascent="480" descent="-32" />
+<missing-glyph horiz-adv-x="512" />
+<glyph unicode="&#x20;" d="" horiz-adv-x="256" />
+<glyph unicode="&#xe000;" d="M480 40v295.969l-111.969 112.031h-296.031c-22.091 0-40-17.908-40-40v-368c0-22.092 17.909-40 40-40h368c22.092 0 40 17.908 40 40zM288 384h32v-96h-32v96zM352 64h-192v127.941c0.017 0.021 0.038 0.041 0.058 0.059h191.885c0.020-0.018 0.041-0.038 0.058-0.059l-0.001-127.941zM416 64h-32v128c0 17.6-14.4 32-32 32h-192c-17.6 0-32-14.4-32-32v-128h-32v320h32v-96c0-17.6 14.4-32 32-32h160c17.6 0 32 14.4 32 32v85.505l64-64.036v-245.469z" />
+<glyph unicode="&#xe001;" d="M425.373 358.627l-66.746 66.745c-12.444 12.446-37.027 22.628-54.627 22.628h-208c-17.6 0-32-14.4-32-32v-384c0-17.6 14.4-32 32-32h320c17.6 0 32 14.4 32 32v272c0 17.6-10.183 42.182-22.627 54.627zM402.745 336.001c3.396-3.398 6.896-9.581 9.447-16.001h-92.192v92.193c6.42-2.55 12.602-6.050 16-9.448l66.745-66.744zM415.942 32h-319.885c-0.020 0.017-0.041 0.038-0.057 0.058v383.885c0.017 0.020 0.038 0.041 0.057 0.057h191.943v-128h128v-255.942c-0.017-0.020-0.038-0.041-0.058-0.058z" />
+<glyph unicode="&#xe002;" d="M512 183.771v80.458l-79.572 7.957c-4.093 15.021-10.044 29.274-17.605 42.49l52.298 63.919-56.526 56.525-63.918-52.298c-13.217 7.562-27.471 13.513-42.491 17.604l-7.957 79.574h-80.458l-7.957-79.573c-15.021-4.093-29.274-10.043-42.49-17.604l-63.919 52.297-56.525-56.525 52.298-63.918c-7.562-13.216-13.513-27.47-17.605-42.49l-79.573-7.958v-80.458l79.573-7.957c4.093-15.021 10.043-29.274 17.605-42.491l-52.298-63.918 56.524-56.524 63.919 52.298c13.216-7.562 27.47-13.514 42.49-17.605l7.958-79.574h80.458l7.957 79.572c15.021 4.093 29.274 10.044 42.491 17.605l63.918-52.298 56.524 56.524-52.298 63.918c7.562 13.217 13.514 27.471 17.605 42.49l79.574 7.96zM352 192l-64-64h-64l-64 64v64l64 64h64l64-64v-64z" />
+<glyph unicode="&#xe003;" d="M32 384h448v-64h-448zM32 192h448v-64h-448zM32 288h288v-64h-288zM32 96h288v-64h-288z" />
+<glyph unicode="&#xe004;" d="M32 384h448v-64h-448zM32 192h448v-64h-448zM128 288h256v-64h-256zM128 96h256v-64h-256z" />
+<glyph unicode="&#xe005;" d="M32 384h448v-64h-448zM32 192h448v-64h-448zM192 288h288v-64h-288zM192 96h288v-64h-288z" />
+<glyph unicode="&#xe006;" d="M32 384h448v-64h-448zM32 192h448v-64h-448zM32 288h448v-64h-448zM32 96h448v-64h-448z" />
+<glyph unicode="&#xe007;" d="M432.204 144.934c-23.235 23.235-53.469 34.002-80.541 31.403l-31.663 31.663 96 96c0 0 64 64 0 128l-160-160-160 160c-64-64 0-128 0-128l96-96-31.663-31.663c-27.072 2.599-57.305-8.169-80.54-31.403-37.49-37.49-42.556-93.209-11.313-124.45 31.241-31.241 86.96-26.177 124.45 11.313 23.235 23.234 34.001 53.469 31.403 80.54l31.663 31.663 31.664-31.664c-2.598-27.072 8.168-57.305 31.403-80.539 37.489-37.49 93.209-42.556 124.449-11.313 31.244 31.241 26.178 86.959-11.312 124.45zM176.562 100.711c-1.106-12.166-7.51-24.913-17.57-34.973-11.106-11.107-25.54-17.738-38.609-17.738-5.262 0-12.649 1.114-17.958 6.424-10.703 10.702-8.688 36.566 11.313 56.568 11.106 11.107 25.54 17.738 38.609 17.738 5.262 0 12.649-1.114 17.958-6.424 6.556-6.555 6.735-16.344 6.257-21.595zM256 176c-17.673 0-32 14.327-32 32s14.327 32 32 32 32-14.327 32-32-14.327-32-32-32zM409.576 54.424c-5.31-5.31-12.696-6.424-17.958-6.424-13.069 0-27.503 6.631-38.609 17.738-10.061 10.060-16.464 22.807-17.569 34.973-0.479 5.251-0.3 15.040 6.257 21.596 5.309 5.311 12.695 6.424 17.958 6.424 13.068 0 27.503-6.631 38.608-17.737 20.002-20.004 22.016-45.868 11.313-56.57z" />
+<glyph unicode="&#xe008;" d="M352 288v80c0 8.8-7.2 16-16 16h-80v32c0 17.6-14.4 32-32 32h-64c-17.602 0-32-14.4-32-32v-32h-80c-8.801 0-16-7.2-16-16v-256c0-8.8 7.199-16 16-16h112v-96h192l96 96v192h-96zM160 415.943c0.017 0.019 0.036 0.039 0.057 0.057h63.884c0.021-0.018 0.041-0.038 0.059-0.057v-31.943h-64v31.943zM96 320v32h192v-32h-192zM352 45.255v50.745h50.745l-50.745-50.745zM416 128h-96v-96h-128v224h224v-128z" />
+<glyph unicode="&#xe009;" d="M444 288h-28v128h32v32h-160v-32h32v-128h-128v128h32v32h-160v-32h32v-128h-28c-19.8 0-36-16.2-36-36v-216c0-19.8 16.2-36 36-36h120c19.8 0 36 16.2 36 36v156h64v-156c0-19.8 16.2-36 36-36h120c19.8 0 36 16.2 36 36v216c0 19.8-16.2 36-36 36zM174 32h-92c-9.9 0-18 7.2-18 16s8.1 16 18 16h92c9.9 0 18-7.2 18-16s-8.1-16-18-16zM272 224h-32c-8.8 0-16 7.2-16 16s7.2 16 16 16h32c8.8 0 16-7.2 16-16s-7.2-16-16-16zM430 32h-92c-9.9 0-18 7.2-18 16s8.1 16 18 16h92c9.9 0 18-7.2 18-16s-8.1-16-18-16z" />
+<glyph unicode="&#xe00a;" d="M192 416h288v-64h-288zM192 256h288v-64h-288zM192 96h288v-64h-288zM64 384c0-17.673 14.327-32 32-32s32 14.327 32 32c0 17.673-14.327 32-32 32-17.673 0-32-14.327-32-32zM64 224c0-17.673 14.327-32 32-32s32 14.327 32 32c0 17.673-14.327 32-32 32-17.673 0-32-14.327-32-32zM64 64c0-17.673 14.327-32 32-32s32 14.327 32 32c0 17.673-14.327 32-32 32-17.673 0-32-14.327-32-32z" />
+<glyph unicode="&#xe00b;" d="M192 416h288v-64h-288zM192 256h288v-64h-288zM192 96h288v-64h-288zM160 215v73h-32v160h-64v-32h32v-128h-32v-32h64v-25l-64-30v-73h64v-32h-64v-32h64v-32h-64v-32h96v160h-64v25z" />
+<glyph unicode="&#xe00c;" d="M32 384h448v-64h-448zM192 192h288v-64h-288zM192 288h288v-64h-288zM32 96h448v-64h-448zM32 288l112-80-112-80z" />
+<glyph unicode="&#xe00d;" d="M32 384h448v-64h-448zM32 192h288v-64h-288zM32 288h288v-64h-288zM32 96h448v-64h-448zM480 288l-112-80 112-80z" />
+<glyph unicode="&#xe00e;" d="M128.214 267.637c52.9 0 95.786-45.585 95.786-101.819 0-56.232-42.886-101.818-95.786-101.818-52.901 0-95.786 45.585-95.786 101.818l-0.428 14.546c0 112.465 85.77 203.636 191.572 203.636v-58.182c-36.55 0-70.913-15.13-96.758-42.602-4.977-5.289-9.517-10.917-13.612-16.828 4.892 0.82 9.903 1.249 15.012 1.249zM384.214 267.637c52.9 0 95.786-45.585 95.786-101.819 0-56.232-42.886-101.818-95.786-101.818-52.901 0-95.786 45.585-95.786 101.818l-0.428 14.546c0 112.465 85.77 203.636 191.572 203.636v-58.182c-36.55 0-70.913-15.13-96.758-42.602-4.978-5.289-9.518-10.917-13.612-16.828 4.892 0.82 9.903 1.249 15.012 1.249z" />
+<glyph unicode="&#xe00f;" d="M352 0c29.5 99.5 67.453 227.633-128 223.048v-111.048l-168.001 168 168.001 168v-108.663c234.046 6.1 272-179.337 128-339.337z" />
+<glyph unicode="&#xe010;" d="M288 339.337v108.663l168.001-168-168.001-168v111.048c-195.453 4.585-157.5-123.548-128-223.048-144 160-106.046 345.437 128 339.337z" />
+<glyph unicode="&#xe011;" d="M463.637 364.892l-66.745 66.744c-10.552 10.552-24.616 16.364-39.599 16.364s-29.047-5.812-39.598-16.363l-82.746-82.745c-21.834-21.834-21.834-57.362 0-79.196l1.373-1.373 33.941 33.941-1.373 1.373c-3.066 3.066-3.066 8.247 0 11.313l82.746 82.746c2.005 2.004 4.404 2.304 5.656 2.304s3.651-0.299 5.656-2.305l66.745-66.744c3.066-3.067 3.066-8.249 0.001-11.314l-82.747-82.747c-2.004-2.004-4.403-2.304-5.655-2.304s-3.651 0.3-5.656 2.306l-1.373 1.373-33.94-33.942 1.371-1.371c10.553-10.554 24.615-16.364 39.6-16.364s29.047 5.812 39.598 16.363l82.747 82.746c21.831 21.833 21.831 57.36-0.002 79.195zM275.678 179.678l-33.941-33.941 1.373-1.373c2.004-2.004 2.305-4.403 2.305-5.655 0-1.253-0.299-3.651-2.303-5.657l-82.747-82.745c-2.005-2.005-4.405-2.305-5.657-2.305s-3.652 0.3-5.657 2.305l-66.746 66.743c-2.005 2.005-2.305 4.405-2.305 5.657s0.299 3.65 2.305 5.656l82.745 82.744c2.005 2.006 4.405 2.306 5.657 2.306s3.652-0.3 5.657-2.306l1.373-1.371 33.941 33.94-1.373 1.373c-10.552 10.552-24.615 16.363-39.598 16.363s-29.046-5.812-39.598-16.363l-82.744-82.743c-10.553-10.552-16.365-24.617-16.365-39.599s5.812-29.047 16.363-39.599l66.745-66.745c10.553-10.551 24.616-16.363 39.599-16.363s29.046 5.812 39.598 16.363l82.747 82.746c10.552 10.552 16.361 24.615 16.361 39.598s-5.812 29.047-16.363 39.598l-1.372 1.373zM176 125c-4.862 0-9.725 1.855-13.435 5.564-7.42 7.42-7.42 19.449 0 26.869l160 160c7.42 7.42 19.448 7.42 26.868 0 7.422-7.42 7.422-19.45 0-26.87l-160-160c-3.708-3.708-8.571-5.563-13.433-5.563z" />
+<glyph unicode="&#xe012;" d="M463.637 364.892l-66.745 66.744c-10.552 10.552-24.616 16.364-39.599 16.364s-29.047-5.812-39.598-16.363l-82.746-82.745c-21.834-21.834-21.834-57.362 0-79.196l1.373-1.373 33.941 33.941-1.373 1.373c-3.066 3.066-3.066 8.247 0 11.313l82.746 82.746c2.005 2.004 4.404 2.304 5.656 2.304s3.651-0.299 5.656-2.305l66.745-66.744c3.066-3.067 3.066-8.249 0.001-11.314l-82.747-82.747c-2.004-2.004-4.403-2.304-5.655-2.304s-3.651 0.3-5.656 2.306l-1.373 1.373-33.94-33.942 1.371-1.371c10.553-10.554 24.615-16.364 39.6-16.364s29.047 5.812 39.598 16.363l82.747 82.746c21.831 21.833 21.831 57.36-0.002 79.195zM275.678 179.678l-33.941-33.941 1.373-1.373c2.004-2.004 2.305-4.403 2.305-5.655 0-1.253-0.299-3.651-2.303-5.657l-82.747-82.745c-2.005-2.005-4.405-2.305-5.657-2.305s-3.652 0.3-5.657 2.305l-66.746 66.743c-2.005 2.005-2.305 4.405-2.305 5.657s0.299 3.65 2.305 5.656l82.745 82.744c2.005 2.006 4.405 2.306 5.657 2.306s3.652-0.3 5.657-2.306l1.373-1.371 33.941 33.94-1.373 1.373c-10.552 10.552-24.615 16.363-39.598 16.363s-29.046-5.812-39.598-16.363l-82.744-82.743c-10.553-10.552-16.365-24.617-16.365-39.599s5.812-29.047 16.363-39.599l66.745-66.745c10.553-10.551 24.616-16.363 39.599-16.363s29.046 5.812 39.598 16.363l82.747 82.746c10.552 10.552 16.361 24.615 16.361 39.598s-5.812 29.047-16.363 39.598l-1.372 1.373zM400 61c-4.862 0-9.725 1.854-13.435 5.565l-64 63.999c-7.422 7.42-7.422 19.449 0 26.869 7.42 7.422 19.448 7.422 26.868 0l64-64c7.422-7.42 7.422-19.448 0-26.868-3.708-3.711-8.571-5.565-13.433-5.565zM304 0c-8.837 0-16 7.163-16 16v64c0 8.837 7.163 16 16 16s16-7.163 16-16v-64c0-8.837-7.163-16-16-16zM464 160h-64c-8.837 0-16 7.163-16 16s7.163 16 16 16h64c8.837 0 16-7.163 16-16s-7.163-16-16-16zM112 387c4.862 0 9.725-1.854 13.435-5.565l64-64c7.421-7.42 7.421-19.449 0-26.869-7.42-7.422-19.449-7.422-26.869 0l-64 64c-7.421 7.42-7.421 19.449 0 26.869 3.709 3.711 8.572 5.565 13.434 5.565zM208 448c8.837 0 16-7.163 16-16v-64c0-8.837-7.163-16-16-16s-16 7.163-16 16v64c0 8.837 7.163 16 16 16zM48 288h64c8.837 0 16-7.163 16-16s-7.163-16-16-16h-64c-8.837 0-16 7.163-16 16s7.163 16 16 16z" />
+<glyph unicode="&#xe013;" d="M128 448v-448l128 128 128-128v448h-256zM352 85.255l-96 96-96-96v330.745h192v-330.745z" />
+<glyph unicode="&#xe014;" d="M448 416h-384c-17.6 0-32-14.4-32-32v-320c0-17.6 14.4-32 32-32h384c17.6 0 32 14.4 32 32v320c0 17.6-14.4 32-32 32zM448 64.058c-0.006-0.007-0.015-0.014-0.021-0.021l-95.979 159.963-80-64-112 144-95.984-239.958c-0.005 0.005-0.011 0.011-0.016 0.016v319.885c0.017 0.020 0.038 0.041 0.057 0.057h383.885c0.020-0.017 0.041-0.038 0.058-0.058v-319.884zM320 304c0-26.51 21.49-48 48-48s48 21.49 48 48c0 26.51-21.49 48-48 48-26.51 0-48-21.49-48-48z" />
+<glyph unicode="&#xe015;" d="M448 416h-384c-17.6 0-32-14.4-32-32v-320c0-17.6 14.4-32 32-32h384c17.6 0 32 14.4 32 32v320c0 17.6-14.4 32-32 32zM128 64h-64v64h64v-64zM128 192h-64v64h64v-64zM128 320h-64v64h64v-64zM352 64h-192v320h192v-320zM448 64h-64v64h64v-64zM448 192h-64v64h64v-64zM448 320h-64v64h64v-64zM192 320v-192l144 96z" />
+<glyph unicode="&#xe016;" d="M224 128h64v-64h-64v64zM352 352c17.673 0 32-14.327 32-32v-83l-114-77h-46v32l96 64v32h-160v64h192zM256 448c-59.833 0-116.083-23.3-158.392-65.608-42.307-42.309-65.608-98.559-65.608-158.392 0-59.832 23.301-116.084 65.608-158.392 42.309-42.308 98.559-65.608 158.392-65.608 59.832 0 116.084 23.3 158.392 65.608 42.308 42.308 65.608 98.56 65.608 158.392 0 59.833-23.3 116.083-65.608 158.392-42.308 42.308-98.56 65.608-158.392 65.608z" />
+<glyph unicode="&#xe017;" d="M208 128l-96 96 96 96-32 32-128-128 128-128zM336 352l-32-32 96-96-96-96 32-32 128 128z" />
+<glyph unicode="&#xe018;" d="M38.899 327.688l40.707-25.441c25.401 40.557 64.394 71.727 110.604 87.123l-15.183 45.547c-56.874-18.949-104.864-57.313-136.128-107.229zM336.973 434.917l-15.183-45.547c46.211-15.396 85.202-46.566 110.604-87.124l40.706 25.441c-31.263 49.917-79.253 88.281-136.127 107.23zM303.987 127.996c-2.404 0-4.846 0.545-7.143 1.693l-72.844 36.422v105.889c0 8.836 7.164 16 16 16s16-7.164 16-16v-86.111l55.155-27.578c7.903-3.951 11.107-13.562 7.155-21.466-2.802-5.607-8.454-8.848-14.323-8.849zM256 384c-106.039 0-192-85.961-192-192s85.961-192 192-192c106.039 0 192 85.961 192 192 0 106.039-85.961 192-192 192zM256 48c-79.529 0-144 64.471-144 144s64.471 144 144 144c79.529 0 144-64.471 144-144 0-79.529-64.471-144-144-144z" />
+<glyph unicode="&#xe019;" d="M32 252.127c22.659 24.96 48.581 46.18 76.636 62.562 45.166 26.372 96.123 40.311 147.364 40.311 51.24 0 102.198-13.939 147.363-40.312 28.056-16.382 53.978-37.602 76.637-62.562v58.716c-16.505 14.059-34.062 26.57-52.434 37.297-52.503 30.657-111.829 46.861-171.566 46.861s-119.064-16.204-171.567-46.86c-18.371-10.727-35.928-23.239-52.433-37.298v-58.715zM256 320c-91.598 0-172.919-50.278-224-128 51.081-77.724 132.402-128 224-128 91.598 0 172.919 50.276 224 128-51.081 77.722-132.402 128-224 128zM256 224c0-17.673-14.327-32-32-32s-32 14.327-32 32c0 17.674 14.327 32 32 32s32-14.326 32-32zM364.033 131.669c-33.717-19.687-70.064-29.669-108.033-29.669s-74.316 9.982-108.033 29.669c-25.777 15.052-49.308 35.655-69.057 60.331 19.749 24.675 43.28 45.279 69.058 60.33 6.638 3.876 13.379 7.37 20.213 10.491-5.256-11.871-8.181-25.004-8.181-38.821 0-53.020 42.981-96 96-96 53.020 0 96 42.98 96 96 0 13.817-2.925 26.95-8.18 38.821 6.834-3.122 13.575-6.615 20.213-10.491 25.777-15.051 49.308-35.655 69.058-60.33-19.749-24.676-43.28-45.279-69.058-60.331z" />
+<glyph unicode="&#xe01a;" d="M325.584 338.083c-12.306 40.981-14.438 45.917-53.584 45.917h-32c-39.809 0-41.332-5.076-54.209-48 0-0.001 0-0.001-0.001-0.002l-71.999-239.998h56.818l28.8 96h113.183l28.8-96h56.815l-72.623 242.083zM218.609 256l19.2 68c5.043 16.809 18.19 15 18.19 15s13.147 1.809 18.19-15h0.002l19.2-68h-74.782z" />
+<glyph unicode="&#xe01b;" d="M32 384v-352h448v352h-448zM192 160v64h128v-64h-128zM320 128v-64h-128v64h128zM320 320v-64h-128v64h128zM160 320v-64h-96v64h96zM64 224h96v-64h-96v64zM352 224h96v-64h-96v64zM352 256v64h96v-64h-96zM64 128h96v-64h-96v64zM352 64v64h96v-64h-96z" />
+<glyph unicode="&#xe01c;" d="M32 256h448v-64h-448z" />
+<glyph unicode="&#xe01d;" d="M32 96h256v-64h-256v64zM384 384h-110.279l-91.883-256h-66.144l91.881 256h-111.575v64h288v-64zM464.887 32l-64.887 64.887-64.887-64.887-31.113 31.113 64.887 64.887-64.887 64.887 31.113 31.113 64.887-64.887 64.887 64.887 31.113-31.113-64.887-64.887 64.887-64.887-31.113-31.113z" />
+<glyph unicode="&#xe01e;" d="M384 25v-25h64v-32h-96v73l64 30v25h-64v32h96v-73zM338 352h-68l-94-94-94 94h-68l128-128-128-128h68l94 94 94-94h68l-128 128z" />
+<glyph unicode="&#xe01f;" d="M384 377v-25h64v-32h-96v73l64 30v25h-64v32h96v-73zM338 352h-68l-94-94-94 94h-68l128-128-128-128h68l94 94 94-94h68l-128 128z" />
+<glyph unicode="&#xe020;" d="M352 64v18.502c75.674 30.814 128 96.91 128 173.498 0 106.039-100.288 192-224 192s-224-85.961-224-192c0-76.588 52.327-142.684 128-173.498v-18.502h-96l-32 48v-112h160v111.406c-50.45 25.681-85.333 80.77-85.333 144.594 0 88.366 66.859 160 149.333 160 82.474 0 149.333-71.634 149.333-160 0-63.824-34.883-118.913-85.333-144.594v-111.406h160v112l-32-48h-96z" />
+<glyph unicode="&#xe021;" d="M256 410c49.683 0 96.391-19.347 131.521-54.478s54.479-81.839 54.479-131.522-19.348-96.391-54.479-131.521-81.838-54.479-131.521-54.479-96.391 19.348-131.522 54.479-54.478 81.838-54.478 131.521 19.347 96.391 54.478 131.522 81.839 54.478 131.522 54.478zM256 448c-123.712 0-224-100.288-224-224s100.288-224 224-224 224 100.288 224 224-100.288 224-224 224v0zM160 288c0-17.673 14.327-32 32-32s32 14.327 32 32c0 17.673-14.327 32-32 32-17.673 0-32-14.327-32-32zM288 288c0-17.673 14.327-32 32-32s32 14.327 32 32c0 17.673-14.327 32-32 32-17.673 0-32-14.327-32-32zM256 152c-50.92 0-96.28 18.437-125.583 47.164 11.563-58.804 63.389-103.164 125.583-103.164 62.194 0 114.020 44.36 125.584 103.164-29.304-28.727-74.664-47.164-125.584-47.164z" />
+<glyph unicode="&#xe022;" d="M128 416h256v-64h-256v64zM448 320h-384c-17.6 0-32-14.4-32-32v-128c0-17.6 14.398-32 32-32h64v-96h256v96h64c17.6 0 32 14.4 32 32v128c0 17.6-14.4 32-32 32zM352 64h-192v128h192v-128zM455.2 272c0-12.813-10.387-23.2-23.199-23.2s-23.201 10.387-23.201 23.2 10.389 23.2 23.201 23.2c12.813 0 23.199-10.387 23.199-23.2z" />
+<glyph unicode="&#xe023;" d="M240 288l-96 96 64 64h-176v-176l64 64 96-96zM320 240l96 96 64-64v176h-176l64-64-96-96zM272 160l96-96-64-64h176v176l-64-64-96 96zM192 208l-96-96-64 64v-176h176l-64 64 96 96z" />
+<glyph unicode="&#xe024;" d="M480 416v32h-96c-17.601 0-32-14.4-32-32v-160c0-7.928 2.929-15.201 7.748-20.807l-151.748-130.193-71 74-41-35 112-144 208 224h64v32h-96v160h96zM128 224h32v192c0 17.6-14.4 32-32 32h-64c-17.6 0-32-14.4-32-32v-192h32v96h64v-96zM64 352v64h64v-64h-64zM320 256v48c0 17.6-4.4 32-22 32 17.6 0 22 14.4 22 32v48c0 17.6-14.4 32-32 32h-96v-224h96c17.6 0 32 14.4 32 32zM224 416h64v-64h-64v64zM224 320h64v-64h-64v64z" />
+<glyph unicode="&#xe025;" d="M224 224h-64v64h64v64h64v-64h64v-64h-64v-64h-64v64zM480 192v-160h-448v160h64v-96h320v96h64z" />
+<glyph unicode="&#xe026;" d="M256 288h64v-32h-64zM256 96h64v-32h-64zM288 192h64v-32h-64zM384 192v-96h-32v-32h64v128zM192 192h64v-32h-64zM160 96h64v-32h-64zM160 288h64v-32h-64zM96 384v-128h32v96h32v32zM352 256h64v128h-32v-96h-32zM32 448v-448h448v448h-448zM448 32h-384v384h384v-384zM96 192v-128h32v96h32v32zM288 384h64v-32h-64zM192 384h64v-32h-64z" />
+<glyph unicode="&#xe027;" d="M408 448l8-192h-320l8 192h16l8-160h256l8 160h16zM104 0l-8 160h320l-8-160h-16l-8 128h-256l-8-128h-16zM32 224h64v-32h-64zM128 224h64v-32h-64zM224 224h64v-32h-64zM320 224h64v-32h-64zM416 224h64v-32h-64z" />
+<glyph unicode="&#xe028;" d="M288 448c123.712 0 224-100.288 224-224s-100.288-224-224-224v48c47.012 0 91.209 18.307 124.451 51.549 33.242 33.242 51.549 77.439 51.549 124.451 0 47.011-18.307 91.209-51.549 124.451-33.242 33.242-77.439 51.549-124.451 51.549-47.011 0-91.209-18.307-124.451-51.549-25.57-25.569-42.291-57.623-48.653-92.451h93.104l-112-128-112 128h82.285c15.53 108.551 108.869 192 221.715 192zM384 256v-64h-128v160h64v-96z" />
+<glyph unicode="&#xe02a;" d="M312.721 232.909c24.037 19.075 39.279 47.428 39.279 79.091 0 57.438-50.145 104-112 104h-112v-384h144c61.856 0 112 46.562 112 104 0 44.098-29.559 81.781-71.279 96.909zM192 328c0 13.255 10.745 24 24 24h33.602c21.207 0 38.398-21.49 38.398-48s-17.191-48-38.398-48h-57.602v72zM273.6 96h-57.6c-13.255 0-24 10.745-24 24v72h81.6c21.209 0 38.4-21.49 38.4-48s-17.191-48-38.4-48z" />
+<glyph unicode="&#xe02b;" d="M416 416v-32h-72l-128-320h72v-32h-224v32h72l128 320h-72v32h224z" />
+<glyph unicode="&#xe02c;" d="M96 64h288v-32h-288v32zM320 416v-192c0-15.656-7.35-30.812-20.695-42.676-15.471-13.751-36.534-21.324-59.305-21.324-22.772 0-43.834 7.573-59.304 21.324-13.346 11.864-20.696 27.020-20.696 42.676v192h-64v-192c0-70.691 64.471-128 144-128s144 57.309 144 128v192h-64z" />
+<glyph unicode="&#xe02d;" d="M480 224h-132.938c-25.039 17.71-57.215 27.43-91.062 27.43-44.603 0-82.286 25.121-82.286 54.856 0 29.735 37.683 54.857 82.286 54.857 37.529 0 70.154-17.788 79.56-41.143h56.508c-3.965 25.322-18.79 48.984-42.029 66.413-25.44 19.080-58.838 29.587-94.039 29.587-35.202 0-68.598-10.507-94.037-29.587-27.394-20.545-43.106-49.751-43.106-80.127s15.712-59.582 43.106-80.127c0.978-0.733 1.971-1.449 2.973-2.158h-132.936v-32h256.266c29.104-8.553 50.021-28.135 50.021-50.286 0-29.734-37.684-54.855-82.286-54.855-37.53 0-70.154 17.787-79.559 41.143h-56.508c3.965-25.32 18.791-48.984 42.030-66.413 25.438-19.082 58.834-29.59 94.036-29.59 35.201 0 68.599 10.508 94.037 29.587 27.395 20.545 43.104 49.751 43.104 80.127 0 17.649-5.327 34.896-15.147 50.286h102.006v32z" />
+<glyph unicode="&#xe02e;" d="M192 416c-61.856 0-112-50.144-112-112s50.144-112 112-112v-160h64v320h32v-320h64v320h64v64h-224z" />
+<glyph unicode="&#xe02f;" d="M224 416c-61.856 0-112-50.144-112-112s50.144-112 112-112v-160h64v320h32v-320h64v320h64v64h-224zM32 32l112 96-112 96z" />
+<glyph unicode="&#xe030;" d="M160 416c-61.856 0-112-50.144-112-112s50.144-112 112-112v-160h64v320h32v-320h64v320h64v64h-224zM480 224l-112-96 112-96z" />
+<glyph unicode="&#xe031;" d="M416 320h-96v32l-96 96h-192v-352h192v-96h288v224l-96 96zM416 274.745l50.745-50.745h-50.745v50.745zM224 402.745l50.745-50.745h-50.745v50.745zM64 416h128v-96h96v-192h-224v288zM480 32h-224v64h64v192h64v-96h96v-160z" />
+<glyph unicode="&#xe032;" d="M384 352h32v-32h-32zM320 288h32v-32h-32zM320 224h32v-32h-32zM320 160h32v-32h-32zM256 224h32v-32h-32zM256 160h32v-32h-32zM192 160h32v-32h-32zM384 288h32v-32h-32zM384 224h32v-32h-32zM384 160h32v-32h-32zM384 96h32v-32h-32zM320 96h32v-32h-32zM256 96h32v-32h-32zM192 96h32v-32h-32zM128 96h32v-32h-32z" />
+<glyph unicode="&#xe034;" d="M464 416h-208l-16 32h-176l-32-64h448zM420.17 128h43.83l16 224h-448l32-320h178.040c-52.441 18.888-90.040 69.133-90.040 128 0 74.991 61.009 136 136 136 74.99 0 136-61.009 136-136 0-10.839-1.311-21.575-3.83-32zM437.498 55.125l-67.248 55.346c8.727 14.461 13.75 31.407 13.75 49.529 0 53.020-42.98 96-96 96s-96-42.98-96-96 42.98-96 96-96c18.122 0 35.069 5.023 49.529 13.75l55.346-67.248c11.481-13.339 31.059-14.070 43.503-1.626l2.746 2.746c12.444 12.444 11.713 32.022-1.626 43.503zM288 98c-34.242 0-62 27.758-62 62s27.758 62 62 62 62-27.758 62-62-27.758-62-62-62z" />
+<glyph unicode="&#xe035;" d="M352 288v80c0 8.8-7.2 16-16 16h-80v32c0 17.6-14.4 32-32 32h-64c-17.602 0-32-14.4-32-32v-32h-80c-8.801 0-16-7.2-16-16v-256c0-8.8 7.199-16 16-16h112v-96h288v288h-96zM160 415.943c0.017 0.019 0.036 0.039 0.057 0.057h63.884c0.021-0.018 0.041-0.038 0.059-0.057v-31.943h-64v31.943zM96 320v32h192v-32h-192zM416 32h-224v224h224v-224zM224 224v-64h16l16 32h32v-96h-24v-32h80v32h-24v96h32l16-32h16v64z" />
+</font></defs></svg> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/tinymce-small.ttf b/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/tinymce-small.ttf
new file mode 100644
index 000000000..afc6ec458
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/tinymce-small.ttf
Binary files differ
diff --git a/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/tinymce-small.woff b/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/tinymce-small.woff
new file mode 100644
index 000000000..fa72c74b4
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/tinymce-small.woff
Binary files differ
diff --git a/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/tinymce.dev.svg b/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/tinymce.dev.svg
new file mode 100644
index 000000000..c87b8cd1a
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/tinymce.dev.svg
@@ -0,0 +1,153 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
+<svg xmlns="http://www.w3.org/2000/svg">
+<metadata>
+This is a custom SVG font generated by IcoMoon.
+<iconset grid="16"></iconset>
+</metadata>
+<defs>
+<font id="tinymce" horiz-adv-x="512" >
+<font-face units-per-em="512" ascent="480" descent="-32" />
+<missing-glyph horiz-adv-x="512" />
+<glyph class="hidden" unicode="&#xf000;" d="M0,480L 512 -32L0 -32 z" horiz-adv-x="0" />
+<glyph unicode="&#xe034;" d="M 464,416L 256,416L 240,448L 64,448L 32,384L 480,384 zM 452.17,128l 37.43,0 L 512,352L0,352 l 32-320l 242.040,0 C 221.599,50.888, 184,101.133, 184,160c0,74.991, 61.009,136, 136,136
+ c 74.99,0, 136-61.009, 136-136C 456,149.161, 454.689,138.425, 452.17,128zM 501.498,23.125l-99.248,87.346C 410.977,124.931, 416,141.878, 416,160c0,53.020-42.98,96-96,96s-96-42.98-96-96
+ s 42.98-96, 96-96c 18.122,0, 35.069,5.023, 49.529,13.75l 87.346-99.248c 11.481-13.339, 31.059-14.070, 43.503-1.626l 2.746,2.746
+ C 515.568-7.934, 514.837,11.644, 501.498,23.125z M 320,98c-34.242,0-62,27.758-62,62s 27.758,62, 62,62s 62-27.758, 62-62
+ S 354.242,98, 320,98z" />
+<glyph unicode="&#xe032;" d="M 384,352L 416,352L 416,320L 384,320zM 320,288L 352,288L 352,256L 320,256zM 320,224L 352,224L 352,192L 320,192zM 320,160L 352,160L 352,128L 320,128zM 256,224L 288,224L 288,192L 256,192zM 256,160L 288,160L 288,128L 256,128zM 192,160L 224,160L 224,128L 192,128zM 384,288L 416,288L 416,256L 384,256zM 384,224L 416,224L 416,192L 384,192zM 384,160L 416,160L 416,128L 384,128zM 384,96L 416,96L 416,64L 384,64zM 320,96L 352,96L 352,64L 320,64zM 256,96L 288,96L 288,64L 256,64zM 192,96L 224,96L 224,64L 192,64zM 128,96L 160,96L 160,64L 128,64z" data-tags="resize, dots" />
+<glyph unicode="&#xe031;" d="M 416,352l-96,0 L 320,384 L 224,480L0,480 l0-384 l 192,0 l0-128 l 320,0 L 512,256 L 416,352z M 416,306.745L 466.745,256L 416,256 L 416,306.745 z M 224,434.745L 274.745,384L 224,384
+ L 224,434.745 z M 32,448l 160,0 l0-96 l 96,0 l0-224 L 32,128 L 32,448 z M 480,0L 224,0 l0,96 l 96,0 L 320,320 l 64,0 l0-96 l 96,0 L 480,0 z" data-tags="copy" />
+<glyph unicode="&#xe030;" d="M 128,448 L 384,448 L 384,384 L 320,384 L 320,0 L 256,0 L 256,384 L 192,384 L 192,0 L 128,0 L 128,224 C 66.144,224 16,274.144 16,336 C 16,397.856 66.144,448 128,448 ZM 480,32L 352,144L 480,256 z" data-tags="rtl" />
+<glyph unicode="&#xe02f;" d="M 224,448 L 480,448 L 480,384 L 416,384 L 416,0 L 352,0 L 352,384 L 288,384 L 288,0 L 224,0 L 224,224 C 162.144,224 112,274.144 112,336 C 112,397.856 162.144,448 224,448 ZM 32,256L 160,144L 32,32 z" data-tags="ltr" />
+<glyph unicode="&#xe02e;" d="M 192,448 L 448,448 L 448,384 L 384,384 L 384,0 L 320,0 L 320,384 L 256,384 L 256,0 L 192,0 L 192,224 C 130.144,224 80,274.144 80,336 C 80,397.856 130.144,448 192,448 Z" data-tags="visualchars" />
+<glyph unicode="&#xe02d;" d="M 365.71,221.482 C 397.67,197.513 416,163.439 416,128 C 416,92.561 397.67,58.487 365.71,34.518 C 336.031,12.259 297.068,0 256,0 C 214.931,0 175.969,12.259 146.29,34.518 C 114.33,58.487 96,92.561 96,128 L 160,128 C 160,93.309 203.963,64 256,64 C 308.037,64 352,93.309 352,128 C 352,162.691 308.037,192 256,192 C 214.931,192 175.969,204.259 146.29,226.518 C 114.33,250.488 96,284.561 96,320 C 96,355.439 114.33,389.512 146.29,413.482 C 175.969,435.741 214.931,448 256,448 C 297.068,448 336.031,435.741 365.71,413.482 C 397.67,389.512 416,355.439 416,320 L 352,320 C 352,354.691 308.037,384 256,384 C 203.963,384 160,354.691 160,320 C 160,285.309 203.963,256 256,256 C 297.068,256 336.031,243.741 365.71,221.482 ZM0,224L 512,224L 512,192L0,192z" data-tags="strikethrough" />
+<glyph unicode="&#xe02c;" d="M 352,448 L 416,448 L 416,240 C 416,160.471 344.366,96 256,96 C 167.635,96 96,160.471 96,240 L 96,448 L 160,448 L 160,240 C 160,219.917 169.119,200.648 185.677,185.747 C 204.125,169.145 229.1,160 256,160 C 282.9,160 307.875,169.145 326.323,185.747 C 342.881,200.648 352,219.917 352,240 L 352,448 ZM 96,64L 416,64L 416,0L 96,0z" data-tags="underline" />
+<glyph unicode="&#xe02b;" d="M 448,448 L 448,416 L 384,416 L 224,32 L 288,32 L 288,0 L 64,0 L 64,32 L 128,32 L 288,416 L 224,416 L 224,448 Z" data-tags="italic" />
+<glyph unicode="&#xe02a;" d="M 353.94,237.674C 372.689,259.945, 384,288.678, 384,320c0,70.58-57.421,128-128,128l-64,0 l-64,0 L 96,448 l0-448 l 32,0 l 64,0 l 96,0
+ c 70.579,0, 128,57.421, 128,128C 416,174.478, 391.101,215.248, 353.94,237.674z M 192,384l 50.75,0 c 27.984,0, 50.75-28.71, 50.75-64
+ s-22.766-64-50.75-64L 192,256 L 192,384 z M 271.5,64L 192,64 L 192,192 l 79.5,0 c 29.225,0, 53-28.71, 53-64S 300.725,64, 271.5,64z" data-tags="bold0" />
+<glyph unicode="&#xe029;" d="M 192,64L 288,64L 288-32L 192-32zM 400,448 C 426.51,448 448,426.51 448,400 L 448,256 L 288,160 L 288,96 L 192,96 L 192,192 L 352,288 L 352,352 L 96,352 L 96,448 L 400,448 Z" />
+<glyph unicode="&#xe028;" d="M 288,448 C 411.712,448 512,347.712 512,224 C 512,100.288 411.712,0 288,0 L 288,48 C 335.012,48 379.209,66.307 412.451,99.549 C 445.693,132.791 464,176.988 464,224 C 464,271.011 445.693,315.209 412.451,348.451 C 379.209,381.693 335.012,400 288,400 C 240.989,400 196.791,381.693 163.549,348.451 C 137.979,322.882 121.258,290.828 114.896,256 L 208,256 L 96,128 L -16,256 L 66.285,256 C 81.815,364.551 175.154,448 288,448 ZM 384,256 L 384,192 L 256,192 L 256,352 L 320,352 L 320,256 Z" data-tags="restoredraft" />
+<glyph unicode="&#xe027;" d="M0,224L 64,224L 64,192L0,192zM 96,224L 192,224L 192,192L 96,192zM 224,224L 288,224L 288,192L 224,192zM 320,224L 416,224L 416,192L 320,192zM 448,224L 512,224L 512,192L 448,192zM 440,480 L 448,256 L 64,256 L 72,480 L 88,480 L 96,288 L 416,288 L 424,480 ZM 72-32 L 64,160 L 448,160 L 440-32 L 424-32 L 416,128 L 96,128 L 88-32 Z" data-tags="pagebreak" />
+<glyph unicode="&#xe026;" d="M 192,384L 256,384L 256,352L 192,352zM 288,384L 352,384L 352,352L 288,352zM 448,384 L 448,256 L 352,256 L 352,288 L 416,288 L 416,352 L 384,352 L 384,384 ZM 160,288L 224,288L 224,256L 160,256zM 256,288L 320,288L 320,256L 256,256zM 96,352 L 96,288 L 128,288 L 128,256 L 64,256 L 64,384 L 160,384 L 160,352 ZM 192,192L 256,192L 256,160L 192,160zM 288,192L 352,192L 352,160L 288,160zM 448,192 L 448,64 L 352,64 L 352,96 L 416,96 L 416,160 L 384,160 L 384,192 ZM 160,96L 224,96L 224,64L 160,64zM 256,96L 320,96L 320,64L 256,64zM 96,160 L 96,96 L 128,96 L 128,64 L 64,64 L 64,192 L 160,192 L 160,160 ZM 480,448 L 32,448 L 32,0 L 480,0 L 480,448 Z M 512,480 L 512,480 L 512-32 L 0-32 L 0,480 L 512,480 Z" data-tags="template" />
+<glyph unicode="&#xe025;" d="M 224,192 L 128,192 L 128,256 L 224,256 L 224,352 L 288,352 L 288,256 L 384,256 L 384,192 L 288,192 L 288,96 L 224,96 ZM 512,160 L 512-32 L 0-32 L 0,160 L 64,160 L 64,32 L 448,32 L 448,160 Z" data-tags="nonbreaking" />
+<glyph unicode="&#xe024;" d="M 64,352l 64,0 l0-96 l 32,0 L 160,448 c0,17.6-14.4,32-32,32L 64,480 C 46.4,480, 32,465.6, 32,448l0-192 l 32,0 L 64,352 z M 64,448l 64,0 l0-64 L 64,384 L 64,448 z M 480,448L 480,480 l-96,0
+ c-17.601,0-32-14.4-32-32l0-160 c0-17.6, 14.399-32, 32-32l 96,0 l0,32 l-96,0 L 384,448 L 480,448 z M 320,400L 320,448 c0,17.6-14.4,32-32,32l-96,0 l0-224 l 96,0
+ c 17.6,0, 32,14.4, 32,32l0,48 c0,17.6-4.4,32-22,32C 315.6,368, 320,382.4, 320,400z M 288,288l-64,0 l0,64 l 64,0 L 288,288 z M 288,384l-64,0 L 224,448 l 64,0 L 288,384 zM 416,192 L 208-32 L 96,112 L 137,147 L 208,73 L 384,224 Z" data-tags="spellchecker" />
+<glyph unicode="&#xe023;" d="M 512,480 L 512,288 L 442.87,357.13 L 336.87,251.13 L 283.13,304.87 L 389.13,410.87 L 320,480 ZM 122.87,410.87 L 228.87,304.87 L 175.13,251.13 L 69.13,357.13 L 0,288 L 0,480 L 192,480 ZM 442.87,90.87 L 512,160 L 512-32 L 320-32 L 389.13,37.13 L 283.13,143.13 L 336.87,196.87 ZM 228.87,143.13 L 122.87,37.13 L 192-32 L 0-32 L 0,160 L 69.13,90.87 L 175.13,196.87 Z" data-tags="fullscreen" />
+<glyph unicode="&#xe022;" d="M 128,448L 384,448L 384,384L 128,384zM 480,352L 32,352 C 14.4,352,0,337.6,0,320l0-160 c0-17.6, 14.398-32, 32-32l 96,0 l0-128 l 256,0 L 384,128 l 96,0 c 17.6,0, 32,14.4, 32,32L 512,320
+ C 512,337.6, 497.6,352, 480,352z M 352,32L 160,32 L 160,192 l 192,0 L 352,32 z M 487.2,304c0-12.813-10.387-23.2-23.199-23.2
+ c-12.813,0-23.201,10.387-23.201,23.2s 10.388,23.2, 23.201,23.2C 476.814,327.2, 487.2,316.813, 487.2,304z" data-tags="print" />
+<glyph unicode="&#xe021;" d="M 256,480C 114.615,480,0,365.386,0,224c0-141.385, 114.614-256, 256-256c 141.385,0, 256,114.615, 256,256
+ C 512,365.386, 397.385,480, 256,480z M 256,8c-119.293,0-216,96.706-216,216c0,119.293, 96.707,216, 216,216c 119.295,0, 216-96.707, 216-216
+ C 472,104.706, 375.295,8, 256,8z M 192,320c0-17.673-14.327-32-32-32s-32,14.327-32,32s 14.327,32, 32,32S 192,337.673, 192,320z
+ M 384,320c0-17.673-14.326-32-32-32s-32,14.327-32,32s 14.326,32, 32,32S 384,337.673, 384,320zM 256,154 C 326.537,154 387.344,182.766 415.231,215.596 C 404.795,129.986 337.087,64 256,64 C 174.941,64 107.251,130.013 96.778,215.584 C 124.671,182.761 185.471,154 256,154 Z" data-tags="emoticons" />
+<glyph unicode="&#xe020;" d="M 352,32 L 480,32 L 512,96 L 512-32 L 320-32 L 320,75.107 C 385.556,103.349 432,173.688 432,256 C 432,363.216 353.201,447.133 256,447.133 C 158.797,447.133 80,363.217 80,256 C 80,173.688 126.443,103.349 192,75.107 L 192-32 L 0-32 L 0,96 L 32,32 L 160,32 L 160,48.295 C 66.185,81.525 0,161.996 0,256 C 0,379.712 114.615,480 256,480 C 397.385,480 512,379.712 512,256 C 512,161.996 445.815,81.525 352,48.295 L 352,32 Z" data-tags="charmap" />
+<glyph unicode="&#xe01f;" d="M 384,377 L 384,352 L 448,352 L 448,320 L 352,320 L 352,393 L 416,423 L 416,448 L 352,448 L 352,480 L 448,480 L 448,407 ZM 338,352L 270,352L 176,258L 82,352L 14,352L 142,224L 14,96L 82,96L 176,190L 270,96L 338,96L 210,224 z" data-tags="sup" />
+<glyph unicode="&#xe01e;" d="M 384,25 L 384,0 L 448,0 L 448-32 L 352-32 L 352,41 L 416,71 L 416,96 L 352,96 L 352,128 L 448,128 L 448,55 ZM 338,352L 270,352L 176,258L 82,352L 14,352L 142,224L 14,96L 82,96L 176,190L 270,96L 338,96L 210,224 z" data-tags="sub" />
+<glyph unicode="&#xe01d;" d="M0,32L 288,32L 288-32L0-32zM 96,480L 448,480L 448,416L 96,416zM 138.694,64 L 241.038,456.082 L 302.963,439.918 L 204.838,64 ZM 464.887-32 L 400,32.887 L 335.113-32 L 304-0.887 L 368.887,64 L 304,128.887 L 335.113,160 L 400,95.113 L 464.887,160 L 496,128.887 L 431.113,64 L 496-0.887 Z" data-tags="removeformat" />
+<glyph unicode="&#xe01c;" d="M0,256L 512,256L 512,192L0,192z" data-tags="hr" />
+<glyph unicode="&#xe01b;" d="M0,448l0-448 l 512,0 L 512,448 L0,448 z M 192,160l0,96 l 128,0 l0-96 L 192,160 z M 320,128l0-96 L 192,32 l0,96 L 320,128 z M 320,384l0-96 L 192,288 L 192,384 L 320,384 z M 160,384l0-96 L 32,288 L 32,384 L 160,384 z
+ M 32,256l 128,0 l0-96 L 32,160 L 32,256 z M 352,256l 128,0 l0-96 L 352,160 L 352,256 z M 352,288L 352,384 l 128,0 l0-96 L 352,288 z M 32,128l 128,0 l0-96 L 32,32 L 32,128 z M 352,32l0,96 l 128,0 l0-96 L 352,32 z" data-tags="table" />
+<glyph unicode="&#xe01a;" d="M 161.009,64l 28.8,96l 132.382,0 l 28.8-96l 56.816,0 L 311.809,384L 200.191,384 l-96-320L 161.009,64 z M 237.809,320l 36.382,0 l 28.8-96l-93.982,0
+ L 237.809,320z" data-tags="forecolor" />
+<glyph unicode="&#xe019;" d="M 256,320C 151.316,320, 58.378,269.722,0,192c 58.378-77.723, 151.316-128, 256-128c 104.684,0, 197.622,50.277, 256,128
+ C 453.622,269.722, 360.684,320, 256,320z M 224,256c 17.673,0, 32-14.327, 32-32s-14.327-32-32-32s-32,14.327-32,32S 206.327,256, 224,256z
+ M 386.808,127.352c-19.824-10.129-40.826-17.931-62.423-23.188C 302.141,98.746, 279.134,96, 256,96
+ c-23.133,0-46.141,2.746-68.384,8.162c-21.597,5.259-42.599,13.061-62.423,23.188c-31.51,16.101-60.111,38.205-83.82,64.649
+ c 23.709,26.444, 52.31,48.55, 83.82,64.649c 16.168,8.261, 33.121,14.973, 50.541,20.020C 165.79,261.547, 160,243.451, 160,224
+ c0-53.020, 42.981-96, 96-96c 53.019,0, 96,42.98, 96,96c0,19.451-5.791,37.547-15.733,52.67c 17.419-5.048, 34.372-11.76, 50.541-20.021
+ c 31.511-16.099, 60.109-38.204, 83.819-64.649C 446.917,165.557, 418.318,143.45, 386.808,127.352z M 430.459,358.139
+ C 376.099,385.916, 317.403,400, 256,400c-61.403,0-120.099-14.084-174.459-41.861C 52.155,343.123, 24.675,324.187,0,302.101l0-54.603
+ c 27.669,29.283, 60.347,53.877, 96.097,72.145C 145.907,345.095, 199.706,358, 256,358s 110.093-12.905, 159.902-38.358
+ c 35.751-18.268, 68.429-42.862, 96.098-72.145L 512,302.1 C 487.325,324.187, 459.846,343.123, 430.459,358.139z" data-tags="preview" />
+<glyph unicode="&#xe018;" d="M 256,384C 149.962,384, 64,298.039, 64,192s 85.961-192, 192-192c 106.037,0, 192,85.961, 192,192S 362.037,384, 256,384z
+ M 357.822,90.177C 330.626,62.979, 294.464,48, 256,48s-74.625,14.979-101.823,42.177C 126.979,117.374, 112,153.536, 112,192
+ s 14.979,74.625, 42.177,101.823C 181.375,321.021, 217.536,336, 256,336s 74.626-14.979, 101.821-42.177
+ C 385.022,266.625, 400,230.464, 400,192S 385.021,117.374, 357.822,90.177zM 162.965,378.069l-21.47,42.939C 92.058,396.24, 51.76,355.942, 26.992,306.504l 42.938-21.47
+ C 90.054,325.202, 122.796,357.945, 162.965,378.069zM 442.067,285.035l 42.939,21.469C 460.24,355.942, 419.943,396.24, 370.504,421.008l-21.472-42.939
+ C 389.201,357.945, 421.944,325.203, 442.067,285.035zM 256,288l-32,0 l0-96 c0-5.055, 2.35-9.555, 6.011-12.486l-0.006-0.008l 80-64l 19.988,24.988L 256,199.689L 256,288 z" data-tags="inserttime" />
+<glyph unicode="&#xe017;" d="M 160,352L 32,224L 160,96L 224,96L 96,224L 224,352 zM 352,352L 288,352L 416,224L 288,96L 352,96L 480,224 z" data-tags="code" />
+<glyph unicode="&#xe016;" d="M 224,128L 288,128L 288,64L 224,64zM 352,352 C 369.673,352 384,337.673 384,320 L 384,224 L 288,160 L 224,160 L 224,192 L 320,256 L 320,288 L 160,288 L 160,352 L 352,352 ZM 256,432 C 200.441,432 148.208,410.364 108.922,371.078 C 69.636,331.792 48,279.559 48,224 C 48,168.441 69.636,116.208 108.922,76.922 C 148.208,37.636 200.441,16 256,16 C 311.559,16 363.792,37.636 403.078,76.922 C 442.364,116.208 464,168.441 464,224 C 464,279.559 442.364,331.792 403.078,371.078 C 363.792,410.364 311.559,432 256,432 Z M 256,480 L 256,480 C 397.385,480 512,365.385 512,224 C 512,82.615 397.385-32 256-32 C 114.615-32 0,82.615 0,224 C 0,365.385 114.615,480 256,480 Z" data-tags="help" />
+<glyph unicode="&#xe015;" d="M0,416l0-384 l 512,0 L 512,416 L0,416 z M 96,64L 32,64 l0,64 l 64,0 L 96,64 z M 96,192L 32,192 l0,64 l 64,0 L 96,192 z M 96,320L 32,320 L 32,384 l 64,0 L 96,320 z M 384,64L 128,64 L 128,384 l 256,0 L 384,64 z
+ M 480,64l-64,0 l0,64 l 64,0 L 480,64 z M 480,192l-64,0 l0,64 l 64,0 L 480,192 z M 480,320l-64,0 L 416,384 l 64,0 L 480,320 zM 192,320L 192,128L 320,224 z" data-tags="media" />
+<glyph unicode="&#xe014;" d="M0,416l0-416 l 512,0 L 512,416 L0,416 z M 480,32L 32,32 L 32,384 l 448,0 L 480,32 zM 352,304A48,48 3060 1 0 448,304A48,48 3060 1 0 352,304zM 448,64 L 64,64 L 160,320 L 288,160 L 352,208 Z" data-tags="image" />
+<glyph unicode="&#xe013;" d="M 96,480l0-512 l 160,160l 160-160L 416,480 L 96,480 z M 384,45.255l-128,128l-128-128L 128,448 l 256,0 L 384,45.255 z" data-tags="anchor" />
+<glyph unicode="&#xe012;" d="M 238.444,142.443c 2.28-4.524, 3.495-9.579, 3.495-14.848c0-8.808-3.372-17.029-9.496-23.154l-81.69-81.69
+ c-6.124-6.124-14.348-9.496-23.154-9.496s-17.030,3.372-23.154,9.496l-49.69,49.69c-6.124,6.125-9.496,14.348-9.496,23.154
+ s 3.372,17.030, 9.496,23.154l 81.69,81.691c 6.124,6.123, 14.348,9.496, 23.154,9.496c 5.269,0, 10.322-1.215, 14.848-3.494l 32.669,32.668
+ c-13.935,10.705-30.72,16.080-47.517,16.080c-19.993,0-39.986-7.583-55.154-22.751l-81.69-81.691
+ c-30.335-30.335-30.335-79.975,0-110.309l 49.69-49.691c 15.167-15.166, 35.16-22.75, 55.153-22.75
+ c 19.994,0, 39.987,7.584, 55.154,22.751l 81.69,81.69c 27.91,27.91, 30.119,72.149, 6.672,102.673L 238.444,142.443zM 489.248,407.558l-49.69,49.691C 424.391,472.417, 404.398,480, 384.404,480c-19.993,0-39.985-7.583-55.153-22.751l-81.691-81.691
+ c-27.91-27.91-30.119-72.149-6.671-102.671l 32.669,32.67c-2.279,4.525-3.494,9.58-3.494,14.847c0,8.808, 3.372,17.030, 9.496,23.154
+ l 81.691,81.691c 6.123,6.124, 14.347,9.497, 23.153,9.497c 8.808,0, 17.030-3.373, 23.154-9.497l 49.69-49.691
+ c 6.124-6.124, 9.496-14.347, 9.496-23.154c0-8.807-3.372-17.030-9.496-23.154l-81.69-81.691c-6.124-6.124-14.347-9.496-23.154-9.496
+ c-5.268,0-10.322,1.215-14.848,3.495l-32.669-32.669c 13.936-10.705, 30.72-16.080, 47.517-16.080c 19.994,0, 39.987,7.584, 55.154,22.752
+ l 81.69,81.69C 519.584,327.584, 519.584,377.223, 489.248,407.558zM 116.684,340.688L 20.687,436.685L 43.315,459.313L 139.312,363.316zM 192,480L 224,480L 224,384L 192,384zM0,288L 96,288L 96,256L0,256zM 395.316,107.312L 491.314,11.314L 468.686-11.314L 372.688,84.684zM 288,64L 320,64L 320-32L 288-32zM 416,192L 512,192L 512,160L 416,160z" data-tags="unlink" />
+<glyph unicode="&#xe011;" d="M 160,128c 8.8-8.8, 23.637-8.363, 32.971,0.971L 351.030,287.029C 360.364,296.363, 360.8,311.2, 352,320
+ s-23.637,8.363-32.971-0.971L 160.971,160.971C 151.637,151.637, 151.2,136.8, 160,128zM 238.444,142.444c 2.28-4.525, 3.495-9.58, 3.495-14.848c0-8.808-3.372-17.030-9.496-23.154l-81.691-81.691
+ c-6.124-6.124-14.347-9.496-23.154-9.496s-17.030,3.372-23.154,9.496l-49.691,49.691c-6.124,6.124-9.496,14.347-9.496,23.154
+ s 3.372,17.030, 9.496,23.154l 81.691,81.691c 6.124,6.124, 14.347,9.497, 23.154,9.497c 5.268,0, 10.322-1.215, 14.848-3.495l 32.669,32.669
+ c-13.935,10.705-30.72,16.080-47.517,16.080c-19.993,0-39.986-7.583-55.154-22.751l-81.691-81.691
+ c-30.335-30.335-30.335-79.974,0-110.309l 49.691-49.691C 87.611-24.416, 107.604-32, 127.597-32
+ c 19.994,0, 39.987,7.584, 55.154,22.751l 81.691,81.691c 27.91,27.91, 30.119,72.149, 6.672,102.672L 238.444,142.444zM 489.249,407.558l-49.691,49.691C 424.391,472.417, 404.398,480, 384.404,480c-19.993,0-39.986-7.583-55.154-22.751l-81.691-81.691
+ c-27.91-27.91-30.119-72.149-6.671-102.671l 32.669,32.67c-2.279,4.525-3.494,9.58-3.494,14.847c0,8.808, 3.372,17.030, 9.496,23.154
+ l 81.691,81.691c 6.124,6.124, 14.347,9.497, 23.154,9.497s 17.030-3.373, 23.154-9.497l 49.691-49.691
+ c 6.124-6.124, 9.496-14.347, 9.496-23.154s-3.372-17.030-9.496-23.154l-81.691-81.691c-6.124-6.124-14.347-9.496-23.154-9.496
+ c-5.268,0-10.322,1.215-14.848,3.495l-32.669-32.669c 13.936-10.705, 30.72-16.080, 47.517-16.080c 19.994,0, 39.987,7.584, 55.154,22.751
+ l 81.691,81.691C 519.584,327.584, 519.584,377.223, 489.249,407.558z" data-tags="link" />
+<glyph unicode="&#xe010;" d="M 288,355.814L 288,480 l 192-192L 288,96L 288,222.912 C 64.625,228.153, 74.206,71.016, 131.070-32
+ C-9.286,119.707, 20.52,362.785, 288,355.814z" data-tags="redo" />
+<glyph unicode="&#xe00f;" d="M 380.931-32C 437.794,71.016, 447.375,228.153, 224,222.912L 224,96 L 32,288L 224,480l0-124.186
+ C 491.481,362.785, 521.285,119.707, 380.931-32z" data-tags="undo" />
+<glyph unicode="&#xe00e;" d="M 112.5,256 C 174.356,256 224.5,205.855 224.5,144 C 224.5,82.144 174.356,32 112.5,32 C 50.644,32 0.5,82.144 0.5,144 L 0,160 C 0,283.712 100.288,384 224,384 L 224,320 C 181.263,320 141.083,303.357 110.863,273.137 C 105.046,267.319 99.737,261.129 94.948,254.627 C 100.667,255.527 106.528,256 112.5,256 ZM 400.5,256 C 462.355,256 512.5,205.855 512.5,144 C 512.5,82.144 462.355,32 400.5,32 C 338.645,32 288.5,82.144 288.5,144 L 288,160 C 288,283.712 388.288,384 512,384 L 512,320 C 469.263,320 429.083,303.357 398.863,273.137 C 393.045,267.319 387.736,261.129 382.947,254.627 C 388.667,255.527 394.527,256 400.5,256 Z" data-tags="blockquote" />
+<glyph unicode="&#xe00d;" d="M0,448L 512,448L 512,384L0,384zM 192,352L 512,352L 512,288L 192,288zM 192,256L 512,256L 512,192L 192,192zM 192,160L 512,160L 512,96L 192,96zM0,64L 512,64L 512,0L0,0zM 128,320 L 128,128 L 0,224 Z" data-tags="outdent" />
+<glyph unicode="&#xe00c;" d="M0,448L 512,448L 512,384L0,384zM 192,352L 512,352L 512,288L 192,288zM 192,256L 512,256L 512,192L 192,192zM 192,160L 512,160L 512,96L 192,96zM0,64L 512,64L 512,0L0,0zM 0,128 L 0,320 L 128,224 Z" data-tags="indent" />
+<glyph unicode="&#xe00b;" d="M 192,64L 512,64L 512,0L 192,0zM 192,256L 512,256L 512,192L 192,192zM 192,448L 512,448L 512,384L 192,384zM 96,480 L 96,352 L 64,352 L 64,448 L 32,448 L 32,480 ZM 64,217 L 64,192 L 128,192 L 128,160 L 32,160 L 32,233 L 96,263 L 96,288 L 32,288 L 32,320 L 128,320 L 128,247 ZM 128,128 L 128-32 L 32-32 L 32,0 L 96,0 L 96,32 L 32,32 L 32,64 L 96,64 L 96,96 L 32,96 L 32,128 Z" data-tags="numlist" />
+<glyph unicode="&#xe00a;" d="M 192,448l 320,0 l0-64 L 192,384 L 192,448 z M 192,256l 320,0 l0-64 L 192,192 L 192,256 z M 192,64l 320,0 l0-64 L 192,0 L 192,64 zM0,416A64,64 3060 1 0 128,416A64,64 3060 1 0 0,416zM0,224A64,64 3060 1 0 128,224A64,64 3060 1 0 0,224zM0,32A64,64 3060 1 0 128,32A64,64 3060 1 0 0,32z" data-tags="bullist" />
+<glyph unicode="&#xe009;" d="M 32,480L 224,480L 224,448L 32,448zM 288,480L 480,480L 480,448L 288,448zM 476,320l-28,0 L 448,448 L 320,448 l0-128 L 192,320 L 192,448 L 64,448 l0-128 L 36,320 c-19.8,0-36-16.2-36-36l0-280 c0-19.8, 16.2-36, 36-36l 152,0 c 19.8,0, 36,16.2, 36,36L 224,192 l 64,0
+ l0-188 c0-19.8, 16.2-36, 36-36l 152,0 c 19.8,0, 36,16.2, 36,36L 512,284 C 512,303.8, 495.8,320, 476,320z M 174,0L 50,0 c-9.9,0-18,7.2-18,16
+ s 8.1,16, 18,16l 124,0 c 9.9,0, 18-7.2, 18-16S 183.9,0, 174,0z M 272,224l-32,0 c-8.8,0-16,7.2-16,16s 7.2,16, 16,16l 32,0 c 8.8,0, 16-7.2, 16-16
+ S 280.8,224, 272,224z M 462,0L 338,0 c-9.9,0-18,7.2-18,16s 8.1,16, 18,16l 124,0 c 9.9,0, 18-7.2, 18-16S 471.9,0, 462,0z" data-tags="searchreplace" />
+<glyph unicode="&#xe008;" d="M 416,320L 416,400 c0,8.8-7.2,16-16,16L 288,416 L 288,448 c0,17.6-14.4,32-32,32l-64,0 c-17.602,0-32-14.4-32-32l0-32 L 48,416 c-8.801,0-16-7.2-16-16l0-320
+ c0-8.8, 7.199-16, 16-16l 144,0 l0-96 l 224,0 l 96,96L 512,320 L 416,320 z M 192,447.943c 0.017,0.019, 0.036,0.039, 0.057,0.057l 63.884,0
+ c 0.021-0.018, 0.041-0.038, 0.059-0.057L 256,416 l-64,0 L 192,447.943 z M 96,352L 96,384 l 256,0 l0-32 L 96,352 z M 416,13.255L 416,64 l 50.745,0 L 416,13.255z M 480,96l-96,0 l0-96
+ L 224,0 L 224,288 l 256,0 L 480,96 z" data-tags="paste" />
+<glyph unicode="&#xe007;" d="M 445.387,125.423c-22.827,22.778-51.864,34.536-78.973,34.536l-14.556,0 l-31.952,32.004l 127.81,128.019
+ c 31.952,32.005, 31.952,96.014,0,128.019L 256.001,255.973L 64.285,448c-31.952-32.004-31.952-96.014,0-128.019l 127.811-128.017
+ l-31.953-32.004l-14.557,0 c-27.11,0-56.146-11.759-78.974-34.538c-40.811-40.721-46.325-101.242-12.315-135.175
+ C 69.282-24.704, 89.441-32, 110.795-32c 27.108,0, 56.145,11.757, 78.973,34.536c 26.792,26.732, 38.371,62, 33.542,92.674l 32.692,32.744
+ l 32.688-32.744c-4.828-30.674, 6.753-65.941, 33.542-92.674C 345.063-20.243, 374.098-32, 401.206-32
+ c 21.354,0, 41.512,7.296, 56.497,22.248C 491.713,24.181, 486.197,84.702, 445.387,125.423z M 176.512,57.231
+ c-3.849-8.941-9.505-17.173-16.813-24.463c-7.318-7.302-15.586-12.959-24.574-16.812c-8.066-3.458-16.48-5.284-24.331-5.284
+ c-7.573,0-18.306,1.701-26.431,9.806c-8.068,8.052-9.76,18.659-9.76,26.144c0,7.771, 1.821,16.105, 5.263,24.106
+ c 3.85,8.942, 9.507,17.173, 16.813,24.463c 7.317,7.303, 15.586,12.957, 24.575,16.812c 8.067,3.457, 16.48,5.284, 24.332,5.284
+ c 7.573,0, 18.306-1.7, 26.429-9.807c 8.067-8.049, 9.761-18.658, 9.761-26.142C 181.777,73.567, 179.957,65.23, 176.512,57.231z
+ M 256.002,146.702c-24.957,0-45.188,20.266-45.188,45.263c0,24.996, 20.231,45.26, 45.188,45.26s 45.186-20.264, 45.186-45.26
+ C 301.188,166.966, 280.958,146.702, 256.002,146.702z M 427.636,20.479c-8.124-8.104-18.856-9.806-26.43-9.806
+ c-7.852,0-16.265,1.826-24.333,5.284c-8.986,3.853-17.254,9.51-24.571,16.812c-7.307,7.29-12.963,15.521-16.813,24.463
+ c-3.443,7.999-5.263,16.336-5.263,24.106c0,7.483, 1.692,18.094, 9.76,26.143c 8.123,8.104, 18.856,9.807, 26.43,9.807
+ c 7.85,0, 16.265-1.827, 24.33-5.284c 8.989-3.854, 17.258-9.509, 24.575-16.812c 7.305-7.29, 12.962-15.521, 16.813-24.463
+ c 3.442-7.999, 5.263-16.335, 5.263-24.106C 437.396,39.138, 435.702,28.53, 427.636,20.479z" data-tags="cut" />
+<glyph unicode="&#xe006;" d="M0,448L 512,448L 512,384L0,384zM0,352L 512,352L 512,288L0,288zM0,256L 512,256L 512,192L0,192zM0,160L 512,160L 512,96L0,96zM0,64L 512,64L 512,0L0,0z" data-tags="alignjustify" />
+<glyph unicode="&#xe005;" d="M0,448L 512,448L 512,384L0,384zM 192,352L 512,352L 512,288L 192,288zM 192,160L 512,160L 512,96L 192,96zM0,256L 512,256L 512,192L0,192zM0,64L 512,64L 512,0L0,0z" data-tags="alignright" />
+<glyph unicode="&#xe004;" d="M0,448L 512,448L 512,384L0,384zM 96,352L 416,352L 416,288L 96,288zM 96,160L 416,160L 416,96L 96,96zM0,256L 512,256L 512,192L0,192zM0,64L 512,64L 512,0L0,0z" data-tags="aligncenter" />
+<glyph unicode="&#xe003;" d="M0,448L 512,448L 512,384L0,384zM0,352L 320,352L 320,288L0,288zM0,160L 320,160L 320,96L0,96zM0,256L 512,256L 512,192L0,192zM0,64L 512,64L 512,0L0,0z" data-tags="alignleft" />
+<glyph unicode="&#xe002;" d="M 512,183.771l0,80.458 l-79.572,7.957c-4.093,15.021-10.044,29.274-17.605,42.49l 52.298,63.919L 410.595,435.12l-63.918-52.298
+ c-13.217,7.562-27.471,13.513-42.491,17.604L 296.229,480l-80.458,0 l-7.957-79.573c-15.021-4.093-29.274-10.043-42.49-17.604
+ L 101.405,435.12L 44.88,378.595l 52.298-63.918c-7.562-13.216-13.513-27.47-17.605-42.49L0,264.229l0-80.458 l 79.573-7.957
+ c 4.093-15.021, 10.043-29.274, 17.605-42.491L 44.88,69.405l 56.524-56.524l 63.919,52.298c 13.216-7.562, 27.47-13.514, 42.49-17.605
+ L 215.771-32l 80.458,0 l 7.957,79.572c 15.021,4.093, 29.274,10.044, 42.491,17.605l 63.918-52.298l 56.524,56.524l-52.298,63.918
+ c 7.562,13.217, 13.514,27.471, 17.605,42.49L 512,183.771z M 352,192l-64-64l-64,0 l-64,64l0,64 l 64,64l 64,0 l 64-64L 352,192 z" data-tags="fullpage" />
+<glyph unicode="&#xe001;" d="M 451.716,380.285l-71.432,71.431C 364.728,467.272, 334,480, 312,480L 72,480 C 50,480, 32,462, 32,440l0-432 c0-22, 18-40, 40-40l 368,0 c 22,0, 40,18, 40,40
+ L 480,312 C 480,334, 467.272,364.729, 451.716,380.285z M 429.089,357.657c 1.565-1.565, 3.125-3.487, 4.64-5.657L 352,352 L 352,433.728
+ c 2.17-1.515, 4.092-3.075, 5.657-4.64L 429.089,357.657z M 448,8c0-4.336-3.664-8-8-8L 72,0 c-4.336,0-8,3.664-8,8L 64,440 c0,4.336, 3.664,8, 8,8
+ l 240,0 c 2.416,0, 5.127-0.305, 8-0.852L 320,320 l 127.148,0 c 0.547-2.873, 0.852-5.583, 0.852-8L 448,8 z" data-tags="newdocument" />
+<glyph unicode="&#xe000;" d="M 448,480L0,480 l0-512 l 512,0 L 512,416 L 448,480z M 256,416l 64,0 l0-128 l-64,0 L 256,416 z M 448,32L 64,32 L 64,416 l 32,0 l0-160 l 288,0 L 384,416 l 37.489,0 L 448,389.491L 448,32 z" data-tags="save" />
+<glyph unicode="&#xe033;" d="M 64,208L 208,64L 448,304L 384,368L 208,192L 128,272 z" />
+<glyph unicode="&#xe035;" d="M 256,224L 256,160L 272,160L 288,192L 320,192L 320,64L 296,64L 296,32L 408,32L 408,64L 384,64L 384,192L 416,192L 432,160L 448,160L 448,224 zM 416,320L 416,400 c0,8.8-7.2,16-16,16L 288,416 L 288,448 c0,17.6-14.4,32-32,32l-64,0 c-17.602,0-32-14.4-32-32l0-32 L 48,416 c-8.801,0-16-7.2-16-16l0-320
+ c0-8.8, 7.199-16, 16-16l 144,0 l0-96 l 320,0 L 512,320 L 416,320 z M 192,447.943c 0.017,0.019, 0.036,0.039, 0.057,0.057l 63.884,0
+ c 0.021-0.018, 0.041-0.038, 0.059-0.057L 256,416 l-64,0 L 192,447.943 z M 96,352L 96,384 l 256,0 l0-32 L 96,352 z M 480,0L 224,0 L 224,288 l 256,0 L 480,0 z" data-tags="pastetext" />
+<glyph unicode="&#x20;" horiz-adv-x="256" />
+</font></defs></svg> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/tinymce.eot b/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/tinymce.eot
new file mode 100644
index 000000000..c1085bfd2
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/tinymce.eot
Binary files differ
diff --git a/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/tinymce.svg b/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/tinymce.svg
new file mode 100644
index 000000000..feb9ba38d
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/tinymce.svg
@@ -0,0 +1,63 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
+<svg xmlns="http://www.w3.org/2000/svg">
+<metadata>Generated by IcoMoon</metadata>
+<defs>
+<font id="tinymce" horiz-adv-x="512">
+<font-face units-per-em="512" ascent="480" descent="-32" />
+<missing-glyph horiz-adv-x="512" />
+<glyph unicode="&#x20;" d="" horiz-adv-x="256" />
+<glyph unicode="&#xe000;" d="M448 480h-448v-512h512v448l-64 64zM256 416h64v-128h-64v128zM448 32h-384v384h32v-160h288v160h37.489l26.511-26.509v-357.491z" />
+<glyph unicode="&#xe001;" d="M451.716 380.285l-71.432 71.431c-15.556 15.556-46.284 28.284-68.284 28.284h-240c-22 0-40-18-40-40v-432c0-22 18-40 40-40h368c22 0 40 18 40 40v304c0 22-12.728 52.729-28.284 68.285zM429.089 357.657c1.565-1.565 3.125-3.487 4.64-5.657h-81.729v81.728c2.17-1.515 4.092-3.075 5.657-4.64l71.432-71.431zM448 8c0-4.336-3.664-8-8-8h-368c-4.336 0-8 3.664-8 8v432c0 4.336 3.664 8 8 8h240c2.416 0 5.127-0.305 8-0.852v-127.148h127.148c0.547-2.873 0.852-5.583 0.852-8v-304z" />
+<glyph unicode="&#xe002;" d="M512 183.771v80.458l-79.572 7.957c-4.093 15.021-10.044 29.274-17.605 42.49l52.298 63.919-56.526 56.525-63.918-52.298c-13.217 7.562-27.471 13.513-42.491 17.604l-7.957 79.574h-80.458l-7.957-79.573c-15.021-4.093-29.274-10.043-42.49-17.604l-63.919 52.297-56.525-56.525 52.298-63.918c-7.562-13.216-13.513-27.47-17.605-42.49l-79.573-7.958v-80.458l79.573-7.957c4.093-15.021 10.043-29.274 17.605-42.491l-52.298-63.918 56.524-56.524 63.919 52.298c13.216-7.562 27.47-13.514 42.49-17.605l7.958-79.574h80.458l7.957 79.572c15.021 4.093 29.274 10.044 42.491 17.605l63.918-52.298 56.524 56.524-52.298 63.918c7.562 13.217 13.514 27.471 17.605 42.49l79.574 7.96zM352 192l-64-64h-64l-64 64v64l64 64h64l64-64v-64z" />
+<glyph unicode="&#xe003;" d="M0 448h512v-64h-512zM0 352h320v-64h-320zM0 160h320v-64h-320zM0 256h512v-64h-512zM0 64h512v-64h-512z" />
+<glyph unicode="&#xe004;" d="M0 448h512v-64h-512zM96 352h320v-64h-320zM96 160h320v-64h-320zM0 256h512v-64h-512zM0 64h512v-64h-512z" />
+<glyph unicode="&#xe005;" d="M0 448h512v-64h-512zM192 352h320v-64h-320zM192 160h320v-64h-320zM0 256h512v-64h-512zM0 64h512v-64h-512z" />
+<glyph unicode="&#xe006;" d="M0 448h512v-64h-512zM0 352h512v-64h-512zM0 256h512v-64h-512zM0 160h512v-64h-512zM0 64h512v-64h-512z" />
+<glyph unicode="&#xe007;" d="M445.387 125.423c-22.827 22.778-51.864 34.536-78.973 34.536h-14.556l-31.952 32.004 127.81 128.019c31.952 32.005 31.952 96.014 0 128.019l-191.715-192.028-191.716 192.027c-31.952-32.004-31.952-96.014 0-128.019l127.811-128.017-31.953-32.004h-14.557c-27.11 0-56.146-11.759-78.974-34.538-40.811-40.721-46.325-101.242-12.315-135.175 14.985-14.951 35.144-22.247 56.498-22.247 27.108 0 56.145 11.757 78.973 34.536 26.792 26.732 38.371 62 33.542 92.674l32.692 32.744 32.688-32.744c-4.828-30.674 6.753-65.941 33.542-92.674 22.831-22.779 51.866-34.536 78.974-34.536 21.354 0 41.512 7.296 56.497 22.248 34.010 33.933 28.494 94.454-12.316 135.175zM176.512 57.231c-3.849-8.941-9.505-17.173-16.813-24.463-7.318-7.302-15.586-12.959-24.574-16.812-8.066-3.458-16.48-5.284-24.331-5.284-7.573 0-18.306 1.701-26.431 9.806-8.068 8.052-9.76 18.659-9.76 26.144 0 7.771 1.821 16.105 5.263 24.106 3.85 8.942 9.507 17.173 16.813 24.463 7.317 7.303 15.586 12.957 24.575 16.812 8.067 3.457 16.48 5.284 24.332 5.284 7.573 0 18.306-1.7 26.429-9.807 8.067-8.049 9.761-18.658 9.761-26.142 0.001-7.771-1.819-16.108-5.264-24.107zM256.002 146.702c-24.957 0-45.188 20.266-45.188 45.263 0 24.996 20.231 45.26 45.188 45.26s45.186-20.264 45.186-45.26c0-24.999-20.23-45.263-45.186-45.263zM427.636 20.479c-8.124-8.104-18.856-9.806-26.43-9.806-7.852 0-16.265 1.826-24.333 5.284-8.986 3.853-17.254 9.51-24.571 16.812-7.307 7.29-12.963 15.521-16.813 24.463-3.443 7.999-5.263 16.336-5.263 24.106 0 7.483 1.692 18.094 9.76 26.143 8.123 8.104 18.856 9.807 26.43 9.807 7.85 0 16.265-1.827 24.33-5.284 8.989-3.854 17.258-9.509 24.575-16.812 7.305-7.29 12.962-15.521 16.813-24.463 3.442-7.999 5.263-16.335 5.263-24.106-0.001-7.485-1.695-18.093-9.761-26.144z" />
+<glyph unicode="&#xe008;" d="M416 320v80c0 8.8-7.2 16-16 16h-112v32c0 17.6-14.4 32-32 32h-64c-17.602 0-32-14.4-32-32v-32h-112c-8.801 0-16-7.2-16-16v-320c0-8.8 7.199-16 16-16h144v-96h224l96 96v256h-96zM192 447.943c0.017 0.019 0.036 0.039 0.057 0.057h63.884c0.021-0.018 0.041-0.038 0.059-0.057v-31.943h-64v31.943zM96 352v32h256v-32h-256zM416 13.255v50.745h50.745l-50.745-50.745zM480 96h-96v-96h-160v288h256v-192z" />
+<glyph unicode="&#xe009;" d="M32 480h192v-32h-192zM288 480h192v-32h-192zM476 320h-28v128h-128v-128h-128v128h-128v-128h-28c-19.8 0-36-16.2-36-36v-280c0-19.8 16.2-36 36-36h152c19.8 0 36 16.2 36 36v188h64v-188c0-19.8 16.2-36 36-36h152c19.8 0 36 16.2 36 36v280c0 19.8-16.2 36-36 36zM174 0h-124c-9.9 0-18 7.2-18 16s8.1 16 18 16h124c9.9 0 18-7.2 18-16s-8.1-16-18-16zM272 224h-32c-8.8 0-16 7.2-16 16s7.2 16 16 16h32c8.8 0 16-7.2 16-16s-7.2-16-16-16zM462 0h-124c-9.9 0-18 7.2-18 16s8.1 16 18 16h124c9.9 0 18-7.2 18-16s-8.1-16-18-16z" />
+<glyph unicode="&#xe00a;" d="M192 448h320v-64h-320v64zM192 256h320v-64h-320v64zM192 64h320v-64h-320v64zM0 416c0 35.346 28.654 64 64 64s64-28.654 64-64c0-35.346-28.654-64-64-64-35.346 0-64 28.654-64 64zM0 224c0 35.346 28.654 64 64 64s64-28.654 64-64c0-35.346-28.654-64-64-64-35.346 0-64 28.654-64 64zM0 32c0 35.346 28.654 64 64 64s64-28.654 64-64c0-35.346-28.654-64-64-64-35.346 0-64 28.654-64 64z" />
+<glyph unicode="&#xe00b;" d="M192 64h320v-64h-320zM192 256h320v-64h-320zM192 448h320v-64h-320zM96 480v-128h-32v96h-32v32zM64 217v-25h64v-32h-96v73l64 30v25h-64v32h96v-73zM128 128v-160h-96v32h64v32h-64v32h64v32h-64v32z" />
+<glyph unicode="&#xe00c;" d="M0 448h512v-64h-512zM192 352h320v-64h-320zM192 256h320v-64h-320zM192 160h320v-64h-320zM0 64h512v-64h-512zM0 128v192l128-96z" />
+<glyph unicode="&#xe00d;" d="M0 448h512v-64h-512zM192 352h320v-64h-320zM192 256h320v-64h-320zM192 160h320v-64h-320zM0 64h512v-64h-512zM128 320v-192l-128 96z" />
+<glyph unicode="&#xe00e;" d="M112.5 256c61.856 0 112-50.145 112-112 0-61.856-50.144-112-112-112-61.856 0-112 50.144-112 112l-0.5 16c0 123.712 100.288 224 224 224v-64c-42.737 0-82.917-16.643-113.137-46.863-5.817-5.818-11.126-12.008-15.915-18.51 5.719 0.9 11.58 1.373 17.552 1.373zM400.5 256c61.855 0 112-50.145 112-112 0-61.856-50.145-112-112-112-61.855 0-112 50.144-112 112l-0.5 16c0 123.712 100.288 224 224 224v-64c-42.737 0-82.917-16.643-113.137-46.863-5.818-5.818-11.127-12.008-15.916-18.51 5.72 0.9 11.58 1.373 17.553 1.373z" />
+<glyph unicode="&#xe00f;" d="M380.931-32c56.863 103.016 66.444 260.153-156.931 254.912v-126.912l-192 192 192 192v-124.186c267.481 6.971 297.285-236.107 156.931-387.814z" />
+<glyph unicode="&#xe010;" d="M288 355.814v124.186l192-192-192-192v126.912c-223.375 5.241-213.794-151.896-156.93-254.912-140.356 151.707-110.55 394.785 156.93 387.814z" />
+<glyph unicode="&#xe011;" d="M160 128c8.8-8.8 23.637-8.363 32.971 0.971l158.059 158.058c9.334 9.334 9.77 24.171 0.97 32.971s-23.637 8.363-32.971-0.971l-158.058-158.058c-9.334-9.334-9.771-24.171-0.971-32.971zM238.444 142.444c2.28-4.525 3.495-9.58 3.495-14.848 0-8.808-3.372-17.030-9.496-23.154l-81.691-81.691c-6.124-6.124-14.347-9.496-23.154-9.496s-17.030 3.372-23.154 9.496l-49.691 49.691c-6.124 6.124-9.496 14.347-9.496 23.154s3.372 17.030 9.496 23.154l81.691 81.691c6.124 6.124 14.347 9.497 23.154 9.497 5.268 0 10.322-1.215 14.848-3.495l32.669 32.669c-13.935 10.705-30.72 16.080-47.517 16.080-19.993 0-39.986-7.583-55.154-22.751l-81.691-81.691c-30.335-30.335-30.335-79.974 0-110.309l49.691-49.691c15.167-15.166 35.16-22.75 55.153-22.75 19.994 0 39.987 7.584 55.154 22.751l81.691 81.691c27.91 27.91 30.119 72.149 6.672 102.672l-32.67-32.67zM489.249 407.558l-49.691 49.691c-15.167 15.168-35.16 22.751-55.154 22.751-19.993 0-39.986-7.583-55.154-22.751l-81.691-81.691c-27.91-27.91-30.119-72.149-6.671-102.671l32.669 32.67c-2.279 4.525-3.494 9.58-3.494 14.847 0 8.808 3.372 17.030 9.496 23.154l81.691 81.691c6.124 6.124 14.347 9.497 23.154 9.497s17.030-3.373 23.154-9.497l49.691-49.691c6.124-6.124 9.496-14.347 9.496-23.154s-3.372-17.030-9.496-23.154l-81.691-81.691c-6.124-6.124-14.347-9.496-23.154-9.496-5.268 0-10.322 1.215-14.848 3.495l-32.669-32.669c13.936-10.705 30.72-16.080 47.517-16.080 19.994 0 39.987 7.584 55.154 22.751l81.691 81.691c30.335 30.333 30.335 79.972 0 110.307z" />
+<glyph unicode="&#xe012;" d="M238.444 142.443c2.28-4.524 3.495-9.579 3.495-14.848 0-8.808-3.372-17.029-9.496-23.154l-81.69-81.69c-6.124-6.124-14.348-9.496-23.154-9.496s-17.030 3.372-23.154 9.496l-49.69 49.69c-6.124 6.125-9.496 14.348-9.496 23.154s3.372 17.030 9.496 23.154l81.69 81.691c6.124 6.123 14.348 9.496 23.154 9.496 5.269 0 10.322-1.215 14.848-3.494l32.669 32.668c-13.935 10.705-30.72 16.080-47.517 16.080-19.993 0-39.986-7.583-55.154-22.751l-81.69-81.691c-30.335-30.335-30.335-79.975 0-110.309l49.69-49.691c15.167-15.166 35.16-22.75 55.153-22.75 19.994 0 39.987 7.584 55.154 22.751l81.69 81.69c27.91 27.91 30.119 72.149 6.672 102.673l-32.67-32.669zM489.248 407.558l-49.69 49.691c-15.167 15.168-35.16 22.751-55.154 22.751-19.993 0-39.985-7.583-55.153-22.751l-81.691-81.691c-27.91-27.91-30.119-72.149-6.671-102.671l32.669 32.67c-2.279 4.525-3.494 9.58-3.494 14.847 0 8.808 3.372 17.030 9.496 23.154l81.691 81.691c6.123 6.124 14.347 9.497 23.153 9.497 8.808 0 17.030-3.373 23.154-9.497l49.69-49.691c6.124-6.124 9.496-14.347 9.496-23.154s-3.372-17.030-9.496-23.154l-81.69-81.691c-6.124-6.124-14.347-9.496-23.154-9.496-5.268 0-10.322 1.215-14.848 3.495l-32.669-32.669c13.936-10.705 30.72-16.080 47.517-16.080 19.994 0 39.987 7.584 55.154 22.752l81.69 81.69c30.336 30.333 30.336 79.972 0 110.307zM116.684 340.688l-95.997 95.997 22.628 22.628 95.997-95.997zM192 480h32v-96h-32zM0 288h96v-32h-96zM395.316 107.312l95.998-95.998-22.628-22.628-95.998 95.998zM288 64h32v-96h-32zM416 192h96v-32h-96z" />
+<glyph unicode="&#xe013;" d="M96 480v-512l160 160 160-160v512h-320zM384 45.255l-128 128-128-128v402.745h256v-402.745z" />
+<glyph unicode="&#xe014;" d="M0 416v-416h512v416h-512zM480 32h-448v352h448v-352zM352 304c0 26.51 21.49 48 48 48s48-21.49 48-48c0-26.51-21.49-48-48-48-26.51 0-48 21.49-48 48zM448 64h-384l96 256 128-160 64 48z" />
+<glyph unicode="&#xe015;" d="M0 416v-384h512v384h-512zM96 64h-64v64h64v-64zM96 192h-64v64h64v-64zM96 320h-64v64h64v-64zM384 64h-256v320h256v-320zM480 64h-64v64h64v-64zM480 192h-64v64h64v-64zM480 320h-64v64h64v-64zM192 320v-192l128 96z" />
+<glyph unicode="&#xe016;" d="M224 128h64v-64h-64zM352 352c17.673 0 32-14.327 32-32v-96l-96-64h-64v32l96 64v32h-160v64h192zM256 432c-55.559 0-107.792-21.636-147.078-60.922s-60.922-91.519-60.922-147.078c0-55.559 21.636-107.792 60.922-147.078 39.286-39.286 91.519-60.922 147.078-60.922 55.559 0 107.792 21.636 147.078 60.922 39.286 39.286 60.922 91.519 60.922 147.078 0 55.559-21.636 107.792-60.922 147.078-39.286 39.286-91.519 60.922-147.078 60.922zM256 480v0c141.385 0 256-114.615 256-256s-114.615-256-256-256c-141.385 0-256 114.615-256 256 0 141.385 114.615 256 256 256z" />
+<glyph unicode="&#xe017;" d="M160 352l-128-128 128-128h64l-128 128 128 128zM352 352h-64l128-128-128-128h64l128 128z" />
+<glyph unicode="&#xe018;" d="M256 384c-106.038 0-192-85.961-192-192s85.961-192 192-192c106.037 0 192 85.961 192 192s-85.963 192-192 192zM357.822 90.177c-27.196-27.198-63.358-42.177-101.822-42.177s-74.625 14.979-101.823 42.177c-27.198 27.197-42.177 63.359-42.177 101.823s14.979 74.625 42.177 101.823c27.198 27.198 63.359 42.177 101.823 42.177s74.626-14.979 101.821-42.177c27.201-27.198 42.179-63.359 42.179-101.823s-14.979-74.626-42.178-101.823zM162.965 378.069l-21.47 42.939c-49.437-24.768-89.735-65.066-114.503-114.504l42.938-21.47c20.124 40.168 52.866 72.911 93.035 93.035zM442.067 285.035l42.939 21.469c-24.766 49.438-65.063 89.736-114.502 114.504l-21.472-42.939c40.169-20.124 72.912-52.866 93.035-93.034zM256 288h-32v-96c0-5.055 2.35-9.555 6.011-12.486l-0.006-0.008 80-64 19.988 24.988-73.993 59.195v88.311z" />
+<glyph unicode="&#xe019;" d="M256 320c-104.684 0-197.622-50.278-256-128 58.378-77.723 151.316-128 256-128 104.684 0 197.622 50.277 256 128-58.378 77.722-151.316 128-256 128zM224 256c17.673 0 32-14.327 32-32s-14.327-32-32-32-32 14.327-32 32 14.327 32 32 32zM386.808 127.352c-19.824-10.129-40.826-17.931-62.423-23.188-22.244-5.418-45.251-8.164-68.385-8.164-23.133 0-46.141 2.746-68.384 8.162-21.597 5.259-42.599 13.061-62.423 23.188-31.51 16.101-60.111 38.205-83.82 64.649 23.709 26.444 52.31 48.55 83.82 64.649 16.168 8.261 33.121 14.973 50.541 20.020-9.944-15.121-15.734-33.217-15.734-52.668 0-53.020 42.981-96 96-96 53.019 0 96 42.98 96 96 0 19.451-5.791 37.547-15.733 52.67 17.419-5.048 34.372-11.76 50.541-20.021 31.511-16.099 60.109-38.204 83.819-64.649-23.71-26.443-52.309-48.55-83.819-64.648zM430.459 358.139c-54.36 27.777-113.056 41.861-174.459 41.861-61.403 0-120.099-14.084-174.459-41.861-29.386-15.016-56.866-33.952-81.541-56.038v-54.603c27.669 29.283 60.347 53.877 96.097 72.145 49.81 25.452 103.609 38.357 159.903 38.357s110.093-12.905 159.902-38.358c35.751-18.268 68.429-42.862 96.098-72.145v54.603c-24.675 22.087-52.154 41.023-81.541 56.039z" />
+<glyph unicode="&#xe01a;" d="M161.009 64l28.8 96h132.382l28.8-96h56.816l-95.998 320h-111.618l-96-320h56.818zM237.809 320h36.382l28.8-96h-93.982l28.8 96z" />
+<glyph unicode="&#xe01b;" d="M0 448v-448h512v448h-512zM192 160v96h128v-96h-128zM320 128v-96h-128v96h128zM320 384v-96h-128v96h128zM160 384v-96h-128v96h128zM32 256h128v-96h-128v96zM352 256h128v-96h-128v96zM352 288v96h128v-96h-128zM32 128h128v-96h-128v96zM352 32v96h128v-96h-128z" />
+<glyph unicode="&#xe01c;" d="M0 256h512v-64h-512z" />
+<glyph unicode="&#xe01d;" d="M0 32h288v-64h-288zM96 480h352v-64h-352zM138.694 64l102.344 392.082 61.925-16.164-98.125-375.918zM464.887-32l-64.887 64.887-64.887-64.887-31.113 31.113 64.887 64.887-64.887 64.887 31.113 31.113 64.887-64.887 64.887 64.887 31.113-31.113-64.887-64.887 64.887-64.887z" />
+<glyph unicode="&#xe01e;" d="M384 25v-25h64v-32h-96v73l64 30v25h-64v32h96v-73zM338 352h-68l-94-94-94 94h-68l128-128-128-128h68l94 94 94-94h68l-128 128z" />
+<glyph unicode="&#xe01f;" d="M384 377v-25h64v-32h-96v73l64 30v25h-64v32h96v-73zM338 352h-68l-94-94-94 94h-68l128-128-128-128h68l94 94 94-94h68l-128 128z" />
+<glyph unicode="&#xe020;" d="M352 32h128l32 64v-128h-192v107.107c65.556 28.242 112 98.581 112 180.893 0 107.216-78.799 191.133-176 191.133-97.203 0-176-83.916-176-191.133 0-82.312 46.443-152.651 112-180.893v-107.107h-192v128l32-64h128v16.295c-93.815 33.23-160 113.701-160 207.705 0 123.712 114.615 224 256 224 141.385 0 256-100.288 256-224 0-94.004-66.185-174.475-160-207.705v-16.295z" />
+<glyph unicode="&#xe021;" d="M256 480c-141.385 0-256-114.614-256-256 0-141.385 114.614-256 256-256 141.385 0 256 114.615 256 256 0 141.386-114.615 256-256 256zM256 8c-119.293 0-216 96.706-216 216 0 119.293 96.707 216 216 216 119.295 0 216-96.707 216-216 0-119.294-96.705-216-216-216zM192 320c0-17.673-14.327-32-32-32s-32 14.327-32 32 14.327 32 32 32 32-14.327 32-32zM384 320c0-17.673-14.326-32-32-32s-32 14.327-32 32 14.326 32 32 32 32-14.327 32-32zM256 154c70.537 0 131.344 28.766 159.231 61.596-10.436-85.61-78.144-151.596-159.231-151.596-81.059 0-148.749 66.013-159.222 151.584 27.893-32.823 88.693-61.584 159.222-61.584z" />
+<glyph unicode="&#xe022;" d="M128 448h256v-64h-256zM480 352h-448c-17.6 0-32-14.4-32-32v-160c0-17.6 14.398-32 32-32h96v-128h256v128h96c17.6 0 32 14.4 32 32v160c0 17.6-14.4 32-32 32zM352 32h-192v160h192v-160zM487.2 304c0-12.813-10.387-23.2-23.199-23.2-12.813 0-23.201 10.387-23.201 23.2s10.388 23.2 23.201 23.2c12.813 0 23.199-10.387 23.199-23.2z" />
+<glyph unicode="&#xe023;" d="M512 480v-192l-69.13 69.13-106-106-53.74 53.74 106 106-69.13 69.13zM122.87 410.87l106-106-53.74-53.74-106 106-69.13-69.13v192h192zM442.87 90.87l69.13 69.13v-192h-192l69.13 69.13-106 106 53.74 53.74zM228.87 143.13l-106-106 69.13-69.13h-192v192l69.13-69.13 106 106z" />
+<glyph unicode="&#xe024;" d="M64 352h64v-96h32v192c0 17.6-14.4 32-32 32h-64c-17.6 0-32-14.4-32-32v-192h32v96zM64 448h64v-64h-64v64zM480 448v32h-96c-17.601 0-32-14.4-32-32v-160c0-17.6 14.399-32 32-32h96v32h-96v160h96zM320 400v48c0 17.6-14.4 32-32 32h-96v-224h96c17.6 0 32 14.4 32 32v48c0 17.6-4.4 32-22 32 17.6 0 22 14.4 22 32zM288 288h-64v64h64v-64zM288 384h-64v64h64v-64zM416 192l-208-224-112 144 41 35 71-74 176 151z" />
+<glyph unicode="&#xe025;" d="M224 192h-96v64h96v96h64v-96h96v-64h-96v-96h-64zM512 160v-192h-512v192h64v-128h384v128z" />
+<glyph unicode="&#xe026;" d="M192 384h64v-32h-64zM288 384h64v-32h-64zM448 384v-128h-96v32h64v64h-32v32zM160 288h64v-32h-64zM256 288h64v-32h-64zM96 352v-64h32v-32h-64v128h96v-32zM192 192h64v-32h-64zM288 192h64v-32h-64zM448 192v-128h-96v32h64v64h-32v32zM160 96h64v-32h-64zM256 96h64v-32h-64zM96 160v-64h32v-32h-64v128h96v-32zM480 448h-448v-448h448v448zM512 480v0-512h-512v512h512z" />
+<glyph unicode="&#xe027;" d="M0 224h64v-32h-64zM96 224h96v-32h-96zM224 224h64v-32h-64zM320 224h96v-32h-96zM448 224h64v-32h-64zM440 480l8-224h-384l8 224h16l8-192h320l8 192zM72-32l-8 192h384l-8-192h-16l-8 160h-320l-8-160z" />
+<glyph unicode="&#xe028;" d="M288 448c123.712 0 224-100.288 224-224s-100.288-224-224-224v48c47.012 0 91.209 18.307 124.451 51.549 33.242 33.242 51.549 77.439 51.549 124.451 0 47.011-18.307 91.209-51.549 124.451-33.242 33.242-77.439 51.549-124.451 51.549-47.011 0-91.209-18.307-124.451-51.549-25.57-25.569-42.291-57.623-48.653-92.451h93.104l-112-128-112 128h82.285c15.53 108.551 108.869 192 221.715 192zM384 256v-64h-128v160h64v-96z" />
+<glyph unicode="&#xe02a;" d="M353.94 237.674c18.749 22.271 30.060 51.004 30.060 82.326 0 70.58-57.421 128-128 128h-160v-448h192c70.579 0 128 57.421 128 128 0 46.478-24.899 87.248-62.060 109.674zM192 384h50.75c27.984 0 50.75-28.71 50.75-64s-22.766-64-50.75-64h-50.75v128zM271.5 64h-79.5v128h79.5c29.225 0 53-28.71 53-64s-23.775-64-53-64z" />
+<glyph unicode="&#xe02b;" d="M448 448v-32h-64l-160-384h64v-32h-224v32h64l160 384h-64v32z" />
+<glyph unicode="&#xe02c;" d="M352 448h64v-208c0-79.529-71.634-144-160-144-88.365 0-160 64.471-160 144v208h64v-208c0-20.083 9.119-39.352 25.677-54.253 18.448-16.602 43.423-25.747 70.323-25.747 26.9 0 51.875 9.145 70.323 25.747 16.558 14.901 25.677 34.17 25.677 54.253v208zM96 64h320v-64h-320z" />
+<glyph unicode="&#xe02d;" d="M365.71 221.482c31.96-23.969 50.29-58.043 50.29-93.482s-18.33-69.513-50.29-93.482c-29.679-22.259-68.642-34.518-109.71-34.518-41.069 0-80.031 12.259-109.71 34.518-31.96 23.969-50.29 58.043-50.29 93.482h64c0-34.691 43.963-64 96-64s96 29.309 96 64c0 34.691-43.963 64-96 64-41.069 0-80.031 12.259-109.71 34.518-31.96 23.97-50.29 58.043-50.29 93.482 0 35.439 18.33 69.512 50.29 93.482 29.679 22.259 68.641 34.518 109.71 34.518 41.068 0 80.031-12.259 109.71-34.518 31.96-23.97 50.29-58.043 50.29-93.482h-64c0 34.691-43.963 64-96 64-52.037 0-96-29.309-96-64 0-34.691 43.963-64 96-64 41.068 0 80.031-12.259 109.71-34.518zM0 224h512v-32h-512z" />
+<glyph unicode="&#xe02e;" d="M192 448h256v-64h-64v-384h-64v384h-64v-384h-64v224c-61.856 0-112 50.144-112 112s50.144 112 112 112z" />
+<glyph unicode="&#xe02f;" d="M224 448h256v-64h-64v-384h-64v384h-64v-384h-64v224c-61.856 0-112 50.144-112 112s50.144 112 112 112zM32 256l128-112-128-112z" />
+<glyph unicode="&#xe030;" d="M128 448h256v-64h-64v-384h-64v384h-64v-384h-64v224c-61.856 0-112 50.144-112 112s50.144 112 112 112zM480 32l-128 112 128 112z" />
+<glyph unicode="&#xe031;" d="M416 352h-96v32l-96 96h-224v-384h192v-128h320v288l-96 96zM416 306.745l50.745-50.745h-50.745v50.745zM224 434.745l50.745-50.745h-50.745v50.745zM32 448h160v-96h96v-224h-256v320zM480 0h-256v96h96v224h64v-96h96v-224z" />
+<glyph unicode="&#xe032;" d="M384 352h32v-32h-32zM320 288h32v-32h-32zM320 224h32v-32h-32zM320 160h32v-32h-32zM256 224h32v-32h-32zM256 160h32v-32h-32zM192 160h32v-32h-32zM384 288h32v-32h-32zM384 224h32v-32h-32zM384 160h32v-32h-32zM384 96h32v-32h-32zM320 96h32v-32h-32zM256 96h32v-32h-32zM192 96h32v-32h-32zM128 96h32v-32h-32z" />
+<glyph unicode="&#xe033;" d="M64 208l144-144 240 240-64 64-176-176-80 80z" />
+<glyph unicode="&#xe034;" d="M464 416h-208l-16 32h-176l-32-64h448zM452.17 128h37.43l22.4 224h-512l32-320h242.040c-52.441 18.888-90.040 69.133-90.040 128 0 74.991 61.009 136 136 136 74.99 0 136-61.009 136-136 0-10.839-1.311-21.575-3.83-32zM501.498 23.125l-99.248 87.346c8.727 14.46 13.75 31.407 13.75 49.529 0 53.020-42.98 96-96 96s-96-42.98-96-96 42.98-96 96-96c18.122 0 35.069 5.023 49.529 13.75l87.346-99.248c11.481-13.339 31.059-14.070 43.503-1.626l2.746 2.746c12.444 12.444 11.713 32.022-1.626 43.503zM320 98c-34.242 0-62 27.758-62 62s27.758 62 62 62 62-27.758 62-62-27.758-62-62-62z" />
+<glyph unicode="&#xe035;" d="M256 224v-64h16l16 32h32v-128h-24v-32h112v32h-24v128h32l16-32h16v64zM416 320v80c0 8.8-7.2 16-16 16h-112v32c0 17.6-14.4 32-32 32h-64c-17.602 0-32-14.4-32-32v-32h-112c-8.801 0-16-7.2-16-16v-320c0-8.8 7.199-16 16-16h144v-96h320v352h-96zM192 447.943c0.017 0.019 0.036 0.039 0.057 0.057h63.884c0.021-0.018 0.041-0.038 0.059-0.057v-31.943h-64v31.943zM96 352v32h256v-32h-256zM480 0h-256v288h256v-288z" />
+</font></defs></svg> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/tinymce.ttf b/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/tinymce.ttf
new file mode 100644
index 000000000..58103c2b6
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/tinymce.ttf
Binary files differ
diff --git a/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/tinymce.woff b/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/tinymce.woff
new file mode 100644
index 000000000..ad1ae396a
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/fonts/tinymce.woff
Binary files differ
diff --git a/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/img/anchor.gif b/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/img/anchor.gif
new file mode 100644
index 000000000..606348c7f
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/img/anchor.gif
Binary files differ
diff --git a/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/img/loader.gif b/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/img/loader.gif
new file mode 100644
index 000000000..c69e93723
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/img/loader.gif
Binary files differ
diff --git a/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/img/object.gif b/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/img/object.gif
new file mode 100644
index 000000000..cccd7f023
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/img/object.gif
Binary files differ
diff --git a/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/img/trans.gif b/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/img/trans.gif
new file mode 100644
index 000000000..388486517
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/img/trans.gif
Binary files differ
diff --git a/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/skin.ie7.min.css b/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/skin.ie7.min.css
new file mode 100644
index 000000000..cd2bbdf3f
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/skin.ie7.min.css
@@ -0,0 +1 @@
+.mce-container,.mce-container *,.mce-widget,.mce-widget *,.mce-reset{margin:0;padding:0;border:0;outline:0;vertical-align:top;background:transparent;text-decoration:none;color:#333;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;text-shadow:none;float:none;position:static;width:auto;height:auto;white-space:nowrap;cursor:inherit;-webkit-tap-highlight-color:transparent;line-height:normal;font-weight:normal;text-align:left;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;direction:ltr}.mce-widget button{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}.mce-container *[unselectable]{-moz-user-select:none;-webkit-user-select:none;-o-user-select:none;user-select:none}.mce-fade{opacity:0;-webkit-transition:opacity .15s linear;transition:opacity .15s linear}.mce-fade.mce-in{opacity:1}.mce-tinymce{visibility:visible !important;position:relative}.mce-fullscreen{border:0;padding:0;margin:0;overflow:hidden;height:100%;z-index:100}div.mce-fullscreen{position:fixed;top:0;left:0;width:100%;height:auto}.mce-tinymce{display:block;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px}.mce-wordcount{position:absolute;top:0;right:0;padding:8px}div.mce-edit-area{background:#FFF;filter:none}.mce-statusbar{position:relative}.mce-statusbar .mce-container-body{position:relative}.mce-fullscreen .mce-resizehandle{display:none}.mce-charmap{border-collapse:collapse}.mce-charmap td{cursor:default;border:1px solid #9e9e9e;width:20px;height:20px;line-height:20px;text-align:center;vertical-align:middle;padding:2px}.mce-charmap td div{text-align:center}.mce-charmap td:hover{background:#d9d9d9}.mce-grid td div{border:1px solid #d6d6d6;width:12px;height:12px;margin:2px;cursor:pointer}.mce-grid td div:focus{border-color:#a1a1a1}.mce-grid{border-spacing:2px;border-collapse:separate}.mce-grid a{display:block;border:1px solid transparent}.mce-grid a:hover,.mce-grid a:focus{border-color:#a1a1a1}.mce-grid-border{margin:0 4px 0 4px}.mce-grid-border a{border-color:#d6d6d6;width:13px;height:13px}.mce-grid-border a:hover,.mce-grid-border a.mce-active{border-color:#a1a1a1;background:#c8def4}.mce-text-center{text-align:center}div.mce-tinymce-inline{width:100%;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.mce-toolbar-grp{padding-bottom:2px}.mce-toolbar-grp .mce-flow-layout-item{margin-bottom:0}.mce-rtl .mce-wordcount{left:0;right:auto}.mce-container,.mce-container-body{display:block}.mce-autoscroll{overflow:hidden}.mce-scrollbar{position:absolute;width:7px;height:100%;top:2px;right:2px;opacity:.4;filter:alpha(opacity=40);zoom:1}.mce-scrollbar-h{top:auto;right:auto;left:2px;bottom:2px;width:100%;height:7px}.mce-scrollbar-thumb{position:absolute;background-color:#000;border:1px solid #888;border-color:rgba(85,85,85,0.6);width:5px;height:100%;-webkit-border-radius:7px;-moz-border-radius:7px;border-radius:7px}.mce-scrollbar-h .mce-scrollbar-thumb{width:100%;height:5px}.mce-scrollbar:hover,.mce-scrollbar.mce-active{background-color:#AAA;opacity:.6;filter:alpha(opacity=60);zoom:1;-webkit-border-radius:7px;-moz-border-radius:7px;border-radius:7px}.mce-scroll{position:relative}.mce-panel{border:0 solid #9e9e9e;background-color:#f0f0f0;background-image:-moz-linear-gradient(top, #fdfdfd, #ddd);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fdfdfd), to(#ddd));background-image:-webkit-linear-gradient(top, #fdfdfd, #ddd);background-image:-o-linear-gradient(top, #fdfdfd, #ddd);background-image:linear-gradient(to bottom, #fdfdfd, #ddd);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffdfdfd', endColorstr='#ffdddddd', GradientType=0);zoom:1}.mce-floatpanel{position:absolute;-webkit-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-moz-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);box-shadow:0 5px 10px rgba(0, 0, 0, 0.2)}.mce-floatpanel.mce-fixed{position:fixed}.mce-floatpanel .mce-arrow,.mce-floatpanel .mce-arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.mce-floatpanel .mce-arrow{border-width:11px}.mce-floatpanel .mce-arrow:after{border-width:10px;content:""}.mce-floatpanel.mce-popover{filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background:transparent;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-moz-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);top:0;left:0;background:#fff;border:1px solid #9e9e9e;border:1px solid rgba(0,0,0,0.25)}.mce-floatpanel.mce-popover.mce-bottom{margin-top:10px;*margin-top:0}.mce-floatpanel.mce-popover.mce-bottom>.mce-arrow{left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#9e9e9e;border-bottom-color:rgba(0,0,0,0.25);top:-11px}.mce-floatpanel.mce-popover.mce-bottom>.mce-arrow:after{top:1px;margin-left:-10px;border-top-width:0;border-bottom-color:#fff}.mce-floatpanel.mce-popover.mce-bottom.mce-start{margin-left:-22px}.mce-floatpanel.mce-popover.mce-bottom.mce-start>.mce-arrow{left:20px}.mce-floatpanel.mce-popover.mce-bottom.mce-end{margin-left:22px}.mce-floatpanel.mce-popover.mce-bottom.mce-end>.mce-arrow{right:10px;left:auto}.mce-fullscreen{border:0;padding:0;margin:0;overflow:hidden;background:#fff;height:100%}div.mce-fullscreen{position:fixed;top:0;left:0}#mce-modal-block{opacity:0;filter:alpha(opacity=0);zoom:1;position:fixed;left:0;top:0;width:100%;height:100%;background:#000}#mce-modal-block.mce-in{opacity:.3;filter:alpha(opacity=30);zoom:1}.mce-window-move{cursor:move}.mce-window{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background:transparent;background:#fff;position:fixed;top:0;left:0;opacity:0;-webkit-transition:opacity 150ms ease-in;transition:opacity 150ms ease-in}.mce-window.mce-in{opacity:1}.mce-window-head{padding:9px 15px;border-bottom:1px solid #c5c5c5;position:relative}.mce-window-head .mce-close{position:absolute;right:15px;top:9px;font-size:20px;font-weight:bold;line-height:20px;color:#858585;cursor:pointer;height:20px;overflow:hidden}.mce-close:hover{color:#adadad}.mce-window-head .mce-title{line-height:20px;font-size:20px;font-weight:bold;text-rendering:optimizelegibility;padding-right:10px}.mce-window .mce-container-body{display:block}.mce-foot{display:block;background-color:#fff;border-top:1px solid #c5c5c5;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px}.mce-window-head .mce-dragh{position:absolute;top:0;left:0;cursor:move;width:90%;height:100%}.mce-window iframe{width:100%;height:100%}.mce-window.mce-fullscreen,.mce-window.mce-fullscreen .mce-foot{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.mce-rtl .mce-window-head .mce-close{position:absolute;right:auto;left:15px}.mce-rtl .mce-window-head .mce-dragh{left:auto;right:0}.mce-rtl .mce-window-head .mce-title{direction:rtl;text-align:right}.mce-abs-layout{position:relative}body .mce-abs-layout-item,.mce-abs-end{position:absolute}.mce-abs-end{width:1px;height:1px}.mce-container-body.mce-abs-layout{overflow:hidden}.mce-tooltip{position:absolute;padding:5px;opacity:.8;filter:alpha(opacity=80);zoom:1}.mce-tooltip-inner{font-size:11px;background-color:#000;color:#fff;max-width:200px;padding:5px 8px 4px 8px;text-align:center;white-space:normal}.mce-tooltip-inner{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.mce-tooltip-inner{-webkit-box-shadow:0 0 5px #000000;-moz-box-shadow:0 0 5px #000000;box-shadow:0 0 5px #000000}.mce-tooltip-arrow{position:absolute;width:0;height:0;line-height:0;border:5px dashed #000}.mce-tooltip-arrow-n{border-bottom-color:#000}.mce-tooltip-arrow-s{border-top-color:#000}.mce-tooltip-arrow-e{border-left-color:#000}.mce-tooltip-arrow-w{border-right-color:#000}.mce-tooltip-nw,.mce-tooltip-sw{margin-left:-14px}.mce-tooltip-n .mce-tooltip-arrow{top:0px;left:50%;margin-left:-5px;border-bottom-style:solid;border-top:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-nw .mce-tooltip-arrow{top:0;left:10px;border-bottom-style:solid;border-top:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-ne .mce-tooltip-arrow{top:0;right:10px;border-bottom-style:solid;border-top:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-s .mce-tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-top-style:solid;border-bottom:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-sw .mce-tooltip-arrow{bottom:0;left:10px;border-top-style:solid;border-bottom:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-se .mce-tooltip-arrow{bottom:0;right:10px;border-top-style:solid;border-bottom:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-e .mce-tooltip-arrow{right:0;top:50%;margin-top:-5px;border-left-style:solid;border-right:none;border-top-color:transparent;border-bottom-color:transparent}.mce-tooltip-w .mce-tooltip-arrow{left:0;top:50%;margin-top:-5px;border-right-style:solid;border-left:none;border-top-color:transparent;border-bottom-color:transparent}.mce-btn{border:1px solid #b1b1b1;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25) rgba(0,0,0,0.25);position:relative;text-shadow:0 1px 1px rgba(255,255,255,0.75);display:inline-block;*display:inline;*zoom:1;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);background-color:#f0f0f0;background-image:-moz-linear-gradient(top, #fff, #d9d9d9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fff), to(#d9d9d9));background-image:-webkit-linear-gradient(top, #fff, #d9d9d9);background-image:-o-linear-gradient(top, #fff, #d9d9d9);background-image:linear-gradient(to bottom, #fff, #d9d9d9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffd9d9d9', GradientType=0);zoom:1}.mce-btn:hover,.mce-btn:focus{color:#333;background-color:#e3e3e3;background-image:-moz-linear-gradient(top, #f2f2f2, #ccc);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#f2f2f2), to(#ccc));background-image:-webkit-linear-gradient(top, #f2f2f2, #ccc);background-image:-o-linear-gradient(top, #f2f2f2, #ccc);background-image:linear-gradient(to bottom, #f2f2f2, #ccc);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffcccccc', GradientType=0);zoom:1}.mce-btn.mce-disabled button,.mce-btn.mce-disabled:hover button{cursor:default;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;opacity:.4;filter:alpha(opacity=40);zoom:1}.mce-btn.mce-active,.mce-btn.mce-active:hover{background-color:#d6d6d6;background-image:-moz-linear-gradient(top, #e6e6e6, #c0c0c0);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#e6e6e6), to(#c0c0c0));background-image:-webkit-linear-gradient(top, #e6e6e6, #c0c0c0);background-image:-o-linear-gradient(top, #e6e6e6, #c0c0c0);background-image:linear-gradient(to bottom, #e6e6e6, #c0c0c0);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe6e6e6', endColorstr='#ffc0c0c0', GradientType=0);zoom:1;-webkit-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05)}.mce-btn:not(.mce-disabled):active{background-color:#d6d6d6;background-image:-moz-linear-gradient(top, #e6e6e6, #c0c0c0);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#e6e6e6), to(#c0c0c0));background-image:-webkit-linear-gradient(top, #e6e6e6, #c0c0c0);background-image:-o-linear-gradient(top, #e6e6e6, #c0c0c0);background-image:linear-gradient(to bottom, #e6e6e6, #c0c0c0);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe6e6e6', endColorstr='#ffc0c0c0', GradientType=0);zoom:1;-webkit-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05)}.mce-btn button{padding:4px 10px;font-size:14px;line-height:20px;*line-height:16px;cursor:pointer;color:#333;text-align:center;overflow:visible;-webkit-appearance:none}.mce-btn button::-moz-focus-inner{border:0;padding:0}.mce-btn i{text-shadow:1px 1px #fff}.mce-primary{min-width:50px;color:#fff;border:1px solid #b1b1b1;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25) rgba(0,0,0,0.25);background-color:#006dcc;background-image:-moz-linear-gradient(top, #08c, #04c);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#08c), to(#04c));background-image:-webkit-linear-gradient(top, #08c, #04c);background-image:-o-linear-gradient(top, #08c, #04c);background-image:linear-gradient(to bottom, #08c, #04c);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0044cc', GradientType=0);zoom:1}.mce-primary:hover,.mce-primary:focus{background-color:#005fb3;background-image:-moz-linear-gradient(top, #0077b3, #003cb3);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#0077b3), to(#003cb3));background-image:-webkit-linear-gradient(top, #0077b3, #003cb3);background-image:-o-linear-gradient(top, #0077b3, #003cb3);background-image:linear-gradient(to bottom, #0077b3, #003cb3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0077b3', endColorstr='#ff003cb3', GradientType=0);zoom:1}.mce-primary.mce-disabled button,.mce-primary.mce-disabled:hover button{cursor:default;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;opacity:.4;filter:alpha(opacity=40);zoom:1}.mce-primary.mce-active,.mce-primary.mce-active:hover,.mce-primary:not(.mce-disabled):active{background-color:#005299;background-image:-moz-linear-gradient(top, #069, #039);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#069), to(#039));background-image:-webkit-linear-gradient(top, #069, #039);background-image:-o-linear-gradient(top, #069, #039);background-image:linear-gradient(to bottom, #069, #039);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff006699', endColorstr='#ff003399', GradientType=0);zoom:1;-webkit-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05)}.mce-primary button,.mce-primary button i{color:#fff;text-shadow:1px 1px #333}.mce-btn-large button{padding:9px 14px;font-size:16px;line-height:normal;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.mce-btn-large i{margin-top:2px}.mce-btn-small button{padding:1px 5px;font-size:12px;*padding-bottom:2px}.mce-btn-small i{line-height:20px;vertical-align:top;*line-height:18px}.mce-btn .mce-caret{margin-top:8px;margin-left:0}.mce-btn-small .mce-caret{margin-top:8px;margin-left:0}.mce-caret{display:inline-block;*display:inline;*zoom:1;width:0;height:0;vertical-align:top;border-top:4px solid #333;border-right:4px solid transparent;border-left:4px solid transparent;content:""}.mce-disabled .mce-caret{border-top-color:#aaa}.mce-caret.mce-up{border-bottom:4px solid #333;border-top:0}.mce-rtl .mce-btn button{direction:rtl}.mce-btn-group .mce-btn{border-width:1px 0 1px 0;margin:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.mce-btn-group .mce-first{border-left:1px solid #b1b1b1;border-left:1px solid rgba(0,0,0,0.25);-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.mce-btn-group .mce-last{border-right:1px solid #b1b1b1;border-right:1px solid rgba(0,0,0,0.1);-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}.mce-btn-group .mce-first.mce-last{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.mce-btn-group .mce-btn.mce-flow-layout-item{margin:0}.mce-checkbox{cursor:pointer}i.mce-i-checkbox{margin:0 3px 0 0;border:1px solid #c5c5c5;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);background-color:#f0f0f0;background-image:-moz-linear-gradient(top, #fff, #d9d9d9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fff), to(#d9d9d9));background-image:-webkit-linear-gradient(top, #fff, #d9d9d9);background-image:-o-linear-gradient(top, #fff, #d9d9d9);background-image:linear-gradient(to bottom, #fff, #d9d9d9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffd9d9d9', GradientType=0);zoom:1;text-indent:-10em;*font-size:0;*line-height:0;*text-indent:0;overflow:hidden}.mce-checked i.mce-i-checkbox{color:#333;font-size:16px;line-height:16px;text-indent:0}.mce-checkbox:focus i.mce-i-checkbox,.mce-checkbox.mce-focus i.mce-i-checkbox{border:1px solid rgba(82,168,236,0.8);-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.65);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.65);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.65)}.mce-checkbox.mce-disabled .mce-label,.mce-checkbox.mce-disabled i.mce-i-checkbox{color:#acacac}.mce-rtl .mce-checkbox{direction:rtl;text-align:right}.mce-rtl i.mce-i-checkbox{margin:0 0 0 3px}.mce-colorbutton .mce-ico{position:relative}.mce-colorbutton-grid{margin:4px}.mce-colorbutton button{padding-right:4px}.mce-colorbutton .mce-preview{padding-right:3px;display:block;position:absolute;left:50%;top:50%;margin-left:-14px;margin-top:7px;background:gray;width:13px;height:2px;overflow:hidden}.mce-colorbutton.mce-btn-small .mce-preview{margin-left:-16px;padding-right:0;width:16px}.mce-colorbutton .mce-open{padding-left:4px;border-left:1px solid transparent;border-right:1px solid transparent}.mce-colorbutton:hover .mce-open{border-left-color:#bdbdbd;border-right-color:#bdbdbd}.mce-colorbutton.mce-btn-small .mce-open{padding:0 3px 0 3px}.mce-rtl .mce-colorbutton{direction:rtl}.mce-rtl .mce-colorbutton .mce-preview{margin-left:0;padding-right:0;padding-left:4px;margin-right:-14px}.mce-rtl .mce-colorbutton.mce-btn-small .mce-preview{margin-left:0;padding-right:0;margin-right:-17px;padding-left:0}.mce-rtl .mce-colorbutton button{padding-right:10px;padding-left:10px}.mce-rtl .mce-colorbutton .mce-open{padding-left:4px;padding-right:4px}.mce-combobox{display:inline-block;*display:inline;*zoom:1;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);*height:32px}.mce-combobox input{border:1px solid #c5c5c5;border-right-color:#c5c5c5;height:28px}.mce-combobox.mce-disabled input{color:#adadad}.mce-combobox.mce-has-open input{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.mce-combobox .mce-btn{border-left:0;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.mce-combobox button{padding-right:8px;padding-left:8px}.mce-combobox.mce-disabled .mce-btn button{cursor:default;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;opacity:.4;filter:alpha(opacity=40);zoom:1}.mce-path{display:inline-block;*display:inline;*zoom:1;padding:8px;white-space:normal}.mce-path .mce-txt{display:inline-block;padding-right:3px}.mce-path .mce-path-body{display:inline-block}.mce-path-item{display:inline-block;*display:inline;*zoom:1;cursor:pointer;color:#333}.mce-path-item:hover{text-decoration:underline}.mce-path-item:focus{background:#666;color:#fff}.mce-path .mce-divider{display:inline}.mce-disabled .mce-path-item{color:#aaa}.mce-rtl .mce-path{direction:rtl}.mce-fieldset{border:0 solid #9E9E9E;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.mce-fieldset>.mce-container-body{margin-top:-15px}.mce-fieldset-title{margin-left:5px;padding:0 5px 0 5px}.mce-fit-layout{display:inline-block;*display:inline;*zoom:1}.mce-fit-layout-item{position:absolute}.mce-flow-layout-item{display:inline-block;*display:inline;*zoom:1}.mce-flow-layout-item{margin:2px 0 2px 2px}.mce-flow-layout-item.mce-last{margin-right:2px}.mce-flow-layout{white-space:normal}.mce-tinymce-inline .mce-flow-layout{white-space:nowrap}.mce-rtl .mce-flow-layout{text-align:right;direction:rtl}.mce-rtl .mce-flow-layout-item{margin:2px 2px 2px 0}.mce-rtl .mce-flow-layout-item.mce-last{margin-left:2px}.mce-iframe{border:0 solid #9e9e9e;width:100%;height:100%}.mce-label{display:inline-block;*display:inline;*zoom:1;text-shadow:0 1px 1px rgba(255,255,255,0.75);overflow:hidden}.mce-label.mce-autoscroll{overflow:auto}.mce-label.mce-disabled{color:#aaa}.mce-label.mce-multiline{white-space:pre-wrap}.mce-label.mce-error{color:#a00}.mce-rtl .mce-label{text-align:right;direction:rtl}.mce-menubar .mce-menubtn{border-color:transparent;background:transparent;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;filter:none}.mce-menubar{border:1px solid #c4c4c4}.mce-menubar .mce-menubtn button span{color:#333}.mce-menubar .mce-caret{border-top-color:#333}.mce-menubar .mce-menubtn:hover,.mce-menubar .mce-menubtn.mce-active,.mce-menubar .mce-menubtn:focus{border-color:transparent;background:#e6e6e6;filter:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.mce-menubtn span{color:#333;margin-right:2px;line-height:20px;*line-height:16px}.mce-menubtn.mce-btn-small span{font-size:12px}.mce-menubtn.mce-fixed-width span{display:inline-block;overflow-x:hidden;text-overflow:ellipsis;width:90px}.mce-menubtn.mce-fixed-width.mce-btn-small span{width:70px}.mce-menubtn .mce-caret{*margin-top:6px}.mce-rtl .mce-menubtn button{direction:rtl;text-align:right}.mce-listbox button{text-align:left;padding-right:20px;position:relative}.mce-listbox .mce-caret{position:absolute;margin-top:-2px;right:8px;top:50%}.mce-rtl .mce-listbox .mce-caret{right:auto;left:8px}.mce-rtl .mce-listbox button{padding-right:10px;padding-left:20px}.mce-menu-item{display:block;padding:6px 15px 6px 12px;clear:both;font-weight:normal;line-height:20px;color:#333;white-space:nowrap;cursor:pointer;line-height:normal;border-left:4px solid transparent;margin-bottom:1px}.mce-menu-item .mce-ico,.mce-menu-item .mce-text{color:#333}.mce-menu-item.mce-disabled .mce-text,.mce-menu-item.mce-disabled .mce-ico{color:#adadad}.mce-menu-item:hover .mce-text,.mce-menu-item.mce-selected .mce-text,.mce-menu-item:focus .mce-text{color:#fff}.mce-menu-item:hover .mce-ico,.mce-menu-item.mce-selected .mce-ico,.mce-menu-item:focus .mce-ico{color:#fff}.mce-menu-item.mce-disabled:hover{background:#ccc}.mce-menu-shortcut{display:inline-block;color:#adadad}.mce-menu-shortcut{display:inline-block;*display:inline;*zoom:1;padding:0 15px 0 20px}.mce-menu-item:hover .mce-menu-shortcut,.mce-menu-item.mce-selected .mce-menu-shortcut,.mce-menu-item:focus .mce-menu-shortcut{color:#fff}.mce-menu-item .mce-caret{margin-top:4px;*margin-top:3px;margin-right:6px;border-top:4px solid transparent;border-bottom:4px solid transparent;border-left:4px solid #333}.mce-menu-item.mce-selected .mce-caret,.mce-menu-item:focus .mce-caret,.mce-menu-item:hover .mce-caret{border-left-color:#fff}.mce-menu-align .mce-menu-shortcut{*margin-top:-2px}.mce-menu-align .mce-menu-shortcut,.mce-menu-align .mce-caret{position:absolute;right:0}.mce-menu-item.mce-active i{visibility:visible}.mce-menu-item-normal.mce-active{background-color:#c8def4}.mce-menu-item-preview.mce-active{border-left:5px solid #aaa}.mce-menu-item-normal.mce-active .mce-text{color:#333}.mce-menu-item-normal.mce-active:hover .mce-text,.mce-menu-item-normal.mce-active:hover .mce-ico{color:#fff}.mce-menu-item-normal.mce-active:focus .mce-text,.mce-menu-item-normal.mce-active:focus .mce-ico{color:#fff}.mce-menu-item:hover,.mce-menu-item.mce-selected,.mce-menu-item:focus{text-decoration:none;color:#fff;background-color:#0081c2;background-image:-moz-linear-gradient(top, #08c, #0077b3);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#08c), to(#0077b3));background-image:-webkit-linear-gradient(top, #08c, #0077b3);background-image:-o-linear-gradient(top, #08c, #0077b3);background-image:linear-gradient(to bottom, #08c, #0077b3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0);zoom:1}div.mce-menu .mce-menu-item-sep,.mce-menu-item-sep:hover{border:0;padding:0;height:1px;margin:9px 1px;overflow:hidden;background:#cbcbcb;border-bottom:1px solid #fff;cursor:default;filter:none}.mce-menu.mce-rtl{direction:rtl}.mce-rtl .mce-menu-item{text-align:right;direction:rtl;padding:6px 12px 6px 15px}.mce-menu-align.mce-rtl .mce-menu-shortcut,.mce-menu-align.mce-rtl .mce-caret{right:auto;left:0}.mce-rtl .mce-menu-item .mce-caret{margin-left:6px;margin-right:0;border-right:4px solid #333;border-left:0}.mce-rtl .mce-menu-item.mce-selected .mce-caret,.mce-rtl .mce-menu-item:focus .mce-caret,.mce-rtl .mce-menu-item:hover .mce-caret{border-left-color:transparent;border-right-color:#fff}.mce-menu{position:absolute;left:0;top:0;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background:transparent;z-index:1000;padding:5px 0 5px 0;margin:2px 0 0;min-width:160px;background:#fff;border:1px solid #989898;border:1px solid rgba(0,0,0,0.2);z-index:1002;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-moz-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);max-height:400px;overflow:auto;overflow-x:hidden}.mce-menu i{display:none}.mce-menu-has-icons i{display:inline-block;*display:inline}.mce-menu-sub-tr-tl{margin:-6px 0 0 -1px}.mce-menu-sub-br-bl{margin:6px 0 0 -1px}.mce-menu-sub-tl-tr{margin:-6px 0 0 1px}.mce-menu-sub-bl-br{margin:6px 0 0 1px}.mce-container-body .mce-resizehandle{position:absolute;right:0;bottom:0;width:16px;height:16px;visibility:visible;cursor:s-resize;margin:0}.mce-container-body .mce-resizehandle-both{cursor:se-resize}i.mce-i-resize{color:#333}.mce-spacer{visibility:hidden}.mce-splitbtn .mce-open{border-left:1px solid transparent;border-right:1px solid transparent}.mce-splitbtn:hover .mce-open{border-left-color:#bdbdbd;border-right-color:#bdbdbd}.mce-splitbtn button{padding-right:4px}.mce-splitbtn .mce-open{padding-left:4px}.mce-splitbtn .mce-open.mce-active{-webkit-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05)}.mce-splitbtn.mce-btn-small .mce-open{padding:0 3px 0 3px}.mce-rtl .mce-splitbtn{direction:rtl;text-align:right}.mce-rtl .mce-splitbtn button{padding-right:10px;padding-left:10px}.mce-rtl .mce-splitbtn .mce-open{padding-left:4px;padding-right:4px}.mce-stack-layout-item{display:block}.mce-tabs{display:block;border-bottom:1px solid #c5c5c5}.mce-tab{display:inline-block;*display:inline;*zoom:1;border:1px solid #c5c5c5;border-width:0 1px 0 0;background:#e3e3e3;padding:8px;text-shadow:0 1px 1px rgba(255,255,255,0.75);height:13px;cursor:pointer}.mce-tab:hover{background:#fdfdfd}.mce-tab.mce-active{background:#fdfdfd;border-bottom-color:transparent;margin-bottom:-1px;height:14px}.mce-rtl .mce-tabs{text-align:right;direction:rtl}.mce-rtl .mce-tab{border-width:0 0 0 1px}.mce-textbox{background:#fff;border:1px solid #c5c5c5;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);display:inline-block;-webkit-transition:border linear .2s, box-shadow linear .2s;transition:border linear .2s, box-shadow linear .2s;height:28px;resize:none;padding:0 4px 0 4px;white-space:pre-wrap;*white-space:pre;color:#333}.mce-textbox:focus,.mce-textbox.mce-focus{border-color:rgba(82,168,236,0.8);-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.65);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.65);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.65)}.mce-placeholder .mce-textbox{color:#aaa}.mce-textbox.mce-multiline{padding:4px}.mce-textbox.mce-disabled{color:#adadad}.mce-rtl .mce-textbox{text-align:right;direction:rtl}.mce-throbber{position:absolute;top:0;left:0;width:100%;height:100%;opacity:.6;filter:alpha(opacity=60);zoom:1;background:#fff url('img/loader.gif') no-repeat center center}.mce-throbber-inline{position:static;height:50px}@font-face{font-family:'tinymce';src:url('fonts/tinymce.eot');src:url('fonts/tinymce.eot?#iefix') format('embedded-opentype'),url('fonts/tinymce.woff') format('woff'),url('fonts/tinymce.ttf') format('truetype'),url('fonts/tinymce.svg#tinymce') format('svg');font-weight:normal;font-style:normal}@font-face{font-family:'tinymce-small';src:url('fonts/tinymce-small.eot');src:url('fonts/tinymce-small.eot?#iefix') format('embedded-opentype'),url('fonts/tinymce-small.woff') format('woff'),url('fonts/tinymce-small.ttf') format('truetype'),url('fonts/tinymce-small.svg#tinymce') format('svg');font-weight:normal;font-style:normal}.mce-ico{font-family:'tinymce';font-style:normal;font-weight:normal;font-size:16px;line-height:16px;vertical-align:text-top;-webkit-font-smoothing:antialiased;display:inline-block;background:transparent center center;width:16px;height:16px;color:#333;-ie7-icon:' '}.mce-btn-small .mce-ico{font-family:'tinymce-small'}.mce-ico,i.mce-i-checkbox{zoom:expression(this.runtimeStyle['zoom'] = '1', this.innerHTML = this.currentStyle['-ie7-icon'].substr(1, 1) + '&nbsp;')}.mce-i-save{-ie7-icon:"\e000"}.mce-i-newdocument{-ie7-icon:"\e001"}.mce-i-fullpage{-ie7-icon:"\e002"}.mce-i-alignleft{-ie7-icon:"\e003"}.mce-i-aligncenter{-ie7-icon:"\e004"}.mce-i-alignright{-ie7-icon:"\e005"}.mce-i-alignjustify{-ie7-icon:"\e006"}.mce-i-cut{-ie7-icon:"\e007"}.mce-i-paste{-ie7-icon:"\e008"}.mce-i-searchreplace{-ie7-icon:"\e009"}.mce-i-bullist{-ie7-icon:"\e00a"}.mce-i-numlist{-ie7-icon:"\e00b"}.mce-i-indent{-ie7-icon:"\e00c"}.mce-i-outdent{-ie7-icon:"\e00d"}.mce-i-blockquote{-ie7-icon:"\e00e"}.mce-i-undo{-ie7-icon:"\e00f"}.mce-i-redo{-ie7-icon:"\e010"}.mce-i-link{-ie7-icon:"\e011"}.mce-i-unlink{-ie7-icon:"\e012"}.mce-i-anchor{-ie7-icon:"\e013"}.mce-i-image{-ie7-icon:"\e014"}.mce-i-media{-ie7-icon:"\e015"}.mce-i-help{-ie7-icon:"\e016"}.mce-i-code{-ie7-icon:"\e017"}.mce-i-insertdatetime{-ie7-icon:"\e018"}.mce-i-preview{-ie7-icon:"\e019"}.mce-i-forecolor{-ie7-icon:"\e01a"}.mce-i-backcolor{-ie7-icon:"\e01a"}.mce-i-table{-ie7-icon:"\e01b"}.mce-i-hr{-ie7-icon:"\e01c"}.mce-i-removeformat{-ie7-icon:"\e01d"}.mce-i-subscript{-ie7-icon:"\e01e"}.mce-i-superscript{-ie7-icon:"\e01f"}.mce-i-charmap{-ie7-icon:"\e020"}.mce-i-emoticons{-ie7-icon:"\e021"}.mce-i-print{-ie7-icon:"\e022"}.mce-i-fullscreen{-ie7-icon:"\e023"}.mce-i-spellchecker{-ie7-icon:"\e024"}.mce-i-nonbreaking{-ie7-icon:"\e025"}.mce-i-template{-ie7-icon:"\e026"}.mce-i-pagebreak{-ie7-icon:"\e027"}.mce-i-restoredraft{-ie7-icon:"\e028"}.mce-i-untitled{-ie7-icon:"\e029"}.mce-i-bold{-ie7-icon:"\e02a"}.mce-i-italic{-ie7-icon:"\e02b"}.mce-i-underline{-ie7-icon:"\e02c"}.mce-i-strikethrough{-ie7-icon:"\e02d"}.mce-i-visualchars{-ie7-icon:"\e02e"}.mce-i-ltr{-ie7-icon:"\e02f"}.mce-i-rtl{-ie7-icon:"\e030"}.mce-i-copy{-ie7-icon:"\e031"}.mce-i-resize{-ie7-icon:"\e032"}.mce-i-browse{-ie7-icon:"\e034"}.mce-i-pastetext{-ie7-icon:"\e035"}.mce-i-checkbox,.mce-i-selected{-ie7-icon:"\e033"}.mce-i-selected{visibility:hidden}.mce-i-backcolor{background:#BBB} \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/skin.min.css b/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/skin.min.css
new file mode 100644
index 000000000..284ac1dea
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/js/skins/lightgray/skin.min.css
@@ -0,0 +1 @@
+.mce-container,.mce-container *,.mce-widget,.mce-widget *,.mce-reset{margin:0;padding:0;border:0;outline:0;vertical-align:top;background:transparent;text-decoration:none;color:#333;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;text-shadow:none;float:none;position:static;width:auto;height:auto;white-space:nowrap;cursor:inherit;-webkit-tap-highlight-color:transparent;line-height:normal;font-weight:normal;text-align:left;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;direction:ltr}.mce-widget button{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}.mce-container *[unselectable]{-moz-user-select:none;-webkit-user-select:none;-o-user-select:none;user-select:none}.mce-fade{opacity:0;-webkit-transition:opacity .15s linear;transition:opacity .15s linear}.mce-fade.mce-in{opacity:1}.mce-tinymce{visibility:visible !important;position:relative}.mce-fullscreen{border:0;padding:0;margin:0;overflow:hidden;height:100%;z-index:100}div.mce-fullscreen{position:fixed;top:0;left:0;width:100%;height:auto}.mce-tinymce{display:block;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px}.mce-wordcount{position:absolute;top:0;right:0;padding:8px}div.mce-edit-area{background:#FFF;filter:none}.mce-statusbar{position:relative}.mce-statusbar .mce-container-body{position:relative}.mce-fullscreen .mce-resizehandle{display:none}.mce-charmap{border-collapse:collapse}.mce-charmap td{cursor:default;border:1px solid #9e9e9e;width:20px;height:20px;line-height:20px;text-align:center;vertical-align:middle;padding:2px}.mce-charmap td div{text-align:center}.mce-charmap td:hover{background:#d9d9d9}.mce-grid td div{border:1px solid #d6d6d6;width:12px;height:12px;margin:2px;cursor:pointer}.mce-grid td div:focus{border-color:#a1a1a1}.mce-grid{border-spacing:2px;border-collapse:separate}.mce-grid a{display:block;border:1px solid transparent}.mce-grid a:hover,.mce-grid a:focus{border-color:#a1a1a1}.mce-grid-border{margin:0 4px 0 4px}.mce-grid-border a{border-color:#d6d6d6;width:13px;height:13px}.mce-grid-border a:hover,.mce-grid-border a.mce-active{border-color:#a1a1a1;background:#c8def4}.mce-text-center{text-align:center}div.mce-tinymce-inline{width:100%;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.mce-toolbar-grp{padding-bottom:2px}.mce-toolbar-grp .mce-flow-layout-item{margin-bottom:0}.mce-rtl .mce-wordcount{left:0;right:auto}.mce-container,.mce-container-body{display:block}.mce-autoscroll{overflow:hidden}.mce-scrollbar{position:absolute;width:7px;height:100%;top:2px;right:2px;opacity:.4;filter:alpha(opacity=40);zoom:1}.mce-scrollbar-h{top:auto;right:auto;left:2px;bottom:2px;width:100%;height:7px}.mce-scrollbar-thumb{position:absolute;background-color:#000;border:1px solid #888;border-color:rgba(85,85,85,0.6);width:5px;height:100%;-webkit-border-radius:7px;-moz-border-radius:7px;border-radius:7px}.mce-scrollbar-h .mce-scrollbar-thumb{width:100%;height:5px}.mce-scrollbar:hover,.mce-scrollbar.mce-active{background-color:#AAA;opacity:.6;filter:alpha(opacity=60);zoom:1;-webkit-border-radius:7px;-moz-border-radius:7px;border-radius:7px}.mce-scroll{position:relative}.mce-panel{border:0 solid #9e9e9e;background-color:#f0f0f0;background-image:-moz-linear-gradient(top, #fdfdfd, #ddd);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fdfdfd), to(#ddd));background-image:-webkit-linear-gradient(top, #fdfdfd, #ddd);background-image:-o-linear-gradient(top, #fdfdfd, #ddd);background-image:linear-gradient(to bottom, #fdfdfd, #ddd);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffdfdfd', endColorstr='#ffdddddd', GradientType=0);zoom:1}.mce-floatpanel{position:absolute;-webkit-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-moz-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);box-shadow:0 5px 10px rgba(0, 0, 0, 0.2)}.mce-floatpanel.mce-fixed{position:fixed}.mce-floatpanel .mce-arrow,.mce-floatpanel .mce-arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.mce-floatpanel .mce-arrow{border-width:11px}.mce-floatpanel .mce-arrow:after{border-width:10px;content:""}.mce-floatpanel.mce-popover{filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background:transparent;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-moz-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);top:0;left:0;background:#fff;border:1px solid #9e9e9e;border:1px solid rgba(0,0,0,0.25)}.mce-floatpanel.mce-popover.mce-bottom{margin-top:10px;*margin-top:0}.mce-floatpanel.mce-popover.mce-bottom>.mce-arrow{left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#9e9e9e;border-bottom-color:rgba(0,0,0,0.25);top:-11px}.mce-floatpanel.mce-popover.mce-bottom>.mce-arrow:after{top:1px;margin-left:-10px;border-top-width:0;border-bottom-color:#fff}.mce-floatpanel.mce-popover.mce-bottom.mce-start{margin-left:-22px}.mce-floatpanel.mce-popover.mce-bottom.mce-start>.mce-arrow{left:20px}.mce-floatpanel.mce-popover.mce-bottom.mce-end{margin-left:22px}.mce-floatpanel.mce-popover.mce-bottom.mce-end>.mce-arrow{right:10px;left:auto}.mce-fullscreen{border:0;padding:0;margin:0;overflow:hidden;background:#fff;height:100%}div.mce-fullscreen{position:fixed;top:0;left:0}#mce-modal-block{opacity:0;filter:alpha(opacity=0);zoom:1;position:fixed;left:0;top:0;width:100%;height:100%;background:#000}#mce-modal-block.mce-in{opacity:.3;filter:alpha(opacity=30);zoom:1}.mce-window-move{cursor:move}.mce-window{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background:transparent;background:#fff;position:fixed;top:0;left:0;opacity:0;-webkit-transition:opacity 150ms ease-in;transition:opacity 150ms ease-in}.mce-window.mce-in{opacity:1}.mce-window-head{padding:9px 15px;border-bottom:1px solid #c5c5c5;position:relative}.mce-window-head .mce-close{position:absolute;right:15px;top:9px;font-size:20px;font-weight:bold;line-height:20px;color:#858585;cursor:pointer;height:20px;overflow:hidden}.mce-close:hover{color:#adadad}.mce-window-head .mce-title{line-height:20px;font-size:20px;font-weight:bold;text-rendering:optimizelegibility;padding-right:10px}.mce-window .mce-container-body{display:block}.mce-foot{display:block;background-color:#fff;border-top:1px solid #c5c5c5;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px}.mce-window-head .mce-dragh{position:absolute;top:0;left:0;cursor:move;width:90%;height:100%}.mce-window iframe{width:100%;height:100%}.mce-window.mce-fullscreen,.mce-window.mce-fullscreen .mce-foot{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.mce-rtl .mce-window-head .mce-close{position:absolute;right:auto;left:15px}.mce-rtl .mce-window-head .mce-dragh{left:auto;right:0}.mce-rtl .mce-window-head .mce-title{direction:rtl;text-align:right}.mce-abs-layout{position:relative}body .mce-abs-layout-item,.mce-abs-end{position:absolute}.mce-abs-end{width:1px;height:1px}.mce-container-body.mce-abs-layout{overflow:hidden}.mce-tooltip{position:absolute;padding:5px;opacity:.8;filter:alpha(opacity=80);zoom:1}.mce-tooltip-inner{font-size:11px;background-color:#000;color:#fff;max-width:200px;padding:5px 8px 4px 8px;text-align:center;white-space:normal}.mce-tooltip-inner{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.mce-tooltip-inner{-webkit-box-shadow:0 0 5px #000000;-moz-box-shadow:0 0 5px #000000;box-shadow:0 0 5px #000000}.mce-tooltip-arrow{position:absolute;width:0;height:0;line-height:0;border:5px dashed #000}.mce-tooltip-arrow-n{border-bottom-color:#000}.mce-tooltip-arrow-s{border-top-color:#000}.mce-tooltip-arrow-e{border-left-color:#000}.mce-tooltip-arrow-w{border-right-color:#000}.mce-tooltip-nw,.mce-tooltip-sw{margin-left:-14px}.mce-tooltip-n .mce-tooltip-arrow{top:0px;left:50%;margin-left:-5px;border-bottom-style:solid;border-top:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-nw .mce-tooltip-arrow{top:0;left:10px;border-bottom-style:solid;border-top:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-ne .mce-tooltip-arrow{top:0;right:10px;border-bottom-style:solid;border-top:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-s .mce-tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-top-style:solid;border-bottom:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-sw .mce-tooltip-arrow{bottom:0;left:10px;border-top-style:solid;border-bottom:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-se .mce-tooltip-arrow{bottom:0;right:10px;border-top-style:solid;border-bottom:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-e .mce-tooltip-arrow{right:0;top:50%;margin-top:-5px;border-left-style:solid;border-right:none;border-top-color:transparent;border-bottom-color:transparent}.mce-tooltip-w .mce-tooltip-arrow{left:0;top:50%;margin-top:-5px;border-right-style:solid;border-left:none;border-top-color:transparent;border-bottom-color:transparent}.mce-btn{border:1px solid #b1b1b1;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25) rgba(0,0,0,0.25);position:relative;text-shadow:0 1px 1px rgba(255,255,255,0.75);display:inline-block;*display:inline;*zoom:1;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);background-color:#f0f0f0;background-image:-moz-linear-gradient(top, #fff, #d9d9d9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fff), to(#d9d9d9));background-image:-webkit-linear-gradient(top, #fff, #d9d9d9);background-image:-o-linear-gradient(top, #fff, #d9d9d9);background-image:linear-gradient(to bottom, #fff, #d9d9d9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffd9d9d9', GradientType=0);zoom:1}.mce-btn:hover,.mce-btn:focus{color:#333;background-color:#e3e3e3;background-image:-moz-linear-gradient(top, #f2f2f2, #ccc);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#f2f2f2), to(#ccc));background-image:-webkit-linear-gradient(top, #f2f2f2, #ccc);background-image:-o-linear-gradient(top, #f2f2f2, #ccc);background-image:linear-gradient(to bottom, #f2f2f2, #ccc);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffcccccc', GradientType=0);zoom:1}.mce-btn.mce-disabled button,.mce-btn.mce-disabled:hover button{cursor:default;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;opacity:.4;filter:alpha(opacity=40);zoom:1}.mce-btn.mce-active,.mce-btn.mce-active:hover{background-color:#d6d6d6;background-image:-moz-linear-gradient(top, #e6e6e6, #c0c0c0);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#e6e6e6), to(#c0c0c0));background-image:-webkit-linear-gradient(top, #e6e6e6, #c0c0c0);background-image:-o-linear-gradient(top, #e6e6e6, #c0c0c0);background-image:linear-gradient(to bottom, #e6e6e6, #c0c0c0);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe6e6e6', endColorstr='#ffc0c0c0', GradientType=0);zoom:1;-webkit-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05)}.mce-btn:not(.mce-disabled):active{background-color:#d6d6d6;background-image:-moz-linear-gradient(top, #e6e6e6, #c0c0c0);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#e6e6e6), to(#c0c0c0));background-image:-webkit-linear-gradient(top, #e6e6e6, #c0c0c0);background-image:-o-linear-gradient(top, #e6e6e6, #c0c0c0);background-image:linear-gradient(to bottom, #e6e6e6, #c0c0c0);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe6e6e6', endColorstr='#ffc0c0c0', GradientType=0);zoom:1;-webkit-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05)}.mce-btn button{padding:4px 10px;font-size:14px;line-height:20px;*line-height:16px;cursor:pointer;color:#333;text-align:center;overflow:visible;-webkit-appearance:none}.mce-btn button::-moz-focus-inner{border:0;padding:0}.mce-btn i{text-shadow:1px 1px #fff}.mce-primary{min-width:50px;color:#fff;border:1px solid #b1b1b1;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25) rgba(0,0,0,0.25);background-color:#006dcc;background-image:-moz-linear-gradient(top, #08c, #04c);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#08c), to(#04c));background-image:-webkit-linear-gradient(top, #08c, #04c);background-image:-o-linear-gradient(top, #08c, #04c);background-image:linear-gradient(to bottom, #08c, #04c);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0044cc', GradientType=0);zoom:1}.mce-primary:hover,.mce-primary:focus{background-color:#005fb3;background-image:-moz-linear-gradient(top, #0077b3, #003cb3);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#0077b3), to(#003cb3));background-image:-webkit-linear-gradient(top, #0077b3, #003cb3);background-image:-o-linear-gradient(top, #0077b3, #003cb3);background-image:linear-gradient(to bottom, #0077b3, #003cb3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0077b3', endColorstr='#ff003cb3', GradientType=0);zoom:1}.mce-primary.mce-disabled button,.mce-primary.mce-disabled:hover button{cursor:default;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;opacity:.4;filter:alpha(opacity=40);zoom:1}.mce-primary.mce-active,.mce-primary.mce-active:hover,.mce-primary:not(.mce-disabled):active{background-color:#005299;background-image:-moz-linear-gradient(top, #069, #039);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#069), to(#039));background-image:-webkit-linear-gradient(top, #069, #039);background-image:-o-linear-gradient(top, #069, #039);background-image:linear-gradient(to bottom, #069, #039);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff006699', endColorstr='#ff003399', GradientType=0);zoom:1;-webkit-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05)}.mce-primary button,.mce-primary button i{color:#fff;text-shadow:1px 1px #333}.mce-btn-large button{padding:9px 14px;font-size:16px;line-height:normal;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.mce-btn-large i{margin-top:2px}.mce-btn-small button{padding:1px 5px;font-size:12px;*padding-bottom:2px}.mce-btn-small i{line-height:20px;vertical-align:top;*line-height:18px}.mce-btn .mce-caret{margin-top:8px;margin-left:0}.mce-btn-small .mce-caret{margin-top:8px;margin-left:0}.mce-caret{display:inline-block;*display:inline;*zoom:1;width:0;height:0;vertical-align:top;border-top:4px solid #333;border-right:4px solid transparent;border-left:4px solid transparent;content:""}.mce-disabled .mce-caret{border-top-color:#aaa}.mce-caret.mce-up{border-bottom:4px solid #333;border-top:0}.mce-rtl .mce-btn button{direction:rtl}.mce-btn-group .mce-btn{border-width:1px 0 1px 0;margin:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.mce-btn-group .mce-first{border-left:1px solid #b1b1b1;border-left:1px solid rgba(0,0,0,0.25);-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.mce-btn-group .mce-last{border-right:1px solid #b1b1b1;border-right:1px solid rgba(0,0,0,0.1);-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}.mce-btn-group .mce-first.mce-last{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.mce-btn-group .mce-btn.mce-flow-layout-item{margin:0}.mce-checkbox{cursor:pointer}i.mce-i-checkbox{margin:0 3px 0 0;border:1px solid #c5c5c5;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);background-color:#f0f0f0;background-image:-moz-linear-gradient(top, #fff, #d9d9d9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fff), to(#d9d9d9));background-image:-webkit-linear-gradient(top, #fff, #d9d9d9);background-image:-o-linear-gradient(top, #fff, #d9d9d9);background-image:linear-gradient(to bottom, #fff, #d9d9d9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffd9d9d9', GradientType=0);zoom:1;text-indent:-10em;*font-size:0;*line-height:0;*text-indent:0;overflow:hidden}.mce-checked i.mce-i-checkbox{color:#333;font-size:16px;line-height:16px;text-indent:0}.mce-checkbox:focus i.mce-i-checkbox,.mce-checkbox.mce-focus i.mce-i-checkbox{border:1px solid rgba(82,168,236,0.8);-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.65);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.65);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.65)}.mce-checkbox.mce-disabled .mce-label,.mce-checkbox.mce-disabled i.mce-i-checkbox{color:#acacac}.mce-rtl .mce-checkbox{direction:rtl;text-align:right}.mce-rtl i.mce-i-checkbox{margin:0 0 0 3px}.mce-colorbutton .mce-ico{position:relative}.mce-colorbutton-grid{margin:4px}.mce-colorbutton button{padding-right:4px}.mce-colorbutton .mce-preview{padding-right:3px;display:block;position:absolute;left:50%;top:50%;margin-left:-14px;margin-top:7px;background:gray;width:13px;height:2px;overflow:hidden}.mce-colorbutton.mce-btn-small .mce-preview{margin-left:-16px;padding-right:0;width:16px}.mce-colorbutton .mce-open{padding-left:4px;border-left:1px solid transparent;border-right:1px solid transparent}.mce-colorbutton:hover .mce-open{border-left-color:#bdbdbd;border-right-color:#bdbdbd}.mce-colorbutton.mce-btn-small .mce-open{padding:0 3px 0 3px}.mce-rtl .mce-colorbutton{direction:rtl}.mce-rtl .mce-colorbutton .mce-preview{margin-left:0;padding-right:0;padding-left:4px;margin-right:-14px}.mce-rtl .mce-colorbutton.mce-btn-small .mce-preview{margin-left:0;padding-right:0;margin-right:-17px;padding-left:0}.mce-rtl .mce-colorbutton button{padding-right:10px;padding-left:10px}.mce-rtl .mce-colorbutton .mce-open{padding-left:4px;padding-right:4px}.mce-combobox{display:inline-block;*display:inline;*zoom:1;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);*height:32px}.mce-combobox input{border:1px solid #c5c5c5;border-right-color:#c5c5c5;height:28px}.mce-combobox.mce-disabled input{color:#adadad}.mce-combobox.mce-has-open input{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.mce-combobox .mce-btn{border-left:0;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.mce-combobox button{padding-right:8px;padding-left:8px}.mce-combobox.mce-disabled .mce-btn button{cursor:default;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;opacity:.4;filter:alpha(opacity=40);zoom:1}.mce-path{display:inline-block;*display:inline;*zoom:1;padding:8px;white-space:normal}.mce-path .mce-txt{display:inline-block;padding-right:3px}.mce-path .mce-path-body{display:inline-block}.mce-path-item{display:inline-block;*display:inline;*zoom:1;cursor:pointer;color:#333}.mce-path-item:hover{text-decoration:underline}.mce-path-item:focus{background:#666;color:#fff}.mce-path .mce-divider{display:inline}.mce-disabled .mce-path-item{color:#aaa}.mce-rtl .mce-path{direction:rtl}.mce-fieldset{border:0 solid #9E9E9E;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.mce-fieldset>.mce-container-body{margin-top:-15px}.mce-fieldset-title{margin-left:5px;padding:0 5px 0 5px}.mce-fit-layout{display:inline-block;*display:inline;*zoom:1}.mce-fit-layout-item{position:absolute}.mce-flow-layout-item{display:inline-block;*display:inline;*zoom:1}.mce-flow-layout-item{margin:2px 0 2px 2px}.mce-flow-layout-item.mce-last{margin-right:2px}.mce-flow-layout{white-space:normal}.mce-tinymce-inline .mce-flow-layout{white-space:nowrap}.mce-rtl .mce-flow-layout{text-align:right;direction:rtl}.mce-rtl .mce-flow-layout-item{margin:2px 2px 2px 0}.mce-rtl .mce-flow-layout-item.mce-last{margin-left:2px}.mce-iframe{border:0 solid #9e9e9e;width:100%;height:100%}.mce-label{display:inline-block;*display:inline;*zoom:1;text-shadow:0 1px 1px rgba(255,255,255,0.75);overflow:hidden}.mce-label.mce-autoscroll{overflow:auto}.mce-label.mce-disabled{color:#aaa}.mce-label.mce-multiline{white-space:pre-wrap}.mce-label.mce-error{color:#a00}.mce-rtl .mce-label{text-align:right;direction:rtl}.mce-menubar .mce-menubtn{border-color:transparent;background:transparent;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;filter:none}.mce-menubar{border:1px solid #c4c4c4}.mce-menubar .mce-menubtn button span{color:#333}.mce-menubar .mce-caret{border-top-color:#333}.mce-menubar .mce-menubtn:hover,.mce-menubar .mce-menubtn.mce-active,.mce-menubar .mce-menubtn:focus{border-color:transparent;background:#e6e6e6;filter:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.mce-menubtn span{color:#333;margin-right:2px;line-height:20px;*line-height:16px}.mce-menubtn.mce-btn-small span{font-size:12px}.mce-menubtn.mce-fixed-width span{display:inline-block;overflow-x:hidden;text-overflow:ellipsis;width:90px}.mce-menubtn.mce-fixed-width.mce-btn-small span{width:70px}.mce-menubtn .mce-caret{*margin-top:6px}.mce-rtl .mce-menubtn button{direction:rtl;text-align:right}.mce-listbox button{text-align:left;padding-right:20px;position:relative}.mce-listbox .mce-caret{position:absolute;margin-top:-2px;right:8px;top:50%}.mce-rtl .mce-listbox .mce-caret{right:auto;left:8px}.mce-rtl .mce-listbox button{padding-right:10px;padding-left:20px}.mce-menu-item{display:block;padding:6px 15px 6px 12px;clear:both;font-weight:normal;line-height:20px;color:#333;white-space:nowrap;cursor:pointer;line-height:normal;border-left:4px solid transparent;margin-bottom:1px}.mce-menu-item .mce-ico,.mce-menu-item .mce-text{color:#333}.mce-menu-item.mce-disabled .mce-text,.mce-menu-item.mce-disabled .mce-ico{color:#adadad}.mce-menu-item:hover .mce-text,.mce-menu-item.mce-selected .mce-text,.mce-menu-item:focus .mce-text{color:#fff}.mce-menu-item:hover .mce-ico,.mce-menu-item.mce-selected .mce-ico,.mce-menu-item:focus .mce-ico{color:#fff}.mce-menu-item.mce-disabled:hover{background:#ccc}.mce-menu-shortcut{display:inline-block;color:#adadad}.mce-menu-shortcut{display:inline-block;*display:inline;*zoom:1;padding:0 15px 0 20px}.mce-menu-item:hover .mce-menu-shortcut,.mce-menu-item.mce-selected .mce-menu-shortcut,.mce-menu-item:focus .mce-menu-shortcut{color:#fff}.mce-menu-item .mce-caret{margin-top:4px;*margin-top:3px;margin-right:6px;border-top:4px solid transparent;border-bottom:4px solid transparent;border-left:4px solid #333}.mce-menu-item.mce-selected .mce-caret,.mce-menu-item:focus .mce-caret,.mce-menu-item:hover .mce-caret{border-left-color:#fff}.mce-menu-align .mce-menu-shortcut{*margin-top:-2px}.mce-menu-align .mce-menu-shortcut,.mce-menu-align .mce-caret{position:absolute;right:0}.mce-menu-item.mce-active i{visibility:visible}.mce-menu-item-normal.mce-active{background-color:#c8def4}.mce-menu-item-preview.mce-active{border-left:5px solid #aaa}.mce-menu-item-normal.mce-active .mce-text{color:#333}.mce-menu-item-normal.mce-active:hover .mce-text,.mce-menu-item-normal.mce-active:hover .mce-ico{color:#fff}.mce-menu-item-normal.mce-active:focus .mce-text,.mce-menu-item-normal.mce-active:focus .mce-ico{color:#fff}.mce-menu-item:hover,.mce-menu-item.mce-selected,.mce-menu-item:focus{text-decoration:none;color:#fff;background-color:#0081c2;background-image:-moz-linear-gradient(top, #08c, #0077b3);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#08c), to(#0077b3));background-image:-webkit-linear-gradient(top, #08c, #0077b3);background-image:-o-linear-gradient(top, #08c, #0077b3);background-image:linear-gradient(to bottom, #08c, #0077b3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0);zoom:1}div.mce-menu .mce-menu-item-sep,.mce-menu-item-sep:hover{border:0;padding:0;height:1px;margin:9px 1px;overflow:hidden;background:#cbcbcb;border-bottom:1px solid #fff;cursor:default;filter:none}.mce-menu.mce-rtl{direction:rtl}.mce-rtl .mce-menu-item{text-align:right;direction:rtl;padding:6px 12px 6px 15px}.mce-menu-align.mce-rtl .mce-menu-shortcut,.mce-menu-align.mce-rtl .mce-caret{right:auto;left:0}.mce-rtl .mce-menu-item .mce-caret{margin-left:6px;margin-right:0;border-right:4px solid #333;border-left:0}.mce-rtl .mce-menu-item.mce-selected .mce-caret,.mce-rtl .mce-menu-item:focus .mce-caret,.mce-rtl .mce-menu-item:hover .mce-caret{border-left-color:transparent;border-right-color:#fff}.mce-menu{position:absolute;left:0;top:0;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background:transparent;z-index:1000;padding:5px 0 5px 0;margin:2px 0 0;min-width:160px;background:#fff;border:1px solid #989898;border:1px solid rgba(0,0,0,0.2);z-index:1002;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-moz-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);max-height:400px;overflow:auto;overflow-x:hidden}.mce-menu i{display:none}.mce-menu-has-icons i{display:inline-block;*display:inline}.mce-menu-sub-tr-tl{margin:-6px 0 0 -1px}.mce-menu-sub-br-bl{margin:6px 0 0 -1px}.mce-menu-sub-tl-tr{margin:-6px 0 0 1px}.mce-menu-sub-bl-br{margin:6px 0 0 1px}.mce-container-body .mce-resizehandle{position:absolute;right:0;bottom:0;width:16px;height:16px;visibility:visible;cursor:s-resize;margin:0}.mce-container-body .mce-resizehandle-both{cursor:se-resize}i.mce-i-resize{color:#333}.mce-spacer{visibility:hidden}.mce-splitbtn .mce-open{border-left:1px solid transparent;border-right:1px solid transparent}.mce-splitbtn:hover .mce-open{border-left-color:#bdbdbd;border-right-color:#bdbdbd}.mce-splitbtn button{padding-right:4px}.mce-splitbtn .mce-open{padding-left:4px}.mce-splitbtn .mce-open.mce-active{-webkit-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05)}.mce-splitbtn.mce-btn-small .mce-open{padding:0 3px 0 3px}.mce-rtl .mce-splitbtn{direction:rtl;text-align:right}.mce-rtl .mce-splitbtn button{padding-right:10px;padding-left:10px}.mce-rtl .mce-splitbtn .mce-open{padding-left:4px;padding-right:4px}.mce-stack-layout-item{display:block}.mce-tabs{display:block;border-bottom:1px solid #c5c5c5}.mce-tab{display:inline-block;*display:inline;*zoom:1;border:1px solid #c5c5c5;border-width:0 1px 0 0;background:#e3e3e3;padding:8px;text-shadow:0 1px 1px rgba(255,255,255,0.75);height:13px;cursor:pointer}.mce-tab:hover{background:#fdfdfd}.mce-tab.mce-active{background:#fdfdfd;border-bottom-color:transparent;margin-bottom:-1px;height:14px}.mce-rtl .mce-tabs{text-align:right;direction:rtl}.mce-rtl .mce-tab{border-width:0 0 0 1px}.mce-textbox{background:#fff;border:1px solid #c5c5c5;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);display:inline-block;-webkit-transition:border linear .2s, box-shadow linear .2s;transition:border linear .2s, box-shadow linear .2s;height:28px;resize:none;padding:0 4px 0 4px;white-space:pre-wrap;*white-space:pre;color:#333}.mce-textbox:focus,.mce-textbox.mce-focus{border-color:rgba(82,168,236,0.8);-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.65);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.65);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.65)}.mce-placeholder .mce-textbox{color:#aaa}.mce-textbox.mce-multiline{padding:4px}.mce-textbox.mce-disabled{color:#adadad}.mce-rtl .mce-textbox{text-align:right;direction:rtl}.mce-throbber{position:absolute;top:0;left:0;width:100%;height:100%;opacity:.6;filter:alpha(opacity=60);zoom:1;background:#fff url('img/loader.gif') no-repeat center center}.mce-throbber-inline{position:static;height:50px}@font-face{font-family:'tinymce';src:url('fonts/tinymce.eot');src:url('fonts/tinymce.eot?#iefix') format('embedded-opentype'),url('fonts/tinymce.woff') format('woff'),url('fonts/tinymce.ttf') format('truetype'),url('fonts/tinymce.svg#tinymce') format('svg');font-weight:normal;font-style:normal}@font-face{font-family:'tinymce-small';src:url('fonts/tinymce-small.eot');src:url('fonts/tinymce-small.eot?#iefix') format('embedded-opentype'),url('fonts/tinymce-small.woff') format('woff'),url('fonts/tinymce-small.ttf') format('truetype'),url('fonts/tinymce-small.svg#tinymce') format('svg');font-weight:normal;font-style:normal}.mce-ico{font-family:'tinymce',Arial;font-style:normal;font-weight:normal;font-variant:normal;font-size:16px;line-height:16px;speak:none;vertical-align:text-top;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;display:inline-block;background:transparent center center;background-size:cover;width:16px;height:16px;color:#333}.mce-btn-small .mce-ico{font-family:'tinymce-small',Arial}.mce-i-save:before{content:"\e000"}.mce-i-newdocument:before{content:"\e001"}.mce-i-fullpage:before{content:"\e002"}.mce-i-alignleft:before{content:"\e003"}.mce-i-aligncenter:before{content:"\e004"}.mce-i-alignright:before{content:"\e005"}.mce-i-alignjustify:before{content:"\e006"}.mce-i-cut:before{content:"\e007"}.mce-i-paste:before{content:"\e008"}.mce-i-searchreplace:before{content:"\e009"}.mce-i-bullist:before{content:"\e00a"}.mce-i-numlist:before{content:"\e00b"}.mce-i-indent:before{content:"\e00c"}.mce-i-outdent:before{content:"\e00d"}.mce-i-blockquote:before{content:"\e00e"}.mce-i-undo:before{content:"\e00f"}.mce-i-redo:before{content:"\e010"}.mce-i-link:before{content:"\e011"}.mce-i-unlink:before{content:"\e012"}.mce-i-anchor:before{content:"\e013"}.mce-i-image:before{content:"\e014"}.mce-i-media:before{content:"\e015"}.mce-i-help:before{content:"\e016"}.mce-i-code:before{content:"\e017"}.mce-i-insertdatetime:before{content:"\e018"}.mce-i-preview:before{content:"\e019"}.mce-i-forecolor:before{content:"\e01a"}.mce-i-backcolor:before{content:"\e01a"}.mce-i-table:before{content:"\e01b"}.mce-i-hr:before{content:"\e01c"}.mce-i-removeformat:before{content:"\e01d"}.mce-i-subscript:before{content:"\e01e"}.mce-i-superscript:before{content:"\e01f"}.mce-i-charmap:before{content:"\e020"}.mce-i-emoticons:before{content:"\e021"}.mce-i-print:before{content:"\e022"}.mce-i-fullscreen:before{content:"\e023"}.mce-i-spellchecker:before{content:"\e024"}.mce-i-nonbreaking:before{content:"\e025"}.mce-i-template:before{content:"\e026"}.mce-i-pagebreak:before{content:"\e027"}.mce-i-restoredraft:before{content:"\e028"}.mce-i-untitled:before{content:"\e029"}.mce-i-bold:before{content:"\e02a"}.mce-i-italic:before{content:"\e02b"}.mce-i-underline:before{content:"\e02c"}.mce-i-strikethrough:before{content:"\e02d"}.mce-i-visualchars:before{content:"\e02e"}.mce-i-visualblocks:before{content:"\e02e"}.mce-i-ltr:before{content:"\e02f"}.mce-i-rtl:before{content:"\e030"}.mce-i-copy:before{content:"\e031"}.mce-i-resize:before{content:"\e032"}.mce-i-browse:before{content:"\e034"}.mce-i-pastetext:before{content:"\e035"}.mce-i-checkbox:before,.mce-i-selected:before{content:"\e033"}.mce-i-selected{visibility:hidden}i.mce-i-backcolor{text-shadow:none;background:#bbb} \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/js/themes/modern/theme.min.js b/node_modules/selenium-webdriver/lib/test/data/js/themes/modern/theme.min.js
new file mode 100644
index 000000000..e25849df3
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/js/themes/modern/theme.min.js
@@ -0,0 +1 @@
+tinymce.ThemeManager.add("modern",function(e){function t(){function t(t){var n,o=[];if(t)return d(t.split(/[ ,]/),function(t){function i(){var i=e.selection;"bullist"==r&&i.selectorChanged("ul > li",function(e,i){for(var n,o=i.parents.length;o--&&(n=i.parents[o].nodeName,"OL"!=n&&"UL"!=n););t.active(e&&"UL"==n)}),"numlist"==r&&i.selectorChanged("ol > li",function(e,i){for(var n,o=i.parents.length;o--&&(n=i.parents[o].nodeName,"OL"!=n&&"UL"!=n););t.active(e&&"OL"==n)}),t.settings.stateSelector&&i.selectorChanged(t.settings.stateSelector,function(e){t.active(e)},!0),t.settings.disabledStateSelector&&i.selectorChanged(t.settings.disabledStateSelector,function(e){t.disabled(e)})}var r;"|"==t?n=null:c.has(t)?(t={type:t},u.toolbar_items_size&&(t.size=u.toolbar_items_size),o.push(t),n=null):(n||(n={type:"buttongroup",items:[]},o.push(n)),e.buttons[t]&&(r=t,t=e.buttons[r],"function"==typeof t&&(t=t()),t.type=t.type||"button",u.toolbar_items_size&&(t.size=u.toolbar_items_size),t=c.create(t),n.items.push(t),e.initialized?i():e.on("init",i)))}),i.push({type:"toolbar",layout:"flow",items:o}),!0}var i=[];if(tinymce.isArray(u.toolbar)){if(0===u.toolbar.length)return;tinymce.each(u.toolbar,function(e,t){u["toolbar"+(t+1)]=e}),delete u.toolbar}for(var n=1;10>n&&t(u["toolbar"+n]);n++);return i.length||u.toolbar===!1||t(u.toolbar||f),i.length?{type:"panel",layout:"stack",classes:"toolbar-grp",ariaRoot:!0,ariaRemember:!0,items:i}:void 0}function i(){function t(t){var i;return"|"==t?{text:"|"}:i=e.menuItems[t]}function i(i){var n,o,r,a,s;if(s=tinymce.makeMap((u.removed_menuitems||"").split(/[ ,]/)),u.menu?(o=u.menu[i],a=!0):o=h[i],o){n={text:o.title},r=[],d((o.items||"").split(/[ ,]/),function(e){var i=t(e);i&&!s[e]&&r.push(t(e))}),a||d(e.menuItems,function(e){e.context==i&&("before"==e.separator&&r.push({text:"|"}),e.prependToContext?r.unshift(e):r.push(e),"after"==e.separator&&r.push({text:"|"}))});for(var l=0;l<r.length;l++)"|"==r[l].text&&(0===l||l==r.length-1)&&r.splice(l,1);if(n.menu=r,!n.menu.length)return null}return n}var n,o=[],r=[];if(u.menu)for(n in u.menu)r.push(n);else for(n in h)r.push(n);for(var a="string"==typeof u.menubar?u.menubar.split(/[ ,]/):r,s=0;s<a.length;s++){var l=a[s];l=i(l),l&&o.push(l)}return o}function n(t){function i(e){var i=t.find(e)[0];i&&i.focus(!0)}e.shortcuts.add("Alt+F9","",function(){i("menubar")}),e.shortcuts.add("Alt+F10","",function(){i("toolbar")}),e.shortcuts.add("Alt+F11","",function(){i("elementpath")}),t.on("cancel",function(){e.focus()})}function o(t,i){function n(e){return{width:e.clientWidth,height:e.clientHeight}}var o,r,a,s;o=e.getContainer(),r=e.getContentAreaContainer().firstChild,a=n(o),s=n(r),null!==t&&(t=Math.max(u.min_width||100,t),t=Math.min(u.max_width||65535,t),m.css(o,"width",t+(a.width-s.width)),m.css(r,"width",t)),i=Math.max(u.min_height||100,i),i=Math.min(u.max_height||65535,i),m.css(r,"height",i),e.fire("ResizeEditor")}function r(t,i){var n=e.getContentAreaContainer();l.resizeTo(n.clientWidth+t,n.clientHeight+i)}function a(o){function r(){if(h&&h.moveRel&&h.visible()&&!h._fixed){var t=e.selection.getScrollContainer(),i=e.getBody(),n=0,o=0;if(t){var r=m.getPos(i),a=m.getPos(t);n=Math.max(0,a.x-r.x),o=Math.max(0,a.y-r.y)}h.fixed(!1).moveRel(i,e.rtl?["tr-br","br-tr"]:["tl-bl","bl-tl"]).moveBy(n,o)}}function a(){h&&(h.show(),r(),m.addClass(e.getBody(),"mce-edit-focus"))}function s(){h&&(h.hide(),m.removeClass(e.getBody(),"mce-edit-focus"))}function d(){return h?void(h.visible()||a()):(h=l.panel=c.create({type:f?"panel":"floatpanel",role:"application",classes:"tinymce tinymce-inline",layout:"flex",direction:"column",align:"stretch",autohide:!1,autofix:!0,fixed:!!f,border:1,items:[u.menubar===!1?null:{type:"menubar",border:"0 0 1 0",items:i()},t()]}),e.fire("BeforeRenderUI"),h.renderTo(f||document.body).reflow(),n(h),a(),e.on("nodeChange",r),e.on("activate",a),e.on("deactivate",s),void e.nodeChanged())}var h,f;return u.fixed_toolbar_container&&(f=m.select(u.fixed_toolbar_container)[0]),u.content_editable=!0,e.on("focus",function(){o.skinUiCss?tinymce.DOM.styleSheetLoader.load(o.skinUiCss,d,d):d()}),e.on("blur hide",s),e.on("remove",function(){h&&(h.remove(),h=null)}),o.skinUiCss&&tinymce.DOM.styleSheetLoader.load(o.skinUiCss),{}}function s(r){var a,s,d;return r.skinUiCss&&tinymce.DOM.loadCSS(r.skinUiCss),a=l.panel=c.create({type:"panel",role:"application",classes:"tinymce",style:"visibility: hidden",layout:"stack",border:1,items:[u.menubar===!1?null:{type:"menubar",border:"0 0 1 0",items:i()},t(),{type:"panel",name:"iframe",layout:"stack",classes:"edit-area",html:"",border:"1 0 0 0"}]}),u.resize!==!1&&(s={type:"resizehandle",direction:u.resize,onResizeStart:function(){var t=e.getContentAreaContainer().firstChild;d={width:t.clientWidth,height:t.clientHeight}},onResize:function(e){"both"==u.resize?o(d.width+e.deltaX,d.height+e.deltaY):o(null,d.height+e.deltaY)}}),u.statusbar!==!1&&a.add({type:"panel",name:"statusbar",classes:"statusbar",layout:"flow",border:"1 0 0 0",ariaRoot:!0,items:[{type:"elementpath"},s]}),u.readonly&&a.find("*").disabled(!0),e.fire("BeforeRenderUI"),a.renderBefore(r.targetNode).reflow(),u.width&&tinymce.DOM.setStyle(a.getEl(),"width",u.width),e.on("remove",function(){a.remove(),a=null}),n(a),{iframeContainer:a.find("#iframe")[0].getEl(),editorContainer:a.getEl()}}var l=this,u=e.settings,c=tinymce.ui.Factory,d=tinymce.each,m=tinymce.DOM,h={file:{title:"File",items:"newdocument"},edit:{title:"Edit",items:"undo redo | cut copy paste pastetext | selectall"},insert:{title:"Insert",items:"|"},view:{title:"View",items:"visualaid |"},format:{title:"Format",items:"bold italic underline strikethrough superscript subscript | formats | removeformat"},table:{title:"Table"},tools:{title:"Tools"}},f="undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image";l.renderUI=function(t){var i=u.skin!==!1?u.skin||"lightgray":!1;if(i){var n=u.skin_url;n=n?e.documentBaseURI.toAbsolute(n):tinymce.baseURL+"/skins/"+i,t.skinUiCss=tinymce.Env.documentMode<=7?n+"/skin.ie7.min.css":n+"/skin.min.css",e.contentCSS.push(n+"/content"+(e.inline?".inline":"")+".min.css")}return e.on("ProgressState",function(e){l.throbber=l.throbber||new tinymce.ui.Throbber(l.panel.getEl("body")),e.state?l.throbber.show(e.time):l.throbber.hide()}),u.inline?a(t):s(t)},l.resizeTo=o,l.resizeBy=r}); \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/js/tinymce.min.js b/node_modules/selenium-webdriver/lib/test/data/js/tinymce.min.js
new file mode 100644
index 000000000..3ab7df7fc
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/js/tinymce.min.js
@@ -0,0 +1,10 @@
+// 4.0.26 (2014-05-06)
+!function(e,t){"use strict";function n(e,t){for(var n,r=[],i=0;i<e.length;++i){if(n=s[e[i]]||o(e[i]),!n)throw"module definition dependecy not found: "+e[i];r.push(n)}t.apply(null,r)}function r(e,r,i){if("string"!=typeof e)throw"invalid module definition, module id must be defined and be a string";if(r===t)throw"invalid module definition, dependencies must be specified";if(i===t)throw"invalid module definition, definition function must be specified";n(r,function(){s[e]=i.apply(null,arguments)})}function i(e){return!!s[e]}function o(t){for(var n=e,r=t.split(/[.\/]/),i=0;i<r.length;++i){if(!n[r[i]])return;n=n[r[i]]}return n}function a(n){for(var r=0;r<n.length;r++){for(var i=e,o=n[r],a=o.split(/[.\/]/),l=0;l<a.length-1;++l)i[a[l]]===t&&(i[a[l]]={}),i=i[a[l]];i[a[a.length-1]]=s[o]}}var s={},l="tinymce/dom/EventUtils",c="tinymce/dom/Sizzle",u="tinymce/dom/DomQuery",d="tinymce/html/Styles",f="tinymce/dom/TreeWalker",p="tinymce/util/Tools",h="tinymce/dom/Range",m="tinymce/html/Entities",g="tinymce/Env",v="tinymce/dom/StyleSheetLoader",y="tinymce/dom/DOMUtils",b="tinymce/dom/ScriptLoader",C="tinymce/AddOnManager",x="tinymce/html/Node",w="tinymce/html/Schema",_="tinymce/html/SaxParser",N="tinymce/html/DomParser",E="tinymce/html/Writer",S="tinymce/html/Serializer",k="tinymce/dom/Serializer",T="tinymce/dom/TridentSelection",R="tinymce/util/VK",A="tinymce/dom/ControlSelection",B="tinymce/dom/RangeUtils",D="tinymce/dom/Selection",L="tinymce/fmt/Preview",M="tinymce/Formatter",H="tinymce/UndoManager",P="tinymce/EnterKey",O="tinymce/ForceBlocks",I="tinymce/EditorCommands",F="tinymce/util/URI",z="tinymce/util/Class",W="tinymce/util/EventDispatcher",V="tinymce/ui/Selector",U="tinymce/ui/Collection",q="tinymce/ui/DomUtils",$="tinymce/ui/Control",j="tinymce/ui/Factory",K="tinymce/ui/KeyboardNavigation",G="tinymce/ui/Container",Y="tinymce/ui/DragHelper",X="tinymce/ui/Scrollable",J="tinymce/ui/Panel",Q="tinymce/ui/Movable",Z="tinymce/ui/Resizable",et="tinymce/ui/FloatPanel",tt="tinymce/ui/Window",nt="tinymce/ui/MessageBox",rt="tinymce/WindowManager",it="tinymce/util/Quirks",ot="tinymce/util/Observable",at="tinymce/EditorObservable",st="tinymce/Shortcuts",lt="tinymce/Editor",ct="tinymce/util/I18n",ut="tinymce/FocusManager",dt="tinymce/EditorManager",ft="tinymce/LegacyInput",pt="tinymce/util/XHR",ht="tinymce/util/JSON",mt="tinymce/util/JSONRequest",gt="tinymce/util/JSONP",vt="tinymce/util/LocalStorage",yt="tinymce/Compat",bt="tinymce/ui/Layout",Ct="tinymce/ui/AbsoluteLayout",xt="tinymce/ui/Tooltip",wt="tinymce/ui/Widget",_t="tinymce/ui/Button",Nt="tinymce/ui/ButtonGroup",Et="tinymce/ui/Checkbox",St="tinymce/ui/PanelButton",kt="tinymce/ui/ColorButton",Tt="tinymce/ui/ComboBox",Rt="tinymce/ui/Path",At="tinymce/ui/ElementPath",Bt="tinymce/ui/FormItem",Dt="tinymce/ui/Form",Lt="tinymce/ui/FieldSet",Mt="tinymce/ui/FilePicker",Ht="tinymce/ui/FitLayout",Pt="tinymce/ui/FlexLayout",Ot="tinymce/ui/FlowLayout",It="tinymce/ui/FormatControls",Ft="tinymce/ui/GridLayout",zt="tinymce/ui/Iframe",Wt="tinymce/ui/Label",Vt="tinymce/ui/Toolbar",Ut="tinymce/ui/MenuBar",qt="tinymce/ui/MenuButton",$t="tinymce/ui/ListBox",jt="tinymce/ui/MenuItem",Kt="tinymce/ui/Menu",Gt="tinymce/ui/Radio",Yt="tinymce/ui/ResizeHandle",Xt="tinymce/ui/Spacer",Jt="tinymce/ui/SplitButton",Qt="tinymce/ui/StackLayout",Zt="tinymce/ui/TabPanel",en="tinymce/ui/TextBox",tn="tinymce/ui/Throbber";r(l,[],function(){function e(e,t,n,r){e.addEventListener?e.addEventListener(t,n,r||!1):e.attachEvent&&e.attachEvent("on"+t,n)}function t(e,t,n,r){e.removeEventListener?e.removeEventListener(t,n,r||!1):e.detachEvent&&e.detachEvent("on"+t,n)}function n(e,t){function n(){return!1}function r(){return!0}var i,o=t||{},l;for(i in e)s[i]||(o[i]=e[i]);if(o.target||(o.target=o.srcElement||document),e&&a.test(e.type)&&e.pageX===l&&e.clientX!==l){var c=o.target.ownerDocument||document,u=c.documentElement,d=c.body;o.pageX=e.clientX+(u&&u.scrollLeft||d&&d.scrollLeft||0)-(u&&u.clientLeft||d&&d.clientLeft||0),o.pageY=e.clientY+(u&&u.scrollTop||d&&d.scrollTop||0)-(u&&u.clientTop||d&&d.clientTop||0)}return o.preventDefault=function(){o.isDefaultPrevented=r,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},o.stopPropagation=function(){o.isPropagationStopped=r,e&&(e.stopPropagation?e.stopPropagation():e.cancelBubble=!0)},o.stopImmediatePropagation=function(){o.isImmediatePropagationStopped=r,o.stopPropagation()},o.isDefaultPrevented||(o.isDefaultPrevented=n,o.isPropagationStopped=n,o.isImmediatePropagationStopped=n),o}function r(n,r,i){function o(){i.domLoaded||(i.domLoaded=!0,r(c))}function a(){("complete"===l.readyState||"interactive"===l.readyState&&l.body)&&(t(l,"readystatechange",a),o())}function s(){try{l.documentElement.doScroll("left")}catch(e){return void setTimeout(s,0)}o()}var l=n.document,c={type:"ready"};return i.domLoaded?void r(c):(l.addEventListener?"complete"===l.readyState?o():e(n,"DOMContentLoaded",o):(e(l,"readystatechange",a),l.documentElement.doScroll&&n.self===n.top&&s()),void e(n,"load",o))}function i(){function i(e,t){var n,r,i,o,a=s[t];if(n=a&&a[e.type])for(r=0,i=n.length;i>r;r++)if(o=n[r],o&&o.func.call(o.scope,e)===!1&&e.preventDefault(),e.isImmediatePropagationStopped())return}var a=this,s={},l,c,u,d,f;c=o+(+new Date).toString(32),d="onmouseenter"in document.documentElement,u="onfocusin"in document.documentElement,f={mouseenter:"mouseover",mouseleave:"mouseout"},l=1,a.domLoaded=!1,a.events=s,a.bind=function(t,o,p,h){function m(e){i(n(e||_.event),g)}var g,v,y,b,C,x,w,_=window;if(t&&3!==t.nodeType&&8!==t.nodeType){for(t[c]?g=t[c]:(g=l++,t[c]=g,s[g]={}),h=h||t,o=o.split(" "),y=o.length;y--;)b=o[y],x=m,C=w=!1,"DOMContentLoaded"===b&&(b="ready"),a.domLoaded&&"ready"===b&&"complete"==t.readyState?p.call(h,n({type:b})):(d||(C=f[b],C&&(x=function(e){var t,r;if(t=e.currentTarget,r=e.relatedTarget,r&&t.contains)r=t.contains(r);else for(;r&&r!==t;)r=r.parentNode;r||(e=n(e||_.event),e.type="mouseout"===e.type?"mouseleave":"mouseenter",e.target=t,i(e,g))})),u||"focusin"!==b&&"focusout"!==b||(w=!0,C="focusin"===b?"focus":"blur",x=function(e){e=n(e||_.event),e.type="focus"===e.type?"focusin":"focusout",i(e,g)}),v=s[g][b],v?"ready"===b&&a.domLoaded?p({type:b}):v.push({func:p,scope:h}):(s[g][b]=v=[{func:p,scope:h}],v.fakeName=C,v.capture=w,v.nativeHandler=x,"ready"===b?r(t,x,a):e(t,C||b,x,w)));return t=v=0,p}},a.unbind=function(e,n,r){var i,o,l,u,d,f;if(!e||3===e.nodeType||8===e.nodeType)return a;if(i=e[c]){if(f=s[i],n){for(n=n.split(" "),l=n.length;l--;)if(d=n[l],o=f[d]){if(r)for(u=o.length;u--;)if(o[u].func===r){var p=o.nativeHandler,h=o.fakeName,m=o.capture;o=o.slice(0,u).concat(o.slice(u+1)),o.nativeHandler=p,o.fakeName=h,o.capture=m,f[d]=o}r&&0!==o.length||(delete f[d],t(e,o.fakeName||d,o.nativeHandler,o.capture))}}else{for(d in f)o=f[d],t(e,o.fakeName||d,o.nativeHandler,o.capture);f={}}for(d in f)return a;delete s[i];try{delete e[c]}catch(g){e[c]=null}}return a},a.fire=function(e,t,r){var o;if(!e||3===e.nodeType||8===e.nodeType)return a;r=n(null,r),r.type=t,r.target=e;do o=e[c],o&&i(r,o),e=e.parentNode||e.ownerDocument||e.defaultView||e.parentWindow;while(e&&!r.isPropagationStopped());return a},a.clean=function(e){var t,n,r=a.unbind;if(!e||3===e.nodeType||8===e.nodeType)return a;if(e[c]&&r(e),e.getElementsByTagName||(e=e.document),e&&e.getElementsByTagName)for(r(e),n=e.getElementsByTagName("*"),t=n.length;t--;)e=n[t],e[c]&&r(e);return a},a.destroy=function(){s={}},a.cancel=function(e){return e&&(e.preventDefault(),e.stopImmediatePropagation()),!1}}var o="mce-data-",a=/^(?:mouse|contextmenu)|click/,s={keyLocation:1,layerX:1,layerY:1,returnValue:1};return i.Event=new i,i.Event.bind(window,"ready",function(){}),i}),r(c,[],function(){function e(e){return mt.test(e+"")}function n(){var e,t=[];return e=function(n,r){return t.push(n+=" ")>_.cacheLength&&delete e[t.shift()],e[n]=r,r}}function r(e){return e[I]=!0,e}function i(e){var t=B.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t=null}}function o(e,t,n,r){var i,o,a,s,l,c,f,p,h,m;if((t?t.ownerDocument||t:F)!==B&&A(t),t=t||B,n=n||[],!e||"string"!=typeof e)return n;if(1!==(s=t.nodeType)&&9!==s)return[];if(L&&!r){if(i=gt.exec(e))if(a=i[1]){if(9===s){if(o=t.getElementById(a),!o||!o.parentNode)return n;if(o.id===a)return n.push(o),n}else if(t.ownerDocument&&(o=t.ownerDocument.getElementById(a))&&O(t,o)&&o.id===a)return n.push(o),n}else{if(i[2])return Z.apply(n,t.getElementsByTagName(e)),n;if((a=i[3])&&z.getElementsByClassName&&t.getElementsByClassName)return Z.apply(n,t.getElementsByClassName(a)),n}if(z.qsa&&!M.test(e)){if(f=!0,p=I,h=t,m=9===s&&e,1===s&&"object"!==t.nodeName.toLowerCase()){for(c=u(e),(f=t.getAttribute("id"))?p=f.replace(bt,"\\$&"):t.setAttribute("id",p),p="[id='"+p+"'] ",l=c.length;l--;)c[l]=p+d(c[l]);h=ht.test(e)&&t.parentNode||t,m=c.join(",")}if(m)try{return Z.apply(n,h.querySelectorAll(m)),n}catch(g){}finally{f||t.removeAttribute("id")}}}return b(e.replace(lt,"$1"),t,n,r)}function a(e,t){var n=t&&e,r=n&&(~t.sourceIndex||Y)-(~e.sourceIndex||Y);if(r)return r;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function s(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function l(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function c(e){return r(function(t){return t=+t,r(function(n,r){for(var i,o=e([],n.length,t),a=o.length;a--;)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function u(e,t){var n,r,i,a,s,l,c,u=q[e+" "];if(u)return t?0:u.slice(0);for(s=e,l=[],c=_.preFilter;s;){(!n||(r=ct.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),l.push(i=[])),n=!1,(r=ut.exec(s))&&(n=r.shift(),i.push({value:n,type:r[0].replace(lt," ")}),s=s.slice(n.length));for(a in _.filter)!(r=pt[a].exec(s))||c[a]&&!(r=c[a](r))||(n=r.shift(),i.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?o.error(e):q(e,l).slice(0)}function d(e){for(var t=0,n=e.length,r="";n>t;t++)r+=e[t].value;return r}function f(e,t,n){var r=t.dir,i=n&&"parentNode"===r,o=V++;return t.first?function(t,n,o){for(;t=t[r];)if(1===t.nodeType||i)return e(t,n,o)}:function(t,n,a){var s,l,c,u=W+" "+o;if(a){for(;t=t[r];)if((1===t.nodeType||i)&&e(t,n,a))return!0}else for(;t=t[r];)if(1===t.nodeType||i)if(c=t[I]||(t[I]={}),(l=c[r])&&l[0]===u){if((s=l[1])===!0||s===w)return s===!0}else if(l=c[r]=[u],l[1]=e(t,n,a)||w,l[1]===!0)return!0}}function p(e){return e.length>1?function(t,n,r){for(var i=e.length;i--;)if(!e[i](t,n,r))return!1;return!0}:e[0]}function h(e,t,n,r,i){for(var o,a=[],s=0,l=e.length,c=null!=t;l>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),c&&t.push(s));return a}function m(e,t,n,i,o,a){return i&&!i[I]&&(i=m(i)),o&&!o[I]&&(o=m(o,a)),r(function(r,a,s,l){var c,u,d,f=[],p=[],m=a.length,g=r||y(t||"*",s.nodeType?[s]:s,[]),v=!e||!r&&t?g:h(g,f,e,s,l),b=n?o||(r?e:m||i)?[]:a:v;if(n&&n(v,b,s,l),i)for(c=h(b,p),i(c,[],s,l),u=c.length;u--;)(d=c[u])&&(b[p[u]]=!(v[p[u]]=d));if(r){if(o||e){if(o){for(c=[],u=b.length;u--;)(d=b[u])&&c.push(v[u]=d);o(null,b=[],c,l)}for(u=b.length;u--;)(d=b[u])&&(c=o?tt.call(r,d):f[u])>-1&&(r[c]=!(a[c]=d))}}else b=h(b===a?b.splice(m,b.length):b),o?o(null,a,b,l):Z.apply(a,b)})}function g(e){for(var t,n,r,i=e.length,o=_.relative[e[0].type],a=o||_.relative[" "],s=o?1:0,l=f(function(e){return e===t},a,!0),c=f(function(e){return tt.call(t,e)>-1},a,!0),u=[function(e,n,r){return!o&&(r||n!==k)||((t=n).nodeType?l(e,n,r):c(e,n,r))}];i>s;s++)if(n=_.relative[e[s].type])u=[f(p(u),n)];else{if(n=_.filter[e[s].type].apply(null,e[s].matches),n[I]){for(r=++s;i>r&&!_.relative[e[r].type];r++);return m(s>1&&p(u),s>1&&d(e.slice(0,s-1)).replace(lt,"$1"),n,r>s&&g(e.slice(s,r)),i>r&&g(e=e.slice(r)),i>r&&d(e))}u.push(n)}return p(u)}function v(e,t){var n=0,i=t.length>0,a=e.length>0,s=function(r,s,l,c,u){var d,f,p,m=[],g=0,v="0",y=r&&[],b=null!=u,C=k,x=r||a&&_.find.TAG("*",u&&s.parentNode||s),N=W+=null==C?1:Math.random()||.1;for(b&&(k=s!==B&&s,w=n);null!=(d=x[v]);v++){if(a&&d){for(f=0;p=e[f++];)if(p(d,s,l)){c.push(d);break}b&&(W=N,w=++n)}i&&((d=!p&&d)&&g--,r&&y.push(d))}if(g+=v,i&&v!==g){for(f=0;p=t[f++];)p(y,m,s,l);if(r){if(g>0)for(;v--;)y[v]||m[v]||(m[v]=J.call(c));m=h(m)}Z.apply(c,m),b&&!r&&m.length>0&&g+t.length>1&&o.uniqueSort(c)}return b&&(W=N,k=C),y};return i?r(s):s}function y(e,t,n){for(var r=0,i=t.length;i>r;r++)o(e,t[r],n);return n}function b(e,t,n,r){var i,o,a,s,l,c=u(e);if(!r&&1===c.length){if(o=c[0]=c[0].slice(0),o.length>2&&"ID"===(a=o[0]).type&&9===t.nodeType&&L&&_.relative[o[1].type]){if(t=(_.find.ID(a.matches[0].replace(xt,wt),t)||[])[0],!t)return n;e=e.slice(o.shift().value.length)}for(i=pt.needsContext.test(e)?0:o.length;i--&&(a=o[i],!_.relative[s=a.type]);)if((l=_.find[s])&&(r=l(a.matches[0].replace(xt,wt),ht.test(o[0].type)&&t.parentNode||t))){if(o.splice(i,1),e=r.length&&d(o),!e)return Z.apply(n,r),n;break}}return S(e,c)(r,t,!L,n,ht.test(e)),n}function C(){}var x,w,_,N,E,S,k,T,R,A,B,D,L,M,H,P,O,I="sizzle"+-new Date,F=window.document,z={},W=0,V=0,U=n(),q=n(),$=n(),j=!1,K=function(){return 0},G=typeof t,Y=1<<31,X=[],J=X.pop,Q=X.push,Z=X.push,et=X.slice,tt=X.indexOf||function(e){for(var t=0,n=this.length;n>t;t++)if(this[t]===e)return t;return-1},nt="[\\x20\\t\\r\\n\\f]",rt="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",it=rt.replace("w","w#"),ot="([*^$|!~]?=)",at="\\["+nt+"*("+rt+")"+nt+"*(?:"+ot+nt+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+it+")|)|)"+nt+"*\\]",st=":("+rt+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+at.replace(3,8)+")*)|.*)\\)|)",lt=new RegExp("^"+nt+"+|((?:^|[^\\\\])(?:\\\\.)*)"+nt+"+$","g"),ct=new RegExp("^"+nt+"*,"+nt+"*"),ut=new RegExp("^"+nt+"*([\\x20\\t\\r\\n\\f>+~])"+nt+"*"),dt=new RegExp(st),ft=new RegExp("^"+it+"$"),pt={ID:new RegExp("^#("+rt+")"),CLASS:new RegExp("^\\.("+rt+")"),NAME:new RegExp("^\\[name=['\"]?("+rt+")['\"]?\\]"),TAG:new RegExp("^("+rt.replace("w","w*")+")"),ATTR:new RegExp("^"+at),PSEUDO:new RegExp("^"+st),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+nt+"*(even|odd|(([+-]|)(\\d*)n|)"+nt+"*(?:([+-]|)"+nt+"*(\\d+)|))"+nt+"*\\)|)","i"),needsContext:new RegExp("^"+nt+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+nt+"*((?:-\\d)?\\d*)"+nt+"*\\)|)(?=[^-]|$)","i")},ht=/[\x20\t\r\n\f]*[+~]/,mt=/^[^{]+\{\s*\[native code/,gt=/^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,vt=/^(?:input|select|textarea|button)$/i,yt=/^h\d$/i,bt=/'|\\/g,Ct=/\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,xt=/\\([\da-fA-F]{1,6}[\x20\t\r\n\f]?|.)/g,wt=function(e,t){var n="0x"+t-65536;return n!==n?t:0>n?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320)};try{Z.apply(X=et.call(F.childNodes),F.childNodes),X[F.childNodes.length].nodeType}catch(_t){Z={apply:X.length?function(e,t){Q.apply(e,et.call(t))}:function(e,t){for(var n=e.length,r=0;e[n++]=t[r++];);e.length=n-1}}}E=o.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},A=o.setDocument=function(n){var r=n?n.ownerDocument||n:F;return r!==B&&9===r.nodeType&&r.documentElement?(B=r,D=r.documentElement,L=!E(r),z.getElementsByTagName=i(function(e){return e.appendChild(r.createComment("")),!e.getElementsByTagName("*").length}),z.attributes=i(function(e){e.innerHTML="<select></select>";var t=typeof e.lastChild.getAttribute("multiple");return"boolean"!==t&&"string"!==t}),z.getElementsByClassName=i(function(e){return e.innerHTML="<div class='hidden e'></div><div class='hidden'></div>",e.getElementsByClassName&&e.getElementsByClassName("e").length?(e.lastChild.className="e",2===e.getElementsByClassName("e").length):!1}),z.getByName=i(function(e){e.id=I+0,e.appendChild(B.createElement("a")).setAttribute("name",I),e.appendChild(B.createElement("i")).setAttribute("name",I),D.appendChild(e);var t=r.getElementsByName&&r.getElementsByName(I).length===2+r.getElementsByName(I+0).length;return D.removeChild(e),t}),z.sortDetached=i(function(e){return e.compareDocumentPosition&&1&e.compareDocumentPosition(B.createElement("div"))}),_.attrHandle=i(function(e){return e.innerHTML="<a href='#'></a>",e.firstChild&&typeof e.firstChild.getAttribute!==G&&"#"===e.firstChild.getAttribute("href")})?{}:{href:function(e){return e.getAttribute("href",2)},type:function(e){return e.getAttribute("type")}},z.getByName?(_.find.ID=function(e,t){if(typeof t.getElementById!==G&&L){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},_.filter.ID=function(e){var t=e.replace(xt,wt);return function(e){return e.getAttribute("id")===t}}):(_.find.ID=function(e,n){if(typeof n.getElementById!==G&&L){var r=n.getElementById(e);return r?r.id===e||typeof r.getAttributeNode!==G&&r.getAttributeNode("id").value===e?[r]:t:[]}},_.filter.ID=function(e){var t=e.replace(xt,wt);return function(e){var n=typeof e.getAttributeNode!==G&&e.getAttributeNode("id");return n&&n.value===t}}),_.find.TAG=z.getElementsByTagName?function(e,t){return typeof t.getElementsByTagName!==G?t.getElementsByTagName(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){for(;n=o[i++];)1===n.nodeType&&r.push(n);return r}return o},_.find.NAME=z.getByName&&function(e,t){return typeof t.getElementsByName!==G?t.getElementsByName(name):void 0},_.find.CLASS=z.getElementsByClassName&&function(e,t){return typeof t.getElementsByClassName!==G&&L?t.getElementsByClassName(e):void 0},H=[],M=[":focus"],(z.qsa=e(r.querySelectorAll))&&(i(function(e){e.innerHTML="<select><option selected=''></option></select>",e.querySelectorAll("[selected]").length||M.push("\\["+nt+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),e.querySelectorAll(":checked").length||M.push(":checked")}),i(function(e){e.innerHTML="<input type='hidden' i=''/>",e.querySelectorAll("[i^='']").length&&M.push("[*^$]="+nt+"*(?:\"\"|'')"),e.querySelectorAll(":enabled").length||M.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),M.push(",.*:")})),(z.matchesSelector=e(P=D.matchesSelector||D.mozMatchesSelector||D.webkitMatchesSelector||D.oMatchesSelector||D.msMatchesSelector))&&i(function(e){z.disconnectedMatch=P.call(e,"div"),P.call(e,"[s!='']:x"),H.push("!=",st)}),M=new RegExp(M.join("|")),H=H.length&&new RegExp(H.join("|")),O=e(D.contains)||D.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},K=D.compareDocumentPosition?function(e,t){if(e===t)return j=!0,0;var n=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t);return n?1&n||T&&t.compareDocumentPosition(e)===n?e===r||O(F,e)?-1:t===r||O(F,t)?1:R?tt.call(R,e)-tt.call(R,t):0:4&n?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var n,i=0,o=e.parentNode,s=t.parentNode,l=[e],c=[t];if(e===t)return j=!0,0;if(!o||!s)return e===r?-1:t===r?1:o?-1:s?1:0;if(o===s)return a(e,t);for(n=e;n=n.parentNode;)l.unshift(n);for(n=t;n=n.parentNode;)c.unshift(n);for(;l[i]===c[i];)i++;return i?a(l[i],c[i]):l[i]===F?-1:c[i]===F?1:0},B):B},o.matches=function(e,t){return o(e,null,null,t)},o.matchesSelector=function(e,t){if((e.ownerDocument||e)!==B&&A(e),t=t.replace(Ct,"='$1']"),z.matchesSelector&&L&&(!H||!H.test(t))&&!M.test(t))try{var n=P.call(e,t);if(n||z.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(r){}return o(t,B,null,[e]).length>0},o.contains=function(e,t){return(e.ownerDocument||e)!==B&&A(e),O(e,t)},o.attr=function(e,t){var n;return(e.ownerDocument||e)!==B&&A(e),L&&(t=t.toLowerCase()),(n=_.attrHandle[t])?n(e):!L||z.attributes?e.getAttribute(t):((n=e.getAttributeNode(t))||e.getAttribute(t))&&e[t]===!0?t:n&&n.specified?n.value:null},o.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},o.uniqueSort=function(e){var t,n=[],r=0,i=0;if(j=!z.detectDuplicates,T=!z.sortDetached,R=!z.sortStable&&e.slice(0),e.sort(K),j){for(;t=e[i++];)t===e[i]&&(r=n.push(i));for(;r--;)e.splice(n[r],1)}return e},N=o.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=N(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=N(t);return n},_=o.selectors={cacheLength:50,createPseudo:r,match:pt,find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(xt,wt),e[3]=(e[4]||e[5]||"").replace(xt,wt),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||o.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&o.error(e[0]),e},PSEUDO:function(e){var t,n=!e[5]&&e[2];return pt.CHILD.test(e[0])?null:(e[4]?e[2]=e[4]:n&&dt.test(n)&&(t=u(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){return"*"===e?function(){return!0}:(e=e.replace(xt,wt).toLowerCase(),function(t){return t.nodeName&&t.nodeName.toLowerCase()===e})},CLASS:function(e){var t=U[e+" "];return t||(t=new RegExp("(^|"+nt+")"+e+"("+nt+"|$)"))&&U(e,function(e){return t.test(e.className||typeof e.getAttribute!==G&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=o.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,l){var c,u,d,f,p,h,m=o!==a?"nextSibling":"previousSibling",g=t.parentNode,v=s&&t.nodeName.toLowerCase(),y=!l&&!s;if(g){if(o){for(;m;){for(d=t;d=d[m];)if(s?d.nodeName.toLowerCase()===v:1===d.nodeType)return!1;h=m="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?g.firstChild:g.lastChild],a&&y){for(u=g[I]||(g[I]={}),c=u[e]||[],p=c[0]===W&&c[1],f=c[0]===W&&c[2],d=p&&g.childNodes[p];d=++p&&d&&d[m]||(f=p=0)||h.pop();)if(1===d.nodeType&&++f&&d===t){u[e]=[W,p,f];break}}else if(y&&(c=(t[I]||(t[I]={}))[e])&&c[0]===W)f=c[1];else for(;(d=++p&&d&&d[m]||(f=p=0)||h.pop())&&((s?d.nodeName.toLowerCase()!==v:1!==d.nodeType)||!++f||(y&&((d[I]||(d[I]={}))[e]=[W,f]),d!==t)););return f-=i,f===r||f%r===0&&f/r>=0}}},PSEUDO:function(e,t){var n,i=_.pseudos[e]||_.setFilters[e.toLowerCase()]||o.error("unsupported pseudo: "+e);return i[I]?i(t):i.length>1?(n=[e,e,"",t],_.setFilters.hasOwnProperty(e.toLowerCase())?r(function(e,n){for(var r,o=i(e,t),a=o.length;a--;)r=tt.call(e,o[a]),e[r]=!(n[r]=o[a])}):function(e){return i(e,0,n)}):i}},pseudos:{not:r(function(e){var t=[],n=[],i=S(e.replace(lt,"$1"));return i[I]?r(function(e,t,n,r){for(var o,a=i(e,null,r,[]),s=e.length;s--;)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,r,o){return t[0]=e,i(t,null,o,n),!n.pop()}}),has:r(function(e){return function(t){return o(e,t).length>0}}),contains:r(function(e){return function(t){return(t.textContent||t.innerText||N(t)).indexOf(e)>-1}}),lang:r(function(e){return ft.test(e||"")||o.error("unsupported lang: "+e),e=e.replace(xt,wt).toLowerCase(),function(t){var n;do if(n=L?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(e){var t=window.location&&window.location.hash;return t&&t.slice(1)===e.id},root:function(e){return e===D},focus:function(e){return e===B.activeElement&&(!B.hasFocus||B.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!_.pseudos.empty(e)},header:function(e){return yt.test(e.nodeName)},input:function(e){return vt.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:c(function(){return[0]}),last:c(function(e,t){return[t-1]}),eq:c(function(e,t,n){return[0>n?n+t:n]}),even:c(function(e,t){for(var n=0;t>n;n+=2)e.push(n);return e}),odd:c(function(e,t){for(var n=1;t>n;n+=2)e.push(n);return e}),lt:c(function(e,t,n){for(var r=0>n?n+t:n;--r>=0;)e.push(r);return e}),gt:c(function(e,t,n){for(var r=0>n?n+t:n;++r<t;)e.push(r);return e})}};for(x in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})_.pseudos[x]=s(x);for(x in{submit:!0,reset:!0})_.pseudos[x]=l(x);return S=o.compile=function(e,t){var n,r=[],i=[],o=$[e+" "];if(!o){for(t||(t=u(e)),n=t.length;n--;)o=g(t[n]),o[I]?r.push(o):i.push(o);o=$(e,v(i,r))}return o},_.pseudos.nth=_.pseudos.eq,C.prototype=_.filters=_.pseudos,_.setFilters=new C,z.sortStable=I.split("").sort(K).join("")===I,A(),[0,0].sort(K),z.detectDuplicates=j,o}),r(u,[l,c],function(e,n){function r(e){return"undefined"!=typeof e}function i(e){return"string"==typeof e}function o(e){var t,n,r;for(r=v.createElement("div"),t=v.createDocumentFragment(),r.innerHTML=e;n=r.firstChild;)t.appendChild(n);return t}function a(e,t,n){var r;if("string"==typeof t)t=o(t);else if(t.length){for(r=0;r<t.length;r++)a(e,t[r],n);return e}for(r=e.length;r--;)n.call(e[r],t.parentNode?t:t);return e}function s(e,t){return e&&t&&-1!==(" "+e.className+" ").indexOf(" "+t+" ")}function l(e,t){var n;for(e=e||[],"string"==typeof e&&(e=e.split(" ")),t=t||{},n=e.length;n--;)t[e[n]]={};return t}function c(e,t){return new c.fn.init(e,t)}function u(e){var t=arguments,n,r,i;for(r=1;r<t.length;r++){n=t[r];for(i in n)e[i]=n[i]}return e}function d(e){var t=[],n,r;for(n=0,r=e.length;r>n;n++)t[n]=e[n];return t}function f(e,t){var n;if(t.indexOf)return t.indexOf(e);for(n=t.length;n--;)if(t[n]===e)return n;return-1}function p(e){return null===e||e===t?"":(""+e).replace(N,"")}function h(e,t){var n,r,i,o,a;if(e)if(n=e.length,n===o){for(r in e)if(e.hasOwnProperty(r)&&(a=e[r],t.call(a,a,r)===!1))break}else for(i=0;n>i&&(a=e[i],t.call(a,a,r)!==!1);i++);return e}function m(e,n,r){for(var i=[],o=e[n];o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!c(o).is(r));)1===o.nodeType&&i.push(o),o=o[n];return i}function g(e,t,n,r){for(var i=[];e;e=e[n])r&&e.nodeType!==r||e===t||i.push(e);return i}var v=document,y=Array.prototype.push,b=Array.prototype.slice,C=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,x=e.Event,w=l("fillOpacity fontWeight lineHeight opacity orphans widows zIndex zoom"),_=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)},N=/^\s*|\s*$/g;return c.fn=c.prototype={constructor:c,selector:"",length:0,init:function(e,t){var n=this,r,a;if(!e)return n;if(e.nodeType)return n.context=n[0]=e,n.length=1,n;if(i(e)){if(r="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:C.exec(e),!r)return c(t||document).find(e);if(r[1])for(a=o(e).firstChild;a;)this.add(a),a=a.nextSibling;else{if(a=v.getElementById(r[2]),a.id!==r[2])return n.find(e);n.length=1,n[0]=a}}else this.add(e);return n},toArray:function(){return d(this)},add:function(e){var t=this;return _(e)?y.apply(t,e):e instanceof c?t.add(e.toArray()):y.call(t,e),t},attr:function(e,n){var i=this;if("object"==typeof e)h(e,function(e,t){i.attr(t,e)});else{if(!r(n))return i[0]&&1===i[0].nodeType?i[0].getAttribute(e):t;this.each(function(){1===this.nodeType&&this.setAttribute(e,n)})}return i},css:function(e,n){var i=this;if("object"==typeof e)h(e,function(e,t){i.css(t,e)});else{if(e=e.replace(/-(\D)/g,function(e,t){return t.toUpperCase()}),!r(n))return i[0]?i[0].style[e]:t;"number"!=typeof n||w[e]||(n+="px"),i.each(function(){var t=this.style;"opacity"===e&&this.runtimeStyle&&"undefined"==typeof this.runtimeStyle.opacity&&(t.filter=""===n?"":"alpha(opacity="+100*n+")");try{t[e]=n}catch(r){}})}return i},remove:function(){for(var e=this,t,n=this.length;n--;)t=e[n],x.clean(t),t.parentNode&&t.parentNode.removeChild(t);return this},empty:function(){for(var e=this,t,n=this.length;n--;)for(t=e[n];t.firstChild;)t.removeChild(t.firstChild);return this},html:function(e){var t=this,n;if(r(e)){for(n=t.length;n--;)t[n].innerHTML=e;return t}return t[0]?t[0].innerHTML:""},text:function(e){var t=this,n;if(r(e)){for(n=t.length;n--;)t[n].innerText=t[0].textContent=e;return t}return t[0]?t[0].innerText||t[0].textContent:""},append:function(){return a(this,arguments,function(e){1===this.nodeType&&this.appendChild(e)})},prepend:function(){return a(this,arguments,function(e){1===this.nodeType&&this.insertBefore(e,this.firstChild)})},before:function(){var e=this;return e[0]&&e[0].parentNode?a(e,arguments,function(e){this.parentNode.insertBefore(e,this.nextSibling)}):e},after:function(){var e=this;return e[0]&&e[0].parentNode?a(e,arguments,function(e){this.parentNode.insertBefore(e,this)}):e},appendTo:function(e){return c(e).append(this),this},addClass:function(e){return this.toggleClass(e,!0)},removeClass:function(e){return this.toggleClass(e,!1)},toggleClass:function(e,t){var n=this;return-1!==e.indexOf(" ")?h(e.split(" "),function(){n.toggleClass(this,t)}):n.each(function(n){var r;s(n,e)!==t&&(r=n.className,t?n.className+=r?" "+e:e:n.className=p((" "+r+" ").replace(" "+e+" "," ")))}),n},hasClass:function(e){return s(this[0],e)},each:function(e){return h(this,e)},on:function(e,t){return this.each(function(){x.bind(this,e,t)})},off:function(e,t){return this.each(function(){x.unbind(this,e,t)})},show:function(){return this.css("display","")},hide:function(){return this.css("display","none")},slice:function(){return new c(b.apply(this,arguments))},eq:function(e){return-1===e?this.slice(e):this.slice(e,+e+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},replaceWith:function(e){var t=this;return t[0]&&t[0].parentNode.replaceChild(c(e)[0],t[0]),t},wrap:function(e){return e=c(e)[0],this.each(function(){var t=this,n=e.cloneNode(!1);t.parentNode.insertBefore(n,t),n.appendChild(t)})},unwrap:function(){return this.each(function(){for(var e=this,t=e.firstChild,n;t;)n=t,t=t.nextSibling,e.parentNode.insertBefore(n,e)})},clone:function(){var e=[];return this.each(function(){e.push(this.cloneNode(!0))}),c(e)},find:function(e){var t,n,r=[];for(t=0,n=this.length;n>t;t++)c.find(e,this[t],r);return c(r)},push:y,sort:[].sort,splice:[].splice},u(c,{extend:u,toArray:d,inArray:f,isArray:_,each:h,trim:p,makeMap:l,find:n,expr:n.selectors,unique:n.uniqueSort,text:n.getText,isXMLDoc:n.isXML,contains:n.contains,filter:function(e,t,n){return n&&(e=":not("+e+")"),t=1===t.length?c.find.matchesSelector(t[0],e)?[t[0]]:[]:c.find.matches(e,t)}}),h({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return m(e,"parentNode")},parentsUntil:function(e,t){return m(e,"parentNode",t)},next:function(e){return g(e,"nextSibling",1)},prev:function(e){return g(e,"previousSibling",1)},nextNodes:function(e){return g(e,"nextSibling")},prevNodes:function(e){return g(e,"previousSibling")},children:function(e){return g(e.firstChild,"nextSibling",1)},contents:function(e){return d(("iframe"===e.nodeName?e.contentDocument||e.contentWindow.document:e).childNodes)}},function(e,t){c.fn[e]=function(n){var r=this,i;if(r.length>1)throw new Error("DomQuery only supports traverse functions on a single node.");return r[0]&&(i=t(r[0],n)),i=c(i),n&&"parentsUntil"!==e?i.filter(n):i}}),c.fn.filter=function(e){return c.filter(e)},c.fn.is=function(e){return!!e&&this.filter(e).length>0},c.fn.init.prototype=c.fn,c}),r(d,[],function(){return function(e,t){function n(e,t,n,r){function i(e){return e=parseInt(e,10).toString(16),e.length>1?e:"0"+e
+}return"#"+i(t)+i(n)+i(r)}var r=/rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/gi,i=/(?:url(?:(?:\(\s*\"([^\"]+)\"\s*\))|(?:\(\s*\'([^\']+)\'\s*\))|(?:\(\s*([^)\s]+)\s*\))))|(?:\'([^\']+)\')|(?:\"([^\"]+)\")/gi,o=/\s*([^:]+):\s*([^;]+);?/g,a=/\s+$/,s,l,c={},u,d="\ufeff";for(e=e||{},u=("\\\" \\' \\; \\: ; : "+d).split(" "),l=0;l<u.length;l++)c[u[l]]=d+l,c[d+l]=u[l];return{toHex:function(e){return e.replace(r,n)},parse:function(t){function s(e,t,n){var r,i,o,a;if(r=m[e+"-top"+t],r&&(i=m[e+"-right"+t],i&&(o=m[e+"-bottom"+t],o&&(a=m[e+"-left"+t])))){var s=[r,i,o,a];for(l=s.length-1;l--&&s[l]===s[l+1];);l>-1&&n||(m[e+t]=-1==l?s[0]:s.join(" "),delete m[e+"-top"+t],delete m[e+"-right"+t],delete m[e+"-bottom"+t],delete m[e+"-left"+t])}}function u(e){var t=m[e],n;if(t){for(t=t.split(" "),n=t.length;n--;)if(t[n]!==t[0])return!1;return m[e]=t[0],!0}}function d(e,t,n,r){u(t)&&u(n)&&u(r)&&(m[e]=m[t]+" "+m[n]+" "+m[r],delete m[t],delete m[n],delete m[r])}function f(e){return b=!0,c[e]}function p(e,t){return b&&(e=e.replace(/\uFEFF[0-9]/g,function(e){return c[e]})),t||(e=e.replace(/\\([\'\";:])/g,"$1")),e}function h(t,n,r,i,o,a){if(o=o||a)return o=p(o),"'"+o.replace(/\'/g,"\\'")+"'";if(n=p(n||r||i),!e.allow_script_urls){var s=n.replace(/[\s\r\n]+/,"");if(/(java|vb)script:/i.test(s))return"";if(!e.allow_svg_data_urls&&/^data:image\/svg/i.test(s))return""}return C&&(n=C.call(x,n,"style")),"url('"+n.replace(/\'/g,"\\'")+"')"}var m={},g,v,y,b,C=e.url_converter,x=e.url_converter_scope||this;if(t){for(t=t.replace(/[\u0000-\u001F]/g,""),t=t.replace(/\\[\"\';:\uFEFF]/g,f).replace(/\"[^\"]+\"|\'[^\']+\'/g,function(e){return e.replace(/[;:]/g,f)});g=o.exec(t);){if(v=g[1].replace(a,"").toLowerCase(),y=g[2].replace(a,""),y=y.replace(/\\[0-9a-f]+/g,function(e){return String.fromCharCode(parseInt(e.substr(1),16))}),v&&y.length>0){if(!e.allow_script_urls&&("behavior"==v||/expression\s*\(|\/\*|\*\//.test(y)))continue;"font-weight"===v&&"700"===y?y="bold":("color"===v||"background-color"===v)&&(y=y.toLowerCase()),y=y.replace(r,n),y=y.replace(i,h),m[v]=b?p(y,!0):y}o.lastIndex=g.index+g[0].length}s("border","",!0),s("border","-width"),s("border","-color"),s("border","-style"),s("padding",""),s("margin",""),d("border","border-width","border-style","border-color"),"medium none"===m.border&&delete m.border,"none"===m["border-image"]&&delete m["border-image"]}return m},serialize:function(e,n){function r(n){var r,o,a,l;if(r=t.styles[n])for(o=0,a=r.length;a>o;o++)n=r[o],l=e[n],l!==s&&l.length>0&&(i+=(i.length>0?" ":"")+n+": "+l+";")}var i="",o,a;if(n&&t&&t.styles)r("*"),r(n);else for(o in e)a=e[o],a!==s&&a.length>0&&(i+=(i.length>0?" ":"")+o+": "+a+";");return i}}}}),r(f,[],function(){return function(e,t){function n(e,n,r,i){var o,a;if(e){if(!i&&e[n])return e[n];if(e!=t){if(o=e[r])return o;for(a=e.parentNode;a&&a!=t;a=a.parentNode)if(o=a[r])return o}}}var r=e;this.current=function(){return r},this.next=function(e){return r=n(r,"firstChild","nextSibling",e)},this.prev=function(e){return r=n(r,"lastChild","previousSibling",e)}}}),r(p,[],function(){function e(e){return null===e||e===t?"":(""+e).replace(m,"")}function n(e,n){return n?"array"==n&&g(e)?!0:typeof e==n:e!==t}function r(e){var t=[],n,r;for(n=0,r=e.length;r>n;n++)t[n]=e[n];return t}function i(e,t,n){var r;for(e=e||[],t=t||",","string"==typeof e&&(e=e.split(t)),n=n||{},r=e.length;r--;)n[e[r]]={};return n}function o(e,n,r){var i,o;if(!e)return 0;if(r=r||e,e.length!==t){for(i=0,o=e.length;o>i;i++)if(n.call(r,e[i],i,e)===!1)return 0}else for(i in e)if(e.hasOwnProperty(i)&&n.call(r,e[i],i,e)===!1)return 0;return 1}function a(e,t){var n=[];return o(e,function(e){n.push(t(e))}),n}function s(e,t){var n=[];return o(e,function(e){(!t||t(e))&&n.push(e)}),n}function l(e,t,n){var r=this,i,o,a,s,l,c=0;if(e=/^((static) )?([\w.]+)(:([\w.]+))?/.exec(e),a=e[3].match(/(^|\.)(\w+)$/i)[2],o=r.createNS(e[3].replace(/\.\w+$/,""),n),!o[a]){if("static"==e[2])return o[a]=t,void(this.onCreate&&this.onCreate(e[2],e[3],o[a]));t[a]||(t[a]=function(){},c=1),o[a]=t[a],r.extend(o[a].prototype,t),e[5]&&(i=r.resolve(e[5]).prototype,s=e[5].match(/\.(\w+)$/i)[1],l=o[a],o[a]=c?function(){return i[s].apply(this,arguments)}:function(){return this.parent=i[s],l.apply(this,arguments)},o[a].prototype[a]=o[a],r.each(i,function(e,t){o[a].prototype[t]=i[t]}),r.each(t,function(e,t){i[t]?o[a].prototype[t]=function(){return this.parent=i[t],e.apply(this,arguments)}:t!=a&&(o[a].prototype[t]=e)})),r.each(t["static"],function(e,t){o[a][t]=e})}}function c(e,t){var n,r;if(e)for(n=0,r=e.length;r>n;n++)if(e[n]===t)return n;return-1}function u(e,n){var r,i,o,a=arguments,s;for(r=1,i=a.length;i>r;r++){n=a[r];for(o in n)n.hasOwnProperty(o)&&(s=n[o],s!==t&&(e[o]=s))}return e}function d(e,t,n,r){r=r||this,e&&(n&&(e=e[n]),o(e,function(e,i){return t.call(r,e,i,n)===!1?!1:void d(e,t,n,r)}))}function f(e,t){var n,r;for(t=t||window,e=e.split("."),n=0;n<e.length;n++)r=e[n],t[r]||(t[r]={}),t=t[r];return t}function p(e,t){var n,r;for(t=t||window,e=e.split("."),n=0,r=e.length;r>n&&(t=t[e[n]],t);n++);return t}function h(t,r){return!t||n(t,"array")?t:a(t.split(r||","),e)}var m=/^\s*|\s*$/g,g=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)};return{trim:e,isArray:g,is:n,toArray:r,makeMap:i,each:o,map:a,grep:s,inArray:c,extend:u,create:l,walk:d,createNS:f,resolve:p,explode:h}}),r(h,[p],function(e){function t(n){function r(){return H.createDocumentFragment()}function i(e,t){_(F,e,t)}function o(e,t){_(z,e,t)}function a(e){i(e.parentNode,j(e))}function s(e){i(e.parentNode,j(e)+1)}function l(e){o(e.parentNode,j(e))}function c(e){o(e.parentNode,j(e)+1)}function u(e){e?(M[U]=M[V],M[q]=M[W]):(M[V]=M[U],M[W]=M[q]),M.collapsed=F}function d(e){a(e),c(e)}function f(e){i(e,0),o(e,1===e.nodeType?e.childNodes.length:e.nodeValue.length)}function p(e,t){var n=M[V],r=M[W],i=M[U],o=M[q],a=t.startContainer,s=t.startOffset,l=t.endContainer,c=t.endOffset;return 0===e?w(n,r,a,s):1===e?w(i,o,a,s):2===e?w(i,o,l,c):3===e?w(n,r,l,c):void 0}function h(){N(I)}function m(){return N(P)}function g(){return N(O)}function v(e){var t=this[V],r=this[W],i,o;3!==t.nodeType&&4!==t.nodeType||!t.nodeValue?(t.childNodes.length>0&&(o=t.childNodes[r]),o?t.insertBefore(e,o):3==t.nodeType?n.insertAfter(e,t):t.appendChild(e)):r?r>=t.nodeValue.length?n.insertAfter(e,t):(i=t.splitText(r),t.parentNode.insertBefore(e,i)):t.parentNode.insertBefore(e,t)}function y(e){var t=M.extractContents();M.insertNode(e),e.appendChild(t),M.selectNode(e)}function b(){return $(new t(n),{startContainer:M[V],startOffset:M[W],endContainer:M[U],endOffset:M[q],collapsed:M.collapsed,commonAncestorContainer:M.commonAncestorContainer})}function C(e,t){var n;if(3==e.nodeType)return e;if(0>t)return e;for(n=e.firstChild;n&&t>0;)--t,n=n.nextSibling;return n?n:e}function x(){return M[V]==M[U]&&M[W]==M[q]}function w(e,t,r,i){var o,a,s,l,c,u;if(e==r)return t==i?0:i>t?-1:1;for(o=r;o&&o.parentNode!=e;)o=o.parentNode;if(o){for(a=0,s=e.firstChild;s!=o&&t>a;)a++,s=s.nextSibling;return a>=t?-1:1}for(o=e;o&&o.parentNode!=r;)o=o.parentNode;if(o){for(a=0,s=r.firstChild;s!=o&&i>a;)a++,s=s.nextSibling;return i>a?-1:1}for(l=n.findCommonAncestor(e,r),c=e;c&&c.parentNode!=l;)c=c.parentNode;for(c||(c=l),u=r;u&&u.parentNode!=l;)u=u.parentNode;if(u||(u=l),c==u)return 0;for(s=l.firstChild;s;){if(s==c)return-1;if(s==u)return 1;s=s.nextSibling}}function _(e,t,r){var i,o;for(e?(M[V]=t,M[W]=r):(M[U]=t,M[q]=r),i=M[U];i.parentNode;)i=i.parentNode;for(o=M[V];o.parentNode;)o=o.parentNode;o==i?w(M[V],M[W],M[U],M[q])>0&&M.collapse(e):M.collapse(e),M.collapsed=x(),M.commonAncestorContainer=n.findCommonAncestor(M[V],M[U])}function N(e){var t,n=0,r=0,i,o,a,s,l,c;if(M[V]==M[U])return E(e);for(t=M[U],i=t.parentNode;i;t=i,i=i.parentNode){if(i==M[V])return S(t,e);++n}for(t=M[V],i=t.parentNode;i;t=i,i=i.parentNode){if(i==M[U])return k(t,e);++r}for(o=r-n,a=M[V];o>0;)a=a.parentNode,o--;for(s=M[U];0>o;)s=s.parentNode,o++;for(l=a.parentNode,c=s.parentNode;l!=c;l=l.parentNode,c=c.parentNode)a=l,s=c;return T(a,s,e)}function E(e){var t,n,i,o,a,s,l,c,u;if(e!=I&&(t=r()),M[W]==M[q])return t;if(3==M[V].nodeType){if(n=M[V].nodeValue,i=n.substring(M[W],M[q]),e!=O&&(o=M[V],c=M[W],u=M[q]-M[W],0===c&&u>=o.nodeValue.length-1?o.parentNode.removeChild(o):o.deleteData(c,u),M.collapse(F)),e==I)return;return i.length>0&&t.appendChild(H.createTextNode(i)),t}for(o=C(M[V],M[W]),a=M[q]-M[W];o&&a>0;)s=o.nextSibling,l=D(o,e),t&&t.appendChild(l),--a,o=s;return e!=O&&M.collapse(F),t}function S(e,t){var n,i,o,a,s,l;if(t!=I&&(n=r()),i=R(e,t),n&&n.appendChild(i),o=j(e),a=o-M[W],0>=a)return t!=O&&(M.setEndBefore(e),M.collapse(z)),n;for(i=e.previousSibling;a>0;)s=i.previousSibling,l=D(i,t),n&&n.insertBefore(l,n.firstChild),--a,i=s;return t!=O&&(M.setEndBefore(e),M.collapse(z)),n}function k(e,t){var n,i,o,a,s,l;for(t!=I&&(n=r()),o=A(e,t),n&&n.appendChild(o),i=j(e),++i,a=M[q]-i,o=e.nextSibling;o&&a>0;)s=o.nextSibling,l=D(o,t),n&&n.appendChild(l),--a,o=s;return t!=O&&(M.setStartAfter(e),M.collapse(F)),n}function T(e,t,n){var i,o,a,s,l,c,u;for(n!=I&&(o=r()),i=A(e,n),o&&o.appendChild(i),a=j(e),s=j(t),++a,l=s-a,c=e.nextSibling;l>0;)u=c.nextSibling,i=D(c,n),o&&o.appendChild(i),c=u,--l;return i=R(t,n),o&&o.appendChild(i),n!=O&&(M.setStartAfter(e),M.collapse(F)),o}function R(e,t){var n=C(M[U],M[q]-1),r,i,o,a,s,l=n!=M[U];if(n==e)return B(n,l,z,t);for(r=n.parentNode,i=B(r,z,z,t);r;){for(;n;)o=n.previousSibling,a=B(n,l,z,t),t!=I&&i.insertBefore(a,i.firstChild),l=F,n=o;if(r==e)return i;n=r.previousSibling,r=r.parentNode,s=B(r,z,z,t),t!=I&&s.appendChild(i),i=s}}function A(e,t){var n=C(M[V],M[W]),r=n!=M[V],i,o,a,s,l;if(n==e)return B(n,r,F,t);for(i=n.parentNode,o=B(i,z,F,t);i;){for(;n;)a=n.nextSibling,s=B(n,r,F,t),t!=I&&o.appendChild(s),r=F,n=a;if(i==e)return o;n=i.nextSibling,i=i.parentNode,l=B(i,z,F,t),t!=I&&l.appendChild(o),o=l}}function B(e,t,r,i){var o,a,s,l,c;if(t)return D(e,i);if(3==e.nodeType){if(o=e.nodeValue,r?(l=M[W],a=o.substring(l),s=o.substring(0,l)):(l=M[q],a=o.substring(0,l),s=o.substring(l)),i!=O&&(e.nodeValue=s),i==I)return;return c=n.clone(e,z),c.nodeValue=a,c}if(i!=I)return n.clone(e,z)}function D(e,t){return t!=I?t==O?n.clone(e,F):e:void e.parentNode.removeChild(e)}function L(){return n.create("body",null,g()).outerText}var M=this,H=n.doc,P=0,O=1,I=2,F=!0,z=!1,W="startOffset",V="startContainer",U="endContainer",q="endOffset",$=e.extend,j=n.nodeIndex;return $(M,{startContainer:H,startOffset:0,endContainer:H,endOffset:0,collapsed:F,commonAncestorContainer:H,START_TO_START:0,START_TO_END:1,END_TO_END:2,END_TO_START:3,setStart:i,setEnd:o,setStartBefore:a,setStartAfter:s,setEndBefore:l,setEndAfter:c,collapse:u,selectNode:d,selectNodeContents:f,compareBoundaryPoints:p,deleteContents:h,extractContents:m,cloneContents:g,insertNode:v,surroundContents:y,cloneRange:b,toStringIE:L}),M}return t.prototype.toString=function(){return this.toStringIE()},t}),r(m,[p],function(e){function t(e){var t;return t=document.createElement("div"),t.innerHTML=e,t.textContent||t.innerText||e}function n(e,t){var n,r,i,a={};if(e){for(e=e.split(","),t=t||10,n=0;n<e.length;n+=2)r=String.fromCharCode(parseInt(e[n],t)),o[r]||(i="&"+e[n+1]+";",a[r]=i,a[i]=r);return a}}var r=e.makeMap,i,o,a,s=/[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,l=/[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,c=/[<>&\"\']/g,u=/&(#x|#)?([\w]+);/g,d={128:"\u20ac",130:"\u201a",131:"\u0192",132:"\u201e",133:"\u2026",134:"\u2020",135:"\u2021",136:"\u02c6",137:"\u2030",138:"\u0160",139:"\u2039",140:"\u0152",142:"\u017d",145:"\u2018",146:"\u2019",147:"\u201c",148:"\u201d",149:"\u2022",150:"\u2013",151:"\u2014",152:"\u02dc",153:"\u2122",154:"\u0161",155:"\u203a",156:"\u0153",158:"\u017e",159:"\u0178"};o={'"':"&quot;","'":"&#39;","<":"&lt;",">":"&gt;","&":"&amp;","`":"&#96;"},a={"&lt;":"<","&gt;":">","&amp;":"&","&quot;":'"',"&apos;":"'"},i=n("50,nbsp,51,iexcl,52,cent,53,pound,54,curren,55,yen,56,brvbar,57,sect,58,uml,59,copy,5a,ordf,5b,laquo,5c,not,5d,shy,5e,reg,5f,macr,5g,deg,5h,plusmn,5i,sup2,5j,sup3,5k,acute,5l,micro,5m,para,5n,middot,5o,cedil,5p,sup1,5q,ordm,5r,raquo,5s,frac14,5t,frac12,5u,frac34,5v,iquest,60,Agrave,61,Aacute,62,Acirc,63,Atilde,64,Auml,65,Aring,66,AElig,67,Ccedil,68,Egrave,69,Eacute,6a,Ecirc,6b,Euml,6c,Igrave,6d,Iacute,6e,Icirc,6f,Iuml,6g,ETH,6h,Ntilde,6i,Ograve,6j,Oacute,6k,Ocirc,6l,Otilde,6m,Ouml,6n,times,6o,Oslash,6p,Ugrave,6q,Uacute,6r,Ucirc,6s,Uuml,6t,Yacute,6u,THORN,6v,szlig,70,agrave,71,aacute,72,acirc,73,atilde,74,auml,75,aring,76,aelig,77,ccedil,78,egrave,79,eacute,7a,ecirc,7b,euml,7c,igrave,7d,iacute,7e,icirc,7f,iuml,7g,eth,7h,ntilde,7i,ograve,7j,oacute,7k,ocirc,7l,otilde,7m,ouml,7n,divide,7o,oslash,7p,ugrave,7q,uacute,7r,ucirc,7s,uuml,7t,yacute,7u,thorn,7v,yuml,ci,fnof,sh,Alpha,si,Beta,sj,Gamma,sk,Delta,sl,Epsilon,sm,Zeta,sn,Eta,so,Theta,sp,Iota,sq,Kappa,sr,Lambda,ss,Mu,st,Nu,su,Xi,sv,Omicron,t0,Pi,t1,Rho,t3,Sigma,t4,Tau,t5,Upsilon,t6,Phi,t7,Chi,t8,Psi,t9,Omega,th,alpha,ti,beta,tj,gamma,tk,delta,tl,epsilon,tm,zeta,tn,eta,to,theta,tp,iota,tq,kappa,tr,lambda,ts,mu,tt,nu,tu,xi,tv,omicron,u0,pi,u1,rho,u2,sigmaf,u3,sigma,u4,tau,u5,upsilon,u6,phi,u7,chi,u8,psi,u9,omega,uh,thetasym,ui,upsih,um,piv,812,bull,816,hellip,81i,prime,81j,Prime,81u,oline,824,frasl,88o,weierp,88h,image,88s,real,892,trade,89l,alefsym,8cg,larr,8ch,uarr,8ci,rarr,8cj,darr,8ck,harr,8dl,crarr,8eg,lArr,8eh,uArr,8ei,rArr,8ej,dArr,8ek,hArr,8g0,forall,8g2,part,8g3,exist,8g5,empty,8g7,nabla,8g8,isin,8g9,notin,8gb,ni,8gf,prod,8gh,sum,8gi,minus,8gn,lowast,8gq,radic,8gt,prop,8gu,infin,8h0,ang,8h7,and,8h8,or,8h9,cap,8ha,cup,8hb,int,8hk,there4,8hs,sim,8i5,cong,8i8,asymp,8j0,ne,8j1,equiv,8j4,le,8j5,ge,8k2,sub,8k3,sup,8k4,nsub,8k6,sube,8k7,supe,8kl,oplus,8kn,otimes,8l5,perp,8m5,sdot,8o8,lceil,8o9,rceil,8oa,lfloor,8ob,rfloor,8p9,lang,8pa,rang,9ea,loz,9j0,spades,9j3,clubs,9j5,hearts,9j6,diams,ai,OElig,aj,oelig,b0,Scaron,b1,scaron,bo,Yuml,m6,circ,ms,tilde,802,ensp,803,emsp,809,thinsp,80c,zwnj,80d,zwj,80e,lrm,80f,rlm,80j,ndash,80k,mdash,80o,lsquo,80p,rsquo,80q,sbquo,80s,ldquo,80t,rdquo,80u,bdquo,810,dagger,811,Dagger,81g,permil,81p,lsaquo,81q,rsaquo,85c,euro",32);var f={encodeRaw:function(e,t){return e.replace(t?s:l,function(e){return o[e]||e})},encodeAllRaw:function(e){return(""+e).replace(c,function(e){return o[e]||e})},encodeNumeric:function(e,t){return e.replace(t?s:l,function(e){return e.length>1?"&#"+(1024*(e.charCodeAt(0)-55296)+(e.charCodeAt(1)-56320)+65536)+";":o[e]||"&#"+e.charCodeAt(0)+";"})},encodeNamed:function(e,t,n){return n=n||i,e.replace(t?s:l,function(e){return o[e]||n[e]||e})},getEncodeFunc:function(e,t){function a(e,n){return e.replace(n?s:l,function(e){return o[e]||t[e]||"&#"+e.charCodeAt(0)+";"||e})}function c(e,n){return f.encodeNamed(e,n,t)}return t=n(t)||i,e=r(e.replace(/\+/g,",")),e.named&&e.numeric?a:e.named?t?c:f.encodeNamed:e.numeric?f.encodeNumeric:f.encodeRaw},decode:function(e){return e.replace(u,function(e,n,r){return n?(r=parseInt(r,2===n.length?16:10),r>65535?(r-=65536,String.fromCharCode(55296+(r>>10),56320+(1023&r))):d[r]||String.fromCharCode(r)):a[e]||i[e]||t(e)})}};return f}),r(g,[],function(){var e=navigator,t=e.userAgent,n,r,i,o,a,s,l;n=window.opera&&window.opera.buildNumber,r=/WebKit/.test(t),i=!r&&!n&&/MSIE/gi.test(t)&&/Explorer/gi.test(e.appName),i=i&&/MSIE (\w+)\./.exec(t)[1],o=-1==t.indexOf("Trident/")||-1==t.indexOf("rv:")&&-1==e.appName.indexOf("Netscape")?!1:11,i=i||o,a=!r&&!o&&/Gecko/.test(t),s=-1!=t.indexOf("Mac"),l=/(iPad|iPhone)/.test(t);var c=!l||t.match(/AppleWebKit\/(\d*)/)[1]>=534;return{opera:n,webkit:r,ie:i,gecko:a,mac:s,iOS:l,contentEditable:c,transparentSrc:"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",caretAfter:8!=i,range:window.getSelection&&"Range"in window,documentMode:i?document.documentMode||7:10}}),r(v,[],function(){return function(e,t){function n(t){e.getElementsByTagName("head")[0].appendChild(t)}function r(t,r,s){function l(){for(var e=v.passed,t=e.length;t--;)e[t]();v.status=2,v.passed=[],v.failed=[]}function c(){for(var e=v.failed,t=e.length;t--;)e[t]();v.status=3,v.passed=[],v.failed=[]}function u(){var e=navigator.userAgent.match(/WebKit\/(\d*)/);return!!(e&&e[1]<536)}function d(e,t){e()||((new Date).getTime()-g<a?window.setTimeout(t,0):c())}function f(){d(function(){for(var t=e.styleSheets,n,r=t.length,i;r--;)if(n=t[r],i=n.ownerNode?n.ownerNode:n.owningElement,i&&i.id===h.id)return l(),!0},f)}function p(){d(function(){try{var e=m.sheet.cssRules;return l(),!!e}catch(t){}},p)}var h,m,g,v;if(o[t]?v=o[t]:(v={passed:[],failed:[]},o[t]=v),r&&v.passed.push(r),s&&v.failed.push(s),1!=v.status){if(2==v.status)return void l();if(3==v.status)return void c();if(v.status=1,h=e.createElement("link"),h.rel="stylesheet",h.type="text/css",h.id="u"+i++,h.async=!1,h.defer=!1,g=(new Date).getTime(),"onload"in h&&!u())h.onload=f,h.onerror=c;else{if(navigator.userAgent.indexOf("Firefox")>0)return m=e.createElement("style"),m.textContent='@import "'+t+'"',p(),void n(m);f()}n(h),h.href=t}}var i=0,o={},a;t=t||{},a=t.maxLoadTime||5e3,this.load=r}}),r(y,[c,d,l,f,h,m,g,p,v],function(e,n,r,i,o,a,s,l,c){function u(e,t){var i=this,o;i.doc=e,i.win=window,i.files={},i.counter=0,i.stdMode=!v||e.documentMode>=8,i.boxModel=!v||"CSS1Compat"==e.compatMode||i.stdMode,i.hasOuterHTML="outerHTML"in e.createElement("a"),i.styleSheetLoader=new c(e),this.boundEvents=[],i.settings=t=m({keep_values:!1,hex_colors:1},t),i.schema=t.schema,i.styles=new n({url_converter:t.url_converter,url_converter_scope:t.url_converter_scope},t.schema),i.fixDoc(e),i.events=t.ownEvents?new r(t.proxy):r.Event,o=t.schema?t.schema.getBlockElements():{},i.isBlock=function(e){if(!e)return!1;var t=e.nodeType;return t?!(1!==t||!o[e.nodeName]):!!o[e]}}var d=l.each,f=l.is,p=l.grep,h=l.trim,m=l.extend,g=s.webkit,v=s.ie,y=/^([a-z0-9],?)+$/i,b=/^[ \t\r\n]*$/,C=l.makeMap("fillOpacity fontWeight lineHeight opacity orphans widows zIndex zoom"," ");return u.prototype={root:null,props:{"for":"htmlFor","class":"className",className:"className",checked:"checked",disabled:"disabled",maxlength:"maxLength",readonly:"readOnly",selected:"selected",value:"value",id:"id",name:"name",type:"type"},fixDoc:function(e){var t=this.settings,n;if(v&&t.schema){"abbr article aside audio canvas details figcaption figure footer header hgroup mark menu meter nav output progress section summary time video".replace(/\w+/g,function(t){e.createElement(t)});for(n in t.schema.getCustomElements())e.createElement(n)}},clone:function(e,t){var n=this,r,i;return!v||1!==e.nodeType||t?e.cloneNode(t):(i=n.doc,t?r.firstChild:(r=i.createElement(e.nodeName),d(n.getAttribs(e),function(t){n.setAttrib(r,t.nodeName,n.getAttrib(e,t.nodeName))}),r))},getRoot:function(){var e=this;return e.get(e.settings.root_element)||e.doc.body},getViewPort:function(e){var t,n;return e=e?e:this.win,t=e.document,n=this.boxModel?t.documentElement:t.body,{x:e.pageXOffset||n.scrollLeft,y:e.pageYOffset||n.scrollTop,w:e.innerWidth||n.clientWidth,h:e.innerHeight||n.clientHeight}},getRect:function(e){var t=this,n,r;return e=t.get(e),n=t.getPos(e),r=t.getSize(e),{x:n.x,y:n.y,w:r.w,h:r.h}},getSize:function(e){var t=this,n,r;return e=t.get(e),n=t.getStyle(e,"width"),r=t.getStyle(e,"height"),-1===n.indexOf("px")&&(n=0),-1===r.indexOf("px")&&(r=0),{w:parseInt(n,10)||e.offsetWidth||e.clientWidth,h:parseInt(r,10)||e.offsetHeight||e.clientHeight}},getParent:function(e,t,n){return this.getParents(e,t,n,!1)},getParents:function(e,n,r,i){var o=this,a,s=[];for(e=o.get(e),i=i===t,r=r||("BODY"!=o.getRoot().nodeName?o.getRoot().parentNode:null),f(n,"string")&&(a=n,n="*"===n?function(e){return 1==e.nodeType}:function(e){return o.is(e,a)});e&&e!=r&&e.nodeType&&9!==e.nodeType;){if(!n||n(e)){if(!i)return e;s.push(e)}e=e.parentNode}return i?s:null},get:function(e){var t;return e&&this.doc&&"string"==typeof e&&(t=e,e=this.doc.getElementById(e),e&&e.id!==t)?this.doc.getElementsByName(t)[1]:e},getNext:function(e,t){return this._findSib(e,t,"nextSibling")},getPrev:function(e,t){return this._findSib(e,t,"previousSibling")},select:function(t,n){var r=this;return e(t,r.get(n)||r.get(r.settings.root_element)||r.doc,[])},is:function(n,r){var i;if(n.length===t){if("*"===r)return 1==n.nodeType;if(y.test(r)){for(r=r.toLowerCase().split(/,/),n=n.nodeName.toLowerCase(),i=r.length-1;i>=0;i--)if(r[i]==n)return!0;return!1}}if(n.nodeType&&1!=n.nodeType)return!1;var o=n.nodeType?[n]:n;return e(r,o[0].ownerDocument||o[0],null,o).length>0},add:function(e,t,n,r,i){var o=this;return this.run(e,function(e){var a;return a=f(t,"string")?o.doc.createElement(t):t,o.setAttribs(a,n),r&&(r.nodeType?a.appendChild(r):o.setHTML(a,r)),i?a:e.appendChild(a)})},create:function(e,t,n){return this.add(this.doc.createElement(e),e,t,n,1)},createHTML:function(e,t,n){var r="",i;r+="<"+e;for(i in t)t.hasOwnProperty(i)&&null!==t[i]&&(r+=" "+i+'="'+this.encode(t[i])+'"');return"undefined"!=typeof n?r+">"+n+"</"+e+">":r+" />"},createFragment:function(e){var t,n,r=this.doc,i;for(i=r.createElement("div"),t=r.createDocumentFragment(),e&&(i.innerHTML=e);n=i.firstChild;)t.appendChild(n);return t},remove:function(e,t){return this.run(e,function(e){var n,r=e.parentNode;if(!r)return null;if(t)for(;n=e.firstChild;)!v||3!==n.nodeType||n.nodeValue?r.insertBefore(n,e):e.removeChild(n);return r.removeChild(e)})},setStyle:function(e,t,n){return this.run(e,function(e){var r=this,i,o;if(t)if("string"==typeof t){i=e.style,t=t.replace(/-(\D)/g,function(e,t){return t.toUpperCase()}),"number"!=typeof n||C[t]||(n+="px"),"opacity"===t&&e.runtimeStyle&&"undefined"==typeof e.runtimeStyle.opacity&&(i.filter=""===n?"":"alpha(opacity="+100*n+")"),"float"==t&&(t="cssFloat"in e.style?"cssFloat":"styleFloat");try{i[t]=n}catch(a){}r.settings.update_styles&&e.removeAttribute("data-mce-style")}else for(o in t)r.setStyle(e,o,t[o])})},getStyle:function(e,n,r){if(e=this.get(e)){if(this.doc.defaultView&&r){n=n.replace(/[A-Z]/g,function(e){return"-"+e});try{return this.doc.defaultView.getComputedStyle(e,null).getPropertyValue(n)}catch(i){return null}}return n=n.replace(/-(\D)/g,function(e,t){return t.toUpperCase()}),"float"==n&&(n=v?"styleFloat":"cssFloat"),e.currentStyle&&r?e.currentStyle[n]:e.style?e.style[n]:t}},setStyles:function(e,t){this.setStyle(e,t)},css:function(e,t,n){this.setStyle(e,t,n)},removeAllAttribs:function(e){return this.run(e,function(e){var t,n=e.attributes;for(t=n.length-1;t>=0;t--)e.removeAttributeNode(n.item(t))})},setAttrib:function(e,t,n){var r=this;if(e&&t)return this.run(e,function(e){var i=r.settings,o=e.getAttribute(t);if(null!==n)switch(t){case"style":if(!f(n,"string"))return void d(n,function(t,n){r.setStyle(e,n,t)});i.keep_values&&(n?e.setAttribute("data-mce-style",n,2):e.removeAttribute("data-mce-style",2)),e.style.cssText=n;break;case"class":e.className=n||"";break;case"src":case"href":i.keep_values&&(i.url_converter&&(n=i.url_converter.call(i.url_converter_scope||r,n,t,e)),r.setAttrib(e,"data-mce-"+t,n,2));break;case"shape":e.setAttribute("data-mce-style",n)}f(n)&&null!==n&&0!==n.length?e.setAttribute(t,""+n,2):e.removeAttribute(t,2),o!=n&&i.onSetAttrib&&i.onSetAttrib({attrElm:e,attrName:t,attrValue:n})})},setAttribs:function(e,t){var n=this;return this.run(e,function(e){d(t,function(t,r){n.setAttrib(e,r,t)})})},getAttrib:function(e,t,n){var r,i=this,o;if(e=i.get(e),!e||1!==e.nodeType)return n===o?!1:n;if(f(n)||(n=""),/^(src|href|style|coords|shape)$/.test(t)&&(r=e.getAttribute("data-mce-"+t)))return r;if(v&&i.props[t]&&(r=e[i.props[t]],r=r&&r.nodeValue?r.nodeValue:r),r||(r=e.getAttribute(t,2)),/^(checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)$/.test(t))return e[i.props[t]]===!0&&""===r?t:r?t:"";if("FORM"===e.nodeName&&e.getAttributeNode(t))return e.getAttributeNode(t).nodeValue;if("style"===t&&(r=r||e.style.cssText,r&&(r=i.serializeStyle(i.parseStyle(r),e.nodeName),i.settings.keep_values&&e.setAttribute("data-mce-style",r))),g&&"class"===t&&r&&(r=r.replace(/(apple|webkit)\-[a-z\-]+/gi,"")),v)switch(t){case"rowspan":case"colspan":1===r&&(r="");break;case"size":("+0"===r||20===r||0===r)&&(r="");break;case"width":case"height":case"vspace":case"checked":case"disabled":case"readonly":0===r&&(r="");break;case"hspace":-1===r&&(r="");break;case"maxlength":case"tabindex":(32768===r||2147483647===r||"32768"===r)&&(r="");break;case"multiple":case"compact":case"noshade":case"nowrap":return 65535===r?t:n;case"shape":r=r.toLowerCase();break;default:0===t.indexOf("on")&&r&&(r=(""+r).replace(/^function\s+\w+\(\)\s+\{\s+(.*)\s+\}$/,"$1"))}return r!==o&&null!==r&&""!==r?""+r:n},getPos:function(e,t){var n=this,r=0,i=0,o,a=n.doc,s;if(e=n.get(e),t=t||a.body,e){if(t===a.body&&e.getBoundingClientRect)return s=e.getBoundingClientRect(),t=n.boxModel?a.documentElement:a.body,r=s.left+(a.documentElement.scrollLeft||a.body.scrollLeft)-t.clientLeft,i=s.top+(a.documentElement.scrollTop||a.body.scrollTop)-t.clientTop,{x:r,y:i};for(o=e;o&&o!=t&&o.nodeType;)r+=o.offsetLeft||0,i+=o.offsetTop||0,o=o.offsetParent;for(o=e.parentNode;o&&o!=t&&o.nodeType;)r-=o.scrollLeft||0,i-=o.scrollTop||0,o=o.parentNode}return{x:r,y:i}},parseStyle:function(e){return this.styles.parse(e)},serializeStyle:function(e,t){return this.styles.serialize(e,t)},addStyle:function(e){var t=this,n=t.doc,r,i;if(t!==u.DOM&&n===document){var o=u.DOM.addedStyles;if(o=o||[],o[e])return;o[e]=!0,u.DOM.addedStyles=o}i=n.getElementById("mceDefaultStyles"),i||(i=n.createElement("style"),i.id="mceDefaultStyles",i.type="text/css",r=n.getElementsByTagName("head")[0],r.firstChild?r.insertBefore(i,r.firstChild):r.appendChild(i)),i.styleSheet?i.styleSheet.cssText+=e:i.appendChild(n.createTextNode(e))},loadCSS:function(e){var t=this,n=t.doc,r;return t!==u.DOM&&n===document?void u.DOM.loadCSS(e):(e||(e=""),r=n.getElementsByTagName("head")[0],void d(e.split(","),function(e){var i;t.files[e]||(t.files[e]=!0,i=t.create("link",{rel:"stylesheet",href:e}),v&&n.documentMode&&n.recalc&&(i.onload=function(){n.recalc&&n.recalc(),i.onload=null}),r.appendChild(i))}))},addClass:function(e,t){return this.run(e,function(e){var n;return t?this.hasClass(e,t)?e.className:(n=this.removeClass(e,t),e.className=n=(""!==n?n+" ":"")+t,n):0})},removeClass:function(e,t){var n=this,r;return n.run(e,function(e){var i;return n.hasClass(e,t)?(r||(r=new RegExp("(^|\\s+)"+t+"(\\s+|$)","g")),i=e.className.replace(r," "),i=h(" "!=i?i:""),e.className=i,i||(e.removeAttribute("class"),e.removeAttribute("className")),i):e.className})},hasClass:function(e,t){return e=this.get(e),e&&t?-1!==(" "+e.className+" ").indexOf(" "+t+" "):!1},toggleClass:function(e,n,r){r=r===t?!this.hasClass(e,n):r,this.hasClass(e,n)!==r&&(r?this.addClass(e,n):this.removeClass(e,n))},show:function(e){return this.setStyle(e,"display","block")},hide:function(e){return this.setStyle(e,"display","none")},isHidden:function(e){return e=this.get(e),!e||"none"==e.style.display||"none"==this.getStyle(e,"display")},uniqueId:function(e){return(e?e:"mce_")+this.counter++},setHTML:function(e,t){var n=this;return n.run(e,function(e){if(v){for(;e.firstChild;)e.removeChild(e.firstChild);try{e.innerHTML="<br />"+t,e.removeChild(e.firstChild)}catch(r){var i=n.create("div");i.innerHTML="<br />"+t,d(p(i.childNodes),function(t,n){n&&e.canHaveHTML&&e.appendChild(t)})}}else e.innerHTML=t;return t})},getOuterHTML:function(e){var t,n=this;return(e=n.get(e))?1===e.nodeType&&n.hasOuterHTML?e.outerHTML:(t=(e.ownerDocument||n.doc).createElement("body"),t.appendChild(e.cloneNode(!0)),t.innerHTML):null},setOuterHTML:function(e,t,n){var r=this;return r.run(e,function(e){function i(){var i,o;for(o=n.createElement("body"),o.innerHTML=t,i=o.lastChild;i;)r.insertAfter(i.cloneNode(!0),e),i=i.previousSibling;r.remove(e)}if(1==e.nodeType)if(n=n||e.ownerDocument||r.doc,v)try{1==e.nodeType&&r.hasOuterHTML?e.outerHTML=t:i()}catch(o){i()}else i()})},decode:a.decode,encode:a.encodeAllRaw,insertAfter:function(e,t){return t=this.get(t),this.run(e,function(e){var n,r;return n=t.parentNode,r=t.nextSibling,r?n.insertBefore(e,r):n.appendChild(e),e})},replace:function(e,t,n){var r=this;return r.run(t,function(t){return f(t,"array")&&(e=e.cloneNode(!0)),n&&d(p(t.childNodes),function(t){e.appendChild(t)}),t.parentNode.replaceChild(e,t)})},rename:function(e,t){var n=this,r;return e.nodeName!=t.toUpperCase()&&(r=n.create(t),d(n.getAttribs(e),function(t){n.setAttrib(r,t.nodeName,n.getAttrib(e,t.nodeName))}),n.replace(r,e,1)),r||e},findCommonAncestor:function(e,t){for(var n=e,r;n;){for(r=t;r&&n!=r;)r=r.parentNode;if(n==r)break;n=n.parentNode}return!n&&e.ownerDocument?e.ownerDocument.documentElement:n},toHex:function(e){return this.styles.toHex(l.trim(e))},run:function(e,t,n){var r=this,i;return"string"==typeof e&&(e=r.get(e)),e?(n=n||this,e.nodeType||!e.length&&0!==e.length?t.call(n,e):(i=[],d(e,function(e,o){e&&("string"==typeof e&&(e=r.get(e)),i.push(t.call(n,e,o)))}),i)):!1},getAttribs:function(e){var t;if(e=this.get(e),!e)return[];if(v){if(t=[],"OBJECT"==e.nodeName)return e.attributes;"OPTION"===e.nodeName&&this.getAttrib(e,"selected")&&t.push({specified:1,nodeName:"selected"});var n=/<\/?[\w:\-]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=[\w\-]+|>/gi;return e.cloneNode(!1).outerHTML.replace(n,"").replace(/[\w:\-]+/gi,function(e){t.push({specified:1,nodeName:e})}),t}return e.attributes},isEmpty:function(e,t){var n=this,r,o,a,s,l,c=0;if(e=e.firstChild){s=new i(e,e.parentNode),t=t||n.schema?n.schema.getNonEmptyElements():null;do{if(a=e.nodeType,1===a){if(e.getAttribute("data-mce-bogus"))continue;if(l=e.nodeName.toLowerCase(),t&&t[l]){if("br"===l){c++;continue}return!1}for(o=n.getAttribs(e),r=o.length;r--;)if(l=o[r].nodeName,"name"===l||"data-mce-bookmark"===l)return!1}if(8==a)return!1;if(3===a&&!b.test(e.nodeValue))return!1}while(e=s.next())}return 1>=c},createRng:function(){var e=this.doc;return e.createRange?e.createRange():new o(this)},nodeIndex:function(e,t){var n=0,r,i;if(e)for(r=e.nodeType,e=e.previousSibling;e;e=e.previousSibling)i=e.nodeType,(!t||3!=i||i!=r&&e.nodeValue.length)&&(n++,r=i);return n},split:function(e,t,n){function r(e){function t(e){var t=e.previousSibling&&"SPAN"==e.previousSibling.nodeName,n=e.nextSibling&&"SPAN"==e.nextSibling.nodeName;return t&&n}var n,o=e.childNodes,a=e.nodeType;if(1!=a||"bookmark"!=e.getAttribute("data-mce-type")){for(n=o.length-1;n>=0;n--)r(o[n]);if(9!=a){if(3==a&&e.nodeValue.length>0){var s=h(e.nodeValue).length;if(!i.isBlock(e.parentNode)||s>0||0===s&&t(e))return}else if(1==a&&(o=e.childNodes,1==o.length&&o[0]&&1==o[0].nodeType&&"bookmark"==o[0].getAttribute("data-mce-type")&&e.parentNode.insertBefore(o[0],e),o.length||/^(br|hr|input|img)$/i.test(e.nodeName)))return;i.remove(e)}return e}}var i=this,o=i.createRng(),a,s,l;return e&&t?(o.setStart(e.parentNode,i.nodeIndex(e)),o.setEnd(t.parentNode,i.nodeIndex(t)),a=o.extractContents(),o=i.createRng(),o.setStart(t.parentNode,i.nodeIndex(t)+1),o.setEnd(e.parentNode,i.nodeIndex(e)+1),s=o.extractContents(),l=e.parentNode,l.insertBefore(r(a),e),n?l.replaceChild(n,t):l.insertBefore(t,e),l.insertBefore(r(s),e),i.remove(e),n||t):void 0},bind:function(e,t,n,r){var i=this;if(l.isArray(e)){for(var o=e.length;o--;)e[o]=i.bind(e[o],t,n,r);return e}return!i.settings.collect||e!==i.doc&&e!==i.win||i.boundEvents.push([e,t,n,r]),i.events.bind(e,t,n,r||i)},unbind:function(e,t,n){var r=this,i;if(l.isArray(e)){for(i=e.length;i--;)e[i]=r.unbind(e[i],t,n);return e}if(r.boundEvents&&(e===r.doc||e===r.win))for(i=r.boundEvents.length;i--;){var o=r.boundEvents[i];e!=o[0]||t&&t!=o[1]||n&&n!=o[2]||this.events.unbind(o[0],o[1],o[2])}return this.events.unbind(e,t,n)},fire:function(e,t,n){return this.events.fire(e,t,n)},getContentEditable:function(e){var t;return e&&1==e.nodeType?(t=e.getAttribute("data-mce-contenteditable"),t&&"inherit"!==t?t:"inherit"!==e.contentEditable?e.contentEditable:null):null},getContentEditableParent:function(e){for(var t=this.getRoot(),n=null;e&&e!==t&&(n=this.getContentEditable(e),null===n);e=e.parentNode);return n},destroy:function(){var t=this;if(t.boundEvents){for(var n=t.boundEvents.length;n--;){var r=t.boundEvents[n];
+this.events.unbind(r[0],r[1],r[2])}t.boundEvents=null}e.setDocument&&e.setDocument(),t.win=t.doc=t.root=t.events=t.frag=null},isChildOf:function(e,t){for(;e;){if(t===e)return!0;e=e.parentNode}return!1},dumpRng:function(e){return"startContainer: "+e.startContainer.nodeName+", startOffset: "+e.startOffset+", endContainer: "+e.endContainer.nodeName+", endOffset: "+e.endOffset},_findSib:function(e,t,n){var r=this,i=t;if(e)for("string"==typeof i&&(i=function(e){return r.is(e,t)}),e=e[n];e;e=e[n])if(i(e))return e;return null}},u.DOM=new u(document),u}),r(b,[y,p],function(e,t){function n(){function e(e,t){function n(){o.remove(s),a&&(a.onreadystatechange=a.onload=a=null),t()}function i(){"undefined"!=typeof console&&console.log&&console.log("Failed to load: "+e)}var o=r,a,s;s=o.uniqueId(),a=document.createElement("script"),a.id=s,a.type="text/javascript",a.src=e,"onreadystatechange"in a?a.onreadystatechange=function(){/loaded|complete/.test(a.readyState)&&n()}:a.onload=n,a.onerror=i,(document.getElementsByTagName("head")[0]||document.body).appendChild(a)}var t=0,n=1,a=2,s={},l=[],c={},u=[],d=0,f;this.isDone=function(e){return s[e]==a},this.markDone=function(e){s[e]=a},this.add=this.load=function(e,n,r){var i=s[e];i==f&&(l.push(e),s[e]=t),n&&(c[e]||(c[e]=[]),c[e].push({func:n,scope:r||this}))},this.loadQueue=function(e,t){this.loadScripts(l,e,t)},this.loadScripts=function(t,r,l){function p(e){i(c[e],function(e){e.func.call(e.scope)}),c[e]=f}var h;u.push({func:r,scope:l||this}),(h=function(){var r=o(t);t.length=0,i(r,function(t){return s[t]==a?void p(t):void(s[t]!=n&&(s[t]=n,d++,e(t,function(){s[t]=a,d--,p(t),h()})))}),d||(i(u,function(e){e.func.call(e.scope)}),u.length=0)})()}}var r=e.DOM,i=t.each,o=t.grep;return n.ScriptLoader=new n,n}),r(C,[b,p],function(e,n){function r(){var e=this;e.items=[],e.urls={},e.lookup={}}var i=n.each;return r.prototype={get:function(e){return this.lookup[e]?this.lookup[e].instance:t},dependencies:function(e){var t;return this.lookup[e]&&(t=this.lookup[e].dependencies),t||[]},requireLangPack:function(t,n){var i=r.language;if(i&&r.languageLoad!==!1){if(n)if(n=","+n+",",-1!=n.indexOf(","+i.substr(0,2)+","))i=i.substr(0,2);else if(-1==n.indexOf(","+i+","))return;e.ScriptLoader.add(this.urls[t]+"/langs/"+i+".js")}},add:function(e,t,n){return this.items.push(t),this.lookup[e]={instance:t,dependencies:n},t},createUrl:function(e,t){return"object"==typeof t?t:{prefix:e.prefix,resource:t,suffix:e.suffix}},addComponents:function(t,n){var r=this.urls[t];i(n,function(t){e.ScriptLoader.add(r+"/"+t)})},load:function(n,o,a,s){function l(){var r=c.dependencies(n);i(r,function(e){var n=c.createUrl(o,e);c.load(n.resource,n,t,t)}),a&&a.call(s?s:e)}var c=this,u=o;c.urls[n]||("object"==typeof o&&(u=o.prefix+o.resource+o.suffix),0!==u.indexOf("/")&&-1==u.indexOf("://")&&(u=r.baseURL+"/"+u),c.urls[n]=u.substring(0,u.lastIndexOf("/")),c.lookup[n]?l():e.ScriptLoader.add(u,l,s))}},r.PluginManager=new r,r.ThemeManager=new r,r}),r(x,[],function(){function e(e,t,n){var r,i,o=n?"lastChild":"firstChild",a=n?"prev":"next";if(e[o])return e[o];if(e!==t){if(r=e[a])return r;for(i=e.parent;i&&i!==t;i=i.parent)if(r=i[a])return r}}function t(e,t){this.name=e,this.type=t,1===t&&(this.attributes=[],this.attributes.map={})}var n=/^[ \t\r\n]*$/,r={"#text":3,"#comment":8,"#cdata":4,"#pi":7,"#doctype":10,"#document-fragment":11};return t.prototype={replace:function(e){var t=this;return e.parent&&e.remove(),t.insert(e,t),t.remove(),t},attr:function(e,t){var n=this,r,i,o;if("string"!=typeof e){for(i in e)n.attr(i,e[i]);return n}if(r=n.attributes){if(t!==o){if(null===t){if(e in r.map)for(delete r.map[e],i=r.length;i--;)if(r[i].name===e)return r=r.splice(i,1),n;return n}if(e in r.map){for(i=r.length;i--;)if(r[i].name===e){r[i].value=t;break}}else r.push({name:e,value:t});return r.map[e]=t,n}return r.map[e]}},clone:function(){var e=this,n=new t(e.name,e.type),r,i,o,a,s;if(o=e.attributes){for(s=[],s.map={},r=0,i=o.length;i>r;r++)a=o[r],"id"!==a.name&&(s[s.length]={name:a.name,value:a.value},s.map[a.name]=a.value);n.attributes=s}return n.value=e.value,n.shortEnded=e.shortEnded,n},wrap:function(e){var t=this;return t.parent.insert(e,t),e.append(t),t},unwrap:function(){var e=this,t,n;for(t=e.firstChild;t;)n=t.next,e.insert(t,e,!0),t=n;e.remove()},remove:function(){var e=this,t=e.parent,n=e.next,r=e.prev;return t&&(t.firstChild===e?(t.firstChild=n,n&&(n.prev=null)):r.next=n,t.lastChild===e?(t.lastChild=r,r&&(r.next=null)):n.prev=r,e.parent=e.next=e.prev=null),e},append:function(e){var t=this,n;return e.parent&&e.remove(),n=t.lastChild,n?(n.next=e,e.prev=n,t.lastChild=e):t.lastChild=t.firstChild=e,e.parent=t,e},insert:function(e,t,n){var r;return e.parent&&e.remove(),r=t.parent||this,n?(t===r.firstChild?r.firstChild=e:t.prev.next=e,e.prev=t.prev,e.next=t,t.prev=e):(t===r.lastChild?r.lastChild=e:t.next.prev=e,e.next=t.next,e.prev=t,t.next=e),e.parent=r,e},getAll:function(t){var n=this,r,i=[];for(r=n.firstChild;r;r=e(r,n))r.name===t&&i.push(r);return i},empty:function(){var t=this,n,r,i;if(t.firstChild){for(n=[],i=t.firstChild;i;i=e(i,t))n.push(i);for(r=n.length;r--;)i=n[r],i.parent=i.firstChild=i.lastChild=i.next=i.prev=null}return t.firstChild=t.lastChild=null,t},isEmpty:function(t){var r=this,i=r.firstChild,o,a;if(i)do{if(1===i.type){if(i.attributes.map["data-mce-bogus"])continue;if(t[i.name])return!1;for(o=i.attributes.length;o--;)if(a=i.attributes[o].name,"name"===a||0===a.indexOf("data-mce-"))return!1}if(8===i.type)return!1;if(3===i.type&&!n.test(i.value))return!1}while(i=e(i,r));return!0},walk:function(t){return e(this,null,t)}},t.create=function(e,n){var i,o;if(i=new t(e,r[e]||1),n)for(o in n)i.attr(o,n[o]);return i},t}),r(w,[p],function(e){function t(e,t){return e?e.split(t||" "):[]}function n(e){function n(e,n,r){function i(e){var t={},n,r;for(n=0,r=e.length;r>n;n++)t[e[n]]={};return t}var o,l,c,u=arguments;for(r=r||[],n=n||"","string"==typeof r&&(r=t(r)),l=3;l<u.length;l++)"string"==typeof u[l]&&(u[l]=t(u[l])),r.push.apply(r,u[l]);for(e=t(e),o=e.length;o--;)c=[].concat(s,t(n)),a[e[o]]={attributes:i(c),attributesOrder:c,children:i(r)}}function i(e,n){var r,i,o,s;for(e=t(e),r=e.length,n=t(n);r--;)for(i=a[e[r]],o=0,s=n.length;s>o;o++)i.attributes[n[o]]={},i.attributesOrder.push(n[o])}var a={},s,l,c,u,d,f;return r[e]?r[e]:(s=t("id accesskey class dir lang style tabindex title"),l=t("address blockquote div dl fieldset form h1 h2 h3 h4 h5 h6 hr menu ol p pre table ul"),c=t("a abbr b bdo br button cite code del dfn em embed i iframe img input ins kbd label map noscript object q s samp script select small span strong sub sup textarea u var #text #comment"),"html4"!=e&&(s.push.apply(s,t("contenteditable contextmenu draggable dropzone hidden spellcheck translate")),l.push.apply(l,t("article aside details dialog figure header footer hgroup section nav")),c.push.apply(c,t("audio canvas command datalist mark meter output progress time wbr video ruby bdi keygen"))),"html5-strict"!=e&&(s.push("xml:lang"),f=t("acronym applet basefont big font strike tt"),c.push.apply(c,f),o(f,function(e){n(e,"",c)}),d=t("center dir isindex noframes"),l.push.apply(l,d),u=[].concat(l,c),o(d,function(e){n(e,"",u)})),u=u||[].concat(l,c),n("html","manifest","head body"),n("head","","base command link meta noscript script style title"),n("title hr noscript br"),n("base","href target"),n("link","href rel media hreflang type sizes hreflang"),n("meta","name http-equiv content charset"),n("style","media type scoped"),n("script","src async defer type charset"),n("body","onafterprint onbeforeprint onbeforeunload onblur onerror onfocus onhashchange onload onmessage onoffline ononline onpagehide onpageshow onpopstate onresize onscroll onstorage onunload",u),n("address dt dd div caption","",u),n("h1 h2 h3 h4 h5 h6 pre p abbr code var samp kbd sub sup i b u bdo span legend em strong small s cite dfn","",c),n("blockquote","cite",u),n("ol","reversed start type","li"),n("ul","","li"),n("li","value",u),n("dl","","dt dd"),n("a","href target rel media hreflang type",c),n("q","cite",c),n("ins del","cite datetime",u),n("img","src alt usemap ismap width height"),n("iframe","src name width height",u),n("embed","src type width height"),n("object","data type typemustmatch name usemap form width height",u,"param"),n("param","name value"),n("map","name",u,"area"),n("area","alt coords shape href target rel media hreflang type"),n("table","border","caption colgroup thead tfoot tbody tr"+("html4"==e?" col":"")),n("colgroup","span","col"),n("col","span"),n("tbody thead tfoot","","tr"),n("tr","","td th"),n("td","colspan rowspan headers",u),n("th","colspan rowspan headers scope abbr",u),n("form","accept-charset action autocomplete enctype method name novalidate target",u),n("fieldset","disabled form name",u,"legend"),n("label","form for",c),n("input","accept alt autocomplete checked dirname disabled form formaction formenctype formmethod formnovalidate formtarget height list max maxlength min multiple name pattern readonly required size src step type value width"),n("button","disabled form formaction formenctype formmethod formnovalidate formtarget name type value","html4"==e?u:c),n("select","disabled form multiple name required size","option optgroup"),n("optgroup","disabled label","option"),n("option","disabled label selected value"),n("textarea","cols dirname disabled form maxlength name readonly required rows wrap"),n("menu","type label",u,"li"),n("noscript","",u),"html4"!=e&&(n("wbr"),n("ruby","",c,"rt rp"),n("figcaption","",u),n("mark rt rp summary bdi","",c),n("canvas","width height",u),n("video","src crossorigin poster preload autoplay mediagroup loop muted controls width height buffered",u,"track source"),n("audio","src crossorigin preload autoplay mediagroup loop muted controls buffered volume",u,"track source"),n("source","src type media"),n("track","kind src srclang label default"),n("datalist","",c,"option"),n("article section nav aside header footer","",u),n("hgroup","","h1 h2 h3 h4 h5 h6"),n("figure","",u,"figcaption"),n("time","datetime",c),n("dialog","open",u),n("command","type label icon disabled checked radiogroup command"),n("output","for form name",c),n("progress","value max",c),n("meter","value min max low high optimum",c),n("details","open",u,"summary"),n("keygen","autofocus challenge disabled form keytype name")),"html5-strict"!=e&&(i("script","language xml:space"),i("style","xml:space"),i("object","declare classid codebase codetype archive standby align border hspace vspace"),i("param","valuetype type"),i("a","charset name rev shape coords"),i("br","clear"),i("applet","codebase archive code object alt name width height align hspace vspace"),i("img","name longdesc align border hspace vspace"),i("iframe","longdesc frameborder marginwidth marginheight scrolling align"),i("font basefont","size color face"),i("input","usemap align"),i("select","onchange"),i("textarea"),i("h1 h2 h3 h4 h5 h6 div p legend caption","align"),i("ul","type compact"),i("li","type"),i("ol dl menu dir","compact"),i("pre","width xml:space"),i("hr","align noshade size width"),i("isindex","prompt"),i("table","summary width frame rules cellspacing cellpadding align bgcolor"),i("col","width align char charoff valign"),i("colgroup","width align char charoff valign"),i("thead","align char charoff valign"),i("tr","align char charoff valign bgcolor"),i("th","axis align char charoff valign nowrap bgcolor width height"),i("form","accept"),i("td","abbr axis scope align char charoff valign nowrap bgcolor width height"),i("tfoot","align char charoff valign"),i("tbody","align char charoff valign"),i("area","nohref"),i("body","background bgcolor text link vlink alink")),"html4"!=e&&(i("input button select textarea","autofocus"),i("input textarea","placeholder"),i("a","download"),i("link script img","crossorigin"),i("iframe","sandbox seamless allowfullscreen")),o(t("a form meter progress dfn"),function(e){a[e]&&delete a[e].children[e]}),delete a.caption.children.table,r[e]=a,a)}var r={},i=e.makeMap,o=e.each,a=e.extend,s=e.explode,l=e.inArray;return function(e){function c(t,n,o){var s=e[t];return s?s=i(s,/[, ]/,i(s.toUpperCase(),/[, ]/)):(s=r[t],s||(s=i(n," ",i(n.toUpperCase()," ")),s=a(s,o),r[t]=s)),s}function u(e){return new RegExp("^"+e.replace(/([?+*])/g,".$1")+"$")}function d(e){var n,r,o,a,s,c,d,f,p,h,m,g,y,C,x,w,_,N,E,S=/^([#+\-])?([^\[!\/]+)(?:\/([^\[!]+))?(?:(!?)\[([^\]]+)\])?$/,k=/^([!\-])?(\w+::\w+|[^=:<]+)?(?:([=:<])(.*))?$/,T=/[*?+]/;if(e)for(e=t(e,","),v["@"]&&(w=v["@"].attributes,_=v["@"].attributesOrder),n=0,r=e.length;r>n;n++)if(s=S.exec(e[n])){if(C=s[1],p=s[2],x=s[3],f=s[5],g={},y=[],c={attributes:g,attributesOrder:y},"#"===C&&(c.paddEmpty=!0),"-"===C&&(c.removeEmpty=!0),"!"===s[4]&&(c.removeEmptyAttrs=!0),w){for(N in w)g[N]=w[N];y.push.apply(y,_)}if(f)for(f=t(f,"|"),o=0,a=f.length;a>o;o++)if(s=k.exec(f[o])){if(d={},m=s[1],h=s[2].replace(/::/g,":"),C=s[3],E=s[4],"!"===m&&(c.attributesRequired=c.attributesRequired||[],c.attributesRequired.push(h),d.required=!0),"-"===m){delete g[h],y.splice(l(y,h),1);continue}C&&("="===C&&(c.attributesDefault=c.attributesDefault||[],c.attributesDefault.push({name:h,value:E}),d.defaultValue=E),":"===C&&(c.attributesForced=c.attributesForced||[],c.attributesForced.push({name:h,value:E}),d.forcedValue=E),"<"===C&&(d.validValues=i(E,"?"))),T.test(h)?(c.attributePatterns=c.attributePatterns||[],d.pattern=u(h),c.attributePatterns.push(d)):(g[h]||y.push(h),g[h]=d)}w||"@"!=p||(w=g,_=y),x&&(c.outputName=p,v[x]=c),T.test(p)?(c.pattern=u(p),b.push(c)):v[p]=c}}function f(e){v={},b=[],d(e),o(x,function(e,t){y[t]=e.children})}function p(e){var n=/^(~)?(.+)$/;e&&(r.text_block_elements=r.block_elements=null,o(t(e,","),function(e){var t=n.exec(e),r="~"===t[1],i=r?"span":"div",s=t[2];if(y[s]=y[i],R[s]=i,r||(S[s.toUpperCase()]={},S[s]={}),!v[s]){var l=v[i];l=a({},l),delete l.removeEmptyAttrs,delete l.removeEmpty,v[s]=l}o(y,function(e,t){e[i]&&(y[t]=e=a({},y[t]),e[s]=e[i])})}))}function h(e){var n=/^([+\-]?)(\w+)\[([^\]]+)\]$/;e&&o(t(e,","),function(e){var r=n.exec(e),i,s;r&&(s=r[1],i=s?y[r[2]]:y[r[2]]={"#comment":{}},i=y[r[2]],o(t(r[3],"|"),function(e){"-"===s?(y[r[2]]=i=a({},y[r[2]]),delete i[e]):i[e]={}}))})}function m(e){var t=v[e],n;if(t)return t;for(n=b.length;n--;)if(t=b[n],t.pattern.test(e))return t}var g=this,v={},y={},b=[],C,x,w,_,N,E,S,k,T,R={},A={};e=e||{},x=n(e.schema),e.verify_html===!1&&(e.valid_elements="*[*]"),e.valid_styles&&(C={},o(e.valid_styles,function(e,t){C[t]=s(e)})),w=c("whitespace_elements","pre script noscript style textarea video audio iframe object"),_=c("self_closing_elements","colgroup dd dt li option p td tfoot th thead tr"),N=c("short_ended_elements","area base basefont br col frame hr img input isindex link meta param embed source wbr track"),E=c("boolean_attributes","checked compact declare defer disabled ismap multiple nohref noresize noshade nowrap readonly selected autoplay loop controls"),k=c("non_empty_elements","td th iframe video audio object script",N),T=c("text_block_elements","h1 h2 h3 h4 h5 h6 p div address pre form blockquote center dir fieldset header footer article section hgroup aside nav figure"),S=c("block_elements","hr table tbody thead tfoot th tr td li ol ul caption dl dt dd noscript menu isindex option datalist select optgroup",T),o((e.special||"script noscript style textarea").split(" "),function(e){A[e]=new RegExp("</"+e+"[^>]*>","gi")}),e.valid_elements?f(e.valid_elements):(o(x,function(e,t){v[t]={attributes:e.attributes,attributesOrder:e.attributesOrder},y[t]=e.children}),"html5"!=e.schema&&o(t("strong/b em/i"),function(e){e=t(e,"/"),v[e[1]].outputName=e[0]}),v.img.attributesDefault=[{name:"alt",value:""}],o(t("ol ul sub sup blockquote span font a table tbody tr strong em b i"),function(e){v[e]&&(v[e].removeEmpty=!0)}),o(t("p h1 h2 h3 h4 h5 h6 th td pre div address caption"),function(e){v[e].paddEmpty=!0}),o(t("span"),function(e){v[e].removeEmptyAttrs=!0})),p(e.custom_elements),h(e.valid_children),d(e.extended_valid_elements),h("+ol[ul|ol],+ul[ul|ol]"),e.invalid_elements&&o(s(e.invalid_elements),function(e){v[e]&&delete v[e]}),m("span")||d("span[!data-mce-type|*]"),g.children=y,g.styles=C,g.getBoolAttrs=function(){return E},g.getBlockElements=function(){return S},g.getTextBlockElements=function(){return T},g.getShortEndedElements=function(){return N},g.getSelfClosingElements=function(){return _},g.getNonEmptyElements=function(){return k},g.getWhiteSpaceElements=function(){return w},g.getSpecialElements=function(){return A},g.isValidChild=function(e,t){var n=y[e];return!(!n||!n[t])},g.isValid=function(e,t){var n,r,i=m(e);if(i){if(!t)return!0;if(i.attributes[t])return!0;if(n=i.attributePatterns)for(r=n.length;r--;)if(n[r].pattern.test(e))return!0}return!1},g.getElementRule=m,g.getCustomElements=function(){return R},g.addValidElements=d,g.setValidElements=f,g.addCustomElements=p,g.addValidChildren=h,g.elements=v}}),r(_,[w,m,p],function(e,t,n){var r=n.each;return function(i,o){function a(){}var s=this;i=i||{},s.schema=o=o||new e,i.fix_self_closing!==!1&&(i.fix_self_closing=!0),r("comment cdata text start end pi doctype".split(" "),function(e){e&&(s[e]=i[e]||a)}),s.parse=function(e){function r(e){var t,n;for(t=f.length;t--&&f[t].name!==e;);if(t>=0){for(n=f.length-1;n>=t;n--)e=f[n],e.valid&&s.end(e.name);f.length=t}}function a(e,t,n,r,o){var a,s,l=/[\s\u0000-\u001F]+/g;if(t=t.toLowerCase(),n=t in C?t:F(n||r||o||""),w&&!v&&0!==t.indexOf("data-")){if(a=k[t],!a&&T){for(s=T.length;s--&&(a=T[s],!a.pattern.test(t)););-1===s&&(a=null)}if(!a)return;if(a.validValues&&!(n in a.validValues))return}if(W[t]&&!i.allow_script_urls){var c=n.replace(l,"");try{c=decodeURIComponent(c)}catch(u){c=unescape(c)}if(V.test(c))return;if(!i.allow_html_data_urls&&U.test(c)&&!/^data:image\//i.test(c))return}p.map[t]=n,p.push({name:t,value:n})}var s=this,l,c=0,u,d,f=[],p,h,m,g,v,y,b,C,x,w,_,N,E,S,k,T,R,A,B,D,L,M,H,P,O,I=0,F=t.decode,z,W=n.makeMap("src,href,data,background,formaction,poster"),V=/((java|vb)script|mhtml):/i,U=/^data:/i;for(M=new RegExp("<(?:(?:!--([\\w\\W]*?)-->)|(?:!\\[CDATA\\[([\\w\\W]*?)\\]\\]>)|(?:!DOCTYPE([\\w\\W]*?)>)|(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)|(?:\\/([^>]+)>)|(?:([A-Za-z0-9\\-\\:\\.]+)((?:\\s+[^\"'>]+(?:(?:\"[^\"]*\")|(?:'[^']*')|[^>]*))*|\\/|\\s+)>))","g"),H=/([\w:\-]+)(?:\s*=\s*(?:(?:\"((?:[^\"])*)\")|(?:\'((?:[^\'])*)\')|([^>\s]+)))?/g,b=o.getShortEndedElements(),L=i.self_closing_elements||o.getSelfClosingElements(),C=o.getBoolAttrs(),w=i.validate,y=i.remove_internals,z=i.fix_self_closing,P=o.getSpecialElements();l=M.exec(e);){if(c<l.index&&s.text(F(e.substr(c,l.index-c))),u=l[6])u=u.toLowerCase(),":"===u.charAt(0)&&(u=u.substr(1)),r(u);else if(u=l[7]){if(u=u.toLowerCase(),":"===u.charAt(0)&&(u=u.substr(1)),x=u in b,z&&L[u]&&f.length>0&&f[f.length-1].name===u&&r(u),!w||(_=o.getElementRule(u))){if(N=!0,w&&(k=_.attributes,T=_.attributePatterns),(S=l[8])?(v=-1!==S.indexOf("data-mce-type"),v&&y&&(N=!1),p=[],p.map={},S.replace(H,a)):(p=[],p.map={}),w&&!v){if(R=_.attributesRequired,A=_.attributesDefault,B=_.attributesForced,D=_.removeEmptyAttrs,D&&!p.length&&(N=!1),B)for(h=B.length;h--;)E=B[h],g=E.name,O=E.value,"{$uid}"===O&&(O="mce_"+I++),p.map[g]=O,p.push({name:g,value:O});if(A)for(h=A.length;h--;)E=A[h],g=E.name,g in p.map||(O=E.value,"{$uid}"===O&&(O="mce_"+I++),p.map[g]=O,p.push({name:g,value:O}));if(R){for(h=R.length;h--&&!(R[h]in p.map););-1===h&&(N=!1)}p.map["data-mce-bogus"]&&(N=!1)}N&&s.start(u,p,x)}else N=!1;if(d=P[u]){d.lastIndex=c=l.index+l[0].length,(l=d.exec(e))?(N&&(m=e.substr(c,l.index-c)),c=l.index+l[0].length):(m=e.substr(c),c=e.length),N&&(m.length>0&&s.text(m,!0),s.end(u)),M.lastIndex=c;continue}x||(S&&S.indexOf("/")==S.length-1?N&&s.end(u):f.push({name:u,valid:N}))}else(u=l[1])?(">"===u.charAt(0)&&(u=" "+u),i.allow_conditional_comments||"[if"!==u.substr(0,3)||(u=" "+u),s.comment(u)):(u=l[2])?s.cdata(u):(u=l[3])?s.doctype(u):(u=l[4])&&s.pi(u,l[5]);c=l.index+l[0].length}for(c<e.length&&s.text(F(e.substr(c))),h=f.length-1;h>=0;h--)u=f[h],u.valid&&s.end(u.name)}}}),r(N,[x,w,_,p],function(e,t,n,r){var i=r.makeMap,o=r.each,a=r.explode,s=r.extend;return function(r,l){function c(t){var n,r,o,a,s,c,d,f,p,h,m,g,v,y;for(m=i("tr,td,th,tbody,thead,tfoot,table"),h=l.getNonEmptyElements(),g=l.getTextBlockElements(),n=0;n<t.length;n++)if(r=t[n],r.parent&&!r.fixed)if(g[r.name]&&"li"==r.parent.name){for(v=r.next;v&&g[v.name];)v.name="li",v.fixed=!0,r.parent.insert(v,r.parent),v=v.next;r.unwrap(r)}else{for(a=[r],o=r.parent;o&&!l.isValidChild(o.name,r.name)&&!m[o.name];o=o.parent)a.push(o);if(o&&a.length>1){for(a.reverse(),s=c=u.filterNode(a[0].clone()),p=0;p<a.length-1;p++){for(l.isValidChild(c.name,a[p].name)?(d=u.filterNode(a[p].clone()),c.append(d)):d=c,f=a[p].firstChild;f&&f!=a[p+1];)y=f.next,d.append(f),f=y;c=d}s.isEmpty(h)?o.insert(r,a[0],!0):(o.insert(s,a[0],!0),o.insert(r,s)),o=a[0],(o.isEmpty(h)||o.firstChild===o.lastChild&&"br"===o.firstChild.name)&&o.empty().remove()}else if(r.parent){if("li"===r.name){if(v=r.prev,v&&("ul"===v.name||"ul"===v.name)){v.append(r);continue}if(v=r.next,v&&("ul"===v.name||"ul"===v.name)){v.insert(r,v.firstChild,!0);continue}r.wrap(u.filterNode(new e("ul",1)));continue}l.isValidChild(r.parent.name,"div")&&l.isValidChild("div",r.name)?r.wrap(u.filterNode(new e("div",1))):"style"===r.name||"script"===r.name?r.empty().remove():r.unwrap()}}}var u=this,d={},f=[],p={},h={};r=r||{},r.validate="validate"in r?r.validate:!0,r.root_name=r.root_name||"body",u.schema=l=l||new t,u.filterNode=function(e){var t,n,r;n in d&&(r=p[n],r?r.push(e):p[n]=[e]),t=f.length;for(;t--;)n=f[t].name,n in e.attributes.map&&(r=h[n],r?r.push(e):h[n]=[e]);return e},u.addNodeFilter=function(e,t){o(a(e),function(e){var n=d[e];n||(d[e]=n=[]),n.push(t)})},u.addAttributeFilter=function(e,t){o(a(e),function(e){var n;for(n=0;n<f.length;n++)if(f[n].name===e)return void f[n].callbacks.push(t);f.push({name:e,callbacks:[t]})})},u.parse=function(t,o){function a(){function e(e){e&&(t=e.firstChild,t&&3==t.type&&(t.value=t.value.replace(R,"")),t=e.lastChild,t&&3==t.type&&(t.value=t.value.replace(D,"")))}var t=y.firstChild,n,i;if(l.isValidChild(y.name,I.toLowerCase())){for(;t;)n=t.next,3==t.type||1==t.type&&"p"!==t.name&&!T[t.name]&&!t.attr("data-mce-type")?i?i.append(t):(i=u(I,1),i.attr(r.forced_root_block_attrs),y.insert(i,t),i.append(t)):(e(i),i=null),t=n;e(i)}}function u(t,n){var r=new e(t,n),i;return t in d&&(i=p[t],i?i.push(r):p[t]=[r]),r}function m(e){var t,n,r;for(t=e.prev;t&&3===t.type;)n=t.value.replace(D,""),n.length>0?(t.value=n,t=t.prev):(r=t.prev,t.remove(),t=r)}function g(e){var t,n={};for(t in e)"li"!==t&&"p"!=t&&(n[t]=e[t]);return n}var v,y,b,C,x,w,_,N,E,S,k,T,R,A=[],B,D,L,M,H,P,O,I;if(o=o||{},p={},h={},T=s(i("script,style,head,html,body,title,meta,param"),l.getBlockElements()),O=l.getNonEmptyElements(),P=l.children,k=r.validate,I="forced_root_block"in o?o.forced_root_block:r.forced_root_block,H=l.getWhiteSpaceElements(),R=/^[ \t\r\n]+/,D=/[ \t\r\n]+$/,L=/[ \t\r\n]+/g,M=/^[ \t\r\n]+$/,v=new n({validate:k,allow_script_urls:r.allow_script_urls,allow_conditional_comments:r.allow_conditional_comments,self_closing_elements:g(l.getSelfClosingElements()),cdata:function(e){b.append(u("#cdata",4)).value=e},text:function(e,t){var n;B||(e=e.replace(L," "),b.lastChild&&T[b.lastChild.name]&&(e=e.replace(R,""))),0!==e.length&&(n=u("#text",3),n.raw=!!t,b.append(n).value=e)},comment:function(e){b.append(u("#comment",8)).value=e},pi:function(e,t){b.append(u(e,7)).value=t,m(b)},doctype:function(e){var t;t=b.append(u("#doctype",10)),t.value=e,m(b)},start:function(e,t,n){var r,i,o,a,s;if(o=k?l.getElementRule(e):{}){for(r=u(o.outputName||e,1),r.attributes=t,r.shortEnded=n,b.append(r),s=P[b.name],s&&P[r.name]&&!s[r.name]&&A.push(r),i=f.length;i--;)a=f[i].name,a in t.map&&(E=h[a],E?E.push(r):h[a]=[r]);T[e]&&m(r),n||(b=r),!B&&H[e]&&(B=!0)}},end:function(t){var n,r,i,o,a;if(r=k?l.getElementRule(t):{}){if(T[t]&&!B){if(n=b.firstChild,n&&3===n.type)if(i=n.value.replace(R,""),i.length>0)n.value=i,n=n.next;else for(o=n.next,n.remove(),n=o;n&&3===n.type;)i=n.value,o=n.next,(0===i.length||M.test(i))&&(n.remove(),n=o),n=o;if(n=b.lastChild,n&&3===n.type)if(i=n.value.replace(D,""),i.length>0)n.value=i,n=n.prev;else for(o=n.prev,n.remove(),n=o;n&&3===n.type;)i=n.value,o=n.prev,(0===i.length||M.test(i))&&(n.remove(),n=o),n=o}if(B&&H[t]&&(B=!1),(r.removeEmpty||r.paddEmpty)&&b.isEmpty(O))if(r.paddEmpty)b.empty().append(new e("#text","3")).value="\xa0";else if(!b.attributes.map.name&&!b.attributes.map.id)return a=b.parent,b.empty().remove(),void(b=a);b=b.parent}}},l),y=b=new e(o.context||r.root_name,11),v.parse(t),k&&A.length&&(o.context?o.invalid=!0:c(A)),I&&("body"==y.name||o.isRootContent)&&a(),!o.invalid){for(S in p){for(E=d[S],C=p[S],_=C.length;_--;)C[_].parent||C.splice(_,1);for(x=0,w=E.length;w>x;x++)E[x](C,S,o)}for(x=0,w=f.length;w>x;x++)if(E=f[x],E.name in h){for(C=h[E.name],_=C.length;_--;)C[_].parent||C.splice(_,1);for(_=0,N=E.callbacks.length;N>_;_++)E.callbacks[_](C,E.name,o)}}return y},r.remove_trailing_brs&&u.addNodeFilter("br",function(t){var n,r=t.length,i,o=s({},l.getBlockElements()),a=l.getNonEmptyElements(),c,u,d,f,p,h;for(o.body=1,n=0;r>n;n++)if(i=t[n],c=i.parent,o[i.parent.name]&&i===c.lastChild){for(d=i.prev;d;){if(f=d.name,"span"!==f||"bookmark"!==d.attr("data-mce-type")){if("br"!==f)break;if("br"===f){i=null;break}}d=d.prev}i&&(i.remove(),c.isEmpty(a)&&(p=l.getElementRule(c.name),p&&(p.removeEmpty?c.remove():p.paddEmpty&&(c.empty().append(new e("#text",3)).value="\xa0"))))}else{for(u=i;c&&c.firstChild===u&&c.lastChild===u&&(u=c,!o[c.name]);)c=c.parent;u===c&&(h=new e("#text",3),h.value="\xa0",i.replace(h))}}),r.allow_html_in_named_anchor||u.addAttributeFilter("id,name",function(e){for(var t=e.length,n,r,i,o;t--;)if(o=e[t],"a"===o.name&&o.firstChild&&!o.attr("href")){i=o.parent,n=o.lastChild;do r=n.prev,i.insert(n,o),n=r;while(n)}})}}),r(E,[m,p],function(e,t){var n=t.makeMap;return function(t){var r=[],i,o,a,s,l;return t=t||{},i=t.indent,o=n(t.indent_before||""),a=n(t.indent_after||""),s=e.getEncodeFunc(t.entity_encoding||"raw",t.entities),l="html"==t.element_format,{start:function(e,t,n){var c,u,d,f;if(i&&o[e]&&r.length>0&&(f=r[r.length-1],f.length>0&&"\n"!==f&&r.push("\n")),r.push("<",e),t)for(c=0,u=t.length;u>c;c++)d=t[c],r.push(" ",d.name,'="',s(d.value,!0),'"');r[r.length]=!n||l?">":" />",n&&i&&a[e]&&r.length>0&&(f=r[r.length-1],f.length>0&&"\n"!==f&&r.push("\n"))},end:function(e){var t;r.push("</",e,">"),i&&a[e]&&r.length>0&&(t=r[r.length-1],t.length>0&&"\n"!==t&&r.push("\n"))},text:function(e,t){e.length>0&&(r[r.length]=t?e:s(e))},cdata:function(e){r.push("<![CDATA[",e,"]]>")},comment:function(e){r.push("<!--",e,"-->")},pi:function(e,t){t?r.push("<?",e," ",t,"?>"):r.push("<?",e,"?>"),i&&r.push("\n")},doctype:function(e){r.push("<!DOCTYPE",e,">",i?"\n":"")},reset:function(){r.length=0},getContent:function(){return r.join("").replace(/\n$/,"")}}}}),r(S,[E,w],function(e,t){return function(n,r){var i=this,o=new e(n);n=n||{},n.validate="validate"in n?n.validate:!0,i.schema=r=r||new t,i.writer=o,i.serialize=function(e){function t(e){var n=i[e.type],s,l,c,u,d,f,p,h,m;if(n)n(e);else{if(s=e.name,l=e.shortEnded,c=e.attributes,a&&c&&c.length>1){for(f=[],f.map={},m=r.getElementRule(e.name),p=0,h=m.attributesOrder.length;h>p;p++)u=m.attributesOrder[p],u in c.map&&(d=c.map[u],f.map[u]=d,f.push({name:u,value:d}));for(p=0,h=c.length;h>p;p++)u=c[p].name,u in f.map||(d=c.map[u],f.map[u]=d,f.push({name:u,value:d}));c=f}if(o.start(e.name,c,l),!l){if(e=e.firstChild)do t(e);while(e=e.next);o.end(s)}}}var i,a;return a=n.validate,i={3:function(e){o.text(e.value,e.raw)},8:function(e){o.comment(e.value)},7:function(e){o.pi(e.name,e.value)},10:function(e){o.doctype(e.value)},4:function(e){o.cdata(e.value)},11:function(e){if(e=e.firstChild)do t(e);while(e=e.next)}},o.reset(),1!=e.type||n.inner?i[11](e):t(e),o.getContent()}}}),r(k,[y,N,m,S,x,w,g,p],function(e,t,n,r,i,o,a,s){var l=s.each,c=s.trim,u=e.DOM;return function(e,i){var s,d,f;return i&&(s=i.dom,d=i.schema),s=s||u,d=d||new o(e),e.entity_encoding=e.entity_encoding||"named",e.remove_trailing_brs="remove_trailing_brs"in e?e.remove_trailing_brs:!0,f=new t(e,d),f.addAttributeFilter("data-mce-tabindex",function(e,t){for(var n=e.length,r;n--;)r=e[n],r.attr("tabindex",r.attributes.map["data-mce-tabindex"]),r.attr(t,null)}),f.addAttributeFilter("src,href,style",function(t,n){for(var r=t.length,i,o,a="data-mce-"+n,l=e.url_converter,c=e.url_converter_scope,u;r--;)i=t[r],o=i.attributes.map[a],o!==u?(i.attr(n,o.length>0?o:null),i.attr(a,null)):(o=i.attributes.map[n],"style"===n?o=s.serializeStyle(s.parseStyle(o),i.name):l&&(o=l.call(c,o,n,i.name)),i.attr(n,o.length>0?o:null))}),f.addAttributeFilter("class",function(e){for(var t=e.length,n,r;t--;)n=e[t],r=n.attr("class").replace(/(?:^|\s)mce-item-\w+(?!\S)/g,""),n.attr("class",r.length>0?r:null)}),f.addAttributeFilter("data-mce-type",function(e,t,n){for(var r=e.length,i;r--;)i=e[r],"bookmark"!==i.attributes.map["data-mce-type"]||n.cleanup||i.remove()}),f.addAttributeFilter("data-mce-expando",function(e,t){for(var n=e.length;n--;)e[n].attr(t,null)}),f.addNodeFilter("noscript",function(e){for(var t=e.length,r;t--;)r=e[t].firstChild,r&&(r.value=n.decode(r.value))}),f.addNodeFilter("script,style",function(e,t){function n(e){return e.replace(/(<!--\[CDATA\[|\]\]-->)/g,"\n").replace(/^[\r\n]*|[\r\n]*$/g,"").replace(/^\s*((<!--)?(\s*\/\/)?\s*<!\[CDATA\[|(<!--\s*)?\/\*\s*<!\[CDATA\[\s*\*\/|(\/\/)?\s*<!--|\/\*\s*<!--\s*\*\/)\s*[\r\n]*/gi,"").replace(/\s*(\/\*\s*\]\]>\s*\*\/(-->)?|\s*\/\/\s*\]\]>(-->)?|\/\/\s*(-->)?|\]\]>|\/\*\s*-->\s*\*\/|\s*-->\s*)\s*$/g,"")}for(var r=e.length,i,o;r--;)if(i=e[r],o=i.firstChild?i.firstChild.value:"","script"===t){var a=(i.attr("type")||"text/javascript").replace(/^mce\-/,"");i.attr("type","text/javascript"===a?null:a),o.length>0&&(i.firstChild.value="// <![CDATA[\n"+n(o)+"\n// ]]>")}else o.length>0&&(i.firstChild.value="<!--\n"+n(o)+"\n-->")}),f.addNodeFilter("#comment",function(e){for(var t=e.length,n;t--;)n=e[t],0===n.value.indexOf("[CDATA[")?(n.name="#cdata",n.type=4,n.value=n.value.replace(/^\[CDATA\[|\]\]$/g,"")):0===n.value.indexOf("mce:protected ")&&(n.name="#text",n.type=3,n.raw=!0,n.value=unescape(n.value).substr(14))}),f.addNodeFilter("xml:namespace,input",function(e,t){for(var n=e.length,r;n--;)r=e[n],7===r.type?r.remove():1===r.type&&("input"!==t||"type"in r.attributes.map||r.attr("type","text"))}),e.fix_list_elements&&f.addNodeFilter("ul,ol",function(e){for(var t=e.length,n,r;t--;)n=e[t],r=n.parent,("ul"===r.name||"ol"===r.name)&&n.prev&&"li"===n.prev.name&&n.prev.append(n)}),f.addAttributeFilter("data-mce-src,data-mce-href,data-mce-style,data-mce-selected",function(e,t){for(var n=e.length;n--;)e[n].attr(t,null)}),{schema:d,addNodeFilter:f.addNodeFilter,addAttributeFilter:f.addAttributeFilter,serialize:function(t,n){var i=this,o,u,p,h,m;return a.ie&&s.select("script,style,select,map").length>0?(m=t.innerHTML,t=t.cloneNode(!1),s.setHTML(t,m)):t=t.cloneNode(!0),o=t.ownerDocument.implementation,o.createHTMLDocument&&(u=o.createHTMLDocument(""),l("BODY"==t.nodeName?t.childNodes:[t],function(e){u.body.appendChild(u.importNode(e,!0))}),t="BODY"!=t.nodeName?u.body.firstChild:u.body,p=s.doc,s.doc=u),n=n||{},n.format=n.format||"html",n.selection&&(n.forced_root_block=""),n.no_events||(n.node=t,i.onPreProcess(n)),h=new r(e,d),n.content=h.serialize(f.parse(c(n.getInner?t.innerHTML:s.getOuterHTML(t)),n)),n.cleanup||(n.content=n.content.replace(/\uFEFF/g,"")),n.no_events||i.onPostProcess(n),p&&(s.doc=p),n.node=null,n.content},addRules:function(e){d.addValidElements(e)},setRules:function(e){d.setValidElements(e)},onPreProcess:function(e){i&&i.fire("PreProcess",e)},onPostProcess:function(e){i&&i.fire("PostProcess",e)}}}}),r(T,[],function(){function e(e){function t(t,n){var r,i=0,o,a,s,l,c,u,d=-1,f;if(r=t.duplicate(),r.collapse(n),f=r.parentElement(),f.ownerDocument===e.dom.doc){for(;"false"===f.contentEditable;)f=f.parentNode;
+if(!f.hasChildNodes())return{node:f,inside:1};for(s=f.children,o=s.length-1;o>=i;)if(u=Math.floor((i+o)/2),l=s[u],r.moveToElementText(l),d=r.compareEndPoints(n?"StartToStart":"EndToEnd",t),d>0)o=u-1;else{if(!(0>d))return{node:l};i=u+1}if(0>d)for(l?r.collapse(!1):(r.moveToElementText(f),r.collapse(!0),l=f,a=!0),c=0;0!==r.compareEndPoints(n?"StartToStart":"StartToEnd",t)&&0!==r.move("character",1)&&f==r.parentElement();)c++;else for(r.collapse(!0),c=0;0!==r.compareEndPoints(n?"StartToStart":"StartToEnd",t)&&0!==r.move("character",-1)&&f==r.parentElement();)c++;return{node:l,position:d,offset:c,inside:a}}}function n(){function n(e){var n=t(o,e),r,i,s=0,l,c,u;if(r=n.node,i=n.offset,n.inside&&!r.hasChildNodes())return void a[e?"setStart":"setEnd"](r,0);if(i===c)return void a[e?"setStartBefore":"setEndAfter"](r);if(n.position<0){if(l=n.inside?r.firstChild:r.nextSibling,!l)return void a[e?"setStartAfter":"setEndAfter"](r);if(!i)return void(3==l.nodeType?a[e?"setStart":"setEnd"](l,0):a[e?"setStartBefore":"setEndBefore"](l));for(;l;){if(u=l.nodeValue,s+=u.length,s>=i){r=l,s-=i,s=u.length-s;break}l=l.nextSibling}}else{if(l=r.previousSibling,!l)return a[e?"setStartBefore":"setEndBefore"](r);if(!i)return void(3==r.nodeType?a[e?"setStart":"setEnd"](l,r.nodeValue.length):a[e?"setStartAfter":"setEndAfter"](l));for(;l;){if(s+=l.nodeValue.length,s>=i){r=l,s-=i;break}l=l.previousSibling}}a[e?"setStart":"setEnd"](r,s)}var o=e.getRng(),a=i.createRng(),s,l,c,u,d;if(s=o.item?o.item(0):o.parentElement(),s.ownerDocument!=i.doc)return a;if(l=e.isCollapsed(),o.item)return a.setStart(s.parentNode,i.nodeIndex(s)),a.setEnd(a.startContainer,a.startOffset+1),a;try{n(!0),l||n()}catch(f){if(-2147024809!=f.number)throw f;d=r.getBookmark(2),c=o.duplicate(),c.collapse(!0),s=c.parentElement(),l||(c=o.duplicate(),c.collapse(!1),u=c.parentElement(),u.innerHTML=u.innerHTML),s.innerHTML=s.innerHTML,r.moveToBookmark(d),o=e.getRng(),n(!0),l||n()}return a}var r=this,i=e.dom,o=!1;this.getBookmark=function(n){function r(e){var t,n,r,o,a=[];for(t=e.parentNode,n=i.getRoot().parentNode;t!=n&&9!==t.nodeType;){for(r=t.children,o=r.length;o--;)if(e===r[o]){a.push(o);break}e=t,t=t.parentNode}return a}function o(e){var n;return n=t(a,e),n?{position:n.position,offset:n.offset,indexes:r(n.node),inside:n.inside}:void 0}var a=e.getRng(),s={};return 2===n&&(a.item?s.start={ctrl:!0,indexes:r(a.item(0))}:(s.start=o(!0),e.isCollapsed()||(s.end=o()))),s},this.moveToBookmark=function(e){function t(e){var t,n,r,o;for(t=i.getRoot(),n=e.length-1;n>=0;n--)o=t.children,r=e[n],r<=o.length-1&&(t=o[r]);return t}function n(n){var i=e[n?"start":"end"],a,s,l,c;i&&(a=i.position>0,s=o.createTextRange(),s.moveToElementText(t(i.indexes)),c=i.offset,c!==l?(s.collapse(i.inside||a),s.moveStart("character",a?-c:c)):s.collapse(n),r.setEndPoint(n?"StartToStart":"EndToStart",s),n&&r.collapse(!0))}var r,o=i.doc.body;e.start&&(e.start.ctrl?(r=o.createControlRange(),r.addElement(t(e.start.indexes)),r.select()):(r=o.createTextRange(),n(!0),n(),r.select()))},this.addRange=function(t){function n(e){var t,n,a,d,h;a=i.create("a"),t=e?s:c,n=e?l:u,d=r.duplicate(),(t==f||t==f.documentElement)&&(t=p,n=0),3==t.nodeType?(t.parentNode.insertBefore(a,t),d.moveToElementText(a),d.moveStart("character",n),i.remove(a),r.setEndPoint(e?"StartToStart":"EndToEnd",d)):(h=t.childNodes,h.length?(n>=h.length?i.insertAfter(a,h[h.length-1]):t.insertBefore(a,h[n]),d.moveToElementText(a)):t.canHaveHTML&&(t.innerHTML="<span>&#xFEFF;</span>",a=t.firstChild,d.moveToElementText(a),d.collapse(o)),r.setEndPoint(e?"StartToStart":"EndToEnd",d),i.remove(a))}var r,a,s,l,c,u,d,f=e.dom.doc,p=f.body,h,m;if(s=t.startContainer,l=t.startOffset,c=t.endContainer,u=t.endOffset,r=p.createTextRange(),s==c&&1==s.nodeType){if(l==u&&!s.hasChildNodes()){if(s.canHaveHTML)return d=s.previousSibling,d&&!d.hasChildNodes()&&i.isBlock(d)?d.innerHTML="&#xFEFF;":d=null,s.innerHTML="<span>&#xFEFF;</span><span>&#xFEFF;</span>",r.moveToElementText(s.lastChild),r.select(),i.doc.selection.clear(),s.innerHTML="",void(d&&(d.innerHTML=""));l=i.nodeIndex(s),s=s.parentNode}if(l==u-1)try{if(m=s.childNodes[l],a=p.createControlRange(),a.addElement(m),a.select(),h=e.getRng(),h.item&&m===h.item(0))return}catch(g){}}n(!0),n(),r.select()},this.getRangeAt=n}return e}),r(R,[g],function(e){return{BACKSPACE:8,DELETE:46,DOWN:40,ENTER:13,LEFT:37,RIGHT:39,SPACEBAR:32,TAB:9,UP:38,modifierPressed:function(e){return e.shiftKey||e.ctrlKey||e.altKey},metaKeyPressed:function(t){return(e.mac?t.metaKey:t.ctrlKey)&&!t.altKey}}}),r(A,[R,p,g],function(e,t,n){return function(r,i){function o(e){var t=i.settings.object_resizing;return t===!1||n.iOS?!1:("string"!=typeof t&&(t="table,img,div"),"false"===e.getAttribute("data-mce-resize")?!1:i.dom.is(e,t))}function a(t){var n,r;n=t.screenX-k,r=t.screenY-T,H=n*E[2]+B,P=r*E[3]+D,H=5>H?5:H,P=5>P?5:P,(e.modifierPressed(t)||"IMG"==w.nodeName&&E[2]*E[3]!==0)&&(H=Math.round(P/L),P=Math.round(H*L)),C.setStyles(_,{width:H,height:P}),E[2]<0&&_.clientWidth<=H&&C.setStyle(_,"left",R+(B-H)),E[3]<0&&_.clientHeight<=P&&C.setStyle(_,"top",A+(D-P)),M||(i.fire("ObjectResizeStart",{target:w,width:B,height:D}),M=!0)}function s(){function e(e,t){t&&(w.style[e]||!i.schema.isValid(w.nodeName.toLowerCase(),e)?C.setStyle(w,e,t):C.setAttrib(w,e,t))}M=!1,e("width",H),e("height",P),C.unbind(O,"mousemove",a),C.unbind(O,"mouseup",s),I!=O&&(C.unbind(I,"mousemove",a),C.unbind(I,"mouseup",s)),C.remove(_),F&&"TABLE"!=w.nodeName||l(w),i.fire("ObjectResized",{target:w,width:H,height:P}),i.nodeChanged()}function l(e,t,r){var l,u,d,f,p,h=i.getBody();g(),l=C.getPos(e,h),R=l.x,A=l.y,p=e.getBoundingClientRect(),u=p.width||p.right-p.left,d=p.height||p.bottom-p.top,w!=e&&(m(),w=e,H=P=0),f=i.fire("ObjectSelected",{target:e}),o(e)&&!f.isDefaultPrevented()?x(N,function(e,o){function l(t){k=t.screenX,T=t.screenY,B=w.clientWidth,D=w.clientHeight,L=D/B,E=e,_=w.cloneNode(!0),C.addClass(_,"mce-clonedresizable"),_.contentEditable=!1,_.unSelectabe=!0,C.setStyles(_,{left:R,top:A,margin:0}),_.removeAttribute("data-mce-selected"),i.getBody().appendChild(_),C.bind(O,"mousemove",a),C.bind(O,"mouseup",s),I!=O&&(C.bind(I,"mousemove",a),C.bind(I,"mouseup",s))}var c,f;return t?void(o==t&&l(r)):(c=C.get("mceResizeHandle"+o),c?C.show(c):(f=i.getBody(),c=C.add(f,"div",{id:"mceResizeHandle"+o,"data-mce-bogus":!0,"class":"mce-resizehandle",unselectable:!0,style:"cursor:"+o+"-resize; margin:0; padding:0"}),n.ie&&(c.contentEditable=!1)),e.elm||(C.bind(c,"mousedown",function(e){e.stopImmediatePropagation(),e.preventDefault(),l(e)}),e.elm=c),void C.setStyles(c,{left:u*e[0]+R-c.offsetWidth/2,top:d*e[1]+A-c.offsetHeight/2}))}):c(),w.setAttribute("data-mce-selected","1")}function c(){var e,t;g(),w&&w.removeAttribute("data-mce-selected");for(e in N)t=C.get("mceResizeHandle"+e),t&&(C.unbind(t),C.remove(t))}function u(e){function t(e,t){if(e)do if(e===t)return!0;while(e=e.parentNode)}var n;return x(C.select("img[data-mce-selected],hr[data-mce-selected]"),function(e){e.removeAttribute("data-mce-selected")}),n="mousedown"==e.type?e.target:r.getNode(),n=C.getParent(n,F?"table":"table,img,hr"),t(n,i.getBody())&&(v(),t(r.getStart(),n)&&t(r.getEnd(),n)&&(!F||n!=r.getStart()&&"IMG"!==r.getStart().nodeName))?void l(n):void c()}function d(e,t,n){e&&e.attachEvent&&e.attachEvent("on"+t,n)}function f(e,t,n){e&&e.detachEvent&&e.detachEvent("on"+t,n)}function p(e){var t=e.srcElement,n,r,o,a,s,c,u;n=t.getBoundingClientRect(),c=S.clientX-n.left,u=S.clientY-n.top;for(r in N)if(o=N[r],a=t.offsetWidth*o[0],s=t.offsetHeight*o[1],Math.abs(a-c)<8&&Math.abs(s-u)<8){E=o;break}M=!0,i.getDoc().selection.empty(),l(t,r,S)}function h(e){var t=e.srcElement;if(t!=w){if(m(),0===t.id.indexOf("mceResizeHandle"))return void(e.returnValue=!1);("IMG"==t.nodeName||"TABLE"==t.nodeName)&&(c(),w=t,d(t,"resizestart",p))}}function m(){f(w,"resizestart",p)}function g(){for(var e in N){var t=N[e];t.elm&&(C.unbind(t.elm),delete t.elm)}}function v(){try{i.getDoc().execCommand("enableObjectResizing",!1,!1)}catch(e){}}function y(e){var t;if(F){t=O.body.createControlRange();try{return t.addElement(e),t.select(),!0}catch(n){}}}function b(){w=_=null,F&&(m(),f(i.getBody(),"controlselect",h))}var C=i.dom,x=t.each,w,_,N,E,S,k,T,R,A,B,D,L,M,H,P,O=i.getDoc(),I=document,F=n.ie&&n.ie<11;N={n:[.5,0,0,-1],e:[1,.5,1,0],s:[.5,1,0,1],w:[0,.5,-1,0],nw:[0,0,-1,-1],ne:[1,0,1,-1],se:[1,1,1,1],sw:[0,1,-1,1]};var z=".mce-content-body";return i.contentStyles.push(z+" div.mce-resizehandle {position: absolute;border: 1px solid black;background: #FFF;width: 5px;height: 5px;z-index: 10000}"+z+" .mce-resizehandle:hover {background: #000}"+z+" img[data-mce-selected], hr[data-mce-selected] {outline: 1px solid black;resize: none}"+z+" .mce-clonedresizable {position: absolute;"+(n.gecko?"":"outline: 1px dashed black;")+"opacity: .5;filter: alpha(opacity=50);z-index: 10000}"),i.on("init",function(){F?(i.on("ObjectResized",function(e){"TABLE"!=e.target.nodeName&&(c(),y(e.target))}),d(i.getBody(),"controlselect",h),i.on("mousedown",function(e){S=e})):(v(),n.ie>=11&&(i.on("mouseup",function(e){var t=e.target.nodeName;/^(TABLE|IMG|HR)$/.test(t)&&(i.selection.select(e.target,"TABLE"==t),i.nodeChanged())}),i.dom.bind(i.getBody(),"mscontrolselect",function(e){/^(TABLE|IMG|HR)$/.test(e.target.nodeName)&&(e.preventDefault(),"IMG"==e.target.tagName&&window.setTimeout(function(){i.selection.select(e.target)},0))}))),i.on("nodechange mousedown mouseup ResizeEditor",u),i.on("keydown keyup",function(e){w&&"TABLE"==w.nodeName&&u(e)}),i.on("hide",c)}),i.on("remove",g),{isResizable:o,showResizeRect:l,hideResizeRect:c,updateResizeRect:u,controlSelect:y,destroy:b}}}),r(B,[p,f],function(e,t){function n(e){this.walk=function(t,n){function i(e){var t;return t=e[0],3===t.nodeType&&t===l&&c>=t.nodeValue.length&&e.splice(0,1),t=e[e.length-1],0===d&&e.length>0&&t===u&&3===t.nodeType&&e.splice(e.length-1,1),e}function o(e,t,n){for(var r=[];e&&e!=n;e=e[t])r.push(e);return r}function a(e,t){do{if(e.parentNode==t)return e;e=e.parentNode}while(e)}function s(e,t,r){var a=r?"nextSibling":"previousSibling";for(m=e,g=m.parentNode;m&&m!=t;m=g)g=m.parentNode,v=o(m==e?m:m[a],a),v.length&&(r||v.reverse(),n(i(v)))}var l=t.startContainer,c=t.startOffset,u=t.endContainer,d=t.endOffset,f,p,h,m,g,v,y;if(y=e.select("td.mce-item-selected,th.mce-item-selected"),y.length>0)return void r(y,function(e){n([e])});if(1==l.nodeType&&l.hasChildNodes()&&(l=l.childNodes[c]),1==u.nodeType&&u.hasChildNodes()&&(u=u.childNodes[Math.min(d-1,u.childNodes.length-1)]),l==u)return n(i([l]));for(f=e.findCommonAncestor(l,u),m=l;m;m=m.parentNode){if(m===u)return s(l,f,!0);if(m===f)break}for(m=u;m;m=m.parentNode){if(m===l)return s(u,f);if(m===f)break}p=a(l,f)||l,h=a(u,f)||u,s(l,p,!0),v=o(p==l?p:p.nextSibling,"nextSibling",h==u?h.nextSibling:h),v.length&&n(i(v)),s(u,h)},this.split=function(e){function t(e,t){return e.splitText(t)}var n=e.startContainer,r=e.startOffset,i=e.endContainer,o=e.endOffset;return n==i&&3==n.nodeType?r>0&&r<n.nodeValue.length&&(i=t(n,r),n=i.previousSibling,o>r?(o-=r,n=i=t(i,o).previousSibling,o=i.nodeValue.length,r=0):o=0):(3==n.nodeType&&r>0&&r<n.nodeValue.length&&(n=t(n,r),r=0),3==i.nodeType&&o>0&&o<i.nodeValue.length&&(i=t(i,o).previousSibling,o=i.nodeValue.length)),{startContainer:n,startOffset:r,endContainer:i,endOffset:o}},this.normalize=function(n){function r(r){function a(n,r){for(var i=new t(n,e.getParent(n.parentNode,e.isBlock)||f);n=i[r?"prev":"next"]();)if("BR"===n.nodeName)return!0}function s(e,t){return e.previousSibling&&e.previousSibling.nodeName==t}function l(n,r){var a,s,l;if(r=r||c,l=e.getParent(r.parentNode,e.isBlock)||f,n&&"BR"==r.nodeName&&g&&e.isEmpty(l))return c=r.parentNode,u=e.nodeIndex(r),void(i=!0);for(a=new t(r,l);p=a[n?"prev":"next"]();){if("false"===e.getContentEditableParent(p))return;if(3===p.nodeType&&p.nodeValue.length>0)return c=p,u=n?p.nodeValue.length:0,void(i=!0);if(e.isBlock(p)||h[p.nodeName.toLowerCase()])return;s=p}o&&s&&(c=s,i=!0,u=0)}var c,u,d,f=e.getRoot(),p,h,m,g;if(c=n[(r?"start":"end")+"Container"],u=n[(r?"start":"end")+"Offset"],g=1==c.nodeType&&u===c.childNodes.length,h=e.schema.getNonEmptyElements(),m=r,1==c.nodeType&&u>c.childNodes.length-1&&(m=!1),9===c.nodeType&&(c=e.getRoot(),u=0),c===f){if(m&&(p=c.childNodes[u>0?u-1:0],p&&(h[p.nodeName]||"TABLE"==p.nodeName)))return;if(c.hasChildNodes()&&(u=Math.min(!m&&u>0?u-1:u,c.childNodes.length-1),c=c.childNodes[u],u=0,c.hasChildNodes()&&!/TABLE/.test(c.nodeName))){p=c,d=new t(c,f);do{if(3===p.nodeType&&p.nodeValue.length>0){u=m?0:p.nodeValue.length,c=p,i=!0;break}if(h[p.nodeName.toLowerCase()]){u=e.nodeIndex(p),c=p.parentNode,"IMG"!=p.nodeName||m||u++,i=!0;break}}while(p=m?d.next():d.prev())}}o&&(3===c.nodeType&&0===u&&l(!0),1===c.nodeType&&(p=c.childNodes[u],p||(p=c.childNodes[u-1]),!p||"BR"!==p.nodeName||s(p,"A")||a(p)||a(p,!0)||l(!0,p))),m&&!o&&3===c.nodeType&&u===c.nodeValue.length&&l(!1),i&&n["set"+(r?"Start":"End")](c,u)}var i,o;return o=n.collapsed,r(!0),o||r(),i&&o&&n.collapse(!0),i}}var r=e.each;return n.compareRanges=function(e,t){if(e&&t){if(!e.item&&!e.duplicate)return e.startContainer==t.startContainer&&e.startOffset==t.startOffset;if(e.item&&t.item&&e.item(0)===t.item(0))return!0;if(e.isEqual&&t.isEqual&&t.isEqual(e))return!0}return!1},n}),r(D,[f,T,A,B,g,p],function(e,n,r,i,o,a){function s(e,t,i,o){var a=this;a.dom=e,a.win=t,a.serializer=i,a.editor=o,a.controlSelection=new r(a,o),a.win.getSelection||(a.tridentSel=new n(a))}var l=a.each,c=a.grep,u=a.trim,d=o.ie,f=o.opera;return s.prototype={setCursorLocation:function(e,t){var n=this,r=n.dom.createRng();e?(r.setStart(e,t),r.setEnd(e,t),n.setRng(r),n.collapse(!1)):(n._moveEndPoint(r,n.editor.getBody(),!0),n.setRng(r))},getContent:function(e){var n=this,r=n.getRng(),i=n.dom.create("body"),o=n.getSel(),a,s,l;return e=e||{},a=s="",e.get=!0,e.format=e.format||"html",e.selection=!0,n.editor.fire("BeforeGetContent",e),"text"==e.format?n.isCollapsed()?"":r.text||(o.toString?o.toString():""):(r.cloneContents?(l=r.cloneContents(),l&&i.appendChild(l)):r.item!==t||r.htmlText!==t?(i.innerHTML="<br>"+(r.item?r.item(0).outerHTML:r.htmlText),i.removeChild(i.firstChild)):i.innerHTML=r.toString(),/^\s/.test(i.innerHTML)&&(a=" "),/\s+$/.test(i.innerHTML)&&(s=" "),e.getInner=!0,e.content=n.isCollapsed()?"":a+n.serializer.serialize(i,e)+s,n.editor.fire("GetContent",e),e.content)},setContent:function(e,t){var n=this,r=n.getRng(),i,o=n.win.document,a,s;if(t=t||{format:"html"},t.set=!0,t.selection=!0,e=t.content=e,t.no_events||n.editor.fire("BeforeSetContent",t),e=t.content,r.insertNode){e+='<span id="__caret">_</span>',r.startContainer==o&&r.endContainer==o?o.body.innerHTML=e:(r.deleteContents(),0===o.body.childNodes.length?o.body.innerHTML=e:r.createContextualFragment?r.insertNode(r.createContextualFragment(e)):(a=o.createDocumentFragment(),s=o.createElement("div"),a.appendChild(s),s.outerHTML=e,r.insertNode(a))),i=n.dom.get("__caret"),r=o.createRange(),r.setStartBefore(i),r.setEndBefore(i),n.setRng(r),n.dom.remove("__caret");try{n.setRng(r)}catch(l){}}else r.item&&(o.execCommand("Delete",!1,null),r=n.getRng()),/^\s+/.test(e)?(r.pasteHTML('<span id="__mce_tmp">_</span>'+e),n.dom.remove("__mce_tmp")):r.pasteHTML(e);t.no_events||n.editor.fire("SetContent",t)},getStart:function(){var e=this,t=e.getRng(),n,r,i,o;if(t.duplicate||t.item){if(t.item)return t.item(0);for(i=t.duplicate(),i.collapse(1),n=i.parentElement(),n.ownerDocument!==e.dom.doc&&(n=e.dom.getRoot()),r=o=t.parentElement();o=o.parentNode;)if(o==n){n=r;break}return n}return n=t.startContainer,1==n.nodeType&&n.hasChildNodes()&&(n=n.childNodes[Math.min(n.childNodes.length-1,t.startOffset)]),n&&3==n.nodeType?n.parentNode:n},getEnd:function(){var e=this,t=e.getRng(),n,r;return t.duplicate||t.item?t.item?t.item(0):(t=t.duplicate(),t.collapse(0),n=t.parentElement(),n.ownerDocument!==e.dom.doc&&(n=e.dom.getRoot()),n&&"BODY"==n.nodeName?n.lastChild||n:n):(n=t.endContainer,r=t.endOffset,1==n.nodeType&&n.hasChildNodes()&&(n=n.childNodes[r>0?r-1:r]),n&&3==n.nodeType?n.parentNode:n)},getBookmark:function(e,t){function n(e,t){var n=0;return l(a.select(e),function(e,r){e==t&&(n=r)}),n}function r(e){function t(t){var n,r,i,o=t?"start":"end";n=e[o+"Container"],r=e[o+"Offset"],1==n.nodeType&&"TR"==n.nodeName&&(i=n.childNodes,n=i[Math.min(t?r:r-1,i.length-1)],n&&(r=t?0:n.childNodes.length,e["set"+(t?"Start":"End")](n,r)))}return t(!0),t(),e}function i(){function e(e,n){var i=e[n?"startContainer":"endContainer"],a=e[n?"startOffset":"endOffset"],s=[],l,c,u=0;if(3==i.nodeType){if(t)for(l=i.previousSibling;l&&3==l.nodeType;l=l.previousSibling)a+=l.nodeValue.length;s.push(a)}else c=i.childNodes,a>=c.length&&c.length&&(u=1,a=Math.max(0,c.length-1)),s.push(o.dom.nodeIndex(c[a],t)+u);for(;i&&i!=r;i=i.parentNode)s.push(o.dom.nodeIndex(i,t));return s}var n=o.getRng(!0),r=a.getRoot(),i={};return i.start=e(n,!0),o.isCollapsed()||(i.end=e(n)),i}var o=this,a=o.dom,s,c,u,d,f,p,h="&#xFEFF;",m;if(2==e)return p=o.getNode(),f=p?p.nodeName:null,"IMG"==f?{name:f,index:n(f,p)}:o.tridentSel?o.tridentSel.getBookmark(e):i();if(e)return{rng:o.getRng()};if(s=o.getRng(),u=a.uniqueId(),d=o.isCollapsed(),m="overflow:hidden;line-height:0px",s.duplicate||s.item){if(s.item)return p=s.item(0),f=p.nodeName,{name:f,index:n(f,p)};c=s.duplicate();try{s.collapse(),s.pasteHTML('<span data-mce-type="bookmark" id="'+u+'_start" style="'+m+'">'+h+"</span>"),d||(c.collapse(!1),s.moveToElementText(c.parentElement()),0===s.compareEndPoints("StartToEnd",c)&&c.move("character",-1),c.pasteHTML('<span data-mce-type="bookmark" id="'+u+'_end" style="'+m+'">'+h+"</span>"))}catch(g){return null}}else{if(p=o.getNode(),f=p.nodeName,"IMG"==f)return{name:f,index:n(f,p)};c=r(s.cloneRange()),d||(c.collapse(!1),c.insertNode(a.create("span",{"data-mce-type":"bookmark",id:u+"_end",style:m},h))),s=r(s),s.collapse(!0),s.insertNode(a.create("span",{"data-mce-type":"bookmark",id:u+"_start",style:m},h))}return o.moveToBookmark({id:u,keep:1}),{id:u}},moveToBookmark:function(e){function t(t){var n=e[t?"start":"end"],r,i,o,l;if(n){for(o=n[0],i=s,r=n.length-1;r>=1;r--){if(l=i.childNodes,n[r]>l.length-1)return;i=l[n[r]]}3===i.nodeType&&(o=Math.min(n[0],i.nodeValue.length)),1===i.nodeType&&(o=Math.min(n[0],i.childNodes.length)),t?a.setStart(i,o):a.setEnd(i,o)}return!0}function n(t){var n=o.get(e.id+"_"+t),r,i,a,s,d=e.keep;if(n&&(r=n.parentNode,"start"==t?(d?(r=n.firstChild,i=1):i=o.nodeIndex(n),u=p=r,h=m=i):(d?(r=n.firstChild,i=1):i=o.nodeIndex(n),p=r,m=i),!d)){for(s=n.previousSibling,a=n.nextSibling,l(c(n.childNodes),function(e){3==e.nodeType&&(e.nodeValue=e.nodeValue.replace(/\uFEFF/g,""))});n=o.get(e.id+"_"+t);)o.remove(n,1);s&&a&&s.nodeType==a.nodeType&&3==s.nodeType&&!f&&(i=s.nodeValue.length,s.appendData(a.nodeValue),o.remove(a),"start"==t?(u=p=s,h=m=i):(p=s,m=i))}}function r(e){return!o.isBlock(e)||e.innerHTML||d||(e.innerHTML='<br data-mce-bogus="1" />'),e}var i=this,o=i.dom,a,s,u,p,h,m;if(e)if(e.start){if(a=o.createRng(),s=o.getRoot(),i.tridentSel)return i.tridentSel.moveToBookmark(e);t(!0)&&t()&&i.setRng(a)}else e.id?(n("start"),n("end"),u&&(a=o.createRng(),a.setStart(r(u),h),a.setEnd(r(p),m),i.setRng(a))):e.name?i.select(o.select(e.name)[e.index]):e.rng&&i.setRng(e.rng)},select:function(e,t){var n=this,r=n.dom,i=r.createRng(),o;if(n.lastFocusBookmark=null,e){if(!t&&n.controlSelection.controlSelect(e))return;o=r.nodeIndex(e),i.setStart(e.parentNode,o),i.setEnd(e.parentNode,o+1),t&&(n._moveEndPoint(i,e,!0),n._moveEndPoint(i,e)),n.setRng(i)}return e},isCollapsed:function(){var e=this,t=e.getRng(),n=e.getSel();return!t||t.item?!1:t.compareEndPoints?0===t.compareEndPoints("StartToEnd",t):!n||t.collapsed},collapse:function(e){var t=this,n=t.getRng(),r;n.item&&(r=n.item(0),n=t.win.document.body.createTextRange(),n.moveToElementText(r)),n.collapse(!!e),t.setRng(n)},getSel:function(){var e=this.win;return e.getSelection?e.getSelection():e.document.selection},getRng:function(e){function t(e,t,n){try{return t.compareBoundaryPoints(e,n)}catch(r){return-1}}var n=this,r,i,o,a=n.win.document,s;if(!e&&n.lastFocusBookmark){var l=n.lastFocusBookmark;return l.startContainer?(i=a.createRange(),i.setStart(l.startContainer,l.startOffset),i.setEnd(l.endContainer,l.endOffset)):i=l,i}if(e&&n.tridentSel)return n.tridentSel.getRangeAt(0);try{(r=n.getSel())&&(i=r.rangeCount>0?r.getRangeAt(0):r.createRange?r.createRange():a.createRange())}catch(c){}if(d&&i&&i.setStart&&a.selection){try{s=a.selection.createRange()}catch(c){}s&&s.item&&(o=s.item(0),i=a.createRange(),i.setStartBefore(o),i.setEndAfter(o))}return i||(i=a.createRange?a.createRange():a.body.createTextRange()),i.setStart&&9===i.startContainer.nodeType&&i.collapsed&&(o=n.dom.getRoot(),i.setStart(o,0),i.setEnd(o,0)),n.selectedRange&&n.explicitRange&&(0===t(i.START_TO_START,i,n.selectedRange)&&0===t(i.END_TO_END,i,n.selectedRange)?i=n.explicitRange:(n.selectedRange=null,n.explicitRange=null)),i},setRng:function(e,t){var n=this,r;if(e.select)try{e.select()}catch(i){}else if(n.tridentSel){if(e.cloneRange)try{return void n.tridentSel.addRange(e)}catch(i){}}else if(r=n.getSel()){n.explicitRange=e;try{r.removeAllRanges(),r.addRange(e)}catch(i){}t===!1&&r.extend&&(r.collapse(e.endContainer,e.endOffset),r.extend(e.startContainer,e.startOffset)),n.selectedRange=r.rangeCount>0?r.getRangeAt(0):null}},setNode:function(e){var t=this;return t.setContent(t.dom.getOuterHTML(e)),e},getNode:function(){function e(e,t){for(var n=e;e&&3===e.nodeType&&0===e.length;)e=t?e.nextSibling:e.previousSibling;return e||n}var t=this,n=t.getRng(),r,i=n.startContainer,o=n.endContainer,a=n.startOffset,s=n.endOffset,l=t.dom.getRoot();return n?n.setStart?(r=n.commonAncestorContainer,!n.collapsed&&(i==o&&2>s-a&&i.hasChildNodes()&&(r=i.childNodes[a]),3===i.nodeType&&3===o.nodeType&&(i=i.length===a?e(i.nextSibling,!0):i.parentNode,o=0===s?e(o.previousSibling,!1):o.parentNode,i&&i===o))?i:r&&3==r.nodeType?r.parentNode:r):(r=n.item?n.item(0):n.parentElement(),r.ownerDocument!==t.win.document&&(r=l),r):l},getSelectedBlocks:function(t,n){var r=this,i=r.dom,o,a,s=[];if(a=i.getRoot(),t=i.getParent(t||r.getStart(),i.isBlock),n=i.getParent(n||r.getEnd(),i.isBlock),t&&t!=a&&s.push(t),t&&n&&t!=n){o=t;for(var l=new e(t,a);(o=l.next())&&o!=n;)i.isBlock(o)&&s.push(o)}return n&&t!=n&&n!=a&&s.push(n),s},isForward:function(){var e=this.dom,t=this.getSel(),n,r;return t&&t.anchorNode&&t.focusNode?(n=e.createRng(),n.setStart(t.anchorNode,t.anchorOffset),n.collapse(!0),r=e.createRng(),r.setStart(t.focusNode,t.focusOffset),r.collapse(!0),n.compareBoundaryPoints(n.START_TO_START,r)<=0):!0},normalize:function(){var e=this,t=e.getRng();return!d&&new i(e.dom).normalize(t)&&e.setRng(t,e.isForward()),t},selectorChanged:function(e,t){var n=this,r;return n.selectorChangedData||(n.selectorChangedData={},r={},n.editor.on("NodeChange",function(e){var t=e.element,i=n.dom,o=i.getParents(t,null,i.getRoot()),a={};l(n.selectorChangedData,function(e,t){l(o,function(n){return i.is(n,t)?(r[t]||(l(e,function(e){e(!0,{node:n,selector:t,parents:o})}),r[t]=e),a[t]=e,!1):void 0})}),l(r,function(e,n){a[n]||(delete r[n],l(e,function(e){e(!1,{node:t,selector:n,parents:o})}))})})),n.selectorChangedData[e]||(n.selectorChangedData[e]=[]),n.selectorChangedData[e].push(t),n},getScrollContainer:function(){for(var e,t=this.dom.getRoot();t&&"BODY"!=t.nodeName;){if(t.scrollHeight>t.clientHeight){e=t;break}t=t.parentNode}return e},scrollIntoView:function(e){function t(e){for(var t=0,n=0,r=e;r&&r.nodeType;)t+=r.offsetLeft||0,n+=r.offsetTop||0,r=r.offsetParent;return{x:t,y:n}}var n,r,i=this,o=i.dom,a=o.getRoot(),s,l;if("BODY"!=a.nodeName){var c=i.getScrollContainer();if(c)return n=t(e).y-t(c).y,l=c.clientHeight,s=c.scrollTop,void((s>n||n+25>s+l)&&(c.scrollTop=s>n?n:n-l+25))}r=o.getViewPort(i.editor.getWin()),n=o.getPos(e).y,s=r.y,l=r.h,(n<r.y||n+25>s+l)&&i.editor.getWin().scrollTo(0,s>n?n:n-l+25)},_moveEndPoint:function(t,n,r){var i=n,a=new e(n,i),s=this.dom.schema.getNonEmptyElements();do{if(3==n.nodeType&&0!==u(n.nodeValue).length)return void(r?t.setStart(n,0):t.setEnd(n,n.nodeValue.length));if(s[n.nodeName])return void(r?t.setStartBefore(n):"BR"==n.nodeName?t.setEndBefore(n):t.setEndAfter(n));if(o.ie&&o.ie<11&&this.dom.isBlock(n)&&this.dom.isEmpty(n))return void(r?t.setStart(n,0):t.setEnd(n,0))}while(n=r?a.next():a.prev());"BODY"==i.nodeName&&(r?t.setStart(i,0):t.setEnd(i,i.childNodes.length))},destroy:function(){this.win=null,this.controlSelection.destroy()}},s}),r(L,[p],function(e){function t(e,t){function r(e){return e.replace(/%(\w+)/g,"")}var i,o,a=e.dom,s="",l,c;if(c=e.settings.preview_styles,c===!1)return"";if(c||(c="font-family font-size font-weight font-style text-decoration text-transform color background-color border border-radius outline text-shadow"),"string"==typeof t){if(t=e.formatter.get(t),!t)return;t=t[0]}return i=t.block||t.inline||"span",o=a.create(i),n(t.styles,function(e,t){e=r(e),e&&a.setStyle(o,t,e)}),n(t.attributes,function(e,t){e=r(e),e&&a.setAttrib(o,t,e)}),n(t.classes,function(e){e=r(e),a.hasClass(o,e)||a.addClass(o,e)}),e.fire("PreviewFormats"),a.setStyles(o,{position:"absolute",left:-65535}),e.getBody().appendChild(o),l=a.getStyle(e.getBody(),"fontSize",!0),l=/px$/.test(l)?parseInt(l,10):0,n(c.split(" "),function(t){var n=a.getStyle(o,t,!0);if(!("background-color"==t&&/transparent|rgba\s*\([^)]+,\s*0\)/.test(n)&&(n=a.getStyle(e.getBody(),t,!0),"#ffffff"==a.toHex(n).toLowerCase())||"color"==t&&"#000000"==a.toHex(n).toLowerCase())){if("font-size"==t&&/em|%$/.test(n)){if(0===l)return;n=parseFloat(n,10)/(/%$/.test(n)?100:1),n=n*l+"px"}"border"==t&&n&&(s+="padding:0 2px;"),s+=t+":"+n+";"}}),e.fire("AfterPreviewFormats"),a.remove(o),s}var n=e.each;return{getCssText:t}}),r(M,[f,B,p,L],function(e,t,n,r){return function(i){function o(e){return e.nodeType&&(e=e.nodeName),!!i.schema.getTextBlockElements()[e.toLowerCase()]}function a(e,t){return z.getParents(e,t,z.getRoot())}function s(e){return 1===e.nodeType&&"_mce_caret"===e.id}function l(){d({valigntop:[{selector:"td,th",styles:{verticalAlign:"top"}}],valignmiddle:[{selector:"td,th",styles:{verticalAlign:"middle"}}],valignbottom:[{selector:"td,th",styles:{verticalAlign:"bottom"}}],alignleft:[{selector:"figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li",styles:{textAlign:"left"},defaultBlock:"div"},{selector:"img,table",collapsed:!1,styles:{"float":"left"}}],aligncenter:[{selector:"figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li",styles:{textAlign:"center"},defaultBlock:"div"},{selector:"img",collapsed:!1,styles:{display:"block",marginLeft:"auto",marginRight:"auto"}},{selector:"table",collapsed:!1,styles:{marginLeft:"auto",marginRight:"auto"}}],alignright:[{selector:"figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li",styles:{textAlign:"right"},defaultBlock:"div"},{selector:"img,table",collapsed:!1,styles:{"float":"right"}}],alignjustify:[{selector:"figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li",styles:{textAlign:"justify"},defaultBlock:"div"}],bold:[{inline:"strong",remove:"all"},{inline:"span",styles:{fontWeight:"bold"}},{inline:"b",remove:"all"}],italic:[{inline:"em",remove:"all"},{inline:"span",styles:{fontStyle:"italic"}},{inline:"i",remove:"all"}],underline:[{inline:"span",styles:{textDecoration:"underline"},exact:!0},{inline:"u",remove:"all"}],strikethrough:[{inline:"span",styles:{textDecoration:"line-through"},exact:!0},{inline:"strike",remove:"all"}],forecolor:{inline:"span",styles:{color:"%value"},wrap_links:!1},hilitecolor:{inline:"span",styles:{backgroundColor:"%value"},wrap_links:!1},fontname:{inline:"span",styles:{fontFamily:"%value"}},fontsize:{inline:"span",styles:{fontSize:"%value"}},fontsize_class:{inline:"span",attributes:{"class":"%value"}},blockquote:{block:"blockquote",wrapper:1,remove:"all"},subscript:{inline:"sub"},superscript:{inline:"sup"},code:{inline:"code"},link:{inline:"a",selector:"a",remove:"all",split:!0,deep:!0,onmatch:function(){return!0},onformat:function(e,t,n){nt(n,function(t,n){z.setAttrib(e,n,t)})}},removeformat:[{selector:"b,strong,em,i,font,u,strike,sub,sup,dfn,code,samp,kbd,var,cite,mark,q",remove:"all",split:!0,expand:!1,block_expand:!0,deep:!0},{selector:"span",attributes:["style","class"],remove:"empty",split:!0,expand:!1,deep:!0},{selector:"*",attributes:["style","class"],split:!1,expand:!1,deep:!0}]}),nt("p h1 h2 h3 h4 h5 h6 div address pre div dt dd samp".split(/\s/),function(e){d(e,{block:e,remove:"all"})}),d(i.settings.formats)}function c(){i.addShortcut("ctrl+b","bold_desc","Bold"),i.addShortcut("ctrl+i","italic_desc","Italic"),i.addShortcut("ctrl+u","underline_desc","Underline");for(var e=1;6>=e;e++)i.addShortcut("ctrl+"+e,"",["FormatBlock",!1,"h"+e]);i.addShortcut("ctrl+7","",["FormatBlock",!1,"p"]),i.addShortcut("ctrl+8","",["FormatBlock",!1,"div"]),i.addShortcut("ctrl+9","",["FormatBlock",!1,"address"])}function u(e){return e?F[e]:F}function d(e,t){e&&("string"!=typeof e?nt(e,function(e,t){d(t,e)}):(t=t.length?t:[t],nt(t,function(e){e.deep===Q&&(e.deep=!e.selector),e.split===Q&&(e.split=!e.selector||e.inline),e.remove===Q&&e.selector&&!e.inline&&(e.remove="none"),e.selector&&e.inline&&(e.mixed=!0,e.block_expand=!0),"string"==typeof e.classes&&(e.classes=e.classes.split(/\s+/))}),F[e]=t))}function f(e){var t;return i.dom.getParent(e,function(e){return t=i.dom.getStyle(e,"text-decoration"),t&&"none"!==t}),t}function p(e){var t;1===e.nodeType&&e.parentNode&&1===e.parentNode.nodeType&&(t=f(e.parentNode),i.dom.getStyle(e,"color")&&t?i.dom.setStyle(e,"text-decoration",t):i.dom.getStyle(e,"textdecoration")===t&&i.dom.setStyle(e,"text-decoration",null))}function h(t,n,r){function a(e,t){if(t=t||m,e){if(t.onformat&&t.onformat(e,t,n,r),nt(t.styles,function(t,r){z.setStyle(e,r,k(t,n))}),t.styles){var i=z.getAttrib(e,"style");i&&e.setAttribute("data-mce-style",i)}nt(t.attributes,function(t,r){z.setAttrib(e,r,k(t,n))}),nt(t.classes,function(t){t=k(t,n),z.hasClass(e,t)||z.addClass(e,t)})}}function l(){function t(t,n){var i=new e(n);for(r=i.current();r;r=i.prev())if(r.childNodes.length>1||r==t||"BR"==r.tagName)return r}var n=i.selection.getRng(),o=n.startContainer,a=n.endContainer;if(o!=a&&0===n.endOffset){var s=t(o,a),l=3==s.nodeType?s.length:s.childNodes.length;n.setEnd(s,l)}return n}function c(e,t,n,r,i){var o=[],a=-1,s,l=-1,c=-1,u;return nt(e.childNodes,function(e,t){return"UL"===e.nodeName||"OL"===e.nodeName?(a=t,s=e,!1):void 0}),nt(e.childNodes,function(e,n){"SPAN"===e.nodeName&&"bookmark"==z.getAttrib(e,"data-mce-type")&&(e.id==t.id+"_start"?l=n:e.id==t.id+"_end"&&(c=n))}),0>=a||a>l&&c>a?(nt(rt(e.childNodes),i),0):(u=z.clone(n,Y),nt(rt(e.childNodes),function(e,t){(a>l&&a>t||l>a&&t>a)&&(o.push(e),e.parentNode.removeChild(e))}),a>l?e.insertBefore(u,s):l>a&&e.insertBefore(u,s.nextSibling),r.push(u),nt(o,function(e){u.appendChild(e)}),u)}function d(e,r,i){var l=[],u,d,p=!0;u=m.inline||m.block,d=z.create(u),a(d),V.walk(e,function(e){function h(e){var y,C,x,w,_;return _=p,y=e.nodeName.toLowerCase(),C=e.parentNode.nodeName.toLowerCase(),1===e.nodeType&&Z(e)&&(_=p,p="true"===Z(e),w=!0),N(y,"br")?(g=0,void(m.block&&z.remove(e))):m.wrapper&&v(e,t,n)?void(g=0):p&&!w&&m.block&&!m.wrapper&&o(y)&&U(C,u)?(e=z.rename(e,u),a(e),l.push(e),void(g=0)):m.selector&&(nt(f,function(t){"collapsed"in t&&t.collapsed!==b||z.is(e,t.selector)&&!s(e)&&(a(e,t),x=!0)}),!m.inline||x)?void(g=0):void(!p||w||!U(u,y)||!U(C,u)||!i&&3===e.nodeType&&1===e.nodeValue.length&&65279===e.nodeValue.charCodeAt(0)||s(e)||m.inline&&q(e)?"li"==y&&r?g=c(e,r,d,l,h):(g=0,nt(rt(e.childNodes),h),w&&(p=_),g=0):(g||(g=z.clone(d,Y),e.parentNode.insertBefore(g,e),l.push(g)),g.appendChild(e)))}var g;nt(e,h)}),m.wrap_links===!1&&nt(l,function(e){function t(e){var n,r,i;if("A"===e.nodeName){for(r=z.clone(d,Y),l.push(r),i=rt(e.childNodes),n=0;n<i.length;n++)r.appendChild(i[n]);e.appendChild(r)}nt(rt(e.childNodes),t)}t(e)}),nt(l,function(e){function r(e){var t=0;return nt(e.childNodes,function(e){T(e)||M(e)||t++}),t}function i(e){var t,n;return nt(e.childNodes,function(e){return 1!=e.nodeType||M(e)||s(e)?void 0:(t=e,Y)
+}),t&&!M(t)&&_(t,m)&&(n=z.clone(t,Y),a(n),z.replace(n,e,X),z.remove(t,1)),n||e}var o;if(o=r(e),(l.length>1||!q(e))&&0===o)return void z.remove(e,1);if(m.inline||m.wrapper){if(m.exact||1!==o||(e=i(e)),nt(f,function(t){nt(z.select(t.inline,e),function(e){var r;if(!M(e)){if(t.wrap_links===!1){r=e.parentNode;do if("A"===r.nodeName)return;while(r=r.parentNode)}B(t,n,e,t.exact?e:null)}})}),v(e.parentNode,t,n))return z.remove(e,1),e=0,X;m.merge_with_parents&&z.getParent(e.parentNode,function(r){return v(r,t,n)?(z.remove(e,1),e=0,X):void 0}),e&&m.merge_siblings!==!1&&(e=H(L(e),e),e=H(e,L(e,X)))}})}var f=u(t),m=f[0],g,y,b=!r&&W.isCollapsed();if(m)if(r)r.nodeType?(y=z.createRng(),y.setStartBefore(r),y.setEndAfter(r),d(A(y,f),null,!0)):d(r,null,!0);else if(b&&m.inline&&!z.select("td.mce-item-selected,th.mce-item-selected").length)O("apply",t,n);else{var C=i.selection.getNode();$||!f[0].defaultBlock||z.getParent(C,z.isBlock)||h(f[0].defaultBlock),i.selection.setRng(l()),g=W.getBookmark(),d(A(W.getRng(X),f),g),m.styles&&(m.styles.color||m.styles.textDecoration)&&(it(C,p,"childNodes"),p(C)),W.moveToBookmark(g),I(W.getRng(X)),i.nodeChanged()}}function m(e,t,n){function r(e){var n,i,o,a,s;if(1===e.nodeType&&Z(e)&&(a=b,b="true"===Z(e),s=!0),n=rt(e.childNodes),b&&!s)for(i=0,o=p.length;o>i&&!B(p[i],t,e,e);i++);if(h.deep&&n.length){for(i=0,o=n.length;o>i;i++)r(n[i]);s&&(b=a)}}function o(n){var r;return nt(a(n.parentNode).reverse(),function(n){var i;r||"_start"==n.id||"_end"==n.id||(i=v(n,e,t),i&&i.split!==!1&&(r=n))}),r}function s(e,n,r,i){var o,a,s,l,c,u;if(e){for(u=e.parentNode,o=n.parentNode;o&&o!=u;o=o.parentNode){for(a=z.clone(o,Y),c=0;c<p.length;c++)if(B(p[c],t,a,a)){a=0;break}a&&(s&&a.appendChild(s),l||(l=a),s=a)}!i||h.mixed&&q(e)||(n=z.split(e,n)),s&&(r.parentNode.insertBefore(s,r),l.appendChild(r))}return n}function l(e){return s(o(e),e,e,!0)}function c(e){var t=z.get(e?"_start":"_end"),n=t[e?"firstChild":"lastChild"];return M(n)&&(n=n[e?"firstChild":"lastChild"]),z.remove(t,!0),n}function d(e){var t,n,o=e.commonAncestorContainer;e=A(e,p,X),h.split&&(t=P(e,X),n=P(e),t!=n?(/^(TR|TH|TD)$/.test(t.nodeName)&&t.firstChild&&(t="TR"==t.nodeName?t.firstChild.firstChild||t:t.firstChild||t),o&&/^T(HEAD|BODY|FOOT|R)$/.test(o.nodeName)&&/^(TH|TD)$/.test(n.nodeName)&&n.firstChild&&(n=n.firstChild||n),t=R(t,"span",{id:"_start","data-mce-type":"bookmark"}),n=R(n,"span",{id:"_end","data-mce-type":"bookmark"}),l(t),l(n),t=c(X),n=c()):t=n=l(t),e.startContainer=t.parentNode,e.startOffset=j(t),e.endContainer=n.parentNode,e.endOffset=j(n)+1),V.walk(e,function(e){nt(e,function(e){r(e),1===e.nodeType&&"underline"===i.dom.getStyle(e,"text-decoration")&&e.parentNode&&"underline"===f(e.parentNode)&&B({deep:!1,exact:!0,inline:"span",styles:{textDecoration:"underline"}},null,e)})})}var p=u(e),h=p[0],m,g,b=!0;return n?void(n.nodeType?(g=z.createRng(),g.setStartBefore(n),g.setEndAfter(n),d(g)):d(n)):void(W.isCollapsed()&&h.inline&&!z.select("td.mce-item-selected,th.mce-item-selected").length?O("remove",e,t):(m=W.getBookmark(),d(W.getRng(X)),W.moveToBookmark(m),h.inline&&y(e,t,W.getStart())&&I(W.getRng(!0)),i.nodeChanged()))}function g(e,t,n){var r=u(e);!y(e,t,n)||"toggle"in r[0]&&!r[0].toggle?h(e,t,n):m(e,t,n)}function v(e,t,n,r){function i(e,t,i){var o,a,s=t[i],l;if(t.onmatch)return t.onmatch(e,t,i);if(s)if(s.length===Q){for(o in s)if(s.hasOwnProperty(o)){if(a="attributes"===i?z.getAttrib(e,o):E(e,o),r&&!a&&!t.exact)return;if((!r||t.exact)&&!N(a,S(k(s[o],n),o)))return}}else for(l=0;l<s.length;l++)if("attributes"===i?z.getAttrib(e,s[l]):E(e,s[l]))return t;return t}var o=u(t),a,s,l;if(o&&e)for(s=0;s<o.length;s++)if(a=o[s],_(e,a)&&i(e,a,"attributes")&&i(e,a,"styles")){if(l=a.classes)for(s=0;s<l.length;s++)if(!z.hasClass(e,l[s]))return;return a}}function y(e,t,n){function r(n){var r=z.getRoot();return n===r?!1:(n=z.getParent(n,function(n){return n.parentNode===r||!!v(n,e,t,!0)}),v(n,e,t))}var i;return n?r(n):(n=W.getNode(),r(n)?X:(i=W.getStart(),i!=n&&r(i)?X:Y))}function b(e,t){var n,r=[],i={};return n=W.getStart(),z.getParent(n,function(n){var o,a;for(o=0;o<e.length;o++)a=e[o],!i[a]&&v(n,a,t)&&(i[a]=!0,r.push(a))},z.getRoot()),r}function C(e){var t=u(e),n,r,i,o,s;if(t)for(n=W.getStart(),r=a(n),o=t.length-1;o>=0;o--){if(s=t[o].selector,!s||t[o].defaultBlock)return X;for(i=r.length-1;i>=0;i--)if(z.is(r[i],s))return X}return Y}function x(e,t,n){var r;return J||(J={},r={},i.on("NodeChange",function(e){var t=a(e.element),n={};nt(J,function(e,i){nt(t,function(o){return v(o,i,{},e.similar)?(r[i]||(nt(e,function(e){e(!0,{node:o,format:i,parents:t})}),r[i]=e),n[i]=e,!1):void 0})}),nt(r,function(i,o){n[o]||(delete r[o],nt(i,function(n){n(!1,{node:e.element,format:o,parents:t})}))})})),nt(e.split(","),function(e){J[e]||(J[e]=[],J[e].similar=n),J[e].push(t)}),this}function w(e){return r.getCssText(i,e)}function _(e,t){return N(e,t.inline)?X:N(e,t.block)?X:t.selector?1==e.nodeType&&z.is(e,t.selector):void 0}function N(e,t){return e=e||"",t=t||"",e=""+(e.nodeName||e),t=""+(t.nodeName||t),e.toLowerCase()==t.toLowerCase()}function E(e,t){return S(z.getStyle(e,t),t)}function S(e,t){return("color"==t||"backgroundColor"==t)&&(e=z.toHex(e)),"fontWeight"==t&&700==e&&(e="bold"),"fontFamily"==t&&(e=e.replace(/[\'\"]/g,"").replace(/,\s+/g,",")),""+e}function k(e,t){return"string"!=typeof e?e=e(t):t&&(e=e.replace(/%(\w+)/g,function(e,n){return t[n]||e})),e}function T(e){return e&&3===e.nodeType&&/^([\t \r\n]+|)$/.test(e.nodeValue)}function R(e,t,n){var r=z.create(t,n);return e.parentNode.insertBefore(r,e),r.appendChild(e),r}function A(t,n,r){function s(e){function t(e){return"BR"==e.nodeName&&e.getAttribute("data-mce-bogus")&&!e.nextSibling}var r,i,o,a,s;if(r=i=e?g:y,a=e?"previousSibling":"nextSibling",s=z.getRoot(),3==r.nodeType&&!T(r)&&(e?v>0:b<r.nodeValue.length))return r;for(;;){if(!n[0].block_expand&&q(i))return i;for(o=i[a];o;o=o[a])if(!M(o)&&!T(o)&&!t(o))return i;if(i.parentNode==s){r=i;break}i=i.parentNode}return r}function l(e,t){for(t===Q&&(t=3===e.nodeType?e.length:e.childNodes.length);e&&e.hasChildNodes();)e=e.childNodes[t],e&&(t=3===e.nodeType?e.length:e.childNodes.length);return{node:e,offset:t}}function c(e){for(var t=e;t;){if(1===t.nodeType&&Z(t))return"false"===Z(t)?t:e;t=t.parentNode}return e}function u(t,n,o){function a(e,t){var n,i,a=e.nodeValue;return"undefined"==typeof t&&(t=o?a.length:0),o?(n=a.lastIndexOf(" ",t),i=a.lastIndexOf("\xa0",t),n=n>i?n:i,-1===n||r||n++):(n=a.indexOf(" ",t),i=a.indexOf("\xa0",t),n=-1!==n&&(-1===i||i>n)?n:i),n}var s,l,c,u;if(3===t.nodeType){if(c=a(t,n),-1!==c)return{container:t,offset:c};u=t}for(s=new e(t,z.getParent(t,q)||i.getBody());l=s[o?"prev":"next"]();)if(3===l.nodeType){if(u=l,c=a(l),-1!==c)return{container:l,offset:c}}else if(q(l))break;return u?(n=o?0:u.length,{container:u,offset:n}):void 0}function d(e,r){var i,o,s,l;for(3==e.nodeType&&0===e.nodeValue.length&&e[r]&&(e=e[r]),i=a(e),o=0;o<i.length;o++)for(s=0;s<n.length;s++)if(l=n[s],!("collapsed"in l&&l.collapsed!==t.collapsed)&&z.is(i[o],l.selector))return i[o];return e}function f(e,t){var r,i=z.getRoot();if(n[0].wrapper||(r=z.getParent(e,n[0].block,i)),r||(r=z.getParent(3==e.nodeType?e.parentNode:e,function(e){return e!=i&&o(e)})),r&&n[0].wrapper&&(r=a(r,"ul,ol").reverse()[0]||r),!r)for(r=e;r[t]&&!q(r[t])&&(r=r[t],!N(r,"br")););return r||e}var p,h,m,g=t.startContainer,v=t.startOffset,y=t.endContainer,b=t.endOffset;if(1==g.nodeType&&g.hasChildNodes()&&(p=g.childNodes.length-1,g=g.childNodes[v>p?p:v],3==g.nodeType&&(v=0)),1==y.nodeType&&y.hasChildNodes()&&(p=y.childNodes.length-1,y=y.childNodes[b>p?p:b-1],3==y.nodeType&&(b=y.nodeValue.length)),g=c(g),y=c(y),(M(g.parentNode)||M(g))&&(g=M(g)?g:g.parentNode,g=g.nextSibling||g,3==g.nodeType&&(v=0)),(M(y.parentNode)||M(y))&&(y=M(y)?y:y.parentNode,y=y.previousSibling||y,3==y.nodeType&&(b=y.length)),n[0].inline&&(t.collapsed&&(m=u(g,v,!0),m&&(g=m.container,v=m.offset),m=u(y,b),m&&(y=m.container,b=m.offset)),h=l(y,b),h.node)){for(;h.node&&0===h.offset&&h.node.previousSibling;)h=l(h.node.previousSibling);h.node&&h.offset>0&&3===h.node.nodeType&&" "===h.node.nodeValue.charAt(h.offset-1)&&h.offset>1&&(y=h.node,y.splitText(h.offset-1))}return(n[0].inline||n[0].block_expand)&&(n[0].inline&&3==g.nodeType&&0!==v||(g=s(!0)),n[0].inline&&3==y.nodeType&&b!==y.nodeValue.length||(y=s())),n[0].selector&&n[0].expand!==Y&&!n[0].inline&&(g=d(g,"previousSibling"),y=d(y,"nextSibling")),(n[0].block||n[0].selector)&&(g=f(g,"previousSibling"),y=f(y,"nextSibling"),n[0].block&&(q(g)||(g=s(!0)),q(y)||(y=s()))),1==g.nodeType&&(v=j(g),g=g.parentNode),1==y.nodeType&&(b=j(y)+1,y=y.parentNode),{startContainer:g,startOffset:v,endContainer:y,endOffset:b}}function B(e,t,n,r){var i,o,a;if(!_(n,e))return Y;if("all"!=e.remove)for(nt(e.styles,function(e,i){e=S(k(e,t),i),"number"==typeof i&&(i=e,r=0),(!r||N(E(r,i),e))&&z.setStyle(n,i,""),a=1}),a&&""===z.getAttrib(n,"style")&&(n.removeAttribute("style"),n.removeAttribute("data-mce-style")),nt(e.attributes,function(e,i){var o;if(e=k(e,t),"number"==typeof i&&(i=e,r=0),!r||N(z.getAttrib(r,i),e)){if("class"==i&&(e=z.getAttrib(n,i),e&&(o="",nt(e.split(/\s+/),function(e){/mce\w+/.test(e)&&(o+=(o?" ":"")+e)}),o)))return void z.setAttrib(n,i,o);"class"==i&&n.removeAttribute("className"),G.test(i)&&n.removeAttribute("data-mce-"+i),n.removeAttribute(i)}}),nt(e.classes,function(e){e=k(e,t),(!r||z.hasClass(r,e))&&z.removeClass(n,e)}),o=z.getAttribs(n),i=0;i<o.length;i++)if(0!==o[i].nodeName.indexOf("_"))return Y;return"none"!=e.remove?(D(n,e),X):void 0}function D(e,t){function n(e,t,n){return e=L(e,t,n),!e||"BR"==e.nodeName||q(e)}var r=e.parentNode,o;t.block&&($?r==z.getRoot()&&(t.list_block&&N(e,t.list_block)||nt(rt(e.childNodes),function(e){U($,e.nodeName.toLowerCase())?o?o.appendChild(e):(o=R(e,$),z.setAttribs(o,i.settings.forced_root_block_attrs)):o=0})):q(e)&&!q(r)&&(n(e,Y)||n(e.firstChild,X,1)||e.insertBefore(z.create("br"),e.firstChild),n(e,X)||n(e.lastChild,Y,1)||e.appendChild(z.create("br")))),t.selector&&t.inline&&!N(t.inline,e)||z.remove(e,1)}function L(e,t,n){if(e)for(t=t?"nextSibling":"previousSibling",e=n?e:e[t];e;e=e[t])if(1==e.nodeType||!T(e))return e}function M(e){return e&&1==e.nodeType&&"bookmark"==e.getAttribute("data-mce-type")}function H(e,t){function n(e,t){function n(e){var t={};return nt(z.getAttribs(e),function(n){var r=n.nodeName.toLowerCase();0!==r.indexOf("_")&&"style"!==r&&"data-mce-style"!==r&&(t[r]=z.getAttrib(e,r))}),t}function r(e,t){var n,r;for(r in e)if(e.hasOwnProperty(r)){if(n=t[r],n===Q)return Y;if(e[r]!=n)return Y;delete t[r]}for(r in t)if(t.hasOwnProperty(r))return Y;return X}return e.nodeName!=t.nodeName?Y:r(n(e),n(t))&&r(z.parseStyle(z.getAttrib(e,"style")),z.parseStyle(z.getAttrib(t,"style")))?!M(e)&&!M(t):Y}function r(e,t){for(i=e;i;i=i[t]){if(3==i.nodeType&&0!==i.nodeValue.length)return e;if(1==i.nodeType&&!M(i))return i}return e}var i,o;if(e&&t&&(e=r(e,"previousSibling"),t=r(t,"nextSibling"),n(e,t))){for(i=e.nextSibling;i&&i!=t;)o=i,i=i.nextSibling,e.appendChild(o);return z.remove(t),nt(rt(t.childNodes),function(t){e.appendChild(t)}),e}return t}function P(t,n){var r,o,a;return r=t[n?"startContainer":"endContainer"],o=t[n?"startOffset":"endOffset"],1==r.nodeType&&(a=r.childNodes.length-1,!n&&o&&o--,r=r.childNodes[o>a?a:o]),3===r.nodeType&&n&&o>=r.nodeValue.length&&(r=new e(r,i.getBody()).next()||r),3!==r.nodeType||n||0!==o||(r=new e(r,i.getBody()).prev()||r),r}function O(t,n,r){function a(e){var t=z.create("span",{id:y,"data-mce-bogus":!0,style:b?"color:red":""});return e&&t.appendChild(i.getDoc().createTextNode(K)),t}function s(e,t){for(;e;){if(3===e.nodeType&&e.nodeValue!==K||e.childNodes.length>1)return!1;t&&1===e.nodeType&&t.push(e),e=e.firstChild}return!0}function l(e){for(;e;){if(e.id===y)return e;e=e.parentNode}}function c(t){var n;if(t)for(n=new e(t,t),t=n.current();t;t=n.next())if(3===t.nodeType)return t}function d(e,t){var n,r;if(e)r=W.getRng(!0),s(e)?(t!==!1&&(r.setStartBefore(e),r.setEndBefore(e)),z.remove(e)):(n=c(e),n.nodeValue.charAt(0)===K&&(n=n.deleteData(0,1)),z.remove(e,1)),W.setRng(r);else if(e=l(W.getStart()),!e)for(;e=z.get(y);)d(e,!1)}function f(){var e,t,i,o,s,d,f;e=W.getRng(!0),o=e.startOffset,d=e.startContainer,f=d.nodeValue,t=l(W.getStart()),t&&(i=c(t)),f&&o>0&&o<f.length&&/\w/.test(f.charAt(o))&&/\w/.test(f.charAt(o-1))?(s=W.getBookmark(),e.collapse(!0),e=A(e,u(n)),e=V.split(e),h(n,r,e),W.moveToBookmark(s)):(t&&i.nodeValue===K?h(n,r,t):(t=a(!0),i=t.firstChild,e.insertNode(t),o=1,h(n,r,t)),W.setCursorLocation(i,o))}function p(){var e=W.getRng(!0),t,i,s,l,c,d,f=[],p,h;for(t=e.startContainer,i=e.startOffset,c=t,3==t.nodeType&&((i!=t.nodeValue.length||t.nodeValue===K)&&(l=!0),c=c.parentNode);c;){if(v(c,n,r)){d=c;break}c.nextSibling&&(l=!0),f.push(c),c=c.parentNode}if(d)if(l)s=W.getBookmark(),e.collapse(!0),e=A(e,u(n),!0),e=V.split(e),m(n,r,e),W.moveToBookmark(s);else{for(h=a(),c=h,p=f.length-1;p>=0;p--)c.appendChild(z.clone(f[p],!1)),c=c.firstChild;c.appendChild(z.doc.createTextNode(K)),c=c.firstChild;var g=z.getParent(d,o);g&&z.isEmpty(g)?d.parentNode.replaceChild(h,d):z.insertAfter(h,d),W.setCursorLocation(c,1),z.isEmpty(d)&&z.remove(d)}}function g(){var e;e=l(W.getStart()),e&&!z.isEmpty(e)&&it(e,function(e){1!=e.nodeType||e.id===y||z.isEmpty(e)||z.setAttrib(e,"data-mce-bogus",null)},"childNodes")}var y="_mce_caret",b=i.settings.caret_debug;i._hasCaretEvents||(tt=function(){var e=[],t;if(s(l(W.getStart()),e))for(t=e.length;t--;)z.setAttrib(e[t],"data-mce-bogus","1")},et=function(e){var t=e.keyCode;d(),(8==t||37==t||39==t)&&d(l(W.getStart())),g()},i.on("SetContent",function(e){e.selection&&g()}),i._hasCaretEvents=!0),"apply"==t?f():p()}function I(t){var n=t.startContainer,r=t.startOffset,i,o,a,s,l;if(3==n.nodeType&&r>=n.nodeValue.length&&(r=j(n),n=n.parentNode,i=!0),1==n.nodeType)for(s=n.childNodes,n=s[Math.min(r,s.length-1)],o=new e(n,z.getParent(n,z.isBlock)),(r>s.length-1||i)&&o.next(),a=o.current();a;a=o.next())if(3==a.nodeType&&!T(a))return l=z.create("a",null,K),a.parentNode.insertBefore(l,a),t.setStart(a,0),W.setRng(t),void z.remove(l)}var F={},z=i.dom,W=i.selection,V=new t(z),U=i.schema.isValidChild,q=z.isBlock,$=i.settings.forced_root_block,j=z.nodeIndex,K="\ufeff",G=/^(src|href|style)$/,Y=!1,X=!0,J,Q,Z=z.getContentEditable,et,tt,nt=n.each,rt=n.grep,it=n.walk,ot=n.extend;ot(this,{get:u,register:d,apply:h,remove:m,toggle:g,match:y,matchAll:b,matchNode:v,canApply:C,formatChanged:x,getCssText:w}),l(),c(),i.on("BeforeGetContent",function(){tt&&tt()}),i.on("mouseup keydown",function(e){et&&et(e)})}}),r(H,[g,p],function(e,t){var n=t.trim,r;return r=new RegExp(["<span[^>]+data-mce-bogus[^>]+>[\u200b\ufeff]+<\\/span>","<div[^>]+data-mce-bogus[^>]+><\\/div>",'\\s?data-mce-selected="[^"]+"'].join("|"),"gi"),function(t){function i(){return n(t.getContent({format:"raw",no_events:1}).replace(r,""))}function o(e){a.typing=!1,a.add({},e)}var a=this,s=0,l=[],c,u,d=0;return t.on("init",function(){a.add()}),t.on("BeforeExecCommand",function(e){var t=e.command;"Undo"!=t&&"Redo"!=t&&"mceRepaint"!=t&&a.beforeChange()}),t.on("ExecCommand",function(e){var t=e.command;"Undo"!=t&&"Redo"!=t&&"mceRepaint"!=t&&o(e)}),t.on("ObjectResizeStart",function(){a.beforeChange()}),t.on("SaveContent ObjectResized blur",o),t.on("DragEnd",o),t.on("KeyUp",function(n){var r=n.keyCode;(r>=33&&36>=r||r>=37&&40>=r||45==r||13==r||n.ctrlKey)&&(o(),t.nodeChanged()),(46==r||8==r||e.mac&&(91==r||93==r))&&t.nodeChanged(),u&&a.typing&&(t.isDirty()||(t.isNotDirty=!l[0]||i()==l[0].content,t.isNotDirty||t.fire("change",{level:l[0],lastLevel:null})),t.fire("TypingUndo"),u=!1,t.nodeChanged())}),t.on("KeyDown",function(e){var t=e.keyCode;return t>=33&&36>=t||t>=37&&40>=t||45==t?void(a.typing&&o(e)):void((16>t||t>20)&&224!=t&&91!=t&&!a.typing&&(a.beforeChange(),a.typing=!0,a.add({},e),u=!0))}),t.on("MouseDown",function(e){a.typing&&o(e)}),t.addShortcut("ctrl+z","","Undo"),t.addShortcut("ctrl+y,ctrl+shift+z","","Redo"),t.on("AddUndo Undo Redo ClearUndos MouseUp",function(e){e.isDefaultPrevented()||t.nodeChanged()}),a={data:l,typing:!1,beforeChange:function(){d||(c=t.selection.getBookmark(2,!0))},add:function(e,n){var r,o=t.settings,a;if(e=e||{},e.content=i(),d||t.removed)return null;if(a=l[s],t.fire("BeforeAddUndo",{level:e,lastLevel:a,originalEvent:n}).isDefaultPrevented())return null;if(a&&a.content==e.content)return null;if(l[s]&&(l[s].beforeBookmark=c),o.custom_undo_redo_levels&&l.length>o.custom_undo_redo_levels){for(r=0;r<l.length-1;r++)l[r]=l[r+1];l.length--,s=l.length}e.bookmark=t.selection.getBookmark(2,!0),s<l.length-1&&(l.length=s+1),l.push(e),s=l.length-1;var u={level:e,lastLevel:a,originalEvent:n};return t.fire("AddUndo",u),s>0&&(t.isNotDirty=!1,t.fire("change",u)),e},undo:function(){var e;return a.typing&&(a.add(),a.typing=!1),s>0&&(e=l[--s],0===s&&(t.isNotDirty=!0),t.setContent(e.content,{format:"raw"}),t.selection.moveToBookmark(e.beforeBookmark),t.fire("undo",{level:e})),e},redo:function(){var e;return s<l.length-1&&(e=l[++s],t.setContent(e.content,{format:"raw"}),t.selection.moveToBookmark(e.bookmark),t.fire("redo",{level:e})),e},clear:function(){l=[],s=0,a.typing=!1,t.fire("ClearUndos")},hasUndo:function(){return s>0||a.typing&&l[0]&&i()!=l[0].content},hasRedo:function(){return s<l.length-1&&!this.typing},transact:function(e){a.beforeChange();try{d++,e()}finally{d--}a.add()}}}}),r(P,[f,B,g],function(e,t,n){var r=n.ie&&n.ie<11;return function(i){function o(o){function f(e){return e&&a.isBlock(e)&&!/^(TD|TH|CAPTION|FORM)$/.test(e.nodeName)&&!/^(fixed|absolute)/i.test(e.style.position)&&"true"!==a.getContentEditable(e)}function p(e){var t;a.isBlock(e)&&(t=s.getRng(),e.appendChild(a.create("span",null,"\xa0")),s.select(e),e.lastChild.outerHTML="",s.setRng(t))}function h(e){for(var t=e,n=[],r;t=t.firstChild;){if(a.isBlock(t))return;1!=t.nodeType||d[t.nodeName.toLowerCase()]||n.push(t)}for(r=n.length;r--;)t=n[r],!t.hasChildNodes()||t.firstChild==t.lastChild&&""===t.firstChild.nodeValue?a.remove(t):"A"==t.nodeName&&" "===(t.innerText||t.textContent)&&a.remove(t)}function m(t){function r(e){for(;e;){if(1==e.nodeType||3==e.nodeType&&e.data&&/[\r\n\s]/.test(e.data))return e;e=e.nextSibling}}var i,o,l,c=t,u;if(n.ie&&n.ie<9&&B&&B.firstChild&&B.firstChild==B.lastChild&&"BR"==B.firstChild.tagName&&a.remove(B.firstChild),"LI"==t.nodeName){var f=r(t.firstChild);f&&/^(UL|OL)$/.test(f.nodeName)&&t.insertBefore(a.doc.createTextNode("\xa0"),t.firstChild)}if(l=a.createRng(),t.hasChildNodes()){for(i=new e(t,t);o=i.current();){if(3==o.nodeType){l.setStart(o,0),l.setEnd(o,0);break}if(d[o.nodeName.toLowerCase()]){l.setStartBefore(o),l.setEndBefore(o);break}c=o,o=i.next()}o||(l.setStart(c,0),l.setEnd(c,0))}else"BR"==t.nodeName?t.nextSibling&&a.isBlock(t.nextSibling)?((!D||9>D)&&(u=a.create("br"),t.parentNode.insertBefore(u,t)),l.setStartBefore(t),l.setEndBefore(t)):(l.setStartAfter(t),l.setEndAfter(t)):(l.setStart(t,0),l.setEnd(t,0));s.setRng(l),a.remove(u),s.scrollIntoView(t)}function g(e){var t=l.forced_root_block;t&&t.toLowerCase()===e.tagName.toLowerCase()&&a.setAttribs(e,l.forced_root_block_attrs)}function v(e){var t=R,n,i,o;if(e||"TABLE"==O?(n=a.create(e||F),g(n)):n=B.cloneNode(!1),o=n,l.keep_styles!==!1)do if(/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U|VAR|CITE|DFN|CODE|MARK|Q|SUP|SUB|SAMP)$/.test(t.nodeName)){if("_mce_caret"==t.id)continue;i=t.cloneNode(!1),a.setAttrib(i,"id",""),n.hasChildNodes()?(i.appendChild(n.firstChild),n.appendChild(i)):(o=i,n.appendChild(i))}while(t=t.parentNode);return r||(o.innerHTML='<br data-mce-bogus="1">'),n}function y(t){var n,r,i;if(3==R.nodeType&&(t?A>0:A<R.nodeValue.length))return!1;if(R.parentNode==B&&z&&!t)return!0;if(t&&1==R.nodeType&&R==B.firstChild)return!0;if("TABLE"===R.nodeName||R.previousSibling&&"TABLE"==R.previousSibling.nodeName)return z&&!t||!z&&t;for(n=new e(R,B),3==R.nodeType&&(t&&0===A?n.prev():t||A!=R.nodeValue.length||n.next());r=n.current();){if(1===r.nodeType){if(!r.getAttribute("data-mce-bogus")&&(i=r.nodeName.toLowerCase(),d[i]&&"br"!==i))return!1}else if(3===r.nodeType&&!/^[ \t\r\n]*$/.test(r.nodeValue))return!1;t?n.prev():n.next()}return!0}function b(e,t){var n,r,o,s,l,c,d=F||"P";if(r=a.getParent(e,a.isBlock),c=i.getBody().nodeName.toLowerCase(),!r||!f(r)){if(r=r||T,!r.hasChildNodes())return n=a.create(d),g(n),r.appendChild(n),S.setStart(n,0),S.setEnd(n,0),n;for(s=e;s.parentNode!=r;)s=s.parentNode;for(;s&&!a.isBlock(s);)o=s,s=s.previousSibling;if(o&&u.isValidChild(c,d.toLowerCase())){for(n=a.create(d),g(n),o.parentNode.insertBefore(n,o),s=o;s&&!a.isBlock(s);)l=s.nextSibling,n.appendChild(s),s=l;S.setStart(e,t),S.setEnd(e,t)}}return e}function C(){function e(e){for(var t=P[e?"firstChild":"lastChild"];t&&1!=t.nodeType;)t=t[e?"nextSibling":"previousSibling"];return t===B}function t(){var e=P.parentNode;return"LI"==e.nodeName?e:P}var n=P.parentNode.nodeName;/^(OL|UL|LI)$/.test(n)&&(F="LI"),M=F?v(F):a.create("BR"),e(!0)&&e()?"LI"==n?a.insertAfter(M,t()):a.replace(M,P):e(!0)?"LI"==n?(a.insertAfter(M,t()),M.appendChild(a.doc.createTextNode(" ")),M.appendChild(P)):P.parentNode.insertBefore(M,P):e()?(a.insertAfter(M,t()),p(M)):(P=t(),k=S.cloneRange(),k.setStartAfter(B),k.setEndAfter(P),H=k.extractContents(),"LI"==F&&"LI"==H.firstChild.nodeName?(M=H.firstChild,a.insertAfter(H,P)):(a.insertAfter(H,P),a.insertAfter(M,P))),a.remove(B),m(M),c.add()}function x(){for(var t=new e(R,B),n;n=t.next();)if(d[n.nodeName.toLowerCase()]||n.length>0)return!0}function w(){var e,t,n;R&&3==R.nodeType&&A>=R.nodeValue.length&&(r||x()||(e=a.create("br"),S.insertNode(e),S.setStartAfter(e),S.setEndAfter(e),t=!0)),e=a.create("br"),S.insertNode(e),r&&"PRE"==O&&(!D||8>D)&&e.parentNode.insertBefore(a.doc.createTextNode("\r"),e),n=a.create("span",{},"&nbsp;"),e.parentNode.insertBefore(n,e),s.scrollIntoView(n),a.remove(n),t?(S.setStartBefore(e),S.setEndBefore(e)):(S.setStartAfter(e),S.setEndAfter(e)),s.setRng(S),c.add()}function _(e){do 3===e.nodeType&&(e.nodeValue=e.nodeValue.replace(/^[\r\n]+/,"")),e=e.firstChild;while(e)}function N(e){var t=a.getRoot(),n,r;for(n=e;n!==t&&"false"!==a.getContentEditable(n);)"true"===a.getContentEditable(n)&&(r=n),n=n.parentNode;return n!==t?r:t}function E(e){var t;r||(e.normalize(),t=e.lastChild,(!t||/^(left|right)$/gi.test(a.getStyle(t,"float",!0)))&&a.add(e,"br"))}var S,k,T,R,A,B,D,L,M,H,P,O,I,F,z;if(S=s.getRng(!0),!o.isDefaultPrevented()){if(!S.collapsed)return void i.execCommand("Delete");if(new t(a).normalize(S),R=S.startContainer,A=S.startOffset,F=(l.force_p_newlines?"p":"")||l.forced_root_block,F=F?F.toUpperCase():"",D=a.doc.documentMode,L=o.shiftKey,1==R.nodeType&&R.hasChildNodes()&&(z=A>R.childNodes.length-1,R=R.childNodes[Math.min(A,R.childNodes.length-1)]||R,A=z&&3==R.nodeType?R.nodeValue.length:0),T=N(R)){if(c.beforeChange(),!a.isBlock(T)&&T!=a.getRoot())return void((!F||L)&&w());if((F&&!L||!F&&L)&&(R=b(R,A)),B=a.getParent(R,a.isBlock),P=B?a.getParent(B.parentNode,a.isBlock):null,O=B?B.nodeName.toUpperCase():"",I=P?P.nodeName.toUpperCase():"","LI"!=I||o.ctrlKey||(B=P,O=I),"LI"==O){if(!F&&L)return void w();if(a.isEmpty(B))return void C()}if("PRE"==O&&l.br_in_pre!==!1){if(!L)return void w()}else if(!F&&!L&&"LI"!=O||F&&L)return void w();F&&B===i.getBody()||(F=F||"P",y()?(M=/^(H[1-6]|PRE|FIGURE)$/.test(O)&&"HGROUP"!=I?v(F):v(),l.end_container_on_empty_block&&f(P)&&a.isEmpty(B)?M=a.split(P,B):a.insertAfter(M,B),m(M)):y(!0)?(M=B.parentNode.insertBefore(v(),B),p(M),m(B)):(k=S.cloneRange(),k.setEndAfter(B),H=k.extractContents(),_(H),M=H.firstChild,a.insertAfter(H,B),h(M),E(B),m(M)),a.setAttrib(M,"id",""),i.fire("NewBlock",{newBlock:M}),c.add())}}}var a=i.dom,s=i.selection,l=i.settings,c=i.undoManager,u=i.schema,d=u.getNonEmptyElements();i.on("keydown",function(e){13==e.keyCode&&o(e)!==!1&&e.preventDefault()})}}),r(O,[],function(){return function(e){function t(){var t=i.getStart(),s=e.getBody(),l,c,u,d,f,p,h,m=-16777215,g,v,y,b,C;if(C=n.forced_root_block,t&&1===t.nodeType&&C){for(;t&&t!=s;){if(a[t.nodeName])return;t=t.parentNode}if(l=i.getRng(),l.setStart){c=l.startContainer,u=l.startOffset,d=l.endContainer,f=l.endOffset;try{v=e.getDoc().activeElement===s}catch(x){}}else l.item&&(t=l.item(0),l=e.getDoc().body.createTextRange(),l.moveToElementText(t)),v=l.parentElement().ownerDocument===e.getDoc(),y=l.duplicate(),y.collapse(!0),u=-1*y.move("character",m),y.collapsed||(y=l.duplicate(),y.collapse(!1),f=-1*y.move("character",m)-u);for(t=s.firstChild,b=s.nodeName.toLowerCase();t;)if((3===t.nodeType||1==t.nodeType&&!a[t.nodeName])&&o.isValidChild(b,C.toLowerCase())){if(3===t.nodeType&&0===t.nodeValue.length){h=t,t=t.nextSibling,r.remove(h);continue}p||(p=r.create(C,e.settings.forced_root_block_attrs),t.parentNode.insertBefore(p,t),g=!0),h=t,t=t.nextSibling,p.appendChild(h)}else p=null,t=t.nextSibling;if(g&&v){if(l.setStart)l.setStart(c,u),l.setEnd(d,f),i.setRng(l);else try{l=e.getDoc().body.createTextRange(),l.moveToElementText(s),l.collapse(!0),l.moveStart("character",u),f>0&&l.moveEnd("character",f),l.select()}catch(x){}e.nodeChanged()}}}var n=e.settings,r=e.dom,i=e.selection,o=e.schema,a=o.getBlockElements();n.forced_root_block&&e.on("NodeChange",t)}}),r(I,[S,g,p],function(e,n,r){var i=r.each,o=r.extend,a=r.map,s=r.inArray,l=r.explode,c=n.gecko,u=n.ie,d=!0,f=!1;return function(r){function p(e,t,n){var r;return e=e.toLowerCase(),(r=N.exec[e])?(r(e,t,n),d):f}function h(e){var t;return e=e.toLowerCase(),(t=N.state[e])?t(e):-1}function m(e){var t;return e=e.toLowerCase(),(t=N.value[e])?t(e):f}function g(e,t){t=t||"exec",i(e,function(e,n){i(n.toLowerCase().split(","),function(n){N[t][n]=e})})}function v(e,n,i){return n===t&&(n=f),i===t&&(i=null),r.getDoc().execCommand(e,n,i)}function y(e){return S.match(e)}function b(e,n){S.toggle(e,n?{value:n}:t),r.nodeChanged()}function C(e){k=_.getBookmark(e)}function x(){_.moveToBookmark(k)}var w=r.dom,_=r.selection,N={state:{},exec:{},value:{}},E=r.settings,S=r.formatter,k;o(this,{execCommand:p,queryCommandState:h,queryCommandValue:m,addCommands:g}),g({"mceResetDesignMode,mceBeginUndoLevel":function(){},"mceEndUndoLevel,mceAddUndoLevel":function(){r.undoManager.add()},"Cut,Copy,Paste":function(e){var t=r.getDoc(),i;try{v(e)}catch(o){i=d}if(i||!t.queryCommandSupported(e)){var a=r.translate("Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X/C/V keyboard shortcuts instead.");n.mac&&(a=a.replace(/Ctrl\+/g,"\u2318+")),r.windowManager.alert(a)}},unlink:function(){if(_.isCollapsed()){var e=_.getNode();return void("A"==e.tagName&&r.dom.remove(e,!0))}S.remove("link")},"JustifyLeft,JustifyCenter,JustifyRight,JustifyFull":function(e){var t=e.substring(7);"full"==t&&(t="justify"),i("left,center,right,justify".split(","),function(e){t!=e&&S.remove("align"+e)}),b("align"+t),p("mceRepaint")},"InsertUnorderedList,InsertOrderedList":function(e){var t,n;v(e),t=w.getParent(_.getNode(),"ol,ul"),t&&(n=t.parentNode,/^(H[1-6]|P|ADDRESS|PRE)$/.test(n.nodeName)&&(C(),w.split(n,t),x()))},"Bold,Italic,Underline,Strikethrough,Superscript,Subscript":function(e){b(e)},"ForeColor,HiliteColor,FontName":function(e,t,n){b(e,n)},FontSize:function(e,t,n){var r,i;n>=1&&7>=n&&(i=l(E.font_size_style_values),r=l(E.font_size_classes),n=r?r[n-1]||n:i[n-1]||n),b(e,n)},RemoveFormat:function(e){S.remove(e)},mceBlockQuote:function(){b("blockquote")},FormatBlock:function(e,t,n){return b(n||"p")},mceCleanup:function(){var e=_.getBookmark();r.setContent(r.getContent({cleanup:d}),{cleanup:d}),_.moveToBookmark(e)},mceRemoveNode:function(e,t,n){var i=n||_.getNode();i!=r.getBody()&&(C(),r.dom.remove(i,d),x())},mceSelectNodeDepth:function(e,t,n){var i=0;w.getParent(_.getNode(),function(e){return 1==e.nodeType&&i++==n?(_.select(e),f):void 0},r.getBody())},mceSelectNode:function(e,t,n){_.select(n)},mceInsertContent:function(t,n,i){function o(e){function t(e){return r[e]&&3==r[e].nodeType}var n,r,i;return n=_.getRng(!0),r=n.startContainer,i=n.startOffset,3==r.nodeType&&(i>0?e=e.replace(/^&nbsp;/," "):t("previousSibling")||(e=e.replace(/^ /,"&nbsp;")),i<r.length?e=e.replace(/&nbsp;(<br>|)$/," "):t("nextSibling")||(e=e.replace(/(&nbsp;| )(<br>|)$/,"&nbsp;"))),e}var a,s,l,c,d,f,p,h,m,g,v;/^ | $/.test(i)&&(i=o(i)),a=r.parser,s=new e({},r.schema),v='<span id="mce_marker" data-mce-type="bookmark">&#xFEFF;&#200B;</span>',f={content:i,format:"html",selection:!0},r.fire("BeforeSetContent",f),i=f.content,-1==i.indexOf("{$caret}")&&(i+="{$caret}"),i=i.replace(/\{\$caret\}/,v),h=_.getRng();var y=h.startContainer||(h.parentElement?h.parentElement():null),b=r.getBody();y===b&&_.isCollapsed()&&w.isBlock(b.firstChild)&&w.isEmpty(b.firstChild)&&(h=w.createRng(),h.setStart(b.firstChild,0),h.setEnd(b.firstChild,0),_.setRng(h)),_.isCollapsed()||r.getDoc().execCommand("Delete",!1,null),l=_.getNode();var C={context:l.nodeName.toLowerCase()};if(d=a.parse(i,C),m=d.lastChild,"mce_marker"==m.attr("id"))for(p=m,m=m.prev;m;m=m.walk(!0))if(3==m.type||!w.isBlock(m.name)){m.parent.insert(p,m,"br"===m.name);break}if(C.invalid){for(_.setContent(v),l=_.getNode(),c=r.getBody(),9==l.nodeType?l=m=c:m=l;m!==c;)l=m,m=m.parentNode;i=l==c?c.innerHTML:w.getOuterHTML(l),i=s.serialize(a.parse(i.replace(/<span (id="mce_marker"|id=mce_marker).+?<\/span>/i,function(){return s.serialize(d)}))),l==c?w.setHTML(c,i):w.setOuterHTML(l,i)}else i=s.serialize(d),m=l.firstChild,g=l.lastChild,!m||m===g&&"BR"===m.nodeName?w.setHTML(l,i):_.setContent(i);p=w.get("mce_marker"),_.scrollIntoView(p),h=w.createRng(),m=p.previousSibling,m&&3==m.nodeType?(h.setStart(m,m.nodeValue.length),u||(g=p.nextSibling,g&&3==g.nodeType&&(m.appendData(g.data),g.parentNode.removeChild(g)))):(h.setStartBefore(p),h.setEndBefore(p)),w.remove(p),_.setRng(h),r.fire("SetContent",f),r.addVisual()},mceInsertRawHTML:function(e,t,n){_.setContent("tiny_mce_marker"),r.setContent(r.getContent().replace(/tiny_mce_marker/g,function(){return n}))},mceToggleFormat:function(e,t,n){b(n)},mceSetContent:function(e,t,n){r.setContent(n)},"Indent,Outdent":function(e){var t,n,o;t=E.indentation,n=/[a-z%]+$/i.exec(t),t=parseInt(t,10),h("InsertUnorderedList")||h("InsertOrderedList")?v(e):(E.forced_root_block||w.getParent(_.getNode(),w.isBlock)||S.apply("div"),i(_.getSelectedBlocks(),function(i){if("LI"!=i.nodeName){var a=r.getParam("indent_use_margin",!1)?"margin":"padding";a+="rtl"==w.getStyle(i,"direction",!0)?"Right":"Left","outdent"==e?(o=Math.max(0,parseInt(i.style[a]||0,10)-t),w.setStyle(i,a,o?o+n:"")):(o=parseInt(i.style[a]||0,10)+t+n,w.setStyle(i,a,o))}}))},mceRepaint:function(){if(c)try{C(d),_.getSel()&&_.getSel().selectAllChildren(r.getBody()),_.collapse(d),x()}catch(e){}},InsertHorizontalRule:function(){r.execCommand("mceInsertContent",!1,"<hr />")},mceToggleVisualAid:function(){r.hasVisual=!r.hasVisual,r.addVisual()},mceReplaceContent:function(e,t,n){r.execCommand("mceInsertContent",!1,n.replace(/\{\$selection\}/g,_.getContent({format:"text"})))},mceInsertLink:function(e,t,n){var r;"string"==typeof n&&(n={href:n}),r=w.getParent(_.getNode(),"a"),n.href=n.href.replace(" ","%20"),r&&n.href||S.remove("link"),n.href&&S.apply("link",n,r)},selectAll:function(){var e=w.getRoot(),t;_.getRng().setStart?(t=w.createRng(),t.setStart(e,0),t.setEnd(e,e.childNodes.length),_.setRng(t)):(t=_.getRng(),t.item||(t.moveToElementText(e),t.select()))},"delete":function(){v("Delete");var e=r.getBody();w.isEmpty(e)&&(r.setContent(""),e.firstChild&&w.isBlock(e.firstChild)?r.selection.setCursorLocation(e.firstChild,0):r.selection.setCursorLocation(e,0))},mceNewDocument:function(){r.setContent("")}}),g({"JustifyLeft,JustifyCenter,JustifyRight,JustifyFull":function(e){var t="align"+e.substring(7),n=_.isCollapsed()?[w.getParent(_.getNode(),w.isBlock)]:_.getSelectedBlocks(),r=a(n,function(e){return!!S.matchNode(e,t)});return-1!==s(r,d)},"Bold,Italic,Underline,Strikethrough,Superscript,Subscript":function(e){return y(e)},mceBlockQuote:function(){return y("blockquote")},Outdent:function(){var e;if(E.inline_styles){if((e=w.getParent(_.getStart(),w.isBlock))&&parseInt(e.style.paddingLeft,10)>0)return d;if((e=w.getParent(_.getEnd(),w.isBlock))&&parseInt(e.style.paddingLeft,10)>0)return d
+}return h("InsertUnorderedList")||h("InsertOrderedList")||!E.inline_styles&&!!w.getParent(_.getNode(),"BLOCKQUOTE")},"InsertUnorderedList,InsertOrderedList":function(e){var t=w.getParent(_.getNode(),"ul,ol");return t&&("insertunorderedlist"===e&&"UL"===t.tagName||"insertorderedlist"===e&&"OL"===t.tagName)}},"state"),g({"FontSize,FontName":function(e){var t=0,n;return(n=w.getParent(_.getNode(),"span"))&&(t="fontsize"==e?n.style.fontSize:n.style.fontFamily.replace(/, /g,",").replace(/[\'\"]/g,"").toLowerCase()),t}},"value"),g({Undo:function(){r.undoManager.undo()},Redo:function(){r.undoManager.redo()}})}}),r(F,[p],function(e){function t(e,i){var o=this,a,s;if(e=r(e),i=o.settings=i||{},/^([\w\-]+):([^\/]{2})/i.test(e)||/^\s*#/.test(e))return void(o.source=e);var l=0===e.indexOf("//");0!==e.indexOf("/")||l||(e=(i.base_uri?i.base_uri.protocol||"http":"http")+"://mce_host"+e),/^[\w\-]*:?\/\//.test(e)||(s=i.base_uri?i.base_uri.path:new t(location.href).directory,e=""===i.base_uri.protocol?"//mce_host"+o.toAbsPath(s,e):(i.base_uri&&i.base_uri.protocol||"http")+"://mce_host"+o.toAbsPath(s,e)),e=e.replace(/@@/g,"(mce_at)"),e=/^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(e),n(["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],function(t,n){var r=e[n];r&&(r=r.replace(/\(mce_at\)/g,"@@")),o[t]=r}),a=i.base_uri,a&&(o.protocol||(o.protocol=a.protocol),o.userInfo||(o.userInfo=a.userInfo),o.port||"mce_host"!==o.host||(o.port=a.port),o.host&&"mce_host"!==o.host||(o.host=a.host),o.source=""),l&&(o.protocol="")}var n=e.each,r=e.trim,i={ftp:21,http:80,https:443,mailto:25};return t.prototype={setPath:function(e){var t=this;e=/^(.*?)\/?(\w+)?$/.exec(e),t.path=e[0],t.directory=e[1],t.file=e[2],t.source="",t.getURI()},toRelative:function(e){var n=this,r;if("./"===e)return e;if(e=new t(e,{base_uri:n}),"mce_host"!=e.host&&n.host!=e.host&&e.host||n.port!=e.port||n.protocol!=e.protocol&&""!==e.protocol)return e.getURI();var i=n.getURI(),o=e.getURI();return i==o||"/"==i.charAt(i.length-1)&&i.substr(0,i.length-1)==o?i:(r=n.toRelPath(n.path,e.path),e.query&&(r+="?"+e.query),e.anchor&&(r+="#"+e.anchor),r)},toAbsolute:function(e,n){return e=new t(e,{base_uri:this}),e.getURI(n&&this.isSameOrigin(e))},isSameOrigin:function(e){if(this.host==e.host&&this.protocol==e.protocol){if(this.port==e.port)return!0;var t=i[this.protocol];if(t&&(this.port||t)==(e.port||t))return!0}return!1},toRelPath:function(e,t){var n,r=0,i="",o,a;if(e=e.substring(0,e.lastIndexOf("/")),e=e.split("/"),n=t.split("/"),e.length>=n.length)for(o=0,a=e.length;a>o;o++)if(o>=n.length||e[o]!=n[o]){r=o+1;break}if(e.length<n.length)for(o=0,a=n.length;a>o;o++)if(o>=e.length||e[o]!=n[o]){r=o+1;break}if(1===r)return t;for(o=0,a=e.length-(r-1);a>o;o++)i+="../";for(o=r-1,a=n.length;a>o;o++)i+=o!=r-1?"/"+n[o]:n[o];return i},toAbsPath:function(e,t){var r,i=0,o=[],a,s;for(a=/\/$/.test(t)?"/":"",e=e.split("/"),t=t.split("/"),n(e,function(e){e&&o.push(e)}),e=o,r=t.length-1,o=[];r>=0;r--)0!==t[r].length&&"."!==t[r]&&(".."!==t[r]?i>0?i--:o.push(t[r]):i++);return r=e.length-i,s=0>=r?o.reverse().join("/"):e.slice(0,r).join("/")+"/"+o.reverse().join("/"),0!==s.indexOf("/")&&(s="/"+s),a&&s.lastIndexOf("/")!==s.length-1&&(s+=a),s},getURI:function(e){var t,n=this;return(!n.source||e)&&(t="",e||(t+=n.protocol?n.protocol+"://":"//",n.userInfo&&(t+=n.userInfo+"@"),n.host&&(t+=n.host),n.port&&(t+=":"+n.port)),n.path&&(t+=n.path),n.query&&(t+="?"+n.query),n.anchor&&(t+="#"+n.anchor),n.source=t),n.source}},t}),r(z,[p],function(e){function t(){}var n=e.each,r=e.extend,i,o;return t.extend=i=function(e){function t(){var e,t,n,r=this;if(!o&&(r.init&&r.init.apply(r,arguments),t=r.Mixins))for(e=t.length;e--;)n=t[e],n.init&&n.init.apply(r,arguments)}function a(){return this}function s(e,t){return function(){var n=this,r=n._super,i;return n._super=c[e],i=t.apply(n,arguments),n._super=r,i}}var l=this,c=l.prototype,u,d,f;o=!0,u=new l,o=!1,e.Mixins&&(n(e.Mixins,function(t){t=t;for(var n in t)"init"!==n&&(e[n]=t[n])}),c.Mixins&&(e.Mixins=c.Mixins.concat(e.Mixins))),e.Methods&&n(e.Methods.split(","),function(t){e[t]=a}),e.Properties&&n(e.Properties.split(","),function(t){var n="_"+t;e[t]=function(e){var t=this,r;return e!==r?(t[n]=e,t):t[n]}}),e.Statics&&n(e.Statics,function(e,n){t[n]=e}),e.Defaults&&c.Defaults&&(e.Defaults=r({},c.Defaults,e.Defaults));for(d in e)f=e[d],u[d]="function"==typeof f&&c[d]?s(d,f):f;return t.prototype=u,t.constructor=t,t.extend=i,t},t}),r(W,[p],function(e){function t(e){function t(){return!1}function n(){return!0}function r(r,i){var o,a,s,u;if(r=r.toLowerCase(),i=i||{},i.type=r,i.target||(i.target=l),i.preventDefault||(i.preventDefault=function(){i.isDefaultPrevented=n},i.stopPropagation=function(){i.isPropagationStopped=n},i.stopImmediatePropagation=function(){i.isImmediatePropagationStopped=n},i.isDefaultPrevented=t,i.isPropagationStopped=t,i.isImmediatePropagationStopped=t),e.beforeFire&&e.beforeFire(i),o=c[r])for(a=0,s=o.length;s>a;a++){if(o[a]=u=o[a],i.isImmediatePropagationStopped())return i.stopPropagation(),i;if(u.call(l,i)===!1)return i.preventDefault(),i}return i}function i(e,n,r){var i,o,a;if(n===!1&&(n=t),n)for(o=e.toLowerCase().split(" "),a=o.length;a--;)e=o[a],i=c[e],i||(i=c[e]=[],u(e,!0)),r?i.unshift(n):i.push(n);return s}function o(e,t){var n,r,i,o,a;if(e)for(o=e.toLowerCase().split(" "),n=o.length;n--;){if(e=o[n],r=c[e],!e){for(i in c)u(i,!1),delete c[i];return s}if(r){if(t)for(a=r.length;a--;)r[a]===t&&r.splice(a,1);else r.length=0;r.length||(u(e,!1),delete c[e])}}else{for(e in c)u(e,!1);c={}}return s}function a(e){return e=e.toLowerCase(),!(!c[e]||0===c[e].length)}var s=this,l,c={},u;e=e||{},l=e.scope||s,u=e.toggleEvent||t,s.fire=r,s.on=i,s.off=o,s.has=a}var n=e.makeMap("focus blur focusin focusout click dblclick mousedown mouseup mousemove mouseover beforepaste paste cut copy selectionchange mouseout mouseenter mouseleave wheel keydown keypress keyup input contextmenu dragstart dragend dragover draggesture dragdrop drop drag submit"," ");return t.isNative=function(e){return!!n[e.toLowerCase()]},t}),r(V,[z],function(e){function t(e){for(var t=[],n=e.length,r;n--;)r=e[n],r.__checked||(t.push(r),r.__checked=1);for(n=t.length;n--;)delete t[n].__checked;return t}var n=/^([\w\\*]+)?(?:#([\w\\]+))?(?:\.([\w\\\.]+))?(?:\[\@?([\w\\]+)([\^\$\*!~]?=)([\w\\]+)\])?(?:\:(.+))?/i,r=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,i=/^\s*|\s*$/g,o,a=e.extend({init:function(e){function t(e){return e?(e=e.toLowerCase(),function(t){return"*"===e||t.type===e}):void 0}function o(e){return e?function(t){return t._name===e}:void 0}function a(e){return e?(e=e.split("."),function(t){for(var n=e.length;n--;)if(!t.hasClass(e[n]))return!1;return!0}):void 0}function s(e,t,n){return e?function(r){var i=r[e]?r[e]():"";return t?"="===t?i===n:"*="===t?i.indexOf(n)>=0:"~="===t?(" "+i+" ").indexOf(" "+n+" ")>=0:"!="===t?i!=n:"^="===t?0===i.indexOf(n):"$="===t?i.substr(i.length-n.length)===n:!1:!!n}:void 0}function l(e){var t;return e?(e=/(?:not\((.+)\))|(.+)/i.exec(e),e[1]?(t=u(e[1],[]),function(e){return!d(e,t)}):(e=e[2],function(t,n,r){return"first"===e?0===n:"last"===e?n===r-1:"even"===e?n%2===0:"odd"===e?n%2===1:t[e]?t[e]():!1})):void 0}function c(e,r,c){function u(e){e&&r.push(e)}var d;return d=n.exec(e.replace(i,"")),u(t(d[1])),u(o(d[2])),u(a(d[3])),u(s(d[4],d[5],d[6])),u(l(d[7])),r.psuedo=!!d[7],r.direct=c,r}function u(e,t){var n=[],i,o,a;do if(r.exec(""),o=r.exec(e),o&&(e=o[3],n.push(o[1]),o[2])){i=o[3];break}while(o);for(i&&u(i,t),e=[],a=0;a<n.length;a++)">"!=n[a]&&e.push(c(n[a],[],">"===n[a-1]));return t.push(e),t}var d=this.match;this._selectors=u(e,[])},match:function(e,t){var n,r,i,o,a,s,l,c,u,d,f,p,h;for(t=t||this._selectors,n=0,r=t.length;r>n;n++){for(a=t[n],o=a.length,h=e,p=0,i=o-1;i>=0;i--)for(c=a[i];h;){if(c.psuedo)for(f=h.parent().items(),u=d=f.length;u--&&f[u]!==h;);for(s=0,l=c.length;l>s;s++)if(!c[s](h,u,d)){s=l+1;break}if(s===l){p++;break}if(i===o-1)break;h=h.parent()}if(p===o)return!0}return!1},find:function(e){function n(e,t,i){var o,a,s,l,c,u=t[i];for(o=0,a=e.length;a>o;o++){for(c=e[o],s=0,l=u.length;l>s;s++)if(!u[s](c,o,a)){s=l+1;break}if(s===l)i==t.length-1?r.push(c):c.items&&n(c.items(),t,i+1);else if(u.direct)return;c.items&&n(c.items(),t,i)}}var r=[],i,s,l=this._selectors;if(e.items){for(i=0,s=l.length;s>i;i++)n(e.items(),l[i],0);s>1&&(r=t(r))}return o||(o=a.Collection),new o(r)}});return a}),r(U,[p,V,z],function(e,t,n){var r,i,o=Array.prototype.push,a=Array.prototype.slice;return i={length:0,init:function(e){e&&this.add(e)},add:function(t){var n=this;return e.isArray(t)?o.apply(n,t):t instanceof r?n.add(t.toArray()):o.call(n,t),n},set:function(e){var t=this,n=t.length,r;for(t.length=0,t.add(e),r=t.length;n>r;r++)delete t[r];return t},filter:function(e){var n=this,i,o,a=[],s,l;for("string"==typeof e?(e=new t(e),l=function(t){return e.match(t)}):l=e,i=0,o=n.length;o>i;i++)s=n[i],l(s)&&a.push(s);return new r(a)},slice:function(){return new r(a.apply(this,arguments))},eq:function(e){return-1===e?this.slice(e):this.slice(e,+e+1)},each:function(t){return e.each(this,t),this},toArray:function(){return e.toArray(this)},indexOf:function(e){for(var t=this,n=t.length;n--&&t[n]!==e;);return n},reverse:function(){return new r(e.toArray(this).reverse())},hasClass:function(e){return this[0]?this[0].hasClass(e):!1},prop:function(e,t){var n=this,r,i;return t!==r?(n.each(function(n){n[e]&&n[e](t)}),n):(i=n[0],i&&i[e]?i[e]():void 0)},exec:function(t){var n=this,r=e.toArray(arguments).slice(1);return n.each(function(e){e[t]&&e[t].apply(e,r)}),n},remove:function(){for(var e=this.length;e--;)this[e].remove();return this}},e.each("fire on off show hide addClass removeClass append prepend before after reflow".split(" "),function(t){i[t]=function(){var n=e.toArray(arguments);return this.each(function(e){t in e&&e[t].apply(e,n)}),this}}),e.each("text name disabled active selected checked visible parent value data".split(" "),function(e){i[e]=function(t){return this.prop(e,t)}}),r=n.extend(i),t.Collection=r,r}),r(q,[p,y],function(e,t){return{id:function(){return t.DOM.uniqueId()},createFragment:function(e){return t.DOM.createFragment(e)},getWindowSize:function(){return t.DOM.getViewPort()},getSize:function(e){var t,n;if(e.getBoundingClientRect){var r=e.getBoundingClientRect();t=Math.max(r.width||r.right-r.left,e.offsetWidth),n=Math.max(r.height||r.bottom-r.bottom,e.offsetHeight)}else t=e.offsetWidth,n=e.offsetHeight;return{width:t,height:n}},getPos:function(e,n){return t.DOM.getPos(e,n)},getViewPort:function(e){return t.DOM.getViewPort(e)},get:function(e){return document.getElementById(e)},addClass:function(e,n){return t.DOM.addClass(e,n)},removeClass:function(e,n){return t.DOM.removeClass(e,n)},hasClass:function(e,n){return t.DOM.hasClass(e,n)},toggleClass:function(e,n,r){return t.DOM.toggleClass(e,n,r)},css:function(e,n,r){return t.DOM.setStyle(e,n,r)},on:function(e,n,r,i){return t.DOM.bind(e,n,r,i)},off:function(e,n,r){return t.DOM.unbind(e,n,r)},fire:function(e,n,r){return t.DOM.fire(e,n,r)},innerHtml:function(e,n){t.DOM.setHTML(e,n)}}}),r($,[z,p,W,U,q],function(e,t,n,r,i){function o(e){return e._eventDispatcher||(e._eventDispatcher=new n({scope:e,toggleEvent:function(t,r){r&&n.isNative(t)&&(e._nativeEvents||(e._nativeEvents={}),e._nativeEvents[t]=!0,e._rendered&&e.bindPendingEvents())}})),e._eventDispatcher}var a={},s="onmousewheel"in document,l=!1,c="mce-",u=e.extend({Statics:{elementIdCache:a,classPrefix:c},isRtl:function(){return u.rtl},classPrefix:c,init:function(e){var n=this,r,o;if(n.settings=e=t.extend({},n.Defaults,e),n._id=e.id||i.id(),n._text=n._name="",n._width=n._height=0,n._aria={role:e.role},r=e.classes)for(r=r.split(" "),r.map={},o=r.length;o--;)r.map[r[o]]=!0;n._classes=r||[],n.visible(!0),t.each("title text width height name classes visible disabled active value".split(" "),function(t){var r=e[t],i;r!==i?n[t](r):n["_"+t]===i&&(n["_"+t]=!1)}),n.on("click",function(){return n.disabled()?!1:void 0}),e.classes&&t.each(e.classes.split(" "),function(e){n.addClass(e)}),n.settings=e,n._borderBox=n.parseBox(e.border),n._paddingBox=n.parseBox(e.padding),n._marginBox=n.parseBox(e.margin),e.hidden&&n.hide()},Properties:"parent,title,text,width,height,disabled,active,name,value",Methods:"renderHtml",getContainerElm:function(){return document.body},getParentCtrl:function(e){for(var t,n=this.getRoot().controlIdLookup;e&&n&&!(t=n[e.id]);)e=e.parentNode;return t},parseBox:function(e){var t,n=10;if(e)return"number"==typeof e?(e=e||0,{top:e,left:e,bottom:e,right:e}):(e=e.split(" "),t=e.length,1===t?e[1]=e[2]=e[3]=e[0]:2===t?(e[2]=e[0],e[3]=e[1]):3===t&&(e[3]=e[1]),{top:parseInt(e[0],n)||0,right:parseInt(e[1],n)||0,bottom:parseInt(e[2],n)||0,left:parseInt(e[3],n)||0})},borderBox:function(){return this._borderBox},paddingBox:function(){return this._paddingBox},marginBox:function(){return this._marginBox},measureBox:function(e,t){function n(t){var n=document.defaultView;return n?(t=t.replace(/[A-Z]/g,function(e){return"-"+e}),n.getComputedStyle(e,null).getPropertyValue(t)):e.currentStyle[t]}function r(e){var t=parseFloat(n(e),10);return isNaN(t)?0:t}return{top:r(t+"TopWidth"),right:r(t+"RightWidth"),bottom:r(t+"BottomWidth"),left:r(t+"LeftWidth")}},initLayoutRect:function(){var e=this,t=e.settings,n,r,o=e.getEl(),a,s,l,c,u,d,f,p;n=e._borderBox=e._borderBox||e.measureBox(o,"border"),e._paddingBox=e._paddingBox||e.measureBox(o,"padding"),e._marginBox=e._marginBox||e.measureBox(o,"margin"),p=i.getSize(o),d=t.minWidth,f=t.minHeight,l=d||p.width,c=f||p.height,a=t.width,s=t.height,u=t.autoResize,u="undefined"!=typeof u?u:!a&&!s,a=a||l,s=s||c;var h=n.left+n.right,m=n.top+n.bottom,g=t.maxWidth||65535,v=t.maxHeight||65535;return e._layoutRect=r={x:t.x||0,y:t.y||0,w:a,h:s,deltaW:h,deltaH:m,contentW:a-h,contentH:s-m,innerW:a-h,innerH:s-m,startMinWidth:d||0,startMinHeight:f||0,minW:Math.min(l,g),minH:Math.min(c,v),maxW:g,maxH:v,autoResize:u,scrollW:0},e._lastLayoutRect={},r},layoutRect:function(e){var t=this,n=t._layoutRect,r,i,o,a,s,l;return n||(n=t.initLayoutRect()),e?(o=n.deltaW,a=n.deltaH,e.x!==s&&(n.x=e.x),e.y!==s&&(n.y=e.y),e.minW!==s&&(n.minW=e.minW),e.minH!==s&&(n.minH=e.minH),i=e.w,i!==s&&(i=i<n.minW?n.minW:i,i=i>n.maxW?n.maxW:i,n.w=i,n.innerW=i-o),i=e.h,i!==s&&(i=i<n.minH?n.minH:i,i=i>n.maxH?n.maxH:i,n.h=i,n.innerH=i-a),i=e.innerW,i!==s&&(i=i<n.minW-o?n.minW-o:i,i=i>n.maxW-o?n.maxW-o:i,n.innerW=i,n.w=i+o),i=e.innerH,i!==s&&(i=i<n.minH-a?n.minH-a:i,i=i>n.maxH-a?n.maxH-a:i,n.innerH=i,n.h=i+a),e.contentW!==s&&(n.contentW=e.contentW),e.contentH!==s&&(n.contentH=e.contentH),r=t._lastLayoutRect,(r.x!==n.x||r.y!==n.y||r.w!==n.w||r.h!==n.h)&&(l=u.repaintControls,l&&l.map&&!l.map[t._id]&&(l.push(t),l.map[t._id]=!0),r.x=n.x,r.y=n.y,r.w=n.w,r.h=n.h),t):n},repaint:function(){var e=this,t,n,r,i,o=0,a=0,s,l;l=document.createRange?function(e){return e}:Math.round,t=e.getEl().style,r=e._layoutRect,s=e._lastRepaintRect||{},i=e._borderBox,o=i.left+i.right,a=i.top+i.bottom,r.x!==s.x&&(t.left=l(r.x)+"px",s.x=r.x),r.y!==s.y&&(t.top=l(r.y)+"px",s.y=r.y),r.w!==s.w&&(t.width=l(r.w-o)+"px",s.w=r.w),r.h!==s.h&&(t.height=l(r.h-a)+"px",s.h=r.h),e._hasBody&&r.innerW!==s.innerW&&(n=e.getEl("body").style,n.width=l(r.innerW)+"px",s.innerW=r.innerW),e._hasBody&&r.innerH!==s.innerH&&(n=n||e.getEl("body").style,n.height=l(r.innerH)+"px",s.innerH=r.innerH),e._lastRepaintRect=s,e.fire("repaint",{},!1)},on:function(e,t){function n(e){var t,n;return"string"!=typeof e?e:function(i){return t||r.parentsAndSelf().each(function(r){var i=r.settings.callbacks;return i&&(t=i[e])?(n=r,!1):void 0}),t.call(n,i)}}var r=this;return o(r).on(e,n(t)),r},off:function(e,t){return o(this).off(e,t),this},fire:function(e,t,n){var r=this;if(t=t||{},t.control||(t.control=r),t=o(r).fire(e,t),n!==!1&&r.parent)for(var i=r.parent();i&&!t.isPropagationStopped();)i.fire(e,t,!1),i=i.parent();return t},hasEventListeners:function(e){return o(this).has(e)},parents:function(e){var t=this,n,i=new r;for(n=t.parent();n;n=n.parent())i.add(n);return e&&(i=i.filter(e)),i},parentsAndSelf:function(e){return new r(this).add(this.parents(e))},next:function(){var e=this.parent().items();return e[e.indexOf(this)+1]},prev:function(){var e=this.parent().items();return e[e.indexOf(this)-1]},findCommonAncestor:function(e,t){for(var n;e;){for(n=t;n&&e!=n;)n=n.parent();if(e==n)break;e=e.parent()}return e},hasClass:function(e,t){var n=this._classes[t||"control"];return e=this.classPrefix+e,n&&!!n.map[e]},addClass:function(e,t){var n=this,r,i;return e=this.classPrefix+e,r=n._classes[t||"control"],r||(r=[],r.map={},n._classes[t||"control"]=r),r.map[e]||(r.map[e]=e,r.push(e),n._rendered&&(i=n.getEl(t),i&&(i.className=r.join(" ")))),n},removeClass:function(e,t){var n=this,r,i,o;if(e=this.classPrefix+e,r=n._classes[t||"control"],r&&r.map[e])for(delete r.map[e],i=r.length;i--;)r[i]===e&&r.splice(i,1);return n._rendered&&(o=n.getEl(t),o&&(o.className=r.join(" "))),n},toggleClass:function(e,t,n){var r=this;return t?r.addClass(e,n):r.removeClass(e,n),r},classes:function(e){var t=this._classes[e||"control"];return t?t.join(" "):""},innerHtml:function(e){return i.innerHtml(this.getEl(),e),this},getEl:function(e,t){var n,r=e?this._id+"-"+e:this._id;return n=a[r]=(t===!0?null:a[r])||i.get(r)},visible:function(e){var t=this,n;return"undefined"!=typeof e?(t._visible!==e&&(t._rendered&&(t.getEl().style.display=e?"":"none"),t._visible=e,n=t.parent(),n&&(n._lastRect=null),t.fire(e?"show":"hide")),t):t._visible},show:function(){return this.visible(!0)},hide:function(){return this.visible(!1)},focus:function(){try{this.getEl().focus()}catch(e){}return this},blur:function(){return this.getEl().blur(),this},aria:function(e,t){var n=this,r=n.getEl(n.ariaTarget);return"undefined"==typeof t?n._aria[e]:(n._aria[e]=t,n._rendered&&r.setAttribute("role"==e?e:"aria-"+e,t),n)},encode:function(e,t){return t!==!1&&(e=this.translate(e)),(e||"").replace(/[&<>"]/g,function(e){return"&#"+e.charCodeAt(0)+";"})},translate:function(e){return u.translate?u.translate(e):e},before:function(e){var t=this,n=t.parent();return n&&n.insert(e,n.items().indexOf(t),!0),t},after:function(e){var t=this,n=t.parent();return n&&n.insert(e,n.items().indexOf(t)),t},remove:function(){var e=this,t=e.getEl(),n=e.parent(),r,o;if(e.items){var s=e.items().toArray();for(o=s.length;o--;)s[o].remove()}n&&n.items&&(r=[],n.items().each(function(t){t!==e&&r.push(t)}),n.items().set(r),n._lastRect=null),e._eventsRoot&&e._eventsRoot==e&&i.off(t);var l=e.getRoot().controlIdLookup;if(l&&delete l[e._id],delete a[e._id],t&&t.parentNode){var c=t.getElementsByTagName("*");for(o=c.length;o--;)delete a[c[o].id];t.parentNode.removeChild(t)}return e._rendered=!1,e},renderBefore:function(e){var t=this;return e.parentNode.insertBefore(i.createFragment(t.renderHtml()),e),t.postRender(),t},renderTo:function(e){var t=this;return e=e||t.getContainerElm(),e.appendChild(i.createFragment(t.renderHtml())),t.postRender(),t},postRender:function(){var e=this,t=e.settings,n,r,o,a,s;for(a in t)0===a.indexOf("on")&&e.on(a.substr(2),t[a]);if(e._eventsRoot){for(o=e.parent();!s&&o;o=o.parent())s=o._eventsRoot;if(s)for(a in s._nativeEvents)e._nativeEvents[a]=!0}e.bindPendingEvents(),t.style&&(n=e.getEl(),n&&(n.setAttribute("style",t.style),n.style.cssText=t.style)),e._visible||i.css(e.getEl(),"display","none"),e.settings.border&&(r=e.borderBox(),i.css(e.getEl(),{"border-top-width":r.top,"border-right-width":r.right,"border-bottom-width":r.bottom,"border-left-width":r.left}));var l=e.getRoot();l.controlIdLookup||(l.controlIdLookup={}),l.controlIdLookup[e._id]=e;for(var c in e._aria)e.aria(c,e._aria[c]);e.fire("postrender",{},!1)},scrollIntoView:function(e){function t(e,t){var n,r,i=e;for(n=r=0;i&&i!=t&&i.nodeType;)n+=i.offsetLeft||0,r+=i.offsetTop||0,i=i.offsetParent;return{x:n,y:r}}var n=this.getEl(),r=n.parentNode,i,o,a,s,l,c,u=t(n,r);return i=u.x,o=u.y,a=n.offsetWidth,s=n.offsetHeight,l=r.clientWidth,c=r.clientHeight,"end"==e?(i-=l-a,o-=c-s):"center"==e&&(i-=l/2-a/2,o-=c/2-s/2),r.scrollLeft=i,r.scrollTop=o,this},bindPendingEvents:function(){function e(e){var t=o.getParentCtrl(e.target);t&&t.fire(e.type,e)}function t(){var e=d._lastHoverCtrl;e&&(e.fire("mouseleave",{target:e.getEl()}),e.parents().each(function(e){e.fire("mouseleave",{target:e.getEl()})}),d._lastHoverCtrl=null)}function n(e){var t=o.getParentCtrl(e.target),n=d._lastHoverCtrl,r=0,i,a,s;if(t!==n){if(d._lastHoverCtrl=t,a=t.parents().toArray().reverse(),a.push(t),n){for(s=n.parents().toArray().reverse(),s.push(n),r=0;r<s.length&&a[r]===s[r];r++);for(i=s.length-1;i>=r;i--)n=s[i],n.fire("mouseleave",{target:n.getEl()})}for(i=r;i<a.length;i++)t=a[i],t.fire("mouseenter",{target:t.getEl()})}}function r(e){e.preventDefault(),"mousewheel"==e.type?(e.deltaY=-1/40*e.wheelDelta,e.wheelDeltaX&&(e.deltaX=-1/40*e.wheelDeltaX)):(e.deltaX=0,e.deltaY=e.detail),e=o.fire("wheel",e)}var o=this,a,c,u,d,f,p;if(o._rendered=!0,f=o._nativeEvents){for(u=o.parents().toArray(),u.unshift(o),a=0,c=u.length;!d&&c>a;a++)d=u[a]._eventsRoot;for(d||(d=u[u.length-1]||o),o._eventsRoot=d,c=a,a=0;c>a;a++)u[a]._eventsRoot=d;var h=d._delegates;h||(h=d._delegates={});for(p in f){if(!f)return!1;"wheel"!==p||l?("mouseenter"===p||"mouseleave"===p?d._hasMouseEnter||(i.on(d.getEl(),"mouseleave",t),i.on(d.getEl(),"mouseover",n),d._hasMouseEnter=1):h[p]||(i.on(d.getEl(),p,e),h[p]=!0),f[p]=!1):s?i.on(o.getEl(),"mousewheel",r):i.on(o.getEl(),"DOMMouseScroll",r)}}},getRoot:function(){for(var e=this,t,n=[];e;){if(e.rootControl){t=e.rootControl;break}n.push(e),t=e,e=e.parent()}t||(t=this);for(var r=n.length;r--;)n[r].rootControl=t;return t},reflow:function(){return this.repaint(),this}});return u}),r(j,[],function(){var e={},t;return{add:function(t,n){e[t.toLowerCase()]=n},has:function(t){return!!e[t.toLowerCase()]},create:function(n,r){var i,o,a;if(!t){a=tinymce.ui;for(o in a)e[o.toLowerCase()]=a[o];t=!0}if("string"==typeof n?(r=r||{},r.type=n):(r=n,n=r.type),n=n.toLowerCase(),i=e[n],!i)throw new Error("Could not find control by type: "+n);return i=new i(r),i.type=n,i}}}),r(K,[],function(){return function(e){function t(e){return e=e||b,e&&e.getAttribute("role")}function n(e){for(var n,r=e||b;r=r.parentNode;)if(n=t(r))return n}function r(e){var t=b;return t?t.getAttribute("aria-"+e):void 0}function i(e){var t=e.tagName.toUpperCase();return"INPUT"==t||"TEXTAREA"==t}function o(e){return i(e)&&!e.hidden?!0:/^(button|menuitem|checkbox|tab|menuitemcheckbox|option|gridcell)$/.test(t(e))?!0:!1}function a(e){function t(e){if(1==e.nodeType&&"none"!=e.style.display){o(e)&&n.push(e);for(var r=0;r<e.childNodes.length;r++)t(e.childNodes[r])}}var n=[];return t(e||y.getEl()),n}function s(e){var t,n;e=e||C,n=e.parents().toArray(),n.unshift(e);for(var r=0;r<n.length&&(t=n[r],!t.settings.ariaRoot);r++);return t}function l(e){var t=s(e),n=a(t.getEl());t.settings.ariaRemember&&"lastAriaIndex"in t?c(t.lastAriaIndex,n):c(0,n)}function c(e,t){return 0>e?e=t.length-1:e>=t.length&&(e=0),t[e]&&t[e].focus(),e}function u(e,t){var n=-1,r=s();t=t||a(r.getEl());for(var i=0;i<t.length;i++)t[i]===b&&(n=i);n+=e,r.lastAriaIndex=c(n,t)}function d(){var e=n();"tablist"==e?u(-1,a(b.parentNode)):C.parent().submenu?g():u(-1)}function f(){var e=t(),i=n();"tablist"==i?u(1,a(b.parentNode)):"menuitem"==e&&"menu"==i&&r("haspopup")?v():u(1)}function p(){u(-1)}function h(){var e=t(),i=n();"menuitem"==e&&"menubar"==i?v():"button"==e&&r("haspopup")?v({key:"down"}):u(1)}function m(e){var t=n();if("tablist"==t){var r=a(C.getEl("body"))[0];r&&r.focus()}else u(e.shiftKey?-1:1)}function g(){C.fire("cancel")}function v(e){e=e||{},C.fire("click",{target:b,aria:e})}var y=e.root,b,C;return b=document.activeElement,C=y.getParentCtrl(b),y.on("keydown",function(e){function t(e,t){i(b)||t(e)!==!1&&e.preventDefault()}if(!e.isDefaultPrevented())switch(e.keyCode){case 37:t(e,d);break;case 39:t(e,f);break;case 38:t(e,p);break;case 40:t(e,h);break;case 27:g();break;case 14:case 13:case 32:t(e,v);break;case 9:m(e)!==!1&&e.preventDefault()}}),y.on("focusin",function(e){b=e.target,C=e.control}),{focusFirst:l}}}),r(G,[$,U,V,j,K,p,q],function(e,t,n,r,i,o,a){var s={};return e.extend({layout:"",innerClass:"container-inner",init:function(e){var n=this;n._super(e),e=n.settings,n._fixed=e.fixed,n._items=new t,n.isRtl()&&n.addClass("rtl"),n.addClass("container"),n.addClass("container-body","body"),e.containerCls&&n.addClass(e.containerCls),n._layout=r.create((e.layout||n.layout)+"layout"),n.settings.items&&n.add(n.settings.items),n._hasBody=!0},items:function(){return this._items},find:function(e){return e=s[e]=s[e]||new n(e),e.find(this)},add:function(e){var t=this;return t.items().add(t.create(e)).parent(t),t},focus:function(e){var t=this,n,r,i;return e&&(r=t.keyboardNav||t.parents().eq(-1)[0].keyboardNav)?void r.focusFirst(t):(i=t.find("*"),t.statusbar&&i.add(t.statusbar.items()),i.each(function(e){return e.settings.autofocus?(n=null,!1):void(e.canFocus&&(n=n||e))}),n&&n.focus(),t)},replace:function(e,t){for(var n,r=this.items(),i=r.length;i--;)if(r[i]===e){r[i]=t;break}i>=0&&(n=t.getEl(),n&&n.parentNode.removeChild(n),n=e.getEl(),n&&n.parentNode.removeChild(n)),t.parent(this)},create:function(t){var n=this,i,a=[];return o.isArray(t)||(t=[t]),o.each(t,function(t){t&&(t instanceof e||("string"==typeof t&&(t={type:t}),i=o.extend({},n.settings.defaults,t),t.type=i.type=i.type||t.type||n.settings.defaultType||(i.defaults?i.defaults.type:null),t=r.create(i)),a.push(t))}),a},renderNew:function(){var e=this;return e.items().each(function(t,n){var r,i;t.parent(e),t._rendered||(r=e.getEl("body"),i=a.createFragment(t.renderHtml()),r.hasChildNodes()&&n<=r.childNodes.length-1?r.insertBefore(i,r.childNodes[n]):r.appendChild(i),t.postRender())}),e._layout.applyClasses(e),e._lastRect=null,e},append:function(e){return this.add(e).renderNew()},prepend:function(e){var t=this;return t.items().set(t.create(e).concat(t.items().toArray())),t.renderNew()},insert:function(e,t,n){var r=this,i,o,a;return e=r.create(e),i=r.items(),!n&&t<i.length-1&&(t+=1),t>=0&&t<i.length&&(o=i.slice(0,t).toArray(),a=i.slice(t).toArray(),i.set(o.concat(e,a))),r.renderNew()},fromJSON:function(e){var t=this;for(var n in e)t.find("#"+n).value(e[n]);return t},toJSON:function(){var e=this,t={};return e.find("*").each(function(e){var n=e.name(),r=e.value();n&&"undefined"!=typeof r&&(t[n]=r)}),t},preRender:function(){},renderHtml:function(){var e=this,t=e._layout,n=this.settings.role;return e.preRender(),t.preRender(e),'<div id="'+e._id+'" class="'+e.classes()+'"'+(n?' role="'+this.settings.role+'"':"")+'><div id="'+e._id+'-body" class="'+e.classes("body")+'">'+(e.settings.html||"")+t.renderHtml(e)+"</div></div>"},postRender:function(){var e=this,t;return e.items().exec("postRender"),e._super(),e._layout.postRender(e),e._rendered=!0,e.settings.style&&a.css(e.getEl(),e.settings.style),e.settings.border&&(t=e.borderBox(),a.css(e.getEl(),{"border-top-width":t.top,"border-right-width":t.right,"border-bottom-width":t.bottom,"border-left-width":t.left})),e.parent()||(e.keyboardNav=new i({root:e})),e},initLayoutRect:function(){var e=this,t=e._super();return e._layout.recalc(e),t},recalc:function(){var e=this,t=e._layoutRect,n=e._lastRect;return n&&n.w==t.w&&n.h==t.h?void 0:(e._layout.recalc(e),t=e.layoutRect(),e._lastRect={x:t.x,y:t.y,w:t.w,h:t.h},!0)},reflow:function(){var t;if(this.visible()){for(e.repaintControls=[],e.repaintControls.map={},this.recalc(),t=e.repaintControls.length;t--;)e.repaintControls[t].repaint();"flow"!==this.settings.layout&&"stack"!==this.settings.layout&&this.repaint(),e.repaintControls=[]}return this}})}),r(Y,[q],function(e){function t(){var e=document,t,n,r,i,o,a,s,l,c=Math.max;return t=e.documentElement,n=e.body,r=c(t.scrollWidth,n.scrollWidth),i=c(t.clientWidth,n.clientWidth),o=c(t.offsetWidth,n.offsetWidth),a=c(t.scrollHeight,n.scrollHeight),s=c(t.clientHeight,n.clientHeight),l=c(t.offsetHeight,n.offsetHeight),{width:o>r?i:r,height:l>a?s:a}}return function(n,r){function i(){return a.getElementById(r.handle||n)}var o,a=document,s,l,c,u,d,f;r=r||{},l=function(n){var l=t(),p,h;n.preventDefault(),s=n.button,p=i(),d=n.screenX,f=n.screenY,h=window.getComputedStyle?window.getComputedStyle(p,null).getPropertyValue("cursor"):p.runtimeStyle.cursor,o=a.createElement("div"),e.css(o,{position:"absolute",top:0,left:0,width:l.width,height:l.height,zIndex:2147483647,opacity:1e-4,background:"red",cursor:h}),a.body.appendChild(o),e.on(a,"mousemove",u),e.on(a,"mouseup",c),r.start(n)},u=function(e){return e.button!==s?c(e):(e.deltaX=e.screenX-d,e.deltaY=e.screenY-f,e.preventDefault(),void r.drag(e))},c=function(t){e.off(a,"mousemove",u),e.off(a,"mouseup",c),o.parentNode.removeChild(o),r.stop&&r.stop(t)},this.destroy=function(){e.off(i())},e.on(i(),"mousedown",l)}}),r(X,[q,Y],function(e,t){return{init:function(){var e=this;e.on("repaint",e.renderScroll)},renderScroll:function(){function n(){function t(t,a,s,l,c,u){var d,f,p,h,m,g,v,y,b;if(f=i.getEl("scroll"+t)){if(y=a.toLowerCase(),b=s.toLowerCase(),i.getEl("absend")&&e.css(i.getEl("absend"),y,i.layoutRect()[l]-1),!c)return void e.css(f,"display","none");e.css(f,"display","block"),d=i.getEl("body"),p=i.getEl("scroll"+t+"t"),h=d["client"+s]-2*o,h-=n&&r?f["client"+u]:0,m=d["scroll"+s],g=h/m,v={},v[y]=d["offset"+a]+o,v[b]=h,e.css(f,v),v={},v[y]=d["scroll"+a]*g,v[b]=h*g,e.css(p,v)}}var n,r,a;a=i.getEl("body"),n=a.scrollWidth>a.clientWidth,r=a.scrollHeight>a.clientHeight,t("h","Left","Width","contentW",n,"Height"),t("v","Top","Height","contentH",r,"Width")}function r(){function n(n,r,a,s,l){var c,u=i._id+"-scroll"+n,d=i.classPrefix;i.getEl().appendChild(e.createFragment('<div id="'+u+'" class="'+d+"scrollbar "+d+"scrollbar-"+n+'"><div id="'+u+'t" class="'+d+'scrollbar-thumb"></div></div>')),i.draghelper=new t(u+"t",{start:function(){c=i.getEl("body")["scroll"+r],e.addClass(e.get(u),d+"active")},drag:function(e){var t,u,d,f,p=i.layoutRect();u=p.contentW>p.innerW,d=p.contentH>p.innerH,f=i.getEl("body")["client"+a]-2*o,f-=u&&d?i.getEl("scroll"+n)["client"+l]:0,t=f/i.getEl("body")["scroll"+a],i.getEl("body")["scroll"+r]=c+e["delta"+s]/t},stop:function(){e.removeClass(e.get(u),d+"active")}})}i.addClass("scroll"),n("v","Top","Height","Y","Width"),n("h","Left","Width","X","Height")}var i=this,o=2;i.settings.autoScroll&&(i._hasScroll||(i._hasScroll=!0,r(),i.on("wheel",function(e){var t=i.getEl("body");t.scrollLeft+=10*(e.deltaX||0),t.scrollTop+=10*e.deltaY,n()}),e.on(i.getEl("body"),"scroll",n)),n())}}}),r(J,[G,X],function(e,t){return e.extend({Defaults:{layout:"fit",containerCls:"panel"},Mixins:[t],renderHtml:function(){var e=this,t=e._layout,n=e.settings.html;return e.preRender(),t.preRender(e),"undefined"==typeof n?n='<div id="'+e._id+'-body" class="'+e.classes("body")+'">'+t.renderHtml(e)+"</div>":("function"==typeof n&&(n=n.call(e)),e._hasBody=!1),'<div id="'+e._id+'" class="'+e.classes()+'" hidefocus="1" tabindex="-1" role="group">'+(e._preBodyHtml||"")+n+"</div>"}})}),r(Q,[q],function(e){function t(t,n,r){var i,o,a,s,l,c,u,d,f,p;return f=e.getViewPort(),o=e.getPos(n),a=o.x,s=o.y,t._fixed&&(a-=f.x,s-=f.y),i=t.getEl(),p=e.getSize(i),l=p.width,c=p.height,p=e.getSize(n),u=p.width,d=p.height,r=(r||"").split(""),"b"===r[0]&&(s+=d),"r"===r[1]&&(a+=u),"c"===r[0]&&(s+=Math.round(d/2)),"c"===r[1]&&(a+=Math.round(u/2)),"b"===r[3]&&(s-=c),"r"===r[4]&&(a-=l),"c"===r[3]&&(s-=Math.round(c/2)),"c"===r[4]&&(a-=Math.round(l/2)),{x:a,y:s,w:l,h:c}}return{testMoveRel:function(n,r){for(var i=e.getViewPort(),o=0;o<r.length;o++){var a=t(this,n,r[o]);if(this._fixed){if(a.x>0&&a.x+a.w<i.w&&a.y>0&&a.y+a.h<i.h)return r[o]}else if(a.x>i.x&&a.x+a.w<i.w+i.x&&a.y>i.y&&a.y+a.h<i.h+i.y)return r[o]}return r[0]},moveRel:function(e,n){"string"!=typeof n&&(n=this.testMoveRel(e,n));
+var r=t(this,e,n);return this.moveTo(r.x,r.y)},moveBy:function(e,t){var n=this,r=n.layoutRect();return n.moveTo(r.x+e,r.y+t),n},moveTo:function(t,n){function r(e,t,n){return 0>e?0:e+n>t?(e=t-n,0>e?0:e):e}var i=this;if(i.settings.constrainToViewport){var o=e.getViewPort(window),a=i.layoutRect();t=r(t,o.w+o.x,a.w),n=r(n,o.h+o.y,a.h)}return i._rendered?i.layoutRect({x:t,y:n}).repaint():(i.settings.x=t,i.settings.y=n),i.fire("move",{x:t,y:n}),i}}}),r(Z,[q],function(e){return{resizeToContent:function(){this._layoutRect.autoResize=!0,this._lastRect=null,this.reflow()},resizeTo:function(t,n){if(1>=t||1>=n){var r=e.getWindowSize();t=1>=t?t*r.w:t,n=1>=n?n*r.h:n}return this._layoutRect.autoResize=!1,this.layoutRect({minW:t,minH:n,w:t,h:n}).reflow()},resizeBy:function(e,t){var n=this,r=n.layoutRect();return n.resizeTo(r.w+e,r.h+t)}}}),r(et,[J,Q,Z,q],function(e,t,n,r){function i(e){var t;for(t=s.length;t--;)s[t]===e&&s.splice(t,1);for(t=l.length;t--;)l[t]===e&&l.splice(t,1)}var o,a,s=[],l=[],c,u=e.extend({Mixins:[t,n],init:function(e){function t(){var e,t=u.zIndex||65535,n;if(l.length)for(e=0;e<l.length;e++)l[e].modal&&(t++,n=l[e]),l[e].getEl().style.zIndex=t,l[e].zIndex=t,t++;var i=document.getElementById(d.classPrefix+"modal-block");n?r.css(i,"z-index",n.zIndex-1):i&&(i.parentNode.removeChild(i),c=!1),u.currentZIndex=t}function n(e,t){for(;e;){if(e==t)return!0;e=e.parent()}}function i(e){function t(t,n){for(var r,i=0;i<s.length;i++)if(s[i]!=e)for(r=s[i].parent();r&&(r=r.parent());)r==e&&s[i].fixed(t).moveBy(0,n).repaint()}var n=r.getViewPort().y;e.settings.autofix&&(e._fixed?e._autoFixY>n&&(e.fixed(!1).layoutRect({y:e._autoFixY}).repaint(),t(!1,e._autoFixY-n)):(e._autoFixY=e.layoutRect().y,e._autoFixY<n&&(e.fixed(!0).layoutRect({y:0}).repaint(),t(!0,n-e._autoFixY))))}var d=this;d._super(e),d._eventsRoot=d,d.addClass("floatpanel"),e.autohide&&(o||(o=function(e){for(var t=s.length;t--;){var r=s[t],i=r.getParentCtrl(e.target);if(r.settings.autohide){if(i&&(n(i,r)||r.parent()===i))continue;e=r.fire("autohide",{target:e.target}),e.isDefaultPrevented()||r.hide()}}},r.on(document,"click",o)),s.push(d)),e.autofix&&(a||(a=function(){var e;for(e=s.length;e--;)i(s[e])},r.on(window,"scroll",a)),d.on("move",function(){i(this)})),d.on("postrender show",function(e){if(e.control==d){var n,i=d.classPrefix;d.modal&&!c&&(n=r.createFragment('<div id="'+i+'modal-block" class="'+i+"reset "+i+'fade"></div>'),n=n.firstChild,d.getContainerElm().appendChild(n),setTimeout(function(){r.addClass(n,i+"in"),r.addClass(d.getEl(),i+"in")},0),c=!0),l.push(d),t()}}),d.on("close hide",function(e){if(e.control==d){for(var n=l.length;n--;)l[n]===d&&l.splice(n,1);t()}}),d.on("show",function(){d.parents().each(function(e){return e._fixed?(d.fixed(!0),!1):void 0})}),e.popover&&(d._preBodyHtml='<div class="'+d.classPrefix+'arrow"></div>',d.addClass("popover").addClass("bottom").addClass(d.isRtl()?"end":"start"))},fixed:function(e){var t=this;if(t._fixed!=e){if(t._rendered){var n=r.getViewPort();e?t.layoutRect().y-=n.y:t.layoutRect().y+=n.y}t.toggleClass("fixed",e),t._fixed=e}return t},show:function(){var e=this,t,n=e._super();for(t=s.length;t--&&s[t]!==e;);return-1===t&&s.push(e),n},hide:function(){return i(this),this._super()},hideAll:function(){u.hideAll()},close:function(){var e=this;return e.fire("close"),e.remove()},remove:function(){i(this),this._super()},postRender:function(){var e=this;return e.settings.bodyRole&&this.getEl("body").setAttribute("role",e.settings.bodyRole),e._super()}});return u.hideAll=function(){for(var e=s.length;e--;){var t=s[e];t&&t.settings.autohide&&(t.hide(),s.splice(e,1))}},u}),r(tt,[et,J,q,Y],function(e,t,n,r){var i=e.extend({modal:!0,Defaults:{border:1,layout:"flex",containerCls:"panel",role:"dialog",callbacks:{submit:function(){this.fire("submit",{data:this.toJSON()})},close:function(){this.close()}}},init:function(e){var n=this;n._super(e),n.isRtl()&&n.addClass("rtl"),n.addClass("window"),n._fixed=!0,e.buttons&&(n.statusbar=new t({layout:"flex",border:"1 0 0 0",spacing:3,padding:10,align:"center",pack:n.isRtl()?"start":"end",defaults:{type:"button"},items:e.buttons}),n.statusbar.addClass("foot"),n.statusbar.parent(n)),n.on("click",function(e){-1!=e.target.className.indexOf(n.classPrefix+"close")&&n.close()}),n.on("cancel",function(){n.close()}),n.aria("describedby",n.describedBy||n._id+"-none"),n.aria("label",e.title),n._fullscreen=!1},recalc:function(){var e=this,t=e.statusbar,r,i,o,a;e._fullscreen&&(e.layoutRect(n.getWindowSize()),e.layoutRect().contentH=e.layoutRect().innerH),e._super(),r=e.layoutRect(),e.settings.title&&!e._fullscreen&&(i=r.headerW,i>r.w&&(o=r.x-Math.max(0,i/2),e.layoutRect({w:i,x:o}),a=!0)),t&&(t.layoutRect({w:e.layoutRect().innerW}).recalc(),i=t.layoutRect().minW+r.deltaW,i>r.w&&(o=r.x-Math.max(0,i-r.w),e.layoutRect({w:i,x:o}),a=!0)),a&&e.recalc()},initLayoutRect:function(){var e=this,t=e._super(),r=0,i;if(e.settings.title&&!e._fullscreen){i=e.getEl("head");var o=n.getSize(i);t.headerW=o.width,t.headerH=o.height,r+=t.headerH}e.statusbar&&(r+=e.statusbar.layoutRect().h),t.deltaH+=r,t.minH+=r,t.h+=r;var a=n.getWindowSize();return t.x=Math.max(0,a.w/2-t.w/2),t.y=Math.max(0,a.h/2-t.h/2),t},renderHtml:function(){var e=this,t=e._layout,n=e._id,r=e.classPrefix,i=e.settings,o="",a="",s=i.html;return e.preRender(),t.preRender(e),i.title&&(o='<div id="'+n+'-head" class="'+r+'window-head"><div id="'+n+'-title" class="'+r+'title">'+e.encode(i.title)+'</div><button type="button" class="'+r+'close" aria-hidden="true">\xd7</button><div id="'+n+'-dragh" class="'+r+'dragh"></div></div>'),i.url&&(s='<iframe src="'+i.url+'" tabindex="-1"></iframe>'),"undefined"==typeof s&&(s=t.renderHtml(e)),e.statusbar&&(a=e.statusbar.renderHtml()),'<div id="'+n+'" class="'+e.classes()+'" hidefocus="1"><div class="'+e.classPrefix+'reset" role="application">'+o+'<div id="'+n+'-body" class="'+e.classes("body")+'">'+s+"</div>"+a+"</div></div>"},fullscreen:function(e){var t=this,r=document.documentElement,i,o=t.classPrefix,a;if(e!=t._fullscreen)if(n.on(window,"resize",function(){var e;if(t._fullscreen)if(i)t._timer||(t._timer=setTimeout(function(){var e=n.getWindowSize();t.moveTo(0,0).resizeTo(e.w,e.h),t._timer=0},50));else{e=(new Date).getTime();var r=n.getWindowSize();t.moveTo(0,0).resizeTo(r.w,r.h),(new Date).getTime()-e>50&&(i=!0)}}),a=t.layoutRect(),t._fullscreen=e,e){t._initial={x:a.x,y:a.y,w:a.w,h:a.h},t._borderBox=t.parseBox("0"),t.getEl("head").style.display="none",a.deltaH-=a.headerH+2,n.addClass(r,o+"fullscreen"),n.addClass(document.body,o+"fullscreen"),t.addClass("fullscreen");var s=n.getWindowSize();t.moveTo(0,0).resizeTo(s.w,s.h)}else t._borderBox=t.parseBox(t.settings.border),t.getEl("head").style.display="",a.deltaH+=a.headerH,n.removeClass(r,o+"fullscreen"),n.removeClass(document.body,o+"fullscreen"),t.removeClass("fullscreen"),t.moveTo(t._initial.x,t._initial.y).resizeTo(t._initial.w,t._initial.h);return t.reflow()},postRender:function(){var e=this,t;setTimeout(function(){e.addClass("in")},0),e._super(),e.statusbar&&e.statusbar.postRender(),e.focus(),this.dragHelper=new r(e._id+"-dragh",{start:function(){t={x:e.layoutRect().x,y:e.layoutRect().y}},drag:function(n){e.moveTo(t.x+n.deltaX,t.y+n.deltaY)}}),e.on("submit",function(t){t.isDefaultPrevented()||e.close()})},submit:function(){return this.fire("submit",{data:this.toJSON()})},remove:function(){var e=this,t=e.classPrefix;e.dragHelper.destroy(),e._super(),e.statusbar&&this.statusbar.remove(),e._fullscreen&&(n.removeClass(document.documentElement,t+"fullscreen"),n.removeClass(document.body,t+"fullscreen"))},getContentWindow:function(){var e=this.getEl().getElementsByTagName("iframe")[0];return e?e.contentWindow:null}});return i}),r(nt,[tt],function(e){var t=e.extend({init:function(e){e={border:1,padding:20,layout:"flex",pack:"center",align:"center",containerCls:"panel",autoScroll:!0,buttons:{type:"button",text:"Ok",action:"ok"},items:{type:"label",multiline:!0,maxWidth:500,maxHeight:200}},this._super(e)},Statics:{OK:1,OK_CANCEL:2,YES_NO:3,YES_NO_CANCEL:4,msgBox:function(n){var r,i=n.callback||function(){};switch(n.buttons){case t.OK_CANCEL:r=[{type:"button",text:"Ok",subtype:"primary",onClick:function(e){e.control.parents()[1].close(),i(!0)}},{type:"button",text:"Cancel",onClick:function(e){e.control.parents()[1].close(),i(!1)}}];break;case t.YES_NO:r=[{type:"button",text:"Ok",subtype:"primary",onClick:function(e){e.control.parents()[1].close(),i(!0)}}];break;case t.YES_NO_CANCEL:r=[{type:"button",text:"Ok",subtype:"primary",onClick:function(e){e.control.parents()[1].close()}}];break;default:r=[{type:"button",text:"Ok",subtype:"primary",onClick:function(e){e.control.parents()[1].close(),i(!0)}}]}return new e({padding:20,x:n.x,y:n.y,minWidth:300,minHeight:100,layout:"flex",pack:"center",align:"center",buttons:r,title:n.title,role:"alertdialog",items:{type:"label",multiline:!0,maxWidth:500,maxHeight:200,text:n.text},onPostRender:function(){this.aria("describedby",this.items()[0]._id)},onClose:n.onClose,onCancel:function(){i(!1)}}).renderTo(document.body).reflow()},alert:function(e,n){return"string"==typeof e&&(e={text:e}),e.callback=n,t.msgBox(e)},confirm:function(e,n){return"string"==typeof e&&(e={text:e}),e.callback=n,e.buttons=t.OK_CANCEL,t.msgBox(e)}}});return t}),r(rt,[tt,nt],function(e,t){return function(n){function r(){return o.length?o[o.length-1]:void 0}var i=this,o=[];i.windows=o,i.open=function(t,r){var i;return n.editorManager.activeEditor=n,t.title=t.title||" ",t.url=t.url||t.file,t.url&&(t.width=parseInt(t.width||320,10),t.height=parseInt(t.height||240,10)),t.body&&(t.items={defaults:t.defaults,type:t.bodyType||"form",items:t.body}),t.url||t.buttons||(t.buttons=[{text:"Ok",subtype:"primary",onclick:function(){i.find("form")[0].submit()}},{text:"Cancel",onclick:function(){i.close()}}]),i=new e(t),o.push(i),i.on("close",function(){for(var e=o.length;e--;)o[e]===i&&o.splice(e,1);n.focus()}),t.data&&i.on("postRender",function(){this.find("*").each(function(e){var n=e.name();n in t.data&&e.value(t.data[n])})}),i.features=t||{},i.params=r||{},n.nodeChanged(),i.renderTo().reflow()},i.alert=function(e,r,i){t.alert(e,function(){r?r.call(i||this):n.focus()})},i.confirm=function(e,n,r){t.confirm(e,function(e){n.call(r||this,e)})},i.close=function(){r()&&r().close()},i.getParams=function(){return r()?r().params:null},i.setParams=function(e){r()&&(r().params=e)},i.getWindows=function(){return o}}}),r(it,[R,B,x,m,g,p],function(e,t,n,r,i,o){return function(a){function s(e,t){try{a.getDoc().execCommand(e,!1,t)}catch(n){}}function l(){var e=a.getDoc().documentMode;return e?e:6}function c(e){return e.isDefaultPrevented()}function u(){function t(e){var t=new i(function(){});o.each(a.getBody().getElementsByTagName("*"),function(e){"SPAN"==e.tagName&&e.setAttribute("mce-data-marked",1),!e.hasAttribute("data-mce-style")&&e.hasAttribute("style")&&a.dom.setAttrib(e,"style",e.getAttribute("style"))}),t.observe(a.getDoc(),{childList:!0,attributes:!0,subtree:!0,attributeFilter:["style"]}),a.getDoc().execCommand(e?"ForwardDelete":"Delete",!1,null);var n=a.selection.getRng(),r=n.startContainer.parentNode;o.each(t.takeRecords(),function(e){if(q.isChildOf(e.target,a.getBody())){if("style"==e.attributeName){var t=e.target.getAttribute("data-mce-style");t?e.target.setAttribute("style",t):e.target.removeAttribute("style")}o.each(e.addedNodes,function(e){if("SPAN"==e.nodeName&&!e.getAttribute("mce-data-marked")){var t,i;e==r&&(t=n.startOffset,i=e.firstChild),q.remove(e,!0),i&&(n.setStart(i,t),n.setEnd(i,t),a.selection.setRng(n))}})}}),t.disconnect(),o.each(a.dom.select("span[mce-data-marked]"),function(e){e.removeAttribute("mce-data-marked")})}var n=a.getDoc(),r="data:text/mce-internal,",i=window.MutationObserver,s,l;i||(s=!0,i=function(){function e(e){var t=e.relatedNode||e.target;n.push({target:t,addedNodes:[t]})}function t(e){var t=e.relatedNode||e.target;n.push({target:t,attributeName:e.attrName})}var n=[],r;this.observe=function(n){r=n,r.addEventListener("DOMSubtreeModified",e,!1),r.addEventListener("DOMNodeInsertedIntoDocument",e,!1),r.addEventListener("DOMNodeInserted",e,!1),r.addEventListener("DOMAttrModified",t,!1)},this.disconnect=function(){r.removeEventListener("DOMSubtreeModified",e,!1),r.removeEventListener("DOMNodeInsertedIntoDocument",e,!1),r.removeEventListener("DOMNodeInserted",e,!1),r.removeEventListener("DOMAttrModified",t,!1)},this.takeRecords=function(){return n}}),a.on("keydown",function(n){var r=n.keyCode==U,i=e.metaKeyPressed(n);if(!c(n)&&(r||n.keyCode==V)){var o=a.selection.getRng(),s=o.startContainer,l=o.startOffset;if(!i&&o.collapsed&&3==s.nodeType&&(r?l<s.data.length:l>0))return;n.preventDefault(),i&&a.selection.getSel().modify("extend",r?"forward":"backward","word"),t(r)}}),a.on("keypress",function(n){c(n)||$.isCollapsed()||!n.charCode||e.metaKeyPressed(n)||(n.preventDefault(),t(!0),a.selection.setContent(String.fromCharCode(n.charCode)))}),a.addCommand("Delete",function(){t()}),a.addCommand("ForwardDelete",function(){t(!0)}),s||(a.on("dragstart",function(e){var t;a.selection.isCollapsed()&&"IMG"==e.target.tagName&&$.select(e.target),l=$.getRng(),t=a.selection.getContent(),t.length>0&&e.dataTransfer.setData("URL","data:text/mce-internal,"+escape(t))}),a.on("drop",function(e){if(!c(e)){var i=e.dataTransfer.getData("URL");if(!i||-1==i.indexOf(r)||!n.caretRangeFromPoint)return;i=unescape(i.substr(r.length)),n.caretRangeFromPoint&&(e.preventDefault(),window.setTimeout(function(){var r=n.caretRangeFromPoint(e.x,e.y);l&&($.setRng(l),l=null),t(),$.setRng(r),a.insertContent(i)},0))}}),a.on("cut",function(e){!c(e)&&e.clipboardData&&(e.preventDefault(),e.clipboardData.clearData(),e.clipboardData.setData("text/html",a.selection.getContent()),e.clipboardData.setData("text/plain",a.selection.getContent({format:"text"})),t(!0))}))}function d(){function e(e){var t=q.create("body"),n=e.cloneContents();return t.appendChild(n),$.serializer.serialize(t,{format:"html"})}function n(n){if(!n.setStart){if(n.item)return!1;var r=n.duplicate();return r.moveToElementText(a.getBody()),t.compareRanges(n,r)}var i=e(n),o=q.createRng();o.selectNode(a.getBody());var s=e(o);return i===s}a.on("keydown",function(e){var t=e.keyCode,r,i;if(!c(e)&&(t==U||t==V)){if(r=a.selection.isCollapsed(),i=a.getBody(),r&&!q.isEmpty(i))return;if(!r&&!n(a.selection.getRng()))return;e.preventDefault(),a.setContent(""),i.firstChild&&q.isBlock(i.firstChild)?a.selection.setCursorLocation(i.firstChild,0):a.selection.setCursorLocation(i,0),a.nodeChanged()}})}function f(){a.on("keydown",function(t){!c(t)&&65==t.keyCode&&e.metaKeyPressed(t)&&(t.preventDefault(),a.execCommand("SelectAll"))})}function p(){a.settings.content_editable||(q.bind(a.getDoc(),"focusin",function(){$.setRng($.getRng())}),q.bind(a.getDoc(),"mousedown",function(e){e.target==a.getDoc().documentElement&&(a.getBody().focus(),$.setRng($.getRng()))}))}function h(){a.on("keydown",function(e){if(!c(e)&&e.keyCode===V&&$.isCollapsed()&&0===$.getRng(!0).startOffset){var t=$.getNode(),n=t.previousSibling;if("HR"==t.nodeName)return q.remove(t),void e.preventDefault();n&&n.nodeName&&"hr"===n.nodeName.toLowerCase()&&(q.remove(n),e.preventDefault())}})}function m(){window.Range.prototype.getClientRects||a.on("mousedown",function(e){if(!c(e)&&"HTML"===e.target.nodeName){var t=a.getBody();t.blur(),setTimeout(function(){t.focus()},0)}})}function g(){a.on("click",function(e){e=e.target,/^(IMG|HR)$/.test(e.nodeName)&&$.getSel().setBaseAndExtent(e,0,e,1),"A"==e.nodeName&&q.hasClass(e,"mce-item-anchor")&&$.select(e),a.nodeChanged()})}function v(){function e(){var e=q.getAttribs($.getStart().cloneNode(!1));return function(){var t=$.getStart();t!==a.getBody()&&(q.setAttrib(t,"style",null),W(e,function(e){t.setAttributeNode(e.cloneNode(!0))}))}}function t(){return!$.isCollapsed()&&q.getParent($.getStart(),q.isBlock)!=q.getParent($.getEnd(),q.isBlock)}a.on("keypress",function(n){var r;return c(n)||8!=n.keyCode&&46!=n.keyCode||!t()?void 0:(r=e(),a.getDoc().execCommand("delete",!1,null),r(),n.preventDefault(),!1)}),q.bind(a.getDoc(),"cut",function(n){var r;!c(n)&&t()&&(r=e(),setTimeout(function(){r()},0))})}function y(){var e,n;a.on("selectionchange",function(){n&&(clearTimeout(n),n=0),n=window.setTimeout(function(){if(!a.removed){var n=$.getRng();e&&t.compareRanges(n,e)||(a.nodeChanged(),e=n)}},50)})}function b(){document.body.setAttribute("role","application")}function C(){a.on("keydown",function(e){if(!c(e)&&e.keyCode===V&&$.isCollapsed()&&0===$.getRng(!0).startOffset){var t=$.getNode().previousSibling;if(t&&t.nodeName&&"table"===t.nodeName.toLowerCase())return e.preventDefault(),!1}})}function x(){l()>7||(s("RespectVisibilityInDesign",!0),a.contentStyles.push(".mceHideBrInPre pre br {display: none}"),q.addClass(a.getBody(),"mceHideBrInPre"),K.addNodeFilter("pre",function(e){for(var t=e.length,r,i,o,a;t--;)for(r=e[t].getAll("br"),i=r.length;i--;)o=r[i],a=o.prev,a&&3===a.type&&"\n"!=a.value.charAt(a.value-1)?a.value+="\n":o.parent.insert(new n("#text",3),o,!0).value="\n"}),G.addNodeFilter("pre",function(e){for(var t=e.length,n,r,i,o;t--;)for(n=e[t].getAll("br"),r=n.length;r--;)i=n[r],o=i.prev,o&&3==o.type&&(o.value=o.value.replace(/\r?\n$/,""))}))}function w(){q.bind(a.getBody(),"mouseup",function(){var e,t=$.getNode();"IMG"==t.nodeName&&((e=q.getStyle(t,"width"))&&(q.setAttrib(t,"width",e.replace(/[^0-9%]+/g,"")),q.setStyle(t,"width","")),(e=q.getStyle(t,"height"))&&(q.setAttrib(t,"height",e.replace(/[^0-9%]+/g,"")),q.setStyle(t,"height","")))})}function _(){a.on("keydown",function(t){var n,r,i,o,s;if(!c(t)&&t.keyCode==e.BACKSPACE&&(n=$.getRng(),r=n.startContainer,i=n.startOffset,o=q.getRoot(),s=r,n.collapsed&&0===i)){for(;s&&s.parentNode&&s.parentNode.firstChild==s&&s.parentNode!=o;)s=s.parentNode;"BLOCKQUOTE"===s.tagName&&(a.formatter.toggle("blockquote",null,s),n=q.createRng(),n.setStart(r,0),n.setEnd(r,0),$.setRng(n))}})}function N(){function e(){a._refreshContentEditable(),s("StyleWithCSS",!1),s("enableInlineTableEditing",!1),j.object_resizing||s("enableObjectResizing",!1)}j.readonly||a.on("BeforeExecCommand MouseDown",e)}function E(){function e(){W(q.select("a"),function(e){var t=e.parentNode,n=q.getRoot();if(t.lastChild===e){for(;t&&!q.isBlock(t);){if(t.parentNode.lastChild!==t||t===n)return;t=t.parentNode}q.add(t,"br",{"data-mce-bogus":1})}})}a.on("SetContent ExecCommand",function(t){("setcontent"==t.type||"mceInsertLink"===t.command)&&e()})}function S(){j.forced_root_block&&a.on("init",function(){s("DefaultParagraphSeparator",j.forced_root_block)})}function k(){a.on("Undo Redo SetContent",function(e){e.initial||a.execCommand("mceRepaint")})}function T(){a.on("keydown",function(e){var t;c(e)||e.keyCode!=V||(t=a.getDoc().selection.createRange(),t&&t.item&&(e.preventDefault(),a.undoManager.beforeChange(),q.remove(t.item(0)),a.undoManager.add()))})}function R(){var e;l()>=10&&(e="",W("p div h1 h2 h3 h4 h5 h6".split(" "),function(t,n){e+=(n>0?",":"")+t+":empty"}),a.contentStyles.push(e+"{padding-right: 1px !important}"))}function A(){l()<9&&(K.addNodeFilter("noscript",function(e){for(var t=e.length,n,r;t--;)n=e[t],r=n.firstChild,r&&n.attr("data-mce-innertext",r.value)}),G.addNodeFilter("noscript",function(e){for(var t=e.length,i,o,a;t--;)i=e[t],o=e[t].firstChild,o?o.value=r.decode(o.value):(a=i.attributes.map["data-mce-innertext"],a&&(i.attr("data-mce-innertext",null),o=new n("#text",3),o.value=a,o.raw=!0,i.append(o)))}))}function B(){function e(e,t){var n=i.createTextRange();try{n.moveToPoint(e,t)}catch(r){n=null}return n}function t(t){var r;t.button?(r=e(t.x,t.y),r&&(r.compareEndPoints("StartToStart",a)>0?r.setEndPoint("StartToStart",a):r.setEndPoint("EndToEnd",a),r.select())):n()}function n(){var e=r.selection.createRange();a&&!e.item&&0===e.compareEndPoints("StartToEnd",e)&&a.select(),q.unbind(r,"mouseup",n),q.unbind(r,"mousemove",t),a=o=0}var r=q.doc,i=r.body,o,a,s;r.documentElement.unselectable=!0,q.bind(r,"mousedown contextmenu",function(i){if("HTML"===i.target.nodeName){if(o&&n(),s=r.documentElement,s.scrollHeight>s.clientHeight)return;o=1,a=e(i.x,i.y),a&&(q.bind(r,"mouseup",n),q.bind(r,"mousemove",t),q.getRoot().focus(),a.select())}})}function D(){a.on("keyup focusin mouseup",function(t){65==t.keyCode&&e.metaKeyPressed(t)||$.normalize()},!0)}function L(){a.contentStyles.push("img:-moz-broken {-moz-force-broken-image-icon:1;min-width:24px;min-height:24px}")}function M(){a.inline||a.on("keydown",function(){document.activeElement==document.body&&a.getWin().focus()})}function H(){a.inline||(a.contentStyles.push("body {min-height: 150px}"),a.on("click",function(e){"HTML"==e.target.nodeName&&(a.getBody().focus(),a.selection.normalize(),a.nodeChanged())}))}function P(){i.mac&&a.on("keydown",function(t){!e.metaKeyPressed(t)||37!=t.keyCode&&39!=t.keyCode||(t.preventDefault(),a.selection.getSel().modify("move",37==t.keyCode?"backward":"forward","word"))})}function O(){s("AutoUrlDetect",!1)}function I(){a.inline||a.on("focus blur beforegetcontent",function(){var e=a.dom.create("br");a.getBody().appendChild(e),e.parentNode.removeChild(e)},!0)}function F(){a.on("click",function(e){var t=e.target;do if("A"===t.tagName)return void e.preventDefault();while(t=t.parentNode)}),a.contentStyles.push(".mce-content-body {-webkit-touch-callout: none}")}function z(){a.on("init",function(){a.dom.bind(a.getBody(),"submit",function(e){e.preventDefault()})})}var W=o.each,V=e.BACKSPACE,U=e.DELETE,q=a.dom,$=a.selection,j=a.settings,K=a.parser,G=a.serializer,Y=i.gecko,X=i.ie,J=i.webkit;C(),_(),d(),D(),J&&(u(),p(),g(),S(),z(),i.iOS?(y(),M(),H(),F()):f()),X&&i.ie<11&&(h(),b(),x(),w(),T(),R(),A(),B()),i.ie>=11&&(H(),I()),i.ie&&(f(),O()),Y&&(h(),m(),v(),N(),E(),k(),L(),P())}}),r(ot,[W],function(e){function t(t){return t._eventDispatcher||(t._eventDispatcher=new e({scope:t,toggleEvent:function(n,r){e.isNative(n)&&t.toggleNativeEvent&&t.toggleNativeEvent(n,r)}})),t._eventDispatcher}return{fire:function(e,n,r){var i=this;if(i.removed&&"remove"!==e)return n;if(n=t(i).fire(e,n,r),r!==!1&&i.parent)for(var o=i.parent();o&&!n.isPropagationStopped();)o.fire(e,n,!1),o=o.parent();return n},on:function(e,n,r){return t(this).on(e,n,r)},off:function(e,n){return t(this).off(e,n)},hasEventListeners:function(e){return t(this).has(e)}}}),r(at,[ot,y,p],function(e,t,n){function r(e,t){return"selectionchange"==t?e.getDoc():!e.inline&&/^mouse|click|contextmenu|drop/.test(t)?e.getDoc():e.getBody()}function i(e,t){var n=e.settings.event_root,i=e.editorManager,a=i.eventRootElm||r(e,t);if(n){if(i.rootEvents||(i.rootEvents={},i.on("RemoveEditor",function(){i.activeEditor||(o.unbind(a),delete i.rootEvents)})),i.rootEvents[t])return;a==e.getBody()&&(a=o.select(n)[0],i.eventRootElm=a),i.rootEvents[t]=!0,o.bind(a,t,function(e){for(var n=e.target,r=i.editors,a=r.length;a--;){var s=r[a].getBody();(s===n||o.isChildOf(n,s))&&(r[a].hidden||r[a].fire(t,e))}})}else e.dom.bind(a,t,function(n){e.hidden||e.fire(t,n)})}var o=t.DOM,a={bindPendingEventDelegates:function(){var e=this;n.each(e._pendingNativeEvents,function(t){i(e,t)})},toggleNativeEvent:function(e,t){var n=this;n.settings.readonly||"focus"!=e&&"blur"!=e&&(t?n.initialized?i(n,e):n._pendingNativeEvents?n._pendingNativeEvents.push(e):n._pendingNativeEvents=[e]:n.initialized&&n.dom.unbind(r(n,e),e))}};return a=n.extend({},e,a)}),r(st,[p,g],function(e,t){var n=e.each,r=e.explode,i={f9:120,f10:121,f11:122};return function(o){var a=this,s={};o.on("keyup keypress keydown",function(e){(e.altKey||e.ctrlKey||e.metaKey)&&n(s,function(n){var r=t.mac?e.metaKey:e.ctrlKey;if(n.ctrl==r&&n.alt==e.altKey&&n.shift==e.shiftKey)return e.keyCode==n.keyCode||e.charCode&&e.charCode==n.charCode?(e.preventDefault(),"keydown"==e.type&&n.func.call(n.scope),!0):void 0})}),a.add=function(t,a,l,c){var u;return u=l,"string"==typeof l?l=function(){o.execCommand(u,!1,null)}:e.isArray(u)&&(l=function(){o.execCommand(u[0],u[1],u[2])}),n(r(t.toLowerCase()),function(e){var t={func:l,scope:c||o,desc:o.translate(a),alt:!1,ctrl:!1,shift:!1};n(r(e,"+"),function(e){switch(e){case"alt":case"ctrl":case"shift":t[e]=!0;break;default:t.charCode=e.charCodeAt(0),t.keyCode=i[e]||e.toUpperCase().charCodeAt(0)}}),s[(t.ctrl?"ctrl":"")+","+(t.alt?"alt":"")+","+(t.shift?"shift":"")+","+t.keyCode]=t}),!0}}}),r(lt,[y,C,x,k,S,D,M,H,P,O,I,F,b,l,rt,w,N,it,g,p,at,st],function(e,n,r,i,o,a,s,l,c,u,d,f,p,h,m,g,v,y,b,C,x,w){function _(e,t,r){var i=this,o,a;o=i.documentBaseUrl=r.documentBaseURL,a=r.baseURI,i.settings=t=k({id:e,theme:"modern",delta_width:0,delta_height:0,popup_css:"",plugins:"",document_base_url:o,add_form_submit_trigger:!0,submit_patch:!0,add_unload_trigger:!0,convert_urls:!0,relative_urls:!0,remove_script_host:!0,object_resizing:!0,doctype:"<!DOCTYPE html>",visual:!0,font_size_style_values:"xx-small,x-small,small,medium,large,x-large,xx-large",font_size_legacy_values:"xx-small,small,medium,large,x-large,xx-large,300%",forced_root_block:"p",hidden_input:!0,padd_empty_editor:!0,render_ui:!0,indentation:"30px",inline_styles:!0,convert_fonts_to_spans:!0,indent:"simple",indent_before:"p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr,section,article,hgroup,aside,figure,option,optgroup,datalist",indent_after:"p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr,section,article,hgroup,aside,figure,option,optgroup,datalist",validate:!0,entity_encoding:"named",url_converter:i.convertURL,url_converter_scope:i,ie7_compat:!0},t),n.language=t.language||"en",n.languageLoad=t.language_load,n.baseURL=r.baseURL,i.id=t.id=e,i.isNotDirty=!0,i.plugins={},i.documentBaseURI=new f(t.document_base_url||o,{base_uri:a}),i.baseURI=a,i.contentCSS=[],i.contentStyles=[],i.shortcuts=new w(i),i.execCommands={},i.queryStateCommands={},i.queryValueCommands={},i.loadedCSS={},i.suffix=r.suffix,i.editorManager=r,i.inline=t.inline,r.fire("SetupEditor",i),i.execCallback("setup",i)}var N=e.DOM,E=n.ThemeManager,S=n.PluginManager,k=C.extend,T=C.each,R=C.explode,A=C.inArray,B=C.trim,D=C.resolve,L=h.Event,M=b.gecko,H=b.ie;return _.prototype={render:function(){function e(){N.unbind(window,"ready",e),n.render()}function t(){var e=p.ScriptLoader;if(r.language&&"en"!=r.language&&!r.language_url&&(r.language_url=n.editorManager.baseURL+"/langs/"+r.language+".js"),r.language_url&&e.add(r.language_url),r.theme&&"function"!=typeof r.theme&&"-"!=r.theme.charAt(0)&&!E.urls[r.theme]){var t=r.theme_url;t=t?n.documentBaseURI.toAbsolute(t):"themes/"+r.theme+"/theme"+o+".js",E.load(r.theme,t)}C.isArray(r.plugins)&&(r.plugins=r.plugins.join(" ")),T(r.external_plugins,function(e,t){S.load(t,e),r.plugins+=" "+t}),T(r.plugins.split(/[ ,]/),function(e){if(e=B(e),e&&!S.urls[e])if("-"==e.charAt(0)){e=e.substr(1,e.length);var t=S.dependencies(e);T(t,function(e){var t={prefix:"plugins/",resource:e,suffix:"/plugin"+o+".js"};e=S.createUrl(t,e),S.load(e.resource,e)})}else S.load(e,{prefix:"plugins/",resource:e,suffix:"/plugin"+o+".js"})}),e.loadQueue(function(){n.removed||n.init()})}var n=this,r=n.settings,i=n.id,o=n.suffix;if(!L.domLoaded)return void N.bind(window,"ready",e);if(n.getElement()&&b.contentEditable){r.inline?n.inline=!0:(n.orgVisibility=n.getElement().style.visibility,n.getElement().style.visibility="hidden");var a=n.getElement().form||N.getParent(i,"form");a&&(n.formElement=a,r.hidden_input&&!/TEXTAREA|INPUT/i.test(n.getElement().nodeName)&&(N.insertAfter(N.create("input",{type:"hidden",name:i}),i),n.hasHiddenInput=!0),n.formEventDelegate=function(e){n.fire(e.type,e)},N.bind(a,"submit reset",n.formEventDelegate),n.on("reset",function(){n.setContent(n.startContent,{format:"raw"})}),!r.submit_patch||a.submit.nodeType||a.submit.length||a._mceOldSubmit||(a._mceOldSubmit=a.submit,a.submit=function(){return n.editorManager.triggerSave(),n.isNotDirty=!0,a._mceOldSubmit(a)})),n.windowManager=new m(n),"xml"==r.encoding&&n.on("GetContent",function(e){e.save&&(e.content=N.encode(e.content))}),r.add_form_submit_trigger&&n.on("submit",function(){n.initialized&&n.save()}),r.add_unload_trigger&&(n._beforeUnload=function(){!n.initialized||n.destroyed||n.isHidden()||n.save({format:"raw",no_events:!0,set_dirty:!1})},n.editorManager.on("BeforeUnload",n._beforeUnload)),t()}},init:function(){function e(n){var r=S.get(n),i,o;i=S.urls[n]||t.documentBaseUrl.replace(/\/$/,""),n=B(n),r&&-1===A(m,n)&&(T(S.dependencies(n),function(t){e(t)}),o=new r(t,i),t.plugins[n]=o,o.init&&(o.init(t,i),m.push(n)))}var t=this,n=t.settings,r=t.getElement(),i,o,a,s,l,c,u,d,f,p,h,m=[];if(t.rtl=this.editorManager.i18n.rtl,t.editorManager.add(t),n.aria_label=n.aria_label||N.getAttrib(r,"aria-label",t.getLang("aria.rich_text_area")),n.theme&&("function"!=typeof n.theme?(n.theme=n.theme.replace(/-/,""),c=E.get(n.theme),t.theme=new c(t,E.urls[n.theme]),t.theme.init&&t.theme.init(t,E.urls[n.theme]||t.documentBaseUrl.replace(/\/$/,""))):t.theme=n.theme),T(n.plugins.replace(/\-/g,"").split(/[ ,]/),e),n.render_ui&&t.theme&&(t.orgDisplay=r.style.display,"function"!=typeof n.theme?(i=n.width||r.style.width||r.offsetWidth,o=n.height||r.style.height||r.offsetHeight,a=n.min_height||100,p=/^[0-9\.]+(|px)$/i,p.test(""+i)&&(i=Math.max(parseInt(i,10),100)),p.test(""+o)&&(o=Math.max(parseInt(o,10),a)),l=t.theme.renderUI({targetNode:r,width:i,height:o,deltaWidth:n.delta_width,deltaHeight:n.delta_height}),n.content_editable||(N.setStyles(l.sizeContainer||l.editorContainer,{wi2dth:i,h2eight:o}),o=(l.iframeHeight||o)+("number"==typeof o?l.deltaHeight||0:""),a>o&&(o=a))):(l=n.theme(t,r),l.editorContainer.nodeType&&(l.editorContainer=l.editorContainer.id=l.editorContainer.id||t.id+"_parent"),l.iframeContainer.nodeType&&(l.iframeContainer=l.iframeContainer.id=l.iframeContainer.id||t.id+"_iframecontainer"),o=l.iframeHeight||r.offsetHeight),t.editorContainer=l.editorContainer),n.content_css&&T(R(n.content_css),function(e){t.contentCSS.push(t.documentBaseURI.toAbsolute(e))}),n.content_style&&t.contentStyles.push(n.content_style),n.content_editable)return r=s=l=null,t.initContentBody();for(t.iframeHTML=n.doctype+"<html><head>",n.document_base_url!=t.documentBaseUrl&&(t.iframeHTML+='<base href="'+t.documentBaseURI.getURI()+'" />'),!b.caretAfter&&n.ie7_compat&&(t.iframeHTML+='<meta http-equiv="X-UA-Compatible" content="IE=7" />'),t.iframeHTML+='<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />',h=0;h<t.contentCSS.length;h++){var g=t.contentCSS[h];t.iframeHTML+='<link type="text/css" rel="stylesheet" href="'+g+'" />',t.loadedCSS[g]=!0}d=n.body_id||"tinymce",-1!=d.indexOf("=")&&(d=t.getParam("body_id","","hash"),d=d[t.id]||d),f=n.body_class||"",-1!=f.indexOf("=")&&(f=t.getParam("body_class","","hash"),f=f[t.id]||""),t.iframeHTML+='</head><body id="'+d+'" class="mce-content-body '+f+'" onload="window.parent.tinymce.get(\''+t.id+"').fire('load');\"><br></body></html>";var v='javascript:(function(){document.open();document.domain="'+document.domain+'";var ed = window.parent.tinymce.get("'+t.id+'");document.write(ed.iframeHTML);document.close();ed.initContentBody(true);})()';if(document.domain!=location.hostname&&(u=v),s=N.add(l.iframeContainer,"iframe",{id:t.id+"_ifr",src:u||'javascript:""',frameBorder:"0",allowTransparency:"true",title:t.editorManager.translate("Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help"),style:{width:"100%",height:o,display:"block"}}),H)try{t.getDoc()}catch(y){s.src=u=v}t.contentAreaContainer=l.iframeContainer,l.editorContainer&&(N.get(l.editorContainer).style.display=t.orgDisplay),N.get(t.id).style.display="none",N.setAttrib(t.id,"aria-hidden",!0),u||t.initContentBody(),r=s=l=null},initContentBody:function(t){var n=this,o=n.settings,f=N.get(n.id),p=n.getDoc(),h,m;o.inline||(n.getElement().style.visibility=n.orgVisibility),t||o.content_editable||(p.open(),p.write(n.iframeHTML),p.close()),o.content_editable&&(n.on("remove",function(){var e=this.getBody();N.removeClass(e,"mce-content-body"),N.removeClass(e,"mce-edit-focus"),N.setAttrib(e,"contentEditable",null)}),N.addClass(f,"mce-content-body"),n.contentDocument=p=o.content_document||document,n.contentWindow=o.content_window||window,n.bodyElement=f,o.content_document=o.content_window=null,o.root_name=f.nodeName.toLowerCase()),h=n.getBody(),h.disabled=!0,o.readonly||(n.inline&&"static"==N.getStyle(h,"position",!0)&&(h.style.position="relative"),h.contentEditable=n.getParam("content_editable_state",!0)),h.disabled=!1,n.schema=new g(o),n.dom=new e(p,{keep_values:!0,url_converter:n.convertURL,url_converter_scope:n,hex_colors:o.force_hex_style_colors,class_filter:o.class_filter,update_styles:!0,root_element:o.content_editable?n.id:null,collect:o.content_editable,schema:n.schema,onSetAttrib:function(e){n.fire("SetAttrib",e)
+}}),n.parser=new v(o,n.schema),n.parser.addAttributeFilter("src,href,style,tabindex",function(e,t){for(var r=e.length,i,o=n.dom,a,s;r--;)i=e[r],a=i.attr(t),s="data-mce-"+t,i.attributes.map[s]||("style"===t?i.attr(s,o.serializeStyle(o.parseStyle(a),i.name)):"tabindex"===t?(i.attr(s,a),i.attr(t,null)):i.attr(s,n.convertURL(a,t,i.name)))}),n.parser.addNodeFilter("script",function(e){for(var t=e.length,n;t--;)n=e[t],n.attr("type","mce-"+(n.attr("type")||"text/javascript"))}),n.parser.addNodeFilter("#cdata",function(e){for(var t=e.length,n;t--;)n=e[t],n.type=8,n.name="#comment",n.value="[CDATA["+n.value+"]]"}),n.parser.addNodeFilter("p,h1,h2,h3,h4,h5,h6,div",function(e){for(var t=e.length,i,o=n.schema.getNonEmptyElements();t--;)i=e[t],i.isEmpty(o)&&(i.empty().append(new r("br",1)).shortEnded=!0)}),n.serializer=new i(o,n),n.selection=new a(n.dom,n.getWin(),n.serializer,n),n.formatter=new s(n),n.undoManager=new l(n),n.forceBlocks=new u(n),n.enterKey=new c(n),n.editorCommands=new d(n),n.fire("PreInit"),o.browser_spellcheck||o.gecko_spellcheck||(p.body.spellcheck=!1,N.setAttrib(h,"spellcheck","false")),n.fire("PostRender"),n.quirks=y(n),o.directionality&&(h.dir=o.directionality),o.nowrap&&(h.style.whiteSpace="nowrap"),o.protect&&n.on("BeforeSetContent",function(e){T(o.protect,function(t){e.content=e.content.replace(t,function(e){return"<!--mce:protected "+escape(e)+"-->"})})}),n.on("SetContent",function(){n.addVisual(n.getBody())}),o.padd_empty_editor&&n.on("PostProcess",function(e){e.content=e.content.replace(/^(<p[^>]*>(&nbsp;|&#160;|\s|\u00a0|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/,"")}),n.load({initial:!0,format:"html"}),n.startContent=n.getContent({format:"raw"}),n.initialized=!0,n.bindPendingEventDelegates(),n.fire("init"),n.focus(!0),n.nodeChanged({initial:!0}),n.execCallback("init_instance_callback",n),n.contentStyles.length>0&&(m="",T(n.contentStyles,function(e){m+=e+"\r\n"}),n.dom.addStyle(m)),T(n.contentCSS,function(e){n.loadedCSS[e]||(n.dom.loadCSS(e),n.loadedCSS[e]=!0)}),o.auto_focus&&setTimeout(function(){var e=n.editorManager.get(o.auto_focus);e.selection.select(e.getBody(),1),e.selection.collapse(1),e.getBody().focus(),e.getWin().focus()},100),f=p=h=null},focus:function(e){var t,n=this,r=n.selection,i=n.settings.content_editable,o,a,s=n.getDoc(),l;if(!e){if(o=r.getRng(),o.item&&(a=o.item(0)),n._refreshContentEditable(),i||(b.opera||n.getBody().focus(),n.getWin().focus()),M||i){if(l=n.getBody(),l.setActive)try{l.setActive()}catch(c){l.focus()}else l.focus();i&&r.normalize()}a&&a.ownerDocument==s&&(o=s.body.createControlRange(),o.addElement(a),o.select())}n.editorManager.activeEditor!=n&&((t=n.editorManager.activeEditor)&&t.fire("deactivate",{relatedTarget:n}),n.fire("activate",{relatedTarget:t})),n.editorManager.activeEditor=n},execCallback:function(e){var t=this,n=t.settings[e],r;if(n)return t.callbackLookup&&(r=t.callbackLookup[e])&&(n=r.func,r=r.scope),"string"==typeof n&&(r=n.replace(/\.\w+$/,""),r=r?D(r):0,n=D(n),t.callbackLookup=t.callbackLookup||{},t.callbackLookup[e]={func:n,scope:r}),n.apply(r||t,Array.prototype.slice.call(arguments,1))},translate:function(e){var t=this.settings.language||"en",n=this.editorManager.i18n;return e?n.data[t+"."+e]||e.replace(/\{\#([^\}]+)\}/g,function(e,r){return n.data[t+"."+r]||"{#"+r+"}"}):""},getLang:function(e,n){return this.editorManager.i18n.data[(this.settings.language||"en")+"."+e]||(n!==t?n:"{#"+e+"}")},getParam:function(e,t,n){var r=e in this.settings?this.settings[e]:t,i;return"hash"===n?(i={},"string"==typeof r?T(r.split(r.indexOf("=")>0?/[;,](?![^=;,]*(?:[;,]|$))/:","),function(e){e=e.split("="),i[B(e[0])]=B(e.length>1?e[1]:e)}):i=r,i):r},nodeChanged:function(){var e=this,t=e.selection,n,r,i;!e.initialized||e.settings.disable_nodechange||e.settings.readonly||(i=e.getBody(),n=t.getStart()||i,n=H&&n.ownerDocument!=e.getDoc()?e.getBody():n,"IMG"==n.nodeName&&t.isCollapsed()&&(n=n.parentNode),r=[],e.dom.getParent(n,function(e){return e===i?!0:void r.push(e)}),e.fire("NodeChange",{element:n,parents:r}))},addButton:function(e,t){var n=this;t.cmd&&(t.onclick=function(){n.execCommand(t.cmd)}),t.text||t.icon||(t.icon=e),n.buttons=n.buttons||{},t.tooltip=t.tooltip||t.title,n.buttons[e]=t},addMenuItem:function(e,t){var n=this;t.cmd&&(t.onclick=function(){n.execCommand(t.cmd)}),n.menuItems=n.menuItems||{},n.menuItems[e]=t},addCommand:function(e,t,n){this.execCommands[e]={func:t,scope:n||this}},addQueryStateHandler:function(e,t,n){this.queryStateCommands[e]={func:t,scope:n||this}},addQueryValueHandler:function(e,t,n){this.queryValueCommands[e]={func:t,scope:n||this}},addShortcut:function(e,t,n,r){this.shortcuts.add(e,t,n,r)},execCommand:function(e,t,n,r){var i=this,o=0,a;return/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint)$/.test(e)||r&&r.skip_focus||i.focus(),r=k({},r),r=i.fire("BeforeExecCommand",{command:e,ui:t,value:n}),r.isDefaultPrevented()?!1:(a=i.execCommands[e])&&a.func.call(a.scope,t,n)!==!0?(i.fire("ExecCommand",{command:e,ui:t,value:n}),!0):(T(i.plugins,function(r){return r.execCommand&&r.execCommand(e,t,n)?(i.fire("ExecCommand",{command:e,ui:t,value:n}),o=!0,!1):void 0}),o?o:i.theme&&i.theme.execCommand&&i.theme.execCommand(e,t,n)?(i.fire("ExecCommand",{command:e,ui:t,value:n}),!0):i.editorCommands.execCommand(e,t,n)?(i.fire("ExecCommand",{command:e,ui:t,value:n}),!0):(i.getDoc().execCommand(e,t,n),void i.fire("ExecCommand",{command:e,ui:t,value:n})))},queryCommandState:function(e){var t=this,n,r;if(!t._isHidden()){if((n=t.queryStateCommands[e])&&(r=n.func.call(n.scope),r!==!0))return r;if(r=t.editorCommands.queryCommandState(e),-1!==r)return r;try{return t.getDoc().queryCommandState(e)}catch(i){}}},queryCommandValue:function(e){var n=this,r,i;if(!n._isHidden()){if((r=n.queryValueCommands[e])&&(i=r.func.call(r.scope),i!==!0))return i;if(i=n.editorCommands.queryCommandValue(e),i!==t)return i;try{return n.getDoc().queryCommandValue(e)}catch(o){}}},show:function(){var e=this;e.hidden&&(e.hidden=!1,e.inline?e.getBody().contentEditable=!0:(N.show(e.getContainer()),N.hide(e.id)),e.load(),e.fire("show"))},hide:function(){var e=this,t=e.getDoc();e.hidden||(e.hidden=!0,H&&t&&!e.inline&&t.execCommand("SelectAll"),e.save(),e.inline?(e.getBody().contentEditable=!1,e==e.editorManager.focusedEditor&&(e.editorManager.focusedEditor=null)):(N.hide(e.getContainer()),N.setStyle(e.id,"display",e.orgDisplay)),e.fire("hide"))},isHidden:function(){return!!this.hidden},setProgressState:function(e,t){this.fire("ProgressState",{state:e,time:t})},load:function(e){var n=this,r=n.getElement(),i;return r?(e=e||{},e.load=!0,i=n.setContent(r.value!==t?r.value:r.innerHTML,e),e.element=r,e.no_events||n.fire("LoadContent",e),e.element=r=null,i):void 0},save:function(e){var t=this,n=t.getElement(),r,i;if(n&&t.initialized)return e=e||{},e.save=!0,e.element=n,r=e.content=t.getContent(e),e.no_events||t.fire("SaveContent",e),r=e.content,/TEXTAREA|INPUT/i.test(n.nodeName)?n.value=r:(t.inline||(n.innerHTML=r),(i=N.getParent(t.id,"form"))&&T(i.elements,function(e){return e.name==t.id?(e.value=r,!1):void 0})),e.element=n=null,e.set_dirty!==!1&&(t.isNotDirty=!0),r},setContent:function(e,t){var n=this,r=n.getBody(),i;return t=t||{},t.format=t.format||"html",t.set=!0,t.content=e,t.no_events||n.fire("BeforeSetContent",t),e=t.content,0===e.length||/^\s+$/.test(e)?(i=n.settings.forced_root_block,i&&n.schema.isValidChild(r.nodeName.toLowerCase(),i.toLowerCase())?(e=H&&11>H?"":'<br data-mce-bogus="1">',e=n.dom.createHTML(i,n.settings.forced_root_block_attrs,e)):H||(e='<br data-mce-bogus="1">'),r.innerHTML=e,n.fire("SetContent",t)):("raw"!==t.format&&(e=new o({},n.schema).serialize(n.parser.parse(e,{isRootContent:!0}))),t.content=B(e),n.dom.setHTML(r,t.content),t.no_events||n.fire("SetContent",t)),t.content},getContent:function(e){var t=this,n,r=t.getBody();return e=e||{},e.format=e.format||"html",e.get=!0,e.getInner=!0,e.no_events||t.fire("BeforeGetContent",e),n="raw"==e.format?r.innerHTML:"text"==e.format?r.innerText||r.textContent:t.serializer.serialize(r,e),e.content="text"!=e.format?B(n):n,e.no_events||t.fire("GetContent",e),e.content},insertContent:function(e){this.execCommand("mceInsertContent",!1,e)},isDirty:function(){return!this.isNotDirty},getContainer:function(){var e=this;return e.container||(e.container=N.get(e.editorContainer||e.id+"_parent")),e.container},getContentAreaContainer:function(){return this.contentAreaContainer},getElement:function(){return N.get(this.settings.content_element||this.id)},getWin:function(){var e=this,t;return e.contentWindow||(t=N.get(e.id+"_ifr"),t&&(e.contentWindow=t.contentWindow)),e.contentWindow},getDoc:function(){var e=this,t;return e.contentDocument||(t=e.getWin(),t&&(e.contentDocument=t.document)),e.contentDocument},getBody:function(){return this.bodyElement||this.getDoc().body},convertURL:function(e,t,n){var r=this,i=r.settings;return i.urlconverter_callback?r.execCallback("urlconverter_callback",e,n,!0,t):!i.convert_urls||n&&"LINK"==n.nodeName||0===e.indexOf("file:")||0===e.length?e:i.relative_urls?r.documentBaseURI.toRelative(e):e=r.documentBaseURI.toAbsolute(e,i.remove_script_host)},addVisual:function(e){var n=this,r=n.settings,i=n.dom,o;e=e||n.getBody(),n.hasVisual===t&&(n.hasVisual=r.visual),T(i.select("table,a",e),function(e){var t;switch(e.nodeName){case"TABLE":return o=r.visual_table_class||"mce-item-table",t=i.getAttrib(e,"border"),void(t&&"0"!=t||(n.hasVisual?i.addClass(e,o):i.removeClass(e,o)));case"A":return void(i.getAttrib(e,"href",!1)||(t=i.getAttrib(e,"name")||e.id,o=r.visual_anchor_class||"mce-item-anchor",t&&(n.hasVisual?i.addClass(e,o):i.removeClass(e,o))))}}),n.fire("VisualAid",{element:e,hasVisual:n.hasVisual})},remove:function(){var e=this;if(!e.removed){e.removed=1,e.save(),e.hasHiddenInput&&N.remove(e.getElement().nextSibling),e.inline||(H&&10>H&&e.getDoc().execCommand("SelectAll",!1,null),N.setStyle(e.id,"display",e.orgDisplay),e.getBody().onload=null,L.unbind(e.getWin()),L.unbind(e.getDoc()));var t=e.getContainer();L.unbind(e.getBody()),L.unbind(t),e.fire("remove"),e.editorManager.remove(e),N.remove(t),e.destroy()}},destroy:function(e){var t=this,n;if(!t.destroyed){if(!e&&!t.removed)return void t.remove();e&&M&&(L.unbind(t.getDoc()),L.unbind(t.getWin()),L.unbind(t.getBody())),e||(t.editorManager.off("beforeunload",t._beforeUnload),t.theme&&t.theme.destroy&&t.theme.destroy(),t.selection.destroy(),t.dom.destroy()),n=t.formElement,n&&(n._mceOldSubmit&&(n.submit=n._mceOldSubmit,n._mceOldSubmit=null),N.unbind(n,"submit reset",t.formEventDelegate)),t.contentAreaContainer=t.formElement=t.container=t.editorContainer=null,t.settings.content_element=t.bodyElement=t.contentDocument=t.contentWindow=null,t.selection&&(t.selection=t.selection.win=t.selection.dom=t.selection.dom.doc=null),t.destroyed=1}},_refreshContentEditable:function(){var e=this,t,n;e._isHidden()&&(t=e.getBody(),n=t.parentNode,n.removeChild(t),n.appendChild(t),t.focus())},_isHidden:function(){var e;return M?(e=this.selection.getSel(),!e||!e.rangeCount||0===e.rangeCount):0}},k(_.prototype,x),_}),r(ct,[],function(){var e={};return{rtl:!1,add:function(t,n){for(var r in n)e[r]=n[r];this.rtl=this.rtl||"rtl"===e._dir},translate:function(t){if("undefined"==typeof t)return t;if("string"!=typeof t&&t.raw)return t.raw;if(t.push){var n=t.slice(1);t=(e[t[0]]||t[0]).replace(/\{([^\}]+)\}/g,function(e,t){return n[t]})}return e[t]||t},data:e}}),r(ut,[y,g],function(e,t){function n(e){function s(){try{return document.activeElement}catch(e){return document.body}}function l(e,t){if(t&&t.startContainer){if(!e.isChildOf(t.startContainer,e.getRoot())||!e.isChildOf(t.endContainer,e.getRoot()))return;return{startContainer:t.startContainer,startOffset:t.startOffset,endContainer:t.endContainer,endOffset:t.endOffset}}return t}function c(e,t){var n;return t.startContainer?(n=e.getDoc().createRange(),n.setStart(t.startContainer,t.startOffset),n.setEnd(t.endContainer,t.endOffset)):n=t,n}function u(e){return!!a.getParent(e,n.isEditorUIElement)}function d(n){var d=n.editor;d.on("init",function(){(d.inline||t.ie)&&(d.on("nodechange keyup",function(){var e=document.activeElement;e&&e.id==d.id+"_ifr"&&(e=d.getBody()),d.dom.isChildOf(e,d.getBody())&&(d.lastRng=d.selection.getRng())}),t.webkit&&!r&&(r=function(){var t=e.activeEditor;if(t&&t.selection){var n=t.selection.getRng();n&&!n.collapsed&&(d.lastRng=n)}},a.bind(document,"selectionchange",r)))}),d.on("setcontent",function(){d.lastRng=null}),d.on("mousedown",function(){d.selection.lastFocusBookmark=null}),d.on("focusin",function(){var t=e.focusedEditor;d.selection.lastFocusBookmark&&(d.selection.setRng(c(d,d.selection.lastFocusBookmark)),d.selection.lastFocusBookmark=null),t!=d&&(t&&t.fire("blur",{focusedEditor:d}),e.activeEditor=d,e.focusedEditor=d,d.fire("focus",{blurredEditor:t}),d.focus(!0)),d.lastRng=null}),d.on("focusout",function(){window.setTimeout(function(){var t=e.focusedEditor;u(s())||t!=d||(d.fire("blur",{focusedEditor:null}),e.focusedEditor=null,d.selection&&(d.selection.lastFocusBookmark=null))},0)}),i||(i=function(t){var n=e.activeEditor;n&&t.target.ownerDocument==document&&(n.selection&&(n.selection.lastFocusBookmark=l(n.dom,n.lastRng)),u(t.target)||e.focusedEditor!=n||(n.fire("blur",{focusedEditor:null}),e.focusedEditor=null))},a.bind(document,"focusin",i)),d.inline&&!o&&(o=function(t){var n=e.activeEditor;if(n.inline&&!n.dom.isChildOf(t.target,n.getBody())){var r=n.selection.getRng();r.collapsed||(n.lastRng=r)}},a.bind(document,"mouseup",o))}function f(t){e.focusedEditor==t.editor&&(e.focusedEditor=null),e.activeEditor||(a.unbind(document,"selectionchange",r),a.unbind(document,"focusin",i),a.unbind(document,"mouseup",o),r=i=o=null)}e.on("AddEditor",d),e.on("RemoveEditor",f)}var r,i,o,a=e.DOM;return n.isEditorUIElement=function(e){return-1!==e.className.toString().indexOf("mce-")},n}),r(dt,[lt,y,F,g,p,ot,ct,ut],function(e,t,n,r,i,o,a,s){function l(e){var t=g.editors,n;delete t[e.id];for(var r=0;r<t.length;r++)if(t[r]==e){t.splice(r,1),n=!0;break}return g.activeEditor==e&&(g.activeEditor=t[0]),g.focusedEditor==e&&(g.focusedEditor=null),n}function c(e){return e&&!(e.getContainer()||e.getBody()).parentNode&&(l(e),e.destroy(!0),e=null),e}var u=t.DOM,d=i.explode,f=i.each,p=i.extend,h=0,m,g;return g={majorVersion:"4",minorVersion:"0.26",releaseDate:"2014-05-06",editors:[],i18n:a,activeEditor:null,setup:function(){var e=this,t,r,i="",o,a;if(r=document.location.href.replace(/[\?#].*$/,"").replace(/[\/\\][^\/]+$/,""),/[\/\\]$/.test(r)||(r+="/"),o=window.tinymce||window.tinyMCEPreInit)t=o.base||o.baseURL,i=o.suffix;else{for(var l=document.getElementsByTagName("script"),c=0;c<l.length;c++)if(a=l[c].src,/tinymce(\.full|\.jquery|)(\.min|\.dev|)\.js/.test(a)){-1!=a.indexOf(".min")&&(i=".min"),t=a.substring(0,a.lastIndexOf("/"));break}!t&&document.currentScript&&(a=document.currentScript.src,-1!=a.indexOf(".min")&&(i=".min"),t=a.substring(0,a.lastIndexOf("/")))}e.baseURL=new n(r).toAbsolute(t),e.documentBaseURL=r,e.baseURI=new n(e.baseURL),e.suffix=i,e.focusManager=new s(e)},init:function(t){function n(e){var t=e.id;return t||(t=e.name,t=t&&!u.get(t)?e.name:u.uniqueId(),e.setAttribute("id",t)),t}function r(t,n){if(!c(s.get(t))){var r=new e(t,n,s);l.push(r),r.render()}}function i(e,t,n){var r=e[t];if(r)return r.apply(n||this,Array.prototype.slice.call(arguments,2))}function o(e,t){return t.constructor===RegExp?t.test(e.className):u.hasClass(e,t)}function a(){var c,g;if(u.unbind(window,"ready",a),i(t,"onpageload"),t.types)return void f(t.types,function(e){f(u.select(e.selector),function(i){r(n(i),p({},t,e))})});if(t.selector)return void f(u.select(t.selector),function(e){r(n(e),t)});switch(t.mode){case"exact":c=t.elements||"",c.length>0&&f(d(c),function(n){u.get(n)?(m=new e(n,t,s),l.push(m),m.render()):f(document.forms,function(e){f(e.elements,function(e){e.name===n&&(n="mce_editor_"+h++,u.setAttrib(e,"id",n),r(n,t))})})});break;case"textareas":case"specific_textareas":f(u.select("textarea"),function(e){t.editor_deselector&&o(e,t.editor_deselector)||(!t.editor_selector||o(e,t.editor_selector))&&r(n(e),t)})}t.oninit&&(c=g=0,f(l,function(e){g++,e.initialized?c++:e.on("init",function(){c++,c==g&&i(t,"oninit")}),c==g&&i(t,"oninit")}))}var s=this,l=[],m;s.settings=t,u.bind(window,"ready",a)},get:function(e){return arguments.length?e in this.editors?this.editors[e]:null:this.editors},add:function(e){var t=this,n=t.editors;return n[e.id]=e,n.push(e),t.activeEditor=e,t.fire("AddEditor",{editor:e}),m||(m=function(){t.fire("BeforeUnload")},u.bind(window,"beforeunload",m)),e},createEditor:function(t,n){return this.add(new e(t,n,this))},remove:function(e){var t=this,n,r=t.editors,i;{if(e)return"string"==typeof e?(e=e.selector||e,void f(u.select(e),function(e){t.remove(r[e.id])})):(i=e,r[i.id]?(l(i)&&t.fire("RemoveEditor",{editor:i}),r.length||u.unbind(window,"beforeunload",m),i.remove(),i):null);for(n=r.length-1;n>=0;n--)t.remove(r[n])}},execCommand:function(t,n,r){var i=this,o=i.get(r);switch(t){case"mceAddEditor":return i.get(r)||new e(r,i.settings,i).render(),!0;case"mceRemoveEditor":return o&&o.remove(),!0;case"mceToggleEditor":return o?(o.isHidden()?o.show():o.hide(),!0):(i.execCommand("mceAddEditor",0,r),!0)}return i.activeEditor?i.activeEditor.execCommand(t,n,r):!1},triggerSave:function(){f(this.editors,function(e){e.save()})},addI18n:function(e,t){a.add(e,t)},translate:function(e){return a.translate(e)}},p(g,o),g.setup(),window.tinymce=window.tinyMCE=g,g}),r(ft,[dt,p],function(e,t){var n=t.each,r=t.explode;e.on("AddEditor",function(e){var t=e.editor;t.on("preInit",function(){function e(e,t){n(t,function(t,n){t&&s.setStyle(e,n,t)}),s.rename(e,"span")}function i(e){s=t.dom,l.convert_fonts_to_spans&&n(s.select("font,u,strike",e.node),function(e){o[e.nodeName.toLowerCase()](s,e)})}var o,a,s,l=t.settings;l.inline_styles&&(a=r(l.font_size_legacy_values),o={font:function(t,n){e(n,{backgroundColor:n.style.backgroundColor,color:n.color,fontFamily:n.face,fontSize:a[parseInt(n.size,10)-1]})},u:function(t,n){e(n,{textDecoration:"underline"})},strike:function(t,n){e(n,{textDecoration:"line-through"})}},t.on("PreProcess SetContent",i))})})}),r(pt,[],function(){return{send:function(e){function t(){!e.async||4==n.readyState||r++>1e4?(e.success&&1e4>r&&200==n.status?e.success.call(e.success_scope,""+n.responseText,n,e):e.error&&e.error.call(e.error_scope,r>1e4?"TIMED_OUT":"GENERAL",n,e),n=null):setTimeout(t,10)}var n,r=0;if(e.scope=e.scope||this,e.success_scope=e.success_scope||e.scope,e.error_scope=e.error_scope||e.scope,e.async=e.async===!1?!1:!0,e.data=e.data||"",n=new XMLHttpRequest){if(n.overrideMimeType&&n.overrideMimeType(e.content_type),n.open(e.type||(e.data?"POST":"GET"),e.url,e.async),e.content_type&&n.setRequestHeader("Content-Type",e.content_type),n.setRequestHeader("X-Requested-With","XMLHttpRequest"),n.send(e.data),!e.async)return t();setTimeout(t,10)}}}}),r(ht,[],function(){function e(t,n){var r,i,o,a;if(n=n||'"',null===t)return"null";if(o=typeof t,"string"==o)return i="\bb t\nn\ff\rr\"\"''\\\\",n+t.replace(/([\u0080-\uFFFF\x00-\x1f\"\'\\])/g,function(e,t){return'"'===n&&"'"===e?e:(r=i.indexOf(t),r+1?"\\"+i.charAt(r+1):(e=t.charCodeAt().toString(16),"\\u"+"0000".substring(e.length)+e))})+n;if("object"==o){if(t.hasOwnProperty&&"[object Array]"===Object.prototype.toString.call(t)){for(r=0,i="[";r<t.length;r++)i+=(r>0?",":"")+e(t[r],n);return i+"]"}i="{";for(a in t)t.hasOwnProperty(a)&&(i+="function"!=typeof t[a]?(i.length>1?","+n:n)+a+n+":"+e(t[a],n):"");return i+"}"}return""+t}return{serialize:e,parse:function(e){try{return window[String.fromCharCode(101)+"val"]("("+e+")")}catch(t){}}}}),r(mt,[ht,pt,p],function(e,t,n){function r(e){this.settings=i({},e),this.count=0}var i=n.extend;return r.sendRPC=function(e){return(new r).send(e)},r.prototype={send:function(n){var r=n.error,o=n.success;n=i(this.settings,n),n.success=function(t,i){t=e.parse(t),"undefined"==typeof t&&(t={error:"JSON Parse error."}),t.error?r.call(n.error_scope||n.scope,t.error,i):o.call(n.success_scope||n.scope,t.result)},n.error=function(e,t){r&&r.call(n.error_scope||n.scope,e,t)},n.data=e.serialize({id:n.id||"c"+this.count++,method:n.method,params:n.params}),n.content_type="application/json",t.send(n)}},r}),r(gt,[y],function(e){return{callbacks:{},count:0,send:function(n){var r=this,i=e.DOM,o=n.count!==t?n.count:r.count,a="tinymce_jsonp_"+o;r.callbacks[o]=function(e){i.remove(a),delete r.callbacks[o],n.callback(e)},i.add(i.doc.body,"script",{id:a,src:n.url,type:"text/javascript"}),r.count++}}}),r(vt,[],function(){function e(){s=[];for(var e in a)s.push(e);i.length=s.length}function n(){function n(e){var n,r;return r=e!==t?u+e:i.indexOf(",",u),-1===r||r>i.length?null:(n=i.substring(u,r),u=r+1,n)}var r,i,s,u=0;if(a={},c){o.load(l),i=o.getAttribute(l)||"";do{var d=n();if(null===d)break;if(r=n(parseInt(d,32)||0),null!==r){if(d=n(),null===d)break;s=n(parseInt(d,32)||0),r&&(a[r]=s)}}while(null!==r);e()}}function r(){var t,n="";if(c){for(var r in a)t=a[r],n+=(n?",":"")+r.length.toString(32)+","+r+","+t.length.toString(32)+","+t;o.setAttribute(l,n);try{o.save(l)}catch(i){}e()}}var i,o,a,s,l,c;try{if(window.localStorage)return localStorage}catch(u){}return l="tinymce",o=document.documentElement,c=!!o.addBehavior,c&&o.addBehavior("#default#userData"),i={key:function(e){return s[e]},getItem:function(e){return e in a?a[e]:null},setItem:function(e,t){a[e]=""+t,r()},removeItem:function(e){delete a[e],r()},clear:function(){a={},r()}},n(),i}),r(yt,[y,l,b,C,p,g],function(e,t,n,r,i,o){var a=window.tinymce;return a.DOM=e.DOM,a.ScriptLoader=n.ScriptLoader,a.PluginManager=r.PluginManager,a.ThemeManager=r.ThemeManager,a.dom=a.dom||{},a.dom.Event=t.Event,i.each(i,function(e,t){a[t]=e}),i.each("isOpera isWebKit isIE isGecko isMac".split(" "),function(e){a[e]=o[e.substr(2).toLowerCase()]}),{}}),r(bt,[z,p],function(e,t){return e.extend({Defaults:{firstControlClass:"first",lastControlClass:"last"},init:function(e){this.settings=t.extend({},this.Defaults,e)},preRender:function(e){e.addClass(this.settings.containerClass,"body")},applyClasses:function(e){var t=this,n=t.settings,r,i,o;r=e.items().filter(":visible"),i=n.firstControlClass,o=n.lastControlClass,r.each(function(e){e.removeClass(i).removeClass(o),n.controlClass&&e.addClass(n.controlClass)}),r.eq(0).addClass(i),r.eq(-1).addClass(o)},renderHtml:function(e){var t=this,n=t.settings,r,i="";return r=e.items(),r.eq(0).addClass(n.firstControlClass),r.eq(-1).addClass(n.lastControlClass),r.each(function(e){n.controlClass&&e.addClass(n.controlClass),i+=e.renderHtml()}),i},recalc:function(){},postRender:function(){}})}),r(Ct,[bt],function(e){return e.extend({Defaults:{containerClass:"abs-layout",controlClass:"abs-layout-item"},recalc:function(e){e.items().filter(":visible").each(function(e){var t=e.settings;e.layoutRect({x:t.x,y:t.y,w:t.w,h:t.h}),e.recalc&&e.recalc()})},renderHtml:function(e){return'<div id="'+e._id+'-absend" class="'+e.classPrefix+'abs-end"></div>'+this._super(e)}})}),r(xt,[$,Q],function(e,t){return e.extend({Mixins:[t],Defaults:{classes:"widget tooltip tooltip-n"},text:function(e){var t=this;return"undefined"!=typeof e?(t._value=e,t._rendered&&(t.getEl().lastChild.innerHTML=t.encode(e)),t):t._value},renderHtml:function(){var e=this,t=e.classPrefix;return'<div id="'+e._id+'" class="'+e.classes()+'" role="presentation"><div class="'+t+'tooltip-arrow"></div><div class="'+t+'tooltip-inner">'+e.encode(e._text)+"</div></div>"},repaint:function(){var e=this,t,n;t=e.getEl().style,n=e._layoutRect,t.left=n.x+"px",t.top=n.y+"px",t.zIndex=131070}})}),r(wt,[$,xt],function(e,t){var n,r=e.extend({init:function(e){var t=this;t._super(e),e=t.settings,t.canFocus=!0,e.tooltip&&r.tooltips!==!1&&(t.on("mouseenter",function(n){var r=t.tooltip().moveTo(-65535);if(n.control==t){var i=r.text(e.tooltip).show().testMoveRel(t.getEl(),["bc-tc","bc-tl","bc-tr"]);r.toggleClass("tooltip-n","bc-tc"==i),r.toggleClass("tooltip-nw","bc-tl"==i),r.toggleClass("tooltip-ne","bc-tr"==i),r.moveRel(t.getEl(),i)}else r.hide()}),t.on("mouseleave mousedown click",function(){t.tooltip().hide()})),t.aria("label",e.ariaLabel||e.tooltip)},tooltip:function(){return n||(n=new t({type:"tooltip"}),n.renderTo()),n},active:function(e){var t=this,n;return e!==n&&(t.aria("pressed",e),t.toggleClass("active",e)),t._super(e)},disabled:function(e){var t=this,n;return e!==n&&(t.aria("disabled",e),t.toggleClass("disabled",e)),t._super(e)},postRender:function(){var e=this,t=e.settings;e._rendered=!0,e._super(),e.parent()||!t.width&&!t.height||(e.initLayoutRect(),e.repaint()),t.autofocus&&e.focus()},remove:function(){this._super(),n&&(n.remove(),n=null)}});return r}),r(_t,[wt],function(e){return e.extend({Defaults:{classes:"widget btn",role:"button"},init:function(e){var t=this,n;t.on("click mousedown",function(e){e.preventDefault()}),t._super(e),n=e.size,e.subtype&&t.addClass(e.subtype),n&&t.addClass("btn-"+n)},icon:function(e){var t=this,n=t.classPrefix;if("undefined"==typeof e)return t.settings.icon;if(t.settings.icon=e,e=e?n+"ico "+n+"i-"+t.settings.icon:"",t._rendered){var r=t.getEl().firstChild,i=r.getElementsByTagName("i")[0];e?(i&&i==r.firstChild||(i=document.createElement("i"),r.insertBefore(i,r.firstChild)),i.className=e):i&&r.removeChild(i),t.text(t._text)}return t},repaint:function(){var e=this.getEl().firstChild.style;e.width=e.height="100%",this._super()},text:function(e){var t=this;if(t._rendered){var n=t.getEl().lastChild.lastChild;n&&(n.data=t.translate(e))}return t._super(e)},renderHtml:function(){var e=this,t=e._id,n=e.classPrefix,r=e.settings.icon,i;return i=e.settings.image,i?(r="none","string"!=typeof i&&(i=window.getSelection?i[0]:i[1]),i=" style=\"background-image: url('"+i+"')\""):i="",r=e.settings.icon?n+"ico "+n+"i-"+r:"",'<div id="'+t+'" class="'+e.classes()+'" tabindex="-1" aria-labelledby="'+t+'"><button role="presentation" type="button" tabindex="-1">'+(r?'<i class="'+r+'"'+i+"></i>":"")+(e._text?(r?"\xa0":"")+e.encode(e._text):"")+"</button></div>"}})}),r(Nt,[G],function(e){return e.extend({Defaults:{defaultType:"button",role:"group"},renderHtml:function(){var e=this,t=e._layout;return e.addClass("btn-group"),e.preRender(),t.preRender(e),'<div id="'+e._id+'" class="'+e.classes()+'"><div id="'+e._id+'-body">'+(e.settings.html||"")+t.renderHtml(e)+"</div></div>"}})}),r(Et,[wt],function(e){return e.extend({Defaults:{classes:"checkbox",role:"checkbox",checked:!1},init:function(e){var t=this;t._super(e),t.on("click mousedown",function(e){e.preventDefault()}),t.on("click",function(e){e.preventDefault(),t.disabled()||t.checked(!t.checked())}),t.checked(t.settings.checked)},checked:function(e){var t=this;return"undefined"!=typeof e?(e?t.addClass("checked"):t.removeClass("checked"),t._checked=e,t.aria("checked",e),t):t._checked},value:function(e){return this.checked(e)},renderHtml:function(){var e=this,t=e._id,n=e.classPrefix;return'<div id="'+t+'" class="'+e.classes()+'" unselectable="on" aria-labelledby="'+t+'-al" tabindex="-1"><i class="'+n+"ico "+n+'i-checkbox"></i><span id="'+t+'-al" class="'+n+'label">'+e.encode(e._text)+"</span></div>"}})}),r(St,[_t,et],function(e,t){return e.extend({showPanel:function(){var e=this,n=e.settings;if(e.active(!0),e.panel)e.panel.show();else{var r=n.panel;r.type&&(r={layout:"grid",items:r}),r.role=r.role||"dialog",r.popover=!0,r.autohide=!0,r.ariaRoot=!0,e.panel=new t(r).on("hide",function(){e.active(!1)}).on("cancel",function(t){t.stopPropagation(),e.focus(),e.hidePanel()}).parent(e).renderTo(e.getContainerElm()),e.panel.fire("show"),e.panel.reflow()}e.panel.moveRel(e.getEl(),n.popoverAlign||(e.isRtl()?["bc-tr","bc-tc"]:["bc-tl","bc-tc"]))},hidePanel:function(){var e=this;e.panel&&e.panel.hide()},postRender:function(){var e=this;return e.aria("haspopup",!0),e.on("click",function(t){t.control===e&&(e.panel&&e.panel.visible()?e.hidePanel():(e.showPanel(),e.panel.focus(!!t.aria)))}),e._super()}})}),r(kt,[St,y],function(e,t){var n=t.DOM;return e.extend({init:function(e){this._super(e),this.addClass("colorbutton")},color:function(e){return e?(this._color=e,this.getEl("preview").style.backgroundColor=e,this):this._color},renderHtml:function(){var e=this,t=e._id,n=e.classPrefix,r=e.settings.icon?n+"ico "+n+"i-"+e.settings.icon:"",i=e.settings.image?" style=\"background-image: url('"+e.settings.image+"')\"":"";return'<div id="'+t+'" class="'+e.classes()+'" role="button" tabindex="-1" aria-haspopup="true"><button role="presentation" hidefocus="1" type="button" tabindex="-1">'+(r?'<i class="'+r+'"'+i+"></i>":"")+'<span id="'+t+'-preview" class="'+n+'preview"></span>'+(e._text?(r?" ":"")+e._text:"")+'</button><button type="button" class="'+n+'open" hidefocus="1" tabindex="-1"> <i class="'+n+'caret"></i></button></div>'},postRender:function(){var e=this,t=e.settings.onclick;return e.on("click",function(r){r.aria&&"down"==r.aria.key||r.control!=e||n.getParent(r.target,"."+e.classPrefix+"open")||(r.stopImmediatePropagation(),t.call(e,r))}),delete e.settings.onclick,e._super()}})}),r(Tt,[wt,j,q],function(e,t,n){return e.extend({init:function(e){var t=this;t._super(e),t.addClass("combobox"),t.subinput=!0,t.ariaTarget="inp",e=t.settings,e.menu=e.menu||e.values,e.menu&&(e.icon="caret"),t.on("click",function(n){for(var r=n.target,i=t.getEl();r&&r!=i;)r.id&&-1!=r.id.indexOf("-open")&&(t.fire("action"),e.menu&&(t.showMenu(),n.aria&&t.menu.items()[0].focus())),r=r.parentNode}),t.on("keydown",function(e){"INPUT"==e.target.nodeName&&13==e.keyCode&&t.parents().reverse().each(function(n){return e.preventDefault(),t.fire("change"),n.hasEventListeners("submit")&&n.toJSON?(n.fire("submit",{data:n.toJSON()}),!1):void 0})}),e.placeholder&&(t.addClass("placeholder"),t.on("focusin",function(){t._hasOnChange||(n.on(t.getEl("inp"),"change",function(){t.fire("change")}),t._hasOnChange=!0),t.hasClass("placeholder")&&(t.getEl("inp").value="",t.removeClass("placeholder"))}),t.on("focusout",function(){0===t.value().length&&(t.getEl("inp").value=e.placeholder,t.addClass("placeholder"))}))},showMenu:function(){var e=this,n=e.settings,r;e.menu||(r=n.menu||[],r.length?r={type:"menu",items:r}:r.type=r.type||"menu",e.menu=t.create(r).parent(e).renderTo(e.getContainerElm()),e.fire("createmenu"),e.menu.reflow(),e.menu.on("cancel",function(t){t.control===e.menu&&e.focus()}),e.menu.on("show hide",function(t){t.control.items().each(function(t){t.active(t.value()==e.value())})}).fire("show"),e.menu.on("select",function(t){e.value(t.control.value())}),e.on("focusin",function(t){"INPUT"==t.target.tagName.toUpperCase()&&e.menu.hide()}),e.aria("expanded",!0)),e.menu.show(),e.menu.layoutRect({w:e.layoutRect().w}),e.menu.moveRel(e.getEl(),e.isRtl()?["br-tr","tr-br"]:["bl-tl","tl-bl"])},value:function(e){var t=this;return"undefined"!=typeof e?(t._value=e,t.removeClass("placeholder"),t._rendered&&(t.getEl("inp").value=e),t):t._rendered?(e=t.getEl("inp").value,e!=t.settings.placeholder?e:""):t._value},disabled:function(e){var t=this;return t._rendered&&"undefined"!=typeof e&&(t.getEl("inp").disabled=e),t._super(e)},focus:function(){this.getEl("inp").focus()},repaint:function(){var e=this,t=e.getEl(),r=e.getEl("open"),i=e.layoutRect(),o,a;o=r?i.w-n.getSize(r).width-10:i.w-10;var s=document;return s.all&&(!s.documentMode||s.documentMode<=8)&&(a=e.layoutRect().h-2+"px"),n.css(t.firstChild,{width:o,lineHeight:a}),e._super(),e},postRender:function(){var e=this;return n.on(this.getEl("inp"),"change",function(){e.fire("change")}),e._super()},remove:function(){n.off(this.getEl("inp")),this._super()},renderHtml:function(){var e=this,t=e._id,n=e.settings,r=e.classPrefix,i=n.value||n.placeholder||"",o,a,s="",l="";return"spellcheck"in n&&(l+=' spellcheck="'+n.spellcheck+'"'),n.maxLength&&(l+=' maxlength="'+n.maxLength+'"'),n.size&&(l+=' size="'+n.size+'"'),n.subtype&&(l+=' type="'+n.subtype+'"'),e.disabled()&&(l+=' disabled="disabled"'),o=n.icon,o&&"caret"!=o&&(o=r+"ico "+r+"i-"+n.icon),a=e._text,(o||a)&&(s='<div id="'+t+'-open" class="'+r+"btn "+r+'open" tabIndex="-1" role="button"><button id="'+t+'-action" type="button" hidefocus="1" tabindex="-1">'+("caret"!=o?'<i class="'+o+'"></i>':'<i class="'+r+'caret"></i>')+(a?(o?" ":"")+a:"")+"</button></div>",e.addClass("has-open")),'<div id="'+t+'" class="'+e.classes()+'"><input id="'+t+'-inp" class="'+r+"textbox "+r+'placeholder" value="'+i+'" hidefocus="1"'+l+" />"+s+"</div>"
+}})}),r(Rt,[wt],function(e){return e.extend({init:function(e){var t=this;e.delimiter||(e.delimiter="\xbb"),t._super(e),t.addClass("path"),t.canFocus=!0,t.on("click",function(e){var n,r=e.target;(n=r.getAttribute("data-index"))&&t.fire("select",{value:t.data()[n],index:n})})},focus:function(){var e=this;return e.getEl().firstChild.focus(),e},data:function(e){var t=this;return"undefined"!=typeof e?(t._data=e,t.update(),t):t._data},update:function(){this.innerHtml(this._getPathHtml())},postRender:function(){var e=this;e._super(),e.data(e.settings.data)},renderHtml:function(){var e=this;return'<div id="'+e._id+'" class="'+e.classes()+'">'+e._getPathHtml()+"</div>"},_getPathHtml:function(){var e=this,t=e._data||[],n,r,i="",o=e.classPrefix;for(n=0,r=t.length;r>n;n++)i+=(n>0?'<div class="'+o+'divider" aria-hidden="true"> '+e.settings.delimiter+" </div>":"")+'<div role="button" class="'+o+"path-item"+(n==r-1?" "+o+"last":"")+'" data-index="'+n+'" tabindex="-1" id="'+e._id+"-"+n+'" aria-level="'+n+'">'+t[n].name+"</div>";return i||(i='<div class="'+o+'path-item">\xa0</div>'),i}})}),r(At,[Rt,dt],function(e,t){return e.extend({postRender:function(){function e(e){if(1===e.nodeType){if("BR"==e.nodeName||e.getAttribute("data-mce-bogus"))return!0;if("bookmark"===e.getAttribute("data-mce-type"))return!0}return!1}var n=this,r=t.activeEditor;return n.on("select",function(t){var n=[],i,o=r.getBody();for(r.focus(),i=r.selection.getStart();i&&i!=o;)e(i)||n.push(i),i=i.parentNode;r.selection.select(n[n.length-1-t.index]),r.nodeChanged()}),r.on("nodeChange",function(t){for(var i=[],o=t.parents,a=o.length;a--;)if(1==o[a].nodeType&&!e(o[a])){var s=r.fire("ResolveName",{name:o[a].nodeName.toLowerCase(),target:o[a]});i.push({name:s.name})}n.data(i)}),n._super()}})}),r(Bt,[G],function(e){return e.extend({Defaults:{layout:"flex",align:"center",defaults:{flex:1}},renderHtml:function(){var e=this,t=e._layout,n=e.classPrefix;return e.addClass("formitem"),t.preRender(e),'<div id="'+e._id+'" class="'+e.classes()+'" hidefocus="1" tabindex="-1">'+(e.settings.title?'<div id="'+e._id+'-title" class="'+n+'title">'+e.settings.title+"</div>":"")+'<div id="'+e._id+'-body" class="'+e.classes("body")+'">'+(e.settings.html||"")+t.renderHtml(e)+"</div></div>"}})}),r(Dt,[G,Bt],function(e,t){return e.extend({Defaults:{containerCls:"form",layout:"flex",direction:"column",align:"stretch",flex:1,padding:20,labelGap:30,spacing:10,callbacks:{submit:function(){this.submit()}}},preRender:function(){var e=this,n=e.items();n.each(function(n){var r,i=n.settings.label;i&&(r=new t({layout:"flex",autoResize:"overflow",defaults:{flex:1},items:[{type:"label",id:n._id+"-l",text:i,flex:0,forId:n._id,disabled:n.disabled()}]}),r.type="formitem",n.aria("labelledby",n._id+"-l"),"undefined"==typeof n.settings.flex&&(n.settings.flex=1),e.replace(n,r),r.add(n))})},recalcLabels:function(){var e=this,t=0,n=[],r,i;if(e.settings.labelGapCalc!==!1)for(e.items().filter("formitem").each(function(e){var r=e.items()[0],i=r.getEl().clientWidth;t=i>t?i:t,n.push(r)}),i=e.settings.labelGap||0,r=n.length;r--;)n[r].settings.minWidth=t+i},visible:function(e){var t=this._super(e);return e===!0&&this._rendered&&this.recalcLabels(),t},submit:function(){return this.fire("submit",{data:this.toJSON()})},postRender:function(){var e=this;e._super(),e.recalcLabels(),e.fromJSON(e.settings.data)}})}),r(Lt,[Dt],function(e){return e.extend({Defaults:{containerCls:"fieldset",layout:"flex",direction:"column",align:"stretch",flex:1,padding:"25 15 5 15",labelGap:30,spacing:10,border:1},renderHtml:function(){var e=this,t=e._layout,n=e.classPrefix;return e.preRender(),t.preRender(e),'<fieldset id="'+e._id+'" class="'+e.classes()+'" hidefocus="1" tabindex="-1">'+(e.settings.title?'<legend id="'+e._id+'-title" class="'+n+'fieldset-title">'+e.settings.title+"</legend>":"")+'<div id="'+e._id+'-body" class="'+e.classes("body")+'">'+(e.settings.html||"")+t.renderHtml(e)+"</div></fieldset>"}})}),r(Mt,[Tt],function(e){return e.extend({init:function(e){var t=this,n=tinymce.activeEditor,r;e.spellcheck=!1,r=n.settings.file_browser_callback,r&&(e.icon="browse",e.onaction=function(){r(t.getEl("inp").id,t.getEl("inp").value,e.filetype,window)}),t._super(e)}})}),r(Ht,[Ct],function(e){return e.extend({recalc:function(e){var t=e.layoutRect(),n=e.paddingBox();e.items().filter(":visible").each(function(e){e.layoutRect({x:n.left,y:n.top,w:t.innerW-n.right-n.left,h:t.innerH-n.top-n.bottom}),e.recalc&&e.recalc()})}})}),r(Pt,[Ct],function(e){return e.extend({recalc:function(e){var t,n,r,i,o,a,s,l,c,u,d,f,p,h,m,g,v=[],y,b,C,x,w,_,N,E,S,k,T,R,A,B,D,L,M,H,P,O,I,F,z=Math.max,W=Math.min;for(r=e.items().filter(":visible"),i=e.layoutRect(),o=e._paddingBox,a=e.settings,f=e.isRtl()?a.direction||"row-reversed":a.direction,s=a.align,l=e.isRtl()?a.pack||"end":a.pack,c=a.spacing||0,("row-reversed"==f||"column-reverse"==f)&&(r=r.set(r.toArray().reverse()),f=f.split("-")[0]),"column"==f?(S="y",N="h",E="minH",k="maxH",R="innerH",T="top",A="deltaH",B="contentH",P="left",M="w",D="x",L="innerW",H="minW",O="right",I="deltaW",F="contentW"):(S="x",N="w",E="minW",k="maxW",R="innerW",T="left",A="deltaW",B="contentW",P="top",M="h",D="y",L="innerH",H="minH",O="bottom",I="deltaH",F="contentH"),d=i[R]-o[T]-o[T],_=u=0,t=0,n=r.length;n>t;t++)p=r[t],h=p.layoutRect(),m=p.settings,g=m.flex,d-=n-1>t?c:0,g>0&&(u+=g,h[k]&&v.push(p),h.flex=g),d-=h[E],y=o[P]+h[H]+o[O],y>_&&(_=y);if(x={},x[E]=0>d?i[E]-d+i[A]:i[R]-d+i[A],x[H]=_+i[I],x[B]=i[R]-d,x[F]=_,x.minW=W(x.minW,i.maxW),x.minH=W(x.minH,i.maxH),x.minW=z(x.minW,i.startMinWidth),x.minH=z(x.minH,i.startMinHeight),!i.autoResize||x.minW==i.minW&&x.minH==i.minH){for(C=d/u,t=0,n=v.length;n>t;t++)p=v[t],h=p.layoutRect(),b=h[k],y=h[E]+h.flex*C,y>b?(d-=h[k]-h[E],u-=h.flex,h.flex=0,h.maxFlexSize=b):h.maxFlexSize=0;for(C=d/u,w=o[T],x={},0===u&&("end"==l?w=d+o[T]:"center"==l?(w=Math.round(i[R]/2-(i[R]-d)/2)+o[T],0>w&&(w=o[T])):"justify"==l&&(w=o[T],c=Math.floor(d/(r.length-1)))),x[D]=o[P],t=0,n=r.length;n>t;t++)p=r[t],h=p.layoutRect(),y=h.maxFlexSize||h[E],"center"===s?x[D]=Math.round(i[L]/2-h[M]/2):"stretch"===s?(x[M]=z(h[H]||0,i[L]-o[P]-o[O]),x[D]=o[P]):"end"===s&&(x[D]=i[L]-h[M]-o.top),h.flex>0&&(y+=h.flex*C),x[N]=y,x[S]=w,p.layoutRect(x),p.recalc&&p.recalc(),w+=y+c}else if(x.w=x.minW,x.h=x.minH,e.layoutRect(x),this.recalc(e),null===e._lastRect){var V=e.parent();V&&(V._lastRect=null,V.recalc())}}})}),r(Ot,[bt],function(e){return e.extend({Defaults:{containerClass:"flow-layout",controlClass:"flow-layout-item",endClass:"break"},recalc:function(e){e.items().filter(":visible").each(function(e){e.recalc&&e.recalc()})}})}),r(It,[$,wt,et,p,dt,g],function(e,t,n,r,i,o){function a(e){function t(t,n){return function(){var r=this;e.on("nodeChange",function(i){var o=e.formatter,a=null;s(i.parents,function(e){return s(t,function(t){return n?o.matchNode(e,n,{value:t.value})&&(a=t.value):o.matchNode(e,t.value)&&(a=t.value),a?!1:void 0}),a?!1:void 0}),r.value(a)})}}function r(e){e=e.replace(/;$/,"").split(";");for(var t=e.length;t--;)e[t]=e[t].split("=");return e}function i(){function t(e){var n=[];if(e)return s(e,function(e){var o={text:e.title,icon:e.icon};if(e.items)o.menu=t(e.items);else{var a=e.format||"custom"+r++;e.format||(e.name=a,i.push(e)),o.format=a}n.push(o)}),n}function n(){var n;return n=t(e.settings.style_formats_merge?e.settings.style_formats?o.concat(e.settings.style_formats):o:e.settings.style_formats||o)}var r=0,i=[],o=[{title:"Headings",items:[{title:"Heading 1",format:"h1"},{title:"Heading 2",format:"h2"},{title:"Heading 3",format:"h3"},{title:"Heading 4",format:"h4"},{title:"Heading 5",format:"h5"},{title:"Heading 6",format:"h6"}]},{title:"Inline",items:[{title:"Bold",icon:"bold",format:"bold"},{title:"Italic",icon:"italic",format:"italic"},{title:"Underline",icon:"underline",format:"underline"},{title:"Strikethrough",icon:"strikethrough",format:"strikethrough"},{title:"Superscript",icon:"superscript",format:"superscript"},{title:"Subscript",icon:"subscript",format:"subscript"},{title:"Code",icon:"code",format:"code"}]},{title:"Blocks",items:[{title:"Paragraph",format:"p"},{title:"Blockquote",format:"blockquote"},{title:"Div",format:"div"},{title:"Pre",format:"pre"}]},{title:"Alignment",items:[{title:"Left",icon:"alignleft",format:"alignleft"},{title:"Center",icon:"aligncenter",format:"aligncenter"},{title:"Right",icon:"alignright",format:"alignright"},{title:"Justify",icon:"alignjustify",format:"alignjustify"}]}];return e.on("init",function(){s(i,function(t){e.formatter.register(t.name,t)})}),{type:"menu",items:n(),onPostRender:function(t){e.fire("renderFormatsMenu",{control:t.control})},itemDefaults:{preview:!0,textStyle:function(){return this.settings.format?e.formatter.getCssText(this.settings.format):void 0},onPostRender:function(){var t=this,n=this.settings.format;n&&t.parent().on("show",function(){t.disabled(!e.formatter.canApply(n)),t.active(e.formatter.match(n))})},onclick:function(){this.settings.format&&d(this.settings.format)}}}}function o(){return e.undoManager?e.undoManager.hasUndo():!1}function a(){return e.undoManager?e.undoManager.hasRedo():!1}function l(){var t=this;t.disabled(!o()),e.on("Undo Redo AddUndo TypingUndo",function(){t.disabled(!o())})}function c(){var t=this;t.disabled(!a()),e.on("Undo Redo AddUndo TypingUndo",function(){t.disabled(!a())})}function u(){var t=this;e.on("VisualAid",function(e){t.active(e.hasVisual)}),t.active(e.hasVisual)}function d(t){t.control&&(t=t.control.value()),t&&e.execCommand("mceToggleFormat",!1,t)}var f;f=i(),s({bold:"Bold",italic:"Italic",underline:"Underline",strikethrough:"Strikethrough",subscript:"Subscript",superscript:"Superscript"},function(t,n){e.addButton(n,{tooltip:t,onPostRender:function(){var t=this;e.formatter?e.formatter.formatChanged(n,function(e){t.active(e)}):e.on("init",function(){e.formatter.formatChanged(n,function(e){t.active(e)})})},onclick:function(){d(n)}})}),s({outdent:["Decrease indent","Outdent"],indent:["Increase indent","Indent"],cut:["Cut","Cut"],copy:["Copy","Copy"],paste:["Paste","Paste"],help:["Help","mceHelp"],selectall:["Select all","SelectAll"],hr:["Insert horizontal rule","InsertHorizontalRule"],removeformat:["Clear formatting","RemoveFormat"],visualaid:["Visual aids","mceToggleVisualAid"],newdocument:["New document","mceNewDocument"]},function(t,n){e.addButton(n,{tooltip:t[0],cmd:t[1]})}),s({blockquote:["Blockquote","mceBlockQuote"],numlist:["Numbered list","InsertOrderedList"],bullist:["Bullet list","InsertUnorderedList"],subscript:["Subscript","Subscript"],superscript:["Superscript","Superscript"],alignleft:["Align left","JustifyLeft"],aligncenter:["Align center","JustifyCenter"],alignright:["Align right","JustifyRight"],alignjustify:["Justify","JustifyFull"]},function(t,n){e.addButton(n,{tooltip:t[0],cmd:t[1],onPostRender:function(){var t=this;e.formatter?e.formatter.formatChanged(n,function(e){t.active(e)}):e.on("init",function(){e.formatter.formatChanged(n,function(e){t.active(e)})})}})}),e.addButton("undo",{tooltip:"Undo",onPostRender:l,cmd:"undo"}),e.addButton("redo",{tooltip:"Redo",onPostRender:c,cmd:"redo"}),e.addMenuItem("newdocument",{text:"New document",shortcut:"Ctrl+N",icon:"newdocument",cmd:"mceNewDocument"}),e.addMenuItem("undo",{text:"Undo",icon:"undo",shortcut:"Ctrl+Z",onPostRender:l,cmd:"undo"}),e.addMenuItem("redo",{text:"Redo",icon:"redo",shortcut:"Ctrl+Y",onPostRender:c,cmd:"redo"}),e.addMenuItem("visualaid",{text:"Visual aids",selectable:!0,onPostRender:u,cmd:"mceToggleVisualAid"}),s({cut:["Cut","Cut","Ctrl+X"],copy:["Copy","Copy","Ctrl+C"],paste:["Paste","Paste","Ctrl+V"],selectall:["Select all","SelectAll","Ctrl+A"],bold:["Bold","Bold","Ctrl+B"],italic:["Italic","Italic","Ctrl+I"],underline:["Underline","Underline"],strikethrough:["Strikethrough","Strikethrough"],subscript:["Subscript","Subscript"],superscript:["Superscript","Superscript"],removeformat:["Clear formatting","RemoveFormat"]},function(t,n){e.addMenuItem(n,{text:t[0],icon:n,shortcut:t[2],cmd:t[1]})}),e.on("mousedown",function(){n.hideAll()}),e.addButton("styleselect",{type:"menubutton",text:"Formats",menu:f}),e.addButton("formatselect",function(){var n=[],i=r(e.settings.block_formats||"Paragraph=p;Address=address;Pre=pre;Heading 1=h1;Heading 2=h2;Heading 3=h3;Heading 4=h4;Heading 5=h5;Heading 6=h6");return s(i,function(t){n.push({text:t[0],value:t[1],textStyle:function(){return e.formatter.getCssText(t[1])}})}),{type:"listbox",text:i[0][0],values:n,fixedWidth:!0,onselect:d,onPostRender:t(n)}}),e.addButton("fontselect",function(){var n="Andale Mono=andale mono,times;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;Comic Sans MS=comic sans ms,sans-serif;Courier New=courier new,courier;Georgia=georgia,palatino;Helvetica=helvetica;Impact=impact,chicago;Symbol=symbol;Tahoma=tahoma,arial,helvetica,sans-serif;Terminal=terminal,monaco;Times New Roman=times new roman,times;Trebuchet MS=trebuchet ms,geneva;Verdana=verdana,geneva;Webdings=webdings;Wingdings=wingdings,zapf dingbats",i=[],o=r(e.settings.font_formats||n);return s(o,function(e){i.push({text:{raw:e[0]},value:e[1],textStyle:-1==e[1].indexOf("dings")?"font-family:"+e[1]:""})}),{type:"listbox",text:"Font Family",tooltip:"Font Family",values:i,fixedWidth:!0,onPostRender:t(i,"fontname"),onselect:function(t){t.control.settings.value&&e.execCommand("FontName",!1,t.control.settings.value)}}}),e.addButton("fontsizeselect",function(){var n=[],r="8pt 10pt 12pt 14pt 18pt 24pt 36pt",i=e.settings.fontsize_formats||r;return s(i.split(" "),function(e){n.push({text:e,value:e})}),{type:"listbox",text:"Font Sizes",tooltip:"Font Sizes",values:n,fixedWidth:!0,onPostRender:t(n,"fontsize"),onclick:function(t){t.control.settings.value&&e.execCommand("FontSize",!1,t.control.settings.value)}}}),e.addMenuItem("formats",{text:"Formats",menu:f})}var s=r.each;i.on("AddEditor",function(t){t.editor.rtl&&(e.rtl=!0),a(t.editor)}),e.translate=function(e){return i.translate(e)},t.tooltips=!o.iOS}),r(Ft,[Ct],function(e){return e.extend({recalc:function(e){var t=e.settings,n,r,i,o,a,s,l,c,u,d,f,p,h,m,g,v,y,b,C,x,w,_,N=[],E=[],S,k,T,R;for(t=e.settings,i=e.items().filter(":visible"),o=e.layoutRect(),r=t.columns||Math.ceil(Math.sqrt(i.length)),n=Math.ceil(i.length/r),y=t.spacingH||t.spacing||0,b=t.spacingV||t.spacing||0,C=t.alignH||t.align,x=t.alignV||t.align,g=e._paddingBox,C&&"string"==typeof C&&(C=[C]),x&&"string"==typeof x&&(x=[x]),d=0;r>d;d++)N.push(0);for(f=0;n>f;f++)E.push(0);for(f=0;n>f;f++)for(d=0;r>d&&(u=i[f*r+d],u);d++)c=u.layoutRect(),S=c.minW,k=c.minH,N[d]=S>N[d]?S:N[d],E[f]=k>E[f]?k:E[f];for(T=o.innerW-g.left-g.right,w=0,d=0;r>d;d++)w+=N[d]+(d>0?y:0),T-=(d>0?y:0)+N[d];for(R=o.innerH-g.top-g.bottom,_=0,f=0;n>f;f++)_+=E[f]+(f>0?b:0),R-=(f>0?b:0)+E[f];if(w+=g.left+g.right,_+=g.top+g.bottom,l={},l.minW=w+(o.w-o.innerW),l.minH=_+(o.h-o.innerH),l.contentW=l.minW-o.deltaW,l.contentH=l.minH-o.deltaH,l.minW=Math.min(l.minW,o.maxW),l.minH=Math.min(l.minH,o.maxH),l.minW=Math.max(l.minW,o.startMinWidth),l.minH=Math.max(l.minH,o.startMinHeight),!o.autoResize||l.minW==o.minW&&l.minH==o.minH){o.autoResize&&(l=e.layoutRect(l),l.contentW=l.minW-o.deltaW,l.contentH=l.minH-o.deltaH);var A;A="start"==t.packV?0:R>0?Math.floor(R/n):0;var B=0,D=t.flexWidths;if(D)for(d=0;d<D.length;d++)B+=D[d];else B=r;var L=T/B;for(d=0;r>d;d++)N[d]+=D?D[d]*L:L;for(h=g.top,f=0;n>f;f++){for(p=g.left,s=E[f]+A,d=0;r>d&&(u=i[f*r+d],u);d++)m=u.settings,c=u.layoutRect(),a=Math.max(N[d],c.startMinWidth),c.x=p,c.y=h,v=m.alignH||(C?C[d]||C[0]:null),"center"==v?c.x=p+a/2-c.w/2:"right"==v?c.x=p+a-c.w:"stretch"==v&&(c.w=a),v=m.alignV||(x?x[d]||x[0]:null),"center"==v?c.y=h+s/2-c.h/2:"bottom"==v?c.y=h+s-c.h:"stretch"==v&&(c.h=s),u.layoutRect(c),p+=a+y,u.recalc&&u.recalc();h+=s+b}}else if(l.w=l.minW,l.h=l.minH,e.layoutRect(l),this.recalc(e),null===e._lastRect){var M=e.parent();M&&(M._lastRect=null,M.recalc())}}})}),r(zt,[wt],function(e){return e.extend({renderHtml:function(){var e=this;return e.addClass("iframe"),e.canFocus=!1,'<iframe id="'+e._id+'" class="'+e.classes()+'" tabindex="-1" src="'+(e.settings.url||"javascript:''")+'" frameborder="0"></iframe>'},src:function(e){this.getEl().src=e},html:function(e,t){var n=this,r=this.getEl().contentWindow.document.body;return r?(r.innerHTML=e,t&&t()):setTimeout(function(){n.html(e)},0),this}})}),r(Wt,[wt,q],function(e,t){return e.extend({init:function(e){var t=this;t._super(e),t.addClass("widget"),t.addClass("label"),t.canFocus=!1,e.multiline&&t.addClass("autoscroll"),e.strong&&t.addClass("strong")},initLayoutRect:function(){var e=this,n=e._super();if(e.settings.multiline){var r=t.getSize(e.getEl());r.width>n.maxW&&(n.minW=n.maxW,e.addClass("multiline")),e.getEl().style.width=n.minW+"px",n.startMinH=n.h=n.minH=Math.min(n.maxH,t.getSize(e.getEl()).height)}return n},repaint:function(){var e=this;return e.settings.multiline||(e.getEl().style.lineHeight=e.layoutRect().h+"px"),e._super()},text:function(e){var t=this;return t._rendered&&e&&this.innerHtml(t.encode(e)),t._super(e)},renderHtml:function(){var e=this,t=e.settings.forId;return'<label id="'+e._id+'" class="'+e.classes()+'"'+(t?' for="'+t+'"':"")+">"+e.encode(e._text)+"</label>"}})}),r(Vt,[G],function(e){return e.extend({Defaults:{role:"toolbar",layout:"flow"},init:function(e){var t=this;t._super(e),t.addClass("toolbar")},postRender:function(){var e=this;return e.items().addClass("toolbar-item"),e._super()}})}),r(Ut,[Vt],function(e){return e.extend({Defaults:{role:"menubar",containerCls:"menubar",ariaRoot:!0,defaults:{type:"menubutton"}}})}),r(qt,[_t,j,Ut],function(e,t,n){function r(e,t){for(;e;){if(t===e)return!0;e=e.parentNode}return!1}var i=e.extend({init:function(e){var t=this;t._renderOpen=!0,t._super(e),t.addClass("menubtn"),e.fixedWidth&&t.addClass("fixed-width"),t.aria("haspopup",!0),t.hasPopup=!0},showMenu:function(){var e=this,n=e.settings,r;return e.menu&&e.menu.visible()?e.hideMenu():(e.menu||(r=n.menu||[],r.length?r={type:"menu",items:r}:r.type=r.type||"menu",e.menu=t.create(r).parent(e).renderTo(),e.fire("createmenu"),e.menu.reflow(),e.menu.on("cancel",function(t){t.control.parent()===e.menu&&(t.stopPropagation(),e.focus(),e.hideMenu())}),e.menu.on("select",function(){e.focus()}),e.menu.on("show hide",function(t){t.control==e.menu&&e.activeMenu("show"==t.type),e.aria("expanded","show"==t.type)}).fire("show")),e.menu.show(),e.menu.layoutRect({w:e.layoutRect().w}),void e.menu.moveRel(e.getEl(),e.isRtl()?["br-tr","tr-br"]:["bl-tl","tl-bl"]))},hideMenu:function(){var e=this;e.menu&&(e.menu.items().each(function(e){e.hideMenu&&e.hideMenu()}),e.menu.hide())},activeMenu:function(e){this.toggleClass("active",e)},renderHtml:function(){var e=this,t=e._id,r=e.classPrefix,i=e.settings.icon?r+"ico "+r+"i-"+e.settings.icon:"";return e.aria("role",e.parent()instanceof n?"menuitem":"button"),'<div id="'+t+'" class="'+e.classes()+'" tabindex="-1" aria-labelledby="'+t+'"><button id="'+t+'-open" role="presentation" type="button" tabindex="-1">'+(i?'<i class="'+i+'"></i>':"")+"<span>"+(e._text?(i?"\xa0":"")+e.encode(e._text):"")+'</span> <i class="'+r+'caret"></i></button></div>'},postRender:function(){var e=this;return e.on("click",function(t){t.control===e&&r(t.target,e.getEl())&&(e.showMenu(),t.aria&&e.menu.items()[0].focus())}),e.on("mouseenter",function(t){var n=t.control,r=e.parent(),o;n&&r&&n instanceof i&&n.parent()==r&&(r.items().filter("MenuButton").each(function(e){e.hideMenu&&e!=n&&(e.menu&&e.menu.visible()&&(o=!0),e.hideMenu())}),o&&(n.focus(),n.showMenu()))}),e._super()},text:function(e){var t=this,n,r;if(t._rendered)for(r=t.getEl("open").getElementsByTagName("span"),n=0;n<r.length;n++)r[n].innerHTML=(t.settings.icon&&e?"\xa0":"")+t.encode(e);return this._super(e)},remove:function(){this._super(),this.menu&&this.menu.remove()}});return i}),r($t,[qt],function(e){return e.extend({init:function(e){var t=this,n,r,i,o,a;if(t._values=n=e.values,n){for(r=0;r<n.length;r++)if(i=n[r].selected||e.value===n[r].value){o=o||n[r].text,t._value=n[r].value;break}!i&&n.length>0&&(o=n[0].text,t._value=n[0].value),e.menu=n}e.text=e.text||o||n[0].text,t._super(e),t.addClass("listbox"),t.on("select",function(n){var r=n.control;a&&(n.lastControl=a),e.multiple?r.active(!r.active()):t.value(n.control.settings.value),a=r})},value:function(e){function t(e,n){e.items().each(function(e){r=e.value()===n,r&&(i=i||e.text()),e.active(r),e.menu&&t(e.menu,n)})}var n=this,r,i,o,a;if("undefined"!=typeof e){if(n.menu)t(n.menu,e);else for(o=n.settings.menu,a=0;a<o.length;a++)r=o[a].value==e,r&&(i=i||o[a].text),o[a].active=r;n.text(i||this.settings.text)}return n._super(e)}})}),r(jt,[wt,j,g],function(e,t,n){return e.extend({Defaults:{border:0,role:"menuitem"},init:function(e){var t=this;t.hasPopup=!0,t._super(e),e=t.settings,t.addClass("menu-item"),e.menu&&t.addClass("menu-item-expand"),e.preview&&t.addClass("menu-item-preview"),("-"===t._text||"|"===t._text)&&(t.addClass("menu-item-sep"),t.aria("role","separator"),t._text="-"),e.selectable&&(t.aria("role","menuitemcheckbox"),t.addClass("menu-item-checkbox"),e.icon="selected"),e.preview||e.selectable||t.addClass("menu-item-normal"),t.on("mousedown",function(e){e.preventDefault()}),e.menu&&!e.ariaHideMenu&&t.aria("haspopup",!0)},hasMenus:function(){return!!this.settings.menu},showMenu:function(){var e=this,n=e.settings,r,i=e.parent();if(i.items().each(function(t){t!==e&&t.hideMenu()}),n.menu){r=e.menu,r?r.show():(r=n.menu,r.length?r={type:"menu",items:r}:r.type=r.type||"menu",i.settings.itemDefaults&&(r.itemDefaults=i.settings.itemDefaults),r=e.menu=t.create(r).parent(e).renderTo(),r.reflow(),r.fire("show"),r.on("cancel",function(t){t.stopPropagation(),e.focus(),r.hide()}),r.on("hide",function(t){t.control===r&&e.removeClass("selected")}),r.submenu=!0),r._parentMenu=i,r.addClass("menu-sub");var o=r.testMoveRel(e.getEl(),e.isRtl()?["tl-tr","bl-br","tr-tl","br-bl"]:["tr-tl","br-bl","tl-tr","bl-br"]);r.moveRel(e.getEl(),o),r.rel=o,o="menu-sub-"+o,r.removeClass(r._lastRel),r.addClass(o),r._lastRel=o,e.addClass("selected"),e.aria("expanded",!0)}},hideMenu:function(){var e=this;return e.menu&&(e.menu.items().each(function(e){e.hideMenu&&e.hideMenu()}),e.menu.hide(),e.aria("expanded",!1)),e},renderHtml:function(){var e=this,t=e._id,r=e.settings,i=e.classPrefix,o=e.encode(e._text),a=e.settings.icon,s="",l=r.shortcut;return a&&e.parent().addClass("menu-has-icons"),r.image&&(a="none",s=" style=\"background-image: url('"+r.image+"')\""),l&&n.mac&&(l=l.replace(/ctrl\+alt\+/i,"&#x2325;&#x2318;"),l=l.replace(/ctrl\+/i,"&#x2318;"),l=l.replace(/alt\+/i,"&#x2325;"),l=l.replace(/shift\+/i,"&#x21E7;")),a=i+"ico "+i+"i-"+(e.settings.icon||"none"),'<div id="'+t+'" class="'+e.classes()+'" tabindex="-1">'+("-"!==o?'<i class="'+a+'"'+s+"></i>\xa0":"")+("-"!==o?'<span id="'+t+'-text" class="'+i+'text">'+o+"</span>":"")+(l?'<div id="'+t+'-shortcut" class="'+i+'menu-shortcut">'+l+"</div>":"")+(r.menu?'<div class="'+i+'caret"></div>':"")+"</div>"},postRender:function(){var e=this,t=e.settings,n=t.textStyle;if("function"==typeof n&&(n=n.call(this)),n){var r=e.getEl("text");r&&r.setAttribute("style",n)}return e.on("mouseenter click",function(n){n.control===e&&(t.menu||"click"!==n.type?(e.showMenu(),n.aria&&e.menu.focus(!0)):(e.fire("select"),e.parent().hideAll()))}),e._super(),e},active:function(e){return"undefined"!=typeof e&&this.aria("checked",e),this._super(e)},remove:function(){this._super(),this.menu&&this.menu.remove()}})}),r(Kt,[et,jt,p],function(e,t,n){var r=e.extend({Defaults:{defaultType:"menuitem",border:1,layout:"stack",role:"application",bodyRole:"menu",ariaRoot:!0},init:function(e){var t=this;if(e.autohide=!0,e.constrainToViewport=!0,e.itemDefaults)for(var r=e.items,i=r.length;i--;)r[i]=n.extend({},e.itemDefaults,r[i]);t._super(e),t.addClass("menu")},repaint:function(){return this.toggleClass("menu-align",!0),this._super(),this.getEl().style.height="",this.getEl("body").style.height="",this},cancel:function(){var e=this;e.hideAll(),e.fire("select")},hideAll:function(){var e=this;return this.find("menuitem").exec("hideMenu"),e._super()},preRender:function(){var e=this;return e.items().each(function(t){var n=t.settings;return n.icon||n.selectable?(e._hasIcons=!0,!1):void 0}),e._super()}});return r}),r(Gt,[Et],function(e){return e.extend({Defaults:{classes:"radio",role:"radio"}})}),r(Yt,[wt,Y],function(e,t){return e.extend({renderHtml:function(){var e=this,t=e.classPrefix;return e.addClass("resizehandle"),"both"==e.settings.direction&&e.addClass("resizehandle-both"),e.canFocus=!1,'<div id="'+e._id+'" class="'+e.classes()+'"><i class="'+t+"ico "+t+'i-resize"></i></div>'},postRender:function(){var e=this;e._super(),e.resizeDragHelper=new t(this._id,{start:function(){e.fire("ResizeStart")},drag:function(t){"both"!=e.settings.direction&&(t.deltaX=0),e.fire("Resize",t)},stop:function(){e.fire("ResizeEnd")}})},remove:function(){return this.resizeDragHelper&&this.resizeDragHelper.destroy(),this._super()}})}),r(Xt,[wt],function(e){return e.extend({renderHtml:function(){var e=this;return e.addClass("spacer"),e.canFocus=!1,'<div id="'+e._id+'" class="'+e.classes()+'"></div>'}})}),r(Jt,[qt,q],function(e,t){return e.extend({Defaults:{classes:"widget btn splitbtn",role:"button"},repaint:function(){var e=this,n=e.getEl(),r=e.layoutRect(),i,o;return e._super(),i=n.firstChild,o=n.lastChild,t.css(i,{width:r.w-t.getSize(o).width,height:r.h-2}),t.css(o,{height:r.h-2}),e},activeMenu:function(e){var n=this;t.toggleClass(n.getEl().lastChild,n.classPrefix+"active",e)},renderHtml:function(){var e=this,t=e._id,n=e.classPrefix,r=e.settings.icon?n+"ico "+n+"i-"+e.settings.icon:"";return'<div id="'+t+'" class="'+e.classes()+'" role="button" tabindex="-1"><button type="button" hidefocus="1" tabindex="-1">'+(r?'<i class="'+r+'"></i>':"")+(e._text?(r?" ":"")+e._text:"")+'</button><button type="button" class="'+n+'open" hidefocus="1" tabindex="-1">'+(e._menuBtnText?(r?"\xa0":"")+e._menuBtnText:"")+' <i class="'+n+'caret"></i></button></div>'},postRender:function(){var e=this,t=e.settings.onclick;return e.on("click",function(e){var n=e.target;if(e.control==this)for(;n;){if(e.aria&&"down"!=e.aria.key||"BUTTON"==n.nodeName&&-1==n.className.indexOf("open"))return e.stopImmediatePropagation(),void t.call(this,e);n=n.parentNode}}),delete e.settings.onclick,e._super()}})}),r(Qt,[Ot],function(e){return e.extend({Defaults:{containerClass:"stack-layout",controlClass:"stack-layout-item",endClass:"break"}})}),r(Zt,[J,q],function(e,t){return e.extend({lastIdx:0,Defaults:{layout:"absolute",defaults:{type:"panel"}},activateTab:function(e){var n;this.activeTabId&&(n=this.getEl(this.activeTabId),t.removeClass(n,this.classPrefix+"active"),n.setAttribute("aria-selected","false")),this.activeTabId="t"+e,n=this.getEl("t"+e),n.setAttribute("aria-selected","true"),t.addClass(n,this.classPrefix+"active"),e!=this.lastIdx&&(this.items()[this.lastIdx].hide(),this.lastIdx=e),this.items()[e].show().fire("showtab"),this.reflow()},renderHtml:function(){var e=this,t=e._layout,n="",r=e.classPrefix;return e.preRender(),t.preRender(e),e.items().each(function(t,i){var o=e._id+"-t"+i;t.aria("role","tabpanel"),t.aria("labelledby",o),n+='<div id="'+o+'" class="'+r+'tab" unselectable="on" role="tab" aria-controls="'+t._id+'" aria-selected="false" tabIndex="-1">'+e.encode(t.settings.title)+"</div>"}),'<div id="'+e._id+'" class="'+e.classes()+'" hidefocus="1" tabindex="-1"><div id="'+e._id+'-head" class="'+r+'tabs" role="tablist">'+n+'</div><div id="'+e._id+'-body" class="'+e.classes("body")+'">'+t.renderHtml(e)+"</div></div>"},postRender:function(){var e=this;e._super(),e.settings.activeTab=e.settings.activeTab||0,e.activateTab(e.settings.activeTab),this.on("click",function(t){var n=t.target.parentNode;if(t.target.parentNode.id==e._id+"-head")for(var r=n.childNodes.length;r--;)n.childNodes[r]==t.target&&e.activateTab(r)})},initLayoutRect:function(){var e=this,n,r,i;r=t.getSize(e.getEl("head")).width,r=0>r?0:r,i=0,e.items().each(function(t,n){r=Math.max(r,t.layoutRect().minW),i=Math.max(i,t.layoutRect().minH),e.settings.activeTab!=n&&t.hide()}),e.items().each(function(e){e.settings.x=0,e.settings.y=0,e.settings.w=r,e.settings.h=i,e.layoutRect({x:0,y:0,w:r,h:i})});var o=t.getSize(e.getEl("head")).height;return e.settings.minWidth=r,e.settings.minHeight=i+o,n=e._super(),n.deltaH+=o,n.innerH=n.h-n.deltaH,n}})}),r(en,[wt,q],function(e,t){return e.extend({init:function(e){var t=this;t._super(e),t._value=e.value||"",t.addClass("textbox"),e.multiline?t.addClass("multiline"):t.on("keydown",function(e){13==e.keyCode&&t.parents().reverse().each(function(t){return e.preventDefault(),t.hasEventListeners("submit")&&t.toJSON?(t.fire("submit",{data:t.toJSON()}),!1):void 0})})},disabled:function(e){var t=this;return t._rendered&&"undefined"!=typeof e&&(t.getEl().disabled=e),t._super(e)},value:function(e){var t=this;return"undefined"!=typeof e?(t._value=e,t._rendered&&(t.getEl().value=e),t):t._rendered?t.getEl().value:t._value},repaint:function(){var e=this,t,n,r,i=0,o=0,a;t=e.getEl().style,n=e._layoutRect,a=e._lastRepaintRect||{};var s=document;return!e.settings.multiline&&s.all&&(!s.documentMode||s.documentMode<=8)&&(t.lineHeight=n.h-o+"px"),r=e._borderBox,i=r.left+r.right+8,o=r.top+r.bottom+(e.settings.multiline?8:0),n.x!==a.x&&(t.left=n.x+"px",a.x=n.x),n.y!==a.y&&(t.top=n.y+"px",a.y=n.y),n.w!==a.w&&(t.width=n.w-i+"px",a.w=n.w),n.h!==a.h&&(t.height=n.h-o+"px",a.h=n.h),e._lastRepaintRect=a,e.fire("repaint",{},!1),e},renderHtml:function(){var e=this,t=e._id,n=e.settings,r=e.encode(e._value,!1),i="";return"spellcheck"in n&&(i+=' spellcheck="'+n.spellcheck+'"'),n.maxLength&&(i+=' maxlength="'+n.maxLength+'"'),n.size&&(i+=' size="'+n.size+'"'),n.subtype&&(i+=' type="'+n.subtype+'"'),e.disabled()&&(i+=' disabled="disabled"'),n.multiline?'<textarea id="'+t+'" class="'+e.classes()+'" '+(n.rows?' rows="'+n.rows+'"':"")+' hidefocus="1"'+i+">"+r+"</textarea>":'<input id="'+t+'" class="'+e.classes()+'" value="'+r+'" hidefocus="1"'+i+" />"},postRender:function(){var e=this;return t.on(e.getEl(),"change",function(t){e.fire("change",t)}),e._super()},remove:function(){t.off(this.getEl()),this._super()}})}),r(tn,[q,$],function(e,t){return function(n,r){var i=this,o,a=t.classPrefix;i.show=function(t){return i.hide(),o=!0,window.setTimeout(function(){o&&n.appendChild(e.createFragment('<div class="'+a+"throbber"+(r?" "+a+"throbber-inline":"")+'"></div>'))},t||0),i},i.hide=function(){var e=n.lastChild;return e&&-1!=e.className.indexOf("throbber")&&e.parentNode.removeChild(e),o=!1,i}}}),a([l,c,u,d,f,p,h,m,g,v,y,b,C,x,w,_,N,E,S,k,T,R,A,B,D,L,M,H,P,O,I,F,z,W,V,U,q,$,j,K,G,Y,X,J,Q,Z,et,tt,nt,rt,it,ot,at,st,lt,ct,ut,dt,ft,pt,ht,mt,gt,vt,yt,bt,Ct,xt,wt,_t,Nt,Et,St,kt,Tt,Rt,At,Bt,Dt,Lt,Mt,Ht,Pt,Ot,It,Ft,zt,Wt,Vt,Ut,qt,$t,jt,Kt,Gt,Yt,Xt,Jt,Qt,Zt,en,tn])}(this);tinymce.PluginManager.add("advlist",function(t){function e(t,e){var n=[];return tinymce.each(e.split(/[ ,]/),function(t){n.push({text:t.replace(/\-/g," ").replace(/\b\w/g,function(t){return t.toUpperCase()}),data:"default"==t?"":t})}),n}function n(e,n){var o,l=t.dom,a=t.selection;o=l.getParent(a.getNode(),"ol,ul"),o&&o.nodeName==e&&n!==!1||t.execCommand("UL"==e?"InsertUnorderedList":"InsertOrderedList"),n=n===!1?i[e]:n,i[e]=n,o=l.getParent(a.getNode(),"ol,ul"),o&&(l.setStyle(o,"listStyleType",n),o.removeAttribute("data-mce-style")),t.focus()}function o(e){var n=t.dom.getStyle(t.dom.getParent(t.selection.getNode(),"ol,ul"),"listStyleType")||"";e.control.items().each(function(t){t.active(t.settings.data===n)})}var l,a,i={};l=e("OL",t.getParam("advlist_number_styles","default,lower-alpha,lower-greek,lower-roman,upper-alpha,upper-roman")),a=e("UL",t.getParam("advlist_bullet_styles","default,circle,disc,square")),t.addButton("numlist",{type:"splitbutton",tooltip:"Numbered list",menu:l,onshow:o,onselect:function(t){n("OL",t.control.settings.data)},onclick:function(){n("OL",!1)}}),t.addButton("bullist",{type:"splitbutton",tooltip:"Bullet list",menu:a,onshow:o,onselect:function(t){n("UL",t.control.settings.data)},onclick:function(){n("UL",!1)}})});tinymce.PluginManager.add("anchor",function(n){function e(){var e=n.selection.getNode();n.windowManager.open({title:"Anchor",body:{type:"textbox",name:"name",size:40,label:"Name",value:e.name||e.id},onsubmit:function(e){n.execCommand("mceInsertContent",!1,n.dom.createHTML("a",{id:e.data.name}))}})}n.addButton("anchor",{icon:"anchor",tooltip:"Anchor",onclick:e,stateSelector:"a:not([href])"}),n.addMenuItem("anchor",{icon:"anchor",text:"Anchor",context:"insert",onclick:e})});tinymce.PluginManager.add("autolink",function(t){function n(t){o(t,-1,"(",!0)}function e(t){o(t,0,"",!0)}function i(t){o(t,-1,"",!1)}function o(t,n,e){function i(t,n){if(0>n&&(n=0),3==t.nodeType){var e=t.data.length;n>e&&(n=e)}return n}function o(t,n){f.setStart(t,i(t,n))}function r(t,n){f.setEnd(t,i(t,n))}var f,d,a,s,c,l,u,g,h;if(f=t.selection.getRng(!0).cloneRange(),f.startOffset<5){if(g=f.endContainer.previousSibling,!g){if(!f.endContainer.firstChild||!f.endContainer.firstChild.nextSibling)return;g=f.endContainer.firstChild.nextSibling}if(h=g.length,o(g,h),r(g,h),f.endOffset<5)return;d=f.endOffset,s=g}else{if(s=f.endContainer,3!=s.nodeType&&s.firstChild){for(;3!=s.nodeType&&s.firstChild;)s=s.firstChild;3==s.nodeType&&(o(s,0),r(s,s.nodeValue.length))}d=1==f.endOffset?2:f.endOffset-1-n}a=d;do o(s,d>=2?d-2:0),r(s,d>=1?d-1:0),d-=1;while(" "!=f.toString()&&""!==f.toString()&&160!=f.toString().charCodeAt(0)&&d-2>=0&&f.toString()!=e);f.toString()==e||160==f.toString().charCodeAt(0)?(o(s,d),r(s,a),d+=1):0===f.startOffset?(o(s,0),r(s,a)):(o(s,d),r(s,a)),l=f.toString(),"."==l.charAt(l.length-1)&&r(s,a-1),l=f.toString(),u=l.match(/^(https?:\/\/|ssh:\/\/|ftp:\/\/|file:\/|www\.|(?:mailto:)?[A-Z0-9._%+\-]+@)(.+)$/i),u&&("www."==u[1]?u[1]="http://www.":/@$/.test(u[1])&&!/^mailto:/.test(u[1])&&(u[1]="mailto:"+u[1]),c=t.selection.getBookmark(),t.selection.setRng(f),t.execCommand("createlink",!1,u[1]+u[2]),t.selection.moveToBookmark(c),t.nodeChanged())}var r;return t.on("keydown",function(n){return 13==n.keyCode?i(t):void 0}),tinymce.Env.ie?void t.on("focus",function(){if(!r){r=!0;try{t.execCommand("AutoUrlDetect",!1,!0)}catch(n){}}}):(t.on("keypress",function(e){return 41==e.keyCode?n(t):void 0}),void t.on("keyup",function(n){return 32==n.keyCode?e(t):void 0}))});tinymce.PluginManager.add("autoresize",function(e){function t(){return e.plugins.fullscreen&&e.plugins.fullscreen.isFullscreen()}function i(n){var s,r,g,u,l,m,h,d,f=tinymce.DOM;if(r=e.getDoc()){if(g=r.body,u=r.documentElement,l=o.autoresize_min_height,!g||n&&"setcontent"===n.type&&n.initial||t())return void(g&&u&&(g.style.overflowY="auto",u.style.overflowY="auto"));h=e.dom.getStyle(g,"margin-top",!0),d=e.dom.getStyle(g,"margin-bottom",!0),m=g.offsetHeight+parseInt(h,10)+parseInt(d,10),(isNaN(m)||0>=m)&&(m=tinymce.Env.ie?g.scrollHeight:tinymce.Env.webkit&&0===g.clientHeight?0:g.offsetHeight),m>o.autoresize_min_height&&(l=m),o.autoresize_max_height&&m>o.autoresize_max_height?(l=o.autoresize_max_height,g.style.overflowY="auto",u.style.overflowY="auto"):(g.style.overflowY="hidden",u.style.overflowY="hidden",g.scrollTop=0),l!==a&&(s=l-a,f.setStyle(f.get(e.id+"_ifr"),"height",l+"px"),a=l,tinymce.isWebKit&&0>s&&i(n))}}function n(e,t,o){setTimeout(function(){i({}),e--?n(e,t,o):o&&o()},t)}var o=e.settings,a=0;e.settings.inline||(o.autoresize_min_height=parseInt(e.getParam("autoresize_min_height",e.getElement().offsetHeight),10),o.autoresize_max_height=parseInt(e.getParam("autoresize_max_height",0),10),e.on("init",function(){var t=e.getParam("autoresize_overflow_padding",1);e.dom.setStyles(e.getBody(),{paddingBottom:e.getParam("autoresize_bottom_margin",50),paddingLeft:t,paddingRight:t})}),e.on("nodechange setcontent keyup FullscreenStateChanged",i),e.getParam("autoresize_on_init",!0)&&e.on("init",function(){n(20,100,function(){n(5,1e3)})}),e.addCommand("mceAutoResize",i))});tinymce.PluginManager.add("autosave",function(e){function t(e,t){var n={s:1e3,m:6e4};return e=/^(\d+)([ms]?)$/.exec(""+(e||t)),(e[2]?n[e[2]]:1)*parseInt(e,10)}function n(){var e=parseInt(l.getItem(d+"time"),10)||0;return(new Date).getTime()-e>v.autosave_retention?(a(!1),!1):!0}function a(t){l.removeItem(d+"draft"),l.removeItem(d+"time"),t!==!1&&e.fire("RemoveDraft")}function r(){!c()&&e.isDirty()&&(l.setItem(d+"draft",e.getContent({format:"raw",no_events:!0})),l.setItem(d+"time",(new Date).getTime()),e.fire("StoreDraft"))}function o(){n()&&(e.setContent(l.getItem(d+"draft"),{format:"raw"}),e.fire("RestoreDraft"))}function i(){m||(setInterval(function(){e.removed||r()},v.autosave_interval),m=!0)}function s(){var t=this;t.disabled(!n()),e.on("StoreDraft RestoreDraft RemoveDraft",function(){t.disabled(!n())}),i()}function u(){e.undoManager.beforeChange(),o(),a(),e.undoManager.add()}function f(){var e;return tinymce.each(tinymce.editors,function(t){t.plugins.autosave&&t.plugins.autosave.storeDraft(),!e&&t.isDirty()&&t.getParam("autosave_ask_before_unload",!0)&&(e=t.translate("You have unsaved changes are you sure you want to navigate away?"))}),e}function c(t){var n=e.settings.forced_root_block;return t=tinymce.trim("undefined"==typeof t?e.getBody().innerHTML:t),""===t||new RegExp("^<"+n+"[^>]*>(( |&nbsp;|[ ]|<br[^>]*>)+?|)</"+n+">|<br>$","i").test(t)}var d,m,v=e.settings,l=tinymce.util.LocalStorage;d=v.autosave_prefix||"tinymce-autosave-{path}{query}-{id}-",d=d.replace(/\{path\}/g,document.location.pathname),d=d.replace(/\{query\}/g,document.location.search),d=d.replace(/\{id\}/g,e.id),v.autosave_interval=t(v.autosave_interval,"30s"),v.autosave_retention=t(v.autosave_retention,"20m"),e.addButton("restoredraft",{title:"Restore last draft",onclick:u,onPostRender:s}),e.addMenuItem("restoredraft",{text:"Restore last draft",onclick:u,onPostRender:s,context:"file"}),e.settings.autosave_restore_when_empty!==!1&&(e.on("init",function(){n()&&c()&&o()}),e.on("saveContent",function(){a()})),window.onbeforeunload=f,this.hasDraft=n,this.storeDraft=r,this.restoreDraft=o,this.removeDraft=a,this.isEmpty=c});!function(){tinymce.create("tinymce.plugins.BBCodePlugin",{init:function(o){var e=this,t=o.getParam("bbcode_dialect","punbb").toLowerCase();o.on("beforeSetContent",function(o){o.content=e["_"+t+"_bbcode2html"](o.content)}),o.on("postProcess",function(o){o.set&&(o.content=e["_"+t+"_bbcode2html"](o.content)),o.get&&(o.content=e["_"+t+"_html2bbcode"](o.content))})},getInfo:function(){return{longname:"BBCode Plugin",author:"Moxiecode Systems AB",authorurl:"http://www.tinymce.com",infourl:"http://www.tinymce.com/wiki.php/Plugin:bbcode"}},_punbb_html2bbcode:function(o){function e(e,t){o=o.replace(e,t)}return o=tinymce.trim(o),e(/<a.*?href=\"(.*?)\".*?>(.*?)<\/a>/gi,"[url=$1]$2[/url]"),e(/<font.*?color=\"(.*?)\".*?class=\"codeStyle\".*?>(.*?)<\/font>/gi,"[code][color=$1]$2[/color][/code]"),e(/<font.*?color=\"(.*?)\".*?class=\"quoteStyle\".*?>(.*?)<\/font>/gi,"[quote][color=$1]$2[/color][/quote]"),e(/<font.*?class=\"codeStyle\".*?color=\"(.*?)\".*?>(.*?)<\/font>/gi,"[code][color=$1]$2[/color][/code]"),e(/<font.*?class=\"quoteStyle\".*?color=\"(.*?)\".*?>(.*?)<\/font>/gi,"[quote][color=$1]$2[/color][/quote]"),e(/<span style=\"color: ?(.*?);\">(.*?)<\/span>/gi,"[color=$1]$2[/color]"),e(/<font.*?color=\"(.*?)\".*?>(.*?)<\/font>/gi,"[color=$1]$2[/color]"),e(/<span style=\"font-size:(.*?);\">(.*?)<\/span>/gi,"[size=$1]$2[/size]"),e(/<font>(.*?)<\/font>/gi,"$1"),e(/<img.*?src=\"(.*?)\".*?\/>/gi,"[img]$1[/img]"),e(/<span class=\"codeStyle\">(.*?)<\/span>/gi,"[code]$1[/code]"),e(/<span class=\"quoteStyle\">(.*?)<\/span>/gi,"[quote]$1[/quote]"),e(/<strong class=\"codeStyle\">(.*?)<\/strong>/gi,"[code][b]$1[/b][/code]"),e(/<strong class=\"quoteStyle\">(.*?)<\/strong>/gi,"[quote][b]$1[/b][/quote]"),e(/<em class=\"codeStyle\">(.*?)<\/em>/gi,"[code][i]$1[/i][/code]"),e(/<em class=\"quoteStyle\">(.*?)<\/em>/gi,"[quote][i]$1[/i][/quote]"),e(/<u class=\"codeStyle\">(.*?)<\/u>/gi,"[code][u]$1[/u][/code]"),e(/<u class=\"quoteStyle\">(.*?)<\/u>/gi,"[quote][u]$1[/u][/quote]"),e(/<\/(strong|b)>/gi,"[/b]"),e(/<(strong|b)>/gi,"[b]"),e(/<\/(em|i)>/gi,"[/i]"),e(/<(em|i)>/gi,"[i]"),e(/<\/u>/gi,"[/u]"),e(/<span style=\"text-decoration: ?underline;\">(.*?)<\/span>/gi,"[u]$1[/u]"),e(/<u>/gi,"[u]"),e(/<blockquote[^>]*>/gi,"[quote]"),e(/<\/blockquote>/gi,"[/quote]"),e(/<br \/>/gi,"\n"),e(/<br\/>/gi,"\n"),e(/<br>/gi,"\n"),e(/<p>/gi,""),e(/<\/p>/gi,"\n"),e(/&nbsp;|\u00a0/gi," "),e(/&quot;/gi,'"'),e(/&lt;/gi,"<"),e(/&gt;/gi,">"),e(/&amp;/gi,"&"),o},_punbb_bbcode2html:function(o){function e(e,t){o=o.replace(e,t)}return o=tinymce.trim(o),e(/\n/gi,"<br />"),e(/\[b\]/gi,"<strong>"),e(/\[\/b\]/gi,"</strong>"),e(/\[i\]/gi,"<em>"),e(/\[\/i\]/gi,"</em>"),e(/\[u\]/gi,"<u>"),e(/\[\/u\]/gi,"</u>"),e(/\[url=([^\]]+)\](.*?)\[\/url\]/gi,'<a href="$1">$2</a>'),e(/\[url\](.*?)\[\/url\]/gi,'<a href="$1">$1</a>'),e(/\[img\](.*?)\[\/img\]/gi,'<img src="$1" />'),e(/\[color=(.*?)\](.*?)\[\/color\]/gi,'<font color="$1">$2</font>'),e(/\[code\](.*?)\[\/code\]/gi,'<span class="codeStyle">$1</span>&nbsp;'),e(/\[quote.*?\](.*?)\[\/quote\]/gi,'<span class="quoteStyle">$1</span>&nbsp;'),o}}),tinymce.PluginManager.add("bbcode",tinymce.plugins.BBCodePlugin)}();tinymce.PluginManager.add("charmap",function(e){function a(){function a(e){for(;e;){if("TD"==e.nodeName)return e;e=e.parentNode}}var t,r,o,n;t='<table role="presentation" cellspacing="0" class="mce-charmap"><tbody>';var l=25;for(o=0;10>o;o++){for(t+="<tr>",r=0;l>r;r++){var s=i[o*l+r];t+='<td title="'+s[1]+'"><div tabindex="-1" title="'+s[1]+'" role="button">'+(s?String.fromCharCode(parseInt(s[0],10)):"&nbsp;")+"</div></td>"}t+="</tr>"}t+="</tbody></table>";var c={type:"container",html:t,onclick:function(a){var i=a.target;"TD"==i.tagName&&(i=i.firstChild),"DIV"==i.tagName&&(e.execCommand("mceInsertContent",!1,i.firstChild.data),a.ctrlKey||n.close())},onmouseover:function(e){var i=a(e.target);i&&n.find("#preview").text(i.firstChild.firstChild.data)}};n=e.windowManager.open({title:"Special character",spacing:10,padding:10,items:[c,{type:"label",name:"preview",text:" ",style:"font-size: 40px; text-align: center",border:1,minWidth:100,minHeight:80}],buttons:[{text:"Close",onclick:function(){n.close()}}]})}var i=[["160","no-break space"],["38","ampersand"],["34","quotation mark"],["162","cent sign"],["8364","euro sign"],["163","pound sign"],["165","yen sign"],["169","copyright sign"],["174","registered sign"],["8482","trade mark sign"],["8240","per mille sign"],["181","micro sign"],["183","middle dot"],["8226","bullet"],["8230","three dot leader"],["8242","minutes / feet"],["8243","seconds / inches"],["167","section sign"],["182","paragraph sign"],["223","sharp s / ess-zed"],["8249","single left-pointing angle quotation mark"],["8250","single right-pointing angle quotation mark"],["171","left pointing guillemet"],["187","right pointing guillemet"],["8216","left single quotation mark"],["8217","right single quotation mark"],["8220","left double quotation mark"],["8221","right double quotation mark"],["8218","single low-9 quotation mark"],["8222","double low-9 quotation mark"],["60","less-than sign"],["62","greater-than sign"],["8804","less-than or equal to"],["8805","greater-than or equal to"],["8211","en dash"],["8212","em dash"],["175","macron"],["8254","overline"],["164","currency sign"],["166","broken bar"],["168","diaeresis"],["161","inverted exclamation mark"],["191","turned question mark"],["710","circumflex accent"],["732","small tilde"],["176","degree sign"],["8722","minus sign"],["177","plus-minus sign"],["247","division sign"],["8260","fraction slash"],["215","multiplication sign"],["185","superscript one"],["178","superscript two"],["179","superscript three"],["188","fraction one quarter"],["189","fraction one half"],["190","fraction three quarters"],["402","function / florin"],["8747","integral"],["8721","n-ary sumation"],["8734","infinity"],["8730","square root"],["8764","similar to"],["8773","approximately equal to"],["8776","almost equal to"],["8800","not equal to"],["8801","identical to"],["8712","element of"],["8713","not an element of"],["8715","contains as member"],["8719","n-ary product"],["8743","logical and"],["8744","logical or"],["172","not sign"],["8745","intersection"],["8746","union"],["8706","partial differential"],["8704","for all"],["8707","there exists"],["8709","diameter"],["8711","backward difference"],["8727","asterisk operator"],["8733","proportional to"],["8736","angle"],["180","acute accent"],["184","cedilla"],["170","feminine ordinal indicator"],["186","masculine ordinal indicator"],["8224","dagger"],["8225","double dagger"],["192","A - grave"],["193","A - acute"],["194","A - circumflex"],["195","A - tilde"],["196","A - diaeresis"],["197","A - ring above"],["198","ligature AE"],["199","C - cedilla"],["200","E - grave"],["201","E - acute"],["202","E - circumflex"],["203","E - diaeresis"],["204","I - grave"],["205","I - acute"],["206","I - circumflex"],["207","I - diaeresis"],["208","ETH"],["209","N - tilde"],["210","O - grave"],["211","O - acute"],["212","O - circumflex"],["213","O - tilde"],["214","O - diaeresis"],["216","O - slash"],["338","ligature OE"],["352","S - caron"],["217","U - grave"],["218","U - acute"],["219","U - circumflex"],["220","U - diaeresis"],["221","Y - acute"],["376","Y - diaeresis"],["222","THORN"],["224","a - grave"],["225","a - acute"],["226","a - circumflex"],["227","a - tilde"],["228","a - diaeresis"],["229","a - ring above"],["230","ligature ae"],["231","c - cedilla"],["232","e - grave"],["233","e - acute"],["234","e - circumflex"],["235","e - diaeresis"],["236","i - grave"],["237","i - acute"],["238","i - circumflex"],["239","i - diaeresis"],["240","eth"],["241","n - tilde"],["242","o - grave"],["243","o - acute"],["244","o - circumflex"],["245","o - tilde"],["246","o - diaeresis"],["248","o slash"],["339","ligature oe"],["353","s - caron"],["249","u - grave"],["250","u - acute"],["251","u - circumflex"],["252","u - diaeresis"],["253","y - acute"],["254","thorn"],["255","y - diaeresis"],["913","Alpha"],["914","Beta"],["915","Gamma"],["916","Delta"],["917","Epsilon"],["918","Zeta"],["919","Eta"],["920","Theta"],["921","Iota"],["922","Kappa"],["923","Lambda"],["924","Mu"],["925","Nu"],["926","Xi"],["927","Omicron"],["928","Pi"],["929","Rho"],["931","Sigma"],["932","Tau"],["933","Upsilon"],["934","Phi"],["935","Chi"],["936","Psi"],["937","Omega"],["945","alpha"],["946","beta"],["947","gamma"],["948","delta"],["949","epsilon"],["950","zeta"],["951","eta"],["952","theta"],["953","iota"],["954","kappa"],["955","lambda"],["956","mu"],["957","nu"],["958","xi"],["959","omicron"],["960","pi"],["961","rho"],["962","final sigma"],["963","sigma"],["964","tau"],["965","upsilon"],["966","phi"],["967","chi"],["968","psi"],["969","omega"],["8501","alef symbol"],["982","pi symbol"],["8476","real part symbol"],["978","upsilon - hook symbol"],["8472","Weierstrass p"],["8465","imaginary part"],["8592","leftwards arrow"],["8593","upwards arrow"],["8594","rightwards arrow"],["8595","downwards arrow"],["8596","left right arrow"],["8629","carriage return"],["8656","leftwards double arrow"],["8657","upwards double arrow"],["8658","rightwards double arrow"],["8659","downwards double arrow"],["8660","left right double arrow"],["8756","therefore"],["8834","subset of"],["8835","superset of"],["8836","not a subset of"],["8838","subset of or equal to"],["8839","superset of or equal to"],["8853","circled plus"],["8855","circled times"],["8869","perpendicular"],["8901","dot operator"],["8968","left ceiling"],["8969","right ceiling"],["8970","left floor"],["8971","right floor"],["9001","left-pointing angle bracket"],["9002","right-pointing angle bracket"],["9674","lozenge"],["9824","black spade suit"],["9827","black club suit"],["9829","black heart suit"],["9830","black diamond suit"],["8194","en space"],["8195","em space"],["8201","thin space"],["8204","zero width non-joiner"],["8205","zero width joiner"],["8206","left-to-right mark"],["8207","right-to-left mark"],["173","soft hyphen"]];e.addButton("charmap",{icon:"charmap",tooltip:"Special character",onclick:a}),e.addMenuItem("charmap",{icon:"charmap",text:"Special character",onclick:a,context:"insert"})});tinymce.PluginManager.add("code",function(e){function o(){e.windowManager.open({title:"Source code",body:{type:"textbox",name:"code",multiline:!0,minWidth:e.getParam("code_dialog_width",600),minHeight:e.getParam("code_dialog_height",Math.min(tinymce.DOM.getViewPort().h-200,500)),value:e.getContent({source_view:!0}),spellcheck:!1,style:"direction: ltr; text-align: left"},onSubmit:function(o){e.focus(),e.undoManager.transact(function(){e.setContent(o.data.code)}),e.selection.setCursorLocation(),e.nodeChanged()}})}e.addCommand("mceCodeEditor",o),e.addButton("code",{icon:"code",tooltip:"Source code",onclick:o}),e.addMenuItem("code",{icon:"code",text:"Source code",context:"tools",onclick:o})});tinymce.PluginManager.add("contextmenu",function(e){var n,t=e.settings.contextmenu_never_use_native;e.on("contextmenu",function(i){var o;if(!i.ctrlKey||t){if(i.preventDefault(),o=e.settings.contextmenu||"link image inserttable | cell row column deletetable",n)n.show();else{var c=[];tinymce.each(o.split(/[ ,]/),function(n){var t=e.menuItems[n];"|"==n&&(t={text:n}),t&&(t.shortcut="",c.push(t))});for(var a=0;a<c.length;a++)"|"==c[a].text&&(0===a||a==c.length-1)&&c.splice(a,1);n=new tinymce.ui.Menu({items:c,context:"contextmenu"}).addClass("contextmenu").renderTo(),e.on("remove",function(){n.remove(),n=null})}var l={x:i.pageX,y:i.pageY};e.inline||(l=tinymce.DOM.getPos(e.getContentAreaContainer()),l.x+=i.clientX,l.y+=i.clientY),n.moveTo(l.x,l.y)}})});tinymce.PluginManager.add("directionality",function(t){function e(e){var i,n=t.dom,r=t.selection.getSelectedBlocks();r.length&&(i=n.getAttrib(r[0],"dir"),tinymce.each(r,function(t){n.getParent(t.parentNode,"*[dir='"+e+"']",n.getRoot())||(i!=e?n.setAttrib(t,"dir",e):n.setAttrib(t,"dir",null))}),t.nodeChanged())}function i(t){var e=[];return tinymce.each("h1 h2 h3 h4 h5 h6 div p".split(" "),function(i){e.push(i+"[dir="+t+"]")}),e.join(",")}t.addCommand("mceDirectionLTR",function(){e("ltr")}),t.addCommand("mceDirectionRTL",function(){e("rtl")}),t.addButton("ltr",{title:"Left to right",cmd:"mceDirectionLTR",stateSelector:i("ltr")}),t.addButton("rtl",{title:"Right to left",cmd:"mceDirectionRTL",stateSelector:i("rtl")})});tinymce.PluginManager.add("emoticons",function(t,e){function a(){var t;return t='<table role="list" class="mce-grid">',tinymce.each(i,function(a){t+="<tr>",tinymce.each(a,function(a){var i=e+"/img/smiley-"+a+".gif";t+='<td><a href="#" data-mce-url="'+i+'" data-mce-alt="'+a+'" tabindex="-1" role="option" aria-label="'+a+'"><img src="'+i+'" style="width: 18px; height: 18px" role="presentation" /></a></td>'}),t+="</tr>"}),t+="</table>"}var i=[["cool","cry","embarassed","foot-in-mouth"],["frown","innocent","kiss","laughing"],["money-mouth","sealed","smile","surprised"],["tongue-out","undecided","wink","yell"]];t.addButton("emoticons",{type:"panelbutton",panel:{role:"application",autohide:!0,html:a,onclick:function(e){var a=t.dom.getParent(e.target,"a");a&&(t.insertContent('<img src="'+a.getAttribute("data-mce-url")+'" alt="'+a.getAttribute("data-mce-alt")+'" />'),this.hide())}},tooltip:"Emoticons"})});tinymce.PluginManager.add("fullpage",function(e){function t(){var t=n();e.windowManager.open({title:"Document properties",data:t,defaults:{type:"textbox",size:40},body:[{name:"title",label:"Title"},{name:"keywords",label:"Keywords"},{name:"description",label:"Description"},{name:"robots",label:"Robots"},{name:"author",label:"Author"},{name:"docencoding",label:"Encoding"}],onSubmit:function(e){l(tinymce.extend(t,e.data))}})}function n(){function t(e,t){var n=e.attr(t);return n||""}var n,l,a=i(),r={};return r.fontface=e.getParam("fullpage_default_fontface",""),r.fontsize=e.getParam("fullpage_default_fontsize",""),n=a.firstChild,7==n.type&&(r.xml_pi=!0,l=/encoding="([^"]+)"/.exec(n.value),l&&(r.docencoding=l[1])),n=a.getAll("#doctype")[0],n&&(r.doctype="<!DOCTYPE"+n.value+">"),n=a.getAll("title")[0],n&&n.firstChild&&(r.title=n.firstChild.value),s(a.getAll("meta"),function(e){var t,n=e.attr("name"),l=e.attr("http-equiv");n?r[n.toLowerCase()]=e.attr("content"):"Content-Type"==l&&(t=/charset\s*=\s*(.*)\s*/gi.exec(e.attr("content")),t&&(r.docencoding=t[1]))}),n=a.getAll("html")[0],n&&(r.langcode=t(n,"lang")||t(n,"xml:lang")),r.stylesheets=[],tinymce.each(a.getAll("link"),function(e){"stylesheet"==e.attr("rel")&&r.stylesheets.push(e.attr("href"))}),n=a.getAll("body")[0],n&&(r.langdir=t(n,"dir"),r.style=t(n,"style"),r.visited_color=t(n,"vlink"),r.link_color=t(n,"link"),r.active_color=t(n,"alink")),r}function l(t){function n(e,t,n){e.attr(t,n?n:void 0)}function l(e){r.firstChild?r.insert(e,r.firstChild):r.append(e)}var a,r,o,c,u,f=e.dom;a=i(),r=a.getAll("head")[0],r||(c=a.getAll("html")[0],r=new m("head",1),c.firstChild?c.insert(r,c.firstChild,!0):c.append(r)),c=a.firstChild,t.xml_pi?(u='version="1.0"',t.docencoding&&(u+=' encoding="'+t.docencoding+'"'),7!=c.type&&(c=new m("xml",7),a.insert(c,a.firstChild,!0)),c.value=u):c&&7==c.type&&c.remove(),c=a.getAll("#doctype")[0],t.doctype?(c||(c=new m("#doctype",10),t.xml_pi?a.insert(c,a.firstChild):l(c)),c.value=t.doctype.substring(9,t.doctype.length-1)):c&&c.remove(),c=null,s(a.getAll("meta"),function(e){"Content-Type"==e.attr("http-equiv")&&(c=e)}),t.docencoding?(c||(c=new m("meta",1),c.attr("http-equiv","Content-Type"),c.shortEnded=!0,l(c)),c.attr("content","text/html; charset="+t.docencoding)):c.remove(),c=a.getAll("title")[0],t.title?(c?c.empty():(c=new m("title",1),l(c)),c.append(new m("#text",3)).value=t.title):c&&c.remove(),s("keywords,description,author,copyright,robots".split(","),function(e){var n,i,r=a.getAll("meta"),o=t[e];for(n=0;n<r.length;n++)if(i=r[n],i.attr("name")==e)return void(o?i.attr("content",o):i.remove());o&&(c=new m("meta",1),c.attr("name",e),c.attr("content",o),c.shortEnded=!0,l(c))});var g={};tinymce.each(a.getAll("link"),function(e){"stylesheet"==e.attr("rel")&&(g[e.attr("href")]=e)}),tinymce.each(t.stylesheets,function(e){g[e]||(c=new m("link",1),c.attr({rel:"stylesheet",text:"text/css",href:e}),c.shortEnded=!0,l(c)),delete g[e]}),tinymce.each(g,function(e){e.remove()}),c=a.getAll("body")[0],c&&(n(c,"dir",t.langdir),n(c,"style",t.style),n(c,"vlink",t.visited_color),n(c,"link",t.link_color),n(c,"alink",t.active_color),f.setAttribs(e.getBody(),{style:t.style,dir:t.dir,vLink:t.visited_color,link:t.link_color,aLink:t.active_color})),c=a.getAll("html")[0],c&&(n(c,"lang",t.langcode),n(c,"xml:lang",t.langcode)),r.firstChild||r.remove(),o=new tinymce.html.Serializer({validate:!1,indent:!0,apply_source_formatting:!0,indent_before:"head,html,body,meta,title,script,link,style",indent_after:"head,html,body,meta,title,script,link,style"}).serialize(a),d=o.substring(0,o.indexOf("</body>"))}function i(){return new tinymce.html.DomParser({validate:!1,root_name:"#document"}).parse(d)}function a(t){function n(e){return e.replace(/<\/?[A-Z]+/g,function(e){return e.toLowerCase()})}var l,a,o,m,u=t.content,f="",g=e.dom;if(!t.selection&&!("raw"==t.format&&d||t.source_view&&e.getParam("fullpage_hide_in_source_view"))){u=u.replace(/<(\/?)BODY/gi,"<$1body"),l=u.indexOf("<body"),-1!=l?(l=u.indexOf(">",l),d=n(u.substring(0,l+1)),a=u.indexOf("</body",l),-1==a&&(a=u.length),t.content=u.substring(l+1,a),c=n(u.substring(a))):(d=r(),c="\n</body>\n</html>"),o=i(),s(o.getAll("style"),function(e){e.firstChild&&(f+=e.firstChild.value)}),m=o.getAll("body")[0],m&&g.setAttribs(e.getBody(),{style:m.attr("style")||"",dir:m.attr("dir")||"",vLink:m.attr("vlink")||"",link:m.attr("link")||"",aLink:m.attr("alink")||""}),g.remove("fullpage_styles");var y=e.getDoc().getElementsByTagName("head")[0];f&&(g.add(y,"style",{id:"fullpage_styles"},f),m=g.get("fullpage_styles"),m.styleSheet&&(m.styleSheet.cssText=f));var h={};tinymce.each(y.getElementsByTagName("link"),function(e){"stylesheet"==e.rel&&e.getAttribute("data-mce-fullpage")&&(h[e.href]=e)}),tinymce.each(o.getAll("link"),function(e){var t=e.attr("href");h[t]||"stylesheet"!=e.attr("rel")||g.add(y,"link",{rel:"stylesheet",text:"text/css",href:t,"data-mce-fullpage":"1"}),delete h[t]}),tinymce.each(h,function(e){e.parentNode.removeChild(e)})}}function r(){var t,n="",l="";return e.getParam("fullpage_default_xml_pi")&&(n+='<?xml version="1.0" encoding="'+e.getParam("fullpage_default_encoding","ISO-8859-1")+'" ?>\n'),n+=e.getParam("fullpage_default_doctype","<!DOCTYPE html>"),n+="\n<html>\n<head>\n",(t=e.getParam("fullpage_default_title"))&&(n+="<title>"+t+"</title>\n"),(t=e.getParam("fullpage_default_encoding"))&&(n+='<meta http-equiv="Content-Type" content="text/html; charset='+t+'" />\n'),(t=e.getParam("fullpage_default_font_family"))&&(l+="font-family: "+t+";"),(t=e.getParam("fullpage_default_font_size"))&&(l+="font-size: "+t+";"),(t=e.getParam("fullpage_default_text_color"))&&(l+="color: "+t+";"),n+="</head>\n<body"+(l?' style="'+l+'"':"")+">\n"}function o(t){t.selection||t.source_view&&e.getParam("fullpage_hide_in_source_view")||(t.content=tinymce.trim(d)+"\n"+tinymce.trim(t.content)+"\n"+tinymce.trim(c))}var d,c,s=tinymce.each,m=tinymce.html.Node;e.addCommand("mceFullPageProperties",t),e.addButton("fullpage",{title:"Document properties",cmd:"mceFullPageProperties"}),e.addMenuItem("fullpage",{text:"Document properties",cmd:"mceFullPageProperties",context:"file"}),e.on("BeforeSetContent",a),e.on("GetContent",o)});tinymce.PluginManager.add("fullscreen",function(e){function t(){var e,t,n=window,i=document,l=i.body;return l.offsetWidth&&(e=l.offsetWidth,t=l.offsetHeight),n.innerWidth&&n.innerHeight&&(e=n.innerWidth,t=n.innerHeight),{w:e,h:t}}function n(){function n(){d.setStyle(a,"height",t().h-(h.clientHeight-a.clientHeight))}var u,h,a,f,m=document.body,g=document.documentElement;s=!s,h=e.getContainer(),u=h.style,a=e.getContentAreaContainer().firstChild,f=a.style,s?(i=f.width,l=f.height,f.width=f.height="100%",c=u.width,o=u.height,u.width=u.height="",d.addClass(m,"mce-fullscreen"),d.addClass(g,"mce-fullscreen"),d.addClass(h,"mce-fullscreen"),d.bind(window,"resize",n),n(),r=n):(f.width=i,f.height=l,c&&(u.width=c),o&&(u.height=o),d.removeClass(m,"mce-fullscreen"),d.removeClass(g,"mce-fullscreen"),d.removeClass(h,"mce-fullscreen"),d.unbind(window,"resize",r)),e.fire("FullscreenStateChanged",{state:s})}var i,l,r,c,o,s=!1,d=tinymce.DOM;return e.settings.inline?void 0:(e.on("init",function(){e.addShortcut("Ctrl+Alt+F","",n)}),e.on("remove",function(){r&&d.unbind(window,"resize",r)}),e.addCommand("mceFullScreen",n),e.addMenuItem("fullscreen",{text:"Fullscreen",shortcut:"Ctrl+Alt+F",selectable:!0,onClick:n,onPostRender:function(){var t=this;e.on("FullscreenStateChanged",function(e){t.active(e.state)})},context:"view"}),e.addButton("fullscreen",{tooltip:"Fullscreen",shortcut:"Ctrl+Alt+F",onClick:n,onPostRender:function(){var t=this;e.on("FullscreenStateChanged",function(e){t.active(e.state)})}}),{isFullscreen:function(){return s}})});tinymce.PluginManager.add("hr",function(n){n.addCommand("InsertHorizontalRule",function(){n.execCommand("mceInsertContent",!1,"<hr />")}),n.addButton("hr",{icon:"hr",tooltip:"Horizontal line",cmd:"InsertHorizontalRule"}),n.addMenuItem("hr",{icon:"hr",text:"Horizontal line",cmd:"InsertHorizontalRule",context:"insert"})});tinymce.PluginManager.add("image",function(e){function t(e,t){function i(e,i){n.parentNode&&n.parentNode.removeChild(n),t({width:e,height:i})}var n=document.createElement("img");n.onload=function(){i(n.clientWidth,n.clientHeight)},n.onerror=function(){i()};var a=n.style;a.visibility="hidden",a.position="fixed",a.bottom=a.left=0,a.width=a.height="auto",document.body.appendChild(n),n.src=e}function i(t){return tinymce.each(t,function(t){t.textStyle=function(){return e.formatter.getCssText({inline:"img",classes:[t.value]})}}),t}function n(t){return function(){var i=e.settings.image_list;"string"==typeof i?tinymce.util.XHR.send({url:i,success:function(e){t(tinymce.util.JSON.parse(e))}}):"function"==typeof i?i(t):t(i)}}function a(n){function a(t,i,n){var a,l=[];return tinymce.each(e.settings[t]||n,function(e){var t={text:e.text||e.title,value:e.value};l.push(t),(f[i]===e.value||!a&&e.selected)&&(a=t)}),a&&!f[i]&&(f[i]=a.value,a.selected=!0),l}function l(){var t=[{text:"None",value:""}];return tinymce.each(n,function(i){t.push({text:i.text||i.title,value:e.convertURL(i.value||i.url,"src"),menu:i.menu})}),t}function o(){var e,t,i,n;e=u.find("#width")[0],t=u.find("#height")[0],i=e.value(),n=t.value(),u.find("#constrain")[0].checked()&&g&&h&&i&&n&&(g!=i?(n=Math.round(i/g*n),t.value(n)):(i=Math.round(n/h*i),e.value(i))),g=i,h=n}function s(){function t(t){function i(){t.onload=t.onerror=null,e.selection.select(t),e.nodeChanged()}t.onload=function(){f.width||f.height||y.setAttribs(t,{width:t.clientWidth,height:t.clientHeight}),i()},t.onerror=i}d(),o(),f=tinymce.extend(f,u.toJSON()),f.alt||(f.alt=""),""===f.width&&(f.width=null),""===f.height&&(f.height=null),f.style||(f.style=null),f={src:f.src,alt:f.alt,width:f.width,height:f.height,style:f.style,"class":f["class"]},f["class"]||delete f["class"],e.undoManager.transact(function(){return f.src?(v?y.setAttribs(v,f):(f.id="__mcenew",e.focus(),e.selection.setContent(y.createHTML("img",f)),v=y.get("__mcenew"),y.setAttrib(v,"id",null)),void t(v)):void(v&&(y.remove(v),e.focus(),e.nodeChanged()))})}function r(e){return e&&(e=e.replace(/px$/,"")),e}function c(){m&&m.value(e.convertURL(this.value(),"src")),t(this.value(),function(e){e.width&&e.height&&(g=e.width,h=e.height,u.find("#width").value(g),u.find("#height").value(h))})}function d(){function t(e){return e.length>0&&/^[0-9]+$/.test(e)&&(e+="px"),e}if(e.settings.image_advtab){var i=u.toJSON(),n=y.parseStyle(i.style);delete n.margin,n["margin-top"]=n["margin-bottom"]=t(i.vspace),n["margin-left"]=n["margin-right"]=t(i.hspace),n["border-width"]=t(i.border),u.find("#style").value(y.serializeStyle(y.parseStyle(y.serializeStyle(n))))}}var u,g,h,m,p,f={},y=e.dom,v=e.selection.getNode();g=y.getAttrib(v,"width"),h=y.getAttrib(v,"height"),"IMG"!=v.nodeName||v.getAttribute("data-mce-object")||v.getAttribute("data-mce-placeholder")?v=null:f={src:y.getAttrib(v,"src"),alt:y.getAttrib(v,"alt"),"class":y.getAttrib(v,"class"),width:g,height:h},n&&(m={type:"listbox",label:"Image list",values:l(),value:f.src&&e.convertURL(f.src,"src"),onselect:function(e){var t=u.find("#alt");(!t.value()||e.lastControl&&t.value()==e.lastControl.text())&&t.value(e.control.text()),u.find("#src").value(e.control.value())},onPostRender:function(){m=this}}),e.settings.image_class_list&&(p={name:"class",type:"listbox",label:"Class",values:i(a("image_class_list","class"))});var b=[{name:"src",type:"filepicker",filetype:"image",label:"Source",autofocus:!0,onchange:c},m];e.settings.image_description!==!1&&b.push({name:"alt",type:"textbox",label:"Image description"}),e.settings.image_dimensions!==!1&&b.push({type:"container",label:"Dimensions",layout:"flex",direction:"row",align:"center",spacing:5,items:[{name:"width",type:"textbox",maxLength:5,size:3,onchange:o,ariaLabel:"Width"},{type:"label",text:"x"},{name:"height",type:"textbox",maxLength:5,size:3,onchange:o,ariaLabel:"Height"},{name:"constrain",type:"checkbox",checked:!0,text:"Constrain proportions"}]}),b.push(p),e.settings.image_advtab?(v&&(f.hspace=r(v.style.marginLeft||v.style.marginRight),f.vspace=r(v.style.marginTop||v.style.marginBottom),f.border=r(v.style.borderWidth),f.style=e.dom.serializeStyle(e.dom.parseStyle(e.dom.getAttrib(v,"style")))),u=e.windowManager.open({title:"Insert/edit image",data:f,bodyType:"tabpanel",body:[{title:"General",type:"form",items:b},{title:"Advanced",type:"form",pack:"start",items:[{label:"Style",name:"style",type:"textbox"},{type:"form",layout:"grid",packV:"start",columns:2,padding:0,alignH:["left","right"],defaults:{type:"textbox",maxWidth:50,onchange:d},items:[{label:"Vertical space",name:"vspace"},{label:"Horizontal space",name:"hspace"},{label:"Border",name:"border"}]}]}],onSubmit:s})):u=e.windowManager.open({title:"Insert/edit image",data:f,body:b,onSubmit:s})}e.addButton("image",{icon:"image",tooltip:"Insert/edit image",onclick:n(a),stateSelector:"img:not([data-mce-object],[data-mce-placeholder])"}),e.addMenuItem("image",{icon:"image",text:"Insert image",onclick:n(a),context:"insert",prependToContext:!0})});tinymce.PluginManager.add("importcss",function(t){function e(t){return"string"==typeof t?function(e){return-1!==e.indexOf(t)}:t instanceof RegExp?function(e){return t.test(e)}:t}function n(e,n){function i(t,e){var c,o=t.href;if(o&&n(o,e)){s(t.imports,function(t){i(t,!0)});try{c=t.cssRules||t.rules}catch(a){}s(c,function(t){t.styleSheet?i(t.styleSheet,!0):t.selectorText&&s(t.selectorText.split(","),function(t){r.push(tinymce.trim(t))})})}}var r=[],c={};s(t.contentCSS,function(t){c[t]=!0}),n||(n=function(t,e){return e||c[t]});try{s(e.styleSheets,function(t){i(t)})}catch(o){}return r}function i(e){var n,i=/^(?:([a-z0-9\-_]+))?(\.[a-z0-9_\-\.]+)$/i.exec(e);if(i){var r=i[1],s=i[2].substr(1).split(".").join(" "),c=tinymce.makeMap("a,img");return i[1]?(n={title:e},t.schema.getTextBlockElements()[r]?n.block=r:t.schema.getBlockElements()[r]||c[r.toLowerCase()]?n.selector=r:n.inline=r):i[2]&&(n={inline:"span",title:e.substr(1),classes:s}),t.settings.importcss_merge_classes!==!1?n.classes=s:n.attributes={"class":s},n}}var r=this,s=tinymce.each;t.on("renderFormatsMenu",function(c){var o=t.settings,a={},l=o.importcss_selector_converter||i,f=e(o.importcss_selector_filter),m=c.control;t.settings.importcss_append||m.items().remove();var u=[];tinymce.each(o.importcss_groups,function(t){t=tinymce.extend({},t),t.filter=e(t.filter),u.push(t)}),s(n(c.doc||t.getDoc(),e(o.importcss_file_filter)),function(e){if(-1===e.indexOf(".mce-")&&!a[e]&&(!f||f(e))){var n,i=l.call(r,e);if(i){var s=i.name||tinymce.DOM.uniqueId();if(u)for(var c=0;c<u.length;c++)if(!u[c].filter||u[c].filter(e)){u[c].item||(u[c].item={text:u[c].title,menu:[]}),n=u[c].item.menu;break}t.formatter.register(s,i);var o=tinymce.extend({},m.settings.itemDefaults,{text:i.title,format:s});n?n.push(o):m.add(o)}a[e]=!0}}),s(u,function(t){m.add(t.item)}),c.control.renderNew()}),r.convertSelectorToFormat=i});tinymce.PluginManager.add("insertdatetime",function(e){function t(t,a){function n(e,t){if(e=""+e,e.length<t)for(var a=0;a<t-e.length;a++)e="0"+e;return e}return a=a||new Date,t=t.replace("%D","%m/%d/%Y"),t=t.replace("%r","%I:%M:%S %p"),t=t.replace("%Y",""+a.getFullYear()),t=t.replace("%y",""+a.getYear()),t=t.replace("%m",n(a.getMonth()+1,2)),t=t.replace("%d",n(a.getDate(),2)),t=t.replace("%H",""+n(a.getHours(),2)),t=t.replace("%M",""+n(a.getMinutes(),2)),t=t.replace("%S",""+n(a.getSeconds(),2)),t=t.replace("%I",""+((a.getHours()+11)%12+1)),t=t.replace("%p",""+(a.getHours()<12?"AM":"PM")),t=t.replace("%B",""+e.translate(m[a.getMonth()])),t=t.replace("%b",""+e.translate(c[a.getMonth()])),t=t.replace("%A",""+e.translate(d[a.getDay()])),t=t.replace("%a",""+e.translate(i[a.getDay()])),t=t.replace("%%","%")}function a(a){var n=t(a);if(e.settings.insertdatetime_element){var r;r=t(/%[HMSIp]/.test(a)?"%Y-%m-%dT%H:%M":"%Y-%m-%d"),n='<time datetime="'+r+'">'+n+"</time>";var i=e.dom.getParent(e.selection.getStart(),"time");if(i)return void e.dom.setOuterHTML(i,n)}e.insertContent(n)}var n,r,i="Sun Mon Tue Wed Thu Fri Sat Sun".split(" "),d="Sunday Monday Tuesday Wednesday Thursday Friday Saturday Sunday".split(" "),c="Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),m="January February March April May June July August September October November December".split(" "),u=[];e.addCommand("mceInsertDate",function(){a(e.getParam("insertdatetime_dateformat",e.translate("%Y-%m-%d")))}),e.addCommand("mceInsertTime",function(){a(e.getParam("insertdatetime_timeformat",e.translate("%H:%M:%S")))}),e.addButton("insertdatetime",{type:"splitbutton",title:"Insert date/time",onclick:function(){a(n||r)},menu:u}),tinymce.each(e.settings.insertdatetime_formats||["%H:%M:%S","%Y-%m-%d","%I:%M:%S %p","%D"],function(e){r||(r=e),u.push({text:t(e),onclick:function(){n=e,a(e)}})}),e.addMenuItem("insertdatetime",{icon:"date",text:"Insert date/time",menu:u,context:"insert"})});!function(e){e.on("AddEditor",function(e){e.editor.settings.inline_styles=!1}),e.PluginManager.add("legacyoutput",function(t){t.on("init",function(){var i="p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img",n=e.explode(t.settings.font_size_style_values),l=t.schema;t.formatter.register({alignleft:{selector:i,attributes:{align:"left"}},aligncenter:{selector:i,attributes:{align:"center"}},alignright:{selector:i,attributes:{align:"right"}},alignjustify:{selector:i,attributes:{align:"justify"}},bold:[{inline:"b",remove:"all"},{inline:"strong",remove:"all"},{inline:"span",styles:{fontWeight:"bold"}}],italic:[{inline:"i",remove:"all"},{inline:"em",remove:"all"},{inline:"span",styles:{fontStyle:"italic"}}],underline:[{inline:"u",remove:"all"},{inline:"span",styles:{textDecoration:"underline"},exact:!0}],strikethrough:[{inline:"strike",remove:"all"},{inline:"span",styles:{textDecoration:"line-through"},exact:!0}],fontname:{inline:"font",attributes:{face:"%value"}},fontsize:{inline:"font",attributes:{size:function(t){return e.inArray(n,t.value)+1}}},forecolor:{inline:"font",attributes:{color:"%value"}},hilitecolor:{inline:"font",styles:{backgroundColor:"%value"}}}),e.each("b,i,u,strike".split(","),function(e){l.addValidElements(e+"[*]")}),l.getElementRule("font")||l.addValidElements("font[face|size|color|style]"),e.each(i.split(","),function(e){var t=l.getElementRule(e);t&&(t.attributes.align||(t.attributes.align={},t.attributesOrder.push("align")))})})})}(tinymce);tinymce.PluginManager.add("link",function(t){function e(e){return function(){var n=t.settings.link_list;"string"==typeof n?tinymce.util.XHR.send({url:n,success:function(t){e(tinymce.util.JSON.parse(t))}}):"function"==typeof n?n(e):e(n)}}function n(e){function n(t){var e=d.find("#text");(!e.value()||t.lastControl&&e.value()==t.lastControl.text())&&e.value(t.control.text()),d.find("#href").value(t.control.value())}function l(){var n=[{text:"None",value:""}];return tinymce.each(e,function(e){n.push({text:e.text||e.title,value:t.convertURL(e.value||e.url,"href"),menu:e.menu})}),n}function i(e){return tinymce.each(e,function(e){e.textStyle=function(){return t.formatter.getCssText({inline:"a",classes:[e.value]})}}),e}function a(e,n,l){var i,a=[];return tinymce.each(t.settings[e]||l,function(t){var e={text:t.text||t.title,value:t.value};a.push(e),(b[n]===t.value||!i&&t.selected)&&(i=e)}),i&&!b[n]&&(b[n]=i.value,i.selected=!0),a}function r(e){var l=[];return tinymce.each(t.dom.select("a:not([href])"),function(t){var n=t.name||t.id;n&&l.push({text:n,value:"#"+n,selected:-1!=e.indexOf("#"+n)})}),l.length?(l.unshift({text:"None",value:""}),{name:"anchor",type:"listbox",label:"Anchors",values:l,onselect:n}):void 0}function o(){h&&h.value(t.convertURL(this.value(),"href")),!f&&0===b.text.length&&x&&this.parent().parent().find("#text")[0].value(this.value())}function s(t){var e=k.getContent();if(/</.test(e)&&(!/^<a [^>]+>[^<]+<\/a>$/.test(e)||-1==e.indexOf("href=")))return!1;if(t){var n,l=t.childNodes;if(0===l.length)return!1;for(n=l.length-1;n>=0;n--)if(3!=l[n].nodeType)return!1}return!0}var u,c,f,d,x,v,h,g,m,p,y,b={},k=t.selection,w=t.dom;u=k.getNode(),c=w.getParent(u,"a[href]"),x=s(),b.text=f=c?c.innerText||c.textContent:k.getContent({format:"text"}),b.href=c?w.getAttrib(c,"href"):"",b.target=c?w.getAttrib(c,"target"):t.settings.default_link_target||null,b.rel=c?w.getAttrib(c,"rel"):null,b["class"]=c?w.getAttrib(c,"class"):null,b.title=c?w.getAttrib(c,"title"):"",x&&(v={name:"text",type:"textbox",size:40,label:"Text to display",onchange:function(){b.text=this.value()}}),e&&(h={type:"listbox",label:"Link list",values:l(),onselect:n,value:t.convertURL(b.href,"href"),onPostRender:function(){h=this}}),t.settings.target_list!==!1&&(m={name:"target",type:"listbox",label:"Target",values:a("target_list","target",[{text:"None",value:""},{text:"New window",value:"_blank"}])}),t.settings.rel_list&&(g={name:"rel",type:"listbox",label:"Rel",values:a("rel_list","rel",[{text:"None",value:""}])}),t.settings.link_class_list&&(p={name:"class",type:"listbox",label:"Class",values:i(a("link_class_list","class"))}),t.settings.link_title!==!1&&(y={name:"title",type:"textbox",label:"Title",value:b.title}),d=t.windowManager.open({title:"Insert link",data:b,body:[{name:"href",type:"filepicker",filetype:"file",size:40,autofocus:!0,label:"Url",onchange:o,onkeyup:o},v,y,r(b.href),h,g,m,p],onSubmit:function(e){function n(e,n){var l=t.selection.getRng();window.setTimeout(function(){t.windowManager.confirm(e,function(e){t.selection.setRng(l),n(e)})},0)}function l(){var e={href:i,target:b.target?b.target:null,rel:b.rel?b.rel:null,"class":b["class"]?b["class"]:null,title:b.title?b.title:null};c?(t.focus(),x&&b.text!=f&&("innerText"in c?c.innerText=b.text:c.textContent=b.text),w.setAttribs(c,e),k.select(c),t.undoManager.add()):x?t.insertContent(w.createHTML("a",e,w.encode(b.text))):t.execCommand("mceInsertLink",!1,e)}var i;return b=tinymce.extend(b,e.data),(i=b.href)?i.indexOf("@")>0&&-1==i.indexOf("//")&&-1==i.indexOf("mailto:")?void n("The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?",function(t){t&&(i="mailto:"+i),l()}):/^\s*www\./i.test(i)?void n("The URL you entered seems to be an external link. Do you want to add the required http:// prefix?",function(t){t&&(i="http://"+i),l()}):void l():void t.execCommand("unlink")}})}t.addButton("link",{icon:"link",tooltip:"Insert/edit link",shortcut:"Ctrl+K",onclick:e(n),stateSelector:"a[href]"}),t.addButton("unlink",{icon:"unlink",tooltip:"Remove link",cmd:"unlink",stateSelector:"a[href]"}),t.addShortcut("Ctrl+K","",e(n)),this.showDialog=n,t.addMenuItem("link",{icon:"link",text:"Insert link",shortcut:"Ctrl+K",onclick:e(n),stateSelector:"a[href]",context:"insert",prependToContext:!0})});tinymce.PluginManager.add("lists",function(e){function t(e){return e&&/^(OL|UL)$/.test(e.nodeName)}function n(e){return e.parentNode.firstChild==e}function r(e){return e.parentNode.lastChild==e}function o(t){return t&&!!e.schema.getTextBlockElements()[t.nodeName]}function i(e){return e&&"SPAN"===e.nodeName&&"bookmark"===e.getAttribute("data-mce-type")}var a=this;e.on("init",function(){function d(e){function t(t){var r,o,i;o=e[t?"startContainer":"endContainer"],i=e[t?"startOffset":"endOffset"],1==o.nodeType&&(r=b.create("span",{"data-mce-type":"bookmark"}),o.hasChildNodes()?(i=Math.min(i,o.childNodes.length-1),t?o.insertBefore(r,o.childNodes[i]):b.insertAfter(r,o.childNodes[i])):o.appendChild(r),o=r,i=0),n[t?"startContainer":"endContainer"]=o,n[t?"startOffset":"endOffset"]=i}var n={};return t(!0),e.collapsed||t(),n}function s(e){function t(t){function n(e){for(var t=e.parentNode.firstChild,n=0;t;){if(t==e)return n;(1!=t.nodeType||"bookmark"!=t.getAttribute("data-mce-type"))&&n++,t=t.nextSibling}return-1}var r,o,i;r=i=e[t?"startContainer":"endContainer"],o=e[t?"startOffset":"endOffset"],r&&(1==r.nodeType&&(o=n(r),r=r.parentNode,b.remove(i)),e[t?"startContainer":"endContainer"]=r,e[t?"startOffset":"endOffset"]=o)}t(!0),t();var n=b.createRng();n.setStart(e.startContainer,e.startOffset),e.endContainer&&n.setEnd(e.endContainer,e.endOffset),k.setRng(n)}function f(t,n){var r,o,i,a=b.createFragment(),d=e.schema.getBlockElements();if(e.settings.forced_root_block&&(n=n||e.settings.forced_root_block),n&&(o=b.create(n),o.tagName===e.settings.forced_root_block&&b.setAttribs(o,e.settings.forced_root_block_attrs),a.appendChild(o)),t)for(;r=t.firstChild;){var s=r.nodeName;i||"SPAN"==s&&"bookmark"==r.getAttribute("data-mce-type")||(i=!0),d[s]?(a.appendChild(r),o=null):n?(o||(o=b.create(n),a.appendChild(o)),o.appendChild(r)):a.appendChild(r)}return e.settings.forced_root_block?i||tinymce.Env.ie&&!(tinymce.Env.ie>10)||o.appendChild(b.create("br",{"data-mce-bogus":"1"})):a.appendChild(b.create("br")),a}function l(){return tinymce.grep(k.getSelectedBlocks(),function(e){return"LI"==e.nodeName})}function c(e,t,n){var r,o,i=b.select('span[data-mce-type="bookmark"]',e);n=n||f(t),r=b.createRng(),r.setStartAfter(t),r.setEndAfter(e),o=r.extractContents(),b.isEmpty(o)||b.insertAfter(o,e),b.insertAfter(n,e),b.isEmpty(t.parentNode)&&(tinymce.each(i,function(e){t.parentNode.parentNode.insertBefore(e,t.parentNode)}),b.remove(t.parentNode)),b.remove(t)}function p(e){var n,r;if(n=e.nextSibling,n&&t(n)&&n.nodeName==e.nodeName){for(;r=n.firstChild;)e.appendChild(r);b.remove(n)}if(n=e.previousSibling,n&&t(n)&&n.nodeName==e.nodeName){for(;r=n.firstChild;)e.insertBefore(r,e.firstChild);b.remove(n)}}function u(e){tinymce.each(tinymce.grep(b.select("ol,ul",e)),function(e){var n,r=e.parentNode;"LI"==r.nodeName&&r.firstChild==e&&(n=r.previousSibling,n&&"LI"==n.nodeName&&(n.appendChild(e),b.isEmpty(r)&&b.remove(r))),t(r)&&(n=r.previousSibling,n&&"LI"==n.nodeName&&n.appendChild(e))})}function m(e){function o(e){b.isEmpty(e)&&b.remove(e)}var i,a=e.parentNode,d=a.parentNode;return n(e)&&r(e)?("LI"==d.nodeName?(b.insertAfter(e,d),o(d),b.remove(a)):t(d)?b.remove(a,!0):(d.insertBefore(f(e),a),b.remove(a)),!0):n(e)?("LI"==d.nodeName?(b.insertAfter(e,d),e.appendChild(a),o(d)):t(d)?d.insertBefore(e,a):(d.insertBefore(f(e),a),b.remove(e)),!0):r(e)?("LI"==d.nodeName?b.insertAfter(e,d):t(d)?b.insertAfter(e,a):(b.insertAfter(f(e),a),b.remove(e)),!0):("LI"==d.nodeName?(a=d,i=f(e,"LI")):i=t(d)?f(e,"LI"):f(e),c(a,e,i),u(a.parentNode),!0)}function h(e){function n(n,r){var o;if(t(n)){for(;o=e.lastChild.firstChild;)r.appendChild(o);b.remove(n)}}var r,o;return r=e.previousSibling,r&&t(r)?(r.appendChild(e),!0):r&&"LI"==r.nodeName&&t(r.lastChild)?(r.lastChild.appendChild(e),n(e.lastChild,r.lastChild),!0):(r=e.nextSibling,r&&t(r)?(r.insertBefore(e,r.firstChild),!0):r&&"LI"==r.nodeName&&t(e.lastChild)?!1:(r=e.previousSibling,r&&"LI"==r.nodeName?(o=b.create(e.parentNode.nodeName),r.appendChild(o),o.appendChild(e),n(e.lastChild,o),!0):!1))}function v(){var t=l();if(t.length){for(var n=d(k.getRng(!0)),r=0;r<t.length&&(h(t[r])||0!==r);r++);return s(n),e.nodeChanged(),!0}}function C(){var t=l();if(t.length){var n,r,o=d(k.getRng(!0)),i=e.getBody();for(n=t.length;n--;)for(var a=t[n].parentNode;a&&a!=i;){for(r=t.length;r--;)if(t[r]===a){t.splice(n,1);break}a=a.parentNode}for(n=0;n<t.length&&(m(t[n])||0!==n);n++);return s(o),e.nodeChanged(),!0}}function g(n){function r(){function t(e){var t,n;for(t=a[e?"startContainer":"endContainer"],n=a[e?"startOffset":"endOffset"],1==t.nodeType&&(t=t.childNodes[Math.min(n,t.childNodes.length-1)]||t);t.parentNode!=d;){if(o(t))return t;if(/^(TD|TH)$/.test(t.parentNode.nodeName))return t;t=t.parentNode}return t}for(var n,r=[],d=e.getBody(),s=t(!0),f=t(),l=[],c=s;c&&(l.push(c),c!=f);c=c.nextSibling);return tinymce.each(l,function(e){if(o(e))return r.push(e),void(n=null);if(b.isBlock(e)||"BR"==e.nodeName)return"BR"==e.nodeName&&b.remove(e),void(n=null);var t=e.nextSibling;return i(e)&&(o(t)||!t&&e.parentNode==d)?void(n=null):(n||(n=b.create("p"),e.parentNode.insertBefore(n,e),r.push(n)),void n.appendChild(e))}),r}var a=k.getRng(!0),f=d(a),l=r();tinymce.each(l,function(e){var r,o;o=e.previousSibling,o&&t(o)&&o.nodeName==n?(r=o,e=b.rename(e,"LI"),o.appendChild(e)):(r=b.create(n),e.parentNode.insertBefore(r,e),r.appendChild(e),e=b.rename(e,"LI")),p(r)}),s(f)}function N(){var n=d(k.getRng(!0)),r=e.getBody();tinymce.each(l(),function(e){var n,o;if(b.isEmpty(e))return void m(e);for(n=e;n&&n!=r;n=n.parentNode)t(n)&&(o=n);c(o,e)}),s(n)}function y(e){var t=b.getParent(k.getStart(),"OL,UL");if(t)if(t.nodeName==e)N(e);else{var n=d(k.getRng(!0));p(b.rename(t,e)),s(n)}else g(e)}var b=e.dom,k=e.selection;a.backspaceDelete=function(e){function n(e,t){var n=e.startContainer,r=e.startOffset;if(3==n.nodeType&&(t?r<n.data.length:r>0))return n;for(var o=new tinymce.dom.TreeWalker(e.startContainer);n=o[t?"next":"prev"]();)if(3==n.nodeType&&n.data.length>0)return n}function r(e,n){var r,o,i=e.parentNode;for(t(n.lastChild)&&(o=n.lastChild),r=n.lastChild,r&&"BR"==r.nodeName&&e.hasChildNodes()&&b.remove(r);r=e.firstChild;)n.appendChild(r);o&&n.appendChild(o),b.remove(e),b.isEmpty(i)&&b.remove(i)}if(k.isCollapsed()){var o=b.getParent(k.getStart(),"LI");if(o){var i=k.getRng(!0),a=b.getParent(n(i,e),"LI");if(a&&a!=o){var f=d(i);return e?r(a,o):r(o,a),s(f),!0}if(!a&&!e&&N(o.parentNode.nodeName))return!0}}},e.addCommand("Indent",function(){return v()?void 0:!0}),e.addCommand("Outdent",function(){return C()?void 0:!0}),e.addCommand("InsertUnorderedList",function(){y("UL")}),e.addCommand("InsertOrderedList",function(){y("OL")}),e.on("keydown",function(t){9==t.keyCode&&e.dom.getParent(e.selection.getStart(),"LI")&&(t.preventDefault(),t.shiftKey?C():v())})}),e.addButton("indent",{icon:"indent",title:"Increase indent",cmd:"Indent",onPostRender:function(){var t=this;e.on("nodechange",function(){for(var r=e.selection.getSelectedBlocks(),o=!1,i=0,a=r.length;!o&&a>i;i++){var d=r[i].nodeName;o="LI"==d&&n(r[i])||"UL"==d||"OL"==d}t.disabled(o)})}}),e.on("keydown",function(e){e.keyCode==tinymce.util.VK.BACKSPACE?a.backspaceDelete()&&e.preventDefault():e.keyCode==tinymce.util.VK.DELETE&&a.backspaceDelete(!0)&&e.preventDefault()})});tinymce.PluginManager.add("media",function(e,t){function i(e){return-1!=e.indexOf(".mp3")?"audio/mpeg":-1!=e.indexOf(".wav")?"audio/wav":-1!=e.indexOf(".mp4")?"video/mp4":-1!=e.indexOf(".webm")?"video/webm":-1!=e.indexOf(".ogg")?"video/ogg":-1!=e.indexOf(".swf")?"application/x-shockwave-flash":""}function r(t){var i=e.settings.media_scripts;if(i)for(var r=0;r<i.length;r++)if(-1!==t.indexOf(i[r].filter))return i[r]}function o(){function t(e){var t,a,c,s;t=i.find("#width")[0],a=i.find("#height")[0],c=t.value(),s=a.value(),i.find("#constrain")[0].checked()&&r&&o&&c&&s&&(e.control==t?(s=Math.round(c/r*s),a.value(s)):(c=Math.round(s/o*c),t.value(c))),r=c,o=s}var i,r,o,m,u=[{name:"source1",type:"filepicker",filetype:"media",size:40,autofocus:!0,label:"Source"}];e.settings.media_alt_source!==!1&&u.push({name:"source2",type:"filepicker",filetype:"media",size:40,label:"Alternative source"}),e.settings.media_poster!==!1&&u.push({name:"poster",type:"filepicker",filetype:"image",size:40,label:"Poster"}),e.settings.media_dimensions!==!1&&u.push({type:"container",label:"Dimensions",layout:"flex",align:"center",spacing:5,items:[{name:"width",type:"textbox",maxLength:3,size:3,onchange:t},{type:"label",text:"x"},{name:"height",type:"textbox",maxLength:3,size:3,onchange:t},{name:"constrain",type:"checkbox",checked:!0,text:"Constrain proportions"}]}),m=n(e.selection.getNode()),r=m.width,o=m.height,i=e.windowManager.open({title:"Insert/edit video",data:m,bodyType:"tabpanel",body:[{title:"General",type:"form",onShowTab:function(){m=s(this.next().find("#embed").value()),this.fromJSON(m)},items:u},{title:"Embed",type:"panel",layout:"flex",direction:"column",align:"stretch",padding:10,spacing:10,onShowTab:function(){this.find("#embed").value(c(this.parent().toJSON()))},items:[{type:"label",text:"Paste your embed code below:",forId:"mcemediasource"},{id:"mcemediasource",type:"textbox",flex:1,name:"embed",value:a(),multiline:!0,label:"Source"}]}],onSubmit:function(){e.insertContent(c(this.toJSON()))}})}function a(){var t=e.selection.getNode();return t.getAttribute("data-mce-object")?e.selection.getContent():void 0}function c(o){var a="";if(!o.source1&&(tinymce.extend(o,s(o.embed)),!o.source1))return"";if(o.source2||(o.source2=""),o.poster||(o.poster=""),o.source1=e.convertURL(o.source1,"source"),o.source2=e.convertURL(o.source2,"source"),o.source1mime=i(o.source1),o.source2mime=i(o.source2),o.poster=e.convertURL(o.poster,"poster"),o.flashPlayerUrl=e.convertURL(t+"/moxieplayer.swf","movie"),tinymce.each(u,function(e){var t,i,r;if(t=e.regex.exec(o.source1)){for(r=e.url,i=0;t[i];i++)r=r.replace("$"+i,function(){return t[i]});o.source1=r,o.type=e.type,o.width=o.width||e.w,o.height=o.height||e.h}}),o.embed)a=m(o.embed,o,!0);else{var c=r(o.source1);c&&(o.type="script",o.width=c.width,o.height=c.height),o.width=o.width||300,o.height=o.height||150,tinymce.each(o,function(t,i){o[i]=e.dom.encode(t)}),"iframe"==o.type?a+='<iframe src="'+o.source1+'" width="'+o.width+'" height="'+o.height+'"></iframe>':"application/x-shockwave-flash"==o.source1mime?(a+='<object data="'+o.source1+'" width="'+o.width+'" height="'+o.height+'" type="application/x-shockwave-flash">',o.poster&&(a+='<img src="'+o.poster+'" width="'+o.width+'" height="'+o.height+'" />'),a+="</object>"):-1!=o.source1mime.indexOf("audio")?e.settings.audio_template_callback?a=e.settings.audio_template_callback(o):a+='<audio controls="controls" src="'+o.source1+'">'+(o.source2?'\n<source src="'+o.source2+'"'+(o.source2mime?' type="'+o.source2mime+'"':"")+" />\n":"")+"</audio>":"script"==o.type?a+='<script src="'+o.source1+'"></script>':a=e.settings.video_template_callback?e.settings.video_template_callback(o):'<video width="'+o.width+'" height="'+o.height+'"'+(o.poster?' poster="'+o.poster+'"':"")+' controls="controls">\n<source src="'+o.source1+'"'+(o.source1mime?' type="'+o.source1mime+'"':"")+" />\n"+(o.source2?'<source src="'+o.source2+'"'+(o.source2mime?' type="'+o.source2mime+'"':"")+" />\n":"")+"</video>"}return a}function s(e){var t={};return new tinymce.html.SaxParser({validate:!1,allow_conditional_comments:!0,special:"script,noscript",start:function(e,i){if(t.source1||"param"!=e||(t.source1=i.map.movie),("iframe"==e||"object"==e||"embed"==e||"video"==e||"audio"==e)&&(t.type||(t.type=e),t=tinymce.extend(i.map,t)),"script"==e){var o=r(i.map.src);if(!o)return;t={type:"script",source1:i.map.src,width:o.width,height:o.height}}"source"==e&&(t.source1?t.source2||(t.source2=i.map.src):t.source1=i.map.src),"img"!=e||t.poster||(t.poster=i.map.src)}}).parse(e),t.source1=t.source1||t.src||t.data,t.source2=t.source2||"",t.poster=t.poster||"",t}function n(t){return t.getAttribute("data-mce-object")?s(e.serializer.serialize(t,{selection:!0})):{}}function m(e,t,i){function r(e,t){var i,r,o,a;for(i in t)if(o=""+t[i],e.map[i])for(r=e.length;r--;)a=e[r],a.name==i&&(o?(e.map[i]=o,a.value=o):(delete e.map[i],e.splice(r,1)));else o&&(e.push({name:i,value:o}),e.map[i]=o)}var o,a=new tinymce.html.Writer,c=0;return new tinymce.html.SaxParser({validate:!1,allow_conditional_comments:!0,special:"script,noscript",comment:function(e){a.comment(e)},cdata:function(e){a.cdata(e)},text:function(e,t){a.text(e,t)},start:function(e,s,n){switch(e){case"video":case"object":case"embed":case"img":case"iframe":r(s,{width:t.width,height:t.height})}if(i)switch(e){case"video":r(s,{poster:t.poster,src:""}),t.source2&&r(s,{src:""});break;case"iframe":r(s,{src:t.source1});break;case"source":if(c++,2>=c&&(r(s,{src:t["source"+c],type:t["source"+c+"mime"]}),!t["source"+c]))return;break;case"img":if(!t.poster)return;o=!0}a.start(e,s,n)},end:function(e){if("video"==e&&i)for(var s=1;2>=s;s++)if(t["source"+s]){var n=[];n.map={},s>c&&(r(n,{src:t["source"+s],type:t["source"+s+"mime"]}),a.start("source",n,!0))}if(t.poster&&"object"==e&&i&&!o){var m=[];m.map={},r(m,{src:t.poster,width:t.width,height:t.height}),a.start("img",m,!0)}a.end(e)}},new tinymce.html.Schema({})).parse(e),a.getContent()}var u=[{regex:/youtu\.be\/([\w\-.]+)/,type:"iframe",w:425,h:350,url:"//www.youtube.com/embed/$1"},{regex:/youtube\.com(.+)v=([^&]+)/,type:"iframe",w:425,h:350,url:"//www.youtube.com/embed/$2"},{regex:/vimeo\.com\/([0-9]+)/,type:"iframe",w:425,h:350,url:"//player.vimeo.com/video/$1?title=0&byline=0&portrait=0&color=8dc7dc"},{regex:/maps\.google\.([a-z]{2,3})\/maps\/(.+)msid=(.+)/,type:"iframe",w:425,h:350,url:'//maps.google.com/maps/ms?msid=$2&output=embed"'}];e.on("ResolveName",function(e){var t;1==e.target.nodeType&&(t=e.target.getAttribute("data-mce-object"))&&(e.name=t)}),e.on("preInit",function(){var t=e.schema.getSpecialElements();tinymce.each("video audio iframe object".split(" "),function(e){t[e]=new RegExp("</"+e+"[^>]*>","gi")});var i=e.schema.getBoolAttrs();tinymce.each("webkitallowfullscreen mozallowfullscreen allowfullscreen".split(" "),function(e){i[e]={}}),e.parser.addNodeFilter("iframe,video,audio,object,embed,script",function(t,i){for(var o,a,c,s,n,m,u,d,l=t.length;l--;)if(a=t[l],a.parent&&("script"!=a.name||(d=r(a.attr("src"))))){for(c=new tinymce.html.Node("img",1),c.shortEnded=!0,d&&(d.width&&a.attr("width",d.width.toString()),d.height&&a.attr("height",d.height.toString())),m=a.attributes,o=m.length;o--;)s=m[o].name,n=m[o].value,"width"!==s&&"height"!==s&&"style"!==s&&(("data"==s||"src"==s)&&(n=e.convertURL(n,s)),c.attr("data-mce-p-"+s,n));u=a.firstChild&&a.firstChild.value,u&&(c.attr("data-mce-html",escape(u)),c.firstChild=null),c.attr({width:a.attr("width")||"300",height:a.attr("height")||("audio"==i?"30":"150"),style:a.attr("style"),src:tinymce.Env.transparentSrc,"data-mce-object":i,"class":"mce-object mce-object-"+i}),a.replace(c)}}),e.serializer.addAttributeFilter("data-mce-object",function(e,t){for(var i,r,o,a,c,s,n,m=e.length;m--;)if(i=e[m],i.parent){for(n=i.attr(t),r=new tinymce.html.Node(n,1),"audio"!=n&&"script"!=n&&r.attr({width:i.attr("width"),height:i.attr("height")}),r.attr({style:i.attr("style")}),a=i.attributes,o=a.length;o--;){var u=a[o].name;0===u.indexOf("data-mce-p-")&&r.attr(u.substr(11),a[o].value)}"script"==n&&r.attr("type","text/javascript"),c=i.attr("data-mce-html"),c&&(s=new tinymce.html.Node("#text",3),s.raw=!0,s.value=unescape(c),r.append(s)),i.replace(r)}})}),e.on("ObjectSelected",function(e){var t=e.target.getAttribute("data-mce-object");("audio"==t||"script"==t)&&e.preventDefault()}),e.on("objectResized",function(e){var t,i=e.target;i.getAttribute("data-mce-object")&&(t=i.getAttribute("data-mce-html"),t&&(t=unescape(t),i.setAttribute("data-mce-html",escape(m(t,{width:e.width,height:e.height})))))}),e.addButton("media",{tooltip:"Insert/edit video",onclick:o,stateSelector:["img[data-mce-object=video]","img[data-mce-object=iframe]"]}),e.addMenuItem("media",{icon:"media",text:"Insert video",onclick:o,context:"insert",prependToContext:!0})});tinymce.PluginManager.add("nonbreaking",function(n){var e=n.getParam("nonbreaking_force_tab");if(n.addCommand("mceNonBreaking",function(){n.insertContent(n.plugins.visualchars&&n.plugins.visualchars.state?'<span class="mce-nbsp">&nbsp;</span>':"&nbsp;"),n.dom.setAttrib(n.dom.select("span.mce-nbsp"),"data-mce-bogus","1")}),n.addButton("nonbreaking",{title:"Insert nonbreaking space",cmd:"mceNonBreaking"}),n.addMenuItem("nonbreaking",{text:"Nonbreaking space",cmd:"mceNonBreaking",context:"insert"}),e){var a=+e>1?+e:3;n.on("keydown",function(e){if(9==e.keyCode){if(e.shiftKey)return;e.preventDefault();for(var t=0;a>t;t++)n.execCommand("mceNonBreaking")}})}});tinymce.PluginManager.add("noneditable",function(e){function t(e){var t;if(1===e.nodeType){if(t=e.getAttribute(u),t&&"inherit"!==t)return t;if(t=e.contentEditable,"inherit"!==t)return t}return null}function n(e){for(var n;e;){if(n=t(e))return"false"===n?e:null;e=e.parentNode}}function r(){function r(e){for(;e;){if(e.id===g)return e;e=e.parentNode}}function a(e){var t;if(e)for(t=new f(e,e),e=t.current();e;e=t.next())if(3===e.nodeType)return e}function i(n,r){var a,i;return"false"===t(n)&&u.isBlock(n)?void s.select(n):(i=u.createRng(),"true"===t(n)&&(n.firstChild||n.appendChild(e.getDoc().createTextNode(" ")),n=n.firstChild,r=!0),a=u.create("span",{id:g,"data-mce-bogus":!0},m),r?n.parentNode.insertBefore(a,n):u.insertAfter(a,n),i.setStart(a.firstChild,1),i.collapse(!0),s.setRng(i),a)}function o(e){var t,n,i,o;if(e)t=s.getRng(!0),t.setStartBefore(e),t.setEndBefore(e),n=a(e),n&&n.nodeValue.charAt(0)==m&&(n=n.deleteData(0,1)),u.remove(e,!0),s.setRng(t);else for(i=r(s.getStart());(e=u.get(g))&&e!==o;)i!==e&&(n=a(e),n&&n.nodeValue.charAt(0)==m&&(n=n.deleteData(0,1)),u.remove(e,!0)),o=e}function l(){function e(e,n){var r,a,i,o,l;if(r=d.startContainer,a=d.startOffset,3==r.nodeType){if(l=r.nodeValue.length,a>0&&l>a||(n?a==l:0===a))return}else{if(!(a<r.childNodes.length))return n?null:e;var u=!n&&a>0?a-1:a;r=r.childNodes[u],r.hasChildNodes()&&(r=r.firstChild)}for(i=new f(r,e);o=i[n?"prev":"next"]();){if(3===o.nodeType&&o.nodeValue.length>0)return;if("true"===t(o))return o}return e}var r,a,l,d,u;o(),l=s.isCollapsed(),r=n(s.getStart()),a=n(s.getEnd()),(r||a)&&(d=s.getRng(!0),l?(r=r||a,(u=e(r,!0))?i(u,!0):(u=e(r,!1))?i(u,!1):s.select(r)):(d=s.getRng(!0),r&&d.setStartBefore(r),a&&d.setEndAfter(a),s.setRng(d)))}function d(a){function i(e,t){for(;e=e[t?"previousSibling":"nextSibling"];)if(3!==e.nodeType||e.nodeValue.length>0)return e}function d(e,t){s.select(e),s.collapse(t)}function g(a){function i(e){for(var t=d;t;){if(t===e)return;t=t.parentNode}u.remove(e),l()}function o(){var r,o,l=e.schema.getNonEmptyElements();for(o=new tinymce.dom.TreeWalker(d,e.getBody());(r=a?o.prev():o.next())&&!l[r.nodeName.toLowerCase()]&&!(3===r.nodeType&&tinymce.trim(r.nodeValue).length>0);)if("false"===t(r))return i(r),!0;return n(r)?!0:!1}var f,d,c,g;if(s.isCollapsed()){if(f=s.getRng(!0),d=f.startContainer,c=f.startOffset,d=r(d)||d,g=n(d))return i(g),!1;if(3==d.nodeType&&(a?c>0:c<d.nodeValue.length))return!0;if(1==d.nodeType&&(d=d.childNodes[c]||d),o())return!1}return!0}var m,p,v,E,h=a.keyCode;if(v=s.getStart(),E=s.getEnd(),m=n(v)||n(E),m&&(112>h||h>124)&&h!=c.DELETE&&h!=c.BACKSPACE){if((tinymce.isMac?a.metaKey:a.ctrlKey)&&(67==h||88==h||86==h))return;if(a.preventDefault(),h==c.LEFT||h==c.RIGHT){var y=h==c.LEFT;if(e.dom.isBlock(m)){var T=y?m.previousSibling:m.nextSibling,C=new f(T,T),b=y?C.prev():C.next();d(b,!y)}else d(m,y)}}else if(h==c.LEFT||h==c.RIGHT||h==c.BACKSPACE||h==c.DELETE){if(p=r(v)){if(h==c.LEFT||h==c.BACKSPACE)if(m=i(p,!0),m&&"false"===t(m)){if(a.preventDefault(),h!=c.LEFT)return void u.remove(m);d(m,!0)}else o(p);if(h==c.RIGHT||h==c.DELETE)if(m=i(p),m&&"false"===t(m)){if(a.preventDefault(),h!=c.RIGHT)return void u.remove(m);d(m,!1)}else o(p)}if((h==c.BACKSPACE||h==c.DELETE)&&!g(h==c.BACKSPACE))return a.preventDefault(),!1}}var u=e.dom,s=e.selection,g="mce_noneditablecaret",m="";e.on("mousedown",function(n){var r=e.selection.getNode();"false"===t(r)&&r==n.target&&l()}),e.on("mouseup keyup",l),e.on("keydown",d)}function a(t){var n=l.length,r=t.content,a=tinymce.trim(o);if("raw"!=t.format){for(;n--;)r=r.replace(l[n],function(t){var n=arguments,i=n[n.length-2];return i>0&&'"'==r.charAt(i-1)?t:'<span class="'+a+'" data-mce-content="'+e.dom.encode(n[0])+'">'+e.dom.encode("string"==typeof n[1]?n[1]:n[0])+"</span>"});t.content=r}}var i,o,l,f=tinymce.dom.TreeWalker,d="contenteditable",u="data-mce-"+d,c=tinymce.util.VK;i=" "+tinymce.trim(e.getParam("noneditable_editable_class","mceEditable"))+" ",o=" "+tinymce.trim(e.getParam("noneditable_noneditable_class","mceNonEditable"))+" ",l=e.getParam("noneditable_regexp"),l&&!l.length&&(l=[l]),e.on("PreInit",function(){r(),l&&e.on("BeforeSetContent",a),e.parser.addAttributeFilter("class",function(e){for(var t,n,r=e.length;r--;)n=e[r],t=" "+n.attr("class")+" ",-1!==t.indexOf(i)?n.attr(u,"true"):-1!==t.indexOf(o)&&n.attr(u,"false")}),e.serializer.addAttributeFilter(u,function(e){for(var t,n=e.length;n--;)t=e[n],l&&t.attr("data-mce-content")?(t.name="#text",t.type=3,t.raw=!0,t.value=t.attr("data-mce-content")):(t.attr(d,null),t.attr(u,null))}),e.parser.addAttributeFilter(d,function(e){for(var t,n=e.length;n--;)t=e[n],t.attr(u,t.attr(d)),t.attr(d,null)})}),e.on("drop",function(e){n(e.target)&&e.preventDefault()})});tinymce.PluginManager.add("pagebreak",function(e){var a="mce-pagebreak",t=e.getParam("pagebreak_separator","<!-- pagebreak -->"),n=new RegExp(t.replace(/[\?\.\*\[\]\(\)\{\}\+\^\$\:]/g,function(e){return"\\"+e}),"gi"),r='<img src="'+tinymce.Env.transparentSrc+'" class="'+a+'" data-mce-resize="false" />';e.addCommand("mcePageBreak",function(){e.insertContent(e.settings.pagebreak_split_block?"<p>"+r+"</p>":r)}),e.addButton("pagebreak",{title:"Page break",cmd:"mcePageBreak"}),e.addMenuItem("pagebreak",{text:"Page break",icon:"pagebreak",cmd:"mcePageBreak",context:"insert"}),e.on("ResolveName",function(t){"IMG"==t.target.nodeName&&e.dom.hasClass(t.target,a)&&(t.name="pagebreak")}),e.on("click",function(t){t=t.target,"IMG"===t.nodeName&&e.dom.hasClass(t,a)&&e.selection.select(t)}),e.on("BeforeSetContent",function(e){e.content=e.content.replace(n,r)}),e.on("PreInit",function(){e.serializer.addNodeFilter("img",function(a){for(var n,r,c=a.length;c--;)if(n=a[c],r=n.attr("class"),r&&-1!==r.indexOf("mce-pagebreak")){var o=n.parent;if(e.schema.getBlockElements()[o.name]&&e.settings.pagebreak_split_block){o.type=3,o.value=t,o.raw=!0,n.remove();continue}n.type=3,n.value=t,n.raw=!0}})})});!function(e,t){"use strict";function n(e,t){for(var n,i=[],r=0;r<e.length;++r){if(n=s[e[r]]||o(e[r]),!n)throw"module definition dependecy not found: "+e[r];i.push(n)}t.apply(null,i)}function i(e,i,r){if("string"!=typeof e)throw"invalid module definition, module id must be defined and be a string";if(i===t)throw"invalid module definition, dependencies must be specified";if(r===t)throw"invalid module definition, definition function must be specified";n(i,function(){s[e]=r.apply(null,arguments)})}function r(e){return!!s[e]}function o(t){for(var n=e,i=t.split(/[.\/]/),r=0;r<i.length;++r){if(!n[i[r]])return;n=n[i[r]]}return n}function a(n){for(var i=0;i<n.length;i++){for(var r=e,o=n[i],a=o.split(/[.\/]/),l=0;l<a.length-1;++l)r[a[l]]===t&&(r[a[l]]={}),r=r[a[l]];r[a[a.length-1]]=s[o]}}var s={},l="tinymce/pasteplugin/Utils",c="tinymce/util/Tools",d="tinymce/html/DomParser",u="tinymce/html/Schema",f="tinymce/pasteplugin/Clipboard",m="tinymce/Env",p="tinymce/util/VK",g="tinymce/pasteplugin/WordFilter",h="tinymce/html/Serializer",v="tinymce/html/Node",b="tinymce/pasteplugin/Quirks",y="tinymce/pasteplugin/Plugin",x="tinymce/PluginManager";i(l,[c,d,u],function(e,t,n){function i(t,n){return e.each(n,function(e){t=e.constructor==RegExp?t.replace(e,""):t.replace(e[0],e[1])}),t}function r(i){function r(e){var t=e.name,n=e;if("br"===t)return void(s+="\n");if(l[t]&&(s+=" "),c[t])return void(s+=" ");if(3==e.type&&(s+=e.value),!e.shortEnded&&(e=e.firstChild))do r(e);while(e=e.next);d[t]&&n.next&&(s+="\n","p"==t&&(s+="\n"))}var o=new n,a=new t({},o),s="",l=o.getShortEndedElements(),c=e.makeMap("script noscript style textarea video audio iframe object"," "),d=o.getBlockElements();return r(a.parse(i)),s}return{filter:i,innerText:r}}),i(f,[m,p,l],function(e,t,n){return function(i){function r(e){var t,n=i.dom;if(t=i.fire("BeforePastePreProcess",{content:e}),t=i.fire("PastePreProcess",t),e=t.content,!t.isDefaultPrevented()){if(i.hasEventListeners("PastePostProcess")&&!t.isDefaultPrevented()){var r=n.add(i.getBody(),"div",{style:"display:none"},e);t=i.fire("PastePostProcess",{node:r}),n.remove(r),e=t.node.innerHTML}t.isDefaultPrevented()||i.insertContent(e)}}function o(e){e=i.dom.encode(e).replace(/\r\n/g,"\n");var t=i.dom.getParent(i.selection.getStart(),i.dom.isBlock),o=i.settings.forced_root_block,a;o&&(a=i.dom.createHTML(o,i.settings.forced_root_block_attrs),a=a.substr(0,a.length-3)+">"),t&&/^(PRE|DIV)$/.test(t.nodeName)||!o?e=n.filter(e,[[/\n/g,"<br>"]]):(e=n.filter(e,[[/\n\n/g,"</p>"+a],[/^(.*<\/p>)(<p>)$/,a+"$1"],[/\n/g,"<br />"]]),-1!=e.indexOf("<p>")&&(e=a+e)),r(e)}function a(){var t=i.dom,n=i.getBody(),r=i.dom.getViewPort(i.getWin()),o=r.y,a=20,s;if(v=i.selection.getRng(),i.inline&&(s=i.selection.getScrollContainer(),s&&s.scrollTop>0&&(o=s.scrollTop)),v.getClientRects){var l=v.getClientRects();if(l.length)a=o+(l[0].top-t.getPos(n).y);else{a=o;var c=v.startContainer;c&&(3==c.nodeType&&c.parentNode!=n&&(c=c.parentNode),1==c.nodeType&&(a=t.getPos(c,s||n).y))}}h=t.add(i.getBody(),"div",{id:"mcepastebin",contentEditable:!0,"data-mce-bogus":"1",style:"position: absolute; top: "+a+"px;width: 10px; height: 10px; overflow: hidden; opacity: 0"},y),(e.ie||e.gecko)&&t.setStyle(h,"left","rtl"==t.getStyle(n,"direction",!0)?65535:-65535),t.bind(h,"beforedeactivate focusin focusout",function(e){e.stopPropagation()}),h.focus(),i.selection.select(h,!0)}function s(){if(h){for(var e;e=i.dom.get("mcepastebin");)i.dom.remove(e),i.dom.unbind(e);v&&i.selection.setRng(v)}x=!1,h=v=null}function l(){var e=y,t,n;for(t=i.dom.select("div[id=mcepastebin]"),n=t.length;n--;){var r=t[n].innerHTML;e==y&&(e=""),r.length>e.length&&(e=r)}return e}function c(e){var t={};if(e&&e.types){var n=e.getData("Text");n&&n.length>0&&(t["text/plain"]=n);for(var i=0;i<e.types.length;i++){var r=e.types[i];t[r]=e.getData(r)}}return t}function d(e){return c(e.clipboardData||i.getDoc().dataTransfer)}function u(e,t){function n(e){if("image/png"==o[a].type){var t=new FileReader;return t.onload=function(){r('<img src="'+t.result+'">')},t.readAsDataURL(e.getAsFile()),!0}}if(!(!i.settings.paste_data_images||"text/html"in t||"text/plain"in t)&&e.clipboardData){var o=e.clipboardData.items;if(o)for(var a=0;a<o.length;a++)if(n(o[a]))return!0}}function f(e){var t=i.getDoc(),n;if(t.caretPositionFromPoint){var r=t.caretPositionFromPoint(e.clientX,e.clientY);n=t.createRange(),n.setStart(r.offsetNode,r.offset),n.collapse(!0)}else t.caretRangeFromPoint&&(n=t.caretRangeFromPoint(e.clientX,e.clientY));return n}function m(e,t){return t in e&&e[t].length>0}function p(){i.on("keydown",function(n){if(!n.isDefaultPrevented()&&(t.metaKeyPressed(n)&&86==n.keyCode||n.shiftKey&&45==n.keyCode)){if(x=n.shiftKey&&86==n.keyCode,n.stopImmediatePropagation(),b=(new Date).getTime(),e.ie&&x)return n.preventDefault(),void i.fire("paste",{ieFake:!0});s(),a()}}),i.on("paste",function(t){var c=d(t),f=(new Date).getTime()-b<1e3,p="text"==g.pasteFormat||x;return t.isDefaultPrevented()?void s():u(t,c)?void s():(f||t.preventDefault(),!e.ie||f&&!t.ieFake||(a(),i.dom.bind(h,"paste",function(e){e.stopPropagation()}),i.getDoc().execCommand("Paste",!1,null),c["text/html"]=l()),void setTimeout(function(){var e=l();return h&&h.firstChild&&"mcepastebin"===h.firstChild.id&&(p=!0),s(),!p&&f&&e&&e!=y&&(c["text/html"]=e),e!=y&&f||(e=c["text/html"]||c["text/plain"]||y,e!=y)?(!m(c,"text/html")&&m(c,"text/plain")&&(p=!0),void(p?o(c["text/plain"]||n.innerText(e)):r(e))):void(f||i.windowManager.alert("Please use Ctrl+V/Cmd+V keyboard shortcuts to paste contents."))},0))}),i.on("dragstart",function(e){if(e.dataTransfer.types)try{e.dataTransfer.setData("mce-internal",i.selection.getContent())}catch(t){}}),i.on("drop",function(e){var t=f(e);if(t&&!e.isDefaultPrevented()){var n=c(e.dataTransfer),a=n["mce-internal"]||n["text/html"]||n["text/plain"];a&&(e.preventDefault(),i.undoManager.transact(function(){n["mce-internal"]&&i.execCommand("Delete"),i.selection.setRng(t),n["text/html"]?r(a):o(a)}))}})}var g=this,h,v,b=0,y="%MCEPASTEBIN%",x;g.pasteHtml=r,g.pasteText=o,i.on("preInit",function(){p(),i.parser.addNodeFilter("img",function(t){if(!i.settings.paste_data_images)for(var n=t.length;n--;){var r=t[n].attributes.map.src;r&&0===r.indexOf("data:image")&&(t[n].attr("data-mce-object")||r===e.transparentSrc||t[n].remove())}})}),i.on("PreProcess",function(){i.dom.remove(i.dom.get("mcepastebin"))})}}),i(g,[c,d,u,h,v,l],function(e,t,n,i,r,o){function a(e){return/<font face="Times New Roman"|class="?Mso|style="[^"]*\bmso-|style='[^'']*\bmso-|w:WordDocument/i.test(e)||/class="OutlineElement/.test(e)||/id="?docs\-internal\-guid\-/.test(e)}function s(s){var l=s.settings;s.on("BeforePastePreProcess",function(c){function d(e){function t(e,t,a,s){var l=e._listLevel||o;l!=o&&(o>l?n&&(n=n.parent.parent):(i=n,n=null)),n&&n.name==a?n.append(e):(i=i||n,n=new r(a,1),s>1&&n.attr("start",""+s),e.wrap(n)),e.name="li",t.value="";var c=t.next;c&&3==c.type&&(c.value=c.value.replace(/^\u00a0+/,"")),l>o&&i&&i.lastChild.append(n),o=l}for(var n,i,o=1,a=e.getAll("p"),s=0;s<a.length;s++)if(e=a[s],"p"==e.name&&e.firstChild){for(var l="",c=e.firstChild;c&&!(l=c.value);)c=c.firstChild;if(/^\s*[\u2022\u00b7\u00a7\u00d8\u25CF]\s*$/.test(l)){t(e,c,"ul");continue}if(/^\s*\w+\.$/.test(l)){var d=/([0-9])\./.exec(l),u=1;d&&(u=parseInt(d[1],10)),t(e,c,"ol",u);continue}n=null}}function u(t,n){var i={},o=s.dom.parseStyle(n);if("p"===t.name){var a=/mso-list:\w+ \w+([0-9]+)/.exec(n);a&&(t._listLevel=parseInt(a[1],10))}return e.each(o,function(e,n){switch(n){case"horiz-align":n="text-align";break;case"vert-align":n="vertical-align";break;case"font-color":case"mso-foreground":n="color";break;case"mso-background":case"mso-highlight":n="background";break;case"font-weight":case"font-style":return void("normal"!=e&&(i[n]=e));case"mso-element":if(/^(comment|comment-list)$/i.test(e))return void t.remove()}return 0===n.indexOf("mso-comment")?void t.remove():void(0!==n.indexOf("mso-")&&("all"==m||p&&p[n])&&(i[n]=e))}),/(bold)/i.test(i["font-weight"])&&(delete i["font-weight"],t.wrap(new r("b",1))),/(italic)/i.test(i["font-style"])&&(delete i["font-style"],t.wrap(new r("i",1))),i=s.dom.serializeStyle(i,t.name),i?i:null}var f=c.content,m,p;if(m=l.paste_retain_style_properties,m&&(p=e.makeMap(m.split(/[, ]/))),l.paste_enable_default_filters!==!1&&a(c.content)){c.wordContent=!0,f=o.filter(f,[/<!--[\s\S]+?-->/gi,/<(!|script[^>]*>.*?<\/script(?=[>\s])|\/?(\?xml(:\w+)?|img|meta|link|style|\w:\w+)(?=[\s\/>]))[^>]*>/gi,[/<(\/?)s>/gi,"<$1strike>"],[/&nbsp;/gi,"\xa0"],[/<span\s+style\s*=\s*"\s*mso-spacerun\s*:\s*yes\s*;?\s*"\s*>([\s\u00a0]*)<\/span>/gi,function(e,t){return t.length>0?t.replace(/./," ").slice(Math.floor(t.length/2)).split("").join("\xa0"):""}]]);var g=l.paste_word_valid_elements;g||(g="-strong/b,-em/i,-span,-p,-ol,-ul,-li,-h1,-h2,-h3,-h4,-h5,-h6,-p/div,-table[width],-tr,-td[colspan|rowspan|width],-th,-thead,-tfoot,-tbody,-a[href|name],sub,sup,strike,br,del");var h=new n({valid_elements:g,valid_children:"-li[p]"});e.each(h.elements,function(e){e.attributes["class"]||(e.attributes["class"]={},e.attributesOrder.push("class")),e.attributes.style||(e.attributes.style={},e.attributesOrder.push("style"))});var v=new t({},h);v.addAttributeFilter("style",function(e){for(var t=e.length,n;t--;)n=e[t],n.attr("style",u(n,n.attr("style"))),"span"==n.name&&n.parent&&!n.attributes.length&&n.unwrap()}),v.addAttributeFilter("class",function(e){for(var t=e.length,n,i;t--;)n=e[t],i=n.attr("class"),/^(MsoCommentReference|MsoCommentText|msoDel)$/i.test(i)&&n.remove(),n.attr("class",null)}),v.addNodeFilter("del",function(e){for(var t=e.length;t--;)e[t].remove()}),v.addNodeFilter("a",function(e){for(var t=e.length,n,i,r;t--;)if(n=e[t],i=n.attr("href"),r=n.attr("name"),i&&-1!=i.indexOf("#_msocom_"))n.remove();else if(i&&0===i.indexOf("file://")&&(i=i.split("#")[1],i&&(i="#"+i)),i||r){if(r&&!/^_?(?:toc|edn|ftn)/i.test(r)){n.unwrap();continue}n.attr({href:i,name:r})}else n.unwrap()});var b=v.parse(f);d(b),c.content=new i({},h).serialize(b)}})}return s.isWordContent=a,s}),i(b,[m,c,g,l],function(e,t,n,i){return function(r){function o(e){r.on("BeforePastePreProcess",function(t){t.content=e(t.content)})}function a(e){return e=i.filter(e,[/^[\s\S]*<body[^>]*>\s*<!--StartFragment-->|<!--EndFragment-->\s*<\/body[^>]*>[\s\S]*$/g,/<!--StartFragment-->|<!--EndFragment-->/g,[/<span class="Apple-converted-space">\u00a0<\/span>/g,"\xa0"],/<br>$/i])}function s(e){if(!n.isWordContent(e))return e;var o=[];t.each(r.schema.getBlockElements(),function(e,t){o.push(t)});var a=new RegExp("(?:<br>&nbsp;[\\s\\r\\n]+|<br>)*(<\\/?("+o.join("|")+")[^>]*>)(?:<br>&nbsp;[\\s\\r\\n]+|<br>)*","g");return e=i.filter(e,[[a,"$1"]]),e=i.filter(e,[[/<br><br>/g,"<BR><BR>"],[/<br>/g," "],[/<BR><BR>/g,"<br>"]])}function l(e){if(n.isWordContent(e))return e;var t=r.settings.paste_webkit_styles;if(r.settings.paste_remove_styles_if_webkit===!1||"all"==t)return e;if(t&&(t=t.split(/[, ]/)),t){var i=r.dom,o=r.selection.getNode();e=e.replace(/(<[^>]+) style="([^"]*)"([^>]*>)/gi,function(e,n,r,a){var s=i.parseStyle(r,"span"),l={};if("none"===t)return n+a;for(var c=0;c<t.length;c++){var d=s[t[c]],u=i.getStyle(o,t[c],!0);/color/.test(t[c])&&(d=i.toHex(d),u=i.toHex(u)),u!=d&&(l[t[c]]=d)}return l=i.serializeStyle(l,"span"),l?n+' style="'+l+'"'+a:""})}else e=e.replace(/(<[^>]+) style="([^"]*)"([^>]*>)/gi,"$1$3");return e=e.replace(/(<[^>]+) data-mce-style="([^"]+)"([^>]*>)/gi,function(e,t,n,i){return t+' style="'+n+'"'+i})}e.webkit&&(o(l),o(a)),e.ie&&o(s)}}),i(y,[x,f,g,b],function(e,t,n,i){var r;e.add("paste",function(e){function o(){"text"==s.pasteFormat?(this.active(!1),s.pasteFormat="html"):(s.pasteFormat="text",this.active(!0),r||(e.windowManager.alert("Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off."),r=!0))}var a=this,s,l=e.settings;a.clipboard=s=new t(e),a.quirks=new i(e),a.wordFilter=new n(e),e.settings.paste_as_text&&(a.clipboard.pasteFormat="text"),l.paste_preprocess&&e.on("PastePreProcess",function(e){l.paste_preprocess.call(a,a,e)}),l.paste_postprocess&&e.on("PastePostProcess",function(e){l.paste_postprocess.call(a,a,e)}),e.addCommand("mceInsertClipboardContent",function(e,t){t.content&&a.clipboard.pasteHtml(t.content),t.text&&a.clipboard.pasteText(t.text)}),e.paste_block_drop&&e.on("dragend dragover draggesture dragdrop drop drag",function(e){e.preventDefault(),e.stopPropagation()}),e.settings.paste_data_images||e.on("drop",function(e){var t=e.dataTransfer;t&&t.files&&t.files.length>0&&e.preventDefault()}),e.addButton("pastetext",{icon:"pastetext",tooltip:"Paste as text",onclick:o,active:"text"==a.clipboard.pasteFormat}),e.addMenuItem("pastetext",{text:"Paste as text",selectable:!0,active:s.pasteFormat,onclick:o})})}),a([l,f,g,b,y])}(this);tinymce.PluginManager.add("preview",function(e){var t=e.settings,i=!tinymce.Env.ie;e.addCommand("mcePreview",function(){e.windowManager.open({title:"Preview",width:parseInt(e.getParam("plugin_preview_width","650"),10),height:parseInt(e.getParam("plugin_preview_height","500"),10),html:'<iframe src="javascript:\'\'" frameborder="0"'+(i?' sandbox="allow-scripts"':"")+"></iframe>",buttons:{text:"Close",onclick:function(){this.parent().parent().close()}},onPostRender:function(){var n,a="";a+='<base href="'+e.documentBaseURI.getURI()+'">',tinymce.each(e.contentCSS,function(t){a+='<link type="text/css" rel="stylesheet" href="'+e.documentBaseURI.toAbsolute(t)+'">'});var r=t.body_id||"tinymce";-1!=r.indexOf("=")&&(r=e.getParam("body_id","","hash"),r=r[e.id]||r);var d=t.body_class||"";-1!=d.indexOf("=")&&(d=e.getParam("body_class","","hash"),d=d[e.id]||"");var o=e.settings.directionality?' dir="'+e.settings.directionality+'"':"";if(n="<!DOCTYPE html><html><head>"+a+'</head><body id="'+r+'" class="mce-content-body '+d+'"'+o+">"+e.getContent()+"</body></html>",i)this.getEl("body").firstChild.src="data:text/html;charset=utf-8,"+encodeURIComponent(n);else{var s=this.getEl("body").firstChild.contentWindow.document;s.open(),s.write(n),s.close()}}})}),e.addButton("preview",{title:"Preview",cmd:"mcePreview"}),e.addMenuItem("preview",{text:"Preview",cmd:"mcePreview",context:"view"})});tinymce.PluginManager.add("print",function(t){t.addCommand("mcePrint",function(){t.getWin().print()}),t.addButton("print",{title:"Print",cmd:"mcePrint"}),t.addShortcut("Ctrl+P","","mcePrint"),t.addMenuItem("print",{text:"Print",cmd:"mcePrint",icon:"print",shortcut:"Ctrl+P",context:"file"})});tinymce.PluginManager.add("save",function(e){function a(){var a;return a=tinymce.DOM.getParent(e.id,"form"),!e.getParam("save_enablewhendirty",!0)||e.isDirty()?(tinymce.triggerSave(),e.getParam("save_onsavecallback")?void(e.execCallback("save_onsavecallback",e)&&(e.startContent=tinymce.trim(e.getContent({format:"raw"})),e.nodeChanged())):void(a?(e.isNotDirty=!0,(!a.onsubmit||a.onsubmit())&&("function"==typeof a.submit?a.submit():e.windowManager.alert("Error: Form submit field collision.")),e.nodeChanged()):e.windowManager.alert("Error: No form element found."))):void 0}function n(){var a=tinymce.trim(e.startContent);return e.getParam("save_oncancelcallback")?void e.execCallback("save_oncancelcallback",e):(e.setContent(a),e.undoManager.clear(),void e.nodeChanged())}function t(){var a=this;e.on("nodeChange",function(){a.disabled(e.getParam("save_enablewhendirty",!0)&&!e.isDirty())})}e.addCommand("mceSave",a),e.addCommand("mceCancel",n),e.addButton("save",{icon:"save",text:"Save",cmd:"mceSave",disabled:!0,onPostRender:t}),e.addButton("cancel",{text:"Cancel",icon:!1,cmd:"mceCancel",disabled:!0,onPostRender:t}),e.addShortcut("ctrl+s","","mceSave")});!function(){function e(e,t,n,a,r){function i(e,t){if(t=t||0,!e[0])throw"findAndReplaceDOMText cannot handle zero-length matches";var n=e.index;if(t>0){var a=e[t];if(!a)throw"Invalid capture group";n+=e[0].indexOf(a),e[0]=a}return[n,n+e[0].length,[e[0]]]}function d(e){var t;if(3===e.nodeType)return e.data;if(h[e.nodeName]&&!u[e.nodeName])return"";if(t="",(u[e.nodeName]||m[e.nodeName])&&(t+="\n"),e=e.firstChild)do t+=d(e);while(e=e.nextSibling);return t}function o(e,t,n){var a,r,i,d,o=[],l=0,c=e,s=t.shift(),f=0;e:for(;;){if((u[c.nodeName]||m[c.nodeName])&&l++,3===c.nodeType&&(!r&&c.length+l>=s[1]?(r=c,d=s[1]-l):a&&o.push(c),!a&&c.length+l>s[0]&&(a=c,i=s[0]-l),l+=c.length),a&&r){if(c=n({startNode:a,startNodeIndex:i,endNode:r,endNodeIndex:d,innerNodes:o,match:s[2],matchIndex:f}),l-=r.length-d,a=null,r=null,o=[],s=t.shift(),f++,!s)break}else{if((!h[c.nodeName]||u[c.nodeName])&&c.firstChild){c=c.firstChild;continue}if(c.nextSibling){c=c.nextSibling;continue}}for(;;){if(c.nextSibling){c=c.nextSibling;break}if(c.parentNode===e)break e;c=c.parentNode}}}function l(e){var t;if("function"!=typeof e){var n=e.nodeType?e:f.createElement(e);t=function(e,t){var a=n.cloneNode(!1);return a.setAttribute("data-mce-index",t),e&&a.appendChild(f.createTextNode(e)),a}}else t=e;return function(e){var n,a,r,i=e.startNode,d=e.endNode,o=e.matchIndex;if(i===d){var l=i;r=l.parentNode,e.startNodeIndex>0&&(n=f.createTextNode(l.data.substring(0,e.startNodeIndex)),r.insertBefore(n,l));var c=t(e.match[0],o);return r.insertBefore(c,l),e.endNodeIndex<l.length&&(a=f.createTextNode(l.data.substring(e.endNodeIndex)),r.insertBefore(a,l)),l.parentNode.removeChild(l),c}n=f.createTextNode(i.data.substring(0,e.startNodeIndex)),a=f.createTextNode(d.data.substring(e.endNodeIndex));for(var s=t(i.data.substring(e.startNodeIndex),o),u=[],h=0,m=e.innerNodes.length;m>h;++h){var g=e.innerNodes[h],p=t(g.data,o);g.parentNode.replaceChild(p,g),u.push(p)}var x=t(d.data.substring(0,e.endNodeIndex),o);return r=i.parentNode,r.insertBefore(n,i),r.insertBefore(s,i),r.removeChild(i),r=d.parentNode,r.insertBefore(x,d),r.insertBefore(a,d),r.removeChild(d),x}}var c,s,f,u,h,m,g=[],p=0;if(f=t.ownerDocument,u=r.getBlockElements(),h=r.getWhiteSpaceElements(),m=r.getShortEndedElements(),s=d(t)){if(e.global)for(;c=e.exec(s);)g.push(i(c,a));else c=s.match(e),g.push(i(c,a));return g.length&&(p=g.length,o(t,g,l(n))),p}}function t(t){function n(){function e(){r.statusbar.find("#next").disabled(!d(s+1).length),r.statusbar.find("#prev").disabled(!d(s-1).length)}function n(){tinymce.ui.MessageBox.alert("Could not find the specified string.",function(){r.find("#find")[0].focus()})}var a={},r=tinymce.ui.Factory.create({type:"window",layout:"flex",pack:"center",align:"center",onClose:function(){t.focus(),c.done()},onSubmit:function(t){var i,o,l,f;return t.preventDefault(),o=r.find("#case").checked(),f=r.find("#words").checked(),l=r.find("#find").value(),l.length?a.text==l&&a.caseState==o&&a.wholeWord==f?0===d(s+1).length?void n():(c.next(),void e()):(i=c.find(l,o,f),i||n(),r.statusbar.items().slice(1).disabled(0===i),e(),void(a={text:l,caseState:o,wholeWord:f})):(c.done(!1),void r.statusbar.items().slice(1).disabled(!0))},buttons:[{text:"Find",onclick:function(){r.submit()}},{text:"Replace",disabled:!0,onclick:function(){c.replace(r.find("#replace").value())||(r.statusbar.items().slice(1).disabled(!0),s=-1,a={})}},{text:"Replace all",disabled:!0,onclick:function(){c.replace(r.find("#replace").value(),!0,!0),r.statusbar.items().slice(1).disabled(!0),a={}}},{type:"spacer",flex:1},{text:"Prev",name:"prev",disabled:!0,onclick:function(){c.prev(),e()}},{text:"Next",name:"next",disabled:!0,onclick:function(){c.next(),e()}}],title:"Find and replace",items:{type:"form",padding:20,labelGap:30,spacing:10,items:[{type:"textbox",name:"find",size:40,label:"Find",value:t.selection.getNode().src},{type:"textbox",name:"replace",size:40,label:"Replace with"},{type:"checkbox",name:"case",text:"Match case",label:" "},{type:"checkbox",name:"words",text:"Whole words",label:" "}]}}).renderTo().reflow()}function a(e){var t=e.getAttribute("data-mce-index");return"number"==typeof t?""+t:t}function r(n){var a,r;return r=t.dom.create("span",{"data-mce-bogus":1}),r.className="mce-match-marker",a=t.getBody(),c.done(!1),e(n,a,r,!1,t.schema)}function i(e){var t=e.parentNode;e.firstChild&&t.insertBefore(e.firstChild,e),e.parentNode.removeChild(e)}function d(e){var n,r=[];if(n=tinymce.toArray(t.getBody().getElementsByTagName("span")),n.length)for(var i=0;i<n.length;i++){var d=a(n[i]);null!==d&&d.length&&d===e.toString()&&r.push(n[i])}return r}function o(e){var n=s,a=t.dom;e=e!==!1,e?n++:n--,a.removeClass(d(s),"mce-match-marker-selected");var r=d(n);return r.length?(a.addClass(d(n),"mce-match-marker-selected"),t.selection.scrollIntoView(r[0]),n):-1}function l(e){e.parentNode.removeChild(e)}var c=this,s=-1;c.init=function(e){e.addMenuItem("searchreplace",{text:"Find and replace",shortcut:"Ctrl+F",onclick:n,separator:"before",context:"edit"}),e.addButton("searchreplace",{tooltip:"Find and replace",shortcut:"Ctrl+F",onclick:n}),e.addCommand("SearchReplace",n),e.shortcuts.add("Ctrl+F","",n)},c.find=function(e,t,n){e=e.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&"),e=n?"\\b"+e+"\\b":e;var a=r(new RegExp(e,t?"g":"gi"));return a&&(s=-1,s=o(!0)),a},c.next=function(){var e=o(!0);-1!==e&&(s=e)},c.prev=function(){var e=o(!1);-1!==e&&(s=e)},c.replace=function(e,n,r){var o,f,u,h,m,g,p=s;for(n=n!==!1,u=t.getBody(),f=tinymce.toArray(u.getElementsByTagName("span")),o=0;o<f.length;o++){var x=a(f[o]);if(null!==x&&x.length)if(h=m=parseInt(x,10),r||h===s){for(e.length?(f[o].firstChild.nodeValue=e,i(f[o])):l(f[o]);f[++o];)if(h=a(f[o]),null!==x&&x.length){if(h!==m){o--;break}l(f[o])}n&&p--}else m>s&&f[o].setAttribute("data-mce-index",m-1)}return t.undoManager.add(),s=p,n?(g=d(p+1).length>0,c.next()):(g=d(p-1).length>0,c.prev()),!r&&g},c.done=function(e){var n,r,d,o;for(r=tinymce.toArray(t.getBody().getElementsByTagName("span")),n=0;n<r.length;n++){var l=a(r[n]);null!==l&&l.length&&(l===s.toString()&&(d||(d=r[n].firstChild),o=r[n].firstChild),i(r[n]))}if(d&&o){var c=t.dom.createRng();return c.setStart(d,0),c.setEnd(o,o.data.length),e!==!1&&t.selection.setRng(c),c}}}tinymce.PluginManager.add("searchreplace",t)}();!function(e,t){"use strict";function n(e,t){for(var n,o=[],r=0;r<e.length;++r){if(n=l[e[r]]||i(e[r]),!n)throw"module definition dependecy not found: "+e[r];o.push(n)}t.apply(null,o)}function o(e,o,r){if("string"!=typeof e)throw"invalid module definition, module id must be defined and be a string";if(o===t)throw"invalid module definition, dependencies must be specified";if(r===t)throw"invalid module definition, definition function must be specified";n(o,function(){l[e]=r.apply(null,arguments)})}function r(e){return!!l[e]}function i(t){for(var n=e,o=t.split(/[.\/]/),r=0;r<o.length;++r){if(!n[o[r]])return;n=n[o[r]]}return n}function a(n){for(var o=0;o<n.length;o++){for(var r=e,i=n[o],a=i.split(/[.\/]/),s=0;s<a.length-1;++s)r[a[s]]===t&&(r[a[s]]={}),r=r[a[s]];r[a[a.length-1]]=l[i]}}var l={},s="tinymce/spellcheckerplugin/DomTextMatcher",c="tinymce/spellcheckerplugin/Plugin",d="tinymce/PluginManager",u="tinymce/util/Tools",f="tinymce/ui/Menu",m="tinymce/dom/DOMUtils",p="tinymce/util/XHR",g="tinymce/util/URI",h="tinymce/util/JSON";o(s,[],function(){return function(e,t){function n(e,t){if(!e[0])throw"findAndReplaceDOMText cannot handle zero-length matches";return{start:e.index,end:e.index+e[0].length,text:e[0],data:t}}function o(e){var t;if(3===e.nodeType)return e.data;if(P[e.nodeName]&&!S[e.nodeName])return"";if(t="",(S[e.nodeName]||N[e.nodeName])&&(t+="\n"),e=e.firstChild)do t+=o(e);while(e=e.nextSibling);return t}function r(e,t,n){var o,r,i,a,l=[],s=0,c=e,d,u=0;t=t.slice(0),t.sort(function(e,t){return e.start-t.start}),d=t.shift();e:for(;;){if((S[c.nodeName]||N[c.nodeName])&&s++,3===c.nodeType&&(!r&&c.length+s>=d.end?(r=c,a=d.end-s):o&&l.push(c),!o&&c.length+s>d.start&&(o=c,i=d.start-s),s+=c.length),o&&r){if(c=n({startNode:o,startNodeIndex:i,endNode:r,endNodeIndex:a,innerNodes:l,match:d.text,matchIndex:u}),s-=r.length-a,o=null,r=null,l=[],d=t.shift(),u++,!d)break}else{if((!P[c.nodeName]||S[c.nodeName])&&c.firstChild){c=c.firstChild;continue}if(c.nextSibling){c=c.nextSibling;continue}}for(;;){if(c.nextSibling){c=c.nextSibling;break}if(c.parentNode===e)break e;c=c.parentNode}}}function i(e){function t(t,n){var o=w[n];o.stencil||(o.stencil=e(o));var r=o.stencil.cloneNode(!1);return r.setAttribute("data-mce-index",n),t&&r.appendChild(k.doc.createTextNode(t)),r}return function(e){var n,o,r,i=e.startNode,a=e.endNode,l=e.matchIndex,s=k.doc;if(i===a){var c=i;r=c.parentNode,e.startNodeIndex>0&&(n=s.createTextNode(c.data.substring(0,e.startNodeIndex)),r.insertBefore(n,c));var d=t(e.match,l);return r.insertBefore(d,c),e.endNodeIndex<c.length&&(o=s.createTextNode(c.data.substring(e.endNodeIndex)),r.insertBefore(o,c)),c.parentNode.removeChild(c),d}n=s.createTextNode(i.data.substring(0,e.startNodeIndex)),o=s.createTextNode(a.data.substring(e.endNodeIndex));for(var u=t(i.data.substring(e.startNodeIndex),l),f=[],m=0,p=e.innerNodes.length;p>m;++m){var g=e.innerNodes[m],h=t(g.data,l);g.parentNode.replaceChild(h,g),f.push(h)}var v=t(a.data.substring(0,e.endNodeIndex),l);return r=i.parentNode,r.insertBefore(n,i),r.insertBefore(u,i),r.removeChild(i),r=a.parentNode,r.insertBefore(v,a),r.insertBefore(o,a),r.removeChild(a),v}}function a(e){var t=e.parentNode;t.insertBefore(e.firstChild,e),e.parentNode.removeChild(e)}function l(t){var n=e.getElementsByTagName("*"),o=[];t="number"==typeof t?""+t:null;for(var r=0;r<n.length;r++){var i=n[r],a=i.getAttribute("data-mce-index");null!==a&&a.length&&(a===t||null===t)&&o.push(i)}return o}function s(e){for(var t=w.length;t--;)if(w[t]===e)return t;return-1}function c(e){var t=[];return d(function(n,o){e(n,o)&&t.push(n)}),w=t,this}function d(e){for(var t=0,n=w.length;n>t&&e(w[t],t)!==!1;t++);return this}function u(t){return w.length&&r(e,w,i(t)),this}function f(e,t){if(C&&e.global)for(;x=e.exec(C);)w.push(n(x,t));return this}function m(e){var t,n=l(e?s(e):null);for(t=n.length;t--;)a(n[t]);return this}function p(e){return w[e.getAttribute("data-mce-index")]}function g(e){return l(s(e))[0]}function h(e,t,n){return w.push({start:e,end:e+t,text:C.substr(e,t),data:n}),this}function v(e){var n=l(s(e)),o=t.dom.createRng();return o.setStartBefore(n[0]),o.setEndAfter(n[n.length-1]),o}function b(e,n){var o=v(e);return o.deleteContents(),n.length>0&&o.insertNode(t.dom.doc.createTextNode(n)),o}function y(){return w.splice(0,w.length),m(),this}var x,w=[],C,k=t.dom,S,P,N;return S=t.schema.getBlockElements(),P=t.schema.getWhiteSpaceElements(),N=t.schema.getShortEndedElements(),C=o(e),{text:C,matches:w,each:d,filter:c,reset:y,matchFromElement:p,elementFromMatch:g,find:f,add:h,wrap:u,unwrap:m,replace:b,rangeFromMatch:v,indexOf:s}}}),o(c,[s,d,u,f,m,p,g,h],function(e,t,n,o,r,i,a,l){t.add("spellchecker",function(t,s){function c(){return C.textMatcher||(C.textMatcher=new e(t.getBody(),t)),C.textMatcher}function d(e,t){var o=[];return n.each(t,function(e){o.push({selectable:!0,text:e.name,data:e.value})}),o}function u(e){for(var t in e)return!1;return!0}function f(e,i){var a=[],l=k[e];n.each(l,function(e){a.push({text:e,onclick:function(){t.insertContent(t.dom.encode(e)),t.dom.remove(i),g()}})}),a.push.apply(a,[{text:"-"},{text:"Ignore",onclick:function(){h(e,i)}},{text:"Ignore all",onclick:function(){h(e,i,!0)}},{text:"Finish",onclick:v}]),P=new o({items:a,context:"contextmenu",onautohide:function(e){-1!=e.target.className.indexOf("spellchecker")&&e.preventDefault()},onhide:function(){P.remove(),P=null}}),P.renderTo(document.body);var s=r.DOM.getPos(t.getContentAreaContainer()),c=t.dom.getPos(i[0]),d=t.dom.getRoot();"BODY"==d.nodeName?(c.x-=d.ownerDocument.documentElement.scrollLeft||d.scrollLeft,c.y-=d.ownerDocument.documentElement.scrollTop||d.scrollTop):(c.x-=d.scrollLeft,c.y-=d.scrollTop),s.x+=c.x,s.y+=c.y,P.moveTo(s.x,s.y+i[0].offsetHeight)}function m(){return t.getParam("spellchecker_wordchar_pattern")||new RegExp('[^\\s!"#$%&()*+,-./:;<=>?@[\\]^_{|}`\xa7\xa9\xab\xae\xb1\xb6\xb7\xb8\xbb\xbc\xbd\xbe\xbf\xd7\xf7\xa4\u201d\u201c\u201e]+',"g")}function p(){function e(e){return t.setProgressState(!1),u(e)?(t.windowManager.alert("No misspellings found"),void(S=!1)):(k=e,c().find(m()).filter(function(t){return!!e[t.text]}).wrap(function(e){return t.dom.create("span",{"class":"mce-spellchecker-word","data-mce-bogus":1,"data-mce-word":e.text})}),void t.fire("SpellcheckStart"))}function n(e){t.windowManager.alert(e),t.setProgressState(!1),v()}function o(e,t,o){i.send({url:new a(s).toAbsolute(N.spellchecker_rpc_url),type:"post",content_type:"application/x-www-form-urlencoded",data:"text="+encodeURIComponent(t)+"&lang="+N.spellchecker_language,success:function(e){e=l.parse(e),e?e.error?n(e.error):o(e.words):n("Sever response wasn't proper JSON.")},error:function(e,t){n("Spellchecker request error: "+t.status)}})}if(S)return void v();v(),S=!0,t.setProgressState(!0);var r=N.spellchecker_callback||o;r.call(C,"spellcheck",c().text,e,n),t.focus()}function g(){t.dom.select("span.mce-spellchecker-word").length||v()}function h(e,o,r){t.selection.collapse(),r?n.each(t.dom.select("span.mce-spellchecker-word"),function(n){n.getAttribute("data-mce-word")==e&&t.dom.remove(n,!0)}):t.dom.remove(o,!0),g()}function v(){c().reset(),C.textMatcher=null,S&&(S=!1,t.fire("SpellcheckEnd"))}function b(e){var t=e.getAttribute("data-mce-index");return"number"==typeof t?""+t:t}function y(e){var o,r=[];if(o=n.toArray(t.getBody().getElementsByTagName("span")),o.length)for(var i=0;i<o.length;i++){var a=b(o[i]);null!==a&&a.length&&a===e.toString()&&r.push(o[i])}return r}function x(e){var t=N.spellchecker_language;e.control.items().each(function(e){e.active(e.settings.data===t)})}var w,C=this,k,S,P,N=t.settings,R=N.spellchecker_languages||"English=en,Danish=da,Dutch=nl,Finnish=fi,French=fr_FR,German=de,Italian=it,Polish=pl,Portuguese=pt_BR,Spanish=es,Swedish=sv";w=d("Language",n.map(R.split(","),function(e){var t=e.split("=");return{name:t[0],value:t[1]}})),t.on("click",function(e){var n=e.target;if("mce-spellchecker-word"==n.className){e.preventDefault();var o=y(b(n));if(o.length>0){var r=t.dom.createRng();r.setStartBefore(o[0]),r.setEndAfter(o[o.length-1]),t.selection.setRng(r),f(n.getAttribute("data-mce-word"),o)}}}),t.addMenuItem("spellchecker",{text:"Spellcheck",context:"tools",onclick:p,selectable:!0,onPostRender:function(){var e=this;t.on("SpellcheckStart SpellcheckEnd",function(){e.active(S)})}});var T={tooltip:"Spellcheck",onclick:p,onPostRender:function(){var e=this;t.on("SpellcheckStart SpellcheckEnd",function(){e.active(S)})}};w.length>1&&(T.type="splitbutton",T.menu=w,T.onshow=x,T.onselect=function(e){N.spellchecker_language=e.control.settings.data}),t.addButton("spellchecker",T),t.addCommand("mceSpellCheck",p),t.on("remove",function(){P&&(P.remove(),P=null)}),t.on("change",g),this.getTextMatcher=c,this.getWordCharPattern=m,this.getLanguage=function(){return N.spellchecker_language},N.spellchecker_language=N.spellchecker_language||N.language||"en"})}),a([s,c])}(this);tinymce.PluginManager.add("tabfocus",function(e){function n(e){9!==e.keyCode||e.ctrlKey||e.altKey||e.metaKey||e.preventDefault()}function t(n){function t(n){function t(e){return"BODY"===e.nodeName||"hidden"!=e.type&&"none"!=e.style.display&&"hidden"!=e.style.visibility&&t(e.parentNode)}function r(e){return e.tabIndex||"INPUT"==e.nodeName||"TEXTAREA"==e.nodeName}function c(e){return!r(e)&&"-1"!=e.getAttribute("tabindex")&&t(e)}if(u=i.select(":input:enabled,*[tabindex]:not(iframe)"),o(u,function(n,t){return n.id==e.id?(a=t,!1):void 0}),n>0){for(d=a+1;d<u.length;d++)if(c(u[d]))return u[d]}else for(d=a-1;d>=0;d--)if(c(u[d]))return u[d];return null}var a,u,c,d;if(!(9!==n.keyCode||n.ctrlKey||n.altKey||n.metaKey)&&(c=r(e.getParam("tab_focus",e.getParam("tabfocus_elements",":prev,:next"))),1==c.length&&(c[1]=c[0],c[0]=":prev"),u=n.shiftKey?":prev"==c[0]?t(-1):i.get(c[0]):":next"==c[1]?t(1):i.get(c[1]))){var y=tinymce.get(u.id||u.name);u.id&&y?y.focus():window.setTimeout(function(){tinymce.Env.webkit||window.focus(),u.focus()},10),n.preventDefault()}}var i=tinymce.DOM,o=tinymce.each,r=tinymce.explode;e.on("init",function(){e.inline&&tinymce.DOM.setAttrib(e.getBody(),"tabIndex",null)}),e.on("keyup",n),tinymce.Env.gecko?e.on("keypress keydown",t):e.on("keydown",t)});!function(e,t){"use strict";function n(e,t){for(var n,o=[],i=0;i<e.length;++i){if(n=l[e[i]]||r(e[i]),!n)throw"module definition dependecy not found: "+e[i];o.push(n)}t.apply(null,o)}function o(e,o,i){if("string"!=typeof e)throw"invalid module definition, module id must be defined and be a string";if(o===t)throw"invalid module definition, dependencies must be specified";if(i===t)throw"invalid module definition, definition function must be specified";n(o,function(){l[e]=i.apply(null,arguments)})}function i(e){return!!l[e]}function r(t){for(var n=e,o=t.split(/[.\/]/),i=0;i<o.length;++i){if(!n[o[i]])return;n=n[o[i]]}return n}function a(n){for(var o=0;o<n.length;o++){for(var i=e,r=n[o],a=r.split(/[.\/]/),s=0;s<a.length-1;++s)i[a[s]]===t&&(i[a[s]]={}),i=i[a[s]];i[a[a.length-1]]=l[r]}}var l={},s="tinymce/tableplugin/TableGrid",c="tinymce/util/Tools",d="tinymce/Env",u="tinymce/tableplugin/Quirks",f="tinymce/util/VK",m="tinymce/tableplugin/CellSelection",p="tinymce/dom/TreeWalker",g="tinymce/tableplugin/Plugin",h="tinymce/PluginManager";o(s,[c,d],function(e,n){function o(e,t){return parseInt(e.getAttribute(t)||1,10)}var i=e.each;return function(r,a){function l(){var e=0;A=[],i(["thead","tbody","tfoot"],function(t){var n=I.select("> "+t+" tr",a);i(n,function(n,r){r+=e,i(I.select("> td, > th",n),function(e,n){var i,a,l,s;if(A[r])for(;A[r][n];)n++;for(l=o(e,"rowspan"),s=o(e,"colspan"),a=r;r+l>a;a++)for(A[a]||(A[a]=[]),i=n;n+s>i;i++)A[a][i]={part:t,real:a==r&&i==n,elm:e,rowspan:l,colspan:s}})}),e+=n.length})}function s(e,t){return e=e.cloneNode(t),e.removeAttribute("id"),e}function c(e,t){var n;return n=A[t],n?n[e]:void 0}function d(e,t,n){e&&(n=parseInt(n,10),1===n?e.removeAttribute(t,1):e.setAttribute(t,n,1))}function u(e){return e&&(I.hasClass(e.elm,"mce-item-selected")||e==M)}function f(){var e=[];return i(a.rows,function(t){i(t.cells,function(n){return I.hasClass(n,"mce-item-selected")||M&&n==M.elm?(e.push(t),!1):void 0})}),e}function m(){var e=I.createRng();e.setStartAfter(a),e.setEndAfter(a),E.setRng(e),I.remove(a)}function p(t){var o,a={};return r.settings.table_clone_elements!==!1&&(a=e.makeMap((r.settings.table_clone_elements||"strong em b i span font h1 h2 h3 h4 h5 h6 p div").toUpperCase(),/[ ,]/)),e.walk(t,function(e){var r;return 3==e.nodeType?(i(I.getParents(e.parentNode,null,t).reverse(),function(e){a[e.nodeName]&&(e=s(e,!1),o?r&&r.appendChild(e):o=r=e,r=e)}),r&&(r.innerHTML=n.ie?"&nbsp;":'<br data-mce-bogus="1" />'),!1):void 0},"childNodes"),t=s(t,!1),d(t,"rowSpan",1),d(t,"colSpan",1),o?t.appendChild(o):n.ie||(t.innerHTML='<br data-mce-bogus="1" />'),t}function g(){var e=I.createRng(),t;return i(I.select("tr",a),function(e){0===e.cells.length&&I.remove(e)}),0===I.select("tr",a).length?(e.setStartBefore(a),e.setEndBefore(a),E.setRng(e),void I.remove(a)):(i(I.select("thead,tbody,tfoot",a),function(e){0===e.rows.length&&I.remove(e)}),l(),void(B&&(t=A[Math.min(A.length-1,B.y)],t&&(E.select(t[Math.min(t.length-1,B.x)].elm,!0),E.collapse(!0)))))}function h(e,t,n,o){var i,r,a,l,s;for(i=A[t][e].elm.parentNode,a=1;n>=a;a++)if(i=I.getNext(i,"tr")){for(r=e;r>=0;r--)if(s=A[t+a][r].elm,s.parentNode==i){for(l=1;o>=l;l++)I.insertAfter(p(s),s);break}if(-1==r)for(l=1;o>=l;l++)i.insertBefore(p(i.cells[0]),i.cells[0])}}function v(){i(A,function(e,t){i(e,function(e,n){var i,r,a;if(u(e)&&(e=e.elm,i=o(e,"colspan"),r=o(e,"rowspan"),i>1||r>1)){for(d(e,"rowSpan",1),d(e,"colSpan",1),a=0;i-1>a;a++)I.insertAfter(p(e),e);h(n,t,r-1,i)}})})}function b(t,n,o){var r,a,s,f,m,p,h,b,y,w,x;if(t?(r=T(t),a=r.x,s=r.y,f=a+(n-1),m=s+(o-1)):(B=D=null,i(A,function(e,t){i(e,function(e,n){u(e)&&(B||(B={x:n,y:t}),D={x:n,y:t})})}),B&&(a=B.x,s=B.y,f=D.x,m=D.y)),b=c(a,s),y=c(f,m),b&&y&&b.part==y.part){for(v(),l(),b=c(a,s).elm,d(b,"colSpan",f-a+1),d(b,"rowSpan",m-s+1),h=s;m>=h;h++)for(p=a;f>=p;p++)A[h]&&A[h][p]&&(t=A[h][p].elm,t!=b&&(w=e.grep(t.childNodes),i(w,function(e){b.appendChild(e)}),w.length&&(w=e.grep(b.childNodes),x=0,i(w,function(e){"BR"==e.nodeName&&I.getAttrib(e,"data-mce-bogus")&&x++<w.length-1&&b.removeChild(e)})),I.remove(t)));g()}}function y(e){var n,r,a,l,c,f,m,g,h;if(i(A,function(t,o){return i(t,function(t){return u(t)&&(t=t.elm,c=t.parentNode,f=s(c,!1),n=o,e)?!1:void 0}),e?!n:void 0}),n!==t){for(l=0;l<A[0].length;l++)if(A[n][l]&&(r=A[n][l].elm,r!=a)){if(e){if(n>0&&A[n-1][l]&&(g=A[n-1][l].elm,h=o(g,"rowSpan"),h>1)){d(g,"rowSpan",h+1);continue}}else if(h=o(r,"rowspan"),h>1){d(r,"rowSpan",h+1);continue}m=p(r),d(m,"colSpan",r.colSpan),f.appendChild(m),a=r}f.hasChildNodes()&&(e?c.parentNode.insertBefore(f,c):I.insertAfter(f,c))}}function w(e){var t,n;i(A,function(n){return i(n,function(n,o){return u(n)&&(t=o,e)?!1:void 0}),e?!t:void 0}),i(A,function(i,r){var a,l,s;i[t]&&(a=i[t].elm,a!=n&&(s=o(a,"colspan"),l=o(a,"rowspan"),1==s?e?(a.parentNode.insertBefore(p(a),a),h(t,r,l-1,s)):(I.insertAfter(p(a),a),h(t,r,l-1,s)):d(a,"colSpan",a.colSpan+1),n=a))})}function x(){var t=[];i(A,function(n){i(n,function(n,r){u(n)&&-1===e.inArray(t,r)&&(i(A,function(e){var t=e[r].elm,n;n=o(t,"colSpan"),n>1?d(t,"colSpan",n-1):I.remove(t)}),t.push(r))})}),g()}function C(){function e(e){var t,n,r;t=I.getNext(e,"tr"),i(e.cells,function(e){var t=o(e,"rowSpan");t>1&&(d(e,"rowSpan",t-1),n=T(e),h(n.x,n.y,1,1))}),n=T(e.cells[0]),i(A[n.y],function(e){var t;e=e.elm,e!=r&&(t=o(e,"rowSpan"),1>=t?I.remove(e):d(e,"rowSpan",t-1),r=e)})}var t;t=f(),i(t.reverse(),function(t){e(t)}),g()}function P(){var e=f();return I.remove(e),g(),e}function S(){var e=f();return i(e,function(t,n){e[n]=s(t,!0)}),e}function R(e,t){var n=f(),o=n[t?0:n.length-1],r=o.cells.length;e&&(i(A,function(e){var t;return r=0,i(e,function(e){e.real&&(r+=e.colspan),e.elm.parentNode==o&&(t=1)}),t?!1:void 0}),t||e.reverse(),i(e,function(e){var n,i=e.cells.length,a;for(n=0;i>n;n++)a=e.cells[n],d(a,"colSpan",1),d(a,"rowSpan",1);for(n=i;r>n;n++)e.appendChild(p(e.cells[i-1]));for(n=r;i>n;n++)I.remove(e.cells[n]);t?o.parentNode.insertBefore(e,o):I.insertAfter(e,o)}),I.removeClass(I.select("td.mce-item-selected,th.mce-item-selected"),"mce-item-selected"))}function T(e){var t;return i(A,function(n,o){return i(n,function(n,i){return n.elm==e?(t={x:i,y:o},!1):void 0}),!t}),t}function k(e){B=T(e)}function N(){var e,t;return e=t=0,i(A,function(n,o){i(n,function(n,i){var r,a;u(n)&&(n=A[o][i],i>e&&(e=i),o>t&&(t=o),n.real&&(r=n.colspan-1,a=n.rowspan-1,r&&i+r>e&&(e=i+r),a&&o+a>t&&(t=o+a)))})}),{x:e,y:t}}function _(e){var t,n,o,i,r,a,l,s,c,d;if(D=T(e),B&&D){for(t=Math.min(B.x,D.x),n=Math.min(B.y,D.y),o=Math.max(B.x,D.x),i=Math.max(B.y,D.y),r=o,a=i,d=n;a>=d;d++)e=A[d][t],e.real||t-(e.colspan-1)<t&&(t-=e.colspan-1);for(c=t;r>=c;c++)e=A[n][c],e.real||n-(e.rowspan-1)<n&&(n-=e.rowspan-1);for(d=n;i>=d;d++)for(c=t;o>=c;c++)e=A[d][c],e.real&&(l=e.colspan-1,s=e.rowspan-1,l&&c+l>r&&(r=c+l),s&&d+s>a&&(a=d+s));for(I.removeClass(I.select("td.mce-item-selected,th.mce-item-selected"),"mce-item-selected"),d=n;a>=d;d++)for(c=t;r>=c;c++)A[d][c]&&I.addClass(A[d][c].elm,"mce-item-selected")}}var A,B,D,M,E=r.selection,I=E.dom;a=a||I.getParent(E.getStart(),"table"),l(),M=I.getParent(E.getStart(),"th,td"),M&&(B=T(M),D=N(),M=c(B.x,B.y)),e.extend(this,{deleteTable:m,split:v,merge:b,insertRow:y,insertCol:w,deleteCols:x,deleteRows:C,cutRows:P,copyRows:S,pasteRows:R,getPos:T,setStartCell:k,setEndCell:_})}}),o(u,[f,d,c],function(e,t,n){function o(e,t){return parseInt(e.getAttribute(t)||1,10)}var i=n.each;return function(n){function r(){function t(t){function r(e,o){var i=e?"previousSibling":"nextSibling",r=n.dom.getParent(o,"tr"),l=r[i];if(l)return h(n,o,l,e),t.preventDefault(),!0;var d=n.dom.getParent(r,"table"),u=r.parentNode,f=u.nodeName.toLowerCase();if("tbody"===f||f===(e?"tfoot":"thead")){var m=a(e,d,u,"tbody");if(null!==m)return s(e,m,o)}return c(e,r,i,d)}function a(e,t,o,i){var r=n.dom.select(">"+i,t),a=r.indexOf(o);if(e&&0===a||!e&&a===r.length-1)return l(e,t);if(-1===a){var s="thead"===o.tagName.toLowerCase()?0:r.length-1;return r[s]}return r[a+(e?-1:1)]}function l(e,t){var o=e?"thead":"tfoot",i=n.dom.select(">"+o,t);return 0!==i.length?i[0]:null}function s(e,o,i){var r=d(o,e);return r&&h(n,i,r,e),t.preventDefault(),!0}function c(e,o,i,a){var l=a[i];if(l)return u(l),!0;var s=n.dom.getParent(a,"td,th");if(s)return r(e,s,t);var c=d(o,!e);return u(c),t.preventDefault(),!1}function d(e,t){var o=e&&e[t?"lastChild":"firstChild"];return o&&"BR"===o.nodeName?n.dom.getParent(o,"td,th"):o}function u(e){n.selection.setCursorLocation(e,0)}function f(){return y==e.UP||y==e.DOWN}function m(e){var t=e.selection.getNode(),n=e.dom.getParent(t,"tr");return null!==n}function p(e){for(var t=0,n=e;n.previousSibling;)n=n.previousSibling,t+=o(n,"colspan");return t}function g(e,t){var n=0,r=0;return i(e.children,function(e,i){return n+=o(e,"colspan"),r=i,n>t?!1:void 0}),r}function h(e,t,o,i){var r=p(n.dom.getParent(t,"td,th")),a=g(o,r),l=o.childNodes[a],s=d(l,i);u(s||l)}function v(e){var t=n.selection.getNode(),o=n.dom.getParent(t,"td,th"),i=n.dom.getParent(e,"td,th");return o&&o!==i&&b(o,i)}function b(e,t){return n.dom.getParent(e,"TABLE")===n.dom.getParent(t,"TABLE")}var y=t.keyCode;if(f()&&m(n)){var w=n.selection.getNode();setTimeout(function(){v(w)&&r(!t.shiftKey&&y===e.UP,w,t)},0)}}n.on("KeyDown",function(e){t(e)})}function a(){function e(e,t){var n=t.ownerDocument,o=n.createRange(),i;return o.setStartBefore(t),o.setEnd(e.endContainer,e.endOffset),i=n.createElement("body"),i.appendChild(o.cloneContents()),0===i.innerHTML.replace(/<(br|img|object|embed|input|textarea)[^>]*>/gi,"-").replace(/<[^>]+>/g,"").length}n.on("KeyDown",function(t){var o,i,r=n.dom;(37==t.keyCode||38==t.keyCode)&&(o=n.selection.getRng(),i=r.getParent(o.startContainer,"table"),i&&n.getBody().firstChild==i&&e(o,i)&&(o=r.createRng(),o.setStartBefore(i),o.setEndBefore(i),n.selection.setRng(o),t.preventDefault()))})}function l(){n.on("KeyDown SetContent VisualAid",function(){var e;for(e=n.getBody().lastChild;e;e=e.previousSibling)if(3==e.nodeType){if(e.nodeValue.length>0)break}else if(1==e.nodeType&&!e.getAttribute("data-mce-bogus"))break;e&&"TABLE"==e.nodeName&&(n.settings.forced_root_block?n.dom.add(n.getBody(),n.settings.forced_root_block,n.settings.forced_root_block_attrs,t.ie&&t.ie<11?"&nbsp;":'<br data-mce-bogus="1" />'):n.dom.add(n.getBody(),"br",{"data-mce-bogus":"1"}))}),n.on("PreProcess",function(e){var t=e.node.lastChild;t&&("BR"==t.nodeName||1==t.childNodes.length&&("BR"==t.firstChild.nodeName||"\xa0"==t.firstChild.nodeValue))&&t.previousSibling&&"TABLE"==t.previousSibling.nodeName&&n.dom.remove(t)})}function s(){function e(e,t,n,o){var i=3,r=e.dom.getParent(t.startContainer,"TABLE"),a,l,s;return r&&(a=r.parentNode),l=t.startContainer.nodeType==i&&0===t.startOffset&&0===t.endOffset&&o&&("TR"==n.nodeName||n==a),s=("TD"==n.nodeName||"TH"==n.nodeName)&&!o,l||s}function t(){var t=n.selection.getRng(),o=n.selection.getNode(),i=n.dom.getParent(t.startContainer,"TD,TH");if(e(n,t,o,i)){i||(i=o);for(var r=i.lastChild;r.lastChild;)r=r.lastChild;t.setEnd(r,r.nodeValue.length),n.selection.setRng(t)}}n.on("KeyDown",function(){t()}),n.on("MouseDown",function(e){2!=e.button&&t()})}function c(){n.on("keydown",function(t){if((t.keyCode==e.DELETE||t.keyCode==e.BACKSPACE)&&!t.isDefaultPrevented()){var o=n.dom.getParent(n.selection.getStart(),"table");if(o){for(var i=n.dom.select("td,th",o),r=i.length;r--;)if(!n.dom.hasClass(i[r],"mce-item-selected"))return;t.preventDefault(),n.execCommand("mceTableDelete")}}})}c(),t.webkit&&(r(),s()),t.gecko&&(a(),l()),t.ie>10&&(a(),l())}}),o(m,[s,p,c],function(e,t,n){return function(o){function i(){o.getBody().style.webkitUserSelect="",d&&(o.dom.removeClass(o.dom.select("td.mce-item-selected,th.mce-item-selected"),"mce-item-selected"),d=!1)}function r(t){var n,i,r=t.target;if(s&&(l||r!=s)&&("TD"==r.nodeName||"TH"==r.nodeName)){i=a.getParent(r,"table"),i==c&&(l||(l=new e(o,i),l.setStartCell(s),o.getBody().style.webkitUserSelect="none"),l.setEndCell(r),d=!0),n=o.selection.getSel();try{n.removeAllRanges?n.removeAllRanges():n.empty()}catch(u){}t.preventDefault()}}var a=o.dom,l,s,c,d=!0;return o.on("MouseDown",function(e){2!=e.button&&(i(),s=a.getParent(e.target,"td,th"),c=a.getParent(s,"table"))}),o.on("mouseover",r),o.on("remove",function(){a.unbind(o.getDoc(),"mouseover",r)}),o.on("MouseUp",function(){function e(e,o){var r=new t(e,e);do{if(3==e.nodeType&&0!==n.trim(e.nodeValue).length)return void(o?i.setStart(e,0):i.setEnd(e,e.nodeValue.length));if("BR"==e.nodeName)return void(o?i.setStartBefore(e):i.setEndBefore(e))}while(e=o?r.next():r.prev())}var i,r=o.selection,d,u,f,m,p;if(s){if(l&&(o.getBody().style.webkitUserSelect=""),d=a.select("td.mce-item-selected,th.mce-item-selected"),d.length>0){i=a.createRng(),f=d[0],p=d[d.length-1],i.setStartBefore(f),i.setEndAfter(f),e(f,1),u=new t(f,a.getParent(d[0],"table"));do if("TD"==f.nodeName||"TH"==f.nodeName){if(!a.hasClass(f,"mce-item-selected"))break;m=f}while(f=u.next());e(m),r.setRng(i)}o.nodeChanged(),s=l=c=null}}),o.on("KeyUp Drop",function(){i(),s=l=c=null}),{clear:i}}}),o(g,[s,u,m,c,p,d,h],function(e,t,n,o,i,r,a){function l(o){function i(e){return e?e.replace(/px$/,""):""}function a(e){return/^[0-9]+$/.test(e)&&(e+="px"),e}function l(e){s("left center right".split(" "),function(t){o.formatter.remove("align"+t,{},e)})}function c(e){s("top middle bottom".split(" "),function(t){o.formatter.remove("valign"+t,{},e)})}function d(){var e=o.dom,t,n,c,d;t=e.getParent(o.selection.getStart(),"table"),d={width:i(e.getStyle(t,"width")||e.getAttrib(t,"width")),height:i(e.getStyle(t,"height")||e.getAttrib(t,"height")),cellspacing:t?e.getAttrib(t,"cellspacing"):"",cellpadding:t?e.getAttrib(t,"cellpadding"):"",border:t?e.getAttrib(t,"border"):"",caption:!!e.select("caption",t)[0]},s("left center right".split(" "),function(e){o.formatter.matchNode(t,"align"+e)&&(d.align=e)}),t||(n={label:"Cols",name:"cols"},c={label:"Rows",name:"rows"}),o.windowManager.open({title:"Table properties",items:{type:"form",layout:"grid",columns:2,data:d,defaults:{type:"textbox",maxWidth:50},items:[n,c,{label:"Width",name:"width"},{label:"Height",name:"height"},{label:"Cell spacing",name:"cellspacing"},{label:"Cell padding",name:"cellpadding"},{label:"Border",name:"border"},{label:"Caption",name:"caption",type:"checkbox"},{label:"Alignment",minWidth:90,name:"align",type:"listbox",text:"None",maxWidth:null,values:[{text:"None",value:""},{text:"Left",value:"left"},{text:"Center",value:"center"},{text:"Right",value:"right"}]}]},onsubmit:function(){var n=this.toJSON(),i;o.undoManager.transact(function(){t||(t=g(n.cols||1,n.rows||1)),o.dom.setAttribs(t,{cellspacing:n.cellspacing,cellpadding:n.cellpadding,border:n.border}),o.dom.setStyles(t,{width:a(n.width),height:a(n.height)}),i=e.select("caption",t)[0],i&&!n.caption&&e.remove(i),!i&&n.caption&&(i=e.create("caption"),i.innerHTML=r.ie?"\xa0":'<br data-mce-bogus="1"/>',t.insertBefore(i,t.firstChild)),l(t),n.align&&o.formatter.apply("align"+n.align,{},t),o.focus(),o.addVisual()})}})}function u(e,t){o.windowManager.open({title:"Merge cells",body:[{label:"Cols",name:"cols",type:"textbox",size:10},{label:"Rows",name:"rows",type:"textbox",size:10}],onsubmit:function(){var n=this.toJSON();o.undoManager.transact(function(){e.merge(t,n.cols,n.rows)})}})}function f(){var e=o.dom,t,n,r=[];r=o.dom.select("td.mce-item-selected,th.mce-item-selected"),t=o.dom.getParent(o.selection.getStart(),"td,th"),!r.length&&t&&r.push(t),t=t||r[0],t&&(n={width:i(e.getStyle(t,"width")||e.getAttrib(t,"width")),height:i(e.getStyle(t,"height")||e.getAttrib(t,"height")),scope:e.getAttrib(t,"scope")},n.type=t.nodeName.toLowerCase(),s("left center right".split(" "),function(e){o.formatter.matchNode(t,"align"+e)&&(n.align=e)}),s("top middle bottom".split(" "),function(e){o.formatter.matchNode(t,"valign"+e)&&(n.valign=e)}),o.windowManager.open({title:"Cell properties",items:{type:"form",data:n,layout:"grid",columns:2,defaults:{type:"textbox",maxWidth:50},items:[{label:"Width",name:"width"},{label:"Height",name:"height"},{label:"Cell type",name:"type",type:"listbox",text:"None",minWidth:90,maxWidth:null,values:[{text:"Cell",value:"td"},{text:"Header cell",value:"th"}]},{label:"Scope",name:"scope",type:"listbox",text:"None",minWidth:90,maxWidth:null,values:[{text:"None",value:""},{text:"Row",value:"row"},{text:"Column",value:"col"},{text:"Row group",value:"rowgroup"},{text:"Column group",value:"colgroup"}]},{label:"H Align",name:"align",type:"listbox",text:"None",minWidth:90,maxWidth:null,values:[{text:"None",value:""},{text:"Left",value:"left"},{text:"Center",value:"center"},{text:"Right",value:"right"}]},{label:"V Align",name:"valign",type:"listbox",text:"None",minWidth:90,maxWidth:null,values:[{text:"None",value:""},{text:"Top",value:"top"},{text:"Middle",value:"middle"},{text:"Bottom",value:"bottom"}]}]},onsubmit:function(){var t=this.toJSON();o.undoManager.transact(function(){s(r,function(n){o.dom.setAttrib(n,"scope",t.scope),o.dom.setStyles(n,{width:a(t.width),height:a(t.height)}),t.type&&n.nodeName.toLowerCase()!=t.type&&(n=e.rename(n,t.type)),l(n),t.align&&o.formatter.apply("align"+t.align,{},n),c(n),t.valign&&o.formatter.apply("valign"+t.valign,{},n)}),o.focus()})}}))}function m(){var e=o.dom,t,n,r,c,d=[];t=o.dom.getParent(o.selection.getStart(),"table"),n=o.dom.getParent(o.selection.getStart(),"td,th"),s(t.rows,function(t){s(t.cells,function(o){return e.hasClass(o,"mce-item-selected")||o==n?(d.push(t),!1):void 0})}),r=d[0],r&&(c={height:i(e.getStyle(r,"height")||e.getAttrib(r,"height")),scope:e.getAttrib(r,"scope")},c.type=r.parentNode.nodeName.toLowerCase(),s("left center right".split(" "),function(e){o.formatter.matchNode(r,"align"+e)&&(c.align=e)}),o.windowManager.open({title:"Row properties",items:{type:"form",data:c,columns:2,defaults:{type:"textbox"},items:[{type:"listbox",name:"type",label:"Row type",text:"None",maxWidth:null,values:[{text:"Header",value:"thead"},{text:"Body",value:"tbody"},{text:"Footer",value:"tfoot"}]},{type:"listbox",name:"align",label:"Alignment",text:"None",maxWidth:null,values:[{text:"None",value:""},{text:"Left",value:"left"},{text:"Center",value:"center"},{text:"Right",value:"right"}]},{label:"Height",name:"height"}]},onsubmit:function(){var t=this.toJSON(),n,i,r;o.undoManager.transact(function(){var c=t.type;s(d,function(s){o.dom.setAttrib(s,"scope",t.scope),o.dom.setStyles(s,{height:a(t.height)}),c!=s.parentNode.nodeName.toLowerCase()&&(n=e.getParent(s,"table"),i=s.parentNode,r=e.select(c,n)[0],r||(r=e.create(c),n.firstChild?n.insertBefore(r,n.firstChild):n.appendChild(r)),r.appendChild(s),i.hasChildNodes()||e.remove(i)),l(s),t.align&&o.formatter.apply("align"+t.align,{},s)}),o.focus()})}}))}function p(e){return function(){o.execCommand(e)}}function g(e,t){var n,i,a;for(a='<table id="__mce"><tbody>',n=0;t>n;n++){for(a+="<tr>",i=0;e>i;i++)a+="<td>"+(r.ie?" ":"<br>")+"</td>";a+="</tr>"}a+="</tbody></table>",o.insertContent(a);var l=o.dom.get("__mce");return o.dom.setAttrib(l,"id",null),l}function h(e,t){function n(){e.disabled(!o.dom.getParent(o.selection.getStart(),t)),o.selection.selectorChanged(t,function(t){e.disabled(!t)})}o.initialized?n():o.on("init",n)}function v(){h(this,"table")}function b(){h(this,"td,th")}function y(){var e="";e='<table role="grid" class="mce-grid mce-grid-border" aria-readonly="true">';for(var t=0;10>t;t++){e+="<tr>";for(var n=0;10>n;n++)e+='<td role="gridcell" tabindex="-1"><a id="mcegrid'+(10*t+n)+'" href="#" data-mce-x="'+n+'" data-mce-y="'+t+'"></a></td>';e+="</tr>"}return e+="</table>",e+='<div class="mce-text-center" role="presentation">1 x 1</div>'}function w(e,t,n){var i=n.getEl().getElementsByTagName("table")[0],r,a,l,s,c,d=n.isRtl()||"tl-tr"==n.parent().rel;for(i.nextSibling.innerHTML=e+1+" x "+(t+1),d&&(e=9-e),a=0;10>a;a++)for(r=0;10>r;r++)s=i.rows[a].childNodes[r].firstChild,c=(d?r>=e:e>=r)&&t>=a,o.dom.toggleClass(s,"mce-active",c),c&&(l=s);return l.parentNode}var x,C,P=this;o.settings.table_grid===!1?o.addMenuItem("inserttable",{text:"Insert table",icon:"table",context:"table",onclick:d}):o.addMenuItem("inserttable",{text:"Insert table",icon:"table",context:"table",ariaHideMenu:!0,onclick:function(e){e.aria&&(this.parent().hideAll(),e.stopImmediatePropagation(),d())},onshow:function(){w(0,0,this.menu.items()[0])},onhide:function(){var e=this.menu.items()[0].getEl().getElementsByTagName("a");o.dom.removeClass(e,"mce-active"),o.dom.addClass(e[0],"mce-active")},menu:[{type:"container",html:y(),onPostRender:function(){this.lastX=this.lastY=0},onmousemove:function(e){var t=e.target,n,o;"A"==t.tagName.toUpperCase()&&(n=parseInt(t.getAttribute("data-mce-x"),10),o=parseInt(t.getAttribute("data-mce-y"),10),(this.isRtl()||"tl-tr"==this.parent().rel)&&(n=9-n),(n!==this.lastX||o!==this.lastY)&&(w(n,o,e.control),this.lastX=n,this.lastY=o))},onkeydown:function(e){var t=this.lastX,n=this.lastY,o;switch(e.keyCode){case 37:t>0&&(t--,o=!0);break;case 39:o=!0,9>t&&t++;break;case 38:o=!0,n>0&&n--;break;case 40:o=!0,9>n&&n++}o&&(e.preventDefault(),e.stopPropagation(),w(t,n,e.control).focus(),this.lastX=t,this.lastY=n)},onclick:function(e){"A"==e.target.tagName.toUpperCase()&&(e.preventDefault(),e.stopPropagation(),this.parent().cancel(),g(this.lastX+1,this.lastY+1))}}]}),o.addMenuItem("tableprops",{text:"Table properties",context:"table",onPostRender:v,onclick:d}),o.addMenuItem("deletetable",{text:"Delete table",context:"table",onPostRender:v,cmd:"mceTableDelete"}),o.addMenuItem("cell",{separator:"before",text:"Cell",context:"table",menu:[{text:"Cell properties",onclick:p("mceTableCellProps"),onPostRender:b},{text:"Merge cells",onclick:p("mceTableMergeCells"),onPostRender:b},{text:"Split cell",onclick:p("mceTableSplitCells"),onPostRender:b}]}),o.addMenuItem("row",{text:"Row",context:"table",menu:[{text:"Insert row before",onclick:p("mceTableInsertRowBefore"),onPostRender:b},{text:"Insert row after",onclick:p("mceTableInsertRowAfter"),onPostRender:b},{text:"Delete row",onclick:p("mceTableDeleteRow"),onPostRender:b},{text:"Row properties",onclick:p("mceTableRowProps"),onPostRender:b},{text:"-"},{text:"Cut row",onclick:p("mceTableCutRow"),onPostRender:b},{text:"Copy row",onclick:p("mceTableCopyRow"),onPostRender:b},{text:"Paste row before",onclick:p("mceTablePasteRowBefore"),onPostRender:b},{text:"Paste row after",onclick:p("mceTablePasteRowAfter"),onPostRender:b}]}),o.addMenuItem("column",{text:"Column",context:"table",menu:[{text:"Insert column before",onclick:p("mceTableInsertColBefore"),onPostRender:b},{text:"Insert column after",onclick:p("mceTableInsertColAfter"),onPostRender:b},{text:"Delete column",onclick:p("mceTableDeleteCol"),onPostRender:b}]});var S=[];s("inserttable tableprops deletetable | cell row column".split(" "),function(e){S.push("|"==e?{text:"-"}:o.menuItems[e])}),o.addButton("table",{type:"menubutton",title:"Table",menu:S}),r.isIE||o.on("click",function(e){e=e.target,"TABLE"===e.nodeName&&(o.selection.select(e),o.nodeChanged())}),P.quirks=new t(o),o.on("Init",function(){x=o.windowManager,P.cellSelection=new n(o)}),s({mceTableSplitCells:function(e){e.split()},mceTableMergeCells:function(e){var t,n,i;i=o.dom.getParent(o.selection.getStart(),"th,td"),i&&(t=i.rowSpan,n=i.colSpan),o.dom.select("td.mce-item-selected,th.mce-item-selected").length?e.merge():u(e,i)},mceTableInsertRowBefore:function(e){e.insertRow(!0)},mceTableInsertRowAfter:function(e){e.insertRow()},mceTableInsertColBefore:function(e){e.insertCol(!0)},mceTableInsertColAfter:function(e){e.insertCol()},mceTableDeleteCol:function(e){e.deleteCols()},mceTableDeleteRow:function(e){e.deleteRows()},mceTableCutRow:function(e){C=e.cutRows()},mceTableCopyRow:function(e){C=e.copyRows()},mceTablePasteRowBefore:function(e){e.pasteRows(C,!0)},mceTablePasteRowAfter:function(e){e.pasteRows(C)},mceTableDelete:function(e){e.deleteTable()}},function(t,n){o.addCommand(n,function(){var n=new e(o);n&&(t(n),o.execCommand("mceRepaint"),P.cellSelection.clear())})}),s({mceInsertTable:function(){d()},mceTableRowProps:m,mceTableCellProps:f},function(e,t){o.addCommand(t,function(t,n){e(n)})})}var s=o.each;a.add("table",l)}),a([s,u,m,g])}(this);tinymce.PluginManager.add("template",function(e){function t(t){return function(){var a=e.settings.templates;"string"==typeof a?tinymce.util.XHR.send({url:a,success:function(e){t(tinymce.util.JSON.parse(e))}}):t(a)}}function a(t){function a(t){function a(t){if(-1==t.indexOf("<html>")){var a="";tinymce.each(e.contentCSS,function(t){a+='<link type="text/css" rel="stylesheet" href="'+e.documentBaseURI.toAbsolute(t)+'">'}),t="<!DOCTYPE html><html><head>"+a+"</head><body>"+t+"</body></html>"}t=r(t,"template_preview_replace_values");var l=n.find("iframe")[0].getEl().contentWindow.document;l.open(),l.write(t),l.close()}var c=t.control.value();c.url?tinymce.util.XHR.send({url:c.url,success:function(e){l=e,a(l)}}):(l=c.content,a(l)),n.find("#description")[0].text(t.control.value().description)}var n,l,i=[];return t&&0!==t.length?(tinymce.each(t,function(e){i.push({selected:!i.length,text:e.title,value:{url:e.url,content:e.content,description:e.description}})}),n=e.windowManager.open({title:"Insert template",layout:"flex",direction:"column",align:"stretch",padding:15,spacing:10,items:[{type:"form",flex:0,padding:0,items:[{type:"container",label:"Templates",items:{type:"listbox",label:"Templates",name:"template",values:i,onselect:a}}]},{type:"label",name:"description",label:"Description",text:" "},{type:"iframe",flex:1,border:1}],onsubmit:function(){c(!1,l)},width:e.getParam("template_popup_width",600),height:e.getParam("template_popup_height",500)}),void n.find("listbox")[0].fire("select")):void e.windowManager.alert("No templates defined")}function n(t,a){function n(e,t){if(e=""+e,e.length<t)for(var a=0;a<t-e.length;a++)e="0"+e;return e}var l="Sun Mon Tue Wed Thu Fri Sat Sun".split(" "),r="Sunday Monday Tuesday Wednesday Thursday Friday Saturday Sunday".split(" "),c="Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),i="January February March April May June July August September October November December".split(" ");return a=a||new Date,t=t.replace("%D","%m/%d/%Y"),t=t.replace("%r","%I:%M:%S %p"),t=t.replace("%Y",""+a.getFullYear()),t=t.replace("%y",""+a.getYear()),t=t.replace("%m",n(a.getMonth()+1,2)),t=t.replace("%d",n(a.getDate(),2)),t=t.replace("%H",""+n(a.getHours(),2)),t=t.replace("%M",""+n(a.getMinutes(),2)),t=t.replace("%S",""+n(a.getSeconds(),2)),t=t.replace("%I",""+((a.getHours()+11)%12+1)),t=t.replace("%p",""+(a.getHours()<12?"AM":"PM")),t=t.replace("%B",""+e.translate(i[a.getMonth()])),t=t.replace("%b",""+e.translate(c[a.getMonth()])),t=t.replace("%A",""+e.translate(r[a.getDay()])),t=t.replace("%a",""+e.translate(l[a.getDay()])),t=t.replace("%%","%")}function l(t){var a=e.dom,n=e.getParam("template_replace_values");i(a.select("*",t),function(e){i(n,function(t,l){a.hasClass(e,l)&&"function"==typeof n[l]&&n[l](e)})})}function r(t,a){return i(e.getParam(a),function(e,a){"function"!=typeof e&&(t=t.replace(new RegExp("\\{\\$"+a+"\\}","g"),e))}),t}function c(t,a){function c(e,t){return new RegExp("\\b"+t+"\\b","g").test(e.className)}var o,s,p=e.dom,m=e.selection.getContent();a=r(a,"template_replace_values"),o=p.create("div",null,a),s=p.select(".mceTmpl",o),s&&s.length>0&&(o=p.create("div",null),o.appendChild(s[0].cloneNode(!0))),i(p.select("*",o),function(t){c(t,e.getParam("template_cdate_classes","cdate").replace(/\s+/g,"|"))&&(t.innerHTML=n(e.getParam("template_cdate_format",e.getLang("template.cdate_format")))),c(t,e.getParam("template_mdate_classes","mdate").replace(/\s+/g,"|"))&&(t.innerHTML=n(e.getParam("template_mdate_format",e.getLang("template.mdate_format")))),c(t,e.getParam("template_selected_content_classes","selcontent").replace(/\s+/g,"|"))&&(t.innerHTML=m)}),l(o),e.execCommand("mceInsertContent",!1,o.innerHTML),e.addVisual()}var i=tinymce.each;e.addCommand("mceInsertTemplate",c),e.addButton("template",{title:"Insert template",onclick:t(a)}),e.addMenuItem("template",{text:"Insert template",onclick:t(a),context:"insert"}),e.on("PreProcess",function(t){var a=e.dom;i(a.select("div",t.node),function(t){a.hasClass(t,"mceTmpl")&&(i(a.select("*",t),function(t){a.hasClass(t,e.getParam("template_mdate_classes","mdate").replace(/\s+/g,"|"))&&(t.innerHTML=n(e.getParam("template_mdate_format",e.getLang("template.mdate_format"))))}),l(t))})})});tinymce.PluginManager.add("textcolor",function(e){function t(){var t,o,l=[];for(o=e.settings.textcolor_map||["000000","Black","993300","Burnt orange","333300","Dark olive","003300","Dark green","003366","Dark azure","000080","Navy Blue","333399","Indigo","333333","Very dark gray","800000","Maroon","FF6600","Orange","808000","Olive","008000","Green","008080","Teal","0000FF","Blue","666699","Grayish blue","808080","Gray","FF0000","Red","FF9900","Amber","99CC00","Yellow green","339966","Sea green","33CCCC","Turquoise","3366FF","Royal blue","800080","Purple","999999","Medium gray","FF00FF","Magenta","FFCC00","Gold","FFFF00","Yellow","00FF00","Lime","00FFFF","Aqua","00CCFF","Sky blue","993366","Red violet","C0C0C0","Silver","FF99CC","Pink","FFCC99","Peach","FFFF99","Light yellow","CCFFCC","Pale green","CCFFFF","Pale cyan","99CCFF","Light sky blue","CC99FF","Plum","FFFFFF","White"],t=0;t<o.length;t+=2)l.push({text:o[t+1],color:o[t]});return l}function o(){var o,l,r,a,c,i,n,F,d,s=this;for(o=t(),r='<table class="mce-grid mce-grid-border mce-colorbutton-grid" role="list" cellspacing="0"><tbody>',a=o.length-1,c=e.settings.textcolor_rows||5,i=e.settings.textcolor_cols||8,F=0;c>F;F++){for(r+="<tr>",n=0;i>n;n++)d=F*i+n,d>a?r+="<td></td>":(l=o[d],r+='<td><div id="'+s._id+"-"+d+'" data-mce-color="'+l.color+'" role="option" tabIndex="-1" style="'+(l?"background-color: #"+l.color:"")+'" title="'+l.text+'"></div></td>');r+="</tr>"}return r+="</tbody></table>"}function l(t){var o,l=this.parent();(o=t.target.getAttribute("data-mce-color"))&&(this.lastId&&document.getElementById(this.lastId).setAttribute("aria-selected",!1),t.target.setAttribute("aria-selected",!0),this.lastId=t.target.id,l.hidePanel(),o="#"+o,l.color(o),e.execCommand(l.settings.selectcmd,!1,o))}function r(){var t=this;t._color&&e.execCommand(t.settings.selectcmd,!1,t._color)}e.addButton("forecolor",{type:"colorbutton",tooltip:"Text color",selectcmd:"ForeColor",panel:{role:"application",ariaRemember:!0,html:o,onclick:l},onclick:r}),e.addButton("backcolor",{type:"colorbutton",tooltip:"Background color",selectcmd:"HiliteColor",panel:{role:"application",ariaRemember:!0,html:o,onclick:l},onclick:r})});tinymce.PluginManager.add("visualblocks",function(e,s){function o(){var s=this;s.active(a),e.on("VisualBlocks",function(){s.active(e.dom.hasClass(e.getBody(),"mce-visualblocks"))})}var l,t,a;window.NodeList&&(e.addCommand("mceVisualBlocks",function(){var o,c=e.dom;l||(l=c.uniqueId(),o=c.create("link",{id:l,rel:"stylesheet",href:s+"/css/visualblocks.css"}),e.getDoc().getElementsByTagName("head")[0].appendChild(o)),e.on("PreviewFormats AfterPreviewFormats",function(s){a&&c.toggleClass(e.getBody(),"mce-visualblocks","afterpreviewformats"==s.type)}),c.toggleClass(e.getBody(),"mce-visualblocks"),a=e.dom.hasClass(e.getBody(),"mce-visualblocks"),t&&t.active(c.hasClass(e.getBody(),"mce-visualblocks")),e.fire("VisualBlocks")}),e.addButton("visualblocks",{title:"Show blocks",cmd:"mceVisualBlocks",onPostRender:o}),e.addMenuItem("visualblocks",{text:"Show blocks",cmd:"mceVisualBlocks",onPostRender:o,selectable:!0,context:"view",prependToContext:!0}),e.on("init",function(){e.settings.visualblocks_default_state&&e.execCommand("mceVisualBlocks",!1,null,{skip_focus:!0})}),e.on("remove",function(){e.dom.removeClass(e.getBody(),"mce-visualblocks")}))});tinymce.PluginManager.add("visualchars",function(e){function a(a){var t,s,i,r,c,d,l=e.getBody(),m=e.selection;if(n=!n,o.state=n,e.fire("VisualChars",{state:n}),a&&(d=m.getBookmark()),n)for(s=[],tinymce.walk(l,function(e){3==e.nodeType&&e.nodeValue&&-1!=e.nodeValue.indexOf(" ")&&s.push(e)},"childNodes"),i=0;i<s.length;i++){for(r=s[i].nodeValue,r=r.replace(/(\u00a0)/g,'<span data-mce-bogus="1" class="mce-nbsp">$1</span>'),c=e.dom.create("div",null,r);t=c.lastChild;)e.dom.insertAfter(t,s[i]);e.dom.remove(s[i])}else for(s=e.dom.select("span.mce-nbsp",l),i=s.length-1;i>=0;i--)e.dom.remove(s[i],1);m.moveToBookmark(d)}function t(){var a=this;e.on("VisualChars",function(e){a.active(e.state)})}var n,o=this;e.addCommand("mceVisualChars",a),e.addButton("visualchars",{title:"Show invisible characters",cmd:"mceVisualChars",onPostRender:t}),e.addMenuItem("visualchars",{text:"Show invisible characters",cmd:"mceVisualChars",onPostRender:t,selectable:!0,context:"view",prependToContext:!0}),e.on("beforegetcontent",function(e){n&&"raw"!=e.format&&!e.draft&&(n=!0,a(!1))})});tinymce.PluginManager.add("wordcount",function(e){function t(){e.theme.panel.find("#wordcount").text(["Words: {0}",a.getCount()])}var n,o,a=this;n=e.getParam("wordcount_countregex",/[\w\u2019\x27\-\u00C0-\u1FFF]+/g),o=e.getParam("wordcount_cleanregex",/[0-9.(),;:!?%#$?\x27\x22_+=\\\/\-]*/g),e.on("init",function(){var n=e.theme.panel&&e.theme.panel.find("#statusbar")[0];n&&window.setTimeout(function(){n.insert({type:"label",name:"wordcount",text:["Words: {0}",a.getCount()],classes:"wordcount",disabled:e.settings.readonly},0),e.on("setcontent beforeaddundo",t),e.on("keyup",function(e){32==e.keyCode&&t()})},0)}),a.getCount=function(){var t=e.getContent({format:"raw"}),a=0;if(t){t=t.replace(/\.\.\./g," "),t=t.replace(/<.[^<>]*?>/g," ").replace(/&nbsp;|&#160;/gi," "),t=t.replace(/(\w+)(&#?[a-z0-9]+;)+(\w+)/i,"$1$3").replace(/&.+?;/g," "),t=t.replace(o,"");var r=t.match(n);r&&(a=r.length)}return a}});tinymce.ThemeManager.add("modern",function(e){function t(){function t(t){var n,o=[];if(t)return d(t.split(/[ ,]/),function(t){function i(){var i=e.selection;"bullist"==r&&i.selectorChanged("ul > li",function(e,i){for(var n,o=i.parents.length;o--&&(n=i.parents[o].nodeName,"OL"!=n&&"UL"!=n););t.active(e&&"UL"==n)}),"numlist"==r&&i.selectorChanged("ol > li",function(e,i){for(var n,o=i.parents.length;o--&&(n=i.parents[o].nodeName,"OL"!=n&&"UL"!=n););t.active(e&&"OL"==n)}),t.settings.stateSelector&&i.selectorChanged(t.settings.stateSelector,function(e){t.active(e)},!0),t.settings.disabledStateSelector&&i.selectorChanged(t.settings.disabledStateSelector,function(e){t.disabled(e)})}var r;"|"==t?n=null:c.has(t)?(t={type:t},u.toolbar_items_size&&(t.size=u.toolbar_items_size),o.push(t),n=null):(n||(n={type:"buttongroup",items:[]},o.push(n)),e.buttons[t]&&(r=t,t=e.buttons[r],"function"==typeof t&&(t=t()),t.type=t.type||"button",u.toolbar_items_size&&(t.size=u.toolbar_items_size),t=c.create(t),n.items.push(t),e.initialized?i():e.on("init",i)))}),i.push({type:"toolbar",layout:"flow",items:o}),!0}var i=[];if(tinymce.isArray(u.toolbar)){if(0===u.toolbar.length)return;tinymce.each(u.toolbar,function(e,t){u["toolbar"+(t+1)]=e}),delete u.toolbar}for(var n=1;10>n&&t(u["toolbar"+n]);n++);return i.length||u.toolbar===!1||t(u.toolbar||f),i.length?{type:"panel",layout:"stack",classes:"toolbar-grp",ariaRoot:!0,ariaRemember:!0,items:i}:void 0}function i(){function t(t){var i;return"|"==t?{text:"|"}:i=e.menuItems[t]}function i(i){var n,o,r,a,s;if(s=tinymce.makeMap((u.removed_menuitems||"").split(/[ ,]/)),u.menu?(o=u.menu[i],a=!0):o=h[i],o){n={text:o.title},r=[],d((o.items||"").split(/[ ,]/),function(e){var i=t(e);i&&!s[e]&&r.push(t(e))}),a||d(e.menuItems,function(e){e.context==i&&("before"==e.separator&&r.push({text:"|"}),e.prependToContext?r.unshift(e):r.push(e),"after"==e.separator&&r.push({text:"|"}))});for(var l=0;l<r.length;l++)"|"==r[l].text&&(0===l||l==r.length-1)&&r.splice(l,1);if(n.menu=r,!n.menu.length)return null}return n}var n,o=[],r=[];if(u.menu)for(n in u.menu)r.push(n);else for(n in h)r.push(n);for(var a="string"==typeof u.menubar?u.menubar.split(/[ ,]/):r,s=0;s<a.length;s++){var l=a[s];l=i(l),l&&o.push(l)}return o}function n(t){function i(e){var i=t.find(e)[0];i&&i.focus(!0)}e.shortcuts.add("Alt+F9","",function(){i("menubar")}),e.shortcuts.add("Alt+F10","",function(){i("toolbar")}),e.shortcuts.add("Alt+F11","",function(){i("elementpath")}),t.on("cancel",function(){e.focus()})}function o(t,i){function n(e){return{width:e.clientWidth,height:e.clientHeight}}var o,r,a,s;o=e.getContainer(),r=e.getContentAreaContainer().firstChild,a=n(o),s=n(r),null!==t&&(t=Math.max(u.min_width||100,t),t=Math.min(u.max_width||65535,t),m.css(o,"width",t+(a.width-s.width)),m.css(r,"width",t)),i=Math.max(u.min_height||100,i),i=Math.min(u.max_height||65535,i),m.css(r,"height",i),e.fire("ResizeEditor")}function r(t,i){var n=e.getContentAreaContainer();l.resizeTo(n.clientWidth+t,n.clientHeight+i)}function a(o){function r(){if(h&&h.moveRel&&h.visible()&&!h._fixed){var t=e.selection.getScrollContainer(),i=e.getBody(),n=0,o=0;if(t){var r=m.getPos(i),a=m.getPos(t);n=Math.max(0,a.x-r.x),o=Math.max(0,a.y-r.y)}h.fixed(!1).moveRel(i,e.rtl?["tr-br","br-tr"]:["tl-bl","bl-tl"]).moveBy(n,o)}}function a(){h&&(h.show(),r(),m.addClass(e.getBody(),"mce-edit-focus"))}function s(){h&&(h.hide(),m.removeClass(e.getBody(),"mce-edit-focus"))}function d(){return h?void(h.visible()||a()):(h=l.panel=c.create({type:f?"panel":"floatpanel",role:"application",classes:"tinymce tinymce-inline",layout:"flex",direction:"column",align:"stretch",autohide:!1,autofix:!0,fixed:!!f,border:1,items:[u.menubar===!1?null:{type:"menubar",border:"0 0 1 0",items:i()},t()]}),e.fire("BeforeRenderUI"),h.renderTo(f||document.body).reflow(),n(h),a(),e.on("nodeChange",r),e.on("activate",a),e.on("deactivate",s),void e.nodeChanged())}var h,f;return u.fixed_toolbar_container&&(f=m.select(u.fixed_toolbar_container)[0]),u.content_editable=!0,e.on("focus",function(){o.skinUiCss?tinymce.DOM.styleSheetLoader.load(o.skinUiCss,d,d):d()}),e.on("blur hide",s),e.on("remove",function(){h&&(h.remove(),h=null)}),o.skinUiCss&&tinymce.DOM.styleSheetLoader.load(o.skinUiCss),{}}function s(r){var a,s,d;return r.skinUiCss&&tinymce.DOM.loadCSS(r.skinUiCss),a=l.panel=c.create({type:"panel",role:"application",classes:"tinymce",style:"visibility: hidden",layout:"stack",border:1,items:[u.menubar===!1?null:{type:"menubar",border:"0 0 1 0",items:i()},t(),{type:"panel",name:"iframe",layout:"stack",classes:"edit-area",html:"",border:"1 0 0 0"}]}),u.resize!==!1&&(s={type:"resizehandle",direction:u.resize,onResizeStart:function(){var t=e.getContentAreaContainer().firstChild;d={width:t.clientWidth,height:t.clientHeight}},onResize:function(e){"both"==u.resize?o(d.width+e.deltaX,d.height+e.deltaY):o(null,d.height+e.deltaY)}}),u.statusbar!==!1&&a.add({type:"panel",name:"statusbar",classes:"statusbar",layout:"flow",border:"1 0 0 0",ariaRoot:!0,items:[{type:"elementpath"},s]}),u.readonly&&a.find("*").disabled(!0),e.fire("BeforeRenderUI"),a.renderBefore(r.targetNode).reflow(),u.width&&tinymce.DOM.setStyle(a.getEl(),"width",u.width),e.on("remove",function(){a.remove(),a=null}),n(a),{iframeContainer:a.find("#iframe")[0].getEl(),editorContainer:a.getEl()}}var l=this,u=e.settings,c=tinymce.ui.Factory,d=tinymce.each,m=tinymce.DOM,h={file:{title:"File",items:"newdocument"},edit:{title:"Edit",items:"undo redo | cut copy paste pastetext | selectall"},insert:{title:"Insert",items:"|"},view:{title:"View",items:"visualaid |"},format:{title:"Format",items:"bold italic underline strikethrough superscript subscript | formats | removeformat"},table:{title:"Table"},tools:{title:"Tools"}},f="undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image";l.renderUI=function(t){var i=u.skin!==!1?u.skin||"lightgray":!1;if(i){var n=u.skin_url;n=n?e.documentBaseURI.toAbsolute(n):tinymce.baseURL+"/skins/"+i,t.skinUiCss=tinymce.Env.documentMode<=7?n+"/skin.ie7.min.css":n+"/skin.min.css",e.contentCSS.push(n+"/content"+(e.inline?".inline":"")+".min.css")}return e.on("ProgressState",function(e){l.throbber=l.throbber||new tinymce.ui.Throbber(l.panel.getEl("body")),e.state?l.throbber.show(e.time):l.throbber.hide()}),u.inline?a(t):s(t)},l.resizeTo=o,l.resizeBy=r}); \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/key_tests/remove_on_keypress.html b/node_modules/selenium-webdriver/lib/test/data/key_tests/remove_on_keypress.html
new file mode 100644
index 000000000..c0f3aab4c
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/key_tests/remove_on_keypress.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<div><input id="target" type="checkbox">Pressing "a" while this checkbox is
+ focused will remove it from the DOM.</div>
+<div><textarea id="log" style="width:250px; height:250px;"></textarea></div>
+<div><button id="clear">Clear log</button></div>
+<script>
+ function removeNode(node) {
+ if (node && node.parentNode) {
+ node.parentNode.removeChild(node);
+ }
+ }
+
+ function log(msg) {
+ document.getElementById('log').value += msg + '\n';
+ }
+
+ document.body.onkeyup = function() {
+ log('keyup (body)');
+ };
+
+ document.getElementById('clear').onclick = function() {
+ document.getElementById('log').value = '';
+ };
+
+ var target = document.getElementById('target');
+ target.onkeydown = function() { log('keydown (target)'); };
+ target.onkeypress = function(e) {
+ e = e || window.event;
+ var k = e.keyCode || e.which;
+ if (k == 97) {
+ log('a pressed; removing');
+ removeNode(target.parentNode);
+ }
+ };
+ target.onkeyup = function() { log('keyup (target)'); };
+</script>
diff --git a/node_modules/selenium-webdriver/lib/test/data/keyboard_shortcut.html b/node_modules/selenium-webdriver/lib/test/data/keyboard_shortcut.html
new file mode 100644
index 000000000..741d7f4b8
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/keyboard_shortcut.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<body style="background: white">
+<div style="background: red">CTRL + 1: red</div>
+<div style="background: green">SHIFT + 1: green</div>
+<div style="background: yellow">CTRL + SHIFT + 1: yellow</div>
+<div style="background: lightblue">ALT + 1: lightblue</div>
+<div style="background: lightgreen">CTRL + ALT + 1: lightgreen</div>
+<div style="background: silver">SHIFT + ALT + 1: silver</div>
+<div style="background: magenta">CTRL + SHIFT + ALT + 1: magenta</div>
+<script>
+ var colors = {};
+ colors[0x001] = 'red';
+ colors[0x010] = 'green';
+ colors[0x011] = 'yellow';
+ colors[0x100] = 'lightblue';
+ colors[0x101] = 'lightgreen';
+ colors[0x110] = 'silver';
+ colors[0x111] = 'magenta';
+
+ document.onkeydown = function(e) {
+ if (e.keyCode != 49) return;
+ var mask = 0;
+ if (e.ctrlKey) mask |= 0x001;
+ if (e.shiftKey) mask |= 0x010;
+ if (e.altKey) mask |= 0x100;
+
+ console.log(mask);
+ if (mask) {
+ document.body.style.backgroundColor = colors[mask];
+ }
+
+ e.preventDefault();
+ e.stopPropagation();
+ return false;
+ };
+</script>
diff --git a/node_modules/selenium-webdriver/lib/test/data/linked_image.html b/node_modules/selenium-webdriver/lib/test/data/linked_image.html
new file mode 100644
index 000000000..7c8df0031
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/linked_image.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+<title>Linking with an image</title>
+</head>
+<body>
+<img style="width: 644px; height: 41px;" alt="banner" src="banner.gif"><br>
+<a id="link" href="./resultPage.html">Click here for next page</a><br>
+<a id="linkWithEnclosedImage" href="./resultPage.html"><img id="enclosedImage" src="./banner.gif"></a><br/>
+<a id="linkToAnchorOnThisPage" href="#link">link to other link</a><br/>
+<a id="justanotherLink" href="./resultPage.html">Just another link.</a><br/>
+<a id="linkWithExtraEnclosedImage" href="./resultPage.html"><div><img id="extraEnclosedImage" src="./banner.gif"></div></a><br/>
+</body>
+
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/locators_tests/boolean_attribute_selected.html b/node_modules/selenium-webdriver/lib/test/data/locators_tests/boolean_attribute_selected.html
new file mode 100644
index 000000000..42b0442b0
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/locators_tests/boolean_attribute_selected.html
@@ -0,0 +1,13 @@
+<html>
+<head>
+ <title>Boolean Attribute Selected</title>
+</head>
+<body>
+<form method="get" action="#" name="myForm">
+ <select name="myDropdown">
+ <option value="one">One</option>
+ <option selected="selected" value="two">Two</option>
+ <option value="four">Four</option>
+ </select>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/locators_tests/boolean_attribute_selected_html4.html b/node_modules/selenium-webdriver/lib/test/data/locators_tests/boolean_attribute_selected_html4.html
new file mode 100644
index 000000000..60cd03381
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/locators_tests/boolean_attribute_selected_html4.html
@@ -0,0 +1,13 @@
+<html>
+<head>
+ <title>Boolean Attribute Selected</title>
+</head>
+<body>
+<form method="get" action="#" name="myForm">
+ <select name="myDropdown">
+ <option value="one">One</option>
+ <option selected value="two">Two</option>
+ <option value="four">Four</option>
+ </select>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/longContentPage.html b/node_modules/selenium-webdriver/lib/test/data/longContentPage.html
new file mode 100644
index 000000000..99a45e773
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/longContentPage.html
@@ -0,0 +1,55 @@
+<html>
+<head>
+ <title>TouchLongContent</title>
+</head>
+
+<body>
+ <h1>Touch API</h1>
+ <h2>Page with long content</h2>
+ <pre> <a id="imagestart"><img src="icon.gif"></a></pre></br></br></br>
+ <p><pre>Long text: This is a very long text to make the screen horizontally movable, to test the flick gesture at a long horizontal distance <a href="xhtmlTest.html" id="link1">Normal link</a> at normal and fast speeds to see results of it <a href="xhtmlTest.html" id="link2">Normal link end</a></pre></p>
+ </br></br></br></br></br></br></br></br></br></br></br>
+ </br></br></br></br></br></br></br></br></br></br></br>
+ </br></br></br></br></br></br></br></br></br></br></br>
+ </br></br></br></br></br></br></br></br></br></br></br>
+ </br></br></br></br></br></br></br></br></br></br></br>
+ </br></br></br></br></br></br></br></br></br></br></br>
+ </br></br></br></br></br></br></br></br></br></br></br>
+ </br></br></br></br></br></br></br></br></br></br></br>
+ </br></br></br></br></br></br></br></br></br></br></br>
+ </br></br></br></br></br></br></br></br></br></br></br>
+ </br></br></br></br></br></br></br></br></br></br></br>
+ </br></br></br></br></br></br></br></br></br></br></br>
+ </br></br></br></br></br></br></br></br></br></br></br>
+ </br></br></br></br></br></br></br></br></br></br></br>
+ </br></br></br></br></br></br></br></br></br></br></br>
+ </br></br></br></br></br></br></br></br></br></br></br>
+ </br></br></br></br></br></br></br></br></br></br></br>
+ </br></br></br></br></br></br></br></br></br></br></br>
+ </br></br></br></br></br></br></br></br></br></br></br>
+ Middle of the screen <a href="xhtmlTest.html" id="link3">Normal link</a>
+ </br></br></br></br></br></br></br></br></br></br></br>
+ </br></br></br></br></br></br></br></br></br></br></br>
+ </br></br></br></br></br></br></br></br></br></br></br>
+ </br></br></br></br></br></br></br></br></br></br></br>
+ </br></br></br></br></br></br></br></br></br></br></br>
+ </br></br></br></br></br></br></br></br></br></br></br>
+ </br></br></br></br></br></br></br></br></br></br></br>
+ </br></br></br></br></br></br></br></br></br></br></br>
+ </br></br></br></br></br></br></br></br></br></br></br>
+ </br></br></br></br></br></br></br></br></br></br></br>
+ </br></br></br></br></br></br></br></br></br></br></br>
+ </br></br></br></br></br></br></br></br></br></br></br>
+ </br></br></br></br></br></br></br></br></br></br></br>
+ </br></br></br></br></br></br></br></br></br></br></br>
+ </br></br></br></br></br></br></br></br></br></br></br>
+ </br></br></br></br></br></br></br></br></br></br></br>
+ </br></br></br></br></br></br></br></br></br></br></br>
+ </br></br></br></br></br></br></br></br></br></br></br>
+ </br></br></br></br></br></br></br></br></br></br></br>
+ </br></br></br></br></br></br></br></br></br></br></br>
+ </br></br></br></br></br></br></br></br></br></br></br>
+ </br></br></br></br></br></br></br></br></br></br></br>
+ Bottom of the screen <a href="xhtmlTest.html" id="link4">Normal link</a></br></br></br>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/macbeth.html b/node_modules/selenium-webdriver/lib/test/data/macbeth.html
new file mode 100644
index 000000000..9fa39d566
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/macbeth.html
@@ -0,0 +1,5255 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
+ <html>
+ <head>
+ <title>Macbeth: Entire Play
+ </title>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ </HEAD>
+ <body bgcolor="#ffffff" text="#000000">
+
+ <!-- Originally from http://shakespeare.mit.edu/macbeth/full.html -->
+
+<a href="#5.8.86">Quick link to last speech</a>
+
+<H3>ACT I</h3>
+<h3>SCENE I. A desert place.</h3>
+<p><blockquote>
+<i>Thunder and lightning. Enter three Witches</i>
+</blockquote>
+
+<A NAME=speech1><b>First Witch</b></a>
+<blockquote>
+<A NAME=1.1.1>When shall we three meet again</A><br>
+<A NAME=1.1.2>In thunder, lightning, or in rain?</A><br>
+</blockquote>
+
+<A NAME=speech2><b>Second Witch</b></a>
+<blockquote>
+<A NAME=1.1.3>When the hurlyburly's done,</A><br>
+<A NAME=1.1.4>When the battle's lost and won.</A><br>
+</blockquote>
+
+<A NAME=speech3><b>Third Witch</b></a>
+<blockquote>
+<A NAME=1.1.5>That will be ere the set of sun.</A><br>
+</blockquote>
+
+<A NAME=speech4><b>First Witch</b></a>
+<blockquote>
+<A NAME=1.1.6>Where the place?</A><br>
+</blockquote>
+
+<A NAME=speech5><b>Second Witch</b></a>
+<blockquote>
+<A NAME=1.1.7> Upon the heath.</A><br>
+</blockquote>
+
+<A NAME=speech6><b>Third Witch</b></a>
+<blockquote>
+<A NAME=1.1.8>There to meet with Macbeth.</A><br>
+</blockquote>
+
+<A NAME=speech7><b>First Witch</b></a>
+<blockquote>
+<A NAME=1.1.9>I come, Graymalkin!</A><br>
+</blockquote>
+
+<A NAME=speech8><b>Second Witch</b></a>
+<blockquote>
+<A NAME=1.1.10>Paddock calls.</A><br>
+</blockquote>
+
+<A NAME=speech9><b>Third Witch</b></a>
+<blockquote>
+<A NAME=1.1.11>Anon.</A><br>
+</blockquote>
+
+<A NAME=speech10><b>ALL</b></a>
+<blockquote>
+<A NAME=1.1.12>Fair is foul, and foul is fair:</A><br>
+<A NAME=1.1.13>Hover through the fog and filthy air.</A><br>
+<p><i>Exeunt</i></p>
+</blockquote>
+<h3>SCENE II. A camp near Forres.</h3>
+<p><blockquote>
+<i>Alarum within. Enter DUNCAN, MALCOLM, DONALBAIN, LENNOX, with Attendants, meeting a bleeding Sergeant</i>
+</blockquote>
+
+<A NAME=speech1><b>DUNCAN</b></a>
+<blockquote>
+<A NAME=1.2.1>What bloody man is that? He can report,</A><br>
+<A NAME=1.2.2>As seemeth by his plight, of the revolt</A><br>
+<A NAME=1.2.3>The newest state.</A><br>
+</blockquote>
+
+<A NAME=speech2><b>MALCOLM</b></a>
+<blockquote>
+<A NAME=1.2.4> This is the sergeant</A><br>
+<A NAME=1.2.5>Who like a good and hardy soldier fought</A><br>
+<A NAME=1.2.6>'Gainst my captivity. Hail, brave friend!</A><br>
+<A NAME=1.2.7>Say to the king the knowledge of the broil</A><br>
+<A NAME=1.2.8>As thou didst leave it.</A><br>
+</blockquote>
+
+<A NAME=speech3><b>Sergeant</b></a>
+<blockquote>
+<A NAME=1.2.9>Doubtful it stood;</A><br>
+<A NAME=1.2.10>As two spent swimmers, that do cling together</A><br>
+<A NAME=1.2.11>And choke their art. The merciless Macdonwald--</A><br>
+<A NAME=1.2.12>Worthy to be a rebel, for to that</A><br>
+<A NAME=1.2.13>The multiplying villanies of nature</A><br>
+<A NAME=1.2.14>Do swarm upon him--from the western isles</A><br>
+<A NAME=1.2.15>Of kerns and gallowglasses is supplied;</A><br>
+<A NAME=1.2.16>And fortune, on his damned quarrel smiling,</A><br>
+<A NAME=1.2.17>Show'd like a rebel's whore: but all's too weak:</A><br>
+<A NAME=1.2.18>For brave Macbeth--well he deserves that name--</A><br>
+<A NAME=1.2.19>Disdaining fortune, with his brandish'd steel,</A><br>
+<A NAME=1.2.20>Which smoked with bloody execution,</A><br>
+<A NAME=1.2.21>Like valour's minion carved out his passage</A><br>
+<A NAME=1.2.22>Till he faced the slave;</A><br>
+<A NAME=1.2.23>Which ne'er shook hands, nor bade farewell to him,</A><br>
+<A NAME=1.2.24>Till he unseam'd him from the nave to the chaps,</A><br>
+<A NAME=1.2.25>And fix'd his head upon our battlements.</A><br>
+</blockquote>
+
+<A NAME=speech4><b>DUNCAN</b></a>
+<blockquote>
+<A NAME=1.2.26>O valiant cousin! worthy gentleman!</A><br>
+</blockquote>
+
+<A NAME=speech5><b>Sergeant</b></a>
+<blockquote>
+<A NAME=1.2.27>As whence the sun 'gins his reflection</A><br>
+<A NAME=1.2.28>Shipwrecking storms and direful thunders break,</A><br>
+<A NAME=1.2.29>So from that spring whence comfort seem'd to come</A><br>
+<A NAME=1.2.30>Discomfort swells. Mark, king of Scotland, mark:</A><br>
+<A NAME=1.2.31>No sooner justice had with valour arm'd</A><br>
+<A NAME=1.2.32>Compell'd these skipping kerns to trust their heels,</A><br>
+<A NAME=1.2.33>But the Norweyan lord surveying vantage,</A><br>
+<A NAME=1.2.34>With furbish'd arms and new supplies of men</A><br>
+<A NAME=1.2.35>Began a fresh assault.</A><br>
+</blockquote>
+
+<A NAME=speech6><b>DUNCAN</b></a>
+<blockquote>
+<A NAME=1.2.36>Dismay'd not this</A><br>
+<A NAME=1.2.37>Our captains, Macbeth and Banquo?</A><br>
+</blockquote>
+
+<A NAME=speech7><b>Sergeant</b></a>
+<blockquote>
+<A NAME=1.2.38>Yes;</A><br>
+<A NAME=1.2.39>As sparrows eagles, or the hare the lion.</A><br>
+<A NAME=1.2.40>If I say sooth, I must report they were</A><br>
+<A NAME=1.2.41>As cannons overcharged with double cracks, so they</A><br>
+<A NAME=1.2.42>Doubly redoubled strokes upon the foe:</A><br>
+<A NAME=1.2.43>Except they meant to bathe in reeking wounds,</A><br>
+<A NAME=1.2.44>Or memorise another Golgotha,</A><br>
+<A NAME=1.2.45>I cannot tell.</A><br>
+<A NAME=1.2.46>But I am faint, my gashes cry for help.</A><br>
+</blockquote>
+
+<A NAME=speech8><b>DUNCAN</b></a>
+<blockquote>
+<A NAME=1.2.47>So well thy words become thee as thy wounds;</A><br>
+<A NAME=1.2.48>They smack of honour both. Go get him surgeons.</A><br>
+<p><i>Exit Sergeant, attended</i></p>
+<A NAME=1.2.49>Who comes here?</A><br>
+<p><i>Enter ROSS</i></p>
+</blockquote>
+
+<A NAME=speech9><b>MALCOLM</b></a>
+<blockquote>
+<A NAME=1.2.50> The worthy thane of Ross.</A><br>
+</blockquote>
+
+<A NAME=speech10><b>LENNOX</b></a>
+<blockquote>
+<A NAME=1.2.51>What a haste looks through his eyes! So should he look</A><br>
+<A NAME=1.2.52>That seems to speak things strange.</A><br>
+</blockquote>
+
+<A NAME=speech11><b>ROSS</b></a>
+<blockquote>
+<A NAME=1.2.53>God save the king!</A><br>
+</blockquote>
+
+<A NAME=speech12><b>DUNCAN</b></a>
+<blockquote>
+<A NAME=1.2.54>Whence camest thou, worthy thane?</A><br>
+</blockquote>
+
+<A NAME=speech13><b>ROSS</b></a>
+<blockquote>
+<A NAME=1.2.55>From Fife, great king;</A><br>
+<A NAME=1.2.56>Where the Norweyan banners flout the sky</A><br>
+<A NAME=1.2.57>And fan our people cold. Norway himself,</A><br>
+<A NAME=1.2.58>With terrible numbers,</A><br>
+<A NAME=1.2.59>Assisted by that most disloyal traitor</A><br>
+<A NAME=1.2.60>The thane of Cawdor, began a dismal conflict;</A><br>
+<A NAME=1.2.61>Till that Bellona's bridegroom, lapp'd in proof,</A><br>
+<A NAME=1.2.62>Confronted him with self-comparisons,</A><br>
+<A NAME=1.2.63>Point against point rebellious, arm 'gainst arm.</A><br>
+<A NAME=1.2.64>Curbing his lavish spirit: and, to conclude,</A><br>
+<A NAME=1.2.65>The victory fell on us.</A><br>
+</blockquote>
+
+<A NAME=speech14><b>DUNCAN</b></a>
+<blockquote>
+<A NAME=1.2.66>Great happiness!</A><br>
+</blockquote>
+
+<A NAME=speech15><b>ROSS</b></a>
+<blockquote>
+<A NAME=1.2.67>That now</A><br>
+<A NAME=1.2.68>Sweno, the Norways' king, craves composition:</A><br>
+<A NAME=1.2.69>Nor would we deign him burial of his men</A><br>
+<A NAME=1.2.70>Till he disbursed at Saint Colme's inch</A><br>
+<A NAME=1.2.71>Ten thousand dollars to our general use.</A><br>
+</blockquote>
+
+<A NAME=speech16><b>DUNCAN</b></a>
+<blockquote>
+<A NAME=1.2.72>No more that thane of Cawdor shall deceive</A><br>
+<A NAME=1.2.73>Our bosom interest: go pronounce his present death,</A><br>
+<A NAME=1.2.74>And with his former title greet Macbeth.</A><br>
+</blockquote>
+
+<A NAME=speech17><b>ROSS</b></a>
+<blockquote>
+<A NAME=1.2.75>I'll see it done.</A><br>
+</blockquote>
+
+<A NAME=speech18><b>DUNCAN</b></a>
+<blockquote>
+<A NAME=1.2.76>What he hath lost noble Macbeth hath won.</A><br>
+<p><i>Exeunt</i></p>
+</blockquote>
+<h3>SCENE III. A heath near Forres.</h3>
+<p><blockquote>
+<i>Thunder. Enter the three Witches</i>
+</blockquote>
+
+<A NAME=speech1><b>First Witch</b></a>
+<blockquote>
+<A NAME=1.3.1>Where hast thou been, sister?</A><br>
+</blockquote>
+
+<A NAME=speech2><b>Second Witch</b></a>
+<blockquote>
+<A NAME=1.3.2>Killing swine.</A><br>
+</blockquote>
+
+<A NAME=speech3><b>Third Witch</b></a>
+<blockquote>
+<A NAME=1.3.3>Sister, where thou?</A><br>
+</blockquote>
+
+<A NAME=speech4><b>First Witch</b></a>
+<blockquote>
+<A NAME=1.3.4>A sailor's wife had chestnuts in her lap,</A><br>
+<A NAME=1.3.5>And munch'd, and munch'd, and munch'd:--</A><br>
+<A NAME=1.3.6>'Give me,' quoth I:</A><br>
+<A NAME=1.3.7>'Aroint thee, witch!' the rump-fed ronyon cries.</A><br>
+<A NAME=1.3.8>Her husband's to Aleppo gone, master o' the Tiger:</A><br>
+<A NAME=1.3.9>But in a sieve I'll thither sail,</A><br>
+<A NAME=1.3.10>And, like a rat without a tail,</A><br>
+<A NAME=1.3.11>I'll do, I'll do, and I'll do.</A><br>
+</blockquote>
+
+<A NAME=speech5><b>Second Witch</b></a>
+<blockquote>
+<A NAME=1.3.12>I'll give thee a wind.</A><br>
+</blockquote>
+
+<A NAME=speech6><b>First Witch</b></a>
+<blockquote>
+<A NAME=1.3.13>Thou'rt kind.</A><br>
+</blockquote>
+
+<A NAME=speech7><b>Third Witch</b></a>
+<blockquote>
+<A NAME=1.3.14>And I another.</A><br>
+</blockquote>
+
+<A NAME=speech8><b>First Witch</b></a>
+<blockquote>
+<A NAME=1.3.15>I myself have all the other,</A><br>
+<A NAME=1.3.16>And the very ports they blow,</A><br>
+<A NAME=1.3.17>All the quarters that they know</A><br>
+<A NAME=1.3.18>I' the shipman's card.</A><br>
+<A NAME=1.3.19>I will drain him dry as hay:</A><br>
+<A NAME=1.3.20>Sleep shall neither night nor day</A><br>
+<A NAME=1.3.21>Hang upon his pent-house lid;</A><br>
+<A NAME=1.3.22>He shall live a man forbid:</A><br>
+<A NAME=1.3.23>Weary se'nnights nine times nine</A><br>
+<A NAME=1.3.24>Shall he dwindle, peak and pine:</A><br>
+<A NAME=1.3.25>Though his bark cannot be lost,</A><br>
+<A NAME=1.3.26>Yet it shall be tempest-tost.</A><br>
+<A NAME=1.3.27>Look what I have.</A><br>
+</blockquote>
+
+<A NAME=speech9><b>Second Witch</b></a>
+<blockquote>
+<A NAME=1.3.28>Show me, show me.</A><br>
+</blockquote>
+
+<A NAME=speech10><b>First Witch</b></a>
+<blockquote>
+<A NAME=1.3.29>Here I have a pilot's thumb,</A><br>
+<A NAME=1.3.30>Wreck'd as homeward he did come.</A><br>
+<p><i>Drum within</i></p>
+</blockquote>
+
+<A NAME=speech11><b>Third Witch</b></a>
+<blockquote>
+<A NAME=1.3.31>A drum, a drum!</A><br>
+<A NAME=1.3.32>Macbeth doth come.</A><br>
+</blockquote>
+
+<A NAME=speech12><b>ALL</b></a>
+<blockquote>
+<A NAME=1.3.33>The weird sisters, hand in hand,</A><br>
+<A NAME=1.3.34>Posters of the sea and land,</A><br>
+<A NAME=1.3.35>Thus do go about, about:</A><br>
+<A NAME=1.3.36>Thrice to thine and thrice to mine</A><br>
+<A NAME=1.3.37>And thrice again, to make up nine.</A><br>
+<A NAME=1.3.38>Peace! the charm's wound up.</A><br>
+<p><i>Enter MACBETH and BANQUO</i></p>
+</blockquote>
+
+<A NAME=speech13><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.3.39>So foul and fair a day I have not seen.</A><br>
+</blockquote>
+
+<A NAME=speech14><b>BANQUO</b></a>
+<blockquote>
+<A NAME=1.3.40>How far is't call'd to Forres? What are these</A><br>
+<A NAME=1.3.41>So wither'd and so wild in their attire,</A><br>
+<A NAME=1.3.42>That look not like the inhabitants o' the earth,</A><br>
+<A NAME=1.3.43>And yet are on't? Live you? or are you aught</A><br>
+<A NAME=1.3.44>That man may question? You seem to understand me,</A><br>
+<A NAME=1.3.45>By each at once her chappy finger laying</A><br>
+<A NAME=1.3.46>Upon her skinny lips: you should be women,</A><br>
+<A NAME=1.3.47>And yet your beards forbid me to interpret</A><br>
+<A NAME=1.3.48>That you are so.</A><br>
+</blockquote>
+
+<A NAME=speech15><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.3.49> Speak, if you can: what are you?</A><br>
+</blockquote>
+
+<A NAME=speech16><b>First Witch</b></a>
+<blockquote>
+<A NAME=1.3.50>All hail, Macbeth! hail to thee, thane of Glamis!</A><br>
+</blockquote>
+
+<A NAME=speech17><b>Second Witch</b></a>
+<blockquote>
+<A NAME=1.3.51>All hail, Macbeth, hail to thee, thane of Cawdor!</A><br>
+</blockquote>
+
+<A NAME=speech18><b>Third Witch</b></a>
+<blockquote>
+<A NAME=1.3.52>All hail, Macbeth, thou shalt be king hereafter!</A><br>
+</blockquote>
+
+<A NAME=speech19><b>BANQUO</b></a>
+<blockquote>
+<A NAME=1.3.53>Good sir, why do you start; and seem to fear</A><br>
+<A NAME=1.3.54>Things that do sound so fair? I' the name of truth,</A><br>
+<A NAME=1.3.55>Are ye fantastical, or that indeed</A><br>
+<A NAME=1.3.56>Which outwardly ye show? My noble partner</A><br>
+<A NAME=1.3.57>You greet with present grace and great prediction</A><br>
+<A NAME=1.3.58>Of noble having and of royal hope,</A><br>
+<A NAME=1.3.59>That he seems rapt withal: to me you speak not.</A><br>
+<A NAME=1.3.60>If you can look into the seeds of time,</A><br>
+<A NAME=1.3.61>And say which grain will grow and which will not,</A><br>
+<A NAME=1.3.62>Speak then to me, who neither beg nor fear</A><br>
+<A NAME=1.3.63>Your favours nor your hate.</A><br>
+</blockquote>
+
+<A NAME=speech20><b>First Witch</b></a>
+<blockquote>
+<A NAME=1.3.64>Hail!</A><br>
+</blockquote>
+
+<A NAME=speech21><b>Second Witch</b></a>
+<blockquote>
+<A NAME=1.3.65>Hail!</A><br>
+</blockquote>
+
+<A NAME=speech22><b>Third Witch</b></a>
+<blockquote>
+<A NAME=1.3.66>Hail!</A><br>
+</blockquote>
+
+<A NAME=speech23><b>First Witch</b></a>
+<blockquote>
+<A NAME=1.3.67>Lesser than Macbeth, and greater.</A><br>
+</blockquote>
+
+<A NAME=speech24><b>Second Witch</b></a>
+<blockquote>
+<A NAME=1.3.68>Not so happy, yet much happier.</A><br>
+</blockquote>
+
+<A NAME=speech25><b>Third Witch</b></a>
+<blockquote>
+<A NAME=1.3.69>Thou shalt get kings, though thou be none:</A><br>
+<A NAME=1.3.70>So all hail, Macbeth and Banquo!</A><br>
+</blockquote>
+
+<A NAME=speech26><b>First Witch</b></a>
+<blockquote>
+<A NAME=1.3.71>Banquo and Macbeth, all hail!</A><br>
+</blockquote>
+
+<A NAME=speech27><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.3.72>Stay, you imperfect speakers, tell me more:</A><br>
+<A NAME=1.3.73>By Sinel's death I know I am thane of Glamis;</A><br>
+<A NAME=1.3.74>But how of Cawdor? the thane of Cawdor lives,</A><br>
+<A NAME=1.3.75>A prosperous gentleman; and to be king</A><br>
+<A NAME=1.3.76>Stands not within the prospect of belief,</A><br>
+<A NAME=1.3.77>No more than to be Cawdor. Say from whence</A><br>
+<A NAME=1.3.78>You owe this strange intelligence? or why</A><br>
+<A NAME=1.3.79>Upon this blasted heath you stop our way</A><br>
+<A NAME=1.3.80>With such prophetic greeting? Speak, I charge you.</A><br>
+<p><i>Witches vanish</i></p>
+</blockquote>
+
+<A NAME=speech28><b>BANQUO</b></a>
+<blockquote>
+<A NAME=1.3.81>The earth hath bubbles, as the water has,</A><br>
+<A NAME=1.3.82>And these are of them. Whither are they vanish'd?</A><br>
+</blockquote>
+
+<A NAME=speech29><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.3.83>Into the air; and what seem'd corporal melted</A><br>
+<A NAME=1.3.84>As breath into the wind. Would they had stay'd!</A><br>
+</blockquote>
+
+<A NAME=speech30><b>BANQUO</b></a>
+<blockquote>
+<A NAME=1.3.85>Were such things here as we do speak about?</A><br>
+<A NAME=1.3.86>Or have we eaten on the insane root</A><br>
+<A NAME=1.3.87>That takes the reason prisoner?</A><br>
+</blockquote>
+
+<A NAME=speech31><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.3.88>Your children shall be kings.</A><br>
+</blockquote>
+
+<A NAME=speech32><b>BANQUO</b></a>
+<blockquote>
+<A NAME=1.3.89>You shall be king.</A><br>
+</blockquote>
+
+<A NAME=speech33><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.3.90>And thane of Cawdor too: went it not so?</A><br>
+</blockquote>
+
+<A NAME=speech34><b>BANQUO</b></a>
+<blockquote>
+<A NAME=1.3.91>To the selfsame tune and words. Who's here?</A><br>
+<p><i>Enter ROSS and ANGUS</i></p>
+</blockquote>
+
+<A NAME=speech35><b>ROSS</b></a>
+<blockquote>
+<A NAME=1.3.92>The king hath happily received, Macbeth,</A><br>
+<A NAME=1.3.93>The news of thy success; and when he reads</A><br>
+<A NAME=1.3.94>Thy personal venture in the rebels' fight,</A><br>
+<A NAME=1.3.95>His wonders and his praises do contend</A><br>
+<A NAME=1.3.96>Which should be thine or his: silenced with that,</A><br>
+<A NAME=1.3.97>In viewing o'er the rest o' the selfsame day,</A><br>
+<A NAME=1.3.98>He finds thee in the stout Norweyan ranks,</A><br>
+<A NAME=1.3.99>Nothing afeard of what thyself didst make,</A><br>
+<A NAME=1.3.100>Strange images of death. As thick as hail</A><br>
+<A NAME=1.3.101>Came post with post; and every one did bear</A><br>
+<A NAME=1.3.102>Thy praises in his kingdom's great defence,</A><br>
+<A NAME=1.3.103>And pour'd them down before him.</A><br>
+</blockquote>
+
+<A NAME=speech36><b>ANGUS</b></a>
+<blockquote>
+<A NAME=1.3.104>We are sent</A><br>
+<A NAME=1.3.105>To give thee from our royal master thanks;</A><br>
+<A NAME=1.3.106>Only to herald thee into his sight,</A><br>
+<A NAME=1.3.107>Not pay thee.</A><br>
+</blockquote>
+
+<A NAME=speech37><b>ROSS</b></a>
+<blockquote>
+<A NAME=1.3.108>And, for an earnest of a greater honour,</A><br>
+<A NAME=1.3.109>He bade me, from him, call thee thane of Cawdor:</A><br>
+<A NAME=1.3.110>In which addition, hail, most worthy thane!</A><br>
+<A NAME=1.3.111>For it is thine.</A><br>
+</blockquote>
+
+<A NAME=speech38><b>BANQUO</b></a>
+<blockquote>
+<A NAME=1.3.112> What, can the devil speak true?</A><br>
+</blockquote>
+
+<A NAME=speech39><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.3.113>The thane of Cawdor lives: why do you dress me</A><br>
+<A NAME=1.3.114>In borrow'd robes?</A><br>
+</blockquote>
+
+<A NAME=speech40><b>ANGUS</b></a>
+<blockquote>
+<A NAME=1.3.115> Who was the thane lives yet;</A><br>
+<A NAME=1.3.116>But under heavy judgment bears that life</A><br>
+<A NAME=1.3.117>Which he deserves to lose. Whether he was combined</A><br>
+<A NAME=1.3.118>With those of Norway, or did line the rebel</A><br>
+<A NAME=1.3.119>With hidden help and vantage, or that with both</A><br>
+<A NAME=1.3.120>He labour'd in his country's wreck, I know not;</A><br>
+<A NAME=1.3.121>But treasons capital, confess'd and proved,</A><br>
+<A NAME=1.3.122>Have overthrown him.</A><br>
+</blockquote>
+
+<A NAME=speech41><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.3.123>[Aside] Glamis, and thane of Cawdor!</A><br>
+<A NAME=1.3.124>The greatest is behind.</A><br>
+<p><i>To ROSS and ANGUS</i></p>
+<A NAME=1.3.125>Thanks for your pains.</A><br>
+<p><i>To BANQUO</i></p>
+<A NAME=1.3.126>Do you not hope your children shall be kings,</A><br>
+<A NAME=1.3.127>When those that gave the thane of Cawdor to me</A><br>
+<A NAME=1.3.128>Promised no less to them?</A><br>
+</blockquote>
+
+<A NAME=speech42><b>BANQUO</b></a>
+<blockquote>
+<A NAME=1.3.129>That trusted home</A><br>
+<A NAME=1.3.130>Might yet enkindle you unto the crown,</A><br>
+<A NAME=1.3.131>Besides the thane of Cawdor. But 'tis strange:</A><br>
+<A NAME=1.3.132>And oftentimes, to win us to our harm,</A><br>
+<A NAME=1.3.133>The instruments of darkness tell us truths,</A><br>
+<A NAME=1.3.134>Win us with honest trifles, to betray's</A><br>
+<A NAME=1.3.135>In deepest consequence.</A><br>
+<A NAME=1.3.136>Cousins, a word, I pray you.</A><br>
+</blockquote>
+
+<A NAME=speech43><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.3.137>[Aside] Two truths are told,</A><br>
+<A NAME=1.3.138>As happy prologues to the swelling act</A><br>
+<A NAME=1.3.139>Of the imperial theme.--I thank you, gentlemen.</A><br>
+<p><i>Aside</i></p>
+<A NAME=1.3.140>Cannot be ill, cannot be good: if ill,</A><br>
+<A NAME=1.3.141>Why hath it given me earnest of success,</A><br>
+<A NAME=1.3.142>Commencing in a truth? I am thane of Cawdor:</A><br>
+<A NAME=1.3.143>If good, why do I yield to that suggestion</A><br>
+<A NAME=1.3.144>Whose horrid image doth unfix my hair</A><br>
+<A NAME=1.3.145>And make my seated heart knock at my ribs,</A><br>
+<A NAME=1.3.146>Against the use of nature? Present fears</A><br>
+<A NAME=1.3.147>Are less than horrible imaginings:</A><br>
+<A NAME=1.3.148>My thought, whose murder yet is but fantastical,</A><br>
+<A NAME=1.3.149>Shakes so my single state of man that function</A><br>
+<A NAME=1.3.150>Is smother'd in surmise, and nothing is</A><br>
+<A NAME=1.3.151>But what is not.</A><br>
+</blockquote>
+
+<A NAME=speech44><b>BANQUO</b></a>
+<blockquote>
+<A NAME=1.3.152> Look, how our partner's rapt.</A><br>
+</blockquote>
+
+<A NAME=speech45><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.3.153>[Aside] If chance will have me king, why, chance may crown me,</A><br>
+<A NAME=1.3.154>Without my stir.</A><br>
+</blockquote>
+
+<A NAME=speech46><b>BANQUO</b></a>
+<blockquote>
+<A NAME=1.3.155> New horrors come upon him,</A><br>
+<A NAME=1.3.156>Like our strange garments, cleave not to their mould</A><br>
+<A NAME=1.3.157>But with the aid of use.</A><br>
+</blockquote>
+
+<A NAME=speech47><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.3.158>[Aside] Come what come may,</A><br>
+<A NAME=1.3.159>Time and the hour runs through the roughest day.</A><br>
+</blockquote>
+
+<A NAME=speech48><b>BANQUO</b></a>
+<blockquote>
+<A NAME=1.3.160>Worthy Macbeth, we stay upon your leisure.</A><br>
+</blockquote>
+
+<A NAME=speech49><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.3.161>Give me your favour: my dull brain was wrought</A><br>
+<A NAME=1.3.162>With things forgotten. Kind gentlemen, your pains</A><br>
+<A NAME=1.3.163>Are register'd where every day I turn</A><br>
+<A NAME=1.3.164>The leaf to read them. Let us toward the king.</A><br>
+<A NAME=1.3.165>Think upon what hath chanced, and, at more time,</A><br>
+<A NAME=1.3.166>The interim having weigh'd it, let us speak</A><br>
+<A NAME=1.3.167>Our free hearts each to other.</A><br>
+</blockquote>
+
+<A NAME=speech50><b>BANQUO</b></a>
+<blockquote>
+<A NAME=1.3.168>Very gladly.</A><br>
+</blockquote>
+
+<A NAME=speech51><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.3.169>Till then, enough. Come, friends.</A><br>
+<p><i>Exeunt</i></p>
+</blockquote>
+<h3>SCENE IV. Forres. The palace.</h3>
+<p><blockquote>
+<i>Flourish. Enter DUNCAN, MALCOLM, DONALBAIN, LENNOX, and Attendants</i>
+</blockquote>
+
+<A NAME=speech1><b>DUNCAN</b></a>
+<blockquote>
+<A NAME=1.4.1>Is execution done on Cawdor? Are not</A><br>
+<A NAME=1.4.2>Those in commission yet return'd?</A><br>
+</blockquote>
+
+<A NAME=speech2><b>MALCOLM</b></a>
+<blockquote>
+<A NAME=1.4.3>My liege,</A><br>
+<A NAME=1.4.4>They are not yet come back. But I have spoke</A><br>
+<A NAME=1.4.5>With one that saw him die: who did report</A><br>
+<A NAME=1.4.6>That very frankly he confess'd his treasons,</A><br>
+<A NAME=1.4.7>Implored your highness' pardon and set forth</A><br>
+<A NAME=1.4.8>A deep repentance: nothing in his life</A><br>
+<A NAME=1.4.9>Became him like the leaving it; he died</A><br>
+<A NAME=1.4.10>As one that had been studied in his death</A><br>
+<A NAME=1.4.11>To throw away the dearest thing he owed,</A><br>
+<A NAME=1.4.12>As 'twere a careless trifle.</A><br>
+</blockquote>
+
+<A NAME=speech3><b>DUNCAN</b></a>
+<blockquote>
+<A NAME=1.4.13>There's no art</A><br>
+<A NAME=1.4.14>To find the mind's construction in the face:</A><br>
+<A NAME=1.4.15>He was a gentleman on whom I built</A><br>
+<A NAME=1.4.16>An absolute trust.</A><br>
+<p><i>Enter MACBETH, BANQUO, ROSS, and ANGUS</i></p>
+<A NAME=1.4.17>O worthiest cousin!</A><br>
+<A NAME=1.4.18>The sin of my ingratitude even now</A><br>
+<A NAME=1.4.19>Was heavy on me: thou art so far before</A><br>
+<A NAME=1.4.20>That swiftest wing of recompense is slow</A><br>
+<A NAME=1.4.21>To overtake thee. Would thou hadst less deserved,</A><br>
+<A NAME=1.4.22>That the proportion both of thanks and payment</A><br>
+<A NAME=1.4.23>Might have been mine! only I have left to say,</A><br>
+<A NAME=1.4.24>More is thy due than more than all can pay.</A><br>
+</blockquote>
+
+<A NAME=speech4><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.4.25>The service and the loyalty I owe,</A><br>
+<A NAME=1.4.26>In doing it, pays itself. Your highness' part</A><br>
+<A NAME=1.4.27>Is to receive our duties; and our duties</A><br>
+<A NAME=1.4.28>Are to your throne and state children and servants,</A><br>
+<A NAME=1.4.29>Which do but what they should, by doing every thing</A><br>
+<A NAME=1.4.30>Safe toward your love and honour.</A><br>
+</blockquote>
+
+<A NAME=speech5><b>DUNCAN</b></a>
+<blockquote>
+<A NAME=1.4.31>Welcome hither:</A><br>
+<A NAME=1.4.32>I have begun to plant thee, and will labour</A><br>
+<A NAME=1.4.33>To make thee full of growing. Noble Banquo,</A><br>
+<A NAME=1.4.34>That hast no less deserved, nor must be known</A><br>
+<A NAME=1.4.35>No less to have done so, let me enfold thee</A><br>
+<A NAME=1.4.36>And hold thee to my heart.</A><br>
+</blockquote>
+
+<A NAME=speech6><b>BANQUO</b></a>
+<blockquote>
+<A NAME=1.4.37>There if I grow,</A><br>
+<A NAME=1.4.38>The harvest is your own.</A><br>
+</blockquote>
+
+<A NAME=speech7><b>DUNCAN</b></a>
+<blockquote>
+<A NAME=1.4.39>My plenteous joys,</A><br>
+<A NAME=1.4.40>Wanton in fulness, seek to hide themselves</A><br>
+<A NAME=1.4.41>In drops of sorrow. Sons, kinsmen, thanes,</A><br>
+<A NAME=1.4.42>And you whose places are the nearest, know</A><br>
+<A NAME=1.4.43>We will establish our estate upon</A><br>
+<A NAME=1.4.44>Our eldest, Malcolm, whom we name hereafter</A><br>
+<A NAME=1.4.45>The Prince of Cumberland; which honour must</A><br>
+<A NAME=1.4.46>Not unaccompanied invest him only,</A><br>
+<A NAME=1.4.47>But signs of nobleness, like stars, shall shine</A><br>
+<A NAME=1.4.48>On all deservers. From hence to Inverness,</A><br>
+<A NAME=1.4.49>And bind us further to you.</A><br>
+</blockquote>
+
+<A NAME=speech8><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.4.50>The rest is labour, which is not used for you:</A><br>
+<A NAME=1.4.51>I'll be myself the harbinger and make joyful</A><br>
+<A NAME=1.4.52>The hearing of my wife with your approach;</A><br>
+<A NAME=1.4.53>So humbly take my leave.</A><br>
+</blockquote>
+
+<A NAME=speech9><b>DUNCAN</b></a>
+<blockquote>
+<A NAME=1.4.54>My worthy Cawdor!</A><br>
+</blockquote>
+
+<A NAME=speech10><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.4.55>[Aside] The Prince of Cumberland! that is a step</A><br>
+<A NAME=1.4.56>On which I must fall down, or else o'erleap,</A><br>
+<A NAME=1.4.57>For in my way it lies. Stars, hide your fires;</A><br>
+<A NAME=1.4.58>Let not light see my black and deep desires:</A><br>
+<A NAME=1.4.59>The eye wink at the hand; yet let that be,</A><br>
+<A NAME=1.4.60>Which the eye fears, when it is done, to see.</A><br>
+<p><i>Exit</i></p>
+</blockquote>
+
+<A NAME=speech11><b>DUNCAN</b></a>
+<blockquote>
+<A NAME=1.4.61>True, worthy Banquo; he is full so valiant,</A><br>
+<A NAME=1.4.62>And in his commendations I am fed;</A><br>
+<A NAME=1.4.63>It is a banquet to me. Let's after him,</A><br>
+<A NAME=1.4.64>Whose care is gone before to bid us welcome:</A><br>
+<A NAME=1.4.65>It is a peerless kinsman.</A><br>
+<p><i>Flourish. Exeunt</i></p>
+</blockquote>
+<h3>SCENE V. Inverness. Macbeth's castle.</h3>
+<p><blockquote>
+<i>Enter LADY MACBETH, reading a letter</i>
+</blockquote>
+
+<A NAME=speech1><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=1.5.1>'They met me in the day of success: and I have</A><br>
+<A NAME=1.5.2>learned by the perfectest report, they have more in</A><br>
+<A NAME=1.5.3>them than mortal knowledge. When I burned in desire</A><br>
+<A NAME=1.5.4>to question them further, they made themselves air,</A><br>
+<A NAME=1.5.5>into which they vanished. Whiles I stood rapt in</A><br>
+<A NAME=1.5.6>the wonder of it, came missives from the king, who</A><br>
+<A NAME=1.5.7>all-hailed me 'Thane of Cawdor;' by which title,</A><br>
+<A NAME=1.5.8>before, these weird sisters saluted me, and referred</A><br>
+<A NAME=1.5.9>me to the coming on of time, with 'Hail, king that</A><br>
+<A NAME=1.5.10>shalt be!' This have I thought good to deliver</A><br>
+<A NAME=1.5.11>thee, my dearest partner of greatness, that thou</A><br>
+<A NAME=1.5.12>mightst not lose the dues of rejoicing, by being</A><br>
+<A NAME=1.5.13>ignorant of what greatness is promised thee. Lay it</A><br>
+<A NAME=1.5.14>to thy heart, and farewell.'</A><br>
+<A NAME=1.5.15>Glamis thou art, and Cawdor; and shalt be</A><br>
+<A NAME=1.5.16>What thou art promised: yet do I fear thy nature;</A><br>
+<A NAME=1.5.17>It is too full o' the milk of human kindness</A><br>
+<A NAME=1.5.18>To catch the nearest way: thou wouldst be great;</A><br>
+<A NAME=1.5.19>Art not without ambition, but without</A><br>
+<A NAME=1.5.20>The illness should attend it: what thou wouldst highly,</A><br>
+<A NAME=1.5.21>That wouldst thou holily; wouldst not play false,</A><br>
+<A NAME=1.5.22>And yet wouldst wrongly win: thou'ldst have, great Glamis,</A><br>
+<A NAME=1.5.23>That which cries 'Thus thou must do, if thou have it;</A><br>
+<A NAME=1.5.24>And that which rather thou dost fear to do</A><br>
+<A NAME=1.5.25>Than wishest should be undone.' Hie thee hither,</A><br>
+<A NAME=1.5.26>That I may pour my spirits in thine ear;</A><br>
+<A NAME=1.5.27>And chastise with the valour of my tongue</A><br>
+<A NAME=1.5.28>All that impedes thee from the golden round,</A><br>
+<A NAME=1.5.29>Which fate and metaphysical aid doth seem</A><br>
+<A NAME=1.5.30>To have thee crown'd withal.</A><br>
+<p><i>Enter a Messenger</i></p>
+<A NAME=1.5.31>What is your tidings?</A><br>
+</blockquote>
+
+<A NAME=speech2><b>Messenger</b></a>
+<blockquote>
+<A NAME=1.5.32>The king comes here to-night.</A><br>
+</blockquote>
+
+<A NAME=speech3><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=1.5.33>Thou'rt mad to say it:</A><br>
+<A NAME=1.5.34>Is not thy master with him? who, were't so,</A><br>
+<A NAME=1.5.35>Would have inform'd for preparation.</A><br>
+</blockquote>
+
+<A NAME=speech4><b>Messenger</b></a>
+<blockquote>
+<A NAME=1.5.36>So please you, it is true: our thane is coming:</A><br>
+<A NAME=1.5.37>One of my fellows had the speed of him,</A><br>
+<A NAME=1.5.38>Who, almost dead for breath, had scarcely more</A><br>
+<A NAME=1.5.39>Than would make up his message.</A><br>
+</blockquote>
+
+<A NAME=speech5><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=1.5.40>Give him tending;</A><br>
+<A NAME=1.5.41>He brings great news.</A><br>
+<p><i>Exit Messenger</i></p>
+<A NAME=1.5.42>The raven himself is hoarse</A><br>
+<A NAME=1.5.43>That croaks the fatal entrance of Duncan</A><br>
+<A NAME=1.5.44>Under my battlements. Come, you spirits</A><br>
+<A NAME=1.5.45>That tend on mortal thoughts, unsex me here,</A><br>
+<A NAME=1.5.46>And fill me from the crown to the toe top-full</A><br>
+<A NAME=1.5.47>Of direst cruelty! make thick my blood;</A><br>
+<A NAME=1.5.48>Stop up the access and passage to remorse,</A><br>
+<A NAME=1.5.49>That no compunctious visitings of nature</A><br>
+<A NAME=1.5.50>Shake my fell purpose, nor keep peace between</A><br>
+<A NAME=1.5.51>The effect and it! Come to my woman's breasts,</A><br>
+<A NAME=1.5.52>And take my milk for gall, you murdering ministers,</A><br>
+<A NAME=1.5.53>Wherever in your sightless substances</A><br>
+<A NAME=1.5.54>You wait on nature's mischief! Come, thick night,</A><br>
+<A NAME=1.5.55>And pall thee in the dunnest smoke of hell,</A><br>
+<A NAME=1.5.56>That my keen knife see not the wound it makes,</A><br>
+<A NAME=1.5.57>Nor heaven peep through the blanket of the dark,</A><br>
+<A NAME=1.5.58>To cry 'Hold, hold!'</A><br>
+<p><i>Enter MACBETH</i></p>
+<A NAME=1.5.59>Great Glamis! worthy Cawdor!</A><br>
+<A NAME=1.5.60>Greater than both, by the all-hail hereafter!</A><br>
+<A NAME=1.5.61>Thy letters have transported me beyond</A><br>
+<A NAME=1.5.62>This ignorant present, and I feel now</A><br>
+<A NAME=1.5.63>The future in the instant.</A><br>
+</blockquote>
+
+<A NAME=speech6><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.5.64>My dearest love,</A><br>
+<A NAME=1.5.65>Duncan comes here to-night.</A><br>
+</blockquote>
+
+<A NAME=speech7><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=1.5.66>And when goes hence?</A><br>
+</blockquote>
+
+<A NAME=speech8><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.5.67>To-morrow, as he purposes.</A><br>
+</blockquote>
+
+<A NAME=speech9><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=1.5.68>O, never</A><br>
+<A NAME=1.5.69>Shall sun that morrow see!</A><br>
+<A NAME=1.5.70>Your face, my thane, is as a book where men</A><br>
+<A NAME=1.5.71>May read strange matters. To beguile the time,</A><br>
+<A NAME=1.5.72>Look like the time; bear welcome in your eye,</A><br>
+<A NAME=1.5.73>Your hand, your tongue: look like the innocent flower,</A><br>
+<A NAME=1.5.74>But be the serpent under't. He that's coming</A><br>
+<A NAME=1.5.75>Must be provided for: and you shall put</A><br>
+<A NAME=1.5.76>This night's great business into my dispatch;</A><br>
+<A NAME=1.5.77>Which shall to all our nights and days to come</A><br>
+<A NAME=1.5.78>Give solely sovereign sway and masterdom.</A><br>
+</blockquote>
+
+<A NAME=speech10><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.5.79>We will speak further.</A><br>
+</blockquote>
+
+<A NAME=speech11><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=1.5.80>Only look up clear;</A><br>
+<A NAME=1.5.81>To alter favour ever is to fear:</A><br>
+<A NAME=1.5.82>Leave all the rest to me.</A><br>
+<p><i>Exeunt</i></p>
+</blockquote>
+<h3>SCENE VI. Before Macbeth's castle.</h3>
+<p><blockquote>
+<i>Hautboys and torches. Enter DUNCAN, MALCOLM, DONALBAIN, BANQUO, LENNOX, MACDUFF, ROSS, ANGUS, and Attendants</i>
+</blockquote>
+
+<A NAME=speech1><b>DUNCAN</b></a>
+<blockquote>
+<A NAME=1.6.1>This castle hath a pleasant seat; the air</A><br>
+<A NAME=1.6.2>Nimbly and sweetly recommends itself</A><br>
+<A NAME=1.6.3>Unto our gentle senses.</A><br>
+</blockquote>
+
+<A NAME=speech2><b>BANQUO</b></a>
+<blockquote>
+<A NAME=1.6.4>This guest of summer,</A><br>
+<A NAME=1.6.5>The temple-haunting martlet, does approve,</A><br>
+<A NAME=1.6.6>By his loved mansionry, that the heaven's breath</A><br>
+<A NAME=1.6.7>Smells wooingly here: no jutty, frieze,</A><br>
+<A NAME=1.6.8>Buttress, nor coign of vantage, but this bird</A><br>
+<A NAME=1.6.9>Hath made his pendent bed and procreant cradle:</A><br>
+<A NAME=1.6.10>Where they most breed and haunt, I have observed,</A><br>
+<A NAME=1.6.11>The air is delicate.</A><br>
+<p><i>Enter LADY MACBETH</i></p>
+</blockquote>
+
+<A NAME=speech3><b>DUNCAN</b></a>
+<blockquote>
+<A NAME=1.6.12>See, see, our honour'd hostess!</A><br>
+<A NAME=1.6.13>The love that follows us sometime is our trouble,</A><br>
+<A NAME=1.6.14>Which still we thank as love. Herein I teach you</A><br>
+<A NAME=1.6.15>How you shall bid God 'ild us for your pains,</A><br>
+<A NAME=1.6.16>And thank us for your trouble.</A><br>
+</blockquote>
+
+<A NAME=speech4><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=1.6.17>All our service</A><br>
+<A NAME=1.6.18>In every point twice done and then done double</A><br>
+<A NAME=1.6.19>Were poor and single business to contend</A><br>
+<A NAME=1.6.20>Against those honours deep and broad wherewith</A><br>
+<A NAME=1.6.21>Your majesty loads our house: for those of old,</A><br>
+<A NAME=1.6.22>And the late dignities heap'd up to them,</A><br>
+<A NAME=1.6.23>We rest your hermits.</A><br>
+</blockquote>
+
+<A NAME=speech5><b>DUNCAN</b></a>
+<blockquote>
+<A NAME=1.6.24>Where's the thane of Cawdor?</A><br>
+<A NAME=1.6.25>We coursed him at the heels, and had a purpose</A><br>
+<A NAME=1.6.26>To be his purveyor: but he rides well;</A><br>
+<A NAME=1.6.27>And his great love, sharp as his spur, hath holp him</A><br>
+<A NAME=1.6.28>To his home before us. Fair and noble hostess,</A><br>
+<A NAME=1.6.29>We are your guest to-night.</A><br>
+</blockquote>
+
+<A NAME=speech6><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=1.6.30>Your servants ever</A><br>
+<A NAME=1.6.31>Have theirs, themselves and what is theirs, in compt,</A><br>
+<A NAME=1.6.32>To make their audit at your highness' pleasure,</A><br>
+<A NAME=1.6.33>Still to return your own.</A><br>
+</blockquote>
+
+<A NAME=speech7><b>DUNCAN</b></a>
+<blockquote>
+<A NAME=1.6.34>Give me your hand;</A><br>
+<A NAME=1.6.35>Conduct me to mine host: we love him highly,</A><br>
+<A NAME=1.6.36>And shall continue our graces towards him.</A><br>
+<A NAME=1.6.37>By your leave, hostess.</A><br>
+<p><i>Exeunt</i></p>
+</blockquote>
+<h3>SCENE VII. Macbeth's castle.</h3>
+<p><blockquote>
+<i>Hautboys and torches. Enter a Sewer, and divers Servants with dishes and service, and pass over the stage. Then enter MACBETH</i>
+</blockquote>
+
+<A NAME=speech1><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.7.1>If it were done when 'tis done, then 'twere well</A><br>
+<A NAME=1.7.2>It were done quickly: if the assassination</A><br>
+<A NAME=1.7.3>Could trammel up the consequence, and catch</A><br>
+<A NAME=1.7.4>With his surcease success; that but this blow</A><br>
+<A NAME=1.7.5>Might be the be-all and the end-all here,</A><br>
+<A NAME=1.7.6>But here, upon this bank and shoal of time,</A><br>
+<A NAME=1.7.7>We'ld jump the life to come. But in these cases</A><br>
+<A NAME=1.7.8>We still have judgment here; that we but teach</A><br>
+<A NAME=1.7.9>Bloody instructions, which, being taught, return</A><br>
+<A NAME=1.7.10>To plague the inventor: this even-handed justice</A><br>
+<A NAME=1.7.11>Commends the ingredients of our poison'd chalice</A><br>
+<A NAME=1.7.12>To our own lips. He's here in double trust;</A><br>
+<A NAME=1.7.13>First, as I am his kinsman and his subject,</A><br>
+<A NAME=1.7.14>Strong both against the deed; then, as his host,</A><br>
+<A NAME=1.7.15>Who should against his murderer shut the door,</A><br>
+<A NAME=1.7.16>Not bear the knife myself. Besides, this Duncan</A><br>
+<A NAME=1.7.17>Hath borne his faculties so meek, hath been</A><br>
+<A NAME=1.7.18>So clear in his great office, that his virtues</A><br>
+<A NAME=1.7.19>Will plead like angels, trumpet-tongued, against</A><br>
+<A NAME=1.7.20>The deep damnation of his taking-off;</A><br>
+<A NAME=1.7.21>And pity, like a naked new-born babe,</A><br>
+<A NAME=1.7.22>Striding the blast, or heaven's cherubim, horsed</A><br>
+<A NAME=1.7.23>Upon the sightless couriers of the air,</A><br>
+<A NAME=1.7.24>Shall blow the horrid deed in every eye,</A><br>
+<A NAME=1.7.25>That tears shall drown the wind. I have no spur</A><br>
+<A NAME=1.7.26>To prick the sides of my intent, but only</A><br>
+<A NAME=1.7.27>Vaulting ambition, which o'erleaps itself</A><br>
+<A NAME=1.7.28>And falls on the other.</A><br>
+<p><i>Enter LADY MACBETH</i></p>
+<A NAME=1.7.29>How now! what news?</A><br>
+</blockquote>
+
+<A NAME=speech2><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=1.7.30>He has almost supp'd: why have you left the chamber?</A><br>
+</blockquote>
+
+<A NAME=speech3><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.7.31>Hath he ask'd for me?</A><br>
+</blockquote>
+
+<A NAME=speech4><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=1.7.32>Know you not he has?</A><br>
+</blockquote>
+
+<A NAME=speech5><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.7.33>We will proceed no further in this business:</A><br>
+<A NAME=1.7.34>He hath honour'd me of late; and I have bought</A><br>
+<A NAME=1.7.35>Golden opinions from all sorts of people,</A><br>
+<A NAME=1.7.36>Which would be worn now in their newest gloss,</A><br>
+<A NAME=1.7.37>Not cast aside so soon.</A><br>
+</blockquote>
+
+<A NAME=speech6><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=1.7.38>Was the hope drunk</A><br>
+<A NAME=1.7.39>Wherein you dress'd yourself? hath it slept since?</A><br>
+<A NAME=1.7.40>And wakes it now, to look so green and pale</A><br>
+<A NAME=1.7.41>At what it did so freely? From this time</A><br>
+<A NAME=1.7.42>Such I account thy love. Art thou afeard</A><br>
+<A NAME=1.7.43>To be the same in thine own act and valour</A><br>
+<A NAME=1.7.44>As thou art in desire? Wouldst thou have that</A><br>
+<A NAME=1.7.45>Which thou esteem'st the ornament of life,</A><br>
+<A NAME=1.7.46>And live a coward in thine own esteem,</A><br>
+<A NAME=1.7.47>Letting 'I dare not' wait upon 'I would,'</A><br>
+<A NAME=1.7.48>Like the poor cat i' the adage?</A><br>
+</blockquote>
+
+<A NAME=speech7><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.7.49>Prithee, peace:</A><br>
+<A NAME=1.7.50>I dare do all that may become a man;</A><br>
+<A NAME=1.7.51>Who dares do more is none.</A><br>
+</blockquote>
+
+<A NAME=speech8><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=1.7.52>What beast was't, then,</A><br>
+<A NAME=1.7.53>That made you break this enterprise to me?</A><br>
+<A NAME=1.7.54>When you durst do it, then you were a man;</A><br>
+<A NAME=1.7.55>And, to be more than what you were, you would</A><br>
+<A NAME=1.7.56>Be so much more the man. Nor time nor place</A><br>
+<A NAME=1.7.57>Did then adhere, and yet you would make both:</A><br>
+<A NAME=1.7.58>They have made themselves, and that their fitness now</A><br>
+<A NAME=1.7.59>Does unmake you. I have given suck, and know</A><br>
+<A NAME=1.7.60>How tender 'tis to love the babe that milks me:</A><br>
+<A NAME=1.7.61>I would, while it was smiling in my face,</A><br>
+<A NAME=1.7.62>Have pluck'd my nipple from his boneless gums,</A><br>
+<A NAME=1.7.63>And dash'd the brains out, had I so sworn as you</A><br>
+<A NAME=1.7.64>Have done to this.</A><br>
+</blockquote>
+
+<A NAME=speech9><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.7.65> If we should fail?</A><br>
+</blockquote>
+
+<A NAME=speech10><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=1.7.66>We fail!</A><br>
+<A NAME=1.7.67>But screw your courage to the sticking-place,</A><br>
+<A NAME=1.7.68>And we'll not fail. When Duncan is asleep--</A><br>
+<A NAME=1.7.69>Whereto the rather shall his day's hard journey</A><br>
+<A NAME=1.7.70>Soundly invite him--his two chamberlains</A><br>
+<A NAME=1.7.71>Will I with wine and wassail so convince</A><br>
+<A NAME=1.7.72>That memory, the warder of the brain,</A><br>
+<A NAME=1.7.73>Shall be a fume, and the receipt of reason</A><br>
+<A NAME=1.7.74>A limbeck only: when in swinish sleep</A><br>
+<A NAME=1.7.75>Their drenched natures lie as in a death,</A><br>
+<A NAME=1.7.76>What cannot you and I perform upon</A><br>
+<A NAME=1.7.77>The unguarded Duncan? what not put upon</A><br>
+<A NAME=1.7.78>His spongy officers, who shall bear the guilt</A><br>
+<A NAME=1.7.79>Of our great quell?</A><br>
+</blockquote>
+
+<A NAME=speech11><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.7.80>Bring forth men-children only;</A><br>
+<A NAME=1.7.81>For thy undaunted mettle should compose</A><br>
+<A NAME=1.7.82>Nothing but males. Will it not be received,</A><br>
+<A NAME=1.7.83>When we have mark'd with blood those sleepy two</A><br>
+<A NAME=1.7.84>Of his own chamber and used their very daggers,</A><br>
+<A NAME=1.7.85>That they have done't?</A><br>
+</blockquote>
+
+<A NAME=speech12><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=1.7.86>Who dares receive it other,</A><br>
+<A NAME=1.7.87>As we shall make our griefs and clamour roar</A><br>
+<A NAME=1.7.88>Upon his death?</A><br>
+</blockquote>
+
+<A NAME=speech13><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.7.89> I am settled, and bend up</A><br>
+<A NAME=1.7.90>Each corporal agent to this terrible feat.</A><br>
+<A NAME=1.7.91>Away, and mock the time with fairest show:</A><br>
+<A NAME=1.7.92>False face must hide what the false heart doth know.</A><br>
+<p><i>Exeunt</i></p>
+</blockquote><p>
+<H3>ACT II</h3>
+<h3>SCENE I. Court of Macbeth's castle.</h3>
+<p><blockquote>
+<i>Enter BANQUO, and FLEANCE bearing a torch before him</i>
+</blockquote>
+
+<A NAME=speech1><b>BANQUO</b></a>
+<blockquote>
+<A NAME=2.1.1>How goes the night, boy?</A><br>
+</blockquote>
+
+<A NAME=speech2><b>FLEANCE</b></a>
+<blockquote>
+<A NAME=2.1.2>The moon is down; I have not heard the clock.</A><br>
+</blockquote>
+
+<A NAME=speech3><b>BANQUO</b></a>
+<blockquote>
+<A NAME=2.1.3>And she goes down at twelve.</A><br>
+</blockquote>
+
+<A NAME=speech4><b>FLEANCE</b></a>
+<blockquote>
+<A NAME=2.1.4>I take't, 'tis later, sir.</A><br>
+</blockquote>
+
+<A NAME=speech5><b>BANQUO</b></a>
+<blockquote>
+<A NAME=2.1.5>Hold, take my sword. There's husbandry in heaven;</A><br>
+<A NAME=2.1.6>Their candles are all out. Take thee that too.</A><br>
+<A NAME=2.1.7>A heavy summons lies like lead upon me,</A><br>
+<A NAME=2.1.8>And yet I would not sleep: merciful powers,</A><br>
+<A NAME=2.1.9>Restrain in me the cursed thoughts that nature</A><br>
+<A NAME=2.1.10>Gives way to in repose!</A><br>
+<p><i>Enter MACBETH, and a Servant with a torch</i></p>
+<A NAME=2.1.11>Give me my sword.</A><br>
+<A NAME=2.1.12>Who's there?</A><br>
+</blockquote>
+
+<A NAME=speech6><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.1.13>A friend.</A><br>
+</blockquote>
+
+<A NAME=speech7><b>BANQUO</b></a>
+<blockquote>
+<A NAME=2.1.14>What, sir, not yet at rest? The king's a-bed:</A><br>
+<A NAME=2.1.15>He hath been in unusual pleasure, and</A><br>
+<A NAME=2.1.16>Sent forth great largess to your offices.</A><br>
+<A NAME=2.1.17>This diamond he greets your wife withal,</A><br>
+<A NAME=2.1.18>By the name of most kind hostess; and shut up</A><br>
+<A NAME=2.1.19>In measureless content.</A><br>
+</blockquote>
+
+<A NAME=speech8><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.1.20>Being unprepared,</A><br>
+<A NAME=2.1.21>Our will became the servant to defect;</A><br>
+<A NAME=2.1.22>Which else should free have wrought.</A><br>
+</blockquote>
+
+<A NAME=speech9><b>BANQUO</b></a>
+<blockquote>
+<A NAME=2.1.23>All's well.</A><br>
+<A NAME=2.1.24>I dreamt last night of the three weird sisters:</A><br>
+<A NAME=2.1.25>To you they have show'd some truth.</A><br>
+</blockquote>
+
+<A NAME=speech10><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.1.26>I think not of them:</A><br>
+<A NAME=2.1.27>Yet, when we can entreat an hour to serve,</A><br>
+<A NAME=2.1.28>We would spend it in some words upon that business,</A><br>
+<A NAME=2.1.29>If you would grant the time.</A><br>
+</blockquote>
+
+<A NAME=speech11><b>BANQUO</b></a>
+<blockquote>
+<A NAME=2.1.30>At your kind'st leisure.</A><br>
+</blockquote>
+
+<A NAME=speech12><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.1.31>If you shall cleave to my consent, when 'tis,</A><br>
+<A NAME=2.1.32>It shall make honour for you.</A><br>
+</blockquote>
+
+<A NAME=speech13><b>BANQUO</b></a>
+<blockquote>
+<A NAME=2.1.33>So I lose none</A><br>
+<A NAME=2.1.34>In seeking to augment it, but still keep</A><br>
+<A NAME=2.1.35>My bosom franchised and allegiance clear,</A><br>
+<A NAME=2.1.36>I shall be counsell'd.</A><br>
+</blockquote>
+
+<A NAME=speech14><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.1.37>Good repose the while!</A><br>
+</blockquote>
+
+<A NAME=speech15><b>BANQUO</b></a>
+<blockquote>
+<A NAME=2.1.38>Thanks, sir: the like to you!</A><br>
+<p><i>Exeunt BANQUO and FLEANCE</i></p>
+</blockquote>
+
+<A NAME=speech16><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.1.39>Go bid thy mistress, when my drink is ready,</A><br>
+<A NAME=2.1.40>She strike upon the bell. Get thee to bed.</A><br>
+<p><i>Exit Servant</i></p>
+<A NAME=2.1.41>Is this a dagger which I see before me,</A><br>
+<A NAME=2.1.42>The handle toward my hand? Come, let me clutch thee.</A><br>
+<A NAME=2.1.43>I have thee not, and yet I see thee still.</A><br>
+<A NAME=2.1.44>Art thou not, fatal vision, sensible</A><br>
+<A NAME=2.1.45>To feeling as to sight? or art thou but</A><br>
+<A NAME=2.1.46>A dagger of the mind, a false creation,</A><br>
+<A NAME=2.1.47>Proceeding from the heat-oppressed brain?</A><br>
+<A NAME=2.1.48>I see thee yet, in form as palpable</A><br>
+<A NAME=2.1.49>As this which now I draw.</A><br>
+<A NAME=2.1.50>Thou marshall'st me the way that I was going;</A><br>
+<A NAME=2.1.51>And such an instrument I was to use.</A><br>
+<A NAME=2.1.52>Mine eyes are made the fools o' the other senses,</A><br>
+<A NAME=2.1.53>Or else worth all the rest; I see thee still,</A><br>
+<A NAME=2.1.54>And on thy blade and dudgeon gouts of blood,</A><br>
+<A NAME=2.1.55>Which was not so before. There's no such thing:</A><br>
+<A NAME=2.1.56>It is the bloody business which informs</A><br>
+<A NAME=2.1.57>Thus to mine eyes. Now o'er the one halfworld</A><br>
+<A NAME=2.1.58>Nature seems dead, and wicked dreams abuse</A><br>
+<A NAME=2.1.59>The curtain'd sleep; witchcraft celebrates</A><br>
+<A NAME=2.1.60>Pale Hecate's offerings, and wither'd murder,</A><br>
+<A NAME=2.1.61>Alarum'd by his sentinel, the wolf,</A><br>
+<A NAME=2.1.62>Whose howl's his watch, thus with his stealthy pace.</A><br>
+<A NAME=2.1.63>With Tarquin's ravishing strides, towards his design</A><br>
+<A NAME=2.1.64>Moves like a ghost. Thou sure and firm-set earth,</A><br>
+<A NAME=2.1.65>Hear not my steps, which way they walk, for fear</A><br>
+<A NAME=2.1.66>Thy very stones prate of my whereabout,</A><br>
+<A NAME=2.1.67>And take the present horror from the time,</A><br>
+<A NAME=2.1.68>Which now suits with it. Whiles I threat, he lives:</A><br>
+<A NAME=2.1.69>Words to the heat of deeds too cold breath gives.</A><br>
+<p><i>A bell rings</i></p>
+<A NAME=2.1.70>I go, and it is done; the bell invites me.</A><br>
+<A NAME=2.1.71>Hear it not, Duncan; for it is a knell</A><br>
+<A NAME=2.1.72>That summons thee to heaven or to hell.</A><br>
+<p><i>Exit</i></p>
+</blockquote>
+<h3>SCENE II. The same.</h3>
+<p><blockquote>
+<i>Enter LADY MACBETH</i>
+</blockquote>
+
+<A NAME=speech1><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.1>That which hath made them drunk hath made me bold;</A><br>
+<A NAME=2.2.2>What hath quench'd them hath given me fire.</A><br>
+<A NAME=2.2.3>Hark! Peace!</A><br>
+<A NAME=2.2.4>It was the owl that shriek'd, the fatal bellman,</A><br>
+<A NAME=2.2.5>Which gives the stern'st good-night. He is about it:</A><br>
+<A NAME=2.2.6>The doors are open; and the surfeited grooms</A><br>
+<A NAME=2.2.7>Do mock their charge with snores: I have drugg'd</A><br>
+<A NAME=2.2.8>their possets,</A><br>
+<A NAME=2.2.9>That death and nature do contend about them,</A><br>
+<A NAME=2.2.10>Whether they live or die.</A><br>
+</blockquote>
+
+<A NAME=speech2><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.11>[Within] Who's there? what, ho!</A><br>
+</blockquote>
+
+<A NAME=speech3><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.12>Alack, I am afraid they have awaked,</A><br>
+<A NAME=2.2.13>And 'tis not done. The attempt and not the deed</A><br>
+<A NAME=2.2.14>Confounds us. Hark! I laid their daggers ready;</A><br>
+<A NAME=2.2.15>He could not miss 'em. Had he not resembled</A><br>
+<A NAME=2.2.16>My father as he slept, I had done't.</A><br>
+<p><i>Enter MACBETH</i></p>
+<A NAME=2.2.17>My husband!</A><br>
+</blockquote>
+
+<A NAME=speech4><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.18>I have done the deed. Didst thou not hear a noise?</A><br>
+</blockquote>
+
+<A NAME=speech5><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.19>I heard the owl scream and the crickets cry.</A><br>
+<A NAME=2.2.20>Did not you speak?</A><br>
+</blockquote>
+
+<A NAME=speech6><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.21> When?</A><br>
+</blockquote>
+
+<A NAME=speech7><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.22>Now.</A><br>
+</blockquote>
+
+<A NAME=speech8><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.23>As I descended?</A><br>
+</blockquote>
+
+<A NAME=speech9><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.24>Ay.</A><br>
+</blockquote>
+
+<A NAME=speech10><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.25>Hark!</A><br>
+<A NAME=2.2.26>Who lies i' the second chamber?</A><br>
+</blockquote>
+
+<A NAME=speech11><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.27>Donalbain.</A><br>
+</blockquote>
+
+<A NAME=speech12><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.28>This is a sorry sight.</A><br>
+<p><i>Looking on his hands</i></p>
+</blockquote>
+
+<A NAME=speech13><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.29>A foolish thought, to say a sorry sight.</A><br>
+</blockquote>
+
+<A NAME=speech14><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.30>There's one did laugh in's sleep, and one cried</A><br>
+<A NAME=2.2.31>'Murder!'</A><br>
+<A NAME=2.2.32>That they did wake each other: I stood and heard them:</A><br>
+<A NAME=2.2.33>But they did say their prayers, and address'd them</A><br>
+<A NAME=2.2.34>Again to sleep.</A><br>
+</blockquote>
+
+<A NAME=speech15><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.35> There are two lodged together.</A><br>
+</blockquote>
+
+<A NAME=speech16><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.36>One cried 'God bless us!' and 'Amen' the other;</A><br>
+<A NAME=2.2.37>As they had seen me with these hangman's hands.</A><br>
+<A NAME=2.2.38>Listening their fear, I could not say 'Amen,'</A><br>
+<A NAME=2.2.39>When they did say 'God bless us!'</A><br>
+</blockquote>
+
+<A NAME=speech17><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.40>Consider it not so deeply.</A><br>
+</blockquote>
+
+<A NAME=speech18><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.41>But wherefore could not I pronounce 'Amen'?</A><br>
+<A NAME=2.2.42>I had most need of blessing, and 'Amen'</A><br>
+<A NAME=2.2.43>Stuck in my throat.</A><br>
+</blockquote>
+
+<A NAME=speech19><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.44>These deeds must not be thought</A><br>
+<A NAME=2.2.45>After these ways; so, it will make us mad.</A><br>
+</blockquote>
+
+<A NAME=speech20><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.46>Methought I heard a voice cry 'Sleep no more!</A><br>
+<A NAME=2.2.47>Macbeth does murder sleep', the innocent sleep,</A><br>
+<A NAME=2.2.48>Sleep that knits up the ravell'd sleeve of care,</A><br>
+<A NAME=2.2.49>The death of each day's life, sore labour's bath,</A><br>
+<A NAME=2.2.50>Balm of hurt minds, great nature's second course,</A><br>
+<A NAME=2.2.51>Chief nourisher in life's feast,--</A><br>
+</blockquote>
+
+<A NAME=speech21><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.52>What do you mean?</A><br>
+</blockquote>
+
+<A NAME=speech22><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.53>Still it cried 'Sleep no more!' to all the house:</A><br>
+<A NAME=2.2.54>'Glamis hath murder'd sleep, and therefore Cawdor</A><br>
+<A NAME=2.2.55>Shall sleep no more; Macbeth shall sleep no more.'</A><br>
+</blockquote>
+
+<A NAME=speech23><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.56>Who was it that thus cried? Why, worthy thane,</A><br>
+<A NAME=2.2.57>You do unbend your noble strength, to think</A><br>
+<A NAME=2.2.58>So brainsickly of things. Go get some water,</A><br>
+<A NAME=2.2.59>And wash this filthy witness from your hand.</A><br>
+<A NAME=2.2.60>Why did you bring these daggers from the place?</A><br>
+<A NAME=2.2.61>They must lie there: go carry them; and smear</A><br>
+<A NAME=2.2.62>The sleepy grooms with blood.</A><br>
+</blockquote>
+
+<A NAME=speech24><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.63>I'll go no more:</A><br>
+<A NAME=2.2.64>I am afraid to think what I have done;</A><br>
+<A NAME=2.2.65>Look on't again I dare not.</A><br>
+</blockquote>
+
+<A NAME=speech25><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.66>Infirm of purpose!</A><br>
+<A NAME=2.2.67>Give me the daggers: the sleeping and the dead</A><br>
+<A NAME=2.2.68>Are but as pictures: 'tis the eye of childhood</A><br>
+<A NAME=2.2.69>That fears a painted devil. If he do bleed,</A><br>
+<A NAME=2.2.70>I'll gild the faces of the grooms withal;</A><br>
+<A NAME=2.2.71>For it must seem their guilt.</A><br>
+<p><i>Exit. Knocking within</i></p>
+</blockquote>
+
+<A NAME=speech26><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.72>Whence is that knocking?</A><br>
+<A NAME=2.2.73>How is't with me, when every noise appals me?</A><br>
+<A NAME=2.2.74>What hands are here? ha! they pluck out mine eyes.</A><br>
+<A NAME=2.2.75>Will all great Neptune's ocean wash this blood</A><br>
+<A NAME=2.2.76>Clean from my hand? No, this my hand will rather</A><br>
+<A NAME=2.2.77>The multitudinous seas in incarnadine,</A><br>
+<A NAME=2.2.78>Making the green one red.</A><br>
+<p><i>Re-enter LADY MACBETH</i></p>
+</blockquote>
+
+<A NAME=speech27><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.79>My hands are of your colour; but I shame</A><br>
+<A NAME=2.2.80>To wear a heart so white.</A><br>
+<p><i>Knocking within</i></p>
+<A NAME=2.2.81>I hear a knocking</A><br>
+<A NAME=2.2.82>At the south entry: retire we to our chamber;</A><br>
+<A NAME=2.2.83>A little water clears us of this deed:</A><br>
+<A NAME=2.2.84>How easy is it, then! Your constancy</A><br>
+<A NAME=2.2.85>Hath left you unattended.</A><br>
+<p><i>Knocking within</i></p>
+<A NAME=2.2.86>Hark! more knocking.</A><br>
+<A NAME=2.2.87>Get on your nightgown, lest occasion call us,</A><br>
+<A NAME=2.2.88>And show us to be watchers. Be not lost</A><br>
+<A NAME=2.2.89>So poorly in your thoughts.</A><br>
+</blockquote>
+
+<A NAME=speech28><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.90>To know my deed, 'twere best not know myself.</A><br>
+<p><i>Knocking within</i></p>
+<A NAME=2.2.91>Wake Duncan with thy knocking! I would thou couldst!</A><br>
+<p><i>Exeunt</i></p>
+</blockquote>
+<h3>SCENE III. The same.</h3>
+<p><blockquote>
+<i>Knocking within. Enter a Porter</i>
+</blockquote>
+
+<A NAME=speech1><b>Porter</b></a>
+<blockquote>
+<A NAME=2.3.1>Here's a knocking indeed! If a</A><br>
+<A NAME=2.3.2>man were porter of hell-gate, he should have</A><br>
+<A NAME=2.3.3>old turning the key.</A><br>
+<p><i>Knocking within</i></p>
+<A NAME=2.3.4>Knock,</A><br>
+<A NAME=2.3.5>knock, knock! Who's there, i' the name of</A><br>
+<A NAME=2.3.6>Beelzebub? Here's a farmer, that hanged</A><br>
+<A NAME=2.3.7>himself on the expectation of plenty: come in</A><br>
+<A NAME=2.3.8>time; have napkins enow about you; here</A><br>
+<A NAME=2.3.9>you'll sweat for't.</A><br>
+<p><i>Knocking within</i></p>
+<A NAME=2.3.10>Knock,</A><br>
+<A NAME=2.3.11>knock! Who's there, in the other devil's</A><br>
+<A NAME=2.3.12>name? Faith, here's an equivocator, that could</A><br>
+<A NAME=2.3.13>swear in both the scales against either scale;</A><br>
+<A NAME=2.3.14>who committed treason enough for God's sake,</A><br>
+<A NAME=2.3.15>yet could not equivocate to heaven: O, come</A><br>
+<A NAME=2.3.16>in, equivocator.</A><br>
+<p><i>Knocking within</i></p>
+<A NAME=2.3.17>Knock,</A><br>
+<A NAME=2.3.18>knock, knock! Who's there? Faith, here's an</A><br>
+<A NAME=2.3.19>English tailor come hither, for stealing out of</A><br>
+<A NAME=2.3.20>a French hose: come in, tailor; here you may</A><br>
+<A NAME=2.3.21>roast your goose.</A><br>
+<p><i>Knocking within</i></p>
+<A NAME=2.3.22>Knock,</A><br>
+<A NAME=2.3.23>knock; never at quiet! What are you? But</A><br>
+<A NAME=2.3.24>this place is too cold for hell. I'll devil-porter</A><br>
+<A NAME=2.3.25>it no further: I had thought to have let in</A><br>
+<A NAME=2.3.26>some of all professions that go the primrose</A><br>
+<A NAME=2.3.27>way to the everlasting bonfire.</A><br>
+<p><i>Knocking within</i></p>
+<A NAME=2.3.28>Anon, anon! I pray you, remember the porter.</A><br>
+<p><i>Opens the gate</i></p>
+<p><i>Enter MACDUFF and LENNOX</i></p>
+</blockquote>
+
+<A NAME=speech2><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=2.3.29>Was it so late, friend, ere you went to bed,</A><br>
+<A NAME=2.3.30>That you do lie so late?</A><br>
+</blockquote>
+
+<A NAME=speech3><b>Porter</b></a>
+<blockquote>
+<A NAME=2.3.31>'Faith sir, we were carousing till the</A><br>
+<A NAME=2.3.32>second cock: and drink, sir, is a great</A><br>
+<A NAME=2.3.33>provoker of three things.</A><br>
+</blockquote>
+
+<A NAME=speech4><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=2.3.34>What three things does drink especially provoke?</A><br>
+</blockquote>
+
+<A NAME=speech5><b>Porter</b></a>
+<blockquote>
+<A NAME=2.3.35>Marry, sir, nose-painting, sleep, and</A><br>
+<A NAME=2.3.36>urine. Lechery, sir, it provokes, and unprovokes;</A><br>
+<A NAME=2.3.37>it provokes the desire, but it takes</A><br>
+<A NAME=2.3.38>away the performance: therefore, much drink</A><br>
+<A NAME=2.3.39>may be said to be an equivocator with lechery:</A><br>
+<A NAME=2.3.40>it makes him, and it mars him; it sets</A><br>
+<A NAME=2.3.41>him on, and it takes him off; it persuades him,</A><br>
+<A NAME=2.3.42>and disheartens him; makes him stand to, and</A><br>
+<A NAME=2.3.43>not stand to; in conclusion, equivocates him</A><br>
+<A NAME=2.3.44>in a sleep, and, giving him the lie, leaves him.</A><br>
+</blockquote>
+
+<A NAME=speech6><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=2.3.45>I believe drink gave thee the lie last night.</A><br>
+</blockquote>
+
+<A NAME=speech7><b>Porter</b></a>
+<blockquote>
+<A NAME=2.3.46>That it did, sir, i' the very throat on</A><br>
+<A NAME=2.3.47>me: but I requited him for his lie; and, I</A><br>
+<A NAME=2.3.48>think, being too strong for him, though he took</A><br>
+<A NAME=2.3.49>up my legs sometime, yet I made a shift to cast</A><br>
+<A NAME=2.3.50>him.</A><br>
+</blockquote>
+
+<A NAME=speech8><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=2.3.51>Is thy master stirring?</A><br>
+<p><i>Enter MACBETH</i></p>
+<A NAME=2.3.52>Our knocking has awaked him; here he comes.</A><br>
+</blockquote>
+
+<A NAME=speech9><b>LENNOX</b></a>
+<blockquote>
+<A NAME=2.3.53>Good morrow, noble sir.</A><br>
+</blockquote>
+
+<A NAME=speech10><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.3.54>Good morrow, both.</A><br>
+</blockquote>
+
+<A NAME=speech11><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=2.3.55>Is the king stirring, worthy thane?</A><br>
+</blockquote>
+
+<A NAME=speech12><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.3.56>Not yet.</A><br>
+</blockquote>
+
+<A NAME=speech13><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=2.3.57>He did command me to call timely on him:</A><br>
+<A NAME=2.3.58>I have almost slipp'd the hour.</A><br>
+</blockquote>
+
+<A NAME=speech14><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.3.59>I'll bring you to him.</A><br>
+</blockquote>
+
+<A NAME=speech15><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=2.3.60>I know this is a joyful trouble to you;</A><br>
+<A NAME=2.3.61>But yet 'tis one.</A><br>
+</blockquote>
+
+<A NAME=speech16><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.3.62>The labour we delight in physics pain.</A><br>
+<A NAME=2.3.63>This is the door.</A><br>
+</blockquote>
+
+<A NAME=speech17><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=2.3.64> I'll make so bold to call,</A><br>
+<A NAME=2.3.65>For 'tis my limited service.</A><br>
+<p><i>Exit</i></p>
+</blockquote>
+
+<A NAME=speech18><b>LENNOX</b></a>
+<blockquote>
+<A NAME=2.3.66>Goes the king hence to-day?</A><br>
+</blockquote>
+
+<A NAME=speech19><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.3.67>He does: he did appoint so.</A><br>
+</blockquote>
+
+<A NAME=speech20><b>LENNOX</b></a>
+<blockquote>
+<A NAME=2.3.68>The night has been unruly: where we lay,</A><br>
+<A NAME=2.3.69>Our chimneys were blown down; and, as they say,</A><br>
+<A NAME=2.3.70>Lamentings heard i' the air; strange screams of death,</A><br>
+<A NAME=2.3.71>And prophesying with accents terrible</A><br>
+<A NAME=2.3.72>Of dire combustion and confused events</A><br>
+<A NAME=2.3.73>New hatch'd to the woeful time: the obscure bird</A><br>
+<A NAME=2.3.74>Clamour'd the livelong night: some say, the earth</A><br>
+<A NAME=2.3.75>Was feverous and did shake.</A><br>
+</blockquote>
+
+<A NAME=speech21><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.3.76>'Twas a rough night.</A><br>
+</blockquote>
+
+<A NAME=speech22><b>LENNOX</b></a>
+<blockquote>
+<A NAME=2.3.77>My young remembrance cannot parallel</A><br>
+<A NAME=2.3.78>A fellow to it.</A><br>
+<p><i>Re-enter MACDUFF</i></p>
+</blockquote>
+
+<A NAME=speech23><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=2.3.79>O horror, horror, horror! Tongue nor heart</A><br>
+<A NAME=2.3.80>Cannot conceive nor name thee!</A><br>
+</blockquote>
+
+<A NAME=speech24><b>MACBETH</b></a>
+
+<A NAME=speech25><b>LENNOX</b></a>
+<blockquote>
+<A NAME=2.3.81>What's the matter.</A><br>
+</blockquote>
+
+<A NAME=speech26><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=2.3.82>Confusion now hath made his masterpiece!</A><br>
+<A NAME=2.3.83>Most sacrilegious murder hath broke ope</A><br>
+<A NAME=2.3.84>The Lord's anointed temple, and stole thence</A><br>
+<A NAME=2.3.85>The life o' the building!</A><br>
+</blockquote>
+
+<A NAME=speech27><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.3.86>What is 't you say? the life?</A><br>
+</blockquote>
+
+<A NAME=speech28><b>LENNOX</b></a>
+<blockquote>
+<A NAME=2.3.87>Mean you his majesty?</A><br>
+</blockquote>
+
+<A NAME=speech29><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=2.3.88>Approach the chamber, and destroy your sight</A><br>
+<A NAME=2.3.89>With a new Gorgon: do not bid me speak;</A><br>
+<A NAME=2.3.90>See, and then speak yourselves.</A><br>
+<p><i>Exeunt MACBETH and LENNOX</i></p>
+<A NAME=2.3.91>Awake, awake!</A><br>
+<A NAME=2.3.92>Ring the alarum-bell. Murder and treason!</A><br>
+<A NAME=2.3.93>Banquo and Donalbain! Malcolm! awake!</A><br>
+<A NAME=2.3.94>Shake off this downy sleep, death's counterfeit,</A><br>
+<A NAME=2.3.95>And look on death itself! up, up, and see</A><br>
+<A NAME=2.3.96>The great doom's image! Malcolm! Banquo!</A><br>
+<A NAME=2.3.97>As from your graves rise up, and walk like sprites,</A><br>
+<A NAME=2.3.98>To countenance this horror! Ring the bell.</A><br>
+<p><i>Bell rings</i></p>
+<p><i>Enter LADY MACBETH</i></p>
+</blockquote>
+
+<A NAME=speech30><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=2.3.99>What's the business,</A><br>
+<A NAME=2.3.100>That such a hideous trumpet calls to parley</A><br>
+<A NAME=2.3.101>The sleepers of the house? speak, speak!</A><br>
+</blockquote>
+
+<A NAME=speech31><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=2.3.102>O gentle lady,</A><br>
+<A NAME=2.3.103>'Tis not for you to hear what I can speak:</A><br>
+<A NAME=2.3.104>The repetition, in a woman's ear,</A><br>
+<A NAME=2.3.105>Would murder as it fell.</A><br>
+<p><i>Enter BANQUO</i></p>
+<A NAME=2.3.106>O Banquo, Banquo,</A><br>
+<A NAME=2.3.107>Our royal master 's murder'd!</A><br>
+</blockquote>
+
+<A NAME=speech32><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=2.3.108>Woe, alas!</A><br>
+<A NAME=2.3.109>What, in our house?</A><br>
+</blockquote>
+
+<A NAME=speech33><b>BANQUO</b></a>
+<blockquote>
+<A NAME=2.3.110>Too cruel any where.</A><br>
+<A NAME=2.3.111>Dear Duff, I prithee, contradict thyself,</A><br>
+<A NAME=2.3.112>And say it is not so.</A><br>
+<p><i>Re-enter MACBETH and LENNOX, with ROSS</i></p>
+</blockquote>
+
+<A NAME=speech34><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.3.113>Had I but died an hour before this chance,</A><br>
+<A NAME=2.3.114>I had lived a blessed time; for, from this instant,</A><br>
+<A NAME=2.3.115>There 's nothing serious in mortality:</A><br>
+<A NAME=2.3.116>All is but toys: renown and grace is dead;</A><br>
+<A NAME=2.3.117>The wine of life is drawn, and the mere lees</A><br>
+<A NAME=2.3.118>Is left this vault to brag of.</A><br>
+<p><i>Enter MALCOLM and DONALBAIN</i></p>
+</blockquote>
+
+<A NAME=speech35><b>DONALBAIN</b></a>
+<blockquote>
+<A NAME=2.3.119>What is amiss?</A><br>
+</blockquote>
+
+<A NAME=speech36><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.3.120> You are, and do not know't:</A><br>
+<A NAME=2.3.121>The spring, the head, the fountain of your blood</A><br>
+<A NAME=2.3.122>Is stopp'd; the very source of it is stopp'd.</A><br>
+</blockquote>
+
+<A NAME=speech37><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=2.3.123>Your royal father 's murder'd.</A><br>
+</blockquote>
+
+<A NAME=speech38><b>MALCOLM</b></a>
+<blockquote>
+<A NAME=2.3.124>O, by whom?</A><br>
+</blockquote>
+
+<A NAME=speech39><b>LENNOX</b></a>
+<blockquote>
+<A NAME=2.3.125>Those of his chamber, as it seem'd, had done 't:</A><br>
+<A NAME=2.3.126>Their hands and faces were an badged with blood;</A><br>
+<A NAME=2.3.127>So were their daggers, which unwiped we found</A><br>
+<A NAME=2.3.128>Upon their pillows:</A><br>
+<A NAME=2.3.129>They stared, and were distracted; no man's life</A><br>
+<A NAME=2.3.130>Was to be trusted with them.</A><br>
+</blockquote>
+
+<A NAME=speech40><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.3.131>O, yet I do repent me of my fury,</A><br>
+<A NAME=2.3.132>That I did kill them.</A><br>
+</blockquote>
+
+<A NAME=speech41><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=2.3.133>Wherefore did you so?</A><br>
+</blockquote>
+
+<A NAME=speech42><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.3.134>Who can be wise, amazed, temperate and furious,</A><br>
+<A NAME=2.3.135>Loyal and neutral, in a moment? No man:</A><br>
+<A NAME=2.3.136>The expedition my violent love</A><br>
+<A NAME=2.3.137>Outrun the pauser, reason. Here lay Duncan,</A><br>
+<A NAME=2.3.138>His silver skin laced with his golden blood;</A><br>
+<A NAME=2.3.139>And his gash'd stabs look'd like a breach in nature</A><br>
+<A NAME=2.3.140>For ruin's wasteful entrance: there, the murderers,</A><br>
+<A NAME=2.3.141>Steep'd in the colours of their trade, their daggers</A><br>
+<A NAME=2.3.142>Unmannerly breech'd with gore: who could refrain,</A><br>
+<A NAME=2.3.143>That had a heart to love, and in that heart</A><br>
+<A NAME=2.3.144>Courage to make 's love kno wn?</A><br>
+</blockquote>
+
+<A NAME=speech43><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=2.3.145>Help me hence, ho!</A><br>
+</blockquote>
+
+<A NAME=speech44><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=2.3.146>Look to the lady.</A><br>
+</blockquote>
+
+<A NAME=speech45><b>MALCOLM</b></a>
+<blockquote>
+<A NAME=2.3.147>[Aside to DONALBAIN] Why do we hold our tongues,</A><br>
+<A NAME=2.3.148>That most may claim this argument for ours?</A><br>
+</blockquote>
+
+<A NAME=speech46><b>DONALBAIN</b></a>
+<blockquote>
+<A NAME=2.3.149>[Aside to MALCOLM] What should be spoken here,</A><br>
+<A NAME=2.3.150>where our fate,</A><br>
+<A NAME=2.3.151>Hid in an auger-hole, may rush, and seize us?</A><br>
+<A NAME=2.3.152>Let 's away;</A><br>
+<A NAME=2.3.153>Our tears are not yet brew'd.</A><br>
+</blockquote>
+
+<A NAME=speech47><b>MALCOLM</b></a>
+<blockquote>
+<A NAME=2.3.154>[Aside to DONALBAIN] Nor our strong sorrow</A><br>
+<A NAME=2.3.155>Upon the foot of motion.</A><br>
+</blockquote>
+
+<A NAME=speech48><b>BANQUO</b></a>
+<blockquote>
+<A NAME=2.3.156>Look to the lady:</A><br>
+<p><i>LADY MACBETH is carried out</i></p>
+<A NAME=2.3.157>And when we have our naked frailties hid,</A><br>
+<A NAME=2.3.158>That suffer in exposure, let us meet,</A><br>
+<A NAME=2.3.159>And question this most bloody piece of work,</A><br>
+<A NAME=2.3.160>To know it further. Fears and scruples shake us:</A><br>
+<A NAME=2.3.161>In the great hand of God I stand; and thence</A><br>
+<A NAME=2.3.162>Against the undivulged pretence I fight</A><br>
+<A NAME=2.3.163>Of treasonous malice.</A><br>
+</blockquote>
+
+<A NAME=speech49><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=2.3.164>And so do I.</A><br>
+</blockquote>
+
+<A NAME=speech50><b>ALL</b></a>
+<blockquote>
+<A NAME=2.3.165>So all.</A><br>
+</blockquote>
+
+<A NAME=speech51><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.3.166>Let's briefly put on manly readiness,</A><br>
+<A NAME=2.3.167>And meet i' the hall together.</A><br>
+</blockquote>
+
+<A NAME=speech52><b>ALL</b></a>
+<blockquote>
+<A NAME=2.3.168>Well contented.</A><br>
+<p><i>Exeunt all but Malcolm and Donalbain.</i></p>
+</blockquote>
+
+<A NAME=speech53><b>MALCOLM</b></a>
+<blockquote>
+<A NAME=2.3.169>What will you do? Let's not consort with them:</A><br>
+<A NAME=2.3.170>To show an unfelt sorrow is an office</A><br>
+<A NAME=2.3.171>Which the false man does easy. I'll to England.</A><br>
+</blockquote>
+
+<A NAME=speech54><b>DONALBAIN</b></a>
+<blockquote>
+<A NAME=2.3.172>To Ireland, I; our separated fortune</A><br>
+<A NAME=2.3.173>Shall keep us both the safer: where we are,</A><br>
+<A NAME=2.3.174>There's daggers in men's smiles: the near in blood,</A><br>
+<A NAME=2.3.175>The nearer bloody.</A><br>
+</blockquote>
+
+<A NAME=speech55><b>MALCOLM</b></a>
+<blockquote>
+<A NAME=2.3.176> This murderous shaft that's shot</A><br>
+<A NAME=2.3.177>Hath not yet lighted, and our safest way</A><br>
+<A NAME=2.3.178>Is to avoid the aim. Therefore, to horse;</A><br>
+<A NAME=2.3.179>And let us not be dainty of leave-taking,</A><br>
+<A NAME=2.3.180>But shift away: there's warrant in that theft</A><br>
+<A NAME=2.3.181>Which steals itself, when there's no mercy left.</A><br>
+<p><i>Exeunt</i></p>
+</blockquote>
+<h3>SCENE IV. Outside Macbeth's castle.</h3>
+<p><blockquote>
+<i>Enter ROSS and an old Man</i>
+</blockquote>
+
+<A NAME=speech1><b>Old Man</b></a>
+<blockquote>
+<A NAME=2.4.1>Threescore and ten I can remember well:</A><br>
+<A NAME=2.4.2>Within the volume of which time I have seen</A><br>
+<A NAME=2.4.3>Hours dreadful and things strange; but this sore night</A><br>
+<A NAME=2.4.4>Hath trifled former knowings.</A><br>
+</blockquote>
+
+<A NAME=speech2><b>ROSS</b></a>
+<blockquote>
+<A NAME=2.4.5>Ah, good father,</A><br>
+<A NAME=2.4.6>Thou seest, the heavens, as troubled with man's act,</A><br>
+<A NAME=2.4.7>Threaten his bloody stage: by the clock, 'tis day,</A><br>
+<A NAME=2.4.8>And yet dark night strangles the travelling lamp:</A><br>
+<A NAME=2.4.9>Is't night's predominance, or the day's shame,</A><br>
+<A NAME=2.4.10>That darkness does the face of earth entomb,</A><br>
+<A NAME=2.4.11>When living light should kiss it?</A><br>
+</blockquote>
+
+<A NAME=speech3><b>Old Man</b></a>
+<blockquote>
+<A NAME=2.4.12>'Tis unnatural,</A><br>
+<A NAME=2.4.13>Even like the deed that's done. On Tuesday last,</A><br>
+<A NAME=2.4.14>A falcon, towering in her pride of place,</A><br>
+<A NAME=2.4.15>Was by a mousing owl hawk'd at and kill'd.</A><br>
+</blockquote>
+
+<A NAME=speech4><b>ROSS</b></a>
+<blockquote>
+<A NAME=2.4.16>And Duncan's horses--a thing most strange and certain--</A><br>
+<A NAME=2.4.17>Beauteous and swift, the minions of their race,</A><br>
+<A NAME=2.4.18>Turn'd wild in nature, broke their stalls, flung out,</A><br>
+<A NAME=2.4.19>Contending 'gainst obedience, as they would make</A><br>
+<A NAME=2.4.20>War with mankind.</A><br>
+</blockquote>
+
+<A NAME=speech5><b>Old Man</b></a>
+<blockquote>
+<A NAME=2.4.21>'Tis said they eat each other.</A><br>
+</blockquote>
+
+<A NAME=speech6><b>ROSS</b></a>
+<blockquote>
+<A NAME=2.4.22>They did so, to the amazement of mine eyes</A><br>
+<A NAME=2.4.23>That look'd upon't. Here comes the good Macduff.</A><br>
+<p><i>Enter MACDUFF</i></p>
+<A NAME=2.4.24>How goes the world, sir, now?</A><br>
+</blockquote>
+
+<A NAME=speech7><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=2.4.25>Why, see you not?</A><br>
+</blockquote>
+
+<A NAME=speech8><b>ROSS</b></a>
+<blockquote>
+<A NAME=2.4.26>Is't known who did this more than bloody deed?</A><br>
+</blockquote>
+
+<A NAME=speech9><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=2.4.27>Those that Macbeth hath slain.</A><br>
+</blockquote>
+
+<A NAME=speech10><b>ROSS</b></a>
+<blockquote>
+<A NAME=2.4.28>Alas, the day!</A><br>
+<A NAME=2.4.29>What good could they pretend?</A><br>
+</blockquote>
+
+<A NAME=speech11><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=2.4.30>They were suborn'd:</A><br>
+<A NAME=2.4.31>Malcolm and Donalbain, the king's two sons,</A><br>
+<A NAME=2.4.32>Are stol'n away and fled; which puts upon them</A><br>
+<A NAME=2.4.33>Suspicion of the deed.</A><br>
+</blockquote>
+
+<A NAME=speech12><b>ROSS</b></a>
+<blockquote>
+<A NAME=2.4.34>'Gainst nature still!</A><br>
+<A NAME=2.4.35>Thriftless ambition, that wilt ravin up</A><br>
+<A NAME=2.4.36>Thine own life's means! Then 'tis most like</A><br>
+<A NAME=2.4.37>The sovereignty will fall upon Macbeth.</A><br>
+</blockquote>
+
+<A NAME=speech13><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=2.4.38>He is already named, and gone to Scone</A><br>
+<A NAME=2.4.39>To be invested.</A><br>
+</blockquote>
+
+<A NAME=speech14><b>ROSS</b></a>
+<blockquote>
+<A NAME=2.4.40> Where is Duncan's body?</A><br>
+</blockquote>
+
+<A NAME=speech15><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=2.4.41>Carried to Colmekill,</A><br>
+<A NAME=2.4.42>The sacred storehouse of his predecessors,</A><br>
+<A NAME=2.4.43>And guardian of their bones.</A><br>
+</blockquote>
+
+<A NAME=speech16><b>ROSS</b></a>
+<blockquote>
+<A NAME=2.4.44>Will you to Scone?</A><br>
+</blockquote>
+
+<A NAME=speech17><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=2.4.45>No, cousin, I'll to Fife.</A><br>
+</blockquote>
+
+<A NAME=speech18><b>ROSS</b></a>
+<blockquote>
+<A NAME=2.4.46>Well, I will thither.</A><br>
+</blockquote>
+
+<A NAME=speech19><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=2.4.47>Well, may you see things well done there: adieu!</A><br>
+<A NAME=2.4.48>Lest our old robes sit easier than our new!</A><br>
+</blockquote>
+
+<A NAME=speech20><b>ROSS</b></a>
+<blockquote>
+<A NAME=2.4.49>Farewell, father.</A><br>
+</blockquote>
+
+<A NAME=speech21><b>Old Man</b></a>
+<blockquote>
+<A NAME=2.4.50>God's benison go with you; and with those</A><br>
+<A NAME=2.4.51>That would make good of bad, and friends of foes!</A><br>
+<p><i>Exeunt</i></p>
+</blockquote><p>
+<H3>ACT III</h3>
+<h3>SCENE I. Forres. The palace.</h3>
+<p><blockquote>
+<i>Enter BANQUO</i>
+</blockquote>
+
+<A NAME=speech1><b>BANQUO</b></a>
+<blockquote>
+<A NAME=3.1.1>Thou hast it now: king, Cawdor, Glamis, all,</A><br>
+<A NAME=3.1.2>As the weird women promised, and, I fear,</A><br>
+<A NAME=3.1.3>Thou play'dst most foully for't: yet it was said</A><br>
+<A NAME=3.1.4>It should not stand in thy posterity,</A><br>
+<A NAME=3.1.5>But that myself should be the root and father</A><br>
+<A NAME=3.1.6>Of many kings. If there come truth from them--</A><br>
+<A NAME=3.1.7>As upon thee, Macbeth, their speeches shine--</A><br>
+<A NAME=3.1.8>Why, by the verities on thee made good,</A><br>
+<A NAME=3.1.9>May they not be my oracles as well,</A><br>
+<A NAME=3.1.10>And set me up in hope? But hush! no more.</A><br>
+<p><i>Sennet sounded. Enter MACBETH, as king, LADY MACBETH, as queen, LENNOX, ROSS, Lords, Ladies, and Attendants</i></p>
+</blockquote>
+
+<A NAME=speech2><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.1.11>Here's our chief guest.</A><br>
+</blockquote>
+
+<A NAME=speech3><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=3.1.12>If he had been forgotten,</A><br>
+<A NAME=3.1.13>It had been as a gap in our great feast,</A><br>
+<A NAME=3.1.14>And all-thing unbecoming.</A><br>
+</blockquote>
+
+<A NAME=speech4><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.1.15>To-night we hold a solemn supper sir,</A><br>
+<A NAME=3.1.16>And I'll request your presence.</A><br>
+</blockquote>
+
+<A NAME=speech5><b>BANQUO</b></a>
+<blockquote>
+<A NAME=3.1.17>Let your highness</A><br>
+<A NAME=3.1.18>Command upon me; to the which my duties</A><br>
+<A NAME=3.1.19>Are with a most indissoluble tie</A><br>
+<A NAME=3.1.20>For ever knit.</A><br>
+</blockquote>
+
+<A NAME=speech6><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.1.21> Ride you this afternoon?</A><br>
+</blockquote>
+
+<A NAME=speech7><b>BANQUO</b></a>
+<blockquote>
+<A NAME=3.1.22>Ay, my good lord.</A><br>
+</blockquote>
+
+<A NAME=speech8><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.1.23>We should have else desired your good advice,</A><br>
+<A NAME=3.1.24>Which still hath been both grave and prosperous,</A><br>
+<A NAME=3.1.25>In this day's council; but we'll take to-morrow.</A><br>
+<A NAME=3.1.26>Is't far you ride?</A><br>
+</blockquote>
+
+<A NAME=speech9><b>BANQUO</b></a>
+<blockquote>
+<A NAME=3.1.27>As far, my lord, as will fill up the time</A><br>
+<A NAME=3.1.28>'Twixt this and supper: go not my horse the better,</A><br>
+<A NAME=3.1.29>I must become a borrower of the night</A><br>
+<A NAME=3.1.30>For a dark hour or twain.</A><br>
+</blockquote>
+
+<A NAME=speech10><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.1.31>Fail not our feast.</A><br>
+</blockquote>
+
+<A NAME=speech11><b>BANQUO</b></a>
+<blockquote>
+<A NAME=3.1.32>My lord, I will not.</A><br>
+</blockquote>
+
+<A NAME=speech12><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.1.33>We hear, our bloody cousins are bestow'd</A><br>
+<A NAME=3.1.34>In England and in Ireland, not confessing</A><br>
+<A NAME=3.1.35>Their cruel parricide, filling their hearers</A><br>
+<A NAME=3.1.36>With strange invention: but of that to-morrow,</A><br>
+<A NAME=3.1.37>When therewithal we shall have cause of state</A><br>
+<A NAME=3.1.38>Craving us jointly. Hie you to horse: adieu,</A><br>
+<A NAME=3.1.39>Till you return at night. Goes Fleance with you?</A><br>
+</blockquote>
+
+<A NAME=speech13><b>BANQUO</b></a>
+<blockquote>
+<A NAME=3.1.40>Ay, my good lord: our time does call upon 's.</A><br>
+</blockquote>
+
+<A NAME=speech14><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.1.41>I wish your horses swift and sure of foot;</A><br>
+<A NAME=3.1.42>And so I do commend you to their backs. Farewell.</A><br>
+<p><i>Exit BANQUO</i></p>
+<A NAME=3.1.43>Let every man be master of his time</A><br>
+<A NAME=3.1.44>Till seven at night: to make society</A><br>
+<A NAME=3.1.45>The sweeter welcome, we will keep ourself</A><br>
+<A NAME=3.1.46>Till supper-time alone: while then, God be with you!</A><br>
+<p><i>Exeunt all but MACBETH, and an attendant</i></p>
+<A NAME=3.1.47>Sirrah, a word with you: attend those men</A><br>
+<A NAME=3.1.48>Our pleasure?</A><br>
+</blockquote>
+
+<A NAME=speech15><b>ATTENDANT</b></a>
+<blockquote>
+<A NAME=3.1.49>They are, my lord, without the palace gate.</A><br>
+</blockquote>
+
+<A NAME=speech16><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.1.50>Bring them before us.</A><br>
+<p><i>Exit Attendant</i></p>
+<A NAME=3.1.51>To be thus is nothing;</A><br>
+<A NAME=3.1.52>But to be safely thus.--Our fears in Banquo</A><br>
+<A NAME=3.1.53>Stick deep; and in his royalty of nature</A><br>
+<A NAME=3.1.54>Reigns that which would be fear'd: 'tis much he dares;</A><br>
+<A NAME=3.1.55>And, to that dauntless temper of his mind,</A><br>
+<A NAME=3.1.56>He hath a wisdom that doth guide his valour</A><br>
+<A NAME=3.1.57>To act in safety. There is none but he</A><br>
+<A NAME=3.1.58>Whose being I do fear: and, under him,</A><br>
+<A NAME=3.1.59>My Genius is rebuked; as, it is said,</A><br>
+<A NAME=3.1.60>Mark Antony's was by Caesar. He chid the sisters</A><br>
+<A NAME=3.1.61>When first they put the name of king upon me,</A><br>
+<A NAME=3.1.62>And bade them speak to him: then prophet-like</A><br>
+<A NAME=3.1.63>They hail'd him father to a line of kings:</A><br>
+<A NAME=3.1.64>Upon my head they placed a fruitless crown,</A><br>
+<A NAME=3.1.65>And put a barren sceptre in my gripe,</A><br>
+<A NAME=3.1.66>Thence to be wrench'd with an unlineal hand,</A><br>
+<A NAME=3.1.67>No son of mine succeeding. If 't be so,</A><br>
+<A NAME=3.1.68>For Banquo's issue have I filed my mind;</A><br>
+<A NAME=3.1.69>For them the gracious Duncan have I murder'd;</A><br>
+<A NAME=3.1.70>Put rancours in the vessel of my peace</A><br>
+<A NAME=3.1.71>Only for them; and mine eternal jewel</A><br>
+<A NAME=3.1.72>Given to the common enemy of man,</A><br>
+<A NAME=3.1.73>To make them kings, the seed of Banquo kings!</A><br>
+<A NAME=3.1.74>Rather than so, come fate into the list.</A><br>
+<A NAME=3.1.75>And champion me to the utterance! Who's there!</A><br>
+<p><i>Re-enter Attendant, with two Murderers</i></p>
+<A NAME=3.1.76>Now go to the door, and stay there till we call.</A><br>
+<p><i>Exit Attendant</i></p>
+<A NAME=3.1.77>Was it not yesterday we spoke together?</A><br>
+</blockquote>
+
+<A NAME=speech17><b>First Murderer</b></a>
+<blockquote>
+<A NAME=3.1.78>It was, so please your highness.</A><br>
+</blockquote>
+
+<A NAME=speech18><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.1.79>Well then, now</A><br>
+<A NAME=3.1.80>Have you consider'd of my speeches? Know</A><br>
+<A NAME=3.1.81>That it was he in the times past which held you</A><br>
+<A NAME=3.1.82>So under fortune, which you thought had been</A><br>
+<A NAME=3.1.83>Our innocent self: this I made good to you</A><br>
+<A NAME=3.1.84>In our last conference, pass'd in probation with you,</A><br>
+<A NAME=3.1.85>How you were borne in hand, how cross'd,</A><br>
+<A NAME=3.1.86>the instruments,</A><br>
+<A NAME=3.1.87>Who wrought with them, and all things else that might</A><br>
+<A NAME=3.1.88>To half a soul and to a notion crazed</A><br>
+<A NAME=3.1.89>Say 'Thus did Banquo.'</A><br>
+</blockquote>
+
+<A NAME=speech19><b>First Murderer</b></a>
+<blockquote>
+<A NAME=3.1.90>You made it known to us.</A><br>
+</blockquote>
+
+<A NAME=speech20><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.1.91>I did so, and went further, which is now</A><br>
+<A NAME=3.1.92>Our point of second meeting. Do you find</A><br>
+<A NAME=3.1.93>Your patience so predominant in your nature</A><br>
+<A NAME=3.1.94>That you can let this go? Are you so gospell'd</A><br>
+<A NAME=3.1.95>To pray for this good man and for his issue,</A><br>
+<A NAME=3.1.96>Whose heavy hand hath bow'd you to the grave</A><br>
+<A NAME=3.1.97>And beggar'd yours for ever?</A><br>
+</blockquote>
+
+<A NAME=speech21><b>First Murderer</b></a>
+<blockquote>
+<A NAME=3.1.98>We are men, my liege.</A><br>
+</blockquote>
+
+<A NAME=speech22><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.1.99>Ay, in the catalogue ye go for men;</A><br>
+<A NAME=3.1.100>As hounds and greyhounds, mongrels, spaniels, curs,</A><br>
+<A NAME=3.1.101>Shoughs, water-rugs and demi-wolves, are clept</A><br>
+<A NAME=3.1.102>All by the name of dogs: the valued file</A><br>
+<A NAME=3.1.103>Distinguishes the swift, the slow, the subtle,</A><br>
+<A NAME=3.1.104>The housekeeper, the hunter, every one</A><br>
+<A NAME=3.1.105>According to the gift which bounteous nature</A><br>
+<A NAME=3.1.106>Hath in him closed; whereby he does receive</A><br>
+<A NAME=3.1.107>Particular addition. from the bill</A><br>
+<A NAME=3.1.108>That writes them all alike: and so of men.</A><br>
+<A NAME=3.1.109>Now, if you have a station in the file,</A><br>
+<A NAME=3.1.110>Not i' the worst rank of manhood, say 't;</A><br>
+<A NAME=3.1.111>And I will put that business in your bosoms,</A><br>
+<A NAME=3.1.112>Whose execution takes your enemy off,</A><br>
+<A NAME=3.1.113>Grapples you to the heart and love of us,</A><br>
+<A NAME=3.1.114>Who wear our health but sickly in his life,</A><br>
+<A NAME=3.1.115>Which in his death were perfect.</A><br>
+</blockquote>
+
+<A NAME=speech23><b>Second Murderer</b></a>
+<blockquote>
+<A NAME=3.1.116>I am one, my liege,</A><br>
+<A NAME=3.1.117>Whom the vile blows and buffets of the world</A><br>
+<A NAME=3.1.118>Have so incensed that I am reckless what</A><br>
+<A NAME=3.1.119>I do to spite the world.</A><br>
+</blockquote>
+
+<A NAME=speech24><b>First Murderer</b></a>
+<blockquote>
+<A NAME=3.1.120>And I another</A><br>
+<A NAME=3.1.121>So weary with disasters, tugg'd with fortune,</A><br>
+<A NAME=3.1.122>That I would set my lie on any chance,</A><br>
+<A NAME=3.1.123>To mend it, or be rid on't.</A><br>
+</blockquote>
+
+<A NAME=speech25><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.1.124>Both of you</A><br>
+<A NAME=3.1.125>Know Banquo was your enemy.</A><br>
+</blockquote>
+
+<A NAME=speech26><b>Both Murderers</b></a>
+<blockquote>
+<A NAME=3.1.126>True, my lord.</A><br>
+</blockquote>
+
+<A NAME=speech27><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.1.127>So is he mine; and in such bloody distance,</A><br>
+<A NAME=3.1.128>That every minute of his being thrusts</A><br>
+<A NAME=3.1.129>Against my near'st of life: and though I could</A><br>
+<A NAME=3.1.130>With barefaced power sweep him from my sight</A><br>
+<A NAME=3.1.131>And bid my will avouch it, yet I must not,</A><br>
+<A NAME=3.1.132>For certain friends that are both his and mine,</A><br>
+<A NAME=3.1.133>Whose loves I may not drop, but wail his fall</A><br>
+<A NAME=3.1.134>Who I myself struck down; and thence it is,</A><br>
+<A NAME=3.1.135>That I to your assistance do make love,</A><br>
+<A NAME=3.1.136>Masking the business from the common eye</A><br>
+<A NAME=3.1.137>For sundry weighty reasons.</A><br>
+</blockquote>
+
+<A NAME=speech28><b>Second Murderer</b></a>
+<blockquote>
+<A NAME=3.1.138>We shall, my lord,</A><br>
+<A NAME=3.1.139>Perform what you command us.</A><br>
+</blockquote>
+
+<A NAME=speech29><b>First Murderer</b></a>
+<blockquote>
+<A NAME=3.1.140>Though our lives--</A><br>
+</blockquote>
+
+<A NAME=speech30><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.1.141>Your spirits shine through you. Within this hour at most</A><br>
+<A NAME=3.1.142>I will advise you where to plant yourselves;</A><br>
+<A NAME=3.1.143>Acquaint you with the perfect spy o' the time,</A><br>
+<A NAME=3.1.144>The moment on't; for't must be done to-night,</A><br>
+<A NAME=3.1.145>And something from the palace; always thought</A><br>
+<A NAME=3.1.146>That I require a clearness: and with him--</A><br>
+<A NAME=3.1.147>To leave no rubs nor botches in the work--</A><br>
+<A NAME=3.1.148>Fleance his son, that keeps him company,</A><br>
+<A NAME=3.1.149>Whose absence is no less material to me</A><br>
+<A NAME=3.1.150>Than is his father's, must embrace the fate</A><br>
+<A NAME=3.1.151>Of that dark hour. Resolve yourselves apart:</A><br>
+<A NAME=3.1.152>I'll come to you anon.</A><br>
+</blockquote>
+
+<A NAME=speech31><b>Both Murderers</b></a>
+<blockquote>
+<A NAME=3.1.153>We are resolved, my lord.</A><br>
+</blockquote>
+
+<A NAME=speech32><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.1.154>I'll call upon you straight: abide within.</A><br>
+<p><i>Exeunt Murderers</i></p>
+<A NAME=3.1.155>It is concluded. Banquo, thy soul's flight,</A><br>
+<A NAME=3.1.156>If it find heaven, must find it out to-night.</A><br>
+<p><i>Exit</i></p>
+</blockquote>
+<h3>SCENE II. The palace.</h3>
+<p><blockquote>
+<i>Enter LADY MACBETH and a Servant</i>
+</blockquote>
+
+<A NAME=speech1><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=3.2.1>Is Banquo gone from court?</A><br>
+</blockquote>
+
+<A NAME=speech2><b>Servant</b></a>
+<blockquote>
+<A NAME=3.2.2>Ay, madam, but returns again to-night.</A><br>
+</blockquote>
+
+<A NAME=speech3><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=3.2.3>Say to the king, I would attend his leisure</A><br>
+<A NAME=3.2.4>For a few words.</A><br>
+</blockquote>
+
+<A NAME=speech4><b>Servant</b></a>
+<blockquote>
+<A NAME=3.2.5> Madam, I will.</A><br>
+<p><i>Exit</i></p>
+</blockquote>
+
+<A NAME=speech5><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=3.2.6>Nought's had, all's spent,</A><br>
+<A NAME=3.2.7>Where our desire is got without content:</A><br>
+<A NAME=3.2.8>'Tis safer to be that which we destroy</A><br>
+<A NAME=3.2.9>Than by destruction dwell in doubtful joy.</A><br>
+<p><i>Enter MACBETH</i></p>
+<A NAME=3.2.10>How now, my lord! why do you keep alone,</A><br>
+<A NAME=3.2.11>Of sorriest fancies your companions making,</A><br>
+<A NAME=3.2.12>Using those thoughts which should indeed have died</A><br>
+<A NAME=3.2.13>With them they think on? Things without all remedy</A><br>
+<A NAME=3.2.14>Should be without regard: what's done is done.</A><br>
+</blockquote>
+
+<A NAME=speech6><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.2.15>We have scotch'd the snake, not kill'd it:</A><br>
+<A NAME=3.2.16>She'll close and be herself, whilst our poor malice</A><br>
+<A NAME=3.2.17>Remains in danger of her former tooth.</A><br>
+<A NAME=3.2.18>But let the frame of things disjoint, both the</A><br>
+<A NAME=3.2.19>worlds suffer,</A><br>
+<A NAME=3.2.20>Ere we will eat our meal in fear and sleep</A><br>
+<A NAME=3.2.21>In the affliction of these terrible dreams</A><br>
+<A NAME=3.2.22>That shake us nightly: better be with the dead,</A><br>
+<A NAME=3.2.23>Whom we, to gain our peace, have sent to peace,</A><br>
+<A NAME=3.2.24>Than on the torture of the mind to lie</A><br>
+<A NAME=3.2.25>In restless ecstasy. Duncan is in his grave;</A><br>
+<A NAME=3.2.26>After life's fitful fever he sleeps well;</A><br>
+<A NAME=3.2.27>Treason has done his worst: nor steel, nor poison,</A><br>
+<A NAME=3.2.28>Malice domestic, foreign levy, nothing,</A><br>
+<A NAME=3.2.29>Can touch him further.</A><br>
+</blockquote>
+
+<A NAME=speech7><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=3.2.30>Come on;</A><br>
+<A NAME=3.2.31>Gentle my lord, sleek o'er your rugged looks;</A><br>
+<A NAME=3.2.32>Be bright and jovial among your guests to-night.</A><br>
+</blockquote>
+
+<A NAME=speech8><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.2.33>So shall I, love; and so, I pray, be you:</A><br>
+<A NAME=3.2.34>Let your remembrance apply to Banquo;</A><br>
+<A NAME=3.2.35>Present him eminence, both with eye and tongue:</A><br>
+<A NAME=3.2.36>Unsafe the while, that we</A><br>
+<A NAME=3.2.37>Must lave our honours in these flattering streams,</A><br>
+<A NAME=3.2.38>And make our faces vizards to our hearts,</A><br>
+<A NAME=3.2.39>Disguising what they are.</A><br>
+</blockquote>
+
+<A NAME=speech9><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=3.2.40>You must leave this.</A><br>
+</blockquote>
+
+<A NAME=speech10><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.2.41>O, full of scorpions is my mind, dear wife!</A><br>
+<A NAME=3.2.42>Thou know'st that Banquo, and his Fleance, lives.</A><br>
+</blockquote>
+
+<A NAME=speech11><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=3.2.43>But in them nature's copy's not eterne.</A><br>
+</blockquote>
+
+<A NAME=speech12><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.2.44>There's comfort yet; they are assailable;</A><br>
+<A NAME=3.2.45>Then be thou jocund: ere the bat hath flown</A><br>
+<A NAME=3.2.46>His cloister'd flight, ere to black Hecate's summons</A><br>
+<A NAME=3.2.47>The shard-borne beetle with his drowsy hums</A><br>
+<A NAME=3.2.48>Hath rung night's yawning peal, there shall be done</A><br>
+<A NAME=3.2.49>A deed of dreadful note.</A><br>
+</blockquote>
+
+<A NAME=speech13><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=3.2.50>What's to be done?</A><br>
+</blockquote>
+
+<A NAME=speech14><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.2.51>Be innocent of the knowledge, dearest chuck,</A><br>
+<A NAME=3.2.52>Till thou applaud the deed. Come, seeling night,</A><br>
+<A NAME=3.2.53>Scarf up the tender eye of pitiful day;</A><br>
+<A NAME=3.2.54>And with thy bloody and invisible hand</A><br>
+<A NAME=3.2.55>Cancel and tear to pieces that great bond</A><br>
+<A NAME=3.2.56>Which keeps me pale! Light thickens; and the crow</A><br>
+<A NAME=3.2.57>Makes wing to the rooky wood:</A><br>
+<A NAME=3.2.58>Good things of day begin to droop and drowse;</A><br>
+<A NAME=3.2.59>While night's black agents to their preys do rouse.</A><br>
+<A NAME=3.2.60>Thou marvell'st at my words: but hold thee still;</A><br>
+<A NAME=3.2.61>Things bad begun make strong themselves by ill.</A><br>
+<A NAME=3.2.62>So, prithee, go with me.</A><br>
+<p><i>Exeunt</i></p>
+</blockquote>
+<h3>SCENE III. A park near the palace.</h3>
+<p><blockquote>
+<i>Enter three Murderers</i>
+</blockquote>
+
+<A NAME=speech1><b>First Murderer</b></a>
+<blockquote>
+<A NAME=3.3.1>But who did bid thee join with us?</A><br>
+</blockquote>
+
+<A NAME=speech2><b>Third Murderer</b></a>
+<blockquote>
+<A NAME=3.3.2>Macbeth.</A><br>
+</blockquote>
+
+<A NAME=speech3><b>Second Murderer</b></a>
+<blockquote>
+<A NAME=3.3.3>He needs not our mistrust, since he delivers</A><br>
+<A NAME=3.3.4>Our offices and what we have to do</A><br>
+<A NAME=3.3.5>To the direction just.</A><br>
+</blockquote>
+
+<A NAME=speech4><b>First Murderer</b></a>
+<blockquote>
+<A NAME=3.3.6>Then stand with us.</A><br>
+<A NAME=3.3.7>The west yet glimmers with some streaks of day:</A><br>
+<A NAME=3.3.8>Now spurs the lated traveller apace</A><br>
+<A NAME=3.3.9>To gain the timely inn; and near approaches</A><br>
+<A NAME=3.3.10>The subject of our watch.</A><br>
+</blockquote>
+
+<A NAME=speech5><b>Third Murderer</b></a>
+<blockquote>
+<A NAME=3.3.11>Hark! I hear horses.</A><br>
+</blockquote>
+
+<A NAME=speech6><b>BANQUO</b></a>
+<blockquote>
+<A NAME=3.3.12>[Within] Give us a light there, ho!</A><br>
+</blockquote>
+
+<A NAME=speech7><b>Second Murderer</b></a>
+<blockquote>
+<A NAME=3.3.13>Then 'tis he: the rest</A><br>
+<A NAME=3.3.14>That are within the note of expectation</A><br>
+<A NAME=3.3.15>Already are i' the court.</A><br>
+</blockquote>
+
+<A NAME=speech8><b>First Murderer</b></a>
+<blockquote>
+<A NAME=3.3.16>His horses go about.</A><br>
+</blockquote>
+
+<A NAME=speech9><b>Third Murderer</b></a>
+<blockquote>
+<A NAME=3.3.17>Almost a mile: but he does usually,</A><br>
+<A NAME=3.3.18>So all men do, from hence to the palace gate</A><br>
+<A NAME=3.3.19>Make it their walk.</A><br>
+</blockquote>
+
+<A NAME=speech10><b>Second Murderer</b></a>
+<blockquote>
+<A NAME=3.3.20>A light, a light!</A><br>
+<p><i>Enter BANQUO, and FLEANCE with a torch</i></p>
+</blockquote>
+
+<A NAME=speech11><b>Third Murderer</b></a>
+<blockquote>
+<A NAME=3.3.21>'Tis he.</A><br>
+</blockquote>
+
+<A NAME=speech12><b>First Murderer</b></a>
+<blockquote>
+<A NAME=3.3.22>Stand to't.</A><br>
+</blockquote>
+
+<A NAME=speech13><b>BANQUO</b></a>
+<blockquote>
+<A NAME=3.3.23>It will be rain to-night.</A><br>
+</blockquote>
+
+<A NAME=speech14><b>First Murderer</b></a>
+<blockquote>
+<A NAME=3.3.24>Let it come down.</A><br>
+<p><i>They set upon BANQUO</i></p>
+</blockquote>
+
+<A NAME=speech15><b>BANQUO</b></a>
+<blockquote>
+<A NAME=3.3.25>O, treachery! Fly, good Fleance, fly, fly, fly!</A><br>
+<A NAME=3.3.26>Thou mayst revenge. O slave!</A><br>
+<p><i>Dies. FLEANCE escapes</i></p>
+</blockquote>
+
+<A NAME=speech16><b>Third Murderer</b></a>
+<blockquote>
+<A NAME=3.3.27>Who did strike out the light?</A><br>
+</blockquote>
+
+<A NAME=speech17><b>First Murderer</b></a>
+<blockquote>
+<A NAME=3.3.28>Wast not the way?</A><br>
+</blockquote>
+
+<A NAME=speech18><b>Third Murderer</b></a>
+<blockquote>
+<A NAME=3.3.29>There's but one down; the son is fled.</A><br>
+</blockquote>
+
+<A NAME=speech19><b>Second Murderer</b></a>
+<blockquote>
+<A NAME=3.3.30>We have lost</A><br>
+<A NAME=3.3.31>Best half of our affair.</A><br>
+</blockquote>
+
+<A NAME=speech20><b>First Murderer</b></a>
+<blockquote>
+<A NAME=3.3.32>Well, let's away, and say how much is done.</A><br>
+<p><i>Exeunt</i></p>
+</blockquote>
+<h3>SCENE IV. The same. Hall in the palace.</h3>
+<p><blockquote>
+<i>A banquet prepared. Enter MACBETH, LADY MACBETH, ROSS, LENNOX, Lords, and Attendants</i>
+</blockquote>
+
+<A NAME=speech1><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.4.1>You know your own degrees; sit down: at first</A><br>
+<A NAME=3.4.2>And last the hearty welcome.</A><br>
+</blockquote>
+
+<A NAME=speech2><b>Lords</b></a>
+<blockquote>
+<A NAME=3.4.3>Thanks to your majesty.</A><br>
+</blockquote>
+
+<A NAME=speech3><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.4.4>Ourself will mingle with society,</A><br>
+<A NAME=3.4.5>And play the humble host.</A><br>
+<A NAME=3.4.6>Our hostess keeps her state, but in best time</A><br>
+<A NAME=3.4.7>We will require her welcome.</A><br>
+</blockquote>
+
+<A NAME=speech4><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=3.4.8>Pronounce it for me, sir, to all our friends;</A><br>
+<A NAME=3.4.9>For my heart speaks they are welcome.</A><br>
+<p><i>First Murderer appears at the door</i></p>
+</blockquote>
+
+<A NAME=speech5><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.4.10>See, they encounter thee with their hearts' thanks.</A><br>
+<A NAME=3.4.11>Both sides are even: here I'll sit i' the midst:</A><br>
+<A NAME=3.4.12>Be large in mirth; anon we'll drink a measure</A><br>
+<A NAME=3.4.13>The table round.</A><br>
+<p><i>Approaching the door</i></p>
+<A NAME=3.4.14>There's blood on thy face.</A><br>
+</blockquote>
+
+<A NAME=speech6><b>First Murderer</b></a>
+<blockquote>
+<A NAME=3.4.15>'Tis Banquo's then.</A><br>
+</blockquote>
+
+<A NAME=speech7><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.4.16>'Tis better thee without than he within.</A><br>
+<A NAME=3.4.17>Is he dispatch'd?</A><br>
+</blockquote>
+
+<A NAME=speech8><b>First Murderer</b></a>
+<blockquote>
+<A NAME=3.4.18>My lord, his throat is cut; that I did for him.</A><br>
+</blockquote>
+
+<A NAME=speech9><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.4.19>Thou art the best o' the cut-throats: yet he's good</A><br>
+<A NAME=3.4.20>That did the like for Fleance: if thou didst it,</A><br>
+<A NAME=3.4.21>Thou art the nonpareil.</A><br>
+</blockquote>
+
+<A NAME=speech10><b>First Murderer</b></a>
+<blockquote>
+<A NAME=3.4.22>Most royal sir,</A><br>
+<A NAME=3.4.23>Fleance is 'scaped.</A><br>
+</blockquote>
+
+<A NAME=speech11><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.4.24>Then comes my fit again: I had else been perfect,</A><br>
+<A NAME=3.4.25>Whole as the marble, founded as the rock,</A><br>
+<A NAME=3.4.26>As broad and general as the casing air:</A><br>
+<A NAME=3.4.27>But now I am cabin'd, cribb'd, confined, bound in</A><br>
+<A NAME=3.4.28>To saucy doubts and fears. But Banquo's safe?</A><br>
+</blockquote>
+
+<A NAME=speech12><b>First Murderer</b></a>
+<blockquote>
+<A NAME=3.4.29>Ay, my good lord: safe in a ditch he bides,</A><br>
+<A NAME=3.4.30>With twenty trenched gashes on his head;</A><br>
+<A NAME=3.4.31>The least a death to nature.</A><br>
+</blockquote>
+
+<A NAME=speech13><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.4.32>Thanks for that:</A><br>
+<A NAME=3.4.33>There the grown serpent lies; the worm that's fled</A><br>
+<A NAME=3.4.34>Hath nature that in time will venom breed,</A><br>
+<A NAME=3.4.35>No teeth for the present. Get thee gone: to-morrow</A><br>
+<A NAME=3.4.36>We'll hear, ourselves, again.</A><br>
+<p><i>Exit Murderer</i></p>
+</blockquote>
+
+<A NAME=speech14><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=3.4.37>My royal lord,</A><br>
+<A NAME=3.4.38>You do not give the cheer: the feast is sold</A><br>
+<A NAME=3.4.39>That is not often vouch'd, while 'tis a-making,</A><br>
+<A NAME=3.4.40>'Tis given with welcome: to feed were best at home;</A><br>
+<A NAME=3.4.41>From thence the sauce to meat is ceremony;</A><br>
+<A NAME=3.4.42>Meeting were bare without it.</A><br>
+</blockquote>
+
+<A NAME=speech15><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.4.43>Sweet remembrancer!</A><br>
+<A NAME=3.4.44>Now, good digestion wait on appetite,</A><br>
+<A NAME=3.4.45>And health on both!</A><br>
+</blockquote>
+
+<A NAME=speech16><b>LENNOX</b></a>
+<blockquote>
+<A NAME=3.4.46>May't please your highness sit.</A><br>
+<p><i>The GHOST OF BANQUO enters, and sits in MACBETH's place</i></p>
+</blockquote>
+
+<A NAME=speech17><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.4.47>Here had we now our country's honour roof'd,</A><br>
+<A NAME=3.4.48>Were the graced person of our Banquo present;</A><br>
+<A NAME=3.4.49>Who may I rather challenge for unkindness</A><br>
+<A NAME=3.4.50>Than pity for mischance!</A><br>
+</blockquote>
+
+<A NAME=speech18><b>ROSS</b></a>
+<blockquote>
+<A NAME=3.4.51>His absence, sir,</A><br>
+<A NAME=3.4.52>Lays blame upon his promise. Please't your highness</A><br>
+<A NAME=3.4.53>To grace us with your royal company.</A><br>
+</blockquote>
+
+<A NAME=speech19><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.4.54>The table's full.</A><br>
+</blockquote>
+
+<A NAME=speech20><b>LENNOX</b></a>
+<blockquote>
+<A NAME=3.4.55> Here is a place reserved, sir.</A><br>
+</blockquote>
+
+<A NAME=speech21><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.4.56>Where?</A><br>
+</blockquote>
+
+<A NAME=speech22><b>LENNOX</b></a>
+<blockquote>
+<A NAME=3.4.57>Here, my good lord. What is't that moves your highness?</A><br>
+</blockquote>
+
+<A NAME=speech23><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.4.58>Which of you have done this?</A><br>
+</blockquote>
+
+<A NAME=speech24><b>Lords</b></a>
+<blockquote>
+<A NAME=3.4.59>What, my good lord?</A><br>
+</blockquote>
+
+<A NAME=speech25><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.4.60>Thou canst not say I did it: never shake</A><br>
+<A NAME=3.4.61>Thy gory locks at me.</A><br>
+</blockquote>
+
+<A NAME=speech26><b>ROSS</b></a>
+<blockquote>
+<A NAME=3.4.62>Gentlemen, rise: his highness is not well.</A><br>
+</blockquote>
+
+<A NAME=speech27><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=3.4.63>Sit, worthy friends: my lord is often thus,</A><br>
+<A NAME=3.4.64>And hath been from his youth: pray you, keep seat;</A><br>
+<A NAME=3.4.65>The fit is momentary; upon a thought</A><br>
+<A NAME=3.4.66>He will again be well: if much you note him,</A><br>
+<A NAME=3.4.67>You shall offend him and extend his passion:</A><br>
+<A NAME=3.4.68>Feed, and regard him not. Are you a man?</A><br>
+</blockquote>
+
+<A NAME=speech28><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.4.69>Ay, and a bold one, that dare look on that</A><br>
+<A NAME=3.4.70>Which might appal the devil.</A><br>
+</blockquote>
+
+<A NAME=speech29><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=3.4.71>O proper stuff!</A><br>
+<A NAME=3.4.72>This is the very painting of your fear:</A><br>
+<A NAME=3.4.73>This is the air-drawn dagger which, you said,</A><br>
+<A NAME=3.4.74>Led you to Duncan. O, these flaws and starts,</A><br>
+<A NAME=3.4.75>Impostors to true fear, would well become</A><br>
+<A NAME=3.4.76>A woman's story at a winter's fire,</A><br>
+<A NAME=3.4.77>Authorized by her grandam. Shame itself!</A><br>
+<A NAME=3.4.78>Why do you make such faces? When all's done,</A><br>
+<A NAME=3.4.79>You look but on a stool.</A><br>
+</blockquote>
+
+<A NAME=speech30><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.4.80>Prithee, see there! behold! look! lo!</A><br>
+<A NAME=3.4.81>how say you?</A><br>
+<A NAME=3.4.82>Why, what care I? If thou canst nod, speak too.</A><br>
+<A NAME=3.4.83>If charnel-houses and our graves must send</A><br>
+<A NAME=3.4.84>Those that we bury back, our monuments</A><br>
+<A NAME=3.4.85>Shall be the maws of kites.</A><br>
+<p><i>GHOST OF BANQUO vanishes</i></p>
+</blockquote>
+
+<A NAME=speech31><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=3.4.86>What, quite unmann'd in folly?</A><br>
+</blockquote>
+
+<A NAME=speech32><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.4.87>If I stand here, I saw him.</A><br>
+</blockquote>
+
+<A NAME=speech33><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=3.4.88>Fie, for shame!</A><br>
+</blockquote>
+
+<A NAME=speech34><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.4.89>Blood hath been shed ere now, i' the olden time,</A><br>
+<A NAME=3.4.90>Ere human statute purged the gentle weal;</A><br>
+<A NAME=3.4.91>Ay, and since too, murders have been perform'd</A><br>
+<A NAME=3.4.92>Too terrible for the ear: the times have been,</A><br>
+<A NAME=3.4.93>That, when the brains were out, the man would die,</A><br>
+<A NAME=3.4.94>And there an end; but now they rise again,</A><br>
+<A NAME=3.4.95>With twenty mortal murders on their crowns,</A><br>
+<A NAME=3.4.96>And push us from our stools: this is more strange</A><br>
+<A NAME=3.4.97>Than such a murder is.</A><br>
+</blockquote>
+
+<A NAME=speech35><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=3.4.98>My worthy lord,</A><br>
+<A NAME=3.4.99>Your noble friends do lack you.</A><br>
+</blockquote>
+
+<A NAME=speech36><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.4.100>I do forget.</A><br>
+<A NAME=3.4.101>Do not muse at me, my most worthy friends,</A><br>
+<A NAME=3.4.102>I have a strange infirmity, which is nothing</A><br>
+<A NAME=3.4.103>To those that know me. Come, love and health to all;</A><br>
+<A NAME=3.4.104>Then I'll sit down. Give me some wine; fill full.</A><br>
+<A NAME=3.4.105>I drink to the general joy o' the whole table,</A><br>
+<A NAME=3.4.106>And to our dear friend Banquo, whom we miss;</A><br>
+<A NAME=3.4.107>Would he were here! to all, and him, we thirst,</A><br>
+<A NAME=3.4.108>And all to all.</A><br>
+</blockquote>
+
+<A NAME=speech37><b>Lords</b></a>
+<blockquote>
+<A NAME=3.4.109> Our duties, and the pledge.</A><br>
+<p><i>Re-enter GHOST OF BANQUO</i></p>
+</blockquote>
+
+<A NAME=speech38><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.4.110>Avaunt! and quit my sight! let the earth hide thee!</A><br>
+<A NAME=3.4.111>Thy bones are marrowless, thy blood is cold;</A><br>
+<A NAME=3.4.112>Thou hast no speculation in those eyes</A><br>
+<A NAME=3.4.113>Which thou dost glare with!</A><br>
+</blockquote>
+
+<A NAME=speech39><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=3.4.114>Think of this, good peers,</A><br>
+<A NAME=3.4.115>But as a thing of custom: 'tis no other;</A><br>
+<A NAME=3.4.116>Only it spoils the pleasure of the time.</A><br>
+</blockquote>
+
+<A NAME=speech40><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.4.117>What man dare, I dare:</A><br>
+<A NAME=3.4.118>Approach thou like the rugged Russian bear,</A><br>
+<A NAME=3.4.119>The arm'd rhinoceros, or the Hyrcan tiger;</A><br>
+<A NAME=3.4.120>Take any shape but that, and my firm nerves</A><br>
+<A NAME=3.4.121>Shall never tremble: or be alive again,</A><br>
+<A NAME=3.4.122>And dare me to the desert with thy sword;</A><br>
+<A NAME=3.4.123>If trembling I inhabit then, protest me</A><br>
+<A NAME=3.4.124>The baby of a girl. Hence, horrible shadow!</A><br>
+<A NAME=3.4.125>Unreal mockery, hence!</A><br>
+<p><i>GHOST OF BANQUO vanishes</i></p>
+<A NAME=3.4.126>Why, so: being gone,</A><br>
+<A NAME=3.4.127>I am a man again. Pray you, sit still.</A><br>
+</blockquote>
+
+<A NAME=speech41><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=3.4.128>You have displaced the mirth, broke the good meeting,</A><br>
+<A NAME=3.4.129>With most admired disorder.</A><br>
+</blockquote>
+
+<A NAME=speech42><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.4.130>Can such things be,</A><br>
+<A NAME=3.4.131>And overcome us like a summer's cloud,</A><br>
+<A NAME=3.4.132>Without our special wonder? You make me strange</A><br>
+<A NAME=3.4.133>Even to the disposition that I owe,</A><br>
+<A NAME=3.4.134>When now I think you can behold such sights,</A><br>
+<A NAME=3.4.135>And keep the natural ruby of your cheeks,</A><br>
+<A NAME=3.4.136>When mine is blanched with fear.</A><br>
+</blockquote>
+
+<A NAME=speech43><b>ROSS</b></a>
+<blockquote>
+<A NAME=3.4.137>What sights, my lord?</A><br>
+</blockquote>
+
+<A NAME=speech44><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=3.4.138>I pray you, speak not; he grows worse and worse;</A><br>
+<A NAME=3.4.139>Question enrages him. At once, good night:</A><br>
+<A NAME=3.4.140>Stand not upon the order of your going,</A><br>
+<A NAME=3.4.141>But go at once.</A><br>
+</blockquote>
+
+<A NAME=speech45><b>LENNOX</b></a>
+<blockquote>
+<A NAME=3.4.142> Good night; and better health</A><br>
+<A NAME=3.4.143>Attend his majesty!</A><br>
+</blockquote>
+
+<A NAME=speech46><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=3.4.144>A kind good night to all!</A><br>
+<p><i>Exeunt all but MACBETH and LADY MACBETH</i></p>
+</blockquote>
+
+<A NAME=speech47><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.4.145>It will have blood; they say, blood will have blood:</A><br>
+<A NAME=3.4.146>Stones have been known to move and trees to speak;</A><br>
+<A NAME=3.4.147>Augurs and understood relations have</A><br>
+<A NAME=3.4.148>By magot-pies and choughs and rooks brought forth</A><br>
+<A NAME=3.4.149>The secret'st man of blood. What is the night?</A><br>
+</blockquote>
+
+<A NAME=speech48><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=3.4.150>Almost at odds with morning, which is which.</A><br>
+</blockquote>
+
+<A NAME=speech49><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.4.151>How say'st thou, that Macduff denies his person</A><br>
+<A NAME=3.4.152>At our great bidding?</A><br>
+</blockquote>
+
+<A NAME=speech50><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=3.4.153>Did you send to him, sir?</A><br>
+</blockquote>
+
+<A NAME=speech51><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.4.154>I hear it by the way; but I will send:</A><br>
+<A NAME=3.4.155>There's not a one of them but in his house</A><br>
+<A NAME=3.4.156>I keep a servant fee'd. I will to-morrow,</A><br>
+<A NAME=3.4.157>And betimes I will, to the weird sisters:</A><br>
+<A NAME=3.4.158>More shall they speak; for now I am bent to know,</A><br>
+<A NAME=3.4.159>By the worst means, the worst. For mine own good,</A><br>
+<A NAME=3.4.160>All causes shall give way: I am in blood</A><br>
+<A NAME=3.4.161>Stepp'd in so far that, should I wade no more,</A><br>
+<A NAME=3.4.162>Returning were as tedious as go o'er:</A><br>
+<A NAME=3.4.163>Strange things I have in head, that will to hand;</A><br>
+<A NAME=3.4.164>Which must be acted ere they may be scann'd.</A><br>
+</blockquote>
+
+<A NAME=speech52><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=3.4.165>You lack the season of all natures, sleep.</A><br>
+</blockquote>
+
+<A NAME=speech53><b>MACBETH</b></a>
+<blockquote>
+<A NAME=3.4.166>Come, we'll to sleep. My strange and self-abuse</A><br>
+<A NAME=3.4.167>Is the initiate fear that wants hard use:</A><br>
+<A NAME=3.4.168>We are yet but young in deed.</A><br>
+<p><i>Exeunt</i></p>
+</blockquote>
+<h3>SCENE V. A Heath.</h3>
+<p><blockquote>
+<i>Thunder. Enter the three Witches meeting HECATE</i>
+</blockquote>
+
+<A NAME=speech1><b>First Witch</b></a>
+<blockquote>
+<A NAME=3.5.1>Why, how now, Hecate! you look angerly.</A><br>
+</blockquote>
+
+<A NAME=speech2><b>HECATE</b></a>
+<blockquote>
+<A NAME=3.5.2>Have I not reason, beldams as you are,</A><br>
+<A NAME=3.5.3>Saucy and overbold? How did you dare</A><br>
+<A NAME=3.5.4>To trade and traffic with Macbeth</A><br>
+<A NAME=3.5.5>In riddles and affairs of death;</A><br>
+<A NAME=3.5.6>And I, the mistress of your charms,</A><br>
+<A NAME=3.5.7>The close contriver of all harms,</A><br>
+<A NAME=3.5.8>Was never call'd to bear my part,</A><br>
+<A NAME=3.5.9>Or show the glory of our art?</A><br>
+<A NAME=3.5.10>And, which is worse, all you have done</A><br>
+<A NAME=3.5.11>Hath been but for a wayward son,</A><br>
+<A NAME=3.5.12>Spiteful and wrathful, who, as others do,</A><br>
+<A NAME=3.5.13>Loves for his own ends, not for you.</A><br>
+<A NAME=3.5.14>But make amends now: get you gone,</A><br>
+<A NAME=3.5.15>And at the pit of Acheron</A><br>
+<A NAME=3.5.16>Meet me i' the morning: thither he</A><br>
+<A NAME=3.5.17>Will come to know his destiny:</A><br>
+<A NAME=3.5.18>Your vessels and your spells provide,</A><br>
+<A NAME=3.5.19>Your charms and every thing beside.</A><br>
+<A NAME=3.5.20>I am for the air; this night I'll spend</A><br>
+<A NAME=3.5.21>Unto a dismal and a fatal end:</A><br>
+<A NAME=3.5.22>Great business must be wrought ere noon:</A><br>
+<A NAME=3.5.23>Upon the corner of the moon</A><br>
+<A NAME=3.5.24>There hangs a vaporous drop profound;</A><br>
+<A NAME=3.5.25>I'll catch it ere it come to ground:</A><br>
+<A NAME=3.5.26>And that distill'd by magic sleights</A><br>
+<A NAME=3.5.27>Shall raise such artificial sprites</A><br>
+<A NAME=3.5.28>As by the strength of their illusion</A><br>
+<A NAME=3.5.29>Shall draw him on to his confusion:</A><br>
+<A NAME=3.5.30>He shall spurn fate, scorn death, and bear</A><br>
+<A NAME=3.5.31>He hopes 'bove wisdom, grace and fear:</A><br>
+<A NAME=3.5.32>And you all know, security</A><br>
+<A NAME=3.5.33>Is mortals' chiefest enemy.</A><br>
+<p><i>Music and a song within: 'Come away, come away,' & c</i></p>
+<A NAME=3.5.34>Hark! I am call'd; my little spirit, see,</A><br>
+<A NAME=3.5.35>Sits in a foggy cloud, and stays for me.</A><br>
+<p><i>Exit</i></p>
+</blockquote>
+
+<A NAME=speech3><b>First Witch</b></a>
+<blockquote>
+<A NAME=3.5.36>Come, let's make haste; she'll soon be back again.</A><br>
+<p><i>Exeunt</i></p>
+</blockquote>
+<h3>SCENE VI. Forres. The palace.</h3>
+<p><blockquote>
+<i>Enter LENNOX and another Lord</i>
+</blockquote>
+
+<A NAME=speech1><b>LENNOX</b></a>
+<blockquote>
+<A NAME=3.6.1>My former speeches have but hit your thoughts,</A><br>
+<A NAME=3.6.2>Which can interpret further: only, I say,</A><br>
+<A NAME=3.6.3>Things have been strangely borne. The</A><br>
+<A NAME=3.6.4>gracious Duncan</A><br>
+<A NAME=3.6.5>Was pitied of Macbeth: marry, he was dead:</A><br>
+<A NAME=3.6.6>And the right-valiant Banquo walk'd too late;</A><br>
+<A NAME=3.6.7>Whom, you may say, if't please you, Fleance kill'd,</A><br>
+<A NAME=3.6.8>For Fleance fled: men must not walk too late.</A><br>
+<A NAME=3.6.9>Who cannot want the thought how monstrous</A><br>
+<A NAME=3.6.10>It was for Malcolm and for Donalbain</A><br>
+<A NAME=3.6.11>To kill their gracious father? damned fact!</A><br>
+<A NAME=3.6.12>How it did grieve Macbeth! did he not straight</A><br>
+<A NAME=3.6.13>In pious rage the two delinquents tear,</A><br>
+<A NAME=3.6.14>That were the slaves of drink and thralls of sleep?</A><br>
+<A NAME=3.6.15>Was not that nobly done? Ay, and wisely too;</A><br>
+<A NAME=3.6.16>For 'twould have anger'd any heart alive</A><br>
+<A NAME=3.6.17>To hear the men deny't. So that, I say,</A><br>
+<A NAME=3.6.18>He has borne all things well: and I do think</A><br>
+<A NAME=3.6.19>That had he Duncan's sons under his key--</A><br>
+<A NAME=3.6.20>As, an't please heaven, he shall not--they</A><br>
+<A NAME=3.6.21>should find</A><br>
+<A NAME=3.6.22>What 'twere to kill a father; so should Fleance.</A><br>
+<A NAME=3.6.23>But, peace! for from broad words and 'cause he fail'd</A><br>
+<A NAME=3.6.24>His presence at the tyrant's feast, I hear</A><br>
+<A NAME=3.6.25>Macduff lives in disgrace: sir, can you tell</A><br>
+<A NAME=3.6.26>Where he bestows himself?</A><br>
+</blockquote>
+
+<A NAME=speech2><b>Lord</b></a>
+<blockquote>
+<A NAME=3.6.27>The son of Duncan,</A><br>
+<A NAME=3.6.28>From whom this tyrant holds the due of birth</A><br>
+<A NAME=3.6.29>Lives in the English court, and is received</A><br>
+<A NAME=3.6.30>Of the most pious Edward with such grace</A><br>
+<A NAME=3.6.31>That the malevolence of fortune nothing</A><br>
+<A NAME=3.6.32>Takes from his high respect: thither Macduff</A><br>
+<A NAME=3.6.33>Is gone to pray the holy king, upon his aid</A><br>
+<A NAME=3.6.34>To wake Northumberland and warlike Siward:</A><br>
+<A NAME=3.6.35>That, by the help of these--with Him above</A><br>
+<A NAME=3.6.36>To ratify the work--we may again</A><br>
+<A NAME=3.6.37>Give to our tables meat, sleep to our nights,</A><br>
+<A NAME=3.6.38>Free from our feasts and banquets bloody knives,</A><br>
+<A NAME=3.6.39>Do faithful homage and receive free honours:</A><br>
+<A NAME=3.6.40>All which we pine for now: and this report</A><br>
+<A NAME=3.6.41>Hath so exasperate the king that he</A><br>
+<A NAME=3.6.42>Prepares for some attempt of war.</A><br>
+</blockquote>
+
+<A NAME=speech3><b>LENNOX</b></a>
+<blockquote>
+<A NAME=3.6.43>Sent he to Macduff?</A><br>
+</blockquote>
+
+<A NAME=speech4><b>Lord</b></a>
+<blockquote>
+<A NAME=3.6.44>He did: and with an absolute 'Sir, not I,'</A><br>
+<A NAME=3.6.45>The cloudy messenger turns me his back,</A><br>
+<A NAME=3.6.46>And hums, as who should say 'You'll rue the time</A><br>
+<A NAME=3.6.47>That clogs me with this answer.'</A><br>
+</blockquote>
+
+<A NAME=speech5><b>LENNOX</b></a>
+<blockquote>
+<A NAME=3.6.48>And that well might</A><br>
+<A NAME=3.6.49>Advise him to a caution, to hold what distance</A><br>
+<A NAME=3.6.50>His wisdom can provide. Some holy angel</A><br>
+<A NAME=3.6.51>Fly to the court of England and unfold</A><br>
+<A NAME=3.6.52>His message ere he come, that a swift blessing</A><br>
+<A NAME=3.6.53>May soon return to this our suffering country</A><br>
+<A NAME=3.6.54>Under a hand accursed!</A><br>
+</blockquote>
+
+<A NAME=speech6><b>Lord</b></a>
+<blockquote>
+<A NAME=3.6.55>I'll send my prayers with him.</A><br>
+<p><i>Exeunt</i></p>
+</blockquote><p>
+<H3>ACT IV</h3>
+<h3>SCENE I. A cavern. In the middle, a boiling cauldron.</h3>
+<p><blockquote>
+<i>Thunder. Enter the three Witches</i>
+</blockquote>
+
+<A NAME=speech1><b>First Witch</b></a>
+<blockquote>
+<A NAME=4.1.1>Thrice the brinded cat hath mew'd.</A><br>
+</blockquote>
+
+<A NAME=speech2><b>Second Witch</b></a>
+<blockquote>
+<A NAME=4.1.2>Thrice and once the hedge-pig whined.</A><br>
+</blockquote>
+
+<A NAME=speech3><b>Third Witch</b></a>
+<blockquote>
+<A NAME=4.1.3>Harpier cries 'Tis time, 'tis time.</A><br>
+</blockquote>
+
+<A NAME=speech4><b>First Witch</b></a>
+<blockquote>
+<A NAME=4.1.4>Round about the cauldron go;</A><br>
+<A NAME=4.1.5>In the poison'd entrails throw.</A><br>
+<A NAME=4.1.6>Toad, that under cold stone</A><br>
+<A NAME=4.1.7>Days and nights has thirty-one</A><br>
+<A NAME=4.1.8>Swelter'd venom sleeping got,</A><br>
+<A NAME=4.1.9>Boil thou first i' the charmed pot.</A><br>
+</blockquote>
+
+<A NAME=speech5><b>ALL</b></a>
+<blockquote>
+<A NAME=4.1.10>Double, double toil and trouble;</A><br>
+<A NAME=4.1.11>Fire burn, and cauldron bubble.</A><br>
+</blockquote>
+
+<A NAME=speech6><b>Second Witch</b></a>
+<blockquote>
+<A NAME=4.1.12>Fillet of a fenny snake,</A><br>
+<A NAME=4.1.13>In the cauldron boil and bake;</A><br>
+<A NAME=4.1.14>Eye of newt and toe of frog,</A><br>
+<A NAME=4.1.15>Wool of bat and tongue of dog,</A><br>
+<A NAME=4.1.16>Adder's fork and blind-worm's sting,</A><br>
+<A NAME=4.1.17>Lizard's leg and owlet's wing,</A><br>
+<A NAME=4.1.18>For a charm of powerful trouble,</A><br>
+<A NAME=4.1.19>Like a hell-broth boil and bubble.</A><br>
+</blockquote>
+
+<A NAME=speech7><b>ALL</b></a>
+<blockquote>
+<A NAME=4.1.20>Double, double toil and trouble;</A><br>
+<A NAME=4.1.21>Fire burn and cauldron bubble.</A><br>
+</blockquote>
+
+<A NAME=speech8><b>Third Witch</b></a>
+<blockquote>
+<A NAME=4.1.22>Scale of dragon, tooth of wolf,</A><br>
+<A NAME=4.1.23>Witches' mummy, maw and gulf</A><br>
+<A NAME=4.1.24>Of the ravin'd salt-sea shark,</A><br>
+<A NAME=4.1.25>Root of hemlock digg'd i' the dark,</A><br>
+<A NAME=4.1.26>Liver of blaspheming Jew,</A><br>
+<A NAME=4.1.27>Gall of goat, and slips of yew</A><br>
+<A NAME=4.1.28>Silver'd in the moon's eclipse,</A><br>
+<A NAME=4.1.29>Nose of Turk and Tartar's lips,</A><br>
+<A NAME=4.1.30>Finger of birth-strangled babe</A><br>
+<A NAME=4.1.31>Ditch-deliver'd by a drab,</A><br>
+<A NAME=4.1.32>Make the gruel thick and slab:</A><br>
+<A NAME=4.1.33>Add thereto a tiger's chaudron,</A><br>
+<A NAME=4.1.34>For the ingredients of our cauldron.</A><br>
+</blockquote>
+
+<A NAME=speech9><b>ALL</b></a>
+<blockquote>
+<A NAME=4.1.35>Double, double toil and trouble;</A><br>
+<A NAME=4.1.36>Fire burn and cauldron bubble.</A><br>
+</blockquote>
+
+<A NAME=speech10><b>Second Witch</b></a>
+<blockquote>
+<A NAME=4.1.37>Cool it with a baboon's blood,</A><br>
+<A NAME=4.1.38>Then the charm is firm and good.</A><br>
+<p><i>Enter HECATE to the other three Witches</i></p>
+</blockquote>
+
+<A NAME=speech11><b>HECATE</b></a>
+<blockquote>
+<A NAME=4.1.39>O well done! I commend your pains;</A><br>
+<A NAME=4.1.40>And every one shall share i' the gains;</A><br>
+<A NAME=4.1.41>And now about the cauldron sing,</A><br>
+<A NAME=4.1.42>Live elves and fairies in a ring,</A><br>
+<A NAME=4.1.43>Enchanting all that you put in.</A><br>
+<p><i>Music and a song: 'Black spirits,' & c</i></p>
+<p><i>HECATE retires</i></p>
+</blockquote>
+
+<A NAME=speech12><b>Second Witch</b></a>
+<blockquote>
+<A NAME=4.1.44>By the pricking of my thumbs,</A><br>
+<A NAME=4.1.45>Something wicked this way comes.</A><br>
+<A NAME=4.1.46>Open, locks,</A><br>
+<A NAME=4.1.47>Whoever knocks!</A><br>
+<p><i>Enter MACBETH</i></p>
+</blockquote>
+
+<A NAME=speech13><b>MACBETH</b></a>
+<blockquote>
+<A NAME=4.1.48>How now, you secret, black, and midnight hags!</A><br>
+<A NAME=4.1.49>What is't you do?</A><br>
+</blockquote>
+
+<A NAME=speech14><b>ALL</b></a>
+<blockquote>
+<A NAME=4.1.50> A deed without a name.</A><br>
+</blockquote>
+
+<A NAME=speech15><b>MACBETH</b></a>
+<blockquote>
+<A NAME=4.1.51>I conjure you, by that which you profess,</A><br>
+<A NAME=4.1.52>Howe'er you come to know it, answer me:</A><br>
+<A NAME=4.1.53>Though you untie the winds and let them fight</A><br>
+<A NAME=4.1.54>Against the churches; though the yesty waves</A><br>
+<A NAME=4.1.55>Confound and swallow navigation up;</A><br>
+<A NAME=4.1.56>Though bladed corn be lodged and trees blown down;</A><br>
+<A NAME=4.1.57>Though castles topple on their warders' heads;</A><br>
+<A NAME=4.1.58>Though palaces and pyramids do slope</A><br>
+<A NAME=4.1.59>Their heads to their foundations; though the treasure</A><br>
+<A NAME=4.1.60>Of nature's germens tumble all together,</A><br>
+<A NAME=4.1.61>Even till destruction sicken; answer me</A><br>
+<A NAME=4.1.62>To what I ask you.</A><br>
+</blockquote>
+
+<A NAME=speech16><b>First Witch</b></a>
+<blockquote>
+<A NAME=4.1.63> Speak.</A><br>
+</blockquote>
+
+<A NAME=speech17><b>Second Witch</b></a>
+<blockquote>
+<A NAME=4.1.64>Demand.</A><br>
+</blockquote>
+
+<A NAME=speech18><b>Third Witch</b></a>
+<blockquote>
+<A NAME=4.1.65>We'll answer.</A><br>
+</blockquote>
+
+<A NAME=speech19><b>First Witch</b></a>
+<blockquote>
+<A NAME=4.1.66>Say, if thou'dst rather hear it from our mouths,</A><br>
+<A NAME=4.1.67>Or from our masters?</A><br>
+</blockquote>
+
+<A NAME=speech20><b>MACBETH</b></a>
+<blockquote>
+<A NAME=4.1.68>Call 'em; let me see 'em.</A><br>
+</blockquote>
+
+<A NAME=speech21><b>First Witch</b></a>
+<blockquote>
+<A NAME=4.1.69>Pour in sow's blood, that hath eaten</A><br>
+<A NAME=4.1.70>Her nine farrow; grease that's sweaten</A><br>
+<A NAME=4.1.71>From the murderer's gibbet throw</A><br>
+<A NAME=4.1.72>Into the flame.</A><br>
+</blockquote>
+
+<A NAME=speech22><b>ALL</b></a>
+<blockquote>
+<A NAME=4.1.73> Come, high or low;</A><br>
+<A NAME=4.1.74>Thyself and office deftly show!</A><br>
+<p><i>Thunder. First Apparition: an armed Head</i></p>
+</blockquote>
+
+<A NAME=speech23><b>MACBETH</b></a>
+<blockquote>
+<A NAME=4.1.75>Tell me, thou unknown power,--</A><br>
+</blockquote>
+
+<A NAME=speech24><b>First Witch</b></a>
+<blockquote>
+<A NAME=4.1.76>He knows thy thought:</A><br>
+<A NAME=4.1.77>Hear his speech, but say thou nought.</A><br>
+</blockquote>
+
+<A NAME=speech25><b>First Apparition</b></a>
+<blockquote>
+<A NAME=4.1.78>Macbeth! Macbeth! Macbeth! beware Macduff;</A><br>
+<A NAME=4.1.79>Beware the thane of Fife. Dismiss me. Enough.</A><br>
+<p><i>Descends</i></p>
+</blockquote>
+
+<A NAME=speech26><b>MACBETH</b></a>
+<blockquote>
+<A NAME=4.1.80>Whate'er thou art, for thy good caution, thanks;</A><br>
+<A NAME=4.1.81>Thou hast harp'd my fear aright: but one</A><br>
+<A NAME=4.1.82>word more,--</A><br>
+</blockquote>
+
+<A NAME=speech27><b>First Witch</b></a>
+<blockquote>
+<A NAME=4.1.83>He will not be commanded: here's another,</A><br>
+<A NAME=4.1.84>More potent than the first.</A><br>
+<p><i>Thunder. Second Apparition: A bloody Child</i></p>
+</blockquote>
+
+<A NAME=speech28><b>Second Apparition</b></a>
+<blockquote>
+<A NAME=4.1.85>Macbeth! Macbeth! Macbeth!</A><br>
+</blockquote>
+
+<A NAME=speech29><b>MACBETH</b></a>
+<blockquote>
+<A NAME=4.1.86>Had I three ears, I'ld hear thee.</A><br>
+</blockquote>
+
+<A NAME=speech30><b>Second Apparition</b></a>
+<blockquote>
+<A NAME=4.1.87>Be bloody, bold, and resolute; laugh to scorn</A><br>
+<A NAME=4.1.88>The power of man, for none of woman born</A><br>
+<A NAME=4.1.89>Shall harm Macbeth.</A><br>
+<p><i>Descends</i></p>
+</blockquote>
+
+<A NAME=speech31><b>MACBETH</b></a>
+<blockquote>
+<A NAME=4.1.90>Then live, Macduff: what need I fear of thee?</A><br>
+<A NAME=4.1.91>But yet I'll make assurance double sure,</A><br>
+<A NAME=4.1.92>And take a bond of fate: thou shalt not live;</A><br>
+<A NAME=4.1.93>That I may tell pale-hearted fear it lies,</A><br>
+<A NAME=4.1.94>And sleep in spite of thunder.</A><br>
+<p><i>Thunder. Third Apparition: a Child crowned, with a tree in his hand</i></p>
+<A NAME=4.1.95>What is this</A><br>
+<A NAME=4.1.96>That rises like the issue of a king,</A><br>
+<A NAME=4.1.97>And wears upon his baby-brow the round</A><br>
+<A NAME=4.1.98>And top of sovereignty?</A><br>
+</blockquote>
+
+<A NAME=speech32><b>ALL</b></a>
+<blockquote>
+<A NAME=4.1.99>Listen, but speak not to't.</A><br>
+</blockquote>
+
+<A NAME=speech33><b>Third Apparition</b></a>
+<blockquote>
+<A NAME=4.1.100>Be lion-mettled, proud; and take no care</A><br>
+<A NAME=4.1.101>Who chafes, who frets, or where conspirers are:</A><br>
+<A NAME=4.1.102>Macbeth shall never vanquish'd be until</A><br>
+<A NAME=4.1.103>Great Birnam wood to high Dunsinane hill</A><br>
+<A NAME=4.1.104>Shall come against him.</A><br>
+<p><i>Descends</i></p>
+</blockquote>
+
+<A NAME=speech34><b>MACBETH</b></a>
+<blockquote>
+<A NAME=4.1.105>That will never be</A><br>
+<A NAME=4.1.106>Who can impress the forest, bid the tree</A><br>
+<A NAME=4.1.107>Unfix his earth-bound root? Sweet bodements! good!</A><br>
+<A NAME=4.1.108>Rebellion's head, rise never till the wood</A><br>
+<A NAME=4.1.109>Of Birnam rise, and our high-placed Macbeth</A><br>
+<A NAME=4.1.110>Shall live the lease of nature, pay his breath</A><br>
+<A NAME=4.1.111>To time and mortal custom. Yet my heart</A><br>
+<A NAME=4.1.112>Throbs to know one thing: tell me, if your art</A><br>
+<A NAME=4.1.113>Can tell so much: shall Banquo's issue ever</A><br>
+<A NAME=4.1.114>Reign in this kingdom?</A><br>
+</blockquote>
+
+<A NAME=speech35><b>ALL</b></a>
+<blockquote>
+<A NAME=4.1.115>Seek to know no more.</A><br>
+</blockquote>
+
+<A NAME=speech36><b>MACBETH</b></a>
+<blockquote>
+<A NAME=4.1.116>I will be satisfied: deny me this,</A><br>
+<A NAME=4.1.117>And an eternal curse fall on you! Let me know.</A><br>
+<A NAME=4.1.118>Why sinks that cauldron? and what noise is this?</A><br>
+<p><i>Hautboys</i></p>
+</blockquote>
+
+<A NAME=speech37><b>First Witch</b></a>
+<blockquote>
+<A NAME=4.1.119>Show!</A><br>
+</blockquote>
+
+<A NAME=speech38><b>Second Witch</b></a>
+<blockquote>
+<A NAME=4.1.120>Show!</A><br>
+</blockquote>
+
+<A NAME=speech39><b>Third Witch</b></a>
+<blockquote>
+<A NAME=4.1.121>Show!</A><br>
+</blockquote>
+
+<A NAME=speech40><b>ALL</b></a>
+<blockquote>
+<A NAME=4.1.122>Show his eyes, and grieve his heart;</A><br>
+<A NAME=4.1.123>Come like shadows, so depart!</A><br>
+<p><i>A show of Eight Kings, the last with a glass in his hand; GHOST OF BANQUO following</i></p>
+</blockquote>
+
+<A NAME=speech41><b>MACBETH</b></a>
+<blockquote>
+<A NAME=4.1.124>Thou art too like the spirit of Banquo: down!</A><br>
+<A NAME=4.1.125>Thy crown does sear mine eye-balls. And thy hair,</A><br>
+<A NAME=4.1.126>Thou other gold-bound brow, is like the first.</A><br>
+<A NAME=4.1.127>A third is like the former. Filthy hags!</A><br>
+<A NAME=4.1.128>Why do you show me this? A fourth! Start, eyes!</A><br>
+<A NAME=4.1.129>What, will the line stretch out to the crack of doom?</A><br>
+<A NAME=4.1.130>Another yet! A seventh! I'll see no more:</A><br>
+<A NAME=4.1.131>And yet the eighth appears, who bears a glass</A><br>
+<A NAME=4.1.132>Which shows me many more; and some I see</A><br>
+<A NAME=4.1.133>That two-fold balls and treble scepters carry:</A><br>
+<A NAME=4.1.134>Horrible sight! Now, I see, 'tis true;</A><br>
+<A NAME=4.1.135>For the blood-bolter'd Banquo smiles upon me,</A><br>
+<A NAME=4.1.136>And points at them for his.</A><br>
+<p><i>Apparitions vanish</i></p>
+<A NAME=4.1.137>What, is this so?</A><br>
+</blockquote>
+
+<A NAME=speech42><b>First Witch</b></a>
+<blockquote>
+<A NAME=4.1.138>Ay, sir, all this is so: but why</A><br>
+<A NAME=4.1.139>Stands Macbeth thus amazedly?</A><br>
+<A NAME=4.1.140>Come, sisters, cheer we up his sprites,</A><br>
+<A NAME=4.1.141>And show the best of our delights:</A><br>
+<A NAME=4.1.142>I'll charm the air to give a sound,</A><br>
+<A NAME=4.1.143>While you perform your antic round:</A><br>
+<A NAME=4.1.144>That this great king may kindly say,</A><br>
+<A NAME=4.1.145>Our duties did his welcome pay.</A><br>
+<p><i>Music. The witches dance and then vanish, with HECATE</i></p>
+</blockquote>
+
+<A NAME=speech43><b>MACBETH</b></a>
+<blockquote>
+<A NAME=4.1.146>Where are they? Gone? Let this pernicious hour</A><br>
+<A NAME=4.1.147>Stand aye accursed in the calendar!</A><br>
+<A NAME=4.1.148>Come in, without there!</A><br>
+<p><i>Enter LENNOX</i></p>
+</blockquote>
+
+<A NAME=speech44><b>LENNOX</b></a>
+<blockquote>
+<A NAME=4.1.149>What's your grace's will?</A><br>
+</blockquote>
+
+<A NAME=speech45><b>MACBETH</b></a>
+<blockquote>
+<A NAME=4.1.150>Saw you the weird sisters?</A><br>
+</blockquote>
+
+<A NAME=speech46><b>LENNOX</b></a>
+<blockquote>
+<A NAME=4.1.151>No, my lord.</A><br>
+</blockquote>
+
+<A NAME=speech47><b>MACBETH</b></a>
+<blockquote>
+<A NAME=4.1.152>Came they not by you?</A><br>
+</blockquote>
+
+<A NAME=speech48><b>LENNOX</b></a>
+<blockquote>
+<A NAME=4.1.153>No, indeed, my lord.</A><br>
+</blockquote>
+
+<A NAME=speech49><b>MACBETH</b></a>
+<blockquote>
+<A NAME=4.1.154>Infected be the air whereon they ride;</A><br>
+<A NAME=4.1.155>And damn'd all those that trust them! I did hear</A><br>
+<A NAME=4.1.156>The galloping of horse: who was't came by?</A><br>
+</blockquote>
+
+<A NAME=speech50><b>LENNOX</b></a>
+<blockquote>
+<A NAME=4.1.157>'Tis two or three, my lord, that bring you word</A><br>
+<A NAME=4.1.158>Macduff is fled to England.</A><br>
+</blockquote>
+
+<A NAME=speech51><b>MACBETH</b></a>
+<blockquote>
+<A NAME=4.1.159>Fled to England!</A><br>
+</blockquote>
+
+<A NAME=speech52><b>LENNOX</b></a>
+<blockquote>
+<A NAME=4.1.160>Ay, my good lord.</A><br>
+</blockquote>
+
+<A NAME=speech53><b>MACBETH</b></a>
+<blockquote>
+<A NAME=4.1.161>Time, thou anticipatest my dread exploits:</A><br>
+<A NAME=4.1.162>The flighty purpose never is o'ertook</A><br>
+<A NAME=4.1.163>Unless the deed go with it; from this moment</A><br>
+<A NAME=4.1.164>The very firstlings of my heart shall be</A><br>
+<A NAME=4.1.165>The firstlings of my hand. And even now,</A><br>
+<A NAME=4.1.166>To crown my thoughts with acts, be it thought and done:</A><br>
+<A NAME=4.1.167>The castle of Macduff I will surprise;</A><br>
+<A NAME=4.1.168>Seize upon Fife; give to the edge o' the sword</A><br>
+<A NAME=4.1.169>His wife, his babes, and all unfortunate souls</A><br>
+<A NAME=4.1.170>That trace him in his line. No boasting like a fool;</A><br>
+<A NAME=4.1.171>This deed I'll do before this purpose cool.</A><br>
+<A NAME=4.1.172>But no more sights!--Where are these gentlemen?</A><br>
+<A NAME=4.1.173>Come, bring me where they are.</A><br>
+<p><i>Exeunt</i></p>
+</blockquote>
+<h3>SCENE II. Fife. Macduff's castle.</h3>
+<p><blockquote>
+<i>Enter LADY MACDUFF, her Son, and ROSS</i>
+</blockquote>
+
+<A NAME=speech1><b>LADY MACDUFF</b></a>
+<blockquote>
+<A NAME=4.2.1>What had he done, to make him fly the land?</A><br>
+</blockquote>
+
+<A NAME=speech2><b>ROSS</b></a>
+<blockquote>
+<A NAME=4.2.2>You must have patience, madam.</A><br>
+</blockquote>
+
+<A NAME=speech3><b>LADY MACDUFF</b></a>
+<blockquote>
+<A NAME=4.2.3>He had none:</A><br>
+<A NAME=4.2.4>His flight was madness: when our actions do not,</A><br>
+<A NAME=4.2.5>Our fears do make us traitors.</A><br>
+</blockquote>
+
+<A NAME=speech4><b>ROSS</b></a>
+<blockquote>
+<A NAME=4.2.6>You know not</A><br>
+<A NAME=4.2.7>Whether it was his wisdom or his fear.</A><br>
+</blockquote>
+
+<A NAME=speech5><b>LADY MACDUFF</b></a>
+<blockquote>
+<A NAME=4.2.8>Wisdom! to leave his wife, to leave his babes,</A><br>
+<A NAME=4.2.9>His mansion and his titles in a place</A><br>
+<A NAME=4.2.10>From whence himself does fly? He loves us not;</A><br>
+<A NAME=4.2.11>He wants the natural touch: for the poor wren,</A><br>
+<A NAME=4.2.12>The most diminutive of birds, will fight,</A><br>
+<A NAME=4.2.13>Her young ones in her nest, against the owl.</A><br>
+<A NAME=4.2.14>All is the fear and nothing is the love;</A><br>
+<A NAME=4.2.15>As little is the wisdom, where the flight</A><br>
+<A NAME=4.2.16>So runs against all reason.</A><br>
+</blockquote>
+
+<A NAME=speech6><b>ROSS</b></a>
+<blockquote>
+<A NAME=4.2.17>My dearest coz,</A><br>
+<A NAME=4.2.18>I pray you, school yourself: but for your husband,</A><br>
+<A NAME=4.2.19>He is noble, wise, judicious, and best knows</A><br>
+<A NAME=4.2.20>The fits o' the season. I dare not speak</A><br>
+<A NAME=4.2.21>much further;</A><br>
+<A NAME=4.2.22>But cruel are the times, when we are traitors</A><br>
+<A NAME=4.2.23>And do not know ourselves, when we hold rumour</A><br>
+<A NAME=4.2.24>From what we fear, yet know not what we fear,</A><br>
+<A NAME=4.2.25>But float upon a wild and violent sea</A><br>
+<A NAME=4.2.26>Each way and move. I take my leave of you:</A><br>
+<A NAME=4.2.27>Shall not be long but I'll be here again:</A><br>
+<A NAME=4.2.28>Things at the worst will cease, or else climb upward</A><br>
+<A NAME=4.2.29>To what they were before. My pretty cousin,</A><br>
+<A NAME=4.2.30>Blessing upon you!</A><br>
+</blockquote>
+
+<A NAME=speech7><b>LADY MACDUFF</b></a>
+<blockquote>
+<A NAME=4.2.31>Father'd he is, and yet he's fatherless.</A><br>
+</blockquote>
+
+<A NAME=speech8><b>ROSS</b></a>
+<blockquote>
+<A NAME=4.2.32>I am so much a fool, should I stay longer,</A><br>
+<A NAME=4.2.33>It would be my disgrace and your discomfort:</A><br>
+<A NAME=4.2.34>I take my leave at once.</A><br>
+<p><i>Exit</i></p>
+</blockquote>
+
+<A NAME=speech9><b>LADY MACDUFF</b></a>
+<blockquote>
+<A NAME=4.2.35>Sirrah, your father's dead;</A><br>
+<A NAME=4.2.36>And what will you do now? How will you live?</A><br>
+</blockquote>
+
+<A NAME=speech10><b>Son</b></a>
+<blockquote>
+<A NAME=4.2.37>As birds do, mother.</A><br>
+</blockquote>
+
+<A NAME=speech11><b>LADY MACDUFF</b></a>
+<blockquote>
+<A NAME=4.2.38>What, with worms and flies?</A><br>
+</blockquote>
+
+<A NAME=speech12><b>Son</b></a>
+<blockquote>
+<A NAME=4.2.39>With what I get, I mean; and so do they.</A><br>
+</blockquote>
+
+<A NAME=speech13><b>LADY MACDUFF</b></a>
+<blockquote>
+<A NAME=4.2.40>Poor bird! thou'ldst never fear the net nor lime,</A><br>
+<A NAME=4.2.41>The pitfall nor the gin.</A><br>
+</blockquote>
+
+<A NAME=speech14><b>Son</b></a>
+<blockquote>
+<A NAME=4.2.42>Why should I, mother? Poor birds they are not set for.</A><br>
+<A NAME=4.2.43>My father is not dead, for all your saying.</A><br>
+</blockquote>
+
+<A NAME=speech15><b>LADY MACDUFF</b></a>
+<blockquote>
+<A NAME=4.2.44>Yes, he is dead; how wilt thou do for a father?</A><br>
+</blockquote>
+
+<A NAME=speech16><b>Son</b></a>
+<blockquote>
+<A NAME=4.2.45>Nay, how will you do for a husband?</A><br>
+</blockquote>
+
+<A NAME=speech17><b>LADY MACDUFF</b></a>
+<blockquote>
+<A NAME=4.2.46>Why, I can buy me twenty at any market.</A><br>
+</blockquote>
+
+<A NAME=speech18><b>Son</b></a>
+<blockquote>
+<A NAME=4.2.47>Then you'll buy 'em to sell again.</A><br>
+</blockquote>
+
+<A NAME=speech19><b>LADY MACDUFF</b></a>
+<blockquote>
+<A NAME=4.2.48>Thou speak'st with all thy wit: and yet, i' faith,</A><br>
+<A NAME=4.2.49>With wit enough for thee.</A><br>
+</blockquote>
+
+<A NAME=speech20><b>Son</b></a>
+<blockquote>
+<A NAME=4.2.50>Was my father a traitor, mother?</A><br>
+</blockquote>
+
+<A NAME=speech21><b>LADY MACDUFF</b></a>
+<blockquote>
+<A NAME=4.2.51>Ay, that he was.</A><br>
+</blockquote>
+
+<A NAME=speech22><b>Son</b></a>
+<blockquote>
+<A NAME=4.2.52>What is a traitor?</A><br>
+</blockquote>
+
+<A NAME=speech23><b>LADY MACDUFF</b></a>
+<blockquote>
+<A NAME=4.2.53>Why, one that swears and lies.</A><br>
+</blockquote>
+
+<A NAME=speech24><b>Son</b></a>
+<blockquote>
+<A NAME=4.2.54>And be all traitors that do so?</A><br>
+</blockquote>
+
+<A NAME=speech25><b>LADY MACDUFF</b></a>
+<blockquote>
+<A NAME=4.2.55>Every one that does so is a traitor, and must be hanged.</A><br>
+</blockquote>
+
+<A NAME=speech26><b>Son</b></a>
+<blockquote>
+<A NAME=4.2.56>And must they all be hanged that swear and lie?</A><br>
+</blockquote>
+
+<A NAME=speech27><b>LADY MACDUFF</b></a>
+<blockquote>
+<A NAME=4.2.57>Every one.</A><br>
+</blockquote>
+
+<A NAME=speech28><b>Son</b></a>
+<blockquote>
+<A NAME=4.2.58>Who must hang them?</A><br>
+</blockquote>
+
+<A NAME=speech29><b>LADY MACDUFF</b></a>
+<blockquote>
+<A NAME=4.2.59>Why, the honest men.</A><br>
+</blockquote>
+
+<A NAME=speech30><b>Son</b></a>
+<blockquote>
+<A NAME=4.2.60>Then the liars and swearers are fools,</A><br>
+<A NAME=4.2.61>for there are liars and swearers enow to beat</A><br>
+<A NAME=4.2.62>the honest men and hang up them.</A><br>
+</blockquote>
+
+<A NAME=speech31><b>LADY MACDUFF</b></a>
+<blockquote>
+<A NAME=4.2.63>Now, God help thee, poor monkey!</A><br>
+<A NAME=4.2.64>But how wilt thou do for a father?</A><br>
+</blockquote>
+
+<A NAME=speech32><b>Son</b></a>
+<blockquote>
+<A NAME=4.2.65>If he were dead, you'ld weep for</A><br>
+<A NAME=4.2.66>him: if you would not, it were a good sign</A><br>
+<A NAME=4.2.67>that I should quickly have a new father.</A><br>
+</blockquote>
+
+<A NAME=speech33><b>LADY MACDUFF</b></a>
+<blockquote>
+<A NAME=4.2.68>Poor prattler, how thou talk'st!</A><br>
+<p><i>Enter a Messenger</i></p>
+</blockquote>
+
+<A NAME=speech34><b>Messenger</b></a>
+<blockquote>
+<A NAME=4.2.69>Bless you, fair dame! I am not to you known,</A><br>
+<A NAME=4.2.70>Though in your state of honour I am perfect.</A><br>
+<A NAME=4.2.71>I doubt some danger does approach you nearly:</A><br>
+<A NAME=4.2.72>If you will take a homely man's advice,</A><br>
+<A NAME=4.2.73>Be not found here; hence, with your little ones.</A><br>
+<A NAME=4.2.74>To fright you thus, methinks, I am too savage;</A><br>
+<A NAME=4.2.75>To do worse to you were fell cruelty,</A><br>
+<A NAME=4.2.76>Which is too nigh your person. Heaven preserve you!</A><br>
+<A NAME=4.2.77>I dare abide no longer.</A><br>
+<p><i>Exit</i></p>
+</blockquote>
+
+<A NAME=speech35><b>LADY MACDUFF</b></a>
+<blockquote>
+<A NAME=4.2.78>Whither should I fly?</A><br>
+<A NAME=4.2.79>I have done no harm. But I remember now</A><br>
+<A NAME=4.2.80>I am in this earthly world; where to do harm</A><br>
+<A NAME=4.2.81>Is often laudable, to do good sometime</A><br>
+<A NAME=4.2.82>Accounted dangerous folly: why then, alas,</A><br>
+<A NAME=4.2.83>Do I put up that womanly defence,</A><br>
+<A NAME=4.2.84>To say I have done no harm?</A><br>
+<p><i>Enter Murderers</i></p>
+<A NAME=4.2.85>What are these faces?</A><br>
+</blockquote>
+
+<A NAME=speech36><b>First Murderer</b></a>
+<blockquote>
+<A NAME=4.2.86>Where is your husband?</A><br>
+</blockquote>
+
+<A NAME=speech37><b>LADY MACDUFF</b></a>
+<blockquote>
+<A NAME=4.2.87>I hope, in no place so unsanctified</A><br>
+<A NAME=4.2.88>Where such as thou mayst find him.</A><br>
+</blockquote>
+
+<A NAME=speech38><b>First Murderer</b></a>
+<blockquote>
+<A NAME=4.2.89>He's a traitor.</A><br>
+</blockquote>
+
+<A NAME=speech39><b>Son</b></a>
+<blockquote>
+<A NAME=4.2.90>Thou liest, thou shag-hair'd villain!</A><br>
+</blockquote>
+
+<A NAME=speech40><b>First Murderer</b></a>
+<blockquote>
+<A NAME=4.2.91>What, you egg!</A><br>
+<p><i>Stabbing him</i></p>
+<A NAME=4.2.92>Young fry of treachery!</A><br>
+</blockquote>
+
+<A NAME=speech41><b>Son</b></a>
+<blockquote>
+<A NAME=4.2.93>He has kill'd me, mother:</A><br>
+<A NAME=4.2.94>Run away, I pray you!</A><br>
+<p><i>Dies</i></p>
+<p><i>Exit LADY MACDUFF, crying 'Murder!' Exeunt Murderers, following her</i></p>
+</blockquote>
+<h3>SCENE III. England. Before the King's palace.</h3>
+<p><blockquote>
+<i>Enter MALCOLM and MACDUFF</i>
+</blockquote>
+
+<A NAME=speech1><b>MALCOLM</b></a>
+<blockquote>
+<A NAME=4.3.1>Let us seek out some desolate shade, and there</A><br>
+<A NAME=4.3.2>Weep our sad bosoms empty.</A><br>
+</blockquote>
+
+<A NAME=speech2><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=4.3.3>Let us rather</A><br>
+<A NAME=4.3.4>Hold fast the mortal sword, and like good men</A><br>
+<A NAME=4.3.5>Bestride our down-fall'n birthdom: each new morn</A><br>
+<A NAME=4.3.6>New widows howl, new orphans cry, new sorrows</A><br>
+<A NAME=4.3.7>Strike heaven on the face, that it resounds</A><br>
+<A NAME=4.3.8>As if it felt with Scotland and yell'd out</A><br>
+<A NAME=4.3.9>Like syllable of dolour.</A><br>
+</blockquote>
+
+<A NAME=speech3><b>MALCOLM</b></a>
+<blockquote>
+<A NAME=4.3.10>What I believe I'll wail,</A><br>
+<A NAME=4.3.11>What know believe, and what I can redress,</A><br>
+<A NAME=4.3.12>As I shall find the time to friend, I will.</A><br>
+<A NAME=4.3.13>What you have spoke, it may be so perchance.</A><br>
+<A NAME=4.3.14>This tyrant, whose sole name blisters our tongues,</A><br>
+<A NAME=4.3.15>Was once thought honest: you have loved him well.</A><br>
+<A NAME=4.3.16>He hath not touch'd you yet. I am young;</A><br>
+<A NAME=4.3.17>but something</A><br>
+<A NAME=4.3.18>You may deserve of him through me, and wisdom</A><br>
+<A NAME=4.3.19>To offer up a weak poor innocent lamb</A><br>
+<A NAME=4.3.20>To appease an angry god.</A><br>
+</blockquote>
+
+<A NAME=speech4><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=4.3.21>I am not treacherous.</A><br>
+</blockquote>
+
+<A NAME=speech5><b>MALCOLM</b></a>
+<blockquote>
+<A NAME=4.3.22>But Macbeth is.</A><br>
+<A NAME=4.3.23>A good and virtuous nature may recoil</A><br>
+<A NAME=4.3.24>In an imperial charge. But I shall crave</A><br>
+<A NAME=4.3.25>your pardon;</A><br>
+<A NAME=4.3.26>That which you are my thoughts cannot transpose:</A><br>
+<A NAME=4.3.27>Angels are bright still, though the brightest fell;</A><br>
+<A NAME=4.3.28>Though all things foul would wear the brows of grace,</A><br>
+<A NAME=4.3.29>Yet grace must still look so.</A><br>
+</blockquote>
+
+<A NAME=speech6><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=4.3.30>I have lost my hopes.</A><br>
+</blockquote>
+
+<A NAME=speech7><b>MALCOLM</b></a>
+<blockquote>
+<A NAME=4.3.31>Perchance even there where I did find my doubts.</A><br>
+<A NAME=4.3.32>Why in that rawness left you wife and child,</A><br>
+<A NAME=4.3.33>Those precious motives, those strong knots of love,</A><br>
+<A NAME=4.3.34>Without leave-taking? I pray you,</A><br>
+<A NAME=4.3.35>Let not my jealousies be your dishonours,</A><br>
+<A NAME=4.3.36>But mine own safeties. You may be rightly just,</A><br>
+<A NAME=4.3.37>Whatever I shall think.</A><br>
+</blockquote>
+
+<A NAME=speech8><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=4.3.38>Bleed, bleed, poor country!</A><br>
+<A NAME=4.3.39>Great tyranny! lay thou thy basis sure,</A><br>
+<A NAME=4.3.40>For goodness dare not cheque thee: wear thou</A><br>
+<A NAME=4.3.41>thy wrongs;</A><br>
+<A NAME=4.3.42>The title is affeer'd! Fare thee well, lord:</A><br>
+<A NAME=4.3.43>I would not be the villain that thou think'st</A><br>
+<A NAME=4.3.44>For the whole space that's in the tyrant's grasp,</A><br>
+<A NAME=4.3.45>And the rich East to boot.</A><br>
+</blockquote>
+
+<A NAME=speech9><b>MALCOLM</b></a>
+<blockquote>
+<A NAME=4.3.46>Be not offended:</A><br>
+<A NAME=4.3.47>I speak not as in absolute fear of you.</A><br>
+<A NAME=4.3.48>I think our country sinks beneath the yoke;</A><br>
+<A NAME=4.3.49>It weeps, it bleeds; and each new day a gash</A><br>
+<A NAME=4.3.50>Is added to her wounds: I think withal</A><br>
+<A NAME=4.3.51>There would be hands uplifted in my right;</A><br>
+<A NAME=4.3.52>And here from gracious England have I offer</A><br>
+<A NAME=4.3.53>Of goodly thousands: but, for all this,</A><br>
+<A NAME=4.3.54>When I shall tread upon the tyrant's head,</A><br>
+<A NAME=4.3.55>Or wear it on my sword, yet my poor country</A><br>
+<A NAME=4.3.56>Shall have more vices than it had before,</A><br>
+<A NAME=4.3.57>More suffer and more sundry ways than ever,</A><br>
+<A NAME=4.3.58>By him that shall succeed.</A><br>
+</blockquote>
+
+<A NAME=speech10><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=4.3.59>What should he be?</A><br>
+</blockquote>
+
+<A NAME=speech11><b>MALCOLM</b></a>
+<blockquote>
+<A NAME=4.3.60>It is myself I mean: in whom I know</A><br>
+<A NAME=4.3.61>All the particulars of vice so grafted</A><br>
+<A NAME=4.3.62>That, when they shall be open'd, black Macbeth</A><br>
+<A NAME=4.3.63>Will seem as pure as snow, and the poor state</A><br>
+<A NAME=4.3.64>Esteem him as a lamb, being compared</A><br>
+<A NAME=4.3.65>With my confineless harms.</A><br>
+</blockquote>
+
+<A NAME=speech12><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=4.3.66>Not in the legions</A><br>
+<A NAME=4.3.67>Of horrid hell can come a devil more damn'd</A><br>
+<A NAME=4.3.68>In evils to top Macbeth.</A><br>
+</blockquote>
+
+<A NAME=speech13><b>MALCOLM</b></a>
+<blockquote>
+<A NAME=4.3.69>I grant him bloody,</A><br>
+<A NAME=4.3.70>Luxurious, avaricious, false, deceitful,</A><br>
+<A NAME=4.3.71>Sudden, malicious, smacking of every sin</A><br>
+<A NAME=4.3.72>That has a name: but there's no bottom, none,</A><br>
+<A NAME=4.3.73>In my voluptuousness: your wives, your daughters,</A><br>
+<A NAME=4.3.74>Your matrons and your maids, could not fill up</A><br>
+<A NAME=4.3.75>The cistern of my lust, and my desire</A><br>
+<A NAME=4.3.76>All continent impediments would o'erbear</A><br>
+<A NAME=4.3.77>That did oppose my will: better Macbeth</A><br>
+<A NAME=4.3.78>Than such an one to reign.</A><br>
+</blockquote>
+
+<A NAME=speech14><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=4.3.79>Boundless intemperance</A><br>
+<A NAME=4.3.80>In nature is a tyranny; it hath been</A><br>
+<A NAME=4.3.81>The untimely emptying of the happy throne</A><br>
+<A NAME=4.3.82>And fall of many kings. But fear not yet</A><br>
+<A NAME=4.3.83>To take upon you what is yours: you may</A><br>
+<A NAME=4.3.84>Convey your pleasures in a spacious plenty,</A><br>
+<A NAME=4.3.85>And yet seem cold, the time you may so hoodwink.</A><br>
+<A NAME=4.3.86>We have willing dames enough: there cannot be</A><br>
+<A NAME=4.3.87>That vulture in you, to devour so many</A><br>
+<A NAME=4.3.88>As will to greatness dedicate themselves,</A><br>
+<A NAME=4.3.89>Finding it so inclined.</A><br>
+</blockquote>
+
+<A NAME=speech15><b>MALCOLM</b></a>
+<blockquote>
+<A NAME=4.3.90>With this there grows</A><br>
+<A NAME=4.3.91>In my most ill-composed affection such</A><br>
+<A NAME=4.3.92>A stanchless avarice that, were I king,</A><br>
+<A NAME=4.3.93>I should cut off the nobles for their lands,</A><br>
+<A NAME=4.3.94>Desire his jewels and this other's house:</A><br>
+<A NAME=4.3.95>And my more-having would be as a sauce</A><br>
+<A NAME=4.3.96>To make me hunger more; that I should forge</A><br>
+<A NAME=4.3.97>Quarrels unjust against the good and loyal,</A><br>
+<A NAME=4.3.98>Destroying them for wealth.</A><br>
+</blockquote>
+
+<A NAME=speech16><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=4.3.99>This avarice</A><br>
+<A NAME=4.3.100>Sticks deeper, grows with more pernicious root</A><br>
+<A NAME=4.3.101>Than summer-seeming lust, and it hath been</A><br>
+<A NAME=4.3.102>The sword of our slain kings: yet do not fear;</A><br>
+<A NAME=4.3.103>Scotland hath foisons to fill up your will.</A><br>
+<A NAME=4.3.104>Of your mere own: all these are portable,</A><br>
+<A NAME=4.3.105>With other graces weigh'd.</A><br>
+</blockquote>
+
+<A NAME=speech17><b>MALCOLM</b></a>
+<blockquote>
+<A NAME=4.3.106>But I have none: the king-becoming graces,</A><br>
+<A NAME=4.3.107>As justice, verity, temperance, stableness,</A><br>
+<A NAME=4.3.108>Bounty, perseverance, mercy, lowliness,</A><br>
+<A NAME=4.3.109>Devotion, patience, courage, fortitude,</A><br>
+<A NAME=4.3.110>I have no relish of them, but abound</A><br>
+<A NAME=4.3.111>In the division of each several crime,</A><br>
+<A NAME=4.3.112>Acting it many ways. Nay, had I power, I should</A><br>
+<A NAME=4.3.113>Pour the sweet milk of concord into hell,</A><br>
+<A NAME=4.3.114>Uproar the universal peace, confound</A><br>
+<A NAME=4.3.115>All unity on earth.</A><br>
+</blockquote>
+
+<A NAME=speech18><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=4.3.116>O Scotland, Scotland!</A><br>
+</blockquote>
+
+<A NAME=speech19><b>MALCOLM</b></a>
+<blockquote>
+<A NAME=4.3.117>If such a one be fit to govern, speak:</A><br>
+<A NAME=4.3.118>I am as I have spoken.</A><br>
+</blockquote>
+
+<A NAME=speech20><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=4.3.119>Fit to govern!</A><br>
+<A NAME=4.3.120>No, not to live. O nation miserable,</A><br>
+<A NAME=4.3.121>With an untitled tyrant bloody-scepter'd,</A><br>
+<A NAME=4.3.122>When shalt thou see thy wholesome days again,</A><br>
+<A NAME=4.3.123>Since that the truest issue of thy throne</A><br>
+<A NAME=4.3.124>By his own interdiction stands accursed,</A><br>
+<A NAME=4.3.125>And does blaspheme his breed? Thy royal father</A><br>
+<A NAME=4.3.126>Was a most sainted king: the queen that bore thee,</A><br>
+<A NAME=4.3.127>Oftener upon her knees than on her feet,</A><br>
+<A NAME=4.3.128>Died every day she lived. Fare thee well!</A><br>
+<A NAME=4.3.129>These evils thou repeat'st upon thyself</A><br>
+<A NAME=4.3.130>Have banish'd me from Scotland. O my breast,</A><br>
+<A NAME=4.3.131>Thy hope ends here!</A><br>
+</blockquote>
+
+<A NAME=speech21><b>MALCOLM</b></a>
+<blockquote>
+<A NAME=4.3.132>Macduff, this noble passion,</A><br>
+<A NAME=4.3.133>Child of integrity, hath from my soul</A><br>
+<A NAME=4.3.134>Wiped the black scruples, reconciled my thoughts</A><br>
+<A NAME=4.3.135>To thy good truth and honour. Devilish Macbeth</A><br>
+<A NAME=4.3.136>By many of these trains hath sought to win me</A><br>
+<A NAME=4.3.137>Into his power, and modest wisdom plucks me</A><br>
+<A NAME=4.3.138>From over-credulous haste: but God above</A><br>
+<A NAME=4.3.139>Deal between thee and me! for even now</A><br>
+<A NAME=4.3.140>I put myself to thy direction, and</A><br>
+<A NAME=4.3.141>Unspeak mine own detraction, here abjure</A><br>
+<A NAME=4.3.142>The taints and blames I laid upon myself,</A><br>
+<A NAME=4.3.143>For strangers to my nature. I am yet</A><br>
+<A NAME=4.3.144>Unknown to woman, never was forsworn,</A><br>
+<A NAME=4.3.145>Scarcely have coveted what was mine own,</A><br>
+<A NAME=4.3.146>At no time broke my faith, would not betray</A><br>
+<A NAME=4.3.147>The devil to his fellow and delight</A><br>
+<A NAME=4.3.148>No less in truth than life: my first false speaking</A><br>
+<A NAME=4.3.149>Was this upon myself: what I am truly,</A><br>
+<A NAME=4.3.150>Is thine and my poor country's to command:</A><br>
+<A NAME=4.3.151>Whither indeed, before thy here-approach,</A><br>
+<A NAME=4.3.152>Old Siward, with ten thousand warlike men,</A><br>
+<A NAME=4.3.153>Already at a point, was setting forth.</A><br>
+<A NAME=4.3.154>Now we'll together; and the chance of goodness</A><br>
+<A NAME=4.3.155>Be like our warranted quarrel! Why are you silent?</A><br>
+</blockquote>
+
+<A NAME=speech22><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=4.3.156>Such welcome and unwelcome things at once</A><br>
+<A NAME=4.3.157>'Tis hard to reconcile.</A><br>
+<p><i>Enter a Doctor</i></p>
+</blockquote>
+
+<A NAME=speech23><b>MALCOLM</b></a>
+<blockquote>
+<A NAME=4.3.158>Well; more anon.--Comes the king forth, I pray you?</A><br>
+</blockquote>
+
+<A NAME=speech24><b>Doctor</b></a>
+<blockquote>
+<A NAME=4.3.159>Ay, sir; there are a crew of wretched souls</A><br>
+<A NAME=4.3.160>That stay his cure: their malady convinces</A><br>
+<A NAME=4.3.161>The great assay of art; but at his touch--</A><br>
+<A NAME=4.3.162>Such sanctity hath heaven given his hand--</A><br>
+<A NAME=4.3.163>They presently amend.</A><br>
+</blockquote>
+
+<A NAME=speech25><b>MALCOLM</b></a>
+<blockquote>
+<A NAME=4.3.164>I thank you, doctor.</A><br>
+<p><i>Exit Doctor</i></p>
+</blockquote>
+
+<A NAME=speech26><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=4.3.165>What's the disease he means?</A><br>
+</blockquote>
+
+<A NAME=speech27><b>MALCOLM</b></a>
+<blockquote>
+<A NAME=4.3.166>'Tis call'd the evil:</A><br>
+<A NAME=4.3.167>A most miraculous work in this good king;</A><br>
+<A NAME=4.3.168>Which often, since my here-remain in England,</A><br>
+<A NAME=4.3.169>I have seen him do. How he solicits heaven,</A><br>
+<A NAME=4.3.170>Himself best knows: but strangely-visited people,</A><br>
+<A NAME=4.3.171>All swoln and ulcerous, pitiful to the eye,</A><br>
+<A NAME=4.3.172>The mere despair of surgery, he cures,</A><br>
+<A NAME=4.3.173>Hanging a golden stamp about their necks,</A><br>
+<A NAME=4.3.174>Put on with holy prayers: and 'tis spoken,</A><br>
+<A NAME=4.3.175>To the succeeding royalty he leaves</A><br>
+<A NAME=4.3.176>The healing benediction. With this strange virtue,</A><br>
+<A NAME=4.3.177>He hath a heavenly gift of prophecy,</A><br>
+<A NAME=4.3.178>And sundry blessings hang about his throne,</A><br>
+<A NAME=4.3.179>That speak him full of grace.</A><br>
+<p><i>Enter ROSS</i></p>
+</blockquote>
+
+<A NAME=speech28><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=4.3.180>See, who comes here?</A><br>
+</blockquote>
+
+<A NAME=speech29><b>MALCOLM</b></a>
+<blockquote>
+<A NAME=4.3.181>My countryman; but yet I know him not.</A><br>
+</blockquote>
+
+<A NAME=speech30><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=4.3.182>My ever-gentle cousin, welcome hither.</A><br>
+</blockquote>
+
+<A NAME=speech31><b>MALCOLM</b></a>
+<blockquote>
+<A NAME=4.3.183>I know him now. Good God, betimes remove</A><br>
+<A NAME=4.3.184>The means that makes us strangers!</A><br>
+</blockquote>
+
+<A NAME=speech32><b>ROSS</b></a>
+<blockquote>
+<A NAME=4.3.185>Sir, amen.</A><br>
+</blockquote>
+
+<A NAME=speech33><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=4.3.186>Stands Scotland where it did?</A><br>
+</blockquote>
+
+<A NAME=speech34><b>ROSS</b></a>
+<blockquote>
+<A NAME=4.3.187>Alas, poor country!</A><br>
+<A NAME=4.3.188>Almost afraid to know itself. It cannot</A><br>
+<A NAME=4.3.189>Be call'd our mother, but our grave; where nothing,</A><br>
+<A NAME=4.3.190>But who knows nothing, is once seen to smile;</A><br>
+<A NAME=4.3.191>Where sighs and groans and shrieks that rend the air</A><br>
+<A NAME=4.3.192>Are made, not mark'd; where violent sorrow seems</A><br>
+<A NAME=4.3.193>A modern ecstasy; the dead man's knell</A><br>
+<A NAME=4.3.194>Is there scarce ask'd for who; and good men's lives</A><br>
+<A NAME=4.3.195>Expire before the flowers in their caps,</A><br>
+<A NAME=4.3.196>Dying or ere they sicken.</A><br>
+</blockquote>
+
+<A NAME=speech35><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=4.3.197>O, relation</A><br>
+<A NAME=4.3.198>Too nice, and yet too true!</A><br>
+</blockquote>
+
+<A NAME=speech36><b>MALCOLM</b></a>
+<blockquote>
+<A NAME=4.3.199>What's the newest grief?</A><br>
+</blockquote>
+
+<A NAME=speech37><b>ROSS</b></a>
+<blockquote>
+<A NAME=4.3.200>That of an hour's age doth hiss the speaker:</A><br>
+<A NAME=4.3.201>Each minute teems a new one.</A><br>
+</blockquote>
+
+<A NAME=speech38><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=4.3.202>How does my wife?</A><br>
+</blockquote>
+
+<A NAME=speech39><b>ROSS</b></a>
+<blockquote>
+<A NAME=4.3.203>Why, well.</A><br>
+</blockquote>
+
+<A NAME=speech40><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=4.3.204> And all my children?</A><br>
+</blockquote>
+
+<A NAME=speech41><b>ROSS</b></a>
+<blockquote>
+<A NAME=4.3.205>Well too.</A><br>
+</blockquote>
+
+<A NAME=speech42><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=4.3.206>The tyrant has not batter'd at their peace?</A><br>
+</blockquote>
+
+<A NAME=speech43><b>ROSS</b></a>
+<blockquote>
+<A NAME=4.3.207>No; they were well at peace when I did leave 'em.</A><br>
+</blockquote>
+
+<A NAME=speech44><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=4.3.208>But not a niggard of your speech: how goes't?</A><br>
+</blockquote>
+
+<A NAME=speech45><b>ROSS</b></a>
+<blockquote>
+<A NAME=4.3.209>When I came hither to transport the tidings,</A><br>
+<A NAME=4.3.210>Which I have heavily borne, there ran a rumour</A><br>
+<A NAME=4.3.211>Of many worthy fellows that were out;</A><br>
+<A NAME=4.3.212>Which was to my belief witness'd the rather,</A><br>
+<A NAME=4.3.213>For that I saw the tyrant's power a-foot:</A><br>
+<A NAME=4.3.214>Now is the time of help; your eye in Scotland</A><br>
+<A NAME=4.3.215>Would create soldiers, make our women fight,</A><br>
+<A NAME=4.3.216>To doff their dire distresses.</A><br>
+</blockquote>
+
+<A NAME=speech46><b>MALCOLM</b></a>
+<blockquote>
+<A NAME=4.3.217>Be't their comfort</A><br>
+<A NAME=4.3.218>We are coming thither: gracious England hath</A><br>
+<A NAME=4.3.219>Lent us good Siward and ten thousand men;</A><br>
+<A NAME=4.3.220>An older and a better soldier none</A><br>
+<A NAME=4.3.221>That Christendom gives out.</A><br>
+</blockquote>
+
+<A NAME=speech47><b>ROSS</b></a>
+<blockquote>
+<A NAME=4.3.222>Would I could answer</A><br>
+<A NAME=4.3.223>This comfort with the like! But I have words</A><br>
+<A NAME=4.3.224>That would be howl'd out in the desert air,</A><br>
+<A NAME=4.3.225>Where hearing should not latch them.</A><br>
+</blockquote>
+
+<A NAME=speech48><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=4.3.226>What concern they?</A><br>
+<A NAME=4.3.227>The general cause? or is it a fee-grief</A><br>
+<A NAME=4.3.228>Due to some single breast?</A><br>
+</blockquote>
+
+<A NAME=speech49><b>ROSS</b></a>
+<blockquote>
+<A NAME=4.3.229>No mind that's honest</A><br>
+<A NAME=4.3.230>But in it shares some woe; though the main part</A><br>
+<A NAME=4.3.231>Pertains to you alone.</A><br>
+</blockquote>
+
+<A NAME=speech50><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=4.3.232>If it be mine,</A><br>
+<A NAME=4.3.233>Keep it not from me, quickly let me have it.</A><br>
+</blockquote>
+
+<A NAME=speech51><b>ROSS</b></a>
+<blockquote>
+<A NAME=4.3.234>Let not your ears despise my tongue for ever,</A><br>
+<A NAME=4.3.235>Which shall possess them with the heaviest sound</A><br>
+<A NAME=4.3.236>That ever yet they heard.</A><br>
+</blockquote>
+
+<A NAME=speech52><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=4.3.237>Hum! I guess at it.</A><br>
+</blockquote>
+
+<A NAME=speech53><b>ROSS</b></a>
+<blockquote>
+<A NAME=4.3.238>Your castle is surprised; your wife and babes</A><br>
+<A NAME=4.3.239>Savagely slaughter'd: to relate the manner,</A><br>
+<A NAME=4.3.240>Were, on the quarry of these murder'd deer,</A><br>
+<A NAME=4.3.241>To add the death of you.</A><br>
+</blockquote>
+
+<A NAME=speech54><b>MALCOLM</b></a>
+<blockquote>
+<A NAME=4.3.242>Merciful heaven!</A><br>
+<A NAME=4.3.243>What, man! ne'er pull your hat upon your brows;</A><br>
+<A NAME=4.3.244>Give sorrow words: the grief that does not speak</A><br>
+<A NAME=4.3.245>Whispers the o'er-fraught heart and bids it break.</A><br>
+</blockquote>
+
+<A NAME=speech55><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=4.3.246>My children too?</A><br>
+</blockquote>
+
+<A NAME=speech56><b>ROSS</b></a>
+<blockquote>
+<A NAME=4.3.247> Wife, children, servants, all</A><br>
+<A NAME=4.3.248>That could be found.</A><br>
+</blockquote>
+
+<A NAME=speech57><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=4.3.249>And I must be from thence!</A><br>
+<A NAME=4.3.250>My wife kill'd too?</A><br>
+</blockquote>
+
+<A NAME=speech58><b>ROSS</b></a>
+<blockquote>
+<A NAME=4.3.251>I have said.</A><br>
+</blockquote>
+
+<A NAME=speech59><b>MALCOLM</b></a>
+<blockquote>
+<A NAME=4.3.252>Be comforted:</A><br>
+<A NAME=4.3.253>Let's make us medicines of our great revenge,</A><br>
+<A NAME=4.3.254>To cure this deadly grief.</A><br>
+</blockquote>
+
+<A NAME=speech60><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=4.3.255>He has no children. All my pretty ones?</A><br>
+<A NAME=4.3.256>Did you say all? O hell-kite! All?</A><br>
+<A NAME=4.3.257>What, all my pretty chickens and their dam</A><br>
+<A NAME=4.3.258>At one fell swoop?</A><br>
+</blockquote>
+
+<A NAME=speech61><b>MALCOLM</b></a>
+<blockquote>
+<A NAME=4.3.259>Dispute it like a man.</A><br>
+</blockquote>
+
+<A NAME=speech62><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=4.3.260>I shall do so;</A><br>
+<A NAME=4.3.261>But I must also feel it as a man:</A><br>
+<A NAME=4.3.262>I cannot but remember such things were,</A><br>
+<A NAME=4.3.263>That were most precious to me. Did heaven look on,</A><br>
+<A NAME=4.3.264>And would not take their part? Sinful Macduff,</A><br>
+<A NAME=4.3.265>They were all struck for thee! naught that I am,</A><br>
+<A NAME=4.3.266>Not for their own demerits, but for mine,</A><br>
+<A NAME=4.3.267>Fell slaughter on their souls. Heaven rest them now!</A><br>
+</blockquote>
+
+<A NAME=speech63><b>MALCOLM</b></a>
+<blockquote>
+<A NAME=4.3.268>Be this the whetstone of your sword: let grief</A><br>
+<A NAME=4.3.269>Convert to anger; blunt not the heart, enrage it.</A><br>
+</blockquote>
+
+<A NAME=speech64><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=4.3.270>O, I could play the woman with mine eyes</A><br>
+<A NAME=4.3.271>And braggart with my tongue! But, gentle heavens,</A><br>
+<A NAME=4.3.272>Cut short all intermission; front to front</A><br>
+<A NAME=4.3.273>Bring thou this fiend of Scotland and myself;</A><br>
+<A NAME=4.3.274>Within my sword's length set him; if he 'scape,</A><br>
+<A NAME=4.3.275>Heaven forgive him too!</A><br>
+</blockquote>
+
+<A NAME=speech65><b>MALCOLM</b></a>
+<blockquote>
+<A NAME=4.3.276>This tune goes manly.</A><br>
+<A NAME=4.3.277>Come, go we to the king; our power is ready;</A><br>
+<A NAME=4.3.278>Our lack is nothing but our leave; Macbeth</A><br>
+<A NAME=4.3.279>Is ripe for shaking, and the powers above</A><br>
+<A NAME=4.3.280>Put on their instruments. Receive what cheer you may:</A><br>
+<A NAME=4.3.281>The night is long that never finds the day.</A><br>
+<p><i>Exeunt</i></p>
+</blockquote><p>
+<H3>ACT V</h3>
+<h3>SCENE I. Dunsinane. Ante-room in the castle.</h3>
+<p><blockquote>
+<i>Enter a Doctor of Physic and a Waiting-Gentlewoman</i>
+</blockquote>
+
+<A NAME=speech1><b>Doctor</b></a>
+<blockquote>
+<A NAME=5.1.1>I have two nights watched with you, but can perceive</A><br>
+<A NAME=5.1.2>no truth in your report. When was it she last walked?</A><br>
+</blockquote>
+
+<A NAME=speech2><b>Gentlewoman</b></a>
+<blockquote>
+<A NAME=5.1.3>Since his majesty went into the field, I have seen</A><br>
+<A NAME=5.1.4>her rise from her bed, throw her night-gown upon</A><br>
+<A NAME=5.1.5>her, unlock her closet, take forth paper, fold it,</A><br>
+<A NAME=5.1.6>write upon't, read it, afterwards seal it, and again</A><br>
+<A NAME=5.1.7>return to bed; yet all this while in a most fast sleep.</A><br>
+</blockquote>
+
+<A NAME=speech3><b>Doctor</b></a>
+<blockquote>
+<A NAME=5.1.8>A great perturbation in nature, to receive at once</A><br>
+<A NAME=5.1.9>the benefit of sleep, and do the effects of</A><br>
+<A NAME=5.1.10>watching! In this slumbery agitation, besides her</A><br>
+<A NAME=5.1.11>walking and other actual performances, what, at any</A><br>
+<A NAME=5.1.12>time, have you heard her say?</A><br>
+</blockquote>
+
+<A NAME=speech4><b>Gentlewoman</b></a>
+<blockquote>
+<A NAME=5.1.13>That, sir, which I will not report after her.</A><br>
+</blockquote>
+
+<A NAME=speech5><b>Doctor</b></a>
+<blockquote>
+<A NAME=5.1.14>You may to me: and 'tis most meet you should.</A><br>
+</blockquote>
+
+<A NAME=speech6><b>Gentlewoman</b></a>
+<blockquote>
+<A NAME=5.1.15>Neither to you nor any one; having no witness to</A><br>
+<A NAME=5.1.16>confirm my speech.</A><br>
+<p><i>Enter LADY MACBETH, with a taper</i></p>
+<A NAME=5.1.17>Lo you, here she comes! This is her very guise;</A><br>
+<A NAME=5.1.18>and, upon my life, fast asleep. Observe her; stand close.</A><br>
+</blockquote>
+
+<A NAME=speech7><b>Doctor</b></a>
+<blockquote>
+<A NAME=5.1.19>How came she by that light?</A><br>
+</blockquote>
+
+<A NAME=speech8><b>Gentlewoman</b></a>
+<blockquote>
+<A NAME=5.1.20>Why, it stood by her: she has light by her</A><br>
+<A NAME=5.1.21>continually; 'tis her command.</A><br>
+</blockquote>
+
+<A NAME=speech9><b>Doctor</b></a>
+<blockquote>
+<A NAME=5.1.22>You see, her eyes are open.</A><br>
+</blockquote>
+
+<A NAME=speech10><b>Gentlewoman</b></a>
+<blockquote>
+<A NAME=5.1.23>Ay, but their sense is shut.</A><br>
+</blockquote>
+
+<A NAME=speech11><b>Doctor</b></a>
+<blockquote>
+<A NAME=5.1.24>What is it she does now? Look, how she rubs her hands.</A><br>
+</blockquote>
+
+<A NAME=speech12><b>Gentlewoman</b></a>
+<blockquote>
+<A NAME=5.1.25>It is an accustomed action with her, to seem thus</A><br>
+<A NAME=5.1.26>washing her hands: I have known her continue in</A><br>
+<A NAME=5.1.27>this a quarter of an hour.</A><br>
+</blockquote>
+
+<A NAME=speech13><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=5.1.28>Yet here's a spot.</A><br>
+</blockquote>
+
+<A NAME=speech14><b>Doctor</b></a>
+<blockquote>
+<A NAME=5.1.29>Hark! she speaks: I will set down what comes from</A><br>
+<A NAME=5.1.30>her, to satisfy my remembrance the more strongly.</A><br>
+</blockquote>
+
+<A NAME=speech15><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=5.1.31>Out, damned spot! out, I say!--One: two: why,</A><br>
+<A NAME=5.1.32>then, 'tis time to do't.--Hell is murky!--Fie, my</A><br>
+<A NAME=5.1.33>lord, fie! a soldier, and afeard? What need we</A><br>
+<A NAME=5.1.34>fear who knows it, when none can call our power to</A><br>
+<A NAME=5.1.35>account?--Yet who would have thought the old man</A><br>
+<A NAME=5.1.36>to have had so much blood in him.</A><br>
+</blockquote>
+
+<A NAME=speech16><b>Doctor</b></a>
+<blockquote>
+<A NAME=5.1.37>Do you mark that?</A><br>
+</blockquote>
+
+<A NAME=speech17><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=5.1.38>The thane of Fife had a wife: where is she now?--</A><br>
+<A NAME=5.1.39>What, will these hands ne'er be clean?--No more o'</A><br>
+<A NAME=5.1.40>that, my lord, no more o' that: you mar all with</A><br>
+<A NAME=5.1.41>this starting.</A><br>
+</blockquote>
+
+<A NAME=speech18><b>Doctor</b></a>
+<blockquote>
+<A NAME=5.1.42>Go to, go to; you have known what you should not.</A><br>
+</blockquote>
+
+<A NAME=speech19><b>Gentlewoman</b></a>
+<blockquote>
+<A NAME=5.1.43>She has spoke what she should not, I am sure of</A><br>
+<A NAME=5.1.44>that: heaven knows what she has known.</A><br>
+</blockquote>
+
+<A NAME=speech20><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=5.1.45>Here's the smell of the blood still: all the</A><br>
+<A NAME=5.1.46>perfumes of Arabia will not sweeten this little</A><br>
+<A NAME=5.1.47>hand. Oh, oh, oh!</A><br>
+</blockquote>
+
+<A NAME=speech21><b>Doctor</b></a>
+<blockquote>
+<A NAME=5.1.48>What a sigh is there! The heart is sorely charged.</A><br>
+</blockquote>
+
+<A NAME=speech22><b>Gentlewoman</b></a>
+<blockquote>
+<A NAME=5.1.49>I would not have such a heart in my bosom for the</A><br>
+<A NAME=5.1.50>dignity of the whole body.</A><br>
+</blockquote>
+
+<A NAME=speech23><b>Doctor</b></a>
+<blockquote>
+<A NAME=5.1.51>Well, well, well,--</A><br>
+</blockquote>
+
+<A NAME=speech24><b>Gentlewoman</b></a>
+<blockquote>
+<A NAME=5.1.52>Pray God it be, sir.</A><br>
+</blockquote>
+
+<A NAME=speech25><b>Doctor</b></a>
+<blockquote>
+<A NAME=5.1.53>This disease is beyond my practise: yet I have known</A><br>
+<A NAME=5.1.54>those which have walked in their sleep who have died</A><br>
+<A NAME=5.1.55>holily in their beds.</A><br>
+</blockquote>
+
+<A NAME=speech26><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=5.1.56>Wash your hands, put on your nightgown; look not so</A><br>
+<A NAME=5.1.57>pale.--I tell you yet again, Banquo's buried; he</A><br>
+<A NAME=5.1.58>cannot come out on's grave.</A><br>
+</blockquote>
+
+<A NAME=speech27><b>Doctor</b></a>
+<blockquote>
+<A NAME=5.1.59>Even so?</A><br>
+</blockquote>
+
+<A NAME=speech28><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=5.1.60>To bed, to bed! there's knocking at the gate:</A><br>
+<A NAME=5.1.61>come, come, come, come, give me your hand. What's</A><br>
+<A NAME=5.1.62>done cannot be undone.--To bed, to bed, to bed!</A><br>
+<p><i>Exit</i></p>
+</blockquote>
+
+<A NAME=speech29><b>Doctor</b></a>
+<blockquote>
+<A NAME=5.1.63>Will she go now to bed?</A><br>
+</blockquote>
+
+<A NAME=speech30><b>Gentlewoman</b></a>
+<blockquote>
+<A NAME=5.1.64>Directly.</A><br>
+</blockquote>
+
+<A NAME=speech31><b>Doctor</b></a>
+<blockquote>
+<A NAME=5.1.65>Foul whisperings are abroad: unnatural deeds</A><br>
+<A NAME=5.1.66>Do breed unnatural troubles: infected minds</A><br>
+<A NAME=5.1.67>To their deaf pillows will discharge their secrets:</A><br>
+<A NAME=5.1.68>More needs she the divine than the physician.</A><br>
+<A NAME=5.1.69>God, God forgive us all! Look after her;</A><br>
+<A NAME=5.1.70>Remove from her the means of all annoyance,</A><br>
+<A NAME=5.1.71>And still keep eyes upon her. So, good night:</A><br>
+<A NAME=5.1.72>My mind she has mated, and amazed my sight.</A><br>
+<A NAME=5.1.73>I think, but dare not speak.</A><br>
+</blockquote>
+
+<A NAME=speech32><b>Gentlewoman</b></a>
+<blockquote>
+<A NAME=5.1.74>Good night, good doctor.</A><br>
+<p><i>Exeunt</i></p>
+</blockquote>
+<h3>SCENE II. The country near Dunsinane.</h3>
+<p><blockquote>
+<i>Drum and colours. Enter MENTEITH, CAITHNESS, ANGUS, LENNOX, and Soldiers</i>
+</blockquote>
+
+<A NAME=speech1><b>MENTEITH</b></a>
+<blockquote>
+<A NAME=5.2.1>The English power is near, led on by Malcolm,</A><br>
+<A NAME=5.2.2>His uncle Siward and the good Macduff:</A><br>
+<A NAME=5.2.3>Revenges burn in them; for their dear causes</A><br>
+<A NAME=5.2.4>Would to the bleeding and the grim alarm</A><br>
+<A NAME=5.2.5>Excite the mortified man.</A><br>
+</blockquote>
+
+<A NAME=speech2><b>ANGUS</b></a>
+<blockquote>
+<A NAME=5.2.6>Near Birnam wood</A><br>
+<A NAME=5.2.7>Shall we well meet them; that way are they coming.</A><br>
+</blockquote>
+
+<A NAME=speech3><b>CAITHNESS</b></a>
+<blockquote>
+<A NAME=5.2.8>Who knows if Donalbain be with his brother?</A><br>
+</blockquote>
+
+<A NAME=speech4><b>LENNOX</b></a>
+<blockquote>
+<A NAME=5.2.9>For certain, sir, he is not: I have a file</A><br>
+<A NAME=5.2.10>Of all the gentry: there is Siward's son,</A><br>
+<A NAME=5.2.11>And many unrough youths that even now</A><br>
+<A NAME=5.2.12>Protest their first of manhood.</A><br>
+</blockquote>
+
+<A NAME=speech5><b>MENTEITH</b></a>
+<blockquote>
+<A NAME=5.2.13>What does the tyrant?</A><br>
+</blockquote>
+
+<A NAME=speech6><b>CAITHNESS</b></a>
+<blockquote>
+<A NAME=5.2.14>Great Dunsinane he strongly fortifies:</A><br>
+<A NAME=5.2.15>Some say he's mad; others that lesser hate him</A><br>
+<A NAME=5.2.16>Do call it valiant fury: but, for certain,</A><br>
+<A NAME=5.2.17>He cannot buckle his distemper'd cause</A><br>
+<A NAME=5.2.18>Within the belt of rule.</A><br>
+</blockquote>
+
+<A NAME=speech7><b>ANGUS</b></a>
+<blockquote>
+<A NAME=5.2.19>Now does he feel</A><br>
+<A NAME=5.2.20>His secret murders sticking on his hands;</A><br>
+<A NAME=5.2.21>Now minutely revolts upbraid his faith-breach;</A><br>
+<A NAME=5.2.22>Those he commands move only in command,</A><br>
+<A NAME=5.2.23>Nothing in love: now does he feel his title</A><br>
+<A NAME=5.2.24>Hang loose about him, like a giant's robe</A><br>
+<A NAME=5.2.25>Upon a dwarfish thief.</A><br>
+</blockquote>
+
+<A NAME=speech8><b>MENTEITH</b></a>
+<blockquote>
+<A NAME=5.2.26>Who then shall blame</A><br>
+<A NAME=5.2.27>His pester'd senses to recoil and start,</A><br>
+<A NAME=5.2.28>When all that is within him does condemn</A><br>
+<A NAME=5.2.29>Itself for being there?</A><br>
+</blockquote>
+
+<A NAME=speech9><b>CAITHNESS</b></a>
+<blockquote>
+<A NAME=5.2.30>Well, march we on,</A><br>
+<A NAME=5.2.31>To give obedience where 'tis truly owed:</A><br>
+<A NAME=5.2.32>Meet we the medicine of the sickly weal,</A><br>
+<A NAME=5.2.33>And with him pour we in our country's purge</A><br>
+<A NAME=5.2.34>Each drop of us.</A><br>
+</blockquote>
+
+<A NAME=speech10><b>LENNOX</b></a>
+<blockquote>
+<A NAME=5.2.35> Or so much as it needs,</A><br>
+<A NAME=5.2.36>To dew the sovereign flower and drown the weeds.</A><br>
+<A NAME=5.2.37>Make we our march towards Birnam.</A><br>
+<p><i>Exeunt, marching</i></p>
+</blockquote>
+<h3>SCENE III. Dunsinane. A room in the castle.</h3>
+<p><blockquote>
+<i>Enter MACBETH, Doctor, and Attendants</i>
+</blockquote>
+
+<A NAME=speech1><b>MACBETH</b></a>
+<blockquote>
+<A NAME=5.3.1>Bring me no more reports; let them fly all:</A><br>
+<A NAME=5.3.2>Till Birnam wood remove to Dunsinane,</A><br>
+<A NAME=5.3.3>I cannot taint with fear. What's the boy Malcolm?</A><br>
+<A NAME=5.3.4>Was he not born of woman? The spirits that know</A><br>
+<A NAME=5.3.5>All mortal consequences have pronounced me thus:</A><br>
+<A NAME=5.3.6>'Fear not, Macbeth; no man that's born of woman</A><br>
+<A NAME=5.3.7>Shall e'er have power upon thee.' Then fly,</A><br>
+<A NAME=5.3.8>false thanes,</A><br>
+<A NAME=5.3.9>And mingle with the English epicures:</A><br>
+<A NAME=5.3.10>The mind I sway by and the heart I bear</A><br>
+<A NAME=5.3.11>Shall never sag with doubt nor shake with fear.</A><br>
+<p><i>Enter a Servant</i></p>
+<A NAME=5.3.12>The devil damn thee black, thou cream-faced loon!</A><br>
+<A NAME=5.3.13>Where got'st thou that goose look?</A><br>
+</blockquote>
+
+<A NAME=speech2><b>Servant</b></a>
+<blockquote>
+<A NAME=5.3.14>There is ten thousand--</A><br>
+</blockquote>
+
+<A NAME=speech3><b>MACBETH</b></a>
+<blockquote>
+<A NAME=5.3.15>Geese, villain!</A><br>
+</blockquote>
+
+<A NAME=speech4><b>Servant</b></a>
+<blockquote>
+<A NAME=5.3.16>Soldiers, sir.</A><br>
+</blockquote>
+
+<A NAME=speech5><b>MACBETH</b></a>
+<blockquote>
+<A NAME=5.3.17>Go prick thy face, and over-red thy fear,</A><br>
+<A NAME=5.3.18>Thou lily-liver'd boy. What soldiers, patch?</A><br>
+<A NAME=5.3.19>Death of thy soul! those linen cheeks of thine</A><br>
+<A NAME=5.3.20>Are counsellors to fear. What soldiers, whey-face?</A><br>
+</blockquote>
+
+<A NAME=speech6><b>Servant</b></a>
+<blockquote>
+<A NAME=5.3.21>The English force, so please you.</A><br>
+</blockquote>
+
+<A NAME=speech7><b>MACBETH</b></a>
+<blockquote>
+<A NAME=5.3.22>Take thy face hence.</A><br>
+<p><i>Exit Servant</i></p>
+<A NAME=5.3.23>Seyton!--I am sick at heart,</A><br>
+<A NAME=5.3.24>When I behold--Seyton, I say!--This push</A><br>
+<A NAME=5.3.25>Will cheer me ever, or disseat me now.</A><br>
+<A NAME=5.3.26>I have lived long enough: my way of life</A><br>
+<A NAME=5.3.27>Is fall'n into the sear, the yellow leaf;</A><br>
+<A NAME=5.3.28>And that which should accompany old age,</A><br>
+<A NAME=5.3.29>As honour, love, obedience, troops of friends,</A><br>
+<A NAME=5.3.30>I must not look to have; but, in their stead,</A><br>
+<A NAME=5.3.31>Curses, not loud but deep, mouth-honour, breath,</A><br>
+<A NAME=5.3.32>Which the poor heart would fain deny, and dare not. Seyton!</A><br>
+<p><i>Enter SEYTON</i></p>
+</blockquote>
+
+<A NAME=speech8><b>SEYTON</b></a>
+<blockquote>
+<A NAME=5.3.33>What is your gracious pleasure?</A><br>
+</blockquote>
+
+<A NAME=speech9><b>MACBETH</b></a>
+<blockquote>
+<A NAME=5.3.34>What news more?</A><br>
+</blockquote>
+
+<A NAME=speech10><b>SEYTON</b></a>
+<blockquote>
+<A NAME=5.3.35>All is confirm'd, my lord, which was reported.</A><br>
+</blockquote>
+
+<A NAME=speech11><b>MACBETH</b></a>
+<blockquote>
+<A NAME=5.3.36>I'll fight till from my bones my flesh be hack'd.</A><br>
+<A NAME=5.3.37>Give me my armour.</A><br>
+</blockquote>
+
+<A NAME=speech12><b>SEYTON</b></a>
+<blockquote>
+<A NAME=5.3.38>'Tis not needed yet.</A><br>
+</blockquote>
+
+<A NAME=speech13><b>MACBETH</b></a>
+<blockquote>
+<A NAME=5.3.39>I'll put it on.</A><br>
+<A NAME=5.3.40>Send out more horses; skirr the country round;</A><br>
+<A NAME=5.3.41>Hang those that talk of fear. Give me mine armour.</A><br>
+<A NAME=5.3.42>How does your patient, doctor?</A><br>
+</blockquote>
+
+<A NAME=speech14><b>Doctor</b></a>
+<blockquote>
+<A NAME=5.3.43>Not so sick, my lord,</A><br>
+<A NAME=5.3.44>As she is troubled with thick coming fancies,</A><br>
+<A NAME=5.3.45>That keep her from her rest.</A><br>
+</blockquote>
+
+<A NAME=speech15><b>MACBETH</b></a>
+<blockquote>
+<A NAME=5.3.46>Cure her of that.</A><br>
+<A NAME=5.3.47>Canst thou not minister to a mind diseased,</A><br>
+<A NAME=5.3.48>Pluck from the memory a rooted sorrow,</A><br>
+<A NAME=5.3.49>Raze out the written troubles of the brain</A><br>
+<A NAME=5.3.50>And with some sweet oblivious antidote</A><br>
+<A NAME=5.3.51>Cleanse the stuff'd bosom of that perilous stuff</A><br>
+<A NAME=5.3.52>Which weighs upon the heart?</A><br>
+</blockquote>
+
+<A NAME=speech16><b>Doctor</b></a>
+<blockquote>
+<A NAME=5.3.53>Therein the patient</A><br>
+<A NAME=5.3.54>Must minister to himself.</A><br>
+</blockquote>
+
+<A NAME=speech17><b>MACBETH</b></a>
+<blockquote>
+<A NAME=5.3.55>Throw physic to the dogs; I'll none of it.</A><br>
+<A NAME=5.3.56>Come, put mine armour on; give me my staff.</A><br>
+<A NAME=5.3.57>Seyton, send out. Doctor, the thanes fly from me.</A><br>
+<A NAME=5.3.58>Come, sir, dispatch. If thou couldst, doctor, cast</A><br>
+<A NAME=5.3.59>The water of my land, find her disease,</A><br>
+<A NAME=5.3.60>And purge it to a sound and pristine health,</A><br>
+<A NAME=5.3.61>I would applaud thee to the very echo,</A><br>
+<A NAME=5.3.62>That should applaud again.--Pull't off, I say.--</A><br>
+<A NAME=5.3.63>What rhubarb, cyme, or what purgative drug,</A><br>
+<A NAME=5.3.64>Would scour these English hence? Hear'st thou of them?</A><br>
+</blockquote>
+
+<A NAME=speech18><b>Doctor</b></a>
+<blockquote>
+<A NAME=5.3.65>Ay, my good lord; your royal preparation</A><br>
+<A NAME=5.3.66>Makes us hear something.</A><br>
+</blockquote>
+
+<A NAME=speech19><b>MACBETH</b></a>
+<blockquote>
+<A NAME=5.3.67>Bring it after me.</A><br>
+<A NAME=5.3.68>I will not be afraid of death and bane,</A><br>
+<A NAME=5.3.69>Till Birnam forest come to Dunsinane.</A><br>
+</blockquote>
+
+<A NAME=speech20><b>Doctor</b></a>
+<blockquote>
+<A NAME=5.3.70>[Aside] Were I from Dunsinane away and clear,</A><br>
+<A NAME=5.3.71>Profit again should hardly draw me here.</A><br>
+<p><i>Exeunt</i></p>
+</blockquote>
+<h3>SCENE IV. Country near Birnam wood.</h3>
+<p><blockquote>
+<i>Drum and colours. Enter MALCOLM, SIWARD and YOUNG SIWARD, MACDUFF, MENTEITH, CAITHNESS, ANGUS, LENNOX, ROSS, and Soldiers, marching</i>
+</blockquote>
+
+<A NAME=speech1><b>MALCOLM</b></a>
+<blockquote>
+<A NAME=5.4.1>Cousins, I hope the days are near at hand</A><br>
+<A NAME=5.4.2>That chambers will be safe.</A><br>
+</blockquote>
+
+<A NAME=speech2><b>MENTEITH</b></a>
+<blockquote>
+<A NAME=5.4.3>We doubt it nothing.</A><br>
+</blockquote>
+
+<A NAME=speech3><b>SIWARD</b></a>
+<blockquote>
+<A NAME=5.4.4>What wood is this before us?</A><br>
+</blockquote>
+
+<A NAME=speech4><b>MENTEITH</b></a>
+<blockquote>
+<A NAME=5.4.5>The wood of Birnam.</A><br>
+</blockquote>
+
+<A NAME=speech5><b>MALCOLM</b></a>
+<blockquote>
+<A NAME=5.4.6>Let every soldier hew him down a bough</A><br>
+<A NAME=5.4.7>And bear't before him: thereby shall we shadow</A><br>
+<A NAME=5.4.8>The numbers of our host and make discovery</A><br>
+<A NAME=5.4.9>Err in report of us.</A><br>
+</blockquote>
+
+<A NAME=speech6><b>Soldiers</b></a>
+<blockquote>
+<A NAME=5.4.10>It shall be done.</A><br>
+</blockquote>
+
+<A NAME=speech7><b>SIWARD</b></a>
+<blockquote>
+<A NAME=5.4.11>We learn no other but the confident tyrant</A><br>
+<A NAME=5.4.12>Keeps still in Dunsinane, and will endure</A><br>
+<A NAME=5.4.13>Our setting down before 't.</A><br>
+</blockquote>
+
+<A NAME=speech8><b>MALCOLM</b></a>
+<blockquote>
+<A NAME=5.4.14>'Tis his main hope:</A><br>
+<A NAME=5.4.15>For where there is advantage to be given,</A><br>
+<A NAME=5.4.16>Both more and less have given him the revolt,</A><br>
+<A NAME=5.4.17>And none serve with him but constrained things</A><br>
+<A NAME=5.4.18>Whose hearts are absent too.</A><br>
+</blockquote>
+
+<A NAME=speech9><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=5.4.19>Let our just censures</A><br>
+<A NAME=5.4.20>Attend the true event, and put we on</A><br>
+<A NAME=5.4.21>Industrious soldiership.</A><br>
+</blockquote>
+
+<A NAME=speech10><b>SIWARD</b></a>
+<blockquote>
+<A NAME=5.4.22>The time approaches</A><br>
+<A NAME=5.4.23>That will with due decision make us know</A><br>
+<A NAME=5.4.24>What we shall say we have and what we owe.</A><br>
+<A NAME=5.4.25>Thoughts speculative their unsure hopes relate,</A><br>
+<A NAME=5.4.26>But certain issue strokes must arbitrate:</A><br>
+<A NAME=5.4.27>Towards which advance the war.</A><br>
+<p><i>Exeunt, marching</i></p>
+</blockquote>
+<h3>SCENE V. Dunsinane. Within the castle.</h3>
+<p><blockquote>
+<i>Enter MACBETH, SEYTON, and Soldiers, with drum and colours</i>
+</blockquote>
+
+<A NAME=speech1><b>MACBETH</b></a>
+<blockquote>
+<A NAME=5.5.1>Hang out our banners on the outward walls;</A><br>
+<A NAME=5.5.2>The cry is still 'They come:' our castle's strength</A><br>
+<A NAME=5.5.3>Will laugh a siege to scorn: here let them lie</A><br>
+<A NAME=5.5.4>Till famine and the ague eat them up:</A><br>
+<A NAME=5.5.5>Were they not forced with those that should be ours,</A><br>
+<A NAME=5.5.6>We might have met them dareful, beard to beard,</A><br>
+<A NAME=5.5.7>And beat them backward home.</A><br>
+<p><i>A cry of women within</i></p>
+<A NAME=5.5.8>What is that noise?</A><br>
+</blockquote>
+
+<A NAME=speech2><b>SEYTON</b></a>
+<blockquote>
+<A NAME=5.5.9>It is the cry of women, my good lord.</A><br>
+<p><i>Exit</i></p>
+</blockquote>
+
+<A NAME=speech3><b>MACBETH</b></a>
+<blockquote>
+<A NAME=5.5.10>I have almost forgot the taste of fears;</A><br>
+<A NAME=5.5.11>The time has been, my senses would have cool'd</A><br>
+<A NAME=5.5.12>To hear a night-shriek; and my fell of hair</A><br>
+<A NAME=5.5.13>Would at a dismal treatise rouse and stir</A><br>
+<A NAME=5.5.14>As life were in't: I have supp'd full with horrors;</A><br>
+<A NAME=5.5.15>Direness, familiar to my slaughterous thoughts</A><br>
+<A NAME=5.5.16>Cannot once start me.</A><br>
+<p><i>Re-enter SEYTON</i></p>
+<A NAME=5.5.17>Wherefore was that cry?</A><br>
+</blockquote>
+
+<A NAME=speech4><b>SEYTON</b></a>
+<blockquote>
+<A NAME=5.5.18>The queen, my lord, is dead.</A><br>
+</blockquote>
+
+<A NAME=speech5><b>MACBETH</b></a>
+<blockquote>
+<A NAME=5.5.19>She should have died hereafter;</A><br>
+<A NAME=5.5.20>There would have been a time for such a word.</A><br>
+<A NAME=5.5.21>To-morrow, and to-morrow, and to-morrow,</A><br>
+<A NAME=5.5.22>Creeps in this petty pace from day to day</A><br>
+<A NAME=5.5.23>To the last syllable of recorded time,</A><br>
+<A NAME=5.5.24>And all our yesterdays have lighted fools</A><br>
+<A NAME=5.5.25>The way to dusty death. Out, out, brief candle!</A><br>
+<A NAME=5.5.26>Life's but a walking shadow, a poor player</A><br>
+<A NAME=5.5.27>That struts and frets his hour upon the stage</A><br>
+<A NAME=5.5.28>And then is heard no more: it is a tale</A><br>
+<A NAME=5.5.29>Told by an idiot, full of sound and fury,</A><br>
+<A NAME=5.5.30>Signifying nothing.</A><br>
+<p><i>Enter a Messenger</i></p>
+<A NAME=5.5.31>Thou comest to use thy tongue; thy story quickly.</A><br>
+</blockquote>
+
+<A NAME=speech6><b>Messenger</b></a>
+<blockquote>
+<A NAME=5.5.32>Gracious my lord,</A><br>
+<A NAME=5.5.33>I should report that which I say I saw,</A><br>
+<A NAME=5.5.34>But know not how to do it.</A><br>
+</blockquote>
+
+<A NAME=speech7><b>MACBETH</b></a>
+<blockquote>
+<A NAME=5.5.35>Well, say, sir.</A><br>
+</blockquote>
+
+<A NAME=speech8><b>Messenger</b></a>
+<blockquote>
+<A NAME=5.5.36>As I did stand my watch upon the hill,</A><br>
+<A NAME=5.5.37>I look'd toward Birnam, and anon, methought,</A><br>
+<A NAME=5.5.38>The wood began to move.</A><br>
+</blockquote>
+
+<A NAME=speech9><b>MACBETH</b></a>
+<blockquote>
+<A NAME=5.5.39>Liar and slave!</A><br>
+</blockquote>
+
+<A NAME=speech10><b>Messenger</b></a>
+<blockquote>
+<A NAME=5.5.40>Let me endure your wrath, if't be not so:</A><br>
+<A NAME=5.5.41>Within this three mile may you see it coming;</A><br>
+<A NAME=5.5.42>I say, a moving grove.</A><br>
+</blockquote>
+
+<A NAME=speech11><b>MACBETH</b></a>
+<blockquote>
+<A NAME=5.5.43>If thou speak'st false,</A><br>
+<A NAME=5.5.44>Upon the next tree shalt thou hang alive,</A><br>
+<A NAME=5.5.45>Till famine cling thee: if thy speech be sooth,</A><br>
+<A NAME=5.5.46>I care not if thou dost for me as much.</A><br>
+<A NAME=5.5.47>I pull in resolution, and begin</A><br>
+<A NAME=5.5.48>To doubt the equivocation of the fiend</A><br>
+<A NAME=5.5.49>That lies like truth: 'Fear not, till Birnam wood</A><br>
+<A NAME=5.5.50>Do come to Dunsinane:' and now a wood</A><br>
+<A NAME=5.5.51>Comes toward Dunsinane. Arm, arm, and out!</A><br>
+<A NAME=5.5.52>If this which he avouches does appear,</A><br>
+<A NAME=5.5.53>There is nor flying hence nor tarrying here.</A><br>
+<A NAME=5.5.54>I gin to be aweary of the sun,</A><br>
+<A NAME=5.5.55>And wish the estate o' the world were now undone.</A><br>
+<A NAME=5.5.56>Ring the alarum-bell! Blow, wind! come, wrack!</A><br>
+<A NAME=5.5.57>At least we'll die with harness on our back.</A><br>
+<p><i>Exeunt</i></p>
+</blockquote>
+<h3>SCENE VI. Dunsinane. Before the castle.</h3>
+<p><blockquote>
+<i>Drum and colours. Enter MALCOLM, SIWARD, MACDUFF, and their Army, with boughs</i>
+</blockquote>
+
+<A NAME=speech1><b>MALCOLM</b></a>
+<blockquote>
+<A NAME=5.6.1>Now near enough: your leafy screens throw down.</A><br>
+<A NAME=5.6.2>And show like those you are. You, worthy uncle,</A><br>
+<A NAME=5.6.3>Shall, with my cousin, your right-noble son,</A><br>
+<A NAME=5.6.4>Lead our first battle: worthy Macduff and we</A><br>
+<A NAME=5.6.5>Shall take upon 's what else remains to do,</A><br>
+<A NAME=5.6.6>According to our order.</A><br>
+</blockquote>
+
+<A NAME=speech2><b>SIWARD</b></a>
+<blockquote>
+<A NAME=5.6.7>Fare you well.</A><br>
+<A NAME=5.6.8>Do we but find the tyrant's power to-night,</A><br>
+<A NAME=5.6.9>Let us be beaten, if we cannot fight.</A><br>
+</blockquote>
+
+<A NAME=speech3><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=5.6.10>Make all our trumpets speak; give them all breath,</A><br>
+<A NAME=5.6.11>Those clamorous harbingers of blood and death.</A><br>
+<p><i>Exeunt</i></p>
+</blockquote>
+<h3>SCENE VII. Another part of the field.</h3>
+<p><blockquote>
+<i>Alarums. Enter MACBETH</i>
+</blockquote>
+
+<A NAME=speech1><b>MACBETH</b></a>
+<blockquote>
+<A NAME=5.7.1>They have tied me to a stake; I cannot fly,</A><br>
+<A NAME=5.7.2>But, bear-like, I must fight the course. What's he</A><br>
+<A NAME=5.7.3>That was not born of woman? Such a one</A><br>
+<A NAME=5.7.4>Am I to fear, or none.</A><br>
+<p><i>Enter YOUNG SIWARD</i></p>
+</blockquote>
+
+<A NAME=speech2><b>YOUNG SIWARD</b></a>
+<blockquote>
+<A NAME=5.7.5>What is thy name?</A><br>
+</blockquote>
+
+<A NAME=speech3><b>MACBETH</b></a>
+<blockquote>
+<A NAME=5.7.6> Thou'lt be afraid to hear it.</A><br>
+</blockquote>
+
+<A NAME=speech4><b>YOUNG SIWARD</b></a>
+<blockquote>
+<A NAME=5.7.7>No; though thou call'st thyself a hotter name</A><br>
+<A NAME=5.7.8>Than any is in hell.</A><br>
+</blockquote>
+
+<A NAME=speech5><b>MACBETH</b></a>
+<blockquote>
+<A NAME=5.7.9>My name's Macbeth.</A><br>
+</blockquote>
+
+<A NAME=speech6><b>YOUNG SIWARD</b></a>
+<blockquote>
+<A NAME=5.7.10>The devil himself could not pronounce a title</A><br>
+<A NAME=5.7.11>More hateful to mine ear.</A><br>
+</blockquote>
+
+<A NAME=speech7><b>MACBETH</b></a>
+<blockquote>
+<A NAME=5.7.12>No, nor more fearful.</A><br>
+</blockquote>
+
+<A NAME=speech8><b>YOUNG SIWARD</b></a>
+<blockquote>
+<A NAME=5.7.13>Thou liest, abhorred tyrant; with my sword</A><br>
+<A NAME=5.7.14>I'll prove the lie thou speak'st.</A><br>
+<p><i>They fight and YOUNG SIWARD is slain</i></p>
+</blockquote>
+
+<A NAME=speech9><b>MACBETH</b></a>
+<blockquote>
+<A NAME=5.7.15>Thou wast born of woman</A><br>
+<A NAME=5.7.16>But swords I smile at, weapons laugh to scorn,</A><br>
+<A NAME=5.7.17>Brandish'd by man that's of a woman born.</A><br>
+<p><i>Exit</i></p>
+<p><i>Alarums. Enter MACDUFF</i></p>
+</blockquote>
+
+<A NAME=speech10><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=5.7.18>That way the noise is. Tyrant, show thy face!</A><br>
+<A NAME=5.7.19>If thou be'st slain and with no stroke of mine,</A><br>
+<A NAME=5.7.20>My wife and children's ghosts will haunt me still.</A><br>
+<A NAME=5.7.21>I cannot strike at wretched kerns, whose arms</A><br>
+<A NAME=5.7.22>Are hired to bear their staves: either thou, Macbeth,</A><br>
+<A NAME=5.7.23>Or else my sword with an unbatter'd edge</A><br>
+<A NAME=5.7.24>I sheathe again undeeded. There thou shouldst be;</A><br>
+<A NAME=5.7.25>By this great clatter, one of greatest note</A><br>
+<A NAME=5.7.26>Seems bruited. Let me find him, fortune!</A><br>
+<A NAME=5.7.27>And more I beg not.</A><br>
+<p><i>Exit. Alarums</i></p>
+<p><i>Enter MALCOLM and SIWARD</i></p>
+</blockquote>
+
+<A NAME=speech11><b>SIWARD</b></a>
+<blockquote>
+<A NAME=5.7.28>This way, my lord; the castle's gently render'd:</A><br>
+<A NAME=5.7.29>The tyrant's people on both sides do fight;</A><br>
+<A NAME=5.7.30>The noble thanes do bravely in the war;</A><br>
+<A NAME=5.7.31>The day almost itself professes yours,</A><br>
+<A NAME=5.7.32>And little is to do.</A><br>
+</blockquote>
+
+<A NAME=speech12><b>MALCOLM</b></a>
+<blockquote>
+<A NAME=5.7.33>We have met with foes</A><br>
+<A NAME=5.7.34>That strike beside us.</A><br>
+</blockquote>
+
+<A NAME=speech13><b>SIWARD</b></a>
+<blockquote>
+<A NAME=5.7.35>Enter, sir, the castle.</A><br>
+<p><i>Exeunt. Alarums</i></p>
+</blockquote>
+<h3>SCENE VIII. Another part of the field.</h3>
+<p><blockquote>
+<i>Enter MACBETH</i>
+</blockquote>
+
+<A NAME=speech1><b>MACBETH</b></a>
+<blockquote>
+<A NAME=5.8.1>Why should I play the Roman fool, and die</A><br>
+<A NAME=5.8.2>On mine own sword? whiles I see lives, the gashes</A><br>
+<A NAME=5.8.3>Do better upon them.</A><br>
+<p><i>Enter MACDUFF</i></p>
+</blockquote>
+
+<A NAME=speech2><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=5.8.4>Turn, hell-hound, turn!</A><br>
+</blockquote>
+
+<A NAME=speech3><b>MACBETH</b></a>
+<blockquote>
+<A NAME=5.8.5>Of all men else I have avoided thee:</A><br>
+<A NAME=5.8.6>But get thee back; my soul is too much charged</A><br>
+<A NAME=5.8.7>With blood of thine already.</A><br>
+</blockquote>
+
+<A NAME=speech4><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=5.8.8>I have no words:</A><br>
+<A NAME=5.8.9>My voice is in my sword: thou bloodier villain</A><br>
+<A NAME=5.8.10>Than terms can give thee out!</A><br>
+<p><i>They fight</i></p>
+</blockquote>
+
+<A NAME=speech5><b>MACBETH</b></a>
+<blockquote>
+<A NAME=5.8.11>Thou losest labour:</A><br>
+<A NAME=5.8.12>As easy mayst thou the intrenchant air</A><br>
+<A NAME=5.8.13>With thy keen sword impress as make me bleed:</A><br>
+<A NAME=5.8.14>Let fall thy blade on vulnerable crests;</A><br>
+<A NAME=5.8.15>I bear a charmed life, which must not yield,</A><br>
+<A NAME=5.8.16>To one of woman born.</A><br>
+</blockquote>
+
+<A NAME=speech6><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=5.8.17>Despair thy charm;</A><br>
+<A NAME=5.8.18>And let the angel whom thou still hast served</A><br>
+<A NAME=5.8.19>Tell thee, Macduff was from his mother's womb</A><br>
+<A NAME=5.8.20>Untimely ripp'd.</A><br>
+</blockquote>
+
+<A NAME=speech7><b>MACBETH</b></a>
+<blockquote>
+<A NAME=5.8.21>Accursed be that tongue that tells me so,</A><br>
+<A NAME=5.8.22>For it hath cow'd my better part of man!</A><br>
+<A NAME=5.8.23>And be these juggling fiends no more believed,</A><br>
+<A NAME=5.8.24>That palter with us in a double sense;</A><br>
+<A NAME=5.8.25>That keep the word of promise to our ear,</A><br>
+<A NAME=5.8.26>And break it to our hope. I'll not fight with thee.</A><br>
+</blockquote>
+
+<A NAME=speech8><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=5.8.27>Then yield thee, coward,</A><br>
+<A NAME=5.8.28>And live to be the show and gaze o' the time:</A><br>
+<A NAME=5.8.29>We'll have thee, as our rarer monsters are,</A><br>
+<A NAME=5.8.30>Painted on a pole, and underwrit,</A><br>
+<A NAME=5.8.31>'Here may you see the tyrant.'</A><br>
+</blockquote>
+
+<A NAME=speech9><b>MACBETH</b></a>
+<blockquote>
+<A NAME=5.8.32>I will not yield,</A><br>
+<A NAME=5.8.33>To kiss the ground before young Malcolm's feet,</A><br>
+<A NAME=5.8.34>And to be baited with the rabble's curse.</A><br>
+<A NAME=5.8.35>Though Birnam wood be come to Dunsinane,</A><br>
+<A NAME=5.8.36>And thou opposed, being of no woman born,</A><br>
+<A NAME=5.8.37>Yet I will try the last. Before my body</A><br>
+<A NAME=5.8.38>I throw my warlike shield. Lay on, Macduff,</A><br>
+<A NAME=5.8.39>And damn'd be him that first cries, 'Hold, enough!'</A><br>
+<p><i>Exeunt, fighting. Alarums</i></p>
+<p><i>Retreat. Flourish. Enter, with drum and colours, MALCOLM, SIWARD, ROSS, the other Thanes, and Soldiers</i></p>
+</blockquote>
+
+<A NAME=speech10><b>MALCOLM</b></a>
+<blockquote>
+<A NAME=5.8.40>I would the friends we miss were safe arrived.</A><br>
+</blockquote>
+
+<A NAME=speech11><b>SIWARD</b></a>
+<blockquote>
+<A NAME=5.8.41>Some must go off: and yet, by these I see,</A><br>
+<A NAME=5.8.42>So great a day as this is cheaply bought.</A><br>
+</blockquote>
+
+<A NAME=speech12><b>MALCOLM</b></a>
+<blockquote>
+<A NAME=5.8.43>Macduff is missing, and your noble son.</A><br>
+</blockquote>
+
+<A NAME=speech13><b>ROSS</b></a>
+<blockquote>
+<A NAME=5.8.44>Your son, my lord, has paid a soldier's debt:</A><br>
+<A NAME=5.8.45>He only lived but till he was a man;</A><br>
+<A NAME=5.8.46>The which no sooner had his prowess confirm'd</A><br>
+<A NAME=5.8.47>In the unshrinking station where he fought,</A><br>
+<A NAME=5.8.48>But like a man he died.</A><br>
+</blockquote>
+
+<A NAME=speech14><b>SIWARD</b></a>
+<blockquote>
+<A NAME=5.8.49>Then he is dead?</A><br>
+</blockquote>
+
+<A NAME=speech15><b>ROSS</b></a>
+<blockquote>
+<A NAME=5.8.50>Ay, and brought off the field: your cause of sorrow</A><br>
+<A NAME=5.8.51>Must not be measured by his worth, for then</A><br>
+<A NAME=5.8.52>It hath no end.</A><br>
+</blockquote>
+
+<A NAME=speech16><b>SIWARD</b></a>
+<blockquote>
+<A NAME=5.8.53> Had he his hurts before?</A><br>
+</blockquote>
+
+<A NAME=speech17><b>ROSS</b></a>
+<blockquote>
+<A NAME=5.8.54>Ay, on the front.</A><br>
+</blockquote>
+
+<A NAME=speech18><b>SIWARD</b></a>
+<blockquote>
+<A NAME=5.8.55> Why then, God's soldier be he!</A><br>
+<A NAME=5.8.56>Had I as many sons as I have hairs,</A><br>
+<A NAME=5.8.57>I would not wish them to a fairer death:</A><br>
+<A NAME=5.8.58>And so, his knell is knoll'd.</A><br>
+</blockquote>
+
+<A NAME=speech19><b>MALCOLM</b></a>
+<blockquote>
+<A NAME=5.8.59>He's worth more sorrow,</A><br>
+<A NAME=5.8.60>And that I'll spend for him.</A><br>
+</blockquote>
+
+<A NAME=speech20><b>SIWARD</b></a>
+<blockquote>
+<A NAME=5.8.61>He's worth no more</A><br>
+<A NAME=5.8.62>They say he parted well, and paid his score:</A><br>
+<A NAME=5.8.63>And so, God be with him! Here comes newer comfort.</A><br>
+<p><i>Re-enter MACDUFF, with MACBETH's head</i></p>
+</blockquote>
+
+<A NAME=speech21><b>MACDUFF</b></a>
+<blockquote>
+<A NAME=5.8.64>Hail, king! for so thou art: behold, where stands</A><br>
+<A NAME=5.8.65>The usurper's cursed head: the time is free:</A><br>
+<A NAME=5.8.66>I see thee compass'd with thy kingdom's pearl,</A><br>
+<A NAME=5.8.67>That speak my salutation in their minds;</A><br>
+<A NAME=5.8.68>Whose voices I desire aloud with mine:</A><br>
+<A NAME=5.8.69>Hail, King of Scotland!</A><br>
+</blockquote>
+
+<A NAME=speech22><b>ALL</b></a>
+<blockquote>
+<A NAME=5.8.70>Hail, King of Scotland!</A><br>
+<p><i>Flourish</i></p>
+</blockquote>
+
+<A NAME=speech23><b>MALCOLM</b></a>
+<blockquote>
+<A NAME=5.8.71>We shall not spend a large expense of time</A><br>
+<A NAME=5.8.72>Before we reckon with your several loves,</A><br>
+<A NAME=5.8.73>And make us even with you. My thanes and kinsmen,</A><br>
+<A NAME=5.8.74>Henceforth be earls, the first that ever Scotland</A><br>
+<A NAME=5.8.75>In such an honour named. What's more to do,</A><br>
+<A NAME=5.8.76>Which would be planted newly with the time,</A><br>
+<A NAME=5.8.77>As calling home our exiled friends abroad</A><br>
+<A NAME=5.8.78>That fled the snares of watchful tyranny;</A><br>
+<A NAME=5.8.79>Producing forth the cruel ministers</A><br>
+<A NAME=5.8.80>Of this dead butcher and his fiend-like queen,</A><br>
+<A NAME=5.8.81>Who, as 'tis thought, by self and violent hands</A><br>
+<A NAME=5.8.82>Took off her life; this, and what needful else</A><br>
+<A NAME=5.8.83>That calls upon us, by the grace of Grace,</A><br>
+<A NAME=5.8.84>We will perform in measure, time and place:</A><br>
+<A NAME=5.8.85>So, thanks to all at once and to each one,</A><br>
+<A NAME=5.8.86>Whom we invite to see us crown'd at Scone.</A><br>
+<p><i>Flourish. Exeunt</i></p>
+</body>
+</html>
+
diff --git a/node_modules/selenium-webdriver/lib/test/data/map.png b/node_modules/selenium-webdriver/lib/test/data/map.png
new file mode 100644
index 000000000..763f56279
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/map.png
Binary files differ
diff --git a/node_modules/selenium-webdriver/lib/test/data/map_visibility.html b/node_modules/selenium-webdriver/lib/test/data/map_visibility.html
new file mode 100644
index 000000000..6cf5f763e
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/map_visibility.html
@@ -0,0 +1,8 @@
+<html>
+<head>
+ <title>Map test page</title>
+</head>
+<body>
+<div style="position: absolute; left: 0px; top: 0px; z-index: 106;"><img style="position: absolute; left: 271px; top: 320px; width: 20px; height: 34px; -moz-user-select: none; border: 0px none; padding: 0px; margin: 0px; z-index: -140270496;" src="markerTransparent.png" class="gmnoprint" usemap="#gmimap0"><map name="gmimap0" id="gmimap0"><area log="miw" coords="9,0,6,1,4,2,2,4,0,8,0,12,1,14,2,16,5,19,7,23,8,26,9,30,9,34,11,34,11,30,12,26,13,24,14,21,16,18,18,16,20,12,20,8,18,4,16,2,15,1,13,0" shape="poly" alt="" href="javascript:void(0)" id="mtgt_unnamed_0" style="position: absolute; left: 159px; top: 342px;"></map></div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/markerTransparent.png b/node_modules/selenium-webdriver/lib/test/data/markerTransparent.png
new file mode 100644
index 000000000..ed4e5e7f4
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/markerTransparent.png
Binary files differ
diff --git a/node_modules/selenium-webdriver/lib/test/data/messages.html b/node_modules/selenium-webdriver/lib/test/data/messages.html
new file mode 100644
index 000000000..74f1a37f7
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/messages.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<script>
+ var messages = [];
+ window.addEventListener('message', function(e) {
+ var message = JSON.stringify(e.data);
+ messages.push(message);
+
+ var pre = document.createElement('pre');
+ pre.style.margin = 0;
+ pre.style.padding = 0;
+ pre.textContent = message;
+
+ document.body.appendChild(pre);
+ }, true);
+</script> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/meta-redirect.html b/node_modules/selenium-webdriver/lib/test/data/meta-redirect.html
new file mode 100644
index 000000000..9d9c2f0cc
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/meta-redirect.html
@@ -0,0 +1,11 @@
+<html>
+<head>
+ <title>Some test page</title>
+ <meta http-equiv="refresh" content="0;URL=resultPage.html"/>
+</head>
+<body>
+<script type="text/javascript" language="javascript"><!--
+location.replace("resultPage.html");
+//--> </script>
+</body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/missedJsReference.html b/node_modules/selenium-webdriver/lib/test/data/missedJsReference.html
new file mode 100644
index 000000000..616775276
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/missedJsReference.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Example page</title>
+</head>
+<body>
+<p>This page contains a nested iframe. Execute some JS to locate a reference to an element in this
+ frame and return it. You should need to switch to that frame in order to use that element.</p>
+<iframe src="simpleTest.html" name="inner"></iframe>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/modal_dialogs/modal_1.html b/node_modules/selenium-webdriver/lib/test/data/modal_dialogs/modal_1.html
new file mode 100644
index 000000000..4eff01acd
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/modal_dialogs/modal_1.html
@@ -0,0 +1,21 @@
+<html>
+<head>
+<title>First Modal</title>
+<script>
+ function openModal() {
+ window.showModalDialog("modal_2.html",'dialogWidth:250px;dialogHeight:200px;resizable:yes')
+ }
+</script>
+</head>
+
+<body>
+<p>Modal dialog sample</p>
+
+<input type="button" value="btn2" onclick="openModal();">
+
+<a id="lnk2" href="javascript:openModal()">lnk2</a>
+<div>
+
+</div>
+</body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/modal_dialogs/modal_2.html b/node_modules/selenium-webdriver/lib/test/data/modal_dialogs/modal_2.html
new file mode 100644
index 000000000..cec3f3f14
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/modal_dialogs/modal_2.html
@@ -0,0 +1,21 @@
+<html>
+<head>
+<title>Second Modal</title>
+<script>
+ function openModal() {
+ window.showModalDialog("modal_3.html",'dialogWidth:250px;dialogHeight:200px;resizable:yes')
+ }
+</script>
+</head>
+
+<body>
+<p>Modal dialog sample</p>
+
+<input type="button" value="btn3" onclick="openModal();">
+
+<a id="lnk3" href="javascript:openModal()">lnk3</a>
+<div>
+
+</div>
+</body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/modal_dialogs/modal_3.html b/node_modules/selenium-webdriver/lib/test/data/modal_dialogs/modal_3.html
new file mode 100644
index 000000000..6c5eb7231
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/modal_dialogs/modal_3.html
@@ -0,0 +1,15 @@
+<html>
+<head>
+<title>Third Modal</title>
+
+</head>
+
+<body>
+<p>Modal dialog sample</p>
+
+
+<div>
+
+</div>
+</body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/modal_dialogs/modalindex.html b/node_modules/selenium-webdriver/lib/test/data/modal_dialogs/modalindex.html
new file mode 100644
index 000000000..0a1c4c941
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/modal_dialogs/modalindex.html
@@ -0,0 +1,21 @@
+<html>
+<head>
+<title>Main window</title>
+<script>
+ function openModal() {
+ window.showModalDialog("modal_1.html",'dialogWidth:250px;dialogHeight:200px;resizable:yes')
+ }
+</script>
+</head>
+
+<body>
+<p>Modal dialog sample</p>
+
+<input type="button" value="btn1" onclick="openModal();">
+
+<a id="lnk1" href="javascript:openModal()">lnk1</a>
+<div>
+
+</div>
+</body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/mouseOver.html b/node_modules/selenium-webdriver/lib/test/data/mouseOver.html
new file mode 100644
index 000000000..d4751bfdf
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/mouseOver.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<body style="margin: 0; padding: 0;">
+<div style="position: absolute; overflow: hidden;
+ left: 0; top: 0; width: 250px; height: 250px;
+ background: green"
+ id="greenbox">
+ <div style="position: relative; left: 75px; top: 75px;
+ width: 75px; height: 75px;
+ background: green"
+ id="redbox">
+ </div>
+ <script>
+ var redbox = document.getElementById('redbox');
+ redbox.onmouseover = function() { redbox.style.background = 'red'; };
+ redbox.onmouseout = function() { redbox.style.background = 'green'; };
+ </script>
+</div>
diff --git a/node_modules/selenium-webdriver/lib/test/data/mousePositionTracker.html b/node_modules/selenium-webdriver/lib/test/data/mousePositionTracker.html
new file mode 100644
index 000000000..39a31cda4
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/mousePositionTracker.html
@@ -0,0 +1,33 @@
+<html>
+ <head>
+ <style type="text/css">
+ div.solidborder
+ {
+ border-style:solid;
+ border-width:1px;
+ }
+ </style>
+
+ <script type="text/javascript" src="js/jquery-1.4.4.min.js"></script>
+ <script type="text/javascript" src="js/jquery-ui-1.8.10.custom.min.js"></script>
+ <script type="text/javascript">
+ jQuery(document).ready(function(){
+ $('#mousetracker').mousemove(function(e){
+ xPos = e.pageX - this.offsetLeft;
+ yPos = e.pageY - this.offsetTop;
+ $('#status').html(xPos + ', ' + yPos);
+ });
+ })
+
+</script>
+<body>
+ <b>Div tracking mouse position.</b>
+ <br>
+<div id="mousetracker" class="solidborder" style="position: absolute; left: 10px; top: 80px; height: 400px; width: 100px; padding: 0px;">
+ Move mouse here.
+</div>
+<h2 id="status">
+0, 0
+</h2>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/nestedElements.html b/node_modules/selenium-webdriver/lib/test/data/nestedElements.html
new file mode 100644
index 000000000..cf00083cf
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/nestedElements.html
@@ -0,0 +1,155 @@
+<html>
+ <body>
+ <p id="test_id">outside</p>
+ <p id="test_id_out">outside</p>
+ <div id="test_id_div">
+ <p id="test_id">inside</p>
+ </div>
+<form method="get" action="resultPage.html" name="form1" style="display: block">
+ Here's a checkbox: <input type="checkbox" id="checky" name="checky" value="furrfu"/><br/>
+ <select name="selectomatic" id="1">
+ <option selected="selected">One</option>
+ <option>Two</option>
+ <option>Four</option>
+ <option>Still learning how to count, apparently</option>
+ </select>
+</form>
+
+<form method="get" action="resultPage.html" name="form2" style="display: block">
+ Here's a checkbox: <input type="checkbox" id="checky" name="checky" value="furrfu"/><br/>
+ <select name="selectomatic" id="2">
+ <option selected="selected">One</option>
+ <option>Two</option>
+ <option>Four</option>
+ <option>Still learning how to count, apparently</option>
+ </select>
+ <select name="selectomatic" id="2">
+ <option selected="selected">One</option>
+ <option>Two</option>
+ <option>Four</option>
+ <option>Still learning how to count, apparently</option>
+ </select>
+</form>
+
+<a href="1.html">hello world</a>
+<div name="div1">
+ <a href="2.html" name="link1">hello world</a>
+ <a href="2.html" name="link2">hello world</a>
+</div>
+</body>
+</html>
+<html>
+ <body>
+
+<form method="get" action="resultPage.html" name="form1" style="display: block">
+ Here's a checkbox: <input type="checkbox" id="checky" name="checky" value="furrfu"/><br/>
+ <select name="selectomatic" id="1">
+ <option selected="selected">One</option>
+ <option>Two</option>
+ <option>Four</option>
+ <option>Still learning how to count, apparently</option>
+ </select>
+</form>
+
+<form method="get" action="resultPage.html" name="form2" style="display: block">
+ Here's a checkbox: <input type="checkbox" id="checky" name="checky" value="furrfu"/><br/>
+ <select name="selectomatic" id="2">
+ <option selected="selected">One</option>
+ <option>Two</option>
+ <option>Four</option>
+ <option>Still learning how to count, apparently</option>
+ </select>
+ <select name="selectomatic" id="2">
+ <option selected="selected">One</option>
+ <option>Two</option>
+ <option>Four</option>
+ <option>Still learning how to count, apparently</option>
+ </select>
+</form>
+
+<a href="1.html">hello world</a>
+<div name="div1">
+ <a href="2.html">hello world</a>
+ <a href="2.html">hello world</a>
+</div>
+</body>
+</html>
+<html>
+ <body>
+
+<form method="get" action="resultPage.html" name="form1" style="display: block">
+ Here's a checkbox: <input type="checkbox" id="checky" name="checky" value="furrfu"/><br/>
+ <select name="selectomatic" id="1">
+ <option selected="selected">One</option>
+ <option>Two</option>
+ <option>Four</option>
+ <option>Still learning how to count, apparently</option>
+ </select>
+</form>
+
+<form method="get" action="resultPage.html" name="form2" style="display: block">
+ Here's a checkbox: <input type="checkbox" id="checky" name="checky" value="furrfu"/><br/>
+ <select name="selectomatic" id="2">
+ <option selected="selected">One</option>
+ <option>Two</option>
+ <option>Four</option>
+ <option>Still learning how to count, apparently</option>
+ </select>
+ <select name="selectomatic" id="2">
+ <option selected="selected">One</option>
+ <option>Two</option>
+ <option>Four</option>
+ <option>Still learning how to count, apparently</option>
+ </select>
+</form>
+
+<a href="1.html">hello world</a>
+<div name="div1">
+ <a href="2.html">hello world</a>
+ <a href="2.html">hello world</a>
+</div>
+</body>
+</html>
+<html>
+ <body>
+
+<form method="get" action="resultPage.html" name="form1" style="display: block">
+ Here's a checkbox: <input type="checkbox" id="checky" name="checky" value="furrfu"/><br/>
+ <select name="selectomatic" id="1">
+ <option selected="selected">One</option>
+ <option>Two</option>
+ <option>Four</option>
+ <option>Still learning how to count, apparently</option>
+ </select>
+</form>
+
+<form method="get" action="resultPage.html" name="form2" style="display: block">
+ Here's a checkbox: <input type="checkbox" id="checky" name="checky" value="furrfu"/><br/>
+ <select name="selectomatic" id="2">
+ <option selected="selected">One</option>
+ <option>Two</option>
+ <option>Four</option>
+ <option>Still learning how to count, apparently</option>
+ </select>
+ <select name="selectomatic" id="2">
+ <option selected="selected">One</option>
+ <option>Two</option>
+ <option>Four</option>
+ <option>Still learning how to count, apparently</option>
+ </select>
+</form>
+
+<a href="1.html">hello world</a>
+<div name="div1">
+ <a href="2.html">hello world</a>
+ <a href="2.html">hello world</a>
+</div>
+
+<span class="one">Span with class of one</span>
+<div name="classes">
+ <span class="one">Find me</span>
+ <span class="one">Also me</span>
+ <span class="oneother">But not me</span>
+</div>
+</body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/overflow-body.html b/node_modules/selenium-webdriver/lib/test/data/overflow-body.html
new file mode 100644
index 000000000..2d2264ce6
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/overflow-body.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html style="overflow: auto;">
+<head>
+ <title>The Visibility of Everyday Things</title>
+</head>
+<body style="overflow: hidden;">
+<!-- When the test is run, the window is resized to less than 700 pixels high -->
+<p>This image is copyright Simon Stewart and donated to the Selenium project for use in its test suites.
+</p>
+<img src="beach.jpg" height="480" width="640" alt="a nice beach">
+
+<!-- So this is off screen -->
+<iframe width="100%" height="1000" name="resultsFrame" src="xhtmlTest.html"></iframe>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/overflow/x_auto_y_auto.html b/node_modules/selenium-webdriver/lib/test/data/overflow/x_auto_y_auto.html
new file mode 100644
index 000000000..cf8a64719
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/overflow/x_auto_y_auto.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Page with overflow</title>
+ <style>
+ #over {
+ width:400px;
+ height: 300px;
+ overflow-x: auto;
+ overflow-y: auto;
+ }
+ </style>
+</head>
+<body>
+ <div id="over">
+ <div style="width: 5000px;">
+ <div style="width: 100%; text-align: right;" ><a href="#" id="right" onclick="document.getElementById('right-clicked').innerText='ok'">Click right</a></div>
+ </div>
+ <div style="height: 5000px; width: 5000px;">
+ Right clicked: <span id="right-clicked"></span></br>
+ Bottom clicked: <span id="bottom-clicked"></span></br>
+ Bottom-right clicked: <span id="bottom-right-clicked"></span></br>
+ </div>
+ <div style="width: 5000px;">
+ <div style="width: 100%; text-align: right;" ><a href="#" id="bottom-right" onclick="document.getElementById('bottom-right-clicked').innerText='ok'">Click bottom-right</a></div>
+ </div>
+ <a href="#" id="bottom" onclick="document.getElementById('bottom-clicked').innerText='ok'">Click bottom</a>
+ </div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/overflow/x_auto_y_hidden.html b/node_modules/selenium-webdriver/lib/test/data/overflow/x_auto_y_hidden.html
new file mode 100644
index 000000000..96fd750a6
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/overflow/x_auto_y_hidden.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Page with overflow</title>
+ <style>
+ #over {
+ width:400px;
+ height: 300px;
+ overflow-x: auto;
+ overflow-y: hidden;
+ }
+ </style>
+</head>
+<body>
+ <div id="over">
+ <div style="width: 5000px;">
+ <div style="width: 100%; text-align: right;" ><a href="#" id="right" onclick="document.getElementById('right-clicked').innerText='ok'">Click right</a></div>
+ </div>
+ <div style="height: 5000px; width: 5000px;">
+ Right clicked: <span id="right-clicked"></span></br>
+ Bottom clicked: <span id="bottom-clicked"></span></br>
+ Bottom-right clicked: <span id="bottom-right-clicked"></span></br>
+ </div>
+ <div style="width: 5000px;">
+ <div style="width: 100%; text-align: right;" ><a href="#" id="bottom-right" onclick="document.getElementById('bottom-right-clicked').innerText='ok'">Click bottom-right</a></div>
+ </div>
+ <a href="#" id="bottom" onclick="document.getElementById('bottom-clicked').innerText='ok'">Click bottom</a>
+ </div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/overflow/x_auto_y_scroll.html b/node_modules/selenium-webdriver/lib/test/data/overflow/x_auto_y_scroll.html
new file mode 100644
index 000000000..6f1d90b6d
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/overflow/x_auto_y_scroll.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Page with overflow</title>
+ <style>
+ #over {
+ width:400px;
+ height: 300px;
+ overflow-x: auto;
+ overflow-y: scroll;
+ }
+ </style>
+</head>
+<body>
+ <div id="over">
+ <div style="width: 5000px;">
+ <div style="width: 100%; text-align: right;" ><a href="#" id="right" onclick="document.getElementById('right-clicked').innerText='ok'">Click right</a></div>
+ </div>
+ <div style="height: 5000px; width: 5000px;">
+ Right clicked: <span id="right-clicked"></span></br>
+ Bottom clicked: <span id="bottom-clicked"></span></br>
+ Bottom-right clicked: <span id="bottom-right-clicked"></span></br>
+ </div>
+ <div style="width: 5000px;">
+ <div style="width: 100%; text-align: right;" ><a href="#" id="bottom-right" onclick="document.getElementById('bottom-right-clicked').innerText='ok'">Click bottom-right</a></div>
+ </div>
+ <a href="#" id="bottom" onclick="document.getElementById('bottom-clicked').innerText='ok'">Click bottom</a>
+ </div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/overflow/x_hidden_y_auto.html b/node_modules/selenium-webdriver/lib/test/data/overflow/x_hidden_y_auto.html
new file mode 100644
index 000000000..24dd19283
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/overflow/x_hidden_y_auto.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Page with overflow</title>
+ <style>
+ #over {
+ width:400px;
+ height: 300px;
+ overflow-x: hidden;
+ overflow-y: auto;
+ }
+ </style>
+</head>
+<body>
+ <div id="over">
+ <div style="width: 5000px;">
+ <div style="width: 100%; text-align: right;" ><a href="#" id="right" onclick="document.getElementById('right-clicked').innerText='ok'">Click right</a></div>
+ </div>
+ <div style="height: 5000px; width: 5000px;">
+ Right clicked: <span id="right-clicked"></span></br>
+ Bottom clicked: <span id="bottom-clicked"></span></br>
+ Bottom-right clicked: <span id="bottom-right-clicked"></span></br>
+ </div>
+ <div style="width: 5000px;">
+ <div style="width: 100%; text-align: right;" ><a href="#" id="bottom-right" onclick="document.getElementById('bottom-right-clicked').innerText='ok'">Click bottom-right</a></div>
+ </div>
+ <a href="#" id="bottom" onclick="document.getElementById('bottom-clicked').innerText='ok'">Click bottom</a>
+ </div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/overflow/x_hidden_y_hidden.html b/node_modules/selenium-webdriver/lib/test/data/overflow/x_hidden_y_hidden.html
new file mode 100644
index 000000000..cae566578
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/overflow/x_hidden_y_hidden.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Page with overflow</title>
+ <style>
+ #over {
+ width:400px;
+ height: 300px;
+ overflow-x: hidden;
+ overflow-y: hidden;
+ }
+ </style>
+</head>
+<body>
+ <div id="over">
+ <div style="width: 5000px;">
+ <div style="width: 100%; text-align: right;" ><a href="#" id="right" onclick="document.getElementById('right-clicked').innerText='ok'">Click right</a></div>
+ </div>
+ <div style="height: 5000px; width: 5000px;">
+ Right clicked: <span id="right-clicked"></span></br>
+ Bottom clicked: <span id="bottom-clicked"></span></br>
+ Bottom-right clicked: <span id="bottom-right-clicked"></span></br>
+ </div>
+ <div style="width: 5000px;">
+ <div style="width: 100%; text-align: right;" ><a href="#" id="bottom-right" onclick="document.getElementById('bottom-right-clicked').innerText='ok'">Click bottom-right</a></div>
+ </div>
+ <a href="#" id="bottom" onclick="document.getElementById('bottom-clicked').innerText='ok'">Click bottom</a>
+ </div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/overflow/x_hidden_y_scroll.html b/node_modules/selenium-webdriver/lib/test/data/overflow/x_hidden_y_scroll.html
new file mode 100644
index 000000000..d4ffa3970
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/overflow/x_hidden_y_scroll.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Page with overflow</title>
+ <style>
+ #over {
+ width:400px;
+ height: 300px;
+ overflow-x: hidden;
+ overflow-y: scroll;
+ }
+ </style>
+</head>
+<body>
+ <div id="over">
+ <div style="width: 5000px;">
+ <div style="width: 100%; text-align: right;" ><a href="#" id="right" onclick="document.getElementById('right-clicked').innerText='ok'">Click right</a></div>
+ </div>
+ <div style="height: 5000px; width: 5000px;">
+ Right clicked: <span id="right-clicked"></span></br>
+ Bottom clicked: <span id="bottom-clicked"></span></br>
+ Bottom-right clicked: <span id="bottom-right-clicked"></span></br>
+ </div>
+ <div style="width: 5000px;">
+ <div style="width: 100%; text-align: right;" ><a href="#" id="bottom-right" onclick="document.getElementById('bottom-right-clicked').innerText='ok'">Click bottom-right</a></div>
+ </div>
+ <a href="#" id="bottom" onclick="document.getElementById('bottom-clicked').innerText='ok'">Click bottom</a>
+ </div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/overflow/x_scroll_y_auto.html b/node_modules/selenium-webdriver/lib/test/data/overflow/x_scroll_y_auto.html
new file mode 100644
index 000000000..d425a2a8a
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/overflow/x_scroll_y_auto.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Page with overflow</title>
+ <style>
+ #over {
+ width:400px;
+ height: 300px;
+ overflow-x: scroll;
+ overflow-y: auto;
+ }
+ </style>
+</head>
+<body>
+ <div id="over">
+ <div style="width: 5000px;">
+ <div style="width: 100%; text-align: right;" ><a href="#" id="right" onclick="document.getElementById('right-clicked').innerText='ok'">Click right</a></div>
+ </div>
+ <div style="height: 5000px; width: 5000px;">
+ Right clicked: <span id="right-clicked"></span></br>
+ Bottom clicked: <span id="bottom-clicked"></span></br>
+ Bottom-right clicked: <span id="bottom-right-clicked"></span></br>
+ </div>
+ <div style="width: 5000px;">
+ <div style="width: 100%; text-align: right;" ><a href="#" id="bottom-right" onclick="document.getElementById('bottom-right-clicked').innerText='ok'">Click bottom-right</a></div>
+ </div>
+ <a href="#" id="bottom" onclick="document.getElementById('bottom-clicked').innerText='ok'">Click bottom</a>
+ </div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/overflow/x_scroll_y_hidden.html b/node_modules/selenium-webdriver/lib/test/data/overflow/x_scroll_y_hidden.html
new file mode 100644
index 000000000..4a6ff595d
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/overflow/x_scroll_y_hidden.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Page with overflow</title>
+ <style>
+ #over {
+ width:400px;
+ height: 300px;
+ overflow-x: scroll;
+ overflow-y: hidden;
+ }
+ </style>
+</head>
+<body>
+ <div id="over">
+ <div style="width: 5000px;">
+ <div style="width: 100%; text-align: right;" ><a href="#" id="right" onclick="document.getElementById('right-clicked').innerText='ok'">Click right</a></div>
+ </div>
+ <div style="height: 5000px; width: 5000px;">
+ Right clicked: <span id="right-clicked"></span></br>
+ Bottom clicked: <span id="bottom-clicked"></span></br>
+ Bottom-right clicked: <span id="bottom-right-clicked"></span></br>
+ </div>
+ <div style="width: 5000px;">
+ <div style="width: 100%; text-align: right;" ><a href="#" id="bottom-right" onclick="document.getElementById('bottom-right-clicked').innerText='ok'">Click bottom-right</a></div>
+ </div>
+ <a href="#" id="bottom" onclick="document.getElementById('bottom-clicked').innerText='ok'">Click bottom</a>
+ </div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/overflow/x_scroll_y_scroll.html b/node_modules/selenium-webdriver/lib/test/data/overflow/x_scroll_y_scroll.html
new file mode 100644
index 000000000..efa80742a
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/overflow/x_scroll_y_scroll.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Page with overflow</title>
+ <style>
+ #over {
+ width:400px;
+ height: 300px;
+ overflow-x: scroll;
+ overflow-y: scroll;
+ }
+ </style>
+</head>
+<body>
+ <div id="over">
+ <div style="width: 5000px;">
+ <div style="width: 100%; text-align: right;" ><a href="#" id="right" onclick="document.getElementById('right-clicked').innerText='ok'">Click right</a></div>
+ </div>
+ <div style="height: 5000px; width: 5000px;">
+ Right clicked: <span id="right-clicked"></span></br>
+ Bottom clicked: <span id="bottom-clicked"></span></br>
+ Bottom-right clicked: <span id="bottom-right-clicked"></span></br>
+ </div>
+ <div style="width: 5000px;">
+ <div style="width: 100%; text-align: right;" ><a href="#" id="bottom-right" onclick="document.getElementById('bottom-right-clicked').innerText='ok'">Click bottom-right</a></div>
+ </div>
+ <a href="#" id="bottom" onclick="document.getElementById('bottom-clicked').innerText='ok'">Click bottom</a>
+ </div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/pageWithOnBeforeUnloadMessage.html b/node_modules/selenium-webdriver/lib/test/data/pageWithOnBeforeUnloadMessage.html
new file mode 100644
index 000000000..cb59707ed
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/pageWithOnBeforeUnloadMessage.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <!--
+ This page differs from pageWithOnUnload.html in that it returns a string in the
+ event handler, which causes a different type of alert to appear (the "Stay on
+ page/Leave current page" alert).
+ -->
+ <script type="text/javascript">
+ function onBeforeUnloadHandler() {
+ return "This is for WebDriver with onbeforeunload event handler.";
+ }
+ window.onbeforeunload = onBeforeUnloadHandler;
+ </script>
+ <title>Page with OnBeforeUnload handler</title>
+ </head>
+ <body>
+ <p>Page with onbeforeunload event handler. <a id="navigate" href="alerts.html">Click here to navigate to another page.</a></p>
+ </body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/pageWithOnLoad.html b/node_modules/selenium-webdriver/lib/test/data/pageWithOnLoad.html
new file mode 100644
index 000000000..2c644ff95
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/pageWithOnLoad.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body onload="javascript:alert('onload')">
+<p>Page with onload event handler</p>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/pageWithOnUnload.html b/node_modules/selenium-webdriver/lib/test/data/pageWithOnUnload.html
new file mode 100644
index 000000000..6070341e2
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/pageWithOnUnload.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body onbeforeunload="alert('onunload')">
+<p>Page with onunload event handler</p>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/page_with_link_to_slow_loading_page.html b/node_modules/selenium-webdriver/lib/test/data/page_with_link_to_slow_loading_page.html
new file mode 100644
index 000000000..ea94f8ec1
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/page_with_link_to_slow_loading_page.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body>
+<a id="link-to-slow-loading-page" href="sleep?time=5">load a slow page</a>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/plain.txt b/node_modules/selenium-webdriver/lib/test/data/plain.txt
new file mode 100644
index 000000000..8318c86b3
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/plain.txt
@@ -0,0 +1 @@
+Test \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/proxy/page1.html b/node_modules/selenium-webdriver/lib/test/data/proxy/page1.html
new file mode 100644
index 000000000..1810f1cdf
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/proxy/page1.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
+<meta http-equiv="X-UA-Compatible" content="IE=edge">
+<title>Page 1</title>
+<p>The <code>next</code> query param must be the URL for the next page to
+link to.
+<script>
+ var match = document.location.search.match(/next=(.*)/);
+ if (match && match.length == 2) {
+ var next = decodeURIComponent(match[1]);
+ var link = document.createElement('a');
+ link.innerText = 'Next!';
+ link.textContent = 'Next!';
+ link.id = 'next';
+ link.href = next + '?next=' + encodeURIComponent(
+ 'http://' + document.location.host +
+ document.location.pathname.replace('page1.html', 'page3.html'));
+ document.body.appendChild(link);
+ }
+</script>
diff --git a/node_modules/selenium-webdriver/lib/test/data/proxy/page2.html b/node_modules/selenium-webdriver/lib/test/data/proxy/page2.html
new file mode 100644
index 000000000..d826f1742
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/proxy/page2.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
+<meta http-equiv="X-UA-Compatible" content="IE=edge">
+<title>Page 2</title>
+This page is a middle man for referrer tests.
+This page will include a link to a "next" page if the <code>next</code> query
+parameter, or the <code>document.referrer</code> is set.
+<p><input type="button" id="forward" value="Forward" onclick="history.go(1)"/>
+<script>
+ var match = document.location.search.match(/next=(.*)/);
+ var next = document.referrer || '';
+ if (match && match.length == 2) {
+ next = decodeURIComponent(match[1]);
+ }
+
+ if (next) {
+ var link = document.createElement('a');
+ link.innerText = 'Next!';
+ link.textContent = 'Next!';
+ link.id = 'next';
+ link.href = next;
+ document.body.appendChild(link);
+ }
+</script>
diff --git a/node_modules/selenium-webdriver/lib/test/data/proxy/page3.html b/node_modules/selenium-webdriver/lib/test/data/proxy/page3.html
new file mode 100644
index 000000000..27048f729
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/proxy/page3.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
+<meta http-equiv="X-UA-Compatible" content="IE=edge">
+<title>Page 3</title>
+<p><input type="button" id="back" value="Back" onclick="history.go(-1)"/>
diff --git a/node_modules/selenium-webdriver/lib/test/data/readOnlyPage.html b/node_modules/selenium-webdriver/lib/test/data/readOnlyPage.html
new file mode 100644
index 000000000..8f257fdc7
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/readOnlyPage.html
@@ -0,0 +1,24 @@
+<html>
+<body>
+<input id="writableTextInput" type="text" value="Test"/>
+
+<input id="readOnlyTextInput" type="text" readonly value="Test"/>
+
+<input id="textInputnotenabled" type="text" disabled="true" value="Test"/>
+
+<textarea id="writableTextArea" rows="2" cols="20">
+This is a sample text area which is supposed to be cleared
+</textarea>
+
+<textarea id="textAreaReadOnly" readonly rows="5" cols="20">
+text area which is not supposed to be cleared</textarea>
+
+<textarea rows="5" id="textAreaNotenabled" disabled="true" cols="20">
+text area which is not supposed to be cleared</textarea>
+
+<div id="content-editable" contentEditable="true"><h1>This</h1><h2>is a</h2><p>contentEditable area</p></div>
+
+<div id="content-editable-blank" contentEditable="true" style="height:50px;"></div>
+</body>
+</html>
+
diff --git a/node_modules/selenium-webdriver/lib/test/data/rectangles.html b/node_modules/selenium-webdriver/lib/test/data/rectangles.html
new file mode 100644
index 000000000..8ba233984
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/rectangles.html
@@ -0,0 +1,40 @@
+<?xml version="1.0"?>
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title>Rectangles</title>
+ <style type="text/css">
+ div {
+ position: absolute;
+ margin: 0;
+ border: 0;
+ padding: 0;
+ }
+ #r1 {
+ background-color: blue;
+ left: 10px;
+ top: 10px;
+ width: 100px;
+ height: 50px;
+ }
+ #r2 {
+ background-color: red;
+ left: 10.9px;
+ top: 10.1px;
+ width: 48.666666667px;
+ height: 49.333333333px;
+ }
+ #r3 {
+ background-color: yellow;
+ left: 60px;
+ top: 10px;
+ width: 50px;
+ height: 25px;
+ }
+ </style>
+</head>
+ <body>
+ <div id="r1">r1</div>
+ <div id="r2">r2</div>
+ <div id="r3">r3</div>
+ </body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/resultPage.html b/node_modules/selenium-webdriver/lib/test/data/resultPage.html
new file mode 100644
index 000000000..94f3e246e
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/resultPage.html
@@ -0,0 +1,25 @@
+<html>
+<head>
+ <title>We Arrive Here</title>
+</head>
+<body>
+
+<p id="greeting">Success!</p>
+
+<div>
+ <h1>List of stuff</h1>
+ <ol>
+ <li class="items">Item 1</li>
+ <li>Item 2</li>
+ </ol>
+</div>
+<div>
+ <h1>Almost empty</h1>
+</div>
+
+<script language="Javascript">
+ document.write("<p>Window name is: " + window.name + "</p>");
+</script>
+
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/rich_text.html b/node_modules/selenium-webdriver/lib/test/data/rich_text.html
new file mode 100644
index 000000000..a42e43aab
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/rich_text.html
@@ -0,0 +1,161 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<script type="text/javascript">
+function setKeypressHandler (el, keyHandler) {
+ if (el.addEventListener) {
+ el.addEventListener('keypress', keyHandler, false);
+ el.addEventListener('keydown', keyHandler, false);
+ el.addEventListener('keyup', keyHandler, false);
+ } else {
+ el.attachEvent('onkeypress', keyHandler);
+ el.attachEvent('onkeydown', keyHandler);
+ el.attachEvent('onkeyup', keyHandler);
+ }
+}
+
+function output (text) {
+ var div = document.getElementById('debugOutput');
+ div.appendChild(document.createTextNode(text));
+ div.appendChild(document.createElement('br'));
+}
+
+window.onload = function () {
+ // Super dumb browser detection
+ var isIE = window.navigator.userAgent.search('MSIE') != -1
+ || window.navigator.userAgent.search('Trident') != -1;
+
+ var editFrame = document.getElementById('editFrame').contentWindow;
+ setKeypressHandler(editFrame.document, printEventData);
+ if (isIE) {
+ editFrame.document.body.contentEditable = true;
+ } else {
+ editFrame.document.designMode = 'On';
+ // Attach a name to the containing HTML element
+ editFrame.document.getElementsByTagName("html")[0].id = "frameHtml";
+ }
+
+ var editDiv = document.getElementById('editDiv');
+ setKeypressHandler(editDiv, printEventData);
+ editDiv.contentEditable = true;
+
+ editFrame.document.body.style.margin = 1;
+ editFrame.document.body.style.padding = 0;
+ editFrame.document.body.id = 'theBody';
+
+ editDiv.style.margin = 1;
+ editDiv.style.padding = 0;
+
+ window.setTimeout(function() {
+ var pre = document.createElement('pre');
+ function write(text) {
+ pre.appendChild(document.createTextNode(text));
+ pre.appendChild(document.createElement('br'));
+ }
+
+ write('frame.contentWindow.document.designMode: ' +
+ editFrame.document.designMode);
+ write('frame.contentWindow.document.body.contentEditable: ' +
+ editFrame.document.body.contentEditable);
+ document.getElementById('editFrameInfo').appendChild(pre);
+
+ pre = document.createElement('pre');
+ write('div.ownerDocument.designMode: ' +
+ editDiv.ownerDocument.designMode);
+ write('div.ownerDocument.body.contentEditable: ' +
+ editDiv.ownerDocument.body.contentEditable);
+ write('div.contentEditable: ' +
+ editDiv.contentEditable);
+ document.getElementById('editDivInfo').appendChild(pre);
+ }, 0);
+};
+
+function isDef(obj, prop) {
+ return typeof obj[prop] != 'undefined';
+}
+
+function printEventData(e) {
+ e = e || window.event;
+
+ function write(id, text, opt_color) {
+ var el = document.getElementById(id);
+ el.innerHTML = '[' + text + ']';
+ el.style.backgroundColor = opt_color || 'white';
+ }
+ write('type', e.type);
+ write('tagName', isDef(e, 'target') ? e.target.tagName : e.srcElement.tagName);
+ write('tagId', isDef(e, 'target') ? e.target.id : e.srcElement.id);
+ write('keyidentifier', isDef(e, 'keyIdentifier') ? e.keyIdentifier : 'n/a');
+ write('keylocation', isDef(e, 'keyLocation') ? e.keyLocation : 'n/a');
+ write('keycode', e.keyCode);
+ write('charcode', e.charCode);
+ write('which', e.which);
+ if (isDef(e, 'isTrusted')) {
+ write('istrusted', e.isTrusted, e.isTrusted ? '#afa' : '#faa');
+ } else {
+ write('istrusted', 'n/a');
+ }
+ write('alt', e.altKey);
+ write('ctrl', e.ctrlKey);
+ write('shift', e.shiftKey);
+ write('meta', e.metaKey);
+
+ var s = "";
+ for (var i in e) {
+ s += i + ": " + e[i] + " ";
+ }
+ //alert(s);
+}
+</script>
+</head>
+<body>
+<div>
+ <div id="butter" style="background-color: #ffa;">
+ </div>
+ <table width="100%">
+ <tr valign="top">
+ <td width="50%">
+ <div>IFRAME</div>
+ <iframe id="editFrame" name="editFrame" src="" height="200px" width="300px" frameborder="0" style="border: 1px solid black;">
+ </iframe>
+ <div id="editFrameInfo">
+ <pre>frame.contentWindow.document.designMode: on<br>frame.contentWindow.document.body.contentEditable: false<br></pre></div>
+ </td>
+ <td>
+ <div>DIV</div>
+ <div id="editDiv" style="border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-style: solid; border-right-style: solid; border-bottom-style: solid; border-left-style: solid; border-top-color: black; border-right-color: black; border-bottom-color: black; border-left-color: black; height: 200px; width: 300px; overflow-x: auto; overflow-y: auto; margin-top: 1px; margin-right: 1px; margin-bottom: 1px; margin-left: 1px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " contenteditable="true">
+ </div>
+ <div id="editDivInfo">
+ <pre>div.ownerDocument.designMode: off<br>div.ownerDocument.body.contentEditable: false<br>div.contentEditable: true<br></pre></div>
+ </td>
+ </tr>
+ </table>
+</div>
+
+<HR>
+<DIV style="margin: 0px;padding:0px;padding-left:10px;font-family:Courier;">
+ <TABLE cellpadding="0" cellspacing="0" width="200px" style="font-size:9pt;">
+ <TBODY>
+ <TR><TD width="110px">type:</TD><TD id="type" width="90px">[]</TD></TR>
+ <TR><TD>tagName:</TD><TD id="tagName">[]</TD></TR>
+ <TR><TD>id:</TD><TD id="tagId">[]</TD></TR>
+ <TR><TD>keyIdentifier:</TD><TD id="keyidentifier">[]</TD></TR>
+ <TR><TD>keyLocation:</TD><TD id="keylocation">[]</TD></TR>
+ <TR><TD>keyCode:</TD><TD id="keycode">[]</TD></TR>
+ <TR><TD>charCode:</TD><TD id="charcode">[]</TD></TR>
+ <TR><TD>which:</TD><TD id="which">[]</TD></TR>
+ <TR><TD>isTrusted:</TD><TD id="istrusted">[]</TD></TR>
+ <TR><TD colspan="2">---------------------</TD></TR>
+ <TR><TD colspan="2">Modifiers</TD></TR>
+ <TR><TD>alt:</TD><TD id="alt">[]</TD></TR>
+ <TR><TD>ctrl:</TD><TD id="ctrl">[]</TD></TR>
+ <TR><TD>shift:</TD><TD id="shift">[]</TD></TR>
+ <TR><TD>meta:</TD><TD id="meta">[]</TD></TR>
+ </TBODY>
+ </TABLE>
+</DIV>
+
+
+
+</BODY></HTML> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/safari/frames_benchmark.html b/node_modules/selenium-webdriver/lib/test/data/safari/frames_benchmark.html
new file mode 100644
index 000000000..8a0592556
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/safari/frames_benchmark.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<script>
+ var start;
+ var dom;
+
+ function run() {
+ if (dom) {
+ dom.parentNode.removeChild(dom);
+ }
+
+ dom = document.createElement('div');
+ var result = document.createElement('div');
+ dom.appendChild(result);
+ document.body.appendChild(dom);
+
+ start = Date.now();
+
+ for (var i = 0; i < 100; i++) {
+ var iframe = document.createElement('iframe');
+ iframe.style.width = '200px';
+ iframe.style.height = '30px';
+ dom.appendChild(iframe);
+ var content = iframe.contentWindow.document.createTextNode('hello, world');
+ iframe.contentWindow.document.body.appendChild(content);
+ iframe.contentWindow.document.body.style.overflow = 'hidden';
+ }
+
+ result.innerHTML = 'Done! ' + (Date.now() - start) + ' ms';
+ }
+</script>
+<input type="button" value="Go!" onclick="run()"> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/screen/screen.css b/node_modules/selenium-webdriver/lib/test/data/screen/screen.css
new file mode 100644
index 000000000..815261850
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/screen/screen.css
@@ -0,0 +1,19 @@
+* {
+ margin: 0;
+}
+html, body, #output {
+ width: 100%;
+ height: 100%;
+}
+table {
+ border: 0px;
+ border-collapse: collapse;
+ border-spacing: 0px;
+ display: table;
+}
+table td {
+ padding: 0px;
+}
+.cell {
+ color: black;
+}
diff --git a/node_modules/selenium-webdriver/lib/test/data/screen/screen.html b/node_modules/selenium-webdriver/lib/test/data/screen/screen.html
new file mode 100644
index 000000000..166665da3
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/screen/screen.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<html><head>
+<title>screen test</title>
+<script type="text/javascript" src="screen.js"></script>
+<link rel="stylesheet" type="text/css" href="screen.css" />
+</head>
+<body>
+<style>
+#content {
+ width: 100%;
+ height: 100%;
+}
+.row {
+ height: 20px;
+ width: 100%;
+}
+.column {
+ height: 20%;
+ width: 20px;
+}
+</style>
+<table id="content">
+<tr class="row row1">
+<td class="column column1 cell" id="cell11">&nbsp;</td>
+<td class="column column2 cell" id="cell12">&nbsp;</td>
+<td class="column column3 cell" id="cell13">&nbsp;</td>
+<td class="column column4 cell" id="cell14">&nbsp;</td>
+<td class="column column5 cell" id="cell15">&nbsp;</td>
+</tr>
+<tr class="row row2">
+<td class="column column1 cell" id="cell21">&nbsp;</td>
+<td class="column column2 cell" id="cell22">&nbsp;</td>
+<td class="column column3 cell" id="cell23">&nbsp;</td>
+<td class="column column4 cell" id="cell24">&nbsp;</td>
+<td class="column column5 cell" id="cell25">&nbsp;</td>
+</tr>
+<tr class="row row3">
+<td class="column column1 cell" id="cell31">&nbsp;</td>
+<td class="column column2 cell" id="cell32">&nbsp;</td>
+<td class="column column3 cell" id="cell33">&nbsp;</td>
+<td class="column column4 cell" id="cell34">&nbsp;</td>
+<td class="column column5 cell" id="cell35">&nbsp;</td>
+</tr>
+<tr class="row row4">
+<td class="column column1 cell" id="cell41">&nbsp;</td>
+<td class="column column2 cell" id="cell42">&nbsp;</td>
+<td class="column column3 cell" id="cell43">&nbsp;</td>
+<td class="column column4 cell" id="cell44">&nbsp;</td>
+<td class="column column5 cell" id="cell45">&nbsp;</td>
+</tr>
+<tr class="row row5">
+<td class="column column1 cell" id="cell51">&nbsp;</td>
+<td class="column column2 cell" id="cell52">&nbsp;</td>
+<td class="column column3 cell" id="cell53">&nbsp;</td>
+<td class="column column4 cell" id="cell54">&nbsp;</td>
+<td class="column column5 cell" id="cell55">&nbsp;</td>
+</tr>
+</table>
+<script>
+var initialColor = 0x0F0F0F;
+var stepColor = 1000;
+var cnt = 1;
+for (var i = 1; i < 6; i++) {
+ for (var j = 1; j < 6; j++) {
+ el = document.getElementById('cell' + i + '' + j);
+ el.style.backgroundColor = toColor(initialColor + (cnt * stepColor));
+ cnt++;
+ }
+}
+</script>
+</body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/screen/screen.js b/node_modules/selenium-webdriver/lib/test/data/screen/screen.js
new file mode 100644
index 000000000..1d1685980
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/screen/screen.js
@@ -0,0 +1,7 @@
+function toColor(num) {
+ num >>>= 0;
+ var b = num & 0xFF,
+ g = (num & 0xFF00) >>> 8,
+ r = (num & 0xFF0000) >>> 16;
+ return "rgb(" + [r, g, b].join(",") + ")";
+} \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/screen/screen_frame1.html b/node_modules/selenium-webdriver/lib/test/data/screen/screen_frame1.html
new file mode 100644
index 000000000..35b03ae13
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/screen/screen_frame1.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<html><head>
+<title>screen frame1</title>
+<script type="text/javascript" src="screen.js"></script>
+<link rel="stylesheet" type="text/css" href="screen.css" />
+</head>
+<body style="width: 100px; height: 100px;">
+<style>
+#content {
+ width: 100%;
+ height: 100%;
+}
+.row {
+ height: 20px;
+ width: 100%;
+}
+.column {
+ height: 20px;
+ width: 20px;
+}
+</style>
+<table id="content">
+<tr class="row row1">
+<td class="column column1 cell" id="cell11">&nbsp;</td>
+<td class="column column2 cell" id="cell12">&nbsp;</td>
+<td class="column column3 cell" id="cell13">&nbsp;</td>
+<td class="column column4 cell" id="cell14">&nbsp;</td>
+<td class="column column5 cell" id="cell15">&nbsp;</td>
+</tr>
+<tr class="row row2">
+<td class="column column1 cell" id="cell21">&nbsp;</td>
+<td class="column column2 cell" id="cell22">&nbsp;</td>
+<td class="column column3 cell" id="cell23">&nbsp;</td>
+<td class="column column4 cell" id="cell24">&nbsp;</td>
+<td class="column column5 cell" id="cell25">&nbsp;</td>
+</tr>
+<tr class="row row3">
+<td class="column column1 cell" id="cell31">&nbsp;</td>
+<td class="column column2 cell" id="cell32">&nbsp;</td>
+<td class="column column3 cell" id="cell33">&nbsp;</td>
+<td class="column column4 cell" id="cell34">&nbsp;</td>
+<td class="column column5 cell" id="cell35">&nbsp;</td>
+</tr>
+<tr class="row row4">
+<td class="column column1 cell" id="cell41">&nbsp;</td>
+<td class="column column2 cell" id="cell42">&nbsp;</td>
+<td class="column column3 cell" id="cell43">&nbsp;</td>
+<td class="column column4 cell" id="cell44">&nbsp;</td>
+<td class="column column5 cell" id="cell45">&nbsp;</td>
+</tr>
+<tr class="row row5">
+<td class="column column1 cell" id="cell51">&nbsp;</td>
+<td class="column column2 cell" id="cell52">&nbsp;</td>
+<td class="column column3 cell" id="cell53">&nbsp;</td>
+<td class="column column4 cell" id="cell54">&nbsp;</td>
+<td class="column column5 cell" id="cell55">&nbsp;</td>
+</tr>
+</table>
+<script>
+var initialColor = 0xDFDFDF;
+var stepColor = 1000;
+var cnt = 1;
+for (var i = 1; i < 6; i++) {
+ for (var j = 1; j < 6; j++) {
+ el = document.getElementById('cell' + i + '' + j);
+ el.style.backgroundColor = toColor(initialColor + (cnt * stepColor));
+ cnt++;
+ }
+}
+</script>
+</body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/screen/screen_frame2.html b/node_modules/selenium-webdriver/lib/test/data/screen/screen_frame2.html
new file mode 100644
index 000000000..e6e17e630
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/screen/screen_frame2.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<html><head>
+<title>screen frame2</title>
+<script type="text/javascript" src="screen.js"></script>
+<link rel="stylesheet" type="text/css" href="screen.css" />
+</head>
+<body style="width: 100px; height: 100px;">
+<style>
+#content {
+ width: 100%;
+ height: 100%;
+}
+.row {
+ height: 20px;
+ width: 100%;
+}
+.column {
+ height: 20px;
+ width: 20px;
+}
+</style>
+<table id="content">
+<tr class="row row1">
+<td class="column column1 cell" id="cell11">&nbsp;</td>
+<td class="column column2 cell" id="cell12">&nbsp;</td>
+<td class="column column3 cell" id="cell13">&nbsp;</td>
+<td class="column column4 cell" id="cell14">&nbsp;</td>
+<td class="column column5 cell" id="cell15">&nbsp;</td>
+</tr>
+<tr class="row row2">
+<td class="column column1 cell" id="cell21">&nbsp;</td>
+<td class="column column2 cell" id="cell22">&nbsp;</td>
+<td class="column column3 cell" id="cell23">&nbsp;</td>
+<td class="column column4 cell" id="cell24">&nbsp;</td>
+<td class="column column5 cell" id="cell25">&nbsp;</td>
+</tr>
+<tr class="row row3">
+<td class="column column1 cell" id="cell31">&nbsp;</td>
+<td class="column column2 cell" id="cell32">&nbsp;</td>
+<td class="column column3 cell" id="cell33">&nbsp;</td>
+<td class="column column4 cell" id="cell34">&nbsp;</td>
+<td class="column column5 cell" id="cell35">&nbsp;</td>
+</tr>
+<tr class="row row4">
+<td class="column column1 cell" id="cell41">&nbsp;</td>
+<td class="column column2 cell" id="cell42">&nbsp;</td>
+<td class="column column3 cell" id="cell43">&nbsp;</td>
+<td class="column column4 cell" id="cell44">&nbsp;</td>
+<td class="column column5 cell" id="cell45">&nbsp;</td>
+</tr>
+<tr class="row row5">
+<td class="column column1 cell" id="cell51">&nbsp;</td>
+<td class="column column2 cell" id="cell52">&nbsp;</td>
+<td class="column column3 cell" id="cell53">&nbsp;</td>
+<td class="column column4 cell" id="cell54">&nbsp;</td>
+<td class="column column5 cell" id="cell55">&nbsp;</td>
+</tr>
+</table>
+<script>
+var initialColor = 0x0F0F0F;
+var stepColor = 1000;
+var cnt = 1;
+for (var i = 1; i < 6; i++) {
+ for (var j = 1; j < 6; j++) {
+ el = document.getElementById('cell' + i + '' + j);
+ el.style.backgroundColor = toColor(initialColor + (cnt * stepColor));
+ cnt++;
+ }
+}
+</script>
+</body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/screen/screen_frames.html b/node_modules/selenium-webdriver/lib/test/data/screen/screen_frames.html
new file mode 100644
index 000000000..46852dcf5
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/screen/screen_frames.html
@@ -0,0 +1,11 @@
+<html>
+<head>
+ <title>screen test</title>
+</head>
+
+<frameset border="0" frameborder="0" framespacing="0" cols="50%, *" rows="*">
+ <frame id="frame1" src="screen_frame1.html"/>
+ <frame id="frame2" src="screen_frame2.html"/>
+</frameset>
+
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/screen/screen_iframes.html b/node_modules/selenium-webdriver/lib/test/data/screen/screen_iframes.html
new file mode 100644
index 000000000..ae3ea1e24
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/screen/screen_iframes.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html><head>
+<title>Screen test</title>
+</head>
+<body>
+<table><tr><td>
+<iframe src="screen_frame1.html" marginheight="0" marginwidth="0" topmargin="0" leftmargin="0" allowtransparency="true" frameborder="0" height="500" scrolling="no" width="500" id="iframe1" name="iframe1-name"></iframe>
+</td><td>
+<iframe src="screen_frame2.html" marginheight="0" marginwidth="0" topmargin="0" leftmargin="0" allowtransparency="true" frameborder="0" height="500" scrolling="no" width="500" id="iframe2" name="iframe2-name"></iframe>
+</td></tr></table>
+</body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/screen/screen_too_long.html b/node_modules/selenium-webdriver/lib/test/data/screen/screen_too_long.html
new file mode 100644
index 000000000..4d00f0270
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/screen/screen_too_long.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<html><head>
+<title>screen test</title>
+<script type="text/javascript" src="screen.js"></script>
+<link rel="stylesheet" type="text/css" href="screen.css" />
+</head>
+<body>
+<style>
+#content {
+ width: 70000px;
+ height: 70000px;
+}
+.column {
+ height: 14000px;
+ width: 14000px;
+}
+</style>
+<table id="content">
+<tr class="row row1">
+<td class="column column1 cell" id="cell11">&nbsp;</td>
+<td class="column column2 cell" id="cell12">&nbsp;</td>
+<td class="column column3 cell" id="cell13">&nbsp;</td>
+<td class="column column4 cell" id="cell14">&nbsp;</td>
+<td class="column column5 cell" id="cell15">&nbsp;</td>
+</tr>
+<tr class="row row2">
+<td class="column column1 cell" id="cell21">&nbsp;</td>
+<td class="column column2 cell" id="cell22">&nbsp;</td>
+<td class="column column3 cell" id="cell23">&nbsp;</td>
+<td class="column column4 cell" id="cell24">&nbsp;</td>
+<td class="column column5 cell" id="cell25">&nbsp;</td>
+</tr>
+<tr class="row row3">
+<td class="column column1 cell" id="cell31">&nbsp;</td>
+<td class="column column2 cell" id="cell32">&nbsp;</td>
+<td class="column column3 cell" id="cell33">&nbsp;</td>
+<td class="column column4 cell" id="cell34">&nbsp;</td>
+<td class="column column5 cell" id="cell35">&nbsp;</td>
+</tr>
+<tr class="row row4">
+<td class="column column1 cell" id="cell41">&nbsp;</td>
+<td class="column column2 cell" id="cell42">&nbsp;</td>
+<td class="column column3 cell" id="cell43">&nbsp;</td>
+<td class="column column4 cell" id="cell44">&nbsp;</td>
+<td class="column column5 cell" id="cell45">&nbsp;</td>
+</tr>
+<tr class="row row5">
+<td class="column column1 cell" id="cell51">&nbsp;</td>
+<td class="column column2 cell" id="cell52">&nbsp;</td>
+<td class="column column3 cell" id="cell53">&nbsp;</td>
+<td class="column column4 cell" id="cell54">&nbsp;</td>
+<td class="column column5 cell" id="cell55">&nbsp;</td>
+</tr>
+</table>
+<script>
+var initialColor = 0x0F0F0F;
+var stepColor = 1000;
+var cnt = 1;
+for (var i = 1; i < 6; i++) {
+ for (var j = 1; j < 6; j++) {
+ el = document.getElementById('cell' + i + '' + j);
+ el.style.backgroundColor = toColor(initialColor + (cnt * stepColor));
+ cnt++;
+ }
+}
+</script>
+</body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/screen/screen_x_long.html b/node_modules/selenium-webdriver/lib/test/data/screen/screen_x_long.html
new file mode 100644
index 000000000..1a6a1002f
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/screen/screen_x_long.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<html><head>
+<title>screen test</title>
+<script type="text/javascript" src="screen.js"></script>
+<link rel="stylesheet" type="text/css" href="screen.css" />
+</head>
+<body>
+<style>
+#content {
+ width: 3000px;
+ height: 100%;
+}
+.row {
+ height: 20px;
+ width: 3000px;
+}
+.column {
+ height: 20%;
+ width: 600px;
+}
+</style>
+<table id="content">
+<tr class="row row1">
+<td class="column column1 cell" id="cell11">&nbsp;</td>
+<td class="column column2 cell" id="cell12">&nbsp;</td>
+<td class="column column3 cell" id="cell13">&nbsp;</td>
+<td class="column column4 cell" id="cell14">&nbsp;</td>
+<td class="column column5 cell" id="cell15">&nbsp;</td>
+</tr>
+<tr class="row row2">
+<td class="column column1 cell" id="cell21">&nbsp;</td>
+<td class="column column2 cell" id="cell22">&nbsp;</td>
+<td class="column column3 cell" id="cell23">&nbsp;</td>
+<td class="column column4 cell" id="cell24">&nbsp;</td>
+<td class="column column5 cell" id="cell25">&nbsp;</td>
+</tr>
+<tr class="row row3">
+<td class="column column1 cell" id="cell31">&nbsp;</td>
+<td class="column column2 cell" id="cell32">&nbsp;</td>
+<td class="column column3 cell" id="cell33">&nbsp;</td>
+<td class="column column4 cell" id="cell34">&nbsp;</td>
+<td class="column column5 cell" id="cell35">&nbsp;</td>
+</tr>
+<tr class="row row4">
+<td class="column column1 cell" id="cell41">&nbsp;</td>
+<td class="column column2 cell" id="cell42">&nbsp;</td>
+<td class="column column3 cell" id="cell43">&nbsp;</td>
+<td class="column column4 cell" id="cell44">&nbsp;</td>
+<td class="column column5 cell" id="cell45">&nbsp;</td>
+</tr>
+<tr class="row row5">
+<td class="column column1 cell" id="cell51">&nbsp;</td>
+<td class="column column2 cell" id="cell52">&nbsp;</td>
+<td class="column column3 cell" id="cell53">&nbsp;</td>
+<td class="column column4 cell" id="cell54">&nbsp;</td>
+<td class="column column5 cell" id="cell55">&nbsp;</td>
+</tr>
+</table>
+<script>
+var initialColor = 0x0F0F0F;
+var stepColor = 1000;
+var cnt = 1;
+for (var i = 1; i < 6; i++) {
+ for (var j = 1; j < 6; j++) {
+ el = document.getElementById('cell' + i + '' + j);
+ el.style.backgroundColor = toColor(initialColor + (cnt * stepColor));
+ cnt++;
+ }
+}
+</script>
+</body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/screen/screen_x_too_long.html b/node_modules/selenium-webdriver/lib/test/data/screen/screen_x_too_long.html
new file mode 100644
index 000000000..3fee005d5
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/screen/screen_x_too_long.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<html><head>
+<title>screen test</title>
+<script type="text/javascript" src="screen.js"></script>
+<link rel="stylesheet" type="text/css" href="screen.css" />
+</head>
+<body>
+<style>
+#content {
+ width: 70000px;
+ height: 100%;
+}
+.row {
+ height: 20px;
+ width: 70000px;
+}
+.column {
+ height: 20%;
+ width: 14000px;
+}
+</style>
+<table id="content">
+<tr class="row row1">
+<td class="column column1 cell" id="cell11">&nbsp;</td>
+<td class="column column2 cell" id="cell12">&nbsp;</td>
+<td class="column column3 cell" id="cell13">&nbsp;</td>
+<td class="column column4 cell" id="cell14">&nbsp;</td>
+<td class="column column5 cell" id="cell15">&nbsp;</td>
+</tr>
+<tr class="row row2">
+<td class="column column1 cell" id="cell21">&nbsp;</td>
+<td class="column column2 cell" id="cell22">&nbsp;</td>
+<td class="column column3 cell" id="cell23">&nbsp;</td>
+<td class="column column4 cell" id="cell24">&nbsp;</td>
+<td class="column column5 cell" id="cell25">&nbsp;</td>
+</tr>
+<tr class="row row3">
+<td class="column column1 cell" id="cell31">&nbsp;</td>
+<td class="column column2 cell" id="cell32">&nbsp;</td>
+<td class="column column3 cell" id="cell33">&nbsp;</td>
+<td class="column column4 cell" id="cell34">&nbsp;</td>
+<td class="column column5 cell" id="cell35">&nbsp;</td>
+</tr>
+<tr class="row row4">
+<td class="column column1 cell" id="cell41">&nbsp;</td>
+<td class="column column2 cell" id="cell42">&nbsp;</td>
+<td class="column column3 cell" id="cell43">&nbsp;</td>
+<td class="column column4 cell" id="cell44">&nbsp;</td>
+<td class="column column5 cell" id="cell45">&nbsp;</td>
+</tr>
+<tr class="row row5">
+<td class="column column1 cell" id="cell51">&nbsp;</td>
+<td class="column column2 cell" id="cell52">&nbsp;</td>
+<td class="column column3 cell" id="cell53">&nbsp;</td>
+<td class="column column4 cell" id="cell54">&nbsp;</td>
+<td class="column column5 cell" id="cell55">&nbsp;</td>
+</tr>
+</table>
+<script>
+var initialColor = 0x0F0F0F;
+var stepColor = 1000;
+var cnt = 1;
+for (var i = 1; i < 6; i++) {
+ for (var j = 1; j < 6; j++) {
+ el = document.getElementById('cell' + i + '' + j);
+ el.style.backgroundColor = toColor(initialColor + (cnt * stepColor));
+ cnt++;
+ }
+}
+</script>
+</body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/screen/screen_y_long.html b/node_modules/selenium-webdriver/lib/test/data/screen/screen_y_long.html
new file mode 100644
index 000000000..31733e073
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/screen/screen_y_long.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<html><head>
+<title>screen test</title>
+<script type="text/javascript" src="screen.js"></script>
+<link rel="stylesheet" type="text/css" href="screen.css" />
+</head>
+<body>
+<style>
+#content {
+ width: 100%;
+ height: 3000px;
+}
+.row {
+ height: 600px;
+ width: 100%;
+}
+.column {
+ height: 20%;
+ width: 20px;
+}
+</style>
+<table id="content">
+<tr class="row row1">
+<td class="column column1 cell" id="cell11">&nbsp;</td>
+<td class="column column2 cell" id="cell12">&nbsp;</td>
+<td class="column column3 cell" id="cell13">&nbsp;</td>
+<td class="column column4 cell" id="cell14">&nbsp;</td>
+<td class="column column5 cell" id="cell15">&nbsp;</td>
+</tr>
+<tr class="row row2">
+<td class="column column1 cell" id="cell21">&nbsp;</td>
+<td class="column column2 cell" id="cell22">&nbsp;</td>
+<td class="column column3 cell" id="cell23">&nbsp;</td>
+<td class="column column4 cell" id="cell24">&nbsp;</td>
+<td class="column column5 cell" id="cell25">&nbsp;</td>
+</tr>
+<tr class="row row3">
+<td class="column column1 cell" id="cell31">&nbsp;</td>
+<td class="column column2 cell" id="cell32">&nbsp;</td>
+<td class="column column3 cell" id="cell33">&nbsp;</td>
+<td class="column column4 cell" id="cell34">&nbsp;</td>
+<td class="column column5 cell" id="cell35">&nbsp;</td>
+</tr>
+<tr class="row row4">
+<td class="column column1 cell" id="cell41">&nbsp;</td>
+<td class="column column2 cell" id="cell42">&nbsp;</td>
+<td class="column column3 cell" id="cell43">&nbsp;</td>
+<td class="column column4 cell" id="cell44">&nbsp;</td>
+<td class="column column5 cell" id="cell45">&nbsp;</td>
+</tr>
+<tr class="row row5">
+<td class="column column1 cell" id="cell51">&nbsp;</td>
+<td class="column column2 cell" id="cell52">&nbsp;</td>
+<td class="column column3 cell" id="cell53">&nbsp;</td>
+<td class="column column4 cell" id="cell54">&nbsp;</td>
+<td class="column column5 cell" id="cell55">&nbsp;</td>
+</tr>
+</table>
+<script>
+var initialColor = 0x0F0F0F;
+var stepColor = 1000;
+var cnt = 1;
+for (var i = 1; i < 6; i++) {
+ for (var j = 1; j < 6; j++) {
+ el = document.getElementById('cell' + i + '' + j);
+ el.style.backgroundColor = toColor(initialColor + (cnt * stepColor));
+ cnt++;
+ }
+}
+</script>
+</body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/screen/screen_y_too_long.html b/node_modules/selenium-webdriver/lib/test/data/screen/screen_y_too_long.html
new file mode 100644
index 000000000..dbef9361a
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/screen/screen_y_too_long.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<html><head>
+<title>screen test</title>
+<script type="text/javascript" src="screen.js"></script>
+<link rel="stylesheet" type="text/css" href="screen.css" />
+</head>
+<body>
+<style>
+#content {
+ width: 100%;
+ height: 70000px;
+}
+.row {
+ height: 14000px;
+ width: 100%;
+}
+.column {
+ height: 20%;
+ width: 20px;
+}
+</style>
+<table id="content">
+<tr class="row row1">
+<td class="column column1 cell" id="cell11">&nbsp;</td>
+<td class="column column2 cell" id="cell12">&nbsp;</td>
+<td class="column column3 cell" id="cell13">&nbsp;</td>
+<td class="column column4 cell" id="cell14">&nbsp;</td>
+<td class="column column5 cell" id="cell15">&nbsp;</td>
+</tr>
+<tr class="row row2">
+<td class="column column1 cell" id="cell21">&nbsp;</td>
+<td class="column column2 cell" id="cell22">&nbsp;</td>
+<td class="column column3 cell" id="cell23">&nbsp;</td>
+<td class="column column4 cell" id="cell24">&nbsp;</td>
+<td class="column column5 cell" id="cell25">&nbsp;</td>
+</tr>
+<tr class="row row3">
+<td class="column column1 cell" id="cell31">&nbsp;</td>
+<td class="column column2 cell" id="cell32">&nbsp;</td>
+<td class="column column3 cell" id="cell33">&nbsp;</td>
+<td class="column column4 cell" id="cell34">&nbsp;</td>
+<td class="column column5 cell" id="cell35">&nbsp;</td>
+</tr>
+<tr class="row row4">
+<td class="column column1 cell" id="cell41">&nbsp;</td>
+<td class="column column2 cell" id="cell42">&nbsp;</td>
+<td class="column column3 cell" id="cell43">&nbsp;</td>
+<td class="column column4 cell" id="cell44">&nbsp;</td>
+<td class="column column5 cell" id="cell45">&nbsp;</td>
+</tr>
+<tr class="row row5">
+<td class="column column1 cell" id="cell51">&nbsp;</td>
+<td class="column column2 cell" id="cell52">&nbsp;</td>
+<td class="column column3 cell" id="cell53">&nbsp;</td>
+<td class="column column4 cell" id="cell54">&nbsp;</td>
+<td class="column column5 cell" id="cell55">&nbsp;</td>
+</tr>
+</table>
+<script>
+var initialColor = 0x0F0F0F;
+var stepColor = 1000;
+var cnt = 1;
+for (var i = 1; i < 6; i++) {
+ for (var j = 1; j < 6; j++) {
+ el = document.getElementById('cell' + i + '' + j);
+ el.style.backgroundColor = toColor(initialColor + (cnt * stepColor));
+ cnt++;
+ }
+}
+</script>
+</body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/scroll.html b/node_modules/selenium-webdriver/lib/test/data/scroll.html
new file mode 100644
index 000000000..cd5214f15
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/scroll.html
@@ -0,0 +1,27 @@
+<html>
+<head>
+</head>
+<body>
+<script>
+function dump(event) {
+ var elt = event.target || event.srcElement;
+ document.getElementById('clicked').innerHTML = elt.innerHTML;
+}
+</script>
+<div style='height: 150px'></div>
+<ul style='overflow: scroll; width: 150px; height: 80px; background-color: yellow' onclick="dump(event)">
+<li id='line1'>line1</li>
+<li id='line2'>line2</li>
+<li id='line3'>line3</li>
+<li id='line4'>line4</li>
+<li id='line5'>line5</li>
+<li id='line6'>line6</li>
+<li id='line7'>line7</li>
+<li id='line8'>line8</li>
+<li id='line9'>line9</li>
+</ul>
+<div>
+Clicked: <span id='clicked'></span>
+</div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/scroll2.html b/node_modules/selenium-webdriver/lib/test/data/scroll2.html
new file mode 100644
index 000000000..0ea66d378
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/scroll2.html
@@ -0,0 +1,21 @@
+<html>
+<head>
+</head>
+<body>
+<ul style='overflow: scroll; height: 100px;'>
+<li></li>
+<li></li>
+<li id="desired">Text</li>
+<li></li>
+<li></li>
+<li></li>
+<li></li>
+<li></li>
+<li></li>
+<li></li>
+<li></li>
+<li></li>
+<li></li>
+</ul>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/scroll3.html b/node_modules/selenium-webdriver/lib/test/data/scroll3.html
new file mode 100644
index 000000000..1aa170929
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/scroll3.html
@@ -0,0 +1,8 @@
+<html>
+<body>
+<br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br />
+<button id="button1">Button1</button>
+<br /><br /><br /><br />
+
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<button id="button2">Button2</button>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+<br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /><br /></br /><br /> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/scroll4.html b/node_modules/selenium-webdriver/lib/test/data/scroll4.html
new file mode 100644
index 000000000..652a778eb
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/scroll4.html
@@ -0,0 +1,7 @@
+<html>
+<body>
+ <br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
+ <input type="radio" id="radio" />
+ <br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/scroll5.html b/node_modules/selenium-webdriver/lib/test/data/scroll5.html
new file mode 100644
index 000000000..b345a8c3f
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/scroll5.html
@@ -0,0 +1,18 @@
+<html>
+<head>
+</head>
+<body>
+<script>
+function dump(text) {
+ document.getElementById('clicked').innerHTML = text;
+}
+</script>
+<div style='overflow: scroll; width: 150px; height: 200px; background-color: yellow' id="outer">
+ <div style="width: 150px; height: 5000px; background-color: red;" onclick="dump('clicked')" id="inner">
+ </div>
+</div>
+<div>
+Clicked: <span id='clicked'></span>
+</div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/frame_with_height_above_200.html b/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/frame_with_height_above_200.html
new file mode 100644
index 000000000..3eb3bf47d
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/frame_with_height_above_200.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Child frame</title>
+</head>
+<body>
+ <h1>This is a scrolling frame test</h1>
+ <div>
+ <table>
+ <tr height="50px">
+ <td>First row</td>
+ </tr>
+ <tr height="50px">
+ <td>Second row</td>
+ </tr>
+ <tr height="50px">
+ <td>Third row</td>
+ </tr>
+ <tr height="50px">
+ <td>Fourth row</td>
+ </tr>
+ </table>
+ </div>
+ <input type='checkbox' name='scroll_checkbox' />
+</body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/frame_with_height_above_2000.html b/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/frame_with_height_above_2000.html
new file mode 100644
index 000000000..61ffe85da
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/frame_with_height_above_2000.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Child frame</title>
+</head>
+<body>
+ <h1>This is a tall frame test</h1>
+ <div>
+ <table>
+ <tr height="500px">
+ <td>First row</td>
+ </tr>
+ <tr height="500px">
+ <td>Second row</td>
+ </tr>
+ <tr height="500px">
+ <td>Third row</td>
+ </tr>
+ <tr height="500px">
+ <td>Fourth row</td>
+ </tr>
+ </table>
+ </div>
+ <input type='checkbox' name='checkbox' />
+</body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/frame_with_nested_scrolling_frame.html b/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/frame_with_nested_scrolling_frame.html
new file mode 100644
index 000000000..153013869
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/frame_with_nested_scrolling_frame.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Welcome Page</title>
+</head>
+<body>
+ <div>
+ <iframe name="nested_scrolling_frame" src="frame_with_height_above_200.html" width="800" height="200" ></iframe>
+ </div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/frame_with_nested_scrolling_frame_out_of_view.html b/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/frame_with_nested_scrolling_frame_out_of_view.html
new file mode 100644
index 000000000..5781aeb9f
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/frame_with_nested_scrolling_frame_out_of_view.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Welcome Page</title>
+</head>
+<body>
+ <div style="height: 5000px;">Placeholder</div>
+ <div>
+ <iframe name="nested_scrolling_frame" src="frame_with_height_above_200.html" width="800" height="200" ></iframe>
+ </div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/frame_with_small_height.html b/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/frame_with_small_height.html
new file mode 100644
index 000000000..047de0f18
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/frame_with_small_height.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Child frame</title>
+</head>
+<body>
+ <h1>This is a non-scrolling frame test</h1>
+ <input type='checkbox' name='checkbox' />
+</body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/page_with_double_overflow_auto.html b/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/page_with_double_overflow_auto.html
new file mode 100644
index 000000000..01b7c3058
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/page_with_double_overflow_auto.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Page with overflow: auto</title>
+ <style type="text/css">
+ html, body {
+ width: 100%;
+ height: 100%;
+ overflow: auto;
+ }
+ </style>
+</head>
+<body>
+ <div style="height: 5000px;">Placeholder</div>
+ <div>
+ <a id="link" href="target_page.html">Click me!</a>
+ </div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/page_with_frame_out_of_view.html b/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/page_with_frame_out_of_view.html
new file mode 100644
index 000000000..c536e41d7
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/page_with_frame_out_of_view.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Welcome Page</title>
+</head>
+<body>
+ <div style="height: 5000px;">Placeholder</div>
+ <div>
+ <iframe name="frame" src="frame_with_small_height.html" width="800" height="200" ></iframe>
+ </div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/page_with_nested_scrolling_frames.html b/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/page_with_nested_scrolling_frames.html
new file mode 100644
index 000000000..e5b76022b
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/page_with_nested_scrolling_frames.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Welcome Page</title>
+</head>
+<body>
+ <div>
+ <iframe name="scrolling_frame" src="frame_with_nested_scrolling_frame.html" width="800" height="150" ></iframe>
+ </div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/page_with_nested_scrolling_frames_out_of_view.html b/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/page_with_nested_scrolling_frames_out_of_view.html
new file mode 100644
index 000000000..f79f7c848
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/page_with_nested_scrolling_frames_out_of_view.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Welcome Page</title>
+</head>
+<body>
+ <div style="height: 5000px;">Placeholder</div>
+ <div>
+ <iframe name="scrolling_frame" src="frame_with_nested_scrolling_frame_out_of_view.html" width="800" height="150" ></iframe>
+ </div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/page_with_non_scrolling_frame.html b/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/page_with_non_scrolling_frame.html
new file mode 100644
index 000000000..0a493fa5d
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/page_with_non_scrolling_frame.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Welcome Page</title>
+</head>
+<body>
+ <div>
+ <iframe name="scrolling_frame" src="frame_with_height_above_200.html" width="800" height="200" scrolling="no"></iframe>
+ </div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/page_with_scrolling_frame.html b/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/page_with_scrolling_frame.html
new file mode 100644
index 000000000..cb5d53a44
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/page_with_scrolling_frame.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Welcome Page</title>
+</head>
+<body>
+ <div>
+ <iframe name="scrolling_frame" src="frame_with_height_above_200.html" width="800" height="200" ></iframe>
+ </div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/page_with_scrolling_frame_out_of_view.html b/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/page_with_scrolling_frame_out_of_view.html
new file mode 100644
index 000000000..5df1115c2
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/page_with_scrolling_frame_out_of_view.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Welcome Page</title>
+</head>
+<body>
+ <div style="height: 5000px;">Placeholder</div>
+ <div>
+ <iframe name="scrolling_frame" src="frame_with_height_above_200.html" width="800" height="200" ></iframe>
+ </div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/page_with_tall_frame.html b/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/page_with_tall_frame.html
new file mode 100644
index 000000000..b7cfaf5a3
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/page_with_tall_frame.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Welcome Page</title>
+</head>
+<body>
+ <div>
+ <iframe name="tall_frame" src="frame_with_height_above_2000.html" width="800" height="2500" ></iframe>
+ </div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/page_with_y_overflow_auto.html b/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/page_with_y_overflow_auto.html
new file mode 100644
index 000000000..b5716e731
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/page_with_y_overflow_auto.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Page with overflow: auto</title>
+</head>
+<body>
+ <div style="width:100%; height: 500px; overflow: hidden; overflow-y: auto;">
+ <div style="height: 5000px; width: 5000px;">Placeholder</div>
+ <div>
+ <a id="link" href="target_page.html">Click me!</a>
+ </div>
+ </div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/target_page.html b/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/target_page.html
new file mode 100644
index 000000000..0457a822f
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/scrolling_tests/target_page.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Clicked Successfully!</title>
+</head>
+<body>
+<h1>Clicked Successfully!</h1>
+</body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/selectPage.html b/node_modules/selenium-webdriver/lib/test/data/selectPage.html
new file mode 100644
index 000000000..4028414c1
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/selectPage.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+<title>Multiple Selection test page</title>
+</head>
+<body>
+ <select id="selectWithoutMultiple">
+ <option value="one">one</option>
+ <option value="two" />two</option>
+ </select>
+
+ <select id="selectWithMultipleEqualsMultiple" multiple="multiple">
+ <option selected="selected" label="emmental">Emmental</option>
+ <option label="roquefort">Roquefort</option>
+ <option label="parmigiano">Parmigiano</option>
+ <option label="cheddar">Cheddar</option>
+ </select>
+
+ <select id="selectWithEmptyStringMultiple" multiple="">
+ <option value="one">one</option>
+ <option value="two">two</option>
+ </select>
+
+ <select id="selectWithMultipleWithoutValue" multiple>
+ <option value="one">one</option>
+ <option value="two">two</option>
+ </select>
+
+ <select id="selectWithRandomMultipleValue" multiple="somethingElse">
+ <option value="one">one</option>
+ <option value="two">two</option>
+ </select>
+
+ <select>
+ <optgroup label="Group">
+ <option value="one">one</option>
+ <option id="two-in-group" value="two">two</option>
+ </optgroup>
+ </select>
+
+ <select id="selectWithMultipleLongList" multiple>
+ <option>one</option>
+ <option>two</option>
+ <option>three</option>
+ <option>four</option>
+ <option>five</option>
+ <option>six</option>
+ </select>
+
+ <select id="narrow" style="width: 50px;">
+ <option value="evalon">LaClare Farms Evalon with Cummin</option>
+ <option value="savourine">Yarra Valley Vintage Savourine</option>
+ <option value="cheddar">Avonlea Clothbound Cheddar</option>
+ </select>
+
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/selectableItems.html b/node_modules/selenium-webdriver/lib/test/data/selectableItems.html
new file mode 100644
index 000000000..190b2ada7
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/selectableItems.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8" />
+ <title>jQuery UI Selectable - Default functionality</title>
+ <link type="text/css" href="css/ui-lightness/jquery-ui-1.8.1.custom.css" rel="stylesheet" />
+ <script type="text/javascript" src="js/jquery-1.4.4.min.js"></script>
+ <script type="text/javascript" src="js/jquery-ui-1.8.10.custom.min.js"></script>
+
+ <style type="text/css">
+ #feedback { font-size: 1.4em; }
+ #selectable .ui-selecting { background: #FECA40; }
+ #selectable .ui-selected { background: #F39814; color: white; }
+ #selectable { list-style-type: none; margin: 0; padding: 0; width: 60%; }
+ #selectable li { margin: 3px; padding: 0.4em; font-size: 1.4em; height: 18px; }
+ </style>
+ <script type="text/javascript">
+ $(function() {
+ $("#selectable").selectable({
+ stop: function() {
+ var infodiv = $("#infodiv").empty();
+ $(".ui-selected", this).each(function() {
+ var that_id = this.id;
+ infodiv.append(' #' + that_id);
+ });
+ }
+ });
+ $("button").button();
+ $("button").click(function() {
+ var list_disabled = $("#selectable").selectable("option", "disabled");
+ $("#infodiv").find('p').html('Disabled: ' + !list_disabled);
+ $("#selectable").selectable("option", "disabled", !list_disabled);
+ });
+ });
+ </script>
+</head>
+<body>
+<div class="demo">
+
+<ol id="selectable">
+ <li id="item1" class="ui-widget-content">Item 1</li>
+ <li id="item2" class="ui-widget-content">Item 2</li>
+ <li id="item3" class="ui-widget-content">Item 3</li>
+ <li id="item4" class="ui-widget-content">Item 4</li>
+ <li id="item5" class="ui-widget-content">Item 5</li>
+ <li id="item6" class="ui-widget-content">Item 6</li>
+ <li id="item7" class="ui-widget-content">Item 7</li>
+</ol>
+
+</div><!-- End demo -->
+
+<div class="demo-description">
+
+ <p>Enable a DOM element (or group of elements) to be selectable. Draw a box with your cursor to select items. Hold down the Ctrl key to make multiple non-adjacent selections. </p>
+
+ <button type="button" id="somebutton">Click to lock</button>
+
+ <div id="infodiv">
+ <p>no info</p>
+ </div>
+
+
+</div><!-- End demo-description -->
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/sessionCookie.html b/node_modules/selenium-webdriver/lib/test/data/sessionCookie.html
new file mode 100644
index 000000000..0ada24ed3
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/sessionCookie.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title></title>
+ <script type="text/javascript">
+ function setcolor(what){
+ document.body.style.backgroundColor=what
+ document.cookie="bgcolor="+what
+ }
+
+ function popuponclick() {
+ my_window = window.open("sessionCookieDest.html", "cookiedestwindow", "status=1,width=350,height=150");
+ }
+ </script>
+</head>
+<body>
+ <input id="setcolorbutton" type="button" value="Set Background Color" onclick="setcolor('#80FFFF')"/>
+ <input id="openwindowbutton" type="button" value="Open New Window" onclick="popuponclick()"/>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/sessionCookieDest.html b/node_modules/selenium-webdriver/lib/test/data/sessionCookieDest.html
new file mode 100644
index 000000000..f5e2ef2e4
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/sessionCookieDest.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>Session cookie destination</title>
+ <script type="text/javascript">
+ //Get cookie routine by Shelley Powers
+ function get_cookie(Name) {
+ var search = Name + "=";
+ var returnvalue = "";
+ if (document.cookie.length > 0) {
+ offset = document.cookie.indexOf(search);
+ // if cookie exists
+ if (offset != -1) {
+ offset += search.length;
+ // set index of beginning of value
+ end = document.cookie.indexOf(";", offset);
+ // set index of end of cookie value
+ if (end == -1) end = document.cookie.length;
+ returnvalue=unescape(document.cookie.substring(offset, end))
+ }
+ }
+ return returnvalue;
+ }
+
+ function setcolor(){
+ document.body.style.backgroundColor=get_cookie("bgcolor")
+ }
+ </script>
+</head>
+<body onload="setcolor()">
+This is the cookie destination page.
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/simple.xml b/node_modules/selenium-webdriver/lib/test/data/simple.xml
new file mode 100644
index 000000000..01f4c87cc
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/simple.xml
@@ -0,0 +1,5 @@
+<xml>
+ <foo>
+ <bar>baz</bar>
+ </foo>
+</xml> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/simpleTest.html b/node_modules/selenium-webdriver/lib/test/data/simpleTest.html
new file mode 100644
index 000000000..38210b9df
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/simpleTest.html
@@ -0,0 +1,98 @@
+<html>
+<head>
+ <title>Hello WebDriver</title>
+</head>
+<body style="" name="body">
+<h1>Heading</h1>
+
+<p id="oneline">A single line of text</p>
+<p id="hiddenline" style="visibility: hidden">A hidden line of text</p>
+
+<div id="multiline">
+ <p>A div containing</p>
+ More than one line of text<br/>
+
+ <div>and block level elements</div>
+</div>
+
+<span id="span">An inline element</span>
+
+<p id="lotsofspaces">This line has lots
+
+ of spaces.
+</p>
+
+<p id="nbsp">This line has a&nbsp;non-breaking space</p>
+
+<p id="nbspandspaces">This line has a &nbsp; non-breaking space and spaces</p>
+
+<p id="multilinenbsp">These lines &nbsp<br />&nbsp have leading and trailing NBSPs&nbsp;&nbsp;</p>
+
+<p id="inline">This <span id="inlinespan"> line has <em>text</em> </span> within elements that are meant to be displayed
+ <!-- not as a block but --> inline</p>
+
+<div id="div-with-pre">
+<p>before pre</p>
+<pre id="preformatted"> This section has a preformatted
+ text block
+ split in four lines
+ </pre>
+<p>after pre</p>
+</div>
+
+<div id="twoblocks"><p>Some text</p><p>Some more text</p></div>
+
+<div id="nestedblocks">Cheese <div><p>Some text</p><div><p>Some more text</p><p>and also</p></div></div>Brie</div>
+
+<div id="collapsingtext"><span></span><div>Hello, world</div><span></span></div>
+
+<div id="withDocumentWrite">
+<script>
+document.write("with document.write");
+document.write(" and with document.write again");
+</script>
+</div>
+
+<form action="resultPage.html">
+ <p>
+ <input type="checkbox" id="checkbox1">
+ <label id="label1" for="checkbox1">foo<br />bar</label>
+ </p>
+</form>
+
+<div id="links">
+<a href=""> link with leading space</a>
+<a href="" id="linkWithTrailingSpace">link with trailing space
+</a>
+<a href=""><b>link with formatting tags</b></a>
+<a href="" id="quote">link with " (double quote)</a>
+<a href="" id="squote">link with ' (single quote)</a>
+<a href="" id="backslash">link with \ (backslash)</a>
+</div>
+
+<div style="text-indent:80%"><a href="resultPage.html" id="multilinelink">this link should break<br />on multiple lines</a></div>
+
+<div name="someDiv">Top level</div>
+<div id="containsSomeDiv">
+ <div name="someDiv">Nested</div>
+</div>
+
+<table id="wrappingtext">
+ <tbody>
+ <tr><td style="width: 10px;"><span>beforeSpace</span><span> </span><span>afterSpace</span></td></tr>
+ </tbody>
+</table>
+
+<!-- Here comes an invalid <img> tag which has no src attribute ... -->
+<img id="invalidImgTag" />
+
+<img id="validImgTag" src="icon.gif" />
+<a id="validAnchorTag" href="icon.gif">a link to an icon</a>
+
+<span id="simpleJsonText">{a="b", c=1, d=true}</span>
+<span id="complexJsonText">{a="\\b\\\"'\'"}</span>
+
+<span id="trimmedSpace">            ​‌‍  ⁠ test            ​‌‍  ⁠ </span>
+
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/slowLoadingAlert.html b/node_modules/selenium-webdriver/lib/test/data/slowLoadingAlert.html
new file mode 100644
index 000000000..a6216e375
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/slowLoadingAlert.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>slowLoadingAlert</title>
+</head>
+
+<body onload="window.setTimeout(function() { window.alert('Look, an alert!'); }, 200);">
+</body>
+
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/slowLoadingResourcePage.html b/node_modules/selenium-webdriver/lib/test/data/slowLoadingResourcePage.html
new file mode 100644
index 000000000..02796c34a
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/slowLoadingResourcePage.html
@@ -0,0 +1,12 @@
+<html>
+<head>
+ <title>This page loads something slowly</title>
+</head>
+<body>
+ <p id="peas">Simulate the situation where a web-bug or analytics script takes waaay
+ too long to respond. Normally these things are loaded in an iframe, which is
+ what we're doing here.</p>
+
+ <img src="sleep?time=7"></img>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/slow_loading_iframes.html b/node_modules/selenium-webdriver/lib/test/data/slow_loading_iframes.html
new file mode 100644
index 000000000..d007248e2
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/slow_loading_iframes.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+ <head>
+ <title>Page with slow loading iFrames</title>
+ </head>
+ <body>
+ <!-- Takes 3 seconds to load -->
+ <iframe src="/common/sleep?time=3"></iframe>
+
+ <!-- Important: do not add a src attribute! -->
+ <iframe id="noSrc"></iframe>
+ </body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/styledPage.html b/node_modules/selenium-webdriver/lib/test/data/styledPage.html
new file mode 100644
index 000000000..30810f091
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/styledPage.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Styled Page</title>
+ <script>
+ function log(message) {
+ document.getElementById('log').innerHTML = "<p>" + message + "</p>"
+ }
+ </script>
+
+</head>
+<body>
+<div style="height: 200px"></div>
+
+<form action="#" method="get" onsubmit="return false;">
+ <input name="searchBox">
+ <input type="submit" name="btn" style="padding-top: 4px; padding-bottom: 4px" value="Search" onclick="log('click');">
+</form>
+
+<div id="log"></div>
+
+<div style="height: 4000px">Content</div>
+</body>
+</html>
+
+
+
+
diff --git a/node_modules/selenium-webdriver/lib/test/data/svgPiechart.xhtml b/node_modules/selenium-webdriver/lib/test/data/svgPiechart.xhtml
new file mode 100644
index 000000000..bf060fded
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/svgPiechart.xhtml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+<head>
+ <title>Pie Chart Test</title>
+</head>
+<body>
+<script type="text/javascript">
+ function markClick(input) {
+ var element = document.getElementById('result');
+ element.innerHTML = input;
+ }
+</script>
+ <div id="someText">
+ Some text for the chart.
+ </div>
+ <div id="result">Nothing.</div>
+ <div id="chart_container" style="width: 400px; height: 220px; border: 1px solid #808080;">
+<svg:svg id="chart_container" height="220" width="400">
+ <svg:defs id="defs"></svg:defs>
+ <svg:g>
+ <svg:rect fill="white" height="220" width="400" y="0" x="0"></svg:rect>
+ <svg:text fill="black" font-weight="bold" font-size="10" font-family="Arial" y="16" x="200" text-anchor="middle">Test Chart</svg:text>
+ <svg:rect fill="white" height="83" width="97" y="77" x="296"></svg:rect>
+ <svg:rect fill="red" stroke="none" height="12" width="12" y="84" x="303"></svg:rect>
+ <svg:text fill="black" font-size="12" font-family="Arial" y="94" x="318" onclick="markClick('text_apple')">Apple</svg:text>
+ <svg:rect fill="green" stroke="none" height="12" width="12" y="103" x="303"></svg:rect>
+ <svg:text fill="black" font-size="12" font-family="Arial" y="113" x="318">Orange</svg:text>
+ <svg:rect fill="#808080" stroke="none" height="12" width="12" y="122" x="303"></svg:rect>
+ <svg:text fill="black" font-size="12" font-family="Arial" y="132" x="318">Banana</svg:text>
+ <svg:rect fill="green" stroke="none" height="12" width="12" y="141" x="303"></svg:rect>
+ <svg:text fill="black" font-size="12" font-family="Arial" y="151" x="318">Orange</svg:text>
+ <svg:g onclick="markClick('slice_red')"><svg:path fill="red" stroke="none" d="M148,119L148,43A76,76,0,0,1,198,61.77Z"></svg:path></svg:g>
+ <svg:g onclick="markClick('slice_green')"><svg:path fill="green" stroke="none" d="M148,119L198,61.77A76,76,0,0,1,202.93,171.52Z"></svg:path></svg:g>
+ <svg:g><svg:path fill="#808080" stroke="none" d="M148,119L202.93,171.52A76,76,0,0,1,72.08,122.41Z"></svg:path></svg:g>
+ <svg:g><svg:path fill="green" stroke="none" d="M148,119L72.08,122.41A76,76,0,0,1,148,43Z"></svg:path></svg:g>
+ </svg:g>
+</svg:svg>
+<svg:svg width="400px" height="120px">
+ <svg:desc>Example RotateScale - Rotate and scale transforms</svg:desc>
+ <svg:g fill="none" stroke="black" stroke-width="3" >
+ <!-- Draw the axes of the original coordinate system -->
+ <svg:line x1="0" y1="1.5" x2="400" y2="1.5" />
+ <svg:line x1="1.5" y1="0" x2="1.5" y2="120" />
+ </svg:g>
+ <!-- Establish a new coordinate system whose origin is at (50,30)
+ in the initial coord. system and which is rotated by 30 degrees. -->
+ <svg:g transform="translate(50,30)">
+ <svg:g transform="rotate(30)" id="rotate">
+ <svg:g fill="none" stroke="red" stroke-width="3" >
+ <svg:line x1="0" y1="0" x2="50" y2="0" />
+ <svg:line x1="0" y1="0" x2="0" y2="50" />
+ </svg:g>
+ <svg:text x="0" y="0" font-size="20" font-family="Verdana" fill="blue" >
+ ABC (rotate)
+ </svg:text>
+ </svg:g>
+ </svg:g>
+ <!-- Establish a new coordinate system whose origin is at (200,40)
+ in the initial coord. system and which is scaled by 1.5. -->
+ <svg:g transform="translate(200,40)">
+ <svg:g transform="scale(1.5)">
+ <svg:g fill="none" stroke="red" stroke-width="3" >
+ <svg:line x1="0" y1="0" x2="50" y2="0" />
+ <svg:line x1="0" y1="0" x2="0" y2="50" />
+ </svg:g>
+ <svg:text x="0" y="0" font-size="20" font-family="Verdana" fill="blue" >
+ ABC (scale)
+ </svg:text>
+ </svg:g>
+ </svg:g>
+</svg:svg>
+ <div style="position: absolute; white-space: nowrap; top: 230px; left: 410px; font-family: Arial; font-size: 12px; display: none; ">WOrange</div>
+ </div>
+ <div style="display: none; position: absolute; top: 230px; left: 410px; white-space: nowrap; font-family: Arial; font-size: 12px;">WOrange</div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/svgTest.svg b/node_modules/selenium-webdriver/lib/test/data/svgTest.svg
new file mode 100644
index 000000000..c6cc28390
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/svgTest.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" baseProfile="full" width="100px" height="100px" viewBox="0 0 100 100">
+ <rect id="rect" width="50" height="50" fill="blue" onclick="document.getElementById('rect').setAttribute('fill', 'green')" />
+</svg>
diff --git a/node_modules/selenium-webdriver/lib/test/data/tables.html b/node_modules/selenium-webdriver/lib/test/data/tables.html
new file mode 100644
index 000000000..a2bc957b8
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/tables.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+ <title>Here be tables</title>
+</head>
+<body>
+
+<table id="base">
+ <tr>
+ <td>Hello</td>
+ <td>World</td>
+ <td>(Cheese!)</td>
+ </tr>
+</table>
+
+<table>
+ <tr id="hidden_text">
+ <td>some text
+ <div style="display: none;">some more text</div>
+ </td>
+ </tr>
+</table>
+
+<table>
+ <tr>
+ <th colspan="3" id="th1">Heading</th>
+ </tr>
+ <tr>
+ <td id="td1">Data 1</td>
+ <td colspan="2" id="td2">Data 2</td>
+ </tr>
+</table>
+
+</body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/tinymce.html b/node_modules/selenium-webdriver/lib/test/data/tinymce.html
new file mode 100644
index 000000000..067b66cd8
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/tinymce.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>TinyMCE</title>
+ <script src="js/tinymce.min.js"></script>
+</head>
+<body onload="tinymce.init({selector:'textarea'})">
+<textarea>Initial content</textarea>
+</body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/transformable.xml b/node_modules/selenium-webdriver/lib/test/data/transformable.xml
new file mode 100644
index 000000000..0b7e7fd58
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/transformable.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?xml-stylesheet type="text/xsl" href="transformable.xsl"?>
+<!DOCTYPE html [ <!ENTITY nbsp "&#160;"> ]>
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:html="http://www.w3.org/1999/xhtml" xmlns:ui="http://dummy.com/mynamespace">
+ <body id='body'>
+ <p>Click the button.</p>
+ <ui:app url="xhtmlTest.html">
+ <ui:someButton id="x" name="y">Go to another page</ui:someButton>
+ </ui:app>
+ </body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/transformable.xsl b/node_modules/selenium-webdriver/lib/test/data/transformable.xsl
new file mode 100644
index 000000000..53db9fded
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/transformable.xsl
@@ -0,0 +1,37 @@
+<?xml version="1.0"?>
+
+<xsl:stylesheet
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:ui="http://dummy.com/mynamespace"
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ version="1.0">
+
+ <xsl:output method="html"
+ doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
+ doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" indent="yes"/>
+
+ <xsl:template match="ui:app">
+ <form>
+ <xsl:attribute name="action">
+ <xsl:value-of select="@url" />
+ </xsl:attribute>
+ <xsl:apply-templates select="./*" />
+ </form>
+ </xsl:template>
+
+ <xsl:template match="ui:someButton">
+ <input type="submit">
+ <xsl:attribute name="id">
+ <xsl:value-of select="@id" />
+ </xsl:attribute>
+ <xsl:attribute name="name">
+ <xsl:value-of select="@name" />
+ </xsl:attribute>
+ <xsl:attribute name="value">
+ <xsl:value-of select="normalize-space(.)"/>
+ </xsl:attribute>
+ </input>
+ </xsl:template>
+
+</xsl:stylesheet> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/transparentUpload.html b/node_modules/selenium-webdriver/lib/test/data/transparentUpload.html
new file mode 100644
index 000000000..87b02bfbc
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/transparentUpload.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Upload Form</title>
+ <style>
+ .fileUpload {
+ position: relative;
+ overflow: hidden;
+ margin: 10px;
+ background-color: #FFFFFF;
+ width: 50px;
+ text-align: center;
+ }
+ .fileUpload input.upload {
+ position: absolute;
+ top: 0;
+ right: 0;
+ margin: 0;
+ padding: 0;
+ font-size: 20px;
+ cursor: pointer;
+ opacity: 0;
+ filter: alpha(opacity=0);
+ height: 100%;
+ text-align: center;
+ }
+ </style>
+ <script>
+ var intervalId;
+ function onTick() {
+ var label = document.getElementById('upload_label');
+ label.innerHTML += '.';
+ }
+
+ function onUploadSubmit() {
+ document.getElementById('upload_target').contentWindow.document.body.
+ innerHTML = '';
+ var label = document.getElementById('upload_label');
+ label.innerHTML = 'Uploading "' + document.forms[0].upload.value + '"';
+ label.style.display = '';
+ intervalId = window.setInterval(onTick, 500);
+ return true;
+ }
+
+ function onUploadDone() {
+ var label = document.getElementById('upload_label');
+ label.style.display = 'none';
+ window.clearInterval(intervalId);
+ return true;
+ }
+ </script>
+</head>
+<body>
+<form action="/common/upload" method="post" name="upload_form"
+ target="upload_target" enctype="multipart/form-data"
+ onsubmit="onUploadSubmit();">
+ <div>
+ <div class="fileUpload">
+ <span>Upload</span>
+ <input id="upload" name="upload" type="file" class="upload" />
+ </div>
+ <div><input id="go" type="submit" value="Go!"/></div>
+ </div>
+ <div id="upload_label" style="display:none"></div>
+ <iframe src="" id="upload_target" name="upload_target"
+ style="width:300px;height:200px">
+ </iframe>
+</form>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/underscore.html b/node_modules/selenium-webdriver/lib/test/data/underscore.html
new file mode 100644
index 000000000..904a4441f
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/underscore.html
@@ -0,0 +1,9 @@
+<html>
+<head>
+<script type="text/javascript">
+_ = [1, 2, 3];
+</script>
+</head>
+<body>
+</body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/unicode_ltr.html b/node_modules/selenium-webdriver/lib/test/data/unicode_ltr.html
new file mode 100644
index 000000000..245acc74e
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/unicode_ltr.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ </head>
+ <body dir="ltr" class="Dw">
+ <div id="EH"><nobr class="">‎Some notes‎</nobr></div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/upload.html b/node_modules/selenium-webdriver/lib/test/data/upload.html
new file mode 100644
index 000000000..aca398ab7
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/upload.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Upload Form</title>
+ <script>
+ var intervalId;
+ function onTick() {
+ var label = document.getElementById('upload_label');
+ label.innerHTML += '.';
+ }
+
+ function onUploadSubmit() {
+ document.getElementById('upload_target').contentWindow.document.body.
+ innerHTML = '';
+ var label = document.getElementById('upload_label');
+ label.innerHTML = 'Uploading "' + document.forms[0].upload.value + '"';
+ label.style.display = '';
+ intervalId = window.setInterval(onTick, 500);
+ return true;
+ }
+
+ function onUploadDone() {
+ var label = document.getElementById('upload_label');
+ label.style.display = 'none';
+ window.clearInterval(intervalId);
+ return true;
+ }
+ </script>
+</head>
+<body>
+<form action="/common/upload" method="post" name="upload_form"
+ target="upload_target" enctype="multipart/form-data"
+ onsubmit="onUploadSubmit();">
+ <div>
+ Enter a file to upload:
+ <div><input id="upload" name="upload" type="file"/></div>
+ <div><input id="go" type="submit" value="Go!"/></div>
+ </div>
+ <div id="upload_label" style="display:none"></div>
+ <iframe src="" id="upload_target" name="upload_target"
+ style="width:300px;height:200px">
+ </iframe>
+</form>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/userDefinedProperty.html b/node_modules/selenium-webdriver/lib/test/data/userDefinedProperty.html
new file mode 100644
index 000000000..2453e6921
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/userDefinedProperty.html
@@ -0,0 +1,8 @@
+<html>
+<body>
+<div id="d"></div>
+<script>
+document.getElementById("d").dynamicProperty = 'sampleValue';
+</script>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/veryLargeCanvas.html b/node_modules/selenium-webdriver/lib/test/data/veryLargeCanvas.html
new file mode 100644
index 000000000..54a2aba46
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/veryLargeCanvas.html
@@ -0,0 +1,81 @@
+<?xml version="1.0"?>
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title>Rectangles</title>
+ <script type="text/javascript">
+ function appendMessage(message) {
+ document.getElementById('result').innerHTML += message
+ + " ";
+ }
+ </script>
+ <style type="text/css">
+ #r1 {
+ position: absolute;
+ background-color: lightblue;
+ left: 100px;
+ top: 100px;
+ width: 100px;
+ height: 50px;
+ }
+ #r2 {
+ position: absolute;
+ background-color: red;
+ left: 2500px;
+ top: 50px;
+ width: 80px;
+ height: 50px;
+ }
+ #r3 {
+ position: absolute;
+ background-color: yellow;
+ left: 60px;
+ top: 1500px;
+ width: 80px;
+ height: 50px;
+ }
+ #r4 {
+ position: absolute;
+ background-color: cyan;
+ left: 220px;
+ top: 180px;
+ width: 100px;
+ height: 50px;
+ }
+ #r5 {
+ position: absolute;
+ background-color: blue;
+ left: 60px;
+ top: 1600px;
+ width: 80px;
+ height: 50px;
+ }
+ #r6 {
+ position: absolute;
+ background-color: green;
+ left: 2581px;
+ top: 50px;
+ width: 80px;
+ height: 50px;
+ }
+ #result {
+ border: 10;
+ padding: 5;
+ background-color: grey;
+ position: absolute;
+ left: 20px;
+ top: 30px;
+ width: 400px;
+ height: 20px;
+ }
+ </style>
+</head>
+<body>
+ <div id="result"></div>
+ <div id="r1" onclick="appendMessage('First')">First Target</div>
+ <div id="r2" onclick="appendMessage('Second')">Second Target</div>
+ <div id="r3" onclick="appendMessage('Third')">Third Target</div>
+ <div id="r4" onclick="appendMessage('Fourth')">Fourth Target</div>
+ <div id="r5" onclick="appendMessage('OOPS2')">Not a Target</div>
+ <div id="r6" onclick="appendMessage('OOPS')">Not a Target</div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/visibility-css.html b/node_modules/selenium-webdriver/lib/test/data/visibility-css.html
new file mode 100644
index 000000000..80cc649ce
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/visibility-css.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta content="text/html; charset=UTF-8" http-equiv="content-type" />
+<title>Visibility test via CSS</title>
+<style>
+
+.box-popup {
+ border : 1px;
+ min-width : 150px;
+}
+body {
+ overflow : hidden;
+}
+</style></head><body>
+<div style="left: 30px; top: 10px; visibility: visible; position: absolute; overflow: visible;"
+ class="box-popup" id="suggest">
+ <p>Hello world. I like cheese.</p>
+</div>
+</body>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/win32frameset.html b/node_modules/selenium-webdriver/lib/test/data/win32frameset.html
new file mode 100644
index 000000000..108b80f8c
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/win32frameset.html
@@ -0,0 +1,8 @@
+<html>
+<head></head>
+<frameset cols="*, *, *">
+ <frame name="first" src="page/1"/>
+ <frame name="second" src="page/2?title=Fish"/>
+ <frame name="third" src="formPage.html"/>
+</frameset>
+</html> \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/lib/test/data/window_switching_tests/page_with_frame.html b/node_modules/selenium-webdriver/lib/test/data/window_switching_tests/page_with_frame.html
new file mode 100644
index 000000000..b94733b5c
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/window_switching_tests/page_with_frame.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test page for WindowSwitchingTest.testShouldFocusOnTheTopMostFrameAfterSwitchingToAWindow</title>
+</head>
+<body>
+ <p><a id="a-link-that-opens-a-new-window" target="newWindow" href="simple_page.html">Open new window</a></p>
+ <div>
+ <iframe name="myframe" src="simple_page.html" width="400" height="200"></iframe>
+ </div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/window_switching_tests/simple_page.html b/node_modules/selenium-webdriver/lib/test/data/window_switching_tests/simple_page.html
new file mode 100644
index 000000000..52c163cbf
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/window_switching_tests/simple_page.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Simple Page</title>
+</head>
+<body>
+ <div>Simple page with simple test.</div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/xhtmlFormPage.xhtml b/node_modules/selenium-webdriver/lib/test/data/xhtmlFormPage.xhtml
new file mode 100644
index 000000000..aca53d343
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/xhtmlFormPage.xhtml
@@ -0,0 +1,17 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>XHTML</title>
+</head>
+<body>
+
+ <form method="POST" enctype="multipart/form-data">
+ <input name="file" id="file" type="file" />
+ <input name="submit" id="submit" type="submit" />
+ </form>
+
+<p id="self-closed" />Here is some content that should not be in the previous p tag
+
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/data/xhtmlTest.html b/node_modules/selenium-webdriver/lib/test/data/xhtmlTest.html
new file mode 100644
index 000000000..d2f3a5da8
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/data/xhtmlTest.html
@@ -0,0 +1,76 @@
+<?xml version="1.0"?>
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title>XHTML Test Page</title>
+</head>
+<body>
+<div class="navigation">
+ <p><a href="resultPage.html" target="result" name="windowOne">Open new window</a></p>
+ <p><a href="iframes.html" target="_blank" name="windowTwo">Create a new anonymous window</a></p>
+ <p><a href="iframes.html" name="sameWindow">Open page with iframes in same window</a></p>
+ <p><a href="javascriptPage.html" target="result" name="windowThree">Open a window with a close button</a></p>
+</div>
+
+<a name="notext"><b></b></a>
+
+<div class="content">
+ <h1 class="header">XHTML Might Be The Future</h1>
+
+ <p>If you'd like to go elsewhere then <a href="resultPage.html">click me</a>.</p>
+
+ <p>Alternatively, <a href="resultPage.html" id="linkId">this goes to the same place</a>.</p>
+
+ <form name="someForm">
+ <input id="username" type="text" value="change"/>
+ </form>
+
+ This link has the same text as another link: <a href="resultPage.html">click me</a>.
+</div>
+
+<div class="extraDiv">Another div starts here.<p/>
+ <h2 class="nameA nameBnoise nameC">An H2 title</h2>
+ <p class="nameC">Some more text</p>
+</div>
+
+<div>
+ <a id="id1" href="#">Foo</a>
+ <ul id="id2" />
+ <span id="id3"/>
+</div>
+
+<div>
+ <table id="table" ></table>
+</div>
+
+<span id="amazing">
+<div>
+ <div>
+ <div>
+ <span/>
+ <a>I have width</a>
+ </div>
+ </div>
+</div>
+</span>
+
+<a name="text" />
+<p id="spaces"> </p>
+<p id="empty"></p>
+<a href="foo" id="linkWithEqualsSign">Link=equalssign</a>
+
+<p class=" spaceAround ">Spaced out</p>
+
+<span id="my_span">
+ <div>first_div</div>
+ <div>second_div</div>
+ <span>first_span</span>
+ <span>second_span</span>
+</span>
+
+<div id="parent">I'm a parent
+ <div id="child">I'm a child</div>
+</div>
+
+<div id="only-exists-on-xhtmltest">Woo woo</div>
+</body>
+</html>
diff --git a/node_modules/selenium-webdriver/lib/test/fileserver.js b/node_modules/selenium-webdriver/lib/test/fileserver.js
new file mode 100644
index 000000000..448b0c9a2
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/fileserver.js
@@ -0,0 +1,319 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+var fs = require('fs'),
+ http = require('http'),
+ path = require('path'),
+ url = require('url');
+
+var express = require('express');
+var multer = require('multer');
+var serveIndex = require('serve-index');
+
+var Server = require('./httpserver').Server,
+ resources = require('./resources'),
+ isDevMode = require('../devmode'),
+ promise = require('../promise');
+
+var WEB_ROOT = '/common';
+var JS_ROOT = '/javascript';
+
+var baseDirectory = resources.locate(isDevMode ? 'common/src/web' : '.');
+var jsDirectory = resources.locate(isDevMode ? 'javascript' : '..');
+
+var Pages = (function() {
+ var pages = {};
+ function addPage(page, path) {
+ pages.__defineGetter__(page, function() {
+ return exports.whereIs(path);
+ });
+ }
+
+ addPage('ajaxyPage', 'ajaxy_page.html');
+ addPage('alertsPage', 'alerts.html');
+ addPage('bodyTypingPage', 'bodyTypingTest.html');
+ addPage('booleanAttributes', 'booleanAttributes.html');
+ addPage('childPage', 'child/childPage.html');
+ addPage('chinesePage', 'cn-test.html');
+ addPage('clickJacker', 'click_jacker.html');
+ addPage('clickEventPage', 'clickEventPage.html');
+ addPage('clicksPage', 'clicks.html');
+ addPage('colorPage', 'colorPage.html');
+ addPage('deletingFrame', 'deletingFrame.htm');
+ addPage('draggableLists', 'draggableLists.html');
+ addPage('dragAndDropPage', 'dragAndDropTest.html');
+ addPage('droppableItems', 'droppableItems.html');
+ addPage('documentWrite', 'document_write_in_onload.html');
+ addPage('dynamicallyModifiedPage', 'dynamicallyModifiedPage.html');
+ addPage('dynamicPage', 'dynamic.html');
+ addPage('echoPage', 'echo');
+ addPage('errorsPage', 'errors.html');
+ addPage('xhtmlFormPage', 'xhtmlFormPage.xhtml');
+ addPage('formPage', 'formPage.html');
+ addPage('formSelectionPage', 'formSelectionPage.html');
+ addPage('framesetPage', 'frameset.html');
+ addPage('grandchildPage', 'child/grandchild/grandchildPage.html');
+ addPage('html5Page', 'html5Page.html');
+ addPage('html5OfflinePage', 'html5/offline.html');
+ addPage('iframePage', 'iframes.html');
+ addPage('javascriptEnhancedForm', 'javascriptEnhancedForm.html');
+ addPage('javascriptPage', 'javascriptPage.html');
+ addPage('linkedImage', 'linked_image.html');
+ addPage('longContentPage', 'longContentPage.html');
+ addPage('macbethPage', 'macbeth.html');
+ addPage('mapVisibilityPage', 'map_visibility.html');
+ addPage('metaRedirectPage', 'meta-redirect.html');
+ addPage('missedJsReferencePage', 'missedJsReference.html');
+ addPage('mouseTrackerPage', 'mousePositionTracker.html');
+ addPage('nestedPage', 'nestedElements.html');
+ addPage('readOnlyPage', 'readOnlyPage.html');
+ addPage('rectanglesPage', 'rectangles.html');
+ addPage('redirectPage', 'redirect');
+ addPage('resultPage', 'resultPage.html');
+ addPage('richTextPage', 'rich_text.html');
+ addPage('selectableItemsPage', 'selectableItems.html');
+ addPage('selectPage', 'selectPage.html');
+ addPage('simpleTestPage', 'simpleTest.html');
+ addPage('simpleXmlDocument', 'simple.xml');
+ addPage('sleepingPage', 'sleep');
+ addPage('slowIframes', 'slow_loading_iframes.html');
+ addPage('slowLoadingAlertPage', 'slowLoadingAlert.html');
+ addPage('svgPage', 'svgPiechart.xhtml');
+ addPage('tables', 'tables.html');
+ addPage('underscorePage', 'underscore.html');
+ addPage('unicodeLtrPage', 'utf8/unicode_ltr.html');
+ addPage('uploadPage', 'upload.html');
+ addPage('veryLargeCanvas', 'veryLargeCanvas.html');
+ addPage('xhtmlTestPage', 'xhtmlTest.html');
+
+ return pages;
+})();
+
+
+var Path = {
+ BASIC_AUTH: WEB_ROOT + '/basicAuth',
+ ECHO: WEB_ROOT + '/echo',
+ GENERATED: WEB_ROOT + '/generated',
+ MANIFEST: WEB_ROOT + '/manifest',
+ REDIRECT: WEB_ROOT + '/redirect',
+ PAGE: WEB_ROOT + '/page',
+ SLEEP: WEB_ROOT + '/sleep',
+ UPLOAD: WEB_ROOT + '/upload'
+};
+
+var app = express();
+
+app.get('/', sendIndex)
+.get('/favicon.ico', function(req, res) {
+ res.writeHead(204);
+ res.end();
+})
+.use(JS_ROOT, serveIndex(jsDirectory), express.static(jsDirectory))
+.post(Path.UPLOAD, handleUpload)
+.use(WEB_ROOT, serveIndex(baseDirectory), express.static(baseDirectory))
+.get(Path.ECHO, sendEcho)
+.get(Path.PAGE, sendInifinitePage)
+.get(Path.PAGE + '/*', sendInifinitePage)
+.get(Path.REDIRECT, redirectToResultPage)
+.get(Path.SLEEP, sendDelayedResponse)
+
+if (isDevMode) {
+ var closureDir = resources.locate('third_party/closure/goog');
+ app.use('/third_party/closure/goog',
+ serveIndex(closureDir), express.static(closureDir));
+}
+var server = new Server(app);
+
+
+function redirectToResultPage(_, response) {
+ response.writeHead(303, {
+ Location: Pages.resultPage
+ });
+ return response.end();
+}
+
+
+function sendInifinitePage(request, response) {
+ var pathname = url.parse(request.url).pathname;
+ var lastIndex = pathname.lastIndexOf('/');
+ var pageNumber =
+ (lastIndex == -1 ? 'Unknown' : pathname.substring(lastIndex + 1));
+ var body = [
+ '<!DOCTYPE html>',
+ '<title>Page', pageNumber, '</title>',
+ 'Page number <span id="pageNumber">', pageNumber, '</span>',
+ '<p><a href="../xhtmlTest.html" target="_top">top</a>'
+ ].join('');
+ response.writeHead(200, {
+ 'Content-Length': Buffer.byteLength(body, 'utf8'),
+ 'Content-Type': 'text/html; charset=utf-8'
+ });
+ response.end(body);
+}
+
+
+function sendDelayedResponse(request, response) {
+ var duration = 0;
+ var query = url.parse(request.url).query || '';
+ var match = query.match(/\btime=(\d+)/);
+ if (match) {
+ duration = parseInt(match[1], 10);
+ }
+
+ setTimeout(function() {
+ var body = [
+ '<!DOCTYPE html>',
+ '<title>Done</title>',
+ '<body>Slept for ', duration, 's</body>'
+ ].join('');
+ response.writeHead(200, {
+ 'Content-Length': Buffer.byteLength(body, 'utf8'),
+ 'Content-Type': 'text/html; charset=utf-8',
+ 'Cache-Control': 'no-cache',
+ 'Pragma': 'no-cache',
+ 'Expires': 0
+ });
+ response.end(body);
+ }, duration * 1000);
+}
+
+
+function handleUpload(request, response, next) {
+ multer({
+ inMemory: true,
+ onFileUploadComplete: function(file) {
+ response.writeHead(200);
+ response.write(file.buffer);
+ response.end('<script>window.top.window.onUploadDone();</script>');
+ }
+ })(request, response, function() {});
+}
+
+
+function sendEcho(request, response) {
+ var body = [
+ '<!DOCTYPE html>',
+ '<title>Echo</title>',
+ '<div class="request">',
+ request.method, ' ', request.url, ' ', 'HTTP/', request.httpVersion,
+ '</div>'
+ ];
+ for (var name in request.headers) {
+ body.push('<div class="header ', name , '">',
+ name, ': ', request.headers[name], '</div>');
+ }
+ body = body.join('');
+ response.writeHead(200, {
+ 'Content-Length': Buffer.byteLength(body, 'utf8'),
+ 'Content-Type': 'text/html; charset=utf-8'
+ });
+ response.end(body);
+}
+
+
+/**
+ * Responds to a request for the file server's main index.
+ * @param {!http.ServerRequest} request The request object.
+ * @param {!http.ServerResponse} response The response object.
+ */
+function sendIndex(request, response) {
+ var pathname = url.parse(request.url).pathname;
+
+ var host = request.headers.host;
+ if (!host) {
+ host = server.host();
+ }
+
+ var requestUrl = ['http://' + host + pathname].join('');
+
+ function createListEntry(path) {
+ var url = requestUrl + path;
+ return ['<li><a href="', url, '">', path, '</a>'].join('');
+ }
+
+ var data = ['<!DOCTYPE html><h1>/</h1><hr/><ul>',
+ createListEntry('common')];
+ if (isDevMode) {
+ data.push(createListEntry('javascript'));
+ }
+ data.push('</ul>');
+ data = data.join('');
+
+ response.writeHead(200, {
+ 'Content-Type': 'text/html; charset=UTF-8',
+ 'Content-Length': Buffer.byteLength(data, 'utf8')
+ });
+ response.end(data);
+}
+
+
+// PUBLIC application
+
+
+/**
+ * Starts the server on the specified port.
+ * @param {number=} opt_port The port to use, or 0 for any free port.
+ * @return {!webdriver.promise.Promise.<Host>} A promise that will resolve
+ * with the server host when it has fully started.
+ */
+exports.start = server.start.bind(server);
+
+
+/**
+ * Stops the server.
+ * @return {!webdriver.promise.Promise} A promise that will resolve when the
+ * server has closed all connections.
+ */
+exports.stop = server.stop.bind(server);
+
+
+/**
+ * Formats a URL for this server.
+ * @param {string=} opt_pathname The desired pathname on the server.
+ * @return {string} The formatted URL.
+ * @throws {Error} If the server is not running.
+ */
+exports.url = server.url.bind(server);
+
+
+/**
+ * Builds the URL for a file in the //common/src/web directory of the
+ * Selenium client.
+ * @param {string} filePath A path relative to //common/src/web to compute a
+ * URL for.
+ * @return {string} The formatted URL.
+ * @throws {Error} If the server is not running.
+ */
+exports.whereIs = function(filePath) {
+ filePath = filePath.replace(/\\/g, '/');
+ if (!filePath.startsWith('/')) {
+ filePath = '/' + filePath;
+ }
+ return server.url(WEB_ROOT + filePath);
+};
+
+
+exports.Pages = Pages;
+
+
+if (require.main === module) {
+ server.start(2310).then(function() {
+ console.log('Server running at ' + server.url());
+ });
+}
diff --git a/node_modules/selenium-webdriver/lib/test/httpserver.js b/node_modules/selenium-webdriver/lib/test/httpserver.js
new file mode 100644
index 000000000..55b12551f
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/httpserver.js
@@ -0,0 +1,122 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+var assert = require('assert'),
+ http = require('http'),
+ url = require('url');
+
+var net = require('../../net'),
+ portprober = require('../../net/portprober'),
+ promise = require('../..').promise;
+
+
+
+/**
+ * Encapsulates a simple HTTP server for testing. The {@code onrequest}
+ * function should be overridden to define request handling behavior.
+ * @param {function(!http.ServerRequest, !http.ServerResponse)} requestHandler
+ * The request handler for the server.
+ * @constructor
+ */
+var Server = function(requestHandler) {
+ var server = http.createServer(function(req, res) {
+ requestHandler(req, res);
+ });
+
+ server.on('connection', function(stream) {
+ stream.setTimeout(4000);
+ });
+
+ /** @typedef {{port: number, address: string, family: string}} */
+ var Host;
+
+ /**
+ * Starts the server on the given port. If no port, or 0, is provided,
+ * the server will be started on a random port.
+ * @param {number=} opt_port The port to start on.
+ * @return {!webdriver.promise.Promise.<Host>} A promise that will resolve
+ * with the server host when it has fully started.
+ */
+ this.start = function(opt_port) {
+ assert(typeof opt_port !== 'function',
+ "start invoked with function, not port (mocha callback)?");
+ var port = opt_port || portprober.findFreePort('localhost');
+ return promise.when(port, function(port) {
+ return promise.checkedNodeCall(
+ server.listen.bind(server, port, 'localhost'));
+ }).then(function() {
+ return server.address();
+ });
+ };
+
+ /**
+ * Stops the server.
+ * @return {!webdriver.promise.Promise} A promise that will resolve when the
+ * server has closed all connections.
+ */
+ this.stop = function() {
+ var d = promise.defer();
+ server.close(d.fulfill);
+ return d.promise;
+ };
+
+ /**
+ * @return {Host} This server's host info.
+ * @throws {Error} If the server is not running.
+ */
+ this.address = function() {
+ var addr = server.address();
+ if (!addr) {
+ throw Error('There server is not running!');
+ }
+ return addr;
+ };
+
+ /**
+ * return {string} The host:port of this server.
+ * @throws {Error} If the server is not running.
+ */
+ this.host = function() {
+ return net.getLoopbackAddress() + ':' +
+ this.address().port;
+ };
+
+ /**
+ * Formats a URL for this server.
+ * @param {string=} opt_pathname The desired pathname on the server.
+ * @return {string} The formatted URL.
+ * @throws {Error} If the server is not running.
+ */
+ this.url = function(opt_pathname) {
+ var addr = this.address();
+ var pathname = opt_pathname || '';
+ return url.format({
+ protocol: 'http',
+ hostname: net.getLoopbackAddress(),
+ port: addr.port,
+ pathname: pathname
+ });
+ };
+};
+
+
+// PUBLIC API
+
+
+exports.Server = Server;
diff --git a/node_modules/selenium-webdriver/lib/test/index.js b/node_modules/selenium-webdriver/lib/test/index.js
new file mode 100644
index 000000000..d702c5fb2
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/index.js
@@ -0,0 +1,301 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+var assert = require('assert');
+
+var build = require('./build'),
+ isDevMode = require('../devmode'),
+ webdriver = require('../../'),
+ flow = webdriver.promise.controlFlow(),
+ firefox = require('../../firefox'),
+ safari = require('../../safari'),
+ remote = require('../../remote'),
+ testing = require('../../testing'),
+ fileserver = require('./fileserver');
+
+
+const LEGACY_FIREFOX = 'legacy-' + webdriver.Browser.FIREFOX;
+const LEGACY_SAFARI = 'legacy-' + webdriver.Browser.SAFARI;
+
+
+/**
+ * Browsers with native support.
+ * @type {!Array.<webdriver.Browser>}
+ */
+var NATIVE_BROWSERS = [
+ webdriver.Browser.CHROME,
+ webdriver.Browser.EDGE,
+ webdriver.Browser.FIREFOX,
+ LEGACY_FIREFOX,
+ webdriver.Browser.IE,
+ webdriver.Browser.OPERA,
+ webdriver.Browser.PHANTOM_JS,
+ webdriver.Browser.SAFARI,
+ LEGACY_SAFARI
+];
+
+
+var serverJar = process.env['SELENIUM_SERVER_JAR'];
+var remoteUrl = process.env['SELENIUM_REMOTE_URL'];
+var useLoopback = process.env['SELENIUM_USE_LOOP_BACK'] == '1';
+var noMarionette = /^0|false$/i.test(process.env['SELENIUM_GECKODRIVER']);
+var startServer = !!serverJar && !remoteUrl;
+var nativeRun = !serverJar && !remoteUrl;
+
+var browsersToTest = (function() {
+ var permitRemoteBrowsers = !!remoteUrl || !!serverJar;
+ var permitUnknownBrowsers = !nativeRun;
+ var browsers = process.env['SELENIUM_BROWSER'] || webdriver.Browser.FIREFOX;
+
+ browsers = browsers.split(',').map(function(browser) {
+ var parts = browser.split(/:/);
+ if (parts[0] === 'ie') {
+ parts[0] = webdriver.Browser.IE;
+ }
+ if (parts[0] === 'edge') {
+ parts[0] = webdriver.Browser.EDGE;
+ }
+ if (noMarionette && parts[0] === webdriver.Browser.FIREFOX) {
+ parts[0] = LEGACY_FIREFOX;
+ }
+ return parts.join(':');
+ });
+
+ browsers.forEach(function(browser) {
+ var parts = browser.split(/:/, 3);
+ if (parts[0] === 'ie') {
+ parts[0] = webdriver.Browser.IE;
+ }
+
+ if (parts[0] === LEGACY_FIREFOX ||
+ parts[0] === LEGACY_SAFARI) {
+ return;
+ }
+
+ if (NATIVE_BROWSERS.indexOf(parts[0]) == -1 && !permitRemoteBrowsers) {
+ throw Error('Browser ' + parts[0] + ' requires a WebDriver server and ' +
+ 'neither the SELENIUM_REMOTE_URL nor the SELENIUM_SERVER_JAR ' +
+ 'environment variables have been set.');
+ }
+
+ var recognized = false;
+ for (var prop in webdriver.Browser) {
+ if (webdriver.Browser.hasOwnProperty(prop) &&
+ webdriver.Browser[prop] === parts[0]) {
+ recognized = true;
+ break;
+ }
+ }
+
+ if (!recognized && !permitUnknownBrowsers) {
+ throw Error('Unrecognized browser: ' + browser);
+ }
+ });
+
+ console.log('Running tests against [' + browsers.join(',') + ']');
+ if (remoteUrl) {
+ console.log('Using remote server ' + remoteUrl);
+ } else if (serverJar) {
+ console.log('Using standalone Selenium server ' + serverJar);
+ if (useLoopback) {
+ console.log('Running tests using loopback address')
+ }
+ }
+
+ return browsers;
+})();
+
+
+/**
+ * Creates a predicate function that ignores tests for specific browsers.
+ * @param {string} currentBrowser The name of the current browser.
+ * @param {!Array.<!Browser>} browsersToIgnore The browsers to ignore.
+ * @return {function(): boolean} The predicate function.
+ */
+function browsers(currentBrowser, browsersToIgnore) {
+ return function() {
+ return browsersToIgnore.indexOf(currentBrowser) != -1;
+ };
+}
+
+
+/**
+ * @param {string} browserName The name to use.
+ * @param {remote.DriverService} server The server to use, if any.
+ * @constructor
+ */
+function TestEnvironment(browserName, server) {
+ var name = browserName;
+
+ this.currentBrowser = function() {
+ return browserName;
+ };
+
+ this.isRemote = function() {
+ return server || remoteUrl;
+ };
+
+ this.isMarionette = function() {
+ return !noMarionette;
+ };
+
+ this.browsers = function(var_args) {
+ var browsersToIgnore = Array.prototype.slice.apply(arguments, [0]);
+ return browsers(browserName, browsersToIgnore);
+ };
+
+ this.builder = function() {
+ var builder = new webdriver.Builder();
+ var realBuild = builder.build;
+
+ builder.build = function() {
+ var parts = browserName.split(/:/, 3);
+
+ if (parts[0] === LEGACY_FIREFOX) {
+ var options = builder.getFirefoxOptions() || new firefox.Options();
+ options.useGeckoDriver(false);
+ builder.setFirefoxOptions(options);
+
+ parts[0] = webdriver.Browser.FIREFOX;
+ }
+
+ if (parts[0] === LEGACY_SAFARI) {
+ var options = builder.getSafariOptions() || new safari.Options();
+ options.useLegacyDriver(true);
+ builder.setSafariOptions(options);
+
+ parts[0] = webdriver.Browser.SAFARI;
+ }
+
+ builder.forBrowser(parts[0], parts[1], parts[2]);
+ if (server) {
+ builder.usingServer(server.address());
+ } else if (remoteUrl) {
+ builder.usingServer(remoteUrl);
+ }
+
+ builder.disableEnvironmentOverrides();
+ return realBuild.call(builder);
+ };
+
+ return builder;
+ };
+}
+
+
+var seleniumServer;
+var inSuite = false;
+
+
+/**
+ * Expands a function to cover each of the target browsers.
+ * @param {function(!TestEnvironment)} fn The top level suite
+ * function.
+ * @param {{browsers: !Array.<string>}=} opt_options Suite specific options.
+ */
+function suite(fn, opt_options) {
+ assert.ok(!inSuite, 'You may not nest suite calls');
+ inSuite = true;
+
+ var suiteOptions = opt_options || {};
+ var browsers = suiteOptions.browsers;
+ if (browsers) {
+ // Filter out browser specific tests when that browser is not currently
+ // selected for testing.
+ browsers = browsers.filter(function(browser) {
+ return browsersToTest.indexOf(browser) != -1;
+ });
+ } else {
+ browsers = browsersToTest;
+ }
+
+ try {
+
+ // Server is only started if required for a specific config.
+ testing.after(function() {
+ if (seleniumServer) {
+ return seleniumServer.stop();
+ }
+ });
+
+ browsers.forEach(function(browser) {
+ testing.describe('[' + browser + ']', function() {
+
+ if (isDevMode && nativeRun) {
+ if (browser === LEGACY_FIREFOX) {
+ testing.before(function() {
+ return build.of('//javascript/firefox-driver:webdriver')
+ .onlyOnce().go();
+ });
+ } else if (browser === LEGACY_SAFARI) {
+ testing.before(function() {
+ return build.of('//javascript/safari-driver:client')
+ .onlyOnce().go();
+ });
+ }
+ }
+
+ var serverToUse = null;
+
+ if (!!serverJar && !remoteUrl) {
+ if (!(serverToUse = seleniumServer)) {
+ serverToUse = seleniumServer = new remote.SeleniumServer(
+ serverJar, {loopback: useLoopback});
+ }
+
+ testing.before(function() {
+ this.timeout(0);
+ return seleniumServer.start(60 * 1000);
+ });
+ }
+ fn(new TestEnvironment(browser, serverToUse));
+ });
+ });
+ } finally {
+ inSuite = false;
+ }
+}
+
+
+// GLOBAL TEST SETUP
+
+testing.before(function() {
+ // Do not pass register fileserver.start directly with testing.before,
+ // as start takes an optional port, which before assumes is an async
+ // callback.
+ return fileserver.start();
+});
+
+testing.after(function() {
+ return fileserver.stop();
+});
+
+// PUBLIC API
+
+
+exports.suite = suite;
+exports.after = testing.after;
+exports.afterEach = testing.afterEach;
+exports.before = testing.before;
+exports.beforeEach = testing.beforeEach;
+exports.it = testing.it;
+exports.ignore = testing.ignore;
+
+exports.Pages = fileserver.Pages;
+exports.whereIs = fileserver.whereIs;
diff --git a/node_modules/selenium-webdriver/lib/test/resources.js b/node_modules/selenium-webdriver/lib/test/resources.js
new file mode 100644
index 000000000..8e9cceb40
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/test/resources.js
@@ -0,0 +1,44 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+var fs = require('fs'),
+ path = require('path');
+
+var resourceRoot = require('../devmode') ?
+ require('./build').projectRoot() :
+ path.join(__dirname, 'data');
+
+
+// PUBLIC API
+
+
+/**
+ * Locates a test resource.
+ * @param {string} resourcePath Path of the resource to locate.
+ * @param {string} filePath The file to locate from the root of the project.
+ * @return {string} The full path for the file, if it exists.
+ * @throws {Error} If the file does not exist.
+ */
+exports.locate = function(filePath) {
+ var fullPath = path.normalize(path.join(resourceRoot, filePath));
+ if (!fs.existsSync(fullPath)) {
+ throw Error('File does not exist: ' + filePath);
+ }
+ return fullPath;
+};
diff --git a/node_modules/selenium-webdriver/lib/until.js b/node_modules/selenium-webdriver/lib/until.js
new file mode 100644
index 000000000..0cc6f5ea3
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/until.js
@@ -0,0 +1,427 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+/**
+ * @fileoverview Defines common conditions for use with
+ * {@link webdriver.WebDriver#wait WebDriver wait}.
+ *
+ * Sample usage:
+ *
+ * driver.get('http://www.google.com/ncr');
+ *
+ * var query = driver.wait(until.elementLocated(By.name('q')));
+ * query.sendKeys('webdriver\n');
+ *
+ * driver.wait(until.titleIs('webdriver - Google Search'));
+ *
+ * To define a custom condition, simply call WebDriver.wait with a function
+ * that will eventually return a truthy-value (neither null, undefined, false,
+ * 0, or the empty string):
+ *
+ * driver.wait(function() {
+ * return driver.getTitle().then(function(title) {
+ * return title === 'webdriver - Google Search';
+ * });
+ * }, 1000);
+ */
+
+'use strict';
+
+const by = require('./by');
+const By = require('./by').By;
+const error = require('./error');
+const webdriver = require('./webdriver'),
+ Condition = webdriver.Condition,
+ WebElementCondition = webdriver.WebElementCondition;
+
+
+/**
+ * Creates a condition that will wait until the input driver is able to switch
+ * to the designated frame. The target frame may be specified as
+ *
+ * 1. a numeric index into
+ * [window.frames](https://developer.mozilla.org/en-US/docs/Web/API/Window.frames)
+ * for the currently selected frame.
+ * 2. a {@link ./webdriver.WebElement}, which must reference a FRAME or IFRAME
+ * element on the current page.
+ * 3. a locator which may be used to first locate a FRAME or IFRAME on the
+ * current page before attempting to switch to it.
+ *
+ * Upon successful resolution of this condition, the driver will be left
+ * focused on the new frame.
+ *
+ * @param {!(number|./webdriver.WebElement|By|
+ * function(!./webdriver.WebDriver): !./webdriver.WebElement)} frame
+ * The frame identifier.
+ * @return {!Condition<boolean>} A new condition.
+ */
+exports.ableToSwitchToFrame = function ableToSwitchToFrame(frame) {
+ var condition;
+ if (typeof frame === 'number' || frame instanceof webdriver.WebElement) {
+ condition = attemptToSwitchFrames;
+ } else {
+ condition = function(driver) {
+ let locator = /** @type {!(By|Function)} */(frame);
+ return driver.findElements(locator).then(function(els) {
+ if (els.length) {
+ return attemptToSwitchFrames(driver, els[0]);
+ }
+ });
+ };
+ }
+
+ return new Condition('to be able to switch to frame', condition);
+
+ function attemptToSwitchFrames(driver, frame) {
+ return driver.switchTo().frame(frame).then(
+ function() { return true; },
+ function(e) {
+ if (!(e instanceof error.NoSuchFrameError)) {
+ throw e;
+ }
+ });
+ }
+};
+
+
+/**
+ * Creates a condition that waits for an alert to be opened. Upon success, the
+ * returned promise will be fulfilled with the handle for the opened alert.
+ *
+ * @return {!Condition<!./webdriver.Alert>} The new condition.
+ */
+exports.alertIsPresent = function alertIsPresent() {
+ return new Condition('for alert to be present', function(driver) {
+ return driver.switchTo().alert().catch(function(e) {
+ if (!(e instanceof error.NoSuchAlertError
+ // XXX: Workaround for GeckoDriver error `TypeError: can't convert null
+ // to object`. For more details, see
+ // https://github.com/SeleniumHQ/selenium/pull/2137
+ || (e instanceof error.WebDriverError
+ && e.message === `can't convert null to object`)
+ )) {
+ throw e;
+ }
+ });
+ });
+};
+
+
+/**
+ * Creates a condition that will wait for the current page's title to match the
+ * given value.
+ *
+ * @param {string} title The expected page title.
+ * @return {!Condition<boolean>} The new condition.
+ */
+exports.titleIs = function titleIs(title) {
+ return new Condition(
+ 'for title to be ' + JSON.stringify(title),
+ function(driver) {
+ return driver.getTitle().then(function(t) {
+ return t === title;
+ });
+ });
+};
+
+
+/**
+ * Creates a condition that will wait for the current page's title to contain
+ * the given substring.
+ *
+ * @param {string} substr The substring that should be present in the page
+ * title.
+ * @return {!Condition<boolean>} The new condition.
+ */
+exports.titleContains = function titleContains(substr) {
+ return new Condition(
+ 'for title to contain ' + JSON.stringify(substr),
+ function(driver) {
+ return driver.getTitle().then(function(title) {
+ return title.indexOf(substr) !== -1;
+ });
+ });
+};
+
+
+/**
+ * Creates a condition that will wait for the current page's title to match the
+ * given regular expression.
+ *
+ * @param {!RegExp} regex The regular expression to test against.
+ * @return {!Condition<boolean>} The new condition.
+ */
+exports.titleMatches = function titleMatches(regex) {
+ return new Condition('for title to match ' + regex, function(driver) {
+ return driver.getTitle().then(function(title) {
+ return regex.test(title);
+ });
+ });
+};
+
+
+/**
+ * Creates a condition that will wait for the current page's url to match the
+ * given value.
+ *
+ * @param {string} url The expected page url.
+ * @return {!Condition<boolean>} The new condition.
+ */
+exports.urlIs = function urlIs(url) {
+ return new Condition(
+ 'for URL to be ' + JSON.stringify(url),
+ function(driver) {
+ return driver.getCurrentUrl().then(function(u) {
+ return u === url;
+ });
+ });
+};
+
+
+/**
+ * Creates a condition that will wait for the current page's url to contain
+ * the given substring.
+ *
+ * @param {string} substrUrl The substring that should be present in the current
+ * URL.
+ * @return {!Condition<boolean>} The new condition.
+ */
+exports.urlContains = function urlContains(substrUrl) {
+ return new Condition(
+ 'for URL to contain ' + JSON.stringify(substrUrl),
+ function(driver) {
+ return driver.getCurrentUrl().then(function(url) {
+ return url.indexOf(substrUrl) !== -1;
+ });
+ });
+};
+
+
+/**
+ * Creates a condition that will wait for the current page's url to match the
+ * given regular expression.
+ *
+ * @param {!RegExp} regex The regular expression to test against.
+ * @return {!Condition<boolean>} The new condition.
+ */
+exports.urlMatches = function urlMatches(regex) {
+ return new Condition('for URL to match ' + regex, function(driver) {
+ return driver.getCurrentUrl().then(function(url) {
+ return regex.test(url);
+ });
+ });
+};
+
+
+/**
+ * Creates a condition that will loop until an element is
+ * {@link ./webdriver.WebDriver#findElement found} with the given locator.
+ *
+ * @param {!(By|Function)} locator The locator to use.
+ * @return {!WebElementCondition} The new condition.
+ */
+exports.elementLocated = function elementLocated(locator) {
+ locator = by.checkedLocator(locator);
+ let locatorStr =
+ typeof locator === 'function' ? 'by function()' : locator + '';
+ return new WebElementCondition('for element to be located ' + locatorStr,
+ function(driver) {
+ return driver.findElements(locator).then(function(elements) {
+ return elements[0];
+ });
+ });
+};
+
+
+/**
+ * Creates a condition that will loop until at least one element is
+ * {@link ./webdriver.WebDriver#findElement found} with the given locator.
+ *
+ * @param {!(By|Function)} locator The locator to use.
+ * @return {!Condition<!Array<!./webdriver.WebElement>>} The new
+ * condition.
+ */
+exports.elementsLocated = function elementsLocated(locator) {
+ locator = by.checkedLocator(locator);
+ let locatorStr =
+ typeof locator === 'function' ? 'by function()' : locator + '';
+ return new Condition(
+ 'for at least one element to be located ' + locatorStr,
+ function(driver) {
+ return driver.findElements(locator).then(function(elements) {
+ return elements.length > 0 ? elements : null;
+ });
+ });
+};
+
+
+/**
+ * Creates a condition that will wait for the given element to become stale. An
+ * element is considered stale once it is removed from the DOM, or a new page
+ * has loaded.
+ *
+ * @param {!./webdriver.WebElement} element The element that should become stale.
+ * @return {!Condition<boolean>} The new condition.
+ */
+exports.stalenessOf = function stalenessOf(element) {
+ return new Condition('element to become stale', function() {
+ return element.getTagName().then(
+ function() { return false; },
+ function(e) {
+ if (e instanceof error.StaleElementReferenceError) {
+ return true;
+ }
+ throw e;
+ });
+ });
+};
+
+
+/**
+ * Creates a condition that will wait for the given element to become visible.
+ *
+ * @param {!./webdriver.WebElement} element The element to test.
+ * @return {!WebElementCondition} The new condition.
+ * @see ./webdriver.WebDriver#isDisplayed
+ */
+exports.elementIsVisible = function elementIsVisible(element) {
+ return new WebElementCondition('until element is visible', function() {
+ return element.isDisplayed().then(v => v ? element : null);
+ });
+};
+
+
+/**
+ * Creates a condition that will wait for the given element to be in the DOM,
+ * yet not visible to the user.
+ *
+ * @param {!./webdriver.WebElement} element The element to test.
+ * @return {!WebElementCondition} The new condition.
+ * @see ./webdriver.WebDriver#isDisplayed
+ */
+exports.elementIsNotVisible = function elementIsNotVisible(element) {
+ return new WebElementCondition('until element is not visible', function() {
+ return element.isDisplayed().then(v => v ? null : element);
+ });
+};
+
+
+/**
+ * Creates a condition that will wait for the given element to be enabled.
+ *
+ * @param {!./webdriver.WebElement} element The element to test.
+ * @return {!WebElementCondition} The new condition.
+ * @see webdriver.WebDriver#isEnabled
+ */
+exports.elementIsEnabled = function elementIsEnabled(element) {
+ return new WebElementCondition('until element is enabled', function() {
+ return element.isEnabled().then(v => v ? element : null);
+ });
+};
+
+
+/**
+ * Creates a condition that will wait for the given element to be disabled.
+ *
+ * @param {!./webdriver.WebElement} element The element to test.
+ * @return {!WebElementCondition} The new condition.
+ * @see webdriver.WebDriver#isEnabled
+ */
+exports.elementIsDisabled = function elementIsDisabled(element) {
+ return new WebElementCondition('until element is disabled', function() {
+ return element.isEnabled().then(v => v ? null : element);
+ });
+};
+
+
+/**
+ * Creates a condition that will wait for the given element to be selected.
+ * @param {!./webdriver.WebElement} element The element to test.
+ * @return {!WebElementCondition} The new condition.
+ * @see webdriver.WebDriver#isSelected
+ */
+exports.elementIsSelected = function elementIsSelected(element) {
+ return new WebElementCondition('until element is selected', function() {
+ return element.isSelected().then(v => v ? element : null);
+ });
+};
+
+
+/**
+ * Creates a condition that will wait for the given element to be deselected.
+ *
+ * @param {!./webdriver.WebElement} element The element to test.
+ * @return {!WebElementCondition} The new condition.
+ * @see webdriver.WebDriver#isSelected
+ */
+exports.elementIsNotSelected = function elementIsNotSelected(element) {
+ return new WebElementCondition('until element is not selected', function() {
+ return element.isSelected().then(v => v ? null : element);
+ });
+};
+
+
+/**
+ * Creates a condition that will wait for the given element's
+ * {@link webdriver.WebDriver#getText visible text} to match the given
+ * {@code text} exactly.
+ *
+ * @param {!./webdriver.WebElement} element The element to test.
+ * @param {string} text The expected text.
+ * @return {!WebElementCondition} The new condition.
+ * @see webdriver.WebDriver#getText
+ */
+exports.elementTextIs = function elementTextIs(element, text) {
+ return new WebElementCondition('until element text is', function() {
+ return element.getText().then(t => t === text ? element : null);
+ });
+};
+
+
+/**
+ * Creates a condition that will wait for the given element's
+ * {@link webdriver.WebDriver#getText visible text} to contain the given
+ * substring.
+ *
+ * @param {!./webdriver.WebElement} element The element to test.
+ * @param {string} substr The substring to search for.
+ * @return {!WebElementCondition} The new condition.
+ * @see webdriver.WebDriver#getText
+ */
+exports.elementTextContains = function elementTextContains(element, substr) {
+ return new WebElementCondition('until element text contains', function() {
+ return element.getText()
+ .then(t => t.indexOf(substr) != -1 ? element : null);
+ });
+};
+
+
+/**
+ * Creates a condition that will wait for the given element's
+ * {@link webdriver.WebDriver#getText visible text} to match a regular
+ * expression.
+ *
+ * @param {!./webdriver.WebElement} element The element to test.
+ * @param {!RegExp} regex The regular expression to test against.
+ * @return {!WebElementCondition} The new condition.
+ * @see webdriver.WebDriver#getText
+ */
+exports.elementTextMatches = function elementTextMatches(element, regex) {
+ return new WebElementCondition('until element text matches', function() {
+ return element.getText().then(t => regex.test(t) ? element : null);
+ });
+};
diff --git a/node_modules/selenium-webdriver/lib/webdriver.js b/node_modules/selenium-webdriver/lib/webdriver.js
new file mode 100644
index 000000000..13077b54e
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/webdriver.js
@@ -0,0 +1,2459 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+/**
+ * @fileoverview The heart of the WebDriver JavaScript API.
+ */
+
+'use strict';
+
+const actions = require('./actions');
+const by = require('./by');
+const Capabilities = require('./capabilities').Capabilities;
+const command = require('./command');
+const error = require('./error');
+const input = require('./input');
+const logging = require('./logging');
+const Session = require('./session').Session;
+const Symbols = require('./symbols');
+const promise = require('./promise');
+
+
+/**
+ * Defines a condition for use with WebDriver's {@linkplain WebDriver#wait wait
+ * command}.
+ *
+ * @template OUT
+ */
+class Condition {
+ /**
+ * @param {string} message A descriptive error message. Should complete the
+ * sentence "Waiting [...]"
+ * @param {function(!WebDriver): OUT} fn The condition function to
+ * evaluate on each iteration of the wait loop.
+ */
+ constructor(message, fn) {
+ /** @private {string} */
+ this.description_ = 'Waiting ' + message;
+
+ /** @type {function(!WebDriver): OUT} */
+ this.fn = fn;
+ }
+
+ /** @return {string} A description of this condition. */
+ description() {
+ return this.description_;
+ }
+}
+
+
+/**
+ * Defines a condition that will result in a {@link WebElement}.
+ *
+ * @extends {Condition<!(WebElement|promise.Promise<!WebElement>)>}
+ */
+class WebElementCondition extends Condition {
+ /**
+ * @param {string} message A descriptive error message. Should complete the
+ * sentence "Waiting [...]"
+ * @param {function(!WebDriver): !(WebElement|promise.Promise<!WebElement>)}
+ * fn The condition function to evaluate on each iteration of the wait
+ * loop.
+ */
+ constructor(message, fn) {
+ super(message, fn);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// WebDriver
+//
+//////////////////////////////////////////////////////////////////////////////
+
+
+/**
+ * Translates a command to its wire-protocol representation before passing it
+ * to the given `executor` for execution.
+ * @param {!command.Executor} executor The executor to use.
+ * @param {!command.Command} command The command to execute.
+ * @return {!Promise} A promise that will resolve with the command response.
+ */
+function executeCommand(executor, command) {
+ return toWireValue(command.getParameters()).
+ then(function(parameters) {
+ command.setParameters(parameters);
+ return executor.execute(command);
+ });
+}
+
+
+/**
+ * Converts an object to its JSON representation in the WebDriver wire protocol.
+ * When converting values of type object, the following steps will be taken:
+ * <ol>
+ * <li>if the object is a WebElement, the return value will be the element's
+ * server ID
+ * <li>if the object defines a {@link Symbols.serialize} method, this algorithm
+ * will be recursively applied to the object's serialized representation
+ * <li>if the object provides a "toJSON" function, this algorithm will
+ * recursively be applied to the result of that function
+ * <li>otherwise, the value of each key will be recursively converted according
+ * to the rules above.
+ * </ol>
+ *
+ * @param {*} obj The object to convert.
+ * @return {!Promise<?>} A promise that will resolve to the input value's JSON
+ * representation.
+ */
+function toWireValue(obj) {
+ if (promise.isPromise(obj)) {
+ return Promise.resolve(obj).then(toWireValue);
+ }
+ return Promise.resolve(convertValue(obj));
+}
+
+
+function convertValue(value) {
+ if (value === void 0 || value === null) {
+ return value;
+ }
+
+ if (typeof value === 'boolean'
+ || typeof value === 'number'
+ || typeof value === 'string') {
+ return value;
+ }
+
+ if (Array.isArray(value)) {
+ return convertKeys(value);
+ }
+
+ if (typeof value === 'function') {
+ return '' + value;
+ }
+
+ if (typeof value[Symbols.serialize] === 'function') {
+ return toWireValue(value[Symbols.serialize]());
+ } else if (typeof value.toJSON === 'function') {
+ return toWireValue(value.toJSON());
+ }
+ return convertKeys(value);
+}
+
+
+function convertKeys(obj) {
+ const isArray = Array.isArray(obj);
+ const numKeys = isArray ? obj.length : Object.keys(obj).length;
+ const ret = isArray ? new Array(numKeys) : {};
+ if (!numKeys) {
+ return Promise.resolve(ret);
+ }
+
+ let numResolved = 0;
+
+ function forEachKey(obj, fn) {
+ if (Array.isArray(obj)) {
+ for (let i = 0, n = obj.length; i < n; i++) {
+ fn(obj[i], i);
+ }
+ } else {
+ for (let key in obj) {
+ fn(obj[key], key);
+ }
+ }
+ }
+
+ return new Promise(function(done, reject) {
+ forEachKey(obj, function(value, key) {
+ if (promise.isPromise(value)) {
+ value.then(toWireValue).then(setValue, reject);
+ } else {
+ value = convertValue(value);
+ if (promise.isPromise(value)) {
+ value.then(toWireValue).then(setValue, reject);
+ } else {
+ setValue(value);
+ }
+ }
+
+ function setValue(value) {
+ ret[key] = value;
+ maybeFulfill();
+ }
+ });
+
+ function maybeFulfill() {
+ if (++numResolved === numKeys) {
+ done(ret);
+ }
+ }
+ });
+}
+
+
+/**
+ * Converts a value from its JSON representation according to the WebDriver wire
+ * protocol. Any JSON object that defines a WebElement ID will be decoded to a
+ * {@link WebElement} object. All other values will be passed through as is.
+ *
+ * @param {!WebDriver} driver The driver to use as the parent of any unwrapped
+ * {@link WebElement} values.
+ * @param {*} value The value to convert.
+ * @return {*} The converted value.
+ */
+function fromWireValue(driver, value) {
+ if (Array.isArray(value)) {
+ value = value.map(v => fromWireValue(driver, v));
+ } else if (WebElement.isId(value)) {
+ let id = WebElement.extractId(value);
+ value = new WebElement(driver, id);
+ } else if (value && typeof value === 'object') {
+ let result = {};
+ for (let key in value) {
+ if (value.hasOwnProperty(key)) {
+ result[key] = fromWireValue(driver, value[key]);
+ }
+ }
+ value = result;
+ }
+ return value;
+}
+
+
+/**
+ * Creates a new WebDriver client, which provides control over a browser.
+ *
+ * Every command.Command returns a {@link promise.Promise} that
+ * represents the result of that command. Callbacks may be registered on this
+ * object to manipulate the command result or catch an expected error. Any
+ * commands scheduled with a callback are considered sub-commands and will
+ * execute before the next command in the current frame. For example:
+ *
+ * var message = [];
+ * driver.call(message.push, message, 'a').then(function() {
+ * driver.call(message.push, message, 'b');
+ * });
+ * driver.call(message.push, message, 'c');
+ * driver.call(function() {
+ * alert('message is abc? ' + (message.join('') == 'abc'));
+ * });
+ *
+ */
+class WebDriver {
+ /**
+ * @param {!(Session|promise.Promise<!Session>)} session Either a
+ * known session or a promise that will be resolved to a session.
+ * @param {!command.Executor} executor The executor to use when sending
+ * commands to the browser.
+ * @param {promise.ControlFlow=} opt_flow The flow to
+ * schedule commands through. Defaults to the active flow object.
+ */
+ constructor(session, executor, opt_flow) {
+ /** @private {!promise.Promise<!Session>} */
+ this.session_ = promise.fulfilled(session);
+
+ /** @private {!command.Executor} */
+ this.executor_ = executor;
+
+ /** @private {!promise.ControlFlow} */
+ this.flow_ = opt_flow || promise.controlFlow();
+
+ /** @private {input.FileDetector} */
+ this.fileDetector_ = null;
+ }
+
+ /**
+ * Creates a new WebDriver client for an existing session.
+ * @param {!command.Executor} executor Command executor to use when querying
+ * for session details.
+ * @param {string} sessionId ID of the session to attach to.
+ * @param {promise.ControlFlow=} opt_flow The control flow all
+ * driver commands should execute under. Defaults to the
+ * {@link promise.controlFlow() currently active} control flow.
+ * @return {!WebDriver} A new client for the specified session.
+ */
+ static attachToSession(executor, sessionId, opt_flow) {
+ let flow = opt_flow || promise.controlFlow();
+ let cmd = new command.Command(command.Name.DESCRIBE_SESSION)
+ .setParameter('sessionId', sessionId);
+ let session = flow.execute(
+ () => executeCommand(executor, cmd).catch(err => {
+ // The DESCRIBE_SESSION command is not supported by the W3C spec, so
+ // if we get back an unknown command, just return a session with
+ // unknown capabilities.
+ if (err instanceof error.UnknownCommandError) {
+ return new Session(sessionId, new Capabilities);
+ }
+ throw err;
+ }),
+ 'WebDriver.attachToSession()');
+ return new WebDriver(session, executor, flow);
+ }
+
+ /**
+ * Creates a new WebDriver session.
+ *
+ * By default, the requested session `capabilities` are merely "desired" and
+ * the remote end will still create a new session even if it cannot satisfy
+ * all of the requested capabilities. You can query which capabilities a
+ * session actually has using the
+ * {@linkplain #getCapabilities() getCapabilities()} method on the returned
+ * WebDriver instance.
+ *
+ * To define _required capabilities_, provide the `capabilities` as an object
+ * literal with `required` and `desired` keys. The `desired` key may be
+ * omitted if all capabilities are required, and vice versa. If the server
+ * cannot create a session with all of the required capabilities, it will
+ * return an {@linkplain error.SessionNotCreatedError}.
+ *
+ * let required = new Capabilities().set('browserName', 'firefox');
+ * let desired = new Capabilities().set('version', '45');
+ * let driver = WebDriver.createSession(executor, {required, desired});
+ *
+ * This function will always return a WebDriver instance. If there is an error
+ * creating the session, such as the aforementioned SessionNotCreatedError,
+ * the driver will have a rejected {@linkplain #getSession session} promise.
+ * It is recommended that this promise is left _unhandled_ so it will
+ * propagate through the {@linkplain promise.ControlFlow control flow} and
+ * cause subsequent commands to fail.
+ *
+ * let required = Capabilities.firefox();
+ * let driver = WebDriver.createSession(executor, {required});
+ *
+ * // If the createSession operation failed, then this command will also
+ * // also fail, propagating the creation failure.
+ * driver.get('http://www.google.com').catch(e => console.log(e));
+ *
+ * @param {!command.Executor} executor The executor to create the new session
+ * with.
+ * @param {(!Capabilities|
+ * {desired: (Capabilities|undefined),
+ * required: (Capabilities|undefined)})} capabilities The desired
+ * capabilities for the new session.
+ * @param {promise.ControlFlow=} opt_flow The control flow all driver
+ * commands should execute under, including the initial session creation.
+ * Defaults to the {@link promise.controlFlow() currently active}
+ * control flow.
+ * @return {!WebDriver} The driver for the newly created session.
+ */
+ static createSession(executor, capabilities, opt_flow) {
+ let flow = opt_flow || promise.controlFlow();
+ let cmd = new command.Command(command.Name.NEW_SESSION);
+
+ if (capabilities && (capabilities.desired || capabilities.required)) {
+ cmd.setParameter('desiredCapabilities', capabilities.desired);
+ cmd.setParameter('requiredCapabilities', capabilities.required);
+ } else {
+ cmd.setParameter('desiredCapabilities', capabilities);
+ }
+
+ let session = flow.execute(
+ () => executeCommand(executor, cmd),
+ 'WebDriver.createSession()');
+ return new WebDriver(session, executor, flow);
+ }
+
+ /**
+ * @return {!promise.ControlFlow} The control flow used by this
+ * instance.
+ */
+ controlFlow() {
+ return this.flow_;
+ }
+
+ /**
+ * Schedules a {@link command.Command} to be executed by this driver's
+ * {@link command.Executor}.
+ *
+ * @param {!command.Command} command The command to schedule.
+ * @param {string} description A description of the command for debugging.
+ * @return {!promise.Promise<T>} A promise that will be resolved
+ * with the command result.
+ * @template T
+ */
+ schedule(command, description) {
+ var self = this;
+
+ checkHasNotQuit();
+ command.setParameter('sessionId', this.session_);
+
+ // If any of the command parameters are rejected promises, those
+ // rejections may be reported as unhandled before the control flow
+ // attempts to execute the command. To ensure parameters errors
+ // propagate through the command itself, we resolve all of the
+ // command parameters now, but suppress any errors until the ControlFlow
+ // actually executes the command. This addresses scenarios like catching
+ // an element not found error in:
+ //
+ // driver.findElement(By.id('foo')).click().catch(function(e) {
+ // if (e instanceof NoSuchElementError) {
+ // // Do something.
+ // }
+ // });
+ var prepCommand = toWireValue(command.getParameters());
+ prepCommand.catch(function() {});
+
+ var flow = this.flow_;
+ var executor = this.executor_;
+ return flow.execute(function() {
+ // A call to WebDriver.quit() may have been scheduled in the same event
+ // loop as this |command|, which would prevent us from detecting that the
+ // driver has quit above. Therefore, we need to make another quick check.
+ // We still check above so we can fail as early as possible.
+ checkHasNotQuit();
+
+ // Retrieve resolved command parameters; any previously suppressed errors
+ // will now propagate up through the control flow as part of the command
+ // execution.
+ return prepCommand.then(function(parameters) {
+ command.setParameters(parameters);
+ return executor.execute(command);
+ }).then(value => fromWireValue(self, value));
+ }, description);
+
+ function checkHasNotQuit() {
+ if (!self.session_) {
+ throw new error.NoSuchSessionError(
+ 'This driver instance does not have a valid session ID ' +
+ '(did you call WebDriver.quit()?) and may no longer be ' +
+ 'used.');
+ }
+ }
+ }
+
+ /**
+ * Sets the {@linkplain input.FileDetector file detector} that should be
+ * used with this instance.
+ * @param {input.FileDetector} detector The detector to use or {@code null}.
+ */
+ setFileDetector(detector) {
+ this.fileDetector_ = detector;
+ }
+
+ /**
+ * @return {!command.Executor} The command executor used by this instance.
+ */
+ getExecutor() {
+ return this.executor_;
+ }
+
+ /**
+ * @return {!promise.Promise<!Session>} A promise for this client's
+ * session.
+ */
+ getSession() {
+ return this.session_;
+ }
+
+ /**
+ * @return {!promise.Promise<!Capabilities>} A promise
+ * that will resolve with the this instance's capabilities.
+ */
+ getCapabilities() {
+ return this.session_.then(session => session.getCapabilities());
+ }
+
+ /**
+ * Schedules a command to quit the current session. After calling quit, this
+ * instance will be invalidated and may no longer be used to issue commands
+ * against the browser.
+ * @return {!promise.Promise<void>} A promise that will be resolved
+ * when the command has completed.
+ */
+ quit() {
+ var result = this.schedule(
+ new command.Command(command.Name.QUIT),
+ 'WebDriver.quit()');
+ // Delete our session ID when the quit command finishes; this will allow us
+ // to throw an error when attemnpting to use a driver post-quit.
+ return result.finally(() => delete this.session_);
+ }
+
+ /**
+ * Creates a new action sequence using this driver. The sequence will not be
+ * scheduled for execution until {@link actions.ActionSequence#perform} is
+ * called. Example:
+ *
+ * driver.actions().
+ * mouseDown(element1).
+ * mouseMove(element2).
+ * mouseUp().
+ * perform();
+ *
+ * @return {!actions.ActionSequence} A new action sequence for this instance.
+ */
+ actions() {
+ return new actions.ActionSequence(this);
+ }
+
+ /**
+ * Creates a new touch sequence using this driver. The sequence will not be
+ * scheduled for execution until {@link actions.TouchSequence#perform} is
+ * called. Example:
+ *
+ * driver.touchActions().
+ * tap(element1).
+ * doubleTap(element2).
+ * perform();
+ *
+ * @return {!actions.TouchSequence} A new touch sequence for this instance.
+ */
+ touchActions() {
+ return new actions.TouchSequence(this);
+ }
+
+ /**
+ * Schedules a command to execute JavaScript in the context of the currently
+ * selected frame or window. The script fragment will be executed as the body
+ * of an anonymous function. If the script is provided as a function object,
+ * that function will be converted to a string for injection into the target
+ * window.
+ *
+ * Any arguments provided in addition to the script will be included as script
+ * arguments and may be referenced using the {@code arguments} object.
+ * Arguments may be a boolean, number, string, or {@linkplain WebElement}.
+ * Arrays and objects may also be used as script arguments as long as each item
+ * adheres to the types previously mentioned.
+ *
+ * The script may refer to any variables accessible from the current window.
+ * Furthermore, the script will execute in the window's context, thus
+ * {@code document} may be used to refer to the current document. Any local
+ * variables will not be available once the script has finished executing,
+ * though global variables will persist.
+ *
+ * If the script has a return value (i.e. if the script contains a return
+ * statement), then the following steps will be taken for resolving this
+ * functions return value:
+ *
+ * - For a HTML element, the value will resolve to a {@linkplain WebElement}
+ * - Null and undefined return values will resolve to null</li>
+ * - Booleans, numbers, and strings will resolve as is</li>
+ * - Functions will resolve to their string representation</li>
+ * - For arrays and objects, each member item will be converted according to
+ * the rules above
+ *
+ * @param {!(string|Function)} script The script to execute.
+ * @param {...*} var_args The arguments to pass to the script.
+ * @return {!promise.Promise<T>} A promise that will resolve to the
+ * scripts return value.
+ * @template T
+ */
+ executeScript(script, var_args) {
+ if (typeof script === 'function') {
+ script = 'return (' + script + ').apply(null, arguments);';
+ }
+ let args =
+ arguments.length > 1 ? Array.prototype.slice.call(arguments, 1) : [];
+ return this.schedule(
+ new command.Command(command.Name.EXECUTE_SCRIPT).
+ setParameter('script', script).
+ setParameter('args', args),
+ 'WebDriver.executeScript()');
+ }
+
+ /**
+ * Schedules a command to execute asynchronous JavaScript in the context of the
+ * currently selected frame or window. The script fragment will be executed as
+ * the body of an anonymous function. If the script is provided as a function
+ * object, that function will be converted to a string for injection into the
+ * target window.
+ *
+ * Any arguments provided in addition to the script will be included as script
+ * arguments and may be referenced using the {@code arguments} object.
+ * Arguments may be a boolean, number, string, or {@code WebElement}.
+ * Arrays and objects may also be used as script arguments as long as each item
+ * adheres to the types previously mentioned.
+ *
+ * Unlike executing synchronous JavaScript with {@link #executeScript},
+ * scripts executed with this function must explicitly signal they are finished
+ * by invoking the provided callback. This callback will always be injected
+ * into the executed function as the last argument, and thus may be referenced
+ * with {@code arguments[arguments.length - 1]}. The following steps will be
+ * taken for resolving this functions return value against the first argument
+ * to the script's callback function:
+ *
+ * - For a HTML element, the value will resolve to a
+ * {@link WebElement}
+ * - Null and undefined return values will resolve to null
+ * - Booleans, numbers, and strings will resolve as is
+ * - Functions will resolve to their string representation
+ * - For arrays and objects, each member item will be converted according to
+ * the rules above
+ *
+ * __Example #1:__ Performing a sleep that is synchronized with the currently
+ * selected window:
+ *
+ * var start = new Date().getTime();
+ * driver.executeAsyncScript(
+ * 'window.setTimeout(arguments[arguments.length - 1], 500);').
+ * then(function() {
+ * console.log(
+ * 'Elapsed time: ' + (new Date().getTime() - start) + ' ms');
+ * });
+ *
+ * __Example #2:__ Synchronizing a test with an AJAX application:
+ *
+ * var button = driver.findElement(By.id('compose-button'));
+ * button.click();
+ * driver.executeAsyncScript(
+ * 'var callback = arguments[arguments.length - 1];' +
+ * 'mailClient.getComposeWindowWidget().onload(callback);');
+ * driver.switchTo().frame('composeWidget');
+ * driver.findElement(By.id('to')).sendKeys('dog@example.com');
+ *
+ * __Example #3:__ Injecting a XMLHttpRequest and waiting for the result. In
+ * this example, the inject script is specified with a function literal. When
+ * using this format, the function is converted to a string for injection, so it
+ * should not reference any symbols not defined in the scope of the page under
+ * test.
+ *
+ * driver.executeAsyncScript(function() {
+ * var callback = arguments[arguments.length - 1];
+ * var xhr = new XMLHttpRequest();
+ * xhr.open("GET", "/resource/data.json", true);
+ * xhr.onreadystatechange = function() {
+ * if (xhr.readyState == 4) {
+ * callback(xhr.responseText);
+ * }
+ * };
+ * xhr.send('');
+ * }).then(function(str) {
+ * console.log(JSON.parse(str)['food']);
+ * });
+ *
+ * @param {!(string|Function)} script The script to execute.
+ * @param {...*} var_args The arguments to pass to the script.
+ * @return {!promise.Promise<T>} A promise that will resolve to the
+ * scripts return value.
+ * @template T
+ */
+ executeAsyncScript(script, var_args) {
+ if (typeof script === 'function') {
+ script = 'return (' + script + ').apply(null, arguments);';
+ }
+ let args = Array.prototype.slice.call(arguments, 1);
+ return this.schedule(
+ new command.Command(command.Name.EXECUTE_ASYNC_SCRIPT).
+ setParameter('script', script).
+ setParameter('args', args),
+ 'WebDriver.executeScript()');
+ }
+
+ /**
+ * Schedules a command to execute a custom function.
+ * @param {function(...): (T|promise.Promise<T>)} fn The function to
+ * execute.
+ * @param {Object=} opt_scope The object in whose scope to execute the function.
+ * @param {...*} var_args Any arguments to pass to the function.
+ * @return {!promise.Promise<T>} A promise that will be resolved'
+ * with the function's result.
+ * @template T
+ */
+ call(fn, opt_scope, var_args) {
+ let args = Array.prototype.slice.call(arguments, 2);
+ let flow = this.flow_;
+ return flow.execute(function() {
+ return promise.fullyResolved(args).then(function(args) {
+ if (promise.isGenerator(fn)) {
+ args.unshift(fn, opt_scope);
+ return promise.consume.apply(null, args);
+ }
+ return fn.apply(opt_scope, args);
+ });
+ }, 'WebDriver.call(' + (fn.name || 'function') + ')');
+ }
+
+ /**
+ * Schedules a command to wait for a condition to hold. The condition may be
+ * specified by a {@link Condition}, as a custom function, or as any
+ * promise-like thenable.
+ *
+ * For a {@link Condition} or function, the wait will repeatedly
+ * evaluate the condition until it returns a truthy value. If any errors occur
+ * while evaluating the condition, they will be allowed to propagate. In the
+ * event a condition returns a {@link promise.Promise promise}, the polling
+ * loop will wait for it to be resolved and use the resolved value for whether
+ * the condition has been satisified. Note the resolution time for a promise
+ * is factored into whether a wait has timed out.
+ *
+ * Note, if the provided condition is a {@link WebElementCondition}, then
+ * the wait will return a {@link WebElementPromise} that will resolve to the
+ * element that satisified the condition.
+ *
+ * _Example:_ waiting up to 10 seconds for an element to be present on the
+ * page.
+ *
+ * var button = driver.wait(until.elementLocated(By.id('foo')), 10000);
+ * button.click();
+ *
+ * This function may also be used to block the command flow on the resolution
+ * of any thenable promise object. When given a promise, the command will
+ * simply wait for its resolution before completing. A timeout may be provided
+ * to fail the command if the promise does not resolve before the timeout
+ * expires.
+ *
+ * _Example:_ Suppose you have a function, `startTestServer`, that returns a
+ * promise for when a server is ready for requests. You can block a WebDriver
+ * client on this promise with:
+ *
+ * var started = startTestServer();
+ * driver.wait(started, 5 * 1000, 'Server should start within 5 seconds');
+ * driver.get(getServerUrl());
+ *
+ * @param {!(promise.Promise<T>|
+ * Condition<T>|
+ * function(!WebDriver): T)} condition The condition to
+ * wait on, defined as a promise, condition object, or a function to
+ * evaluate as a condition.
+ * @param {number=} opt_timeout How long to wait for the condition to be true.
+ * @param {string=} opt_message An optional message to use if the wait times
+ * out.
+ * @return {!(promise.Promise<T>|WebElementPromise)} A promise that will be
+ * resolved with the first truthy value returned by the condition
+ * function, or rejected if the condition times out. If the input
+ * input condition is an instance of a {@link WebElementCondition},
+ * the returned value will be a {@link WebElementPromise}.
+ * @template T
+ */
+ wait(condition, opt_timeout, opt_message) {
+ if (promise.isPromise(condition)) {
+ return this.flow_.wait(
+ /** @type {!promise.Promise} */(condition),
+ opt_timeout, opt_message);
+ }
+
+ var message = opt_message;
+ var fn = /** @type {!Function} */(condition);
+ if (condition instanceof Condition) {
+ message = message || condition.description();
+ fn = condition.fn;
+ }
+
+ var driver = this;
+ var result = this.flow_.wait(function() {
+ if (promise.isGenerator(fn)) {
+ return promise.consume(fn, null, [driver]);
+ }
+ return fn(driver);
+ }, opt_timeout, message);
+
+ if (condition instanceof WebElementCondition) {
+ result = new WebElementPromise(this, result.then(function(value) {
+ if (!(value instanceof WebElement)) {
+ throw TypeError(
+ 'WebElementCondition did not resolve to a WebElement: '
+ + Object.prototype.toString.call(value));
+ }
+ return value;
+ }));
+ }
+ return result;
+ }
+
+ /**
+ * Schedules a command to make the driver sleep for the given amount of time.
+ * @param {number} ms The amount of time, in milliseconds, to sleep.
+ * @return {!promise.Promise<void>} A promise that will be resolved
+ * when the sleep has finished.
+ */
+ sleep(ms) {
+ return this.flow_.timeout(ms, 'WebDriver.sleep(' + ms + ')');
+ }
+
+ /**
+ * Schedules a command to retrieve the current window handle.
+ * @return {!promise.Promise<string>} A promise that will be
+ * resolved with the current window handle.
+ */
+ getWindowHandle() {
+ return this.schedule(
+ new command.Command(command.Name.GET_CURRENT_WINDOW_HANDLE),
+ 'WebDriver.getWindowHandle()');
+ }
+
+ /**
+ * Schedules a command to retrieve the current list of available window handles.
+ * @return {!promise.Promise.<!Array<string>>} A promise that will
+ * be resolved with an array of window handles.
+ */
+ getAllWindowHandles() {
+ return this.schedule(
+ new command.Command(command.Name.GET_WINDOW_HANDLES),
+ 'WebDriver.getAllWindowHandles()');
+ }
+
+ /**
+ * Schedules a command to retrieve the current page's source. The page source
+ * returned is a representation of the underlying DOM: do not expect it to be
+ * formatted or escaped in the same way as the response sent from the web
+ * server.
+ * @return {!promise.Promise<string>} A promise that will be
+ * resolved with the current page source.
+ */
+ getPageSource() {
+ return this.schedule(
+ new command.Command(command.Name.GET_PAGE_SOURCE),
+ 'WebDriver.getPageSource()');
+ }
+
+ /**
+ * Schedules a command to close the current window.
+ * @return {!promise.Promise<void>} A promise that will be resolved
+ * when this command has completed.
+ */
+ close() {
+ return this.schedule(new command.Command(command.Name.CLOSE),
+ 'WebDriver.close()');
+ }
+
+ /**
+ * Schedules a command to navigate to the given URL.
+ * @param {string} url The fully qualified URL to open.
+ * @return {!promise.Promise<void>} A promise that will be resolved
+ * when the document has finished loading.
+ */
+ get(url) {
+ return this.navigate().to(url);
+ }
+
+ /**
+ * Schedules a command to retrieve the URL of the current page.
+ * @return {!promise.Promise<string>} A promise that will be
+ * resolved with the current URL.
+ */
+ getCurrentUrl() {
+ return this.schedule(
+ new command.Command(command.Name.GET_CURRENT_URL),
+ 'WebDriver.getCurrentUrl()');
+ }
+
+ /**
+ * Schedules a command to retrieve the current page's title.
+ * @return {!promise.Promise<string>} A promise that will be
+ * resolved with the current page's title.
+ */
+ getTitle() {
+ return this.schedule(new command.Command(command.Name.GET_TITLE),
+ 'WebDriver.getTitle()');
+ }
+
+ /**
+ * Schedule a command to find an element on the page. If the element cannot be
+ * found, a {@link bot.ErrorCode.NO_SUCH_ELEMENT} result will be returned
+ * by the driver. Unlike other commands, this error cannot be suppressed. In
+ * other words, scheduling a command to find an element doubles as an assert
+ * that the element is present on the page. To test whether an element is
+ * present on the page, use {@link #isElementPresent} instead.
+ *
+ * The search criteria for an element may be defined using one of the
+ * factories in the {@link webdriver.By} namespace, or as a short-hand
+ * {@link webdriver.By.Hash} object. For example, the following two statements
+ * are equivalent:
+ *
+ * var e1 = driver.findElement(By.id('foo'));
+ * var e2 = driver.findElement({id:'foo'});
+ *
+ * You may also provide a custom locator function, which takes as input this
+ * instance and returns a {@link WebElement}, or a promise that will resolve
+ * to a WebElement. If the returned promise resolves to an array of
+ * WebElements, WebDriver will use the first element. For example, to find the
+ * first visible link on a page, you could write:
+ *
+ * var link = driver.findElement(firstVisibleLink);
+ *
+ * function firstVisibleLink(driver) {
+ * var links = driver.findElements(By.tagName('a'));
+ * return promise.filter(links, function(link) {
+ * return link.isDisplayed();
+ * });
+ * }
+ *
+ * @param {!(by.By|Function)} locator The locator to use.
+ * @return {!WebElementPromise} A WebElement that can be used to issue
+ * commands against the located element. If the element is not found, the
+ * element will be invalidated and all scheduled commands aborted.
+ */
+ findElement(locator) {
+ let id;
+ locator = by.checkedLocator(locator);
+ if (typeof locator === 'function') {
+ id = this.findElementInternal_(locator, this);
+ } else {
+ let cmd = new command.Command(command.Name.FIND_ELEMENT).
+ setParameter('using', locator.using).
+ setParameter('value', locator.value);
+ id = this.schedule(cmd, 'WebDriver.findElement(' + locator + ')');
+ }
+ return new WebElementPromise(this, id);
+ }
+
+ /**
+ * @param {!Function} locatorFn The locator function to use.
+ * @param {!(WebDriver|WebElement)} context The search
+ * context.
+ * @return {!promise.Promise.<!WebElement>} A
+ * promise that will resolve to a list of WebElements.
+ * @private
+ */
+ findElementInternal_(locatorFn, context) {
+ return this.call(() => locatorFn(context)).then(function(result) {
+ if (Array.isArray(result)) {
+ result = result[0];
+ }
+ if (!(result instanceof WebElement)) {
+ throw new TypeError('Custom locator did not return a WebElement');
+ }
+ return result;
+ });
+ }
+
+ /**
+ * Schedule a command to search for multiple elements on the page.
+ *
+ * @param {!(by.By|Function)} locator The locator to use.
+ * @return {!promise.Promise.<!Array.<!WebElement>>} A
+ * promise that will resolve to an array of WebElements.
+ */
+ findElements(locator) {
+ locator = by.checkedLocator(locator);
+ if (typeof locator === 'function') {
+ return this.findElementsInternal_(locator, this);
+ } else {
+ let cmd = new command.Command(command.Name.FIND_ELEMENTS).
+ setParameter('using', locator.using).
+ setParameter('value', locator.value);
+ let res = this.schedule(cmd, 'WebDriver.findElements(' + locator + ')');
+ return res.catch(function(e) {
+ if (e instanceof error.NoSuchElementError) {
+ return [];
+ }
+ throw e;
+ });
+ }
+ }
+
+ /**
+ * @param {!Function} locatorFn The locator function to use.
+ * @param {!(WebDriver|WebElement)} context The search context.
+ * @return {!promise.Promise<!Array<!WebElement>>} A promise that
+ * will resolve to an array of WebElements.
+ * @private
+ */
+ findElementsInternal_(locatorFn, context) {
+ return this.call(() => locatorFn(context)).then(function(result) {
+ if (result instanceof WebElement) {
+ return [result];
+ }
+
+ if (!Array.isArray(result)) {
+ return [];
+ }
+
+ return result.filter(function(item) {
+ return item instanceof WebElement;
+ });
+ });
+ }
+
+ /**
+ * Schedule a command to take a screenshot. The driver makes a best effort to
+ * return a screenshot of the following, in order of preference:
+ *
+ * 1. Entire page
+ * 2. Current window
+ * 3. Visible portion of the current frame
+ * 4. The entire display containing the browser
+ *
+ * @return {!promise.Promise<string>} A promise that will be
+ * resolved to the screenshot as a base-64 encoded PNG.
+ */
+ takeScreenshot() {
+ return this.schedule(new command.Command(command.Name.SCREENSHOT),
+ 'WebDriver.takeScreenshot()');
+ }
+
+ /**
+ * @return {!Options} The options interface for this instance.
+ */
+ manage() {
+ return new Options(this);
+ }
+
+ /**
+ * @return {!Navigation} The navigation interface for this instance.
+ */
+ navigate() {
+ return new Navigation(this);
+ }
+
+ /**
+ * @return {!TargetLocator} The target locator interface for this
+ * instance.
+ */
+ switchTo() {
+ return new TargetLocator(this);
+ }
+}
+
+
+/**
+ * Interface for navigating back and forth in the browser history.
+ *
+ * This class should never be instantiated directly. Insead, obtain an instance
+ * with
+ *
+ * webdriver.navigate()
+ *
+ * @see WebDriver#navigate()
+ */
+class Navigation {
+ /**
+ * @param {!WebDriver} driver The parent driver.
+ * @private
+ */
+ constructor(driver) {
+ /** @private {!WebDriver} */
+ this.driver_ = driver;
+ }
+
+ /**
+ * Schedules a command to navigate to a new URL.
+ * @param {string} url The URL to navigate to.
+ * @return {!promise.Promise<void>} A promise that will be resolved
+ * when the URL has been loaded.
+ */
+ to(url) {
+ return this.driver_.schedule(
+ new command.Command(command.Name.GET).
+ setParameter('url', url),
+ 'WebDriver.navigate().to(' + url + ')');
+ }
+
+ /**
+ * Schedules a command to move backwards in the browser history.
+ * @return {!promise.Promise<void>} A promise that will be resolved
+ * when the navigation event has completed.
+ */
+ back() {
+ return this.driver_.schedule(
+ new command.Command(command.Name.GO_BACK),
+ 'WebDriver.navigate().back()');
+ }
+
+ /**
+ * Schedules a command to move forwards in the browser history.
+ * @return {!promise.Promise<void>} A promise that will be resolved
+ * when the navigation event has completed.
+ */
+ forward() {
+ return this.driver_.schedule(
+ new command.Command(command.Name.GO_FORWARD),
+ 'WebDriver.navigate().forward()');
+ }
+
+ /**
+ * Schedules a command to refresh the current page.
+ * @return {!promise.Promise<void>} A promise that will be resolved
+ * when the navigation event has completed.
+ */
+ refresh() {
+ return this.driver_.schedule(
+ new command.Command(command.Name.REFRESH),
+ 'WebDriver.navigate().refresh()');
+ }
+}
+
+
+/**
+ * Provides methods for managing browser and driver state.
+ *
+ * This class should never be instantiated directly. Insead, obtain an instance
+ * with {@linkplain WebDriver#manage() webdriver.manage()}.
+ */
+class Options {
+ /**
+ * @param {!WebDriver} driver The parent driver.
+ * @private
+ */
+ constructor(driver) {
+ /** @private {!WebDriver} */
+ this.driver_ = driver;
+ }
+
+ /**
+ * Schedules a command to add a cookie.
+ *
+ * __Sample Usage:__
+ *
+ * // Set a basic cookie.
+ * driver.options().addCookie({name: 'foo', value: 'bar'});
+ *
+ * // Set a cookie that expires in 10 minutes.
+ * let expiry = new Date(Date.now() + (10 * 60 * 1000));
+ * driver.options().addCookie({name: 'foo', value: 'bar', expiry});
+ *
+ * // The cookie expiration may also be specified in seconds since epoch.
+ * driver.options().addCookie({
+ * name: 'foo',
+ * value: 'bar',
+ * expiry: Math.floor(Date.now() / 1000)
+ * });
+ *
+ * @param {!Options.Cookie} spec Defines the cookie to add.
+ * @return {!promise.Promise<void>} A promise that will be resolved
+ * when the cookie has been added to the page.
+ * @throws {error.InvalidArgumentError} if any of the cookie parameters are
+ * invalid.
+ * @throws {TypeError} if `spec` is not a cookie object.
+ */
+ addCookie(spec) {
+ if (!spec || typeof spec !== 'object') {
+ throw TypeError('addCookie called with non-cookie parameter');
+ }
+
+ // We do not allow '=' or ';' in the name.
+ let name = spec.name;
+ if (/[;=]/.test(name)) {
+ throw new error.InvalidArgumentError(
+ 'Invalid cookie name "' + name + '"');
+ }
+
+ // We do not allow ';' in value.
+ let value = spec.value;
+ if (/;/.test(value)) {
+ throw new error.InvalidArgumentError(
+ 'Invalid cookie value "' + value + '"');
+ }
+
+ let cookieString = name + '=' + value +
+ (spec.domain ? ';domain=' + spec.domain : '') +
+ (spec.path ? ';path=' + spec.path : '') +
+ (spec.secure ? ';secure' : '');
+
+ let expiry;
+ if (typeof spec.expiry === 'number') {
+ expiry = Math.floor(spec.expiry);
+ cookieString += ';expires=' + new Date(spec.expiry * 1000).toUTCString();
+ } else if (spec.expiry instanceof Date) {
+ let date = /** @type {!Date} */(spec.expiry);
+ expiry = Math.floor(date.getTime() / 1000);
+ cookieString += ';expires=' + date.toUTCString();
+ }
+
+ return this.driver_.schedule(
+ new command.Command(command.Name.ADD_COOKIE).
+ setParameter('cookie', {
+ 'name': name,
+ 'value': value,
+ 'path': spec.path,
+ 'domain': spec.domain,
+ 'secure': !!spec.secure,
+ 'expiry': expiry
+ }),
+ 'WebDriver.manage().addCookie(' + cookieString + ')');
+ }
+
+ /**
+ * Schedules a command to delete all cookies visible to the current page.
+ * @return {!promise.Promise<void>} A promise that will be resolved
+ * when all cookies have been deleted.
+ */
+ deleteAllCookies() {
+ return this.driver_.schedule(
+ new command.Command(command.Name.DELETE_ALL_COOKIES),
+ 'WebDriver.manage().deleteAllCookies()');
+ }
+
+ /**
+ * Schedules a command to delete the cookie with the given name. This command
+ * is a no-op if there is no cookie with the given name visible to the current
+ * page.
+ * @param {string} name The name of the cookie to delete.
+ * @return {!promise.Promise<void>} A promise that will be resolved
+ * when the cookie has been deleted.
+ */
+ deleteCookie(name) {
+ return this.driver_.schedule(
+ new command.Command(command.Name.DELETE_COOKIE).
+ setParameter('name', name),
+ 'WebDriver.manage().deleteCookie(' + name + ')');
+ }
+
+ /**
+ * Schedules a command to retrieve all cookies visible to the current page.
+ * Each cookie will be returned as a JSON object as described by the WebDriver
+ * wire protocol.
+ * @return {!promise.Promise<!Array<!Options.Cookie>>} A promise that will be
+ * resolved with the cookies visible to the current browsing context.
+ */
+ getCookies() {
+ return this.driver_.schedule(
+ new command.Command(command.Name.GET_ALL_COOKIES),
+ 'WebDriver.manage().getCookies()');
+ }
+
+ /**
+ * Schedules a command to retrieve the cookie with the given name. Returns null
+ * if there is no such cookie. The cookie will be returned as a JSON object as
+ * described by the WebDriver wire protocol.
+ *
+ * @param {string} name The name of the cookie to retrieve.
+ * @return {!promise.Promise<?Options.Cookie>} A promise that will be resolved
+ * with the named cookie, or `null` if there is no such cookie.
+ */
+ getCookie(name) {
+ return this.getCookies().then(function(cookies) {
+ for (let cookie of cookies) {
+ if (cookie && cookie['name'] === name) {
+ return cookie;
+ }
+ }
+ return null;
+ });
+ }
+
+ /**
+ * @return {!Logs} The interface for managing driver
+ * logs.
+ */
+ logs() {
+ return new Logs(this.driver_);
+ }
+
+ /**
+ * @return {!Timeouts} The interface for managing driver timeouts.
+ */
+ timeouts() {
+ return new Timeouts(this.driver_);
+ }
+
+ /**
+ * @return {!Window} The interface for managing the current window.
+ */
+ window() {
+ return new Window(this.driver_);
+ }
+}
+
+
+/**
+ * A record object describing a browser cookie.
+ *
+ * @record
+ */
+Options.Cookie = function() {};
+
+
+/**
+ * The name of the cookie.
+ *
+ * @type {string}
+ */
+Options.Cookie.prototype.name;
+
+
+/**
+ * The cookie value.
+ *
+ * @type {string}
+ */
+Options.Cookie.prototype.value;
+
+
+/**
+ * The cookie path. Defaults to "/" when adding a cookie.
+ *
+ * @type {(string|undefined)}
+ */
+Options.Cookie.prototype.path;
+
+
+/**
+ * The domain the cookie is visible to. Defaults to the current browsing
+ * context's document's URL when adding a cookie.
+ *
+ * @type {(string|undefined)}
+ */
+Options.Cookie.prototype.domain;
+
+
+/**
+ * Whether the cookie is a secure cookie. Defaults to false when adding a new
+ * cookie.
+ *
+ * @type {(boolean|undefined)}
+ */
+Options.Cookie.prototype.secure;
+
+
+/**
+ * Whether the cookie is an HTTP only cookie. Defaults to false when adding a
+ * new cookie.
+ *
+ * @type {(boolean|undefined)}
+ */
+Options.Cookie.prototype.httpOnly;
+
+
+/**
+ * When the cookie expires.
+ *
+ * When {@linkplain Options#addCookie() adding a cookie}, this may be specified
+ * in _seconds_ since Unix epoch (January 1, 1970). The expiry will default to
+ * 20 years in the future if omitted.
+ *
+ * The expiry is always returned in seconds since epoch when
+ * {@linkplain Options#getCookies() retrieving cookies} from the browser.
+ *
+ * @type {(!Date|number|undefined)}
+ */
+Options.Cookie.prototype.expiry;
+
+
+/**
+ * An interface for managing timeout behavior for WebDriver instances.
+ *
+ * This class should never be instantiated directly. Insead, obtain an instance
+ * with
+ *
+ * webdriver.manage().timeouts()
+ *
+ * @see WebDriver#manage()
+ * @see Options#timeouts()
+ */
+class Timeouts {
+ /**
+ * @param {!WebDriver} driver The parent driver.
+ * @private
+ */
+ constructor(driver) {
+ /** @private {!WebDriver} */
+ this.driver_ = driver;
+ }
+
+ /**
+ * Specifies the amount of time the driver should wait when searching for an
+ * element if it is not immediately present.
+ *
+ * When searching for a single element, the driver should poll the page
+ * until the element has been found, or this timeout expires before failing
+ * with a {@link bot.ErrorCode.NO_SUCH_ELEMENT} error. When searching
+ * for multiple elements, the driver should poll the page until at least one
+ * element has been found or this timeout has expired.
+ *
+ * Setting the wait timeout to 0 (its default value), disables implicit
+ * waiting.
+ *
+ * Increasing the implicit wait timeout should be used judiciously as it
+ * will have an adverse effect on test run time, especially when used with
+ * slower location strategies like XPath.
+ *
+ * @param {number} ms The amount of time to wait, in milliseconds.
+ * @return {!promise.Promise<void>} A promise that will be resolved
+ * when the implicit wait timeout has been set.
+ */
+ implicitlyWait(ms) {
+ return this._scheduleCommand(ms, 'implicit', 'implicitlyWait');
+ }
+
+ /**
+ * Sets the amount of time to wait, in milliseconds, for an asynchronous
+ * script to finish execution before returning an error. If the timeout is
+ * less than or equal to 0, the script will be allowed to run indefinitely.
+ *
+ * @param {number} ms The amount of time to wait, in milliseconds.
+ * @return {!promise.Promise<void>} A promise that will be resolved
+ * when the script timeout has been set.
+ */
+ setScriptTimeout(ms) {
+ return this._scheduleCommand(ms, 'script', 'setScriptTimeout');
+ }
+
+ /**
+ * Sets the amount of time to wait for a page load to complete before
+ * returning an error. If the timeout is negative, page loads may be
+ * indefinite.
+ *
+ * @param {number} ms The amount of time to wait, in milliseconds.
+ * @return {!promise.Promise<void>} A promise that will be resolved
+ * when the timeout has been set.
+ */
+ pageLoadTimeout(ms) {
+ return this._scheduleCommand(ms, 'page load', 'pageLoadTimeout');
+ }
+
+ _scheduleCommand(ms, timeoutIdentifier, timeoutName) {
+ return this.driver_.schedule(
+ new command.Command(command.Name.SET_TIMEOUT).
+ setParameter('type', timeoutIdentifier).
+ setParameter('ms', ms),
+ `WebDriver.manage().timeouts().${timeoutName}(${ms})`);
+ }
+}
+
+
+/**
+ * An interface for managing the current window.
+ *
+ * This class should never be instantiated directly. Insead, obtain an instance
+ * with
+ *
+ * webdriver.manage().window()
+ *
+ * @see WebDriver#manage()
+ * @see Options#window()
+ */
+class Window {
+ /**
+ * @param {!WebDriver} driver The parent driver.
+ * @private
+ */
+ constructor(driver) {
+ /** @private {!WebDriver} */
+ this.driver_ = driver;
+ }
+
+ /**
+ * Retrieves the window's current position, relative to the top left corner of
+ * the screen.
+ * @return {!promise.Promise.<{x: number, y: number}>} A promise
+ * that will be resolved with the window's position in the form of a
+ * {x:number, y:number} object literal.
+ */
+ getPosition() {
+ return this.driver_.schedule(
+ new command.Command(command.Name.GET_WINDOW_POSITION).
+ setParameter('windowHandle', 'current'),
+ 'WebDriver.manage().window().getPosition()');
+ }
+
+ /**
+ * Repositions the current window.
+ * @param {number} x The desired horizontal position, relative to the left
+ * side of the screen.
+ * @param {number} y The desired vertical position, relative to the top of the
+ * of the screen.
+ * @return {!promise.Promise<void>} A promise that will be resolved
+ * when the command has completed.
+ */
+ setPosition(x, y) {
+ return this.driver_.schedule(
+ new command.Command(command.Name.SET_WINDOW_POSITION).
+ setParameter('windowHandle', 'current').
+ setParameter('x', x).
+ setParameter('y', y),
+ 'WebDriver.manage().window().setPosition(' + x + ', ' + y + ')');
+ }
+
+ /**
+ * Retrieves the window's current size.
+ * @return {!promise.Promise<{width: number, height: number}>} A
+ * promise that will be resolved with the window's size in the form of a
+ * {width:number, height:number} object literal.
+ */
+ getSize() {
+ return this.driver_.schedule(
+ new command.Command(command.Name.GET_WINDOW_SIZE).
+ setParameter('windowHandle', 'current'),
+ 'WebDriver.manage().window().getSize()');
+ }
+
+ /**
+ * Resizes the current window.
+ * @param {number} width The desired window width.
+ * @param {number} height The desired window height.
+ * @return {!promise.Promise<void>} A promise that will be resolved
+ * when the command has completed.
+ */
+ setSize(width, height) {
+ return this.driver_.schedule(
+ new command.Command(command.Name.SET_WINDOW_SIZE).
+ setParameter('windowHandle', 'current').
+ setParameter('width', width).
+ setParameter('height', height),
+ 'WebDriver.manage().window().setSize(' + width + ', ' + height + ')');
+ }
+
+ /**
+ * Maximizes the current window.
+ * @return {!promise.Promise<void>} A promise that will be resolved
+ * when the command has completed.
+ */
+ maximize() {
+ return this.driver_.schedule(
+ new command.Command(command.Name.MAXIMIZE_WINDOW).
+ setParameter('windowHandle', 'current'),
+ 'WebDriver.manage().window().maximize()');
+ }
+}
+
+
+/**
+ * Interface for managing WebDriver log records.
+ *
+ * This class should never be instantiated directly. Instead, obtain an
+ * instance with
+ *
+ * webdriver.manage().logs()
+ *
+ * @see WebDriver#manage()
+ * @see Options#logs()
+ */
+class Logs {
+ /**
+ * @param {!WebDriver} driver The parent driver.
+ * @private
+ */
+ constructor(driver) {
+ /** @private {!WebDriver} */
+ this.driver_ = driver;
+ }
+
+ /**
+ * Fetches available log entries for the given type.
+ *
+ * Note that log buffers are reset after each call, meaning that available
+ * log entries correspond to those entries not yet returned for a given log
+ * type. In practice, this means that this call will return the available log
+ * entries since the last call, or from the start of the session.
+ *
+ * @param {!logging.Type} type The desired log type.
+ * @return {!promise.Promise.<!Array.<!logging.Entry>>} A
+ * promise that will resolve to a list of log entries for the specified
+ * type.
+ */
+ get(type) {
+ let cmd = new command.Command(command.Name.GET_LOG).
+ setParameter('type', type);
+ return this.driver_.schedule(
+ cmd, 'WebDriver.manage().logs().get(' + type + ')').
+ then(function(entries) {
+ return entries.map(function(entry) {
+ if (!(entry instanceof logging.Entry)) {
+ return new logging.Entry(
+ entry['level'], entry['message'], entry['timestamp'],
+ entry['type']);
+ }
+ return entry;
+ });
+ });
+ }
+
+ /**
+ * Retrieves the log types available to this driver.
+ * @return {!promise.Promise<!Array<!logging.Type>>} A
+ * promise that will resolve to a list of available log types.
+ */
+ getAvailableLogTypes() {
+ return this.driver_.schedule(
+ new command.Command(command.Name.GET_AVAILABLE_LOG_TYPES),
+ 'WebDriver.manage().logs().getAvailableLogTypes()');
+ }
+}
+
+
+/**
+ * An interface for changing the focus of the driver to another frame or window.
+ *
+ * This class should never be instantiated directly. Instead, obtain an
+ * instance with
+ *
+ * webdriver.switchTo()
+ *
+ * @see WebDriver#switchTo()
+ */
+class TargetLocator {
+ /**
+ * @param {!WebDriver} driver The parent driver.
+ * @private
+ */
+ constructor(driver) {
+ /** @private {!WebDriver} */
+ this.driver_ = driver;
+ }
+
+ /**
+ * Schedules a command retrieve the {@code document.activeElement} element on
+ * the current document, or {@code document.body} if activeElement is not
+ * available.
+ * @return {!WebElementPromise} The active element.
+ */
+ activeElement() {
+ var id = this.driver_.schedule(
+ new command.Command(command.Name.GET_ACTIVE_ELEMENT),
+ 'WebDriver.switchTo().activeElement()');
+ return new WebElementPromise(this.driver_, id);
+ }
+
+ /**
+ * Schedules a command to switch focus of all future commands to the topmost
+ * frame on the page.
+ * @return {!promise.Promise<void>} A promise that will be resolved
+ * when the driver has changed focus to the default content.
+ */
+ defaultContent() {
+ return this.driver_.schedule(
+ new command.Command(command.Name.SWITCH_TO_FRAME).
+ setParameter('id', null),
+ 'WebDriver.switchTo().defaultContent()');
+ }
+
+ /**
+ * Schedules a command to switch the focus of all future commands to another
+ * frame on the page. The target frame may be specified as one of the
+ * following:
+ *
+ * - A number that specifies a (zero-based) index into [window.frames](
+ * https://developer.mozilla.org/en-US/docs/Web/API/Window.frames).
+ * - A {@link WebElement} reference, which correspond to a `frame` or `iframe`
+ * DOM element.
+ * - The `null` value, to select the topmost frame on the page. Passing `null`
+ * is the same as calling {@link #defaultContent defaultContent()}.
+ *
+ * If the specified frame can not be found, the returned promise will be
+ * rejected with a {@linkplain error.NoSuchFrameError}.
+ *
+ * @param {(number|WebElement|null)} id The frame locator.
+ * @return {!promise.Promise<void>} A promise that will be resolved
+ * when the driver has changed focus to the specified frame.
+ */
+ frame(id) {
+ return this.driver_.schedule(
+ new command.Command(command.Name.SWITCH_TO_FRAME).
+ setParameter('id', id),
+ 'WebDriver.switchTo().frame(' + id + ')');
+ }
+
+ /**
+ * Schedules a command to switch the focus of all future commands to another
+ * window. Windows may be specified by their {@code window.name} attribute or
+ * by its handle (as returned by {@link WebDriver#getWindowHandles}).
+ *
+ * If the specified window cannot be found, the returned promise will be
+ * rejected with a {@linkplain error.NoSuchWindowError}.
+ *
+ * @param {string} nameOrHandle The name or window handle of the window to
+ * switch focus to.
+ * @return {!promise.Promise<void>} A promise that will be resolved
+ * when the driver has changed focus to the specified window.
+ */
+ window(nameOrHandle) {
+ return this.driver_.schedule(
+ new command.Command(command.Name.SWITCH_TO_WINDOW).
+ // "name" supports the legacy drivers. "handle" is the W3C
+ // compliant parameter.
+ setParameter('name', nameOrHandle).
+ setParameter('handle', nameOrHandle),
+ 'WebDriver.switchTo().window(' + nameOrHandle + ')');
+ }
+
+ /**
+ * Schedules a command to change focus to the active modal dialog, such as
+ * those opened by `window.alert()`, `window.confirm()`, and
+ * `window.prompt()`. The returned promise will be rejected with a
+ * {@linkplain error.NoSuchAlertError} if there are no open alerts.
+ *
+ * @return {!AlertPromise} The open alert.
+ */
+ alert() {
+ var text = this.driver_.schedule(
+ new command.Command(command.Name.GET_ALERT_TEXT),
+ 'WebDriver.switchTo().alert()');
+ var driver = this.driver_;
+ return new AlertPromise(driver, text.then(function(text) {
+ return new Alert(driver, text);
+ }));
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// WebElement
+//
+//////////////////////////////////////////////////////////////////////////////
+
+
+const LEGACY_ELEMENT_ID_KEY = 'ELEMENT';
+const ELEMENT_ID_KEY = 'element-6066-11e4-a52e-4f735466cecf';
+
+
+/**
+ * Represents a DOM element. WebElements can be found by searching from the
+ * document root using a {@link WebDriver} instance, or by searching
+ * under another WebElement:
+ *
+ * driver.get('http://www.google.com');
+ * var searchForm = driver.findElement(By.tagName('form'));
+ * var searchBox = searchForm.findElement(By.name('q'));
+ * searchBox.sendKeys('webdriver');
+ */
+class WebElement {
+ /**
+ * @param {!WebDriver} driver the parent WebDriver instance for this element.
+ * @param {(!IThenable<string>|string)} id The server-assigned opaque ID for
+ * the underlying DOM element.
+ */
+ constructor(driver, id) {
+ /** @private {!WebDriver} */
+ this.driver_ = driver;
+
+ /** @private {!promise.Promise<string>} */
+ this.id_ = promise.fulfilled(id);
+ }
+
+ /**
+ * @param {string} id The raw ID.
+ * @param {boolean=} opt_noLegacy Whether to exclude the legacy element key.
+ * @return {!Object} The element ID for use with WebDriver's wire protocol.
+ */
+ static buildId(id, opt_noLegacy) {
+ return opt_noLegacy
+ ? {[ELEMENT_ID_KEY]: id}
+ : {[ELEMENT_ID_KEY]: id, [LEGACY_ELEMENT_ID_KEY]: id};
+ }
+
+ /**
+ * Extracts the encoded WebElement ID from the object.
+ *
+ * @param {?} obj The object to extract the ID from.
+ * @return {string} the extracted ID.
+ * @throws {TypeError} if the object is not a valid encoded ID.
+ */
+ static extractId(obj) {
+ if (obj && typeof obj === 'object') {
+ if (typeof obj[ELEMENT_ID_KEY] === 'string') {
+ return obj[ELEMENT_ID_KEY];
+ } else if (typeof obj[LEGACY_ELEMENT_ID_KEY] === 'string') {
+ return obj[LEGACY_ELEMENT_ID_KEY];
+ }
+ }
+ throw new TypeError('object is not a WebElement ID');
+ }
+
+ /**
+ * @param {?} obj the object to test.
+ * @return {boolean} whether the object is a valid encoded WebElement ID.
+ */
+ static isId(obj) {
+ return obj && typeof obj === 'object'
+ && (typeof obj[ELEMENT_ID_KEY] === 'string'
+ || typeof obj[LEGACY_ELEMENT_ID_KEY] === 'string');
+ }
+
+ /**
+ * Compares two WebElements for equality.
+ *
+ * @param {!WebElement} a A WebElement.
+ * @param {!WebElement} b A WebElement.
+ * @return {!promise.Promise<boolean>} A promise that will be
+ * resolved to whether the two WebElements are equal.
+ */
+ static equals(a, b) {
+ if (a === b) {
+ return promise.fulfilled(true);
+ }
+ let ids = [a.getId(), b.getId()];
+ return promise.all(ids).then(function(ids) {
+ // If the two element's have the same ID, they should be considered
+ // equal. Otherwise, they may still be equivalent, but we'll need to
+ // ask the server to check for us.
+ if (ids[0] === ids[1]) {
+ return true;
+ }
+
+ let cmd = new command.Command(command.Name.ELEMENT_EQUALS);
+ cmd.setParameter('id', ids[0]);
+ cmd.setParameter('other', ids[1]);
+ return a.driver_.schedule(cmd, 'WebElement.equals()');
+ });
+ }
+
+ /** @return {!WebDriver} The parent driver for this instance. */
+ getDriver() {
+ return this.driver_;
+ }
+
+ /**
+ * @return {!promise.Promise<string>} A promise that resolves to
+ * the server-assigned opaque ID assigned to this element.
+ */
+ getId() {
+ return this.id_;
+ }
+
+ /**
+ * @return {!Object} Returns the serialized representation of this WebElement.
+ */
+ [Symbols.serialize]() {
+ return this.getId().then(WebElement.buildId);
+ }
+
+ /**
+ * Schedules a command that targets this element with the parent WebDriver
+ * instance. Will ensure this element's ID is included in the command
+ * parameters under the "id" key.
+ *
+ * @param {!command.Command} command The command to schedule.
+ * @param {string} description A description of the command for debugging.
+ * @return {!promise.Promise<T>} A promise that will be resolved
+ * with the command result.
+ * @template T
+ * @see WebDriver#schedule
+ * @private
+ */
+ schedule_(command, description) {
+ command.setParameter('id', this.getId());
+ return this.driver_.schedule(command, description);
+ }
+
+ /**
+ * Schedule a command to find a descendant of this element. If the element
+ * cannot be found, the returned promise will be rejected with a
+ * {@linkplain error.NoSuchElementError NoSuchElementError}.
+ *
+ * The search criteria for an element may be defined using one of the static
+ * factories on the {@link by.By} class, or as a short-hand
+ * {@link ./by.ByHash} object. For example, the following two statements
+ * are equivalent:
+ *
+ * var e1 = element.findElement(By.id('foo'));
+ * var e2 = element.findElement({id:'foo'});
+ *
+ * You may also provide a custom locator function, which takes as input this
+ * instance and returns a {@link WebElement}, or a promise that will resolve
+ * to a WebElement. If the returned promise resolves to an array of
+ * WebElements, WebDriver will use the first element. For example, to find the
+ * first visible link on a page, you could write:
+ *
+ * var link = element.findElement(firstVisibleLink);
+ *
+ * function firstVisibleLink(element) {
+ * var links = element.findElements(By.tagName('a'));
+ * return promise.filter(links, function(link) {
+ * return link.isDisplayed();
+ * });
+ * }
+ *
+ * @param {!(by.By|Function)} locator The locator strategy to use when
+ * searching for the element.
+ * @return {!WebElementPromise} A WebElement that can be used to issue
+ * commands against the located element. If the element is not found, the
+ * element will be invalidated and all scheduled commands aborted.
+ */
+ findElement(locator) {
+ locator = by.checkedLocator(locator);
+ let id;
+ if (typeof locator === 'function') {
+ id = this.driver_.findElementInternal_(locator, this);
+ } else {
+ let cmd = new command.Command(
+ command.Name.FIND_CHILD_ELEMENT).
+ setParameter('using', locator.using).
+ setParameter('value', locator.value);
+ id = this.schedule_(cmd, 'WebElement.findElement(' + locator + ')');
+ }
+ return new WebElementPromise(this.driver_, id);
+ }
+
+ /**
+ * Schedules a command to find all of the descendants of this element that
+ * match the given search criteria.
+ *
+ * @param {!(by.By|Function)} locator The locator strategy to use when
+ * searching for the element.
+ * @return {!promise.Promise<!Array<!WebElement>>} A
+ * promise that will resolve to an array of WebElements.
+ */
+ findElements(locator) {
+ locator = by.checkedLocator(locator);
+ let id;
+ if (typeof locator === 'function') {
+ return this.driver_.findElementsInternal_(locator, this);
+ } else {
+ var cmd = new command.Command(
+ command.Name.FIND_CHILD_ELEMENTS).
+ setParameter('using', locator.using).
+ setParameter('value', locator.value);
+ return this.schedule_(cmd, 'WebElement.findElements(' + locator + ')');
+ }
+ }
+
+ /**
+ * Schedules a command to click on this element.
+ * @return {!promise.Promise<void>} A promise that will be resolved
+ * when the click command has completed.
+ */
+ click() {
+ return this.schedule_(
+ new command.Command(command.Name.CLICK_ELEMENT),
+ 'WebElement.click()');
+ }
+
+ /**
+ * Schedules a command to type a sequence on the DOM element represented by
+ * this instance.
+ *
+ * Modifier keys (SHIFT, CONTROL, ALT, META) are stateful; once a modifier is
+ * processed in the keysequence, that key state is toggled until one of the
+ * following occurs:
+ *
+ * - The modifier key is encountered again in the sequence. At this point the
+ * state of the key is toggled (along with the appropriate keyup/down
+ * events).
+ * - The {@link input.Key.NULL} key is encountered in the sequence. When
+ * this key is encountered, all modifier keys current in the down state are
+ * released (with accompanying keyup events). The NULL key can be used to
+ * simulate common keyboard shortcuts:
+ *
+ * element.sendKeys("text was",
+ * Key.CONTROL, "a", Key.NULL,
+ * "now text is");
+ * // Alternatively:
+ * element.sendKeys("text was",
+ * Key.chord(Key.CONTROL, "a"),
+ * "now text is");
+ *
+ * - The end of the keysequence is encountered. When there are no more keys
+ * to type, all depressed modifier keys are released (with accompanying
+ * keyup events).
+ *
+ * If this element is a file input ({@code <input type="file">}), the
+ * specified key sequence should specify the path to the file to attach to
+ * the element. This is analgous to the user clicking "Browse..." and entering
+ * the path into the file select dialog.
+ *
+ * var form = driver.findElement(By.css('form'));
+ * var element = form.findElement(By.css('input[type=file]'));
+ * element.sendKeys('/path/to/file.txt');
+ * form.submit();
+ *
+ * For uploads to function correctly, the entered path must reference a file
+ * on the _browser's_ machine, not the local machine running this script. When
+ * running against a remote Selenium server, a {@link input.FileDetector}
+ * may be used to transparently copy files to the remote machine before
+ * attempting to upload them in the browser.
+ *
+ * __Note:__ On browsers where native keyboard events are not supported
+ * (e.g. Firefox on OS X), key events will be synthesized. Special
+ * punctionation keys will be synthesized according to a standard QWERTY en-us
+ * keyboard layout.
+ *
+ * @param {...(number|string|!IThenable<(number|string)>)} var_args The
+ * sequence of keys to type. Number keys may be referenced numerically or
+ * by string (1 or '1'). All arguments will be joined into a single
+ * sequence.
+ * @return {!promise.Promise<void>} A promise that will be resolved
+ * when all keys have been typed.
+ */
+ sendKeys(var_args) {
+ let keys = Promise.all(Array.prototype.slice.call(arguments, 0)).
+ then(keys => {
+ let ret = [];
+ keys.forEach(key => {
+ let type = typeof key;
+ if (type === 'number') {
+ key = String(key);
+ } else if (type !== 'string') {
+ throw TypeError(
+ 'each key must be a number of string; got ' + type);
+ }
+
+ // The W3C protocol requires keys to be specified as an array where
+ // each element is a single key.
+ ret.push.apply(ret, key.split(''));
+ });
+ return ret;
+ });
+
+ if (!this.driver_.fileDetector_) {
+ return this.schedule_(
+ new command.Command(command.Name.SEND_KEYS_TO_ELEMENT).
+ setParameter('value', keys),
+ 'WebElement.sendKeys()');
+ }
+
+ // Suppress unhandled rejection errors until the flow executes the command.
+ keys.catch(function() {});
+
+ var element = this;
+ return this.driver_.flow_.execute(function() {
+ return keys.then(function(keys) {
+ return element.driver_.fileDetector_
+ .handleFile(element.driver_, keys.join(''));
+ }).then(function(keys) {
+ return element.schedule_(
+ new command.Command(command.Name.SEND_KEYS_TO_ELEMENT).
+ setParameter('value', keys.split('')),
+ 'WebElement.sendKeys()');
+ });
+ }, 'WebElement.sendKeys()');
+ }
+
+ /**
+ * Schedules a command to query for the tag/node name of this element.
+ * @return {!promise.Promise<string>} A promise that will be
+ * resolved with the element's tag name.
+ */
+ getTagName() {
+ return this.schedule_(
+ new command.Command(command.Name.GET_ELEMENT_TAG_NAME),
+ 'WebElement.getTagName()');
+ }
+
+ /**
+ * Schedules a command to query for the computed style of the element
+ * represented by this instance. If the element inherits the named style from
+ * its parent, the parent will be queried for its value. Where possible, color
+ * values will be converted to their hex representation (e.g. #00ff00 instead
+ * of rgb(0, 255, 0)).
+ *
+ * _Warning:_ the value returned will be as the browser interprets it, so
+ * it may be tricky to form a proper assertion.
+ *
+ * @param {string} cssStyleProperty The name of the CSS style property to look
+ * up.
+ * @return {!promise.Promise<string>} A promise that will be
+ * resolved with the requested CSS value.
+ */
+ getCssValue(cssStyleProperty) {
+ var name = command.Name.GET_ELEMENT_VALUE_OF_CSS_PROPERTY;
+ return this.schedule_(
+ new command.Command(name).
+ setParameter('propertyName', cssStyleProperty),
+ 'WebElement.getCssValue(' + cssStyleProperty + ')');
+ }
+
+ /**
+ * Schedules a command to query for the value of the given attribute of the
+ * element. Will return the current value, even if it has been modified after
+ * the page has been loaded. More exactly, this method will return the value
+ * of the given attribute, unless that attribute is not present, in which case
+ * the value of the property with the same name is returned. If neither value
+ * is set, null is returned (for example, the "value" property of a textarea
+ * element). The "style" attribute is converted as best can be to a
+ * text representation with a trailing semi-colon. The following are deemed to
+ * be "boolean" attributes and will return either "true" or null:
+ *
+ * async, autofocus, autoplay, checked, compact, complete, controls, declare,
+ * defaultchecked, defaultselected, defer, disabled, draggable, ended,
+ * formnovalidate, hidden, indeterminate, iscontenteditable, ismap, itemscope,
+ * loop, multiple, muted, nohref, noresize, noshade, novalidate, nowrap, open,
+ * paused, pubdate, readonly, required, reversed, scoped, seamless, seeking,
+ * selected, spellcheck, truespeed, willvalidate
+ *
+ * Finally, the following commonly mis-capitalized attribute/property names
+ * are evaluated as expected:
+ *
+ * - "class"
+ * - "readonly"
+ *
+ * @param {string} attributeName The name of the attribute to query.
+ * @return {!promise.Promise<?string>} A promise that will be
+ * resolved with the attribute's value. The returned value will always be
+ * either a string or null.
+ */
+ getAttribute(attributeName) {
+ return this.schedule_(
+ new command.Command(command.Name.GET_ELEMENT_ATTRIBUTE).
+ setParameter('name', attributeName),
+ 'WebElement.getAttribute(' + attributeName + ')');
+ }
+
+ /**
+ * Get the visible (i.e. not hidden by CSS) innerText of this element,
+ * including sub-elements, without any leading or trailing whitespace.
+ *
+ * @return {!promise.Promise<string>} A promise that will be
+ * resolved with the element's visible text.
+ */
+ getText() {
+ return this.schedule_(
+ new command.Command(command.Name.GET_ELEMENT_TEXT),
+ 'WebElement.getText()');
+ }
+
+ /**
+ * Schedules a command to compute the size of this element's bounding box, in
+ * pixels.
+ * @return {!promise.Promise.<{width: number, height: number}>} A
+ * promise that will be resolved with the element's size as a
+ * {@code {width:number, height:number}} object.
+ */
+ getSize() {
+ return this.schedule_(
+ new command.Command(command.Name.GET_ELEMENT_SIZE),
+ 'WebElement.getSize()');
+ }
+
+ /**
+ * Schedules a command to compute the location of this element in page space.
+ * @return {!promise.Promise.<{x: number, y: number}>} A promise that
+ * will be resolved to the element's location as a
+ * {@code {x:number, y:number}} object.
+ */
+ getLocation() {
+ return this.schedule_(
+ new command.Command(command.Name.GET_ELEMENT_LOCATION),
+ 'WebElement.getLocation()');
+ }
+
+ /**
+ * Schedules a command to query whether the DOM element represented by this
+ * instance is enabled, as dicted by the {@code disabled} attribute.
+ * @return {!promise.Promise<boolean>} A promise that will be
+ * resolved with whether this element is currently enabled.
+ */
+ isEnabled() {
+ return this.schedule_(
+ new command.Command(command.Name.IS_ELEMENT_ENABLED),
+ 'WebElement.isEnabled()');
+ }
+
+ /**
+ * Schedules a command to query whether this element is selected.
+ * @return {!promise.Promise<boolean>} A promise that will be
+ * resolved with whether this element is currently selected.
+ */
+ isSelected() {
+ return this.schedule_(
+ new command.Command(command.Name.IS_ELEMENT_SELECTED),
+ 'WebElement.isSelected()');
+ }
+
+ /**
+ * Schedules a command to submit the form containing this element (or this
+ * element if it is a FORM element). This command is a no-op if the element is
+ * not contained in a form.
+ * @return {!promise.Promise<void>} A promise that will be resolved
+ * when the form has been submitted.
+ */
+ submit() {
+ return this.schedule_(
+ new command.Command(command.Name.SUBMIT_ELEMENT),
+ 'WebElement.submit()');
+ }
+
+ /**
+ * Schedules a command to clear the `value` of this element. This command has
+ * no effect if the underlying DOM element is neither a text INPUT element
+ * nor a TEXTAREA element.
+ * @return {!promise.Promise<void>} A promise that will be resolved
+ * when the element has been cleared.
+ */
+ clear() {
+ return this.schedule_(
+ new command.Command(command.Name.CLEAR_ELEMENT),
+ 'WebElement.clear()');
+ }
+
+ /**
+ * Schedules a command to test whether this element is currently displayed.
+ * @return {!promise.Promise<boolean>} A promise that will be
+ * resolved with whether this element is currently visible on the page.
+ */
+ isDisplayed() {
+ return this.schedule_(
+ new command.Command(command.Name.IS_ELEMENT_DISPLAYED),
+ 'WebElement.isDisplayed()');
+ }
+
+ /**
+ * Take a screenshot of the visible region encompassed by this element's
+ * bounding rectangle.
+ *
+ * @param {boolean=} opt_scroll Optional argument that indicates whether the
+ * element should be scrolled into view before taking a screenshot.
+ * Defaults to false.
+ * @return {!promise.Promise<string>} A promise that will be
+ * resolved to the screenshot as a base-64 encoded PNG.
+ */
+ takeScreenshot(opt_scroll) {
+ var scroll = !!opt_scroll;
+ return this.schedule_(
+ new command.Command(command.Name.TAKE_ELEMENT_SCREENSHOT)
+ .setParameter('scroll', scroll),
+ 'WebElement.takeScreenshot(' + scroll + ')');
+ }
+}
+
+
+/**
+ * WebElementPromise is a promise that will be fulfilled with a WebElement.
+ * This serves as a forward proxy on WebElement, allowing calls to be
+ * scheduled without directly on this instance before the underlying
+ * WebElement has been fulfilled. In other words, the following two statements
+ * are equivalent:
+ *
+ * driver.findElement({id: 'my-button'}).click();
+ * driver.findElement({id: 'my-button'}).then(function(el) {
+ * return el.click();
+ * });
+ *
+ * @implements {promise.Thenable<!WebElement>}
+ * @final
+ */
+class WebElementPromise extends WebElement {
+ /**
+ * @param {!WebDriver} driver The parent WebDriver instance for this
+ * element.
+ * @param {!promise.Promise<!WebElement>} el A promise
+ * that will resolve to the promised element.
+ */
+ constructor(driver, el) {
+ super(driver, 'unused');
+
+ /** @override */
+ this.cancel = el.cancel.bind(el);
+
+ /** @override */
+ this.isPending = el.isPending.bind(el);
+
+ /** @override */
+ this.then = el.then.bind(el);
+
+ /** @override */
+ this.catch = el.catch.bind(el);
+
+ /** @override */
+ this.finally = el.finally.bind(el);
+
+ /**
+ * Defers returning the element ID until the wrapped WebElement has been
+ * resolved.
+ * @override
+ */
+ this.getId = function() {
+ return el.then(function(el) {
+ return el.getId();
+ });
+ };
+ }
+}
+promise.Thenable.addImplementation(WebElementPromise);
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Alert
+//
+//////////////////////////////////////////////////////////////////////////////
+
+
+/**
+ * Represents a modal dialog such as {@code alert}, {@code confirm}, or
+ * {@code prompt}. Provides functions to retrieve the message displayed with
+ * the alert, accept or dismiss the alert, and set the response text (in the
+ * case of {@code prompt}).
+ */
+class Alert {
+ /**
+ * @param {!WebDriver} driver The driver controlling the browser this alert
+ * is attached to.
+ * @param {string} text The message text displayed with this alert.
+ */
+ constructor(driver, text) {
+ /** @private {!WebDriver} */
+ this.driver_ = driver;
+
+ /** @private {!promise.Promise<string>} */
+ this.text_ = promise.fulfilled(text);
+ }
+
+ /**
+ * Retrieves the message text displayed with this alert. For instance, if the
+ * alert were opened with alert("hello"), then this would return "hello".
+ *
+ * @return {!promise.Promise<string>} A promise that will be
+ * resolved to the text displayed with this alert.
+ */
+ getText() {
+ return this.text_;
+ }
+
+ /**
+ * Sets the username and password in an alert prompting for credentials (such
+ * as a Basic HTTP Auth prompt). This method will implicitly
+ * {@linkplain #accept() submit} the dialog.
+ *
+ * @param {string} username The username to send.
+ * @param {string} password The password to send.
+ * @return {!promise.Promise<void>} A promise that will be resolved when this
+ * command has completed.
+ */
+ authenticateAs(username, password) {
+ return this.driver_.schedule(
+ new command.Command(command.Name.SET_ALERT_CREDENTIALS),
+ 'WebDriver.switchTo().alert()'
+ + `.authenticateAs("${username}", "${password}")`);
+ }
+
+ /**
+ * Accepts this alert.
+ *
+ * @return {!promise.Promise<void>} A promise that will be resolved
+ * when this command has completed.
+ */
+ accept() {
+ return this.driver_.schedule(
+ new command.Command(command.Name.ACCEPT_ALERT),
+ 'WebDriver.switchTo().alert().accept()');
+ }
+
+ /**
+ * Dismisses this alert.
+ *
+ * @return {!promise.Promise<void>} A promise that will be resolved
+ * when this command has completed.
+ */
+ dismiss() {
+ return this.driver_.schedule(
+ new command.Command(command.Name.DISMISS_ALERT),
+ 'WebDriver.switchTo().alert().dismiss()');
+ }
+
+ /**
+ * Sets the response text on this alert. This command will return an error if
+ * the underlying alert does not support response text (e.g. window.alert and
+ * window.confirm).
+ *
+ * @param {string} text The text to set.
+ * @return {!promise.Promise<void>} A promise that will be resolved
+ * when this command has completed.
+ */
+ sendKeys(text) {
+ return this.driver_.schedule(
+ new command.Command(command.Name.SET_ALERT_TEXT).
+ setParameter('text', text),
+ 'WebDriver.switchTo().alert().sendKeys(' + text + ')');
+ }
+}
+
+
+/**
+ * AlertPromise is a promise that will be fulfilled with an Alert. This promise
+ * serves as a forward proxy on an Alert, allowing calls to be scheduled
+ * directly on this instance before the underlying Alert has been fulfilled. In
+ * other words, the following two statements are equivalent:
+ *
+ * driver.switchTo().alert().dismiss();
+ * driver.switchTo().alert().then(function(alert) {
+ * return alert.dismiss();
+ * });
+ *
+ * @implements {promise.Thenable.<!webdriver.Alert>}
+ * @final
+ */
+class AlertPromise extends Alert {
+ /**
+ * @param {!WebDriver} driver The driver controlling the browser this
+ * alert is attached to.
+ * @param {!promise.Thenable<!Alert>} alert A thenable
+ * that will be fulfilled with the promised alert.
+ */
+ constructor(driver, alert) {
+ super(driver, 'unused');
+
+ /** @override */
+ this.cancel = alert.cancel.bind(alert);
+
+ /** @override */
+ this.isPending = alert.isPending.bind(alert);
+
+ /** @override */
+ this.then = alert.then.bind(alert);
+
+ /** @override */
+ this.catch = alert.catch.bind(alert);
+
+ /** @override */
+ this.finally = alert.finally.bind(alert);
+
+ /**
+ * Defer returning text until the promised alert has been resolved.
+ * @override
+ */
+ this.getText = function() {
+ return alert.then(function(alert) {
+ return alert.getText();
+ });
+ };
+
+ /**
+ * Defers action until the alert has been located.
+ * @override
+ */
+ this.authenticateAs = function(username, password) {
+ return alert.then(function(alert) {
+ return alert.authenticateAs(username, password);
+ });
+ };
+
+ /**
+ * Defers action until the alert has been located.
+ * @override
+ */
+ this.accept = function() {
+ return alert.then(function(alert) {
+ return alert.accept();
+ });
+ };
+
+ /**
+ * Defers action until the alert has been located.
+ * @override
+ */
+ this.dismiss = function() {
+ return alert.then(function(alert) {
+ return alert.dismiss();
+ });
+ };
+
+ /**
+ * Defers action until the alert has been located.
+ * @override
+ */
+ this.sendKeys = function(text) {
+ return alert.then(function(alert) {
+ return alert.sendKeys(text);
+ });
+ };
+ }
+}
+promise.Thenable.addImplementation(AlertPromise);
+
+
+// PUBLIC API
+
+
+module.exports = {
+ Alert: Alert,
+ AlertPromise: AlertPromise,
+ Condition: Condition,
+ Logs: Logs,
+ Navigation: Navigation,
+ Options: Options,
+ TargetLocator: TargetLocator,
+ Timeouts: Timeouts,
+ WebDriver: WebDriver,
+ WebElement: WebElement,
+ WebElementCondition: WebElementCondition,
+ WebElementPromise: WebElementPromise,
+ Window: Window
+};
diff --git a/node_modules/selenium-webdriver/net/index.js b/node_modules/selenium-webdriver/net/index.js
new file mode 100644
index 000000000..6c193db5c
--- /dev/null
+++ b/node_modules/selenium-webdriver/net/index.js
@@ -0,0 +1,117 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+var os = require('os');
+
+
+function getLoInterface() {
+ var name;
+ if (process.platform === 'darwin') {
+ name = 'lo0';
+ } else if (process.platform === 'linux') {
+ name = 'lo';
+ }
+ return name ? os.networkInterfaces()[name] : null;
+}
+
+
+/**
+ * Queries the system network interfaces for an IP address.
+ * @param {boolean} loopback Whether to find a loopback address.
+ * @param {string=} opt_family The IP family (IPv4 or IPv6). Defaults to IPv4.
+ * @return {string} The located IP address or undefined.
+ */
+function getAddress(loopback, opt_family) {
+ var family = opt_family || 'IPv4';
+ var addresses = [];
+
+ var interfaces;
+ if (loopback) {
+ var lo = getLoInterface();
+ interfaces = lo ? [lo] : null;
+ }
+ interfaces = interfaces || os.networkInterfaces();
+ for (var key in interfaces) {
+ if (!interfaces.hasOwnProperty(key)) {
+ continue;
+ }
+
+ interfaces[key].forEach(function(ipAddress) {
+ if (ipAddress.family === family &&
+ ipAddress.internal === loopback) {
+ addresses.push(ipAddress.address);
+ }
+ });
+ }
+ return addresses[0];
+}
+
+
+// PUBLIC API
+
+
+/**
+ * Retrieves the external IP address for this host.
+ * @param {string=} opt_family The IP family to retrieve. Defaults to "IPv4".
+ * @return {string} The IP address or undefined if not available.
+ */
+exports.getAddress = function(opt_family) {
+ return getAddress(false, opt_family);
+};
+
+
+/**
+ * Retrieves a loopback address for this machine.
+ * @param {string=} opt_family The IP family to retrieve. Defaults to "IPv4".
+ * @return {string} The IP address or undefined if not available.
+ */
+exports.getLoopbackAddress = function(opt_family) {
+ return getAddress(true, opt_family);
+};
+
+
+/**
+ * Splits a hostport string, e.g. "www.example.com:80", into its component
+ * parts.
+ *
+ * @param {string} hostport The string to split.
+ * @return {{host: string, port: ?number}} A host and port. If no port is
+ * present in the argument `hostport`, port is null.
+ */
+exports.splitHostAndPort = function(hostport) {
+ let lastIndex = hostport.lastIndexOf(':');
+ if (lastIndex < 0) {
+ return {host: hostport, port: null};
+ }
+
+ let firstIndex = hostport.indexOf(':');
+ if (firstIndex != lastIndex && !hostport.includes('[')) {
+ // Multiple colons but no brackets, so assume the string is an IPv6 address
+ // with no port (e.g. "1234:5678:9:0:1234:5678:9:0").
+ return {host: hostport, port: null};
+ }
+
+ let host = hostport.slice(0, lastIndex);
+ if (host.startsWith('[') && host.endsWith(']')) {
+ host = host.slice(1, -1);
+ }
+
+ let port = parseInt(hostport.slice(lastIndex + 1), 10);
+ return {host, port};
+};
diff --git a/node_modules/selenium-webdriver/net/portprober.js b/node_modules/selenium-webdriver/net/portprober.js
new file mode 100644
index 000000000..3ea33b2b3
--- /dev/null
+++ b/node_modules/selenium-webdriver/net/portprober.js
@@ -0,0 +1,205 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+var exec = require('child_process').exec,
+ fs = require('fs'),
+ net = require('net');
+
+
+/**
+ * The IANA suggested ephemeral port range.
+ * @type {{min: number, max: number}}
+ * @const
+ * @see http://en.wikipedia.org/wiki/Ephemeral_ports
+ */
+const DEFAULT_IANA_RANGE = {min: 49152, max: 65535};
+
+
+/**
+ * The epheremal port range for the current system. Lazily computed on first
+ * access.
+ * @type {Promise.<{min: number, max: number}>}
+ */
+var systemRange = null;
+
+
+/**
+ * Computes the ephemeral port range for the current system. This is based on
+ * http://stackoverflow.com/a/924337.
+ * @return {!Promise<{min: number, max: number}>} A promise that will resolve to
+ * the ephemeral port range of the current system.
+ */
+function findSystemPortRange() {
+ if (systemRange) {
+ return systemRange;
+ }
+ var range = process.platform === 'win32' ?
+ findWindowsPortRange() : findUnixPortRange();
+ return systemRange = range.catch(function() {
+ return DEFAULT_IANA_RANGE;
+ });
+}
+
+
+/**
+ * Executes a command and returns its output if it succeeds.
+ * @param {string} cmd The command to execute.
+ * @return {!Promise<string>} A promise that will resolve with the command's
+ * stdout data.
+ */
+function execute(cmd) {
+ return new Promise((resolve, reject) => {
+ exec(cmd, function(err, stdout) {
+ if (err) {
+ reject(err);
+ } else {
+ resolve(stdout);
+ }
+ });
+ });
+}
+
+
+/**
+ * Computes the ephemeral port range for a Unix-like system.
+ * @return {!Promise<{min: number, max: number}>} A promise that will resolve
+ * with the ephemeral port range on the current system.
+ */
+function findUnixPortRange() {
+ var cmd;
+ if (process.platform === 'sunos') {
+ cmd =
+ '/usr/sbin/ndd /dev/tcp tcp_smallest_anon_port tcp_largest_anon_port';
+ } else if (fs.existsSync('/proc/sys/net/ipv4/ip_local_port_range')) {
+ // Linux
+ cmd = 'cat /proc/sys/net/ipv4/ip_local_port_range';
+ } else {
+ cmd = 'sysctl net.inet.ip.portrange.first net.inet.ip.portrange.last' +
+ ' | sed -e "s/.*:\\s*//"';
+ }
+
+ return execute(cmd).then(function(stdout) {
+ if (!stdout || !stdout.length) return DEFAULT_IANA_RANGE;
+ var range = stdout.trim().split(/\s+/).map(Number);
+ if (range.some(isNaN)) return DEFAULT_IANA_RANGE;
+ return {min: range[0], max: range[1]};
+ });
+}
+
+
+/**
+ * Computes the ephemeral port range for a Windows system.
+ * @return {!Promise<{min: number, max: number}>} A promise that will resolve
+ * with the ephemeral port range on the current system.
+ */
+function findWindowsPortRange() {
+ // First, check if we're running on XP. If this initial command fails,
+ // we just fallback on the default IANA range.
+ return execute('cmd.exe /c ver').then(function(stdout) {
+ if (/Windows XP/.test(stdout)) {
+ // TODO: Try to read these values from the registry.
+ return {min: 1025, max: 5000};
+ } else {
+ return execute('netsh int ipv4 show dynamicport tcp').
+ then(function(stdout) {
+ /* > netsh int ipv4 show dynamicport tcp
+ Protocol tcp Dynamic Port Range
+ ---------------------------------
+ Start Port : 49152
+ Number of Ports : 16384
+ */
+ var range = stdout.split(/\n/).filter(function(line) {
+ return /.*:\s*\d+/.test(line);
+ }).map(function(line) {
+ return Number(line.split(/:\s*/)[1]);
+ });
+
+ return {
+ min: range[0],
+ max: range[0] + range[1]
+ };
+ });
+ }
+ });
+}
+
+
+/**
+ * Tests if a port is free.
+ * @param {number} port The port to test.
+ * @param {string=} opt_host The bound host to test the {@code port} against.
+ * Defaults to {@code INADDR_ANY}.
+ * @return {!Promise<boolean>} A promise that will resolve with whether the port
+ * is free.
+ */
+function isFree(port, opt_host) {
+ return new Promise((resolve, reject) => {
+ let server = net.createServer().on('error', function(e) {
+ if (e.code === 'EADDRINUSE') {
+ resolve(false);
+ } else {
+ reject(e);
+ }
+ });
+
+ server.listen(port, opt_host, function() {
+ server.close(() => resolve(true));
+ });
+ });
+}
+
+
+/**
+ * @param {string=} opt_host The bound host to test the {@code port} against.
+ * Defaults to {@code INADDR_ANY}.
+ * @return {!Promise<number>} A promise that will resolve to a free port. If a
+ * port cannot be found, the promise will be rejected.
+ */
+function findFreePort(opt_host) {
+ return findSystemPortRange().then(function(range) {
+ var attempts = 0;
+ return new Promise((resolve, reject) => {
+ findPort();
+
+ function findPort() {
+ attempts += 1;
+ if (attempts > 10) {
+ reject(Error('Unable to find a free port'));
+ }
+
+ var port = Math.floor(
+ Math.random() * (range.max - range.min) + range.min);
+ isFree(port, opt_host).then(function(isFree) {
+ if (isFree) {
+ resolve(port);
+ } else {
+ findPort();
+ }
+ });
+ }
+ });
+ });
+}
+
+
+// PUBLIC API
+
+
+exports.findFreePort = findFreePort;
+exports.isFree = isFree;
diff --git a/node_modules/selenium-webdriver/node_modules/.bin/rimraf b/node_modules/selenium-webdriver/node_modules/.bin/rimraf
new file mode 120000
index 000000000..632d6da23
--- /dev/null
+++ b/node_modules/selenium-webdriver/node_modules/.bin/rimraf
@@ -0,0 +1 @@
+../../../rimraf/bin.js \ No newline at end of file
diff --git a/node_modules/selenium-webdriver/opera.js b/node_modules/selenium-webdriver/opera.js
new file mode 100644
index 000000000..7106511f7
--- /dev/null
+++ b/node_modules/selenium-webdriver/opera.js
@@ -0,0 +1,402 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+/**
+ * @fileoverview Defines a {@linkplain Driver WebDriver} client for the
+ * Opera web browser (v26+). Before using this module, you must download the
+ * latest OperaDriver
+ * [release](https://github.com/operasoftware/operachromiumdriver/releases) and
+ * ensure it can be found on your system
+ * [PATH](http://en.wikipedia.org/wiki/PATH_%28variable%29).
+ *
+ * There are three primary classes exported by this module:
+ *
+ * 1. {@linkplain ServiceBuilder}: configures the
+ * {@link selenium-webdriver/remote.DriverService remote.DriverService}
+ * that manages the
+ * [OperaDriver](https://github.com/operasoftware/operachromiumdriver)
+ * child process.
+ *
+ * 2. {@linkplain Options}: defines configuration options for each new Opera
+ * session, such as which {@linkplain Options#setProxy proxy} to use,
+ * what {@linkplain Options#addExtensions extensions} to install, or
+ * what {@linkplain Options#addArguments command-line switches} to use when
+ * starting the browser.
+ *
+ * 3. {@linkplain Driver}: the WebDriver client; each new instance will control
+ * a unique browser session with a clean user profile (unless otherwise
+ * configured through the {@link Options} class).
+ *
+ * By default, every Opera session will use a single driver service, which is
+ * started the first time a {@link Driver} instance is created and terminated
+ * when this process exits. The default service will inherit its environment
+ * from the current process and direct all output to /dev/null. You may obtain
+ * a handle to this default service using
+ * {@link #getDefaultService getDefaultService()} and change its configuration
+ * with {@link #setDefaultService setDefaultService()}.
+ *
+ * You may also create a {@link Driver} with its own driver service. This is
+ * useful if you need to capture the server's log output for a specific session:
+ *
+ * var opera = require('selenium-webdriver/opera');
+ *
+ * var service = new opera.ServiceBuilder()
+ * .loggingTo('/my/log/file.txt')
+ * .enableVerboseLogging()
+ * .build();
+ *
+ * var options = new opera.Options();
+ * // configure browser options ...
+ *
+ * var driver = new opera.Driver(options, service);
+ *
+ * Users should only instantiate the {@link Driver} class directly when they
+ * need a custom driver service configuration (as shown above). For normal
+ * operation, users should start Opera using the
+ * {@link selenium-webdriver.Builder}.
+ */
+
+'use strict';
+
+const fs = require('fs');
+
+const http = require('./http'),
+ io = require('./io'),
+ capabilities = require('./lib/capabilities'),
+ promise = require('./lib/promise'),
+ Symbols = require('./lib/symbols'),
+ webdriver = require('./lib/webdriver'),
+ portprober = require('./net/portprober'),
+ remote = require('./remote');
+
+
+/**
+ * Name of the OperaDriver executable.
+ * @type {string}
+ * @const
+ */
+const OPERADRIVER_EXE =
+ process.platform === 'win32' ? 'operadriver.exe' : 'operadriver';
+
+
+/**
+ * Creates {@link remote.DriverService} instances that manages an
+ * [OperaDriver](https://github.com/operasoftware/operachromiumdriver)
+ * server in a child process.
+ */
+class ServiceBuilder extends remote.DriverService.Builder {
+ /**
+ * @param {string=} opt_exe Path to the server executable to use. If omitted,
+ * the builder will attempt to locate the operadriver on the current
+ * PATH.
+ * @throws {Error} If provided executable does not exist, or the operadriver
+ * cannot be found on the PATH.
+ */
+ constructor(opt_exe) {
+ let exe = opt_exe || io.findInPath(OPERADRIVER_EXE, true);
+ if (!exe) {
+ throw Error(
+ 'The OperaDriver could not be found on the current PATH. Please ' +
+ 'download the latest version of the OperaDriver from ' +
+ 'https://github.com/operasoftware/operachromiumdriver/releases and ' +
+ 'ensure it can be found on your PATH.');
+ }
+
+ super(exe);
+ this.setLoopback(true);
+ }
+
+ /**
+ * Sets the path of the log file the driver should log to. If a log file is
+ * not specified, the driver will log to stderr.
+ * @param {string} path Path of the log file to use.
+ * @return {!ServiceBuilder} A self reference.
+ */
+ loggingTo(path) {
+ return this.addArguments('--log-path=' + path);
+ }
+
+ /**
+ * Enables verbose logging.
+ * @return {!ServiceBuilder} A self reference.
+ */
+ enableVerboseLogging() {
+ return this.addArguments('--verbose');
+ }
+
+ /**
+ * Silence sthe drivers output.
+ * @return {!ServiceBuilder} A self reference.
+ */
+ silent() {
+ return this.addArguments('--silent');
+ }
+}
+
+
+
+/** @type {remote.DriverService} */
+var defaultService = null;
+
+
+/**
+ * Sets the default service to use for new OperaDriver instances.
+ * @param {!remote.DriverService} service The service to use.
+ * @throws {Error} If the default service is currently running.
+ */
+function setDefaultService(service) {
+ if (defaultService && defaultService.isRunning()) {
+ throw Error(
+ 'The previously configured OperaDriver service is still running. ' +
+ 'You must shut it down before you may adjust its configuration.');
+ }
+ defaultService = service;
+}
+
+
+/**
+ * Returns the default OperaDriver service. If such a service has not been
+ * configured, one will be constructed using the default configuration for
+ * a OperaDriver executable found on the system PATH.
+ * @return {!remote.DriverService} The default OperaDriver service.
+ */
+function getDefaultService() {
+ if (!defaultService) {
+ defaultService = new ServiceBuilder().build();
+ }
+ return defaultService;
+}
+
+
+/**
+ * @type {string}
+ * @const
+ */
+var OPTIONS_CAPABILITY_KEY = 'chromeOptions';
+
+
+/**
+ * Class for managing {@linkplain Driver OperaDriver} specific options.
+ */
+class Options {
+ constructor() {
+ /** @private {!Array.<string>} */
+ this.args_ = [];
+
+ /** @private {?string} */
+ this.binary_ = null;
+
+ /** @private {!Array.<(string|!Buffer)>} */
+ this.extensions_ = [];
+
+ /** @private {./lib/logging.Preferences} */
+ this.logPrefs_ = null;
+
+ /** @private {?capabilities.ProxyConfig} */
+ this.proxy_ = null;
+ }
+
+ /**
+ * Extracts the OperaDriver specific options from the given capabilities
+ * object.
+ * @param {!capabilities.Capabilities} caps The capabilities object.
+ * @return {!Options} The OperaDriver options.
+ */
+ static fromCapabilities(caps) {
+ var options;
+ var o = caps.get(OPTIONS_CAPABILITY_KEY);
+ if (o instanceof Options) {
+ options = o;
+ } else if (o) {
+ options = new Options()
+ .addArguments(o.args || [])
+ .addExtensions(o.extensions || [])
+ .setOperaBinaryPath(o.binary);
+ } else {
+ options = new Options;
+ }
+
+ if (caps.has(capabilities.Capability.PROXY)) {
+ options.setProxy(caps.get(capabilities.Capability.PROXY));
+ }
+
+ if (caps.has(capabilities.Capability.LOGGING_PREFS)) {
+ options.setLoggingPrefs(
+ caps.get(capabilities.Capability.LOGGING_PREFS));
+ }
+
+ return options;
+ }
+
+ /**
+ * Add additional command line arguments to use when launching the Opera
+ * browser. Each argument may be specified with or without the "--" prefix
+ * (e.g. "--foo" and "foo"). Arguments with an associated value should be
+ * delimited by an "=": "foo=bar".
+ * @param {...(string|!Array.<string>)} var_args The arguments to add.
+ * @return {!Options} A self reference.
+ */
+ addArguments(var_args) {
+ this.args_ = this.args_.concat.apply(this.args_, arguments);
+ return this;
+ }
+
+ /**
+ * Add additional extensions to install when launching Opera. Each extension
+ * should be specified as the path to the packed CRX file, or a Buffer for an
+ * extension.
+ * @param {...(string|!Buffer|!Array.<(string|!Buffer)>)} var_args The
+ * extensions to add.
+ * @return {!Options} A self reference.
+ */
+ addExtensions(var_args) {
+ this.extensions_ = this.extensions_.concat.apply(
+ this.extensions_, arguments);
+ return this;
+ }
+
+ /**
+ * Sets the path to the Opera binary to use. On Mac OS X, this path should
+ * reference the actual Opera executable, not just the application binary. The
+ * binary path be absolute or relative to the operadriver server executable, but
+ * it must exist on the machine that will launch Opera.
+ *
+ * @param {string} path The path to the Opera binary to use.
+ * @return {!Options} A self reference.
+ */
+ setOperaBinaryPath(path) {
+ this.binary_ = path;
+ return this;
+ }
+
+ /**
+ * Sets the logging preferences for the new session.
+ * @param {!./lib/logging.Preferences} prefs The logging preferences.
+ * @return {!Options} A self reference.
+ */
+ setLoggingPrefs(prefs) {
+ this.logPrefs_ = prefs;
+ return this;
+ }
+
+ /**
+ * Sets the proxy settings for the new session.
+ * @param {capabilities.ProxyConfig} proxy The proxy configuration to use.
+ * @return {!Options} A self reference.
+ */
+ setProxy(proxy) {
+ this.proxy_ = proxy;
+ return this;
+ }
+
+ /**
+ * Converts this options instance to a {@link capabilities.Capabilities}
+ * object.
+ * @param {capabilities.Capabilities=} opt_capabilities The capabilities to
+ * merge these options into, if any.
+ * @return {!capabilities.Capabilities} The capabilities.
+ */
+ toCapabilities(opt_capabilities) {
+ var caps = opt_capabilities || capabilities.Capabilities.opera();
+ caps.
+ set(capabilities.Capability.PROXY, this.proxy_).
+ set(capabilities.Capability.LOGGING_PREFS, this.logPrefs_).
+ set(OPTIONS_CAPABILITY_KEY, this);
+ return caps;
+ }
+
+ /**
+ * Converts this instance to its JSON wire protocol representation. Note this
+ * function is an implementation not intended for general use.
+ * @return {!Object} The JSON wire protocol representation of this instance.
+ */
+ [Symbols.serialize]() {
+ var json = {
+ args: this.args_,
+ extensions: this.extensions_.map(function(extension) {
+ if (Buffer.isBuffer(extension)) {
+ return extension.toString('base64');
+ }
+ return io.read(/** @type {string} */(extension))
+ .then(buffer => buffer.toString('base64'));
+ })
+ };
+ if (this.binary_) {
+ json.binary = this.binary_;
+ }
+ return json;
+ }
+}
+
+
+/**
+ * Creates a new WebDriver client for Opera.
+ */
+class Driver extends webdriver.WebDriver {
+ /**
+ * @param {(capabilities.Capabilities|Options)=} opt_config The configuration
+ * options.
+ * @param {remote.DriverService=} opt_service The session to use; will use
+ * the {@link getDefaultService default service} by default.
+ * @param {promise.ControlFlow=} opt_flow The control flow to use,
+ * or {@code null} to use the currently active flow.
+ */
+ constructor(opt_config, opt_service, opt_flow) {
+ var service = opt_service || getDefaultService();
+ var client = service.start().then(url => new http.HttpClient(url));
+ var executor = new http.Executor(client);
+
+ var caps =
+ opt_config instanceof Options ? opt_config.toCapabilities() :
+ (opt_config || capabilities.Capabilities.opera());
+
+ // On Linux, the OperaDriver does not look for Opera on the PATH, so we
+ // must explicitly find it. See: operachromiumdriver #9.
+ if (process.platform === 'linux') {
+ var options = Options.fromCapabilities(caps);
+ if (!options.binary_) {
+ let exe = io.findInPath('opera', true);
+ if (!exe) {
+ throw Error(
+ 'The opera executable could not be found on the current PATH');
+ }
+ options.setOperaBinaryPath(exe);
+ }
+ caps = options.toCapabilities(caps);
+ }
+
+ var driver = webdriver.WebDriver.createSession(executor, caps, opt_flow);
+ super(driver.getSession(), executor, driver.controlFlow());
+ }
+
+ /**
+ * This function is a no-op as file detectors are not supported by this
+ * implementation.
+ * @override
+ */
+ setFileDetector() {}
+}
+
+
+// PUBLIC API
+
+
+exports.Driver = Driver;
+exports.Options = Options;
+exports.ServiceBuilder = ServiceBuilder;
+exports.getDefaultService = getDefaultService;
+exports.setDefaultService = setDefaultService;
diff --git a/node_modules/selenium-webdriver/package.json b/node_modules/selenium-webdriver/package.json
new file mode 100644
index 000000000..8f94d5300
--- /dev/null
+++ b/node_modules/selenium-webdriver/package.json
@@ -0,0 +1,43 @@
+{
+ "name": "selenium-webdriver",
+ "version": "3.0.0-beta-3",
+ "description": "The official WebDriver JavaScript bindings from the Selenium project",
+ "license": "Apache-2.0",
+ "keywords": [
+ "automation",
+ "selenium",
+ "testing",
+ "webdriver",
+ "webdriverjs"
+ ],
+ "homepage": "https://github.com/SeleniumHQ/selenium",
+ "bugs": {
+ "url": "https://github.com/SeleniumHQ/selenium/issues"
+ },
+ "main": "./index",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/SeleniumHQ/selenium.git"
+ },
+ "engines": {
+ "node": ">= 4.2.x"
+ },
+ "dependencies": {
+ "adm-zip": "0.4.4",
+ "rimraf": "^2.2.8",
+ "tmp": "0.0.24",
+ "ws": "^1.0.1",
+ "xml2js": "0.4.4"
+ },
+ "devDependencies": {
+ "express": "^4.11.2",
+ "mocha": ">= 1.21.x",
+ "multer": "^0.1.7",
+ "promises-aplus-tests": "^2.1.0",
+ "serve-index": "^1.6.1",
+ "sinon": "^1.17.2"
+ },
+ "scripts": {
+ "test": "mocha --harmony -t 600000 --recursive test"
+ }
+}
diff --git a/node_modules/selenium-webdriver/phantomjs.js b/node_modules/selenium-webdriver/phantomjs.js
new file mode 100644
index 000000000..2de5364de
--- /dev/null
+++ b/node_modules/selenium-webdriver/phantomjs.js
@@ -0,0 +1,289 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+/**
+ * @fileoverview Defines a {@linkplain Driver WebDriver} client for the
+ * PhantomJS web browser. By default, it is expected that the PhantomJS
+ * executable can be located on your
+ * [PATH](https://en.wikipedia.org/wiki/PATH_(variable))
+ *
+ * __Using a Custom PhantomJS Binary__
+ *
+ * If you have PhantomJS.exe placed somewhere other than the root of your
+ * working directory, you can build a custom Capability and attach the
+ * executable's location to the Capability
+ *
+ * For example, if you're using the
+ * [phantomjs-prebuilt](https://www.npmjs.com/package/phantomjs-prebuilt) module
+ * from npm:
+ *
+ * //setup custom phantomJS capability
+ * var phantomjs_exe = require('phantomjs-prebuilt').path;
+ * var customPhantom = selenium.Capabilities.phantomjs();
+ * customPhantom.set("phantomjs.binary.path", phantomjs_exe);
+ * //build custom phantomJS driver
+ * var driver = new selenium.Builder().
+ * withCapabilities(customPhantom).
+ * build();
+ *
+ */
+
+'use strict';
+
+const fs = require('fs');
+
+const http = require('./http'),
+ io = require('./io'),
+ capabilities = require('./lib/capabilities'),
+ command = require('./lib/command'),
+ logging = require('./lib/logging'),
+ promise = require('./lib/promise'),
+ webdriver = require('./lib/webdriver'),
+ portprober = require('./net/portprober'),
+ remote = require('./remote');
+
+
+/**
+ * Name of the PhantomJS executable.
+ * @type {string}
+ * @const
+ */
+const PHANTOMJS_EXE =
+ process.platform === 'win32' ? 'phantomjs.exe' : 'phantomjs';
+
+
+/**
+ * Capability that designates the location of the PhantomJS executable to use.
+ * @type {string}
+ * @const
+ */
+const BINARY_PATH_CAPABILITY = 'phantomjs.binary.path';
+
+
+/**
+ * Capability that designates the CLI arguments to pass to PhantomJS.
+ * @type {string}
+ * @const
+ */
+const CLI_ARGS_CAPABILITY = 'phantomjs.cli.args';
+
+
+/**
+ * Custom command names supported by PhantomJS.
+ * @enum {string}
+ */
+const Command = {
+ EXECUTE_PHANTOM_SCRIPT: 'executePhantomScript'
+};
+
+
+/**
+ * Finds the PhantomJS executable.
+ * @param {string=} opt_exe Path to the executable to use.
+ * @return {string} The located executable.
+ * @throws {Error} If the executable cannot be found on the PATH, or if the
+ * provided executable path does not exist.
+ */
+function findExecutable(opt_exe) {
+ var exe = opt_exe || io.findInPath(PHANTOMJS_EXE, true);
+ if (!exe) {
+ throw Error(
+ 'The PhantomJS executable could not be found on the current PATH. ' +
+ 'Please download the latest version from ' +
+ 'http://phantomjs.org/download.html and ensure it can be found on ' +
+ 'your PATH. For more information, see ' +
+ 'https://github.com/ariya/phantomjs/wiki');
+ }
+ if (!fs.existsSync(exe)) {
+ throw Error('File does not exist: ' + exe);
+ }
+ return exe;
+}
+
+
+/**
+ * Maps WebDriver logging level name to those recognised by PhantomJS.
+ * @const {!Map<string, string>}
+ */
+const WEBDRIVER_TO_PHANTOMJS_LEVEL = new Map([
+ [logging.Level.ALL.name, 'DEBUG'],
+ [logging.Level.DEBUG.name, 'DEBUG'],
+ [logging.Level.INFO.name, 'INFO'],
+ [logging.Level.WARNING.name, 'WARN'],
+ [logging.Level.SEVERE.name, 'ERROR']]);
+
+
+/**
+ * Creates a command executor with support for PhantomJS' custom commands.
+ * @param {!Promise<string>} url The server's URL.
+ * @return {!command.Executor} The new command executor.
+ */
+function createExecutor(url) {
+ let client = url.then(url => new http.HttpClient(url));
+ let executor = new http.Executor(client);
+
+ executor.defineCommand(
+ Command.EXECUTE_PHANTOM_SCRIPT,
+ 'POST', '/session/:sessionId/phantom/execute');
+
+ return executor;
+}
+
+/**
+ * Creates a new WebDriver client for PhantomJS.
+ */
+class Driver extends webdriver.WebDriver {
+ /**
+ * @param {capabilities.Capabilities=} opt_capabilities The desired
+ * capabilities.
+ * @param {promise.ControlFlow=} opt_flow The control flow to use,
+ * or {@code null} to use the currently active flow.
+ * @param {string=} opt_logFile Path to the log file for the phantomjs
+ * executable's output. For convenience, this may be set at runtime with
+ * the `SELENIUM_PHANTOMJS_LOG` environment variable.
+ */
+ constructor(opt_capabilities, opt_flow, opt_logFile) {
+ // TODO: add an Options class for consistency with the other driver types.
+
+ var caps = opt_capabilities || capabilities.Capabilities.phantomjs();
+ var exe = findExecutable(caps.get(BINARY_PATH_CAPABILITY));
+ var args = [];
+
+ var logPrefs = caps.get(capabilities.Capability.LOGGING_PREFS);
+ if (logPrefs instanceof logging.Preferences) {
+ logPrefs = logPrefs.toJSON();
+ }
+
+ if (logPrefs && logPrefs[logging.Type.DRIVER]) {
+ let level = WEBDRIVER_TO_PHANTOMJS_LEVEL.get(
+ logPrefs[logging.Type.DRIVER]);
+ if (level) {
+ args.push('--webdriver-loglevel=' + level);
+ }
+ }
+
+ opt_logFile = process.env['SELENIUM_PHANTOMJS_LOG'] || opt_logFile;
+ if (typeof opt_logFile === 'string') {
+ args.push('--webdriver-logfile=' + opt_logFile);
+ }
+
+ var proxy = caps.get(capabilities.Capability.PROXY);
+ if (proxy) {
+ switch (proxy.proxyType) {
+ case 'manual':
+ if (proxy.httpProxy) {
+ args.push(
+ '--proxy-type=http',
+ '--proxy=' + proxy.httpProxy);
+ console.log(args);
+ }
+ break;
+ case 'pac':
+ throw Error('PhantomJS does not support Proxy PAC files');
+ case 'system':
+ args.push('--proxy-type=system');
+ break;
+ case 'direct':
+ args.push('--proxy-type=none');
+ break;
+ }
+ }
+ args = args.concat(caps.get(CLI_ARGS_CAPABILITY) || []);
+
+ var port = portprober.findFreePort();
+ var service = new remote.DriverService(exe, {
+ port: port,
+ stdio: 'inherit',
+ args: Promise.resolve(port).then(function(port) {
+ args.push('--webdriver=' + port);
+ return args;
+ })
+ });
+
+ var executor = createExecutor(service.start());
+ var driver = webdriver.WebDriver.createSession(executor, caps, opt_flow);
+
+ super(driver.getSession(), executor, driver.controlFlow());
+
+ var boundQuit = this.quit.bind(this);
+
+ /** @override */
+ this.quit = function() {
+ let killService = () => service.kill();
+ return boundQuit().then(killService, killService);
+ };
+ }
+
+ /**
+ * This function is a no-op as file detectors are not supported by this
+ * implementation.
+ * @override
+ */
+ setFileDetector() {}
+
+ /**
+ * Executes a PhantomJS fragment. This method is similar to
+ * {@link #executeScript}, except it exposes the
+ * <a href="http://phantomjs.org/api/">PhantomJS API</a> to the injected
+ * script.
+ *
+ * <p>The injected script will execute in the context of PhantomJS's
+ * {@code page} variable. If a page has not been loaded before calling this
+ * method, one will be created.</p>
+ *
+ * <p>Be sure to wrap callback definitions in a try/catch block, as failures
+ * may cause future WebDriver calls to fail.</p>
+ *
+ * <p>Certain callbacks are used by GhostDriver (the PhantomJS WebDriver
+ * implementation) and overriding these may cause the script to fail. It is
+ * recommended that you check for existing callbacks before defining your own.
+ * </p>
+ *
+ * As with {@link #executeScript}, the injected script may be defined as
+ * a string for an anonymous function body (e.g. "return 123;"), or as a
+ * function. If a function is provided, it will be decompiled to its original
+ * source. Note that injecting functions is provided as a convenience to
+ * simplify defining complex scripts. Care must be taken that the function
+ * only references variables that will be defined in the page's scope and
+ * that the function does not override {@code Function.prototype.toString}
+ * (overriding toString() will interfere with how the function is
+ * decompiled.
+ *
+ * @param {(string|!Function)} script The script to execute.
+ * @param {...*} var_args The arguments to pass to the script.
+ * @return {!promise.Promise<T>} A promise that resolve to the
+ * script's return value.
+ * @template T
+ */
+ executePhantomJS(script, var_args) {
+ if (typeof script === 'function') {
+ script = 'return (' + script + ').apply(this, arguments);';
+ }
+ var args = arguments.length > 1
+ ? Array.prototype.slice.call(arguments, 1) : [];
+ return this.schedule(
+ new command.Command(Command.EXECUTE_PHANTOM_SCRIPT)
+ .setParameter('script', script)
+ .setParameter('args', args),
+ 'Driver.executePhantomJS()');
+ }
+}
+
+
+// PUBLIC API
+
+exports.Driver = Driver;
diff --git a/node_modules/selenium-webdriver/proxy.js b/node_modules/selenium-webdriver/proxy.js
new file mode 100644
index 000000000..80e52a7a8
--- /dev/null
+++ b/node_modules/selenium-webdriver/proxy.js
@@ -0,0 +1,32 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+/**
+ * @fileoverview Proxy module alias.
+ *
+ * var webdriver = require('selenium-webdriver'),
+ * proxy = require('selenium-webdriver/proxy');
+ *
+ * var driver = new webdriver.Builder()
+ * .withCapabilities(webdriver.Capabilities.chrome())
+ * .setProxy(proxy.manual({http: 'host:1234'}))
+ * .build();
+ */
+
+'use strict';
+
+module.exports = require('./lib/proxy');
diff --git a/node_modules/selenium-webdriver/remote/index.js b/node_modules/selenium-webdriver/remote/index.js
new file mode 100644
index 000000000..ab76b4476
--- /dev/null
+++ b/node_modules/selenium-webdriver/remote/index.js
@@ -0,0 +1,593 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+const AdmZip = require('adm-zip');
+const fs = require('fs');
+const path = require('path');
+const url = require('url');
+const util = require('util');
+
+const httpUtil = require('../http/util');
+const io = require('../io');
+const exec = require('../io/exec');
+const cmd = require('../lib/command');
+const input = require('../lib/input');
+const promise = require('../lib/promise');
+const webdriver = require('../lib/webdriver');
+const net = require('../net');
+const portprober = require('../net/portprober');
+
+
+/**
+ * @typedef {(string|!Array<string|number|!stream.Stream|null|undefined>)}
+ */
+var StdIoOptions;
+
+
+/**
+ * @typedef {(string|!IThenable<string>)}
+ */
+var CommandLineFlag;
+
+
+/**
+ * A record object that defines the configuration options for a DriverService
+ * instance.
+ *
+ * @record
+ */
+function ServiceOptions() {}
+
+/**
+ * Whether the service should only be accessed on this host's loopback address.
+ *
+ * @type {(boolean|undefined)}
+ */
+ServiceOptions.prototype.loopback;
+
+/**
+ * The host name to access the server on. If this option is specified, the
+ * {@link #loopback} option will be ignored.
+ *
+ * @type {(string|undefined)}
+ */
+ServiceOptions.prototype.hostname;
+
+/**
+ * The port to start the server on (must be > 0). If the port is provided as a
+ * promise, the service will wait for the promise to resolve before starting.
+ *
+ * @type {(number|!IThenable<number>)}
+ */
+ServiceOptions.prototype.port;
+
+/**
+ * The arguments to pass to the service. If a promise is provided, the service
+ * will wait for it to resolve before starting.
+ *
+ * @type {!(Array<CommandLineFlag>|IThenable<!Array<CommandLineFlag>>)}
+ */
+ServiceOptions.prototype.args;
+
+/**
+ * The base path on the server for the WebDriver wire protocol (e.g. '/wd/hub').
+ * Defaults to '/'.
+ *
+ * @type {(string|undefined|null)}
+ */
+ServiceOptions.prototype.path;
+
+/**
+ * The environment variables that should be visible to the server process.
+ * Defaults to inheriting the current process's environment.
+ *
+ * @type {(Object<string, string>|undefined)}
+ */
+ServiceOptions.prototype.env;
+
+/**
+ * IO configuration for the spawned server process. For more information, refer
+ * to the documentation of `child_process.spawn`.
+ *
+ * @type {(StdIoOptions|undefined)}
+ * @see https://nodejs.org/dist/latest-v4.x/docs/api/child_process.html#child_process_options_stdio
+ */
+ServiceOptions.prototype.stdio;
+
+
+/**
+ * Manages the life and death of a native executable WebDriver server.
+ *
+ * It is expected that the driver server implements the
+ * https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol.
+ * Furthermore, the managed server should support multiple concurrent sessions,
+ * so that this class may be reused for multiple clients.
+ */
+class DriverService {
+ /**
+ * @param {string} executable Path to the executable to run.
+ * @param {!ServiceOptions} options Configuration options for the service.
+ */
+ constructor(executable, options) {
+ /** @private {string} */
+ this.executable_ = executable;
+
+ /** @private {boolean} */
+ this.loopbackOnly_ = !!options.loopback;
+
+ /** @private {(string|undefined)} */
+ this.hostname_ = options.hostname;
+
+ /** @private {(number|!IThenable<number>)} */
+ this.port_ = options.port;
+
+ /**
+ * @private {!(Array<CommandLineFlag>|
+ * IThenable<!Array<CommandLineFlag>>)}
+ */
+ this.args_ = options.args;
+
+ /** @private {string} */
+ this.path_ = options.path || '/';
+
+ /** @private {!Object<string, string>} */
+ this.env_ = options.env || process.env;
+
+ /**
+ * @private {(string|!Array<string|number|!stream.Stream|null|undefined>)}
+ */
+ this.stdio_ = options.stdio || 'ignore';
+
+ /**
+ * A promise for the managed subprocess, or null if the server has not been
+ * started yet. This promise will never be rejected.
+ * @private {Promise<!exec.Command>}
+ */
+ this.command_ = null;
+
+ /**
+ * Promise that resolves to the server's address or null if the server has
+ * not been started. This promise will be rejected if the server terminates
+ * before it starts accepting WebDriver requests.
+ * @private {Promise<string>}
+ */
+ this.address_ = null;
+ }
+
+ /**
+ * @return {!Promise<string>} A promise that resolves to the server's address.
+ * @throws {Error} If the server has not been started.
+ */
+ address() {
+ if (this.address_) {
+ return this.address_;
+ }
+ throw Error('Server has not been started.');
+ }
+
+ /**
+ * Returns whether the underlying process is still running. This does not take
+ * into account whether the process is in the process of shutting down.
+ * @return {boolean} Whether the underlying service process is running.
+ */
+ isRunning() {
+ return !!this.address_;
+ }
+
+ /**
+ * Starts the server if it is not already running.
+ * @param {number=} opt_timeoutMs How long to wait, in milliseconds, for the
+ * server to start accepting requests. Defaults to 30 seconds.
+ * @return {!Promise<string>} A promise that will resolve to the server's base
+ * URL when it has started accepting requests. If the timeout expires
+ * before the server has started, the promise will be rejected.
+ */
+ start(opt_timeoutMs) {
+ if (this.address_) {
+ return this.address_;
+ }
+
+ var timeout = opt_timeoutMs || DriverService.DEFAULT_START_TIMEOUT_MS;
+ var self = this;
+
+ let resolveCommand;
+ this.command_ = new Promise(resolve => resolveCommand = resolve);
+
+ this.address_ = new Promise((resolveAddress, rejectAddress) => {
+ resolveAddress(Promise.resolve(this.port_).then(port => {
+ if (port <= 0) {
+ throw Error('Port must be > 0: ' + port);
+ }
+
+ return resolveCommandLineFlags(this.args_).then(args => {
+ var command = exec(self.executable_, {
+ args: args,
+ env: self.env_,
+ stdio: self.stdio_
+ });
+
+ resolveCommand(command);
+
+ var earlyTermination = command.result().then(function(result) {
+ var error = result.code == null ?
+ Error('Server was killed with ' + result.signal) :
+ Error('Server terminated early with status ' + result.code);
+ rejectAddress(error);
+ self.address_ = null;
+ self.command_ = null;
+ throw error;
+ });
+
+ var hostname = self.hostname_;
+ if (!hostname) {
+ hostname = !self.loopbackOnly_ && net.getAddress()
+ || net.getLoopbackAddress();
+ }
+
+ var serverUrl = url.format({
+ protocol: 'http',
+ hostname: hostname,
+ port: port + '',
+ pathname: self.path_
+ });
+
+ return new Promise(function(fulfill, reject) {
+ var ready = httpUtil.waitForServer(serverUrl, timeout)
+ .then(fulfill, reject);
+ earlyTermination.catch(function(e) {
+ ready.cancel(/** @type {Error} */(e));
+ reject(Error(e.message));
+ });
+ }).then(function() {
+ return serverUrl;
+ });
+ });
+ }));
+ });
+
+ return this.address_;
+ }
+
+ /**
+ * Stops the service if it is not currently running. This function will kill
+ * the server immediately. To synchronize with the active control flow, use
+ * {@link #stop()}.
+ * @return {!Promise} A promise that will be resolved when the server has been
+ * stopped.
+ */
+ kill() {
+ if (!this.address_ || !this.command_) {
+ return Promise.resolve(); // Not currently running.
+ }
+ return this.command_.then(function(command) {
+ command.kill('SIGTERM');
+ });
+ }
+
+ /**
+ * Schedules a task in the current control flow to stop the server if it is
+ * currently running.
+ * @return {!promise.Promise} A promise that will be resolved when
+ * the server has been stopped.
+ */
+ stop() {
+ return promise.controlFlow().execute(this.kill.bind(this));
+ }
+}
+
+
+/**
+ * @param {!(Array<CommandLineFlag>|IThenable<!Array<CommandLineFlag>>)} args
+ * @return {!Promise<!Array<string>>}
+ */
+function resolveCommandLineFlags(args) {
+ return Promise.resolve(args) // Resolve the outer array.
+ .then(args => Promise.all(args)); // Then resolve the individual flags.
+}
+
+
+/**
+ * The default amount of time, in milliseconds, to wait for the server to
+ * start.
+ * @const {number}
+ */
+DriverService.DEFAULT_START_TIMEOUT_MS = 30 * 1000;
+
+
+/**
+ * Creates {@link DriverService} objects that manage a WebDriver server in a
+ * child process.
+ */
+DriverService.Builder = class {
+ /**
+ * @param {string} exe Path to the executable to use. This executable must
+ * accept the `--port` flag for defining the port to start the server on.
+ * @throws {Error} If the provided executable path does not exist.
+ */
+ constructor(exe) {
+ if (!fs.existsSync(exe)) {
+ throw Error(`The specified executable path does not exist: ${exe}`);
+ }
+
+ /** @private @const {string} */
+ this.exe_ = exe;
+
+ /** @private {!ServiceOptions} */
+ this.options_ = {
+ args: [],
+ port: 0,
+ env: null,
+ stdio: 'ignore'
+ };
+ }
+
+ /**
+ * Define additional command line arguments to use when starting the server.
+ *
+ * @param {...CommandLineFlag} var_args The arguments to include.
+ * @return {!THIS} A self reference.
+ * @this {THIS}
+ * @template THIS
+ */
+ addArguments(var_args) {
+ let args = Array.prototype.slice.call(arguments, 0);
+ this.options_.args = this.options_.args.concat(args);
+ return this;
+ }
+
+ /**
+ * Sets the host name to access the server on. If specified, the
+ * {@linkplain #setLoopback() loopback} setting will be ignored.
+ *
+ * @param {string} hostname
+ * @return {!DriverService.Builder} A self reference.
+ */
+ setHostname(hostname) {
+ this.options_.hostname = hostname;
+ return this;
+ }
+
+ /**
+ * Sets whether the service should be accessed at this host's loopback
+ * address.
+ *
+ * @param {boolean} loopback
+ * @return {!DriverService.Builder} A self reference.
+ */
+ setLoopback(loopback) {
+ this.options_.loopback = loopback;
+ return this;
+ }
+
+ /**
+ * Sets the base path for WebDriver REST commands (e.g. "/wd/hub").
+ * By default, the driver will accept commands relative to "/".
+ *
+ * @param {?string} basePath The base path to use, or `null` to use the
+ * default.
+ * @return {!DriverService.Builder} A self reference.
+ */
+ setPath(basePath) {
+ this.options_.path = basePath;
+ return this;
+ }
+
+ /**
+ * Sets the port to start the server on.
+ *
+ * @param {number} port The port to use, or 0 for any free port.
+ * @return {!DriverService.Builder} A self reference.
+ * @throws {Error} If an invalid port is specified.
+ */
+ setPort(port) {
+ if (port < 0) {
+ throw Error(`port must be >= 0: ${port}`);
+ }
+ this.options_.port = port;
+ return this;
+ }
+
+ /**
+ * Defines the environment to start the server under. This setting will be
+ * inherited by every browser session started by the server. By default, the
+ * server will inherit the enviroment of the current process.
+ *
+ * @param {(Map<string, string>|Object<string, string>|null)} env The desired
+ * environment to use, or `null` if the server should inherit the
+ * current environment.
+ * @return {!DriverService.Builder} A self reference.
+ */
+ setEnvironment(env) {
+ if (env instanceof Map) {
+ let tmp = {};
+ env.forEach((value, key) => tmp[key] = value);
+ env = tmp;
+ }
+ this.options_.env = env;
+ return this;
+ }
+
+ /**
+ * IO configuration for the spawned server process. For more information,
+ * refer to the documentation of `child_process.spawn`.
+ *
+ * @param {StdIoOptions} config The desired IO configuration.
+ * @return {!DriverService.Builder} A self reference.
+ * @see https://nodejs.org/dist/latest-v4.x/docs/api/child_process.html#child_process_options_stdio
+ */
+ setStdio(config) {
+ this.options_.stdio = config;
+ return this;
+ }
+
+ /**
+ * Creates a new DriverService using this instance's current configuration.
+ *
+ * @return {!DriverService} A new driver service.
+ */
+ build() {
+ let port = this.options_.port || portprober.findFreePort();
+ let args = Promise.resolve(port).then(port => {
+ return this.options_.args.concat('--port=' + port);
+ });
+
+ let options =
+ /** @type {!ServiceOptions} */
+ (Object.assign({}, this.options_, {args, port}));
+ return new DriverService(this.exe_, options);
+ }
+};
+
+
+/**
+ * Manages the life and death of the
+ * <a href="http://selenium-release.storage.googleapis.com/index.html">
+ * standalone Selenium server</a>.
+ */
+class SeleniumServer extends DriverService {
+ /**
+ * @param {string} jar Path to the Selenium server jar.
+ * @param {SeleniumServer.Options=} opt_options Configuration options for the
+ * server.
+ * @throws {Error} If the path to the Selenium jar is not specified or if an
+ * invalid port is specified.
+ */
+ constructor(jar, opt_options) {
+ if (!jar) {
+ throw Error('Path to the Selenium jar not specified');
+ }
+
+ var options = opt_options || {};
+
+ if (options.port < 0) {
+ throw Error('Port must be >= 0: ' + options.port);
+ }
+
+ let port = options.port || portprober.findFreePort();
+ let args = Promise.all([port, options.jvmArgs || [], options.args || []])
+ .then(resolved => {
+ let port = resolved[0];
+ let jvmArgs = resolved[1];
+ let args = resolved[2];
+ return jvmArgs.concat('-jar', jar, '-port', port).concat(args);
+ });
+
+ super('java', {
+ loopback: options.loopback,
+ port: port,
+ args: args,
+ path: '/wd/hub',
+ env: options.env,
+ stdio: options.stdio
+ });
+ }
+}
+
+
+/**
+ * Options for the Selenium server:
+ *
+ * - `loopback` - Whether the server should only be accessed on this host's
+ * loopback address.
+ * - `port` - The port to start the server on (must be > 0). If the port is
+ * provided as a promise, the service will wait for the promise to resolve
+ * before starting.
+ * - `args` - The arguments to pass to the service. If a promise is provided,
+ * the service will wait for it to resolve before starting.
+ * - `jvmArgs` - The arguments to pass to the JVM. If a promise is provided,
+ * the service will wait for it to resolve before starting.
+ * - `env` - The environment variables that should be visible to the server
+ * process. Defaults to inheriting the current process's environment.
+ * - `stdio` - IO configuration for the spawned server process. For more
+ * information, refer to the documentation of `child_process.spawn`.
+ *
+ * @typedef {{
+ * loopback: (boolean|undefined),
+ * port: (number|!promise.Promise<number>),
+ * args: !(Array<string>|promise.Promise<!Array<string>>),
+ * jvmArgs: (!Array<string>|
+ * !promise.Promise<!Array<string>>|
+ * undefined),
+ * env: (!Object<string, string>|undefined),
+ * stdio: (string|!Array<string|number|!stream.Stream|null|undefined>|
+ * undefined)
+ * }}
+ */
+SeleniumServer.Options;
+
+
+
+/**
+ * A {@link webdriver.FileDetector} that may be used when running
+ * against a remote
+ * [Selenium server](http://selenium-release.storage.googleapis.com/index.html).
+ *
+ * When a file path on the local machine running this script is entered with
+ * {@link webdriver.WebElement#sendKeys WebElement#sendKeys}, this file detector
+ * will transfer the specified file to the Selenium server's host; the sendKeys
+ * command will be updated to use the transfered file's path.
+ *
+ * __Note:__ This class depends on a non-standard command supported on the
+ * Java Selenium server. The file detector will fail if used with a server that
+ * only supports standard WebDriver commands (such as the ChromeDriver).
+ *
+ * @final
+ */
+class FileDetector extends input.FileDetector {
+ /**
+ * Prepares a `file` for use with the remote browser. If the provided path
+ * does not reference a normal file (i.e. it does not exist or is a
+ * directory), then the promise returned by this method will be resolved with
+ * the original file path. Otherwise, this method will upload the file to the
+ * remote server, which will return the file's path on the remote system so
+ * it may be referenced in subsequent commands.
+ *
+ * @override
+ */
+ handleFile(driver, file) {
+ return io.stat(file).then(function(stats) {
+ if (stats.isDirectory()) {
+ return file; // Not a valid file, return original input.
+ }
+
+ var zip = new AdmZip();
+ zip.addLocalFile(file);
+ // Stored compression, see https://en.wikipedia.org/wiki/Zip_(file_format)
+ zip.getEntries()[0].header.method = 0;
+
+ var command = new cmd.Command(cmd.Name.UPLOAD_FILE)
+ .setParameter('file', zip.toBuffer().toString('base64'));
+ return driver.schedule(command,
+ 'remote.FileDetector.handleFile(' + file + ')');
+ }, function(err) {
+ if (err.code === 'ENOENT') {
+ return file; // Not a file; return original input.
+ }
+ throw err;
+ });
+ }
+}
+
+
+// PUBLIC API
+
+exports.DriverService = DriverService;
+exports.FileDetector = FileDetector;
+exports.SeleniumServer = SeleniumServer;
+exports.ServiceOptions = ServiceOptions; // Exported for API docs.
diff --git a/node_modules/selenium-webdriver/safari.js b/node_modules/selenium-webdriver/safari.js
new file mode 100644
index 000000000..bbfff06e8
--- /dev/null
+++ b/node_modules/selenium-webdriver/safari.js
@@ -0,0 +1,700 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+/**
+ * @fileoverview Defines a WebDriver client for Safari.
+ *
+ *
+ * __Testing Older Versions of Safari__
+ *
+ * To test versions of Safari prior to Safari 10.0, you must install the
+ * [latest version](http://selenium-release.storage.googleapis.com/index.html)
+ * of the SafariDriver browser extension; using Safari for normal browsing is
+ * not recommended once the extension has been installed. You can, and should,
+ * disable the extension when the browser is not being used with WebDriver.
+ *
+ * You must also enable the use of legacy driver using the {@link Options} class.
+ *
+ * let options = new safari.Options()
+ * .useLegacyDriver(true);
+ *
+ * let driver = new (require('selenium-webdriver')).Builder()
+ * .forBrowser('safari')
+ * .setSafariOptions(options)
+ * .build();
+ */
+
+'use strict';
+
+const events = require('events');
+const fs = require('fs');
+const http = require('http');
+const path = require('path');
+const url = require('url');
+const util = require('util');
+const ws = require('ws');
+
+const io = require('./io');
+const exec = require('./io/exec');
+const isDevMode = require('./lib/devmode');
+const Capabilities = require('./lib/capabilities').Capabilities;
+const Capability = require('./lib/capabilities').Capability;
+const command = require('./lib/command');
+const error = require('./lib/error');
+const logging = require('./lib/logging');
+const promise = require('./lib/promise');
+const Session = require('./lib/session').Session;
+const Symbols = require('./lib/symbols');
+const webdriver = require('./lib/webdriver');
+const portprober = require('./net/portprober');
+const remote = require('./remote');
+const http_ = require('./http');
+
+
+/** @const */
+const CLIENT_PATH = isDevMode
+ ? path.join(__dirname,
+ '../../../buck-out/gen/javascript/safari-driver/client.js')
+ : path.join(__dirname, 'lib/safari/client.js');
+
+
+/** @const */
+const LIBRARY_DIR = (function() {
+ if (process.platform === 'darwin') {
+ return path.join('/Users', process.env['USER'], 'Library/Safari');
+ } else if (process.platform === 'win32') {
+ return path.join(process.env['APPDATA'], 'Apple Computer', 'Safari');
+ } else {
+ return '/dev/null';
+ }
+})();
+
+
+/** @const */
+const SESSION_DATA_FILES = (function() {
+ if (process.platform === 'darwin') {
+ var libraryDir = path.join('/Users', process.env['USER'], 'Library');
+ return [
+ path.join(libraryDir, 'Caches/com.apple.Safari/Cache.db'),
+ path.join(libraryDir, 'Cookies/Cookies.binarycookies'),
+ path.join(libraryDir, 'Cookies/Cookies.plist'),
+ path.join(libraryDir, 'Safari/History.plist'),
+ path.join(libraryDir, 'Safari/LastSession.plist'),
+ path.join(libraryDir, 'Safari/LocalStorage'),
+ path.join(libraryDir, 'Safari/Databases')
+ ];
+ } else if (process.platform === 'win32') {
+ var appDataDir = path.join(process.env['APPDATA'],
+ 'Apple Computer', 'Safari');
+ var localDataDir = path.join(process.env['LOCALAPPDATA'],
+ 'Apple Computer', 'Safari');
+ return [
+ path.join(appDataDir, 'History.plist'),
+ path.join(appDataDir, 'LastSession.plist'),
+ path.join(appDataDir, 'Cookies/Cookies.plist'),
+ path.join(appDataDir, 'Cookies/Cookies.binarycookies'),
+ path.join(localDataDir, 'Cache.db'),
+ path.join(localDataDir, 'Databases'),
+ path.join(localDataDir, 'LocalStorage')
+ ];
+ } else {
+ return [];
+ }
+})();
+
+
+/** @typedef {{port: number, address: string, family: string}} */
+var Host;
+
+
+/**
+ * A basic HTTP/WebSocket server used to communicate with the legacy SafariDriver
+ * browser extension.
+ */
+class Server extends events.EventEmitter {
+ constructor() {
+ super();
+ var server = http.createServer(function(req, res) {
+ if (req.url === '/favicon.ico') {
+ res.writeHead(204);
+ res.end();
+ return;
+ }
+
+ var query = url.parse(/** @type {string} */(req.url)).query || '';
+ if (query.indexOf('url=') == -1) {
+ var address = server.address()
+ var host = address.address + ':' + address.port;
+ res.writeHead(
+ 302, {'Location': 'http://' + host + '?url=ws://' + host});
+ res.end();
+ }
+
+ fs.readFile(CLIENT_PATH, 'utf8', function(err, data) {
+ if (err) {
+ res.writeHead(500, {'Content-Type': 'text/plain'});
+ res.end(err.stack);
+ return;
+ }
+ var content = '<!DOCTYPE html><body><script>' + data + '</script>';
+ res.writeHead(200, {
+ 'Content-Type': 'text/html; charset=utf-8',
+ 'Content-Length': Buffer.byteLength(content, 'utf8'),
+ });
+ res.end(content);
+ });
+ });
+
+ var wss = new ws.Server({server: server});
+ wss.on('connection', this.emit.bind(this, 'connection'));
+
+ /**
+ * Starts the server on a random port.
+ * @return {!Promise<Host>} A promise that will resolve with the server host
+ * when it has fully started.
+ */
+ this.start = function() {
+ if (server.address()) {
+ return Promise.resolve(server.address());
+ }
+ return portprober.findFreePort('localhost').then(function(port) {
+ return promise.checkedNodeCall(
+ server.listen.bind(server, port, 'localhost'));
+ }).then(function() {
+ return server.address();
+ });
+ };
+
+ /**
+ * Stops the server.
+ * @return {!Promise} A promise that will resolve when the server has closed
+ * all connections.
+ */
+ this.stop = function() {
+ return new Promise(fulfill => server.close(fulfill));
+ };
+
+ /**
+ * @return {Host} This server's host info.
+ * @throws {Error} If the server is not running.
+ */
+ this.address = function() {
+ var addr = server.address();
+ if (!addr) {
+ throw Error('There server is not running!');
+ }
+ return addr;
+ };
+ }
+}
+
+
+/**
+ * @return {!Promise<string>} A promise that will resolve with the path
+ * to Safari on the current system.
+ */
+function findSafariExecutable() {
+ switch (process.platform) {
+ case 'darwin':
+ return Promise.resolve('/Applications/Safari.app/Contents/MacOS/Safari');
+
+ case 'win32':
+ var files = [
+ process.env['PROGRAMFILES'] || '\\Program Files',
+ process.env['PROGRAMFILES(X86)'] || '\\Program Files (x86)'
+ ].map(function(prefix) {
+ return path.join(prefix, 'Safari\\Safari.exe');
+ });
+ return io.exists(files[0]).then(function(exists) {
+ return exists ? files[0] : io.exists(files[1]).then(function(exists) {
+ if (exists) {
+ return files[1];
+ }
+ throw Error('Unable to find Safari on the current system');
+ });
+ });
+
+ default:
+ return Promise.reject(
+ Error('Safari is not supported on the current platform: ' +
+ process.platform));
+ }
+}
+
+
+/**
+ * @param {string} serverUrl The URL to connect to.
+ * @return {!Promise<string>} A promise for the path to a file that Safari can
+ * open on start-up to trigger a new connection to the WebSocket server.
+ */
+function createConnectFile(serverUrl) {
+ return io.tmpFile({postfix: '.html'}).then(function(f) {
+ let contents =
+ `<!DOCTYPE html><script>window.location = "${serverUrl}";</script>`;
+ return io.write(f, contents).then(() => f);
+ });
+}
+
+
+/**
+ * Deletes all session data files if so desired.
+ * @param {!Object} desiredCapabilities .
+ * @return {!Array<!Promise>} A list of promises for the deleted files.
+ */
+function cleanSession(desiredCapabilities) {
+ if (!desiredCapabilities) {
+ return [];
+ }
+ var options = desiredCapabilities[OPTIONS_CAPABILITY_KEY];
+ if (!options) {
+ return [];
+ }
+ if (!options['cleanSession']) {
+ return [];
+ }
+ return SESSION_DATA_FILES.map(function(file) {
+ return io.unlink(file);
+ });
+}
+
+
+/** @return {string} . */
+function getRandomString() {
+ let seed = Date.now();
+ return Math.floor(Math.random() * seed).toString(36)
+ + Math.abs(Math.floor(Math.random() * seed) ^ Date.now()).toString(36);
+}
+
+
+/**
+ * @implements {command.Executor}
+ */
+class CommandExecutor {
+ constructor() {
+ this.server_ = null;
+
+ /** @private {ws.WebSocket} */
+ this.socket_ = null;
+
+ /** @private {?string} 8*/
+ this.sessionId_ = null;
+
+ /** @private {Promise<!exec.Command>} */
+ this.safari_ = null;
+
+ /** @private {!logging.Logger} */
+ this.log_ = logging.getLogger('webdriver.safari');
+ }
+
+ /** @override */
+ execute(cmd) {
+ var self = this;
+ return new promise.Promise(function(fulfill, reject) {
+ var safariCommand = JSON.stringify({
+ 'origin': 'webdriver',
+ 'type': 'command',
+ 'command': {
+ 'id': getRandomString(),
+ 'name': cmd.getName(),
+ 'parameters': cmd.getParameters()
+ }
+ });
+
+ switch (cmd.getName()) {
+ case command.Name.NEW_SESSION:
+ self.startSafari_(cmd)
+ .then(() => self.sendCommand_(safariCommand))
+ .then(caps => new Session(self.sessionId(), caps))
+ .then(fulfill, reject);
+ break;
+
+ case command.Name.DESCRIBE_SESSION:
+ self.sendCommand_(safariCommand)
+ .then(caps => new Session(self.sessionId(), caps))
+ .then(fulfill, reject);
+ break;
+
+ case command.Name.QUIT:
+ self.destroySession_().then(() => fulfill(null), reject);
+ break;
+
+ default:
+ self.sendCommand_(safariCommand).then(fulfill, reject);
+ break;
+ }
+ });
+ }
+
+ /**
+ * @return {string} The static session ID for this executor's current
+ * connection.
+ */
+ sessionId() {
+ if (!this.sessionId_) {
+ throw Error('not currently connected')
+ }
+ return this.sessionId_;
+ }
+
+ /**
+ * @param {string} data .
+ * @return {!promise.Promise} .
+ * @private
+ */
+ sendCommand_(data) {
+ let self = this;
+ return new promise.Promise(function(fulfill, reject) {
+ // TODO: support reconnecting with the extension.
+ if (!self.socket_) {
+ self.destroySession_().finally(function() {
+ reject(Error('The connection to the SafariDriver was closed'));
+ });
+ return;
+ }
+
+ self.log_.fine(() => '>>> ' + data);
+ self.socket_.send(data, function(err) {
+ if (err) {
+ reject(err);
+ return;
+ }
+ });
+
+ self.socket_.once('message', function(data) {
+ try {
+ self.log_.fine(() => '<<< ' + data);
+ data = JSON.parse(data);
+ } catch (ex) {
+ reject(Error('Failed to parse driver message: ' + data));
+ return;
+ }
+
+ try {
+ error.checkLegacyResponse(data['response']);
+ fulfill(data['response']['value']);
+ } catch (ex) {
+ reject(ex);
+ }
+ });
+ });
+ }
+
+ /**
+ * @param {!command.Command} command .
+ * @private
+ */
+ startSafari_(command) {
+ this.server_ = new Server();
+
+ this.safari_ = this.server_.start().then(function(address) {
+ var tasks = cleanSession(
+ /** @type {!Object} */(
+ command.getParameters()['desiredCapabilities']));
+ tasks.push(
+ findSafariExecutable(),
+ createConnectFile(
+ 'http://' + address.address + ':' + address.port));
+
+ return Promise.all(tasks).then(function(/** !Array<string> */tasks) {
+ var exe = tasks[tasks.length - 2];
+ var html = tasks[tasks.length - 1];
+ return exec(exe, {args: [html]});
+ });
+ });
+
+ return new Promise((resolve, reject) => {
+ let start = Date.now();
+ let timer = setTimeout(function() {
+ let elapsed = Date.now() - start;
+ reject(Error(
+ 'Failed to connect to the SafariDriver after ' + elapsed +
+ ' ms; Have you installed the latest extension from ' +
+ 'http://selenium-release.storage.googleapis.com/index.html?'));
+ }, 10 * 1000);
+
+ this.server_.once('connection', socket => {
+ clearTimeout(timer);
+ this.socket_ = socket;
+ this.sessionId_ = getRandomString();
+ socket.once('close', () => {
+ this.socket_ = null;
+ this.sessionId_ = null;
+ });
+ resolve();
+ });
+ });
+ }
+
+ /**
+ * Destroys the active session by stopping the WebSocket server and killing the
+ * Safari subprocess.
+ * @private
+ */
+ destroySession_() {
+ var tasks = [];
+ if (this.server_) {
+ tasks.push(this.server_.stop());
+ }
+ if (this.safari_) {
+ tasks.push(this.safari_.then(function(safari) {
+ safari.kill();
+ return safari.result();
+ }));
+ }
+ var self = this;
+ return promise.all(tasks).finally(function() {
+ self.server_ = null;
+ self.socket_ = null;
+ self.safari_ = null;
+ });
+ }
+}
+
+
+/**
+ * @return {string} .
+ * @throws {Error}
+ */
+function findSafariDriver() {
+ let exe = io.findInPath('safaridriver', true);
+ if (!exe) {
+ throw Error(
+ `The safaridriver executable could not be found on the current PATH.
+ Please ensure you are using Safari 10.0 or above.`);
+ }
+ return exe;
+}
+
+
+/**
+ * Creates {@link selenium-webdriver/remote.DriverService} instances that manage
+ * a [safaridriver] server in a child process.
+ *
+ * [safaridriver]: https://developer.apple.com/library/prerelease/content/releasenotes/General/WhatsNewInSafari/Articles/Safari_10_0.html#//apple_ref/doc/uid/TP40014305-CH11-DontLinkElementID_28
+ */
+class ServiceBuilder extends remote.DriverService.Builder {
+ /**
+ * @param {string=} opt_exe Path to the server executable to use. If omitted,
+ * the builder will attempt to locate the safaridriver on the system PATH.
+ */
+ constructor(opt_exe) {
+ super(opt_exe || findSafariDriver());
+ this.setLoopback(true); // Required.
+ }
+}
+
+
+/** @const */
+const OPTIONS_CAPABILITY_KEY = 'safari.options';
+const LEGACY_DRIVER_CAPABILITY_KEY = 'legacyDriver'
+
+
+
+/**
+ * Configuration options specific to the {@link Driver SafariDriver}.
+ */
+class Options {
+ constructor() {
+ /** @private {Object<string, *>} */
+ this.options_ = null;
+
+ /** @private {./lib/logging.Preferences} */
+ this.logPrefs_ = null;
+
+ /** @private {?./lib/capabilities.ProxyConfig} */
+ this.proxy_ = null;
+
+ /** @private {boolean} */
+ this.legacyDriver_ = false;
+ }
+
+ /**
+ * Extracts the SafariDriver specific options from the given capabilities
+ * object.
+ * @param {!Capabilities} capabilities The capabilities object.
+ * @return {!Options} The ChromeDriver options.
+ */
+ static fromCapabilities(capabilities) {
+ var options = new Options();
+
+ var o = capabilities.get(OPTIONS_CAPABILITY_KEY);
+ if (o instanceof Options) {
+ options = o;
+ } else if (o) {
+ options.setCleanSession(o.cleanSession);
+ }
+
+ if (capabilities.has(Capability.PROXY)) {
+ options.setProxy(capabilities.get(Capability.PROXY));
+ }
+
+ if (capabilities.has(Capability.LOGGING_PREFS)) {
+ options.setLoggingPrefs(capabilities.get(Capability.LOGGING_PREFS));
+ }
+
+ if (capabilities.has(LEGACY_DRIVER_CAPABILITY_KEY)) {
+ options.useLegacyDriver(capabilities.get(LEGACY_DRIVER_CAPABILITY_KEY));
+ }
+
+ return options;
+ }
+
+ /**
+ * Sets whether to force Safari to start with a clean session. Enabling this
+ * option will cause all global browser data to be deleted.
+ * @param {boolean} clean Whether to make sure the session has no cookies,
+ * cache entries, local storage, or databases.
+ * @return {!Options} A self reference.
+ */
+ setCleanSession(clean) {
+ if (!this.options_) {
+ this.options_ = {};
+ }
+ this.options_['cleanSession'] = clean;
+ return this;
+ }
+
+ /**
+ * Sets whether to use the legacy driver from the Selenium project. This option
+ * is disabled by default.
+ *
+ * @param {boolean} enable Whether to enable the legacy driver.
+ * @return {!Options} A self reference.
+ */
+ useLegacyDriver(enable) {
+ this.legacyDriver_ = enable;
+ return this;
+ }
+
+ /**
+ * Sets the logging preferences for the new session.
+ * @param {!./lib/logging.Preferences} prefs The logging preferences.
+ * @return {!Options} A self reference.
+ */
+ setLoggingPrefs(prefs) {
+ this.logPrefs_ = prefs;
+ return this;
+ }
+
+ /**
+ * Sets the proxy to use.
+ *
+ * @param {./lib/capabilities.ProxyConfig} proxy The proxy configuration to use.
+ * @return {!Options} A self reference.
+ */
+ setProxy(proxy) {
+ this.proxy_ = proxy;
+ return this;
+ }
+
+ /**
+ * Converts this options instance to a {@link Capabilities} object.
+ * @param {Capabilities=} opt_capabilities The capabilities to
+ * merge these options into, if any.
+ * @return {!Capabilities} The capabilities.
+ */
+ toCapabilities(opt_capabilities) {
+ var caps = opt_capabilities || Capabilities.safari();
+ if (this.logPrefs_) {
+ caps.set(Capability.LOGGING_PREFS, this.logPrefs_);
+ }
+ if (this.proxy_) {
+ caps.set(Capability.PROXY, this.proxy_);
+ }
+ if (this.options_) {
+ caps.set(OPTIONS_CAPABILITY_KEY, this);
+ }
+ caps.set(LEGACY_DRIVER_CAPABILITY_KEY, this.legacyDriver_);
+ return caps;
+ }
+
+ /**
+ * Converts this instance to its JSON wire protocol representation. Note this
+ * function is an implementation detail not intended for general use.
+ * @return {!Object<string, *>} The JSON wire protocol representation of this
+ * instance.
+ */
+ [Symbols.serialize]() {
+ return this.options_ || {};
+ }
+}
+
+
+/**
+ * A WebDriver client for Safari. This class should never be instantiated
+ * directly; instead, use the {@linkplain ./builder.Builder Builder}:
+ *
+ * var driver = new Builder()
+ * .forBrowser('safari')
+ * .build();
+ *
+ */
+class Driver extends webdriver.WebDriver {
+ /**
+ * @param {(Options|Capabilities)=} opt_config The configuration
+ * options for the new session.
+ * @param {promise.ControlFlow=} opt_flow The control flow to create
+ * the driver under.
+ */
+ constructor(opt_config, opt_flow) {
+ let caps,
+ executor,
+ useLegacyDriver = false,
+ onQuit = () => {};
+
+ if (opt_config instanceof Options) {
+ caps = opt_config.toCapabilities();
+ } else {
+ caps = opt_config || Capabilities.safari()
+ }
+
+ if (caps.has(LEGACY_DRIVER_CAPABILITY_KEY)) {
+ useLegacyDriver = caps.get(LEGACY_DRIVER_CAPABILITY_KEY);
+ caps.delete(LEGACY_DRIVER_CAPABILITY_KEY);
+ }
+
+ if (useLegacyDriver) {
+ executor = new CommandExecutor();
+ } else {
+ let service = new ServiceBuilder().build();
+
+ executor = new http_.Executor(
+ service.start()
+ .then(url => new http_.HttpClient(url))
+ );
+
+ onQuit = () => service.kill();
+ }
+
+ let driver = webdriver.WebDriver.createSession(executor, caps, opt_flow);
+
+ super(driver.getSession(), executor, driver.controlFlow());
+
+ /** @override */
+ this.quit = () => {
+ return super.quit().finally(onQuit);
+ };
+ }
+}
+
+
+// Public API
+
+
+exports.Driver = Driver;
+exports.Options = Options;
+exports.ServiceBuilder = ServiceBuilder;
diff --git a/node_modules/selenium-webdriver/test/actions_test.js b/node_modules/selenium-webdriver/test/actions_test.js
new file mode 100644
index 000000000..7ea0047ad
--- /dev/null
+++ b/node_modules/selenium-webdriver/test/actions_test.js
@@ -0,0 +1,54 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+var Browser = require('..').Browser,
+ By = require('..').By,
+ until = require('..').until,
+ test = require('../lib/test'),
+ fileServer = require('../lib/test/fileserver');
+
+
+test.suite(function(env) {
+ var driver;
+ test.beforeEach(function() { driver = env.builder().build(); });
+ test.afterEach(function() { driver.quit(); });
+
+ test.ignore(
+ env.browsers(Browser.FIREFOX, Browser.PHANTOM_JS, Browser.SAFARI)).
+ describe('WebDriver.actions()', function() {
+
+ test.it('can move to and click element in an iframe', function() {
+ driver.get(fileServer.whereIs('click_tests/click_in_iframe.html'));
+
+ driver.wait(until.elementLocated(By.id('ifr')), 5000)
+ .then(function(frame) {
+ driver.switchTo().frame(frame);
+ });
+
+ var link = driver.findElement(By.id('link'));
+ driver.actions()
+ .mouseMove(link)
+ .click()
+ .perform();
+
+ driver.wait(until.titleIs('Submitted Successfully!'), 5000);
+ });
+
+ });
+});
diff --git a/node_modules/selenium-webdriver/test/chrome/options_test.js b/node_modules/selenium-webdriver/test/chrome/options_test.js
new file mode 100644
index 000000000..28c4faa91
--- /dev/null
+++ b/node_modules/selenium-webdriver/test/chrome/options_test.js
@@ -0,0 +1,227 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+var fs = require('fs');
+
+var webdriver = require('../..'),
+ chrome = require('../../chrome'),
+ symbols = require('../../lib/symbols'),
+ proxy = require('../../proxy'),
+ assert = require('../../testing/assert');
+
+var test = require('../../lib/test');
+
+
+describe('chrome.Options', function() {
+ describe('fromCapabilities', function() {
+
+ it('should return a new Options instance if none were defined',
+ function() {
+ var options = chrome.Options.fromCapabilities(
+ new webdriver.Capabilities());
+ assert(options).instanceOf(chrome.Options);
+ });
+
+ it('should return options instance if present', function() {
+ var options = new chrome.Options();
+ var caps = options.toCapabilities();
+ assert(caps).instanceOf(webdriver.Capabilities);
+ assert(chrome.Options.fromCapabilities(caps)).equalTo(options);
+ });
+
+ it('should rebuild options from wire representation', function() {
+ var expectedExtension = fs.readFileSync(__filename, 'base64');
+ var caps = webdriver.Capabilities.chrome().set('chromeOptions', {
+ args: ['a', 'b'],
+ extensions: [__filename],
+ binary: 'binaryPath',
+ logPath: 'logFilePath',
+ detach: true,
+ localState: 'localStateValue',
+ prefs: 'prefsValue'
+ });
+
+ var options = chrome.Options.fromCapabilities(caps);
+ var json = options[symbols.serialize]();
+
+ assert(json.args.length).equalTo(2);
+ assert(json.args[0]).equalTo('a');
+ assert(json.args[1]).equalTo('b');
+ assert(json.extensions.length).equalTo(1);
+ assert(json.extensions[0]).equalTo(expectedExtension);
+ assert(json.binary).equalTo('binaryPath');
+ assert(json.logPath).equalTo('logFilePath');
+ assert(json.detach).equalTo(true);
+ assert(json.localState).equalTo('localStateValue');
+ assert(json.prefs).equalTo('prefsValue');
+ });
+
+ it('should rebuild options from incomplete wire representation',
+ function() {
+ var caps = webdriver.Capabilities.chrome().set('chromeOptions', {
+ logPath: 'logFilePath'
+ });
+
+ var options = chrome.Options.fromCapabilities(caps);
+ var json = options[symbols.serialize]();
+ assert(json.args).isUndefined();
+ assert(json.binary).isUndefined();
+ assert(json.detach).isUndefined();
+ assert(json.excludeSwitches).isUndefined();
+ assert(json.extensions).isUndefined();
+ assert(json.localState).isUndefined();
+ assert(json.logPath).equalTo('logFilePath');
+ assert(json.prefs).isUndefined();
+ assert(json.minidumpPath).isUndefined();
+ assert(json.mobileEmulation).isUndefined();
+ assert(json.perfLoggingPrefs).isUndefined();
+ });
+
+ it('should extract supported WebDriver capabilities', function() {
+ var proxyPrefs = proxy.direct();
+ var logPrefs = {};
+ var caps = webdriver.Capabilities.chrome().
+ set(webdriver.Capability.PROXY, proxyPrefs).
+ set(webdriver.Capability.LOGGING_PREFS, logPrefs);
+
+ var options = chrome.Options.fromCapabilities(caps);
+ assert(options.proxy_).equalTo(proxyPrefs);
+ assert(options.logPrefs_).equalTo(logPrefs);
+ });
+ });
+
+ describe('addArguments', function() {
+ it('takes var_args', function() {
+ var options = new chrome.Options();
+ assert(options[symbols.serialize]().args).isUndefined();
+
+ options.addArguments('a', 'b');
+ var json = options[symbols.serialize]();
+ assert(json.args.length).equalTo(2);
+ assert(json.args[0]).equalTo('a');
+ assert(json.args[1]).equalTo('b');
+ });
+
+ it('flattens input arrays', function() {
+ var options = new chrome.Options();
+ assert(options[symbols.serialize]().args).isUndefined();
+
+ options.addArguments(['a', 'b'], 'c', [1, 2], 3);
+ var json = options[symbols.serialize]();
+ assert(json.args.length).equalTo(6);
+ assert(json.args[0]).equalTo('a');
+ assert(json.args[1]).equalTo('b');
+ assert(json.args[2]).equalTo('c');
+ assert(json.args[3]).equalTo(1);
+ assert(json.args[4]).equalTo(2);
+ assert(json.args[5]).equalTo(3);
+ });
+ });
+
+ describe('addExtensions', function() {
+ it('takes var_args', function() {
+ var options = new chrome.Options();
+ assert(options.extensions_.length).equalTo(0);
+
+ options.addExtensions('a', 'b');
+ assert(options.extensions_.length).equalTo(2);
+ assert(options.extensions_[0]).equalTo('a');
+ assert(options.extensions_[1]).equalTo('b');
+ });
+
+ it('flattens input arrays', function() {
+ var options = new chrome.Options();
+ assert(options.extensions_.length).equalTo(0);
+
+ options.addExtensions(['a', 'b'], 'c', [1, 2], 3);
+ assert(options.extensions_.length).equalTo(6);
+ assert(options.extensions_[0]).equalTo('a');
+ assert(options.extensions_[1]).equalTo('b');
+ assert(options.extensions_[2]).equalTo('c');
+ assert(options.extensions_[3]).equalTo(1);
+ assert(options.extensions_[4]).equalTo(2);
+ assert(options.extensions_[5]).equalTo(3);
+ });
+ });
+
+ describe('serialize', function() {
+ it('base64 encodes extensions', function() {
+ var expected = fs.readFileSync(__filename, 'base64');
+ var wire = new chrome.Options()
+ .addExtensions(__filename)
+ [symbols.serialize]();
+ assert(wire.extensions.length).equalTo(1);
+ assert(wire.extensions[0]).equalTo(expected);
+ });
+ });
+
+ describe('toCapabilities', function() {
+ it('returns a new capabilities object if one is not provided', function() {
+ var options = new chrome.Options();
+ var caps = options.toCapabilities();
+ assert(caps.get('browserName')).equalTo('chrome');
+ assert(caps.get('chromeOptions')).equalTo(options);
+ });
+
+ it('adds to input capabilities object', function() {
+ var caps = webdriver.Capabilities.firefox();
+ var options = new chrome.Options();
+ assert(options.toCapabilities(caps)).equalTo(caps);
+ assert(caps.get('browserName')).equalTo('firefox');
+ assert(caps.get('chromeOptions')).equalTo(options);
+ });
+
+ it('sets generic driver capabilities', function() {
+ var proxyPrefs = {};
+ var loggingPrefs = {};
+ var options = new chrome.Options().
+ setLoggingPrefs(loggingPrefs).
+ setProxy(proxyPrefs);
+
+ var caps = options.toCapabilities();
+ assert(caps.get('proxy')).equalTo(proxyPrefs);
+ assert(caps.get('loggingPrefs')).equalTo(loggingPrefs);
+ });
+ });
+});
+
+test.suite(function(env) {
+ var driver;
+
+ test.afterEach(function() {
+ driver.quit();
+ });
+
+ describe('Chrome options', function() {
+ test.it('can start Chrome with custom args', function() {
+ var options = new chrome.Options().
+ addArguments('user-agent=foo;bar');
+
+ driver = env.builder().
+ setChromeOptions(options).
+ build();
+
+ driver.get(test.Pages.ajaxyPage);
+
+ var userAgent = driver.executeScript(
+ 'return window.navigator.userAgent');
+ assert(userAgent).equalTo('foo;bar');
+ });
+ });
+}, {browsers: ['chrome']});
diff --git a/node_modules/selenium-webdriver/test/chrome/service_test.js b/node_modules/selenium-webdriver/test/chrome/service_test.js
new file mode 100644
index 000000000..0ccc93b5a
--- /dev/null
+++ b/node_modules/selenium-webdriver/test/chrome/service_test.js
@@ -0,0 +1,45 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+var webdriver = require('../..'),
+ chrome = require('../../chrome'),
+ assert = require('../../testing/assert');
+
+var test = require('../../lib/test');
+
+
+test.suite(function(env) {
+ describe('chromedriver', function() {
+ var service;
+ test.afterEach(function() {
+ if (service) {
+ return service.kill();
+ }
+ });
+
+ test.it('can be started on a custom path', function() {
+ service = new chrome.ServiceBuilder()
+ .setPath('/foo/bar/baz')
+ .build();
+ return service.start().then(function(url) {
+ assert(url).endsWith('/foo/bar/baz');
+ });
+ });
+ });
+}, {browsers: ['chrome']});
diff --git a/node_modules/selenium-webdriver/test/cookie_test.js b/node_modules/selenium-webdriver/test/cookie_test.js
new file mode 100644
index 000000000..3912fdbee
--- /dev/null
+++ b/node_modules/selenium-webdriver/test/cookie_test.js
@@ -0,0 +1,214 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+var assert = require('assert'),
+ url = require('url');
+
+var test = require('../lib/test'),
+ fileserver = require('../lib/test/fileserver'),
+ Browser = require('..').Browser,
+ Pages = test.Pages;
+
+
+test.suite(function(env) {
+ var driver;
+
+ test.before(function() {
+ driver = env.builder().build();
+ });
+
+ test.after(function() {
+ driver.quit();
+ });
+
+ test.ignore(env.browsers(Browser.SAFARI)). // Cookie handling is broken.
+ describe('Cookie Management;', function() {
+
+ test.beforeEach(function() {
+ driver.get(fileserver.Pages.ajaxyPage);
+ driver.manage().deleteAllCookies();
+ assertHasCookies();
+ });
+
+ test.it('can add new cookies', function() {
+ var cookie = createCookieSpec();
+
+ driver.manage().addCookie(cookie);
+ driver.manage().getCookie(cookie.name).then(function(actual) {
+ assert.equal(actual.value, cookie.value);
+ });
+ });
+
+ test.it('can get all cookies', function() {
+ var cookie1 = createCookieSpec();
+ var cookie2 = createCookieSpec();
+
+ driver.manage().addCookie(cookie1);
+ driver.manage().addCookie(cookie2);
+
+ assertHasCookies(cookie1, cookie2);
+ });
+
+ test.ignore(env.browsers(Browser.IE)).
+ it('only returns cookies visible to the current page', function() {
+ var cookie1 = createCookieSpec();
+
+ driver.manage().addCookie(cookie1);
+
+ var pageUrl = fileserver.whereIs('page/1');
+ var cookie2 = createCookieSpec({
+ path: url.parse(pageUrl).pathname
+ });
+ driver.get(pageUrl);
+ driver.manage().addCookie(cookie2);
+ assertHasCookies(cookie1, cookie2);
+
+ driver.get(fileserver.Pages.ajaxyPage);
+ assertHasCookies(cookie1);
+
+ driver.get(pageUrl);
+ assertHasCookies(cookie1, cookie2);
+ });
+
+ test.it('can delete all cookies', function() {
+ var cookie1 = createCookieSpec();
+ var cookie2 = createCookieSpec();
+
+ driver.executeScript(
+ 'document.cookie = arguments[0] + "=" + arguments[1];' +
+ 'document.cookie = arguments[2] + "=" + arguments[3];',
+ cookie1.name, cookie1.value, cookie2.name, cookie2.value);
+ assertHasCookies(cookie1, cookie2);
+
+ driver.manage().deleteAllCookies();
+ assertHasCookies();
+ });
+
+ test.it('can delete cookies by name', function() {
+ var cookie1 = createCookieSpec();
+ var cookie2 = createCookieSpec();
+
+ driver.executeScript(
+ 'document.cookie = arguments[0] + "=" + arguments[1];' +
+ 'document.cookie = arguments[2] + "=" + arguments[3];',
+ cookie1.name, cookie1.value, cookie2.name, cookie2.value);
+ assertHasCookies(cookie1, cookie2);
+
+ driver.manage().deleteCookie(cookie1.name);
+ assertHasCookies(cookie2);
+ });
+
+ test.it('should only delete cookie with exact name', function() {
+ var cookie1 = createCookieSpec();
+ var cookie2 = createCookieSpec();
+ var cookie3 = {name: cookie1.name + 'xx', value: cookie1.value};
+
+ driver.executeScript(
+ 'document.cookie = arguments[0] + "=" + arguments[1];' +
+ 'document.cookie = arguments[2] + "=" + arguments[3];' +
+ 'document.cookie = arguments[4] + "=" + arguments[5];',
+ cookie1.name, cookie1.value, cookie2.name, cookie2.value,
+ cookie3.name, cookie3.value);
+ assertHasCookies(cookie1, cookie2, cookie3);
+
+ driver.manage().deleteCookie(cookie1.name);
+ assertHasCookies(cookie2, cookie3);
+ });
+
+ test.it('can delete cookies set higher in the path', function() {
+ var cookie = createCookieSpec();
+ var childUrl = fileserver.whereIs('child/childPage.html');
+ var grandchildUrl = fileserver.whereIs(
+ 'child/grandchild/grandchildPage.html');
+
+ driver.get(childUrl);
+ driver.manage().addCookie(cookie);
+ assertHasCookies(cookie);
+
+ driver.get(grandchildUrl);
+ assertHasCookies(cookie);
+
+ driver.manage().deleteCookie(cookie.name);
+ assertHasCookies();
+
+ driver.get(childUrl);
+ assertHasCookies();
+ });
+
+ test.ignore(env.browsers(
+ Browser.ANDROID,
+ Browser.FIREFOX,
+ 'legacy-' + Browser.FIREFOX,
+ Browser.IE)).
+ it('should retain cookie expiry', function() {
+ let expirationDelay = 5 * 1000;
+ let expiry = new Date(Date.now() + expirationDelay);
+ let cookie = createCookieSpec({expiry});
+
+ driver.manage().addCookie(cookie);
+ driver.manage().getCookie(cookie.name).then(function(actual) {
+ assert.equal(actual.value, cookie.value);
+ // expiry times are exchanged in seconds since January 1, 1970 UTC.
+ assert.equal(actual.expiry, Math.floor(expiry.getTime() / 1000));
+ });
+
+ driver.sleep(expirationDelay);
+ assertHasCookies();
+ });
+ });
+
+ function createCookieSpec(opt_options) {
+ let spec = {
+ name: getRandomString(),
+ value: getRandomString()
+ };
+ if (opt_options) {
+ spec = Object.assign(spec, opt_options);
+ }
+ return spec;
+ }
+
+ function buildCookieMap(cookies) {
+ var map = {};
+ cookies.forEach(function(cookie) {
+ map[cookie.name] = cookie;
+ });
+ return map;
+ }
+
+ function assertHasCookies(var_args) {
+ var expected = Array.prototype.slice.call(arguments, 0);
+ driver.manage().getCookies().then(function(cookies) {
+ assert.equal(cookies.length, expected.length,
+ 'Wrong # of cookies.' +
+ '\n Expected: ' + JSON.stringify(expected) +
+ '\n Was : ' + JSON.stringify(cookies));
+
+ var map = buildCookieMap(cookies);
+ for (var i = 0; i < expected.length; ++i) {
+ assert.equal(expected[i].value, map[expected[i].name].value);
+ }
+ });
+ }
+
+ function getRandomString() {
+ var x = 1234567890;
+ return Math.floor(Math.random() * x).toString(36);
+ }
+});
diff --git a/node_modules/selenium-webdriver/test/element_finding_test.js b/node_modules/selenium-webdriver/test/element_finding_test.js
new file mode 100644
index 000000000..819e15655
--- /dev/null
+++ b/node_modules/selenium-webdriver/test/element_finding_test.js
@@ -0,0 +1,410 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+var fail = require('assert').fail;
+
+var Browser = require('..').Browser,
+ By = require('..').By,
+ error = require('..').error,
+ until = require('..').until,
+ promise = require('../lib/promise'),
+ test = require('../lib/test'),
+ assert = require('../testing/assert'),
+ Pages = test.Pages;
+
+
+test.suite(function(env) {
+ var browsers = env.browsers;
+
+ var driver;
+
+ test.before(function() {
+ driver = env.builder().build();
+ });
+
+ test.after(function() {
+ driver.quit();
+ });
+
+ describe('finding elements', function() {
+ test.it(
+ 'should work after loading multiple pages in a row',
+ function() {
+ driver.get(Pages.formPage);
+ driver.get(Pages.xhtmlTestPage);
+ driver.findElement(By.linkText('click me')).click();
+ driver.wait(until.titleIs('We Arrive Here'), 5000);
+ });
+
+ describe('By.id()', function() {
+ test.it('should work', function() {
+ driver.get(Pages.xhtmlTestPage);
+ driver.findElement(By.id('linkId')).click();
+ driver.wait(until.titleIs('We Arrive Here'), 5000);
+ });
+
+ test.it('should fail if ID not present on page', function() {
+ driver.get(Pages.formPage);
+ driver.findElement(By.id('nonExistantButton')).
+ then(fail, function(e) {
+ assert(e).instanceOf(error.NoSuchElementError);
+ });
+ });
+
+ test.it(
+ 'should find multiple elements by ID even though that is ' +
+ 'malformed HTML',
+ function() {
+ driver.get(Pages.nestedPage);
+ driver.findElements(By.id('2')).then(function(elements) {
+ assert(elements.length).equalTo(8);
+ });
+ });
+ });
+
+ describe('By.linkText()', function() {
+ test.it('should be able to click on link identified by text', function() {
+ driver.get(Pages.xhtmlTestPage);
+ driver.findElement(By.linkText('click me')).click();
+ driver.wait(until.titleIs('We Arrive Here'), 5000);
+ });
+
+ test.it(
+ 'should be able to find elements by partial link text',
+ function() {
+ driver.get(Pages.xhtmlTestPage);
+ driver.findElement(By.partialLinkText('ick me')).click();
+ driver.wait(until.titleIs('We Arrive Here'), 5000);
+ });
+
+ test.it('should work when link text contains equals sign', function() {
+ driver.get(Pages.xhtmlTestPage);
+ var id = driver.findElement(By.linkText('Link=equalssign')).
+ getAttribute('id');
+ assert(id).equalTo('linkWithEqualsSign');
+ });
+
+ test.it('matches by partial text when containing equals sign',
+ function() {
+ driver.get(Pages.xhtmlTestPage);
+ var id = driver.findElement(By.partialLinkText('Link=')).
+ getAttribute('id');
+ assert(id).equalTo('linkWithEqualsSign');
+ });
+
+ test.it('works when searching for multiple and text contains =',
+ function() {
+ driver.get(Pages.xhtmlTestPage);
+ driver.findElements(By.linkText('Link=equalssign')).
+ then(function(elements) {
+ assert(elements.length).equalTo(1);
+ return elements[0].getAttribute('id');
+ }).
+ then(function(id) {
+ assert(id).equalTo('linkWithEqualsSign');
+ });
+ });
+
+ test.it(
+ 'works when searching for multiple with partial text containing =',
+ function() {
+ driver.get(Pages.xhtmlTestPage);
+ driver.findElements(By.partialLinkText('Link=')).
+ then(function(elements) {
+ assert(elements.length).equalTo(1);
+ return elements[0].getAttribute('id');
+ }).
+ then(function(id) {
+ assert(id).equalTo('linkWithEqualsSign');
+ });
+ });
+
+ test.it('should be able to find multiple exact matches',
+ function() {
+ driver.get(Pages.xhtmlTestPage);
+ driver.findElements(By.linkText('click me')).
+ then(function(elements) {
+ assert(elements.length).equalTo(2);
+ });
+ });
+
+ test.it('should be able to find multiple partial matches',
+ function() {
+ driver.get(Pages.xhtmlTestPage);
+ driver.findElements(By.partialLinkText('ick me')).
+ then(function(elements) {
+ assert(elements.length).equalTo(2);
+ });
+ });
+
+ // See https://github.com/mozilla/geckodriver/issues/137
+ test.ignore(browsers(Browser.FIREFOX)).
+ it('works on XHTML pages', function() {
+ driver.get(test.whereIs('actualXhtmlPage.xhtml'));
+
+ var el = driver.findElement(By.linkText('Foo'));
+ assert(el.getText()).equalTo('Foo');
+ });
+ });
+
+ describe('By.name()', function() {
+ test.it('should work', function() {
+ driver.get(Pages.formPage);
+
+ var el = driver.findElement(By.name('checky'));
+ assert(el.getAttribute('value')).equalTo('furrfu');
+ });
+
+ test.it('should find multiple elements with same name', function() {
+ driver.get(Pages.nestedPage);
+ driver.findElements(By.name('checky')).then(function(elements) {
+ assert(elements.length).greaterThan(1);
+ });
+ });
+
+ test.it(
+ 'should be able to find elements that do not support name property',
+ function() {
+ driver.get(Pages.nestedPage);
+ driver.findElement(By.name('div1'));
+ // Pass if this does not return an error.
+ });
+
+ test.it('shoudl be able to find hidden elements by name', function() {
+ driver.get(Pages.formPage);
+ driver.findElement(By.name('hidden'));
+ // Pass if this does not return an error.
+ });
+ });
+
+ describe('By.className()', function() {
+ test.it('should work', function() {
+ driver.get(Pages.xhtmlTestPage);
+
+ var el = driver.findElement(By.className('extraDiv'));
+ assert(el.getText()).startsWith('Another div starts here.');
+ });
+
+ test.it('should work when name is first name among many', function() {
+ driver.get(Pages.xhtmlTestPage);
+
+ var el = driver.findElement(By.className('nameA'));
+ assert(el.getText()).equalTo('An H2 title');
+ });
+
+ test.it('should work when name is last name among many', function() {
+ driver.get(Pages.xhtmlTestPage);
+
+ var el = driver.findElement(By.className('nameC'));
+ assert(el.getText()).equalTo('An H2 title');
+ });
+
+ test.it('should work when name is middle of many', function() {
+ driver.get(Pages.xhtmlTestPage);
+
+ var el = driver.findElement(By.className('nameBnoise'));
+ assert(el.getText()).equalTo('An H2 title');
+ });
+
+ test.it('should work when name surrounded by whitespace', function() {
+ driver.get(Pages.xhtmlTestPage);
+
+ var el = driver.findElement(By.className('spaceAround'));
+ assert(el.getText()).equalTo('Spaced out');
+ });
+
+ test.it('should fail if queried name only partially matches', function() {
+ driver.get(Pages.xhtmlTestPage);
+ driver.findElement(By.className('nameB')).
+ then(fail, function(e) {
+ assert(e).instanceOf(error.NoSuchElementError);
+ });
+ });
+
+ test.it('should implicitly wait', function() {
+ var TIMEOUT_IN_MS = 1000;
+ var EPSILON = TIMEOUT_IN_MS / 2;
+
+ driver.manage().timeouts().implicitlyWait(TIMEOUT_IN_MS);
+ driver.get(Pages.formPage);
+
+ var start = new Date();
+ driver.findElement(By.id('nonExistantButton')).
+ then(fail, function(e) {
+ var end = new Date();
+ assert(e).instanceOf(error.NoSuchElementError);
+ assert(end - start).closeTo(TIMEOUT_IN_MS, EPSILON);
+ });
+ });
+
+ test.it('should be able to find multiple matches', function() {
+ driver.get(Pages.xhtmlTestPage);
+ driver.findElements(By.className('nameC')).then(function(elements) {
+ assert(elements.length).greaterThan(1);
+ });
+ });
+
+ test.it('permits compound class names', function() {
+ return driver.get(Pages.xhtmlTestPage)
+ .then(() => driver.findElement(By.className('nameA nameC')))
+ .then(el => el.getText())
+ .then(text => assert(text).equalTo('An H2 title'));
+ });
+ });
+
+ describe('By.xpath()', function() {
+ test.it('should work with multiple matches', function() {
+ driver.get(Pages.xhtmlTestPage);
+ driver.findElements(By.xpath('//div')).then(function(elements) {
+ assert(elements.length).greaterThan(1);
+ });
+ });
+
+ test.it('should work for selectors using contains keyword', function() {
+ driver.get(Pages.nestedPage);
+ driver.findElement(By.xpath('//a[contains(., "hello world")]'));
+ // Pass if no error.
+ });
+ });
+
+ describe('By.tagName()', function() {
+ test.it('works', function() {
+ driver.get(Pages.formPage);
+
+ var el = driver.findElement(By.tagName('input'));
+ assert(el.getTagName()).equalTo('input');
+ });
+
+ test.it('can find multiple elements', function() {
+ driver.get(Pages.formPage);
+ driver.findElements(By.tagName('input')).then(function(elements) {
+ assert(elements.length).greaterThan(1);
+ });
+ });
+ });
+
+ describe('By.css()', function() {
+ test.it('works', function() {
+ driver.get(Pages.xhtmlTestPage);
+ driver.findElement(By.css('div.content'));
+ // Pass if no error.
+ });
+
+ test.it('can find multiple elements', function() {
+ driver.get(Pages.xhtmlTestPage);
+ driver.findElements(By.css('p')).then(function(elements) {
+ assert(elements.length).greaterThan(1);
+ });
+ // Pass if no error.
+ });
+
+ test.it(
+ 'should find first matching element when searching by ' +
+ 'compound CSS selector',
+ function() {
+ driver.get(Pages.xhtmlTestPage);
+ var el = driver.findElement(By.css('div.extraDiv, div.content'));
+ assert(el.getAttribute('class')).equalTo('content');
+ });
+
+ test.it('should be able to find multiple elements by compound selector',
+ function() {
+ driver.get(Pages.xhtmlTestPage);
+ driver.findElements(By.css('div.extraDiv, div.content')).
+ then(function(elements) {
+ assertClassIs(elements[0], 'content');
+ assertClassIs(elements[1], 'extraDiv');
+
+ function assertClassIs(el, expected) {
+ assert(el.getAttribute('class')).equalTo(expected);
+ }
+ });
+ });
+
+ // IE only supports short version option[selected].
+ test.ignore(browsers(Browser.IE)).
+ it('should be able to find element by boolean attribute', function() {
+ driver.get(test.whereIs(
+ 'locators_tests/boolean_attribute_selected.html'));
+
+ var el = driver.findElement(By.css('option[selected="selected"]'));
+ assert(el.getAttribute('value')).equalTo('two');
+ });
+
+ test.it(
+ 'should be able to find element with short ' +
+ 'boolean attribute selector',
+ function() {
+ driver.get(test.whereIs(
+ 'locators_tests/boolean_attribute_selected.html'));
+
+ var el = driver.findElement(By.css('option[selected]'));
+ assert(el.getAttribute('value')).equalTo('two');
+ });
+
+ test.it(
+ 'should be able to find element with short boolean attribute ' +
+ 'selector on HTML4 page',
+ function() {
+ driver.get(test.whereIs(
+ 'locators_tests/boolean_attribute_selected_html4.html'));
+
+ var el = driver.findElement(By.css('option[selected]'));
+ assert(el.getAttribute('value')).equalTo('two');
+ });
+ });
+
+ describe('by custom locator', function() {
+ test.it('handles single element result', function() {
+ driver.get(Pages.javascriptPage);
+
+ let link = driver.findElement(function(driver) {
+ let links = driver.findElements(By.tagName('a'));
+ return promise.filter(links, function(link) {
+ return link.getAttribute('id').then(id => id === 'updatediv');
+ }).then(links => links[0]);
+ });
+
+ assert(link.getText()).isEqualTo('Update a div');
+ });
+
+ test.it('uses first element if locator resolves to list', function() {
+ driver.get(Pages.javascriptPage);
+
+ let link = driver.findElement(function() {
+ return driver.findElements(By.tagName('a'));
+ });
+
+ assert(link.getText()).isEqualTo('Change the page title!');
+ });
+
+ test.it('fails if locator returns non-webelement value', function() {
+ driver.get(Pages.javascriptPage);
+
+ let link = driver.findElement(function() {
+ return driver.getTitle();
+ });
+
+ return link.then(
+ () => fail('Should have failed'),
+ (e) => assert(e).instanceOf(TypeError));
+ });
+ });
+ });
+});
diff --git a/node_modules/selenium-webdriver/test/execute_script_test.js b/node_modules/selenium-webdriver/test/execute_script_test.js
new file mode 100644
index 000000000..2ee150ed8
--- /dev/null
+++ b/node_modules/selenium-webdriver/test/execute_script_test.js
@@ -0,0 +1,352 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+var fail = require('assert').fail;
+
+var webdriver = require('..'),
+ Browser = webdriver.Browser,
+ By = webdriver.By,
+ assert = require('../testing/assert'),
+ test = require('../lib/test');
+
+
+test.suite(function(env) {
+ var driver;
+
+ test.before(function() {
+ driver = env.builder().build();
+ });
+
+ test.after(function() {
+ driver.quit();
+ });
+
+ test.beforeEach(function() {
+ driver.get(test.Pages.echoPage);
+ });
+
+ describe('executeScript;', function() {
+ var shouldHaveFailed = new Error('Should have failed');
+
+ test.it('fails if script throws', function() {
+ execute('throw new Error("boom")')
+ .then(function() { throw shouldHaveFailed; })
+ .catch(function(e) {
+ // The java WebDriver server adds a bunch of crap to error messages.
+ // Error message will just be "JavaScript error" for IE.
+ assert(e.message).matches(/.*(JavaScript error|boom).*/);
+ });
+ });
+
+ test.it('fails if script does not parse', function() {
+ execute('throw function\\*')
+ .then(function() { throw shouldHaveFailed; })
+ .catch(function(e) {
+ assert(e).notEqualTo(shouldHaveFailed);
+ });
+ });
+
+ describe('scripts;', function() {
+ test.it('do not pollute the global scope', function() {
+ execute('var x = 1;');
+ assert(execute('return typeof x;')).equalTo('undefined');
+ });
+
+ test.it('can set global variables', function() {
+ execute('window.x = 1234;');
+ assert(execute('return x;')).equalTo(1234);
+ });
+
+ test.it('may be defined as a function expression', function() {
+ assert(execute(function() {
+ return 1234 + 'abc';
+ })).equalTo('1234abc');
+ });
+ });
+
+ describe('return values;', function() {
+
+ test.it('returns undefined as null', function() {
+ assert(execute('var x; return x;')).isNull();
+ });
+
+ test.it('can return null', function() {
+ assert(execute('return null;')).isNull();
+ });
+
+ test.it('can return numbers', function() {
+ assert(execute('return 1234')).equalTo(1234);
+ assert(execute('return 3.1456')).equalTo(3.1456);
+ });
+
+ test.it('can return strings', function() {
+ assert(execute('return "hello"')).equalTo('hello');
+ });
+
+ test.it('can return booleans', function() {
+ assert(execute('return true')).equalTo(true);
+ assert(execute('return false')).equalTo(false);
+ });
+
+ test.it('can return an array of primitives', function() {
+ execute('var x; return [1, false, null, 3.14, x]')
+ .then(verifyJson([1, false, null, 3.14, null]));
+ });
+
+ test.it('can return nested arrays', function() {
+ execute('return [[1, 2, [3]]]')
+ .then(verifyJson([[1, 2, [3]]]));
+ });
+
+ test.ignore(env.browsers(Browser.IE, Browser.SAFARI)).
+ it('can return empty object literal', function() {
+ execute('return {}').then(verifyJson({}));
+ });
+
+ test.it('can return object literals', function() {
+ execute('return {a: 1, b: false, c: null}').then(function(result) {
+ verifyJson(['a', 'b', 'c'])(Object.keys(result).sort());
+ assert(result.a).equalTo(1);
+ assert(result.b).equalTo(false);
+ assert(result.c).isNull();
+ });
+ });
+
+ test.it('can return complex object literals', function() {
+ execute('return {a:{b: "hello"}}').then(verifyJson({a:{b: 'hello'}}));
+ });
+
+ test.it('can return dom elements as web elements', function() {
+ execute('return document.querySelector(".header.host")')
+ .then(function(result) {
+ assert(result).instanceOf(webdriver.WebElement);
+ assert(result.getText()).startsWith('host: ');
+ });
+ });
+
+ test.it('can return array of dom elements', function() {
+ execute('var nodes = document.querySelectorAll(".request,.host");' +
+ 'return [nodes[0], nodes[1]];')
+ .then(function(result) {
+ assert(result.length).equalTo(2);
+
+ assert(result[0]).instanceOf(webdriver.WebElement);
+ assert(result[0].getText()).startsWith('GET ');
+
+ assert(result[1]).instanceOf(webdriver.WebElement);
+ assert(result[1].getText()).startsWith('host: ');
+ });
+ });
+
+ test.it('can return a NodeList as an array of web elements', function() {
+ execute('return document.querySelectorAll(".request,.host");')
+ .then(function(result) {
+ assert(result.length).equalTo(2);
+
+ assert(result[0]).instanceOf(webdriver.WebElement);
+ assert(result[0].getText()).startsWith('GET ');
+
+ assert(result[1]).instanceOf(webdriver.WebElement);
+ assert(result[1].getText()).startsWith('host: ');
+ });
+ });
+
+ test.it('can return object literal with element property', function() {
+ execute('return {a: document.body}').then(function(result) {
+ assert(result.a).instanceOf(webdriver.WebElement);
+ assert(result.a.getTagName()).equalTo('body');
+ });
+ });
+ });
+
+ describe('parameters;', function() {
+ test.it('can pass numeric arguments', function() {
+ assert(execute('return arguments[0]', 12)).equalTo(12);
+ assert(execute('return arguments[0]', 3.14)).equalTo(3.14);
+ });
+
+ test.it('can pass boolean arguments', function() {
+ assert(execute('return arguments[0]', true)).equalTo(true);
+ assert(execute('return arguments[0]', false)).equalTo(false);
+ });
+
+ test.it('can pass string arguments', function() {
+ assert(execute('return arguments[0]', 'hi')).equalTo('hi');
+ });
+
+ test.it('can pass null arguments', function() {
+ assert(execute('return arguments[0] === null', null)).equalTo(true);
+ assert(execute('return arguments[0]', null)).equalTo(null);
+ });
+
+ test.it('passes undefined as a null argument', function() {
+ var x;
+ assert(execute('return arguments[0] === null', x)).equalTo(true);
+ assert(execute('return arguments[0]', x)).equalTo(null);
+ });
+
+ test.it('can pass multiple arguments', function() {
+ assert(execute('return arguments.length')).equalTo(0);
+ assert(execute('return arguments.length', 1, 'a', false)).equalTo(3);
+ });
+
+ test.ignore(env.browsers(Browser.FIREFOX)).
+ it('can return arguments object as array', function() {
+ execute('return arguments', 1, 'a', false).then(function(val) {
+ assert(val.length).equalTo(3);
+ assert(val[0]).equalTo(1);
+ assert(val[1]).equalTo('a');
+ assert(val[2]).equalTo(false);
+ });
+ });
+
+ test.it('can pass object literal', function() {
+ execute(
+ 'return [typeof arguments[0], arguments[0].a]', {a: 'hello'})
+ .then(function(result) {
+ assert(result[0]).equalTo('object');
+ assert(result[1]).equalTo('hello');
+ });
+ });
+
+ test.it('WebElement arguments are passed as DOM elements', function() {
+ var el = driver.findElement(By.tagName('div'));
+ assert(execute('return arguments[0].tagName.toLowerCase();', el))
+ .equalTo('div');
+ });
+
+ test.it('can pass array containing object literals', function() {
+ execute('return arguments[0]', [{color: "red"}]).then(function(result) {
+ assert(result.length).equalTo(1);
+ assert(result[0].color).equalTo('red');
+ });
+ });
+
+ test.it('does not modify object literal parameters', function() {
+ var input = {color: 'red'};
+ execute('return arguments[0];', input).then(verifyJson(input));
+ });
+ });
+
+ // See https://code.google.com/p/selenium/issues/detail?id=8223.
+ describe('issue 8223;', function() {
+ describe('using for..in loops;', function() {
+ test.it('can return array built from for-loop index', function() {
+ execute(function() {
+ var ret = [];
+ for (var i = 0; i < 3; i++) {
+ ret.push(i);
+ }
+ return ret;
+ }).then(verifyJson[0, 1, 2]);
+ });
+
+ test.it('can copy input array contents', function() {
+ execute(function(input) {
+ var ret = [];
+ for (var i in input) {
+ ret.push(input[i]);
+ }
+ return ret;
+ }, ['fa', 'fe', 'fi']).then(verifyJson(['fa', 'fe', 'fi']));
+ });
+
+ test.it('can iterate over input object keys', function() {
+ execute(function(thing) {
+ var ret = [];
+ for (var w in thing.words) {
+ ret.push(thing.words[w].word);
+ }
+ return ret;
+ }, {words: [{word: 'fa'}, {word: 'fe'}, {word: 'fi'}]})
+ .then(verifyJson(['fa', 'fe', 'fi']));
+ });
+
+ describe('recursive functions;', function() {
+ test.it('can build array from input', function() {
+ var input = ['fa', 'fe', 'fi'];
+ execute(function(thearray) {
+ var ret = [];
+ function build_response(thearray, ret) {
+ ret.push(thearray.shift());
+ return (!thearray.length && ret
+ || build_response(thearray, ret));
+ }
+ return build_response(thearray, ret);
+ }, input).then(verifyJson(input));
+ });
+
+ test.it('can build array from elements in object', function() {
+ var input = {words: [{word: 'fa'}, {word: 'fe'}, {word: 'fi'}]};
+ execute(function(thing) {
+ var ret = [];
+ function build_response(thing, ret) {
+ var item = thing.words.shift();
+ ret.push(item.word);
+ return (!thing.words.length && ret
+ || build_response(thing, ret));
+ }
+ return build_response(thing, ret);
+ }, input).then(verifyJson(['fa', 'fe', 'fi']));
+ });
+ });
+ });
+ });
+
+ describe('async timeouts', function() {
+ var TIMEOUT_IN_MS = 200;
+ var ACCEPTABLE_WAIT = TIMEOUT_IN_MS / 10;
+ var TOO_LONG_WAIT = TIMEOUT_IN_MS * 10;
+
+ before(function() {
+ return driver.manage().timeouts().setScriptTimeout(TIMEOUT_IN_MS)
+ });
+
+ test.it('does not fail if script execute in time', function() {
+ return executeTimeOutScript(ACCEPTABLE_WAIT);
+ });
+
+ test.it('fails if script took too long', function() {
+ return executeTimeOutScript(TOO_LONG_WAIT)
+ .then(function() {
+ fail('it should have timed out');
+ }).catch(function(e) {
+ assert(e.name).equalTo('ScriptTimeoutError');
+ });
+ });
+
+ function executeTimeOutScript(sleepTime) {
+ return driver.executeAsyncScript(function(sleepTime) {
+ var callback = arguments[arguments.length - 1];
+ setTimeout(callback, sleepTime)
+ }, sleepTime);
+ }
+ })
+ });
+
+ function verifyJson(expected) {
+ return function(actual) {
+ assert(JSON.stringify(actual)).equalTo(JSON.stringify(expected));
+ };
+ }
+
+ function execute() {
+ return driver.executeScript.apply(driver, arguments);
+ }
+});
diff --git a/node_modules/selenium-webdriver/test/fingerprint_test.js b/node_modules/selenium-webdriver/test/fingerprint_test.js
new file mode 100644
index 000000000..ca36ce142
--- /dev/null
+++ b/node_modules/selenium-webdriver/test/fingerprint_test.js
@@ -0,0 +1,57 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+var assert = require('../testing/assert'),
+ test = require('../lib/test'),
+ Pages = test.Pages;
+
+
+test.suite(function(env) {
+ var browsers = env.browsers;
+
+ var driver;
+ test.before(function() {
+ driver = env.builder().build();
+ });
+
+ test.after(function() {
+ driver.quit();
+ });
+
+ describe('fingerprinting', function() {
+ test.it('it should fingerprint the navigator object', function() {
+ driver.get(Pages.simpleTestPage);
+ assert(driver.executeScript('return navigator.webdriver')).equalTo(true);
+ });
+
+ test.it('fingerprint must not be writable', function() {
+ driver.get(Pages.simpleTestPage);
+ assert(driver.executeScript(
+ 'navigator.webdriver = "ohai"; return navigator.webdriver'))
+ .equalTo(true);
+ });
+
+ test.it('leaves fingerprint on svg pages', function() {
+ driver.get(Pages.svgPage);
+ assert(driver.executeScript('return navigator.webdriver')).equalTo(true);
+ });
+ });
+
+// Currently only implemented in legacy firefox.
+}, {browsers: ['legacy-firefox']});
diff --git a/node_modules/selenium-webdriver/test/firefox/extension_test.js b/node_modules/selenium-webdriver/test/firefox/extension_test.js
new file mode 100644
index 000000000..50936f7cd
--- /dev/null
+++ b/node_modules/selenium-webdriver/test/firefox/extension_test.js
@@ -0,0 +1,96 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+var AdmZip = require('adm-zip'),
+ assert = require('assert'),
+ crypto = require('crypto'),
+ fs = require('fs'),
+ path = require('path');
+
+var extension = require('../../firefox/extension'),
+ io = require('../../io'),
+ it = require('../../testing').it;
+
+
+var JETPACK_EXTENSION = path.join(__dirname,
+ '../../lib/test/data/firefox/jetpack-sample.xpi');
+var NORMAL_EXTENSION = path.join(__dirname,
+ '../../lib/test/data/firefox/sample.xpi');
+
+var JETPACK_EXTENSION_ID = 'jid1-EaXX7k0wwiZR7w@jetpack';
+var NORMAL_EXTENSION_ID = 'sample@seleniumhq.org';
+
+
+describe('extension', function() {
+ it('can install a jetpack xpi file', function() {
+ return io.tmpDir().then(function(dir) {
+ return extension.install(JETPACK_EXTENSION, dir).then(function(id) {
+ assert.equal(JETPACK_EXTENSION_ID, id);
+ var file = path.join(dir, id + '.xpi');
+ assert.ok(fs.existsSync(file), 'no such file: ' + file);
+ assert.ok(!fs.statSync(file).isDirectory());
+
+ var copiedSha1 = crypto.createHash('sha1')
+ .update(fs.readFileSync(file))
+ .digest('hex');
+
+ var goldenSha1 = crypto.createHash('sha1')
+ .update(fs.readFileSync(JETPACK_EXTENSION))
+ .digest('hex');
+
+ assert.equal(copiedSha1, goldenSha1);
+ });
+ });
+ });
+
+ it('can install a normal xpi file', function() {
+ return io.tmpDir().then(function(dir) {
+ return extension.install(NORMAL_EXTENSION, dir).then(function(id) {
+ assert.equal(NORMAL_EXTENSION_ID, id);
+
+ var file = path.join(dir, NORMAL_EXTENSION_ID);
+ assert.ok(fs.statSync(file).isDirectory());
+
+ assert.ok(fs.existsSync(path.join(file, 'chrome.manifest')));
+ assert.ok(fs.existsSync(path.join(file, 'content/overlay.xul')));
+ assert.ok(fs.existsSync(path.join(file, 'content/overlay.js')));
+ assert.ok(fs.existsSync(path.join(file, 'install.rdf')));
+ });
+ });
+ });
+
+ it('can install an extension from a directory', function() {
+ return io.tmpDir().then(function(srcDir) {
+ var buf = fs.readFileSync(NORMAL_EXTENSION);
+ new AdmZip(buf).extractAllTo(srcDir, true);
+ return io.tmpDir().then(function(dstDir) {
+ return extension.install(srcDir, dstDir).then(function(id) {
+ assert.equal(NORMAL_EXTENSION_ID, id);
+
+ var dir = path.join(dstDir, NORMAL_EXTENSION_ID);
+
+ assert.ok(fs.existsSync(path.join(dir, 'chrome.manifest')));
+ assert.ok(fs.existsSync(path.join(dir, 'content/overlay.xul')));
+ assert.ok(fs.existsSync(path.join(dir, 'content/overlay.js')));
+ assert.ok(fs.existsSync(path.join(dir, 'install.rdf')));
+ });
+ });
+ });
+ });
+});
diff --git a/node_modules/selenium-webdriver/test/firefox/firefox_test.js b/node_modules/selenium-webdriver/test/firefox/firefox_test.js
new file mode 100644
index 000000000..6f3f4b3d9
--- /dev/null
+++ b/node_modules/selenium-webdriver/test/firefox/firefox_test.js
@@ -0,0 +1,188 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+var path = require('path');
+
+var firefox = require('../../firefox'),
+ io = require('../../io'),
+ test = require('../../lib/test'),
+ assert = require('../../testing/assert'),
+ Context = require('../../firefox').Context,
+ error = require('../..').error;
+
+
+var JETPACK_EXTENSION = path.join(__dirname,
+ '../../lib/test/data/firefox/jetpack-sample.xpi');
+var NORMAL_EXTENSION = path.join(__dirname,
+ '../../lib/test/data/firefox/sample.xpi');
+
+
+test.suite(function(env) {
+ describe('firefox', function() {
+ describe('Options', function() {
+ var driver;
+
+ test.beforeEach(function() {
+ driver = null;
+ });
+
+ test.afterEach(function() {
+ if (driver) {
+ driver.quit();
+ }
+ });
+
+ test.it('can start Firefox with custom preferences', function() {
+ var profile = new firefox.Profile();
+ profile.setPreference('general.useragent.override', 'foo;bar');
+
+ var options = new firefox.Options().setProfile(profile);
+
+ driver = env.builder().
+ setFirefoxOptions(options).
+ build();
+
+ driver.get('data:text/html,<html><div>content</div></html>');
+
+ var userAgent = driver.executeScript(
+ 'return window.navigator.userAgent');
+ assert(userAgent).equalTo('foo;bar');
+ });
+
+ test.it('can start Firefox with a jetpack extension', function() {
+ var profile = new firefox.Profile();
+ profile.addExtension(JETPACK_EXTENSION);
+
+ var options = new firefox.Options().setProfile(profile);
+
+ driver = env.builder().
+ setFirefoxOptions(options).
+ build();
+
+ loadJetpackPage(driver,
+ 'data:text/html;charset=UTF-8,<html><div>content</div></html>');
+ assert(driver.findElement({id: 'jetpack-sample-banner'}).getText())
+ .equalTo('Hello, world!');
+ });
+
+ test.it('can start Firefox with a normal extension', function() {
+ var profile = new firefox.Profile();
+ profile.addExtension(NORMAL_EXTENSION);
+
+ var options = new firefox.Options().setProfile(profile);
+
+ driver = env.builder().
+ setFirefoxOptions(options).
+ build();
+
+ driver.get('data:text/html,<html><div>content</div></html>');
+ assert(driver.findElement({id: 'sample-extension-footer'}).getText())
+ .equalTo('Goodbye');
+ });
+
+ test.it('can start Firefox with multiple extensions', function() {
+ var profile = new firefox.Profile();
+ profile.addExtension(JETPACK_EXTENSION);
+ profile.addExtension(NORMAL_EXTENSION);
+
+ var options = new firefox.Options().setProfile(profile);
+
+ driver = env.builder().
+ setFirefoxOptions(options).
+ build();
+
+ loadJetpackPage(driver,
+ 'data:text/html;charset=UTF-8,<html><div>content</div></html>');
+ assert(driver.findElement({id: 'jetpack-sample-banner'}).getText())
+ .equalTo('Hello, world!');
+ assert(driver.findElement({id: 'sample-extension-footer'}).getText())
+ .equalTo('Goodbye');
+ });
+
+ function loadJetpackPage(driver, url) {
+ // On linux the jetpack extension does not always run the first time
+ // we load a page. If this happens, just reload the page (a simple
+ // refresh doesn't appear to work).
+ driver.wait(function() {
+ driver.get(url);
+ return driver.findElements({id: 'jetpack-sample-banner'})
+ .then(found => found.length > 0);
+ }, 3000);
+ }
+ });
+
+ describe('binary management', function() {
+ var driver1, driver2;
+
+ test.ignore(env.isRemote).
+ it('can start multiple sessions with single binary instance', function() {
+ var options = new firefox.Options().setBinary(new firefox.Binary);
+ env.builder().setFirefoxOptions(options);
+ driver1 = env.builder().build();
+ driver2 = env.builder().build();
+ // Ok if this doesn't fail.
+ });
+
+ test.afterEach(function() {
+ if (driver1) {
+ driver1.quit();
+ }
+
+ if (driver2) {
+ driver2.quit();
+ }
+ });
+ });
+
+ describe('context switching', function() {
+ var driver;
+
+ test.beforeEach(function() {
+ driver = env.builder().build();
+ });
+
+ test.afterEach(function() {
+ if (driver) {
+ driver.quit();
+ }
+ });
+
+ test.ignore(() => !env.isMarionette).
+ it('can get context', function() {
+ assert(driver.getContext()).equalTo(Context.CONTENT);
+ });
+
+ test.ignore(() => !env.isMarionette).
+ it('can set context', function() {
+ driver.setContext(Context.CHROME);
+ assert(driver.getContext()).equalTo(Context.CHROME);
+ driver.setContext(Context.CONTENT);
+ assert(driver.getContext()).equalTo(Context.CONTENT);
+ });
+
+ test.ignore(() => !env.isMarionette).
+ it('throws on unknown context', function() {
+ driver.setContext("foo").then(assert.fail, function(e) {
+ assert(e).instanceOf(error.InvalidArgumentError);
+ });
+ });
+ });
+
+ });
+}, {browsers: ['firefox']});
diff --git a/node_modules/selenium-webdriver/test/firefox/profile_test.js b/node_modules/selenium-webdriver/test/firefox/profile_test.js
new file mode 100644
index 000000000..807c07b72
--- /dev/null
+++ b/node_modules/selenium-webdriver/test/firefox/profile_test.js
@@ -0,0 +1,186 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+var AdmZip = require('adm-zip'),
+ assert = require('assert'),
+ fs = require('fs'),
+ path = require('path');
+
+var promise = require('../..').promise,
+ Profile = require('../../firefox/profile').Profile,
+ decode = require('../../firefox/profile').decode,
+ loadUserPrefs = require('../../firefox/profile').loadUserPrefs,
+ io = require('../../io');
+
+
+var JETPACK_EXTENSION = path.join(__dirname,
+ '../../lib/test/data/firefox/jetpack-sample.xpi');
+var NORMAL_EXTENSION = path.join(__dirname,
+ '../../lib/test/data/firefox/sample.xpi');
+
+var JETPACK_EXTENSION_ID = 'jid1-EaXX7k0wwiZR7w@jetpack.xpi';
+var NORMAL_EXTENSION_ID = 'sample@seleniumhq.org';
+var WEBDRIVER_EXTENSION_ID = 'fxdriver@googlecode.com';
+
+
+
+describe('Profile', function() {
+ describe('setPreference', function() {
+ it('allows setting custom properties', function() {
+ var profile = new Profile();
+ assert.equal(undefined, profile.getPreference('foo'));
+
+ profile.setPreference('foo', 'bar');
+ assert.equal('bar', profile.getPreference('foo'));
+ });
+
+ it('allows overriding mutable properties', function() {
+ var profile = new Profile();
+ assert.equal('about:blank', profile.getPreference('browser.newtab.url'));
+
+ profile.setPreference('browser.newtab.url', 'http://www.example.com');
+ assert.equal('http://www.example.com',
+ profile.getPreference('browser.newtab.url'));
+ });
+
+ it('throws if setting a frozen preference', function() {
+ var profile = new Profile();
+ assert.throws(function() {
+ profile.setPreference('app.update.auto', true);
+ });
+ });
+ });
+
+ describe('writeToDisk', function() {
+ it('copies template directory recursively', function() {
+ var templateDir;
+ return io.tmpDir().then(function(td) {
+ templateDir = td;
+ var foo = path.join(templateDir, 'foo');
+ fs.writeFileSync(foo, 'Hello, world');
+
+ var bar = path.join(templateDir, 'subfolder/bar');
+ fs.mkdirSync(path.dirname(bar));
+ fs.writeFileSync(bar, 'Goodbye, world!');
+
+ return new Profile(templateDir).writeToDisk();
+ }).then(function(profileDir) {
+ assert.notEqual(profileDir, templateDir);
+
+ assert.equal('Hello, world',
+ fs.readFileSync(path.join(profileDir, 'foo')));
+ assert.equal('Goodbye, world!',
+ fs.readFileSync(path.join(profileDir, 'subfolder/bar')));
+ });
+ });
+
+ it('does not copy lock files', function() {
+ return io.tmpDir().then(function(dir) {
+ fs.writeFileSync(path.join(dir, 'parent.lock'), 'lock');
+ fs.writeFileSync(path.join(dir, 'lock'), 'lock');
+ fs.writeFileSync(path.join(dir, '.parentlock'), 'lock');
+ return new Profile(dir).writeToDisk();
+ }).then(function(dir) {
+ assert.ok(fs.existsSync(dir));
+ assert.ok(!fs.existsSync(path.join(dir, 'parent.lock')));
+ assert.ok(!fs.existsSync(path.join(dir, 'lock')));
+ assert.ok(!fs.existsSync(path.join(dir, '.parentlock')));
+ });
+ });
+
+ describe('user.js', function() {
+
+ it('writes defaults', function() {
+ return new Profile().writeToDisk().then(function(dir) {
+ return loadUserPrefs(path.join(dir, 'user.js'));
+ }).then(function(prefs) {
+ // Just check a few.
+ assert.equal(false, prefs['app.update.auto']);
+ assert.equal(true, prefs['browser.EULA.override']);
+ assert.equal(false, prefs['extensions.update.enabled']);
+ assert.equal('about:blank', prefs['browser.newtab.url']);
+ assert.equal(30, prefs['dom.max_script_run_time']);
+ });
+ });
+
+ it('merges template user.js into preferences', function() {
+ return io.tmpDir().then(function(dir) {
+ fs.writeFileSync(path.join(dir, 'user.js'), [
+ 'user_pref("browser.newtab.url", "http://www.example.com")',
+ 'user_pref("dom.max_script_run_time", 1234)'
+ ].join('\n'));
+
+ return new Profile(dir).writeToDisk();
+ }).then(function(profile) {
+ return loadUserPrefs(path.join(profile, 'user.js'));
+ }).then(function(prefs) {
+ assert.equal('http://www.example.com', prefs['browser.newtab.url']);
+ assert.equal(1234, prefs['dom.max_script_run_time']);
+ });
+ });
+
+ it('ignores frozen preferences when merging template user.js',
+ function() {
+ return io.tmpDir().then(function(dir) {
+ fs.writeFileSync(path.join(dir, 'user.js'),
+ 'user_pref("app.update.auto", true)');
+ return new Profile(dir).writeToDisk();
+ }).then(function(profile) {
+ return loadUserPrefs(path.join(profile, 'user.js'));
+ }).then(function(prefs) {
+ assert.equal(false, prefs['app.update.auto']);
+ });
+ });
+ });
+
+ describe('extensions', function() {
+ it('are copied into new profile directory', function() {
+ var profile = new Profile();
+ profile.addExtension(JETPACK_EXTENSION);
+ profile.addExtension(NORMAL_EXTENSION);
+
+ return profile.writeToDisk().then(function(dir) {
+ dir = path.join(dir, 'extensions');
+ assert.ok(fs.existsSync(path.join(dir, JETPACK_EXTENSION_ID)));
+ assert.ok(fs.existsSync(path.join(dir, NORMAL_EXTENSION_ID)));
+ assert.ok(fs.existsSync(path.join(dir, WEBDRIVER_EXTENSION_ID)));
+ });
+ });
+ });
+ });
+
+ describe('encode', function() {
+ it('excludes the bundled WebDriver extension', function() {
+ return new Profile().encode().then(function(data) {
+ return decode(data);
+ }).then(function(dir) {
+ assert.ok(fs.existsSync(path.join(dir, 'user.js')));
+ assert.ok(fs.existsSync(path.join(dir, 'extensions')));
+ return loadUserPrefs(path.join(dir, 'user.js'));
+ }).then(function(prefs) {
+ // Just check a few.
+ assert.equal(false, prefs['app.update.auto']);
+ assert.equal(true, prefs['browser.EULA.override']);
+ assert.equal(false, prefs['extensions.update.enabled']);
+ assert.equal('about:blank', prefs['browser.newtab.url']);
+ assert.equal(30, prefs['dom.max_script_run_time']);
+ });
+ });
+ });
+});
diff --git a/node_modules/selenium-webdriver/test/http/http_test.js b/node_modules/selenium-webdriver/test/http/http_test.js
new file mode 100644
index 000000000..6056cda8e
--- /dev/null
+++ b/node_modules/selenium-webdriver/test/http/http_test.js
@@ -0,0 +1,205 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+var assert = require('assert'),
+ http = require('http'),
+ url = require('url');
+
+var HttpClient = require('../../http').HttpClient,
+ HttpRequest = require('../../lib/http').Request,
+ HttpResponse = require('../../lib/http').Response,
+ Server = require('../../lib/test/httpserver').Server;
+
+describe('HttpClient', function() {
+ this.timeout(4 * 1000);
+
+ var server = new Server(function(req, res) {
+ let parsedUrl = url.parse(req.url);
+
+ if (req.method == 'GET' && req.url == '/echo') {
+ res.writeHead(200, req.headers);
+ res.end();
+
+ } else if (req.method == 'GET' && req.url == '/redirect') {
+ res.writeHead(303, {'Location': server.url('/hello')});
+ res.end();
+
+ } else if (req.method == 'GET' && req.url == '/hello') {
+ res.writeHead(200, {'content-type': 'text/plain'});
+ res.end('hello, world!');
+
+ } else if (req.method == 'GET' && req.url == '/badredirect') {
+ res.writeHead(303, {});
+ res.end();
+
+ } else if (req.method == 'GET' && req.url == '/protected') {
+ var denyAccess = function() {
+ res.writeHead(401, {'WWW-Authenticate': 'Basic realm="test"'});
+ res.end('Access denied');
+ };
+
+ var basicAuthRegExp = /^\s*basic\s+([a-z0-9\-\._~\+\/]+)=*\s*$/i
+ var auth = req.headers.authorization;
+ var match = basicAuthRegExp.exec(auth || '');
+ if (!match) {
+ denyAccess();
+ return;
+ }
+
+ var userNameAndPass = new Buffer(match[1], 'base64').toString();
+ var parts = userNameAndPass.split(':', 2);
+ if (parts[0] !== 'genie' && parts[1] !== 'bottle') {
+ denyAccess();
+ return;
+ }
+
+ res.writeHead(200, {'content-type': 'text/plain'});
+ res.end('Access granted!');
+
+ } else if (req.method == 'GET'
+ && parsedUrl.pathname
+ && parsedUrl.pathname.endsWith('/proxy')) {
+ let headers = Object.assign({}, req.headers);
+ headers['x-proxy-request-uri'] = req.url;
+ res.writeHead(200, headers);
+ res.end();
+
+ } else if (req.method == 'GET'
+ && parsedUrl.pathname
+ && parsedUrl.pathname.endsWith('/proxy/redirect')) {
+ let path = `/proxy${parsedUrl.search || ''}${parsedUrl.hash || ''}`;
+ res.writeHead(303, {'Location': path});
+ res.end();
+
+ } else {
+ res.writeHead(404, {});
+ res.end();
+ }
+ });
+
+ before(function() {
+ return server.start();
+ });
+
+ after(function() {
+ return server.stop();
+ });
+
+ it('can send a basic HTTP request', function() {
+ var request = new HttpRequest('GET', '/echo');
+ request.headers.set('Foo', 'Bar');
+
+ var agent = new http.Agent();
+ agent.maxSockets = 1; // Only making 1 request.
+
+ var client = new HttpClient(server.url(), agent);
+ return client.send(request).then(function(response) {
+ assert.equal(200, response.status);
+ assert.equal(response.headers.get('content-length'), '0');
+ assert.equal(response.headers.get('connection'), 'keep-alive');
+ assert.equal(response.headers.get('host'), server.host());
+
+ assert.equal(request.headers.get('Foo'), 'Bar');
+ assert.equal(
+ request.headers.get('Accept'), 'application/json; charset=utf-8');
+ });
+ });
+
+ it('can use basic auth', function() {
+ var parsed = url.parse(server.url());
+ parsed.auth = 'genie:bottle';
+
+ var client = new HttpClient(url.format(parsed));
+ var request = new HttpRequest('GET', '/protected');
+ return client.send(request).then(function(response) {
+ assert.equal(200, response.status);
+ assert.equal(response.headers.get('content-type'), 'text/plain');
+ assert.equal(response.body, 'Access granted!');
+ });
+ });
+
+ it('fails requests missing required basic auth', function() {
+ var client = new HttpClient(server.url());
+ var request = new HttpRequest('GET', '/protected');
+ return client.send(request).then(function(response) {
+ assert.equal(401, response.status);
+ assert.equal(response.body, 'Access denied');
+ });
+ });
+
+ it('automatically follows redirects', function() {
+ var request = new HttpRequest('GET', '/redirect');
+ var client = new HttpClient(server.url());
+ return client.send(request).then(function(response) {
+ assert.equal(200, response.status);
+ assert.equal(response.headers.get('content-type'), 'text/plain');
+ assert.equal(response.body, 'hello, world!');
+ });
+ });
+
+ it('handles malformed redirect responses', function() {
+ var request = new HttpRequest('GET', '/badredirect');
+ var client = new HttpClient(server.url());
+ return client.send(request).then(assert.fail, function(err) {
+ assert.ok(/Failed to parse "Location"/.test(err.message),
+ 'Not the expected error: ' + err.message);
+ });
+ });
+
+ describe('with proxy', function() {
+ it('sends request to proxy with absolute URI', function() {
+ var request = new HttpRequest('GET', '/proxy');
+ var client = new HttpClient(
+ 'http://another.server.com', undefined, server.url());
+ return client.send(request).then(function(response) {
+ assert.equal(200, response.status);
+ assert.equal(response.headers.get('host'), 'another.server.com');
+ assert.equal(
+ response.headers.get('x-proxy-request-uri'),
+ 'http://another.server.com/proxy');
+ });
+ });
+
+ it('uses proxy when following redirects', function() {
+ var request = new HttpRequest('GET', '/proxy/redirect');
+ var client = new HttpClient(
+ 'http://another.server.com', undefined, server.url());
+ return client.send(request).then(function(response) {
+ assert.equal(200, response.status);
+ assert.equal(response.headers.get('host'), 'another.server.com');
+ assert.equal(
+ response.headers.get('x-proxy-request-uri'),
+ 'http://another.server.com/proxy');
+ });
+ });
+
+ it('includes search and hash in redirect URI', function() {
+ var request = new HttpRequest('GET', '/proxy/redirect?foo#bar');
+ var client = new HttpClient(
+ 'http://another.server.com', undefined, server.url());
+ return client.send(request).then(function(response) {
+ assert.equal(200, response.status);
+ assert.equal(response.headers.get('host'), 'another.server.com');
+ assert.equal(
+ response.headers.get('x-proxy-request-uri'),
+ 'http://another.server.com/proxy?foo#bar');
+ });
+ });
+ });
+});
diff --git a/node_modules/selenium-webdriver/test/http/util_test.js b/node_modules/selenium-webdriver/test/http/util_test.js
new file mode 100644
index 000000000..aa7a9158a
--- /dev/null
+++ b/node_modules/selenium-webdriver/test/http/util_test.js
@@ -0,0 +1,184 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+var assert = require('assert'),
+ http = require('http');
+
+var error = require('../../lib/error');
+var util = require('../../http/util');
+
+describe('selenium-webdriver/http/util', function() {
+
+ var server, baseUrl;
+
+ var status, value, responseCode;
+
+ function startServer(done) {
+ if (server) return done();
+
+ server = http.createServer(function(req, res) {
+ var data = JSON.stringify({status: status, value: value});
+ res.writeHead(responseCode, {
+ 'Content-Type': 'application/json; charset=utf-8',
+ 'Content-Length': Buffer.byteLength(data, 'utf8')
+ });
+ res.end(data);
+ });
+
+ server.listen(0, '127.0.0.1', function(e) {
+ if (e) return done(e);
+
+ var addr = server.address();
+ baseUrl = 'http://' + addr.address + ':' + addr.port;
+ done();
+ });
+ }
+
+ function killServer(done) {
+ if (!server) return done();
+ server.close(done);
+ server = null;
+ }
+
+ after(killServer);
+
+ beforeEach(function(done) {
+ status = 0;
+ value = 'abc123';
+ responseCode = 200;
+ startServer(done);
+ });
+
+ describe('#getStatus', function() {
+ it('should return value field on success', function() {
+ return util.getStatus(baseUrl).then(function(response) {
+ assert.equal('abc123', response);
+ });
+ });
+
+ it('should fail if response object is not success', function() {
+ status = 1;
+ return util.getStatus(baseUrl).then(function() {
+ throw Error('expected a failure');
+ }, function(err) {
+ assert.ok(err instanceof error.WebDriverError);
+ assert.equal(err.code, error.WebDriverError.code);
+ assert.equal(err.message, value);
+ });
+ });
+
+ it('should fail if the server is not listening', function(done) {
+ killServer(function(e) {
+ if(e) return done(e);
+
+ util.getStatus(baseUrl).then(function() {
+ done(Error('expected a failure'));
+ }, function() {
+ // Expected.
+ done();
+ });
+ });
+ });
+
+ it('should fail if HTTP status is not 200', function() {
+ status = 1;
+ responseCode = 404;
+ return util.getStatus(baseUrl).then(function() {
+ throw Error('expected a failure');
+ }, function(err) {
+ assert.ok(err instanceof error.WebDriverError);
+ assert.equal(err.code, error.WebDriverError.code);
+ assert.equal(err.message, value);
+ });
+ });
+ });
+
+ describe('#waitForServer', function() {
+ it('resolves when server is ready', function() {
+ status = 1;
+ setTimeout(function() { status = 0; }, 50);
+ return util.waitForServer(baseUrl, 100);
+ });
+
+ it('should fail if server does not become ready', function() {
+ status = 1;
+ return util.waitForServer(baseUrl, 50).
+ then(function() {throw Error('Expected to time out')},
+ function() {});
+ });
+
+ it('can cancel wait', function(done) {
+ status = 1;
+ var err = Error('cancelled!');
+ var isReady = util.waitForServer(baseUrl, 200).
+ then(function() { done('Did not expect to succeed'); }).
+ then(null, function(e) {
+ assert.equal('cancelled!', e.message);
+ }).
+ then(function() { done(); }, done);
+
+ setTimeout(function() {
+ isReady.cancel('cancelled!');
+ }, 50);
+ });
+ });
+
+ describe('#waitForUrl', function() {
+ it('succeeds when URL returns 2xx', function() {
+ responseCode = 404;
+ setTimeout(function() { responseCode = 200; }, 50);
+
+ return util.waitForUrl(baseUrl, 200);
+ });
+
+ it('fails if URL always returns 4xx', function() {
+ responseCode = 404;
+
+ return util.waitForUrl(baseUrl, 50)
+ .then(() => assert.fail('Expected to time out'),
+ () => true);
+ });
+
+ it('fails if cannot connect to server', function() {
+ return new Promise((resolve, reject) => {
+ killServer(function(e) {
+ if (e) return reject(e);
+
+ util.waitForUrl(baseUrl, 50).
+ then(function() { reject(Error('Expected to time out')); },
+ function() { resolve(); });
+ });
+ });
+ });
+
+ it('can cancel wait', function(done) {
+ responseCode = 404;
+ var isReady = util.waitForUrl(baseUrl, 200).
+ then(function() { done('Did not expect to succeed'); }).
+ then(null, function(e) {
+ assert.equal('cancelled!', e.message);
+ }).
+ then(function() { done(); }, done);
+
+ setTimeout(function() {
+ isReady.cancel('cancelled!');
+ }, 50);
+ });
+ });
+});
diff --git a/node_modules/selenium-webdriver/test/io_test.js b/node_modules/selenium-webdriver/test/io_test.js
new file mode 100644
index 000000000..42ec8d3a3
--- /dev/null
+++ b/node_modules/selenium-webdriver/test/io_test.js
@@ -0,0 +1,321 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+var assert = require('assert'),
+ fs = require('fs'),
+ path = require('path'),
+ tmp = require('tmp');
+
+var io = require('../io');
+
+
+describe('io', function() {
+ describe('copy', function() {
+ var tmpDir;
+
+ before(function() {
+ return io.tmpDir().then(function(d) {
+ tmpDir = d;
+
+ fs.writeFileSync(path.join(d, 'foo'), 'Hello, world');
+ });
+ });
+
+ it('can copy one file to another', function() {
+ return io.tmpFile().then(function(f) {
+ return io.copy(path.join(tmpDir, 'foo'), f).then(function(p) {
+ assert.equal(p, f);
+ assert.equal('Hello, world', fs.readFileSync(p));
+ });
+ });
+ });
+
+ it('can copy symlink to destination', function() {
+ if (process.platform === 'win32') {
+ return; // No symlinks on windows.
+ }
+ fs.symlinkSync(
+ path.join(tmpDir, 'foo'),
+ path.join(tmpDir, 'symlinked-foo'));
+ return io.tmpFile().then(function(f) {
+ return io.copy(path.join(tmpDir, 'symlinked-foo'), f).then(function(p) {
+ assert.equal(p, f);
+ assert.equal('Hello, world', fs.readFileSync(p));
+ });
+ });
+ });
+
+ it('fails if given a directory as a source', function() {
+ return io.tmpFile().then(function(f) {
+ return io.copy(tmpDir, f);
+ }).then(function() {
+ throw Error('Should have failed with a type error');
+ }, function() {
+ // Do nothing; expected.
+ });
+ });
+ });
+
+ describe('copyDir', function() {
+ it('copies recursively', function() {
+ return io.tmpDir().then(function(dir) {
+ fs.writeFileSync(path.join(dir, 'file1'), 'hello');
+ fs.mkdirSync(path.join(dir, 'sub'));
+ fs.mkdirSync(path.join(dir, 'sub/folder'));
+ fs.writeFileSync(path.join(dir, 'sub/folder/file2'), 'goodbye');
+
+ return io.tmpDir().then(function(dst) {
+ return io.copyDir(dir, dst).then(function(ret) {
+ assert.equal(dst, ret);
+
+ assert.equal('hello',
+ fs.readFileSync(path.join(dst, 'file1')));
+ assert.equal('goodbye',
+ fs.readFileSync(path.join(dst, 'sub/folder/file2')));
+ });
+ });
+ });
+ });
+
+ it('creates destination dir if necessary', function() {
+ return io.tmpDir().then(function(srcDir) {
+ fs.writeFileSync(path.join(srcDir, 'foo'), 'hi');
+ return io.tmpDir().then(function(dstDir) {
+ return io.copyDir(srcDir, path.join(dstDir, 'sub'));
+ });
+ }).then(function(p) {
+ assert.equal('sub', path.basename(p));
+ assert.equal('hi', fs.readFileSync(path.join(p, 'foo')));
+ });
+ });
+
+ it('supports regex exclusion filter', function() {
+ return io.tmpDir().then(function(src) {
+ fs.writeFileSync(path.join(src, 'foo'), 'a');
+ fs.writeFileSync(path.join(src, 'bar'), 'b');
+ fs.writeFileSync(path.join(src, 'baz'), 'c');
+ fs.mkdirSync(path.join(src, 'sub'));
+ fs.writeFileSync(path.join(src, 'sub/quux'), 'd');
+ fs.writeFileSync(path.join(src, 'sub/quot'), 'e');
+
+ return io.tmpDir().then(function(dst) {
+ return io.copyDir(src, dst, /(bar|quux)/);
+ });
+ }).then(function(dir) {
+ assert.equal('a', fs.readFileSync(path.join(dir, 'foo')));
+ assert.equal('c', fs.readFileSync(path.join(dir, 'baz')));
+ assert.equal('e', fs.readFileSync(path.join(dir, 'sub/quot')));
+
+ assert.ok(!fs.existsSync(path.join(dir, 'bar')));
+ assert.ok(!fs.existsSync(path.join(dir, 'sub/quux')));
+ });
+ });
+
+ it('supports exclusion filter function', function() {
+ return io.tmpDir().then(function(src) {
+ fs.writeFileSync(path.join(src, 'foo'), 'a');
+ fs.writeFileSync(path.join(src, 'bar'), 'b');
+ fs.writeFileSync(path.join(src, 'baz'), 'c');
+ fs.mkdirSync(path.join(src, 'sub'));
+ fs.writeFileSync(path.join(src, 'sub/quux'), 'd');
+ fs.writeFileSync(path.join(src, 'sub/quot'), 'e');
+
+ return io.tmpDir().then(function(dst) {
+ return io.copyDir(src, dst, function(f) {
+ return f !== path.join(src, 'foo')
+ && f !== path.join(src, 'sub/quot');
+ });
+ });
+ }).then(function(dir) {
+ assert.equal('b', fs.readFileSync(path.join(dir, 'bar')));
+ assert.equal('c', fs.readFileSync(path.join(dir, 'baz')));
+ assert.equal('d', fs.readFileSync(path.join(dir, 'sub/quux')));
+
+ assert.ok(!fs.existsSync(path.join(dir, 'foo')));
+ assert.ok(!fs.existsSync(path.join(dir, 'sub/quot')));
+ });
+ });
+ });
+
+ describe('exists', function() {
+ var dir;
+
+ before(function() {
+ return io.tmpDir().then(function(d) {
+ dir = d;
+ });
+ });
+
+ it('returns a rejected promise if input value is invalid', function() {
+ return io.exists(undefined).then(
+ () => assert.fail('should have failed'),
+ e => assert.ok(e instanceof TypeError));
+ });
+
+ it('works for directories', function() {
+ return io.exists(dir).then(assert.ok);
+ });
+
+ it('works for files', function() {
+ var file = path.join(dir, 'foo');
+ fs.writeFileSync(file, '');
+ return io.exists(file).then(assert.ok);
+ });
+
+ it('does not return a rejected promise if file does not exist', function() {
+ return io.exists(path.join(dir, 'not-there')).then(function(exists) {
+ assert.ok(!exists);
+ });
+ });
+ });
+
+ describe('unlink', function() {
+ var dir;
+
+ before(function() {
+ return io.tmpDir().then(function(d) {
+ dir = d;
+ });
+ });
+
+ it('silently succeeds if the path does not exist', function() {
+ return io.unlink(path.join(dir, 'not-there'));
+ });
+
+ it('deletes files', function() {
+ var file = path.join(dir, 'foo');
+ fs.writeFileSync(file, '');
+ return io.exists(file).then(assert.ok).then(function() {
+ return io.unlink(file);
+ }).then(function() {
+ return io.exists(file);
+ }).then(function(exists) {
+ return assert.ok(!exists);
+ });
+ });
+ });
+
+ describe('rmDir', function() {
+ it('succeeds if the designated directory does not exist', function() {
+ return io.tmpDir().then(function(d) {
+ return io.rmDir(path.join(d, 'i/do/not/exist'));
+ });
+ });
+
+ it('deletes recursively', function() {
+ return io.tmpDir().then(function(dir) {
+ fs.writeFileSync(path.join(dir, 'file1'), 'hello');
+ fs.mkdirSync(path.join(dir, 'sub'));
+ fs.mkdirSync(path.join(dir, 'sub/folder'));
+ fs.writeFileSync(path.join(dir, 'sub/folder/file2'), 'goodbye');
+
+ return io.rmDir(dir).then(function() {
+ assert.ok(!fs.existsSync(dir));
+ assert.ok(!fs.existsSync(path.join(dir, 'sub/folder/file2')));
+ });
+ });
+ });
+ });
+
+ describe('findInPath', function() {
+ const savedPathEnv = process.env['PATH'];
+ afterEach(() => process.env['PATH'] = savedPathEnv);
+
+ const cwd = process.cwd;
+ afterEach(() => process.cwd = cwd);
+
+ let dirs;
+ beforeEach(() => {
+ return Promise.all([io.tmpDir(), io.tmpDir(), io.tmpDir()]).then(arr => {
+ dirs = arr;
+ process.env['PATH'] = arr.join(path.delimiter);
+ });
+ });
+
+ it('returns null if file cannot be found', () => {
+ assert.strictEqual(io.findInPath('foo.txt'), null);
+ });
+
+ it('can find file on path', () => {
+ let filePath = path.join(dirs[1], 'foo.txt');
+ fs.writeFileSync(filePath, 'hi');
+
+ assert.strictEqual(io.findInPath('foo.txt'), filePath);
+ });
+
+ it('returns null if file is in a subdir of a directory on the path', () => {
+ let subDir = path.join(dirs[2], 'sub');
+ fs.mkdirSync(subDir);
+
+ let filePath = path.join(subDir, 'foo.txt');
+ fs.writeFileSync(filePath, 'hi');
+
+ assert.strictEqual(io.findInPath('foo.txt'), null);
+ });
+
+ it('does not match on directories', () => {
+ fs.mkdirSync(path.join(dirs[2], 'sub'));
+ assert.strictEqual(io.findInPath('sub'), null);
+ });
+
+ it('will look in cwd first if requested', () => {
+ return io.tmpDir().then(fakeCwd => {
+ process.cwd = () => fakeCwd;
+
+ let theFile = path.join(fakeCwd, 'foo.txt');
+
+ fs.writeFileSync(path.join(dirs[1], 'foo.txt'), 'hi');
+ fs.writeFileSync(theFile, 'bye');
+
+ assert.strictEqual(io.findInPath('foo.txt', true), theFile);
+ });
+ });
+ });
+
+ describe('read', function() {
+ var tmpDir;
+
+ before(function() {
+ return io.tmpDir().then(function(d) {
+ tmpDir = d;
+
+ fs.writeFileSync(path.join(d, 'foo'), 'Hello, world');
+ });
+ });
+
+ it('can read a file', function() {
+ return io.read(path.join(tmpDir, 'foo')).then(buff => {
+ assert.ok(buff instanceof Buffer);
+ assert.equal('Hello, world', buff.toString());
+ });
+ });
+
+ it('catches errors from invalid input', function() {
+ return io.read(1234)
+ .then(() => assert.fail('should have failed'),
+ (e) => assert.ok(e instanceof TypeError));
+ });
+
+ it('rejects returned promise if file does not exist', function() {
+ return io.read(path.join(tmpDir, 'not-there'))
+ .then(() => assert.fail('should have failed'),
+ (e) => assert.equal('ENOENT', e.code));
+ });
+ });
+});
diff --git a/node_modules/selenium-webdriver/test/lib/by_test.js b/node_modules/selenium-webdriver/test/lib/by_test.js
new file mode 100644
index 000000000..1669ec917
--- /dev/null
+++ b/node_modules/selenium-webdriver/test/lib/by_test.js
@@ -0,0 +1,139 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+var assert = require('assert');
+var by = require('../../lib/by');
+
+describe('by', function() {
+ describe('By', function() {
+ describe('className', function() {
+ it('delegates to By.css', function() {
+ let locator = by.By.className('foo');
+ assert.equal('css selector', locator.using);
+ assert.equal('.foo', locator.value);
+ });
+
+ it('escapes class name', function() {
+ let locator = by.By.className('foo#bar');
+ assert.equal('css selector', locator.using);
+ assert.equal('.foo\\#bar', locator.value);
+ });
+
+ it('translates compound class names', function() {
+ let locator = by.By.className('a b');
+ assert.equal('css selector', locator.using);
+ assert.equal('.a.b', locator.value);
+
+ locator = by.By.className(' x y z-1 "g" ');
+ assert.equal('css selector', locator.using);
+ assert.equal('.x.y.z-1.\\"g\\"', locator.value);
+ });
+ });
+
+ describe('id', function() {
+ it('delegates to By.css', function() {
+ let locator = by.By.id('foo');
+ assert.equal('css selector', locator.using);
+ assert.equal('*[id="foo"]', locator.value);
+ });
+
+ it('escapes the ID', function() {
+ let locator = by.By.id('foo#bar');
+ assert.equal('css selector', locator.using);
+ assert.equal('*[id="foo\\#bar"]', locator.value);
+ });
+ });
+
+ describe('name', function() {
+ it('delegates to By.css', function() {
+ let locator = by.By.name('foo')
+ assert.equal('css selector', locator.using);
+ assert.equal('*[name="foo"]', locator.value);
+ });
+
+ it('escapes the name', function() {
+ let locator = by.By.name('foo"bar"')
+ assert.equal('css selector', locator.using);
+ assert.equal('*[name="foo\\"bar\\""]', locator.value);
+ });
+
+ it('escapes the name when it starts with a number', function() {
+ let locator = by.By.name('123foo"bar"')
+ assert.equal('css selector', locator.using);
+ assert.equal('*[name="\\31 23foo\\"bar\\""]', locator.value);
+ });
+
+ it('escapes the name when it starts with a negative number', function() {
+ let locator = by.By.name('-123foo"bar"')
+ assert.equal('css selector', locator.using);
+ assert.equal('*[name="-\\31 23foo\\"bar\\""]', locator.value);
+ });
+ });
+ });
+
+ describe('checkedLocator', function() {
+ it('accepts class name', function() {
+ let locator = by.checkedLocator({className: 'foo'});
+ assert.equal('css selector', locator.using);
+ assert.equal('.foo', locator.value);
+ });
+
+ it('accepts css', function() {
+ let locator = by.checkedLocator({css: 'a > b'});
+ assert.equal('css selector', locator.using);
+ assert.equal('a > b', locator.value);
+ });
+
+ it('accepts id', function() {
+ let locator = by.checkedLocator({id: 'foobar'});
+ assert.equal('css selector', locator.using);
+ assert.equal('*[id="foobar"]', locator.value);
+ });
+
+ it('accepts linkText', function() {
+ let locator = by.checkedLocator({linkText: 'hello'});
+ assert.equal('link text', locator.using);
+ assert.equal('hello', locator.value);
+ });
+
+ it('accepts name', function() {
+ let locator = by.checkedLocator({name: 'foobar'});
+ assert.equal('css selector', locator.using);
+ assert.equal('*[name="foobar"]', locator.value);
+ });
+
+ it('accepts partialLinkText', function() {
+ let locator = by.checkedLocator({partialLinkText: 'hello'});
+ assert.equal('partial link text', locator.using);
+ assert.equal('hello', locator.value);
+ });
+
+ it('accepts tagName', function() {
+ let locator = by.checkedLocator({tagName: 'div'});
+ assert.equal('css selector', locator.using);
+ assert.equal('div', locator.value);
+ });
+
+ it('accepts xpath', function() {
+ let locator = by.checkedLocator({xpath: '//div[1]'});
+ assert.equal('xpath', locator.using);
+ assert.equal('//div[1]', locator.value);
+ });
+ });
+});
diff --git a/node_modules/selenium-webdriver/test/lib/capabilities_test.js b/node_modules/selenium-webdriver/test/lib/capabilities_test.js
new file mode 100644
index 000000000..0d0c67c9a
--- /dev/null
+++ b/node_modules/selenium-webdriver/test/lib/capabilities_test.js
@@ -0,0 +1,111 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+const Capabilities = require('../../lib/capabilities').Capabilities;
+const Symbols = require('../../lib/symbols');
+
+const assert = require('assert');
+
+describe('Capabilities', function() {
+ it('can set and unset a capability', function() {
+ let caps = new Capabilities();
+ assert.equal(undefined, caps.get('foo'));
+ assert.ok(!caps.has('foo'));
+
+ caps.set('foo', 'bar');
+ assert.equal('bar', caps.get('foo'));
+ assert.ok(caps.has('foo'));
+
+ caps.set('foo', null);
+ assert.equal(null, caps.get('foo'));
+ assert.ok(caps.has('foo'));
+ });
+
+ it('requires string capability keys', function() {
+ let caps = new Capabilities();
+ assert.throws(() => caps.set({}, 'hi'));
+ });
+
+ it('can merge capabilities', function() {
+ let caps1 = new Capabilities()
+ .set('foo', 'bar')
+ .set('color', 'red');
+
+ let caps2 = new Capabilities()
+ .set('color', 'green');
+
+ assert.equal('bar', caps1.get('foo'));
+ assert.equal('red', caps1.get('color'));
+ assert.equal('green', caps2.get('color'));
+ assert.equal(undefined, caps2.get('foo'));
+
+ caps2.merge(caps1);
+ assert.equal('bar', caps1.get('foo'));
+ assert.equal('red', caps1.get('color'));
+ assert.equal('red', caps2.get('color'));
+ assert.equal('bar', caps2.get('foo'));
+ });
+
+ it('can be initialized from a hash object', function() {
+ let caps = new Capabilities({'one': 123, 'abc': 'def'});
+ assert.equal(123, caps.get('one'));
+ assert.equal('def', caps.get('abc'));
+ });
+
+ it('can be initialized from a map', function() {
+ let m = new Map([['one', 123], ['abc', 'def']]);
+
+ let caps = new Capabilities(m);
+ assert.equal(123, caps.get('one'));
+ assert.equal('def', caps.get('abc'));
+ });
+
+ describe('serialize', function() {
+ it('works for simple capabilities', function() {
+ let m = new Map([['one', 123], ['abc', 'def']]);
+ let caps = new Capabilities(m);
+ assert.deepEqual({one: 123, abc: 'def'}, caps[Symbols.serialize]());
+ });
+
+ it('does not omit capabilities set to a false-like value', function() {
+ let caps = new Capabilities;
+ caps.set('bool', false);
+ caps.set('number', 0);
+ caps.set('string', '');
+
+ assert.deepEqual(
+ {bool: false, number: 0, string: ''},
+ caps[Symbols.serialize]());
+ });
+
+ it('omits capabilities with a null value', function() {
+ let caps = new Capabilities;
+ caps.set('foo', null);
+ caps.set('bar', 123);
+ assert.deepEqual({bar: 123}, caps[Symbols.serialize]());
+ });
+
+ it('omits capabilities with an undefined value', function() {
+ let caps = new Capabilities;
+ caps.set('foo', undefined);
+ caps.set('bar', 123);
+ assert.deepEqual({bar: 123}, caps[Symbols.serialize]());
+ });
+ });
+});
diff --git a/node_modules/selenium-webdriver/test/lib/error_test.js b/node_modules/selenium-webdriver/test/lib/error_test.js
new file mode 100644
index 000000000..a9fc87193
--- /dev/null
+++ b/node_modules/selenium-webdriver/test/lib/error_test.js
@@ -0,0 +1,286 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+describe('error', function() {
+ let assert = require('assert');
+ let error = require('../../lib/error');
+
+ describe('checkResponse', function() {
+ it('defaults to WebDriverError if type is unrecognized', function() {
+ assert.throws(
+ () => error.checkResponse({error: 'foo', message: 'hi there'}),
+ (e) => {
+ assert.equal(e.constructor, error.WebDriverError);
+ return true;
+ });
+ });
+
+ it('does not throw if error property is not a string', function() {
+ let resp = {error: 123, message: 'abc123'};
+ let out = error.checkResponse(resp);
+ assert.strictEqual(out, resp);
+ });
+
+ test('unknown error', error.WebDriverError);
+ test('element not selectable', error.ElementNotSelectableError);
+ test('element not visible', error.ElementNotVisibleError);
+ test('invalid argument', error.InvalidArgumentError);
+ test('invalid cookie domain', error.InvalidCookieDomainError);
+ test('invalid element coordinates', error.InvalidElementCoordinatesError);
+ test('invalid element state', error.InvalidElementStateError);
+ test('invalid selector', error.InvalidSelectorError);
+ test('invalid session id', error.NoSuchSessionError);
+ test('javascript error', error.JavascriptError);
+ test('move target out of bounds', error.MoveTargetOutOfBoundsError);
+ test('no such alert', error.NoSuchAlertError);
+ test('no such element', error.NoSuchElementError);
+ test('no such frame', error.NoSuchFrameError);
+ test('no such window', error.NoSuchWindowError);
+ test('script timeout', error.ScriptTimeoutError);
+ test('session not created', error.SessionNotCreatedError);
+ test('stale element reference', error.StaleElementReferenceError);
+ test('timeout', error.TimeoutError);
+ test('unable to set cookie', error.UnableToSetCookieError);
+ test('unable to capture screen', error.UnableToCaptureScreenError);
+ test('unexpected alert open', error.UnexpectedAlertOpenError);
+ test('unknown command', error.UnknownCommandError);
+ test('unknown method', error.UnknownMethodError);
+ test('unsupported operation', error.UnsupportedOperationError);
+
+ function test(status, expectedType) {
+ it(`"${status}" => ${expectedType.name}`, function() {
+ assert.throws(
+ () => error.checkResponse({error: status, message: 'oops'}),
+ (e) => {
+ assert.equal(expectedType, e.constructor);
+ assert.equal(e.message, 'oops');
+ return true;
+ });
+ });
+ }
+ });
+
+ describe('encodeError', function() {
+ describe('defaults to an unknown error', function() {
+ it('for a generic error value', function() {
+ runTest('hi', 'unknown error', 'hi');
+ runTest(1, 'unknown error', '1');
+ runTest({}, 'unknown error', '[object Object]');
+ });
+
+ it('for a generic Error object', function() {
+ runTest(Error('oops'), 'unknown error', 'oops');
+ runTest(TypeError('bad value'), 'unknown error', 'bad value');
+ });
+ });
+
+ test(error.WebDriverError, 'unknown error');
+ test(error.ElementNotSelectableError, 'element not selectable');
+ test(error.ElementNotVisibleError, 'element not visible');
+ test(error.InvalidArgumentError, 'invalid argument');
+ test(error.InvalidCookieDomainError, 'invalid cookie domain');
+ test(error.InvalidElementStateError, 'invalid element state');
+ test(error.InvalidSelectorError, 'invalid selector');
+ test(error.NoSuchSessionError, 'invalid session id');
+ test(error.JavascriptError, 'javascript error');
+ test(error.MoveTargetOutOfBoundsError, 'move target out of bounds');
+ test(error.NoSuchAlertError, 'no such alert');
+ test(error.NoSuchElementError, 'no such element');
+ test(error.NoSuchFrameError, 'no such frame');
+ test(error.NoSuchWindowError, 'no such window');
+ test(error.ScriptTimeoutError, 'script timeout');
+ test(error.SessionNotCreatedError, 'session not created');
+ test(error.StaleElementReferenceError, 'stale element reference');
+ test(error.TimeoutError, 'timeout');
+ test(error.UnableToSetCookieError, 'unable to set cookie');
+ test(error.UnableToCaptureScreenError, 'unable to capture screen');
+ test(error.UnexpectedAlertOpenError, 'unexpected alert open');
+ test(error.UnknownCommandError, 'unknown command');
+ test(error.UnknownMethodError, 'unknown method');
+ test(error.UnsupportedOperationError, 'unsupported operation');
+
+ function test(ctor, code) {
+ it(`${ctor.name} => "${code}"`, () => {
+ runTest(new ctor('oops'), code, 'oops');
+ });
+ }
+
+ function runTest(err, code, message) {
+ let obj = error.encodeError(err);
+ assert.strictEqual(obj['error'], code);
+ assert.strictEqual(obj['message'], message);
+ }
+ });
+
+ describe('throwDecodedError', function() {
+ it('defaults to WebDriverError if type is unrecognized', function() {
+ assert.throws(
+ () => error.throwDecodedError({error: 'foo', message: 'hi there'}),
+ (e) => {
+ assert.equal(e.constructor, error.WebDriverError);
+ return true;
+ });
+ });
+
+ it('throws generic error if encoded data is not valid', function() {
+ assert.throws(
+ () => error.throwDecodedError({error: 123, message: 'abc123'}),
+ (e) => {
+ assert.strictEqual(e.constructor, error.WebDriverError);
+ return true;
+ });
+
+ assert.throws(
+ () => error.throwDecodedError('null'),
+ (e) => {
+ assert.strictEqual(e.constructor, error.WebDriverError);
+ return true;
+ });
+
+ assert.throws(
+ () => error.throwDecodedError(''),
+ (e) => {
+ assert.strictEqual(e.constructor, error.WebDriverError);
+ return true;
+ });
+ });
+
+ test('unknown error', error.WebDriverError);
+ test('element not selectable', error.ElementNotSelectableError);
+ test('element not visible', error.ElementNotVisibleError);
+ test('invalid argument', error.InvalidArgumentError);
+ test('invalid cookie domain', error.InvalidCookieDomainError);
+ test('invalid element coordinates', error.InvalidElementCoordinatesError);
+ test('invalid element state', error.InvalidElementStateError);
+ test('invalid selector', error.InvalidSelectorError);
+ test('invalid session id', error.NoSuchSessionError);
+ test('javascript error', error.JavascriptError);
+ test('move target out of bounds', error.MoveTargetOutOfBoundsError);
+ test('no such alert', error.NoSuchAlertError);
+ test('no such element', error.NoSuchElementError);
+ test('no such frame', error.NoSuchFrameError);
+ test('no such window', error.NoSuchWindowError);
+ test('script timeout', error.ScriptTimeoutError);
+ test('session not created', error.SessionNotCreatedError);
+ test('stale element reference', error.StaleElementReferenceError);
+ test('timeout', error.TimeoutError);
+ test('unable to set cookie', error.UnableToSetCookieError);
+ test('unable to capture screen', error.UnableToCaptureScreenError);
+ test('unexpected alert open', error.UnexpectedAlertOpenError);
+ test('unknown command', error.UnknownCommandError);
+ test('unknown method', error.UnknownMethodError);
+ test('unsupported operation', error.UnsupportedOperationError);
+
+ function test(status, expectedType) {
+ it(`"${status}" => ${expectedType.name}`, function() {
+ assert.throws(
+ () => error.throwDecodedError({error: status, message: 'oops'}),
+ (e) => {
+ assert.strictEqual(e.constructor, expectedType);
+ assert.strictEqual(e.message, 'oops');
+ return true;
+ });
+ });
+ }
+ });
+
+ describe('checkLegacyResponse', function() {
+ it('does not throw for success', function() {
+ let resp = {status: error.ErrorCode.SUCCESS};
+ assert.strictEqual(resp, error.checkLegacyResponse(resp));
+ });
+
+ test('NO_SUCH_ELEMENT', error.NoSuchElementError);
+ test('NO_SUCH_FRAME', error.NoSuchFrameError);
+ test('UNKNOWN_COMMAND', error.UnsupportedOperationError);
+ test('UNSUPPORTED_OPERATION', error.UnsupportedOperationError);
+ test('STALE_ELEMENT_REFERENCE', error.StaleElementReferenceError);
+ test('ELEMENT_NOT_VISIBLE', error.ElementNotVisibleError);
+ test('INVALID_ELEMENT_STATE', error.InvalidElementStateError);
+ test('UNKNOWN_ERROR', error.WebDriverError);
+ test('ELEMENT_NOT_SELECTABLE', error.ElementNotSelectableError);
+ test('JAVASCRIPT_ERROR', error.JavascriptError);
+ test('XPATH_LOOKUP_ERROR', error.InvalidSelectorError);
+ test('TIMEOUT', error.TimeoutError);
+ test('NO_SUCH_WINDOW', error.NoSuchWindowError);
+ test('INVALID_COOKIE_DOMAIN', error.InvalidCookieDomainError);
+ test('UNABLE_TO_SET_COOKIE', error.UnableToSetCookieError);
+ test('UNEXPECTED_ALERT_OPEN', error.UnexpectedAlertOpenError);
+ test('NO_SUCH_ALERT', error.NoSuchAlertError);
+ test('SCRIPT_TIMEOUT', error.ScriptTimeoutError);
+ test('INVALID_ELEMENT_COORDINATES', error.InvalidElementCoordinatesError);
+ test('INVALID_SELECTOR_ERROR', error.InvalidSelectorError);
+ test('SESSION_NOT_CREATED', error.SessionNotCreatedError);
+ test('MOVE_TARGET_OUT_OF_BOUNDS', error.MoveTargetOutOfBoundsError);
+ test('INVALID_XPATH_SELECTOR', error.InvalidSelectorError);
+ test('INVALID_XPATH_SELECTOR_RETURN_TYPE', error.InvalidSelectorError);
+ test('METHOD_NOT_ALLOWED', error.UnsupportedOperationError);
+
+ describe('UnexpectedAlertOpenError', function() {
+ it('includes alert text from the response object', function() {
+ let response = {
+ status: error.ErrorCode.UNEXPECTED_ALERT_OPEN,
+ value: {
+ message: 'hi',
+ alert: {text: 'alert text here'}
+ }
+ };
+ assert.throws(
+ () => error.checkLegacyResponse(response),
+ (e) => {
+ assert.equal(error.UnexpectedAlertOpenError, e.constructor);
+ assert.equal(e.message, 'hi');
+ assert.equal(e.getAlertText(), 'alert text here');
+ return true;
+ });
+ });
+
+ it('uses an empty string if alert text omitted', function() {
+ let response = {
+ status: error.ErrorCode.UNEXPECTED_ALERT_OPEN,
+ value: {
+ message: 'hi'
+ }
+ };
+ assert.throws(
+ () => error.checkLegacyResponse(response),
+ (e) => {
+ assert.equal(error.UnexpectedAlertOpenError, e.constructor);
+ assert.equal(e.message, 'hi');
+ assert.equal(e.getAlertText(), '');
+ return true;
+ });
+ });
+ });
+
+ function test(codeKey, expectedType) {
+ it(`${codeKey} => ${expectedType.name}`, function() {
+ let code = error.ErrorCode[codeKey];
+ let resp = {status: code, value: {message: 'hi'}};
+ assert.throws(
+ () => error.checkLegacyResponse(resp),
+ (e) => {
+ assert.equal(expectedType, e.constructor);
+ assert.equal(e.message, 'hi');
+ return true;
+ });
+ });
+ }
+ });
+});
diff --git a/node_modules/selenium-webdriver/test/lib/events_test.js b/node_modules/selenium-webdriver/test/lib/events_test.js
new file mode 100644
index 000000000..a02da9747
--- /dev/null
+++ b/node_modules/selenium-webdriver/test/lib/events_test.js
@@ -0,0 +1,177 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+const EventEmitter = require('../../lib/events').EventEmitter;
+
+const assert = require('assert');
+const sinon = require('sinon');
+
+describe('EventEmitter', function() {
+ describe('#emit()', function() {
+ it('can emit events when nothing is registered', function() {
+ let emitter = new EventEmitter;
+ emitter.emit('foo');
+ // Ok if no errors are thrown.
+ });
+
+ it('can pass args to listeners on emit', function() {
+ let emitter = new EventEmitter;
+ let now = Date.now();
+
+ let messages = [];
+ emitter.on('foo', (arg) => messages.push(arg));
+
+ emitter.emit('foo', now);
+ assert.deepEqual([now], messages);
+
+ emitter.emit('foo', now + 15);
+ assert.deepEqual([now, now + 15], messages);
+ });
+ });
+
+ describe('#addListener()', function() {
+ it('can add multiple listeners for one event', function() {
+ let emitter = new EventEmitter;
+ let count = 0;
+ emitter.addListener('foo', () => count++);
+ emitter.addListener('foo', () => count++);
+ emitter.addListener('foo', () => count++);
+ emitter.emit('foo');
+ assert.equal(3, count);
+ });
+
+ it('only registers each listener function once', function() {
+ let emitter = new EventEmitter;
+ let count = 0;
+ let onFoo = () => count++;
+ emitter.addListener('foo', onFoo);
+ emitter.addListener('foo', onFoo);
+ emitter.addListener('foo', onFoo);
+
+ emitter.emit('foo');
+ assert.equal(1, count);
+
+ emitter.emit('foo');
+ assert.equal(2, count);
+ });
+
+ it('allows users to specify a custom scope', function() {
+ let obj = {
+ count: 0,
+ inc: function() {
+ this.count++;
+ }
+ };
+ let emitter = new EventEmitter;
+ emitter.addListener('foo', obj.inc, obj);
+
+ emitter.emit('foo');
+ assert.equal(1, obj.count);
+
+ emitter.emit('foo');
+ assert.equal(2, obj.count);
+ });
+ });
+
+ describe('#once()', function() {
+ it('only calls registered callback once', function() {
+ let emitter = new EventEmitter;
+ let count = 0;
+ emitter.once('foo', () => count++);
+ emitter.once('foo', () => count++);
+ emitter.once('foo', () => count++);
+
+ emitter.emit('foo');
+ assert.equal(3, count);
+
+ emitter.emit('foo');
+ assert.equal(3, count);
+
+ emitter.emit('foo');
+ assert.equal(3, count);
+ });
+ });
+
+ describe('#removeListeners()', function() {
+ it('only removes the given listener function', function() {
+ let emitter = new EventEmitter;
+ let count = 0;
+ emitter.addListener('foo', () => count++);
+ emitter.addListener('foo', () => count++);
+
+ let toRemove = () => count++;
+ emitter.addListener('foo', toRemove);
+
+ emitter.emit('foo');
+ assert.equal(3, count);
+
+ emitter.removeListener('foo', toRemove);
+ emitter.emit('foo');
+ assert.equal(5, count);
+ });
+ });
+
+ describe('#removeAllListeners()', function() {
+ it('only removes listeners for type if specified', function() {
+ let emitter = new EventEmitter;
+ let count = 0;
+ emitter.addListener('foo', () => count++);
+ emitter.addListener('foo', () => count++);
+ emitter.addListener('foo', () => count++);
+ emitter.addListener('bar', () => count++);
+
+ emitter.emit('foo');
+ assert.equal(3, count);
+
+ emitter.removeAllListeners('foo');
+
+ emitter.emit('foo');
+ assert.equal(3, count);
+
+ emitter.emit('bar');
+ assert.equal(4, count);
+ });
+
+ it('removes absolutely all listeners if no type specified', function() {
+ let emitter = new EventEmitter;
+ let count = 0;
+ emitter.addListener('foo', () => count++);
+ emitter.addListener('bar', () => count++);
+ emitter.addListener('baz', () => count++);
+ emitter.addListener('baz', () => count++);
+
+ emitter.emit('foo');
+ assert.equal(1, count);
+
+ emitter.emit('baz');
+ assert.equal(3, count);
+
+ emitter.removeAllListeners();
+
+ emitter.emit('foo');
+ assert.equal(3, count);
+
+ emitter.emit('bar');
+ assert.equal(3, count);
+
+ emitter.emit('baz');
+ assert.equal(3, count);
+ });
+ });
+});
diff --git a/node_modules/selenium-webdriver/test/lib/http_test.js b/node_modules/selenium-webdriver/test/lib/http_test.js
new file mode 100644
index 000000000..343a04800
--- /dev/null
+++ b/node_modules/selenium-webdriver/test/lib/http_test.js
@@ -0,0 +1,655 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+var assert = require('assert'),
+ sinon = require('sinon');
+
+var Capabilities = require('../../lib/capabilities').Capabilities,
+ Command = require('../../lib/command').Command,
+ CommandName = require('../../lib/command').Name,
+ error = require('../../lib/error'),
+ http = require('../../lib/http'),
+ Session = require('../../lib/session').Session,
+ promise = require('../../lib/promise'),
+ WebElement = require('../../lib/webdriver').WebElement;
+
+describe('http', function() {
+ describe('buildPath', function() {
+ it('properly replaces path segments with command parameters', function() {
+ var parameters = {'sessionId':'foo', 'url':'http://www.google.com'};
+ var finalPath = http.buildPath('/session/:sessionId/url', parameters);
+ assert.equal(finalPath, '/session/foo/url');
+ assert.deepEqual(parameters, {'url':'http://www.google.com'});
+ });
+
+ it('handles web element references', function() {
+ var parameters = {'sessionId':'foo', 'id': WebElement.buildId('bar')};
+
+ var finalPath = http.buildPath(
+ '/session/:sessionId/element/:id/click', parameters);
+ assert.equal(finalPath, '/session/foo/element/bar/click');
+ assert.deepEqual(parameters, {});
+ });
+
+ it('throws if missing a parameter', function() {
+ assert.throws(
+ () => http.buildPath('/session/:sessionId', {}),
+ function(err) {
+ return err instanceof error.InvalidArgumentError
+ && 'Missing required parameter: sessionId' === err.message;
+ });
+
+ assert.throws(
+ () => http.buildPath(
+ '/session/:sessionId/element/:id', {'sessionId': 'foo'}),
+ function(err) {
+ return err instanceof error.InvalidArgumentError
+ && 'Missing required parameter: id' === err.message;
+ });
+ });
+
+ it('does not match on segments that do not start with a colon', function() {
+ assert.equal(
+ http.buildPath('/session/foo:bar/baz', {}),
+ '/session/foo:bar/baz');
+ });
+ });
+
+ describe('Executor', function() {
+ let executor;
+ let client;
+ let send;
+
+ beforeEach(function setUp() {
+ client = new http.Client;
+ send = sinon.stub(client, 'send');
+ executor = new http.Executor(client);
+ });
+
+ describe('command routing', function() {
+ it('rejects unrecognized commands', function() {
+ assert.throws(
+ () => executor.execute(new Command('fake-name')),
+ function (err) {
+ return err instanceof error.UnknownCommandError
+ && 'Unrecognized command: fake-name' === err.message;
+ });
+ });
+
+ it('rejects promise if client fails to send request', function() {
+ let error = new Error('boom');
+ send.returns(Promise.reject(error));
+ return assertFailsToSend(new Command(CommandName.NEW_SESSION))
+ .then(function(e) {
+ assert.strictEqual(error, e);
+ assertSent(
+ 'POST', '/session', {},
+ [['Accept', 'application/json; charset=utf-8']]);
+ });
+ });
+
+ it('can execute commands with no URL parameters', function() {
+ var resp = JSON.stringify({sessionId: 'abc123'});
+ send.returns(Promise.resolve(new http.Response(200, {}, resp)));
+
+ let command = new Command(CommandName.NEW_SESSION);
+ return assertSendsSuccessfully(command).then(function(response) {
+ assertSent(
+ 'POST', '/session', {},
+ [['Accept', 'application/json; charset=utf-8']]);
+ });
+ });
+
+ it('rejects commands missing URL parameters', function() {
+ let command =
+ new Command(CommandName.FIND_CHILD_ELEMENT).
+ setParameter('sessionId', 's123').
+ // Let this be missing: setParameter('id', {'ELEMENT': 'e456'}).
+ setParameter('using', 'id').
+ setParameter('value', 'foo');
+
+ assert.throws(
+ () => executor.execute(command),
+ function(err) {
+ return err instanceof error.InvalidArgumentError
+ && 'Missing required parameter: id' === err.message;
+ });
+ assert.ok(!send.called);
+ });
+
+ it('replaces URL parameters with command parameters', function() {
+ var command = new Command(CommandName.GET).
+ setParameter('sessionId', 's123').
+ setParameter('url', 'http://www.google.com');
+
+ send.returns(Promise.resolve(new http.Response(200, {}, '')));
+
+ return assertSendsSuccessfully(command).then(function(response) {
+ assertSent(
+ 'POST', '/session/s123/url', {'url': 'http://www.google.com'},
+ [['Accept', 'application/json; charset=utf-8']]);
+ });
+ });
+
+ describe('uses correct URL', function() {
+ beforeEach(() => executor = new http.Executor(client));
+
+ describe('in legacy mode', function() {
+ test(CommandName.GET_WINDOW_SIZE, {sessionId:'s123'}, false,
+ 'GET', '/session/s123/window/current/size');
+
+ test(CommandName.SET_WINDOW_SIZE,
+ {sessionId:'s123', width: 1, height: 1}, false,
+ 'POST', '/session/s123/window/current/size',
+ {width: 1, height: 1});
+
+ test(CommandName.MAXIMIZE_WINDOW, {sessionId:'s123'}, false,
+ 'POST', '/session/s123/window/current/maximize');
+
+ // This is consistent b/w legacy and W3C, just making sure.
+ test(CommandName.GET,
+ {sessionId:'s123', url: 'http://www.example.com'}, false,
+ 'POST', '/session/s123/url', {url: 'http://www.example.com'});
+ });
+
+ describe('in W3C mode', function() {
+ test(CommandName.GET_WINDOW_SIZE,
+ {sessionId:'s123'}, true,
+ 'GET', '/session/s123/window/size');
+
+ test(CommandName.SET_WINDOW_SIZE,
+ {sessionId:'s123', width: 1, height: 1}, true,
+ 'POST', '/session/s123/window/size', {width: 1, height: 1});
+
+ test(CommandName.MAXIMIZE_WINDOW, {sessionId:'s123'}, true,
+ 'POST', '/session/s123/window/maximize');
+
+ // This is consistent b/w legacy and W3C, just making sure.
+ test(CommandName.GET,
+ {sessionId:'s123', url: 'http://www.example.com'}, true,
+ 'POST', '/session/s123/url', {url: 'http://www.example.com'});
+ });
+
+ function test(command, parameters, w3c,
+ expectedMethod, expectedUrl, opt_expectedParams) {
+ it(`command=${command}`, function() {
+ var resp = JSON.stringify({sessionId: 'abc123'});
+ send.returns(Promise.resolve(new http.Response(200, {}, resp)));
+
+ let cmd = new Command(command).setParameters(parameters);
+ executor.w3c = w3c;
+ return executor.execute(cmd).then(function() {
+ assertSent(
+ expectedMethod, expectedUrl, opt_expectedParams || {},
+ [['Accept', 'application/json; charset=utf-8']]);
+ });
+ });
+ }
+ });
+ });
+
+ describe('response parsing', function() {
+ it('extracts value from JSON response', function() {
+ var responseObj = {
+ 'status': error.ErrorCode.SUCCESS,
+ 'value': 'http://www.google.com'
+ };
+
+ var command = new Command(CommandName.GET_CURRENT_URL)
+ .setParameter('sessionId', 's123');
+
+ send.returns(Promise.resolve(
+ new http.Response(200, {}, JSON.stringify(responseObj))));
+
+ return executor.execute(command).then(function(response) {
+ assertSent('GET', '/session/s123/url', {},
+ [['Accept', 'application/json; charset=utf-8']]);
+ assert.strictEqual(response, 'http://www.google.com');
+ });
+ });
+
+ describe('extracts Session from NEW_SESSION response', function() {
+ beforeEach(() => executor = new http.Executor(client));
+
+ const command = new Command(CommandName.NEW_SESSION);
+
+ describe('fails if server returns invalid response', function() {
+ describe('(empty response)', function() {
+ test(true);
+ test(false);
+
+ function test(w3c) {
+ it('w3c === ' + w3c, function() {
+ send.returns(Promise.resolve(new http.Response(200, {}, '')));
+ executor.w3c = w3c;
+ return executor.execute(command).then(
+ () => assert.fail('expected to fail'),
+ (e) => {
+ if (!e.message.startsWith('Unable to parse')) {
+ throw e;
+ }
+ });
+ });
+ }
+ });
+
+ describe('(no session ID)', function() {
+ test(true);
+ test(false);
+
+ function test(w3c) {
+ it('w3c === ' + w3c, function() {
+ let resp = {value:{name: 'Bob'}};
+ send.returns(Promise.resolve(
+ new http.Response(200, {}, JSON.stringify(resp))));
+ executor.w3c = w3c;
+ return executor.execute(command).then(
+ () => assert.fail('expected to fail'),
+ (e) => {
+ if (!e.message.startsWith('Unable to parse')) {
+ throw e;
+ }
+ });
+ });
+ }
+ });
+ });
+
+ it('handles legacy response', function() {
+ var rawResponse = {sessionId: 's123', status: 0, value: {name: 'Bob'}};
+
+ send.returns(Promise.resolve(
+ new http.Response(200, {}, JSON.stringify(rawResponse))));
+
+ assert.ok(!executor.w3c);
+ return executor.execute(command).then(function(response) {
+ assert.ok(response instanceof Session);
+ assert.equal(response.getId(), 's123');
+
+ let caps = response.getCapabilities();
+ assert.ok(caps instanceof Capabilities);
+ assert.equal(caps.get('name'), 'Bob');
+
+ assert.ok(!executor.w3c);
+ });
+ });
+
+ it('auto-upgrades on W3C response', function() {
+ var rawResponse = {sessionId: 's123', value: {name: 'Bob'}};
+
+ send.returns(Promise.resolve(
+ new http.Response(200, {}, JSON.stringify(rawResponse))));
+
+ assert.ok(!executor.w3c);
+ return executor.execute(command).then(function(response) {
+ assert.ok(response instanceof Session);
+ assert.equal(response.getId(), 's123');
+
+ let caps = response.getCapabilities();
+ assert.ok(caps instanceof Capabilities);
+ assert.equal(caps.get('name'), 'Bob');
+
+ assert.ok(executor.w3c);
+ });
+ });
+
+ it('if w3c, does not downgrade on legacy response', function() {
+ var rawResponse = {sessionId: 's123', status: 0, value: null};
+
+ send.returns(Promise.resolve(
+ new http.Response(200, {}, JSON.stringify(rawResponse))));
+
+ executor.w3c = true;
+ return executor.execute(command).then(function(response) {
+ assert.ok(response instanceof Session);
+ assert.equal(response.getId(), 's123');
+ assert.equal(response.getCapabilities().size, 0);
+ assert.ok(executor.w3c, 'should never downgrade');
+ });
+ });
+ });
+
+ describe('extracts Session from DESCRIBE_SESSION response', function() {
+ let command;
+
+ beforeEach(function() {
+ executor = new http.Executor(client);
+ command = new Command(CommandName.DESCRIBE_SESSION)
+ .setParameter('sessionId', 'foo');
+ });
+
+ describe('fails if server returns invalid response', function() {
+ describe('(empty response)', function() {
+ test(true);
+ test(false);
+
+ function test(w3c) {
+ it('w3c === ' + w3c, function() {
+ send.returns(Promise.resolve(new http.Response(200, {}, '')));
+ executor.w3c = w3c;
+ return executor.execute(command).then(
+ () => assert.fail('expected to fail'),
+ (e) => {
+ if (!e.message.startsWith('Unable to parse')) {
+ throw e;
+ }
+ });
+ });
+ }
+ });
+
+ describe('(no session ID)', function() {
+ test(true);
+ test(false);
+
+ function test(w3c) {
+ it('w3c === ' + w3c, function() {
+ let resp = {value:{name: 'Bob'}};
+ send.returns(Promise.resolve(
+ new http.Response(200, {}, JSON.stringify(resp))));
+ executor.w3c = w3c;
+ return executor.execute(command).then(
+ () => assert.fail('expected to fail'),
+ (e) => {
+ if (!e.message.startsWith('Unable to parse')) {
+ throw e;
+ }
+ });
+ });
+ }
+ });
+ });
+
+ it('handles legacy response', function() {
+ var rawResponse = {sessionId: 's123', status: 0, value: {name: 'Bob'}};
+
+ send.returns(Promise.resolve(
+ new http.Response(200, {}, JSON.stringify(rawResponse))));
+
+ assert.ok(!executor.w3c);
+ return executor.execute(command).then(function(response) {
+ assert.ok(response instanceof Session);
+ assert.equal(response.getId(), 's123');
+
+ let caps = response.getCapabilities();
+ assert.ok(caps instanceof Capabilities);
+ assert.equal(caps.get('name'), 'Bob');
+
+ assert.ok(!executor.w3c);
+ });
+ });
+
+ it('does not auto-upgrade on W3C response', function() {
+ var rawResponse = {sessionId: 's123', value: {name: 'Bob'}};
+
+ send.returns(Promise.resolve(
+ new http.Response(200, {}, JSON.stringify(rawResponse))));
+
+ assert.ok(!executor.w3c);
+ return executor.execute(command).then(function(response) {
+ assert.ok(response instanceof Session);
+ assert.equal(response.getId(), 's123');
+
+ let caps = response.getCapabilities();
+ assert.ok(caps instanceof Capabilities);
+ assert.equal(caps.get('name'), 'Bob');
+
+ assert.ok(!executor.w3c);
+ });
+ });
+
+ it('if w3c, does not downgrade on legacy response', function() {
+ var rawResponse = {sessionId: 's123', status: 0, value: null};
+
+ send.returns(Promise.resolve(
+ new http.Response(200, {}, JSON.stringify(rawResponse))));
+
+ executor.w3c = true;
+ return executor.execute(command).then(function(response) {
+ assert.ok(response instanceof Session);
+ assert.equal(response.getId(), 's123');
+ assert.equal(response.getCapabilities().size, 0);
+ assert.ok(executor.w3c, 'should never downgrade');
+ });
+ });
+ });
+
+ it('handles JSON null', function() {
+ var command = new Command(CommandName.GET_CURRENT_URL)
+ .setParameter('sessionId', 's123');
+
+ send.returns(Promise.resolve(new http.Response(200, {}, 'null')));
+
+ return executor.execute(command).then(function(response) {
+ assertSent('GET', '/session/s123/url', {},
+ [['Accept', 'application/json; charset=utf-8']]);
+ assert.strictEqual(response, null);
+ });
+ });
+
+ describe('falsy values', function() {
+ test(0);
+ test(false);
+ test('');
+
+ function test(value) {
+ it(`value=${value}`, function() {
+ var command = new Command(CommandName.GET_CURRENT_URL)
+ .setParameter('sessionId', 's123');
+
+ send.returns(Promise.resolve(
+ new http.Response(200, {},
+ JSON.stringify({status: 0, value: value}))));
+
+ return executor.execute(command).then(function(response) {
+ assertSent('GET', '/session/s123/url', {},
+ [['Accept', 'application/json; charset=utf-8']]);
+ assert.strictEqual(response, value);
+ });
+ });
+ }
+ });
+
+ it('handles non-object JSON', function() {
+ var command = new Command(CommandName.GET_CURRENT_URL)
+ .setParameter('sessionId', 's123');
+
+ send.returns(Promise.resolve(new http.Response(200, {}, '123')));
+
+ return executor.execute(command).then(function(response) {
+ assertSent('GET', '/session/s123/url', {},
+ [['Accept', 'application/json; charset=utf-8']]);
+ assert.strictEqual(response, 123);
+ });
+ });
+
+ it('returns body text when 2xx but not JSON', function() {
+ var command = new Command(CommandName.GET_CURRENT_URL)
+ .setParameter('sessionId', 's123');
+
+ send.returns(Promise.resolve(
+ new http.Response(200, {}, 'hello, world\r\ngoodbye, world!')));
+
+ return executor.execute(command).then(function(response) {
+ assertSent('GET', '/session/s123/url', {},
+ [['Accept', 'application/json; charset=utf-8']]);
+ assert.strictEqual(response, 'hello, world\ngoodbye, world!');
+ });
+ });
+
+ it('returns body text when 2xx but invalid JSON', function() {
+ var command = new Command(CommandName.GET_CURRENT_URL)
+ .setParameter('sessionId', 's123');
+
+ send.returns(Promise.resolve(
+ new http.Response(200, {}, '[')));
+
+ return executor.execute(command).then(function(response) {
+ assertSent('GET', '/session/s123/url', {},
+ [['Accept', 'application/json; charset=utf-8']]);
+ assert.strictEqual(response, '[');
+ });
+ });
+
+ it('returns null if no body text and 2xx', function() {
+ var command = new Command(CommandName.GET_CURRENT_URL)
+ .setParameter('sessionId', 's123');
+
+ send.returns(Promise.resolve(new http.Response(200, {}, '')));
+
+ return executor.execute(command).then(function(response) {
+ assertSent('GET', '/session/s123/url', {},
+ [['Accept', 'application/json; charset=utf-8']]);
+ assert.strictEqual(response, null);
+ });
+ });
+
+ it('returns normalized body text when 2xx but not JSON', function() {
+ var command = new Command(CommandName.GET_CURRENT_URL)
+ .setParameter('sessionId', 's123');
+
+ send.returns(Promise.resolve(new http.Response(200, {}, '\r\n\n\n\r\n')));
+
+ return executor.execute(command).then(function(response) {
+ assertSent('GET', '/session/s123/url', {},
+ [['Accept', 'application/json; charset=utf-8']]);
+ assert.strictEqual(response, '\n\n\n\n');
+ });
+ });
+
+ it('throws UnsupportedOperationError for 404 and body not JSON',
+ function() {
+ var command = new Command(CommandName.GET_CURRENT_URL)
+ .setParameter('sessionId', 's123');
+
+ send.returns(Promise.resolve(
+ new http.Response(404, {}, 'hello, world\r\ngoodbye, world!')));
+
+ return executor.execute(command)
+ .then(
+ () => assert.fail('should have failed'),
+ checkError(
+ error.UnsupportedOperationError,
+ 'hello, world\ngoodbye, world!'));
+ });
+
+ it('throws WebDriverError for generic 4xx when body not JSON',
+ function() {
+ var command = new Command(CommandName.GET_CURRENT_URL)
+ .setParameter('sessionId', 's123');
+
+ send.returns(Promise.resolve(
+ new http.Response(500, {}, 'hello, world\r\ngoodbye, world!')));
+
+ return executor.execute(command)
+ .then(
+ () => assert.fail('should have failed'),
+ checkError(
+ error.WebDriverError,
+ 'hello, world\ngoodbye, world!'))
+ .then(function() {
+ assertSent('GET', '/session/s123/url', {},
+ [['Accept', 'application/json; charset=utf-8']]);
+ });
+ });
+ });
+
+ it('canDefineNewCommands', function() {
+ executor.defineCommand('greet', 'GET', '/person/:name');
+
+ var command = new Command('greet').
+ setParameter('name', 'Bob');
+
+ send.returns(Promise.resolve(new http.Response(200, {}, '')));
+
+ return assertSendsSuccessfully(command).then(function(response) {
+ assertSent('GET', '/person/Bob', {},
+ [['Accept', 'application/json; charset=utf-8']]);
+ });
+ });
+
+ it('canRedefineStandardCommands', function() {
+ executor.defineCommand(CommandName.GO_BACK, 'POST', '/custom/back');
+
+ var command = new Command(CommandName.GO_BACK).
+ setParameter('times', 3);
+
+ send.returns(Promise.resolve(new http.Response(200, {}, '')));
+
+ return assertSendsSuccessfully(command).then(function(response) {
+ assertSent('POST', '/custom/back', {'times': 3},
+ [['Accept', 'application/json; charset=utf-8']]);
+ });
+ });
+
+ it('accepts promised http clients', function() {
+ executor = new http.Executor(Promise.resolve(client));
+
+ var resp = JSON.stringify({sessionId: 'abc123'});
+ send.returns(Promise.resolve(new http.Response(200, {}, resp)));
+
+ let command = new Command(CommandName.NEW_SESSION);
+ return executor.execute(command).then(response => {
+ assertSent(
+ 'POST', '/session', {},
+ [['Accept', 'application/json; charset=utf-8']]);
+ });
+ });
+
+ function entries(map) {
+ let entries = [];
+ for (let e of map.entries()) {
+ entries.push(e);
+ }
+ return entries;
+ }
+
+ function checkError(type, message) {
+ return function(e) {
+ if (e instanceof type) {
+ assert.strictEqual(e.message, message);
+ } else {
+ throw e;
+ }
+ };
+ }
+
+ function assertSent(method, path, data, headers) {
+ assert.ok(send.calledWith(sinon.match(function(value) {
+ assert.equal(value.method, method);
+ assert.equal(value.path, path);
+ assert.deepEqual(value.data, data);
+ assert.deepEqual(entries(value.headers), headers);
+ return true;
+ })));
+ }
+
+ function assertSendsSuccessfully(command) {
+ return executor.execute(command).then(function(response) {
+ return response;
+ });
+ }
+
+ function assertFailsToSend(command, opt_onError) {
+ return executor.execute(command).then(
+ () => {throw Error('should have failed')},
+ (e) => {return e});
+ }
+ });
+});
diff --git a/node_modules/selenium-webdriver/test/lib/logging_test.js b/node_modules/selenium-webdriver/test/lib/logging_test.js
new file mode 100644
index 000000000..29d2af40f
--- /dev/null
+++ b/node_modules/selenium-webdriver/test/lib/logging_test.js
@@ -0,0 +1,272 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+const assert = require('assert');
+const sinon = require('sinon');
+const logging = require('../../lib/logging');
+
+describe('logging', function() {
+ let mgr, root, clock;
+
+ beforeEach(function setUp() {
+ mgr = new logging.LogManager;
+ root = mgr.getLogger('');
+
+ clock = sinon.useFakeTimers();
+ });
+
+ afterEach(function tearDown() {
+ clock.restore();
+ });
+
+ describe('LogManager', function() {
+ describe('getLogger()', function() {
+ it('handles falsey input', function() {
+ assert.strictEqual(root, mgr.getLogger());
+ assert.strictEqual(root, mgr.getLogger(''));
+ assert.strictEqual(root, mgr.getLogger(null));
+ assert.strictEqual(root, mgr.getLogger(0));
+ });
+
+ it('creates parent loggers', function() {
+ let logger = mgr.getLogger('foo.bar.baz');
+ assert.strictEqual(logger.parent_, mgr.getLogger('foo.bar'));
+
+ logger = logger.parent_;
+ assert.strictEqual(logger.parent_, mgr.getLogger('foo'));
+
+ logger = logger.parent_;
+ assert.strictEqual(logger.parent_, mgr.getLogger(''));
+
+ assert.strictEqual(logger.parent_.parent_, null);
+ });
+ });
+ });
+
+ describe('Logger', function() {
+ describe('getEffectiveLevel()', function() {
+ it('defaults to OFF', function() {
+ assert.strictEqual(root.getLevel(), logging.Level.OFF);
+ assert.strictEqual(root.getEffectiveLevel(), logging.Level.OFF);
+
+ root.setLevel(null);
+ assert.strictEqual(root.getLevel(), null);
+ assert.strictEqual(root.getEffectiveLevel(), logging.Level.OFF);
+ });
+
+ it('uses own level if set', function() {
+ let logger = mgr.getLogger('foo.bar.baz');
+ assert.strictEqual(logger.getLevel(), null);
+ assert.strictEqual(logger.getEffectiveLevel(), logging.Level.OFF);
+
+ logger.setLevel(logging.Level.INFO);
+ assert.strictEqual(logger.getLevel(), logging.Level.INFO);
+ assert.strictEqual(logger.getEffectiveLevel(), logging.Level.INFO);
+ });
+
+ it('uses level from set on nearest parent', function() {
+ let ancestor = mgr.getLogger('foo');
+ ancestor.setLevel(logging.Level.SEVERE);
+
+ let logger = mgr.getLogger('foo.bar.baz');
+ assert.strictEqual(logger.getLevel(), null);
+ assert.strictEqual(logger.getEffectiveLevel(), logging.Level.SEVERE);
+ });
+ });
+
+ describe('isLoggable()', function() {
+ it('compares level against logger\'s effective level', function() {
+ const log1 = mgr.getLogger('foo');
+ log1.setLevel(logging.Level.WARNING);
+
+ const log2 = mgr.getLogger('foo.bar.baz');
+
+ assert(!log2.isLoggable(logging.Level.FINEST));
+ assert(!log2.isLoggable(logging.Level.INFO));
+ assert(log2.isLoggable(logging.Level.WARNING));
+ assert(log2.isLoggable(logging.Level.SEVERE));
+
+ log2.setLevel(logging.Level.INFO);
+
+ assert(!log2.isLoggable(logging.Level.FINEST));
+ assert(log2.isLoggable(logging.Level.INFO));
+ assert(log2.isLoggable(logging.Level.WARNING));
+ assert(log2.isLoggable(logging.Level.SEVERE));
+
+ log2.setLevel(logging.Level.ALL);
+
+ assert(log2.isLoggable(logging.Level.FINEST));
+ assert(log2.isLoggable(logging.Level.INFO));
+ assert(log2.isLoggable(logging.Level.WARNING));
+ assert(log2.isLoggable(logging.Level.SEVERE));
+ });
+
+ it('Level.OFF is never loggable', function() {
+ function test(level) {
+ root.setLevel(level);
+ assert(!root.isLoggable(logging.Level.OFF),
+ 'OFF should not be loggable at ' + level);
+ }
+
+ test(logging.Level.ALL);
+ test(logging.Level.INFO);
+ test(logging.Level.OFF);
+ });
+ });
+
+ describe('log()', function() {
+ it('does not invoke loggable if message is not loggable', function() {
+ const log = mgr.getLogger('foo');
+ log.setLevel(logging.Level.OFF);
+
+ let callback = sinon.spy();
+ log.addHandler(callback);
+ root.addHandler(callback);
+
+ assert(!callback.called);
+ });
+
+ it('invokes handlers for each parent logger', function() {
+ const cb1 = sinon.spy();
+ const cb2 = sinon.spy();
+ const cb3 = sinon.spy();
+ const cb4 = sinon.spy();
+
+ const log1 = mgr.getLogger('foo');
+ const log2 = mgr.getLogger('foo.bar');
+ const log3 = mgr.getLogger('foo.bar.baz');
+ const log4 = mgr.getLogger('foo.bar.baz.quot');
+
+ log1.addHandler(cb1);
+ log1.setLevel(logging.Level.INFO);
+
+ log2.addHandler(cb2);
+ log2.setLevel(logging.Level.WARNING);
+
+ log3.addHandler(cb3);
+ log3.setLevel(logging.Level.FINER);
+
+ clock.tick(123456);
+
+ log4.finest('this is the finest message');
+ log4.finer('this is a finer message');
+ log4.info('this is an info message');
+ log4.warning('this is a warning message');
+ log4.severe('this is a severe message');
+
+ assert.equal(4, cb1.callCount);
+ assert.equal(4, cb2.callCount);
+ assert.equal(4, cb3.callCount);
+
+ const entry1 = new logging.Entry(
+ logging.Level.FINER,
+ '[foo.bar.baz.quot] this is a finer message',
+ 123456);
+ const entry2 = new logging.Entry(
+ logging.Level.INFO,
+ '[foo.bar.baz.quot] this is an info message',
+ 123456);
+ const entry3 = new logging.Entry(
+ logging.Level.WARNING,
+ '[foo.bar.baz.quot] this is a warning message',
+ 123456);
+ const entry4 = new logging.Entry(
+ logging.Level.SEVERE,
+ '[foo.bar.baz.quot] this is a severe message',
+ 123456);
+
+ check(cb1.getCall(0).args[0], entry1);
+ check(cb1.getCall(1).args[0], entry2);
+ check(cb1.getCall(2).args[0], entry3);
+ check(cb1.getCall(3).args[0], entry4);
+
+ check(cb2.getCall(0).args[0], entry1);
+ check(cb2.getCall(1).args[0], entry2);
+ check(cb2.getCall(2).args[0], entry3);
+ check(cb2.getCall(3).args[0], entry4);
+
+ check(cb3.getCall(0).args[0], entry1);
+ check(cb3.getCall(1).args[0], entry2);
+ check(cb3.getCall(2).args[0], entry3);
+ check(cb3.getCall(3).args[0], entry4);
+
+ function check(entry, expected) {
+ assert.equal(entry.level, expected.level, 'wrong level');
+ assert.equal(entry.message, expected.message, 'wrong message');
+ assert.equal(entry.timestamp, expected.timestamp, 'wrong time');
+ }
+ });
+
+ it('does not invoke removed handler', function() {
+ root.setLevel(logging.Level.INFO);
+ const cb = sinon.spy();
+
+ root.addHandler(cb);
+ root.info('hi');
+ assert.equal(1, cb.callCount);
+
+ assert(root.removeHandler(cb));
+ root.info('bye');
+ assert.equal(1, cb.callCount);
+
+ assert(!root.removeHandler(cb));
+ });
+ });
+ });
+
+ describe('getLevel()', function() {
+ it('converts named levels', function() {
+ assert.strictEqual(logging.Level.DEBUG, logging.getLevel('DEBUG'));
+ assert.strictEqual(logging.Level.ALL, logging.getLevel('FAKE'));
+ });
+
+ it('converts numeric levels', function() {
+ assert.strictEqual(
+ logging.Level.DEBUG,
+ logging.getLevel(logging.Level.DEBUG.value));
+ });
+
+ it('normalizes numeric levels', function() {
+ assert.strictEqual(
+ logging.Level.OFF,
+ logging.getLevel(logging.Level.OFF.value * 2));
+
+ let diff = logging.Level.SEVERE.value - logging.Level.WARNING.value;
+ assert.strictEqual(
+ logging.Level.WARNING,
+ logging.getLevel(logging.Level.WARNING.value + (diff * .5)));
+
+ assert.strictEqual(logging.Level.ALL, logging.getLevel(0));
+ assert.strictEqual(logging.Level.ALL, logging.getLevel(-1));
+ });
+ });
+
+ describe('Preferences', function() {
+ it('can be converted to JSON', function() {
+ let prefs = new logging.Preferences;
+ assert.equal('{}', JSON.stringify(prefs));
+
+ prefs.setLevel('foo', logging.Level.DEBUG);
+ assert.equal('{"foo":"DEBUG"}', JSON.stringify(prefs));
+
+ prefs.setLevel(logging.Type.BROWSER, logging.Level.FINE);
+ assert.equal('{"foo":"DEBUG","browser":"FINE"}', JSON.stringify(prefs));
+ });
+ });
+});
diff --git a/node_modules/selenium-webdriver/test/lib/promise_aplus_test.js b/node_modules/selenium-webdriver/test/lib/promise_aplus_test.js
new file mode 100644
index 000000000..a89391590
--- /dev/null
+++ b/node_modules/selenium-webdriver/test/lib/promise_aplus_test.js
@@ -0,0 +1,74 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+const promise = require('../../lib/promise');
+
+describe('Promises/A+ Compliance Tests', function() {
+ // The promise spec does not define behavior for unhandled rejections and
+ // assumes they are effectively swallowed. This is not the case with our
+ // implementation, so we have to disable error propagation to test that the
+ // rest of our behavior is compliant.
+ // We run the tests with a separate instance of the control flow to ensure
+ // disablign error propagation does not impact other tests.
+ var flow = new promise.ControlFlow();
+ flow.setPropagateUnhandledRejections(false);
+
+ // Skip the tests in 2.2.6.1/2. We are not compliant in these scenarios.
+ var realDescribe = global.describe;
+ global.describe = function(name, fn) {
+ realDescribe(name, function() {
+ var prefix = 'Promises/A+ Compliance Tests 2.2.6: '
+ + '`then` may be called multiple times on the same promise.';
+ var suffix = 'even when one handler is added inside another handler';
+ if (this.fullTitle().startsWith(prefix)
+ && this.fullTitle().endsWith(suffix)) {
+ var realSpecify = global.specify;
+ try {
+ global.specify = function(name) {
+ realSpecify(name);
+ };
+ fn();
+ } finally {
+ global.specify = realSpecify;
+ }
+ } else {
+ fn();
+ }
+ });
+ };
+
+ require('promises-aplus-tests').mocha({
+ resolved: function(value) {
+ return new promise.Promise((fulfill) => fulfill(value), flow);
+ },
+ rejected: function(error) {
+ return new promise.Promise((_, reject) => reject(error), flow);
+ },
+ deferred: function() {
+ var d = new promise.Deferred(flow);
+ return {
+ resolve: d.fulfill,
+ reject: d.reject,
+ promise: d.promise
+ };
+ }
+ });
+
+ global.describe = realDescribe;
+});
diff --git a/node_modules/selenium-webdriver/test/lib/promise_error_test.js b/node_modules/selenium-webdriver/test/lib/promise_error_test.js
new file mode 100644
index 000000000..e6de3cb92
--- /dev/null
+++ b/node_modules/selenium-webdriver/test/lib/promise_error_test.js
@@ -0,0 +1,880 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+/**
+ * @fileoverview Contains tests against promise error handling. Many tests use
+ * NativePromise to control test termination independent of promise
+ * (and notably promise.ControlFlow).
+ */
+
+'use strict';
+
+const testutil = require('./testutil');
+
+const assert = require('assert');
+const promise = require('../../lib/promise');
+
+const NativePromise = Promise;
+const StubError = testutil.StubError;
+const throwStubError = testutil.throwStubError;
+const assertIsStubError = testutil.assertIsStubError;
+
+describe('promise error handling', function() {
+ var flow, uncaughtExceptions;
+
+ beforeEach(function setUp() {
+ flow = promise.controlFlow();
+ uncaughtExceptions = [];
+ flow.on('uncaughtException', onUncaughtException);
+ });
+
+ afterEach(function tearDown() {
+ return waitForIdle(flow).then(function() {
+ assert.deepEqual(
+ [], uncaughtExceptions, 'There were uncaught exceptions');
+ flow.reset();
+ });
+ });
+
+ function onUncaughtException(e) {
+ uncaughtExceptions.push(e);
+ }
+
+ function waitForAbort(opt_flow, opt_n) {
+ var n = opt_n || 1;
+ var theFlow = opt_flow || flow;
+ theFlow.removeAllListeners(
+ promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION);
+ return new NativePromise(function(fulfill, reject) {
+ theFlow.once('idle', function() {
+ reject(Error('expected flow to report an unhandled error'));
+ });
+
+ var errors = [];
+ theFlow.on('uncaughtException', onError);
+ function onError(e) {
+ errors.push(e);
+ if (errors.length === n) {
+ theFlow.removeListener('uncaughtException', onError);
+ fulfill(n === 1 ? errors[0] : errors);
+ }
+ }
+ });
+ }
+
+ function waitForIdle(opt_flow) {
+ var theFlow = opt_flow || flow;
+ return new NativePromise(function(fulfill, reject) {
+ if (theFlow.isIdle()) {
+ fulfill();
+ return;
+ }
+ theFlow.once('idle', fulfill);
+ theFlow.once('uncaughtException', reject);
+ });
+ }
+
+ it('testRejectedPromiseTriggersErrorCallback', function() {
+ return promise.rejected(new StubError).
+ then(assert.fail, assertIsStubError);
+ });
+
+ describe('callback throws trigger subsequent error callback', function() {
+ it('fulfilled promise', function() {
+ return promise.fulfilled().
+ then(throwStubError).
+ then(assert.fail, assertIsStubError);
+ });
+
+ it('rejected promise', function() {
+ var e = Error('not the droids you are looking for');
+ return promise.rejected(e).
+ then(assert.fail, throwStubError).
+ then(assert.fail, assertIsStubError);
+ });
+ });
+
+ describe('callback returns rejected promise triggers subsequent errback', function() {
+ it('from fulfilled callback', function() {
+ return promise.fulfilled().then(function() {
+ return promise.rejected(new StubError);
+ }).then(assert.fail, assertIsStubError);
+ });
+
+ it('from rejected callback', function() {
+ var e = Error('not the droids you are looking for');
+ return promise.rejected(e).
+ then(assert.fail, function() {
+ return promise.rejected(new StubError);
+ }).
+ then(assert.fail, assertIsStubError);
+ });
+ });
+
+ it('testReportsUnhandledRejectionsThroughTheControlFlow', function() {
+ promise.rejected(new StubError);
+ return waitForAbort().then(assertIsStubError);
+ });
+
+ describe('multiple unhandled rejections outside a task', function() {
+ it('are reported in order they occurred', function() {
+ var e1 = Error('error 1');
+ var e2 = Error('error 2');
+
+ promise.rejected(e1);
+ promise.rejected(e2);
+
+ return waitForAbort(flow).then(function(error) {
+ assert.ok(
+ error instanceof promise.MultipleUnhandledRejectionError);
+ // TODO: switch to Array.from when we drop node 0.12.x
+ var errors = [];
+ for (var e of error.errors) {
+ errors.push(e);
+ }
+ assert.deepEqual([e1, e2], errors);
+ });
+ });
+ });
+
+ describe('does not report unhandled rejection when', function() {
+ it('handler added before next tick', function() {
+ promise.rejected(new StubError).then(assert.fail, assertIsStubError);
+ return waitForIdle();
+ });
+
+ it('added async but before next tick', function() {
+ var called = false;
+ return new NativePromise(function(fulfill, reject) {
+ var aPromise;
+ NativePromise.resolve().then(function() {
+ aPromise.then(assert.fail, function(e) {
+ called = true;
+ assertIsStubError(e);
+ });
+ waitForIdle().then(fulfill, reject);
+ });
+ aPromise = promise.rejected(new StubError);
+ }).then(function() {
+ assert.ok(called);
+ })
+ });
+ });
+
+ it('testTaskThrows', function() {
+ return flow.execute(throwStubError).then(assert.fail, assertIsStubError);
+ });
+
+ it('testTaskReturnsRejectedPromise', function() {
+ return flow.execute(function() {
+ return promise.rejected(new StubError)
+ }).then(assert.fail, assertIsStubError);
+ });
+
+ it('testTaskHasUnhandledRejection', function() {
+ return flow.execute(function() {
+ promise.rejected(new StubError)
+ }).then(assert.fail, assertIsStubError);
+ });
+
+ it('testTaskfails_returnedPromiseIsUnhandled', function() {
+ flow.execute(throwStubError);
+ return waitForAbort().then(assertIsStubError);
+ });
+
+ it('testTaskHasUnhandledRejection_cancelsRemainingSubTasks', function() {
+ var seen = [];
+ flow.execute(function() {
+ promise.rejected(new StubError);
+
+ flow.execute(() => seen.push('a'))
+ .then(() => seen.push('b'), (e) => seen.push(e));
+ flow.execute(() => seen.push('c'))
+ .then(() => seen.push('b'), (e) => seen.push(e));
+ });
+
+ return waitForAbort()
+ .then(assertIsStubError)
+ .then(() => assert.deepEqual([], seen));
+ });
+
+ describe('nested task failures', function() {
+ it('returns up to paren', function() {
+ return flow.execute(function() {
+ return flow.execute(function() {
+ return flow.execute(throwStubError);
+ });
+ }).then(assert.fail, assertIsStubError);
+ });
+
+ it('task throws; uncaught error bubbles up', function() {
+ flow.execute(function() {
+ flow.execute(function() {
+ flow.execute(throwStubError);
+ });
+ });
+ return waitForAbort().then(assertIsStubError);
+ });
+
+ it('task throws; uncaught error bubbles up; is caught at root', function() {
+ flow.execute(function() {
+ flow.execute(function() {
+ flow.execute(throwStubError);
+ });
+ }).then(assert.fail, assertIsStubError);
+ return waitForIdle();
+ });
+
+ it('unhandled rejection bubbles up', function() {
+ flow.execute(function() {
+ flow.execute(function() {
+ flow.execute(function() {
+ promise.rejected(new StubError);
+ });
+ });
+ });
+ return waitForAbort().then(assertIsStubError);
+ });
+
+ it('unhandled rejection bubbles up; caught at root', function() {
+ flow.execute(function() {
+ flow.execute(function() {
+ promise.rejected(new StubError);
+ });
+ }).then(assert.fail, assertIsStubError);
+ return waitForIdle();
+ });
+
+ it('mixtureof hanging and free subtasks', function() {
+ flow.execute(function() {
+ return flow.execute(function() {
+ flow.execute(throwStubError);
+ });
+ });
+ return waitForAbort().then(assertIsStubError);
+ });
+
+ it('cancels remaining tasks', function() {
+ var seen = [];
+ flow.execute(function() {
+ flow.execute(() => promise.rejected(new StubError));
+ flow.execute(() => seen.push('a'))
+ .then(() => seen.push('b'), (e) => seen.push(e));
+ flow.execute(() => seen.push('c'))
+ .then(() => seen.push('b'), (e) => seen.push(e));
+ });
+
+ return waitForAbort()
+ .then(assertIsStubError)
+ .then(() => assert.deepEqual([], seen));
+ });
+ });
+
+ it('testTaskReturnsPromiseLikeObjectThatInvokesErrback', function() {
+ return flow.execute(function() {
+ return {
+ 'then': function(_, errback) {
+ errback('abc123');
+ }
+ };
+ }).then(assert.fail, function(value) {
+ assert.equal('abc123', value);
+ });
+ });
+
+ describe('ControlFlow#wait();', function() {
+ describe('condition throws;', function() {
+ it('failure is caught', function() {
+ return flow.wait(throwStubError, 50).then(assert.fail, assertIsStubError);
+ });
+
+ it('failure is not caught', function() {
+ flow.wait(throwStubError, 50);
+ return waitForAbort().then(assertIsStubError);
+ });
+ });
+
+ describe('condition returns promise', function() {
+ it('failure is caught', function() {
+ return flow.wait(function() {
+ return promise.rejected(new StubError);
+ }, 50).then(assert.fail, assertIsStubError);
+ });
+
+ it('failure is not caught', function() {
+ flow.wait(function() {
+ return promise.rejected(new StubError);
+ }, 50);
+ return waitForAbort().then(assertIsStubError);
+ });
+ });
+
+ describe('condition has unhandled promise rejection', function() {
+ it('failure is caught', function() {
+ return flow.wait(function() {
+ promise.rejected(new StubError);
+ }, 50).then(assert.fail, assertIsStubError);
+ });
+
+ it('failure is not caught', function() {
+ flow.wait(function() {
+ promise.rejected(new StubError);
+ }, 50);
+ return waitForAbort().then(assertIsStubError);
+ });
+ });
+
+ describe('condition has subtask failure', function() {
+ it('failure is caught', function() {
+ return flow.wait(function() {
+ flow.execute(function() {
+ flow.execute(throwStubError);
+ });
+ }, 50).then(assert.fail, assertIsStubError);
+ });
+
+ it('failure is not caught', function() {
+ flow.wait(function() {
+ flow.execute(function() {
+ flow.execute(throwStubError);
+ });
+ }, 50);
+ return waitForAbort().then(assertIsStubError);
+ });
+ });
+ });
+
+ describe('errback throws a new error', function() {
+ it('start with normal promise', function() {
+ var error = Error('an error');
+ return promise.rejected(error).
+ catch(function(e) {
+ assert.equal(e, error);
+ throw new StubError;
+ }).
+ catch(assertIsStubError);
+ });
+
+ it('start with task result', function() {
+ var error = Error('an error');
+ return flow.execute(function() {
+ throw error;
+ }).
+ catch(function(e) {
+ assert.equal(e, error);
+ throw new StubError;
+ }).
+ catch(assertIsStubError);
+ });
+
+ it('start with normal promise; uncaught error', function() {
+ var error = Error('an error');
+ promise.rejected(error).
+ catch(function(e) {
+ assert.equal(e, error);
+ throw new StubError;
+ });
+ return waitForAbort().then(assertIsStubError);
+ });
+
+ it('start with task result; uncaught error', function() {
+ var error = Error('an error');
+ flow.execute(function() {
+ throw error;
+ }).
+ catch(function(e) {
+ assert.equal(e, error);
+ throw new StubError;
+ });
+ return waitForAbort().then(assertIsStubError);
+ });
+ });
+
+ it('thrownPromiseCausesCallbackRejection', function() {
+ let p = promise.fulfilled(1234);
+ return promise.fulfilled().then(function() {
+ throw p;
+ }).then(assert.fail, function(value) {
+ assert.strictEqual(p, value);
+ });
+ });
+
+ describe('task throws promise', function() {
+ it('promise was fulfilled', function() {
+ var toThrow = promise.fulfilled(1234);
+ flow.execute(function() {
+ throw toThrow;
+ }).then(assert.fail, function(value) {
+ assert.equal(toThrow, value);
+ return toThrow;
+ }).then(function(value) {
+ assert.equal(1234, value);
+ });
+ return waitForIdle();
+ });
+
+ it('promise was rejected', function() {
+ var toThrow = promise.rejected(new StubError);
+ toThrow.catch(function() {}); // For tearDown.
+ flow.execute(function() {
+ throw toThrow;
+ }).then(assert.fail, function(e) {
+ assert.equal(toThrow, e);
+ return e;
+ }).then(assert.fail, assertIsStubError);
+ return waitForIdle();
+ });
+ });
+
+ it('testFailsTaskIfThereIsAnUnhandledErrorWhileWaitingOnTaskResult', function() {
+ var d = promise.defer();
+ flow.execute(function() {
+ promise.rejected(new StubError);
+ return d.promise;
+ }).then(assert.fail, assertIsStubError);
+
+ return waitForIdle().then(function() {
+ return d.promise;
+ }).then(assert.fail, function(e) {
+ assert.equal('CancellationError: StubError', e.toString());
+ });
+ });
+
+ it('testFailsParentTaskIfAsyncScheduledTaskFails', function() {
+ var d = promise.defer();
+ flow.execute(function() {
+ flow.execute(throwStubError);
+ return d.promise;
+ }).then(assert.fail, assertIsStubError);
+
+ return waitForIdle().then(function() {
+ return d.promise;
+ }).then(assert.fail, function(e) {
+ assert.equal('CancellationError: StubError', e.toString());
+ });
+ });
+
+ describe('long stack traces', function() {
+ afterEach(() => promise.LONG_STACK_TRACES = false);
+
+ it('always includes task stacks in failures', function() {
+ promise.LONG_STACK_TRACES = false;
+ flow.execute(function() {
+ flow.execute(function() {
+ flow.execute(throwStubError, 'throw error');
+ }, 'two');
+ }, 'three').
+ then(assert.fail, function(e) {
+ assertIsStubError(e);
+ if (typeof e.stack !== 'string') {
+ return;
+ }
+ var messages = e.stack.split(/\n/).filter(function(line, index) {
+ return /^From: /.test(line);
+ });
+ assert.deepEqual([
+ 'From: Task: throw error',
+ 'From: Task: two',
+ 'From: Task: three'
+ ], messages);
+ });
+ return waitForIdle();
+ });
+
+ it('does not include completed tasks', function () {
+ flow.execute(function() {}, 'succeeds');
+ flow.execute(throwStubError, 'kaboom').then(assert.fail, function(e) {
+ assertIsStubError(e);
+ if (typeof e.stack !== 'string') {
+ return;
+ }
+ var messages = e.stack.split(/\n/).filter(function(line, index) {
+ return /^From: /.test(line);
+ });
+ assert.deepEqual(['From: Task: kaboom'], messages);
+ });
+ return waitForIdle();
+ });
+
+ it('does not include promise chain when disabled', function() {
+ promise.LONG_STACK_TRACES = false;
+ flow.execute(function() {
+ flow.execute(function() {
+ return promise.fulfilled().
+ then(function() {}).
+ then(function() {}).
+ then(throwStubError);
+ }, 'eventually assert.fails');
+ }, 'start').
+ then(assert.fail, function(e) {
+ assertIsStubError(e);
+ if (typeof e.stack !== 'string') {
+ return;
+ }
+ var messages = e.stack.split(/\n/).filter(function(line, index) {
+ return /^From: /.test(line);
+ });
+ assert.deepEqual([
+ 'From: Task: eventually assert.fails',
+ 'From: Task: start'
+ ], messages);
+ });
+ return waitForIdle();
+ });
+
+ it('includes promise chain when enabled', function() {
+ promise.LONG_STACK_TRACES = true;
+ flow.execute(function() {
+ flow.execute(function() {
+ return promise.fulfilled().
+ then(function() {}).
+ then(function() {}).
+ then(throwStubError);
+ }, 'eventually assert.fails');
+ }, 'start').
+ then(assert.fail, function(e) {
+ assertIsStubError(e);
+ if (typeof e.stack !== 'string') {
+ return;
+ }
+ var messages = e.stack.split(/\n/).filter(function(line, index) {
+ return /^From: /.test(line);
+ });
+ assert.deepEqual([
+ 'From: Promise: then',
+ 'From: Task: eventually assert.fails',
+ 'From: Task: start'
+ ], messages);
+ });
+ return waitForIdle();
+ });
+ });
+
+ describe('frame cancels remaining tasks', function() {
+ it('on unhandled task failure', function() {
+ var run = false;
+ return flow.execute(function() {
+ flow.execute(throwStubError);
+ flow.execute(function() { run = true; });
+ }).then(assert.fail, function(e) {
+ assertIsStubError(e);
+ assert.ok(!run);
+ });
+ });
+
+ it('on unhandled promise rejection', function() {
+ var run = false;
+ return flow.execute(function() {
+ promise.rejected(new StubError);
+ flow.execute(function() { run = true; });
+ }).then(assert.fail, function(e) {
+ assertIsStubError(e);
+ assert.ok(!run);
+ });
+ });
+
+ it('if task throws', function() {
+ var run = false;
+ return flow.execute(function() {
+ flow.execute(function() { run = true; });
+ throw new StubError;
+ }).then(assert.fail, function(e) {
+ assertIsStubError(e);
+ assert.ok(!run);
+ });
+ });
+
+ describe('task callbacks scheduled in another frame', function() {
+ flow = promise.controlFlow();
+ function noop() {}
+
+ let subTask;
+
+ before(function() {
+ flow.execute(function() {
+ // This task will be discarded and never run because of the error below.
+ subTask = flow.execute(() => 'abc');
+ throw new StubError('stub');
+ }).catch(noop);
+ });
+
+ function assertCancellation(e) {
+ assert.ok(e instanceof promise.CancellationError);
+ assert.equal(
+ 'Task was discarded due to a previous failure: stub', e.message);
+ }
+
+ it('are rejected with cancellation error', function() {
+ let result;
+ return Promise.resolve().then(function() {
+ return flow.execute(function() {
+ result = subTask.then(assert.fail);
+ });
+ })
+ .then(() => result)
+ .then(assert.fail, assertCancellation);
+ });
+
+ it('cancellation errors propagate through callbacks (1)', function() {
+ let result;
+ return Promise.resolve().then(function() {
+ return flow.execute(function() {
+ result = subTask
+ .then(assert.fail, assertCancellation)
+ .then(() => 'abc123');
+ });
+ })
+ .then(() => result)
+ .then(value => assert.equal('abc123', value));
+ });
+
+ it('cancellation errors propagate through callbacks (2)', function() {
+ let result;
+ return Promise.resolve().then(function() {
+ return flow.execute(function() {
+ result = subTask.then(assert.fail)
+ .then(noop, assertCancellation)
+ .then(() => 'fin');
+ });
+ })
+ // Verify result actually computed successfully all the way through.
+ .then(() => result)
+ .then(value => assert.equal('fin', value));
+ });
+ });
+ });
+
+ it('testRegisteredTaskCallbacksAreDroppedWhenTaskIsCancelled_return', function() {
+ var seen = [];
+ return flow.execute(function() {
+ flow.execute(throwStubError);
+
+ flow.execute(function() {
+ seen.push(1);
+ }).then(function() {
+ seen.push(2);
+ }, function() {
+ seen.push(3);
+ });
+ }).then(assert.fail, function(e) {
+ assertIsStubError(e);
+ assert.deepEqual([], seen);
+ });
+ });
+
+ it('testRegisteredTaskCallbacksAreDroppedWhenTaskIsCancelled_withReturn', function() {
+ var seen = [];
+ return flow.execute(function() {
+ flow.execute(throwStubError);
+
+ return flow.execute(function() {
+ seen.push(1);
+ }).then(function() {
+ seen.push(2);
+ }, function() {
+ seen.push(3);
+ });
+ }).then(assert.fail, function(e) {
+ assertIsStubError(e);
+ assert.deepEqual([], seen);
+ });
+ });
+
+ it('testTasksWithinACallbackAreDroppedIfContainingTaskIsAborted', function() {
+ var seen = [];
+ return flow.execute(function() {
+ flow.execute(throwStubError);
+
+ // None of the callbacks on this promise should execute because the
+ // task assert.failure above is never handled, causing the containing task to
+ // abort.
+ promise.fulfilled().then(function() {
+ seen.push(1);
+ return flow.execute(function() {
+ seen.push(2);
+ });
+ }).finally(function() {
+ seen.push(3);
+ });
+
+ }).then(assert.fail, function(e) {
+ assertIsStubError(e);
+ assert.deepEqual([], seen);
+ });
+ });
+
+ it('testTaskIsCancelledAfterWaitTimeout', function() {
+ var seen = [];
+ return flow.execute(function() {
+ flow.wait(function() {
+ return promise.delayed(50);
+ }, 5);
+
+ return flow.execute(function() {
+ seen.push(1);
+ }).then(function() {
+ seen.push(2);
+ }, function() {
+ seen.push(3);
+ });
+ }).then(assert.fail, function() {
+ assert.deepEqual([], seen);
+ });
+ });
+
+ describe('task callbacks get cancellation error if registered after task was cancelled', function() {
+ it('(a)', function() {
+ var task;
+ flow.execute(function() {
+ flow.execute(throwStubError);
+ task = flow.execute(function() {});
+ }).then(assert.fail, assertIsStubError);
+ return waitForIdle().then(function() {
+ return task.then(assert.fail, function(e) {
+ assert.ok(e instanceof promise.CancellationError);
+ });
+ });
+ });
+
+ it('(b)', function() {
+ var seen = [];
+
+ var task;
+ flow.execute(function() {
+ flow.execute(throwStubError);
+ task = flow.execute(function() {});
+
+ task.then(() => seen.push(1))
+ .then(() => seen.push(2));
+ task.then(() => seen.push(3))
+ .then(() => seen.push(4));
+
+ }).then(assert.fail, assertIsStubError);
+
+ return waitForIdle().then(function() {
+ return task.then(assert.fail, function(e) {
+ seen.push(5);
+ assert.ok(e instanceof promise.CancellationError);
+ });
+ }).then(() => assert.deepEqual([5], seen));
+ });
+ });
+
+ it('unhandledRejectionInParallelTaskQueue', function() {
+ var seen = [];
+ function schedule(name) {
+ return flow.execute(() => seen.push(name), name);
+ }
+
+ flow.async(function() {
+ schedule('a.1');
+ flow.execute(throwStubError, 'a.2 (throws)');
+ });
+
+ var b3;
+ flow.async(function() {
+ schedule('b.1');
+ schedule('b.2');
+ b3 = schedule('b.3');
+ });
+
+ var c3;
+ flow.async(function() {
+ schedule('c.1');
+ schedule('c.2');
+ c3 = schedule('c.3');
+ });
+
+ function assertWasCancelled(p) {
+ return p.then(assert.fail, function(e) {
+ assert.ok(e instanceof promise.CancellationError);
+ });
+ }
+
+ return waitForAbort()
+ .then(function() {
+ assert.deepEqual(['a.1', 'b.1', 'c.1', 'b.2', 'c.2'], seen);
+ assert.ok(!b3.isPending());
+ assert.ok(!c3.isPending());
+ })
+ .then(() => assertWasCancelled(b3))
+ .then(() => assertWasCancelled(c3));
+ });
+
+ it('errorsInAsyncFunctionsAreReportedAsUnhandledRejection', function() {
+ flow.removeAllListeners(); // For tearDown.
+
+ var task;
+ return new Promise(function(fulfill) {
+ flow.once('uncaughtException', fulfill);
+ flow.async(function() {
+ task = flow.execute(function() {});
+ throw Error('boom');
+ });
+ }).then(function(error) {
+ assert.ok(error instanceof promise.CancellationError);
+ assert.ok(!task.isPending());
+ return task.catch(function(error) {
+ assert.ok(error instanceof promise.CancellationError);
+ });
+ });
+ });
+
+ describe('does not wait for values thrown from callbacks to be resolved', function() {
+ it('(a)', function() {
+ var p1 = promise.fulfilled();
+ var reason = promise.fulfilled('should not see me');
+ return p1.then(function() {
+ throw reason;
+ }).then(assert.fail, function(e) {
+ assert.equal(reason, e);
+ });
+ });
+
+ it('(b)', function() {
+ var p1 = promise.fulfilled();
+ var reason = promise.rejected('should not see me');
+ reason.catch(function() {}); // For tearDown.
+ return p1.then(function() {
+ throw reason;
+ }).then(assert.fail, function(e) {
+ assert.equal(reason, e);
+ });
+ });
+
+ it('(c)', function() {
+ var p1 = promise.fulfilled();
+ var reason = promise.defer();
+ setTimeout(() => reason.fulfill('should not see me'), 100);
+ return p1.then(function() {
+ throw reason.promise;
+ }).then(assert.fail, function(e) {
+ assert.equal(reason.promise, e);
+ });
+ });
+
+ it('(d)', function() {
+ var p1 = promise.fulfilled();
+ var reason = {then: function() {}}; // A thenable like object.
+ return p1.then(function() {
+ throw reason;
+ }).then(assert.fail, function(e) {
+ assert.equal(reason, e);
+ });
+ });
+ });
+});
diff --git a/node_modules/selenium-webdriver/test/lib/promise_flow_test.js b/node_modules/selenium-webdriver/test/lib/promise_flow_test.js
new file mode 100644
index 000000000..b42ac5209
--- /dev/null
+++ b/node_modules/selenium-webdriver/test/lib/promise_flow_test.js
@@ -0,0 +1,2284 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+const assert = require('assert');
+const fail = assert.fail;
+const sinon = require('sinon');
+
+const testutil = require('./testutil');
+const promise = require('../../lib/promise');
+
+const NativePromise = Promise;
+
+// Aliases for readability.
+const StubError = testutil.StubError;
+const assertIsStubError = testutil.assertIsStubError;
+const callbackPair = testutil.callbackPair;
+const throwStubError = testutil.throwStubError;
+
+describe('promise control flow', function() {
+ let flow, flowHistory, messages, uncaughtExceptions;
+
+ beforeEach(function setUp() {
+ promise.LONG_STACK_TRACES = false;
+ flow = new promise.ControlFlow();
+ promise.setDefaultFlow(flow);
+ messages = [];
+ flowHistory = [];
+
+ uncaughtExceptions = [];
+ flow.on(promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION,
+ onUncaughtException);
+ });
+
+ afterEach(function tearDown() {
+ flow.removeAllListeners(
+ promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION);
+ assert.deepEqual([], uncaughtExceptions,
+ 'There were uncaught exceptions');
+ flow.reset();
+ promise.LONG_STACK_TRACES = false;
+ });
+
+ function onUncaughtException(e) {
+ uncaughtExceptions.push(e);
+ }
+
+ function waitForAbort(opt_flow) {
+ var theFlow = opt_flow || flow;
+ theFlow.removeAllListeners(
+ promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION);
+ return new NativePromise(function(fulfill, reject) {
+ theFlow.once(promise.ControlFlow.EventType.IDLE, function() {
+ reject(Error('expected flow to report an unhandled error'));
+ });
+ theFlow.once(
+ promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION,
+ fulfill);
+ });
+ }
+
+ function waitForIdle(opt_flow) {
+ var theFlow = opt_flow || flow;
+ return new NativePromise(function(fulfill, reject) {
+ theFlow.once(promise.ControlFlow.EventType.IDLE, fulfill);
+ theFlow.once(
+ promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, reject);
+ });
+ }
+
+ function timeout(ms) {
+ return new NativePromise(function(fulfill) {
+ setTimeout(fulfill, ms);
+ });
+ }
+
+
+ function schedule(msg, opt_return) {
+ return scheduleAction(msg, function() {
+ return opt_return;
+ });
+ }
+
+ /**
+ * @param {string} value The value to push.
+ * @param {promise.Promise=} opt_taskPromise Promise to return from
+ * the task.
+ * @return {!promise.Promise} The result.
+ */
+ function schedulePush(value, opt_taskPromise) {
+ return scheduleAction(value, function() {
+ messages.push(value);
+ return opt_taskPromise;
+ });
+ }
+
+ /**
+ * @param {string} msg Debug message.
+ * @param {!Function} actionFn The function.
+ * @return {!promise.Promise} The function result.
+ */
+ function scheduleAction(msg, actionFn) {
+ return promise.controlFlow().execute(function() {
+ flowHistory.push(msg);
+ return actionFn();
+ }, msg);
+ }
+
+ /**
+ * @param {!Function} condition The condition function.
+ * @param {number=} opt_timeout The timeout.
+ * @param {string=} opt_message Optional message.
+ * @return {!promise.Promise} The wait result.
+ */
+ function scheduleWait(condition, opt_timeout, opt_message) {
+ var msg = opt_message || '';
+ // It's not possible to hook into when the wait itself is scheduled, so
+ // we record each iteration of the wait loop.
+ var count = 0;
+ return promise.controlFlow().wait(function() {
+ flowHistory.push((count++) + ': ' + msg);
+ return condition();
+ }, opt_timeout, msg);
+ }
+
+ function asyncRun(fn, opt_self) {
+ NativePromise.resolve().then(() => fn.call(opt_self));
+ }
+
+ function assertFlowHistory(var_args) {
+ var expected = Array.prototype.slice.call(arguments, 0);
+ assert.deepEqual(expected, flowHistory);
+ }
+
+ function assertMessages(var_args) {
+ var expected = Array.prototype.slice.call(arguments, 0);
+ assert.deepEqual(expected, messages);
+ }
+
+ function assertingMessages(var_args) {
+ var args = Array.prototype.slice.call(arguments, 0);
+ return () => assertMessages.apply(null, args);
+ }
+
+ function assertFlowIs(flow) {
+ assert.equal(flow, promise.controlFlow());
+ }
+
+ describe('testScheduling', function() {
+ it('aSimpleFunction', function() {
+ schedule('go');
+ return waitForIdle().then(function() {
+ assertFlowHistory('go');
+ });
+ });
+
+ it('aSimpleFunctionWithANonPromiseReturnValue', function() {
+ schedule('go', 123).then(function(value) {
+ assert.equal(123, value);
+ });
+ return waitForIdle().then(function() {
+ assertFlowHistory('go');
+ });
+ });
+
+ it('aSimpleSequence', function() {
+ schedule('a');
+ schedule('b');
+ schedule('c');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c');
+ });
+ });
+
+ it('invokesCallbacksWhenTaskIsDone', function() {
+ var d = new promise.Deferred();
+ var called = false;
+ var done = schedule('a', d.promise).then(function(value) {
+ called = true;
+ assert.equal(123, value);
+ });
+ return timeout(5).then(function() {
+ assert.ok(!called);
+ d.fulfill(123);
+ return done;
+ }).
+ then(function() {
+ assertFlowHistory('a');
+ });
+ });
+
+ it('blocksUntilPromiseReturnedByTaskIsResolved', function() {
+ var done = promise.defer();
+ schedulePush('a', done.promise);
+ schedulePush('b');
+ setTimeout(function() {
+ done.fulfill();
+ messages.push('c');
+ }, 25);
+ return waitForIdle().then(assertingMessages('a', 'c', 'b'));
+ });
+
+ it('waitsForReturnedPromisesToResolve', function() {
+ var d1 = new promise.Deferred();
+ var d2 = new promise.Deferred();
+
+ var callback = sinon.spy();
+ schedule('a', d1.promise).then(callback);
+
+ return timeout(5).then(function() {
+ assert(!callback.called);
+ d1.fulfill(d2.promise);
+ return timeout(5);
+ }).then(function() {
+ assert(!callback.called);
+ d2.fulfill('fluffy bunny');
+ return waitForIdle();
+ }).then(function() {
+ assert(callback.called);
+ assert.equal('fluffy bunny', callback.getCall(0).args[0]);
+ assertFlowHistory('a');
+ });
+ });
+
+ it('executesTasksInAFutureTurnAfterTheyAreScheduled', function() {
+ var count = 0;
+ function incr() { count++; }
+
+ scheduleAction('', incr);
+ assert.equal(0, count);
+ return waitForIdle().then(function() {
+ assert.equal(1, count);
+ });
+ });
+
+ it('executesOneTaskPerTurnOfTheEventLoop', function() {
+ var order = [];
+ function go() {
+ order.push(order.length / 2);
+ asyncRun(function() {
+ order.push('-');
+ });
+ }
+
+ scheduleAction('', go);
+ scheduleAction('', go);
+ return waitForIdle().then(function() {
+ assert.deepEqual([0, '-', 1, '-'], order);
+ })
+ });
+
+ it('firstScheduledTaskIsWithinACallback', function() {
+ promise.fulfilled().then(function() {
+ schedule('a');
+ schedule('b');
+ schedule('c');
+ }).then(function() {
+ assertFlowHistory('a', 'b', 'c');
+ });
+ return waitForIdle();
+ });
+
+ it('newTasksAddedWhileWaitingOnTaskReturnedPromise1', function() {
+ scheduleAction('a', function() {
+ var d = promise.defer();
+ setTimeout(function() {
+ schedule('c');
+ d.fulfill();
+ }, 10);
+ return d.promise;
+ });
+ schedule('b');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c');
+ });
+ });
+
+ it('newTasksAddedWhileWaitingOnTaskReturnedPromise2', function() {
+ scheduleAction('a', function() {
+ var d = promise.defer();
+ setTimeout(function() {
+ schedule('c');
+ asyncRun(d.fulfill);
+ }, 10);
+ return d.promise;
+ });
+ schedule('b');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'c', 'b');
+ });
+ });
+ });
+
+ describe('testFraming', function() {
+ it('callbacksRunInANewFrame', function() {
+ schedule('a').then(function() {
+ schedule('c');
+ });
+ schedule('b');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'c', 'b');
+ });
+ });
+
+ it('lotsOfNesting', function() {
+ schedule('a').then(function() {
+ schedule('c').then(function() {
+ schedule('e').then(function() {
+ schedule('g');
+ });
+ schedule('f');
+ });
+ schedule('d');
+ });
+ schedule('b');
+
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'c', 'e', 'g', 'f', 'd', 'b');
+ });
+ });
+
+ it('callbackReturnsPromiseThatDependsOnATask_1', function() {
+ schedule('a').then(function() {
+ schedule('b');
+ return promise.delayed(5).then(function() {
+ return schedule('c');
+ });
+ });
+ schedule('d');
+
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c', 'd');
+ });
+ });
+
+ it('callbackReturnsPromiseThatDependsOnATask_2', function() {
+ schedule('a').then(function() {
+ schedule('b');
+ return promise.delayed(5).
+ then(function() { return promise.delayed(5) }).
+ then(function() { return promise.delayed(5) }).
+ then(function() { return promise.delayed(5) }).
+ then(function() { return schedule('c'); });
+ });
+ schedule('d');
+
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c', 'd');
+ });
+ });
+
+ it('eachCallbackWaitsForAllScheduledTasksToComplete', function() {
+ schedule('a').
+ then(function() {
+ schedule('b');
+ schedule('c');
+ }).
+ then(function() {
+ schedule('d');
+ });
+ schedule('e');
+
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c', 'd', 'e');
+ });
+ });
+
+ it('eachCallbackWaitsForReturnTasksToComplete', function() {
+ schedule('a').
+ then(function() {
+ schedule('b');
+ return schedule('c');
+ }).
+ then(function() {
+ schedule('d');
+ });
+ schedule('e');
+
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c', 'd', 'e');
+ });
+ });
+
+ it('callbacksOnAResolvedPromiseInsertIntoTheCurrentFlow', function() {
+ promise.fulfilled().then(function() {
+ schedule('b');
+ });
+ schedule('a');
+
+ return waitForIdle().then(function() {
+ assertFlowHistory('b', 'a');
+ });
+ });
+
+ it('callbacksInterruptTheFlowWhenPromiseIsResolved', function() {
+ schedule('a').then(function() {
+ schedule('c');
+ });
+ schedule('b');
+
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'c', 'b');
+ });
+ });
+
+ it('allCallbacksInAFrameAreScheduledWhenPromiseIsResolved', function() {
+ var a = schedule('a');
+ a.then(function() { schedule('b'); });
+ schedule('c');
+ a.then(function() { schedule('d'); });
+ schedule('e');
+
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c', 'd', 'e');
+ });
+ });
+
+ it('tasksScheduledInInActiveFrameDoNotGetPrecedence', function() {
+ var d = promise.fulfilled();
+ schedule('a');
+ schedule('b');
+ d.then(function() { schedule('c'); });
+
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c');
+ });
+ });
+
+ it('tasksScheduledInAFrameGetPrecedence_1', function() {
+ var a = schedule('a');
+ schedule('b').then(function() {
+ a.then(function() {
+ schedule('c');
+ schedule('d');
+ });
+ var e = schedule('e');
+ a.then(function() {
+ schedule('f');
+ e.then(function() {
+ schedule('g');
+ });
+ schedule('h');
+ });
+ schedule('i');
+ });
+ schedule('j');
+
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j');
+ });
+ });
+ });
+
+ describe('testErrorHandling', function() {
+ it('thrownErrorsArePassedToTaskErrback', function() {
+ scheduleAction('function that throws', throwStubError).
+ then(fail, assertIsStubError);
+ return waitForIdle();
+ });
+
+ it('thrownErrorsPropagateThroughPromiseChain', function() {
+ scheduleAction('function that throws', throwStubError).
+ then(fail).
+ then(fail, assertIsStubError);
+ return waitForIdle();
+ });
+
+ it('catchesErrorsFromFailedTasksInAFrame', function() {
+ schedule('a').then(function() {
+ schedule('b');
+ scheduleAction('function that throws', throwStubError);
+ }).
+ then(fail, assertIsStubError);
+ return waitForIdle();
+ });
+
+ it('abortsIfOnlyTaskReturnsAnUnhandledRejection', function() {
+ scheduleAction('function that returns rejected promise', function() {
+ return promise.rejected(new StubError);
+ });
+ return waitForAbort().then(assertIsStubError);
+ });
+
+ it('abortsIfThereIsAnUnhandledRejection', function() {
+ promise.rejected(new StubError);
+ schedule('this should not run');
+ return waitForAbort().
+ then(assertIsStubError).
+ then(function() {
+ assertFlowHistory(/* none */);
+ });
+ });
+
+ it('abortsSequenceIfATaskFails', function() {
+ schedule('a');
+ schedule('b');
+ scheduleAction('c', throwStubError);
+ schedule('d'); // Should never execute.
+
+ return waitForAbort().
+ then(assertIsStubError).
+ then(function() {
+ assertFlowHistory('a', 'b', 'c');
+ });
+ });
+
+ it('abortsFromUnhandledFramedTaskFailures_1', function() {
+ schedule('outer task').then(function() {
+ scheduleAction('inner task', throwStubError);
+ });
+ schedule('this should not run');
+ return waitForAbort().
+ then(assertIsStubError).
+ then(function() {
+ assertFlowHistory('outer task', 'inner task');
+ });
+ });
+
+ it('abortsFromUnhandledFramedTaskFailures_2', function() {
+ schedule('a').then(function() {
+ schedule('b').then(function() {
+ scheduleAction('c', throwStubError);
+ // This should not execute.
+ schedule('d');
+ });
+ });
+
+ return waitForAbort().
+ then(assertIsStubError).
+ then(function() {
+ assertFlowHistory('a', 'b', 'c');
+ });
+ });
+
+ it('abortsWhenErrorBubblesUpFromFullyResolvingAnObject', function() {
+ var callback = sinon.spy();
+
+ scheduleAction('', function() {
+ var obj = {'foo': promise.rejected(new StubError)};
+ return promise.fullyResolved(obj).then(callback);
+ });
+
+ return waitForAbort().
+ then(assertIsStubError).
+ then(() => assert(!callback.called));
+ });
+
+ it('abortsWhenErrorBubblesUpFromFullyResolvingAnObject_withCallback', function() {
+ var callback1 = sinon.spy();
+ var callback2 = sinon.spy();
+
+ scheduleAction('', function() {
+ var obj = {'foo': promise.rejected(new StubError)};
+ return promise.fullyResolved(obj).then(callback1);
+ }).then(callback2);
+
+ return waitForAbort().
+ then(assertIsStubError).
+ then(() => assert(!callback1.called)).
+ then(() => assert(!callback2.called));
+ });
+
+ it('canCatchErrorsFromNestedTasks', function() {
+ var errback = sinon.spy();
+ schedule('a').
+ then(function() {
+ return scheduleAction('b', throwStubError);
+ }).
+ catch(errback);
+ return waitForIdle().then(function() {
+ assert(errback.called);
+ assertIsStubError(errback.getCall(0).args[0]);
+ });
+ });
+
+ it('nestedCommandFailuresCanBeCaughtAndSuppressed', function() {
+ var errback = sinon.spy();
+ schedule('a').then(function() {
+ return schedule('b').then(function() {
+ return schedule('c').then(function() {
+ throw new StubError;
+ });
+ });
+ }).catch(errback);
+ schedule('d');
+ return waitForIdle().
+ then(function() {
+ assert(errback.called);
+ assertIsStubError(errback.getCall(0).args[0]);
+ assertFlowHistory('a', 'b', 'c', 'd');
+ });
+ });
+
+ it('aTaskWithAnUnhandledPromiseRejection', function() {
+ schedule('a');
+ scheduleAction('sub-tasks', function() {
+ promise.rejected(new StubError);
+ });
+ schedule('should never run');
+
+ return waitForAbort().
+ then(assertIsStubError).
+ then(function() {
+ assertFlowHistory('a', 'sub-tasks');
+ });
+ });
+
+ it('aTaskThatReutrnsARejectedPromise', function() {
+ schedule('a');
+ scheduleAction('sub-tasks', function() {
+ return promise.rejected(new StubError);
+ });
+ schedule('should never run');
+
+ return waitForAbort().
+ then(assertIsStubError).
+ then(function() {
+ assertFlowHistory('a', 'sub-tasks');
+ });
+ });
+
+ it('discardsSubtasksIfTaskThrows', function() {
+ var pair = callbackPair(null, assertIsStubError);
+ scheduleAction('a', function() {
+ schedule('b');
+ schedule('c');
+ throwStubError();
+ }).then(pair.callback, pair.errback);
+ schedule('d');
+
+ return waitForIdle().
+ then(pair.assertErrback).
+ then(function() {
+ assertFlowHistory('a', 'd');
+ });
+ });
+
+ it('discardsRemainingSubtasksIfASubtaskFails', function() {
+ var pair = callbackPair(null, assertIsStubError);
+ scheduleAction('a', function() {
+ schedule('b');
+ scheduleAction('c', throwStubError);
+ schedule('d');
+ }).then(pair.callback, pair.errback);
+ schedule('e');
+
+ return waitForIdle().
+ then(pair.assertErrback).
+ then(function() {
+ assertFlowHistory('a', 'b', 'c', 'e');
+ });
+ });
+ });
+
+ describe('testTryModelingFinally', function() {
+ it('happyPath', function() {
+ /* Model:
+ try {
+ doFoo();
+ doBar();
+ } finally {
+ doBaz();
+ }
+ */
+ schedulePush('foo').
+ then(() => schedulePush('bar')).
+ finally(() => schedulePush('baz'));
+ return waitForIdle().then(assertingMessages('foo', 'bar', 'baz'));
+ });
+
+ it('firstTryFails', function() {
+ /* Model:
+ try {
+ doFoo();
+ doBar();
+ } finally {
+ doBaz();
+ }
+ */
+
+ scheduleAction('doFoo and throw', function() {
+ messages.push('foo');
+ throw new StubError;
+ }).
+ then(function() { schedulePush('bar'); }).
+ finally(function() { schedulePush('baz'); });
+
+ return waitForAbort().
+ then(assertIsStubError).
+ then(assertingMessages('foo', 'baz'));
+ });
+
+ it('secondTryFails', function() {
+ /* Model:
+ try {
+ doFoo();
+ doBar();
+ } finally {
+ doBaz();
+ }
+ */
+
+ schedulePush('foo').
+ then(function() {
+ return scheduleAction('doBar and throw', function() {
+ messages.push('bar');
+ throw new StubError;
+ });
+ }).
+ finally(function() {
+ return schedulePush('baz');
+ });
+ return waitForAbort().
+ then(assertIsStubError).
+ then(assertingMessages('foo', 'bar', 'baz'));
+ });
+ });
+
+ describe('testTaskCallbacksInterruptFlow', function() {
+ it('(base case)', function() {
+ schedule('a').then(function() {
+ schedule('b');
+ });
+ schedule('c');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c');
+ });
+ });
+
+ it('taskDependsOnImmediatelyFulfilledPromise', function() {
+ scheduleAction('a', function() {
+ return promise.fulfilled();
+ }).then(function() {
+ schedule('b');
+ });
+ schedule('c');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c');
+ });
+ });
+
+ it('taskDependsOnPreviouslyFulfilledPromise', function() {
+ var aPromise = promise.fulfilled(123);
+ scheduleAction('a', function() {
+ return aPromise;
+ }).then(function(value) {
+ assert.equal(123, value);
+ schedule('b');
+ });
+ schedule('c');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c');
+ });
+ });
+
+ it('taskDependsOnAsyncPromise', function() {
+ scheduleAction('a', function() {
+ return promise.delayed(25);
+ }).then(function() {
+ schedule('b');
+ });
+ schedule('c');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c');
+ });
+ });
+
+ it('promiseChainedToTaskInterruptFlow', function() {
+ schedule('a').then(function() {
+ return promise.fulfilled();
+ }).then(function() {
+ return promise.fulfilled();
+ }).then(function() {
+ schedule('b');
+ });
+ schedule('c');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c');
+ });
+ });
+
+ it('nestedTaskCallbacksInterruptFlowWhenResolved', function() {
+ schedule('a').then(function() {
+ schedule('b').then(function() {
+ schedule('c');
+ });
+ });
+ schedule('d');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c', 'd');
+ });
+ });
+ });
+
+ describe('testDelayedNesting', function() {
+
+ it('1', function() {
+ var a = schedule('a');
+ schedule('b').then(function() {
+ a.then(function() { schedule('c'); });
+ schedule('d');
+ });
+ schedule('e');
+
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c', 'd', 'e');
+ });
+ });
+
+ it('2', function() {
+ var a = schedule('a');
+ schedule('b').then(function() {
+ a.then(function() { schedule('c'); });
+ schedule('d');
+ a.then(function() { schedule('e'); });
+ });
+ schedule('f');
+
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c', 'd', 'e', 'f');
+ });
+ });
+
+ it('3', function() {
+ var a = schedule('a');
+ schedule('b').then(function() {
+ a.then(function() { schedule('c'); });
+ a.then(function() { schedule('d'); });
+ });
+ schedule('e');
+
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c', 'd', 'e');
+ });
+ });
+
+ it('4', function() {
+ var a = schedule('a');
+ schedule('b').then(function() {
+ a.then(function() { schedule('c'); }).then(function() {
+ schedule('d');
+ });
+ a.then(function() { schedule('e'); });
+ });
+ schedule('f');
+
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c', 'd', 'e', 'f');
+ });
+ });
+
+ it('5', function() {
+ var a = schedule('a');
+ schedule('b').then(function() {
+ var c;
+ a.then(function() { c = schedule('c'); }).then(function() {
+ schedule('d');
+ a.then(function() { schedule('e'); });
+ c.then(function() { schedule('f'); });
+ schedule('g');
+ });
+ a.then(function() { schedule('h'); });
+ });
+ schedule('i');
+
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i');
+ });
+ });
+ });
+
+ describe('testWaiting', function() {
+ it('onAConditionThatIsAlwaysTrue', function() {
+ scheduleWait(function() { return true;}, 0, 'waiting on true');
+ return waitForIdle().then(function() {
+ assertFlowHistory('0: waiting on true');
+ });
+ });
+
+ it('aSimpleCountingCondition', function() {
+ var count = 0;
+ scheduleWait(function() {
+ return ++count == 3;
+ }, 100, 'counting to 3');
+
+ return waitForIdle().then(function() {
+ assert.equal(3, count);
+ });
+ });
+
+ it('aConditionThatReturnsAPromise', function() {
+ var d = new promise.Deferred();
+ var count = 0;
+
+ scheduleWait(function() {
+ count += 1;
+ return d.promise;
+ }, 0, 'waiting for promise');
+
+ return timeout(50).then(function() {
+ assert.equal(1, count);
+ d.fulfill(123);
+ return waitForIdle();
+ });
+ });
+
+ it('aConditionThatReturnsAPromise_2', function() {
+ var count = 0;
+ scheduleWait(function() {
+ return promise.fulfilled(++count == 3);
+ }, 100, 'waiting for promise');
+
+ return waitForIdle().then(function() {
+ assert.equal(3, count);
+ });
+ });
+
+ it('aConditionThatReturnsATaskResult', function() {
+ var count = 0;
+ scheduleWait(function() {
+ return scheduleAction('increment count', function() {
+ return ++count == 3;
+ });
+ }, 100, 'counting to 3');
+ schedule('post wait');
+
+ return waitForIdle().then(function() {
+ assert.equal(3, count);
+ assertFlowHistory(
+ '0: counting to 3', 'increment count',
+ '1: counting to 3', 'increment count',
+ '2: counting to 3', 'increment count',
+ 'post wait');
+ });
+ });
+
+ it('conditionContainsASubtask', function() {
+ var count = 0;
+ scheduleWait(function() {
+ schedule('sub task');
+ return ++count == 3;
+ }, 100, 'counting to 3');
+ schedule('post wait');
+
+ return waitForIdle().then(function() {
+ assert.equal(3, count);
+ assertFlowHistory(
+ '0: counting to 3', 'sub task',
+ '1: counting to 3', 'sub task',
+ '2: counting to 3', 'sub task',
+ 'post wait');
+ });
+ });
+
+ it('cancelsWaitIfScheduledTaskFails', function() {
+ var pair = callbackPair(null, assertIsStubError);
+ scheduleWait(function() {
+ scheduleAction('boom', throwStubError);
+ schedule('this should not run');
+ return true;
+ }, 100, 'waiting to go boom').then(pair.callback, pair.errback);
+ schedule('post wait');
+
+ return waitForIdle().
+ then(pair.assertErrback).
+ then(function() {
+ assertFlowHistory(
+ '0: waiting to go boom', 'boom',
+ 'post wait');
+ });
+ });
+
+ it('failsIfConditionThrows', function() {
+ var callbacks = callbackPair(null, assertIsStubError);
+ scheduleWait(throwStubError, 0, 'goes boom').
+ then(callbacks.callback, callbacks.errback);
+ schedule('post wait');
+
+ return waitForIdle().
+ then(callbacks.assertErrback).
+ then(function() {
+ assertFlowHistory('0: goes boom', 'post wait');
+ });
+ });
+
+ it('failsIfConditionReturnsARejectedPromise', function() {
+ var callbacks = callbackPair(null, assertIsStubError);
+ scheduleWait(function() {
+ return promise.rejected(new StubError);
+ }, 0, 'goes boom').then(callbacks.callback, callbacks.errback);
+ schedule('post wait');
+
+ return waitForIdle().
+ then(callbacks.assertErrback).
+ then(function() {
+ assertFlowHistory('0: goes boom', 'post wait');
+ });
+ });
+
+ it('failsIfConditionHasUnhandledRejection', function() {
+ var callbacks = callbackPair(null, assertIsStubError);
+ scheduleWait(function() {
+ promise.controlFlow().execute(throwStubError);
+ }, 0, 'goes boom').then(callbacks.callback, callbacks.errback);
+ schedule('post wait');
+
+ return waitForIdle().
+ then(callbacks.assertErrback).
+ then(function() {
+ assertFlowHistory('0: goes boom', 'post wait');
+ });
+ });
+
+ it('failsIfConditionHasAFailedSubtask', function() {
+ var callbacks = callbackPair(null, assertIsStubError);
+ var count = 0;
+ scheduleWait(function() {
+ scheduleAction('maybe throw', function() {
+ if (++count == 2) {
+ throw new StubError;
+ }
+ });
+ }, 100, 'waiting').then(callbacks.callback, callbacks.errback);
+ schedule('post wait');
+
+ return waitForIdle().then(function() {
+ assert.equal(2, count);
+ assertFlowHistory(
+ '0: waiting', 'maybe throw',
+ '1: waiting', 'maybe throw',
+ 'post wait');
+ });
+ });
+
+ it('pollingLoopWaitsForAllScheduledTasksInCondition', function() {
+ var count = 0;
+ scheduleWait(function() {
+ scheduleAction('increment count', function() { ++count; });
+ return count >= 3;
+ }, 100, 'counting to 3');
+ schedule('post wait');
+
+ return waitForIdle().then(function() {
+ assert.equal(4, count);
+ assertFlowHistory(
+ '0: counting to 3', 'increment count',
+ '1: counting to 3', 'increment count',
+ '2: counting to 3', 'increment count',
+ '3: counting to 3', 'increment count',
+ 'post wait');
+ });
+ });
+
+ it('waitsForeverOnAZeroTimeout', function() {
+ var done = false;
+ setTimeout(function() {
+ done = true;
+ }, 150);
+ var waitResult = scheduleWait(function() {
+ return done;
+ }, 0);
+
+ return timeout(75).then(function() {
+ assert.ok(!done);
+ return timeout(100);
+ }).then(function() {
+ assert.ok(done);
+ return waitResult;
+ });
+ });
+
+ it('waitsForeverIfTimeoutOmitted', function() {
+ var done = false;
+ setTimeout(function() {
+ done = true;
+ }, 150);
+ var waitResult = scheduleWait(function() {
+ return done;
+ });
+
+ return timeout(75).then(function() {
+ assert.ok(!done);
+ return timeout(100);
+ }).then(function() {
+ assert.ok(done);
+ return waitResult;
+ });
+ });
+
+ it('timesOut_nonZeroTimeout', function() {
+ var count = 0;
+ scheduleWait(function() {
+ count += 1;
+ var ms = count === 2 ? 65 : 5;
+ return promise.delayed(ms).then(function() {
+ return false;
+ });
+ }, 60, 'counting to 3');
+ return waitForAbort().then(function(e) {
+ switch (count) {
+ case 1:
+ assertFlowHistory('0: counting to 3');
+ break;
+ case 2:
+ assertFlowHistory('0: counting to 3', '1: counting to 3');
+ break;
+ default:
+ fail('unexpected polling count: ' + count);
+ }
+ assert.ok(
+ /^counting to 3\nWait timed out after \d+ms$/.test(e.message));
+ });
+ });
+
+ it('shouldFailIfConditionReturnsARejectedPromise', function() {
+ scheduleWait(function() {
+ return promise.rejected(new StubError);
+ }, 100, 'returns rejected promise on first pass');
+ return waitForAbort().then(assertIsStubError);
+ });
+
+ it('scheduleWithIntermittentWaits', function() {
+ schedule('a');
+ scheduleWait(function() { return true; }, 0, 'wait 1');
+ schedule('b');
+ scheduleWait(function() { return true; }, 0, 'wait 2');
+ schedule('c');
+ scheduleWait(function() { return true; }, 0, 'wait 3');
+
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', '0: wait 1', 'b', '0: wait 2', 'c', '0: wait 3');
+ });
+ });
+
+ it('scheduleWithIntermittentAndNestedWaits', function() {
+ schedule('a');
+ scheduleWait(function() { return true; }, 0, 'wait 1').
+ then(function() {
+ schedule('d');
+ scheduleWait(function() { return true; }, 0, 'wait 2');
+ schedule('e');
+ });
+ schedule('b');
+ scheduleWait(function() { return true; }, 0, 'wait 3');
+ schedule('c');
+ scheduleWait(function() { return true; }, 0, 'wait 4');
+
+ return waitForIdle().then(function() {
+ assertFlowHistory(
+ 'a', '0: wait 1', 'd', '0: wait 2', 'e', 'b', '0: wait 3', 'c',
+ '0: wait 4');
+ });
+ });
+
+ it('requiresConditionToBeAPromiseOrFunction', function() {
+ assert.throws(function() {
+ flow.wait(1234, 0);
+ });
+ flow.wait(function() { return true;}, 0);
+ flow.wait(promise.fulfilled(), 0);
+ return waitForIdle();
+ });
+
+ it('promiseThatDoesNotResolveBeforeTimeout', function() {
+ var d = promise.defer();
+ flow.wait(d.promise, 5).then(fail, function(e) {
+ assert.ok(
+ /Timed out waiting for promise to resolve after \d+ms/
+ .test(e.message),
+ 'unexpected error message: ' + e.message);
+ });
+ return waitForIdle().then(function() {
+ assert.ok('Promise should not be cancelled', d.promise.isPending());
+ });
+ });
+
+ it('unboundedWaitOnPromiseResolution', function() {
+ var messages = [];
+ var d = promise.defer();
+ var waitResult = flow.wait(d.promise).then(function(value) {
+ messages.push('b');
+ assert.equal(1234, value);
+ });
+ setTimeout(function() {
+ messages.push('a');
+ }, 5);
+
+ timeout(10).then(function() {
+ assert.deepEqual(['a'], messages);
+ assert.ok(waitResult.isPending());
+ d.fulfill(1234);
+ return waitResult;
+ }).then(function() {
+ assert.deepEqual(['a', 'b'], messages);
+ });
+
+ return waitForIdle();
+ });
+ });
+
+ describe('testSubtasks', function() {
+ it('(base case)', function() {
+ schedule('a');
+ scheduleAction('sub-tasks', function() {
+ schedule('c');
+ schedule('d');
+ });
+ schedule('b');
+
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'sub-tasks', 'c', 'd', 'b');
+ });
+ });
+
+ it('nesting', function() {
+ schedule('a');
+ scheduleAction('sub-tasks', function() {
+ schedule('b');
+ scheduleAction('sub-sub-tasks', function() {
+ schedule('c');
+ schedule('d');
+ });
+ schedule('e');
+ });
+ schedule('f');
+
+ return waitForIdle().then(function() {
+ assertFlowHistory(
+ 'a', 'sub-tasks', 'b', 'sub-sub-tasks', 'c', 'd', 'e', 'f');
+ });
+ });
+
+ it('taskReturnsSubTaskResult_1', function() {
+ schedule('a');
+ scheduleAction('sub-tasks', function() {
+ return schedule('c');
+ });
+ schedule('b');
+
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'sub-tasks', 'c', 'b');
+ });
+ });
+
+ it('taskReturnsSubTaskResult_2', function() {
+ let pair = callbackPair((value) => assert.equal(123, value));
+ schedule('a');
+ schedule('sub-tasks', promise.fulfilled(123)).then(pair.callback);
+ schedule('b');
+
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'sub-tasks','b');
+ pair.assertCallback();
+ });
+ });
+
+ it('taskReturnsPromiseThatDependsOnSubtask_1', function() {
+ scheduleAction('a', function() {
+ return promise.delayed(10).then(function() {
+ schedule('b');
+ });
+ });
+ schedule('c');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c');
+ });
+ });
+
+ it('taskReturnsPromiseThatDependsOnSubtask_2', function() {
+ scheduleAction('a', function() {
+ return promise.fulfilled().then(function() {
+ schedule('b');
+ });
+ });
+ schedule('c');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c');
+ });
+ });
+
+ it('taskReturnsPromiseThatDependsOnSubtask_3', function() {
+ scheduleAction('a', function() {
+ return promise.delayed(10).then(function() {
+ return schedule('b');
+ });
+ });
+ schedule('c');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c');
+ });
+ });
+
+ it('taskReturnsPromiseThatDependsOnSubtask_4', function() {
+ scheduleAction('a', function() {
+ return promise.delayed(5).then(function() {
+ return promise.delayed(5).then(function() {
+ return schedule('b');
+ });
+ });
+ });
+ schedule('c');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c');
+ });
+ });
+
+ it('taskReturnsPromiseThatDependsOnSubtask_5', function() {
+ scheduleAction('a', function() {
+ return promise.delayed(5).then(function() {
+ return promise.delayed(5).then(function() {
+ return promise.delayed(5).then(function() {
+ return promise.delayed(5).then(function() {
+ return schedule('b');
+ });
+ });
+ });
+ });
+ });
+ schedule('c');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c');
+ });
+ });
+
+ it('taskReturnsPromiseThatDependsOnSubtask_6', function() {
+ scheduleAction('a', function() {
+ return promise.delayed(5).
+ then(function() { return promise.delayed(5) }).
+ then(function() { return promise.delayed(5) }).
+ then(function() { return promise.delayed(5) }).
+ then(function() { return schedule('b'); });
+ });
+ schedule('c');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c');
+ });
+ });
+
+ it('subTaskFails_1', function() {
+ schedule('a');
+ scheduleAction('sub-tasks', function() {
+ scheduleAction('sub-task that fails', throwStubError);
+ });
+ schedule('should never execute');
+
+ return waitForAbort().
+ then(assertIsStubError).
+ then(function() {
+ assertFlowHistory('a', 'sub-tasks', 'sub-task that fails');
+ });
+ });
+
+ it('subTaskFails_2', function() {
+ schedule('a');
+ scheduleAction('sub-tasks', function() {
+ return promise.rejected(new StubError);
+ });
+ schedule('should never execute');
+
+ return waitForAbort().
+ then(assertIsStubError).
+ then(function() {
+ assertFlowHistory('a', 'sub-tasks');
+ });
+ });
+
+ it('subTaskFails_3', function() {
+ var callbacks = callbackPair(null, assertIsStubError);
+
+ schedule('a');
+ scheduleAction('sub-tasks', function() {
+ return promise.rejected(new StubError);
+ }).then(callbacks.callback, callbacks.errback);
+ schedule('b');
+
+ return waitForIdle().
+ then(function() {
+ assertFlowHistory('a', 'sub-tasks', 'b');
+ callbacks.assertErrback();
+ });
+ });
+ });
+
+ describe('testEventLoopWaitsOnPendingPromiseRejections', function() {
+ it('oneRejection', function() {
+ var d = new promise.Deferred;
+ scheduleAction('one', function() {
+ return d.promise;
+ });
+ scheduleAction('two', function() {});
+
+ return timeout(50).then(function() {
+ assertFlowHistory('one');
+ d.reject(new StubError);
+ return waitForAbort();
+ }).
+ then(assertIsStubError).
+ then(function() {
+ assertFlowHistory('one');
+ });
+ });
+
+ it('multipleRejections', function() {
+ var once = Error('once');
+ var twice = Error('twice');
+
+ scheduleAction('one', function() {
+ promise.rejected(once);
+ promise.rejected(twice);
+ });
+ var twoResult = scheduleAction('two', function() {});
+
+ flow.removeAllListeners(
+ promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION);
+ return new NativePromise(function(fulfill, reject) {
+ setTimeout(function() {
+ reject(Error('Should have reported the two errors by now'));
+ }, 50);
+ flow.on(
+ promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION,
+ fulfill);
+ }).then(function(e) {
+ assert.ok(e instanceof promise.MultipleUnhandledRejectionError,
+ 'Not a MultipleUnhandledRejectionError');
+ let errors = Array.from(e.errors);
+ assert.deepEqual([once, twice], errors);
+ assertFlowHistory('one');
+ assert.ok(!twoResult.isPending(), 'Did not cancel the second task');
+ });
+ });
+ });
+
+ describe('testCancelsPromiseReturnedByCallbackIfFrameFails', function() {
+ it('promiseCallback', function() {
+ var chainPair = callbackPair(null, assertIsStubError);
+ var deferredPair = callbackPair(null, function(e) {
+ assert.equal('CancellationError: StubError', e.toString(),
+ 'callback result should be cancelled');
+ });
+
+ var d = new promise.Deferred();
+ d.promise.then(deferredPair.callback, deferredPair.errback);
+
+ promise.fulfilled().
+ then(function() {
+ scheduleAction('boom', throwStubError);
+ schedule('this should not run');
+ return d.promise;
+ }).
+ then(chainPair.callback, chainPair.errback);
+
+ return waitForIdle().then(function() {
+ assertFlowHistory('boom');
+ chainPair.assertErrback('chain errback not invoked');
+ deferredPair.assertErrback('deferred errback not invoked');
+ });
+ });
+
+ it('taskCallback', function() {
+ var chainPair = callbackPair(null, assertIsStubError);
+ var deferredPair = callbackPair(null, function(e) {
+ assert.equal('CancellationError: StubError', e.toString(),
+ 'callback result should be cancelled');
+ });
+
+ var d = new promise.Deferred();
+ d.promise.then(deferredPair.callback, deferredPair.errback);
+
+ schedule('a').
+ then(function() {
+ scheduleAction('boom', throwStubError);
+ schedule('this should not run');
+ return d.promise;
+ }).
+ then(chainPair.callback, chainPair.errback);
+
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'boom');
+ chainPair.assertErrback('chain errback not invoked');
+ deferredPair.assertErrback('deferred errback not invoked');
+ });
+ });
+ });
+
+ it('testMaintainsOrderInCallbacksWhenATaskReturnsAPromise', function() {
+ schedule('__start__', promise.fulfilled()).
+ then(function() {
+ messages.push('a');
+ schedulePush('b');
+ messages.push('c');
+ }).
+ then(function() {
+ messages.push('d');
+ });
+ schedulePush('e');
+
+ return waitForIdle().then(function() {
+ assertFlowHistory('__start__', 'b', 'e');
+ assertMessages('a', 'c', 'b', 'd', 'e');
+ });
+ });
+
+ it('testOwningFlowIsActivatedForExecutingTasks', function() {
+ var defaultFlow = promise.controlFlow();
+ var order = [];
+
+ promise.createFlow(function(flow) {
+ assertFlowIs(flow);
+ order.push(0);
+
+ defaultFlow.execute(function() {
+ assertFlowIs(defaultFlow);
+ order.push(1);
+ });
+ });
+
+ return waitForIdle().then(function() {
+ assertFlowIs(defaultFlow);
+ assert.deepEqual([0, 1], order);
+ });
+ });
+
+ it('testCreateFlowReturnsPromisePairedWithCreatedFlow', function() {
+ return new NativePromise(function(fulfill, reject) {
+ var newFlow;
+ promise.createFlow(function(flow) {
+ newFlow = flow;
+ assertFlowIs(newFlow);
+ }).then(function() {
+ assertFlowIs(newFlow);
+ waitForIdle(newFlow).then(fulfill, reject);
+ });
+ });
+ });
+
+ it('testDeferredFactoriesCreateForActiveFlow_defaultFlow', function() {
+ var e = Error();
+ var defaultFlow = promise.controlFlow();
+ promise.fulfilled().then(function() {
+ assertFlowIs(defaultFlow);
+ });
+ promise.rejected(e).then(null, function(err) {
+ assert.equal(e, err);
+ assertFlowIs(defaultFlow);
+ });
+ promise.defer().promise.then(function() {
+ assertFlowIs(defaultFlow);
+ });
+
+ return waitForIdle();
+ });
+
+ it('testDeferredFactoriesCreateForActiveFlow_newFlow', function() {
+ var e = Error();
+ var newFlow = new promise.ControlFlow;
+ newFlow.execute(function() {
+ promise.fulfilled().then(function() {
+ assertFlowIs(newFlow);
+ });
+
+ promise.rejected(e).then(null, function(err) {
+ assert.equal(e, err);
+ assertFlowIs(newFlow);
+ });
+
+ let d = promise.defer();
+ d.promise.then(function() {
+ assertFlowIs(newFlow);
+ });
+ d.fulfill();
+ }).then(function() {
+ assertFlowIs(newFlow);
+ });
+
+ return waitForIdle(newFlow);
+ });
+
+ it('testFlowsSynchronizeWithThemselvesNotEachOther', function() {
+ var defaultFlow = promise.controlFlow();
+ schedulePush('a', 'a');
+ promise.controlFlow().timeout(250);
+ schedulePush('b', 'b');
+
+ promise.createFlow(function() {
+ schedulePush('c', 'c');
+ schedulePush('d', 'd');
+ });
+
+ return waitForIdle().then(function() {
+ assertMessages('a', 'c', 'd', 'b');
+ });
+ });
+
+ it('testUnhandledErrorsAreReportedToTheOwningFlow', function() {
+ var error1 = Error('e1');
+ var error2 = Error('e2');
+
+ var defaultFlow = promise.controlFlow();
+ defaultFlow.removeAllListeners('uncaughtException');
+
+ var flow1Error = NativePromise.defer();
+ flow1Error.promise.then(function(value) {
+ assert.equal(error2, value);
+ });
+
+ var flow2Error = NativePromise.defer();
+ flow2Error.promise.then(function(value) {
+ assert.equal(error1, value);
+ });
+
+ promise.createFlow(function(flow) {
+ flow.once('uncaughtException', flow2Error.resolve);
+ promise.rejected(error1);
+
+ defaultFlow.once('uncaughtException', flow1Error.resolve);
+ defaultFlow.execute(function() {
+ promise.rejected(error2);
+ });
+ });
+
+ return NativePromise.all([flow1Error.promise, flow2Error.promise]);
+ });
+
+ it('testCanSynchronizeFlowsByReturningPromiseFromOneToAnother', function() {
+ var flow1 = new promise.ControlFlow;
+ var flow1Done = NativePromise.defer();
+ flow1.once('idle', flow1Done.resolve);
+ flow1.once('uncaughtException', flow1Done.reject);
+
+ var flow2 = new promise.ControlFlow;
+ var flow2Done = NativePromise.defer();
+ flow2.once('idle', flow2Done.resolve);
+ flow2.once('uncaughtException', flow2Done.reject);
+
+ flow1.execute(function() {
+ schedulePush('a', 'a');
+ return promise.delayed(25);
+ }, 'start flow 1');
+
+ flow2.execute(function() {
+ schedulePush('b', 'b');
+ schedulePush('c', 'c');
+ flow2.execute(function() {
+ return flow1.execute(function() {
+ schedulePush('d', 'd');
+ }, 'flow 1 task');
+ }, 'inject flow1 result into flow2');
+ schedulePush('e', 'e');
+ }, 'start flow 2');
+
+ return NativePromise.all([flow1Done.promise, flow2Done.promise]).
+ then(function() {
+ assertMessages('a', 'b', 'c', 'd', 'e');
+ });
+ });
+
+ it('testFramesWaitToCompleteForPendingRejections', function() {
+ return new NativePromise(function(fulfill, reject) {
+
+ promise.controlFlow().execute(function() {
+ promise.rejected(new StubError);
+ }).then(fulfill, reject);
+
+ }).
+ then(() => fail('expected to fail'), assertIsStubError);
+ });
+
+ it('testSynchronizeErrorsPropagateToOuterFlow', function() {
+ var outerFlow = new promise.ControlFlow;
+ var innerFlow = new promise.ControlFlow;
+
+ var block = NativePromise.defer();
+ innerFlow.execute(function() {
+ return block.promise;
+ }, 'block inner flow');
+
+ outerFlow.execute(function() {
+ block.resolve();
+ return innerFlow.execute(function() {
+ promise.rejected(new StubError);
+ }, 'trigger unhandled rejection error');
+ }, 'run test');
+
+ return NativePromise.all([
+ waitForIdle(innerFlow),
+ waitForAbort(outerFlow).then(assertIsStubError)
+ ]);
+ });
+
+ it('testFailsIfErrbackThrows', function() {
+ promise.rejected('').then(null, throwStubError);
+ return waitForAbort().then(assertIsStubError);
+ });
+
+ it('testFailsIfCallbackReturnsRejectedPromise', function() {
+ promise.fulfilled().then(function() {
+ return promise.rejected(new StubError);
+ });
+ return waitForAbort().then(assertIsStubError);
+ });
+
+ it('testAbortsFrameIfTaskFails', function() {
+ promise.fulfilled().then(function() {
+ promise.controlFlow().execute(throwStubError);
+ });
+ return waitForAbort().then(assertIsStubError);
+ });
+
+ it('testAbortsFramePromisedChainedFromTaskIsNotHandled', function() {
+ promise.fulfilled().then(function() {
+ promise.controlFlow().execute(function() {}).
+ then(throwStubError);
+ });
+ return waitForAbort().then(assertIsStubError);
+ });
+
+ it('testTrapsChainedUnhandledRejectionsWithinAFrame', function() {
+ var pair = callbackPair(null, assertIsStubError);
+ promise.fulfilled().then(function() {
+ promise.controlFlow().execute(function() {}).
+ then(throwStubError);
+ }).then(pair.callback, pair.errback);
+
+ return waitForIdle().then(pair.assertErrback);
+ });
+
+ it('testCancelsRemainingTasksIfFrameThrowsDuringScheduling', function() {
+ var task1, task2;
+ var pair = callbackPair(null, assertIsStubError);
+ var flow = promise.controlFlow();
+ flow.execute(function() {
+ task1 = flow.execute(function() {});
+ task2 = flow.execute(function() {});
+ throw new StubError;
+ }).then(pair.callback, pair.errback);
+
+ return waitForIdle().
+ then(pair.assertErrback).
+ then(function() {
+ assert.ok(!task1.isPending());
+ pair = callbackPair();
+ return task1.then(pair.callback, pair.errback);
+ }).
+ then(function() {
+ pair.assertErrback();
+ assert.ok(!task2.isPending());
+ pair = callbackPair();
+ return task2.then(pair.callback, pair.errback);
+ }).
+ then(function() {
+ pair.assertErrback();
+ });
+ });
+
+ it('testCancelsRemainingTasksInFrameIfATaskFails', function() {
+ var task;
+ var pair = callbackPair(null, assertIsStubError);
+ var flow = promise.controlFlow();
+ flow.execute(function() {
+ flow.execute(throwStubError);
+ task = flow.execute(function() {});
+ }).then(pair.callback, pair.errback);
+
+ return waitForIdle().then(pair.assertErrback).then(function() {
+ assert.ok(!task.isPending());
+ pair = callbackPair();
+ task.then(pair.callback, pair.errback);
+ }).then(function() {
+ pair.assertErrback();
+ });
+ });
+
+ it('testDoesNotModifyRejectionErrorIfPromiseNotInsideAFlow', function() {
+ var error = Error('original message');
+ var originalStack = error.stack;
+ var originalStr = error.toString();
+
+ var pair = callbackPair(null, function(e) {
+ assert.equal(error, e);
+ assert.equal('original message', e.message);
+ assert.equal(originalStack, e.stack);
+ assert.equal(originalStr, e.toString());
+ });
+
+ promise.rejected(error).then(pair.callback, pair.errback);
+ return waitForIdle().then(pair.assertErrback);
+ });
+
+ /** See https://github.com/SeleniumHQ/selenium/issues/444 */
+ it('testMaintainsOrderWithPromiseChainsCreatedWithinAForeach_1', function() {
+ var messages = [];
+ flow.execute(function() {
+ return promise.fulfilled(['a', 'b', 'c', 'd']);
+ }, 'start').then(function(steps) {
+ steps.forEach(function(step) {
+ promise.fulfilled(step)
+ .then(function() {
+ messages.push(step + '.1');
+ }).then(function() {
+ messages.push(step + '.2');
+ });
+ })
+ });
+ return waitForIdle().then(function() {
+ assert.deepEqual(
+ ['a.1', 'a.2', 'b.1', 'b.2', 'c.1', 'c.2', 'd.1', 'd.2'],
+ messages);
+ });
+ });
+
+ /** See https://github.com/SeleniumHQ/selenium/issues/444 */
+ it('testMaintainsOrderWithPromiseChainsCreatedWithinAForeach_2', function() {
+ var messages = [];
+ flow.execute(function() {
+ return promise.fulfilled(['a', 'b', 'c', 'd']);
+ }, 'start').then(function(steps) {
+ steps.forEach(function(step) {
+ promise.fulfilled(step)
+ .then(function() {
+ messages.push(step + '.1');
+ }).then(function() {
+ flow.execute(function() {}, step + '.2').then(function() {
+ messages.push(step + '.2');
+ });
+ });
+ })
+ });
+ return waitForIdle().then(function() {
+ assert.deepEqual(
+ ['a.1', 'a.2', 'b.1', 'b.2', 'c.1', 'c.2', 'd.1', 'd.2'],
+ messages);
+ });
+ });
+
+ /** See https://github.com/SeleniumHQ/selenium/issues/444 */
+ it('testMaintainsOrderWithPromiseChainsCreatedWithinAForeach_3', function() {
+ var messages = [];
+ flow.execute(function() {
+ return promise.fulfilled(['a', 'b', 'c', 'd']);
+ }, 'start').then(function(steps) {
+ steps.forEach(function(step) {
+ promise.fulfilled(step)
+ .then(function(){})
+ .then(function() {
+ messages.push(step + '.1');
+ return flow.execute(function() {}, step + '.1');
+ }).then(function() {
+ flow.execute(function() {}, step + '.2').then(function(text) {
+ messages.push(step + '.2');
+ });
+ });
+ })
+ });
+ return waitForIdle().then(function() {
+ assert.deepEqual(
+ ['a.1', 'a.2', 'b.1', 'b.2', 'c.1', 'c.2', 'd.1', 'd.2'],
+ messages);
+ });
+ });
+
+ /** See https://github.com/SeleniumHQ/selenium/issues/363 */
+ it('testTasksScheduledInASeparateTurnOfTheEventLoopGetASeparateTaskQueue_2', function() {
+ scheduleAction('a', () => promise.delayed(10));
+ schedule('b');
+ setTimeout(() => schedule('c'), 0);
+
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'c', 'b');
+ });
+ });
+
+ /** See https://github.com/SeleniumHQ/selenium/issues/363 */
+ it('testTasksScheduledInASeparateTurnOfTheEventLoopGetASeparateTaskQueue_2', function() {
+ scheduleAction('a', () => promise.delayed(10));
+ schedule('b');
+ schedule('c');
+ setTimeout(function() {
+ schedule('d');
+ scheduleAction('e', () => promise.delayed(10));
+ schedule('f');
+ }, 0);
+
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'd', 'e', 'b', 'c', 'f');
+ });
+ });
+
+ /** See https://github.com/SeleniumHQ/selenium/issues/363 */
+ it('testCanSynchronizeTasksFromAdjacentTaskQueues', function() {
+ var task1 = scheduleAction('a', () => promise.delayed(10));
+ schedule('b');
+ setTimeout(function() {
+ scheduleAction('c', () => task1);
+ schedule('d');
+ }, 0);
+
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'c', 'd', 'b');
+ });
+ });
+
+ describe('testCancellingAScheduledTask', function() {
+ it('1', function() {
+ var called = false;
+ var task1 = scheduleAction('a', () => called = true);
+ task1.cancel('no soup for you');
+
+ return waitForIdle().then(function() {
+ assert.ok(!called);
+ assertFlowHistory();
+ return task1.catch(function(e) {
+ assert.ok(e instanceof promise.CancellationError);
+ assert.equal('no soup for you', e.message);
+ });
+ });
+ });
+
+ it('2', function() {
+ schedule('a');
+ var called = false;
+ var task2 = scheduleAction('b', () => called = true);
+ schedule('c');
+
+ task2.cancel('no soup for you');
+
+ return waitForIdle().then(function() {
+ assert.ok(!called);
+ assertFlowHistory('a', 'c');
+ return task2.catch(function(e) {
+ assert.ok(e instanceof promise.CancellationError);
+ assert.equal('no soup for you', e.message);
+ });
+ });
+ });
+
+ it('3', function() {
+ var called = false;
+ var task = scheduleAction('a', () => called = true);
+ task.cancel(new StubError);
+
+ return waitForIdle().then(function() {
+ assert.ok(!called);
+ assertFlowHistory();
+ return task.catch(function(e) {
+ assert.ok(e instanceof promise.CancellationError);
+ });
+ });
+ });
+
+ it('4', function() {
+ var seen = [];
+ var task = scheduleAction('a', () => seen.push(1))
+ .then(() => seen.push(2))
+ .then(() => seen.push(3))
+ .then(() => seen.push(4))
+ .then(() => seen.push(5));
+ task.cancel(new StubError);
+
+ return waitForIdle().then(function() {
+ assert.deepEqual([], seen);
+ assertFlowHistory();
+ return task.catch(function(e) {
+ assert.ok(e instanceof promise.CancellationError);
+ });
+ });
+ });
+
+ it('fromWithinAnExecutingTask', function() {
+ var called = false;
+ var task;
+ scheduleAction('a', function() {
+ task.cancel('no soup for you');
+ });
+ task = scheduleAction('b', () => called = true);
+ schedule('c');
+
+ return waitForIdle().then(function() {
+ assert.ok(!called);
+ assertFlowHistory('a', 'c');
+ return task.catch(function(e) {
+ assert.ok(e instanceof promise.CancellationError);
+ assert.equal('no soup for you', e.message);
+ });
+ });
+ });
+ });
+
+ it('testCancellingAPendingTask', function() {
+ var order = [];
+ var unresolved = promise.defer();
+
+ var innerTask;
+ var outerTask = scheduleAction('a', function() {
+ order.push(1);
+
+ // Schedule a task that will never finish.
+ innerTask = scheduleAction('a.1', function() {
+ return unresolved.promise;
+ });
+
+ // Since the outerTask is cancelled below, innerTask should be cancelled
+ // with a DiscardedTaskError, which means its callbacks are silently
+ // dropped - so this should never execute.
+ innerTask.catch(function(e) {
+ order.push(2);
+ });
+ });
+ schedule('b');
+
+ outerTask.catch(function(e) {
+ order.push(3);
+ assert.ok(e instanceof promise.CancellationError);
+ assert.equal('no soup for you', e.message);
+ });
+
+ unresolved.promise.catch(function(e) {
+ order.push(4);
+ assert.ok(e instanceof promise.CancellationError);
+ });
+
+ return timeout(10).then(function() {
+ assert.deepEqual([1], order);
+ assert.ok(unresolved.promise.isPending());
+
+ outerTask.cancel('no soup for you');
+ return waitForIdle();
+ }).then(function() {
+ assertFlowHistory('a', 'a.1', 'b');
+ assert.deepEqual([1, 3, 4], order);
+ });
+ });
+
+ it('testCancellingAPendingPromiseCallback', function() {
+ var called = false;
+
+ var root = promise.fulfilled();
+ root.then(function() {
+ cb2.cancel('no soup for you');
+ });
+
+ var cb2 = root.then(fail, fail); // These callbacks should never be called.
+ cb2.then(fail, function(e) {
+ called = true;
+ assert.ok(e instanceof promise.CancellationError);
+ assert.equal('no soup for you', e.message);
+ });
+
+ return waitForIdle().then(function() {
+ assert.ok(called);
+ });
+ });
+
+ describe('testResetFlow', function() {
+ it('1', function() {
+ var called = 0;
+ var task = flow.execute(() => called++);
+ task.finally(() => called++);
+
+ return new Promise(function(fulfill) {
+ flow.once('reset', fulfill);
+ flow.reset();
+
+ }).then(function() {
+ assert.equal(0, called);
+ assert.ok(!task.isPending());
+ return task;
+
+ }).then(fail, function(e) {
+ assert.ok(e instanceof promise.CancellationError);
+ assert.equal('ControlFlow was reset', e.message);
+ });
+ });
+
+ it('2', function() {
+ var called = 0;
+ var task1 = flow.execute(() => called++);
+ task1.finally(() => called++);
+
+ var task2 = flow.execute(() => called++);
+ task2.finally(() => called++);
+
+ var task3 = flow.execute(() => called++);
+ task3.finally(() => called++);
+
+ return new Promise(function(fulfill) {
+ flow.once('reset', fulfill);
+ flow.reset();
+
+ }).then(function() {
+ assert.equal(0, called);
+ assert.ok(!task1.isPending());
+ assert.ok(!task2.isPending());
+ assert.ok(!task3.isPending());
+ });
+ });
+ });
+
+ describe('testPromiseFulfilledInsideTask', function() {
+ it('1', function() {
+ var order = [];
+
+ flow.execute(function() {
+ var d = promise.defer();
+
+ d.promise.then(() => order.push('a'));
+ d.promise.then(() => order.push('b'));
+ d.promise.then(() => order.push('c'));
+ d.fulfill();
+
+ flow.execute(() => order.push('d'));
+
+ }).then(() => order.push('fin'));
+
+ return waitForIdle().then(function() {
+ assert.deepEqual(['a', 'b', 'c', 'd', 'fin'], order);
+ });
+ });
+
+ it('2', function() {
+ var order = [];
+
+ flow.execute(function() {
+ flow.execute(() => order.push('a'));
+ flow.execute(() => order.push('b'));
+
+ var d = promise.defer();
+ d.promise.then(() => order.push('c'));
+ d.promise.then(() => order.push('d'));
+ d.fulfill();
+
+ flow.execute(() => order.push('e'));
+
+ }).then(() => order.push('fin'));
+
+ return waitForIdle().then(function() {
+ assert.deepEqual(['a', 'b', 'c', 'd', 'e', 'fin'], order);
+ });
+ });
+
+ it('3', function() {
+ var order = [];
+ var d = promise.defer();
+ d.promise.then(() => order.push('c'));
+ d.promise.then(() => order.push('d'));
+
+ flow.execute(function() {
+ flow.execute(() => order.push('a'));
+ flow.execute(() => order.push('b'));
+
+ d.promise.then(() => order.push('e'));
+ d.fulfill();
+
+ flow.execute(() => order.push('f'));
+
+ }).then(() => order.push('fin'));
+
+ return waitForIdle().then(function() {
+ assert.deepEqual(['c', 'd', 'a', 'b', 'e', 'f', 'fin'], order);
+ });
+ });
+
+ it('4', function() {
+ var order = [];
+ var d = promise.defer();
+ d.promise.then(() => order.push('a'));
+ d.promise.then(() => order.push('b'));
+
+ flow.execute(function() {
+ flow.execute(function() {
+ order.push('c');
+ flow.execute(() => order.push('d'));
+ d.promise.then(() => order.push('e'));
+ });
+ flow.execute(() => order.push('f'));
+
+ d.promise.then(() => order.push('g'));
+ d.fulfill();
+
+ flow.execute(() => order.push('h'));
+
+ }).then(() => order.push('fin'));
+
+ return waitForIdle().then(function() {
+ assert.deepEqual(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'fin'], order);
+ });
+ });
+ });
+
+ describe('testSettledPromiseCallbacksInsideATask', function() {
+ it('1', function() {
+ var order = [];
+ var p = promise.fulfilled();
+
+ flow.execute(function() {
+ flow.execute(() => order.push('a'));
+ p.then(() => order.push('b'));
+ flow.execute(() => order.push('c'));
+ p.then(() => order.push('d'));
+ }).then(() => order.push('fin'));
+
+ return waitForIdle().then(function() {
+ assert.deepEqual(['a', 'b', 'c', 'd', 'fin'], order);
+ });
+ });
+
+ it('2', function() {
+ var order = [];
+
+ flow.execute(function() {
+ flow.execute(() => order.push('a'))
+ .then( () => order.push('c'));
+ flow.execute(() => order.push('b'));
+ }).then(() => order.push('fin'));
+
+ return waitForIdle().then(function() {
+ assert.deepEqual(['a', 'c', 'b', 'fin'], order);
+ });
+ });
+ });
+
+ it('testTasksDoNotWaitForNewlyCreatedPromises', function() {
+ var order = [];
+
+ flow.execute(function() {
+ var d = promise.defer();
+
+ // This is a normal promise, not a task, so the task for this callback is
+ // considered volatile. Volatile tasks should be skipped when they reach
+ // the front of the task queue.
+ d.promise.then(() => order.push('a'));
+
+ flow.execute(() => order.push('b'));
+ flow.execute(function() {
+ flow.execute(() => order.push('c'));
+ d.promise.then(() => order.push('d'));
+ d.fulfill();
+ });
+ flow.execute(() => order.push('e'));
+
+ }).then(() => order.push('fin'));
+
+ return waitForIdle().then(function() {
+ assert.deepEqual(['b', 'a', 'c', 'd', 'e', 'fin'], order);
+ });
+ });
+
+ it('testCallbackDependenciesDoNotDeadlock', function() {
+ var order = [];
+ var root = promise.defer();
+ var dep = promise.fulfilled().then(function() {
+ order.push('a');
+ return root.promise.then(function() {
+ order.push('b');
+ });
+ });
+ // This callback depends on |dep|, which depends on another callback
+ // attached to |root| via a chain.
+ root.promise.then(function() {
+ order.push('c');
+ return dep.then(() => order.push('d'));
+ }).then(() => order.push('fin'));
+
+ setTimeout(() => root.fulfill(), 20);
+
+ return waitForIdle().then(function() {
+ assert.deepEqual(['a', 'b', 'c', 'd', 'fin'], order);
+ });
+ });
+});
diff --git a/node_modules/selenium-webdriver/test/lib/promise_generator_test.js b/node_modules/selenium-webdriver/test/lib/promise_generator_test.js
new file mode 100644
index 000000000..5fdff9f80
--- /dev/null
+++ b/node_modules/selenium-webdriver/test/lib/promise_generator_test.js
@@ -0,0 +1,308 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+const assert = require('assert');
+const promise = require('../../lib/promise');
+
+describe('promise.consume()', function() {
+ it('requires inputs to be generator functions', function() {
+ assert.throws(function() {
+ promise.consume(function() {});
+ });
+ });
+
+ it('handles a basic generator with no yielded promises', function() {
+ var values = [];
+ return promise.consume(function* () {
+ var i = 0;
+ while (i < 4) {
+ i = yield i + 1;
+ values.push(i);
+ }
+ }).then(function() {
+ assert.deepEqual([1, 2, 3, 4], values);
+ });
+ });
+
+ it('handles a promise yielding generator', function() {
+ var values = [];
+ return promise.consume(function* () {
+ var i = 0;
+ while (i < 4) {
+ // Test that things are actually async here.
+ setTimeout(function() {
+ values.push(i * 2);
+ }, 10);
+
+ yield promise.delayed(10).then(function() {
+ values.push(i++);
+ });
+ }
+ }).then(function() {
+ assert.deepEqual([0, 0, 2, 1, 4, 2, 6, 3], values);
+ });
+ });
+
+ it('assignemnts to yielded promises get fulfilled value', function() {
+ return promise.consume(function* () {
+ var p = promise.fulfilled(2);
+ var x = yield p;
+ assert.equal(2, x);
+ });
+ });
+
+ it('is possible to cancel promise generators', function() {
+ var values = [];
+ var p = promise.consume(function* () {
+ var i = 0;
+ while (i < 3) {
+ yield promise.delayed(100).then(function() {
+ values.push(i++);
+ });
+ }
+ });
+ return promise.delayed(75).then(function() {
+ p.cancel();
+ return p.catch(function() {
+ return promise.delayed(300);
+ });
+ }).then(function() {
+ assert.deepEqual([0], values);
+ });
+ });
+
+ it('uses final return value as fulfillment value', function() {
+ return promise.consume(function* () {
+ yield 1;
+ yield 2;
+ return 3;
+ }).then(function(value) {
+ assert.equal(3, value);
+ });
+ });
+
+ it('throws rejected promise errors within the generator', function() {
+ var values = [];
+ return promise.consume(function* () {
+ values.push('a');
+ var e = Error('stub error');
+ try {
+ yield promise.rejected(e);
+ values.push('b');
+ } catch (ex) {
+ assert.equal(e, ex);
+ values.push('c');
+ }
+ values.push('d');
+ }).then(function() {
+ assert.deepEqual(['a', 'c', 'd'], values);
+ });
+ });
+
+ it('aborts the generator if there is an unhandled rejection', function() {
+ var values = [];
+ var e = Error('stub error');
+ return promise.consume(function* () {
+ values.push(1);
+ yield promise.rejected(e);
+ values.push(2);
+ }).catch(function() {
+ assert.deepEqual([1], values);
+ });
+ });
+
+ it('yield waits for promises', function() {
+ var values = [];
+ var d = promise.defer();
+
+ setTimeout(function() {
+ assert.deepEqual([1], values);
+ d.fulfill(2);
+ }, 100);
+
+ return promise.consume(function* () {
+ values.push(1);
+ values.push((yield d.promise), 3);
+ }).then(function() {
+ assert.deepEqual([1, 2, 3], values);
+ });
+ });
+
+ it('accepts custom scopes', function() {
+ return promise.consume(function* () {
+ return this.name;
+ }, {name: 'Bob'}).then(function(value) {
+ assert.equal('Bob', value);
+ });
+ });
+
+ it('accepts initial generator arguments', function() {
+ return promise.consume(function* (a, b) {
+ assert.equal('red', a);
+ assert.equal('apples', b);
+ }, null, 'red', 'apples');
+ });
+
+ it('executes generator within the control flow', function() {
+ var promises = [
+ promise.defer(),
+ promise.defer()
+ ];
+ var values = [];
+
+ setTimeout(function() {
+ assert.deepEqual([], values);
+ promises[0].fulfill(1);
+ }, 100);
+
+ setTimeout(function() {
+ assert.deepEqual([1], values);
+ promises[1].fulfill(2);
+ }, 200);
+
+ return promise.controlFlow().execute(function* () {
+ values.push(yield promises[0].promise);
+ values.push(yield promises[1].promise);
+ values.push('fin');
+ }).then(function() {
+ assert.deepEqual([1, 2, 'fin'], values);
+ });
+ });
+
+ it('handles tasks scheduled in generator', function() {
+ var flow = promise.controlFlow();
+ return flow.execute(function* () {
+ var x = yield flow.execute(function() {
+ return promise.delayed(10).then(function() {
+ return 1;
+ });
+ });
+
+ var y = yield flow.execute(function() {
+ return 2;
+ });
+
+ return x + y;
+ }).then(function(value) {
+ assert.equal(3, value);
+ });
+ });
+
+ it('blocks the control flow while processing generator', function() {
+ var values = [];
+ return promise.controlFlow().wait(function* () {
+ yield values.push(1);
+ values.push(yield promise.delayed(10).then(function() {
+ return 2;
+ }));
+ yield values.push(3);
+ return values.length === 6;
+ }, 250).then(function() {
+ assert.deepEqual([1, 2, 3, 1, 2, 3], values);
+ });
+ });
+
+ it('ControlFlow.wait() will timeout on long generator', function() {
+ var values = [];
+ return promise.controlFlow().wait(function* () {
+ var i = 0;
+ while (i < 3) {
+ yield promise.delayed(100).then(function() {
+ values.push(i++);
+ });
+ }
+ }, 75).catch(function() {
+ assert.deepEqual(
+ [0, 1, 2], values, 'Should complete one loop of wait condition');
+ });
+ });
+
+ describe('generators in promise callbacks', function() {
+ it('works with no initial value', function() {
+ var promises = [
+ promise.defer(),
+ promise.defer()
+ ];
+ var values = [];
+
+ setTimeout(function() {
+ promises[0].fulfill(1);
+ }, 50);
+
+ setTimeout(function() {
+ promises[1].fulfill(2);
+ }, 100);
+
+ return promise.fulfilled().then(function*() {
+ values.push(yield promises[0].promise);
+ values.push(yield promises[1].promise);
+ values.push('fin');
+ }).then(function() {
+ assert.deepEqual([1, 2, 'fin'], values);
+ });
+ });
+
+ it('starts the generator with promised value', function() {
+ var promises = [
+ promise.defer(),
+ promise.defer()
+ ];
+ var values = [];
+
+ setTimeout(function() {
+ promises[0].fulfill(1);
+ }, 50);
+
+ setTimeout(function() {
+ promises[1].fulfill(2);
+ }, 100);
+
+ return promise.fulfilled(3).then(function*(value) {
+ var p1 = yield promises[0].promise;
+ var p2 = yield promises[1].promise;
+ values.push(value + p1);
+ values.push(value + p2);
+ values.push('fin');
+ }).then(function() {
+ assert.deepEqual([4, 5, 'fin'], values);
+ });
+ });
+
+ it('throws yielded rejections within the generator callback', function() {
+ var d = promise.defer();
+ var e = Error('stub');
+
+ setTimeout(function() {
+ d.reject(e);
+ }, 50);
+
+ return promise.fulfilled().then(function*() {
+ var threw = false;
+ try {
+ yield d.promise;
+ } catch (ex) {
+ threw = true;
+ assert.equal(e, ex);
+ }
+ assert.ok(threw);
+ });
+ });
+ });
+});
+
diff --git a/node_modules/selenium-webdriver/test/lib/promise_test.js b/node_modules/selenium-webdriver/test/lib/promise_test.js
new file mode 100644
index 000000000..51554ecd9
--- /dev/null
+++ b/node_modules/selenium-webdriver/test/lib/promise_test.js
@@ -0,0 +1,1067 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+const assert = require('assert');
+
+const testutil = require('./testutil');
+const promise = require('../../lib/promise');
+
+// Aliases for readability.
+const NativePromise = Promise;
+const StubError = testutil.StubError;
+const assertIsStubError = testutil.assertIsStubError;
+const callbackHelper = testutil.callbackHelper;
+const callbackPair = testutil.callbackPair;
+const throwStubError = testutil.throwStubError;
+const fail = () => assert.fail();
+
+// Refer to promise_aplus_test for promise compliance with standard behavior.
+describe('promise', function() {
+ var app, uncaughtExceptions;
+
+ beforeEach(function setUp() {
+ promise.LONG_STACK_TRACES = false;
+ uncaughtExceptions = [];
+
+ app = promise.controlFlow();
+ app.on(promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION,
+ (e) => uncaughtExceptions.push(e));
+ });
+
+ afterEach(function tearDown() {
+ app.reset();
+ promise.setDefaultFlow(new promise.ControlFlow);
+ assert.deepEqual([], uncaughtExceptions,
+ 'Did not expect any uncaught exceptions');
+ promise.LONG_STACK_TRACES = false;
+ });
+
+ const assertIsPromise = (p) => assert.ok(promise.isPromise(p));
+ const assertNotPromise = (v) => assert.ok(!promise.isPromise(v));
+
+ function createRejectedPromise(reason) {
+ var p = promise.rejected(reason);
+ p.catch(function() {});
+ return p;
+ }
+
+ it('testCanDetectPromiseLikeObjects', function() {
+ assertIsPromise(new promise.Promise(function(fulfill) {
+ fulfill();
+ }));
+ assertIsPromise(new promise.Deferred().promise);
+ assertIsPromise({then:function() {}});
+
+ assertNotPromise(new promise.Deferred());
+ assertNotPromise(undefined);
+ assertNotPromise(null);
+ assertNotPromise('');
+ assertNotPromise(true);
+ assertNotPromise(false);
+ assertNotPromise(1);
+ assertNotPromise({});
+ assertNotPromise({then:1});
+ assertNotPromise({then:true});
+ assertNotPromise({then:''});
+ });
+
+ describe('then', function() {
+ it('returnsOwnPromiseIfNoCallbacksWereGiven', function() {
+ var deferred = new promise.Deferred();
+ assert.equal(deferred.promise, deferred.promise.then());
+ assert.equal(deferred.promise, deferred.promise.catch());
+ assert.equal(deferred.promise, promise.when(deferred.promise));
+ });
+
+ it('stillConsideredUnHandledIfNoCallbacksWereGivenOnCallsToThen', function() {
+ promise.rejected(new StubError).then();
+ var handler = callbackHelper(assertIsStubError);
+
+ // so tearDown() doesn't throw
+ app.removeAllListeners();
+ app.on(promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, handler);
+ return NativePromise.resolve()
+ // Macro yield so the uncaught exception has a chance to trigger.
+ .then(() => new NativePromise(resolve => setTimeout(resolve, 0)))
+ .then(() => handler.assertCalled());
+ });
+ });
+
+ describe('finally', function() {
+ it('nonFailingCallbackDoesNotSuppressOriginalError', function() {
+ var done = callbackHelper(assertIsStubError);
+ return promise.rejected(new StubError).
+ finally(function() {}).
+ catch(done).
+ finally(done.assertCalled);
+ });
+
+ it('failingCallbackSuppressesOriginalError', function() {
+ var done = callbackHelper(assertIsStubError);
+ return promise.rejected(new Error('original')).
+ finally(throwStubError).
+ catch(done).
+ finally(done.assertCalled);
+ });
+
+ it('callbackThrowsAfterFulfilledPromise', function() {
+ var done = callbackHelper(assertIsStubError);
+ return promise.fulfilled().
+ finally(throwStubError).
+ catch(done).
+ finally(done.assertCalled);
+ });
+
+ it('callbackReturnsRejectedPromise', function() {
+ var done = callbackHelper(assertIsStubError);
+ return promise.fulfilled().
+ finally(function() {
+ return promise.rejected(new StubError);
+ }).
+ catch(done).
+ finally(done.assertCalled);
+ });
+ });
+
+ describe('cancel', function() {
+ it('passesTheCancellationReasonToReject', function() {
+ var d = new promise.Deferred();
+ var res = d.promise.then(assert.fail, function(e) {
+ assert.ok(e instanceof promise.CancellationError);
+ assert.equal('because i said so', e.message);
+ });
+ d.promise.cancel('because i said so');
+ return res;
+ });
+
+ describe('can cancel original promise from its child;', function() {
+ it('child created by then()', function() {
+ var d = new promise.Deferred();
+ var p = d.promise.then(assert.fail, function(e) {
+ assert.ok(e instanceof promise.CancellationError);
+ assert.equal('because i said so', e.message);
+ return 123;
+ });
+
+ p.cancel('because i said so');
+ return p.then(v => assert.equal(123, v));
+ });
+
+ it('child linked by resolving with parent', function() {
+ let parent = promise.defer();
+ let child = new promise.Promise(resolve => resolve(parent.promise));
+ child.cancel('all done');
+
+ return parent.promise.then(
+ () => assert.fail('expected a cancellation'),
+ e => {
+ assert.ok(e instanceof promise.CancellationError);
+ assert.equal('all done', e.message);
+ });
+ });
+
+ it('grand child through thenable chain', function() {
+ let p = new promise.Promise(function() {/* never resolve*/});
+
+ let noop = function() {};
+ let gc = p.then(noop).then(noop).then(noop);
+ gc.cancel('stop!');
+
+ return p.then(
+ () => assert.fail('expected to be cancelled'),
+ (e) => {
+ assert.ok(e instanceof promise.CancellationError);
+ assert.equal('stop!', e.message);
+ });
+ });
+
+ it('grand child through thenable chain started at resolve', function() {
+ function noop() {}
+
+ let parent = promise.defer();
+ let child = new promise.Promise(resolve => resolve(parent.promise));
+ let grandChild = child.then(noop).then(noop).then(noop);
+ grandChild.cancel('all done');
+
+ return parent.promise.then(
+ () => assert.fail('expected a cancellation'),
+ e => {
+ assert.ok(e instanceof promise.CancellationError);
+ assert.equal('all done', e.message);
+ });
+ });
+
+ it('"parent" is a Thenable', function() {
+ function noop() {}
+
+ class FakeThenable {
+ constructor(p) {
+ this.promise = p;
+ }
+
+ cancel(reason) {
+ this.promise.cancel(reason);
+ }
+
+ then(cb, eb) {
+ let result = this.promise.then(cb, eb);
+ return new FakeThenable(result);
+ }
+ }
+ promise.Thenable.addImplementation(FakeThenable);
+
+ let root = new promise.Promise(noop);
+ let thenable = new FakeThenable(root);
+ assert.ok(promise.Thenable.isImplementation(thenable));
+
+ let child = new promise.Promise(resolve => resolve(thenable));
+ assert.ok(child instanceof promise.Promise);
+ child.cancel('stop!');
+
+ function assertStopped(p) {
+ return p.then(
+ () => assert.fail('not stopped!'),
+ (e) => {
+ assert.ok(e instanceof promise.CancellationError);
+ assert.equal('stop!', e.message);
+ });
+ }
+
+ return assertStopped(child).then(() => assertStopped(root));
+ });
+ });
+
+ it('canCancelATimeout', function() {
+ var p = promise.delayed(25)
+ .then(assert.fail, (e) => e instanceof promise.CancellationError);
+ setTimeout(() => p.cancel(), 20);
+ p.cancel();
+ return p;
+ });
+
+ it('can cancel timeout from grandchild', function() {
+ });
+
+ it('cancelIsANoopOnceAPromiseHasBeenFulfilled', function() {
+ var p = promise.fulfilled(123);
+ p.cancel();
+ return p.then((v) => assert.equal(123, v));
+ });
+
+ it('cancelIsANoopOnceAPromiseHasBeenRejected', function() {
+ var p = promise.rejected(new StubError);
+ p.cancel();
+
+ var pair = callbackPair(null, assertIsStubError);
+ return p.then(assert.fail, assertIsStubError);
+ });
+
+ it('noopCancelTriggeredOnCallbackOfResolvedPromise', function() {
+ var d = promise.defer();
+ var p = d.promise.then();
+
+ d.fulfill();
+ p.cancel(); // This should not throw.
+ return p; // This should not trigger a failure.
+ });
+ });
+
+ describe('when', function() {
+ it('ReturnsAResolvedPromiseIfGivenANonPromiseValue', function() {
+ var ret = promise.when('abc');
+ assertIsPromise(ret);
+ return ret.then((value) => assert.equal('abc', value));
+ });
+
+ it('PassesRawErrorsToCallbacks', function() {
+ var error = new Error('boo!');
+ return promise.when(error, function(value) {
+ assert.equal(error, value);
+ });
+ });
+
+ it('WaitsForValueToBeResolvedBeforeInvokingCallback', function() {
+ var d = new promise.Deferred(), callback;
+ let result = promise.when(d.promise, callback = callbackHelper(function(value) {
+ assert.equal('hi', value);
+ }));
+ callback.assertNotCalled();
+ d.fulfill('hi');
+ return result.then(callback.assertCalled);
+ });
+ });
+
+ it('firesUncaughtExceptionEventIfRejectionNeverHandled', function() {
+ promise.rejected(new StubError);
+ var handler = callbackHelper(assertIsStubError);
+
+ // so tearDown() doesn't throw
+ app.removeAllListeners();
+ app.on(promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, handler);
+
+ return NativePromise.resolve()
+ // Macro yield so the uncaught exception has a chance to trigger.
+ .then(() => new NativePromise(resolve => setTimeout(resolve, 0)))
+ .then(handler.assertCalled);
+ });
+
+ it('cannotResolveADeferredWithItself', function() {
+ var deferred = new promise.Deferred();
+ assert.throws(() => deferred.fulfill(deferred));
+ assert.throws(() => deferred.reject(deferred));
+ });
+
+ describe('fullyResolved', function() {
+ it('primitives', function() {
+ function runTest(value) {
+ var callback, errback;
+ return promise.fullyResolved(value)
+ .then((resolved) => assert.equal(value, resolved));
+ }
+ return runTest(true)
+ .then(() => runTest(function() {}))
+ .then(() => runTest(null))
+ .then(() => runTest(123))
+ .then(() => runTest('foo bar'))
+ .then(() => runTest(undefined));
+ });
+
+ it('arrayOfPrimitives', function() {
+ var fn = function() {};
+ var array = [true, fn, null, 123, '', undefined, 1];
+ return promise.fullyResolved(array).then(function(resolved) {
+ assert.equal(array, resolved);
+ assert.deepEqual([true, fn, null, 123, '', undefined, 1],
+ resolved);
+ });
+ });
+
+ it('nestedArrayOfPrimitives', function() {
+ var fn = function() {};
+ var array = [true, [fn, null, 123], '', undefined];
+ return promise.fullyResolved(array)
+ .then(function(resolved) {
+ assert.equal(array, resolved);
+ assert.deepEqual([true, [fn, null, 123], '', undefined], resolved);
+ assert.deepEqual([fn, null, 123], resolved[1]);
+ });
+ });
+
+ it('arrayWithPromisedPrimitive', function() {
+ return promise.fullyResolved([promise.fulfilled(123)])
+ .then(function(resolved) {
+ assert.deepEqual([123], resolved);
+ });
+ });
+
+ it('promiseResolvesToPrimitive', function() {
+ return promise.fullyResolved(promise.fulfilled(123))
+ .then((resolved) => assert.equal(123, resolved));
+ });
+
+ it('promiseResolvesToArray', function() {
+ var fn = function() {};
+ var array = [true, [fn, null, 123], '', undefined];
+ var aPromise = promise.fulfilled(array);
+
+ var result = promise.fullyResolved(aPromise);
+ return result.then(function(resolved) {
+ assert.equal(array, resolved);
+ assert.deepEqual([true, [fn, null, 123], '', undefined],
+ resolved);
+ assert.deepEqual([fn, null, 123], resolved[1]);
+ });
+ });
+
+ it('promiseResolvesToArrayWithPromises', function() {
+ var nestedPromise = promise.fulfilled(123);
+ var aPromise = promise.fulfilled([true, nestedPromise]);
+ return promise.fullyResolved(aPromise)
+ .then(function(resolved) {
+ assert.deepEqual([true, 123], resolved);
+ });
+ });
+
+ it('rejectsIfArrayPromiseRejects', function() {
+ var nestedPromise = createRejectedPromise(new StubError);
+ var aPromise = promise.fulfilled([true, nestedPromise]);
+
+ var pair = callbackPair(null, assertIsStubError);
+ return promise.fullyResolved(aPromise)
+ .then(assert.fail, assertIsStubError);
+ });
+
+ it('rejectsOnFirstArrayRejection', function() {
+ var e1 = new Error('foo');
+ var e2 = new Error('bar');
+ var aPromise = promise.fulfilled([
+ createRejectedPromise(e1),
+ createRejectedPromise(e2)
+ ]);
+
+ return promise.fullyResolved(aPromise)
+ .then(assert.fail, function(error) {
+ assert.strictEqual(e1, error);
+ });
+ });
+
+ it('rejectsIfNestedArrayPromiseRejects', function() {
+ var aPromise = promise.fulfilled([
+ promise.fulfilled([
+ createRejectedPromise(new StubError)
+ ])
+ ]);
+
+ return promise.fullyResolved(aPromise)
+ .then(assert.fail, assertIsStubError);
+ });
+
+ it('simpleHash', function() {
+ var hash = {'a': 123};
+ return promise.fullyResolved(hash)
+ .then(function(resolved) {
+ assert.strictEqual(hash, resolved);
+ assert.deepEqual(hash, {'a': 123});
+ });
+ });
+
+ it('nestedHash', function() {
+ var nestedHash = {'foo':'bar'};
+ var hash = {'a': 123, 'b': nestedHash};
+
+ return promise.fullyResolved(hash)
+ .then(function(resolved) {
+ assert.strictEqual(hash, resolved);
+ assert.deepEqual({'a': 123, 'b': {'foo': 'bar'}}, resolved);
+ assert.strictEqual(nestedHash, resolved['b']);
+ });
+ });
+
+ it('promiseResolvesToSimpleHash', function() {
+ var hash = {'a': 123};
+ var aPromise = promise.fulfilled(hash);
+
+ return promise.fullyResolved(aPromise)
+ .then((resolved) => assert.strictEqual(hash, resolved));
+ });
+
+ it('promiseResolvesToNestedHash', function() {
+ var nestedHash = {'foo':'bar'};
+ var hash = {'a': 123, 'b': nestedHash};
+ var aPromise = promise.fulfilled(hash);
+
+ return promise.fullyResolved(aPromise)
+ .then(function(resolved) {
+ assert.strictEqual(hash, resolved);
+ assert.strictEqual(nestedHash, resolved['b']);
+ assert.deepEqual(hash, {'a': 123, 'b': {'foo': 'bar'}});
+ });
+ });
+
+ it('promiseResolvesToHashWithPromises', function() {
+ var aPromise = promise.fulfilled({
+ 'a': promise.fulfilled(123)
+ });
+
+ return promise.fullyResolved(aPromise)
+ .then(function(resolved) {
+ assert.deepEqual({'a': 123}, resolved);
+ });
+ });
+
+ it('rejectsIfHashPromiseRejects', function() {
+ var aPromise = promise.fulfilled({
+ 'a': createRejectedPromise(new StubError)
+ });
+
+ return promise.fullyResolved(aPromise)
+ .then(assert.fail, assertIsStubError);
+ });
+
+ it('rejectsIfNestedHashPromiseRejects', function() {
+ var aPromise = promise.fulfilled({
+ 'a': {'b': createRejectedPromise(new StubError)}
+ });
+
+ return promise.fullyResolved(aPromise)
+ .then(assert.fail, assertIsStubError);
+ });
+
+ it('instantiatedObject', function() {
+ function Foo() {
+ this.bar = 'baz';
+ }
+ var foo = new Foo;
+
+ return promise.fullyResolved(foo).then(function(resolvedFoo) {
+ assert.equal(foo, resolvedFoo);
+ assert.ok(resolvedFoo instanceof Foo);
+ assert.deepEqual(new Foo, resolvedFoo);
+ });
+ });
+
+ it('withEmptyArray', function() {
+ return promise.fullyResolved([]).then(function(resolved) {
+ assert.deepEqual([], resolved);
+ });
+ });
+
+ it('withEmptyHash', function() {
+ return promise.fullyResolved({}).then(function(resolved) {
+ assert.deepEqual({}, resolved);
+ });
+ });
+
+ it('arrayWithPromisedHash', function() {
+ var obj = {'foo': 'bar'};
+ var array = [promise.fulfilled(obj)];
+
+ return promise.fullyResolved(array).then(function(resolved) {
+ assert.deepEqual(resolved, [obj]);
+ });
+ });
+ });
+
+ describe('checkedNodeCall', function() {
+ it('functionThrows', function() {
+ return promise.checkedNodeCall(throwStubError)
+ .then(assert.fail, assertIsStubError);
+ });
+
+ it('functionReturnsAnError', function() {
+ return promise.checkedNodeCall(function(callback) {
+ callback(new StubError);
+ }).then(assert.fail, assertIsStubError);
+ });
+
+ it('functionReturnsSuccess', function() {
+ var success = 'success!';
+ return promise.checkedNodeCall(function(callback) {
+ callback(null, success);
+ }).then((value) => assert.equal(success, value));
+ });
+
+ it('functionReturnsAndThrows', function() {
+ var error = new Error('boom');
+ var error2 = new Error('boom again');
+ return promise.checkedNodeCall(function(callback) {
+ callback(error);
+ throw error2;
+ }).then(assert.fail, (e) => assert.equal(error, e));
+ });
+
+ it('functionThrowsAndReturns', function() {
+ var error = new Error('boom');
+ var error2 = new Error('boom again');
+ return promise.checkedNodeCall(function(callback) {
+ setTimeout(() => callback(error), 10);
+ throw error2;
+ }).then(assert.fail, (e) => assert.equal(error2, e));
+ });
+ });
+
+ describe('all', function() {
+ it('(base case)', function() {
+ let defer = [promise.defer(), promise.defer()];
+ var a = [
+ 0, 1,
+ defer[0].promise,
+ defer[1].promise,
+ 4, 5, 6
+ ];
+ delete a[5];
+
+ var pair = callbackPair(function(value) {
+ assert.deepEqual([0, 1, 2, 3, 4, undefined, 6], value);
+ });
+
+ var result = promise.all(a).then(pair.callback, pair.errback);
+ pair.assertNeither();
+
+ defer[0].fulfill(2);
+ pair.assertNeither();
+
+ defer[1].fulfill(3);
+ return result.then(() => pair.assertCallback());
+ });
+
+ it('empty array', function() {
+ return promise.all([]).then((a) => assert.deepEqual([], a));
+ });
+
+ it('usesFirstRejection', function() {
+ let defer = [promise.defer(), promise.defer()];
+ let a = [defer[0].promise, defer[1].promise];
+
+ var result = promise.all(a).then(assert.fail, assertIsStubError);
+ defer[1].reject(new StubError);
+ setTimeout(() => defer[0].reject(Error('ignored')), 0);
+ return result;
+ });
+ });
+
+ describe('map', function() {
+ it('(base case)', function() {
+ var a = [1, 2, 3];
+ return promise.map(a, function(value, index, a2) {
+ assert.equal(a, a2);
+ assert.equal('number', typeof index, 'not a number');
+ return value + 1;
+ }).then(function(value) {
+ assert.deepEqual([2, 3, 4], value);
+ });
+ });
+
+ it('omitsDeleted', function() {
+ var a = [0, 1, 2, 3, 4, 5, 6];
+ delete a[1];
+ delete a[3];
+ delete a[4];
+ delete a[6];
+
+ var expected = [0, 1, 4, 9, 16, 25, 36];
+ delete expected[1];
+ delete expected[3];
+ delete expected[4];
+ delete expected[6];
+
+ return promise.map(a, function(value) {
+ return value * value;
+ }).then(function(value) {
+ assert.deepEqual(expected, value);
+ });
+ });
+
+ it('emptyArray', function() {
+ return promise.map([], function(value) {
+ return value + 1;
+ }).then(function(value) {
+ assert.deepEqual([], value);
+ });
+ });
+
+ it('inputIsPromise', function() {
+ var input = promise.defer();
+ var result = promise.map(input.promise, function(value) {
+ return value + 1;
+ });
+
+ var pair = callbackPair(function(value) {
+ assert.deepEqual([2, 3, 4], value);
+ });
+ result = result.then(pair.callback, pair.errback);
+
+ setTimeout(function() {
+ pair.assertNeither();
+ input.fulfill([1, 2, 3]);
+ }, 10);
+
+ return result;
+ });
+
+ it('waitsForFunctionResultToResolve', function() {
+ var innerResults = [
+ promise.defer(),
+ promise.defer()
+ ];
+
+ var result = promise.map([1, 2], function(value, index) {
+ return innerResults[index].promise;
+ });
+
+ var pair = callbackPair(function(value) {
+ assert.deepEqual(['a', 'b'], value);
+ });
+ result = result.then(pair.callback, pair.errback);
+
+ return NativePromise.resolve()
+ .then(function() {
+ pair.assertNeither();
+ innerResults[0].fulfill('a');
+ })
+ .then(function() {
+ pair.assertNeither();
+ innerResults[1].fulfill('b');
+ return result;
+ })
+ .then(pair.assertCallback);
+ });
+
+ it('rejectsPromiseIfFunctionThrows', function() {
+ return promise.map([1], throwStubError)
+ .then(assert.fail, assertIsStubError);
+ });
+
+ it('rejectsPromiseIfFunctionReturnsRejectedPromise', function() {
+ return promise.map([1], function() {
+ return promise.rejected(new StubError);
+ }).then(assert.fail, assertIsStubError);
+ });
+
+ it('stopsCallingFunctionIfPreviousIterationFailed', function() {
+ var count = 0;
+ return promise.map([1, 2, 3, 4], function() {
+ count++;
+ if (count == 3) {
+ throw new StubError;
+ }
+ }).then(assert.fail, function(e) {
+ assertIsStubError(e);
+ assert.equal(3, count);
+ });
+ });
+
+ it('rejectsWithFirstRejectedPromise', function() {
+ var innerResult = [
+ promise.fulfilled(),
+ createRejectedPromise(new StubError),
+ createRejectedPromise(Error('should be ignored'))
+ ];
+ var count = 0;
+ return promise.map([1, 2, 3, 4], function(value, index) {
+ count += 1;
+ return innerResult[index];
+ }).then(assert.fail, function(e) {
+ assertIsStubError(e);
+ assert.equal(2, count);
+ });
+ });
+
+ it('preservesOrderWhenMapReturnsPromise', function() {
+ var deferreds = [
+ promise.defer(),
+ promise.defer(),
+ promise.defer(),
+ promise.defer()
+ ];
+ var result = promise.map(deferreds, function(value) {
+ return value.promise;
+ });
+
+ var pair = callbackPair(function(value) {
+ assert.deepEqual([0, 1, 2, 3], value);
+ });
+ result = result.then(pair.callback, pair.errback);
+
+ return NativePromise.resolve()
+ .then(function() {
+ pair.assertNeither();
+ for (let i = deferreds.length; i > 0; i -= 1) {
+ deferreds[i - 1].fulfill(i - 1);
+ }
+ return result;
+ }).then(pair.assertCallback);
+ });
+ });
+
+ describe('filter', function() {
+ it('basicFiltering', function() {
+ var a = [0, 1, 2, 3];
+ return promise.filter(a, function(val, index, a2) {
+ assert.equal(a, a2);
+ assert.equal('number', typeof index, 'not a number');
+ return val > 1;
+ }).then(function(val) {
+ assert.deepEqual([2, 3], val);
+ });
+ });
+
+ it('omitsDeleted', function() {
+ var a = [0, 1, 2, 3, 4, 5, 6];
+ delete a[3];
+ delete a[4];
+
+ return promise.filter(a, function(value) {
+ return value > 1 && value < 6;
+ }).then(function(val) {
+ assert.deepEqual([2, 5], val);
+ });
+ });
+
+ it('preservesInputs', function() {
+ var a = [0, 1, 2, 3];
+
+ return promise.filter(a, function(value, i, a2) {
+ assert.equal(a, a2);
+ // Even if a function modifies the input array, the original value
+ // should be inserted into the new array.
+ a2[i] = a2[i] - 1;
+ return a2[i] >= 1;
+ }).then(function(val) {
+ assert.deepEqual([2, 3], val);
+ });
+ });
+
+ it('inputIsPromise', function() {
+ var input = promise.defer();
+ var result = promise.filter(input.promise, function(value) {
+ return value > 1 && value < 3;
+ });
+
+ var pair = callbackPair(function(value) {
+ assert.deepEqual([2], value);
+ });
+ result = result.then(pair.callback, pair.errback);
+ return NativePromise.resolve()
+ .then(function() {
+ pair.assertNeither();
+ input.fulfill([1, 2, 3]);
+ return result;
+ })
+ .then(pair.assertCallback);
+ });
+
+ it('waitsForFunctionResultToResolve', function() {
+ var innerResults = [
+ promise.defer(),
+ promise.defer()
+ ];
+
+ var result = promise.filter([1, 2], function(value, index) {
+ return innerResults[index].promise;
+ });
+
+ var pair = callbackPair(function(value) {
+ assert.deepEqual([2], value);
+ });
+ result = result.then(pair.callback, pair.errback);
+ return NativePromise.resolve()
+ .then(function() {
+ pair.assertNeither();
+ innerResults[0].fulfill(false);
+ })
+ .then(function() {
+ pair.assertNeither();
+ innerResults[1].fulfill(true);
+ return result;
+ })
+ .then(pair.assertCallback);
+ });
+
+ it('rejectsPromiseIfFunctionReturnsRejectedPromise', function() {
+ return promise.filter([1], function() {
+ return promise.rejected(new StubError);
+ }).then(assert.fail, assertIsStubError);
+ });
+
+ it('stopsCallingFunctionIfPreviousIterationFailed', function() {
+ var count = 0;
+ return promise.filter([1, 2, 3, 4], function() {
+ count++;
+ if (count == 3) {
+ throw new StubError;
+ }
+ }).then(assert.fail, function(e) {
+ assertIsStubError(e);
+ assert.equal(3, count);
+ });
+ });
+
+ it('rejectsWithFirstRejectedPromise', function() {
+ var innerResult = [
+ promise.fulfilled(),
+ createRejectedPromise(new StubError),
+ createRejectedPromise(Error('should be ignored'))
+ ];
+
+ return promise.filter([1, 2, 3, 4], function(value, index) {
+ assert.ok(index < innerResult.length);
+ return innerResult[index];
+ }).then(assert.fail, assertIsStubError);
+ });
+
+ it('preservesOrderWhenFilterReturnsPromise', function() {
+ var deferreds = [
+ promise.defer(),
+ promise.defer(),
+ promise.defer(),
+ promise.defer()
+ ];
+ var result = promise.filter([0, 1, 2, 3], function(value, index) {
+ return deferreds[index].promise;
+ });
+
+ var pair = callbackPair(function(value) {
+ assert.deepEqual([1, 2], value);
+ });
+ result = result.then(pair.callback, pair.errback);
+
+ return NativePromise.resolve()
+ .then(function() {
+ pair.assertNeither();
+ for (let i = deferreds.length - 1; i >= 0; i -= 1) {
+ deferreds[i].fulfill(i > 0 && i < 3);
+ }
+ return result;
+ }).then(pair.assertCallback);
+ });
+ });
+
+ it('testAddThenableImplementation', function() {
+ function tmp() {}
+ assert.ok(!promise.Thenable.isImplementation(new tmp()));
+ promise.Thenable.addImplementation(tmp);
+ assert.ok(promise.Thenable.isImplementation(new tmp()));
+
+ class tmpClass {}
+ assert.ok(!promise.Thenable.isImplementation(new tmpClass()));
+ promise.Thenable.addImplementation(tmpClass);
+ assert.ok(promise.Thenable.isImplementation(new tmpClass()));
+ });
+
+ describe('testLongStackTraces', function() {
+ beforeEach(() => promise.LONG_STACK_TRACES = false);
+ afterEach(() => promise.LONG_STACK_TRACES = false);
+
+ it('doesNotAppendStackIfFeatureDisabled', function() {
+ promise.LONG_STACK_TRACES = false;
+
+ var error = Error('hello');
+ var originalStack = error.stack;
+ return promise.rejected(error).
+ then(fail).
+ then(fail).
+ then(fail).
+ then(fail, function(e) {
+ assert.equal(error, e);
+ assert.equal(originalStack, e.stack);
+ });
+ });
+
+ function getStackMessages(error) {
+ return error.stack.split(/\n/).filter(function(line) {
+ return /^From: /.test(line);
+ });
+ }
+
+ it('appendsInitialPromiseCreation_resolverThrows', function() {
+ promise.LONG_STACK_TRACES = true;
+
+ var error = Error('hello');
+ var originalStack = '(placeholder; will be overwritten later)';
+
+ return new promise.Promise(function() {
+ try {
+ throw error;
+ } catch (e) {
+ originalStack = e.stack;
+ throw e;
+ }
+ }).then(fail, function(e) {
+ assert.strictEqual(error, e);
+ if (typeof originalStack !== 'string') {
+ return;
+ }
+ assert.notEqual(originalStack, e.stack);
+ assert.equal(e.stack.indexOf(originalStack), 0,
+ 'should start with original stack');
+ assert.deepEqual(['From: ManagedPromise: new'], getStackMessages(e));
+ });
+ });
+
+ it('appendsInitialPromiseCreation_rejectCalled', function() {
+ promise.LONG_STACK_TRACES = true;
+
+ var error = Error('hello');
+ var originalStack = error.stack;
+
+ return new promise.Promise(function(_, reject) {
+ reject(error);
+ }).then(fail, function(e) {
+ assert.equal(error, e);
+ if (typeof originalStack !== 'string') {
+ return;
+ }
+ assert.notEqual(originalStack, e.stack);
+ assert.equal(e.stack.indexOf(originalStack), 0,
+ 'should start with original stack');
+ assert.deepEqual(['From: ManagedPromise: new'], getStackMessages(e));
+ });
+ });
+
+ it('appendsEachStepToRejectionError', function() {
+ promise.LONG_STACK_TRACES = true;
+
+ var error = Error('hello');
+ var originalStack = '(placeholder; will be overwritten later)';
+
+ return new promise.Promise(function() {
+ try {
+ throw error;
+ } catch (e) {
+ originalStack = e.stack;
+ throw e;
+ }
+ }).
+ then(fail).
+ catch(function(e) { throw e; }).
+ then(fail).
+ catch(function(e) { throw e; }).
+ then(fail, function(e) {
+ assert.equal(error, e);
+ if (typeof originalStack !== 'string') {
+ return;
+ }
+ assert.notEqual(originalStack, e.stack);
+ assert.equal(e.stack.indexOf(originalStack), 0,
+ 'should start with original stack');
+ assert.deepEqual([
+ 'From: ManagedPromise: new',
+ 'From: Promise: then',
+ 'From: Promise: catch',
+ 'From: Promise: then',
+ 'From: Promise: catch',
+ ], getStackMessages(e));
+ });
+ });
+
+ it('errorOccursInCallbackChain', function() {
+ promise.LONG_STACK_TRACES = true;
+
+ var error = Error('hello');
+ var originalStack = '(placeholder; will be overwritten later)';
+
+ return promise.fulfilled().
+ then(function() {}).
+ then(function() {}).
+ then(function() {
+ try {
+ throw error;
+ } catch (e) {
+ originalStack = e.stack;
+ throw e;
+ }
+ }).
+ catch(function(e) { throw e; }).
+ then(fail, function(e) {
+ assert.equal(error, e);
+ if (typeof originalStack !== 'string') {
+ return;
+ }
+ assert.notEqual(originalStack, e.stack);
+ assert.equal(e.stack.indexOf(originalStack), 0,
+ 'should start with original stack');
+ assert.deepEqual([
+ 'From: Promise: then',
+ 'From: Promise: catch',
+ ], getStackMessages(e));
+ });
+ });
+ });
+});
diff --git a/node_modules/selenium-webdriver/test/lib/testutil.js b/node_modules/selenium-webdriver/test/lib/testutil.js
new file mode 100644
index 000000000..e68ca28ba
--- /dev/null
+++ b/node_modules/selenium-webdriver/test/lib/testutil.js
@@ -0,0 +1,90 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+const assert = require('assert');
+const sinon = require('sinon');
+
+
+class StubError extends Error {
+ constructor(opt_msg) {
+ super(opt_msg);
+ this.name = this.constructor.name;
+ }
+}
+exports.StubError = StubError;
+
+exports.throwStubError = function() {
+ throw new StubError;
+};
+
+exports.assertIsStubError = function(value) {
+ assert.ok(value instanceof StubError, value + ' is not a ' + StubError.name);
+};
+
+exports.assertIsInstance = function(ctor, value) {
+ assert.ok(value instanceof ctor, 'Not a ' + ctor.name + ': ' + value);
+};
+
+function callbackPair(cb, eb) {
+ if (cb && eb) {
+ throw new TypeError('can only specify one of callback or errback');
+ }
+
+ let callback = cb ? sinon.spy(cb) : sinon.spy();
+ let errback = eb ? sinon.spy(eb) : sinon.spy();
+
+ function assertCallback() {
+ assert.ok(callback.called, 'callback not called');
+ assert.ok(!errback.called, 'errback called');
+ if (callback.threw()) {
+ throw callback.exceptions[0];
+ }
+ }
+
+ function assertErrback() {
+ assert.ok(!callback.called, 'callback called');
+ assert.ok(errback.called, 'errback not called');
+ if (errback.threw()) {
+ throw errback.exceptions[0];
+ }
+ }
+
+ function assertNeither() {
+ assert.ok(!callback.called, 'callback called');
+ assert.ok(!errback.called, 'errback called');
+ }
+
+ return {
+ callback,
+ errback,
+ assertCallback,
+ assertErrback,
+ assertNeither
+ };
+}
+exports.callbackPair = callbackPair;
+
+
+exports.callbackHelper = function(cb) {
+ let pair = callbackPair(cb);
+ let wrapped = pair.callback.bind(null);
+ wrapped.assertCalled = () => pair.assertCallback();
+ wrapped.assertNotCalled = () => pair.assertNeither();
+ return wrapped;
+};
diff --git a/node_modules/selenium-webdriver/test/lib/until_test.js b/node_modules/selenium-webdriver/test/lib/until_test.js
new file mode 100644
index 000000000..d3e81ec18
--- /dev/null
+++ b/node_modules/selenium-webdriver/test/lib/until_test.js
@@ -0,0 +1,463 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+const assert = require('assert');
+
+const By = require('../../lib/by').By;
+const CommandName = require('../../lib/command').Name;
+const error = require('../../lib/error');
+const promise = require('../../lib/promise');
+const until = require('../../lib/until');
+const webdriver = require('../../lib/webdriver'),
+ WebElement = webdriver.WebElement;
+
+describe('until', function() {
+ let driver, executor;
+
+ class TestExecutor {
+ constructor() {
+ this.handlers_ = {};
+ }
+
+ on(cmd, handler) {
+ this.handlers_[cmd] = handler;
+ return this;
+ }
+
+ execute(cmd) {
+ let self = this;
+ return Promise.resolve().then(function() {
+ if (!self.handlers_[cmd.getName()]) {
+ throw new error.UnknownCommandError(cmd.getName());
+ }
+ return self.handlers_[cmd.getName()](cmd);
+ });
+ }
+ }
+
+ function fail(opt_msg) {
+ throw new assert.AssertionError({message: opt_msg});
+ }
+
+ beforeEach(function setUp() {
+ executor = new TestExecutor();
+ driver = new webdriver.WebDriver('session-id', executor);
+ });
+
+ describe('ableToSwitchToFrame', function() {
+ it('failsFastForNonSwitchErrors', function() {
+ let e = Error('boom');
+ executor.on(CommandName.SWITCH_TO_FRAME, function() {
+ throw e;
+ });
+ return driver.wait(until.ableToSwitchToFrame(0), 100)
+ .then(fail, (e2) => assert.strictEqual(e2, e));
+ });
+
+ it('byIndex', function() {
+ executor.on(CommandName.SWITCH_TO_FRAME, () => true);
+ return driver.wait(until.ableToSwitchToFrame(0), 100);
+ });
+
+ it('byWebElement', function() {
+ executor.on(CommandName.SWITCH_TO_FRAME, () => true);
+ var el = new webdriver.WebElement(driver, {ELEMENT: 1234});
+ return driver.wait(until.ableToSwitchToFrame(el), 100);
+ });
+
+ it('byWebElementPromise', function() {
+ executor.on(CommandName.SWITCH_TO_FRAME, () => true);
+ var el = new webdriver.WebElementPromise(driver,
+ promise.fulfilled(new webdriver.WebElement(driver, {ELEMENT: 1234})));
+ return driver.wait(until.ableToSwitchToFrame(el), 100);
+ });
+
+ it('byLocator', function() {
+ executor.on(CommandName.FIND_ELEMENTS, () => [WebElement.buildId(1234)]);
+ executor.on(CommandName.SWITCH_TO_FRAME, () => true);
+ return driver.wait(until.ableToSwitchToFrame(By.id('foo')), 100);
+ });
+
+ it('byLocator_elementNotInitiallyFound', function() {
+ var foundResponses = [[], [], [WebElement.buildId(1234)]];
+ executor.on(CommandName.FIND_ELEMENTS, () => foundResponses.shift());
+ executor.on(CommandName.SWITCH_TO_FRAME, () => true);
+
+ return driver.wait(until.ableToSwitchToFrame(By.id('foo')), 2000)
+ .then(function() {
+ assert.equal(foundResponses.length, 0);
+ });
+ });
+
+ it('timesOutIfNeverAbletoSwitchFrames', function() {
+ var count = 0;
+ executor.on(CommandName.SWITCH_TO_FRAME, function() {
+ count += 1;
+ throw new error.NoSuchFrameError;
+ });
+
+ return driver.wait(until.ableToSwitchToFrame(0), 100)
+ .then(fail, function(e) {
+ assert.ok(count > 0);
+ assert.ok(
+ e.message.startsWith('Waiting to be able to switch to frame'),
+ 'Wrong message: ' + e.message);
+ });
+ });
+ });
+
+ describe('alertIsPresent', function() {
+ it('failsFastForNonAlertSwitchErrors', function() {
+ return driver.wait(until.alertIsPresent(), 100).then(fail, function(e) {
+ assert.ok(e instanceof error.UnknownCommandError);
+ assert.equal(e.message, CommandName.GET_ALERT_TEXT);
+ });
+ });
+
+ it('waitsForAlert', function() {
+ var count = 0;
+ executor.on(CommandName.GET_ALERT_TEXT, function() {
+ if (count++ < 3) {
+ throw new error.NoSuchAlertError;
+ } else {
+ return true;
+ }
+ }).on(CommandName.DISMISS_ALERT, () => true);
+
+ return driver.wait(until.alertIsPresent(), 1000).then(function(alert) {
+ assert.equal(count, 4);
+ return alert.dismiss();
+ });
+ });
+
+ // TODO: Remove once GeckoDriver doesn't throw this unwanted error.
+ // See https://github.com/SeleniumHQ/selenium/pull/2137
+ describe('workaround for GeckoDriver', function() {
+ it('doesNotFailWhenCannotConvertNullToObject', function() {
+ var count = 0;
+ executor.on(CommandName.GET_ALERT_TEXT, function() {
+ if (count++ < 3) {
+ throw new error.WebDriverError(`can't convert null to object`);
+ } else {
+ return true;
+ }
+ }).on(CommandName.DISMISS_ALERT, () => true);
+
+ return driver.wait(until.alertIsPresent(), 1000).then(function(alert) {
+ assert.equal(count, 4);
+ return alert.dismiss();
+ });
+ });
+
+ it('keepsRaisingRegularWebdriverError', function() {
+ var webDriverError = new error.WebDriverError;
+
+ executor.on(CommandName.GET_ALERT_TEXT, function() {
+ throw webDriverError;
+ });
+
+ return driver.wait(until.alertIsPresent(), 1000).then(function() {
+ throw new Error('driver did not fail against WebDriverError');
+ }, function(error) {
+ assert.equal(error, webDriverError);
+ });
+ })
+ });
+ });
+
+ it('testUntilTitleIs', function() {
+ var titles = ['foo', 'bar', 'baz'];
+ executor.on(CommandName.GET_TITLE, () => titles.shift());
+
+ return driver.wait(until.titleIs('bar'), 3000).then(function() {
+ assert.deepStrictEqual(titles, ['baz']);
+ });
+ });
+
+ it('testUntilTitleContains', function() {
+ var titles = ['foo', 'froogle', 'google'];
+ executor.on(CommandName.GET_TITLE, () => titles.shift());
+
+ return driver.wait(until.titleContains('oogle'), 3000).then(function() {
+ assert.deepStrictEqual(titles, ['google']);
+ });
+ });
+
+ it('testUntilTitleMatches', function() {
+ var titles = ['foo', 'froogle', 'aaaabc', 'aabbbc', 'google'];
+ executor.on(CommandName.GET_TITLE, () => titles.shift());
+
+ return driver.wait(until.titleMatches(/^a{2,3}b+c$/), 3000)
+ .then(function() {
+ assert.deepStrictEqual(titles, ['google']);
+ });
+ });
+
+ it('testUntilUrlIs', function() {
+ var urls = ['http://www.foo.com', 'https://boo.com', 'http://docs.yes.com'];
+ executor.on(CommandName.GET_CURRENT_URL, () => urls.shift());
+
+ return driver.wait(until.urlIs('https://boo.com'), 3000).then(function() {
+ assert.deepStrictEqual(urls, ['http://docs.yes.com']);
+ });
+ });
+
+ it('testUntilUrlContains', function() {
+ var urls =
+ ['http://foo.com', 'https://groups.froogle.com', 'http://google.com'];
+ executor.on(CommandName.GET_CURRENT_URL, () => urls.shift());
+
+ return driver.wait(until.urlContains('oogle.com'), 3000).then(function() {
+ assert.deepStrictEqual(urls, ['http://google.com']);
+ });
+ });
+
+ it('testUntilUrlMatches', function() {
+ var urls = ['foo', 'froogle', 'aaaabc', 'aabbbc', 'google'];
+ executor.on(CommandName.GET_CURRENT_URL, () => urls.shift());
+
+ return driver.wait(until.urlMatches(/^a{2,3}b+c$/), 3000)
+ .then(function() {
+ assert.deepStrictEqual(urls, ['google']);
+ });
+ });
+
+ it('testUntilElementLocated', function() {
+ var responses = [
+ [],
+ [WebElement.buildId('abc123'), WebElement.buildId('foo')],
+ ['end']
+ ];
+ executor.on(CommandName.FIND_ELEMENTS, () => responses.shift());
+
+ let element = driver.wait(until.elementLocated(By.id('quux')), 2000);
+ assert.ok(element instanceof webdriver.WebElementPromise);
+ return element.getId().then(function(id) {
+ assert.deepStrictEqual(responses, [['end']]);
+ assert.equal(id, 'abc123');
+ });
+ });
+
+ describe('untilElementLocated, elementNeverFound', function() {
+ function runNoElementFoundTest(locator, locatorStr) {
+ executor.on(CommandName.FIND_ELEMENTS, () => []);
+
+ function expectedFailure() {
+ fail('expected condition to timeout');
+ }
+
+ return driver.wait(until.elementLocated(locator), 100)
+ .then(expectedFailure, function(error) {
+ var expected = 'Waiting for element to be located ' + locatorStr;
+ var lines = error.message.split(/\n/, 2);
+ assert.equal(lines[0], expected);
+
+ let regex = /^Wait timed out after \d+ms$/;
+ assert.ok(regex.test(lines[1]),
+ `Lines <${lines[1]}> does not match ${regex}`);
+ });
+ }
+
+ it('byLocator', function() {
+ return runNoElementFoundTest(
+ By.id('quux'), 'By(css selector, *[id="quux"])');
+ });
+
+ it('byHash', function() {
+ return runNoElementFoundTest(
+ {id: 'quux'}, 'By(css selector, *[id="quux"])');
+ });
+
+ it('byFunction', function() {
+ return runNoElementFoundTest(function() {}, 'by function()');
+ });
+ });
+
+ it('testUntilElementsLocated', function() {
+ var responses = [
+ [],
+ [WebElement.buildId('abc123'), WebElement.buildId('foo')],
+ ['end']
+ ];
+ executor.on(CommandName.FIND_ELEMENTS, () => responses.shift());
+
+ return driver.wait(until.elementsLocated(By.id('quux')), 2000)
+ .then(function(els) {
+ return Promise.all(els.map(e => e.getId()));
+ }).then(function(ids) {
+ assert.deepStrictEqual(responses, [['end']]);
+ assert.equal(ids.length, 2);
+ assert.equal(ids[0], 'abc123');
+ assert.equal(ids[1], 'foo');
+ });
+ });
+
+ describe('untilElementsLocated, noElementsFound', function() {
+ function runNoElementsFoundTest(locator, locatorStr) {
+ executor.on(CommandName.FIND_ELEMENTS, () => []);
+
+ function expectedFailure() {
+ fail('expected condition to timeout');
+ }
+
+ return driver.wait(until.elementsLocated(locator), 100)
+ .then(expectedFailure, function(error) {
+ var expected =
+ 'Waiting for at least one element to be located ' + locatorStr;
+ var lines = error.message.split(/\n/, 2);
+ assert.equal(lines[0], expected);
+
+ let regex = /^Wait timed out after \d+ms$/;
+ assert.ok(regex.test(lines[1]),
+ `Lines <${lines[1]}> does not match ${regex}`);
+ });
+ }
+
+ it('byLocator', function() {
+ return runNoElementsFoundTest(
+ By.id('quux'), 'By(css selector, *[id="quux"])');
+ });
+
+ it('byHash', function() {
+ return runNoElementsFoundTest(
+ {id: 'quux'}, 'By(css selector, *[id="quux"])');
+ });
+
+ it('byFunction', function() {
+ return runNoElementsFoundTest(function() {}, 'by function()');
+ });
+ });
+
+ it('testUntilStalenessOf', function() {
+ let count = 0;
+ executor.on(CommandName.GET_ELEMENT_TAG_NAME, function() {
+ while (count < 3) {
+ count += 1;
+ return 'body';
+ }
+ throw new error.StaleElementReferenceError('now stale');
+ });
+
+ var el = new webdriver.WebElement(driver, {ELEMENT: 'foo'});
+ return driver.wait(until.stalenessOf(el), 2000)
+ .then(() => assert.equal(count, 3));
+ });
+
+ describe('element state conditions', function() {
+ function runElementStateTest(predicate, command, responses, var_args) {
+ let original = new webdriver.WebElement(driver, 'foo');
+ let predicateArgs = [original];
+ if (arguments.length > 3) {
+ predicateArgs = predicateArgs.concat(arguments[1]);
+ command = arguments[2];
+ responses = arguments[3];
+ }
+
+ assert.ok(responses.length > 1);
+
+ responses = responses.concat(['end']);
+ executor.on(command, () => responses.shift());
+
+ let result = driver.wait(predicate.apply(null, predicateArgs), 2000);
+ assert.ok(result instanceof webdriver.WebElementPromise);
+ return result.then(function(value) {
+ assert.ok(value instanceof webdriver.WebElement);
+ assert.ok(!(value instanceof webdriver.WebElementPromise));
+ return value.getId();
+ }).then(function(id) {
+ assert.equal('foo', id);
+ assert.deepStrictEqual(responses, ['end']);
+ });
+ }
+
+ it('elementIsVisible', function() {
+ return runElementStateTest(
+ until.elementIsVisible,
+ CommandName.IS_ELEMENT_DISPLAYED, [false, false, true]);
+ });
+
+ it('elementIsNotVisible', function() {
+ return runElementStateTest(
+ until.elementIsNotVisible,
+ CommandName.IS_ELEMENT_DISPLAYED, [true, true, false]);
+ });
+
+ it('elementIsEnabled', function() {
+ return runElementStateTest(
+ until.elementIsEnabled,
+ CommandName.IS_ELEMENT_ENABLED, [false, false, true]);
+ });
+
+ it('elementIsDisabled', function() {
+ return runElementStateTest(
+ until.elementIsDisabled,
+ CommandName.IS_ELEMENT_ENABLED, [true, true, false]);
+ });
+
+ it('elementIsSelected', function() {
+ return runElementStateTest(
+ until.elementIsSelected,
+ CommandName.IS_ELEMENT_SELECTED, [false, false, true]);
+ });
+
+ it('elementIsNotSelected', function() {
+ return runElementStateTest(
+ until.elementIsNotSelected,
+ CommandName.IS_ELEMENT_SELECTED, [true, true, false]);
+ });
+
+ it('elementTextIs', function() {
+ return runElementStateTest(
+ until.elementTextIs, 'foobar',
+ CommandName.GET_ELEMENT_TEXT,
+ ['foo', 'fooba', 'foobar']);
+ });
+
+ it('elementTextContains', function() {
+ return runElementStateTest(
+ until.elementTextContains, 'bar',
+ CommandName.GET_ELEMENT_TEXT,
+ ['foo', 'foobaz', 'foobarbaz']);
+ });
+
+ it('elementTextMatches', function() {
+ return runElementStateTest(
+ until.elementTextMatches, /fo+bar{3}/,
+ CommandName.GET_ELEMENT_TEXT,
+ ['foo', 'foobar', 'fooobarrr']);
+ });
+ });
+
+ describe('WebElementCondition', function() {
+ it('fails if wait completes with a non-WebElement value', function() {
+ let result = driver.wait(
+ new webdriver.WebElementCondition('testing', () => 123), 1000);
+
+ return result.then(
+ () => assert.fail('expected to fail'),
+ function(e) {
+ assert.ok(e instanceof TypeError);
+ assert.equal(
+ 'WebElementCondition did not resolve to a WebElement: '
+ + '[object Number]',
+ e.message);
+ });
+ });
+ });
+});
diff --git a/node_modules/selenium-webdriver/test/lib/webdriver_test.js b/node_modules/selenium-webdriver/test/lib/webdriver_test.js
new file mode 100644
index 000000000..d2d87ca3e
--- /dev/null
+++ b/node_modules/selenium-webdriver/test/lib/webdriver_test.js
@@ -0,0 +1,2177 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+const testutil = require('./testutil');
+
+const By = require('../../lib/by').By;
+const Capabilities = require('../../lib/capabilities').Capabilities;
+const Executor = require('../../lib/command').Executor;
+const CName = require('../../lib/command').Name;
+const error = require('../../lib/error');
+const Button = require('../../lib/input').Button;
+const Key = require('../../lib/input').Key;
+const logging = require('../../lib/logging');
+const Session = require('../../lib/session').Session;
+const promise = require('../../lib/promise');
+const Alert = require('../../lib/webdriver').Alert;
+const AlertPromise = require('../../lib/webdriver').AlertPromise;
+const UnhandledAlertError = require('../../lib/webdriver').UnhandledAlertError;
+const WebDriver = require('../../lib/webdriver').WebDriver;
+const WebElement = require('../../lib/webdriver').WebElement;
+const WebElementPromise = require('../../lib/webdriver').WebElementPromise;
+
+const assert = require('assert');
+const sinon = require('sinon');
+
+const SESSION_ID = 'test_session_id';
+
+// Aliases for readability.
+const NativePromise = Promise;
+const StubError = testutil.StubError;
+const assertIsInstance = testutil.assertIsInstance;
+const assertIsStubError = testutil.assertIsStubError;
+const throwStubError = testutil.throwStubError;
+const fail = (msg) => assert.fail(msg);
+
+describe('WebDriver', function() {
+ const LOG = logging.getLogger('webdriver.test');
+
+ // before(function() {
+ // logging.getLogger('webdriver').setLevel(logging.Level.ALL);
+ // logging.installConsoleHandler();
+ // });
+
+ // after(function() {
+ // logging.getLogger('webdriver').setLevel(null);
+ // logging.removeConsoleHandler();
+ // });
+
+ var driver;
+ var flow;
+ var uncaughtExceptions;
+
+ beforeEach(function setUp() {
+ flow = promise.controlFlow();
+ uncaughtExceptions = [];
+ flow.on('uncaughtException', onUncaughtException);
+ });
+
+ afterEach(function tearDown() {
+ return waitForIdle(flow).then(function() {
+ assert.deepEqual([], uncaughtExceptions);
+ flow.reset();
+ });
+ });
+
+ function onUncaughtException(e) {
+ uncaughtExceptions.push(e);
+ }
+
+ function waitForIdle(opt_flow) {
+ var theFlow = opt_flow || flow;
+ return new Promise(function(fulfill, reject) {
+ if (theFlow.isIdle()) {
+ fulfill();
+ return;
+ }
+ theFlow.once('idle', fulfill);
+ theFlow.once('uncaughtException', reject);
+ });
+ }
+
+ function waitForAbort(opt_flow, opt_n) {
+ var n = opt_n || 1;
+ var theFlow = opt_flow || flow;
+ theFlow.removeAllListeners(
+ promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION);
+ return new Promise(function(fulfill, reject) {
+ theFlow.once('idle', function() {
+ reject(Error('expected flow to report an unhandled error'));
+ });
+
+ var errors = [];
+ theFlow.on('uncaughtException', onError);
+ function onError(e) {
+ errors.push(e);
+ if (errors.length === n) {
+ theFlow.removeListener('uncaughtException', onError);
+ fulfill(n === 1 ? errors[0] : errors);
+ }
+ }
+ });
+ }
+
+ function expectedError(ctor, message) {
+ return function(e) {
+ assertIsInstance(ctor, e);
+ assert.equal(message, e.message);
+ };
+ }
+
+ class Expectation {
+ constructor(executor, name, opt_parameters) {
+ this.executor_ = executor;
+ this.name_ = name;
+ this.times_ = 1;
+ this.sessionId_ = SESSION_ID;
+ this.check_ = null;
+ this.toDo_ = null;
+ this.withParameters(opt_parameters || {});
+ }
+
+ anyTimes() {
+ this.times_ = Infinity;
+ return this;
+ }
+
+ times(n) {
+ this.times_ = n;
+ return this;
+ }
+
+ withParameters(parameters) {
+ this.parameters_ = parameters;
+ if (this.name_ !== CName.NEW_SESSION) {
+ this.parameters_['sessionId'] = this.sessionId_;
+ }
+ return this;
+ }
+
+ andReturn(code, opt_value) {
+ this.toDo_ = function(command) {
+ LOG.info('executing ' + command.getName() + '; returning ' + code);
+ return Promise.resolve(opt_value !== void(0) ? opt_value : null);
+ };
+ return this;
+ }
+
+ andReturnSuccess(opt_value) {
+ this.toDo_ = function(command) {
+ LOG.info('executing ' + command.getName() + '; returning success');
+ return Promise.resolve(opt_value !== void(0) ? opt_value : null);
+ };
+ return this;
+ }
+
+ andReturnError(error) {
+ if (typeof error === 'number') {
+ throw Error('need error type');
+ }
+ this.toDo_ = function(command) {
+ LOG.info('executing ' + command.getName() + '; returning failure');
+ return Promise.reject(error);
+ };
+ return this;
+ }
+
+ expect(name, opt_parameters) {
+ this.end();
+ return this.executor_.expect(name, opt_parameters);
+ }
+
+ end() {
+ if (!this.toDo_) {
+ this.andReturnSuccess(null);
+ }
+ return this.executor_;
+ }
+
+ execute(command) {
+ assert.deepEqual(this.parameters_, command.getParameters());
+ return this.toDo_(command);
+ }
+ }
+
+ class FakeExecutor {
+ constructor() {
+ this.commands_ = new Map;
+ }
+
+ execute(command) {
+ let expectations = this.commands_.get(command.getName());
+ if (!expectations || !expectations.length) {
+ assert.fail('unexpected command: ' + command.getName());
+ return;
+ }
+
+ let next = expectations[0];
+ let result = next.execute(command);
+ if (next.times_ != Infinity) {
+ next.times_ -= 1;
+ if (!next.times_) {
+ expectations.shift();
+ }
+ }
+ return result;
+ }
+
+ expect(commandName, opt_parameters) {
+ if (!this.commands_.has(commandName)) {
+ this.commands_.set(commandName, []);
+ }
+ let e = new Expectation(this, commandName, opt_parameters);
+ this.commands_.get(commandName).push(e);
+ return e;
+ }
+
+ createDriver(opt_session) {
+ let session = opt_session || new Session(SESSION_ID, {});
+ return new WebDriver(session, this);
+ }
+ }
+
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // Tests
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+
+ describe('testAttachToSession', function() {
+ it('sessionIsAvailable', function() {
+ let aSession = new Session(SESSION_ID, {'browserName': 'firefox'});
+ let executor = new FakeExecutor().
+ expect(CName.DESCRIBE_SESSION).
+ withParameters({'sessionId': SESSION_ID}).
+ andReturnSuccess(aSession).
+ end();
+
+ let driver = WebDriver.attachToSession(executor, SESSION_ID);
+ return driver.getSession().then(v => assert.strictEqual(v, aSession));
+ });
+
+ it('failsToGetSessionInfo', function() {
+ let e = new Error('boom');
+ let executor = new FakeExecutor().
+ expect(CName.DESCRIBE_SESSION).
+ withParameters({'sessionId': SESSION_ID}).
+ andReturnError(e).
+ end();
+
+ let driver = WebDriver.attachToSession(executor, SESSION_ID);
+ return driver.getSession()
+ .then(() => assert.fail('should have failed!'),
+ (actual) => assert.strictEqual(actual, e));
+ });
+
+ it('remote end does not recognize DESCRIBE_SESSION command', function() {
+ let e = new error.UnknownCommandError;
+ let executor = new FakeExecutor().
+ expect(CName.DESCRIBE_SESSION).
+ withParameters({'sessionId': SESSION_ID}).
+ andReturnError(e).
+ end();
+
+ let driver = WebDriver.attachToSession(executor, SESSION_ID);
+ return driver.getSession().then(session => {
+ assert.ok(session instanceof Session);
+ assert.strictEqual(session.getId(), SESSION_ID);
+ assert.equal(session.getCapabilities().size, 0);
+ });
+ });
+
+ it('usesActiveFlowByDefault', function() {
+ let executor = new FakeExecutor().
+ expect(CName.DESCRIBE_SESSION).
+ withParameters({'sessionId': SESSION_ID}).
+ andReturnSuccess({}).
+ end();
+
+ var driver = WebDriver.attachToSession(executor, SESSION_ID);
+ assert.equal(driver.controlFlow(), promise.controlFlow());
+
+ return waitForIdle(driver.controlFlow());
+ });
+
+ it('canAttachInCustomFlow', function() {
+ let executor = new FakeExecutor().
+ expect(CName.DESCRIBE_SESSION).
+ withParameters({'sessionId': SESSION_ID}).
+ andReturnSuccess({}).
+ end();
+
+ var otherFlow = new promise.ControlFlow();
+ var driver = WebDriver.attachToSession(executor, SESSION_ID, otherFlow);
+ assert.equal(otherFlow, driver.controlFlow());
+ assert.notEqual(otherFlow, promise.controlFlow());
+
+ return waitForIdle(otherFlow);
+ });
+ });
+
+ describe('testCreateSession', function() {
+ it('happyPathWithCapabilitiesHashObject', function() {
+ let aSession = new Session(SESSION_ID, {'browserName': 'firefox'});
+ let executor = new FakeExecutor().
+ expect(CName.NEW_SESSION).
+ withParameters({
+ 'desiredCapabilities': {'browserName': 'firefox'}
+ }).
+ andReturnSuccess(aSession).
+ end();
+
+ var driver = WebDriver.createSession(executor, {
+ 'browserName': 'firefox'
+ });
+ return driver.getSession().then(v => assert.strictEqual(v, aSession));
+ });
+
+ it('happyPathWithCapabilitiesInstance', function() {
+ let aSession = new Session(SESSION_ID, {'browserName': 'firefox'});
+ let executor = new FakeExecutor().
+ expect(CName.NEW_SESSION).
+ withParameters({'desiredCapabilities': {'browserName': 'firefox'}}).
+ andReturnSuccess(aSession).
+ end();
+
+ var driver = WebDriver.createSession(executor, Capabilities.firefox());
+ return driver.getSession().then(v => assert.strictEqual(v, aSession));
+ });
+
+ it('handles desired and requried capabilities', function() {
+ let aSession = new Session(SESSION_ID, {'browserName': 'firefox'});
+ let executor = new FakeExecutor().
+ expect(CName.NEW_SESSION).
+ withParameters({
+ 'desiredCapabilities': {'foo': 'bar'},
+ 'requiredCapabilities': {'bim': 'baz'}
+ }).
+ andReturnSuccess(aSession).
+ end();
+
+ let desired = new Capabilities().set('foo', 'bar');
+ let required = new Capabilities().set('bim', 'baz');
+ var driver = WebDriver.createSession(executor, {desired, required});
+ return driver.getSession().then(v => assert.strictEqual(v, aSession));
+ });
+
+ it('failsToCreateSession', function() {
+ let executor = new FakeExecutor().
+ expect(CName.NEW_SESSION).
+ withParameters({'desiredCapabilities': {'browserName': 'firefox'}}).
+ andReturnError(new StubError()).
+ end();
+
+ var driver =
+ WebDriver.createSession(executor, {'browserName': 'firefox'});
+ return driver.getSession().then(fail, assertIsStubError);
+ });
+
+ it('usesActiveFlowByDefault', function() {
+ let executor = new FakeExecutor().
+ expect(CName.NEW_SESSION).
+ withParameters({'desiredCapabilities': {}}).
+ andReturnSuccess(new Session(SESSION_ID)).
+ end();
+
+ var driver = WebDriver.createSession(executor, {});
+ assert.equal(promise.controlFlow(), driver.controlFlow());
+
+ return waitForIdle(driver.controlFlow());
+ });
+
+ it('canCreateInCustomFlow', function() {
+ let executor = new FakeExecutor().
+ expect(CName.NEW_SESSION).
+ withParameters({'desiredCapabilities': {}}).
+ andReturnSuccess({}).
+ end();
+
+ var otherFlow = new promise.ControlFlow();
+ var driver = WebDriver.createSession(executor, {}, otherFlow);
+ assert.equal(otherFlow, driver.controlFlow());
+ assert.notEqual(otherFlow, promise.controlFlow());
+
+ return waitForIdle(otherFlow);
+ });
+ });
+
+ it('testDoesNotExecuteCommandIfSessionDoesNotResolve', function() {
+ var session = Promise.reject(new StubError);
+ new FakeExecutor().createDriver(session).getTitle();
+ return waitForAbort().then(assertIsStubError);
+ });
+
+ it('testCommandReturnValuesArePassedToFirstCallback', function() {
+ let executor = new FakeExecutor().
+ expect(CName.GET_TITLE).andReturnSuccess('Google Search').
+ end();
+
+ var driver = executor.createDriver();
+ return driver.getTitle().then(function(title) {
+ assert.equal('Google Search', title);
+ });
+ });
+
+ it('testStopsCommandExecutionWhenAnErrorOccurs', function() {
+ let e = new error.NoSuchWindowError('window not found');
+ let executor = new FakeExecutor().
+ expect(CName.SWITCH_TO_WINDOW).
+ withParameters({
+ 'name': 'foo',
+ 'handle': 'foo'
+ }).
+ andReturnError(e).
+ end();
+
+ var driver = executor.createDriver();
+ driver.switchTo().window('foo');
+ driver.getTitle(); // mock should blow if this gets executed
+
+ return waitForAbort().then(v => assert.strictEqual(v, e));
+ });
+
+ it('testCanSuppressCommandFailures', function() {
+ let e = new error.NoSuchWindowError('window not found');
+ let executor = new FakeExecutor().
+ expect(CName.SWITCH_TO_WINDOW).
+ withParameters({
+ 'name': 'foo',
+ 'handle': 'foo'
+ }).
+ andReturnError(e).
+ expect(CName.GET_TITLE).
+ andReturnSuccess('Google Search').
+ end();
+
+ var driver = executor.createDriver();
+ driver.switchTo().window('foo')
+ .catch(v => assert.strictEqual(v, e));
+ driver.getTitle();
+ return waitForIdle();
+ });
+
+ it('testErrorsPropagateUpToTheRunningApplication', function() {
+ let e = new error.NoSuchWindowError('window not found');
+ let executor = new FakeExecutor().
+ expect(CName.SWITCH_TO_WINDOW).
+ withParameters({
+ 'name': 'foo',
+ 'handle': 'foo'
+ }).
+ andReturnError(e).
+ end();
+
+ executor.createDriver().switchTo().window('foo');
+ return waitForAbort().then(v => assert.strictEqual(v, e));
+ });
+
+ it('testErrbacksThatReturnErrorsStillSwitchToCallbackChain', function() {
+ let executor = new FakeExecutor().
+ expect(CName.SWITCH_TO_WINDOW).
+ withParameters({
+ 'name': 'foo',
+ 'handle': 'foo'
+ }).
+ andReturnError(new error.NoSuchWindowError('window not found')).
+ end();
+
+ var driver = executor.createDriver();
+ return driver.switchTo().window('foo').
+ catch(function() { return new StubError; });
+ then(assertIsStubError);
+ });
+
+ it('testErrbacksThrownCanOverrideOriginalError', function() {
+ let executor = new FakeExecutor().
+ expect(CName.SWITCH_TO_WINDOW, {
+ 'name': 'foo',
+ 'handle': 'foo'
+ }).
+ andReturnError(new error.NoSuchWindowError('window not found')).
+ end();
+
+ var driver = executor.createDriver();
+ driver.switchTo().window('foo').catch(throwStubError);
+
+ return waitForAbort().then(assertIsStubError);
+ });
+
+ it('testCannotScheduleCommandsIfTheSessionIdHasBeenDeleted', function() {
+ var driver = new FakeExecutor().createDriver();
+ delete driver.session_;
+ assert.throws(() => driver.get('http://www.google.com'));
+ });
+
+ it('testDeletesSessionIdAfterQuitting', function() {
+ var driver;
+ let executor = new FakeExecutor().
+ expect(CName.QUIT).
+ end();
+
+ driver = executor.createDriver();
+ return driver.quit().then(function() {
+ assert.equal(void 0, driver.session_);
+ });
+ });
+
+ it('testReportsErrorWhenExecutingCommandsAfterExecutingAQuit', function() {
+ let executor = new FakeExecutor().
+ expect(CName.QUIT).
+ end();
+
+ var driver = executor.createDriver();
+ driver.quit();
+ driver.get('http://www.google.com');
+ return waitForAbort().
+ then(expectedError(
+ error.NoSuchSessionError,
+ 'This driver instance does not have a valid session ID ' +
+ '(did you call WebDriver.quit()?) and may no longer be used.'));
+ });
+
+ it('testCallbackCommandsExecuteBeforeNextCommand', function() {
+ let executor = new FakeExecutor().
+ expect(CName.GET_CURRENT_URL).
+ expect(CName.GET, {'url': 'http://www.google.com'}).
+ expect(CName.CLOSE).
+ expect(CName.GET_TITLE).
+ end();
+
+ var driver = executor.createDriver();
+ driver.getCurrentUrl().then(function() {
+ driver.get('http://www.google.com').then(function() {
+ driver.close();
+ });
+ });
+ driver.getTitle();
+
+ return waitForIdle();
+ });
+
+ it('testEachCallbackFrameRunsToCompletionBeforeTheNext', function() {
+ let executor = new FakeExecutor().
+ expect(CName.GET_TITLE).
+ expect(CName.GET_CURRENT_URL).
+ expect(CName.GET_CURRENT_WINDOW_HANDLE).
+ expect(CName.CLOSE).
+ expect(CName.QUIT).
+ end();
+
+ var driver = executor.createDriver();
+ driver.getTitle().
+ // Everything in this callback...
+ then(function() {
+ driver.getCurrentUrl();
+ driver.getWindowHandle();
+ }).
+ // ...should execute before everything in this callback.
+ then(function() {
+ driver.close();
+ });
+ // This should execute after everything above
+ driver.quit();
+
+ return waitForIdle();
+ });
+
+ describe('nestedCommandFailures', function() {
+ it('bubbleUpToGlobalHandlerIfUnsuppressed', function() {
+ let e = new error.NoSuchWindowError('window not found');
+ let executor = new FakeExecutor().
+ expect(CName.GET_TITLE).
+ expect(CName.SWITCH_TO_WINDOW, {
+ 'name': 'foo',
+ 'handle': 'foo'
+ }).
+ andReturnError(e).
+ end();
+
+ var driver = executor.createDriver();
+ driver.getTitle().then(function() {
+ driver.switchTo().window('foo');
+ });
+
+ return waitForAbort().then(v => assert.strictEqual(v, e));
+ });
+
+ it('canBeSuppressWhenTheyOccur', function() {
+ let executor = new FakeExecutor().
+ expect(CName.GET_TITLE).
+ expect(CName.SWITCH_TO_WINDOW, {
+ 'name': 'foo',
+ 'handle': 'foo'
+ }).
+ andReturnError(new error.NoSuchWindowError('window not found')).
+ expect(CName.CLOSE).
+ end();
+
+ var driver = executor.createDriver();
+ driver.getTitle().then(function() {
+ driver.switchTo().window('foo').catch(function() {});
+ });
+ driver.close();
+
+ return waitForIdle();
+ });
+
+ it('bubbleUpThroughTheFrameStack', function() {
+ let e = new error.NoSuchWindowError('window not found');
+ let executor = new FakeExecutor().
+ expect(CName.GET_TITLE).
+ expect(CName.SWITCH_TO_WINDOW, {
+ 'name': 'foo',
+ 'handle': 'foo'
+ }).
+ andReturnError(e).
+ end();
+
+ var driver = executor.createDriver();
+ driver.getTitle().
+ then(function() {
+ return driver.switchTo().window('foo');
+ }).
+ catch(v => assert.strictEqual(v, e));
+
+ return waitForIdle();
+ });
+
+ it('canBeCaughtAndSuppressed', function() {
+ let executor = new FakeExecutor().
+ expect(CName.GET_TITLE).
+ expect(CName.GET_CURRENT_URL).
+ expect(CName.SWITCH_TO_WINDOW, {
+ 'name': 'foo',
+ 'handle': 'foo'
+ }).
+ andReturnError(new StubError()).
+ expect(CName.CLOSE).
+ end();
+
+ var driver = executor.createDriver();
+ driver.getTitle().then(function() {
+ driver.getCurrentUrl().
+ then(function() {
+ return driver.switchTo().window('foo');
+ }).
+ catch(function() {});
+ driver.close();
+ });
+
+ return waitForIdle();
+ });
+ });
+
+ describe('returningAPromise', function() {
+ it('fromACallback', function() {
+ let executor = new FakeExecutor().
+ expect(CName.GET_TITLE).
+ expect(CName.GET_CURRENT_URL).
+ andReturnSuccess('http://www.google.com').
+ end();
+
+ var driver = executor.createDriver();
+ driver.getTitle().
+ then(function() {
+ return driver.getCurrentUrl();
+ }).
+ then(function(value) {
+ assert.equal('http://www.google.com', value);
+ });
+ return waitForIdle();
+ });
+
+ it('fromAnErrbackSuppressesTheError', function() {
+ var count = 0;
+ let executor = new FakeExecutor().
+ expect(CName.SWITCH_TO_WINDOW, {
+ 'name': 'foo',
+ 'handle': 'foo'
+ }).
+ andReturnError(new StubError()).
+ expect(CName.GET_CURRENT_URL).
+ andReturnSuccess('http://www.google.com').
+ end();
+
+ var driver = executor.createDriver();
+ driver.switchTo().window('foo').
+ catch(function(e) {
+ assertIsStubError(e);
+ count += 1;
+ return driver.getCurrentUrl();
+ }).
+ then(function(url) {
+ count += 1;
+ assert.equal('http://www.google.com', url);
+ });
+ return waitForIdle().then(function() {
+ assert.equal(2, count);
+ });
+ });
+ });
+
+ describe('customFunctions', function() {
+ it('returnsANonPromiseValue', function() {
+ var driver = new FakeExecutor().createDriver();
+ return driver.call(() => 'abc123').then(function(value) {
+ assert.equal('abc123', value);
+ });
+ });
+
+ it('executionOrderWithCustomFunctions', function() {
+ var msg = [];
+ let executor = new FakeExecutor().
+ expect(CName.GET_TITLE).andReturnSuccess('cheese ').
+ expect(CName.GET_CURRENT_URL).andReturnSuccess('tasty').
+ end();
+
+ var driver = executor.createDriver();
+
+ var pushMsg = msg.push.bind(msg);
+ driver.getTitle().then(pushMsg);
+ driver.call(() => 'is ').then(pushMsg);
+ driver.getCurrentUrl().then(pushMsg);
+ driver.call(() => '!').then(pushMsg);
+
+ return waitForIdle().then(function() {
+ assert.equal('cheese is tasty!', msg.join(''));
+ });
+ });
+
+ it('passingArgumentsToACustomFunction', function() {
+ var add = function(a, b) {
+ return a + b;
+ };
+ var driver = new FakeExecutor().createDriver();
+ return driver.call(add, null, 1, 2).then(function(value) {
+ assert.equal(3, value);
+ });
+ });
+
+ it('passingPromisedArgumentsToACustomFunction', function() {
+ var promisedArg = promise.fulfilled(2);
+ var add = function(a, b) {
+ return a + b;
+ };
+ var driver = new FakeExecutor().createDriver();
+ return driver.call(add, null, 1, promisedArg).then(function(value) {
+ assert.equal(3, value);
+ });
+ });
+
+ it('passingArgumentsAndScopeToACustomFunction', function() {
+ function Foo(name) {
+ this.name = name;
+ }
+ Foo.prototype.getName = function() {
+ return this.name;
+ };
+ var foo = new Foo('foo');
+
+ var driver = new FakeExecutor().createDriver();
+ return driver.call(foo.getName, foo).then(function(value) {
+ assert.equal('foo', value);
+ });
+ });
+
+ it('customFunctionThrowsAnError', function() {
+ var driver = new FakeExecutor().createDriver();
+ return driver.call(throwStubError).then(fail, assertIsStubError);
+ });
+
+ it('customFunctionSchedulesCommands', function() {
+ let executor = new FakeExecutor().
+ expect(CName.GET_TITLE).
+ expect(CName.CLOSE).
+ expect(CName.QUIT).
+ end();
+
+ var driver = executor.createDriver();
+ driver.call(function() {
+ driver.getTitle();
+ driver.close();
+ });
+ driver.quit();
+ return waitForIdle();
+ });
+
+ it('returnsATaskResultAfterSchedulingAnother', function() {
+ let executor = new FakeExecutor().
+ expect(CName.GET_TITLE).
+ andReturnSuccess('Google Search').
+ expect(CName.CLOSE).
+ end();
+
+ var driver = executor.createDriver();
+ return driver.call(function() {
+ var title = driver.getTitle();
+ driver.close();
+ return title;
+ }).then(function(title) {
+ assert.equal('Google Search', title);
+ });
+ });
+
+ it('hasANestedCommandThatFails', function() {
+ let executor = new FakeExecutor().
+ expect(CName.SWITCH_TO_WINDOW, {
+ 'name': 'foo',
+ 'handle': 'foo'
+ }).
+ andReturnError(new StubError()).
+ end();
+
+ var driver = executor.createDriver();
+ return driver.call(function() {
+ return driver.switchTo().window('foo');
+ }).then(fail, assertIsStubError);
+ });
+
+ it('doesNotCompleteUntilReturnedPromiseIsResolved', function() {
+ var order = [];
+ var driver = new FakeExecutor().createDriver();
+
+ var d = promise.defer();
+ d.promise.then(function() {
+ order.push('b');
+ });
+
+ driver.call(function() {
+ order.push('a');
+ return d.promise;
+ });
+ driver.call(function() {
+ order.push('c');
+ });
+
+ // timeout to ensure the first function starts its execution before we
+ // trigger d's callbacks.
+ return new Promise(f => setTimeout(f, 0)).then(function() {
+ assert.deepEqual(['a'], order);
+ d.fulfill();
+ return waitForIdle().then(function() {
+ assert.deepEqual(['a', 'b', 'c'], order);
+ });
+ });
+ });
+
+ it('returnsADeferredAction', function() {
+ let executor = new FakeExecutor().
+ expect(CName.GET_TITLE).andReturnSuccess('Google').
+ end();
+
+ var driver = executor.createDriver();
+ driver.call(function() {
+ return driver.getTitle();
+ }).then(function(title) {
+ assert.equal('Google', title);
+ });
+ return waitForIdle();
+ });
+ });
+
+ describe('nestedCommands', function() {
+ it('commandExecutionOrder', function() {
+ var msg = [];
+ var driver = new FakeExecutor().createDriver();
+ driver.call(msg.push, msg, 'a');
+ driver.call(function() {
+ driver.call(msg.push, msg, 'c');
+ driver.call(function() {
+ driver.call(msg.push, msg, 'e');
+ driver.call(msg.push, msg, 'f');
+ });
+ driver.call(msg.push, msg, 'd');
+ });
+ driver.call(msg.push, msg, 'b');
+ return waitForIdle().then(function() {
+ assert.equal('acefdb', msg.join(''));
+ });
+ });
+
+ it('basicUsage', function() {
+ var msg = [];
+ var driver = new FakeExecutor().createDriver();
+ var pushMsg = msg.push.bind(msg);
+ driver.call(() => 'cheese ').then(pushMsg);
+ driver.call(function() {
+ driver.call(() => 'is ').then(pushMsg);
+ driver.call(() => 'tasty').then(pushMsg);
+ });
+ driver.call(() => '!').then(pushMsg);
+ return waitForIdle().then(function() {
+ assert.equal('cheese is tasty!', msg.join(''));
+ });
+ });
+
+ it('canReturnValueFromNestedFunction', function() {
+ var driver = new FakeExecutor().createDriver();
+ return driver.call(function() {
+ return driver.call(function() {
+ return driver.call(() => 'foobar');
+ });
+ }).then(function(value) {
+ assert.equal('foobar', value);
+ });
+ });
+
+ it('normalCommandAfterNestedCommandThatReturnsAnAction', function() {
+ var msg = [];
+ let executor = new FakeExecutor().
+ expect(CName.CLOSE).
+ end();
+ var driver = executor.createDriver();
+ driver.call(function() {
+ return driver.call(function() {
+ msg.push('a');
+ return driver.call(() => 'foobar');
+ });
+ });
+ driver.close().then(function() {
+ msg.push('b');
+ });
+ return waitForIdle().then(function() {
+ assert.equal('ab', msg.join(''));
+ });
+ });
+
+ it('errorsBubbleUp_caught', function() {
+ var driver = new FakeExecutor().createDriver();
+ var result = driver.call(function() {
+ return driver.call(function() {
+ return driver.call(throwStubError);
+ });
+ }).then(fail, assertIsStubError);
+ return Promise.all([waitForIdle(), result]);
+ });
+
+ it('errorsBubbleUp_uncaught', function() {
+ var driver = new FakeExecutor().createDriver();
+ driver.call(function() {
+ return driver.call(function() {
+ return driver.call(throwStubError);
+ });
+ });
+ return waitForAbort().then(assertIsStubError);
+ });
+
+ it('canScheduleCommands', function() {
+ let executor = new FakeExecutor().
+ expect(CName.GET_TITLE).
+ expect(CName.CLOSE).
+ end();
+
+ var driver = executor.createDriver();
+ driver.call(function() {
+ driver.call(function() {
+ driver.getTitle();
+ });
+ driver.close();
+ });
+ return waitForIdle();
+ });
+ });
+
+ describe('WebElementPromise', function() {
+ it('resolvesWhenUnderlyingElementDoes', function() {
+ var el = new WebElement({}, {'ELEMENT': 'foo'});
+ return new WebElementPromise({}, promise.fulfilled(el)).then(function(e) {
+ assert.strictEqual(e, el);
+ });
+ });
+
+ it('resolvesBeforeCallbacksOnWireValueTrigger', function() {
+ var el = new promise.Deferred();
+
+ var element = new WebElementPromise({}, el.promise);
+ var messages = [];
+
+ element.then(function() {
+ messages.push('element resolved');
+ });
+ element.getId().then(function() {
+ messages.push('wire value resolved');
+ });
+
+ assert.deepEqual([], messages);
+ el.fulfill(new WebElement({}, {'ELEMENT': 'foo'}));
+ return waitForIdle().then(function() {
+ assert.deepEqual([
+ 'element resolved',
+ 'wire value resolved'
+ ], messages);
+ });
+ });
+
+ it('isRejectedIfUnderlyingIdIsRejected', function() {
+ var element = new WebElementPromise({}, promise.rejected(new StubError));
+ return element.then(fail, assertIsStubError);
+ });
+ });
+
+ describe('executeScript', function() {
+ it('nullReturnValue', function() {
+ let executor = new FakeExecutor().
+ expect(CName.EXECUTE_SCRIPT).
+ withParameters({
+ 'script': 'return document.body;',
+ 'args': []
+ }).
+ andReturnSuccess(null).
+ end();
+
+ var driver = executor.createDriver();
+ return driver.executeScript('return document.body;')
+ .then((result) => assert.equal(null, result));
+ });
+
+ it('primitiveReturnValue', function() {
+ let executor = new FakeExecutor().
+ expect(CName.EXECUTE_SCRIPT).
+ withParameters({
+ 'script': 'return document.body;',
+ 'args': []
+ }).
+ andReturnSuccess(123).
+ end();
+
+ var driver = executor.createDriver();
+ return driver.executeScript('return document.body;')
+ .then((result) => assert.equal(123, result));
+ });
+
+ it('webElementReturnValue', function() {
+ var json = WebElement.buildId('foo');
+
+ let executor = new FakeExecutor().
+ expect(CName.EXECUTE_SCRIPT).
+ withParameters({
+ 'script': 'return document.body;',
+ 'args': []
+ }).
+ andReturnSuccess(json).
+ end();
+
+ var driver = executor.createDriver();
+ return driver.executeScript('return document.body;')
+ .then((element) => element.getId())
+ .then((id) => assert.equal(id, 'foo'));
+ });
+
+ it('arrayReturnValue', function() {
+ var json = [WebElement.buildId('foo')];
+
+ let executor = new FakeExecutor().
+ expect(CName.EXECUTE_SCRIPT).
+ withParameters({
+ 'script': 'return document.body;',
+ 'args': []
+ }).
+ andReturnSuccess(json).
+ end();
+
+ var driver = executor.createDriver();
+ return driver.executeScript('return document.body;')
+ .then(function(array) {
+ assert.equal(1, array.length);
+ return array[0].getId();
+ })
+ .then((id) => assert.equal('foo', id));
+ });
+
+ it('objectReturnValue', function() {
+ var json = {'foo': WebElement.buildId('foo')};
+
+ let executor = new FakeExecutor().
+ expect(CName.EXECUTE_SCRIPT).
+ withParameters({
+ 'script': 'return document.body;',
+ 'args': []
+ }).
+ andReturnSuccess(json).
+ end();
+
+ var driver = executor.createDriver();
+ var callback;
+ return driver.executeScript('return document.body;')
+ .then((obj) => obj['foo'].getId())
+ .then((id) => assert.equal(id, 'foo'));
+ });
+
+ it('scriptAsFunction', function() {
+ let executor = new FakeExecutor().
+ expect(CName.EXECUTE_SCRIPT).
+ withParameters({
+ 'script': 'return (' + function() {} +
+ ').apply(null, arguments);',
+ 'args': []
+ }).
+ andReturnSuccess(null).
+ end();
+
+ var driver = executor.createDriver();
+ return driver.executeScript(function() {});
+ });
+
+ it('simpleArgumentConversion', function() {
+ let executor = new FakeExecutor().
+ expect(CName.EXECUTE_SCRIPT).
+ withParameters({
+ 'script': 'return 1;',
+ 'args': ['abc', 123, true, [123, {'foo': 'bar'}]]
+ }).
+ andReturnSuccess(null).
+ end();
+
+ var driver = executor.createDriver();
+ return driver.executeScript(
+ 'return 1;', 'abc', 123, true, [123, {'foo': 'bar'}]);
+ });
+
+ it('webElementArgumentConversion', function() {
+ var elementJson = WebElement.buildId('fefifofum');
+
+ let executor = new FakeExecutor().
+ expect(CName.EXECUTE_SCRIPT).
+ withParameters({
+ 'script': 'return 1;',
+ 'args': [elementJson]
+ }).
+ andReturnSuccess(null).
+ end();
+
+ var driver = executor.createDriver();
+ return driver.executeScript('return 1;',
+ new WebElement(driver, 'fefifofum'));
+ });
+
+ it('webElementPromiseArgumentConversion', function() {
+ var elementJson = WebElement.buildId('bar');
+
+ let executor = new FakeExecutor().
+ expect(CName.FIND_ELEMENT,
+ {'using': 'css selector', 'value': '*[id="foo"]'}).
+ andReturnSuccess(elementJson).
+ expect(CName.EXECUTE_SCRIPT).
+ withParameters({
+ 'script': 'return 1;',
+ 'args': [elementJson]
+ }).
+ andReturnSuccess(null).
+ end();
+
+ var driver = executor.createDriver();
+ var element = driver.findElement(By.id('foo'));
+ return driver.executeScript('return 1;', element);
+ });
+
+ it('argumentConversion', function() {
+ var elementJson = WebElement.buildId('fefifofum');
+
+ let executor = new FakeExecutor().
+ expect(CName.EXECUTE_SCRIPT).
+ withParameters({
+ 'script': 'return 1;',
+ 'args': ['abc', 123, true, elementJson, [123, {'foo': 'bar'}]]
+ }).
+ andReturnSuccess(null).
+ end();
+
+ var driver = executor.createDriver();
+ var element = new WebElement(driver, 'fefifofum');
+ return driver.executeScript('return 1;',
+ 'abc', 123, true, element, [123, {'foo': 'bar'}]);
+ });
+
+ it('scriptReturnsAnError', function() {
+ let executor = new FakeExecutor().
+ expect(CName.EXECUTE_SCRIPT).
+ withParameters({
+ 'script': 'throw Error(arguments[0]);',
+ 'args': ['bam']
+ }).
+ andReturnError(new StubError).
+ end();
+ var driver = executor.createDriver();
+ return driver.executeScript('throw Error(arguments[0]);', 'bam').
+ then(fail, assertIsStubError);
+ });
+
+ it('failsIfArgumentIsARejectedPromise', function() {
+ let executor = new FakeExecutor();
+
+ var arg = promise.rejected(new StubError);
+ arg.catch(function() {}); // Suppress default handler.
+
+ var driver = executor.createDriver();
+ return driver.executeScript(function() {}, arg).
+ then(fail, assertIsStubError);
+ });
+ });
+
+ describe('executeAsyncScript', function() {
+ it('failsIfArgumentIsARejectedPromise', function() {
+ var arg = promise.rejected(new StubError);
+ arg.catch(function() {}); // Suppress default handler.
+
+ var driver = new FakeExecutor().createDriver();
+ return driver.executeAsyncScript(function() {}, arg).
+ then(fail, assertIsStubError);
+ });
+ });
+
+ describe('findElement', function() {
+ it('elementNotFound', function() {
+ let executor = new FakeExecutor().
+ expect(CName.FIND_ELEMENT,
+ {using: 'css selector', value: '*[id="foo"]'}).
+ andReturnError(new StubError).
+ end();
+
+ var driver = executor.createDriver();
+ var element = driver.findElement(By.id('foo'));
+ element.click(); // This should never execute.
+ return waitForAbort().then(assertIsStubError);
+ });
+
+ it('elementNotFoundInACallback', function() {
+ let executor = new FakeExecutor().
+ expect(CName.FIND_ELEMENT,
+ {using: 'css selector', value: '*[id="foo"]'}).
+ andReturnError(new StubError).
+ end();
+
+ var driver = executor.createDriver();
+ promise.fulfilled().then(function() {
+ var element = driver.findElement(By.id('foo'));
+ return element.click(); // Should not execute.
+ });
+ return waitForAbort().then(assertIsStubError);
+ });
+
+ it('elementFound', function() {
+ let executor = new FakeExecutor().
+ expect(CName.FIND_ELEMENT,
+ {using: 'css selector', value: '*[id="foo"]'}).
+ andReturnSuccess(WebElement.buildId('bar')).
+ expect(CName.CLICK_ELEMENT, {'id': 'bar'}).
+ andReturnSuccess().
+ end();
+
+ var driver = executor.createDriver();
+ var element = driver.findElement(By.id('foo'));
+ element.click();
+ return waitForIdle();
+ });
+
+ it('canUseElementInCallback', function() {
+ let executor = new FakeExecutor().
+ expect(CName.FIND_ELEMENT,
+ {using: 'css selector', value: '*[id="foo"]'}).
+ andReturnSuccess(WebElement.buildId('bar')).
+ expect(CName.CLICK_ELEMENT, {'id': 'bar'}).
+ andReturnSuccess().
+ end();
+
+ var driver = executor.createDriver();
+ driver.findElement(By.id('foo')).then(function(element) {
+ element.click();
+ });
+ return waitForIdle();
+ });
+
+ it('byJs', function() {
+ let executor = new FakeExecutor().
+ expect(CName.EXECUTE_SCRIPT, {
+ 'script': 'return document.body',
+ 'args': []
+ }).
+ andReturnSuccess(WebElement.buildId('bar')).
+ expect(CName.CLICK_ELEMENT, {'id': 'bar'}).
+ end();
+
+ var driver = executor.createDriver();
+ var element = driver.findElement(By.js('return document.body'));
+ element.click(); // just to make sure
+ return waitForIdle();
+ });
+
+ it('byJs_returnsNonWebElementValue', function() {
+ let executor = new FakeExecutor().
+ expect(CName.EXECUTE_SCRIPT, {'script': 'return 123', 'args': []}).
+ andReturnSuccess(123).
+ end();
+
+ var driver = executor.createDriver();
+ var element = driver.findElement(By.js('return 123'));
+ element.click(); // Should not execute.
+ return waitForAbort().then(function(e) {
+ assertIsInstance(TypeError, e);
+ assert.equal('Custom locator did not return a WebElement', e.message);
+ });
+ });
+
+ it('byJs_canPassArguments', function() {
+ var script = 'return document.getElementsByTagName(arguments[0]);';
+ let executor = new FakeExecutor().
+ expect(CName.EXECUTE_SCRIPT, {
+ 'script': script,
+ 'args': ['div']
+ }).
+ andReturnSuccess({'ELEMENT':'one'}).
+ end();
+ var driver = executor.createDriver();
+ driver.findElement(By.js(script, 'div'));
+ return waitForIdle();
+ });
+
+ it('customLocator', function() {
+ let executor = new FakeExecutor().
+ expect(CName.FIND_ELEMENTS, {'using': 'css selector', 'value': 'a'}).
+ andReturnSuccess([
+ WebElement.buildId('foo'),
+ WebElement.buildId('bar')]).
+ expect(CName.CLICK_ELEMENT, {'id': 'foo'}).
+ andReturnSuccess().
+ end();
+
+ var driver = executor.createDriver();
+ var element = driver.findElement(function(d) {
+ assert.equal(driver, d);
+ return d.findElements(By.tagName('a'));
+ });
+ element.click();
+ return waitForIdle();
+ });
+
+ it('customLocatorThrowsIfresultIsNotAWebElement', function() {
+ var driver = new FakeExecutor().createDriver();
+ driver.findElement(function() {
+ return 1;
+ });
+ return waitForAbort().then(function(e) {
+ assertIsInstance(TypeError, e);
+ assert.equal('Custom locator did not return a WebElement', e.message);
+ });
+ });
+ });
+
+ describe('findElements', function() {
+ it('returnsMultipleElements', function() {
+ var ids = ['foo', 'bar', 'baz'];
+ let executor = new FakeExecutor().
+ expect(CName.FIND_ELEMENTS, {'using':'css selector', 'value':'a'}).
+ andReturnSuccess(ids.map(WebElement.buildId)).
+ end();
+
+ var driver = executor.createDriver();
+ return driver.findElements(By.tagName('a'))
+ .then(function(elements) {
+ return promise.all(elements.map(function(e) {
+ assert.ok(e instanceof WebElement);
+ return e.getId();
+ }));
+ })
+ .then((actual) => assert.deepEqual(ids, actual));
+ });
+
+ it('byJs', function() {
+ var ids = ['foo', 'bar', 'baz'];
+ let executor = new FakeExecutor().
+ expect(CName.EXECUTE_SCRIPT, {
+ 'script': 'return document.getElementsByTagName("div");',
+ 'args': []
+ }).
+ andReturnSuccess(ids.map(WebElement.buildId)).
+ end();
+
+ var driver = executor.createDriver();
+
+ return driver.
+ findElements(By.js('return document.getElementsByTagName("div");')).
+ then(function(elements) {
+ return promise.all(elements.map(function(e) {
+ assert.ok(e instanceof WebElement);
+ return e.getId();
+ }));
+ }).
+ then((actual) => assert.deepEqual(ids, actual));
+ });
+
+ it('byJs_filtersOutNonWebElementResponses', function() {
+ var ids = ['foo', 'bar', 'baz'];
+ var json = [
+ WebElement.buildId(ids[0]),
+ 123,
+ 'a',
+ false,
+ WebElement.buildId(ids[1]),
+ {'not a web element': 1},
+ WebElement.buildId(ids[2])
+ ];
+ let executor = new FakeExecutor().
+ expect(CName.EXECUTE_SCRIPT, {
+ 'script': 'return document.getElementsByTagName("div");',
+ 'args': []
+ }).
+ andReturnSuccess(json).
+ end();
+
+ var driver = executor.createDriver();
+ driver.findElements(By.js('return document.getElementsByTagName("div");')).
+ then(function(elements) {
+ return promise.all(elements.map(function(e) {
+ assert.ok(e instanceof WebElement);
+ return e.getId();
+ }));
+ }).
+ then((actual) => assert.deepEqual(ids, actual));
+ return waitForIdle();
+ });
+
+ it('byJs_convertsSingleWebElementResponseToArray', function() {
+ let executor = new FakeExecutor().
+ expect(CName.EXECUTE_SCRIPT, {
+ 'script': 'return document.getElementsByTagName("div");',
+ 'args': []
+ }).
+ andReturnSuccess(WebElement.buildId('foo')).
+ end();
+
+ var driver = executor.createDriver();
+ return driver.
+ findElements(By.js('return document.getElementsByTagName("div");')).
+ then(function(elements) {
+ return promise.all(elements.map(function(e) {
+ assert.ok(e instanceof WebElement);
+ return e.getId();
+ }));
+ }).
+ then((actual) => assert.deepEqual(['foo'], actual));
+ });
+
+ it('byJs_canPassScriptArguments', function() {
+ var script = 'return document.getElementsByTagName(arguments[0]);';
+ let executor = new FakeExecutor().
+ expect(CName.EXECUTE_SCRIPT, {
+ 'script': script,
+ 'args': ['div']
+ }).
+ andReturnSuccess([
+ WebElement.buildId('one'),
+ WebElement.buildId('two')
+ ]).
+ end();
+
+ var driver = executor.createDriver();
+ return driver.findElements(By.js(script, 'div'))
+ then(function(elements) {
+ return promise.all(elements.map(function(e) {
+ assert.ok(e instanceof WebElement);
+ return e.getId();
+ }));
+ }).
+ then((actual) => assert.deepEqual(['one', 'two'], actual));
+ });
+ });
+
+ describe('sendKeys', function() {
+ it('convertsVarArgsIntoStrings_simpleArgs', function() {
+ let executor = new FakeExecutor().
+ expect(CName.SEND_KEYS_TO_ELEMENT,
+ {'id': 'one', 'value':'12abc3'.split('')}).
+ andReturnSuccess().
+ end();
+
+ var driver = executor.createDriver();
+ var element = new WebElement(driver, 'one');
+ element.sendKeys(1, 2, 'abc', 3);
+ return waitForIdle();
+ });
+
+ it('convertsVarArgsIntoStrings_promisedArgs', function() {
+ let executor = new FakeExecutor().
+ expect(CName.FIND_ELEMENT,
+ {'using':'css selector', 'value':'*[id="foo"]'}).
+ andReturnSuccess(WebElement.buildId('one')).
+ expect(CName.SEND_KEYS_TO_ELEMENT,
+ {'id':'one', 'value':'abc123def'.split('')}).
+ andReturnSuccess().
+ end();
+
+ var driver = executor.createDriver();
+ var element = driver.findElement(By.id('foo'));
+ element.sendKeys(
+ promise.fulfilled('abc'), 123,
+ promise.fulfilled('def'));
+ return waitForIdle();
+ });
+
+ it('sendKeysWithAFileDetector', function() {
+ let executor = new FakeExecutor().
+ expect(CName.FIND_ELEMENT,
+ {'using':'css selector', 'value':'*[id="foo"]'}).
+ andReturnSuccess(WebElement.buildId('one')).
+ expect(CName.SEND_KEYS_TO_ELEMENT,
+ {'id': 'one', 'value':'modified/path'.split('')}).
+ andReturnSuccess().
+ end();
+
+ let driver = executor.createDriver();
+ let handleFile = function(d, path) {
+ assert.strictEqual(driver, d);
+ assert.equal(path, 'original/path');
+ return promise.fulfilled('modified/path');
+ };
+ driver.setFileDetector({handleFile});
+
+ var element = driver.findElement(By.id('foo'));
+ element.sendKeys('original/', 'path');
+ return waitForIdle();
+ });
+ });
+
+ describe("switchTo()", function() {
+ describe("window", function() {
+ it('should return a resolved promise when the window is found', function() {
+ let executor = new FakeExecutor().
+ expect(CName.SWITCH_TO_WINDOW).
+ withParameters({
+ 'name': 'foo',
+ 'handle': 'foo'
+ }).
+ andReturnSuccess().
+ end();
+
+ executor.createDriver().switchTo().window('foo');
+ return waitForIdle();
+ });
+
+ it('should propogate exceptions', function() {
+ let e = new error.NoSuchWindowError('window not found');
+ let executor = new FakeExecutor().
+ expect(CName.SWITCH_TO_WINDOW).
+ withParameters({
+ 'name': 'foo',
+ 'handle': 'foo'
+ }).
+ andReturnError(e).
+ end();
+
+ executor.createDriver().switchTo().window('foo');
+ return waitForAbort().then(v => assert.strictEqual(v, e));
+ });
+ });
+ });
+
+ describe('elementEquality', function() {
+ it('isReflexive', function() {
+ var a = new WebElement({}, 'foo');
+ return WebElement.equals(a, a).then(assert.ok);
+ });
+
+ it('failsIfAnInputElementCouldNotBeFound', function() {
+ var id = promise.rejected(new StubError);
+ id.catch(function() {}); // Suppress default handler.
+
+ var driver = new FakeExecutor().createDriver();
+ var a = new WebElement(driver, 'foo');
+ var b = new WebElementPromise(driver, id);
+
+ return WebElement.equals(a, b).then(fail, assertIsStubError);
+ });
+ });
+
+ describe('waiting', function() {
+ it('waitSucceeds', function() {
+ let executor = new FakeExecutor().
+ expect(CName.FIND_ELEMENTS,
+ {using: 'css selector', value: '*[id="foo"]'}).
+ andReturnSuccess([]).
+ times(2).
+ expect(CName.FIND_ELEMENTS,
+ {using: 'css selector', value: '*[id="foo"]'}).
+ andReturnSuccess([WebElement.buildId('bar')]).
+ end();
+
+ var driver = executor.createDriver();
+ driver.wait(function() {
+ return driver.findElements(By.id('foo')).then(els => els.length > 0);
+ }, 200);
+ return waitForIdle();
+ });
+
+ it('waitTimesout_timeoutCaught', function() {
+ let executor = new FakeExecutor().
+ expect(CName.FIND_ELEMENTS,
+ {using: 'css selector', value: '*[id="foo"]'}).
+ andReturnSuccess([]).
+ anyTimes().
+ end();
+
+ var driver = executor.createDriver();
+ return driver.wait(function() {
+ return driver.findElements(By.id('foo')).then(els => els.length > 0);
+ }, 25).then(fail, function(e) {
+ assert.equal('Wait timed out after ',
+ e.message.substring(0, 'Wait timed out after '.length));
+ });
+ });
+
+ it('waitTimesout_timeoutNotCaught', function() {
+ let executor = new FakeExecutor().
+ expect(CName.FIND_ELEMENTS,
+ {using: 'css selector', value: '*[id="foo"]'}).
+ andReturnSuccess([]).
+ anyTimes().
+ end();
+
+ var driver = executor.createDriver();
+ driver.wait(function() {
+ return driver.findElements(By.id('foo')).then(els => els.length > 0);
+ }, 25);
+ return waitForAbort().then(function(e) {
+ assert.equal('Wait timed out after ',
+ e.message.substring(0, 'Wait timed out after '.length));
+ });
+ });
+ });
+
+ describe('alert handling', function() {
+ it('alertResolvesWhenPromisedTextResolves', function() {
+ var deferredText = new promise.Deferred();
+
+ var alert = new AlertPromise({}, deferredText.promise);
+ assert.ok(alert.isPending());
+
+ deferredText.fulfill(new Alert({}, 'foo'));
+ return alert.getText().then(function(text) {
+ assert.equal('foo', text);
+ });
+ });
+
+ it('cannotSwitchToAlertThatIsNotPresent', function() {
+ let e = new error.NoSuchAlertError;
+ let executor = new FakeExecutor()
+ .expect(CName.GET_ALERT_TEXT)
+ .andReturnError(e)
+ .end();
+
+ var driver = executor.createDriver();
+ var alert = driver.switchTo().alert();
+ alert.dismiss(); // Should never execute.
+ return waitForAbort().then(v => assert.strictEqual(v, e));
+ });
+
+ it('alertsBelongToSameFlowAsParentDriver', function() {
+ let executor = new FakeExecutor()
+ .expect(CName.GET_ALERT_TEXT).andReturnSuccess('hello')
+ .end();
+
+ var driver = executor.createDriver();
+ var otherFlow = new promise.ControlFlow();
+ otherFlow.execute(function() {
+ driver.switchTo().alert().then(function() {
+ assert.strictEqual(
+ driver.controlFlow(), promise.controlFlow(),
+ 'Alert should belong to the same flow as its parent driver');
+ });
+ });
+
+ assert.notEqual(otherFlow, driver.controlFlow);
+ return Promise.all([
+ waitForIdle(otherFlow),
+ waitForIdle(driver.controlFlow())
+ ]);
+ });
+
+ it('commandsFailIfAlertNotPresent', function() {
+ let e = new error.NoSuchAlertError;
+ let executor = new FakeExecutor()
+ .expect(CName.GET_ALERT_TEXT)
+ .andReturnError(e)
+ .end();
+
+ var driver = executor.createDriver();
+ var alert = driver.switchTo().alert();
+
+ var expectError = (v) => assert.strictEqual(v, e);
+
+ return alert.getText()
+ .then(fail, expectedError)
+ .then(() => alert.accept())
+ .then(fail, expectedError)
+ .then(() => alert.dismiss())
+ .then(fail, expectError)
+ .then(() => alert.sendKeys('hi'))
+ .then(fail, expectError);
+ });
+ });
+
+ it('testWebElementsBelongToSameFlowAsParentDriver', function() {
+ let executor = new FakeExecutor()
+ .expect(CName.FIND_ELEMENT,
+ {using: 'css selector', value: '*[id="foo"]'})
+ .andReturnSuccess(WebElement.buildId('abc123'))
+ .end();
+
+ var driver = executor.createDriver();
+ var otherFlow = new promise.ControlFlow();
+ otherFlow.execute(function() {
+ driver.findElement({id: 'foo'}).then(function() {
+ assert.equal(driver.controlFlow(), promise.controlFlow());
+ });
+ });
+
+ assert.notEqual(otherFlow, driver.controlFlow);
+ return Promise.all([
+ waitForIdle(otherFlow),
+ waitForIdle(driver.controlFlow())
+ ]);
+ });
+
+ it('testFetchingLogs', function() {
+ let executor = new FakeExecutor().
+ expect(CName.GET_LOG, {'type': 'browser'}).
+ andReturnSuccess([
+ {'level': 'INFO', 'message': 'hello', 'timestamp': 1234},
+ {'level': 'DEBUG', 'message': 'abc123', 'timestamp': 5678}
+ ]).
+ end();
+
+ var driver = executor.createDriver();
+ return driver.manage().logs().get('browser').then(function(entries) {
+ assert.equal(2, entries.length);
+
+ assert.ok(entries[0] instanceof logging.Entry);
+ assert.equal(logging.Level.INFO.value, entries[0].level.value);
+ assert.equal('hello', entries[0].message);
+ assert.equal(1234, entries[0].timestamp);
+
+ assert.ok(entries[1] instanceof logging.Entry);
+ assert.equal(logging.Level.DEBUG.value, entries[1].level.value);
+ assert.equal('abc123', entries[1].message);
+ assert.equal(5678, entries[1].timestamp);
+ });
+ });
+
+ it('testCommandsFailIfInitialSessionCreationFailed', function() {
+ var session = promise.rejected(new StubError);
+
+ var driver = new FakeExecutor().createDriver(session);
+ var navigateResult = driver.get('some-url').then(fail, assertIsStubError);
+ var quitResult = driver.quit().then(fail, assertIsStubError);
+
+ return waitForIdle().then(function() {
+ return promise.all(navigateResult, quitResult);
+ });
+ });
+
+ it('testWebElementCommandsFailIfInitialDriverCreationFailed', function() {
+ var session = Promise.reject(new StubError);
+ var driver = new FakeExecutor().createDriver(session);
+ return driver.findElement(By.id('foo')).click().
+ then(fail, assertIsStubError);
+ });
+
+ it('testWebElementCommansFailIfElementCouldNotBeFound', function() {
+ let e = new error.NoSuchElementError('Unable to find element');
+ let executor = new FakeExecutor().
+ expect(CName.FIND_ELEMENT,
+ {using: 'css selector', value: '*[id="foo"]'}).
+ andReturnError(e).
+ end();
+
+ var driver = executor.createDriver();
+ return driver.findElement(By.id('foo')).click()
+ .then(fail, v => assert.strictEqual(v, e));
+ });
+
+ it('testCannotFindChildElementsIfParentCouldNotBeFound', function() {
+ let e = new error.NoSuchElementError('Unable to find element');
+ let executor = new FakeExecutor().
+ expect(CName.FIND_ELEMENT,
+ {using: 'css selector', value: '*[id="foo"]'}).
+ andReturnError(e).
+ end();
+
+ var driver = executor.createDriver();
+ return driver.findElement(By.id('foo'))
+ .findElement(By.id('bar'))
+ .findElement(By.id('baz'))
+ .then(fail, v => assert.strictEqual(v, e));
+ });
+
+ describe('actions()', function() {
+ it('failsIfInitialDriverCreationFailed', function() {
+ let session = promise.rejected(new StubError);
+ session.catch(function() {});
+ return new FakeExecutor().
+ createDriver(session).
+ actions().
+ mouseDown().
+ mouseUp().
+ perform().
+ catch(assertIsStubError);
+ });
+
+ describe('mouseMove', function() {
+ it('noElement', function() {
+ let executor = new FakeExecutor()
+ .expect(CName.MOVE_TO, {'xoffset': 0, 'yoffset': 125})
+ .andReturnSuccess()
+ .end();
+
+ return executor.createDriver().
+ actions().
+ mouseMove({x: 0, y: 125}).
+ perform();
+ });
+
+ it('element', function() {
+ let executor = new FakeExecutor()
+ .expect(CName.FIND_ELEMENT,
+ {using: 'css selector', value: '*[id="foo"]'})
+ .andReturnSuccess(WebElement.buildId('abc123'))
+ .expect(CName.MOVE_TO,
+ {'element': 'abc123', 'xoffset': 0, 'yoffset': 125})
+ .andReturnSuccess()
+ .end();
+
+ var driver = executor.createDriver();
+ var element = driver.findElement(By.id('foo'));
+ return driver.actions()
+ .mouseMove(element, {x: 0, y: 125})
+ .perform();
+ });
+ });
+
+ it('supportsMouseDown', function() {
+ let executor = new FakeExecutor()
+ .expect(CName.MOUSE_DOWN, {'button': Button.LEFT})
+ .andReturnSuccess()
+ .end();
+
+ return executor.createDriver().
+ actions().
+ mouseDown().
+ perform();
+ });
+
+ it('testActionSequence', function() {
+ let executor = new FakeExecutor()
+ .expect(CName.FIND_ELEMENT,
+ {using: 'css selector', value: '*[id="a"]'})
+ .andReturnSuccess(WebElement.buildId('id1'))
+ .expect(CName.FIND_ELEMENT,
+ {using: 'css selector', value: '*[id="b"]'})
+ .andReturnSuccess(WebElement.buildId('id2'))
+ .expect(CName.SEND_KEYS_TO_ACTIVE_ELEMENT,
+ {'value': [Key.SHIFT]})
+ .andReturnSuccess()
+ .expect(CName.MOVE_TO, {'element': 'id1'})
+ .andReturnSuccess()
+ .expect(CName.CLICK, {'button': Button.LEFT})
+ .andReturnSuccess()
+ .expect(CName.MOVE_TO, {'element': 'id2'})
+ .andReturnSuccess()
+ .expect(CName.CLICK, {'button': Button.LEFT})
+ .andReturnSuccess()
+ .end();
+
+ var driver = executor.createDriver();
+ var element1 = driver.findElement(By.id('a'));
+ var element2 = driver.findElement(By.id('b'));
+
+ return driver.actions()
+ .keyDown(Key.SHIFT)
+ .click(element1)
+ .click(element2)
+ .perform();
+ });
+ });
+
+ describe('touchActions()', function() {
+ it('failsIfInitialDriverCreationFailed', function() {
+ let session = promise.rejected(new StubError);
+ session.catch(function() {});
+ return new FakeExecutor().
+ createDriver(session).
+ touchActions().
+ scroll({x: 3, y: 4}).
+ perform().
+ catch(assertIsStubError);
+ });
+
+ it('testTouchActionSequence', function() {
+ let executor = new FakeExecutor()
+ .expect(CName.TOUCH_DOWN, {x: 1, y: 2}).andReturnSuccess()
+ .expect(CName.TOUCH_MOVE, {x: 3, y: 4}).andReturnSuccess()
+ .expect(CName.TOUCH_UP, {x: 5, y: 6}).andReturnSuccess()
+ .end();
+
+ var driver = executor.createDriver();
+ return driver.touchActions()
+ .tapAndHold({x: 1, y: 2})
+ .move({x: 3, y: 4})
+ .release({x: 5, y: 6})
+ .perform();
+ });
+ });
+
+ describe('generator support', function() {
+ var driver;
+
+ beforeEach(function() {
+ driver = new WebDriver(
+ new Session('test-session', {}),
+ new ExplodingExecutor());
+ });
+
+ it('canUseGeneratorsWithWebDriverCall', function() {
+ return driver.call(function* () {
+ var x = yield promise.fulfilled(1);
+ var y = yield promise.fulfilled(2);
+ return x + y;
+ }).then(function(value) {
+ assert.deepEqual(3, value);
+ });
+ });
+
+ it('canDefineScopeOnGeneratorCall', function() {
+ return driver.call(function* () {
+ var x = yield promise.fulfilled(1);
+ return this.name + x;
+ }, {name: 'Bob'}).then(function(value) {
+ assert.deepEqual('Bob1', value);
+ });
+ });
+
+ it('canSpecifyArgsOnGeneratorCall', function() {
+ return driver.call(function* (a, b) {
+ var x = yield promise.fulfilled(1);
+ var y = yield promise.fulfilled(2);
+ return [x + y, a, b];
+ }, null, 'abc', 123).then(function(value) {
+ assert.deepEqual([3, 'abc', 123], value);
+ });
+ });
+
+ it('canUseGeneratorWithWebDriverWait', function() {
+ var values = [];
+ return driver.wait(function* () {
+ yield values.push(1);
+ values.push(yield promise.delayed(10).then(function() {
+ return 2;
+ }));
+ yield values.push(3);
+ return values.length === 6;
+ }, 250).then(function() {
+ assert.deepEqual([1, 2, 3, 1, 2, 3], values);
+ });
+ });
+
+ /**
+ * @constructor
+ * @implements {CommandExecutor}
+ */
+ function ExplodingExecutor() {}
+
+
+ /** @override */
+ ExplodingExecutor.prototype.execute = function(command, cb) {
+ cb(Error('Unsupported operation'));
+ };
+ });
+
+ describe('wire format', function() {
+ describe('can serialize', function() {
+ function runSerializeTest(input, want) {
+ let executor = new FakeExecutor().
+ expect(CName.NEW_SESSION).
+ withParameters({'desiredCapabilities': want}).
+ andReturnSuccess({'browserName': 'firefox'}).
+ end();
+ return WebDriver.createSession(executor, input)
+ .getSession();
+ }
+
+ it('function as a string', function() {
+ function foo() { return 'foo'; }
+ return runSerializeTest(foo, '' + foo);
+ });
+
+ it('object with toJSON()', function() {
+ return runSerializeTest(
+ new Date(605728511546),
+ '1989-03-12T17:55:11.546Z');
+ });
+
+ it('Session', function() {
+ return runSerializeTest(new Session('foo', {}), 'foo');
+ });
+
+ it('Capabilities', function() {
+ var prefs = new logging.Preferences();
+ prefs.setLevel(logging.Type.BROWSER, logging.Level.DEBUG);
+
+ var caps = Capabilities.chrome();
+ caps.setLoggingPrefs(prefs);
+
+ return runSerializeTest(
+ caps,
+ {
+ 'browserName': 'chrome',
+ 'loggingPrefs': {'browser': 'DEBUG'}
+ });
+ });
+
+ it('WebElement', function() {
+ return runSerializeTest(
+ new WebElement({}, 'fefifofum'),
+ WebElement.buildId('fefifofum'));
+ });
+
+ it('WebElementPromise', function() {
+ return runSerializeTest(
+ new WebElementPromise(
+ {}, promise.fulfilled(new WebElement({}, 'fefifofum'))),
+ WebElement.buildId('fefifofum'));
+ });
+
+ describe('an array', function() {
+ it('with Serializable', function() {
+ return runSerializeTest([new Session('foo', {})], ['foo']);
+ });
+
+ it('with WebElement', function() {
+ return runSerializeTest(
+ [new WebElement({}, 'fefifofum')],
+ [WebElement.buildId('fefifofum')]);
+ });
+
+ it('with WebElementPromise', function() {
+ return runSerializeTest(
+ [new WebElementPromise(
+ {}, promise.fulfilled(new WebElement({}, 'fefifofum')))],
+ [WebElement.buildId('fefifofum')]);
+ });
+
+ it('complex array', function() {
+ var expected = [
+ 'abc', 123, true, WebElement.buildId('fefifofum'),
+ [123, {'foo': 'bar'}]
+ ];
+
+ var element = new WebElement({}, 'fefifofum');
+ var input = ['abc', 123, true, element, [123, {'foo': 'bar'}]];
+ return runSerializeTest(input, expected);
+ });
+
+ it('nested promises', function() {
+ return runSerializeTest(
+ ['abc', Promise.resolve([123, Promise.resolve(true)])],
+ ['abc', [123, true]]);
+ });
+ });
+
+ describe('an object', function() {
+ it('literal', function() {
+ var expected = {sessionId: 'foo'};
+ return runSerializeTest({sessionId: 'foo'}, expected);
+ });
+
+ it('with sub-objects', function() {
+ var expected = {sessionId: {value: 'foo'}};
+ return runSerializeTest(
+ {sessionId: {value: 'foo'}}, expected);
+ });
+
+ it('with values that have toJSON', function() {
+ return runSerializeTest(
+ {a: {b: new Date(605728511546)}},
+ {a: {b: '1989-03-12T17:55:11.546Z'}});
+ });
+
+ it('with a Session', function() {
+ return runSerializeTest(
+ {a: new Session('foo', {})},
+ {a: 'foo'});
+ });
+
+ it('nested', function() {
+ var elementJson = WebElement.buildId('fefifofum');
+ var expected = {
+ 'script': 'return 1',
+ 'args': ['abc', 123, true, elementJson, [123, {'foo': 'bar'}]],
+ 'sessionId': 'foo'
+ };
+
+ var element = new WebElement({}, 'fefifofum');
+ var parameters = {
+ 'script': 'return 1',
+ 'args':['abc', 123, true, element, [123, {'foo': 'bar'}]],
+ 'sessionId': new Session('foo', {})
+ };
+
+ return runSerializeTest(parameters, expected);
+ });
+ });
+ });
+
+ describe('can deserialize', function() {
+ function runDeserializeTest(original, want) {
+ let executor = new FakeExecutor()
+ .expect(CName.GET_CURRENT_URL)
+ .andReturnSuccess(original)
+ .end();
+ let driver = executor.createDriver();
+ return driver.getCurrentUrl().then(function(got) {
+ assert.deepEqual(got, want);
+ });
+ }
+
+ it('primitives', function() {
+ return Promise.all([
+ runDeserializeTest(1, 1),
+ runDeserializeTest('', ''),
+ runDeserializeTest(true, true),
+ runDeserializeTest(undefined, undefined),
+ runDeserializeTest(null, null)
+ ]);
+ });
+
+ it('simple object', function() {
+ return runDeserializeTest(
+ {sessionId: 'foo'},
+ {sessionId: 'foo'});
+ });
+
+ it('nested object', function() {
+ return runDeserializeTest(
+ {'foo': {'bar': 123}},
+ {'foo': {'bar': 123}});
+ });
+
+ it('array', function() {
+ return runDeserializeTest(
+ [{'foo': {'bar': 123}}],
+ [{'foo': {'bar': 123}}]);
+ });
+
+ it('passes through function properties', function() {
+ function bar() {}
+ return runDeserializeTest(
+ [{foo: {'bar': 123}, func: bar}],
+ [{foo: {'bar': 123}, func: bar}]);
+ });
+ });
+ });
+});
diff --git a/node_modules/selenium-webdriver/test/logging_test.js b/node_modules/selenium-webdriver/test/logging_test.js
new file mode 100644
index 000000000..fd88daa72
--- /dev/null
+++ b/node_modules/selenium-webdriver/test/logging_test.js
@@ -0,0 +1,169 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+var Browser = require('..').Browser,
+ By = require('..').By,
+ logging = require('..').logging,
+ assert = require('../testing/assert'),
+ test = require('../lib/test');
+
+test.suite(function(env) {
+ // Logging API has numerous issues with PhantomJS:
+ // - does not support adjusting log levels for type "browser".
+ // - does not return proper log level for "browser" messages.
+ // - does not delete logs after retrieval
+ // Logging API is not supported in IE.
+ // Logging API not supported in Marionette.
+ // Tests depend on opening data URLs, which is broken in Safari (issue 7586)
+ test.ignore(env.browsers(
+ Browser.PHANTOM_JS, Browser.IE, Browser.SAFARI, Browser.FIREFOX)).
+ describe('logging', function() {
+ var driver;
+
+ test.beforeEach(function() {
+ driver = null;
+ });
+
+ test.afterEach(function() {
+ if (driver) {
+ driver.quit();
+ }
+ });
+
+ test.it('can be disabled', function() {
+ var prefs = new logging.Preferences();
+ prefs.setLevel(logging.Type.BROWSER, logging.Level.OFF);
+
+ driver = env.builder()
+ .setLoggingPrefs(prefs)
+ .build();
+
+ driver.get(dataUrl(
+ '<!DOCTYPE html><script>',
+ 'console.info("hello");',
+ 'console.warn("this is a warning");',
+ 'console.error("and this is an error");',
+ '</script>'));
+ driver.manage().logs().get(logging.Type.BROWSER).then(function(entries) {
+ assert(entries.length).equalTo(0);
+ });
+ });
+
+ // Firefox does not capture JS error console log messages.
+ test.ignore(env.browsers(Browser.FIREFOX, 'legacy-firefox')).
+ it('can be turned down', function() {
+ var prefs = new logging.Preferences();
+ prefs.setLevel(logging.Type.BROWSER, logging.Level.SEVERE);
+
+ driver = env.builder()
+ .setLoggingPrefs(prefs)
+ .build();
+
+ driver.get(dataUrl(
+ '<!DOCTYPE html><script>',
+ 'console.info("hello");',
+ 'console.warn("this is a warning");',
+ 'console.error("and this is an error");',
+ '</script>'));
+ driver.manage().logs().get(logging.Type.BROWSER).then(function(entries) {
+ assert(entries.length).equalTo(1);
+ assert(entries[0].level.name).equalTo('SEVERE');
+ assert(entries[0].message).endsWith('and this is an error');
+ });
+ });
+
+ // Firefox does not capture JS error console log messages.
+ test.ignore(env.browsers(Browser.FIREFOX, 'legacy-firefox')).
+ it('can be made verbose', function() {
+ var prefs = new logging.Preferences();
+ prefs.setLevel(logging.Type.BROWSER, logging.Level.DEBUG);
+
+ driver = env.builder()
+ .setLoggingPrefs(prefs)
+ .build();
+
+ driver.get(dataUrl(
+ '<!DOCTYPE html><script>',
+ 'console.debug("hello");',
+ 'console.warn("this is a warning");',
+ 'console.error("and this is an error");',
+ '</script>'));
+ driver.manage().logs().get(logging.Type.BROWSER).then(function(entries) {
+ assert(entries.length).equalTo(3);
+ assert(entries[0].level.name).equalTo('DEBUG');
+ assert(entries[0].message).endsWith('hello');
+
+ assert(entries[1].level.name).equalTo('WARNING');
+ assert(entries[1].message).endsWith('this is a warning');
+
+ assert(entries[2].level.name).equalTo('SEVERE');
+ assert(entries[2].message).endsWith('and this is an error');
+ });
+ });
+
+ // Firefox does not capture JS error console log messages.
+ test.ignore(env.browsers(Browser.FIREFOX, 'legacy-firefox')).
+ it('clears records after retrieval', function() {
+ var prefs = new logging.Preferences();
+ prefs.setLevel(logging.Type.BROWSER, logging.Level.DEBUG);
+
+ driver = env.builder()
+ .setLoggingPrefs(prefs)
+ .build();
+
+ driver.get(dataUrl(
+ '<!DOCTYPE html><script>',
+ 'console.debug("hello");',
+ 'console.warn("this is a warning");',
+ 'console.error("and this is an error");',
+ '</script>'));
+ driver.manage().logs().get(logging.Type.BROWSER).then(function(entries) {
+ assert(entries.length).equalTo(3);
+ });
+ driver.manage().logs().get(logging.Type.BROWSER).then(function(entries) {
+ assert(entries.length).equalTo(0);
+ });
+ });
+
+ test.it('does not mix log types', function() {
+ var prefs = new logging.Preferences();
+ prefs.setLevel(logging.Type.BROWSER, logging.Level.DEBUG);
+ prefs.setLevel(logging.Type.DRIVER, logging.Level.SEVERE);
+
+ driver = env.builder()
+ .setLoggingPrefs(prefs)
+ .build();
+
+ driver.get(dataUrl(
+ '<!DOCTYPE html><script>',
+ 'console.debug("hello");',
+ 'console.warn("this is a warning");',
+ 'console.error("and this is an error");',
+ '</script>'));
+ driver.manage().logs().get(logging.Type.DRIVER).then(function(entries) {
+ assert(entries.length).equalTo(0);
+ });
+ });
+ });
+
+ function dataUrl(var_args) {
+ return 'data:text/html,'
+ + Array.prototype.slice.call(arguments, 0).join('');
+ }
+});
diff --git a/node_modules/selenium-webdriver/test/net/index_test.js b/node_modules/selenium-webdriver/test/net/index_test.js
new file mode 100644
index 000000000..88c52c063
--- /dev/null
+++ b/node_modules/selenium-webdriver/test/net/index_test.js
@@ -0,0 +1,60 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+var assert = require('assert');
+
+var net = require('../../net');
+
+describe('net.splitHostAndPort', function() {
+ it('hostname with no port', function() {
+ assert.deepEqual(
+ net.splitHostAndPort('www.example.com'),
+ {host: 'www.example.com', port: null});
+ });
+
+ it('hostname with port', function() {
+ assert.deepEqual(
+ net.splitHostAndPort('www.example.com:80'),
+ {host: 'www.example.com', port: 80});
+ });
+
+ it('IPv4 with no port', function() {
+ assert.deepEqual(
+ net.splitHostAndPort('127.0.0.1'),
+ {host: '127.0.0.1', port: null});
+ });
+
+ it('IPv4 with port', function() {
+ assert.deepEqual(
+ net.splitHostAndPort('127.0.0.1:1234'),
+ {host: '127.0.0.1', port: 1234});
+ });
+
+ it('IPv6 with no port', function() {
+ assert.deepEqual(
+ net.splitHostAndPort('1234:0:1000:5768:1234:5678:90'),
+ {host: '1234:0:1000:5768:1234:5678:90', port: null});
+ });
+
+ it('IPv6 with port', function() {
+ assert.deepEqual(
+ net.splitHostAndPort('[1234:0:1000:5768:1234:5678:90]:1234'),
+ {host: '1234:0:1000:5768:1234:5678:90', port: 1234});
+ });
+});
diff --git a/node_modules/selenium-webdriver/test/net/portprober_test.js b/node_modules/selenium-webdriver/test/net/portprober_test.js
new file mode 100644
index 000000000..03a2f7a10
--- /dev/null
+++ b/node_modules/selenium-webdriver/test/net/portprober_test.js
@@ -0,0 +1,129 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+var assert = require('assert'),
+ net = require('net');
+
+var promise = require('../..').promise,
+ portprober = require('../../net/portprober');
+
+describe('isFree', function() {
+
+ var server;
+
+ beforeEach(function() {
+ server = net.createServer(function(){});
+ })
+
+ afterEach(function(done) {
+ if (!server) return done();
+ server.close(function() {
+ done();
+ });
+ });
+
+ it('should work for INADDR_ANY', function(done) {
+ server.listen(0, function() {
+ var port = server.address().port;
+ assertPortNotfree(port).then(function() {
+ var done = promise.defer();
+ server.close(function() {
+ server = null;
+ done.fulfill(assertPortIsFree(port));
+ });
+ return done.promise;
+ }).then(function() { done(); }, done);
+ });
+ });
+
+ it('should work for a specific host', function(done) {
+ var host = '127.0.0.1';
+ server.listen(0, host, function() {
+ var port = server.address().port;
+ assertPortNotfree(port, host).then(function() {
+ var done = promise.defer();
+ server.close(function() {
+ server = null;
+ done.fulfill(assertPortIsFree(port, host));
+ });
+ return done.promise;
+ }).then(function() { done(); }, done);
+ });
+ });
+});
+
+describe('findFreePort', function() {
+ var server;
+
+ beforeEach(function() {
+ server = net.createServer(function(){});
+ })
+
+ afterEach(function(done) {
+ if (!server) return done();
+ server.close(function() {
+ done();
+ });
+ });
+
+ it('should work for INADDR_ANY', function(done) {
+ portprober.findFreePort().then(function(port) {
+ server.listen(port, function() {
+ assertPortNotfree(port).then(function() {
+ var done = promise.defer();
+ server.close(function() {
+ server = null;
+ done.fulfill(assertPortIsFree(port));
+ });
+ return done.promise;
+ }).then(function() { done(); }, done);
+ });
+ });
+ });
+
+ it('should work for a specific host', function(done) {
+ var host = '127.0.0.1';
+ portprober.findFreePort(host).then(function(port) {
+ server.listen(port, host, function() {
+ assertPortNotfree(port, host).then(function() {
+ var done = promise.defer();
+ server.close(function() {
+ server = null;
+ done.fulfill(assertPortIsFree(port, host));
+ });
+ return done.promise;
+ }).then(function() { done(); }, done);
+ });
+ });
+ });
+});
+
+
+function assertPortIsFree(port, opt_host) {
+ return portprober.isFree(port, opt_host).then(function(free) {
+ assert.ok(free, 'port should be free');
+ });
+}
+
+
+function assertPortNotfree(port, opt_host) {
+ return portprober.isFree(port, opt_host).then(function(free) {
+ assert.ok(!free, 'port should is not free');
+ });
+}
diff --git a/node_modules/selenium-webdriver/test/page_loading_test.js b/node_modules/selenium-webdriver/test/page_loading_test.js
new file mode 100644
index 000000000..098460370
--- /dev/null
+++ b/node_modules/selenium-webdriver/test/page_loading_test.js
@@ -0,0 +1,166 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+var Browser = require('..').Browser,
+ By = require('..').By,
+ until = require('..').until,
+ assert = require('../testing/assert'),
+ error = require('../lib/error'),
+ test = require('../lib/test'),
+ Pages = test.Pages;
+
+
+test.suite(function(env) {
+ var browsers = env.browsers;
+
+ var driver;
+ test.before(function() {
+ driver = env.builder().build();
+ });
+
+ test.after(function() {
+ driver.quit();
+ });
+
+ test.it('should wait for document to be loaded', function() {
+ driver.get(Pages.simpleTestPage);
+ assert(driver.getTitle()).equalTo('Hello WebDriver');
+ });
+
+ test.it('should follow redirects sent in the http response headers',
+ function() {
+ driver.get(Pages.redirectPage);
+ assert(driver.getTitle()).equalTo('We Arrive Here');
+ });
+
+ test.it('should follow meta redirects', function() {
+ driver.get(Pages.metaRedirectPage);
+ assert(driver.getTitle()).equalTo('We Arrive Here');
+ });
+
+ // Skip Firefox; see https://bugzilla.mozilla.org/show_bug.cgi?id=1280300
+ test.ignore(browsers(Browser.FIREFOX)).
+ it('should be able to get a fragment on the current page', function() {
+ driver.get(Pages.xhtmlTestPage);
+ driver.get(Pages.xhtmlTestPage + '#text');
+ driver.findElement(By.id('id1'));
+ });
+
+ test.ignore(browsers(Browser.IPAD, Browser.IPHONE)).
+ it('should wait for all frames to load in a frameset', function() {
+ driver.get(Pages.framesetPage);
+ driver.switchTo().frame(0);
+
+ driver.findElement(By.css('span#pageNumber')).getText().then(function(txt) {
+ assert(txt.trim()).equalTo('1');
+ });
+
+ driver.switchTo().defaultContent();
+ driver.switchTo().frame(1);
+ driver.findElement(By.css('span#pageNumber')).getText().then(function(txt) {
+ assert(txt.trim()).equalTo('2');
+ });
+ });
+
+ test.ignore(browsers(Browser.SAFARI)).
+ it('should be able to navigate back in browser history', function() {
+ driver.get(Pages.formPage);
+
+ driver.findElement(By.id('imageButton')).click();
+ driver.wait(until.titleIs('We Arrive Here'), 2500);
+
+ driver.navigate().back();
+ driver.wait(until.titleIs('We Leave From Here'), 2500);
+ });
+
+ test.ignore(browsers(Browser.SAFARI)).
+ it('should be able to navigate back in presence of iframes', function() {
+ driver.get(Pages.xhtmlTestPage);
+
+ driver.findElement(By.name('sameWindow')).click();
+ driver.wait(until.titleIs('This page has iframes'), 2500);
+
+ driver.navigate().back();
+ driver.wait(until.titleIs('XHTML Test Page'), 2500);
+ });
+
+ test.ignore(browsers(Browser.SAFARI)).
+ it('should be able to navigate forwards in browser history', function() {
+ driver.get(Pages.formPage);
+
+ driver.findElement(By.id('imageButton')).click();
+ driver.wait(until.titleIs('We Arrive Here'), 5000);
+
+ driver.navigate().back();
+ driver.wait(until.titleIs('We Leave From Here'), 5000);
+
+ driver.navigate().forward();
+ driver.wait(until.titleIs('We Arrive Here'), 5000);
+ });
+
+ // PhantomJS 2.0 does not properly reload pages on refresh.
+ test.ignore(browsers(Browser.PHANTOM_JS)).
+ it('should be able to refresh a page', function() {
+ driver.get(Pages.xhtmlTestPage);
+
+ driver.navigate().refresh();
+
+ assert(driver.getTitle()).equalTo('XHTML Test Page');
+ });
+
+ test.it('should return title of page if set', function() {
+ driver.get(Pages.xhtmlTestPage);
+ assert(driver.getTitle()).equalTo('XHTML Test Page');
+
+ driver.get(Pages.simpleTestPage);
+ assert(driver.getTitle()).equalTo('Hello WebDriver');
+ });
+
+ // Only implemented in Firefox.
+ test.ignore(browsers(
+ Browser.CHROME,
+ Browser.IE,
+ Browser.IPAD,
+ Browser.IPHONE,
+ Browser.OPERA,
+ Browser.PHANTOM_JS,
+ Browser.SAFARI)).
+ it('should timeout if page load timeout is set', function() {
+ driver.call(function() {
+ driver.manage().timeouts().pageLoadTimeout(1);
+ driver.get(Pages.sleepingPage + '?time=3').
+ then(function() {
+ throw Error('Should have timed out on page load');
+ }, function(e) {
+ if (!(e instanceof error.ScriptTimeoutError)
+ && !(e instanceof error.TimeoutError)) {
+ throw Error('Unexpected error response: ' + e);
+ }
+ });
+ }).then(resetPageLoad, function(err) {
+ resetPageLoad().finally(function() {
+ throw err;
+ });
+ });
+
+ function resetPageLoad() {
+ return driver.manage().timeouts().pageLoadTimeout(-1);
+ }
+ });
+});
diff --git a/node_modules/selenium-webdriver/test/phantomjs/execute_phantomjs_test.js b/node_modules/selenium-webdriver/test/phantomjs/execute_phantomjs_test.js
new file mode 100644
index 000000000..82a814a31
--- /dev/null
+++ b/node_modules/selenium-webdriver/test/phantomjs/execute_phantomjs_test.js
@@ -0,0 +1,73 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+var assert = require('assert');
+var path = require('path');
+var test = require('../../lib/test');
+
+test.suite(function(env) {
+ var driver;
+
+ test.before(function() {
+ driver = env.builder().build();
+ });
+
+ test.after(function() {
+ driver.quit();
+ });
+
+ var testPageUrl =
+ 'data:text/html,<html><h1>' + path.basename(__filename) + '</h1></html>';
+
+ test.beforeEach(function() {
+ driver.get(testPageUrl);
+ });
+
+ describe('phantomjs.Driver', function() {
+ describe('#executePhantomJS()', function() {
+
+ test.it('can execute scripts using PhantomJS API', function() {
+ return driver.executePhantomJS('return this.url;').then(function(url) {
+ assert.equal(testPageUrl, decodeURIComponent(url));
+ });
+ });
+
+ test.it('can execute scripts as functions', function() {
+ driver.executePhantomJS(function(a, b) {
+ return a + b;
+ }, 1, 2).then(function(result) {
+ assert.equal(3, result);
+ });
+ });
+
+ test.it('can manipulate the current page', function() {
+ driver.manage().addCookie({name: 'foo', value: 'bar'});
+ driver.manage().getCookie('foo').then(function(cookie) {
+ assert.equal('bar', cookie.value);
+ });
+ driver.executePhantomJS(function() {
+ this.clearCookies();
+ });
+ driver.manage().getCookie('foo').then(function(cookie) {
+ assert.equal(null, cookie);
+ });
+ });
+ });
+ });
+}, {browsers: ['phantomjs']});
diff --git a/node_modules/selenium-webdriver/test/proxy_test.js b/node_modules/selenium-webdriver/test/proxy_test.js
new file mode 100644
index 000000000..c25565b48
--- /dev/null
+++ b/node_modules/selenium-webdriver/test/proxy_test.js
@@ -0,0 +1,180 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+var http = require('http'),
+ url = require('url');
+
+var Browser = require('..').Browser,
+ promise = require('..').promise,
+ firefox = require('../firefox'),
+ proxy = require('../proxy'),
+ assert = require('../testing/assert'),
+ test = require('../lib/test'),
+ Server = require('../lib/test/httpserver').Server,
+ Pages = test.Pages;
+
+test.suite(function(env) {
+ function writeResponse(res, body, encoding, contentType) {
+ res.writeHead(200, {
+ 'Content-Length': Buffer.byteLength(body, encoding),
+ 'Content-Type': contentType
+ });
+ res.end(body);
+ }
+
+ function writePacFile(res) {
+ writeResponse(res, [
+ 'function FindProxyForURL(url, host) {',
+ ' if (shExpMatch(url, "' + goodbyeServer.url('*') + '")) {',
+ ' return "DIRECT";',
+ ' }',
+ ' return "PROXY ' + proxyServer.host() + '";',
+ '}'
+ ].join('\n'), 'ascii', 'application/x-javascript-config');
+ }
+
+ var proxyServer = new Server(function(req, res) {
+ var pathname = url.parse(req.url).pathname;
+ if (pathname === '/proxy.pac') {
+ return writePacFile(res);
+ }
+
+ writeResponse(res, [
+ '<!DOCTYPE html>',
+ '<title>Proxy page</title>',
+ '<h3>This is the proxy landing page</h3>'
+ ].join(''), 'utf8', 'text/html; charset=UTF-8');
+ });
+
+ var helloServer = new Server(function(req, res) {
+ writeResponse(res, [
+ '<!DOCTYPE html>',
+ '<title>Hello</title>',
+ '<h3>Hello, world!</h3>'
+ ].join(''), 'utf8', 'text/html; charset=UTF-8');
+ });
+
+ var goodbyeServer = new Server(function(req, res) {
+ writeResponse(res, [
+ '<!DOCTYPE html>',
+ '<title>Goodbye</title>',
+ '<h3>Goodbye, world!</h3>'
+ ].join(''), 'utf8', 'text/html; charset=UTF-8');
+ });
+
+ // Cannot pass start directly to mocha's before, as mocha will interpret the optional
+ // port parameter as an async callback parameter.
+ function mkStartFunc(server) {
+ return function() {
+ return server.start();
+ };
+ }
+
+ test.before(mkStartFunc(proxyServer));
+ test.before(mkStartFunc(helloServer));
+ test.before(mkStartFunc(goodbyeServer));
+
+ test.after(proxyServer.stop.bind(proxyServer));
+ test.after(helloServer.stop.bind(helloServer));
+ test.after(goodbyeServer.stop.bind(goodbyeServer));
+
+ var driver;
+ test.beforeEach(function() { driver = null; });
+ test.afterEach(function() { driver && driver.quit(); });
+
+ function createDriver(proxy) {
+ // For Firefox we need to explicitly enable proxies for localhost by
+ // clearing the network.proxy.no_proxies_on preference.
+ let profile = new firefox.Profile();
+ profile.setPreference('network.proxy.no_proxies_on', '');
+
+ driver = env.builder()
+ .setFirefoxOptions(new firefox.Options().setProfile(profile))
+ .setProxy(proxy)
+ .build();
+ }
+
+ // Proxy support not implemented.
+ test.ignore(env.browsers(Browser.IE, Browser.OPERA, Browser.SAFARI)).
+ describe('manual proxy settings', function() {
+ // phantomjs 1.9.1 in webdriver mode does not appear to respect proxy
+ // settings.
+ test.ignore(env.browsers(Browser.PHANTOM_JS)).
+ it('can configure HTTP proxy host', function() {
+ createDriver(proxy.manual({
+ http: proxyServer.host()
+ }));
+
+ driver.get(helloServer.url());
+ assert(driver.getTitle()).equalTo('Proxy page');
+ assert(driver.findElement({tagName: 'h3'}).getText()).
+ equalTo('This is the proxy landing page');
+ });
+
+ // PhantomJS does not support bypassing the proxy for individual hosts.
+ // geckodriver does not support the bypass option, this must be configured
+ // through profile preferences.
+ test.ignore(env.browsers(
+ Browser.FIREFOX,
+ 'legacy-' + Browser.FIREFOX,
+ Browser.PHANTOM_JS)).
+ it('can bypass proxy for specific hosts', function() {
+ createDriver(proxy.manual({
+ http: proxyServer.host(),
+ bypass: helloServer.host()
+ }));
+
+ driver.get(helloServer.url());
+ assert(driver.getTitle()).equalTo('Hello');
+ assert(driver.findElement({tagName: 'h3'}).getText()).
+ equalTo('Hello, world!');
+
+ driver.get(goodbyeServer.url());
+ assert(driver.getTitle()).equalTo('Proxy page');
+ assert(driver.findElement({tagName: 'h3'}).getText()).
+ equalTo('This is the proxy landing page');
+ });
+
+ // TODO: test ftp and https proxies.
+ });
+
+ // PhantomJS does not support PAC file proxy configuration.
+ // Safari does not support proxies.
+ test.ignore(env.browsers(
+ Browser.IE, Browser.OPERA, Browser.PHANTOM_JS, Browser.SAFARI)).
+ describe('pac proxy settings', function() {
+ test.it('can configure proxy through PAC file', function() {
+ createDriver(proxy.pac(proxyServer.url('/proxy.pac')));
+
+ driver.get(helloServer.url());
+ assert(driver.getTitle()).equalTo('Proxy page');
+ assert(driver.findElement({tagName: 'h3'}).getText()).
+ equalTo('This is the proxy landing page');
+
+ driver.get(goodbyeServer.url());
+ assert(driver.getTitle()).equalTo('Goodbye');
+ assert(driver.findElement({tagName: 'h3'}).getText()).
+ equalTo('Goodbye, world!');
+ });
+ });
+
+ // TODO: figure out how to test direct and system proxy settings.
+ describe.skip('direct proxy settings', function() {});
+ describe.skip('system proxy settings', function() {});
+});
diff --git a/node_modules/selenium-webdriver/test/remote_test.js b/node_modules/selenium-webdriver/test/remote_test.js
new file mode 100644
index 000000000..5edc448bb
--- /dev/null
+++ b/node_modules/selenium-webdriver/test/remote_test.js
@@ -0,0 +1,117 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+var assert = require('assert'),
+ fs = require('fs'),
+ path = require('path');
+
+var promise = require('../').promise,
+ io = require('../io'),
+ cmd = require('../lib/command'),
+ remote = require('../remote');
+
+describe('DriverService', function() {
+ describe('start()', function() {
+ var service;
+
+ beforeEach(function() {
+ service = new remote.DriverService(process.execPath, {
+ port: 1234,
+ args: ['-e', 'process.exit(1)']
+ });
+ });
+
+ afterEach(function() {
+ return service.kill();
+ });
+
+ it('fails if child-process dies', function(done) {
+ this.timeout(1000);
+ service.start(500)
+ .then(expectFailure.bind(null, done), verifyFailure.bind(null, done));
+ });
+
+ it('failures propagate through control flow if child-process dies',
+ function(done) {
+ this.timeout(1000);
+
+ promise.controlFlow().execute(function() {
+ promise.controlFlow().execute(function() {
+ return service.start(500);
+ });
+ })
+ .then(expectFailure.bind(null, done), verifyFailure.bind(null, done));
+ });
+
+ function verifyFailure(done, e) {
+ try {
+ assert.ok(!(e instanceof promise.CancellationError));
+ assert.equal('Server terminated early with status 1', e.message);
+ done();
+ } catch (ex) {
+ done(ex);
+ }
+ }
+
+ function expectFailure(done) {
+ done(Error('expected to fail'));
+ }
+ });
+});
+
+describe('FileDetector', function() {
+ class ExplodingDriver {
+ schedule() {
+ throw Error('unexpected call');
+ }
+ }
+
+ it('returns the original path if the file does not exist', function() {
+ return io.tmpDir(dir => {
+ let theFile = path.join(dir, 'not-there');
+ return (new remote.FileDetector)
+ .handleFile(new ExplodingDriver, theFile)
+ .then(f => assert.equal(f, theFile));
+ });
+ });
+
+ it('returns the original path if it is a directory', function() {
+ return io.tmpDir(dir => {
+ return (new remote.FileDetector)
+ .handleFile(new ExplodingDriver, dir)
+ .then(f => assert.equal(f, dir));
+ });
+ });
+
+ it('attempts to upload valid files', function() {
+ return io.tmpFile(theFile => {
+ return (new remote.FileDetector)
+ .handleFile(
+ new (class FakeDriver {
+ schedule(command) {
+ assert.equal(command.getName(), cmd.Name.UPLOAD_FILE);
+ assert.equal(typeof command.getParameters()['file'], 'string');
+ return Promise.resolve('success!');
+ }
+ }),
+ theFile)
+ .then(f => assert.equal(f, 'success!'));
+ });
+ });
+});
diff --git a/node_modules/selenium-webdriver/test/safari_test.js b/node_modules/selenium-webdriver/test/safari_test.js
new file mode 100644
index 000000000..41525d9cb
--- /dev/null
+++ b/node_modules/selenium-webdriver/test/safari_test.js
@@ -0,0 +1,112 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+const webdriver = require('..'),
+ proxy = require('../proxy'),
+ safari = require('../safari'),
+ assert = require('../testing/assert'),
+ test = require('../lib/test');
+
+
+describe('safari.Options', function() {
+ describe('fromCapabilities', function() {
+ it('returns a new Options instance if none were defined', function() {
+ let options = safari.Options.fromCapabilities(
+ new webdriver.Capabilities());
+ assert(options).instanceOf(safari.Options);
+ });
+
+ it('returns the options instance if present', function() {
+ let options = new safari.Options().setCleanSession(true),
+ caps = options.toCapabilities();
+ assert(safari.Options.fromCapabilities(caps)).equalTo(options);
+ });
+
+ it('extracts supported WebDriver capabilities', function() {
+ let proxyPrefs = proxy.direct(),
+ logPrefs = {},
+ caps = webdriver.Capabilities.chrome()
+ .set(webdriver.Capability.PROXY, proxyPrefs)
+ .set(webdriver.Capability.LOGGING_PREFS, logPrefs)
+ .set('legacyDriver', true);
+
+ let options = safari.Options.fromCapabilities(caps);
+ assert(options.proxy_).equalTo(proxyPrefs);
+ assert(options.logPrefs_).equalTo(logPrefs);
+ assert(options.legacyDriver_).equalTo(true);
+ });
+ });
+
+ describe('toCapabilities', function() {
+ let options;
+
+ before(function() {
+ options = new safari.Options()
+ .setCleanSession(true);
+ });
+
+ it('returns a new capabilities object if one is not provided', function() {
+ let caps = options.toCapabilities();
+ assert(caps).instanceOf(webdriver.Capabilities);
+ assert(caps.get('browserName')).equalTo('safari');
+ assert(caps.get('safari.options')).equalTo(options);
+ });
+
+ it('adds to input capabilities object', function() {
+ let caps = webdriver.Capabilities.safari();
+ assert(options.toCapabilities(caps)).equalTo(caps);
+ assert(caps.get('safari.options')).equalTo(options);
+ });
+
+ it('sets generic driver capabilities', function() {
+ let proxyPrefs = proxy.direct(),
+ loggingPrefs = {};
+
+ options
+ .setLoggingPrefs(loggingPrefs)
+ .setProxy(proxyPrefs)
+ .useLegacyDriver(true);
+
+ let caps = options.toCapabilities();
+ assert(caps.get('proxy')).equalTo(proxyPrefs);
+ assert(caps.get('loggingPrefs')).equalTo(loggingPrefs);
+ assert(caps.get('legacyDriver')).equalTo(true);
+ });
+ });
+});
+
+test.suite(function(env) {
+ describe('safaridriver', function() {
+ let service;
+
+ test.afterEach(function() {
+ if (service) {
+ return service.kill();
+ }
+ });
+
+ test.it('can start safaridriver', function() {
+ service = new safari.ServiceBuilder().build();
+
+ return service.start().then(function(url) {
+ assert(url).matches(/127\.0\.0\.1/);
+ });
+ });
+ });
+}, {browsers: ['safari']});
diff --git a/node_modules/selenium-webdriver/test/session_test.js b/node_modules/selenium-webdriver/test/session_test.js
new file mode 100644
index 000000000..1fb7475b4
--- /dev/null
+++ b/node_modules/selenium-webdriver/test/session_test.js
@@ -0,0 +1,53 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+var WebDriver = require('..').WebDriver,
+ assert = require('../testing/assert'),
+ test = require('../lib/test'),
+ Pages = test.Pages;
+
+
+test.suite(function(env) {
+ var browsers = env.browsers;
+
+ var driver;
+ test.before(function() {
+ driver = env.builder().build();
+ });
+
+ test.after(function() {
+ driver.quit();
+ });
+
+ test.it('can connect to an existing session', function() {
+ driver.get(Pages.simpleTestPage);
+ assert(driver.getTitle()).equalTo('Hello WebDriver');
+
+ return driver.getSession().then(session1 => {
+ let driver2 = WebDriver.attachToSession(
+ driver.getExecutor(),
+ session1.getId());
+
+ assert(driver2.getTitle()).equalTo('Hello WebDriver');
+
+ let session2Id = driver2.getSession().then(s => s.getId());
+ assert(session2Id).equalTo(session1.getId());
+ });
+ });
+});
diff --git a/node_modules/selenium-webdriver/test/stale_element_test.js b/node_modules/selenium-webdriver/test/stale_element_test.js
new file mode 100644
index 000000000..6ab8de7ec
--- /dev/null
+++ b/node_modules/selenium-webdriver/test/stale_element_test.js
@@ -0,0 +1,61 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+var fail = require('assert').fail;
+
+var Browser = require('..').Browser,
+ By = require('..').By,
+ error = require('..').error,
+ until = require('..').until,
+ assert = require('../testing/assert'),
+ test = require('../lib/test'),
+ Pages = test.Pages;
+
+
+test.suite(function(env) {
+ var driver;
+ test.before(function() { driver = env.builder().build(); });
+ test.after(function() { driver.quit(); });
+
+ test.it(
+ 'dynamically removing elements from the DOM trigger a ' +
+ 'StaleElementReferenceError',
+ function() {
+ driver.get(Pages.javascriptPage);
+
+ var toBeDeleted = driver.findElement(By.id('deleted'));
+ assert(toBeDeleted.isDisplayed()).isTrue();
+
+ driver.findElement(By.id('delete')).click();
+ driver.wait(until.stalenessOf(toBeDeleted), 5000);
+ });
+
+ test.it('an element found in a different frame is stale', function() {
+ driver.get(Pages.missedJsReferencePage);
+
+ var frame = driver.findElement(By.css('iframe[name="inner"]'));
+ driver.switchTo().frame(frame);
+
+ var el = driver.findElement(By.id('oneline'));
+ driver.switchTo().defaultContent();
+ el.getText().then(fail, function(e) {
+ assert(e).instanceOf(error.StaleElementReferenceError);
+ });
+ });
+});
diff --git a/node_modules/selenium-webdriver/test/tag_name_test.js b/node_modules/selenium-webdriver/test/tag_name_test.js
new file mode 100644
index 000000000..d5e18a9a2
--- /dev/null
+++ b/node_modules/selenium-webdriver/test/tag_name_test.js
@@ -0,0 +1,34 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+var By = require('..').By,
+ assert = require('../testing/assert'),
+ test = require('../lib/test');
+
+
+test.suite(function(env) {
+ var driver;
+ test.after(function() { driver.quit(); });
+
+ test.it('should return lower case tag name', function() {
+ driver = env.builder().build();
+ driver.get(test.Pages.formPage);
+ assert(driver.findElement(By.id('cheese')).getTagName()).equalTo('input');
+ });
+});
diff --git a/node_modules/selenium-webdriver/test/testing/assert_test.js b/node_modules/selenium-webdriver/test/testing/assert_test.js
new file mode 100644
index 000000000..8c8848254
--- /dev/null
+++ b/node_modules/selenium-webdriver/test/testing/assert_test.js
@@ -0,0 +1,374 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+const assert = require('../../testing/assert');
+
+const AssertionError = require('assert').AssertionError;
+const assertTrue = require('assert').ok;
+const assertEqual = require('assert').equal;
+const assertThrows = require('assert').throws;
+const fail = require('assert').fail;
+
+
+describe('assert', function() {
+ describe('atLeast', function() {
+ it('compares subject >= value', function() {
+ assert(1).atLeast(0);
+ assert(1).atLeast(1);
+ assertThrows(() => assert(1).atLeast(2));
+ });
+
+ it('accepts failure message', function() {
+ assertThrows(
+ () => assert(1).atLeast(2, 'hi there!'),
+ (error) => error.message.indexOf('hi there') != -1);
+ });
+
+ it('fails if given a non-numeric subject', function() {
+ assertThrows(() => assert('a').atLeast(1));
+ });
+
+ it('fails if given a non-numeric bound', function() {
+ assertThrows(() => assert(1).atLeast('a'));
+ });
+
+ it('waits for promised subject', function() {
+ return assert(Promise.resolve(123)).atLeast(100);
+ });
+
+ it('waits for promised subject (with failure)', function() {
+ return assert(Promise.resolve(100))
+ .atLeast(123)
+ .then(() => fail('should have failed'), function(e) {
+ assertInstanceOf(AssertionError, e);
+ assertEqual('100 >= 123', e.message);
+ });
+ });
+ });
+
+ describe('atMost', function() {
+ it('compares subject <= value', function() {
+ assertThrows(() => assert(1).atMost(0));
+ assert(1).atMost(1);
+ assert(1).atMost(2);
+ });
+
+ it('accepts failure message', function() {
+ assertThrows(
+ () => assert(1).atMost(0, 'hi there!'),
+ (error) => error.message.indexOf('hi there!') != -1);
+ });
+
+ it('fails if given a non-numeric subject', function() {
+ assertThrows(() => assert(1).atMost('a'));
+ });
+
+ it('fails if given a non-numeric bound', function() {
+ assertThrows(() => assert('a').atMost(1));
+ });
+
+ it('waits for promised subject', function() {
+ return assert(Promise.resolve(100)).atMost(123);
+ });
+
+ it('waits for promised subject (with failure)', function() {
+ return assert(Promise.resolve(123))
+ .atMost(100)
+ .then(() => fail('should have failed'), function(e) {
+ assertInstanceOf(AssertionError, e);
+ assertEqual('123 <= 100', e.message);
+ });
+ });
+ });
+
+ describe('greaterThan', function() {
+ it('compares subject > value', function() {
+ assertThrows(() => assert(1).greaterThan(1));
+ assertThrows(() => assert(1).greaterThan(2));
+ assert(2).greaterThan(1);
+ });
+
+ it('accepts failure message', function() {
+ assertThrows(
+ () => assert(0).greaterThan(1, 'hi there!'),
+ (error) => error.message.indexOf('hi there!') != -1);
+ });
+
+ it('fails if given a non-numeric subject', function() {
+ assertThrows(() => assert('a').atMost(1));
+ });
+
+ it('fails if given a non-numeric bound', function() {
+ assertThrows(() => assert(1).atMost('a'));
+ });
+
+ it('waits for promised subject', function() {
+ return assert(Promise.resolve(123)).greaterThan(100);
+ });
+
+ it('waits for promised subject (with failure)', function() {
+ return assert(Promise.resolve(100))
+ .greaterThan(123)
+ .then(() => fail('should have failed'), function(e) {
+ assertInstanceOf(AssertionError, e);
+ assertEqual('100 > 123', e.message);
+ });
+ });
+ });
+
+ describe('lessThan', function() {
+ it('compares subject < value', function() {
+ assertThrows(() => assert(1).lessThan(0));
+ assertThrows(() => assert(1).lessThan(1));
+ assert(1).lessThan(2);
+ });
+
+ it('accepts failure message', function() {
+ assertThrows(
+ () => assert(1).lessThan(0, 'hi there!'),
+ (error) => error.message.indexOf('hi there!') != -1);
+ });
+
+ it('fails if given a non-numeric subject', function() {
+ assertThrows(() => assert('a').lessThan(1));
+ });
+
+ it('fails if given a non-numeric bound', function() {
+ assertThrows(() => assert(1).lessThan('a'));
+ });
+
+ it('waits for promised subject', function() {
+ return assert(Promise.resolve(100)).lessThan(123);
+ });
+
+ it('waits for promised subject (with failure)', function() {
+ return assert(Promise.resolve(123))
+ .lessThan(100)
+ .then(() => fail('should have failed'), function(e) {
+ assertInstanceOf(AssertionError, e);
+ assertEqual('123 < 100', e.message);
+ });
+ });
+ });
+
+ describe('closeTo', function() {
+ it('accepts values within epislon of target', function() {
+ assert(123).closeTo(123, 0);
+ assert(123).closeTo(124, 1);
+ assert(125).closeTo(124, 1);
+
+ assertThrows(() => assert(123).closeTo(125, .1));
+ assertThrows(() => assert(1./3).closeTo(.8, .01));
+ });
+
+ it('waits for promised values', function() {
+ let d = Promise.defer();
+ setTimeout(() => d.resolve(123), 10);
+ return assert(d.promise).closeTo(124, 1);
+ });
+ });
+
+ describe('instanceOf', function() {
+ it('works with direct instances', function() {
+ assert(Error('foo')).instanceOf(Error);
+ });
+
+ it('works with sub-types', function() {
+ assert(TypeError('foo')).instanceOf(Error);
+ });
+
+ it('parent types are not instances of sub-types', function() {
+ assertThrows(() => assert(Error('foo')).instanceOf(TypeError));
+ });
+ });
+
+ describe('isNull', function() {
+ it('normal case', function() {
+ assert(null).isNull();
+ assertThrows(() => assert(1).isNull());
+ });
+
+ it('handles promised values', function() {
+ let p = new Promise(function(f) {
+ setTimeout(() => f(null), 10);
+ });
+ return assert(p).isNull();
+ });
+
+ it('does not match on undefined', function() {
+ assertThrows(() => assert(void(0)).isNull());
+ })
+ });
+
+ describe('isUndefined', function() {
+ it('normal case', function() {
+ assert(void(0)).isUndefined();
+ assertThrows(() => assert(1).isUndefined());
+ });
+
+ it('handles promised values', function() {
+ let p = new Promise(function(f) {
+ setTimeout(() => f(void(0)), 10);
+ });
+ return assert(p).isUndefined();
+ });
+
+ it('does not match on null', function() {
+ assertThrows(() => assert(null).isUndefined());
+ })
+ });
+
+ describe('contains', function() {
+ it('works with strings', function() {
+ assert('abc').contains('a');
+ assert('abc').contains('ab');
+ assert('abc').contains('abc');
+ assert('abc').contains('bc');
+ assert('abc').contains('c');
+ assertThrows(() => assert('abc').contains('d'));
+ });
+
+ it('works with arrays', function() {
+ assert([1, 2, 3]).contains(1);
+ assert([1, 2, 3]).contains(2);
+ assert([1, 2, 3]).contains(3);
+ assertThrows(() => assert([1, 2]).contains(3));
+ });
+
+ it('works with maps', function() {
+ let m = new Map;
+ m.set(1, 2);
+ assert(m).contains(1);
+ assertThrows(() => assert(m).contains(2));
+ });
+
+ it('works with sets', function() {
+ let s = new Set;
+ s.add(1);
+ assert(s).contains(1);
+ assertThrows(() => assert(s).contains(2));
+ });
+
+ it('requires an array, string, map, or set subject', function() {
+ assertThrows(() => assert(123).contains('a'));
+ });
+ });
+
+ describe('endsWith', function() {
+ it('works', function() {
+ assert('abc').endsWith('abc');
+ assert('abc').endsWith('bc');
+ assert('abc').endsWith('c');
+ assertThrows(() => assert('abc').endsWith('d'));
+ })
+ });
+
+ describe('startsWith', function() {
+ it('works', function() {
+ assert('abc').startsWith('abc');
+ assert('abc').startsWith('ab');
+ assert('abc').startsWith('a');
+ assertThrows(() => assert('abc').startsWith('d'));
+ })
+ });
+
+ describe('matches', function() {
+ it('requires a regex value', function() {
+ assertThrows(() => assert('abc').matches(1234));
+ });
+
+ it('requires a string value', function() {
+ assertThrows(() => assert(1234).matches(/abc/));
+ });
+
+ it('requires a string value (promise case)', function() {
+ return assert(Promise.resolve(1234))
+ .matches(/abc/)
+ .then(fail, function(error) {
+ assertEqual(
+ 'Expected a string matching /abc/, got <1234> (number)',
+ error.message);
+ });
+ });
+
+ it('applies regex', function() {
+ assert('abc').matches(/abc/);
+ assertThrows(() => assert('def').matches(/abc/));
+ });
+ });
+
+ describe('isTrue', function() {
+ it('only accepts booleans', function() {
+ assertThrows(() => assert(123).isTrue());
+ });
+
+ it('accepts true values', function() {
+ assert(true).isTrue();
+ assert(Boolean('abc')).isTrue();
+ return assert(Promise.resolve(true)).isTrue();
+ });
+
+ it('rejects false values', function() {
+ assertThrows(() => assert(false).isTrue());
+ assertThrows(() => assert(Boolean(0)).isTrue());
+ return assert(Promise.resolve(false)).isTrue()
+ .then(fail, function() {/*no-op, ok*/});
+ });
+ });
+
+ describe('isFalse', function() {
+ it('only accepts booleans', function() {
+ assertThrows(() => assert(123).isFalse());
+ })
+
+ it('accepts false values', function() {
+ assert(false).isFalse();
+ assert(Boolean('')).isFalse();
+ return assert(Promise.resolve(false)).isFalse();
+ });
+
+ it('rejects true values', function() {
+ assertThrows(() => assert(true).isFalse());
+ assertThrows(() => assert(Boolean(1)).isFalse());
+ return assert(Promise.resolve(true)).isFalse()
+ .then(fail, function() {/*no-op, ok*/});
+ });
+ });
+
+ describe('isEqualTo', function() {
+ it('is strict equality', function() {
+ assert('abc').isEqualTo('abc');
+ assert('abc').equals('abc');
+ assert('abc').equalTo('abc');
+ assertThrows(() => assert('1').isEqualTo(1));
+ });
+ });
+
+ describe('notEqualTo', function() {
+ it('tests strict equality', function() {
+ assert('1').notEqualTo(1);
+ assert(1).notEqualTo('1');
+ assertThrows(() => assert('abc').notEqualTo('abc'));
+ });
+ });
+
+ function assertInstanceOf(ctor, value) {
+ assertTrue(value instanceof ctor);
+ }
+});
diff --git a/node_modules/selenium-webdriver/test/testing/index_test.js b/node_modules/selenium-webdriver/test/testing/index_test.js
new file mode 100644
index 000000000..31acff238
--- /dev/null
+++ b/node_modules/selenium-webdriver/test/testing/index_test.js
@@ -0,0 +1,178 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+var assert = require('assert');
+var promise = require('../..').promise;
+
+var test = require('../../testing');
+
+describe('Mocha Integration', function() {
+
+ describe('beforeEach properly binds "this"', function() {
+ beforeEach(function() { this.x = 1; });
+ test.beforeEach(function() { this.x = 2; });
+ it('', function() { assert.equal(this.x, 2); });
+ });
+
+ describe('afterEach properly binds "this"', function() {
+ it('', function() { this.x = 1; });
+ test.afterEach(function() { this.x = 2; });
+ afterEach(function() { assert.equal(this.x, 2); });
+ });
+
+ describe('it properly binds "this"', function() {
+ beforeEach(function() { this.x = 1; });
+ test.it('', function() { this.x = 2; });
+ afterEach(function() { assert.equal(this.x, 2); });
+ });
+
+ describe('timeout handling', function() {
+ describe('it does not reset the control flow on a non-timeout', function() {
+ var flowReset = false;
+
+ beforeEach(function() {
+ flowReset = false;
+ test.controlFlow().once(promise.ControlFlow.EventType.RESET, onreset);
+ });
+
+ test.it('', function() {
+ this.timeout(100);
+ return promise.delayed(50);
+ });
+
+ afterEach(function() {
+ assert.ok(!flowReset);
+ test.controlFlow().removeListener(
+ promise.ControlFlow.EventType.RESET, onreset);
+ });
+
+ function onreset() {
+ flowReset = true;
+ }
+ });
+
+ describe('it resets the control flow after a timeout' ,function() {
+ var timeoutErr, flowReset;
+
+ beforeEach(function() {
+ flowReset = false;
+ test.controlFlow().once(promise.ControlFlow.EventType.RESET, onreset);
+ });
+
+ test.it('', function() {
+ var callback = this.runnable().callback;
+ var test = this;
+ this.runnable().callback = function(err) {
+ timeoutErr = err;
+ // Reset our timeout to 0 so Mocha does not fail the test.
+ test.timeout(0);
+ // When we invoke the real callback, do not pass along the error so
+ // Mocha does not fail the test.
+ return callback.call(this);
+ };
+
+ test.timeout(50);
+ return promise.defer().promise;
+ });
+
+ afterEach(function() {
+ return Promise.resolve().then(function() {
+ test.controlFlow().removeListener(
+ promise.ControlFlow.EventType.RESET, onreset);
+ assert.ok(flowReset, 'control flow was not reset after a timeout');
+ });
+ });
+
+ function onreset() {
+ flowReset = true;
+ }
+ });
+ });
+});
+
+describe('Mocha async "done" support', function() {
+ this.timeout(2*1000);
+
+ var waited = false;
+ var DELAY = 100; // ms enough to notice
+
+ // Each test asynchronously sets waited to true, so clear/check waited
+ // before/after:
+ beforeEach(function() {
+ waited = false;
+ });
+
+ afterEach(function() {
+ assert.strictEqual(waited, true);
+ });
+
+ // --- First, vanilla mocha "it" should support the "done" callback correctly.
+
+ // This 'it' should block until 'done' is invoked
+ it('vanilla delayed', function(done) {
+ setTimeout(function delayedVanillaTimeout() {
+ waited = true;
+ done();
+ }, DELAY);
+ });
+
+ // --- Now with the webdriver wrappers for 'it' should support the "done" callback:
+
+ test.it('delayed', function(done) {
+ assert(done);
+ assert.strictEqual(typeof done, 'function');
+ setTimeout(function delayedTimeoutCallback() {
+ waited = true;
+ done();
+ }, DELAY);
+ });
+
+ // --- And test that the webdriver wrapper for 'it' works with a returned promise, too:
+
+ test.it('delayed by promise', function() {
+ var defer = promise.defer();
+ setTimeout(function delayedPromiseCallback() {
+ waited = true;
+ defer.fulfill('ignored');
+ });
+ return defer.promise;
+ });
+
+});
+
+describe('ControlFlow and "done" work together', function() {
+ var flow, order;
+ before(function() {
+ order = [];
+ flow = test.controlFlow();
+ flow.execute(function() { order.push(1); });
+ });
+
+ test.it('control flow updates and async done', function(done) {
+ flow.execute(function() { order.push(2); });
+ flow.execute(function() { order.push(3); }).then(function() {
+ order.push(4);
+ });
+ done();
+ })
+
+ after(function() {
+ assert.deepEqual([1, 2, 3, 4], order);
+ })
+});
diff --git a/node_modules/selenium-webdriver/test/upload_test.js b/node_modules/selenium-webdriver/test/upload_test.js
new file mode 100644
index 000000000..3329f7ca7
--- /dev/null
+++ b/node_modules/selenium-webdriver/test/upload_test.js
@@ -0,0 +1,86 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+var fs = require('fs');
+
+var Browser = require('..').Browser,
+ By = require('..').By,
+ until = require('..').until,
+ io = require('../io'),
+ remote = require('../remote'),
+ assert = require('../testing/assert'),
+ test = require('../lib/test'),
+ Pages = test.Pages;
+
+test.suite(function(env) {
+ var LOREM_IPSUM_TEXT = 'lorem ipsum dolor sit amet';
+ var FILE_HTML = '<!DOCTYPE html><div>' + LOREM_IPSUM_TEXT + '</div>';
+
+ var fp;
+ test.before(function() {
+ return fp = io.tmpFile().then(function(fp) {
+ fs.writeFileSync(fp, FILE_HTML);
+ return fp;
+ });
+ })
+
+ var driver;
+ test.before(function() {
+ driver = env.builder().build();
+ });
+
+ test.after(function() {
+ if (driver) {
+ driver.quit();
+ }
+ });
+
+ test.ignore(env.browsers(
+ Browser.IPAD,
+ Browser.IPHONE,
+ // Uploads broken in PhantomJS 2.0.
+ // See https://github.com/ariya/phantomjs/issues/12506
+ Browser.PHANTOM_JS,
+ Browser.SAFARI)).
+ it('can upload files', function() {
+ driver.setFileDetector(new remote.FileDetector);
+
+ driver.get(Pages.uploadPage);
+
+ var fp = driver.call(function() {
+ return io.tmpFile().then(function(fp) {
+ fs.writeFileSync(fp, FILE_HTML);
+ return fp;
+ });
+ });
+
+ driver.findElement(By.id('upload')).sendKeys(fp);
+ driver.findElement(By.id('go')).click();
+
+ // Uploading files across a network may take a while, even if they're small.
+ var label = driver.findElement(By.id('upload_label'));
+ driver.wait(until.elementIsNotVisible(label),
+ 10 * 1000, 'File took longer than 10 seconds to upload!');
+
+ var frame = driver.findElement(By.id('upload_target'));
+ driver.switchTo().frame(frame);
+ assert(driver.findElement(By.css('body')).getText())
+ .equalTo(LOREM_IPSUM_TEXT);
+ });
+});
diff --git a/node_modules/selenium-webdriver/test/window_test.js b/node_modules/selenium-webdriver/test/window_test.js
new file mode 100644
index 000000000..6213d19ac
--- /dev/null
+++ b/node_modules/selenium-webdriver/test/window_test.js
@@ -0,0 +1,175 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+var Browser = require('..').Browser,
+ By = require('..').By,
+ assert = require('../testing/assert'),
+ test = require('../lib/test');
+
+
+test.suite(function(env) {
+ var driver;
+
+ test.before(function() { driver = env.builder().build(); });
+ test.after(function() { driver.quit(); });
+
+ test.beforeEach(function() {
+ driver.switchTo().defaultContent();
+ });
+
+ test.it('can set size of the current window', function() {
+ driver.get(test.Pages.echoPage);
+ changeSizeBy(-20, -20);
+ });
+
+ test.it('can set size of the current window from frame', function() {
+ driver.get(test.Pages.framesetPage);
+
+ var frame = driver.findElement({css: 'frame[name="fourth"]'});
+ driver.switchTo().frame(frame);
+ changeSizeBy(-20, -20);
+ });
+
+ test.it('can set size of the current window from iframe', function() {
+ driver.get(test.Pages.iframePage);
+
+ var frame = driver.findElement({css: 'iframe[name="iframe1-name"]'});
+ driver.switchTo().frame(frame);
+ changeSizeBy(-20, -20);
+ });
+
+ test.it('can switch to a new window', function() {
+ driver.get(test.Pages.xhtmlTestPage);
+
+ driver.getWindowHandle().then(function(handle) {
+ driver.getAllWindowHandles().then(function(originalHandles) {
+ driver.findElement(By.linkText("Open new window")).click();
+
+ driver.wait(forNewWindowToBeOpened(originalHandles), 2000);
+
+ assert(driver.getTitle()).equalTo("XHTML Test Page");
+
+ getNewWindowHandle(originalHandles).then(function(newHandle) {
+ driver.switchTo().window(newHandle);
+
+ assert(driver.getTitle()).equalTo("We Arrive Here")
+ });
+ });
+ });
+ });
+
+ // See https://github.com/mozilla/geckodriver/issues/113
+ test.ignore(env.browsers(Browser.FIREFOX)).
+ it('can set the window position of the current window', function() {
+ driver.manage().window().getPosition().then(function(position) {
+ driver.manage().window().setSize(640, 480);
+ driver.manage().window().setPosition(position.x + 10, position.y + 10);
+
+ // For phantomjs, setPosition is a no-op and the "window" stays at (0, 0)
+ if (env.currentBrowser() === Browser.PHANTOM_JS) {
+ driver.manage().window().getPosition().then(function(position) {
+ assert(position.x).equalTo(0);
+ assert(position.y).equalTo(0);
+ });
+ } else {
+ var dx = position.x + 10;
+ var dy = position.y + 10;
+ // On OSX, Safari position's the window relative to below the menubar
+ // at the top of the screen, which is 23 pixels tall.
+ if (env.currentBrowser() === Browser.SAFARI &&
+ process.platform === 'darwin') {
+ dy += 23;
+ }
+ }
+ });
+ });
+
+ // See https://github.com/mozilla/geckodriver/issues/113
+ test.ignore(env.browsers(Browser.FIREFOX)).
+ it('can set the window position from a frame', function() {
+ driver.get(test.Pages.iframePage);
+ driver.switchTo().frame('iframe1-name');
+ driver.manage().window().getPosition().then(function(position) {
+ driver.manage().window().setSize(640, 480);
+ driver.manage().window().setPosition(position.x + 10, position.y + 10);
+
+ // For phantomjs, setPosition is a no-op and the "window" stays at (0, 0)
+ if (env.currentBrowser() === Browser.PHANTOM_JS) {
+ driver.manage().window().getPosition().then(function(position) {
+ assert(position.x).equalTo(0);
+ assert(position.y).equalTo(0);
+ });
+ } else {
+ var dx = position.x + 10;
+ var dy = position.y + 10;
+ // On OSX, Safari position's the window relative to below the menubar
+ // at the top of the screen, which is 23 pixels tall.
+ if (env.currentBrowser() === Browser.SAFARI &&
+ process.platform === 'darwin') {
+ dy += 23;
+ }
+ driver.wait(forPositionToBe(dx, dy), 1000);
+ }
+ });
+ });
+
+ function changeSizeBy(dx, dy) {
+ driver.manage().window().getSize().then(function(size) {
+ driver.manage().window().setSize(size.width + dx, size.height + dy);
+ driver.wait(forSizeToBe(size.width + dx, size.height + dy), 1000);
+ });
+ }
+
+ function forSizeToBe(w, h) {
+ return function() {
+ return driver.manage().window().getSize().then(function(size) {
+ return size.width === w && size.height === h;
+ });
+ };
+ }
+
+ function forPositionToBe(x, y) {
+ return function() {
+ return driver.manage().window().getPosition().then(function(position) {
+ return position.x === x &&
+ // On OSX, the window height may be bumped down 22px for the top
+ // status bar.
+ // On Linux, Opera's window position will be off by 28px.
+ (position.y >= y && position.y <= (y + 28));
+ });
+ };
+ }
+
+ function forNewWindowToBeOpened(originalHandles) {
+ return function() {
+ return driver.getAllWindowHandles().then(function(currentHandles) {
+ return currentHandles.length > originalHandles.length;
+ });
+ };
+ }
+
+ function getNewWindowHandle(originalHandles) {
+ // Note: this assumes there's just one new window.
+ return driver.getAllWindowHandles().then(function(currentHandles) {
+ return currentHandles.filter(function(i) {
+ return originalHandles.indexOf(i) < 0;
+ })[0];
+ });
+ }
+});
diff --git a/node_modules/selenium-webdriver/testing/assert.js b/node_modules/selenium-webdriver/testing/assert.js
new file mode 100644
index 000000000..253aca034
--- /dev/null
+++ b/node_modules/selenium-webdriver/testing/assert.js
@@ -0,0 +1,378 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+/**
+ * @fileoverview Defines a library that simplifies writing assertions against
+ * promised values.
+ *
+ * > <hr>
+ * > __NOTE:__ This module is considered experimental and is subject to
+ * > change, or removal, at any time!
+ * > <hr>
+ *
+ * Sample usage:
+ *
+ * var driver = new webdriver.Builder().build();
+ * driver.get('http://www.google.com');
+ *
+ * assert(driver.getTitle()).equalTo('Google');
+ */
+
+'use strict';
+
+const assert = require('assert');
+
+function trueType(v) {
+ if (v === null) {
+ return 'null';
+ }
+
+ let type = typeof v;
+ if (type === 'object') {
+ if (Array.isArray(v)) {
+ type = 'array';
+ }
+ }
+ return type;
+}
+
+
+function checkType(v, want) {
+ let got = trueType(v);
+ if (got !== want) {
+ throw new TypeError('require ' + want + ', but got ' + got);
+ }
+ return v;
+}
+
+const checkNumber = v => checkType(v, 'number');
+const checkFunction = v => checkType(v, 'function');
+const checkString = v => checkType(v, 'string');
+
+const isFunction = v => trueType(v) === 'function';
+const isNumber = v => trueType(v) === 'number';
+const isObject = v => trueType(v) === 'object';
+const isString = v => trueType(v) === 'string';
+
+
+function describe(value) {
+ let ret;
+ try {
+ ret = `<${String(value)}>`;
+ } catch (e) {
+ ret = `<toString failed: ${e.message}>`;
+ }
+
+ if (null !== value && void(0) !== value) {
+ ret += ` (${trueType(value)})`;
+ }
+
+ return ret;
+}
+
+
+function evaluate(value, predicate) {
+ if (isObject(value) && isFunction(value.then)) {
+ return value.then(predicate);
+ }
+ predicate(value);
+}
+
+
+/**
+ * @private
+ */
+class Assertion {
+ /**
+ * @param {?} subject The subject of this assertion.
+ * @param {boolean=} opt_invert Whether to invert any assertions performed by
+ * this instance.
+ */
+ constructor(subject, opt_invert) {
+ /** @private {?} */
+ this.subject_ = subject;
+ /** @private {boolean} */
+ this.invert_ = !!opt_invert;
+ }
+
+ /**
+ * @param {number} expected The minimum permissible value (inclusive).
+ * @param {string=} opt_message An optional failure message.
+ * @return {(Promise|undefined)} The result of this assertion, if the subject
+ * is a promised-value. Otherwise, the assertion is performed immediately
+ * and nothing is returned.
+ */
+ atLeast(expected, opt_message) {
+ checkNumber(expected);
+ return evaluate(this.subject_, function(actual) {
+ if (!isNumber(actual) || actual < expected) {
+ assert.fail(actual, expected, opt_message, '>=');
+ }
+ });
+ }
+
+ /**
+ * @param {number} expected The maximum permissible value (inclusive).
+ * @param {string=} opt_message An optional failure message.
+ * @return {(Promise|undefined)} The result of this assertion, if the subject
+ * is a promised-value. Otherwise, the assertion is performed immediately
+ * and nothing is returned.
+ */
+ atMost(expected, opt_message) {
+ checkNumber(expected);
+ return evaluate(this.subject_, function (actual) {
+ if (!isNumber(actual) || actual > expected) {
+ assert.fail(actual, expected, opt_message, '<=');
+ }
+ });
+ }
+
+ /**
+ * @param {number} expected The maximum permissible value (exclusive).
+ * @param {string=} opt_message An optional failure message.
+ * @return {(Promise|undefined)} The result of this assertion, if the subject
+ * is a promised-value. Otherwise, the assertion is performed immediately
+ * and nothing is returned.
+ */
+ greaterThan(expected, opt_message) {
+ checkNumber(expected);
+ return evaluate(this.subject_, function(actual) {
+ if (!isNumber(actual) || actual <= expected) {
+ assert.fail(actual, expected, opt_message, '>');
+ }
+ });
+ }
+
+ /**
+ * @param {number} expected The minimum permissible value (exclusive).
+ * @param {string=} opt_message An optional failure message.
+ * @return {(Promise|undefined)} The result of this assertion, if the subject
+ * is a promised-value. Otherwise, the assertion is performed immediately
+ * and nothing is returned.
+ */
+ lessThan(expected, opt_message) {
+ checkNumber(expected);
+ return evaluate(this.subject_, function (actual) {
+ if (!isNumber(actual) || actual >= expected) {
+ assert.fail(actual, expected, opt_message, '<');
+ }
+ });
+ }
+
+ /**
+ * @param {number} expected The desired value.
+ * @param {number} epsilon The maximum distance from the desired value.
+ * @param {string=} opt_message An optional failure message.
+ * @return {(Promise|undefined)} The result of this assertion, if the subject
+ * is a promised-value. Otherwise, the assertion is performed immediately
+ * and nothing is returned.
+ */
+ closeTo(expected, epsilon, opt_message) {
+ checkNumber(expected);
+ checkNumber(epsilon);
+ return evaluate(this.subject_, function(actual) {
+ checkNumber(actual);
+ if (Math.abs(expected - actual) > epsilon) {
+ assert.fail(opt_message || `${actual} === ${expected} (± ${epsilon})`);
+ }
+ });
+ }
+
+ /**
+ * @param {function(new: ?)} ctor The exptected type's constructor.
+ * @param {string=} opt_message An optional failure message.
+ * @return {(Promise|undefined)} The result of this assertion, if the subject
+ * is a promised-value. Otherwise, the assertion is performed immediately
+ * and nothing is returned.
+ */
+ instanceOf(ctor, opt_message) {
+ checkFunction(ctor);
+ return evaluate(this.subject_, function(actual) {
+ if (!(actual instanceof ctor)) {
+ assert.fail(
+ opt_message
+ || `${describe(actual)} instanceof ${ctor.name || ctor}`);
+ }
+ });
+ }
+
+ /**
+ * @param {string=} opt_message An optional failure message.
+ * @return {(Promise|undefined)} The result of this assertion, if the subject
+ * is a promised-value. Otherwise, the assertion is performed immediately
+ * and nothing is returned.
+ */
+ isNull(opt_message) {
+ return this.isEqualTo(null);
+ }
+
+ /**
+ * @param {string=} opt_message An optional failure message.
+ * @return {(Promise|undefined)} The result of this assertion, if the subject
+ * is a promised-value. Otherwise, the assertion is performed immediately
+ * and nothing is returned.
+ */
+ isUndefined(opt_message) {
+ return this.isEqualTo(void(0));
+ }
+
+ /**
+ * Ensures the subject of this assertion is either a string or array
+ * containing the given `value`.
+ *
+ * @param {?} value The value expected to be contained within the subject.
+ * @param {string=} opt_message An optional failure message.
+ * @return {(Promise|undefined)} The result of this assertion, if the subject
+ * is a promised-value. Otherwise, the assertion is performed immediately
+ * and nothing is returned.
+ */
+ contains(value, opt_message) {
+ return evaluate(this.subject_, function(actual) {
+ if (actual instanceof Map || actual instanceof Set) {
+ assert.ok(actual.has(value), opt_message || `${actual}.has(${value})`);
+ } else if (Array.isArray(actual) || isString(actual)) {
+ assert.ok(
+ actual.indexOf(value) !== -1,
+ opt_message || `${actual}.indexOf(${value}) !== -1`);
+ } else {
+ assert.fail(
+ `Expected an array, map, set, or string: got ${describe(actual)}`);
+ }
+ });
+ }
+
+ /**
+ * @param {string} str The expected suffix.
+ * @param {string=} opt_message An optional failure message.
+ * @return {(Promise|undefined)} The result of this assertion, if the subject
+ * is a promised-value. Otherwise, the assertion is performed immediately
+ * and nothing is returned.
+ */
+ endsWith(str, opt_message) {
+ checkString(str);
+ return evaluate(this.subject_, function(actual) {
+ if (!isString(actual) || !actual.endsWith(str)) {
+ assert.fail(actual, str, 'ends with');
+ }
+ });
+ }
+
+ /**
+ * @param {string} str The expected prefix.
+ * @param {string=} opt_message An optional failure message.
+ * @return {(Promise|undefined)} The result of this assertion, if the subject
+ * is a promised-value. Otherwise, the assertion is performed immediately
+ * and nothing is returned.
+ */
+ startsWith(str, opt_message) {
+ checkString(str);
+ return evaluate(this.subject_, function(actual) {
+ if (!isString(actual) || !actual.startsWith(str)) {
+ assert.fail(actual, str, 'starts with');
+ }
+ });
+ }
+
+ /**
+ * @param {!RegExp} regex The regex the subject is expected to match.
+ * @param {string=} opt_message An optional failure message.
+ * @return {(Promise|undefined)} The result of this assertion, if the subject
+ * is a promised-value. Otherwise, the assertion is performed immediately
+ * and nothing is returned.
+ */
+ matches(regex, opt_message) {
+ if (!(regex instanceof RegExp)) {
+ throw TypeError(`Not a RegExp: ${describe(regex)}`);
+ }
+ return evaluate(this.subject_, function(actual) {
+ if (!isString(actual) || !regex.test(actual)) {
+ let message = opt_message
+ || `Expected a string matching ${regex}, got ${describe(actual)}`;
+ assert.fail(actual, regex, message);
+ }
+ });
+ }
+
+ /**
+ * @param {?} value The unexpected value.
+ * @param {string=} opt_message An optional failure message.
+ * @return {(Promise|undefined)} The result of this assertion, if the subject
+ * is a promised-value. Otherwise, the assertion is performed immediately
+ * and nothing is returned.
+ */
+ notEqualTo(value, opt_message) {
+ return evaluate(this.subject_, function(actual) {
+ assert.notStrictEqual(actual, value, opt_message);
+ });
+ }
+
+ /** An alias for {@link #isEqualTo}. */
+ equalTo(value, opt_message) {
+ return this.isEqualTo(value, opt_message);
+ }
+
+ /** An alias for {@link #isEqualTo}. */
+ equals(value, opt_message) {
+ return this.isEqualTo(value, opt_message);
+ }
+
+ /**
+ * @param {?} value The expected value.
+ * @param {string=} opt_message An optional failure message.
+ * @return {(Promise|undefined)} The result of this assertion, if the subject
+ * is a promised-value. Otherwise, the assertion is performed immediately
+ * and nothing is returned.
+ */
+ isEqualTo(value, opt_message) {
+ return evaluate(this.subject_, function(actual) {
+ assert.strictEqual(actual, value, opt_message);
+ });
+ }
+
+ /**
+ * @param {string=} opt_message An optional failure message.
+ * @return {(Promise|undefined)} The result of this assertion, if the subject
+ * is a promised-value. Otherwise, the assertion is performed immediately
+ * and nothing is returned.
+ */
+ isTrue(opt_message) {
+ return this.isEqualTo(true, opt_message);
+ }
+
+ /**
+ * @param {string=} opt_message An optional failure message.
+ * @return {(Promise|undefined)} The result of this assertion, if the subject
+ * is a promised-value. Otherwise, the assertion is performed immediately
+ * and nothing is returned.
+ */
+ isFalse(opt_message) {
+ return this.isEqualTo(false, opt_message);
+ }
+}
+
+
+// PUBLIC API
+
+
+/**
+ * Creates an assertion about the given `value`.
+ * @return {!Assertion} the new assertion.
+ */
+module.exports = function assertThat(value) {
+ return new Assertion(value);
+};
+module.exports.Assertion = Assertion; // Exported to help generated docs
diff --git a/node_modules/selenium-webdriver/testing/index.js b/node_modules/selenium-webdriver/testing/index.js
new file mode 100644
index 000000000..0e9d06232
--- /dev/null
+++ b/node_modules/selenium-webdriver/testing/index.js
@@ -0,0 +1,277 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+/**
+ * @fileoverview Provides wrappers around the following global functions from
+ * [Mocha's BDD interface](https://github.com/mochajs/mocha):
+ *
+ * - after
+ * - afterEach
+ * - before
+ * - beforeEach
+ * - it
+ * - it.only
+ * - it.skip
+ * - xit
+ *
+ * The provided wrappers leverage the {@link webdriver.promise.ControlFlow}
+ * to simplify writing asynchronous tests:
+ *
+ * var By = require('selenium-webdriver').By,
+ * until = require('selenium-webdriver').until,
+ * firefox = require('selenium-webdriver/firefox'),
+ * test = require('selenium-webdriver/testing');
+ *
+ * test.describe('Google Search', function() {
+ * var driver;
+ *
+ * test.before(function() {
+ * driver = new firefox.Driver();
+ * });
+ *
+ * test.after(function() {
+ * driver.quit();
+ * });
+ *
+ * test.it('should append query to title', function() {
+ * driver.get('http://www.google.com/ncr');
+ * driver.findElement(By.name('q')).sendKeys('webdriver');
+ * driver.findElement(By.name('btnG')).click();
+ * driver.wait(until.titleIs('webdriver - Google Search'), 1000);
+ * });
+ * });
+ *
+ * You may conditionally suppress a test function using the exported
+ * "ignore" function. If the provided predicate returns true, the attached
+ * test case will be skipped:
+ *
+ * test.ignore(maybe()).it('is flaky', function() {
+ * if (Math.random() < 0.5) throw Error();
+ * });
+ *
+ * function maybe() { return Math.random() < 0.5; }
+ */
+
+var promise = require('..').promise;
+var flow = promise.controlFlow();
+
+
+/**
+ * Wraps a function so that all passed arguments are ignored.
+ * @param {!Function} fn The function to wrap.
+ * @return {!Function} The wrapped function.
+ */
+function seal(fn) {
+ return function() {
+ fn();
+ };
+}
+
+
+/**
+ * Wraps a function on Mocha's BDD interface so it runs inside a
+ * webdriver.promise.ControlFlow and waits for the flow to complete before
+ * continuing.
+ * @param {!Function} globalFn The function to wrap.
+ * @return {!Function} The new function.
+ */
+function wrapped(globalFn) {
+ return function() {
+ if (arguments.length === 1) {
+ return globalFn(wrapArgument(arguments[0]));
+
+ } else if (arguments.length === 2) {
+ return globalFn(arguments[0], wrapArgument(arguments[1]));
+
+ } else {
+ throw Error('Invalid # arguments: ' + arguments.length);
+ }
+ };
+}
+
+
+function wrapArgument(value) {
+ if (typeof value === 'function') {
+ return makeAsyncTestFn(value);
+ }
+ return value;
+}
+
+
+/**
+ * Make a wrapper to invoke caller's test function, fn. Run the test function
+ * within a ControlFlow.
+ *
+ * Should preserve the semantics of Mocha's Runnable.prototype.run (See
+ * https://github.com/mochajs/mocha/blob/master/lib/runnable.js#L192)
+ *
+ * @param {Function} fn
+ * @return {Function}
+ */
+function makeAsyncTestFn(fn) {
+ var async = fn.length > 0; // if test function expects a callback, its "async"
+
+ var ret = /** @type {function(this: mocha.Context)}*/ (function(done) {
+ var runnable = this.runnable();
+ var mochaCallback = runnable.callback;
+ runnable.callback = function() {
+ flow.reset();
+ return mochaCallback.apply(this, arguments);
+ };
+
+ var testFn = fn.bind(this);
+ flow.execute(function controlFlowExecute() {
+ return new promise.Promise(function(fulfill, reject) {
+ if (async) {
+ // If testFn is async (it expects a done callback), resolve the promise of this
+ // test whenever that callback says to. Any promises returned from testFn are
+ // ignored.
+ testFn(function testFnDoneCallback(err) {
+ if (err) {
+ reject(err);
+ } else {
+ fulfill();
+ }
+ });
+ } else {
+ // Without a callback, testFn can return a promise, or it will
+ // be assumed to have completed synchronously
+ fulfill(testFn());
+ }
+ }, flow);
+ }, runnable.fullTitle()).then(seal(done), done);
+ });
+
+ ret.toString = function() {
+ return fn.toString();
+ };
+
+ return ret;
+}
+
+
+/**
+ * Ignores the test chained to this function if the provided predicate returns
+ * true.
+ * @param {function(): boolean} predicateFn A predicate to call to determine
+ * if the test should be suppressed. This function MUST be synchronous.
+ * @return {!Object} An object with wrapped versions of {@link #it()} and
+ * {@link #describe()} that ignore tests as indicated by the predicate.
+ */
+function ignore(predicateFn) {
+ var describe = wrap(exports.xdescribe, exports.describe);
+ describe.only = wrap(exports.xdescribe, exports.describe.only);
+
+ var it = wrap(exports.xit, exports.it);
+ it.only = wrap(exports.xit, exports.it.only);
+
+ return {
+ describe: describe,
+ it: it
+ };
+
+ function wrap(onSkip, onRun) {
+ return function(title, fn) {
+ if (predicateFn()) {
+ onSkip(title, fn);
+ } else {
+ onRun(title, fn);
+ }
+ };
+ }
+}
+
+
+// PUBLIC API
+
+
+/**
+ * @return {!promise.ControlFlow} the control flow instance used by this module
+ * to coordinate test actions.
+ */
+exports.controlFlow = function(){
+ return flow;
+};
+
+
+/**
+ * Registers a new test suite.
+ * @param {string} name The suite name.
+ * @param {function()=} fn The suite function, or {@code undefined} to define
+ * a pending test suite.
+ */
+exports.describe = global.describe;
+
+/**
+ * Defines a suppressed test suite.
+ * @param {string} name The suite name.
+ * @param {function()=} fn The suite function, or {@code undefined} to define
+ * a pending test suite.
+ */
+exports.xdescribe = global.xdescribe;
+exports.describe.skip = global.describe.skip;
+
+/**
+ * Register a function to call after the current suite finishes.
+ * @param {function()} fn .
+ */
+exports.after = wrapped(global.after);
+
+/**
+ * Register a function to call after each test in a suite.
+ * @param {function()} fn .
+ */
+exports.afterEach = wrapped(global.afterEach);
+
+/**
+ * Register a function to call before the current suite starts.
+ * @param {function()} fn .
+ */
+exports.before = wrapped(global.before);
+
+/**
+ * Register a function to call before each test in a suite.
+ * @param {function()} fn .
+ */
+exports.beforeEach = wrapped(global.beforeEach);
+
+/**
+ * Add a test to the current suite.
+ * @param {string} name The test name.
+ * @param {function()=} fn The test function, or {@code undefined} to define
+ * a pending test case.
+ */
+exports.it = wrapped(global.it);
+
+/**
+ * An alias for {@link #it()} that flags the test as the only one that should
+ * be run within the current suite.
+ * @param {string} name The test name.
+ * @param {function()=} fn The test function, or {@code undefined} to define
+ * a pending test case.
+ */
+exports.iit = exports.it.only = wrapped(global.it.only);
+
+/**
+ * Adds a test to the current suite while suppressing it so it is not run.
+ * @param {string} name The test name.
+ * @param {function()=} fn The test function, or {@code undefined} to define
+ * a pending test case.
+ */
+exports.xit = exports.it.skip = wrapped(global.xit);
+
+exports.ignore = ignore;