Server IP : 213.176.29.180 / Your IP : 3.144.25.130 Web Server : Apache System : Linux 213.176.29.180.hostiran.name 4.18.0-553.22.1.el8_10.x86_64 #1 SMP Tue Sep 24 05:16:59 EDT 2024 x86_64 User : webtaragh ( 1001) PHP Version : 8.3.14 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON Directory (0750) : /home/webtaragh/public_html/ |
[ Home ] | [ C0mmand ] | [ Upload File ] |
---|
history.txt 0000644 00000011447 14736103451 0007021 0 ustar 00 1.1.1 - Added complete German Translation, contributed by Ulf Schroeder - Fixed menu overflow hidden - Tweaks: label changed, 1.1.2 - Changed registration links to WCOP - Matched configuration strings with WCOP - Fixed ticketing area - Improved WHMCS configuration area 1.1.3 - Match pages outlook to WHMCS pages 1.3.9 - debug info - several tweaks - bug fixed in communication to helper when index.html is being served from whmcs end 1.4.0 - new pages > qoutes, network issues, announcements, downloads, email history - Admin page for general settings formated 1.4.1 - button class bug when menu is hidden - stop redirect for view invoices 1.4.2 - fixed order process (missing configurations) - updated language files - formated network status - Contacts subaccoutns heading remains issue - fixed 1.4.3 - All common language files added 1.4.4 - Bug fixed in ticket reply - client area now open in customer currency (change were made by shakeel sb, this is being added for record only) 1.4.5 - All languages updated for translations (other than admin strings) 1.4.6 - Removed %> from services.php - fixed "products/service" > "product/service" string 1.4.7 - Improved helper testing function - implimented reset password feature - Missing strings in menus - no_redirect added to email history - removed all alerts, now messages are shown on pages - logout, now goes to logged out page, logout URL created. 1.9 (beta) - SSO and many other things in process - WCOM is being implimented - Admin tranlation strings converted to admin context 2.0 (beta7) - WCOM implimented 2.0 (b12) - upgrade/downgrade implimented - Product not found in database error - fixed 2.0 (b13) - updated WCOM for enhanced data-tables - fixed order new service tabs links from other pages 2.0 (b14) - styled wcap admin settings - some more classes in whcom 2.0 (b15) - Upgrade/ products implimented(pending review) - Admin styled more 2.0 (beta) - Urgent release for current clients 2.0 (beta-1) - Removed rdudendent CSS from CAP - Improved WCOM > Styles page 2.0 (beta-2) - bulk payments - improved order follow - upgrade/downgrade services 2.0 (beta-3) - HTML updated - work in progress options upgrade 2.0 (beta-4) - domain order fixed 2.0 (beta-5b) - Major HTML changes, only WHCOM is being used now 2.0 (beta6) - HTML mega changes 2.0 (beta7) - Implimented heartbeat to refresh page incase of session is expired. - Domain renewal process complete. 2.1 - releasing 2.1 - Works with latest WHMPress, 2.1.1 - Dashboard services fixed - Tickets spinner updated - Typos fixed. - Added all domain pages to work with not logged in users - Bug Fix while ordering from WHMPress with services that require domain 2.1.2 - Changed duration dropdown name to work with CSS for compatibility issues with WCOP - Get currencies functions changed with whcom function - little changes in wcom 2.1.3 - PHP Notice in case of 404/ invalid URL in client area 2.2 - Changing WCAP to complete JSON - Order-Processes replaced to new codes - Removed all old cart files. 2.3 - beeing released, compatible with WHMPress 4.3 - compatible with with WP 4.9 2.3.1 - Order process with summry updated - Security questions always requried bug fixed 2.3.2 - Language files shifted to whcom 2.3.3 - fixed data-table reload warning issue. 2.5b - Debug info improved and moved to whcom - 2.5.1 - helper changed, - all payments methods visible bug fixed. -2.5.2 - helper updated to remove missing setup for addons - several cap improvements - sidebar not hiding on all pages - translations updated - 2.5.3 - Translation bug fixed for Ajax Loading... - 2.5.3-revision.1 - Sidebar issue on store/order new server page, side bar will always be visisble regardless of user choice. - Option added to select POPUP or new Page for invoices - Multilingual options for client area - 2.5.3-revision.5 - upgrade downgrade services > fixed - upgrade downgrade options > fixed - domain renewals invoicing > fixed - date translations now follow WP rules - 2.5.4 - released - 2.5.4-revision.1 - whcom updated - invoice view settings implimented in invoices section too - 2.5.4-revision.2 - table width for columns on certain views fixed. - .js double submit on load fixed. - 2.5.4-revision.3 - All Helper settings can now be done from WP admin - 2.5.4-revision.4 - Admin menu's/ pages rearranged to be more user friendly - All advance settings moved to a new section, advance setting - 2.5.4-revision.5 - Registration implimented - Summry pages added with ID's and further classes to offer easy customization - 2.6.0-revision.0 - common updated. - bug removed in helper for verification email redirection - new shortcode whmpress-extended domain search implimented, multidomain ordering functions added assets/css/styles.css 0000644 00000021162 14736103451 0010701 0 ustar 00 .wcap_line-center{margin:0;padding:0 10px;background:#fff;display:inline-block}.wcap_config_title{text-align:center;position:relative;z-index:2}.wcap_config_title:after{content:"";position:absolute;top:50%;left:0;right:0;border-top:solid 1px #666;z-index:-1}.wcap_hr_separator{margin:2px;border-top:1px solid #a9b3bd;width:100%}.wcop_product_domain_options .whcom_product_domain_option{background:#f3f3f3;border-radius:4px;margin-bottom:5px;overflow:hidden}.wcop_product_domain_options .whcom_product_domain_option .whcom_product_domain_option_form{display:none;width:80%;margin:0 auto}#primary_nav_wrap{margin:15px 0;display:block!important;background:#0E5077;color:#fff;font-weight:400;overflow:visible!important;padding:0!important}#primary_nav_wrap:after{clear:both;content:' ';width:100%;display:block}#primary_nav_wrap ul{list-style:none;position:relative;margin:0;padding:0}#primary_nav_wrap ul li{list-style:none;position:relative;margin:0;padding:0}#primary_nav_wrap ul li.separator{height:1px;background-color:#ddd}#primary_nav_wrap ul li.current-menu-item{background:#043D5F}#primary_nav_wrap ul li.current-menu-item>a{color:#fff!important}#primary_nav_wrap ul li:hover{background:#043D5F}#primary_nav_wrap ul li:hover>ul{display:block;z-index:999}#primary_nav_wrap ul li:hover>a{color:#fff!important}#primary_nav_wrap ul a{display:block;text-decoration:none;font-weight:700;font-size:12px;line-height:32px;padding:5px 15px;color:#fff}#primary_nav_wrap ul ul{display:none;position:absolute;top:100%;right:0;background:#fff;border:1px solid #043D5F;padding:0}#primary_nav_wrap ul ul li{float:none;width:200px}#primary_nav_wrap ul ul a{line-height:120%;padding:10px 15px;color:#333}#primary_nav_wrap ul ul ul{top:0;left:100%}#primary_nav_wrap ul.wcap_navbar_right>li>ul{right:auto;left:0}@media (min-width:768px){#primary_nav_wrap ul li{float:right}#primary_nav_wrap ul.wcap_navbar_left{float:right}#primary_nav_wrap ul.wcap_navbar_right{float:left}}.wcap_dialog,.wcap_page_container{background:#fff;max-width:1170px;padding:15px;margin:0 auto 50px!important;color:#040404;font-size:14px;line-height:1.4;min-height:400px}.wcap_dialog,.wcap_dialog *,.wcap_dialog:after,.wcap_dialog:before,.wcap_page_container,.wcap_page_container *,.wcap_page_container:after,.wcap_page_container:before{box-sizing:border-box}.wcap_dialog .text_small,.wcap_dialog small,.wcap_page_container .text_small,.wcap_page_container small{font-size:12px}.wcap_dialog .text_large,.wcap_page_container .text_large{font-size:16px}.wcap_dialog h2,.wcap_page_container h2{font-size:30px}.wcap_dialog h3,.wcap_page_container h3{font-size:22px}.wcap_dialog table.dataTable.dtr-inline.collapsed tbody td:first-child:before,.wcap_dialog table.dataTable.dtr-inline.collapsed tbody th:first-child:before,.wcap_page_container table.dataTable.dtr-inline.collapsed tbody td:first-child:before,.wcap_page_container table.dataTable.dtr-inline.collapsed tbody th:first-child:before{color:#040404;border:0;box-shadow:none;background-color:transparent;content:"\f078";font-family:FontAwesome,serif;top:50%;margin-top:-8px}.wcap_dialog table.dataTable.dtr-inline.collapsed tbody tr.parent td:first-child:before,.wcap_dialog table.dataTable.dtr-inline.collapsed tbody tr.parent th:first-child:before,.wcap_page_container table.dataTable.dtr-inline.collapsed tbody tr.parent td:first-child:before,.wcap_page_container table.dataTable.dtr-inline.collapsed tbody tr.parent th:first-child:before{content:"\f077";font-family:FontAwesome,serif;background-color:transparent}.wcap_dialog .wcap_load_page,.wcap_page_container .wcap_load_page{visibility:visible}.wcap_display_block{display:block!important}.wcap_page_heading{margin-bottom:30px}.wcap_page_heading h2{margin:0;padding:6px 0;border-bottom:1px solid #ccc;font-weight:400}.wcap_sub_heading{margin-bottom:15px}.wcap_progress_circle{max-width:150px;margin:-25px auto 0;max-height:100px;overflow:hidden}.wcap_services .wcap_services_header{text-align:right;margin-bottom:15px!important}.wcap_services .wcap_services_header input[type=text],.wcap_services .wcap_services_header label{margin-left:15px}.wcap_services .wcap_service_box{padding:15px;border-right:1px solid #e6e6e6;background:#F6F6F6;margin-bottom:15px!important;position:relative}.wcap_services .wcap_service_box .wcap_service_box_qty{font-size:28px;margin-top:20px}.wcap_services .wcap_service_box i.wcap_service_icon{position:absolute;left:15px;top:15px;font-size:50px;line-height:40px;color:#d3d3d3;width:auto;height:auto}.wcap_services .wcap_services_boxes>div:nth-child(1) .wcap_service_box .wcap_service_box_title{border-bottom:2px solid #5bc0de}.wcap_services .wcap_services_boxes>div:nth-child(2) .wcap_service_box .wcap_service_box_title{border-bottom:2px solid #5cb85c}.wcap_services .wcap_services_boxes>div:nth-child(3) .wcap_service_box .wcap_service_box_title{border-bottom:2px solid #d9534f}.wcap_services .wcap_services_boxes>div:nth-child(4) .wcap_service_box .wcap_service_box_title{border-bottom:2px solid #f0ad4e}.wcap_href{cursor:pointer!important}div.wcap small{font-size:12px;color:#666}#wcap_loading{position:fixed;z-index:999;height:2em;width:2em;overflow:visible;margin:auto;top:0;left:0;bottom:0;right:0;display:none}#wcap_loading:before{content:'';display:block;position:fixed;top:0;left:0;width:100%;height:100%;background-color:rgba(0,0,0,.3)}#wcap_loading:not(:required){font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}#wcap_loading:not(:required):after{content:'';display:block;font-size:10px;width:1em;height:1em;margin-top:-.5em;-webkit-animation:spinner 1.5s infinite linear;animation:spinner 1.5s infinite linear;border-radius:.5em;box-shadow:rgba(0,0,0,.75) 1.5em 0 0 0,rgba(0,0,0,.75) 1.1em 1.1em 0 0,rgba(0,0,0,.75) 0 1.5em 0 0,rgba(0,0,0,.75) -1.1em 1.1em 0 0,rgba(0,0,0,.75) -1.5em 0 0 0,rgba(0,0,0,.75) -1.1em -1.1em 0 0,rgba(0,0,0,.75) 0 -1.5em 0 0,rgba(0,0,0,.75) 1.1em -1.1em 0 0}@-webkit-keyframes spinner{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spinner{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}#wcap_update_password_form1,#whmcs_validation_form{max-width:480px;margin:0 auto}.wcap_product_boxes{display:-webkit-box;display:flex;flex-wrap:wrap;-webkit-box-pack:justify;justify-content:space-between;-webkit-box-align:stretch;align-items:stretch}.wcap_product_boxes .wcap_product_box{width:49%;min-width:240px;position:relative;padding-bottom:65px}.wcap_product_boxes .wcap_product_box .wcap_product_info{text-align:center;display:block;padding:0 0 10px;min-height:95px;border-bottom:1px solid #e6e6e6;margin-bottom:10px}.wcap_product_boxes .wcap_product_box .wcap_product_info .wcap_product_price{font-size:18px;font-weight:700}.wcap_product_boxes .wcap_product_box .wcap_product_submit{padding:10px 0;position:absolute;left:0;right:0;bottom:0}.wcap_summary_panel{border:1px solid #f8f8f8;border-bottom:4px solid #666;margin-bottom:15px!important;border-radius:8px;overflow:hidden;line-height:1.42857143;font-size:.8em}.wcap_summary_panel .wcap_text_total{font-size:2.3em;display:block}.wcap_summary_panel .wcap_summary_panel_header,.wcap_summary_panel .whcom_panel_footer{background:#666;color:#fff}.wcap_summary_panel .wcap_summary_panel_body{background:#f8f8f8}.wcap_summary_panel .wcap_summary_panel_footer.whcom_panel_footer_white,.wcap_summary_panel .wcap_summary_panel_header.wcap_summary_panel_header_white{background:#fff}.wcap_summary_panel .wcap_summary_panel_body,.wcap_summary_panel .wcap_summary_panel_footer,.wcap_summary_panel .wcap_summary_panel_header{overflow:hidden;padding:10px}.wcap_summary_panel .wcap_summary_panel_body.panel_body_has_list,.wcap_summary_panel .wcap_summary_panel_footer.panel_body_has_list,.wcap_summary_panel .wcap_summary_panel_header.panel_body_has_list{padding:10px 0}.wcap_summary_panel .wcap_summary_panel_header{font-size:20px;border-bottom:1px solid #f8f8f8}.wcap_summary_panel .wcap_summary_panel_header.wcap_summary_panel_has_icon{padding-left:40px;position:relative}.wcap_summary_panel .wcap_summary_panel_header.wcap_summary_panel_has_icon [class^=whcom_icon_]{font-size:20px;line-height:30px;position:absolute;height:30px;width:30px;text-align:center;top:50%;margin-top:-15px;left:10px}.wcap_summary_panel .wcap_summary_panel_footer{border-top:1px solid #f8f8f8}.wcap_checkout_form_container{margin:0 auto 15px!important}.wcap_checkout_form_container .submit_div{min-width:180px!important} .dataTables_wrapper .dataTables_info{float:right;} .dataTables_wrapper .dataTables_filter{float:left;} table.dataTable.dtr-inline.collapsed tbody td:first-child::before, table.dataTable.dtr-inline.collapsed tbody th:first-child::before{left:auto;right:4px;} assets/css/styles.less 0000644 00000022536 14736103451 0011065 0 ustar 00 @nav_gray: #e6e6e6; @nav_gray_light: #F6F6F6; @nav_gray_dark: #d6d6d6; @nav_blue: #0E5077; @nav_blue_dark: #043D5F; @text_color: #040404; @color_gray:#f8f8f8; @color_gray_light:#fefefe; @border_radius: 4px; @color_gray_dark:#666666; @color_white:#ffffff; @wcap_border:#D9D9D9;; //Nadeem .wcap_line-center{ margin:0;padding:0 10px; background:#fff; display:inline-block; } .wcap_config_title{ text-align:center; position:relative; z-index:2; } .wcap_config_title:after{ content:""; position:absolute; top:50%; left:0; right:0; border-top:solid 1px @color_gray_dark; z-index:-1; } .wcap_hr_separator { margin: 2px; border-top: 1px solid #a9b3bd; width: 100%; } //WCOP product domain options ----Credit: Abdul Waheed .wcop_product_domain_options { .whcom_product_domain_option { background: lighten(@wcap_border, 10%); border-radius: 4px; margin-bottom: 5px; overflow: hidden; .whcom_product_domain_option_form { display: none; width: 80%; margin: 0 auto; } } } /***************************************/ /** Nav-bar **/ /***************************************/ #primary_nav_wrap { margin: 15px 0; display: block !important; background: #0E5077; color: white; font-weight: normal; overflow: visible !important; padding: 0 !important; &:after { clear: both; content: ' '; width: 100%; display: block; } ul { list-style: none; position: relative; margin: 0; padding: 0; li { list-style: none; position: relative; margin: 0; padding: 0; &.separator { height: 1px; background-color: #ddd; } &.current-menu-item { background: @nav_blue_dark; > a { color: white !important; } } &:hover { background: @nav_blue_dark; > ul { display: block; z-index: 999; } > a { color: white !important; } } } a { display: block; text-decoration: none; font-weight: 700; font-size: 12px; line-height: 32px; padding: 5px 15px; color: white; } ul { display: none; position: absolute; top: 100%; left: 0; background: #fff; border: 1px solid @nav_blue_dark; padding: 0; li { float: none; width: 200px; } a { line-height: 120%; padding: 10px 15px; color: #333; } ul { top: 0; left: 100%; } } &.wcap_navbar_right { > li > ul { left: auto; right: 0; } } @media (min-width: 768px) { li { float: left; } &.wcap_navbar_left { float: left; } &.wcap_navbar_right { float: right; } } } } /***************************************/ /** Main Container **/ /***************************************/ .wcap_page_container, .wcap_dialog { background: white; max-width: 1170px; padding: 15px; margin: 0 auto 50px !important; color: @text_color; font-size: 14px; line-height: 1.4; min-height: 400px; &, *, &:after, &:before, &:after, &:before { box-sizing: border-box; } .text_small, small { font-size: 12px; } .text_large { font-size: 16px; } h2 { font-size: 30px; } h3 { font-size: 22px; } table.dataTable.dtr-inline.collapsed tbody td:first-child:before, table.dataTable.dtr-inline.collapsed tbody th:first-child:before { color: @text_color; border: 0; box-shadow: none; background-color: transparent; content: "\f078"; font-family: FontAwesome, serif; top: 50%; margin-top: -8px; } table.dataTable.dtr-inline.collapsed tbody tr.parent td:first-child:before, table.dataTable.dtr-inline.collapsed tbody tr.parent th:first-child:before { content: "\f077"; font-family: FontAwesome, serif; background-color: transparent; } .wcap_load_page { visibility: visible; } } /***************************************/ /** General Components **/ /***************************************/ .wcap_display_block { display: block !important; } .wcap_page_heading { margin-bottom: 30px; h2 { margin: 0; padding: 6px 0; border-bottom: 1px solid #ccc; font-weight: 400; } } .wcap_sub_heading { margin-bottom: 15px; } .wcap_progress_circle { max-width: 150px; margin: -25px auto 0; max-height: 100px; overflow: hidden; } .wcap_services { .wcap_services_header { text-align: right; margin-bottom: 15px !important; input[type="text"], label { margin-left: 15px; } } .wcap_service_box { padding: 15px; border-right: 1px solid @nav_gray; background: @nav_gray_light; margin-bottom: 15px !important; position: relative; .wcap_service_box_qty { font-size: 28px; margin-top: 20px; } .wcap_service_box_title { } i.wcap_service_icon { position: absolute; right: 15px; top: 15px; font-size: 50px; line-height: 40px; color: lightgray; width: auto; height: auto; } } .wcap_services_boxes > div { &:nth-child(1) { .wcap_service_box { .wcap_service_box_title { border-bottom: 2px solid #5bc0de; } } } &:nth-child(2) { .wcap_service_box { .wcap_service_box_title { border-bottom: 2px solid #5cb85c; } } } &:nth-child(3) { .wcap_service_box { .wcap_service_box_title { border-bottom: 2px solid #d9534f; } } } &:nth-child(4) { .wcap_service_box { .wcap_service_box_title { border-bottom: 2px solid #f0ad4e; } } } } } .wcap_href { cursor: pointer !important; } div.wcap small { font-size: 12px; color: #666; } #wcap_loading { position: fixed; z-index: 999; height: 2em; width: 2em; overflow: visible; margin: auto; top: 0; left: 0; bottom: 0; right: 0; display: none; &:before { content: ''; display: block; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.3); } &:not(:required) { /* hide "loading..." text */ font: 0/0 a; color: transparent; text-shadow: none; background-color: transparent; border: 0; } &:not(:required):after { content: ''; display: block; font-size: 10px; width: 1em; height: 1em; margin-top: -0.5em; animation: spinner 1500ms infinite linear; border-radius: 0.5em; box-shadow: rgba(0, 0, 0, 0.75) 1.5em 0 0 0, rgba(0, 0, 0, 0.75) 1.1em 1.1em 0 0, rgba(0, 0, 0, 0.75) 0 1.5em 0 0, rgba(0, 0, 0, 0.75) -1.1em 1.1em 0 0, rgba(0, 0, 0, 0.75) -1.5em 0 0 0, rgba(0, 0, 0, 0.75) -1.1em -1.1em 0 0, rgba(0, 0, 0, 0.75) 0 -1.5em 0 0, rgba(0, 0, 0, 0.75) 1.1em -1.1em 0 0; } } @keyframes spinner { 0% { -webkit-transform: rotate(0deg); -moz-transform: rotate(0deg); -ms-transform: rotate(0deg); -o-transform: rotate(0deg); transform: rotate(0deg); } 100% { -webkit-transform: rotate(360deg); -moz-transform: rotate(360deg); -ms-transform: rotate(360deg); -o-transform: rotate(360deg); transform: rotate(360deg); } } #whmcs_validation_form, #wcap_update_password_form1 { max-width: 480px; margin: 0 auto; } .wcap_product_boxes { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: stretch; .wcap_product_box { width: 49%; min-width: 240px; position: relative; padding-bottom: 65px; .wcap_product_info { text-align: center; display: block; padding: 0 0 10px; min-height: 95px; border-bottom: 1px solid @nav_gray; margin-bottom: 10px; .wcap_product_price { font-size: 18px; font-weight: bold; } } .wcap_product_submit { padding: 10px 0; position: absolute; left: 0; right: 0; bottom: 0; } } } /* Start of summary Panel */ @font_size:12px; @header_font_size:20px; @panel_body_padding:5px; .wcap_summary_panel { border: 1px solid @color_gray; border-bottom: 4px solid @color_gray_dark; margin-bottom: 15px !important; border-radius: @border_radius+4; overflow: hidden; line-height: 1.42857143; font-size: 0.8em; .wcap_text_total{ font-size: 2.3em; display:block; } .wcap_summary_panel_header, .whcom_panel_footer { background: @color_gray_dark; color:@color_white; } .wcap_summary_panel_body { background: @color_gray; } .wcap_summary_panel_header.wcap_summary_panel_header_white, .wcap_summary_panel_footer.whcom_panel_footer_white { background: white; } .wcap_summary_panel_header, .wcap_summary_panel_body, .wcap_summary_panel_footer { overflow: hidden; padding: 10px; &.panel_body_has_list { padding: 10px 0; } } .wcap_summary_panel_header { font-size:@header_font_size ; border-bottom: 1px solid @color_gray; &.wcap_summary_panel_has_icon { padding-left: 40px; position: relative; [class^="whcom_icon_"] { font-size: 20px; line-height: 30px; position: absolute; height: 30px; width: 30px; text-align: center; top: 50%; margin-top: -15px; left: 10px; } } } .wcap_summary_panel_footer { border-top: 1px solid @color_gray; } } /* End of Summary Panel */ .wcap_checkout_form_container { margin: 0 auto 15px !important; .submit_div { min-width: 180px !important; } } assets/css/index.php 0000644 00000000000 14736103451 0010450 0 ustar 00 assets/css/jquery.circliful.css 0000644 00000001566 14736103451 0012656 0 ustar 00 .circliful .outer { fill: transparent; stroke: #333; stroke-width: 19.8; stroke-dasharray: 534; transition: stroke-dashoffset 1s; -webkit-animation-play-state: running; /* firefox bug fix - won't rotate at 90deg angles */ -moz-transform: rotate(-89deg) translateX(-190px); } /* full circle 25 empty 534 */ .circliful .inner { fill: transparent; stroke: orange; stroke-width: 20; stroke-dasharray: 534; transition: stroke-dashoffset 1s; -webkit-animation-play-state: running; /* firefox bug fix - won't rotate at 90deg angles */ -moz-transform: rotate(-89deg) translateX(-190px); stroke-dashoffset: 0; } .circliful { overflow: visible !important; } .svg-container { width: 100%; margin: 0 auto; overflow: visible; } svg .icon { font-family: FontAwesome; } assets/js/circles/jquery.circliful.min.js 0000644 00000015340 14736103451 0014527 0 ustar 00 "use strict";!function(a){a.fn.circliful=function(b,c){var d=a.extend({foregroundColor:"#3498DB",backgroundColor:"#ccc",pointColor:"none",fillColor:"none",foregroundBorderWidth:15,backgroundBorderWidth:15,pointSize:28.5,fontColor:"#aaa",percent:75,animation:1,animationStep:5,icon:"none",iconSize:"30",iconColor:"#ccc",iconPosition:"top",target:0,start:0,showPercent:1,percentageTextSize:22,textAdditionalCss:"",targetPercent:0,targetTextSize:17,targetColor:"#2980B9",text:null,textStyle:null,textColor:"#666",multiPercentage:0,percentages:null,textBelow:!1,noPercentageSign:!1,replacePercentageByText:null,halfCircle:!1,animateInView:!1,decimals:0,alwaysDecimals:!1},b);return this.each(function(){function y(){var a=window.setInterval(function(){r>=x?(window.clearInterval(a),t=1,"function"==typeof c&&c.call(this)):(r+=s,u+=v),r/3.6>=e&&1==t&&(r=3.6*e),u>d.target&&1==t&&(u=d.target),null==d.replacePercentageByText&&(w=d.halfCircle?parseFloat(100*r/360*2):parseFloat(100*r/360),w=w.toFixed(d.decimals),!d.alwaysDecimals&&(0==e||e>1&&1!=t)&&(w=parseInt(w))),o.attr("stroke-dasharray",r+", 20000"),1==d.showPercent?p.find(".number").text(w):(p.find(".number").text(u),p.find(".percent").text(""))}.bind(o),q)}function z(){var b=navigator.userAgent.toLowerCase().indexOf("webkit")!=-1?"body":"html",c=a(b).scrollTop(),d=c+a(window).height(),e=Math.round(o.offset().top),f=e+o.height();return e<d&&f>c}function A(){o.hasClass("start")||z(o)&&(o.addClass("start"),setTimeout(y,250))}function B(b,c){a.each(b,function(a,d){a.toLowerCase()in c&&(b[a]=c[a.toLowerCase()])})}var b=a(this);B(d,b.data());var j,k,l,e=d.percent,f=83,g=100,h=100,i=100,m=d.backgroundBorderWidth;if(d.halfCircle?"left"==d.iconPosition?(g=80,f=100,i=117,h=100):d.halfCircle&&(f=80,h=100):"bottom"==d.iconPosition?(f=124,h=95):"left"==d.iconPosition?(g=80,f=110,i=117):"middle"==d.iconPosition?1==d.multiPercentage?"object"==typeof d.percentages?m=30:(f=110,k='<g stroke="'+("none"!=d.backgroundColor?d.backgroundColor:"#ccc")+'" ><line x1="133" y1="50" x2="140" y2="40" stroke-width="2" /></g>',k+='<g stroke="'+("none"!=d.backgroundColor?d.backgroundColor:"#ccc")+'" ><line x1="140" y1="40" x2="200" y2="40" stroke-width="2" /></g>',i=228,h=47):(f=110,k='<g stroke="'+("none"!=d.backgroundColor?d.backgroundColor:"#ccc")+'" ><line x1="133" y1="50" x2="140" y2="40" stroke-width="2" /></g>',k+='<g stroke="'+("none"!=d.backgroundColor?d.backgroundColor:"#ccc")+'" ><line x1="140" y1="40" x2="200" y2="40" stroke-width="2" /></g>',i=170,h=35):"right"==d.iconPosition&&(g=120,f=110,i=80),d.targetPercent>0&&(h=95,k='<g stroke="'+("none"!=d.backgroundColor?d.backgroundColor:"#ccc")+'" ><line x1="75" y1="101" x2="125" y2="101" stroke-width="1" /></g>',k+='<text text-anchor="middle" x="'+i+'" y="120" style="font-size: '+d.targetTextSize+'px;" fill="'+d.targetColor+'">'+d.targetPercent+(d.noPercentageSign&&null==d.replacePercentageByText?"":"%")+"</text>",k+='<circle cx="100" cy="100" r="69" fill="none" stroke="'+d.backgroundColor+'" stroke-width="3" stroke-dasharray="450" transform="rotate(-90,100,100)" />',k+='<circle cx="100" cy="100" r="69" fill="none" stroke="'+d.targetColor+'" stroke-width="3" stroke-dasharray="'+3.6*d.targetPercent+', 20000" transform="rotate(-90,100,100)" />'),null!=d.text&&(d.halfCircle?d.textBelow?k+='<text text-anchor="middle" x="100" y="120" style="'+d.textStyle+'" fill="'+d.textColor+'">'+d.text+"</text>":0==d.multiPercentage?k+='<text text-anchor="middle" x="100" y="115" style="'+d.textStyle+'" fill="'+d.textColor+'">'+d.text+"</text>":1==d.multiPercentage&&(k+='<text text-anchor="middle" x="228" y="65" style="'+d.textStyle+'" fill="'+d.textColor+'">'+d.text+"</text>"):d.textBelow?k+='<text text-anchor="middle" x="100" y="190" style="'+d.textStyle+'" fill="'+d.textColor+'">'+d.text+"</text>":0==d.multiPercentage?k+='<text text-anchor="middle" x="100" y="115" style="'+d.textStyle+'" fill="'+d.textColor+'">'+d.text+"</text>":1==d.multiPercentage&&(k+='<text text-anchor="middle" x="228" y="65" style="'+d.textStyle+'" fill="'+d.textColor+'">'+d.text+"</text>")),"none"!=d.icon&&(l='<text text-anchor="middle" x="'+g+'" y="'+f+'" class="icon" style="font-size: '+d.iconSize+'px" fill="'+d.iconColor+'">&#x'+d.icon+"</text>"),d.halfCircle){var n='transform="rotate(-180,100,100)"';b.addClass("svg-container").append(a('<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 194 186" class="circliful">'+k+'<clipPath id="cut-off-bottom"> <rect x="100" y="0" width="100" height="200" /> </clipPath><circle cx="100" cy="100" r="57" class="border" fill="'+d.fillColor+'" stroke="'+d.backgroundColor+'" stroke-width="'+m+'" stroke-dasharray="360" clip-path="url(#cut-off-bottom)" transform="rotate(-90,100,100)" /><circle class="circle" cx="100" cy="100" r="57" class="border" fill="none" stroke="'+d.foregroundColor+'" stroke-width="'+d.foregroundBorderWidth+'" stroke-dasharray="0,20000" '+n+' /><circle cx="100" cy="100" r="'+d.pointSize+'" fill="'+d.pointColor+'" clip-path="url(#cut-off-bottom)" transform="rotate(-90,100,100)" />'+l+'<text class="timer" text-anchor="middle" x="'+i+'" y="'+h+'" style="font-size: '+d.percentageTextSize+"px; "+j+";"+d.textAdditionalCss+'" fill="'+d.fontColor+'"><tspan class="number">'+(null==d.replacePercentageByText?0:d.replacePercentageByText)+'</tspan><tspan class="percent">'+(d.noPercentageSign||null!=d.replacePercentageByText?"":"%")+"</tspan></text>"))}else b.addClass("svg-container").append(a('<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 194 186" class="circliful">'+k+'<circle cx="100" cy="100" r="57" class="border" fill="'+d.fillColor+'" stroke="'+d.backgroundColor+'" stroke-width="'+m+'" stroke-dasharray="360" transform="rotate(-90,100,100)" /><circle class="circle" cx="100" cy="100" r="57" class="border" fill="none" stroke="'+d.foregroundColor+'" stroke-width="'+d.foregroundBorderWidth+'" stroke-dasharray="0,20000" transform="rotate(-90,100,100)" /><circle cx="100" cy="100" r="'+d.pointSize+'" fill="'+d.pointColor+'" />'+l+'<text class="timer" text-anchor="middle" x="'+i+'" y="'+h+'" style="font-size: '+d.percentageTextSize+"px; "+j+";"+d.textAdditionalCss+'" fill="'+d.fontColor+'"><tspan class="number">'+(null==d.replacePercentageByText?0:d.replacePercentageByText)+'</tspan><tspan class="percent">'+(d.noPercentageSign||null!=d.replacePercentageByText?"":"%")+"</tspan></text>"));var o=b.find(".circle"),p=b.find(".timer"),q=30,r=0,s=d.animationStep,t=0,u=0,v=0,w=e,x=3.6*e;d.halfCircle&&(x=3.6*e/2),null!=d.replacePercentageByText&&(w=d.replacePercentageByText),d.start>0&&d.target>0&&(e=d.start/(d.target/100),v=d.target/100),1==d.animation?d.animateInView?a(window).scroll(function(){A()}):y():(o.attr("stroke-dasharray",x+", 20000"),1==d.showPercent?p.find(".number").text(w):(p.find(".number").text(d.target),p.find(".percent").text("")))})}}(jQuery); assets/js/circles/circles.js 0000644 00000034717 14736103451 0012110 0 ustar 00 /** * jquery-circle-progress - jQuery Plugin to draw animated circular progress bars: * {@link http://kottenator.github.io/jquery-circle-progress/} * * @author Rostyslav Bryzgunov <kottenator@gmail.com> * @version 1.2.1 * @licence MIT * @preserve */ // UMD factory - https://github.com/umdjs/umd/blob/d31bb6ee7098715e019f52bdfe27b3e4bfd2b97e/templates/jqueryPlugin.js // Uses AMD, CommonJS or browser globals to create a jQuery plugin. (function(factory) { if (typeof define === 'function' && define.amd) { // AMD - register as an anonymous module define(['jquery'], factory); } else if (typeof module === 'object' && module.exports) { // Node/CommonJS var $ = require('jquery'); factory($); module.exports = $; } else { // Browser globals factory(jQuery); } })(function($) { /** * Inner implementation of the circle progress bar. * The class is not exposed _yet_ but you can create an instance through jQuery method call. * * @param {object} config - You can customize any class member (property or method). * @class * @alias CircleProgress */ function CircleProgress(config) { this.init(config); } CircleProgress.prototype = { //--------------------------------------- public options --------------------------------------- /** * This is the only required option. It should be from `0.0` to `1.0`. * @type {number} * @default 0.0 */ value: 0.0, /** * Size of the canvas in pixels. * It's a square so we need only one dimension. * @type {number} * @default 100.0 */ size: 100.0, /** * Initial angle for `0.0` value in radians. * @type {number} * @default -Math.PI */ startAngle: -Math.PI, /** * Width of the arc in pixels. * If it's `'auto'` - the value is calculated as `[this.size]{@link CircleProgress#size} / 14`. * @type {number|string} * @default 'auto' */ thickness: 'auto', /** * Fill of the arc. You may set it to: * * - solid color: * - `'#3aeabb'` * - `{ color: '#3aeabb' }` * - `{ color: 'rgba(255, 255, 255, .3)' }` * - linear gradient _(left to right)_: * - `{ gradient: ['#3aeabb', '#fdd250'], gradientAngle: Math.PI / 4 }` * - `{ gradient: ['red', 'green', 'blue'], gradientDirection: [x0, y0, x1, y1] }` * - `{ gradient: [["red", .2], ["green", .3], ["blue", .8]] }` * - image: * - `{ image: 'http://i.imgur.com/pT0i89v.png' }` * - `{ image: imageObject }` * - `{ color: 'lime', image: 'http://i.imgur.com/pT0i89v.png' }` - * color displayed until the image is loaded * * @default {gradient: ['#3aeabb', '#fdd250']} */ fill: { gradient: ['#3aeabb', '#fdd250'] }, /** * Color of the "empty" arc. Only a color fill supported by now. * @type {string} * @default 'rgba(0, 0, 0, .1)' */ emptyFill: 'rgba(0, 0, 0, .1)', /** * jQuery Animation config. * You can pass `false` to disable the animation. * @see http://api.jquery.com/animate/ * @type {object|boolean} * @default {duration: 1200, easing: 'circleProgressEasing'} */ animation: { duration: 1200, easing: 'circleProgressEasing' }, /** * Default animation starts at `0.0` and ends at specified `value`. Let's call this _direct animation_. * If you want to make _reversed animation_ - set `animationStartValue: 1.0`. * Also you may specify any other value from `0.0` to `1.0`. * @type {number} * @default 0.0 */ animationStartValue: 0.0, /** * Reverse animation and arc draw. * By default, the arc is filled from `0.0` to `value`, _clockwise_. * With `reverse: true` the arc is filled from `1.0` to `value`, _counter-clockwise_. * @type {boolean} * @default false */ reverse: false, /** * Arc line cap: `'butt'`, `'round'` or `'square'` - * [read more]{@link https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D.lineCap}. * @type {string} * @default 'butt' */ lineCap: 'butt', /** * Canvas insertion mode: append or prepend it into the parent element? * @type {string} * @default 'prepend' */ insertMode: 'prepend', //------------------------------ protected properties and methods ------------------------------ /** * Link to {@link CircleProgress} constructor. * @protected */ constructor: CircleProgress, /** * Container element. Should be passed into constructor config. * @protected * @type {jQuery} */ el: null, /** * Canvas element. Automatically generated and prepended to [this.el]{@link CircleProgress#el}. * @protected * @type {HTMLCanvasElement} */ canvas: null, /** * 2D-context of [this.canvas]{@link CircleProgress#canvas}. * @protected * @type {CanvasRenderingContext2D} */ ctx: null, /** * Radius of the outer circle. Automatically calculated as `[this.size]{@link CircleProgress#size} / 2`. * @protected * @type {number} */ radius: 0.0, /** * Fill of the main arc. Automatically calculated, depending on [this.fill]{@link CircleProgress#fill} option. * @protected * @type {string|CanvasGradient|CanvasPattern} */ arcFill: null, /** * Last rendered frame value. * @protected * @type {number} */ lastFrameValue: 0.0, /** * Init/re-init the widget. * * Throws a jQuery event: * * - `circle-inited(jqEvent)` * * @param {object} config - You can customize any class member (property or method). */ init: function(config) { $.extend(this, config); this.radius = this.size / 2; this.initWidget(); this.initFill(); this.draw(); this.el.trigger('circle-inited'); }, /** * Initialize `<canvas>`. * @protected */ initWidget: function() { if (!this.canvas) this.canvas = $('<canvas>')[this.insertMode == 'prepend' ? 'prependTo' : 'appendTo'](this.el)[0]; var canvas = this.canvas; canvas.width = this.size; canvas.height = this.size; this.ctx = canvas.getContext('2d'); if (window.devicePixelRatio > 1) { var scaleBy = window.devicePixelRatio; canvas.style.width = canvas.style.height = this.size + 'px'; canvas.width = canvas.height = this.size * scaleBy; this.ctx.scale(scaleBy, scaleBy); } }, /** * This method sets [this.arcFill]{@link CircleProgress#arcFill}. * It could do this async (on image load). * @protected */ initFill: function() { var self = this, fill = this.fill, ctx = this.ctx, size = this.size; if (!fill) throw Error("The fill is not specified!"); if (typeof fill == 'string') fill = {color: fill}; if (fill.color) this.arcFill = fill.color; if (fill.gradient) { var gr = fill.gradient; if (gr.length == 1) { this.arcFill = gr[0]; } else if (gr.length > 1) { var ga = fill.gradientAngle || 0, // gradient direction angle; 0 by default gd = fill.gradientDirection || [ size / 2 * (1 - Math.cos(ga)), // x0 size / 2 * (1 + Math.sin(ga)), // y0 size / 2 * (1 + Math.cos(ga)), // x1 size / 2 * (1 - Math.sin(ga)) // y1 ]; var lg = ctx.createLinearGradient.apply(ctx, gd); for (var i = 0; i < gr.length; i++) { var color = gr[i], pos = i / (gr.length - 1); if ($.isArray(color)) { pos = color[1]; color = color[0]; } lg.addColorStop(pos, color); } this.arcFill = lg; } } if (fill.image) { var img; if (fill.image instanceof Image) { img = fill.image; } else { img = new Image(); img.src = fill.image; } if (img.complete) setImageFill(); else img.onload = setImageFill; } function setImageFill() { var bg = $('<canvas>')[0]; bg.width = self.size; bg.height = self.size; bg.getContext('2d').drawImage(img, 0, 0, size, size); self.arcFill = self.ctx.createPattern(bg, 'no-repeat'); self.drawFrame(self.lastFrameValue); } }, /** * Draw the circle. * @protected */ draw: function() { if (this.animation) this.drawAnimated(this.value); else this.drawFrame(this.value); }, /** * Draw a single animation frame. * @protected * @param {number} v - Frame value. */ drawFrame: function(v) { this.lastFrameValue = v; this.ctx.clearRect(0, 0, this.size, this.size); this.drawEmptyArc(v); this.drawArc(v); }, /** * Draw the arc (part of the circle). * @protected * @param {number} v - Frame value. */ drawArc: function(v) { if (v === 0) return; var ctx = this.ctx, r = this.radius, t = this.getThickness(), a = this.startAngle; ctx.save(); ctx.beginPath(); if (!this.reverse) { ctx.arc(r, r, r - t / 2, a, a + Math.PI * 2 * v); } else { ctx.arc(r, r, r - t / 2, a - Math.PI * 2 * v, a); } ctx.lineWidth = t; ctx.lineCap = this.lineCap; ctx.strokeStyle = this.arcFill; ctx.stroke(); ctx.restore(); }, /** * Draw the _empty (background)_ arc (part of the circle). * @protected * @param {number} v - Frame value. */ drawEmptyArc: function(v) { var ctx = this.ctx, r = this.radius, t = this.getThickness(), a = this.startAngle; if (v < 1) { ctx.save(); ctx.beginPath(); if (v <= 0) { ctx.arc(r, r, r - t / 2, 0, Math.PI * 2); } else { if (!this.reverse) { ctx.arc(r, r, r - t / 2, a + Math.PI * 2 * v, a); } else { ctx.arc(r, r, r - t / 2, a, a - Math.PI * 2 * v); } } ctx.lineWidth = t; ctx.strokeStyle = this.emptyFill; ctx.stroke(); ctx.restore(); } }, /** * Animate the progress bar. * * Throws 3 jQuery events: * * - `circle-animation-start(jqEvent)` * - `circle-animation-progress(jqEvent, animationProgress, stepValue)` - multiple event * animationProgress: from `0.0` to `1.0`; stepValue: from `0.0` to `value` * - `circle-animation-end(jqEvent)` * * @protected * @param {number} v - Final value. */ drawAnimated: function(v) { var self = this, el = this.el, canvas = $(this.canvas); // stop previous animation before new "start" event is triggered canvas.stop(true, false); el.trigger('circle-animation-start'); canvas .css({animationProgress: 0}) .animate({animationProgress: 1}, $.extend({}, this.animation, { step: function(animationProgress) { var stepValue = self.animationStartValue * (1 - animationProgress) + v * animationProgress; self.drawFrame(stepValue); el.trigger('circle-animation-progress', [animationProgress, stepValue]); } })) .promise() .always(function() { // trigger on both successful & failure animation end el.trigger('circle-animation-end'); }); }, /** * Get the circle thickness. * @see CircleProgress#thickness * @protected * @returns {number} */ getThickness: function() { return $.isNumeric(this.thickness) ? this.thickness : this.size / 14; }, /** * Get current value. * @protected * @return {number} */ getValue: function() { return this.value; }, /** * Set current value (with smooth animation transition). * @protected * @param {number} newValue */ setValue: function(newValue) { if (this.animation) this.animationStartValue = this.lastFrameValue; this.value = newValue; this.draw(); } }; //----------------------------------- Initiating jQuery plugin ----------------------------------- $.circleProgress = { // Default options (you may override them) defaults: CircleProgress.prototype }; // ease-in-out-cubic $.easing.circleProgressEasing = function(x, t, b, c, d) { if ((t /= d / 2) < 1) return c / 2 * t * t * t + b; return c / 2 * ((t -= 2) * t * t + 2) + b; }; /** * Creates an instance of {@link CircleProgress}. * Produces [init event]{@link CircleProgress#init} and [animation events]{@link CircleProgress#drawAnimated}. * * @param {object} [configOrCommand] - Config object or command name. * * Config example (you can specify any {@link CircleProgress} property): * * ```js * { value: 0.75, size: 50, animation: false } * ``` * * Commands: * * ```js * el.circleProgress('widget'); // get the <canvas> * el.circleProgress('value'); // get the value * el.circleProgress('value', newValue); // update the value * el.circleProgress('redraw'); // redraw the circle * el.circleProgress(); // the same as 'redraw' * ``` * * @param {string} [commandArgument] - Some commands (like `'value'`) may require an argument. * @see CircleProgress * @alias "$(...).circleProgress" */ $.fn.circleProgress = function(configOrCommand, commandArgument) { var dataName = 'circle-progress', firstInstance = this.data(dataName); if (configOrCommand == 'widget') { if (!firstInstance) throw Error('Calling "widget" method on not initialized instance is forbidden'); return firstInstance.canvas; } if (configOrCommand == 'value') { if (!firstInstance) throw Error('Calling "value" method on not initialized instance is forbidden'); if (typeof commandArgument == 'undefined') { return firstInstance.getValue(); } else { var newValue = arguments[1]; return this.each(function() { $(this).data(dataName).setValue(newValue); }); } } return this.each(function() { var el = $(this), instance = el.data(dataName), config = $.isPlainObject(configOrCommand) ? configOrCommand : {}; if (instance) { instance.init(config); } else { var initialConfig = $.extend({}, el.data()); if (typeof initialConfig.fill == 'string') initialConfig.fill = JSON.parse(initialConfig.fill); if (typeof initialConfig.animation == 'string') initialConfig.animation = JSON.parse(initialConfig.animation); config = $.extend(initialConfig, config); config.el = el; instance = new CircleProgress(config); el.data(dataName, instance); } }); }; }); assets/js/tablesaw/tablesaw.css 0000644 00000036233 14736103451 0012613 0 ustar 00 /*! Tablesaw - v3.0.1 - 2017-02-16 * https://github.com/filamentgroup/tablesaw * Copyright (c) 2017 Filament Group; Licensed MIT */ .tablesaw { width: 100%; max-width: 100%; empty-cells: show; border-collapse: collapse; border: 0; padding: 0; } .tablesaw * { box-sizing: border-box; } .tablesaw th, .tablesaw td { padding: .5em .7em; } .tablesaw thead tr:first-child th { padding-top: .9em; padding-bottom: .7em; } .tablesaw-bar .btn { border: 1px solid #ccc; border-radius: .25em; background: none; box-shadow: 0 1px 0 rgba(255,255,255,1); color: #4a4a4a; cursor: pointer; display: inline-block; margin: 0; padding: .5em .85em .4em .85em; position: relative; text-align: center; text-decoration: none; text-transform: capitalize; -webkit-appearance: none; -moz-appearance: none; appearance: none; } .tablesaw-bar a.btn { color: #1c95d4; } .tablesaw-bar .btn:hover { text-decoration: none; } /* Default radio/checkbox styling horizonal controlgroups. */ .tablesaw-bar .btn:active { background-color: #ddd; } @supports (box-shadow: none ) { .tablesaw-bar .btn:focus { background-color: #fff; outline: none; } .tablesaw-bar .btn:focus { box-shadow: 0 0 .35em #4faeef !important; } } .tablesaw-bar .btn-select select { background: none; border: none; display: inline-block; position: absolute; left: 0; top: 0; margin: 0; width: 100%; height: 100%; z-index: 2; min-height: 1em; opacity: 0; color: transparent; } .tablesaw-bar .btn select option { background: #fff; color: #000; } .tablesaw-bar .btn { display: inline-block; width: auto; height: auto; position: relative; top: 0; } .tablesaw-bar .btn.btn-small { font-size: 1.0625em; line-height: 19px; padding: .3em 1em .3em 1em; } .tablesaw-bar .btn.btn-micro { font-size: .8125em; padding: .4em .7em .25em .7em; } .tablesaw-bar .btn-select { padding-right: 1.5em; text-align: left; display: inline-block; color: #4d4d4d; padding-right: 2.5em; min-width: 7.25em; text-align: left; } .tablesaw-bar .btn-select:after { content: " "; position: absolute; background: none; background-repeat: no-repeat; background-position: .25em .45em; content: "\25bc"; font-size: .55em; padding-top: 1.2em; padding-left: 1em; left: auto; right: 0; margin: 0; top: 0; bottom: 0; width: 1.8em; } .tablesaw-bar .btn-select.btn-small:after, .tablesaw-bar .btn-select.btn-micro:after { width: 1.2em; font-size: .5em; padding-top: 1em; padding-right: .5em; line-height: 1.65; background: none; box-shadow: none; border-left-width: 0; } /* Column navigation buttons for swipe and columntoggle tables */ .tablesaw-advance .btn { -webkit-appearance: none; -moz-appearance: none; box-sizing: border-box; text-shadow: 0 1px 0 #fff; border-radius: .25em; } .tablesaw-advance .btn.btn-micro { font-size: .8125em; padding: .3em .7em .25em .7em; } .tablesaw-bar .tablesaw-advance a.tablesaw-nav-btn:first-child { margin-left: 0; } .tablesaw-bar .tablesaw-advance a.tablesaw-nav-btn:last-child { margin-right: 0; } .tablesaw-bar .tablesaw-advance a.tablesaw-nav-btn { display: inline-block; overflow: hidden; width: 1.8em; height: 1.8em; background-position: 50% 50%; margin-left: .25em; margin-right: .25em; position: relative; } .tablesaw-bar .tablesaw-advance a.tablesaw-nav-btn.left:before, .tablesaw-bar .tablesaw-advance a.tablesaw-nav-btn.right:before, .tablesaw-bar .tablesaw-advance a.tablesaw-nav-btn.down:before, .tablesaw-bar .tablesaw-advance a.tablesaw-nav-btn.up:before { content: "\0020"; overflow: hidden; width: 0; height: 0; position: absolute; } .tablesaw-bar .tablesaw-advance a.tablesaw-nav-btn.down:before { left: .5em; top: .65em; border-left: 5px solid transparent; border-right: 5px solid transparent; border-top: 5px solid #808080; } .tablesaw-bar .tablesaw-advance a.tablesaw-nav-btn.up:before { left: .5em; top: .65em; border-left: 5px solid transparent; border-right: 5px solid transparent; border-bottom: 5px solid #808080; } .tablesaw-bar .tablesaw-advance a.tablesaw-nav-btn.left:before, .tablesaw-bar .tablesaw-advance a.tablesaw-nav-btn.right:before { top: .45em; border-top: 5px solid transparent; border-bottom: 5px solid transparent; } .tablesaw-bar .tablesaw-advance a.tablesaw-nav-btn.left:before { left: .6em; border-right: 5px solid #808080; } .tablesaw-bar .tablesaw-advance a.tablesaw-nav-btn.right:before { left: .7em; border-left: 5px solid #808080; } .tablesaw-advance a.tablesaw-nav-btn.disabled { opacity: .25; cursor: default; pointer-events: none; } /* Table Toolbar */ .tablesaw-bar { clear: both; } .tablesaw-toolbar { float: left; } .tablesaw-toolbar label { font-size: .875em; padding: .5em 0; clear: both; display: block; color: #888; margin-right: .5em; text-transform: uppercase; } .tablesaw-bar .btn, .tablesaw-enhanced .tablesaw-bar .btn { margin-top: .5em; margin-bottom: .5em; } .tablesaw-bar .btn-select, .tablesaw-enhanced .tablesaw-bar .btn-select { margin-bottom: 0; } .tablesaw-bar .tablesaw-toolbar .btn { margin-left: .4em; margin-top: 0; text-transform: uppercase; border: none; box-shadow: none; background: transparent; font-size: 1em; padding-left: .3em; } .tablesaw-bar .tablesaw-toolbar .btn-select { min-width: 0; } .tablesaw-bar .tablesaw-toolbar .btn-select:after { padding-top: .9em; } .tablesaw-bar .tablesaw-toolbar select { color: #888; text-transform: none; background: transparent; } .tablesaw-toolbar ~ table { clear: both; } .tablesaw-toolbar .a11y-sm { clip: rect(0 0 0 0); height: 1px; overflow: hidden; position: absolute; width: 1px; } @media (min-width: 24em) { .tablesaw-toolbar .a11y-sm { clip: none; height: auto; width: auto; position: static; overflow: visible; } } .tablesaw th, .tablesaw td { text-align: left; vertical-align: middle; } .tablesaw thead th { text-align: left; } /* Table rows have a gray bottom stroke by default */ .tablesaw-row-border tr { border-bottom: 1px solid #dfdfdf; } /* Zebra striping */ .tablesaw-row-zebra tr:nth-child(2n) { background-color: #f8f8f8; } .tablesaw caption { text-align: left; margin: .59375em 0; } .tablesaw-swipe .tablesaw-cell-persist { border-right: 2px solid #e4e1de; } .tablesaw-swipe-shadow .tablesaw-cell-persist { border-right-width: 1px; } .tablesaw-swipe-shadow .tablesaw-cell-persist { box-shadow: 3px 0 4px -1px #e4e1de; } .tablesaw-stack td .tablesaw-cell-label, .tablesaw-stack th .tablesaw-cell-label { display: none; } /* Mobile first styles: Begin with the stacked presentation at narrow widths */ /* Support note IE9+: @media only all */ @media only all { /* Show the table cells as a block level element */ .tablesaw-stack { clear: both; } .tablesaw-stack td, .tablesaw-stack th { text-align: left; display: block; } .tablesaw-stack tr { clear: both; display: table-row; } /* Make the label elements a percentage width */ .tablesaw-stack td .tablesaw-cell-label, .tablesaw-stack th .tablesaw-cell-label { display: block; padding: 0 .6em 0 0; width: 30%; display: inline-block; } /* For grouped headers, have a different style to visually separate the levels by classing the first label in each col group */ .tablesaw-stack th .tablesaw-cell-label-top, .tablesaw-stack td .tablesaw-cell-label-top { display: block; padding: .4em 0; margin: .4em 0; } .tablesaw-cell-label { display: block; } /* Avoid double strokes when stacked */ .tablesaw-stack tbody th.group { margin-top: -1px; } /* Avoid double strokes when stacked */ .tablesaw-stack th.group b.tablesaw-cell-label { display: none !important; } } @media (max-width: 39.9375em) { /* Table rows have a gray bottom stroke by default */ .tablesaw-stack tbody tr { display: block; width: 100%; border-bottom: 1px solid #dfdfdf; } .tablesaw-stack thead td, .tablesaw-stack thead th { display: none; } .tablesaw-stack tbody td, .tablesaw-stack tbody th { display: block; float: left; clear: left; width: 100%; } .tablesaw-cell-label { vertical-align: top; } .tablesaw-cell-content { max-width: 67%; display: inline-block; } .tablesaw-stack td:empty, .tablesaw-stack th:empty { display: none; } } /* Media query to show as a standard table at 560px (35em x 16px) or wider */ @media (min-width: 40em) { .tablesaw-stack tr { display: table-row; } /* Show the table header rows */ .tablesaw-stack td, .tablesaw-stack th, .tablesaw-stack thead td, .tablesaw-stack thead th { display: table-cell; margin: 0; } /* Hide the labels in each cell */ .tablesaw-stack td .tablesaw-cell-label, .tablesaw-stack th .tablesaw-cell-label { display: none !important; } } .tablesaw-fix-persist { table-layout: fixed; } @media only all { /* Unchecked manually: Always hide */ .tablesaw-swipe th.tablesaw-cell-hidden, .tablesaw-swipe td.tablesaw-cell-hidden { display: none; } } .btn.tablesaw-columntoggle-btn span { text-indent: -9999px; display: inline-block; } .tablesaw-columntoggle-btnwrap { position: relative; /* for dialog positioning */ } .tablesaw-columntoggle-btnwrap .dialog-content { padding: .5em; } .tablesaw-columntoggle tbody td { line-height: 1.5; } /* Remove top/bottom margins around the fieldcontain on check list */ .tablesaw-columntoggle-popup { display: none; } .tablesaw-columntoggle-btnwrap.visible .tablesaw-columntoggle-popup { display: block; position: absolute; top: 2em; right: 0; background-color: #fff; padding: .5em .8em; border: 1px solid #ccc; box-shadow: 0 1px 2px #ccc; border-radius: .2em; z-index: 1; } .tablesaw-columntoggle-popup fieldset { margin: 0; } /* Hide all prioritized columns by default */ @media only all { .tablesaw-columntoggle th.tablesaw-priority-6, .tablesaw-columntoggle td.tablesaw-priority-6, .tablesaw-columntoggle th.tablesaw-priority-5, .tablesaw-columntoggle td.tablesaw-priority-5, .tablesaw-columntoggle th.tablesaw-priority-4, .tablesaw-columntoggle td.tablesaw-priority-4, .tablesaw-columntoggle th.tablesaw-priority-3, .tablesaw-columntoggle td.tablesaw-priority-3, .tablesaw-columntoggle th.tablesaw-priority-2, .tablesaw-columntoggle td.tablesaw-priority-2, .tablesaw-columntoggle th.tablesaw-priority-1, .tablesaw-columntoggle td.tablesaw-priority-1 { display: none; } } .tablesaw-columntoggle-btnwrap .dialog-content { top: 0 !important; right: 1em; left: auto !important; width: 12em; max-width: 18em; margin: -.5em auto 0; } .tablesaw-columntoggle-btnwrap .dialog-content:focus { outline-style: none; } /* Preset breakpoints if "" class added to table */ /* Show priority 1 at 320px (20em x 16px) */ @media (min-width: 20em) { .tablesaw-columntoggle th.tablesaw-priority-1, .tablesaw-columntoggle td.tablesaw-priority-1 { display: table-cell; } } /* Show priority 2 at 480px (30em x 16px) */ @media (min-width: 30em) { .tablesaw-columntoggle th.tablesaw-priority-2, .tablesaw-columntoggle td.tablesaw-priority-2 { display: table-cell; } } /* Show priority 3 at 640px (40em x 16px) */ @media (min-width: 40em) { .tablesaw-columntoggle th.tablesaw-priority-3, .tablesaw-columntoggle td.tablesaw-priority-3 { display: table-cell; } .tablesaw-columntoggle tbody td { line-height: 2; } } /* Show priority 4 at 800px (50em x 16px) */ @media (min-width: 50em) { .tablesaw-columntoggle th.tablesaw-priority-4, .tablesaw-columntoggle td.tablesaw-priority-4 { display: table-cell; } } /* Show priority 5 at 960px (60em x 16px) */ @media (min-width: 60em) { .tablesaw-columntoggle th.tablesaw-priority-5, .tablesaw-columntoggle td.tablesaw-priority-5 { display: table-cell; } } /* Show priority 6 at 1,120px (70em x 16px) */ @media (min-width: 70em) { .tablesaw-columntoggle th.tablesaw-priority-6, .tablesaw-columntoggle td.tablesaw-priority-6 { display: table-cell; } } @media only all { /* Unchecked manually: Always hide */ .tablesaw-columntoggle th.tablesaw-cell-hidden, .tablesaw-columntoggle td.tablesaw-cell-hidden { display: none; } /* Checked manually: Always show */ .tablesaw-columntoggle th.tablesaw-cell-visible, .tablesaw-columntoggle td.tablesaw-cell-visible { display: table-cell; } } .tablesaw-columntoggle-popup .btn-group > label { display: block; padding: .2em 0; white-space: nowrap; } .tablesaw-columntoggle-popup .btn-group > label input { margin-right: .8em; } .tablesaw-sortable, .tablesaw-sortable thead, .tablesaw-sortable thead tr, .tablesaw-sortable thead tr th { position: relative; } .tablesaw-sortable thead tr th { vertical-align: top; } .tablesaw-sortable th.tablesaw-sortable-head.tablesaw-sortable-head { padding: 0; } .tablesaw-sortable th.tablesaw-sortable-head button { padding-top: .9em; padding-bottom: .7em; padding-left: .6em; padding-right: .6em; } .tablesaw-sortable .tablesaw-sortable-head button { min-width: 100%; color: inherit; background: transparent; border: 0; padding: 0; text-align: inherit; font: inherit; text-transform: inherit; } .tablesaw-sortable-arrow:after { display: inline-block; width: 10px; height: 14px; content: " "; margin-left: .3125em; } .tablesaw-sortable .tablesaw-sortable-head.tablesaw-sortable-ascending .tablesaw-sortable-arrow:after, .tablesaw-sortable .tablesaw-sortable-head.tablesaw-sortable-descending .tablesaw-sortable-arrow:after { content: "\0020"; } .tablesaw-sortable .tablesaw-sortable-head.tablesaw-sortable-ascending .tablesaw-sortable-arrow:after { content: "\2191"; } .tablesaw-sortable .tablesaw-sortable-head.tablesaw-sortable-descending .tablesaw-sortable-arrow:after { content: "\2193"; } .tablesaw-sortable .not-applicable:after { content: "--"; display: block; } .tablesaw-sortable .not-applicable span { display: none; } .tablesaw-advance { float: right; } .tablesaw-advance.minimap { margin-right: .4em; } .tablesaw-advance-dots { float: left; margin: 0; padding: 0; list-style: none; } .tablesaw-advance-dots li { display: table-cell; margin: 0; padding: .4em .2em; } .tablesaw-advance-dots li i { width: .25em; height: .25em; background: #555; border-radius: 100%; display: inline-block; } .tablesaw-advance-dots-hide { opacity: .25; cursor: default; pointer-events: none; } assets/js/tablesaw/tablesaw-init.js 0000644 00000001522 14736103451 0013371 0 ustar 00 /*! Tablesaw - v3.0.1 - 2017-02-16 * https://github.com/filamentgroup/tablesaw * Copyright (c) 2017 Filament Group; Licensed MIT */ (function( win ) { "use strict"; var $; if( 'shoestring' in win ) { $ = win.shoestring; } else if( 'jQuery' in win ) { $ = win.jQuery; } else { throw new Error( "tablesaw: DOM library not found." ); } // DOM-ready auto-init of plugins. // Many plugins bind to an "enhance" event to init themselves on dom ready, or when new markup is inserted into the DOM // Use raw DOMContentLoaded instead of shoestring (may have issues in Android 2.3, exhibited by stack table) if( "addEventListener" in document ) { document.addEventListener( "DOMContentLoaded", function() { $( document ).trigger( "enhance.tablesaw" ); }); } })( typeof window !== "undefined" ? window : this ); assets/js/tablesaw/tablesaw.jquery.js 0000644 00000117222 14736103451 0013753 0 ustar 00 /*! Tablesaw - v3.0.1 - 2017-02-16 * https://github.com/filamentgroup/tablesaw * Copyright (c) 2017 Filament Group; Licensed MIT */ // UMD module definition // From: https://github.com/umdjs/umd/blob/master/templates/jqueryPlugin.js (function (factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module. define(['jquery'], factory); } else if (typeof module === 'object' && module.exports) { // Node/CommonJS module.exports = function( root, jQuery ) { if ( jQuery === undefined ) { // require('jQuery') returns a factory that requires window to // build a jQuery instance, we normalize how we use modules // that require this pattern but the window provided is a noop // if it's defined (how jquery works) if ( typeof window !== 'undefined' ) { jQuery = require('jquery'); } else { jQuery = require('jquery')(root); } } factory(jQuery); return jQuery; }; } else { // Browser globals factory(jQuery); } }(function ($) { "use strict"; var win = typeof window !== "undefined" ? window : this; var Tablesaw = { i18n: { modes: [ 'Stack', 'Swipe', 'Toggle' ], columns: 'Col<span class=\"a11y-sm\">umn</span>s', columnBtnText: 'Columns', columnsDialogError: 'No eligible columns.', sort: 'Sort' }, // cut the mustard mustard: ( 'head' in document ) && // IE9+, Firefox 4+, Safari 5.1+, Mobile Safari 4.1+, Opera 11.5+, Android 2.3+ ( !window.blackberry || window.WebKitPoint ) && // only WebKit Blackberry (OS 6+) !window.operamini }; if( Tablesaw.mustard ) { $( document.documentElement ).addClass( 'tablesaw-enhanced' ); } (function() { var pluginName = "tablesaw"; var classes = { toolbar: "tablesaw-bar" }; var events = { create: "tablesawcreate", destroy: "tablesawdestroy", refresh: "tablesawrefresh", resize: "tablesawresize" }; var defaultMode = "stack"; var initSelector = "table[data-tablesaw-mode],table[data-tablesaw-sortable]"; var defaultConfig = { getHeaderCells: function() { return this.$table.find( "thead" ).children().filter( "tr" ).eq( 0 ).find( "th" ); } }; Tablesaw.events = events; var Table = function( element ) { if( !element ) { throw new Error( "Tablesaw requires an element." ); } this.table = element; this.$table = $( element ); this.mode = this.$table.attr( "data-tablesaw-mode" ) || defaultMode; this.init(); }; Table.prototype.init = function() { // assign an id if there is none if ( !this.$table.attr( "id" ) ) { this.$table.attr( "id", pluginName + "-" + Math.round( Math.random() * 10000 ) ); } this.createToolbar(); // TODO this is used inside stack table init for some reason? what does it do? this._initCells(); this.$table.trigger( events.create, [ this ] ); }; Table.prototype.getConfig = function( pluginSpecificConfig ) { // shoestring extend doesn’t support arbitrary args var configs = $.extend( defaultConfig, pluginSpecificConfig || {} ); return $.extend( configs, typeof TablesawConfig !== "undefined" ? TablesawConfig : {} ); }; Table.prototype._getPrimaryHeaderCells = function() { return this.getConfig().getHeaderCells.call( this ); }; Table.prototype._findHeadersForCell = function( cell ) { var $headers = this._getPrimaryHeaderCells(); var results = []; for( var rowNumber = 1; rowNumber < this.headerMapping.length; rowNumber++ ) { for( var colNumber = 0; colNumber < this.headerMapping[ rowNumber ].length; colNumber++ ) { if( this.headerMapping[ rowNumber ][ colNumber ] === cell ) { results.push( $headers[ colNumber ] ); } } } return results; }; Table.prototype._initCells = function() { var $rows = this.$table.find( "tr" ); var columnLookup = []; $rows.each(function( rowNumber ) { columnLookup[ rowNumber ] = []; }); $rows.each(function( rowNumber ) { var coltally = 0; var $t = $( this ); var children = $t.children(); // var isInHeader = $t.closest( "thead" ).length; children.each(function() { var colspan = parseInt( this.getAttribute( "colspan" ), 10 ); var rowspan = parseInt( this.getAttribute( "rowspan" ), 10 ); // set in a previous rowspan while( columnLookup[ rowNumber ][ coltally ] ) { coltally++; } columnLookup[ rowNumber ][ coltally ] = this; // TODO both colspan and rowspan if( colspan ) { for( var k = 0; k < colspan - 1; k++ ){ coltally++; columnLookup[ rowNumber ][ coltally ] = this; } } if( rowspan ) { for( var j = 1; j < rowspan; j++ ){ columnLookup[ rowNumber + j ][ coltally ] = this; } } coltally++; }); }); for( var colNumber = 0; colNumber < columnLookup[ 0 ].length; colNumber++ ) { var headerCol = columnLookup[ 0 ][ colNumber ]; var rowNumber = 0; var rowCell; if( !headerCol.cells ) { headerCol.cells = []; } while( rowNumber < columnLookup.length ) { rowCell = columnLookup[ rowNumber ][ colNumber ]; if( headerCol !== rowCell ) { headerCol.cells.push( rowCell ); } rowNumber++; } } this.headerMapping = columnLookup; }; Table.prototype.refresh = function() { this._initCells(); this.$table.trigger( events.refresh, [ this ] ); }; Table.prototype.createToolbar = function() { // Insert the toolbar // TODO move this into a separate component var $toolbar = this.$table.prev().filter( '.' + classes.toolbar ); if( !$toolbar.length ) { $toolbar = $( '<div>' ) .addClass( classes.toolbar ) .insertBefore( this.$table ); } this.$toolbar = $toolbar; if( this.mode ) { this.$toolbar.addClass( 'tablesaw-mode-' + this.mode ); } }; Table.prototype.destroy = function() { // Don’t remove the toolbar. Some of the table features are not yet destroy-friendly. this.$table.prev().filter( '.' + classes.toolbar ).each(function() { this.className = this.className.replace( /\btablesaw-mode\-\w*\b/gi, '' ); }); var tableId = this.$table.attr( 'id' ); $( document ).off( "." + tableId ); $( window ).off( "." + tableId ); // other plugins this.$table.trigger( events.destroy, [ this ] ); this.$table.removeData( pluginName ); }; // Collection method. $.fn[ pluginName ] = function() { return this.each( function() { var $t = $( this ); if( $t.data( pluginName ) ){ return; } var table = new Table( this ); $t.data( pluginName, table ); }); }; var $doc = $( win.document ); $doc.on( "enhance.tablesaw", function( e ) { // Cut the mustard if( Tablesaw.mustard ) { $( e.target ).find( initSelector )[ pluginName ](); } }); // Avoid a resize during scroll: // Some Mobile devices trigger a resize during scroll (sometimes when // doing elastic stretch at the end of the document or from the // location bar hide) var isScrolling = false; var scrollTimeout; $doc.on( "scroll.tablesaw", function() { isScrolling = true; win.clearTimeout( scrollTimeout ); scrollTimeout = win.setTimeout(function() { isScrolling = false; }, 300 ); }); $doc.on( "resize.tablesaw", function() { if( !isScrolling ) { $doc.trigger( events.resize ); } }); }()); (function(){ var classes = { stackTable: 'tablesaw-stack', cellLabels: 'tablesaw-cell-label', cellContentLabels: 'tablesaw-cell-content' }; var data = { key: 'tablesaw-stack' }; var attrs = { labelless: 'data-tablesaw-no-labels', hideempty: 'data-tablesaw-hide-empty' }; var Stack = function( element, tablesaw ) { this.tablesaw = tablesaw; this.$table = $( element ); this.labelless = this.$table.is( '[' + attrs.labelless + ']' ); this.hideempty = this.$table.is( '[' + attrs.hideempty + ']' ); this.$table.data( data.key, this ); }; Stack.prototype.init = function() { this.$table.addClass( classes.stackTable ); if( this.labelless ) { return; } var self = this; this.$table.find( "th, td" ).filter(function() { return !$( this ).closest( "thead" ).length; }).filter(function() { return !$( this ).closest( "tr" ).is( "[" + attrs.labelless + "]" ) && ( !self.hideempty || !!$( this ).html() ); }).each(function() { var html = []; var $cell = $( this ); // headers $( self.tablesaw._findHeadersForCell( this ) ).each(function() { var $header = $( this.cloneNode( true ) ); // TODO decouple from sortable better // Changed from .text() in https://github.com/filamentgroup/tablesaw/commit/b9c12a8f893ec192830ec3ba2d75f062642f935b // to preserve structural html in headers, like <a> var $sortableButton = $header.find( ".tablesaw-sortable-btn" ); $header.find( ".tablesaw-sortable-arrow" ).remove(); html.push( $sortableButton.length ? $sortableButton.html() : $header.html() ); }); if( !$cell.find( "." + classes.cellContentLabels ).length ) { $cell.wrapInner( "<span class='" + classes.cellContentLabels + "'></span>" ); } // Update if already exists. var $label = $cell.find( "." + classes.cellLabels ); var newHtml = html.join( ", " ); if( !$label.length ) { $cell.prepend( "<b class='" + classes.cellLabels + "'>" + newHtml + "</b>" ); } else if( $label.html() !== newHtml ) { // only if changed $label.html( newHtml ); } }); }; Stack.prototype.destroy = function() { this.$table.removeClass( classes.stackTable ); this.$table.find( '.' + classes.cellLabels ).remove(); this.$table.find( '.' + classes.cellContentLabels ).each(function() { $( this ).replaceWith( this.childNodes ); }); }; // on tablecreate, init $( document ).on( Tablesaw.events.create, function( e, tablesaw ){ if( tablesaw.mode === 'stack' ){ var table = new Stack( tablesaw.table, tablesaw ); table.init(); } }).on( Tablesaw.events.refresh, function( e, tablesaw ){ if( tablesaw.mode === 'stack' ){ $( tablesaw.table ).data( data.key ).init(); } }).on( Tablesaw.events.destroy, function( e, tablesaw ){ if( tablesaw.mode === 'stack' ){ $( tablesaw.table ).data( data.key ).destroy(); } }); }()); (function() { var pluginName = "tablesawbtn", methods = { _create: function(){ return $( this ).each(function() { $( this ) .trigger( "beforecreate." + pluginName )[ pluginName ]( "_init" ) .trigger( "create." + pluginName ); }); }, _init: function(){ var oEl = $( this ), sel = this.getElementsByTagName( "select" )[ 0 ]; if( sel ) { $( this ) .addClass( "btn-select" )[ pluginName ]( "_select", sel ); } return oEl; }, _select: function( sel ) { var update = function( oEl, sel ) { var opts = $( sel ).find( "option" ); var label = document.createElement( "span" ); var el; var children; var found = false; label.setAttribute( "aria-hidden", "true" ); label.innerHTML = " "; opts.each(function() { var opt = this; if( opt.selected ) { label.innerHTML = opt.text; } }); children = oEl.childNodes; if( opts.length > 0 ){ for( var i = 0, l = children.length; i < l; i++ ) { el = children[ i ]; if( el && el.nodeName.toUpperCase() === "SPAN" ) { oEl.replaceChild( label, el ); found = true; } } if( !found ) { oEl.insertBefore( label, oEl.firstChild ); } } }; update( this, sel ); $( this ).on( "change refresh", function() { update( this, sel ); }); } }; // Collection method. $.fn[ pluginName ] = function( arrg, a, b, c ) { return this.each(function() { // if it's a method if( arrg && typeof( arrg ) === "string" ){ return $.fn[ pluginName ].prototype[ arrg ].call( this, a, b, c ); } // don't re-init if( $( this ).data( pluginName + "active" ) ){ return $( this ); } $( this ).data( pluginName + "active", true ); $.fn[ pluginName ].prototype._create.call( this ); }); }; // add methods $.extend( $.fn[ pluginName ].prototype, methods ); }()); (function(){ var data = { key: "tablesaw-coltoggle" }; var ColumnToggle = function( element ) { this.$table = $( element ); if( !this.$table.length ) { return; } this.classes = { columnToggleTable: 'tablesaw-columntoggle', columnBtnContain: 'tablesaw-columntoggle-btnwrap tablesaw-advance', columnBtn: 'tablesaw-columntoggle-btn tablesaw-nav-btn down', popup: 'tablesaw-columntoggle-popup', priorityPrefix: 'tablesaw-priority-', // TODO duplicate class, also in tables.js toolbar: 'tablesaw-bar' }; // Expose headers and allHeaders properties on the widget // headers references the THs within the first TR in the table this.headers = this.$table.find( "tr" ).eq( 0 ).find( "th" ); this.$table.data( data.key, this ); }; ColumnToggle.prototype.init = function() { if( !this.$table.length ) { return; } var tableId, id, $menuButton, $popup, $menu, $btnContain, self = this; this.$table.addClass( this.classes.columnToggleTable ); tableId = this.$table.attr( "id" ); id = tableId + "-popup"; $btnContain = $( "<div class='" + this.classes.columnBtnContain + "'></div>" ); $menuButton = $( "<a href='#" + id + "' class='btn btn-micro " + this.classes.columnBtn +"' data-popup-link>" + "<span>" + Tablesaw.i18n.columnBtnText + "</span></a>" ); $popup = $( "<div class='dialog-table-coltoggle " + this.classes.popup + "' id='" + id + "'></div>" ); $menu = $( "<div class='btn-group'></div>" ); var hasNonPersistentHeaders = false; $( this.headers ).not( "td" ).each( function() { var $this = $( this ), priority = $this.attr("data-tablesaw-priority"), $cells = self.$getCells( this ); if( priority && priority !== "persist" ) { $cells.addClass( self.classes.priorityPrefix + priority ); $("<label><input type='checkbox' checked>" + $this.text() + "</label>" ) .appendTo( $menu ) .children() .first() .data( "tablesaw-header", this ); hasNonPersistentHeaders = true; } }); if( !hasNonPersistentHeaders ) { $menu.append( '<label>' + Tablesaw.i18n.columnsDialogError + '</label>' ); } $menu.appendTo( $popup ); // bind change event listeners to inputs - TODO: move to a private method? $menu.find( 'input[type="checkbox"]' ).on( "change", function(e) { var checked = e.target.checked; var $cells = self.$getCellsFromCheckbox( e.target ); $cells[ !checked ? "addClass" : "removeClass" ]( "tablesaw-cell-hidden" ); $cells[ checked ? "addClass" : "removeClass" ]( "tablesaw-cell-visible" ); self.$table.trigger( 'tablesawcolumns' ); }); $menuButton.appendTo( $btnContain ); $btnContain.appendTo( this.$table.prev().filter( '.' + this.classes.toolbar ) ); function closePopup( event ) { // Click came from inside the popup, ignore. if( event && $( event.target ).closest( "." + self.classes.popup ).length ) { return; } $( document ).off( 'click.' + tableId ); $menuButton.removeClass( 'up' ).addClass( 'down' ); $btnContain.removeClass( 'visible' ); } var closeTimeout; function openPopup() { $btnContain.addClass( 'visible' ); $menuButton.removeClass( 'down' ).addClass( 'up' ); $( document ).off( 'click.' + tableId, closePopup ); window.clearTimeout( closeTimeout ); closeTimeout = window.setTimeout(function() { $( document ).on( 'click.' + tableId, closePopup ); }, 15 ); } $menuButton.on( "click.tablesaw", function( event ) { event.preventDefault(); if( !$btnContain.is( ".visible" ) ) { openPopup(); } else { closePopup(); } }); $popup.appendTo( $btnContain ); this.$menu = $menu; $(window).on( Tablesaw.events.resize + "." + tableId, function(){ self.refreshToggle(); }); this.refreshToggle(); }; ColumnToggle.prototype.$getCells = function( th ) { return $( th ).add( th.cells ); }; ColumnToggle.prototype.$getCellsFromCheckbox = function( checkbox ) { var th = $( checkbox ).data( "tablesaw-header" ); return this.$getCells( th ); }; ColumnToggle.prototype.refreshToggle = function() { var self = this; this.$menu.find( "input" ).each( function() { this.checked = self.$getCellsFromCheckbox( this ).eq( 0 ).css( "display" ) === "table-cell"; }); }; ColumnToggle.prototype.refreshPriority = function(){ var self = this; $(this.headers).not( "td" ).each( function() { var $this = $( this ), priority = $this.attr("data-tablesaw-priority"), $cells = $this.add( this.cells ); if( priority && priority !== "persist" ) { $cells.addClass( self.classes.priorityPrefix + priority ); } }); }; ColumnToggle.prototype.destroy = function() { this.$table.removeClass( this.classes.columnToggleTable ); this.$table.find( 'th, td' ).each(function() { var $cell = $( this ); $cell.removeClass( 'tablesaw-cell-hidden' ) .removeClass( 'tablesaw-cell-visible' ); this.className = this.className.replace( /\bui\-table\-priority\-\d\b/g, '' ); }); }; // on tablecreate, init $( document ).on( Tablesaw.events.create, function( e, tablesaw ){ if( tablesaw.mode === 'columntoggle' ){ var table = new ColumnToggle( tablesaw.table ); table.init(); } } ); $( document ).on( Tablesaw.events.destroy, function( e, tablesaw ){ if( tablesaw.mode === 'columntoggle' ){ $( tablesaw.table ).data( data.key ).destroy(); } } ); }()); ;(function() { function getSortValue( cell ) { var text = []; $( cell.childNodes ).each(function() { var $el = $( this ); if( $el.is( 'input, select' ) ) { text.push( $el.val() ); } else if( $el.is( '.tablesaw-cell-label' ) ) { } else { text.push( ( $el.text() || '' ).replace(/^\s+|\s+$/g, '') ); } }); return text.join( '' ); } var pluginName = "tablesaw-sortable", initSelector = "table[data-" + pluginName + "]", sortableSwitchSelector = "[data-" + pluginName + "-switch]", attrs = { defaultCol: "data-tablesaw-sortable-default-col", numericCol: "data-tablesaw-sortable-numeric" }, classes = { head: pluginName + "-head", ascend: pluginName + "-ascending", descend: pluginName + "-descending", switcher: pluginName + "-switch", tableToolbar: 'tablesaw-toolbar', sortButton: pluginName + "-btn" }, methods = { _create: function( o ){ return $( this ).each(function() { var init = $( this ).data( pluginName + "-init" ); if( init ) { return false; } $( this ) .data( pluginName + "-init", true ) .trigger( "beforecreate." + pluginName ) [ pluginName ]( "_init" , o ) .trigger( "create." + pluginName ); }); }, _init: function(){ var el = $( this ), heads, $switcher; var addClassToTable = function(){ el.addClass( pluginName ); }, addClassToHeads = function( h ){ $.each( h , function( i , v ){ $( v ).addClass( classes.head ); }); }, makeHeadsActionable = function( h , fn ){ $.each( h , function( i , v ){ var b = $( "<button class='" + classes.sortButton + "'/>" ); b.on( "click" , { col: v } , fn ); $( v ).wrapInner( b ); b.append( "<span class='tablesaw-sortable-arrow'>" ); }); }, clearOthers = function( sibs ){ $.each( sibs , function( i , v ){ var col = $( v ); col.removeAttr( attrs.defaultCol ); col.removeClass( classes.ascend ); col.removeClass( classes.descend ); }); }, headsOnAction = function( e ){ if( $( e.target ).is( 'a[href]' ) ) { return; } e.stopPropagation(); var head = $( this ).parent(), v = e.data.col, newSortValue = heads.index( head[0] ); clearOthers( head.siblings() ); if( head.is( "." + classes.descend ) || !head.is( "." + classes.ascend ) ){ el[ pluginName ]( "sortBy" , v , true); newSortValue += '_asc'; } else { el[ pluginName ]( "sortBy" , v ); newSortValue += '_desc'; } if( $switcher ) { $switcher.find( 'select' ).val( newSortValue ).trigger( 'refresh' ); } e.preventDefault(); }, handleDefault = function( heads ){ $.each( heads , function( idx , el ){ var $el = $( el ); if( $el.is( "[" + attrs.defaultCol + "]" ) ){ if( !$el.is( "." + classes.descend ) ) { $el.addClass( classes.ascend ); } } }); }, addSwitcher = function( heads ){ $switcher = $( '<div>' ).addClass( classes.switcher ).addClass( classes.tableToolbar ); var html = [ '<label>' + Tablesaw.i18n.sort + ':' ]; html.push( '<span class="btn"><select>' ); heads.each(function( j ) { var $t = $( this ); var isDefaultCol = $t.is( "[" + attrs.defaultCol + "]" ); var isDescending = $t.is( "." + classes.descend ); var hasNumericAttribute = $t.is( '[' + attrs.numericCol + ']' ); var numericCount = 0; // Check only the first four rows to see if the column is numbers. var numericCountMax = 5; $( this.cells.slice( 0, numericCountMax ) ).each(function() { if( !isNaN( parseInt( getSortValue( this ), 10 ) ) ) { numericCount++; } }); var isNumeric = numericCount === numericCountMax; if( !hasNumericAttribute ) { $t.attr( attrs.numericCol, isNumeric ? "" : "false" ); } html.push( '<option' + ( isDefaultCol && !isDescending ? ' selected' : '' ) + ' value="' + j + '_asc">' + $t.text() + ' ' + ( isNumeric ? '↑' : '(A-Z)' ) + '</option>' ); html.push( '<option' + ( isDefaultCol && isDescending ? ' selected' : '' ) + ' value="' + j + '_desc">' + $t.text() + ' ' + ( isNumeric ? '↓' : '(Z-A)' ) + '</option>' ); }); html.push( '</select></span></label>' ); $switcher.html( html.join('') ); var $toolbar = el.prev().filter( '.tablesaw-bar' ), $firstChild = $toolbar.children().eq( 0 ); if( $firstChild.length ) { $switcher.insertBefore( $firstChild ); } else { $switcher.appendTo( $toolbar ); } $switcher.find( '.btn' ).tablesawbtn(); $switcher.find( 'select' ).on( 'change', function() { var val = $( this ).val().split( '_' ), head = heads.eq( val[ 0 ] ); clearOthers( head.siblings() ); el[ pluginName ]( 'sortBy', head.get( 0 ), val[ 1 ] === 'asc' ); }); }; addClassToTable(); heads = el.find( "thead th[data-" + pluginName + "-col]" ); addClassToHeads( heads ); makeHeadsActionable( heads , headsOnAction ); handleDefault( heads ); if( el.is( sortableSwitchSelector ) ) { addSwitcher( heads, el.find('tbody tr:nth-child(-n+3)') ); } }, getColumnNumber: function( col ){ return $( col ).prevAll().length; }, getTableRows: function(){ return $( this ).find( "tbody tr" ); }, sortRows: function( rows , colNum , ascending, col ){ var cells, fn, sorted; var getCells = function( rows ){ var cells = []; $.each( rows , function( i , r ){ var element = $( r ).children().get( colNum ); cells.push({ element: element, cell: getSortValue( element ), rowNum: i }); }); return cells; }, getSortFxn = function( ascending, forceNumeric ){ var fn, regex = /[^\-\+\d\.]/g; if( ascending ){ fn = function( a , b ){ if( forceNumeric ) { return parseFloat( a.cell.replace( regex, '' ) ) - parseFloat( b.cell.replace( regex, '' ) ); } else { return a.cell.toLowerCase() > b.cell.toLowerCase() ? 1 : -1; } }; } else { fn = function( a , b ){ if( forceNumeric ) { return parseFloat( b.cell.replace( regex, '' ) ) - parseFloat( a.cell.replace( regex, '' ) ); } else { return a.cell.toLowerCase() < b.cell.toLowerCase() ? 1 : -1; } }; } return fn; }, applyToRows = function( sorted , rows ){ var newRows = [], i, l, cur; for( i = 0, l = sorted.length ; i < l ; i++ ){ cur = sorted[ i ].rowNum; newRows.push( rows[cur] ); } return newRows; }; cells = getCells( rows ); var customFn = $( col ).data( 'tablesaw-sort' ); fn = ( customFn && typeof customFn === "function" ? customFn( ascending ) : false ) || getSortFxn( ascending, $( col ).is( '[' + attrs.numericCol + ']' ) && !$( col ).is( '[' + attrs.numericCol + '="false"]' ) ); sorted = cells.sort( fn ); rows = applyToRows( sorted , rows ); return rows; }, replaceTableRows: function( rows ){ var el = $( this ), body = el.find( "tbody" ); for( var j = 0, k = rows.length; j < k; j++ ) { body.append( rows[ j ] ); } }, makeColDefault: function( col , a ){ var c = $( col ); c.attr( attrs.defaultCol , "true" ); if( a ){ c.removeClass( classes.descend ); c.addClass( classes.ascend ); } else { c.removeClass( classes.ascend ); c.addClass( classes.descend ); } }, sortBy: function( col , ascending ){ var el = $( this ), colNum, rows; colNum = el[ pluginName ]( "getColumnNumber" , col ); rows = el[ pluginName ]( "getTableRows" ); rows = el[ pluginName ]( "sortRows" , rows , colNum , ascending, col ); el[ pluginName ]( "replaceTableRows" , rows ); el[ pluginName ]( "makeColDefault" , col , ascending ); el.trigger( "tablesaw-sorted" ); } }; // Collection method. $.fn[ pluginName ] = function( arrg ) { var args = Array.prototype.slice.call( arguments , 1), returnVal; // if it's a method if( arrg && typeof( arrg ) === "string" ){ returnVal = $.fn[ pluginName ].prototype[ arrg ].apply( this[0], args ); return (typeof returnVal !== "undefined")? returnVal:$(this); } // check init if( !$( this ).data( pluginName + "-active" ) ){ $( this ).data( pluginName + "-active", true ); $.fn[ pluginName ].prototype._create.call( this , arrg ); } return $(this); }; // add methods $.extend( $.fn[ pluginName ].prototype, methods ); $( document ).on( Tablesaw.events.create, function( e, Tablesaw ) { if( Tablesaw.$table.is( initSelector ) ) { Tablesaw.$table[ pluginName ](); } }); }()); (function(){ var classes = { // TODO duplicate class, also in tables.js toolbar: "tablesaw-bar", hideBtn: "disabled", persistWidths: "tablesaw-fix-persist", allColumnsVisible: 'tablesaw-all-cols-visible' }; var attrs = { disableTouchEvents: "data-tablesaw-no-touch" }; function createSwipeTable( tbl, $table ){ var $btns = $( "<div class='tablesaw-advance'></div>" ); var $prevBtn = $( "<a href='#' class='tablesaw-nav-btn btn btn-micro left' title='Previous Column'></a>" ).appendTo( $btns ); var $nextBtn = $( "<a href='#' class='tablesaw-nav-btn btn btn-micro right' title='Next Column'></a>" ).appendTo( $btns ); var $headerCells = tbl._getPrimaryHeaderCells(); var $headerCellsNoPersist = $headerCells.not( '[data-tablesaw-priority="persist"]' ); var headerWidths = []; var $head = $( document.head || 'head' ); var tableId = $table.attr( 'id' ); if( !$headerCells.length ) { throw new Error( "tablesaw swipe: no header cells found. Are you using <th> inside of <thead>?" ); } $table.addClass( "tablesaw-swipe" ); // Calculate initial widths $headerCells.each(function() { var width = this.offsetWidth; headerWidths.push( width ); }); $btns.appendTo( $table.prev().filter( '.tablesaw-bar' ) ); if( !tableId ) { tableId = 'tableswipe-' + Math.round( Math.random() * 10000 ); $table.attr( 'id', tableId ); } function $getCells( headerCell ) { return $( headerCell.cells ).add( headerCell ); } function showColumn( headerCell ) { $getCells( headerCell ).removeClass( 'tablesaw-cell-hidden' ); } function hideColumn( headerCell ) { $getCells( headerCell ).addClass( 'tablesaw-cell-hidden' ); } function persistColumn( headerCell ) { $getCells( headerCell ).addClass( 'tablesaw-cell-persist' ); } function isPersistent( headerCell ) { return $( headerCell ).is( '[data-tablesaw-priority="persist"]' ); } function unmaintainWidths() { $table.removeClass( classes.persistWidths ); $( '#' + tableId + '-persist' ).remove(); } function maintainWidths() { var prefix = '#' + tableId + '.tablesaw-swipe ', styles = [], tableWidth = $table.width(), hash = [], newHash; // save persistent column widths (as long as they take up less than 75% of table width) $headerCells.each(function( index ) { var width; if( isPersistent( this ) ) { width = this.offsetWidth; if( width < tableWidth * 0.75 ) { hash.push( index + '-' + width ); styles.push( prefix + ' .tablesaw-cell-persist:nth-child(' + ( index + 1 ) + ') { width: ' + width + 'px; }' ); } } }); newHash = hash.join( '_' ); if( styles.length ) { $table.addClass( classes.persistWidths ); var $style = $( '#' + tableId + '-persist' ); // If style element not yet added OR if the widths have changed if( !$style.length || $style.data( 'tablesaw-hash' ) !== newHash ) { // Remove existing $style.remove(); $( '<style>' + styles.join( "\n" ) + '</style>' ) .attr( 'id', tableId + '-persist' ) .data( 'tablesaw-hash', newHash ) .appendTo( $head ); } } } function getNext(){ var next = [], checkFound; $headerCellsNoPersist.each(function( i ) { var $t = $( this ), isHidden = $t.css( "display" ) === "none" || $t.is( ".tablesaw-cell-hidden" ); if( !isHidden && !checkFound ) { checkFound = true; next[ 0 ] = i; } else if( isHidden && checkFound ) { next[ 1 ] = i; return false; } }); return next; } function getPrev(){ var next = getNext(); return [ next[ 1 ] - 1 , next[ 0 ] - 1 ]; } function nextpair( fwd ){ return fwd ? getNext() : getPrev(); } function canAdvance( pair ){ return pair[ 1 ] > -1 && pair[ 1 ] < $headerCellsNoPersist.length; } function matchesMedia() { var matchMedia = $table.attr( "data-tablesaw-swipe-media" ); return !matchMedia || ( "matchMedia" in win ) && win.matchMedia( matchMedia ).matches; } function fakeBreakpoints() { if( !matchesMedia() ) { return; } var containerWidth = $table.parent().width(), persist = [], sum = 0, sums = [], visibleNonPersistantCount = $headerCells.length; $headerCells.each(function( index ) { var $t = $( this ), isPersist = $t.is( '[data-tablesaw-priority="persist"]' ); persist.push( isPersist ); sum += headerWidths[ index ]; sums.push( sum ); // is persistent or is hidden if( isPersist || sum > containerWidth ) { visibleNonPersistantCount--; } }); // We need at least one column to swipe. var needsNonPersistentColumn = visibleNonPersistantCount === 0; $headerCells.each(function( index ) { if( persist[ index ] ) { // for visual box-shadow persistColumn( this ); return; } if( sums[ index ] <= containerWidth || needsNonPersistentColumn ) { needsNonPersistentColumn = false; showColumn( this ); } else { hideColumn( this ); } }); unmaintainWidths(); $table.trigger( 'tablesawcolumns' ); } function advance( fwd ){ var pair = nextpair( fwd ); if( canAdvance( pair ) ){ if( isNaN( pair[ 0 ] ) ){ if( fwd ){ pair[0] = 0; } else { pair[0] = $headerCellsNoPersist.length - 1; } } maintainWidths(); hideColumn( $headerCellsNoPersist.get( pair[ 0 ] ) ); showColumn( $headerCellsNoPersist.get( pair[ 1 ] ) ); $table.trigger( 'tablesawcolumns' ); } } $prevBtn.add( $nextBtn ).on( "click", function( e ){ advance( !!$( e.target ).closest( $nextBtn ).length ); e.preventDefault(); }); function getCoord( event, key ) { return ( event.touches || event.originalEvent.touches )[ 0 ][ key ]; } if( !$table.is( "[" + attrs.disableTouchEvents + "]" ) ) { $table .on( "touchstart.swipetoggle", function( e ){ var originX = getCoord( e, 'pageX' ); var originY = getCoord( e, 'pageY' ); var x; var y; var scrollTop = window.pageYOffset; $( win ).off( Tablesaw.events.resize, fakeBreakpoints ); $( this ) .on( "touchmove.swipetoggle", function( e ){ x = getCoord( e, 'pageX' ); y = getCoord( e, 'pageY' ); }) .on( "touchend.swipetoggle", function() { var cfg = tbl.getConfig({ swipeHorizontalThreshold: 30, swipeVerticalThreshold: 30 }); // This config code is a little awkward because shoestring doesn’t support deep $.extend // Trying to work around when devs only override one of (not both) horizontalThreshold or // verticalThreshold in their TablesawConfig. var verticalThreshold = cfg.swipe ? cfg.swipe.verticalThreshold : cfg.swipeVerticalThreshold; var horizontalThreshold = cfg.swipe ? cfg.swipe.horizontalThreshold : cfg.swipeHorizontalThreshold; var isPageScrolled = Math.abs( window.pageYOffset - scrollTop ) >= verticalThreshold; var isVerticalSwipe = Math.abs( y - originY ) >= verticalThreshold; if( !isVerticalSwipe && !isPageScrolled ) { if( x - originX < -1 * horizontalThreshold ){ advance( true ); } if( x - originX > horizontalThreshold ){ advance( false ); } } window.setTimeout(function() { $( win ).on( Tablesaw.events.resize, fakeBreakpoints ); }, 300); $( this ).off( "touchmove.swipetoggle touchend.swipetoggle" ); }); }); } $table .on( "tablesawcolumns.swipetoggle", function(){ var canGoPrev = canAdvance( getPrev() ); var canGoNext = canAdvance( getNext() ); $prevBtn[ canGoPrev ? "removeClass" : "addClass" ]( classes.hideBtn ); $nextBtn[ canGoNext ? "removeClass" : "addClass" ]( classes.hideBtn ); $prevBtn.closest( "." + classes.toolbar )[ !canGoPrev && !canGoNext ? 'addClass' : 'removeClass' ]( classes.allColumnsVisible ); }) .on( "tablesawnext.swipetoggle", function(){ advance( true ); } ) .on( "tablesawprev.swipetoggle", function(){ advance( false ); } ) .on( Tablesaw.events.destroy + ".swipetoggle", function(){ var $t = $( this ); $t.removeClass( 'tablesaw-swipe' ); $t.prev().filter( '.tablesaw-bar' ).find( '.tablesaw-advance' ).remove(); $( win ).off( Tablesaw.events.resize, fakeBreakpoints ); $t.off( ".swipetoggle" ); }) .on( Tablesaw.events.refresh, function() { // manual refresh headerWidths = []; $headerCells.each(function() { var width = this.offsetWidth; headerWidths.push( width ); }); fakeBreakpoints(); }); fakeBreakpoints(); $( win ).on( Tablesaw.events.resize, fakeBreakpoints ); } // on tablecreate, init $( document ).on( Tablesaw.events.create, function( e, tablesaw ){ if( tablesaw.mode === 'swipe' ){ createSwipeTable( tablesaw, tablesaw.$table ); } } ); }()); ;(function(){ var MiniMap = { attr: { init: 'data-tablesaw-minimap' } }; function createMiniMap( $table ){ var $btns = $( '<div class="tablesaw-advance minimap">' ), $dotNav = $( '<ul class="tablesaw-advance-dots">' ).appendTo( $btns ), hideDot = 'tablesaw-advance-dots-hide', $headerCells = $table.find( 'thead th' ); // populate dots $headerCells.each(function(){ $dotNav.append( '<li><i></i></li>' ); }); $btns.appendTo( $table.prev().filter( '.tablesaw-bar' ) ); function showMinimap( $table ) { var mq = $table.attr( MiniMap.attr.init ); return !mq || win.matchMedia && win.matchMedia( mq ).matches; } function showHideNav(){ if( !showMinimap( $table ) ) { $btns.css( "display", "none" ); return; } $btns.css( "display", "block" ); // show/hide dots var dots = $dotNav.find( "li" ).removeClass( hideDot ); $table.find( "thead th" ).each(function(i){ if( $( this ).css( "display" ) === "none" ){ dots.eq( i ).addClass( hideDot ); } }); } // run on init and resize showHideNav(); $( win ).on( Tablesaw.events.resize, showHideNav ); $table .on( "tablesawcolumns.minimap", function(){ showHideNav(); }) .on( Tablesaw.events.destroy + ".minimap", function(){ var $t = $( this ); $t.prev().filter( '.tablesaw-bar' ).find( '.tablesaw-advance' ).remove(); $( win ).off( Tablesaw.events.resize, showHideNav ); $t.off( ".minimap" ); }); } // on tablecreate, init $( document ).on( Tablesaw.events.create, function( e, tablesaw ){ if( ( tablesaw.mode === 'swipe' || tablesaw.mode === 'columntoggle' ) && tablesaw.$table.is( '[ ' + MiniMap.attr.init + ']' ) ){ createMiniMap( tablesaw.$table ); } } ); }()); ;(function() { var S = { selectors: { init: 'table[data-tablesaw-mode-switch]' }, attributes: { excludeMode: 'data-tablesaw-mode-exclude' }, classes: { main: 'tablesaw-modeswitch', toolbar: 'tablesaw-toolbar' }, modes: [ 'stack', 'swipe', 'columntoggle' ], init: function( table ) { var $table = $( table ), ignoreMode = $table.attr( S.attributes.excludeMode ), $toolbar = $table.prev().filter( '.tablesaw-bar' ), modeVal = '', $switcher = $( '<div>' ).addClass( S.classes.main + ' ' + S.classes.toolbar ); var html = [ '<label>' + Tablesaw.i18n.columns + ':' ], dataMode = $table.attr( 'data-tablesaw-mode' ), isSelected; html.push( '<span class="btn"><select>' ); for( var j=0, k = S.modes.length; j<k; j++ ) { if( ignoreMode && ignoreMode.toLowerCase() === S.modes[ j ] ) { continue; } isSelected = dataMode === S.modes[ j ]; if( isSelected ) { modeVal = S.modes[ j ]; } html.push( '<option' + ( isSelected ? ' selected' : '' ) + ' value="' + S.modes[ j ] + '">' + Tablesaw.i18n.modes[ j ] + '</option>' ); } html.push( '</select></span></label>' ); $switcher.html( html.join( '' ) ); var $otherToolbarItems = $toolbar.find( '.tablesaw-advance' ).eq( 0 ); if( $otherToolbarItems.length ) { $switcher.insertBefore( $otherToolbarItems ); } else { $switcher.appendTo( $toolbar ); } $switcher.find( '.btn' ).tablesawbtn(); $switcher.find( 'select' ).on( 'change', S.onModeChange ); }, onModeChange: function() { var $t = $( this ), $switcher = $t.closest( '.' + S.classes.main ), $table = $t.closest( '.tablesaw-bar' ).next().eq( 0 ), val = $t.val(); $switcher.remove(); $table.data( 'tablesaw' ).destroy(); $table.attr( 'data-tablesaw-mode', val ); $table.tablesaw(); } }; $( win.document ).on( Tablesaw.events.create, function( e, Tablesaw ) { if( Tablesaw.$table.is( S.selectors.init ) ) { S.init( Tablesaw.table ); } }); })(); })); assets/js/tablesaw/tablesaw.js 0000644 00000237233 14736103451 0012442 0 ustar 00 /*! Tablesaw - v3.0.1 - 2017-02-16 * https://github.com/filamentgroup/tablesaw * Copyright (c) 2017 Filament Group; Licensed MIT */ /*! Shoestring - v2.0.0 - 2017-02-14 * http://github.com/filamentgroup/shoestring/ * Copyright (c) 2017 Scott Jehl, Filament Group, Inc; Licensed MIT & GPLv2 */ (function( factory ) { if( typeof define === 'function' && define.amd ) { // AMD. Register as an anonymous module. define( [ 'shoestring' ], factory ); } else if (typeof module === 'object' && module.exports) { // Node/CommonJS module.exports = factory(); } else { // Browser globals factory(); } }(function () { var win = typeof window !== "undefined" ? window : this; var doc = win.document; /** * The shoestring object constructor. * * @param {string,object} prim The selector to find or element to wrap. * @param {object} sec The context in which to match the `prim` selector. * @returns shoestring * @this window */ function shoestring( prim, sec ){ var pType = typeof( prim ), ret = [], sel; // return an empty shoestring object if( !prim ){ return new Shoestring( ret ); } // ready calls if( prim.call ){ return shoestring.ready( prim ); } // handle re-wrapping shoestring objects if( prim.constructor === Shoestring && !sec ){ return prim; } // if string starting with <, make html if( pType === "string" && prim.indexOf( "<" ) === 0 ){ var dfrag = doc.createElement( "div" ); dfrag.innerHTML = prim; // TODO depends on children (circular) return shoestring( dfrag ).children().each(function(){ dfrag.removeChild( this ); }); } // if string, it's a selector, use qsa if( pType === "string" ){ if( sec ){ return shoestring( sec ).find( prim ); } sel = doc.querySelectorAll( prim ); return new Shoestring( sel, prim ); } // array like objects or node lists if( Object.prototype.toString.call( pType ) === '[object Array]' || (win.NodeList && prim instanceof win.NodeList) ){ return new Shoestring( prim, prim ); } // if it's an array, use all the elements if( prim.constructor === Array ){ return new Shoestring( prim, prim ); } // otherwise assume it's an object the we want at an index return new Shoestring( [prim], prim ); } var Shoestring = function( ret, prim ) { this.length = 0; this.selector = prim; shoestring.merge(this, ret); }; // TODO only required for tests Shoestring.prototype.reverse = [].reverse; // For adding element set methods shoestring.fn = Shoestring.prototype; shoestring.Shoestring = Shoestring; // For extending objects // TODO move to separate module when we use prototypes shoestring.extend = function( first, second ){ for( var i in second ){ if( second.hasOwnProperty( i ) ){ first[ i ] = second[ i ]; } } return first; }; // taken directly from jQuery shoestring.merge = function( first, second ) { var len, j, i; len = +second.length, j = 0, i = first.length; for ( ; j < len; j++ ) { first[ i++ ] = second[ j ]; } first.length = i; return first; }; // expose win.shoestring = shoestring; /** * Iterates over `shoestring` collections. * * @param {function} callback The callback to be invoked on each element and index * @return shoestring * @this shoestring */ shoestring.fn.each = function( callback ){ return shoestring.each( this, callback ); }; shoestring.each = function( collection, callback ) { var val; for( var i = 0, il = collection.length; i < il; i++ ){ val = callback.call( collection[i], i, collection[i] ); if( val === false ){ break; } } return collection; }; /** * Check for array membership. * * @param {object} needle The thing to find. * @param {object} haystack The thing to find the needle in. * @return {boolean} * @this window */ shoestring.inArray = function( needle, haystack ){ var isin = -1; for( var i = 0, il = haystack.length; i < il; i++ ){ if( haystack.hasOwnProperty( i ) && haystack[ i ] === needle ){ isin = i; } } return isin; }; /** * Bind callbacks to be run when the DOM is "ready". * * @param {function} fn The callback to be run * @return shoestring * @this shoestring */ shoestring.ready = function( fn ){ if( ready && fn ){ fn.call( doc ); } else if( fn ){ readyQueue.push( fn ); } else { runReady(); } return [doc]; }; // TODO necessary? shoestring.fn.ready = function( fn ){ shoestring.ready( fn ); return this; }; // Empty and exec the ready queue var ready = false, readyQueue = [], runReady = function(){ if( !ready ){ while( readyQueue.length ){ readyQueue.shift().call( doc ); } ready = true; } }; // If DOM is already ready at exec time, depends on the browser. // From: https://github.com/mobify/mobifyjs/blob/526841be5509e28fc949038021799e4223479f8d/src/capture.js#L128 if (doc.attachEvent ? doc.readyState === "complete" : doc.readyState !== "loading") { runReady(); } else { doc.addEventListener( "DOMContentLoaded", runReady, false ); doc.addEventListener( "readystatechange", runReady, false ); win.addEventListener( "load", runReady, false ); } /** * Checks the current set of elements against the selector, if one matches return `true`. * * @param {string} selector The selector to check. * @return {boolean} * @this {shoestring} */ shoestring.fn.is = function( selector ){ var ret = false, self = this, parents, check; // assume a dom element if( typeof selector !== "string" ){ // array-like, ie shoestring objects or element arrays if( selector.length && selector[0] ){ check = selector; } else { check = [selector]; } return _checkElements(this, check); } parents = this.parent(); if( !parents.length ){ parents = shoestring( doc ); } parents.each(function( i, e ) { var children; children = e.querySelectorAll( selector ); ret = _checkElements( self, children ); }); return ret; }; function _checkElements(needles, haystack){ var ret = false; needles.each(function() { var j = 0; while( j < haystack.length ){ if( this === haystack[j] ){ ret = true; } j++; } }); return ret; } /** * Get data attached to the first element or set data values on all elements in the current set. * * @param {string} name The data attribute name. * @param {any} value The value assigned to the data attribute. * @return {any|shoestring} * @this shoestring */ shoestring.fn.data = function( name, value ){ if( name !== undefined ){ if( value !== undefined ){ return this.each(function(){ if( !this.shoestringData ){ this.shoestringData = {}; } this.shoestringData[ name ] = value; }); } else { if( this[ 0 ] ) { if( this[ 0 ].shoestringData ) { return this[ 0 ].shoestringData[ name ]; } } } } else { return this[ 0 ] ? this[ 0 ].shoestringData || {} : undefined; } }; /** * Remove data associated with `name` or all the data, for each element in the current set. * * @param {string} name The data attribute name. * @return shoestring * @this shoestring */ shoestring.fn.removeData = function( name ){ return this.each(function(){ if( name !== undefined && this.shoestringData ){ this.shoestringData[ name ] = undefined; delete this.shoestringData[ name ]; } else { this[ 0 ].shoestringData = {}; } }); }; /** * An alias for the `shoestring` constructor. */ win.$ = shoestring; /** * Add a class to each DOM element in the set of elements. * * @param {string} className The name of the class to be added. * @return shoestring * @this shoestring */ shoestring.fn.addClass = function( className ){ var classes = className.replace(/^\s+|\s+$/g, '').split( " " ); return this.each(function(){ for( var i = 0, il = classes.length; i < il; i++ ){ if( this.className !== undefined && (this.className === "" || !this.className.match( new RegExp( "(^|\\s)" + classes[ i ] + "($|\\s)"))) ){ this.className += " " + classes[ i ]; } } }); }; /** * Add elements matching the selector to the current set. * * @param {string} selector The selector for the elements to add from the DOM * @return shoestring * @this shoestring */ shoestring.fn.add = function( selector ){ var ret = []; this.each(function(){ ret.push( this ); }); shoestring( selector ).each(function(){ ret.push( this ); }); return shoestring( ret ); }; /** * Insert an element or HTML string as the last child of each element in the set. * * @param {string|HTMLElement} fragment The HTML or HTMLElement to insert. * @return shoestring * @this shoestring */ shoestring.fn.append = function( fragment ){ if( typeof( fragment ) === "string" || fragment.nodeType !== undefined ){ fragment = shoestring( fragment ); } return this.each(function( i ){ for( var j = 0, jl = fragment.length; j < jl; j++ ){ this.appendChild( i > 0 ? fragment[ j ].cloneNode( true ) : fragment[ j ] ); } }); }; /** * Insert the current set as the last child of the elements matching the selector. * * @param {string} selector The selector after which to append the current set. * @return shoestring * @this shoestring */ shoestring.fn.appendTo = function( selector ){ return this.each(function(){ shoestring( selector ).append( this ); }); }; /** * Get the value of the first element of the set or set the value of all the elements in the set. * * @param {string} name The attribute name. * @param {string} value The new value for the attribute. * @return {shoestring|string|undefined} * @this {shoestring} */ shoestring.fn.attr = function( name, value ){ var nameStr = typeof( name ) === "string"; if( value !== undefined || !nameStr ){ return this.each(function(){ if( nameStr ){ this.setAttribute( name, value ); } else { for( var i in name ){ if( name.hasOwnProperty( i ) ){ this.setAttribute( i, name[ i ] ); } } } }); } else { return this[ 0 ] ? this[ 0 ].getAttribute( name ) : undefined; } }; /** * Insert an element or HTML string before each element in the current set. * * @param {string|HTMLElement} fragment The HTML or HTMLElement to insert. * @return shoestring * @this shoestring */ shoestring.fn.before = function( fragment ){ if( typeof( fragment ) === "string" || fragment.nodeType !== undefined ){ fragment = shoestring( fragment ); } return this.each(function( i ){ for( var j = 0, jl = fragment.length; j < jl; j++ ){ this.parentNode.insertBefore( i > 0 ? fragment[ j ].cloneNode( true ) : fragment[ j ], this ); } }); }; /** * Get the children of the current collection. * @return shoestring * @this shoestring */ shoestring.fn.children = function(){ var ret = [], childs, j; this.each(function(){ childs = this.children; j = -1; while( j++ < childs.length-1 ){ if( shoestring.inArray( childs[ j ], ret ) === -1 ){ ret.push( childs[ j ] ); } } }); return shoestring(ret); }; /** * Find an element matching the selector in the set of the current element and its parents. * * @param {string} selector The selector used to identify the target element. * @return shoestring * @this shoestring */ shoestring.fn.closest = function( selector ){ var ret = []; if( !selector ){ return shoestring( ret ); } this.each(function(){ var element, $self = shoestring( element = this ); if( $self.is(selector) ){ ret.push( this ); return; } while( element.parentElement ) { if( shoestring(element.parentElement).is(selector) ){ ret.push( element.parentElement ); break; } element = element.parentElement; } }); return shoestring( ret ); }; shoestring.cssExceptions = { 'float': [ 'cssFloat' ] }; (function() { var cssExceptions = shoestring.cssExceptions; // IE8 uses marginRight instead of margin-right function convertPropertyName( str ) { return str.replace( /\-([A-Za-z])/g, function ( match, character ) { return character.toUpperCase(); }); } function _getStyle( element, property ) { return win.getComputedStyle( element, null ).getPropertyValue( property ); } var vendorPrefixes = [ '', '-webkit-', '-ms-', '-moz-', '-o-', '-khtml-' ]; /** * Private function for getting the computed style of an element. * * **NOTE** Please use the [css](../css.js.html) method instead. * * @method _getStyle * @param {HTMLElement} element The element we want the style property for. * @param {string} property The css property we want the style for. */ shoestring._getStyle = function( element, property ) { var convert, value, j, k; if( cssExceptions[ property ] ) { for( j = 0, k = cssExceptions[ property ].length; j < k; j++ ) { value = _getStyle( element, cssExceptions[ property ][ j ] ); if( value ) { return value; } } } for( j = 0, k = vendorPrefixes.length; j < k; j++ ) { convert = convertPropertyName( vendorPrefixes[ j ] + property ); // VendorprefixKeyName || key-name value = _getStyle( element, convert ); if( convert !== property ) { value = value || _getStyle( element, property ); } if( vendorPrefixes[ j ] ) { // -vendorprefix-key-name value = value || _getStyle( element, vendorPrefixes[ j ] + property ); } if( value ) { return value; } } return undefined; }; })(); (function() { var cssExceptions = shoestring.cssExceptions; // IE8 uses marginRight instead of margin-right function convertPropertyName( str ) { return str.replace( /\-([A-Za-z])/g, function ( match, character ) { return character.toUpperCase(); }); } /** * Private function for setting the style of an element. * * **NOTE** Please use the [css](../css.js.html) method instead. * * @method _setStyle * @param {HTMLElement} element The element we want to style. * @param {string} property The property being used to style the element. * @param {string} value The css value for the style property. */ shoestring._setStyle = function( element, property, value ) { var convertedProperty = convertPropertyName(property); element.style[ property ] = value; if( convertedProperty !== property ) { element.style[ convertedProperty ] = value; } if( cssExceptions[ property ] ) { for( var j = 0, k = cssExceptions[ property ].length; j<k; j++ ) { element.style[ cssExceptions[ property ][ j ] ] = value; } } }; })(); /** * Get the compute style property of the first element or set the value of a style property * on all elements in the set. * * @method _setStyle * @param {string} property The property being used to style the element. * @param {string|undefined} value The css value for the style property. * @return {string|shoestring} * @this shoestring */ shoestring.fn.css = function( property, value ){ if( !this[0] ){ return; } if( typeof property === "object" ) { return this.each(function() { for( var key in property ) { if( property.hasOwnProperty( key ) ) { shoestring._setStyle( this, key, property[key] ); } } }); } else { // assignment else retrieve first if( value !== undefined ){ return this.each(function(){ shoestring._setStyle( this, property, value ); }); } return shoestring._getStyle( this[0], property ); } }; /** * Returns the indexed element wrapped in a new `shoestring` object. * * @param {integer} index The index of the element to wrap and return. * @return shoestring * @this shoestring */ shoestring.fn.eq = function( index ){ if( this[index] ){ return shoestring( this[index] ); } return shoestring([]); }; /** * Filter out the current set if they do *not* match the passed selector or * the supplied callback returns false * * @param {string,function} selector The selector or boolean return value callback used to filter the elements. * @return shoestring * @this shoestring */ shoestring.fn.filter = function( selector ){ var ret = []; this.each(function( index ){ var wsel; if( typeof selector === 'function' ) { if( selector.call( this, index ) !== false ) { ret.push( this ); } } else { if( !this.parentNode ){ var context = shoestring( doc.createDocumentFragment() ); context[ 0 ].appendChild( this ); wsel = shoestring( selector, context ); } else { wsel = shoestring( selector, this.parentNode ); } if( shoestring.inArray( this, wsel ) > -1 ){ ret.push( this ); } } }); return shoestring( ret ); }; /** * Find descendant elements of the current collection. * * @param {string} selector The selector used to find the children * @return shoestring * @this shoestring */ shoestring.fn.find = function( selector ){ var ret = [], finds; this.each(function(){ finds = this.querySelectorAll( selector ); for( var i = 0, il = finds.length; i < il; i++ ){ ret = ret.concat( finds[i] ); } }); return shoestring( ret ); }; /** * Returns the first element of the set wrapped in a new `shoestring` object. * * @return shoestring * @this shoestring */ shoestring.fn.first = function(){ return this.eq( 0 ); }; /** * Returns the raw DOM node at the passed index. * * @param {integer} index The index of the element to wrap and return. * @return {HTMLElement|undefined|array} * @this shoestring */ shoestring.fn.get = function( index ){ // return an array of elements if index is undefined if( index === undefined ){ var elements = []; for( var i = 0; i < this.length; i++ ){ elements.push( this[ i ] ); } return elements; } else { return this[ index ]; } }; var set = function( html ){ if( typeof html === "string" || typeof html === "number" ){ return this.each(function(){ this.innerHTML = "" + html; }); } else { var h = ""; if( typeof html.length !== "undefined" ){ for( var i = 0, l = html.length; i < l; i++ ){ h += html[i].outerHTML; } } else { h = html.outerHTML; } return this.each(function(){ this.innerHTML = h; }); } }; /** * Gets or sets the `innerHTML` from all the elements in the set. * * @param {string|undefined} html The html to assign * @return {string|shoestring} * @this shoestring */ shoestring.fn.html = function( html ){ if( typeof html !== "undefined" ){ return set.call( this, html ); } else { // get var pile = ""; this.each(function(){ pile += this.innerHTML; }); return pile; } }; (function() { function _getIndex( set, test ) { var i, result, element; for( i = result = 0; i < set.length; i++ ) { element = set.item ? set.item(i) : set[i]; if( test(element) ){ return result; } // ignore text nodes, etc // NOTE may need to be more permissive if( element.nodeType === 1 ){ result++; } } return -1; } /** * Find the index in the current set for the passed selector. * Without a selector it returns the index of the first node within the array of its siblings. * * @param {string|undefined} selector The selector used to search for the index. * @return {integer} * @this {shoestring} */ shoestring.fn.index = function( selector ){ var self, children; self = this; // no arg? check the children, otherwise check each element that matches if( selector === undefined ){ children = ( ( this[ 0 ] && this[0].parentNode ) || doc.documentElement).childNodes; // check if the element matches the first of the set return _getIndex(children, function( element ) { return self[0] === element; }); } else { // check if the element matches the first selected node from the parent return _getIndex(self, function( element ) { return element === (shoestring( selector, element.parentNode )[ 0 ]); }); } }; })(); /** * Insert the current set before the elements matching the selector. * * @param {string} selector The selector before which to insert the current set. * @return shoestring * @this shoestring */ shoestring.fn.insertBefore = function( selector ){ return this.each(function(){ shoestring( selector ).before( this ); }); }; /** * Returns the last element of the set wrapped in a new `shoestring` object. * * @return shoestring * @this shoestring */ shoestring.fn.last = function(){ return this.eq( this.length - 1 ); }; /** * Returns a `shoestring` object with the set of siblings of each element in the original set. * * @return shoestring * @this shoestring */ shoestring.fn.next = function(){ var result = []; // TODO need to implement map this.each(function() { var children, item, found; // get the child nodes for this member of the set children = shoestring( this.parentNode )[0].childNodes; for( var i = 0; i < children.length; i++ ){ item = children.item( i ); // found the item we needed (found) which means current item value is // the next node in the list, as long as it's viable grab it // NOTE may need to be more permissive if( found && item.nodeType === 1 ){ result.push( item ); break; } // find the current item and mark it as found if( item === this ){ found = true; } } }); return shoestring( result ); }; /** * Removes elements from the current set. * * @param {string} selector The selector to use when removing the elements. * @return shoestring * @this shoestring */ shoestring.fn.not = function( selector ){ var ret = []; this.each(function(){ var found = shoestring( selector, this.parentNode ); if( shoestring.inArray(this, found) === -1 ){ ret.push( this ); } }); return shoestring( ret ); }; /** * Returns the set of first parents for each element in the current set. * * @return shoestring * @this shoestring */ shoestring.fn.parent = function(){ var ret = [], parent; this.each(function(){ // no parent node, assume top level // jQuery parent: return the document object for <html> or the parent node if it exists parent = (this === doc.documentElement ? doc : this.parentNode); // if there is a parent and it's not a document fragment if( parent && parent.nodeType !== 11 ){ ret.push( parent ); } }); return shoestring(ret); }; /** * Add an HTML string or element before the children of each element in the current set. * * @param {string|HTMLElement} fragment The HTML string or element to add. * @return shoestring * @this shoestring */ shoestring.fn.prepend = function( fragment ){ if( typeof( fragment ) === "string" || fragment.nodeType !== undefined ){ fragment = shoestring( fragment ); } return this.each(function( i ){ for( var j = 0, jl = fragment.length; j < jl; j++ ){ var insertEl = i > 0 ? fragment[ j ].cloneNode( true ) : fragment[ j ]; if ( this.firstChild ){ this.insertBefore( insertEl, this.firstChild ); } else { this.appendChild( insertEl ); } } }); }; /** * Returns a `shoestring` object with the set of *one* siblingx before each element in the original set. * * @return shoestring * @this shoestring */ shoestring.fn.prev = function(){ var result = []; // TODO need to implement map this.each(function() { var children, item, found; // get the child nodes for this member of the set children = shoestring( this.parentNode )[0].childNodes; for( var i = children.length -1; i >= 0; i-- ){ item = children.item( i ); // found the item we needed (found) which means current item value is // the next node in the list, as long as it's viable grab it // NOTE may need to be more permissive if( found && item.nodeType === 1 ){ result.push( item ); break; } // find the current item and mark it as found if( item === this ){ found = true; } } }); return shoestring( result ); }; /** * Returns a `shoestring` object with the set of *all* siblings before each element in the original set. * * @return shoestring * @this shoestring */ shoestring.fn.prevAll = function(){ var result = []; this.each(function() { var $previous = shoestring( this ).prev(); while( $previous.length ){ result.push( $previous[0] ); $previous = $previous.prev(); } }); return shoestring( result ); }; /** * Remove an attribute from each element in the current set. * * @param {string} name The name of the attribute. * @return shoestring * @this shoestring */ shoestring.fn.removeAttr = function( name ){ return this.each(function(){ this.removeAttribute( name ); }); }; /** * Remove a class from each DOM element in the set of elements. * * @param {string} className The name of the class to be removed. * @return shoestring * @this shoestring */ shoestring.fn.removeClass = function( cname ){ var classes = cname.replace(/^\s+|\s+$/g, '').split( " " ); return this.each(function(){ var newClassName, regex; for( var i = 0, il = classes.length; i < il; i++ ){ if( this.className !== undefined ){ regex = new RegExp( "(^|\\s)" + classes[ i ] + "($|\\s)", "gmi" ); newClassName = this.className.replace( regex, " " ); this.className = newClassName.replace(/^\s+|\s+$/g, ''); } } }); }; /** * Remove the current set of elements from the DOM. * * @return shoestring * @this shoestring */ shoestring.fn.remove = function(){ return this.each(function(){ if( this.parentNode ) { this.parentNode.removeChild( this ); } }); }; /** * Replace each element in the current set with that argument HTML string or HTMLElement. * * @param {string|HTMLElement} fragment The value to assign. * @return shoestring * @this shoestring */ shoestring.fn.replaceWith = function( fragment ){ if( typeof( fragment ) === "string" ){ fragment = shoestring( fragment ); } var ret = []; if( fragment.length > 1 ){ fragment = fragment.reverse(); } this.each(function( i ){ var clone = this.cloneNode( true ), insertEl; ret.push( clone ); // If there is no parentNode, this is pointless, drop it. if( !this.parentNode ){ return; } if( fragment.length === 1 ){ insertEl = i > 0 ? fragment[ 0 ].cloneNode( true ) : fragment[ 0 ]; this.parentNode.replaceChild( insertEl, this ); } else { for( var j = 0, jl = fragment.length; j < jl; j++ ){ insertEl = i > 0 ? fragment[ j ].cloneNode( true ) : fragment[ j ]; this.parentNode.insertBefore( insertEl, this.nextSibling ); } this.parentNode.removeChild( this ); } }); return shoestring( ret ); }; /** * Get all of the sibling elements for each element in the current set. * * @return shoestring * @this shoestring */ shoestring.fn.siblings = function(){ if( !this.length ) { return shoestring( [] ); } var sibs = [], el = this[ 0 ].parentNode.firstChild; do { if( el.nodeType === 1 && el !== this[ 0 ] ) { sibs.push( el ); } el = el.nextSibling; } while( el ); return shoestring( sibs ); }; var getText = function( elem ){ var node, ret = "", i = 0, nodeType = elem.nodeType; if ( !nodeType ) { // If no nodeType, this is expected to be an array while ( (node = elem[i++]) ) { // Do not traverse comment nodes ret += getText( node ); } } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { // Use textContent for elements // innerText usage removed for consistency of new lines (jQuery #11153) if ( typeof elem.textContent === "string" ) { return elem.textContent; } else { // Traverse its children for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { ret += getText( elem ); } } } else if ( nodeType === 3 || nodeType === 4 ) { return elem.nodeValue; } // Do not include comment or processing instruction nodes return ret; }; /** * Recursively retrieve the text content of the each element in the current set. * * @return shoestring * @this shoestring */ shoestring.fn.text = function() { return getText( this ); }; /** * Get the value of the first element or set the value of all elements in the current set. * * @param {string} value The value to set. * @return shoestring * @this shoestring */ shoestring.fn.val = function( value ){ var el; if( value !== undefined ){ return this.each(function(){ if( this.tagName === "SELECT" ){ var optionSet, option, options = this.options, values = [], i = options.length, newIndex; values[0] = value; while ( i-- ) { option = options[ i ]; if ( (option.selected = shoestring.inArray( option.value, values ) >= 0) ) { optionSet = true; newIndex = i; } } // force browsers to behave consistently when non-matching value is set if ( !optionSet ) { this.selectedIndex = -1; } else { this.selectedIndex = newIndex; } } else { this.value = value; } }); } else { el = this[0]; if( el.tagName === "SELECT" ){ if( el.selectedIndex < 0 ){ return ""; } return el.options[ el.selectedIndex ].value; } else { return el.value; } } }; /** * Private function for setting/getting the offset property for height/width. * * **NOTE** Please use the [width](width.js.html) or [height](height.js.html) methods instead. * * @param {shoestring} set The set of elements. * @param {string} name The string "height" or "width". * @param {float|undefined} value The value to assign. * @return shoestring * @this window */ shoestring._dimension = function( set, name, value ){ var offsetName; if( value === undefined ){ offsetName = name.replace(/^[a-z]/, function( letter ) { return letter.toUpperCase(); }); return set[ 0 ][ "offset" + offsetName ]; } else { // support integer values as pixels value = typeof value === "string" ? value : value + "px"; return set.each(function(){ this.style[ name ] = value; }); } }; /** * Gets the width value of the first element or sets the width for the whole set. * * @param {float|undefined} value The value to assign. * @return shoestring * @this shoestring */ shoestring.fn.width = function( value ){ return shoestring._dimension( this, "width", value ); }; /** * Wraps the child elements in the provided HTML. * * @param {string} html The wrapping HTML. * @return shoestring * @this shoestring */ shoestring.fn.wrapInner = function( html ){ return this.each(function(){ var inH = this.innerHTML; this.innerHTML = ""; shoestring( this ).append( shoestring( html ).html( inH ) ); }); }; function initEventCache( el, evt ) { if ( !el.shoestringData ) { el.shoestringData = {}; } if ( !el.shoestringData.events ) { el.shoestringData.events = {}; } if ( !el.shoestringData.loop ) { el.shoestringData.loop = {}; } if ( !el.shoestringData.events[ evt ] ) { el.shoestringData.events[ evt ] = []; } } function addToEventCache( el, evt, eventInfo ) { var obj = {}; obj.isCustomEvent = eventInfo.isCustomEvent; obj.callback = eventInfo.callfunc; obj.originalCallback = eventInfo.originalCallback; obj.namespace = eventInfo.namespace; el.shoestringData.events[ evt ].push( obj ); if( eventInfo.customEventLoop ) { el.shoestringData.loop[ evt ] = eventInfo.customEventLoop; } } /** * Bind a callback to an event for the currrent set of elements. * * @param {string} evt The event(s) to watch for. * @param {object,function} data Data to be included with each event or the callback. * @param {function} originalCallback Callback to be invoked when data is define.d. * @return shoestring * @this shoestring */ shoestring.fn.bind = function( evt, data, originalCallback ){ if( typeof data === "function" ){ originalCallback = data; data = null; } var evts = evt.split( " " ); // NOTE the `triggeredElement` is purely for custom events from IE function encasedCallback( e, namespace, triggeredElement ){ var result; if( e._namespace && e._namespace !== namespace ) { return; } e.data = data; e.namespace = e._namespace; var returnTrue = function(){ return true; }; e.isDefaultPrevented = function(){ return false; }; var originalPreventDefault = e.preventDefault; var preventDefaultConstructor = function(){ if( originalPreventDefault ) { return function(){ e.isDefaultPrevented = returnTrue; originalPreventDefault.call(e); }; } else { return function(){ e.isDefaultPrevented = returnTrue; e.returnValue = false; }; } }; // thanks https://github.com/jonathantneal/EventListener e.target = triggeredElement || e.target || e.srcElement; e.preventDefault = preventDefaultConstructor(); e.stopPropagation = e.stopPropagation || function () { e.cancelBubble = true; }; result = originalCallback.apply(this, [ e ].concat( e._args ) ); if( result === false ){ e.preventDefault(); e.stopPropagation(); } return result; } return this.each(function(){ var domEventCallback, customEventCallback, customEventLoop, oEl = this; for( var i = 0, il = evts.length; i < il; i++ ){ var split = evts[ i ].split( "." ), evt = split[ 0 ], namespace = split.length > 0 ? split[ 1 ] : null; domEventCallback = function( originalEvent ) { if( oEl.ssEventTrigger ) { originalEvent._namespace = oEl.ssEventTrigger._namespace; originalEvent._args = oEl.ssEventTrigger._args; oEl.ssEventTrigger = null; } return encasedCallback.call( oEl, originalEvent, namespace ); }; customEventCallback = null; customEventLoop = null; initEventCache( this, evt ); this.addEventListener( evt, domEventCallback, false ); addToEventCache( this, evt, { callfunc: customEventCallback || domEventCallback, isCustomEvent: !!customEventCallback, customEventLoop: customEventLoop, originalCallback: originalCallback, namespace: namespace }); } }); }; shoestring.fn.on = shoestring.fn.bind; /** * Unbind a previous bound callback for an event. * * @param {string} event The event(s) the callback was bound to.. * @param {function} callback Callback to unbind. * @return shoestring * @this shoestring */ shoestring.fn.unbind = function( event, callback ){ var evts = event ? event.split( " " ) : []; return this.each(function(){ if( !this.shoestringData || !this.shoestringData.events ) { return; } if( !evts.length ) { unbindAll.call( this ); } else { var split, evt, namespace; for( var i = 0, il = evts.length; i < il; i++ ){ split = evts[ i ].split( "." ), evt = split[ 0 ], namespace = split.length > 0 ? split[ 1 ] : null; if( evt ) { unbind.call( this, evt, namespace, callback ); } else { unbindAll.call( this, namespace, callback ); } } } }); }; function unbind( evt, namespace, callback ) { var bound = this.shoestringData.events[ evt ]; if( !(bound && bound.length) ) { return; } var matched = [], j, jl; for( j = 0, jl = bound.length; j < jl; j++ ) { if( !namespace || namespace === bound[ j ].namespace ) { if( callback === undefined || callback === bound[ j ].originalCallback ) { this.removeEventListener( evt, bound[ j ].callback, false ); matched.push( j ); } } } for( j = 0, jl = matched.length; j < jl; j++ ) { this.shoestringData.events[ evt ].splice( j, 1 ); } } function unbindAll( namespace, callback ) { for( var evtKey in this.shoestringData.events ) { unbind.call( this, evtKey, namespace, callback ); } } shoestring.fn.off = shoestring.fn.unbind; /** * Bind a callback to an event for the currrent set of elements, unbind after one occurence. * * @param {string} event The event(s) to watch for. * @param {function} callback Callback to invoke on the event. * @return shoestring * @this shoestring */ shoestring.fn.one = function( event, callback ){ var evts = event.split( " " ); return this.each(function(){ var thisevt, cbs = {}, $t = shoestring( this ); for( var i = 0, il = evts.length; i < il; i++ ){ thisevt = evts[ i ]; cbs[ thisevt ] = function( e ){ var $t = shoestring( this ); for( var j in cbs ) { $t.unbind( j, cbs[ j ] ); } return callback.apply( this, [ e ].concat( e._args ) ); }; $t.bind( thisevt, cbs[ thisevt ] ); } }); }; /** * Trigger an event on the first element in the set, no bubbling, no defaults. * * @param {string} event The event(s) to trigger. * @param {object} args Arguments to append to callback invocations. * @return shoestring * @this shoestring */ shoestring.fn.triggerHandler = function( event, args ){ var e = event.split( " " )[ 0 ], el = this[ 0 ], ret; // See this.fireEvent( 'on' + evts[ i ], document.createEventObject() ); instead of click() etc in trigger. if( doc.createEvent && el.shoestringData && el.shoestringData.events && el.shoestringData.events[ e ] ){ var bindings = el.shoestringData.events[ e ]; for (var i in bindings ){ if( bindings.hasOwnProperty( i ) ){ event = doc.createEvent( "Event" ); event.initEvent( e, true, true ); event._args = args; args.unshift( event ); ret = bindings[ i ].originalCallback.apply( event.target, args ); } } } return ret; }; /** * Trigger an event on each of the DOM elements in the current set. * * @param {string} event The event(s) to trigger. * @param {object} args Arguments to append to callback invocations. * @return shoestring * @this shoestring */ shoestring.fn.trigger = function( event, args ){ var evts = event.split( " " ); return this.each(function(){ var split, evt, namespace; for( var i = 0, il = evts.length; i < il; i++ ){ split = evts[ i ].split( "." ), evt = split[ 0 ], namespace = split.length > 0 ? split[ 1 ] : null; if( evt === "click" ){ if( this.tagName === "INPUT" && this.type === "checkbox" && this.click ){ this.click(); return false; } } if( doc.createEvent ){ var event = doc.createEvent( "Event" ); event.initEvent( evt, true, true ); event._args = args; event._namespace = namespace; this.dispatchEvent( event ); } } }); }; return shoestring; })); // UMD module definition // From: https://github.com/umdjs/umd/blob/master/templates/jqueryPlugin.js (function (factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module. define(['shoestring'], factory); } else if (typeof module === 'object' && module.exports) { // Node/CommonJS module.exports = function( root, shoestring ) { if ( shoestring === undefined ) { // require('shoestring') returns a factory that requires window to // build a shoestring instance, we normalize how we use modules // that require this pattern but the window provided is a noop // if it's defined (how jquery works) if ( typeof window !== 'undefined' ) { shoestring = require('shoestring'); } else { shoestring = require('shoestring')(root); } } factory(shoestring); return shoestring; }; } else { // Browser globals factory(shoestring); } }(function ($) { "use strict"; var win = typeof window !== "undefined" ? window : this; var Tablesaw = { i18n: { modes: [ 'Stack', 'Swipe', 'Toggle' ], columns: 'Col<span class=\"a11y-sm\">umn</span>s', columnBtnText: 'Columns', columnsDialogError: 'No eligible columns.', sort: 'Sort' }, // cut the mustard mustard: ( 'head' in document ) && // IE9+, Firefox 4+, Safari 5.1+, Mobile Safari 4.1+, Opera 11.5+, Android 2.3+ ( !window.blackberry || window.WebKitPoint ) && // only WebKit Blackberry (OS 6+) !window.operamini }; if( Tablesaw.mustard ) { $( document.documentElement ).addClass( 'tablesaw-enhanced' ); } (function() { var pluginName = "tablesaw"; var classes = { toolbar: "tablesaw-bar" }; var events = { create: "tablesawcreate", destroy: "tablesawdestroy", refresh: "tablesawrefresh", resize: "tablesawresize" }; var defaultMode = "stack"; var initSelector = "table[data-tablesaw-mode],table[data-tablesaw-sortable]"; var defaultConfig = { getHeaderCells: function() { return this.$table.find( "thead" ).children().filter( "tr" ).eq( 0 ).find( "th" ); } }; Tablesaw.events = events; var Table = function( element ) { if( !element ) { throw new Error( "Tablesaw requires an element." ); } this.table = element; this.$table = $( element ); this.mode = this.$table.attr( "data-tablesaw-mode" ) || defaultMode; this.init(); }; Table.prototype.init = function() { // assign an id if there is none if ( !this.$table.attr( "id" ) ) { this.$table.attr( "id", pluginName + "-" + Math.round( Math.random() * 10000 ) ); } this.createToolbar(); // TODO this is used inside stack table init for some reason? what does it do? this._initCells(); this.$table.trigger( events.create, [ this ] ); }; Table.prototype.getConfig = function( pluginSpecificConfig ) { // shoestring extend doesn’t support arbitrary args var configs = $.extend( defaultConfig, pluginSpecificConfig || {} ); return $.extend( configs, typeof TablesawConfig !== "undefined" ? TablesawConfig : {} ); }; Table.prototype._getPrimaryHeaderCells = function() { return this.getConfig().getHeaderCells.call( this ); }; Table.prototype._findHeadersForCell = function( cell ) { var $headers = this._getPrimaryHeaderCells(); var results = []; for( var rowNumber = 1; rowNumber < this.headerMapping.length; rowNumber++ ) { for( var colNumber = 0; colNumber < this.headerMapping[ rowNumber ].length; colNumber++ ) { if( this.headerMapping[ rowNumber ][ colNumber ] === cell ) { results.push( $headers[ colNumber ] ); } } } return results; }; Table.prototype._initCells = function() { var $rows = this.$table.find( "tr" ); var columnLookup = []; $rows.each(function( rowNumber ) { columnLookup[ rowNumber ] = []; }); $rows.each(function( rowNumber ) { var coltally = 0; var $t = $( this ); var children = $t.children(); // var isInHeader = $t.closest( "thead" ).length; children.each(function() { var colspan = parseInt( this.getAttribute( "colspan" ), 10 ); var rowspan = parseInt( this.getAttribute( "rowspan" ), 10 ); // set in a previous rowspan while( columnLookup[ rowNumber ][ coltally ] ) { coltally++; } columnLookup[ rowNumber ][ coltally ] = this; // TODO both colspan and rowspan if( colspan ) { for( var k = 0; k < colspan - 1; k++ ){ coltally++; columnLookup[ rowNumber ][ coltally ] = this; } } if( rowspan ) { for( var j = 1; j < rowspan; j++ ){ columnLookup[ rowNumber + j ][ coltally ] = this; } } coltally++; }); }); for( var colNumber = 0; colNumber < columnLookup[ 0 ].length; colNumber++ ) { var headerCol = columnLookup[ 0 ][ colNumber ]; var rowNumber = 0; var rowCell; if( !headerCol.cells ) { headerCol.cells = []; } while( rowNumber < columnLookup.length ) { rowCell = columnLookup[ rowNumber ][ colNumber ]; if( headerCol !== rowCell ) { headerCol.cells.push( rowCell ); } rowNumber++; } } this.headerMapping = columnLookup; }; Table.prototype.refresh = function() { this._initCells(); this.$table.trigger( events.refresh, [ this ] ); }; Table.prototype.createToolbar = function() { // Insert the toolbar // TODO move this into a separate component var $toolbar = this.$table.prev().filter( '.' + classes.toolbar ); if( !$toolbar.length ) { $toolbar = $( '<div>' ) .addClass( classes.toolbar ) .insertBefore( this.$table ); } this.$toolbar = $toolbar; if( this.mode ) { this.$toolbar.addClass( 'tablesaw-mode-' + this.mode ); } }; Table.prototype.destroy = function() { // Don’t remove the toolbar. Some of the table features are not yet destroy-friendly. this.$table.prev().filter( '.' + classes.toolbar ).each(function() { this.className = this.className.replace( /\btablesaw-mode\-\w*\b/gi, '' ); }); var tableId = this.$table.attr( 'id' ); $( document ).off( "." + tableId ); $( window ).off( "." + tableId ); // other plugins this.$table.trigger( events.destroy, [ this ] ); this.$table.removeData( pluginName ); }; // Collection method. $.fn[ pluginName ] = function() { return this.each( function() { var $t = $( this ); if( $t.data( pluginName ) ){ return; } var table = new Table( this ); $t.data( pluginName, table ); }); }; var $doc = $( win.document ); $doc.on( "enhance.tablesaw", function( e ) { // Cut the mustard if( Tablesaw.mustard ) { $( e.target ).find( initSelector )[ pluginName ](); } }); // Avoid a resize during scroll: // Some Mobile devices trigger a resize during scroll (sometimes when // doing elastic stretch at the end of the document or from the // location bar hide) var isScrolling = false; var scrollTimeout; $doc.on( "scroll.tablesaw", function() { isScrolling = true; win.clearTimeout( scrollTimeout ); scrollTimeout = win.setTimeout(function() { isScrolling = false; }, 300 ); }); $doc.on( "resize.tablesaw", function() { if( !isScrolling ) { $doc.trigger( events.resize ); } }); }()); (function(){ var classes = { stackTable: 'tablesaw-stack', cellLabels: 'tablesaw-cell-label', cellContentLabels: 'tablesaw-cell-content' }; var data = { key: 'tablesaw-stack' }; var attrs = { labelless: 'data-tablesaw-no-labels', hideempty: 'data-tablesaw-hide-empty' }; var Stack = function( element, tablesaw ) { this.tablesaw = tablesaw; this.$table = $( element ); this.labelless = this.$table.is( '[' + attrs.labelless + ']' ); this.hideempty = this.$table.is( '[' + attrs.hideempty + ']' ); this.$table.data( data.key, this ); }; Stack.prototype.init = function() { this.$table.addClass( classes.stackTable ); if( this.labelless ) { return; } var self = this; this.$table.find( "th, td" ).filter(function() { return !$( this ).closest( "thead" ).length; }).filter(function() { return !$( this ).closest( "tr" ).is( "[" + attrs.labelless + "]" ) && ( !self.hideempty || !!$( this ).html() ); }).each(function() { var html = []; var $cell = $( this ); // headers $( self.tablesaw._findHeadersForCell( this ) ).each(function() { var $header = $( this.cloneNode( true ) ); // TODO decouple from sortable better // Changed from .text() in https://github.com/filamentgroup/tablesaw/commit/b9c12a8f893ec192830ec3ba2d75f062642f935b // to preserve structural html in headers, like <a> var $sortableButton = $header.find( ".tablesaw-sortable-btn" ); $header.find( ".tablesaw-sortable-arrow" ).remove(); html.push( $sortableButton.length ? $sortableButton.html() : $header.html() ); }); if( !$cell.find( "." + classes.cellContentLabels ).length ) { $cell.wrapInner( "<span class='" + classes.cellContentLabels + "'></span>" ); } // Update if already exists. var $label = $cell.find( "." + classes.cellLabels ); var newHtml = html.join( ", " ); if( !$label.length ) { $cell.prepend( "<b class='" + classes.cellLabels + "'>" + newHtml + "</b>" ); } else if( $label.html() !== newHtml ) { // only if changed $label.html( newHtml ); } }); }; Stack.prototype.destroy = function() { this.$table.removeClass( classes.stackTable ); this.$table.find( '.' + classes.cellLabels ).remove(); this.$table.find( '.' + classes.cellContentLabels ).each(function() { $( this ).replaceWith( this.childNodes ); }); }; // on tablecreate, init $( document ).on( Tablesaw.events.create, function( e, tablesaw ){ if( tablesaw.mode === 'stack' ){ var table = new Stack( tablesaw.table, tablesaw ); table.init(); } }).on( Tablesaw.events.refresh, function( e, tablesaw ){ if( tablesaw.mode === 'stack' ){ $( tablesaw.table ).data( data.key ).init(); } }).on( Tablesaw.events.destroy, function( e, tablesaw ){ if( tablesaw.mode === 'stack' ){ $( tablesaw.table ).data( data.key ).destroy(); } }); }()); (function() { var pluginName = "tablesawbtn", methods = { _create: function(){ return $( this ).each(function() { $( this ) .trigger( "beforecreate." + pluginName )[ pluginName ]( "_init" ) .trigger( "create." + pluginName ); }); }, _init: function(){ var oEl = $( this ), sel = this.getElementsByTagName( "select" )[ 0 ]; if( sel ) { $( this ) .addClass( "btn-select" )[ pluginName ]( "_select", sel ); } return oEl; }, _select: function( sel ) { var update = function( oEl, sel ) { var opts = $( sel ).find( "option" ); var label = document.createElement( "span" ); var el; var children; var found = false; label.setAttribute( "aria-hidden", "true" ); label.innerHTML = " "; opts.each(function() { var opt = this; if( opt.selected ) { label.innerHTML = opt.text; } }); children = oEl.childNodes; if( opts.length > 0 ){ for( var i = 0, l = children.length; i < l; i++ ) { el = children[ i ]; if( el && el.nodeName.toUpperCase() === "SPAN" ) { oEl.replaceChild( label, el ); found = true; } } if( !found ) { oEl.insertBefore( label, oEl.firstChild ); } } }; update( this, sel ); $( this ).on( "change refresh", function() { update( this, sel ); }); } }; // Collection method. $.fn[ pluginName ] = function( arrg, a, b, c ) { return this.each(function() { // if it's a method if( arrg && typeof( arrg ) === "string" ){ return $.fn[ pluginName ].prototype[ arrg ].call( this, a, b, c ); } // don't re-init if( $( this ).data( pluginName + "active" ) ){ return $( this ); } $( this ).data( pluginName + "active", true ); $.fn[ pluginName ].prototype._create.call( this ); }); }; // add methods $.extend( $.fn[ pluginName ].prototype, methods ); }()); (function(){ var data = { key: "tablesaw-coltoggle" }; var ColumnToggle = function( element ) { this.$table = $( element ); if( !this.$table.length ) { return; } this.classes = { columnToggleTable: 'tablesaw-columntoggle', columnBtnContain: 'tablesaw-columntoggle-btnwrap tablesaw-advance', columnBtn: 'tablesaw-columntoggle-btn tablesaw-nav-btn down', popup: 'tablesaw-columntoggle-popup', priorityPrefix: 'tablesaw-priority-', // TODO duplicate class, also in tables.js toolbar: 'tablesaw-bar' }; // Expose headers and allHeaders properties on the widget // headers references the THs within the first TR in the table this.headers = this.$table.find( "tr" ).eq( 0 ).find( "th" ); this.$table.data( data.key, this ); }; ColumnToggle.prototype.init = function() { if( !this.$table.length ) { return; } var tableId, id, $menuButton, $popup, $menu, $btnContain, self = this; this.$table.addClass( this.classes.columnToggleTable ); tableId = this.$table.attr( "id" ); id = tableId + "-popup"; $btnContain = $( "<div class='" + this.classes.columnBtnContain + "'></div>" ); $menuButton = $( "<a href='#" + id + "' class='btn btn-micro " + this.classes.columnBtn +"' data-popup-link>" + "<span>" + Tablesaw.i18n.columnBtnText + "</span></a>" ); $popup = $( "<div class='dialog-table-coltoggle " + this.classes.popup + "' id='" + id + "'></div>" ); $menu = $( "<div class='btn-group'></div>" ); var hasNonPersistentHeaders = false; $( this.headers ).not( "td" ).each( function() { var $this = $( this ), priority = $this.attr("data-tablesaw-priority"), $cells = self.$getCells( this ); if( priority && priority !== "persist" ) { $cells.addClass( self.classes.priorityPrefix + priority ); $("<label><input type='checkbox' checked>" + $this.text() + "</label>" ) .appendTo( $menu ) .children() .first() .data( "tablesaw-header", this ); hasNonPersistentHeaders = true; } }); if( !hasNonPersistentHeaders ) { $menu.append( '<label>' + Tablesaw.i18n.columnsDialogError + '</label>' ); } $menu.appendTo( $popup ); // bind change event listeners to inputs - TODO: move to a private method? $menu.find( 'input[type="checkbox"]' ).on( "change", function(e) { var checked = e.target.checked; var $cells = self.$getCellsFromCheckbox( e.target ); $cells[ !checked ? "addClass" : "removeClass" ]( "tablesaw-cell-hidden" ); $cells[ checked ? "addClass" : "removeClass" ]( "tablesaw-cell-visible" ); self.$table.trigger( 'tablesawcolumns' ); }); $menuButton.appendTo( $btnContain ); $btnContain.appendTo( this.$table.prev().filter( '.' + this.classes.toolbar ) ); function closePopup( event ) { // Click came from inside the popup, ignore. if( event && $( event.target ).closest( "." + self.classes.popup ).length ) { return; } $( document ).off( 'click.' + tableId ); $menuButton.removeClass( 'up' ).addClass( 'down' ); $btnContain.removeClass( 'visible' ); } var closeTimeout; function openPopup() { $btnContain.addClass( 'visible' ); $menuButton.removeClass( 'down' ).addClass( 'up' ); $( document ).off( 'click.' + tableId, closePopup ); window.clearTimeout( closeTimeout ); closeTimeout = window.setTimeout(function() { $( document ).on( 'click.' + tableId, closePopup ); }, 15 ); } $menuButton.on( "click.tablesaw", function( event ) { event.preventDefault(); if( !$btnContain.is( ".visible" ) ) { openPopup(); } else { closePopup(); } }); $popup.appendTo( $btnContain ); this.$menu = $menu; $(window).on( Tablesaw.events.resize + "." + tableId, function(){ self.refreshToggle(); }); this.refreshToggle(); }; ColumnToggle.prototype.$getCells = function( th ) { return $( th ).add( th.cells ); }; ColumnToggle.prototype.$getCellsFromCheckbox = function( checkbox ) { var th = $( checkbox ).data( "tablesaw-header" ); return this.$getCells( th ); }; ColumnToggle.prototype.refreshToggle = function() { var self = this; this.$menu.find( "input" ).each( function() { this.checked = self.$getCellsFromCheckbox( this ).eq( 0 ).css( "display" ) === "table-cell"; }); }; ColumnToggle.prototype.refreshPriority = function(){ var self = this; $(this.headers).not( "td" ).each( function() { var $this = $( this ), priority = $this.attr("data-tablesaw-priority"), $cells = $this.add( this.cells ); if( priority && priority !== "persist" ) { $cells.addClass( self.classes.priorityPrefix + priority ); } }); }; ColumnToggle.prototype.destroy = function() { this.$table.removeClass( this.classes.columnToggleTable ); this.$table.find( 'th, td' ).each(function() { var $cell = $( this ); $cell.removeClass( 'tablesaw-cell-hidden' ) .removeClass( 'tablesaw-cell-visible' ); this.className = this.className.replace( /\bui\-table\-priority\-\d\b/g, '' ); }); }; // on tablecreate, init $( document ).on( Tablesaw.events.create, function( e, tablesaw ){ if( tablesaw.mode === 'columntoggle' ){ var table = new ColumnToggle( tablesaw.table ); table.init(); } } ); $( document ).on( Tablesaw.events.destroy, function( e, tablesaw ){ if( tablesaw.mode === 'columntoggle' ){ $( tablesaw.table ).data( data.key ).destroy(); } } ); }()); ;(function() { function getSortValue( cell ) { var text = []; $( cell.childNodes ).each(function() { var $el = $( this ); if( $el.is( 'input, select' ) ) { text.push( $el.val() ); } else if( $el.is( '.tablesaw-cell-label' ) ) { } else { text.push( ( $el.text() || '' ).replace(/^\s+|\s+$/g, '') ); } }); return text.join( '' ); } var pluginName = "tablesaw-sortable", initSelector = "table[data-" + pluginName + "]", sortableSwitchSelector = "[data-" + pluginName + "-switch]", attrs = { defaultCol: "data-tablesaw-sortable-default-col", numericCol: "data-tablesaw-sortable-numeric" }, classes = { head: pluginName + "-head", ascend: pluginName + "-ascending", descend: pluginName + "-descending", switcher: pluginName + "-switch", tableToolbar: 'tablesaw-toolbar', sortButton: pluginName + "-btn" }, methods = { _create: function( o ){ return $( this ).each(function() { var init = $( this ).data( pluginName + "-init" ); if( init ) { return false; } $( this ) .data( pluginName + "-init", true ) .trigger( "beforecreate." + pluginName ) [ pluginName ]( "_init" , o ) .trigger( "create." + pluginName ); }); }, _init: function(){ var el = $( this ), heads, $switcher; var addClassToTable = function(){ el.addClass( pluginName ); }, addClassToHeads = function( h ){ $.each( h , function( i , v ){ $( v ).addClass( classes.head ); }); }, makeHeadsActionable = function( h , fn ){ $.each( h , function( i , v ){ var b = $( "<button class='" + classes.sortButton + "'/>" ); b.on( "click" , { col: v } , fn ); $( v ).wrapInner( b ); b.append( "<span class='tablesaw-sortable-arrow'>" ); }); }, clearOthers = function( sibs ){ $.each( sibs , function( i , v ){ var col = $( v ); col.removeAttr( attrs.defaultCol ); col.removeClass( classes.ascend ); col.removeClass( classes.descend ); }); }, headsOnAction = function( e ){ if( $( e.target ).is( 'a[href]' ) ) { return; } e.stopPropagation(); var head = $( this ).parent(), v = e.data.col, newSortValue = heads.index( head[0] ); clearOthers( head.siblings() ); if( head.is( "." + classes.descend ) || !head.is( "." + classes.ascend ) ){ el[ pluginName ]( "sortBy" , v , true); newSortValue += '_asc'; } else { el[ pluginName ]( "sortBy" , v ); newSortValue += '_desc'; } if( $switcher ) { $switcher.find( 'select' ).val( newSortValue ).trigger( 'refresh' ); } e.preventDefault(); }, handleDefault = function( heads ){ $.each( heads , function( idx , el ){ var $el = $( el ); if( $el.is( "[" + attrs.defaultCol + "]" ) ){ if( !$el.is( "." + classes.descend ) ) { $el.addClass( classes.ascend ); } } }); }, addSwitcher = function( heads ){ $switcher = $( '<div>' ).addClass( classes.switcher ).addClass( classes.tableToolbar ); var html = [ '<label>' + Tablesaw.i18n.sort + ':' ]; html.push( '<span class="btn"><select>' ); heads.each(function( j ) { var $t = $( this ); var isDefaultCol = $t.is( "[" + attrs.defaultCol + "]" ); var isDescending = $t.is( "." + classes.descend ); var hasNumericAttribute = $t.is( '[' + attrs.numericCol + ']' ); var numericCount = 0; // Check only the first four rows to see if the column is numbers. var numericCountMax = 5; $( this.cells.slice( 0, numericCountMax ) ).each(function() { if( !isNaN( parseInt( getSortValue( this ), 10 ) ) ) { numericCount++; } }); var isNumeric = numericCount === numericCountMax; if( !hasNumericAttribute ) { $t.attr( attrs.numericCol, isNumeric ? "" : "false" ); } html.push( '<option' + ( isDefaultCol && !isDescending ? ' selected' : '' ) + ' value="' + j + '_asc">' + $t.text() + ' ' + ( isNumeric ? '↑' : '(A-Z)' ) + '</option>' ); html.push( '<option' + ( isDefaultCol && isDescending ? ' selected' : '' ) + ' value="' + j + '_desc">' + $t.text() + ' ' + ( isNumeric ? '↓' : '(Z-A)' ) + '</option>' ); }); html.push( '</select></span></label>' ); $switcher.html( html.join('') ); var $toolbar = el.prev().filter( '.tablesaw-bar' ), $firstChild = $toolbar.children().eq( 0 ); if( $firstChild.length ) { $switcher.insertBefore( $firstChild ); } else { $switcher.appendTo( $toolbar ); } $switcher.find( '.btn' ).tablesawbtn(); $switcher.find( 'select' ).on( 'change', function() { var val = $( this ).val().split( '_' ), head = heads.eq( val[ 0 ] ); clearOthers( head.siblings() ); el[ pluginName ]( 'sortBy', head.get( 0 ), val[ 1 ] === 'asc' ); }); }; addClassToTable(); heads = el.find( "thead th[data-" + pluginName + "-col]" ); addClassToHeads( heads ); makeHeadsActionable( heads , headsOnAction ); handleDefault( heads ); if( el.is( sortableSwitchSelector ) ) { addSwitcher( heads, el.find('tbody tr:nth-child(-n+3)') ); } }, getColumnNumber: function( col ){ return $( col ).prevAll().length; }, getTableRows: function(){ return $( this ).find( "tbody tr" ); }, sortRows: function( rows , colNum , ascending, col ){ var cells, fn, sorted; var getCells = function( rows ){ var cells = []; $.each( rows , function( i , r ){ var element = $( r ).children().get( colNum ); cells.push({ element: element, cell: getSortValue( element ), rowNum: i }); }); return cells; }, getSortFxn = function( ascending, forceNumeric ){ var fn, regex = /[^\-\+\d\.]/g; if( ascending ){ fn = function( a , b ){ if( forceNumeric ) { return parseFloat( a.cell.replace( regex, '' ) ) - parseFloat( b.cell.replace( regex, '' ) ); } else { return a.cell.toLowerCase() > b.cell.toLowerCase() ? 1 : -1; } }; } else { fn = function( a , b ){ if( forceNumeric ) { return parseFloat( b.cell.replace( regex, '' ) ) - parseFloat( a.cell.replace( regex, '' ) ); } else { return a.cell.toLowerCase() < b.cell.toLowerCase() ? 1 : -1; } }; } return fn; }, applyToRows = function( sorted , rows ){ var newRows = [], i, l, cur; for( i = 0, l = sorted.length ; i < l ; i++ ){ cur = sorted[ i ].rowNum; newRows.push( rows[cur] ); } return newRows; }; cells = getCells( rows ); var customFn = $( col ).data( 'tablesaw-sort' ); fn = ( customFn && typeof customFn === "function" ? customFn( ascending ) : false ) || getSortFxn( ascending, $( col ).is( '[' + attrs.numericCol + ']' ) && !$( col ).is( '[' + attrs.numericCol + '="false"]' ) ); sorted = cells.sort( fn ); rows = applyToRows( sorted , rows ); return rows; }, replaceTableRows: function( rows ){ var el = $( this ), body = el.find( "tbody" ); for( var j = 0, k = rows.length; j < k; j++ ) { body.append( rows[ j ] ); } }, makeColDefault: function( col , a ){ var c = $( col ); c.attr( attrs.defaultCol , "true" ); if( a ){ c.removeClass( classes.descend ); c.addClass( classes.ascend ); } else { c.removeClass( classes.ascend ); c.addClass( classes.descend ); } }, sortBy: function( col , ascending ){ var el = $( this ), colNum, rows; colNum = el[ pluginName ]( "getColumnNumber" , col ); rows = el[ pluginName ]( "getTableRows" ); rows = el[ pluginName ]( "sortRows" , rows , colNum , ascending, col ); el[ pluginName ]( "replaceTableRows" , rows ); el[ pluginName ]( "makeColDefault" , col , ascending ); el.trigger( "tablesaw-sorted" ); } }; // Collection method. $.fn[ pluginName ] = function( arrg ) { var args = Array.prototype.slice.call( arguments , 1), returnVal; // if it's a method if( arrg && typeof( arrg ) === "string" ){ returnVal = $.fn[ pluginName ].prototype[ arrg ].apply( this[0], args ); return (typeof returnVal !== "undefined")? returnVal:$(this); } // check init if( !$( this ).data( pluginName + "-active" ) ){ $( this ).data( pluginName + "-active", true ); $.fn[ pluginName ].prototype._create.call( this , arrg ); } return $(this); }; // add methods $.extend( $.fn[ pluginName ].prototype, methods ); $( document ).on( Tablesaw.events.create, function( e, Tablesaw ) { if( Tablesaw.$table.is( initSelector ) ) { Tablesaw.$table[ pluginName ](); } }); }()); (function(){ var classes = { // TODO duplicate class, also in tables.js toolbar: "tablesaw-bar", hideBtn: "disabled", persistWidths: "tablesaw-fix-persist", allColumnsVisible: 'tablesaw-all-cols-visible' }; var attrs = { disableTouchEvents: "data-tablesaw-no-touch" }; function createSwipeTable( tbl, $table ){ var $btns = $( "<div class='tablesaw-advance'></div>" ); var $prevBtn = $( "<a href='#' class='tablesaw-nav-btn btn btn-micro left' title='Previous Column'></a>" ).appendTo( $btns ); var $nextBtn = $( "<a href='#' class='tablesaw-nav-btn btn btn-micro right' title='Next Column'></a>" ).appendTo( $btns ); var $headerCells = tbl._getPrimaryHeaderCells(); var $headerCellsNoPersist = $headerCells.not( '[data-tablesaw-priority="persist"]' ); var headerWidths = []; var $head = $( document.head || 'head' ); var tableId = $table.attr( 'id' ); if( !$headerCells.length ) { throw new Error( "tablesaw swipe: no header cells found. Are you using <th> inside of <thead>?" ); } $table.addClass( "tablesaw-swipe" ); // Calculate initial widths $headerCells.each(function() { var width = this.offsetWidth; headerWidths.push( width ); }); $btns.appendTo( $table.prev().filter( '.tablesaw-bar' ) ); if( !tableId ) { tableId = 'tableswipe-' + Math.round( Math.random() * 10000 ); $table.attr( 'id', tableId ); } function $getCells( headerCell ) { return $( headerCell.cells ).add( headerCell ); } function showColumn( headerCell ) { $getCells( headerCell ).removeClass( 'tablesaw-cell-hidden' ); } function hideColumn( headerCell ) { $getCells( headerCell ).addClass( 'tablesaw-cell-hidden' ); } function persistColumn( headerCell ) { $getCells( headerCell ).addClass( 'tablesaw-cell-persist' ); } function isPersistent( headerCell ) { return $( headerCell ).is( '[data-tablesaw-priority="persist"]' ); } function unmaintainWidths() { $table.removeClass( classes.persistWidths ); $( '#' + tableId + '-persist' ).remove(); } function maintainWidths() { var prefix = '#' + tableId + '.tablesaw-swipe ', styles = [], tableWidth = $table.width(), hash = [], newHash; // save persistent column widths (as long as they take up less than 75% of table width) $headerCells.each(function( index ) { var width; if( isPersistent( this ) ) { width = this.offsetWidth; if( width < tableWidth * 0.75 ) { hash.push( index + '-' + width ); styles.push( prefix + ' .tablesaw-cell-persist:nth-child(' + ( index + 1 ) + ') { width: ' + width + 'px; }' ); } } }); newHash = hash.join( '_' ); if( styles.length ) { $table.addClass( classes.persistWidths ); var $style = $( '#' + tableId + '-persist' ); // If style element not yet added OR if the widths have changed if( !$style.length || $style.data( 'tablesaw-hash' ) !== newHash ) { // Remove existing $style.remove(); $( '<style>' + styles.join( "\n" ) + '</style>' ) .attr( 'id', tableId + '-persist' ) .data( 'tablesaw-hash', newHash ) .appendTo( $head ); } } } function getNext(){ var next = [], checkFound; $headerCellsNoPersist.each(function( i ) { var $t = $( this ), isHidden = $t.css( "display" ) === "none" || $t.is( ".tablesaw-cell-hidden" ); if( !isHidden && !checkFound ) { checkFound = true; next[ 0 ] = i; } else if( isHidden && checkFound ) { next[ 1 ] = i; return false; } }); return next; } function getPrev(){ var next = getNext(); return [ next[ 1 ] - 1 , next[ 0 ] - 1 ]; } function nextpair( fwd ){ return fwd ? getNext() : getPrev(); } function canAdvance( pair ){ return pair[ 1 ] > -1 && pair[ 1 ] < $headerCellsNoPersist.length; } function matchesMedia() { var matchMedia = $table.attr( "data-tablesaw-swipe-media" ); return !matchMedia || ( "matchMedia" in win ) && win.matchMedia( matchMedia ).matches; } function fakeBreakpoints() { if( !matchesMedia() ) { return; } var containerWidth = $table.parent().width(), persist = [], sum = 0, sums = [], visibleNonPersistantCount = $headerCells.length; $headerCells.each(function( index ) { var $t = $( this ), isPersist = $t.is( '[data-tablesaw-priority="persist"]' ); persist.push( isPersist ); sum += headerWidths[ index ]; sums.push( sum ); // is persistent or is hidden if( isPersist || sum > containerWidth ) { visibleNonPersistantCount--; } }); // We need at least one column to swipe. var needsNonPersistentColumn = visibleNonPersistantCount === 0; $headerCells.each(function( index ) { if( persist[ index ] ) { // for visual box-shadow persistColumn( this ); return; } if( sums[ index ] <= containerWidth || needsNonPersistentColumn ) { needsNonPersistentColumn = false; showColumn( this ); } else { hideColumn( this ); } }); unmaintainWidths(); $table.trigger( 'tablesawcolumns' ); } function advance( fwd ){ var pair = nextpair( fwd ); if( canAdvance( pair ) ){ if( isNaN( pair[ 0 ] ) ){ if( fwd ){ pair[0] = 0; } else { pair[0] = $headerCellsNoPersist.length - 1; } } maintainWidths(); hideColumn( $headerCellsNoPersist.get( pair[ 0 ] ) ); showColumn( $headerCellsNoPersist.get( pair[ 1 ] ) ); $table.trigger( 'tablesawcolumns' ); } } $prevBtn.add( $nextBtn ).on( "click", function( e ){ advance( !!$( e.target ).closest( $nextBtn ).length ); e.preventDefault(); }); function getCoord( event, key ) { return ( event.touches || event.originalEvent.touches )[ 0 ][ key ]; } if( !$table.is( "[" + attrs.disableTouchEvents + "]" ) ) { $table .on( "touchstart.swipetoggle", function( e ){ var originX = getCoord( e, 'pageX' ); var originY = getCoord( e, 'pageY' ); var x; var y; var scrollTop = window.pageYOffset; $( win ).off( Tablesaw.events.resize, fakeBreakpoints ); $( this ) .on( "touchmove.swipetoggle", function( e ){ x = getCoord( e, 'pageX' ); y = getCoord( e, 'pageY' ); }) .on( "touchend.swipetoggle", function() { var cfg = tbl.getConfig({ swipeHorizontalThreshold: 30, swipeVerticalThreshold: 30 }); // This config code is a little awkward because shoestring doesn’t support deep $.extend // Trying to work around when devs only override one of (not both) horizontalThreshold or // verticalThreshold in their TablesawConfig. var verticalThreshold = cfg.swipe ? cfg.swipe.verticalThreshold : cfg.swipeVerticalThreshold; var horizontalThreshold = cfg.swipe ? cfg.swipe.horizontalThreshold : cfg.swipeHorizontalThreshold; var isPageScrolled = Math.abs( window.pageYOffset - scrollTop ) >= verticalThreshold; var isVerticalSwipe = Math.abs( y - originY ) >= verticalThreshold; if( !isVerticalSwipe && !isPageScrolled ) { if( x - originX < -1 * horizontalThreshold ){ advance( true ); } if( x - originX > horizontalThreshold ){ advance( false ); } } window.setTimeout(function() { $( win ).on( Tablesaw.events.resize, fakeBreakpoints ); }, 300); $( this ).off( "touchmove.swipetoggle touchend.swipetoggle" ); }); }); } $table .on( "tablesawcolumns.swipetoggle", function(){ var canGoPrev = canAdvance( getPrev() ); var canGoNext = canAdvance( getNext() ); $prevBtn[ canGoPrev ? "removeClass" : "addClass" ]( classes.hideBtn ); $nextBtn[ canGoNext ? "removeClass" : "addClass" ]( classes.hideBtn ); $prevBtn.closest( "." + classes.toolbar )[ !canGoPrev && !canGoNext ? 'addClass' : 'removeClass' ]( classes.allColumnsVisible ); }) .on( "tablesawnext.swipetoggle", function(){ advance( true ); } ) .on( "tablesawprev.swipetoggle", function(){ advance( false ); } ) .on( Tablesaw.events.destroy + ".swipetoggle", function(){ var $t = $( this ); $t.removeClass( 'tablesaw-swipe' ); $t.prev().filter( '.tablesaw-bar' ).find( '.tablesaw-advance' ).remove(); $( win ).off( Tablesaw.events.resize, fakeBreakpoints ); $t.off( ".swipetoggle" ); }) .on( Tablesaw.events.refresh, function() { // manual refresh headerWidths = []; $headerCells.each(function() { var width = this.offsetWidth; headerWidths.push( width ); }); fakeBreakpoints(); }); fakeBreakpoints(); $( win ).on( Tablesaw.events.resize, fakeBreakpoints ); } // on tablecreate, init $( document ).on( Tablesaw.events.create, function( e, tablesaw ){ if( tablesaw.mode === 'swipe' ){ createSwipeTable( tablesaw, tablesaw.$table ); } } ); }()); ;(function(){ var MiniMap = { attr: { init: 'data-tablesaw-minimap' } }; function createMiniMap( $table ){ var $btns = $( '<div class="tablesaw-advance minimap">' ), $dotNav = $( '<ul class="tablesaw-advance-dots">' ).appendTo( $btns ), hideDot = 'tablesaw-advance-dots-hide', $headerCells = $table.find( 'thead th' ); // populate dots $headerCells.each(function(){ $dotNav.append( '<li><i></i></li>' ); }); $btns.appendTo( $table.prev().filter( '.tablesaw-bar' ) ); function showMinimap( $table ) { var mq = $table.attr( MiniMap.attr.init ); return !mq || win.matchMedia && win.matchMedia( mq ).matches; } function showHideNav(){ if( !showMinimap( $table ) ) { $btns.css( "display", "none" ); return; } $btns.css( "display", "block" ); // show/hide dots var dots = $dotNav.find( "li" ).removeClass( hideDot ); $table.find( "thead th" ).each(function(i){ if( $( this ).css( "display" ) === "none" ){ dots.eq( i ).addClass( hideDot ); } }); } // run on init and resize showHideNav(); $( win ).on( Tablesaw.events.resize, showHideNav ); $table .on( "tablesawcolumns.minimap", function(){ showHideNav(); }) .on( Tablesaw.events.destroy + ".minimap", function(){ var $t = $( this ); $t.prev().filter( '.tablesaw-bar' ).find( '.tablesaw-advance' ).remove(); $( win ).off( Tablesaw.events.resize, showHideNav ); $t.off( ".minimap" ); }); } // on tablecreate, init $( document ).on( Tablesaw.events.create, function( e, tablesaw ){ if( ( tablesaw.mode === 'swipe' || tablesaw.mode === 'columntoggle' ) && tablesaw.$table.is( '[ ' + MiniMap.attr.init + ']' ) ){ createMiniMap( tablesaw.$table ); } } ); }()); ;(function() { var S = { selectors: { init: 'table[data-tablesaw-mode-switch]' }, attributes: { excludeMode: 'data-tablesaw-mode-exclude' }, classes: { main: 'tablesaw-modeswitch', toolbar: 'tablesaw-toolbar' }, modes: [ 'stack', 'swipe', 'columntoggle' ], init: function( table ) { var $table = $( table ), ignoreMode = $table.attr( S.attributes.excludeMode ), $toolbar = $table.prev().filter( '.tablesaw-bar' ), modeVal = '', $switcher = $( '<div>' ).addClass( S.classes.main + ' ' + S.classes.toolbar ); var html = [ '<label>' + Tablesaw.i18n.columns + ':' ], dataMode = $table.attr( 'data-tablesaw-mode' ), isSelected; html.push( '<span class="btn"><select>' ); for( var j=0, k = S.modes.length; j<k; j++ ) { if( ignoreMode && ignoreMode.toLowerCase() === S.modes[ j ] ) { continue; } isSelected = dataMode === S.modes[ j ]; if( isSelected ) { modeVal = S.modes[ j ]; } html.push( '<option' + ( isSelected ? ' selected' : '' ) + ' value="' + S.modes[ j ] + '">' + Tablesaw.i18n.modes[ j ] + '</option>' ); } html.push( '</select></span></label>' ); $switcher.html( html.join( '' ) ); var $otherToolbarItems = $toolbar.find( '.tablesaw-advance' ).eq( 0 ); if( $otherToolbarItems.length ) { $switcher.insertBefore( $otherToolbarItems ); } else { $switcher.appendTo( $toolbar ); } $switcher.find( '.btn' ).tablesawbtn(); $switcher.find( 'select' ).on( 'change', S.onModeChange ); }, onModeChange: function() { var $t = $( this ), $switcher = $t.closest( '.' + S.classes.main ), $table = $t.closest( '.tablesaw-bar' ).next().eq( 0 ), val = $t.val(); $switcher.remove(); $table.data( 'tablesaw' ).destroy(); $table.attr( 'data-tablesaw-mode', val ); $table.tablesaw(); } }; $( win.document ).on( Tablesaw.events.create, function( e, Tablesaw ) { if( Tablesaw.$table.is( S.selectors.init ) ) { S.init( Tablesaw.table ); } }); })(); })); assets/js/tablesaw/dependencies/qunit.js 0000644 00000373126 14736103451 0014430 0 ustar 00 /*! * QUnit 2.1.1 * https://qunitjs.com/ * * Copyright jQuery Foundation and other contributors * Released under the MIT license * https://jquery.org/license * * Date: 2017-01-06T01:52Z */ (function (global$1) { 'use strict'; global$1 = 'default' in global$1 ? global$1['default'] : global$1; var window = global$1.window; var console = global$1.console; var setTimeout = global$1.setTimeout; var clearTimeout = global$1.clearTimeout; var document = window && window.document; var navigator = window && window.navigator; var sessionStorage = window && window.sessionStorage; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; var classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; var createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var toConsumableArray = function (arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; return arr2; } else { return Array.from(arr); } }; var toString = Object.prototype.toString; var hasOwn = Object.prototype.hasOwnProperty; var now = Date.now || function () { return new Date().getTime(); }; var defined = { document: window && window.document !== undefined, setTimeout: setTimeout !== undefined }; // Returns a new Array with the elements that are in a but not in b function diff(a, b) { var i, j, result = a.slice(); for (i = 0; i < result.length; i++) { for (j = 0; j < b.length; j++) { if (result[i] === b[j]) { result.splice(i, 1); i--; break; } } } return result; } // From jquery.js function inArray(elem, array) { if (array.indexOf) { return array.indexOf(elem); } for (var i = 0, length = array.length; i < length; i++) { if (array[i] === elem) { return i; } } return -1; } /** * Makes a clone of an object using only Array or Object as base, * and copies over the own enumerable properties. * * @param {Object} obj * @return {Object} New object with only the own properties (recursively). */ function objectValues(obj) { var key, val, vals = is("array", obj) ? [] : {}; for (key in obj) { if (hasOwn.call(obj, key)) { val = obj[key]; vals[key] = val === Object(val) ? objectValues(val) : val; } } return vals; } function extend(a, b, undefOnly) { for (var prop in b) { if (hasOwn.call(b, prop)) { if (b[prop] === undefined) { delete a[prop]; } else if (!(undefOnly && typeof a[prop] !== "undefined")) { a[prop] = b[prop]; } } } return a; } function objectType(obj) { if (typeof obj === "undefined") { return "undefined"; } // Consider: typeof null === object if (obj === null) { return "null"; } var match = toString.call(obj).match(/^\[object\s(.*)\]$/), type = match && match[1]; switch (type) { case "Number": if (isNaN(obj)) { return "nan"; } return "number"; case "String": case "Boolean": case "Array": case "Set": case "Map": case "Date": case "RegExp": case "Function": case "Symbol": return type.toLowerCase(); } if ((typeof obj === "undefined" ? "undefined" : _typeof(obj)) === "object") { return "object"; } } // Safe object type checking function is(type, obj) { return objectType(obj) === type; } // Test for equality any JavaScript type. // Author: Philippe Rathé <prathe@gmail.com> var equiv = (function () { // Stack to decide between skip/abort functions var callers = []; // Stack to avoiding loops from circular referencing var parents = []; var parentsB = []; var getProto = Object.getPrototypeOf || function (obj) { return obj.__proto__; }; function useStrictEquality(b, a) { // To catch short annotation VS 'new' annotation of a declaration. e.g.: // `var i = 1;` // `var j = new Number(1);` if ((typeof a === "undefined" ? "undefined" : _typeof(a)) === "object") { a = a.valueOf(); } if ((typeof b === "undefined" ? "undefined" : _typeof(b)) === "object") { b = b.valueOf(); } return a === b; } function compareConstructors(a, b) { var protoA = getProto(a); var protoB = getProto(b); // Comparing constructors is more strict than using `instanceof` if (a.constructor === b.constructor) { return true; } // Ref #851 // If the obj prototype descends from a null constructor, treat it // as a null prototype. if (protoA && protoA.constructor === null) { protoA = null; } if (protoB && protoB.constructor === null) { protoB = null; } // Allow objects with no prototype to be equivalent to // objects with Object as their constructor. if (protoA === null && protoB === Object.prototype || protoB === null && protoA === Object.prototype) { return true; } return false; } function getRegExpFlags(regexp) { return "flags" in regexp ? regexp.flags : regexp.toString().match(/[gimuy]*$/)[0]; } var callbacks = { "string": useStrictEquality, "boolean": useStrictEquality, "number": useStrictEquality, "null": useStrictEquality, "undefined": useStrictEquality, "symbol": useStrictEquality, "date": useStrictEquality, "nan": function nan() { return true; }, "regexp": function regexp(b, a) { return a.source === b.source && // Include flags in the comparison getRegExpFlags(a) === getRegExpFlags(b); }, // - skip when the property is a method of an instance (OOP) // - abort otherwise, // initial === would have catch identical references anyway "function": function _function(b, a) { var caller = callers[callers.length - 1]; return caller !== Object && typeof caller !== "undefined" && a.toString() === b.toString(); }, "array": function array(b, a) { var i, j, len, loop, aCircular, bCircular; len = a.length; if (len !== b.length) { // Safe and faster return false; } // Track reference to avoid circular references parents.push(a); parentsB.push(b); for (i = 0; i < len; i++) { loop = false; for (j = 0; j < parents.length; j++) { aCircular = parents[j] === a[i]; bCircular = parentsB[j] === b[i]; if (aCircular || bCircular) { if (a[i] === b[i] || aCircular && bCircular) { loop = true; } else { parents.pop(); parentsB.pop(); return false; } } } if (!loop && !innerEquiv(a[i], b[i])) { parents.pop(); parentsB.pop(); return false; } } parents.pop(); parentsB.pop(); return true; }, "set": function set$$1(b, a) { var innerEq, outerEq = true; if (a.size !== b.size) { return false; } a.forEach(function (aVal) { innerEq = false; b.forEach(function (bVal) { if (innerEquiv(bVal, aVal)) { innerEq = true; } }); if (!innerEq) { outerEq = false; } }); return outerEq; }, "map": function map(b, a) { var innerEq, outerEq = true; if (a.size !== b.size) { return false; } a.forEach(function (aVal, aKey) { innerEq = false; b.forEach(function (bVal, bKey) { if (innerEquiv([bVal, bKey], [aVal, aKey])) { innerEq = true; } }); if (!innerEq) { outerEq = false; } }); return outerEq; }, "object": function object(b, a) { var i, j, loop, aCircular, bCircular; // Default to true var eq = true; var aProperties = []; var bProperties = []; if (compareConstructors(a, b) === false) { return false; } // Stack constructor before traversing properties callers.push(a.constructor); // Track reference to avoid circular references parents.push(a); parentsB.push(b); // Be strict: don't ensure hasOwnProperty and go deep for (i in a) { loop = false; for (j = 0; j < parents.length; j++) { aCircular = parents[j] === a[i]; bCircular = parentsB[j] === b[i]; if (aCircular || bCircular) { if (a[i] === b[i] || aCircular && bCircular) { loop = true; } else { eq = false; break; } } } aProperties.push(i); if (!loop && !innerEquiv(a[i], b[i])) { eq = false; break; } } parents.pop(); parentsB.pop(); // Unstack, we are done callers.pop(); for (i in b) { // Collect b's properties bProperties.push(i); } // Ensures identical properties name return eq && innerEquiv(aProperties.sort(), bProperties.sort()); } }; function typeEquiv(a, b) { var type = objectType(a); return objectType(b) === type && callbacks[type](b, a); } // The real equiv function function innerEquiv(a, b) { // We're done when there's nothing more to compare if (arguments.length < 2) { return true; } // Require type-specific equality return (a === b || typeEquiv(a, b)) && ( // ...across all consecutive argument pairs arguments.length === 2 || innerEquiv.apply(this, [].slice.call(arguments, 1))); } return innerEquiv; })(); /** * Config object: Maintain internal state * Later exposed as QUnit.config * `config` initialized at top of scope */ var config = { // The queue of tests to run queue: [], // Block until document ready blocking: true, // By default, run previously failed tests first // very useful in combination with "Hide passed tests" checked reorder: true, // By default, modify document.title when suite is done altertitle: true, // HTML Reporter: collapse every test except the first failing test // If false, all failing tests will be expanded collapse: true, // By default, scroll to top of the page when suite is done scrolltop: true, // Depth up-to which object will be dumped maxDepth: 5, // When enabled, all tests must call expect() requireExpects: false, // Placeholder for user-configurable form-exposed URL parameters urlConfig: [], // Set of all modules. modules: [], // Stack of nested modules moduleStack: [], // The first unnamed module currentModule: { name: "", tests: [], childModules: [], testsRun: 0 }, callbacks: {}, // The storage module to use for reordering tests storage: sessionStorage }; // take a predefined QUnit.config and extend the defaults var globalConfig = window && window.QUnit && window.QUnit.config; // only extend the global config if there is no QUnit overload if (window && window.QUnit && !window.QUnit.version) { extend(config, globalConfig); } // Push a loose unnamed module to the modules collection config.modules.push(config.currentModule); // Based on jsDump by Ariel Flesler // http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html var dump = (function () { function quote(str) { return "\"" + str.toString().replace(/\\/g, "\\\\").replace(/"/g, "\\\"") + "\""; } function literal(o) { return o + ""; } function join(pre, arr, post) { var s = dump.separator(), base = dump.indent(), inner = dump.indent(1); if (arr.join) { arr = arr.join("," + s + inner); } if (!arr) { return pre + post; } return [pre, inner + arr, base + post].join(s); } function array(arr, stack) { var i = arr.length, ret = new Array(i); if (dump.maxDepth && dump.depth > dump.maxDepth) { return "[object Array]"; } this.up(); while (i--) { ret[i] = this.parse(arr[i], undefined, stack); } this.down(); return join("[", ret, "]"); } function isArray(obj) { return ( //Native Arrays toString.call(obj) === "[object Array]" || // NodeList objects typeof obj.length === "number" && obj.item !== undefined && (obj.length ? obj.item(0) === obj[0] : obj.item(0) === null && obj[0] === undefined) ); } var reName = /^function (\w+)/, dump = { // The objType is used mostly internally, you can fix a (custom) type in advance parse: function parse(obj, objType, stack) { stack = stack || []; var res, parser, parserType, inStack = inArray(obj, stack); if (inStack !== -1) { return "recursion(" + (inStack - stack.length) + ")"; } objType = objType || this.typeOf(obj); parser = this.parsers[objType]; parserType = typeof parser === "undefined" ? "undefined" : _typeof(parser); if (parserType === "function") { stack.push(obj); res = parser.call(this, obj, stack); stack.pop(); return res; } return parserType === "string" ? parser : this.parsers.error; }, typeOf: function typeOf(obj) { var type; if (obj === null) { type = "null"; } else if (typeof obj === "undefined") { type = "undefined"; } else if (is("regexp", obj)) { type = "regexp"; } else if (is("date", obj)) { type = "date"; } else if (is("function", obj)) { type = "function"; } else if (obj.setInterval !== undefined && obj.document !== undefined && obj.nodeType === undefined) { type = "window"; } else if (obj.nodeType === 9) { type = "document"; } else if (obj.nodeType) { type = "node"; } else if (isArray(obj)) { type = "array"; } else if (obj.constructor === Error.prototype.constructor) { type = "error"; } else { type = typeof obj === "undefined" ? "undefined" : _typeof(obj); } return type; }, separator: function separator() { if (this.multiline) { return this.HTML ? "<br />" : "\n"; } else { return this.HTML ? " " : " "; } }, // Extra can be a number, shortcut for increasing-calling-decreasing indent: function indent(extra) { if (!this.multiline) { return ""; } var chr = this.indentChar; if (this.HTML) { chr = chr.replace(/\t/g, " ").replace(/ /g, " "); } return new Array(this.depth + (extra || 0)).join(chr); }, up: function up(a) { this.depth += a || 1; }, down: function down(a) { this.depth -= a || 1; }, setParser: function setParser(name, parser) { this.parsers[name] = parser; }, // The next 3 are exposed so you can use them quote: quote, literal: literal, join: join, depth: 1, maxDepth: config.maxDepth, // This is the list of parsers, to modify them, use dump.setParser parsers: { window: "[Window]", document: "[Document]", error: function error(_error) { return "Error(\"" + _error.message + "\")"; }, unknown: "[Unknown]", "null": "null", "undefined": "undefined", "function": function _function(fn) { var ret = "function", // Functions never have name in IE name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1]; if (name) { ret += " " + name; } ret += "("; ret = [ret, dump.parse(fn, "functionArgs"), "){"].join(""); return join(ret, dump.parse(fn, "functionCode"), "}"); }, array: array, nodelist: array, "arguments": array, object: function object(map, stack) { var keys, key, val, i, nonEnumerableProperties, ret = []; if (dump.maxDepth && dump.depth > dump.maxDepth) { return "[object Object]"; } dump.up(); keys = []; for (key in map) { keys.push(key); } // Some properties are not always enumerable on Error objects. nonEnumerableProperties = ["message", "name"]; for (i in nonEnumerableProperties) { key = nonEnumerableProperties[i]; if (key in map && inArray(key, keys) < 0) { keys.push(key); } } keys.sort(); for (i = 0; i < keys.length; i++) { key = keys[i]; val = map[key]; ret.push(dump.parse(key, "key") + ": " + dump.parse(val, undefined, stack)); } dump.down(); return join("{", ret, "}"); }, node: function node(_node) { var len, i, val, open = dump.HTML ? "<" : "<", close = dump.HTML ? ">" : ">", tag = _node.nodeName.toLowerCase(), ret = open + tag, attrs = _node.attributes; if (attrs) { for (i = 0, len = attrs.length; i < len; i++) { val = attrs[i].nodeValue; // IE6 includes all attributes in .attributes, even ones not explicitly // set. Those have values like undefined, null, 0, false, "" or // "inherit". if (val && val !== "inherit") { ret += " " + attrs[i].nodeName + "=" + dump.parse(val, "attribute"); } } } ret += close; // Show content of TextNode or CDATASection if (_node.nodeType === 3 || _node.nodeType === 4) { ret += _node.nodeValue; } return ret + open + "/" + tag + close; }, // Function calls it internally, it's the arguments part of the function functionArgs: function functionArgs(fn) { var args, l = fn.length; if (!l) { return ""; } args = new Array(l); while (l--) { // 97 is 'a' args[l] = String.fromCharCode(97 + l); } return " " + args.join(", ") + " "; }, // Object calls it internally, the key part of an item in a map key: quote, // Function calls it internally, it's the content of the function functionCode: "[code]", // Node calls it internally, it's a html attribute value attribute: quote, string: quote, date: quote, regexp: literal, number: literal, "boolean": literal, symbol: function symbol(sym) { return sym.toString(); } }, // If true, entities are escaped ( <, >, \t, space and \n ) HTML: false, // Indentation unit indentChar: " ", // If true, items in a collection, are separated by a \n, else just a space. multiline: true }; return dump; })(); // Register logging callbacks function registerLoggingCallbacks(obj) { var i, l, key, callbackNames = ["begin", "done", "log", "testStart", "testDone", "moduleStart", "moduleDone"]; function registerLoggingCallback(key) { var loggingCallback = function loggingCallback(callback) { if (objectType(callback) !== "function") { throw new Error("QUnit logging methods require a callback function as their first parameters."); } config.callbacks[key].push(callback); }; return loggingCallback; } for (i = 0, l = callbackNames.length; i < l; i++) { key = callbackNames[i]; // Initialize key collection of logging callback if (objectType(config.callbacks[key]) === "undefined") { config.callbacks[key] = []; } obj[key] = registerLoggingCallback(key); } } function runLoggingCallbacks(key, args) { var i, l, callbacks; callbacks = config.callbacks[key]; for (i = 0, l = callbacks.length; i < l; i++) { callbacks[i](args); } } // Doesn't support IE9, it will return undefined on these browsers // See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack var fileName = (sourceFromStacktrace(0) || "").replace(/(:\d+)+\)?/, "").replace(/.+\//, ""); function extractStacktrace(e, offset) { offset = offset === undefined ? 4 : offset; var stack, include, i; if (e && e.stack) { stack = e.stack.split("\n"); if (/^error$/i.test(stack[0])) { stack.shift(); } if (fileName) { include = []; for (i = offset; i < stack.length; i++) { if (stack[i].indexOf(fileName) !== -1) { break; } include.push(stack[i]); } if (include.length) { return include.join("\n"); } } return stack[offset]; } } function sourceFromStacktrace(offset) { var error = new Error(); // Support: Safari <=7 only, IE <=10 - 11 only // Not all browsers generate the `stack` property for `new Error()`, see also #636 if (!error.stack) { try { throw error; } catch (err) { error = err; } } return extractStacktrace(error, offset); } var unitSampler; var focused = false; var priorityCount = 0; function Test(settings) { var i, l; ++Test.count; this.expected = null; extend(this, settings); this.assertions = []; this.semaphore = 0; this.usedAsync = false; this.module = config.currentModule; this.stack = sourceFromStacktrace(3); // Register unique strings for (i = 0, l = this.module.tests; i < l.length; i++) { if (this.module.tests[i].name === this.testName) { this.testName += " "; } } this.testId = generateHash(this.module.name, this.testName); this.module.tests.push({ name: this.testName, testId: this.testId }); if (settings.skip) { // Skipped tests will fully ignore any sent callback this.callback = function () {}; this.async = false; this.expected = 0; } else { this.assert = new Assert(this); } } Test.count = 0; function getNotStartedModules(startModule) { var module = startModule, modules = []; while (module && module.testsRun === 0) { modules.push(module); module = module.parentModule; } return modules; } Test.prototype = { before: function before() { var i, startModule, module = this.module, notStartedModules = getNotStartedModules(module); for (i = notStartedModules.length - 1; i >= 0; i--) { startModule = notStartedModules[i]; startModule.stats = { all: 0, bad: 0, started: now() }; runLoggingCallbacks("moduleStart", { name: startModule.name, tests: startModule.tests }); } config.current = this; if (module.testEnvironment) { delete module.testEnvironment.before; delete module.testEnvironment.beforeEach; delete module.testEnvironment.afterEach; delete module.testEnvironment.after; } this.testEnvironment = extend({}, module.testEnvironment); this.started = now(); runLoggingCallbacks("testStart", { name: this.testName, module: module.name, testId: this.testId, previousFailure: this.previousFailure }); if (!config.pollution) { saveGlobal(); } }, run: function run() { var promise; config.current = this; this.callbackStarted = now(); if (config.notrycatch) { runTest(this); return; } try { runTest(this); } catch (e) { this.pushFailure("Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + (e.message || e), extractStacktrace(e, 0)); // Else next test will carry the responsibility saveGlobal(); // Restart the tests if they're blocking if (config.blocking) { internalRecover(this); } } function runTest(test) { promise = test.callback.call(test.testEnvironment, test.assert); test.resolvePromise(promise); } }, after: function after() { checkPollution(); }, queueHook: function queueHook(hook, hookName, hookOwner) { var promise, test = this; return function runHook() { if (hookName === "before") { if (hookOwner.testsRun !== 0) { return; } test.preserveEnvironment = true; } if (hookName === "after" && hookOwner.testsRun !== numberOfTests(hookOwner) - 1) { return; } config.current = test; if (config.notrycatch) { callHook(); return; } try { callHook(); } catch (error) { test.pushFailure(hookName + " failed on " + test.testName + ": " + (error.message || error), extractStacktrace(error, 0)); } function callHook() { promise = hook.call(test.testEnvironment, test.assert); test.resolvePromise(promise, hookName); } }; }, // Currently only used for module level hooks, can be used to add global level ones hooks: function hooks(handler) { var hooks = []; function processHooks(test, module) { if (module.parentModule) { processHooks(test, module.parentModule); } if (module.testEnvironment && objectType(module.testEnvironment[handler]) === "function") { hooks.push(test.queueHook(module.testEnvironment[handler], handler, module)); } } // Hooks are ignored on skipped tests if (!this.skip) { processHooks(this, this.module); } return hooks; }, finish: function finish() { config.current = this; if (config.requireExpects && this.expected === null) { this.pushFailure("Expected number of assertions to be defined, but expect() was " + "not called.", this.stack); } else if (this.expected !== null && this.expected !== this.assertions.length) { this.pushFailure("Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack); } else if (this.expected === null && !this.assertions.length) { this.pushFailure("Expected at least one assertion, but none were run - call " + "expect(0) to accept zero assertions.", this.stack); } var i, module = this.module, moduleName = module.name, testName = this.testName, skipped = !!this.skip, bad = 0, storage = config.storage; this.runtime = now() - this.started; config.stats.all += this.assertions.length; module.stats.all += this.assertions.length; for (i = 0; i < this.assertions.length; i++) { if (!this.assertions[i].result) { bad++; config.stats.bad++; module.stats.bad++; } } notifyTestsRan(module); // Store result when possible if (storage) { if (bad) { storage.setItem("qunit-test-" + moduleName + "-" + testName, bad); } else { storage.removeItem("qunit-test-" + moduleName + "-" + testName); } } runLoggingCallbacks("testDone", { name: testName, module: moduleName, skipped: skipped, failed: bad, passed: this.assertions.length - bad, total: this.assertions.length, runtime: skipped ? 0 : this.runtime, // HTML Reporter use assertions: this.assertions, testId: this.testId, // Source of Test source: this.stack }); if (module.testsRun === numberOfTests(module)) { runLoggingCallbacks("moduleDone", { name: module.name, tests: module.tests, failed: module.stats.bad, passed: module.stats.all - module.stats.bad, total: module.stats.all, runtime: now() - module.stats.started }); } config.current = undefined; }, preserveTestEnvironment: function preserveTestEnvironment() { if (this.preserveEnvironment) { this.module.testEnvironment = this.testEnvironment; this.testEnvironment = extend({}, this.module.testEnvironment); } }, queue: function queue() { var priority, previousFailCount, test = this; if (!this.valid()) { return; } function run() { // Each of these can by async synchronize([function () { test.before(); }, test.hooks("before"), function () { test.preserveTestEnvironment(); }, test.hooks("beforeEach"), function () { test.run(); }, test.hooks("afterEach").reverse(), test.hooks("after").reverse(), function () { test.after(); }, function () { test.finish(); }]); } previousFailCount = config.storage && +config.storage.getItem("qunit-test-" + this.module.name + "-" + this.testName); // Prioritize previously failed tests, detected from storage priority = config.reorder && previousFailCount; this.previousFailure = !!previousFailCount; return synchronize(run, priority, config.seed); }, pushResult: function pushResult(resultInfo) { // Destructure of resultInfo = { result, actual, expected, message, negative } var source, details = { module: this.module.name, name: this.testName, result: resultInfo.result, message: resultInfo.message, actual: resultInfo.actual, expected: resultInfo.expected, testId: this.testId, negative: resultInfo.negative || false, runtime: now() - this.started }; if (!resultInfo.result) { source = sourceFromStacktrace(); if (source) { details.source = source; } } runLoggingCallbacks("log", details); this.assertions.push({ result: !!resultInfo.result, message: resultInfo.message }); }, pushFailure: function pushFailure(message, source, actual) { if (!(this instanceof Test)) { throw new Error("pushFailure() assertion outside test context, was " + sourceFromStacktrace(2)); } var details = { module: this.module.name, name: this.testName, result: false, message: message || "error", actual: actual || null, testId: this.testId, runtime: now() - this.started }; if (source) { details.source = source; } runLoggingCallbacks("log", details); this.assertions.push({ result: false, message: message }); }, resolvePromise: function resolvePromise(promise, phase) { var then, resume, message, test = this; if (promise != null) { then = promise.then; if (objectType(then) === "function") { resume = internalStop(test); then.call(promise, function () { resume(); }, function (error) { message = "Promise rejected " + (!phase ? "during" : phase.replace(/Each$/, "")) + " \"" + test.testName + "\": " + (error && error.message || error); test.pushFailure(message, extractStacktrace(error, 0)); // Else next test will carry the responsibility saveGlobal(); // Unblock resume(); }); } } }, valid: function valid() { var filter = config.filter, regexFilter = /^(!?)\/([\w\W]*)\/(i?$)/.exec(filter), module = config.module && config.module.toLowerCase(), fullName = this.module.name + ": " + this.testName; function moduleChainNameMatch(testModule) { var testModuleName = testModule.name ? testModule.name.toLowerCase() : null; if (testModuleName === module) { return true; } else if (testModule.parentModule) { return moduleChainNameMatch(testModule.parentModule); } else { return false; } } function moduleChainIdMatch(testModule) { return inArray(testModule.moduleId, config.moduleId) > -1 || testModule.parentModule && moduleChainIdMatch(testModule.parentModule); } // Internally-generated tests are always valid if (this.callback && this.callback.validTest) { return true; } if (config.moduleId && config.moduleId.length > 0 && !moduleChainIdMatch(this.module)) { return false; } if (config.testId && config.testId.length > 0 && inArray(this.testId, config.testId) < 0) { return false; } if (module && !moduleChainNameMatch(this.module)) { return false; } if (!filter) { return true; } return regexFilter ? this.regexFilter(!!regexFilter[1], regexFilter[2], regexFilter[3], fullName) : this.stringFilter(filter, fullName); }, regexFilter: function regexFilter(exclude, pattern, flags, fullName) { var regex = new RegExp(pattern, flags); var match = regex.test(fullName); return match !== exclude; }, stringFilter: function stringFilter(filter, fullName) { filter = filter.toLowerCase(); fullName = fullName.toLowerCase(); var include = filter.charAt(0) !== "!"; if (!include) { filter = filter.slice(1); } // If the filter matches, we need to honour include if (fullName.indexOf(filter) !== -1) { return include; } // Otherwise, do the opposite return !include; } }; function pushFailure() { if (!config.current) { throw new Error("pushFailure() assertion outside test context, in " + sourceFromStacktrace(2)); } // Gets current test obj var currentTest = config.current; return currentTest.pushFailure.apply(currentTest, arguments); } // Based on Java's String.hashCode, a simple but not // rigorously collision resistant hashing function function generateHash(module, testName) { var hex, i = 0, hash = 0, str = module + "\x1C" + testName, len = str.length; for (; i < len; i++) { hash = (hash << 5) - hash + str.charCodeAt(i); hash |= 0; } // Convert the possibly negative integer hash code into an 8 character hex string, which isn't // strictly necessary but increases user understanding that the id is a SHA-like hash hex = (0x100000000 + hash).toString(16); if (hex.length < 8) { hex = "0000000" + hex; } return hex.slice(-8); } function synchronize(callback, priority, seed) { var last = !priority, index; if (objectType(callback) === "array") { while (callback.length) { synchronize(callback.shift()); } return; } if (priority) { config.queue.splice(priorityCount++, 0, callback); } else if (seed) { if (!unitSampler) { unitSampler = unitSamplerGenerator(seed); } // Insert into a random position after all priority items index = Math.floor(unitSampler() * (config.queue.length - priorityCount + 1)); config.queue.splice(priorityCount + index, 0, callback); } else { config.queue.push(callback); } if (internalState.autorun && !config.blocking) { process(last); } } function unitSamplerGenerator(seed) { // 32-bit xorshift, requires only a nonzero seed // http://excamera.com/sphinx/article-xorshift.html var sample = parseInt(generateHash(seed), 16) || -1; return function () { sample ^= sample << 13; sample ^= sample >>> 17; sample ^= sample << 5; // ECMAScript has no unsigned number type if (sample < 0) { sample += 0x100000000; } return sample / 0x100000000; }; } function saveGlobal() { config.pollution = []; if (config.noglobals) { for (var key in global$1) { if (hasOwn.call(global$1, key)) { // In Opera sometimes DOM element ids show up here, ignore them if (/^qunit-test-output/.test(key)) { continue; } config.pollution.push(key); } } } } function checkPollution() { var newGlobals, deletedGlobals, old = config.pollution; saveGlobal(); newGlobals = diff(config.pollution, old); if (newGlobals.length > 0) { pushFailure("Introduced global variable(s): " + newGlobals.join(", ")); } deletedGlobals = diff(old, config.pollution); if (deletedGlobals.length > 0) { pushFailure("Deleted global variable(s): " + deletedGlobals.join(", ")); } } // Will be exposed as QUnit.test function test(testName, callback) { if (focused) { return; } var newTest; newTest = new Test({ testName: testName, callback: callback }); newTest.queue(); } // Will be exposed as QUnit.skip function skip(testName) { if (focused) { return; } var test = new Test({ testName: testName, skip: true }); test.queue(); } // Will be exposed as QUnit.only function only(testName, callback) { var newTest; if (focused) { return; } config.queue.length = 0; focused = true; newTest = new Test({ testName: testName, callback: callback }); newTest.queue(); } // Put a hold on processing and return a function that will release it. function internalStop(test) { var released = false; test.semaphore += 1; config.blocking = true; // Set a recovery timeout, if so configured. if (config.testTimeout && defined.setTimeout) { clearTimeout(config.timeout); config.timeout = setTimeout(function () { pushFailure("Test timed out", sourceFromStacktrace(2)); internalRecover(test); }, config.testTimeout); } return function resume() { if (released) { return; } released = true; test.semaphore -= 1; internalStart(test); }; } // Forcefully release all processing holds. function internalRecover(test) { test.semaphore = 0; internalStart(test); } // Release a processing hold, scheduling a resumption attempt if no holds remain. function internalStart(test) { // If semaphore is non-numeric, throw error if (isNaN(test.semaphore)) { test.semaphore = 0; pushFailure("Invalid value on test.semaphore", sourceFromStacktrace(2)); return; } // Don't start until equal number of stop-calls if (test.semaphore > 0) { return; } // Throw an Error if start is called more often than stop if (test.semaphore < 0) { test.semaphore = 0; pushFailure("Tried to restart test while already started (test's semaphore was 0 already)", sourceFromStacktrace(2)); return; } // Add a slight delay to allow more assertions etc. if (defined.setTimeout) { if (config.timeout) { clearTimeout(config.timeout); } config.timeout = setTimeout(function () { if (test.semaphore > 0) { return; } if (config.timeout) { clearTimeout(config.timeout); } begin(); }, 13); } else { begin(); } } function numberOfTests(module) { var count = module.tests.length, modules = [].concat(toConsumableArray(module.childModules)); // Do a breadth-first traversal of the child modules while (modules.length) { var nextModule = modules.shift(); count += nextModule.tests.length; modules.push.apply(modules, toConsumableArray(nextModule.childModules)); } return count; } function notifyTestsRan(module) { module.testsRun++; while (module = module.parentModule) { module.testsRun++; } } var Assert = function () { function Assert(testContext) { classCallCheck(this, Assert); this.test = testContext; } // Assert helpers // Specify the number of expected assertions to guarantee that failed test // (no assertions are run at all) don't slip through. createClass(Assert, [{ key: "expect", value: function expect(asserts) { if (arguments.length === 1) { this.test.expected = asserts; } else { return this.test.expected; } } // Put a hold on processing and return a function that will release it a maximum of once. }, { key: "async", value: function async(count) { var test$$1 = this.test, popped = false, acceptCallCount = count; if (typeof acceptCallCount === "undefined") { acceptCallCount = 1; } test$$1.usedAsync = true; var resume = internalStop(test$$1); return function done() { if (popped) { test$$1.pushFailure("Too many calls to the `assert.async` callback", sourceFromStacktrace(2)); return; } acceptCallCount -= 1; if (acceptCallCount > 0) { return; } popped = true; resume(); }; } // Exports test.push() to the user API // Alias of pushResult. }, { key: "push", value: function push(result, actual, expected, message, negative) { console.warn("assert.push is deprecated and will be removed in QUnit 3.0." + " Please use assert.pushResult instead (http://api.qunitjs.com/pushResult/)."); var currentAssert = this instanceof Assert ? this : config.current.assert; return currentAssert.pushResult({ result: result, actual: actual, expected: expected, message: message, negative: negative }); } }, { key: "pushResult", value: function pushResult(resultInfo) { // Destructure of resultInfo = { result, actual, expected, message, negative } var assert = this, currentTest = assert instanceof Assert && assert.test || config.current; // Backwards compatibility fix. // Allows the direct use of global exported assertions and QUnit.assert.* // Although, it's use is not recommended as it can leak assertions // to other tests from async tests, because we only get a reference to the current test, // not exactly the test where assertion were intended to be called. if (!currentTest) { throw new Error("assertion outside test context, in " + sourceFromStacktrace(2)); } if (currentTest.usedAsync === true && currentTest.semaphore === 0) { currentTest.pushFailure("Assertion after the final `assert.async` was resolved", sourceFromStacktrace(2)); // Allow this assertion to continue running anyway... } if (!(assert instanceof Assert)) { assert = currentTest.assert; } return assert.test.pushResult(resultInfo); } }, { key: "ok", value: function ok(result, message) { if (!message) { message = result ? "okay" : "failed, expected argument to be truthy, was: " + dump.parse(result); } this.pushResult({ result: !!result, actual: result, expected: true, message: message }); } }, { key: "notOk", value: function notOk(result, message) { if (!message) { message = !result ? "okay" : "failed, expected argument to be falsy, was: " + dump.parse(result); } this.pushResult({ result: !result, actual: result, expected: false, message: message }); } }, { key: "equal", value: function equal(actual, expected, message) { // eslint-disable-next-line eqeqeq var result = expected == actual; this.pushResult({ result: result, actual: actual, expected: expected, message: message }); } }, { key: "notEqual", value: function notEqual(actual, expected, message) { // eslint-disable-next-line eqeqeq var result = expected != actual; this.pushResult({ result: result, actual: actual, expected: expected, message: message, negative: true }); } }, { key: "propEqual", value: function propEqual(actual, expected, message) { actual = objectValues(actual); expected = objectValues(expected); this.pushResult({ result: equiv(actual, expected), actual: actual, expected: expected, message: message }); } }, { key: "notPropEqual", value: function notPropEqual(actual, expected, message) { actual = objectValues(actual); expected = objectValues(expected); this.pushResult({ result: !equiv(actual, expected), actual: actual, expected: expected, message: message, negative: true }); } }, { key: "deepEqual", value: function deepEqual(actual, expected, message) { this.pushResult({ result: equiv(actual, expected), actual: actual, expected: expected, message: message }); } }, { key: "notDeepEqual", value: function notDeepEqual(actual, expected, message) { this.pushResult({ result: !equiv(actual, expected), actual: actual, expected: expected, message: message, negative: true }); } }, { key: "strictEqual", value: function strictEqual(actual, expected, message) { this.pushResult({ result: expected === actual, actual: actual, expected: expected, message: message }); } }, { key: "notStrictEqual", value: function notStrictEqual(actual, expected, message) { this.pushResult({ result: expected !== actual, actual: actual, expected: expected, message: message, negative: true }); } }, { key: "throws", value: function throws(block, expected, message) { var actual = void 0, result = false, currentTest = this instanceof Assert && this.test || config.current; // 'expected' is optional unless doing string comparison if (objectType(expected) === "string") { if (message == null) { message = expected; expected = null; } else { throw new Error("throws/raises does not accept a string value for the expected argument.\n" + "Use a non-string object value (e.g. regExp) instead if it's necessary."); } } currentTest.ignoreGlobalErrors = true; try { block.call(currentTest.testEnvironment); } catch (e) { actual = e; } currentTest.ignoreGlobalErrors = false; if (actual) { var expectedType = objectType(expected); // We don't want to validate thrown error if (!expected) { result = true; expected = null; // Expected is a regexp } else if (expectedType === "regexp") { result = expected.test(errorString(actual)); // Expected is a constructor, maybe an Error constructor } else if (expectedType === "function" && actual instanceof expected) { result = true; // Expected is an Error object } else if (expectedType === "object") { result = actual instanceof expected.constructor && actual.name === expected.name && actual.message === expected.message; // Expected is a validation function which returns true if validation passed } else if (expectedType === "function" && expected.call({}, actual) === true) { expected = null; result = true; } } currentTest.assert.pushResult({ result: result, actual: actual, expected: expected, message: message }); } }]); return Assert; }(); // Provide an alternative to assert.throws(), for environments that consider throws a reserved word // Known to us are: Closure Compiler, Narwhal // eslint-disable-next-line dot-notation Assert.prototype.raises = Assert.prototype["throws"]; /** * Converts an error into a simple string for comparisons. * * @param {Error} error * @return {String} */ function errorString(error) { var resultErrorString = error.toString(); if (resultErrorString.substring(0, 7) === "[object") { var name = error.name ? error.name.toString() : "Error"; var message = error.message ? error.message.toString() : ""; if (name && message) { return name + ": " + message; } else if (name) { return name; } else if (message) { return message; } else { return "Error"; } } else { return resultErrorString; } } /* global module, exports, define */ function exportQUnit(QUnit) { if (defined.document) { // QUnit may be defined when it is preconfigured but then only QUnit and QUnit.config may be defined. if (window.QUnit && window.QUnit.version) { throw new Error("QUnit has already been defined."); } window.QUnit = QUnit; } // For nodejs if (typeof module !== "undefined" && module && module.exports) { module.exports = QUnit; // For consistency with CommonJS environments' exports module.exports.QUnit = QUnit; } // For CommonJS with exports, but without module.exports, like Rhino if (typeof exports !== "undefined" && exports) { exports.QUnit = QUnit; } if (typeof define === "function" && define.amd) { define(function () { return QUnit; }); QUnit.config.autostart = false; } } (function () { if (!defined.document) { return; } // `onErrorFnPrev` initialized at top of scope // Preserve other handlers var onErrorFnPrev = window.onerror; // Cover uncaught exceptions // Returning true will suppress the default browser handler, // returning false will let it run. window.onerror = function (error, filePath, linerNr) { var ret = false; if (onErrorFnPrev) { ret = onErrorFnPrev(error, filePath, linerNr); } // Treat return value as window.onerror itself does, // Only do our handling if not suppressed. if (ret !== true) { if (config.current) { if (config.current.ignoreGlobalErrors) { return true; } pushFailure(error, filePath + ":" + linerNr); } else { test("global failure", extend(function () { pushFailure(error, filePath + ":" + linerNr); }, { validTest: true })); } return false; } return ret; }; })(); var QUnit = {}; var globalStartCalled = false; var runStarted = false; var internalState = { autorun: false }; // Figure out if we're running the tests from a server or not QUnit.isLocal = !(defined.document && window.location.protocol !== "file:"); // Expose the current QUnit version QUnit.version = "2.1.1"; extend(QUnit, { // Call on start of module test to prepend name to all tests module: function module(name, testEnvironment, executeNow) { var module, moduleFns; var currentModule = config.currentModule; if (arguments.length === 2) { if (objectType(testEnvironment) === "function") { executeNow = testEnvironment; testEnvironment = undefined; } } module = createModule(); moduleFns = { before: setHook(module, "before"), beforeEach: setHook(module, "beforeEach"), afterEach: setHook(module, "afterEach"), after: setHook(module, "after") }; if (objectType(executeNow) === "function") { config.moduleStack.push(module); setCurrentModule(module); executeNow.call(module.testEnvironment, moduleFns); config.moduleStack.pop(); module = module.parentModule || currentModule; } setCurrentModule(module); function createModule() { var parentModule = config.moduleStack.length ? config.moduleStack.slice(-1)[0] : null; var moduleName = parentModule !== null ? [parentModule.name, name].join(" > ") : name; var module = { name: moduleName, parentModule: parentModule, tests: [], moduleId: generateHash(moduleName), testsRun: 0, childModules: [] }; var env = {}; if (parentModule) { parentModule.childModules.push(module); extend(env, parentModule.testEnvironment); delete env.beforeEach; delete env.afterEach; } extend(env, testEnvironment); module.testEnvironment = env; config.modules.push(module); return module; } function setCurrentModule(module) { config.currentModule = module; } }, test: test, skip: skip, only: only, start: function start(count) { var globalStartAlreadyCalled = globalStartCalled; if (!config.current) { globalStartCalled = true; if (runStarted) { throw new Error("Called start() while test already started running"); } else if (globalStartAlreadyCalled || count > 1) { throw new Error("Called start() outside of a test context too many times"); } else if (config.autostart) { throw new Error("Called start() outside of a test context when " + "QUnit.config.autostart was true"); } else if (!defined.document && !config.pageLoaded) { // Starts from Node even if .load was not previously called QUnit.load(); } else if (!config.pageLoaded) { // The page isn't completely loaded yet, so bail out and let `QUnit.load` handle it config.autostart = true; return; } } else { throw new Error("QUnit.start cannot be called inside a test context."); } scheduleBegin(); }, config: config, is: is, objectType: objectType, extend: extend, load: function load() { config.pageLoaded = true; // Initialize the configuration options extend(config, { stats: { all: 0, bad: 0 }, started: 0, updateRate: 1000, autostart: true, filter: "" }, true); if (!runStarted) { config.blocking = false; if (config.autostart) { scheduleBegin(); } } }, stack: function stack(offset) { offset = (offset || 0) + 2; return sourceFromStacktrace(offset); } }); QUnit.pushFailure = pushFailure; QUnit.assert = Assert.prototype; QUnit.equiv = equiv; QUnit.dump = dump; registerLoggingCallbacks(QUnit); function scheduleBegin() { runStarted = true; // Add a slight delay to allow definition of more modules and tests. if (defined.setTimeout) { setTimeout(function () { begin(); }, 13); } else { begin(); } } function begin() { var i, l, modulesLog = []; // If the test run hasn't officially begun yet if (!config.started) { // Record the time of the test run's beginning config.started = now(); // Delete the loose unnamed module if unused. if (config.modules[0].name === "" && config.modules[0].tests.length === 0) { config.modules.shift(); } // Avoid unnecessary information by not logging modules' test environments for (i = 0, l = config.modules.length; i < l; i++) { modulesLog.push({ name: config.modules[i].name, tests: config.modules[i].tests }); } // The test run is officially beginning now runLoggingCallbacks("begin", { totalTests: Test.count, modules: modulesLog }); } config.blocking = false; process(true); } function process(last) { function next() { process(last); } var start = now(); config.depth = (config.depth || 0) + 1; while (config.queue.length && !config.blocking) { if (!defined.setTimeout || config.updateRate <= 0 || now() - start < config.updateRate) { if (config.current) { // Reset async tracking for each phase of the Test lifecycle config.current.usedAsync = false; } config.queue.shift()(); } else { setTimeout(next, 13); break; } } config.depth--; if (last && !config.blocking && !config.queue.length && config.depth === 0) { done(); } } function done() { var runtime, passed, i, key, storage = config.storage; internalState.autorun = true; runtime = now() - config.started; passed = config.stats.all - config.stats.bad; runLoggingCallbacks("done", { failed: config.stats.bad, passed: passed, total: config.stats.all, runtime: runtime }); // Clear own storage items if all tests passed if (storage && config.stats.bad === 0) { for (i = storage.length - 1; i >= 0; i--) { key = storage.key(i); if (key.indexOf("qunit-test-") === 0) { storage.removeItem(key); } } } } function setHook(module, hookName) { if (module.testEnvironment === undefined) { module.testEnvironment = {}; } return function (callback) { module.testEnvironment[hookName] = callback; }; } exportQUnit(QUnit); (function () { if (typeof window === "undefined" || typeof document === "undefined") { return; } var config = QUnit.config, hasOwn = Object.prototype.hasOwnProperty; // Stores fixture HTML for resetting later function storeFixture() { // Avoid overwriting user-defined values if (hasOwn.call(config, "fixture")) { return; } var fixture = document.getElementById("qunit-fixture"); if (fixture) { config.fixture = fixture.innerHTML; } } QUnit.begin(storeFixture); // Resets the fixture DOM element if available. function resetFixture() { if (config.fixture == null) { return; } var fixture = document.getElementById("qunit-fixture"); if (fixture) { fixture.innerHTML = config.fixture; } } QUnit.testStart(resetFixture); })(); (function () { // Only interact with URLs via window.location var location = typeof window !== "undefined" && window.location; if (!location) { return; } var urlParams = getUrlParams(); QUnit.urlParams = urlParams; // Match module/test by inclusion in an array QUnit.config.moduleId = [].concat(urlParams.moduleId || []); QUnit.config.testId = [].concat(urlParams.testId || []); // Exact case-insensitive match of the module name QUnit.config.module = urlParams.module; // Regular expression or case-insenstive substring match against "moduleName: testName" QUnit.config.filter = urlParams.filter; // Test order randomization if (urlParams.seed === true) { // Generate a random seed if the option is specified without a value QUnit.config.seed = Math.random().toString(36).slice(2); } else if (urlParams.seed) { QUnit.config.seed = urlParams.seed; } // Add URL-parameter-mapped config values with UI form rendering data QUnit.config.urlConfig.push({ id: "hidepassed", label: "Hide passed tests", tooltip: "Only show tests and assertions that fail. Stored as query-strings." }, { id: "noglobals", label: "Check for Globals", tooltip: "Enabling this will test if any test introduces new properties on the " + "global object (`window` in Browsers). Stored as query-strings." }, { id: "notrycatch", label: "No try-catch", tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging " + "exceptions in IE reasonable. Stored as query-strings." }); QUnit.begin(function () { var i, option, urlConfig = QUnit.config.urlConfig; for (i = 0; i < urlConfig.length; i++) { // Options can be either strings or objects with nonempty "id" properties option = QUnit.config.urlConfig[i]; if (typeof option !== "string") { option = option.id; } if (QUnit.config[option] === undefined) { QUnit.config[option] = urlParams[option]; } } }); function getUrlParams() { var i, param, name, value; var urlParams = Object.create(null); var params = location.search.slice(1).split("&"); var length = params.length; for (i = 0; i < length; i++) { if (params[i]) { param = params[i].split("="); name = decodeQueryParam(param[0]); // Allow just a key to turn on a flag, e.g., test.html?noglobals value = param.length === 1 || decodeQueryParam(param.slice(1).join("=")); if (name in urlParams) { urlParams[name] = [].concat(urlParams[name], value); } else { urlParams[name] = value; } } } return urlParams; } function decodeQueryParam(param) { return decodeURIComponent(param.replace(/\+/g, "%20")); } })(); // Escape text for attribute or text content. function escapeText(s) { if (!s) { return ""; } s = s + ""; // Both single quotes and double quotes (for attributes) return s.replace(/['"<>&]/g, function (s) { switch (s) { case "'": return "'"; case "\"": return """; case "<": return "<"; case ">": return ">"; case "&": return "&"; } }); } (function () { // Don't load the HTML Reporter on non-browser environments if (typeof window === "undefined" || !window.document) { return; } var config = QUnit.config, document$$1 = window.document, collapseNext = false, hasOwn = Object.prototype.hasOwnProperty, unfilteredUrl = setUrl({ filter: undefined, module: undefined, moduleId: undefined, testId: undefined }), modulesList = []; function addEvent(elem, type, fn) { elem.addEventListener(type, fn, false); } function removeEvent(elem, type, fn) { elem.removeEventListener(type, fn, false); } function addEvents(elems, type, fn) { var i = elems.length; while (i--) { addEvent(elems[i], type, fn); } } function hasClass(elem, name) { return (" " + elem.className + " ").indexOf(" " + name + " ") >= 0; } function addClass(elem, name) { if (!hasClass(elem, name)) { elem.className += (elem.className ? " " : "") + name; } } function toggleClass(elem, name, force) { if (force || typeof force === "undefined" && !hasClass(elem, name)) { addClass(elem, name); } else { removeClass(elem, name); } } function removeClass(elem, name) { var set = " " + elem.className + " "; // Class name may appear multiple times while (set.indexOf(" " + name + " ") >= 0) { set = set.replace(" " + name + " ", " "); } // Trim for prettiness elem.className = typeof set.trim === "function" ? set.trim() : set.replace(/^\s+|\s+$/g, ""); } function id(name) { return document$$1.getElementById && document$$1.getElementById(name); } function abortTests() { var abortButton = id("qunit-abort-tests-button"); if (abortButton) { abortButton.disabled = true; abortButton.innerHTML = "Aborting..."; } QUnit.config.queue.length = 0; return false; } function interceptNavigation(ev) { applyUrlParams(); if (ev && ev.preventDefault) { ev.preventDefault(); } return false; } function getUrlConfigHtml() { var i, j, val, escaped, escapedTooltip, selection = false, urlConfig = config.urlConfig, urlConfigHtml = ""; for (i = 0; i < urlConfig.length; i++) { // Options can be either strings or objects with nonempty "id" properties val = config.urlConfig[i]; if (typeof val === "string") { val = { id: val, label: val }; } escaped = escapeText(val.id); escapedTooltip = escapeText(val.tooltip); if (!val.value || typeof val.value === "string") { urlConfigHtml += "<label for='qunit-urlconfig-" + escaped + "' title='" + escapedTooltip + "'><input id='qunit-urlconfig-" + escaped + "' name='" + escaped + "' type='checkbox'" + (val.value ? " value='" + escapeText(val.value) + "'" : "") + (config[val.id] ? " checked='checked'" : "") + " title='" + escapedTooltip + "' />" + escapeText(val.label) + "</label>"; } else { urlConfigHtml += "<label for='qunit-urlconfig-" + escaped + "' title='" + escapedTooltip + "'>" + val.label + ": </label><select id='qunit-urlconfig-" + escaped + "' name='" + escaped + "' title='" + escapedTooltip + "'><option></option>"; if (QUnit.is("array", val.value)) { for (j = 0; j < val.value.length; j++) { escaped = escapeText(val.value[j]); urlConfigHtml += "<option value='" + escaped + "'" + (config[val.id] === val.value[j] ? (selection = true) && " selected='selected'" : "") + ">" + escaped + "</option>"; } } else { for (j in val.value) { if (hasOwn.call(val.value, j)) { urlConfigHtml += "<option value='" + escapeText(j) + "'" + (config[val.id] === j ? (selection = true) && " selected='selected'" : "") + ">" + escapeText(val.value[j]) + "</option>"; } } } if (config[val.id] && !selection) { escaped = escapeText(config[val.id]); urlConfigHtml += "<option value='" + escaped + "' selected='selected' disabled='disabled'>" + escaped + "</option>"; } urlConfigHtml += "</select>"; } } return urlConfigHtml; } // Handle "click" events on toolbar checkboxes and "change" for select menus. // Updates the URL with the new state of `config.urlConfig` values. function toolbarChanged() { var updatedUrl, value, tests, field = this, params = {}; // Detect if field is a select menu or a checkbox if ("selectedIndex" in field) { value = field.options[field.selectedIndex].value || undefined; } else { value = field.checked ? field.defaultValue || true : undefined; } params[field.name] = value; updatedUrl = setUrl(params); // Check if we can apply the change without a page refresh if ("hidepassed" === field.name && "replaceState" in window.history) { QUnit.urlParams[field.name] = value; config[field.name] = value || false; tests = id("qunit-tests"); if (tests) { toggleClass(tests, "hidepass", value || false); } window.history.replaceState(null, "", updatedUrl); } else { window.location = updatedUrl; } } function setUrl(params) { var key, arrValue, i, querystring = "?", location = window.location; params = QUnit.extend(QUnit.extend({}, QUnit.urlParams), params); for (key in params) { // Skip inherited or undefined properties if (hasOwn.call(params, key) && params[key] !== undefined) { // Output a parameter for each value of this key (but usually just one) arrValue = [].concat(params[key]); for (i = 0; i < arrValue.length; i++) { querystring += encodeURIComponent(key); if (arrValue[i] !== true) { querystring += "=" + encodeURIComponent(arrValue[i]); } querystring += "&"; } } } return location.protocol + "//" + location.host + location.pathname + querystring.slice(0, -1); } function applyUrlParams() { var i, selectedModules = [], modulesList = id("qunit-modulefilter-dropdown-list").getElementsByTagName("input"), filter = id("qunit-filter-input").value; for (i = 0; i < modulesList.length; i++) { if (modulesList[i].checked) { selectedModules.push(modulesList[i].value); } } window.location = setUrl({ filter: filter === "" ? undefined : filter, moduleId: selectedModules.length === 0 ? undefined : selectedModules, // Remove module and testId filter module: undefined, testId: undefined }); } function toolbarUrlConfigContainer() { var urlConfigContainer = document$$1.createElement("span"); urlConfigContainer.innerHTML = getUrlConfigHtml(); addClass(urlConfigContainer, "qunit-url-config"); addEvents(urlConfigContainer.getElementsByTagName("input"), "change", toolbarChanged); addEvents(urlConfigContainer.getElementsByTagName("select"), "change", toolbarChanged); return urlConfigContainer; } function abortTestsButton() { var button = document$$1.createElement("button"); button.id = "qunit-abort-tests-button"; button.innerHTML = "Abort"; addEvent(button, "click", abortTests); return button; } function toolbarLooseFilter() { var filter = document$$1.createElement("form"), label = document$$1.createElement("label"), input = document$$1.createElement("input"), button = document$$1.createElement("button"); addClass(filter, "qunit-filter"); label.innerHTML = "Filter: "; input.type = "text"; input.value = config.filter || ""; input.name = "filter"; input.id = "qunit-filter-input"; button.innerHTML = "Go"; label.appendChild(input); filter.appendChild(label); filter.appendChild(document$$1.createTextNode(" ")); filter.appendChild(button); addEvent(filter, "submit", interceptNavigation); return filter; } function moduleListHtml() { var i, checked, html = ""; for (i = 0; i < config.modules.length; i++) { if (config.modules[i].name !== "") { checked = config.moduleId.indexOf(config.modules[i].moduleId) > -1; html += "<li><label class='clickable" + (checked ? " checked" : "") + "'><input type='checkbox' " + "value='" + config.modules[i].moduleId + "'" + (checked ? " checked='checked'" : "") + " />" + escapeText(config.modules[i].name) + "</label></li>"; } } return html; } function toolbarModuleFilter() { var allCheckbox, commit, reset, moduleFilter = document$$1.createElement("form"), label = document$$1.createElement("label"), moduleSearch = document$$1.createElement("input"), dropDown = document$$1.createElement("div"), actions = document$$1.createElement("span"), dropDownList = document$$1.createElement("ul"), dirty = false; moduleSearch.id = "qunit-modulefilter-search"; addEvent(moduleSearch, "input", searchInput); addEvent(moduleSearch, "input", searchFocus); addEvent(moduleSearch, "focus", searchFocus); addEvent(moduleSearch, "click", searchFocus); label.id = "qunit-modulefilter-search-container"; label.innerHTML = "Module: "; label.appendChild(moduleSearch); actions.id = "qunit-modulefilter-actions"; actions.innerHTML = "<button style='display:none'>Apply</button>" + "<button type='reset' style='display:none'>Reset</button>" + "<label class='clickable" + (config.moduleId.length ? "" : " checked") + "'><input type='checkbox'" + (config.moduleId.length ? "" : " checked='checked'") + ">All modules</label>"; allCheckbox = actions.lastChild.firstChild; commit = actions.firstChild; reset = commit.nextSibling; addEvent(commit, "click", applyUrlParams); dropDownList.id = "qunit-modulefilter-dropdown-list"; dropDownList.innerHTML = moduleListHtml(); dropDown.id = "qunit-modulefilter-dropdown"; dropDown.style.display = "none"; dropDown.appendChild(actions); dropDown.appendChild(dropDownList); addEvent(dropDown, "change", selectionChange); selectionChange(); moduleFilter.id = "qunit-modulefilter"; moduleFilter.appendChild(label); moduleFilter.appendChild(dropDown); addEvent(moduleFilter, "submit", interceptNavigation); addEvent(moduleFilter, "reset", function () { // Let the reset happen, then update styles window.setTimeout(selectionChange); }); // Enables show/hide for the dropdown function searchFocus() { if (dropDown.style.display !== "none") { return; } dropDown.style.display = "block"; addEvent(document$$1, "click", hideHandler); addEvent(document$$1, "keydown", hideHandler); // Hide on Escape keydown or outside-container click function hideHandler(e) { var inContainer = moduleFilter.contains(e.target); if (e.keyCode === 27 || !inContainer) { if (e.keyCode === 27 && inContainer) { moduleSearch.focus(); } dropDown.style.display = "none"; removeEvent(document$$1, "click", hideHandler); removeEvent(document$$1, "keydown", hideHandler); moduleSearch.value = ""; searchInput(); } } } // Processes module search box input function searchInput() { var i, item, searchText = moduleSearch.value.toLowerCase(), listItems = dropDownList.children; for (i = 0; i < listItems.length; i++) { item = listItems[i]; if (!searchText || item.textContent.toLowerCase().indexOf(searchText) > -1) { item.style.display = ""; } else { item.style.display = "none"; } } } // Processes selection changes function selectionChange(evt) { var i, item, checkbox = evt && evt.target || allCheckbox, modulesList = dropDownList.getElementsByTagName("input"), selectedNames = []; toggleClass(checkbox.parentNode, "checked", checkbox.checked); dirty = false; if (checkbox.checked && checkbox !== allCheckbox) { allCheckbox.checked = false; removeClass(allCheckbox.parentNode, "checked"); } for (i = 0; i < modulesList.length; i++) { item = modulesList[i]; if (!evt) { toggleClass(item.parentNode, "checked", item.checked); } else if (checkbox === allCheckbox && checkbox.checked) { item.checked = false; removeClass(item.parentNode, "checked"); } dirty = dirty || item.checked !== item.defaultChecked; if (item.checked) { selectedNames.push(item.parentNode.textContent); } } commit.style.display = reset.style.display = dirty ? "" : "none"; moduleSearch.placeholder = selectedNames.join(", ") || allCheckbox.parentNode.textContent; moduleSearch.title = "Type to filter list. Current selection:\n" + (selectedNames.join("\n") || allCheckbox.parentNode.textContent); } return moduleFilter; } function appendToolbar() { var toolbar = id("qunit-testrunner-toolbar"); if (toolbar) { toolbar.appendChild(toolbarUrlConfigContainer()); toolbar.appendChild(toolbarModuleFilter()); toolbar.appendChild(toolbarLooseFilter()); toolbar.appendChild(document$$1.createElement("div")).className = "clearfix"; } } function appendHeader() { var header = id("qunit-header"); if (header) { header.innerHTML = "<a href='" + escapeText(unfilteredUrl) + "'>" + header.innerHTML + "</a> "; } } function appendBanner() { var banner = id("qunit-banner"); if (banner) { banner.className = ""; } } function appendTestResults() { var tests = id("qunit-tests"), result = id("qunit-testresult"), controls; if (result) { result.parentNode.removeChild(result); } if (tests) { tests.innerHTML = ""; result = document$$1.createElement("p"); result.id = "qunit-testresult"; result.className = "result"; tests.parentNode.insertBefore(result, tests); result.innerHTML = "<div id=\"qunit-testresult-display\">Running...<br /> </div>" + "<div id=\"qunit-testresult-controls\"></div>" + "<div class=\"clearfix\"></div>"; controls = id("qunit-testresult-controls"); } if (controls) { controls.appendChild(abortTestsButton()); } } function appendFilteredTest() { var testId = QUnit.config.testId; if (!testId || testId.length <= 0) { return ""; } return "<div id='qunit-filteredTest'>Rerunning selected tests: " + escapeText(testId.join(", ")) + " <a id='qunit-clearFilter' href='" + escapeText(unfilteredUrl) + "'>Run all tests</a></div>"; } function appendUserAgent() { var userAgent = id("qunit-userAgent"); if (userAgent) { userAgent.innerHTML = ""; userAgent.appendChild(document$$1.createTextNode("QUnit " + QUnit.version + "; " + navigator.userAgent)); } } function appendInterface() { var qunit = id("qunit"); if (qunit) { qunit.innerHTML = "<h1 id='qunit-header'>" + escapeText(document$$1.title) + "</h1>" + "<h2 id='qunit-banner'></h2>" + "<div id='qunit-testrunner-toolbar'></div>" + appendFilteredTest() + "<h2 id='qunit-userAgent'></h2>" + "<ol id='qunit-tests'></ol>"; } appendHeader(); appendBanner(); appendTestResults(); appendUserAgent(); appendToolbar(); } function appendTestsList(modules) { var i, l, x, z, test, moduleObj; for (i = 0, l = modules.length; i < l; i++) { moduleObj = modules[i]; for (x = 0, z = moduleObj.tests.length; x < z; x++) { test = moduleObj.tests[x]; appendTest(test.name, test.testId, moduleObj.name); } } } function appendTest(name, testId, moduleName) { var title, rerunTrigger, testBlock, assertList, tests = id("qunit-tests"); if (!tests) { return; } title = document$$1.createElement("strong"); title.innerHTML = getNameHtml(name, moduleName); rerunTrigger = document$$1.createElement("a"); rerunTrigger.innerHTML = "Rerun"; rerunTrigger.href = setUrl({ testId: testId }); testBlock = document$$1.createElement("li"); testBlock.appendChild(title); testBlock.appendChild(rerunTrigger); testBlock.id = "qunit-test-output-" + testId; assertList = document$$1.createElement("ol"); assertList.className = "qunit-assert-list"; testBlock.appendChild(assertList); tests.appendChild(testBlock); } // HTML Reporter initialization and load QUnit.begin(function (details) { var i, moduleObj, tests; // Sort modules by name for the picker for (i = 0; i < details.modules.length; i++) { moduleObj = details.modules[i]; if (moduleObj.name) { modulesList.push(moduleObj.name); } } modulesList.sort(function (a, b) { return a.localeCompare(b); }); // Initialize QUnit elements appendInterface(); appendTestsList(details.modules); tests = id("qunit-tests"); if (tests && config.hidepassed) { addClass(tests, "hidepass"); } }); QUnit.done(function (details) { var banner = id("qunit-banner"), tests = id("qunit-tests"), abortButton = id("qunit-abort-tests-button"), html = ["Tests completed in ", details.runtime, " milliseconds.<br />", "<span class='passed'>", details.passed, "</span> assertions of <span class='total'>", details.total, "</span> passed, <span class='failed'>", details.failed, "</span> failed."].join(""), test, assertLi, assertList; // Update remaing tests to aborted if (abortButton && abortButton.disabled) { html = "Tests aborted after " + details.runtime + " milliseconds."; for (var i = 0; i < tests.children.length; i++) { test = tests.children[i]; if (test.className === "" || test.className === "running") { test.className = "aborted"; assertList = test.getElementsByTagName("ol")[0]; assertLi = document$$1.createElement("li"); assertLi.className = "fail"; assertLi.innerHTML = "Test aborted."; assertList.appendChild(assertLi); } } } if (banner && (!abortButton || abortButton.disabled === false)) { banner.className = details.failed ? "qunit-fail" : "qunit-pass"; } if (abortButton) { abortButton.parentNode.removeChild(abortButton); } if (tests) { id("qunit-testresult-display").innerHTML = html; } if (config.altertitle && document$$1.title) { // Show ✖ for good, ✔ for bad suite result in title // use escape sequences in case file gets loaded with non-utf-8-charset document$$1.title = [details.failed ? "\u2716" : "\u2714", document$$1.title.replace(/^[\u2714\u2716] /i, "")].join(" "); } // Scroll back to top to show results if (config.scrolltop && window.scrollTo) { window.scrollTo(0, 0); } }); function getNameHtml(name, module) { var nameHtml = ""; if (module) { nameHtml = "<span class='module-name'>" + escapeText(module) + "</span>: "; } nameHtml += "<span class='test-name'>" + escapeText(name) + "</span>"; return nameHtml; } QUnit.testStart(function (details) { var running, testBlock, bad; testBlock = id("qunit-test-output-" + details.testId); if (testBlock) { testBlock.className = "running"; } else { // Report later registered tests appendTest(details.name, details.testId, details.module); } running = id("qunit-testresult-display"); if (running) { bad = QUnit.config.reorder && details.previousFailure; running.innerHTML = (bad ? "Rerunning previously failed test: <br />" : "Running: <br />") + getNameHtml(details.name, details.module); } }); function stripHtml(string) { // Strip tags, html entity and whitespaces return string.replace(/<\/?[^>]+(>|$)/g, "").replace(/\"/g, "").replace(/\s+/g, ""); } QUnit.log(function (details) { var assertList, assertLi, message, expected, actual, diff, showDiff = false, testItem = id("qunit-test-output-" + details.testId); if (!testItem) { return; } message = escapeText(details.message) || (details.result ? "okay" : "failed"); message = "<span class='test-message'>" + message + "</span>"; message += "<span class='runtime'>@ " + details.runtime + " ms</span>"; // The pushFailure doesn't provide details.expected // when it calls, it's implicit to also not show expected and diff stuff // Also, we need to check details.expected existence, as it can exist and be undefined if (!details.result && hasOwn.call(details, "expected")) { if (details.negative) { expected = "NOT " + QUnit.dump.parse(details.expected); } else { expected = QUnit.dump.parse(details.expected); } actual = QUnit.dump.parse(details.actual); message += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" + escapeText(expected) + "</pre></td></tr>"; if (actual !== expected) { message += "<tr class='test-actual'><th>Result: </th><td><pre>" + escapeText(actual) + "</pre></td></tr>"; // Don't show diff if actual or expected are booleans if (!/^(true|false)$/.test(actual) && !/^(true|false)$/.test(expected)) { diff = QUnit.diff(expected, actual); showDiff = stripHtml(diff).length !== stripHtml(expected).length + stripHtml(actual).length; } // Don't show diff if expected and actual are totally different if (showDiff) { message += "<tr class='test-diff'><th>Diff: </th><td><pre>" + diff + "</pre></td></tr>"; } } else if (expected.indexOf("[object Array]") !== -1 || expected.indexOf("[object Object]") !== -1) { message += "<tr class='test-message'><th>Message: </th><td>" + "Diff suppressed as the depth of object is more than current max depth (" + QUnit.config.maxDepth + ").<p>Hint: Use <code>QUnit.dump.maxDepth</code> to " + " run with a higher max depth or <a href='" + escapeText(setUrl({ maxDepth: -1 })) + "'>" + "Rerun</a> without max depth.</p></td></tr>"; } else { message += "<tr class='test-message'><th>Message: </th><td>" + "Diff suppressed as the expected and actual results have an equivalent" + " serialization</td></tr>"; } if (details.source) { message += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText(details.source) + "</pre></td></tr>"; } message += "</table>"; // This occurs when pushFailure is set and we have an extracted stack trace } else if (!details.result && details.source) { message += "<table>" + "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText(details.source) + "</pre></td></tr>" + "</table>"; } assertList = testItem.getElementsByTagName("ol")[0]; assertLi = document$$1.createElement("li"); assertLi.className = details.result ? "pass" : "fail"; assertLi.innerHTML = message; assertList.appendChild(assertLi); }); QUnit.testDone(function (details) { var testTitle, time, testItem, assertList, good, bad, testCounts, skipped, sourceName, tests = id("qunit-tests"); if (!tests) { return; } testItem = id("qunit-test-output-" + details.testId); assertList = testItem.getElementsByTagName("ol")[0]; good = details.passed; bad = details.failed; if (bad === 0) { // Collapse the passing tests addClass(assertList, "qunit-collapsed"); } else if (config.collapse) { if (!collapseNext) { // Skip collapsing the first failing test collapseNext = true; } else { // Collapse remaining tests addClass(assertList, "qunit-collapsed"); } } // The testItem.firstChild is the test name testTitle = testItem.firstChild; testCounts = bad ? "<b class='failed'>" + bad + "</b>, " + "<b class='passed'>" + good + "</b>, " : ""; testTitle.innerHTML += " <b class='counts'>(" + testCounts + details.assertions.length + ")</b>"; if (details.skipped) { testItem.className = "skipped"; skipped = document$$1.createElement("em"); skipped.className = "qunit-skipped-label"; skipped.innerHTML = "skipped"; testItem.insertBefore(skipped, testTitle); } else { addEvent(testTitle, "click", function () { toggleClass(assertList, "qunit-collapsed"); }); testItem.className = bad ? "fail" : "pass"; time = document$$1.createElement("span"); time.className = "runtime"; time.innerHTML = details.runtime + " ms"; testItem.insertBefore(time, assertList); } // Show the source of the test when showing assertions if (details.source) { sourceName = document$$1.createElement("p"); sourceName.innerHTML = "<strong>Source: </strong>" + details.source; addClass(sourceName, "qunit-source"); if (bad === 0) { addClass(sourceName, "qunit-collapsed"); } addEvent(testTitle, "click", function () { toggleClass(sourceName, "qunit-collapsed"); }); testItem.appendChild(sourceName); } }); // Avoid readyState issue with phantomjs // Ref: #818 var notPhantom = function (p) { return !(p && p.version && p.version.major > 0); }(window.phantom); if (notPhantom && document$$1.readyState === "complete") { QUnit.load(); } else { addEvent(window, "load", QUnit.load); } })(); /* * This file is a modified version of google-diff-match-patch's JavaScript implementation * (https://code.google.com/p/google-diff-match-patch/source/browse/trunk/javascript/diff_match_patch_uncompressed.js), * modifications are licensed as more fully set forth in LICENSE.txt. * * The original source of google-diff-match-patch is attributable and licensed as follows: * * Copyright 2006 Google Inc. * https://code.google.com/p/google-diff-match-patch/ * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * More Info: * https://code.google.com/p/google-diff-match-patch/ * * Usage: QUnit.diff(expected, actual) * */ QUnit.diff = function () { function DiffMatchPatch() {} // DIFF FUNCTIONS /** * The data structure representing a diff is an array of tuples: * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']] * which means: delete 'Hello', add 'Goodbye' and keep ' world.' */ var DIFF_DELETE = -1, DIFF_INSERT = 1, DIFF_EQUAL = 0; /** * Find the differences between two texts. Simplifies the problem by stripping * any common prefix or suffix off the texts before diffing. * @param {string} text1 Old string to be diffed. * @param {string} text2 New string to be diffed. * @param {boolean=} optChecklines Optional speedup flag. If present and false, * then don't run a line-level diff first to identify the changed areas. * Defaults to true, which does a faster, slightly less optimal diff. * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples. */ DiffMatchPatch.prototype.DiffMain = function (text1, text2, optChecklines) { var deadline, checklines, commonlength, commonprefix, commonsuffix, diffs; // The diff must be complete in up to 1 second. deadline = new Date().getTime() + 1000; // Check for null inputs. if (text1 === null || text2 === null) { throw new Error("Null input. (DiffMain)"); } // Check for equality (speedup). if (text1 === text2) { if (text1) { return [[DIFF_EQUAL, text1]]; } return []; } if (typeof optChecklines === "undefined") { optChecklines = true; } checklines = optChecklines; // Trim off common prefix (speedup). commonlength = this.diffCommonPrefix(text1, text2); commonprefix = text1.substring(0, commonlength); text1 = text1.substring(commonlength); text2 = text2.substring(commonlength); // Trim off common suffix (speedup). commonlength = this.diffCommonSuffix(text1, text2); commonsuffix = text1.substring(text1.length - commonlength); text1 = text1.substring(0, text1.length - commonlength); text2 = text2.substring(0, text2.length - commonlength); // Compute the diff on the middle block. diffs = this.diffCompute(text1, text2, checklines, deadline); // Restore the prefix and suffix. if (commonprefix) { diffs.unshift([DIFF_EQUAL, commonprefix]); } if (commonsuffix) { diffs.push([DIFF_EQUAL, commonsuffix]); } this.diffCleanupMerge(diffs); return diffs; }; /** * Reduce the number of edits by eliminating operationally trivial equalities. * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples. */ DiffMatchPatch.prototype.diffCleanupEfficiency = function (diffs) { var changes, equalities, equalitiesLength, lastequality, pointer, preIns, preDel, postIns, postDel; changes = false; equalities = []; // Stack of indices where equalities are found. equalitiesLength = 0; // Keeping our own length var is faster in JS. /** @type {?string} */ lastequality = null; // Always equal to diffs[equalities[equalitiesLength - 1]][1] pointer = 0; // Index of current position. // Is there an insertion operation before the last equality. preIns = false; // Is there a deletion operation before the last equality. preDel = false; // Is there an insertion operation after the last equality. postIns = false; // Is there a deletion operation after the last equality. postDel = false; while (pointer < diffs.length) { // Equality found. if (diffs[pointer][0] === DIFF_EQUAL) { if (diffs[pointer][1].length < 4 && (postIns || postDel)) { // Candidate found. equalities[equalitiesLength++] = pointer; preIns = postIns; preDel = postDel; lastequality = diffs[pointer][1]; } else { // Not a candidate, and can never become one. equalitiesLength = 0; lastequality = null; } postIns = postDel = false; // An insertion or deletion. } else { if (diffs[pointer][0] === DIFF_DELETE) { postDel = true; } else { postIns = true; } /* * Five types to be split: * <ins>A</ins><del>B</del>XY<ins>C</ins><del>D</del> * <ins>A</ins>X<ins>C</ins><del>D</del> * <ins>A</ins><del>B</del>X<ins>C</ins> * <ins>A</del>X<ins>C</ins><del>D</del> * <ins>A</ins><del>B</del>X<del>C</del> */ if (lastequality && (preIns && preDel && postIns && postDel || lastequality.length < 2 && preIns + preDel + postIns + postDel === 3)) { // Duplicate record. diffs.splice(equalities[equalitiesLength - 1], 0, [DIFF_DELETE, lastequality]); // Change second copy to insert. diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT; equalitiesLength--; // Throw away the equality we just deleted; lastequality = null; if (preIns && preDel) { // No changes made which could affect previous entry, keep going. postIns = postDel = true; equalitiesLength = 0; } else { equalitiesLength--; // Throw away the previous equality. pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1; postIns = postDel = false; } changes = true; } } pointer++; } if (changes) { this.diffCleanupMerge(diffs); } }; /** * Convert a diff array into a pretty HTML report. * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples. * @param {integer} string to be beautified. * @return {string} HTML representation. */ DiffMatchPatch.prototype.diffPrettyHtml = function (diffs) { var op, data, x, html = []; for (x = 0; x < diffs.length; x++) { op = diffs[x][0]; // Operation (insert, delete, equal) data = diffs[x][1]; // Text of change. switch (op) { case DIFF_INSERT: html[x] = "<ins>" + escapeText(data) + "</ins>"; break; case DIFF_DELETE: html[x] = "<del>" + escapeText(data) + "</del>"; break; case DIFF_EQUAL: html[x] = "<span>" + escapeText(data) + "</span>"; break; } } return html.join(""); }; /** * Determine the common prefix of two strings. * @param {string} text1 First string. * @param {string} text2 Second string. * @return {number} The number of characters common to the start of each * string. */ DiffMatchPatch.prototype.diffCommonPrefix = function (text1, text2) { var pointermid, pointermax, pointermin, pointerstart; // Quick check for common null cases. if (!text1 || !text2 || text1.charAt(0) !== text2.charAt(0)) { return 0; } // Binary search. // Performance analysis: https://neil.fraser.name/news/2007/10/09/ pointermin = 0; pointermax = Math.min(text1.length, text2.length); pointermid = pointermax; pointerstart = 0; while (pointermin < pointermid) { if (text1.substring(pointerstart, pointermid) === text2.substring(pointerstart, pointermid)) { pointermin = pointermid; pointerstart = pointermin; } else { pointermax = pointermid; } pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin); } return pointermid; }; /** * Determine the common suffix of two strings. * @param {string} text1 First string. * @param {string} text2 Second string. * @return {number} The number of characters common to the end of each string. */ DiffMatchPatch.prototype.diffCommonSuffix = function (text1, text2) { var pointermid, pointermax, pointermin, pointerend; // Quick check for common null cases. if (!text1 || !text2 || text1.charAt(text1.length - 1) !== text2.charAt(text2.length - 1)) { return 0; } // Binary search. // Performance analysis: https://neil.fraser.name/news/2007/10/09/ pointermin = 0; pointermax = Math.min(text1.length, text2.length); pointermid = pointermax; pointerend = 0; while (pointermin < pointermid) { if (text1.substring(text1.length - pointermid, text1.length - pointerend) === text2.substring(text2.length - pointermid, text2.length - pointerend)) { pointermin = pointermid; pointerend = pointermin; } else { pointermax = pointermid; } pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin); } return pointermid; }; /** * Find the differences between two texts. Assumes that the texts do not * have any common prefix or suffix. * @param {string} text1 Old string to be diffed. * @param {string} text2 New string to be diffed. * @param {boolean} checklines Speedup flag. If false, then don't run a * line-level diff first to identify the changed areas. * If true, then run a faster, slightly less optimal diff. * @param {number} deadline Time when the diff should be complete by. * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples. * @private */ DiffMatchPatch.prototype.diffCompute = function (text1, text2, checklines, deadline) { var diffs, longtext, shorttext, i, hm, text1A, text2A, text1B, text2B, midCommon, diffsA, diffsB; if (!text1) { // Just add some text (speedup). return [[DIFF_INSERT, text2]]; } if (!text2) { // Just delete some text (speedup). return [[DIFF_DELETE, text1]]; } longtext = text1.length > text2.length ? text1 : text2; shorttext = text1.length > text2.length ? text2 : text1; i = longtext.indexOf(shorttext); if (i !== -1) { // Shorter text is inside the longer text (speedup). diffs = [[DIFF_INSERT, longtext.substring(0, i)], [DIFF_EQUAL, shorttext], [DIFF_INSERT, longtext.substring(i + shorttext.length)]]; // Swap insertions for deletions if diff is reversed. if (text1.length > text2.length) { diffs[0][0] = diffs[2][0] = DIFF_DELETE; } return diffs; } if (shorttext.length === 1) { // Single character string. // After the previous speedup, the character can't be an equality. return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]]; } // Check to see if the problem can be split in two. hm = this.diffHalfMatch(text1, text2); if (hm) { // A half-match was found, sort out the return data. text1A = hm[0]; text1B = hm[1]; text2A = hm[2]; text2B = hm[3]; midCommon = hm[4]; // Send both pairs off for separate processing. diffsA = this.DiffMain(text1A, text2A, checklines, deadline); diffsB = this.DiffMain(text1B, text2B, checklines, deadline); // Merge the results. return diffsA.concat([[DIFF_EQUAL, midCommon]], diffsB); } if (checklines && text1.length > 100 && text2.length > 100) { return this.diffLineMode(text1, text2, deadline); } return this.diffBisect(text1, text2, deadline); }; /** * Do the two texts share a substring which is at least half the length of the * longer text? * This speedup can produce non-minimal diffs. * @param {string} text1 First string. * @param {string} text2 Second string. * @return {Array.<string>} Five element Array, containing the prefix of * text1, the suffix of text1, the prefix of text2, the suffix of * text2 and the common middle. Or null if there was no match. * @private */ DiffMatchPatch.prototype.diffHalfMatch = function (text1, text2) { var longtext, shorttext, dmp, text1A, text2B, text2A, text1B, midCommon, hm1, hm2, hm; longtext = text1.length > text2.length ? text1 : text2; shorttext = text1.length > text2.length ? text2 : text1; if (longtext.length < 4 || shorttext.length * 2 < longtext.length) { return null; // Pointless. } dmp = this; // 'this' becomes 'window' in a closure. /** * Does a substring of shorttext exist within longtext such that the substring * is at least half the length of longtext? * Closure, but does not reference any external variables. * @param {string} longtext Longer string. * @param {string} shorttext Shorter string. * @param {number} i Start index of quarter length substring within longtext. * @return {Array.<string>} Five element Array, containing the prefix of * longtext, the suffix of longtext, the prefix of shorttext, the suffix * of shorttext and the common middle. Or null if there was no match. * @private */ function diffHalfMatchI(longtext, shorttext, i) { var seed, j, bestCommon, prefixLength, suffixLength, bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB; // Start with a 1/4 length substring at position i as a seed. seed = longtext.substring(i, i + Math.floor(longtext.length / 4)); j = -1; bestCommon = ""; while ((j = shorttext.indexOf(seed, j + 1)) !== -1) { prefixLength = dmp.diffCommonPrefix(longtext.substring(i), shorttext.substring(j)); suffixLength = dmp.diffCommonSuffix(longtext.substring(0, i), shorttext.substring(0, j)); if (bestCommon.length < suffixLength + prefixLength) { bestCommon = shorttext.substring(j - suffixLength, j) + shorttext.substring(j, j + prefixLength); bestLongtextA = longtext.substring(0, i - suffixLength); bestLongtextB = longtext.substring(i + prefixLength); bestShorttextA = shorttext.substring(0, j - suffixLength); bestShorttextB = shorttext.substring(j + prefixLength); } } if (bestCommon.length * 2 >= longtext.length) { return [bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB, bestCommon]; } else { return null; } } // First check if the second quarter is the seed for a half-match. hm1 = diffHalfMatchI(longtext, shorttext, Math.ceil(longtext.length / 4)); // Check again based on the third quarter. hm2 = diffHalfMatchI(longtext, shorttext, Math.ceil(longtext.length / 2)); if (!hm1 && !hm2) { return null; } else if (!hm2) { hm = hm1; } else if (!hm1) { hm = hm2; } else { // Both matched. Select the longest. hm = hm1[4].length > hm2[4].length ? hm1 : hm2; } // A half-match was found, sort out the return data. if (text1.length > text2.length) { text1A = hm[0]; text1B = hm[1]; text2A = hm[2]; text2B = hm[3]; } else { text2A = hm[0]; text2B = hm[1]; text1A = hm[2]; text1B = hm[3]; } midCommon = hm[4]; return [text1A, text1B, text2A, text2B, midCommon]; }; /** * Do a quick line-level diff on both strings, then rediff the parts for * greater accuracy. * This speedup can produce non-minimal diffs. * @param {string} text1 Old string to be diffed. * @param {string} text2 New string to be diffed. * @param {number} deadline Time when the diff should be complete by. * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples. * @private */ DiffMatchPatch.prototype.diffLineMode = function (text1, text2, deadline) { var a, diffs, linearray, pointer, countInsert, countDelete, textInsert, textDelete, j; // Scan the text on a line-by-line basis first. a = this.diffLinesToChars(text1, text2); text1 = a.chars1; text2 = a.chars2; linearray = a.lineArray; diffs = this.DiffMain(text1, text2, false, deadline); // Convert the diff back to original text. this.diffCharsToLines(diffs, linearray); // Eliminate freak matches (e.g. blank lines) this.diffCleanupSemantic(diffs); // Rediff any replacement blocks, this time character-by-character. // Add a dummy entry at the end. diffs.push([DIFF_EQUAL, ""]); pointer = 0; countDelete = 0; countInsert = 0; textDelete = ""; textInsert = ""; while (pointer < diffs.length) { switch (diffs[pointer][0]) { case DIFF_INSERT: countInsert++; textInsert += diffs[pointer][1]; break; case DIFF_DELETE: countDelete++; textDelete += diffs[pointer][1]; break; case DIFF_EQUAL: // Upon reaching an equality, check for prior redundancies. if (countDelete >= 1 && countInsert >= 1) { // Delete the offending records and add the merged ones. diffs.splice(pointer - countDelete - countInsert, countDelete + countInsert); pointer = pointer - countDelete - countInsert; a = this.DiffMain(textDelete, textInsert, false, deadline); for (j = a.length - 1; j >= 0; j--) { diffs.splice(pointer, 0, a[j]); } pointer = pointer + a.length; } countInsert = 0; countDelete = 0; textDelete = ""; textInsert = ""; break; } pointer++; } diffs.pop(); // Remove the dummy entry at the end. return diffs; }; /** * Find the 'middle snake' of a diff, split the problem in two * and return the recursively constructed diff. * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations. * @param {string} text1 Old string to be diffed. * @param {string} text2 New string to be diffed. * @param {number} deadline Time at which to bail if not yet complete. * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples. * @private */ DiffMatchPatch.prototype.diffBisect = function (text1, text2, deadline) { var text1Length, text2Length, maxD, vOffset, vLength, v1, v2, x, delta, front, k1start, k1end, k2start, k2end, k2Offset, k1Offset, x1, x2, y1, y2, d, k1, k2; // Cache the text lengths to prevent multiple calls. text1Length = text1.length; text2Length = text2.length; maxD = Math.ceil((text1Length + text2Length) / 2); vOffset = maxD; vLength = 2 * maxD; v1 = new Array(vLength); v2 = new Array(vLength); // Setting all elements to -1 is faster in Chrome & Firefox than mixing // integers and undefined. for (x = 0; x < vLength; x++) { v1[x] = -1; v2[x] = -1; } v1[vOffset + 1] = 0; v2[vOffset + 1] = 0; delta = text1Length - text2Length; // If the total number of characters is odd, then the front path will collide // with the reverse path. front = delta % 2 !== 0; // Offsets for start and end of k loop. // Prevents mapping of space beyond the grid. k1start = 0; k1end = 0; k2start = 0; k2end = 0; for (d = 0; d < maxD; d++) { // Bail out if deadline is reached. if (new Date().getTime() > deadline) { break; } // Walk the front path one step. for (k1 = -d + k1start; k1 <= d - k1end; k1 += 2) { k1Offset = vOffset + k1; if (k1 === -d || k1 !== d && v1[k1Offset - 1] < v1[k1Offset + 1]) { x1 = v1[k1Offset + 1]; } else { x1 = v1[k1Offset - 1] + 1; } y1 = x1 - k1; while (x1 < text1Length && y1 < text2Length && text1.charAt(x1) === text2.charAt(y1)) { x1++; y1++; } v1[k1Offset] = x1; if (x1 > text1Length) { // Ran off the right of the graph. k1end += 2; } else if (y1 > text2Length) { // Ran off the bottom of the graph. k1start += 2; } else if (front) { k2Offset = vOffset + delta - k1; if (k2Offset >= 0 && k2Offset < vLength && v2[k2Offset] !== -1) { // Mirror x2 onto top-left coordinate system. x2 = text1Length - v2[k2Offset]; if (x1 >= x2) { // Overlap detected. return this.diffBisectSplit(text1, text2, x1, y1, deadline); } } } } // Walk the reverse path one step. for (k2 = -d + k2start; k2 <= d - k2end; k2 += 2) { k2Offset = vOffset + k2; if (k2 === -d || k2 !== d && v2[k2Offset - 1] < v2[k2Offset + 1]) { x2 = v2[k2Offset + 1]; } else { x2 = v2[k2Offset - 1] + 1; } y2 = x2 - k2; while (x2 < text1Length && y2 < text2Length && text1.charAt(text1Length - x2 - 1) === text2.charAt(text2Length - y2 - 1)) { x2++; y2++; } v2[k2Offset] = x2; if (x2 > text1Length) { // Ran off the left of the graph. k2end += 2; } else if (y2 > text2Length) { // Ran off the top of the graph. k2start += 2; } else if (!front) { k1Offset = vOffset + delta - k2; if (k1Offset >= 0 && k1Offset < vLength && v1[k1Offset] !== -1) { x1 = v1[k1Offset]; y1 = vOffset + x1 - k1Offset; // Mirror x2 onto top-left coordinate system. x2 = text1Length - x2; if (x1 >= x2) { // Overlap detected. return this.diffBisectSplit(text1, text2, x1, y1, deadline); } } } } } // Diff took too long and hit the deadline or // number of diffs equals number of characters, no commonality at all. return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]]; }; /** * Given the location of the 'middle snake', split the diff in two parts * and recurse. * @param {string} text1 Old string to be diffed. * @param {string} text2 New string to be diffed. * @param {number} x Index of split point in text1. * @param {number} y Index of split point in text2. * @param {number} deadline Time at which to bail if not yet complete. * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples. * @private */ DiffMatchPatch.prototype.diffBisectSplit = function (text1, text2, x, y, deadline) { var text1a, text1b, text2a, text2b, diffs, diffsb; text1a = text1.substring(0, x); text2a = text2.substring(0, y); text1b = text1.substring(x); text2b = text2.substring(y); // Compute both diffs serially. diffs = this.DiffMain(text1a, text2a, false, deadline); diffsb = this.DiffMain(text1b, text2b, false, deadline); return diffs.concat(diffsb); }; /** * Reduce the number of edits by eliminating semantically trivial equalities. * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples. */ DiffMatchPatch.prototype.diffCleanupSemantic = function (diffs) { var changes, equalities, equalitiesLength, lastequality, pointer, lengthInsertions2, lengthDeletions2, lengthInsertions1, lengthDeletions1, deletion, insertion, overlapLength1, overlapLength2; changes = false; equalities = []; // Stack of indices where equalities are found. equalitiesLength = 0; // Keeping our own length var is faster in JS. /** @type {?string} */ lastequality = null; // Always equal to diffs[equalities[equalitiesLength - 1]][1] pointer = 0; // Index of current position. // Number of characters that changed prior to the equality. lengthInsertions1 = 0; lengthDeletions1 = 0; // Number of characters that changed after the equality. lengthInsertions2 = 0; lengthDeletions2 = 0; while (pointer < diffs.length) { if (diffs[pointer][0] === DIFF_EQUAL) { // Equality found. equalities[equalitiesLength++] = pointer; lengthInsertions1 = lengthInsertions2; lengthDeletions1 = lengthDeletions2; lengthInsertions2 = 0; lengthDeletions2 = 0; lastequality = diffs[pointer][1]; } else { // An insertion or deletion. if (diffs[pointer][0] === DIFF_INSERT) { lengthInsertions2 += diffs[pointer][1].length; } else { lengthDeletions2 += diffs[pointer][1].length; } // Eliminate an equality that is smaller or equal to the edits on both // sides of it. if (lastequality && lastequality.length <= Math.max(lengthInsertions1, lengthDeletions1) && lastequality.length <= Math.max(lengthInsertions2, lengthDeletions2)) { // Duplicate record. diffs.splice(equalities[equalitiesLength - 1], 0, [DIFF_DELETE, lastequality]); // Change second copy to insert. diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT; // Throw away the equality we just deleted. equalitiesLength--; // Throw away the previous equality (it needs to be reevaluated). equalitiesLength--; pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1; // Reset the counters. lengthInsertions1 = 0; lengthDeletions1 = 0; lengthInsertions2 = 0; lengthDeletions2 = 0; lastequality = null; changes = true; } } pointer++; } // Normalize the diff. if (changes) { this.diffCleanupMerge(diffs); } // Find any overlaps between deletions and insertions. // e.g: <del>abcxxx</del><ins>xxxdef</ins> // -> <del>abc</del>xxx<ins>def</ins> // e.g: <del>xxxabc</del><ins>defxxx</ins> // -> <ins>def</ins>xxx<del>abc</del> // Only extract an overlap if it is as big as the edit ahead or behind it. pointer = 1; while (pointer < diffs.length) { if (diffs[pointer - 1][0] === DIFF_DELETE && diffs[pointer][0] === DIFF_INSERT) { deletion = diffs[pointer - 1][1]; insertion = diffs[pointer][1]; overlapLength1 = this.diffCommonOverlap(deletion, insertion); overlapLength2 = this.diffCommonOverlap(insertion, deletion); if (overlapLength1 >= overlapLength2) { if (overlapLength1 >= deletion.length / 2 || overlapLength1 >= insertion.length / 2) { // Overlap found. Insert an equality and trim the surrounding edits. diffs.splice(pointer, 0, [DIFF_EQUAL, insertion.substring(0, overlapLength1)]); diffs[pointer - 1][1] = deletion.substring(0, deletion.length - overlapLength1); diffs[pointer + 1][1] = insertion.substring(overlapLength1); pointer++; } } else { if (overlapLength2 >= deletion.length / 2 || overlapLength2 >= insertion.length / 2) { // Reverse overlap found. // Insert an equality and swap and trim the surrounding edits. diffs.splice(pointer, 0, [DIFF_EQUAL, deletion.substring(0, overlapLength2)]); diffs[pointer - 1][0] = DIFF_INSERT; diffs[pointer - 1][1] = insertion.substring(0, insertion.length - overlapLength2); diffs[pointer + 1][0] = DIFF_DELETE; diffs[pointer + 1][1] = deletion.substring(overlapLength2); pointer++; } } pointer++; } pointer++; } }; /** * Determine if the suffix of one string is the prefix of another. * @param {string} text1 First string. * @param {string} text2 Second string. * @return {number} The number of characters common to the end of the first * string and the start of the second string. * @private */ DiffMatchPatch.prototype.diffCommonOverlap = function (text1, text2) { var text1Length, text2Length, textLength, best, length, pattern, found; // Cache the text lengths to prevent multiple calls. text1Length = text1.length; text2Length = text2.length; // Eliminate the null case. if (text1Length === 0 || text2Length === 0) { return 0; } // Truncate the longer string. if (text1Length > text2Length) { text1 = text1.substring(text1Length - text2Length); } else if (text1Length < text2Length) { text2 = text2.substring(0, text1Length); } textLength = Math.min(text1Length, text2Length); // Quick check for the worst case. if (text1 === text2) { return textLength; } // Start by looking for a single character match // and increase length until no match is found. // Performance analysis: https://neil.fraser.name/news/2010/11/04/ best = 0; length = 1; while (true) { pattern = text1.substring(textLength - length); found = text2.indexOf(pattern); if (found === -1) { return best; } length += found; if (found === 0 || text1.substring(textLength - length) === text2.substring(0, length)) { best = length; length++; } } }; /** * Split two texts into an array of strings. Reduce the texts to a string of * hashes where each Unicode character represents one line. * @param {string} text1 First string. * @param {string} text2 Second string. * @return {{chars1: string, chars2: string, lineArray: !Array.<string>}} * An object containing the encoded text1, the encoded text2 and * the array of unique strings. * The zeroth element of the array of unique strings is intentionally blank. * @private */ DiffMatchPatch.prototype.diffLinesToChars = function (text1, text2) { var lineArray, lineHash, chars1, chars2; lineArray = []; // E.g. lineArray[4] === 'Hello\n' lineHash = {}; // E.g. lineHash['Hello\n'] === 4 // '\x00' is a valid character, but various debuggers don't like it. // So we'll insert a junk entry to avoid generating a null character. lineArray[0] = ""; /** * Split a text into an array of strings. Reduce the texts to a string of * hashes where each Unicode character represents one line. * Modifies linearray and linehash through being a closure. * @param {string} text String to encode. * @return {string} Encoded string. * @private */ function diffLinesToCharsMunge(text) { var chars, lineStart, lineEnd, lineArrayLength, line; chars = ""; // Walk the text, pulling out a substring for each line. // text.split('\n') would would temporarily double our memory footprint. // Modifying text would create many large strings to garbage collect. lineStart = 0; lineEnd = -1; // Keeping our own length variable is faster than looking it up. lineArrayLength = lineArray.length; while (lineEnd < text.length - 1) { lineEnd = text.indexOf("\n", lineStart); if (lineEnd === -1) { lineEnd = text.length - 1; } line = text.substring(lineStart, lineEnd + 1); lineStart = lineEnd + 1; if (lineHash.hasOwnProperty ? lineHash.hasOwnProperty(line) : lineHash[line] !== undefined) { chars += String.fromCharCode(lineHash[line]); } else { chars += String.fromCharCode(lineArrayLength); lineHash[line] = lineArrayLength; lineArray[lineArrayLength++] = line; } } return chars; } chars1 = diffLinesToCharsMunge(text1); chars2 = diffLinesToCharsMunge(text2); return { chars1: chars1, chars2: chars2, lineArray: lineArray }; }; /** * Rehydrate the text in a diff from a string of line hashes to real lines of * text. * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples. * @param {!Array.<string>} lineArray Array of unique strings. * @private */ DiffMatchPatch.prototype.diffCharsToLines = function (diffs, lineArray) { var x, chars, text, y; for (x = 0; x < diffs.length; x++) { chars = diffs[x][1]; text = []; for (y = 0; y < chars.length; y++) { text[y] = lineArray[chars.charCodeAt(y)]; } diffs[x][1] = text.join(""); } }; /** * Reorder and merge like edit sections. Merge equalities. * Any edit section can move as long as it doesn't cross an equality. * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples. */ DiffMatchPatch.prototype.diffCleanupMerge = function (diffs) { var pointer, countDelete, countInsert, textInsert, textDelete, commonlength, changes, diffPointer, position; diffs.push([DIFF_EQUAL, ""]); // Add a dummy entry at the end. pointer = 0; countDelete = 0; countInsert = 0; textDelete = ""; textInsert = ""; while (pointer < diffs.length) { switch (diffs[pointer][0]) { case DIFF_INSERT: countInsert++; textInsert += diffs[pointer][1]; pointer++; break; case DIFF_DELETE: countDelete++; textDelete += diffs[pointer][1]; pointer++; break; case DIFF_EQUAL: // Upon reaching an equality, check for prior redundancies. if (countDelete + countInsert > 1) { if (countDelete !== 0 && countInsert !== 0) { // Factor out any common prefixes. commonlength = this.diffCommonPrefix(textInsert, textDelete); if (commonlength !== 0) { if (pointer - countDelete - countInsert > 0 && diffs[pointer - countDelete - countInsert - 1][0] === DIFF_EQUAL) { diffs[pointer - countDelete - countInsert - 1][1] += textInsert.substring(0, commonlength); } else { diffs.splice(0, 0, [DIFF_EQUAL, textInsert.substring(0, commonlength)]); pointer++; } textInsert = textInsert.substring(commonlength); textDelete = textDelete.substring(commonlength); } // Factor out any common suffixies. commonlength = this.diffCommonSuffix(textInsert, textDelete); if (commonlength !== 0) { diffs[pointer][1] = textInsert.substring(textInsert.length - commonlength) + diffs[pointer][1]; textInsert = textInsert.substring(0, textInsert.length - commonlength); textDelete = textDelete.substring(0, textDelete.length - commonlength); } } // Delete the offending records and add the merged ones. if (countDelete === 0) { diffs.splice(pointer - countInsert, countDelete + countInsert, [DIFF_INSERT, textInsert]); } else if (countInsert === 0) { diffs.splice(pointer - countDelete, countDelete + countInsert, [DIFF_DELETE, textDelete]); } else { diffs.splice(pointer - countDelete - countInsert, countDelete + countInsert, [DIFF_DELETE, textDelete], [DIFF_INSERT, textInsert]); } pointer = pointer - countDelete - countInsert + (countDelete ? 1 : 0) + (countInsert ? 1 : 0) + 1; } else if (pointer !== 0 && diffs[pointer - 1][0] === DIFF_EQUAL) { // Merge this equality with the previous one. diffs[pointer - 1][1] += diffs[pointer][1]; diffs.splice(pointer, 1); } else { pointer++; } countInsert = 0; countDelete = 0; textDelete = ""; textInsert = ""; break; } } if (diffs[diffs.length - 1][1] === "") { diffs.pop(); // Remove the dummy entry at the end. } // Second pass: look for single edits surrounded on both sides by equalities // which can be shifted sideways to eliminate an equality. // e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC changes = false; pointer = 1; // Intentionally ignore the first and last element (don't need checking). while (pointer < diffs.length - 1) { if (diffs[pointer - 1][0] === DIFF_EQUAL && diffs[pointer + 1][0] === DIFF_EQUAL) { diffPointer = diffs[pointer][1]; position = diffPointer.substring(diffPointer.length - diffs[pointer - 1][1].length); // This is a single edit surrounded by equalities. if (position === diffs[pointer - 1][1]) { // Shift the edit over the previous equality. diffs[pointer][1] = diffs[pointer - 1][1] + diffs[pointer][1].substring(0, diffs[pointer][1].length - diffs[pointer - 1][1].length); diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1]; diffs.splice(pointer - 1, 1); changes = true; } else if (diffPointer.substring(0, diffs[pointer + 1][1].length) === diffs[pointer + 1][1]) { // Shift the edit over the next equality. diffs[pointer - 1][1] += diffs[pointer + 1][1]; diffs[pointer][1] = diffs[pointer][1].substring(diffs[pointer + 1][1].length) + diffs[pointer + 1][1]; diffs.splice(pointer + 1, 1); changes = true; } } pointer++; } // If shifts were made, the diff needs reordering and another shift sweep. if (changes) { this.diffCleanupMerge(diffs); } }; return function (o, n) { var diff, output, text; diff = new DiffMatchPatch(); output = diff.DiffMain(o, n); diff.diffCleanupEfficiency(output); text = diff.diffPrettyHtml(output); return text; }; }(); }((function() { return this; }()))); assets/js/tablesaw/dependencies/qunit.css 0000644 00000020012 14736103451 0014563 0 ustar 00 /*! * QUnit 2.1.1 * https://qunitjs.com/ * * Copyright jQuery Foundation and other contributors * Released under the MIT license * https://jquery.org/license * * Date: 2017-01-06T01:52Z */ /** Font Family and Sizes */ #qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult { font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif; } #qunit-testrunner-toolbar, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } #qunit-tests { font-size: smaller; } /** Resets */ #qunit-tests, #qunit-header, #qunit-banner, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter { margin: 0; padding: 0; } /** Header (excluding toolbar) */ #qunit-header { padding: 0.5em 0 0.5em 1em; color: #8699A4; background-color: #0D3349; font-size: 1.5em; line-height: 1em; font-weight: 400; border-radius: 5px 5px 0 0; } #qunit-header a { text-decoration: none; color: #C2CCD1; } #qunit-header a:hover, #qunit-header a:focus { color: #FFF; } #qunit-banner { height: 5px; } #qunit-filteredTest { padding: 0.5em 1em 0.5em 1em; color: #366097; background-color: #F4FF77; } #qunit-userAgent { padding: 0.5em 1em 0.5em 1em; color: #FFF; background-color: #2B81AF; text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; } /** Toolbar */ #qunit-testrunner-toolbar { padding: 0.5em 1em 0.5em 1em; color: #5E740B; background-color: #EEE; } #qunit-testrunner-toolbar .clearfix { height: 0; clear: both; } #qunit-testrunner-toolbar label { display: inline-block; } #qunit-testrunner-toolbar input[type=checkbox], #qunit-testrunner-toolbar input[type=radio] { margin: 3px; vertical-align: -2px; } #qunit-testrunner-toolbar input[type=text] { box-sizing: border-box; height: 1.6em; } .qunit-url-config, .qunit-filter, #qunit-modulefilter { display: inline-block; line-height: 2.1em; } .qunit-filter, #qunit-modulefilter { float: right; position: relative; margin-left: 1em; } .qunit-url-config label { margin-right: 0.5em; } #qunit-modulefilter-search { box-sizing: border-box; width: 400px; } #qunit-modulefilter-search-container:after { position: absolute; right: 0.3em; content: "\25bc"; color: black; } #qunit-modulefilter-dropdown { /* align with #qunit-modulefilter-search */ box-sizing: border-box; width: 400px; position: absolute; right: 0; top: 50%; margin-top: 0.8em; border: 1px solid #D3D3D3; border-top: none; border-radius: 0 0 .25em .25em; color: #000; background-color: #F5F5F5; z-index: 99; } #qunit-modulefilter-dropdown a { color: inherit; text-decoration: none; } #qunit-modulefilter-dropdown .clickable.checked { font-weight: bold; color: #000; background-color: #D2E0E6; } #qunit-modulefilter-dropdown .clickable:hover { color: #FFF; background-color: #0D3349; } #qunit-modulefilter-actions { display: block; overflow: auto; /* align with #qunit-modulefilter-dropdown-list */ font: smaller/1.5em sans-serif; } #qunit-modulefilter-dropdown #qunit-modulefilter-actions > * { box-sizing: border-box; max-height: 2.8em; display: block; padding: 0.4em; } #qunit-modulefilter-dropdown #qunit-modulefilter-actions > button { float: right; font: inherit; } #qunit-modulefilter-dropdown #qunit-modulefilter-actions > :last-child { /* insert padding to align with checkbox margins */ padding-left: 3px; } #qunit-modulefilter-dropdown-list { max-height: 200px; overflow-y: auto; margin: 0; border-top: 2px groove threedhighlight; padding: 0.4em 0 0; font: smaller/1.5em sans-serif; } #qunit-modulefilter-dropdown-list li { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } #qunit-modulefilter-dropdown-list .clickable { display: block; padding-left: 0.15em; } /** Tests: Pass/Fail */ #qunit-tests { list-style-position: inside; } #qunit-tests li { padding: 0.4em 1em 0.4em 1em; border-bottom: 1px solid #FFF; list-style-position: inside; } #qunit-tests > li { display: none; } #qunit-tests li.running, #qunit-tests li.pass, #qunit-tests li.fail, #qunit-tests li.skipped, #qunit-tests li.aborted { display: list-item; } #qunit-tests.hidepass { position: relative; } #qunit-tests.hidepass li.running, #qunit-tests.hidepass li.pass { visibility: hidden; position: absolute; width: 0; height: 0; padding: 0; border: 0; margin: 0; } #qunit-tests li strong { cursor: pointer; } #qunit-tests li.skipped strong { cursor: default; } #qunit-tests li a { padding: 0.5em; color: #C2CCD1; text-decoration: none; } #qunit-tests li p a { padding: 0.25em; color: #6B6464; } #qunit-tests li a:hover, #qunit-tests li a:focus { color: #000; } #qunit-tests li .runtime { float: right; font-size: smaller; } .qunit-assert-list { margin-top: 0.5em; padding: 0.5em; background-color: #FFF; border-radius: 5px; } .qunit-source { margin: 0.6em 0 0.3em; } .qunit-collapsed { display: none; } #qunit-tests table { border-collapse: collapse; margin-top: 0.2em; } #qunit-tests th { text-align: right; vertical-align: top; padding: 0 0.5em 0 0; } #qunit-tests td { vertical-align: top; } #qunit-tests pre { margin: 0; white-space: pre-wrap; word-wrap: break-word; } #qunit-tests del { color: #374E0C; background-color: #E0F2BE; text-decoration: none; } #qunit-tests ins { color: #500; background-color: #FFCACA; text-decoration: none; } /*** Test Counts */ #qunit-tests b.counts { color: #000; } #qunit-tests b.passed { color: #5E740B; } #qunit-tests b.failed { color: #710909; } #qunit-tests li li { padding: 5px; background-color: #FFF; border-bottom: none; list-style-position: inside; } /*** Passing Styles */ #qunit-tests li li.pass { color: #3C510C; background-color: #FFF; border-left: 10px solid #C6E746; } #qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; } #qunit-tests .pass .test-name { color: #366097; } #qunit-tests .pass .test-actual, #qunit-tests .pass .test-expected { color: #999; } #qunit-banner.qunit-pass { background-color: #C6E746; } /*** Failing Styles */ #qunit-tests li li.fail { color: #710909; background-color: #FFF; border-left: 10px solid #EE5757; white-space: pre; } #qunit-tests > li:last-child { border-radius: 0 0 5px 5px; } #qunit-tests .fail { color: #000; background-color: #EE5757; } #qunit-tests .fail .test-name, #qunit-tests .fail .module-name { color: #000; } #qunit-tests .fail .test-actual { color: #EE5757; } #qunit-tests .fail .test-expected { color: #008000; } #qunit-banner.qunit-fail { background-color: #EE5757; } /*** Aborted tests */ #qunit-tests .aborted { color: #000; background-color: orange; } /*** Skipped tests */ #qunit-tests .skipped { background-color: #EBECE9; } #qunit-tests .qunit-skipped-label { background-color: #F4FF77; display: inline-block; font-style: normal; color: #366097; line-height: 1.8em; padding: 0 0.5em; margin: -0.4em 0.4em -0.4em 0; } /** Result */ #qunit-testresult { color: #2B81AF; background-color: #D2E0E6; border-bottom: 1px solid #FFF; } #qunit-testresult .clearfix { height: 0; clear: both; } #qunit-testresult .module-name { font-weight: 700; } #qunit-testresult-display { padding: 0.5em 1em 0.5em 1em; width: 85%; float:left; } #qunit-testresult-controls { padding: 0.5em 1em 0.5em 1em; width: 10%; float:left; } /** Fixture */ #qunit-fixture { position: absolute; top: -10000px; left: -10000px; width: 1000px; height: 1000px; } assets/js/tablesaw/dependencies/naturalsort.js 0000644 00000004062 14736103451 0015634 0 ustar 00 /* * Natural Sort algorithm for Javascript - Version 0.7 - Released under MIT license * Author: Jim Palmer (based on chunking idea from Dave Koelle) */ /*jshint unused:false */ module.exports = function naturalSort (a, b) { "use strict"; var re = /(^([+\-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)?$|^0x[0-9a-f]+$|\d+)/gi, sre = /(^[ ]*|[ ]*$)/g, dre = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/, hre = /^0x[0-9a-f]+$/i, ore = /^0/, i = function(s) { return naturalSort.insensitive && ('' + s).toLowerCase() || '' + s; }, // convert all to strings strip whitespace x = i(a).replace(sre, '') || '', y = i(b).replace(sre, '') || '', // chunk/tokenize xN = x.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'), yN = y.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'), // numeric, hex or date detection xD = parseInt(x.match(hre), 16) || (xN.length !== 1 && x.match(dre) && Date.parse(x)), yD = parseInt(y.match(hre), 16) || xD && y.match(dre) && Date.parse(y) || null, oFxNcL, oFyNcL; // first try and sort Hex codes or Dates if (yD) { if ( xD < yD ) { return -1; } else if ( xD > yD ) { return 1; } } // natural sorting through split numeric strings and default strings for(var cLoc=0, numS=Math.max(xN.length, yN.length); cLoc < numS; cLoc++) { // find floats not starting with '0', string or 0 if not defined (Clint Priest) oFxNcL = !(xN[cLoc] || '').match(ore) && parseFloat(xN[cLoc]) || xN[cLoc] || 0; oFyNcL = !(yN[cLoc] || '').match(ore) && parseFloat(yN[cLoc]) || yN[cLoc] || 0; // handle numeric vs string comparison - number < string - (Kyle Adams) if (isNaN(oFxNcL) !== isNaN(oFyNcL)) { return (isNaN(oFxNcL)) ? 1 : -1; } // rely on string comparison if different types - i.e. '02' < 2 != '02' < '2' else if (typeof oFxNcL !== typeof oFyNcL) { oFxNcL += ''; oFyNcL += ''; } if (oFxNcL < oFyNcL) { return -1; } if (oFxNcL > oFyNcL) { return 1; } } return 0; }; assets/js/tablesaw/dependencies/jquery.js 0000644 00001035646 14736103451 0014612 0 ustar 00 /*! * jQuery JavaScript Library v3.1.1 * https://jquery.com/ * * Includes Sizzle.js * https://sizzlejs.com/ * * Copyright jQuery Foundation and other contributors * Released under the MIT license * https://jquery.org/license * * Date: 2016-09-22T22:30Z */ ( function( global, factory ) { "use strict"; if ( typeof module === "object" && typeof module.exports === "object" ) { // For CommonJS and CommonJS-like environments where a proper `window` // is present, execute the factory and get jQuery. // For environments that do not have a `window` with a `document` // (such as Node.js), expose a factory as module.exports. // This accentuates the need for the creation of a real `window`. // e.g. var jQuery = require("jquery")(window); // See ticket #14549 for more info. module.exports = global.document ? factory( global, true ) : function( w ) { if ( !w.document ) { throw new Error( "jQuery requires a window with a document" ); } return factory( w ); }; } else { factory( global ); } // Pass this if window is not defined yet } )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) { // Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1 // throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode // arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common // enough that all such attempts are guarded in a try block. "use strict"; var arr = []; var document = window.document; var getProto = Object.getPrototypeOf; var slice = arr.slice; var concat = arr.concat; var push = arr.push; var indexOf = arr.indexOf; var class2type = {}; var toString = class2type.toString; var hasOwn = class2type.hasOwnProperty; var fnToString = hasOwn.toString; var ObjectFunctionString = fnToString.call( Object ); var support = {}; function DOMEval( code, doc ) { doc = doc || document; var script = doc.createElement( "script" ); script.text = code; doc.head.appendChild( script ).parentNode.removeChild( script ); } /* global Symbol */ // Defining this global in .eslintrc.json would create a danger of using the global // unguarded in another place, it seems safer to define global only for this module var version = "3.1.1", // Define a local copy of jQuery jQuery = function( selector, context ) { // The jQuery object is actually just the init constructor 'enhanced' // Need init if jQuery is called (just allow error to be thrown if not included) return new jQuery.fn.init( selector, context ); }, // Support: Android <=4.0 only // Make sure we trim BOM and NBSP rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, // Matches dashed string for camelizing rmsPrefix = /^-ms-/, rdashAlpha = /-([a-z])/g, // Used by jQuery.camelCase as callback to replace() fcamelCase = function( all, letter ) { return letter.toUpperCase(); }; jQuery.fn = jQuery.prototype = { // The current version of jQuery being used jquery: version, constructor: jQuery, // The default length of a jQuery object is 0 length: 0, toArray: function() { return slice.call( this ); }, // Get the Nth element in the matched element set OR // Get the whole matched element set as a clean array get: function( num ) { // Return all the elements in a clean array if ( num == null ) { return slice.call( this ); } // Return just the one element from the set return num < 0 ? this[ num + this.length ] : this[ num ]; }, // Take an array of elements and push it onto the stack // (returning the new matched element set) pushStack: function( elems ) { // Build a new jQuery matched element set var ret = jQuery.merge( this.constructor(), elems ); // Add the old object onto the stack (as a reference) ret.prevObject = this; // Return the newly-formed element set return ret; }, // Execute a callback for every element in the matched set. each: function( callback ) { return jQuery.each( this, callback ); }, map: function( callback ) { return this.pushStack( jQuery.map( this, function( elem, i ) { return callback.call( elem, i, elem ); } ) ); }, slice: function() { return this.pushStack( slice.apply( this, arguments ) ); }, first: function() { return this.eq( 0 ); }, last: function() { return this.eq( -1 ); }, eq: function( i ) { var len = this.length, j = +i + ( i < 0 ? len : 0 ); return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); }, end: function() { return this.prevObject || this.constructor(); }, // For internal use only. // Behaves like an Array's method, not like a jQuery method. push: push, sort: arr.sort, splice: arr.splice }; jQuery.extend = jQuery.fn.extend = function() { var options, name, src, copy, copyIsArray, clone, target = arguments[ 0 ] || {}, i = 1, length = arguments.length, deep = false; // Handle a deep copy situation if ( typeof target === "boolean" ) { deep = target; // Skip the boolean and the target target = arguments[ i ] || {}; i++; } // 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 ( i === length ) { target = this; i--; } for ( ; i < length; i++ ) { // Only deal with non-null/undefined values if ( ( options = arguments[ i ] ) != null ) { // Extend the base object for ( name in options ) { src = target[ name ]; copy = options[ name ]; // Prevent never-ending loop if ( target === copy ) { continue; } // Recurse if we're merging plain objects or arrays if ( deep && copy && ( jQuery.isPlainObject( copy ) || ( copyIsArray = jQuery.isArray( copy ) ) ) ) { if ( copyIsArray ) { copyIsArray = false; clone = src && jQuery.isArray( src ) ? src : []; } else { clone = src && jQuery.isPlainObject( src ) ? src : {}; } // Never move original objects, clone them target[ name ] = jQuery.extend( deep, clone, copy ); // Don't bring in undefined values } else if ( copy !== undefined ) { target[ name ] = copy; } } } } // Return the modified object return target; }; jQuery.extend( { // Unique for each copy of jQuery on the page expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), // Assume jQuery is ready without the ready module isReady: true, error: function( msg ) { throw new Error( msg ); }, noop: function() {}, isFunction: function( obj ) { return jQuery.type( obj ) === "function"; }, isArray: Array.isArray, isWindow: function( obj ) { return obj != null && obj === obj.window; }, isNumeric: function( obj ) { // As of jQuery 3.0, isNumeric is limited to // strings and numbers (primitives or objects) // that can be coerced to finite numbers (gh-2662) var type = jQuery.type( obj ); return ( type === "number" || type === "string" ) && // parseFloat NaNs numeric-cast false positives ("") // ...but misinterprets leading-number strings, particularly hex literals ("0x...") // subtraction forces infinities to NaN !isNaN( obj - parseFloat( obj ) ); }, isPlainObject: function( obj ) { var proto, Ctor; // Detect obvious negatives // Use toString instead of jQuery.type to catch host objects if ( !obj || toString.call( obj ) !== "[object Object]" ) { return false; } proto = getProto( obj ); // Objects with no prototype (e.g., `Object.create( null )`) are plain if ( !proto ) { return true; } // Objects with prototype are plain iff they were constructed by a global Object function Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; }, isEmptyObject: function( obj ) { /* eslint-disable no-unused-vars */ // See https://github.com/eslint/eslint/issues/6125 var name; for ( name in obj ) { return false; } return true; }, type: function( obj ) { if ( obj == null ) { return obj + ""; } // Support: Android <=2.3 only (functionish RegExp) return typeof obj === "object" || typeof obj === "function" ? class2type[ toString.call( obj ) ] || "object" : typeof obj; }, // Evaluates a script in a global context globalEval: function( code ) { DOMEval( code ); }, // Convert dashed to camelCase; used by the css and data modules // Support: IE <=9 - 11, Edge 12 - 13 // Microsoft forgot to hump their vendor prefix (#9572) camelCase: function( string ) { return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); }, nodeName: function( elem, name ) { return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); }, each: function( obj, callback ) { var length, i = 0; if ( isArrayLike( obj ) ) { length = obj.length; for ( ; i < length; i++ ) { if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { break; } } } else { for ( i in obj ) { if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { break; } } } return obj; }, // Support: Android <=4.0 only trim: function( text ) { return text == null ? "" : ( text + "" ).replace( rtrim, "" ); }, // results is for internal usage only makeArray: function( arr, results ) { var ret = results || []; if ( arr != null ) { if ( isArrayLike( Object( arr ) ) ) { jQuery.merge( ret, typeof arr === "string" ? [ arr ] : arr ); } else { push.call( ret, arr ); } } return ret; }, inArray: function( elem, arr, i ) { return arr == null ? -1 : indexOf.call( arr, elem, i ); }, // Support: Android <=4.0 only, PhantomJS 1 only // push.apply(_, arraylike) throws on ancient WebKit merge: function( first, second ) { var len = +second.length, j = 0, i = first.length; for ( ; j < len; j++ ) { first[ i++ ] = second[ j ]; } first.length = i; return first; }, grep: function( elems, callback, invert ) { var callbackInverse, matches = [], i = 0, length = elems.length, callbackExpect = !invert; // Go through the array, only saving the items // that pass the validator function for ( ; i < length; i++ ) { callbackInverse = !callback( elems[ i ], i ); if ( callbackInverse !== callbackExpect ) { matches.push( elems[ i ] ); } } return matches; }, // arg is for internal usage only map: function( elems, callback, arg ) { var length, value, i = 0, ret = []; // Go through the array, translating each of the items to their new values if ( isArrayLike( elems ) ) { length = elems.length; for ( ; i < length; i++ ) { value = callback( elems[ i ], i, arg ); if ( value != null ) { ret.push( value ); } } // Go through every key on the object, } else { for ( i in elems ) { value = callback( elems[ i ], i, arg ); if ( value != null ) { ret.push( value ); } } } // Flatten any nested arrays return concat.apply( [], ret ); }, // A global GUID counter for objects guid: 1, // Bind a function to a context, optionally partially applying any // arguments. proxy: function( fn, context ) { var tmp, args, proxy; if ( typeof context === "string" ) { tmp = fn[ context ]; context = fn; fn = tmp; } // Quick check to determine if target is callable, in the spec // this throws a TypeError, but we will just return undefined. if ( !jQuery.isFunction( fn ) ) { return undefined; } // Simulated bind args = slice.call( arguments, 2 ); proxy = function() { return fn.apply( context || this, args.concat( slice.call( arguments ) ) ); }; // Set the guid of unique handler to the same of original handler, so it can be removed proxy.guid = fn.guid = fn.guid || jQuery.guid++; return proxy; }, now: Date.now, // jQuery.support is not used in Core but other projects attach their // properties to it so it needs to exist. support: support } ); if ( typeof Symbol === "function" ) { jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; } // Populate the class2type map jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), function( i, name ) { class2type[ "[object " + name + "]" ] = name.toLowerCase(); } ); function isArrayLike( obj ) { // Support: real iOS 8.2 only (not reproducible in simulator) // `in` check used to prevent JIT error (gh-2145) // hasOwn isn't used here due to false negatives // regarding Nodelist length in IE var length = !!obj && "length" in obj && obj.length, type = jQuery.type( obj ); if ( type === "function" || jQuery.isWindow( obj ) ) { return false; } return type === "array" || length === 0 || typeof length === "number" && length > 0 && ( length - 1 ) in obj; } var Sizzle = /*! * Sizzle CSS Selector Engine v2.3.3 * https://sizzlejs.com/ * * Copyright jQuery Foundation and other contributors * Released under the MIT license * http://jquery.org/license * * Date: 2016-08-08 */ (function( window ) { var i, support, Expr, getText, isXML, tokenize, compile, select, outermostContext, sortInput, hasDuplicate, // Local document vars setDocument, document, docElem, documentIsHTML, rbuggyQSA, rbuggyMatches, matches, contains, // Instance-specific data expando = "sizzle" + 1 * new Date(), preferredDoc = window.document, dirruns = 0, done = 0, classCache = createCache(), tokenCache = createCache(), compilerCache = createCache(), sortOrder = function( a, b ) { if ( a === b ) { hasDuplicate = true; } return 0; }, // Instance methods hasOwn = ({}).hasOwnProperty, arr = [], pop = arr.pop, push_native = arr.push, push = arr.push, slice = arr.slice, // Use a stripped-down indexOf as it's faster than native // https://jsperf.com/thor-indexof-vs-for/5 indexOf = function( list, elem ) { var i = 0, len = list.length; for ( ; i < len; i++ ) { if ( list[i] === elem ) { return i; } } return -1; }, booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", // Regular expressions // http://www.w3.org/TR/css3-selectors/#whitespace whitespace = "[\\x20\\t\\r\\n\\f]", // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier identifier = "(?:\\\\.|[\\w-]|[^\0-\\xa0])+", // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + // Operator (capture 2) "*([*^$|!~]?=)" + whitespace + // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + "*\\]", pseudos = ":(" + identifier + ")(?:\\((" + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: // 1. quoted (capture 3; capture 4 or capture 5) "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + // 2. simple (capture 6) "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + // 3. anything else (capture 2) ".*" + ")\\)|)", // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter rwhitespace = new RegExp( whitespace + "+", "g" ), rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ), rpseudo = new RegExp( pseudos ), ridentifier = new RegExp( "^" + identifier + "$" ), matchExpr = { "ID": new RegExp( "^#(" + identifier + ")" ), "CLASS": new RegExp( "^\\.(" + identifier + ")" ), "TAG": new RegExp( "^(" + identifier + "|[*])" ), "ATTR": new RegExp( "^" + attributes ), "PSEUDO": new RegExp( "^" + pseudos ), "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), // For use in libraries implementing .is() // We use this for POS matching in `select` "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) }, rinputs = /^(?:input|select|textarea|button)$/i, rheader = /^h\d$/i, rnative = /^[^{]+\{\s*\[native \w/, // Easily-parseable/retrievable ID or TAG or CLASS selectors rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, rsibling = /[+~]/, // CSS escapes // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), funescape = function( _, escaped, escapedWhitespace ) { var high = "0x" + escaped - 0x10000; // NaN means non-codepoint // Support: Firefox<24 // Workaround erroneous numeric interpretation of +"0x" return high !== high || escapedWhitespace ? escaped : high < 0 ? // BMP codepoint String.fromCharCode( high + 0x10000 ) : // Supplemental Plane codepoint (surrogate pair) String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); }, // CSS string/identifier serialization // https://drafts.csswg.org/cssom/#common-serializing-idioms rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g, fcssescape = function( ch, asCodePoint ) { if ( asCodePoint ) { // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER if ( ch === "\0" ) { return "\uFFFD"; } // Control characters and (dependent upon position) numbers get escaped as code points return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; } // Other potentially-special ASCII characters get backslash-escaped return "\\" + ch; }, // Used for iframes // See setDocument() // Removing the function wrapper causes a "Permission Denied" // error in IE unloadHandler = function() { setDocument(); }, disabledAncestor = addCombinator( function( elem ) { return elem.disabled === true && ("form" in elem || "label" in elem); }, { dir: "parentNode", next: "legend" } ); // Optimize for push.apply( _, NodeList ) try { push.apply( (arr = slice.call( preferredDoc.childNodes )), preferredDoc.childNodes ); // Support: Android<4.0 // Detect silently failing push.apply arr[ preferredDoc.childNodes.length ].nodeType; } catch ( e ) { push = { apply: arr.length ? // Leverage slice if possible function( target, els ) { push_native.apply( target, slice.call(els) ); } : // Support: IE<9 // Otherwise append directly function( target, els ) { var j = target.length, i = 0; // Can't trust NodeList.length while ( (target[j++] = els[i++]) ) {} target.length = j - 1; } }; } function Sizzle( selector, context, results, seed ) { var m, i, elem, nid, match, groups, newSelector, newContext = context && context.ownerDocument, // nodeType defaults to 9, since context defaults to document nodeType = context ? context.nodeType : 9; results = results || []; // Return early from calls with invalid selector or context if ( typeof selector !== "string" || !selector || nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { return results; } // Try to shortcut find operations (as opposed to filters) in HTML documents if ( !seed ) { if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { setDocument( context ); } context = context || document; if ( documentIsHTML ) { // If the selector is sufficiently simple, try using a "get*By*" DOM method // (excepting DocumentFragment context, where the methods don't exist) if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) { // ID selector if ( (m = match[1]) ) { // Document context if ( nodeType === 9 ) { if ( (elem = context.getElementById( m )) ) { // Support: IE, Opera, Webkit // TODO: identify versions // getElementById can match elements by name instead of ID if ( elem.id === m ) { results.push( elem ); return results; } } else { return results; } // Element context } else { // Support: IE, Opera, Webkit // TODO: identify versions // getElementById can match elements by name instead of ID if ( newContext && (elem = newContext.getElementById( m )) && contains( context, elem ) && elem.id === m ) { results.push( elem ); return results; } } // Type selector } else if ( match[2] ) { push.apply( results, context.getElementsByTagName( selector ) ); return results; // Class selector } else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) { push.apply( results, context.getElementsByClassName( m ) ); return results; } } // Take advantage of querySelectorAll if ( support.qsa && !compilerCache[ selector + " " ] && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { if ( nodeType !== 1 ) { newContext = context; newSelector = selector; // qSA looks outside Element context, which is not what we want // Thanks to Andrew Dupont for this workaround technique // Support: IE <=8 // Exclude object elements } else if ( context.nodeName.toLowerCase() !== "object" ) { // Capture the context ID, setting it first if necessary if ( (nid = context.getAttribute( "id" )) ) { nid = nid.replace( rcssescape, fcssescape ); } else { context.setAttribute( "id", (nid = expando) ); } // Prefix every selector in the list groups = tokenize( selector ); i = groups.length; while ( i-- ) { groups[i] = "#" + nid + " " + toSelector( groups[i] ); } newSelector = groups.join( "," ); // Expand context for sibling selectors newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context; } if ( newSelector ) { try { push.apply( results, newContext.querySelectorAll( newSelector ) ); return results; } catch ( qsaError ) { } finally { if ( nid === expando ) { context.removeAttribute( "id" ); } } } } } } // All others return select( selector.replace( rtrim, "$1" ), context, results, seed ); } /** * Create key-value caches of limited size * @returns {function(string, object)} Returns the Object data after storing it on itself with * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) * deleting the oldest entry */ function createCache() { var keys = []; function cache( key, value ) { // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) if ( keys.push( key + " " ) > Expr.cacheLength ) { // Only keep the most recent entries delete cache[ keys.shift() ]; } return (cache[ key + " " ] = value); } return cache; } /** * Mark a function for special use by Sizzle * @param {Function} fn The function to mark */ function markFunction( fn ) { fn[ expando ] = true; return fn; } /** * Support testing using an element * @param {Function} fn Passed the created element and returns a boolean result */ function assert( fn ) { var el = document.createElement("fieldset"); try { return !!fn( el ); } catch (e) { return false; } finally { // Remove from its parent by default if ( el.parentNode ) { el.parentNode.removeChild( el ); } // release memory in IE el = null; } } /** * Adds the same handler for all of the specified attrs * @param {String} attrs Pipe-separated list of attributes * @param {Function} handler The method that will be applied */ function addHandle( attrs, handler ) { var arr = attrs.split("|"), i = arr.length; while ( i-- ) { Expr.attrHandle[ arr[i] ] = handler; } } /** * Checks document order of two siblings * @param {Element} a * @param {Element} b * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b */ function siblingCheck( a, b ) { var cur = b && a, diff = cur && a.nodeType === 1 && b.nodeType === 1 && a.sourceIndex - b.sourceIndex; // Use IE sourceIndex if available on both nodes if ( diff ) { return diff; } // Check if b follows a if ( cur ) { while ( (cur = cur.nextSibling) ) { if ( cur === b ) { return -1; } } } return a ? 1 : -1; } /** * Returns a function to use in pseudos for input types * @param {String} type */ function createInputPseudo( type ) { return function( elem ) { var name = elem.nodeName.toLowerCase(); return name === "input" && elem.type === type; }; } /** * Returns a function to use in pseudos for buttons * @param {String} type */ function createButtonPseudo( type ) { return function( elem ) { var name = elem.nodeName.toLowerCase(); return (name === "input" || name === "button") && elem.type === type; }; } /** * Returns a function to use in pseudos for :enabled/:disabled * @param {Boolean} disabled true for :disabled; false for :enabled */ function createDisabledPseudo( disabled ) { // Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable return function( elem ) { // Only certain elements can match :enabled or :disabled // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled if ( "form" in elem ) { // Check for inherited disabledness on relevant non-disabled elements: // * listed form-associated elements in a disabled fieldset // https://html.spec.whatwg.org/multipage/forms.html#category-listed // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled // * option elements in a disabled optgroup // https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled // All such elements have a "form" property. if ( elem.parentNode && elem.disabled === false ) { // Option elements defer to a parent optgroup if present if ( "label" in elem ) { if ( "label" in elem.parentNode ) { return elem.parentNode.disabled === disabled; } else { return elem.disabled === disabled; } } // Support: IE 6 - 11 // Use the isDisabled shortcut property to check for disabled fieldset ancestors return elem.isDisabled === disabled || // Where there is no isDisabled, check manually /* jshint -W018 */ elem.isDisabled !== !disabled && disabledAncestor( elem ) === disabled; } return elem.disabled === disabled; // Try to winnow out elements that can't be disabled before trusting the disabled property. // Some victims get caught in our net (label, legend, menu, track), but it shouldn't // even exist on them, let alone have a boolean value. } else if ( "label" in elem ) { return elem.disabled === disabled; } // Remaining elements are neither :enabled nor :disabled return false; }; } /** * Returns a function to use in pseudos for positionals * @param {Function} fn */ function createPositionalPseudo( fn ) { return markFunction(function( argument ) { argument = +argument; return markFunction(function( seed, matches ) { var j, matchIndexes = fn( [], seed.length, argument ), i = matchIndexes.length; // Match elements found at the specified indexes while ( i-- ) { if ( seed[ (j = matchIndexes[i]) ] ) { seed[j] = !(matches[j] = seed[j]); } } }); }); } /** * Checks a node for validity as a Sizzle context * @param {Element|Object=} context * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value */ function testContext( context ) { return context && typeof context.getElementsByTagName !== "undefined" && context; } // Expose support vars for convenience support = Sizzle.support = {}; /** * Detects XML nodes * @param {Element|Object} elem An element or a document * @returns {Boolean} True iff elem is a non-HTML XML node */ isXML = Sizzle.isXML = function( elem ) { // documentElement is verified for cases where it doesn't yet exist // (such as loading iframes in IE - #4833) var documentElement = elem && (elem.ownerDocument || elem).documentElement; return documentElement ? documentElement.nodeName !== "HTML" : false; }; /** * Sets document-related variables once based on the current document * @param {Element|Object} [doc] An element or document object to use to set the document * @returns {Object} Returns the current document */ setDocument = Sizzle.setDocument = function( node ) { var hasCompare, subWindow, doc = node ? node.ownerDocument || node : preferredDoc; // Return early if doc is invalid or already selected if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { return document; } // Update global variables document = doc; docElem = document.documentElement; documentIsHTML = !isXML( document ); // Support: IE 9-11, Edge // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936) if ( preferredDoc !== document && (subWindow = document.defaultView) && subWindow.top !== subWindow ) { // Support: IE 11, Edge if ( subWindow.addEventListener ) { subWindow.addEventListener( "unload", unloadHandler, false ); // Support: IE 9 - 10 only } else if ( subWindow.attachEvent ) { subWindow.attachEvent( "onunload", unloadHandler ); } } /* Attributes ---------------------------------------------------------------------- */ // Support: IE<8 // Verify that getAttribute really returns attributes and not properties // (excepting IE8 booleans) support.attributes = assert(function( el ) { el.className = "i"; return !el.getAttribute("className"); }); /* getElement(s)By* ---------------------------------------------------------------------- */ // Check if getElementsByTagName("*") returns only elements support.getElementsByTagName = assert(function( el ) { el.appendChild( document.createComment("") ); return !el.getElementsByTagName("*").length; }); // Support: IE<9 support.getElementsByClassName = rnative.test( document.getElementsByClassName ); // Support: IE<10 // Check if getElementById returns elements by name // The broken getElementById methods don't pick up programmatically-set names, // so use a roundabout getElementsByName test support.getById = assert(function( el ) { docElem.appendChild( el ).id = expando; return !document.getElementsByName || !document.getElementsByName( expando ).length; }); // ID filter and find if ( support.getById ) { Expr.filter["ID"] = function( id ) { var attrId = id.replace( runescape, funescape ); return function( elem ) { return elem.getAttribute("id") === attrId; }; }; Expr.find["ID"] = function( id, context ) { if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { var elem = context.getElementById( id ); return elem ? [ elem ] : []; } }; } else { Expr.filter["ID"] = function( id ) { var attrId = id.replace( runescape, funescape ); return function( elem ) { var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); return node && node.value === attrId; }; }; // Support: IE 6 - 7 only // getElementById is not reliable as a find shortcut Expr.find["ID"] = function( id, context ) { if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { var node, i, elems, elem = context.getElementById( id ); if ( elem ) { // Verify the id attribute node = elem.getAttributeNode("id"); if ( node && node.value === id ) { return [ elem ]; } // Fall back on getElementsByName elems = context.getElementsByName( id ); i = 0; while ( (elem = elems[i++]) ) { node = elem.getAttributeNode("id"); if ( node && node.value === id ) { return [ elem ]; } } } return []; } }; } // Tag Expr.find["TAG"] = support.getElementsByTagName ? function( tag, context ) { if ( typeof context.getElementsByTagName !== "undefined" ) { return context.getElementsByTagName( tag ); // DocumentFragment nodes don't have gEBTN } else if ( support.qsa ) { return context.querySelectorAll( tag ); } } : function( tag, context ) { var elem, tmp = [], i = 0, // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too results = context.getElementsByTagName( tag ); // Filter out possible comments if ( tag === "*" ) { while ( (elem = results[i++]) ) { if ( elem.nodeType === 1 ) { tmp.push( elem ); } } return tmp; } return results; }; // Class Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { return context.getElementsByClassName( className ); } }; /* QSA/matchesSelector ---------------------------------------------------------------------- */ // QSA and matchesSelector support // matchesSelector(:active) reports false when true (IE9/Opera 11.5) rbuggyMatches = []; // qSa(:focus) reports false when true (Chrome 21) // We allow this because of a bug in IE8/9 that throws an error // whenever `document.activeElement` is accessed on an iframe // So, we allow :focus to pass through QSA all the time to avoid the IE error // See https://bugs.jquery.com/ticket/13378 rbuggyQSA = []; if ( (support.qsa = rnative.test( document.querySelectorAll )) ) { // Build QSA regex // Regex strategy adopted from Diego Perini assert(function( el ) { // Select is set to empty string on purpose // This is to test IE's treatment of not explicitly // setting a boolean content attribute, // since its presence should be enough // https://bugs.jquery.com/ticket/12359 docElem.appendChild( el ).innerHTML = "<a id='" + expando + "'></a>" + "<select id='" + expando + "-\r\\' msallowcapture=''>" + "<option selected=''></option></select>"; // Support: IE8, Opera 11-12.16 // Nothing should be selected when empty strings follow ^= or $= or *= // The test attribute must be unknown in Opera but "safe" for WinRT // https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section if ( el.querySelectorAll("[msallowcapture^='']").length ) { rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); } // Support: IE8 // Boolean attributes and "value" are not treated correctly if ( !el.querySelectorAll("[selected]").length ) { rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); } // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+ if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) { rbuggyQSA.push("~="); } // Webkit/Opera - :checked should return selected option elements // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked // IE8 throws error here and will not see later tests if ( !el.querySelectorAll(":checked").length ) { rbuggyQSA.push(":checked"); } // Support: Safari 8+, iOS 8+ // https://bugs.webkit.org/show_bug.cgi?id=136851 // In-page `selector#id sibling-combinator selector` fails if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) { rbuggyQSA.push(".#.+[+~]"); } }); assert(function( el ) { el.innerHTML = "<a href='' disabled='disabled'></a>" + "<select disabled='disabled'><option/></select>"; // Support: Windows 8 Native Apps // The type and name attributes are restricted during .innerHTML assignment var input = document.createElement("input"); input.setAttribute( "type", "hidden" ); el.appendChild( input ).setAttribute( "name", "D" ); // Support: IE8 // Enforce case-sensitivity of name attribute if ( el.querySelectorAll("[name=d]").length ) { rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); } // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) // IE8 throws error here and will not see later tests if ( el.querySelectorAll(":enabled").length !== 2 ) { rbuggyQSA.push( ":enabled", ":disabled" ); } // Support: IE9-11+ // IE's :disabled selector does not pick up the children of disabled fieldsets docElem.appendChild( el ).disabled = true; if ( el.querySelectorAll(":disabled").length !== 2 ) { rbuggyQSA.push( ":enabled", ":disabled" ); } // Opera 10-11 does not throw on post-comma invalid pseudos el.querySelectorAll("*,:x"); rbuggyQSA.push(",.*:"); }); } if ( (support.matchesSelector = rnative.test( (matches = docElem.matches || docElem.webkitMatchesSelector || docElem.mozMatchesSelector || docElem.oMatchesSelector || docElem.msMatchesSelector) )) ) { assert(function( el ) { // Check to see if it's possible to do matchesSelector // on a disconnected node (IE 9) support.disconnectedMatch = matches.call( el, "*" ); // This should fail with an exception // Gecko does not error, returns false instead matches.call( el, "[s!='']:x" ); rbuggyMatches.push( "!=", pseudos ); }); } rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); /* Contains ---------------------------------------------------------------------- */ hasCompare = rnative.test( docElem.compareDocumentPosition ); // Element contains another // Purposefully self-exclusive // As in, an element does not contain itself contains = hasCompare || rnative.test( docElem.contains ) ? function( a, b ) { var adown = a.nodeType === 9 ? a.documentElement : a, bup = b && b.parentNode; return a === bup || !!( bup && bup.nodeType === 1 && ( adown.contains ? adown.contains( bup ) : a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 )); } : function( a, b ) { if ( b ) { while ( (b = b.parentNode) ) { if ( b === a ) { return true; } } } return false; }; /* Sorting ---------------------------------------------------------------------- */ // Document order sorting sortOrder = hasCompare ? function( a, b ) { // Flag for duplicate removal if ( a === b ) { hasDuplicate = true; return 0; } // Sort on method existence if only one input has compareDocumentPosition var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; if ( compare ) { return compare; } // Calculate position if both inputs belong to the same document compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ? a.compareDocumentPosition( b ) : // Otherwise we know they are disconnected 1; // Disconnected nodes if ( compare & 1 || (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { // Choose the first element that is related to our preferred document if ( a === document || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { return -1; } if ( b === document || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { return 1; } // Maintain original order return sortInput ? ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : 0; } return compare & 4 ? -1 : 1; } : function( a, b ) { // Exit early if the nodes are identical if ( a === b ) { hasDuplicate = true; return 0; } var cur, i = 0, aup = a.parentNode, bup = b.parentNode, ap = [ a ], bp = [ b ]; // Parentless nodes are either documents or disconnected if ( !aup || !bup ) { return a === document ? -1 : b === document ? 1 : aup ? -1 : bup ? 1 : sortInput ? ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : 0; // If the nodes are siblings, we can do a quick check } else if ( aup === bup ) { return siblingCheck( a, b ); } // Otherwise we need full lists of their ancestors for comparison cur = a; while ( (cur = cur.parentNode) ) { ap.unshift( cur ); } cur = b; while ( (cur = cur.parentNode) ) { bp.unshift( cur ); } // Walk down the tree looking for a discrepancy while ( ap[i] === bp[i] ) { i++; } return i ? // Do a sibling check if the nodes have a common ancestor siblingCheck( ap[i], bp[i] ) : // Otherwise nodes in our document sort first ap[i] === preferredDoc ? -1 : bp[i] === preferredDoc ? 1 : 0; }; return document; }; Sizzle.matches = function( expr, elements ) { return Sizzle( expr, null, null, elements ); }; Sizzle.matchesSelector = function( elem, expr ) { // Set document vars if needed if ( ( elem.ownerDocument || elem ) !== document ) { setDocument( elem ); } // Make sure that attribute selectors are quoted expr = expr.replace( rattributeQuotes, "='$1']" ); if ( support.matchesSelector && documentIsHTML && !compilerCache[ expr + " " ] && ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { try { var ret = matches.call( elem, expr ); // IE 9's matchesSelector returns false on disconnected nodes if ( ret || support.disconnectedMatch || // As well, disconnected nodes are said to be in a document // fragment in IE 9 elem.document && elem.document.nodeType !== 11 ) { return ret; } } catch (e) {} } return Sizzle( expr, document, null, [ elem ] ).length > 0; }; Sizzle.contains = function( context, elem ) { // Set document vars if needed if ( ( context.ownerDocument || context ) !== document ) { setDocument( context ); } return contains( context, elem ); }; Sizzle.attr = function( elem, name ) { // Set document vars if needed if ( ( elem.ownerDocument || elem ) !== document ) { setDocument( elem ); } var fn = Expr.attrHandle[ name.toLowerCase() ], // Don't get fooled by Object.prototype properties (jQuery #13807) val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? fn( elem, name, !documentIsHTML ) : undefined; return val !== undefined ? val : support.attributes || !documentIsHTML ? elem.getAttribute( name ) : (val = elem.getAttributeNode(name)) && val.specified ? val.value : null; }; Sizzle.escape = function( sel ) { return (sel + "").replace( rcssescape, fcssescape ); }; Sizzle.error = function( msg ) { throw new Error( "Syntax error, unrecognized expression: " + msg ); }; /** * Document sorting and removing duplicates * @param {ArrayLike} results */ Sizzle.uniqueSort = function( results ) { var elem, duplicates = [], j = 0, i = 0; // Unless we *know* we can detect duplicates, assume their presence hasDuplicate = !support.detectDuplicates; sortInput = !support.sortStable && results.slice( 0 ); results.sort( sortOrder ); if ( hasDuplicate ) { while ( (elem = results[i++]) ) { if ( elem === results[ i ] ) { j = duplicates.push( i ); } } while ( j-- ) { results.splice( duplicates[ j ], 1 ); } } // Clear input after sorting to release objects // See https://github.com/jquery/sizzle/pull/225 sortInput = null; return results; }; /** * Utility function for retrieving the text value of an array of DOM nodes * @param {Array|Element} elem */ getText = Sizzle.getText = function( elem ) { var node, ret = "", i = 0, nodeType = elem.nodeType; if ( !nodeType ) { // If no nodeType, this is expected to be an array while ( (node = elem[i++]) ) { // Do not traverse comment nodes ret += getText( node ); } } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { // Use textContent for elements // innerText usage removed for consistency of new lines (jQuery #11153) if ( typeof elem.textContent === "string" ) { return elem.textContent; } else { // Traverse its children for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { ret += getText( elem ); } } } else if ( nodeType === 3 || nodeType === 4 ) { return elem.nodeValue; } // Do not include comment or processing instruction nodes return ret; }; Expr = Sizzle.selectors = { // Can be adjusted by the user cacheLength: 50, createPseudo: markFunction, match: matchExpr, attrHandle: {}, find: {}, relative: { ">": { dir: "parentNode", first: true }, " ": { dir: "parentNode" }, "+": { dir: "previousSibling", first: true }, "~": { dir: "previousSibling" } }, preFilter: { "ATTR": function( match ) { match[1] = match[1].replace( runescape, funescape ); // Move the given value to match[3] whether quoted or unquoted match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape ); if ( match[2] === "~=" ) { match[3] = " " + match[3] + " "; } return match.slice( 0, 4 ); }, "CHILD": function( match ) { /* matches from matchExpr["CHILD"] 1 type (only|nth|...) 2 what (child|of-type) 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) 4 xn-component of xn+y argument ([+-]?\d*n|) 5 sign of xn-component 6 x of xn-component 7 sign of y-component 8 y of y-component */ match[1] = match[1].toLowerCase(); if ( match[1].slice( 0, 3 ) === "nth" ) { // nth-* requires argument if ( !match[3] ) { Sizzle.error( match[0] ); } // numeric x and y parameters for Expr.filter.CHILD // remember that false/true cast respectively to 0/1 match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); // other types prohibit arguments } else if ( match[3] ) { Sizzle.error( match[0] ); } return match; }, "PSEUDO": function( match ) { var excess, unquoted = !match[6] && match[2]; if ( matchExpr["CHILD"].test( match[0] ) ) { return null; } // Accept quoted arguments as-is if ( match[3] ) { match[2] = match[4] || match[5] || ""; // Strip excess characters from unquoted arguments } else if ( unquoted && rpseudo.test( unquoted ) && // Get excess from tokenize (recursively) (excess = tokenize( unquoted, true )) && // advance to the next closing parenthesis (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { // excess is a negative index match[0] = match[0].slice( 0, excess ); match[2] = unquoted.slice( 0, excess ); } // Return only captures needed by the pseudo filter method (type and argument) return match.slice( 0, 3 ); } }, filter: { "TAG": function( nodeNameSelector ) { var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); return nodeNameSelector === "*" ? function() { return true; } : function( elem ) { return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; }; }, "CLASS": function( className ) { var pattern = classCache[ className + " " ]; return pattern || (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && classCache( className, function( elem ) { return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "" ); }); }, "ATTR": function( name, operator, check ) { return function( elem ) { var result = Sizzle.attr( elem, name ); if ( result == null ) { return operator === "!="; } if ( !operator ) { return true; } result += ""; return operator === "=" ? result === check : operator === "!=" ? result !== check : operator === "^=" ? check && result.indexOf( check ) === 0 : operator === "*=" ? check && result.indexOf( check ) > -1 : operator === "$=" ? check && result.slice( -check.length ) === check : operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : false; }; }, "CHILD": function( type, what, argument, first, last ) { var simple = type.slice( 0, 3 ) !== "nth", forward = type.slice( -4 ) !== "last", ofType = what === "of-type"; return first === 1 && last === 0 ? // Shortcut for :nth-*(n) function( elem ) { return !!elem.parentNode; } : function( elem, context, xml ) { var cache, uniqueCache, outerCache, node, nodeIndex, start, dir = simple !== forward ? "nextSibling" : "previousSibling", parent = elem.parentNode, name = ofType && elem.nodeName.toLowerCase(), useCache = !xml && !ofType, diff = false; if ( parent ) { // :(first|last|only)-(child|of-type) if ( simple ) { while ( dir ) { node = elem; while ( (node = node[ dir ]) ) { if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) { return false; } } // Reverse direction for :only-* (if we haven't yet done so) start = dir = type === "only" && !start && "nextSibling"; } return true; } start = [ forward ? parent.firstChild : parent.lastChild ]; // non-xml :nth-child(...) stores cache data on `parent` if ( forward && useCache ) { // Seek `elem` from a previously-cached index // ...in a gzip-friendly way node = parent; outerCache = node[ expando ] || (node[ expando ] = {}); // Support: IE <9 only // Defend against cloned attroperties (jQuery gh-1709) uniqueCache = outerCache[ node.uniqueID ] || (outerCache[ node.uniqueID ] = {}); cache = uniqueCache[ type ] || []; nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; diff = nodeIndex && cache[ 2 ]; node = nodeIndex && parent.childNodes[ nodeIndex ]; while ( (node = ++nodeIndex && node && node[ dir ] || // Fallback to seeking `elem` from the start (diff = nodeIndex = 0) || start.pop()) ) { // When found, cache indexes on `parent` and break if ( node.nodeType === 1 && ++diff && node === elem ) { uniqueCache[ type ] = [ dirruns, nodeIndex, diff ]; break; } } } else { // Use previously-cached element index if available if ( useCache ) { // ...in a gzip-friendly way node = elem; outerCache = node[ expando ] || (node[ expando ] = {}); // Support: IE <9 only // Defend against cloned attroperties (jQuery gh-1709) uniqueCache = outerCache[ node.uniqueID ] || (outerCache[ node.uniqueID ] = {}); cache = uniqueCache[ type ] || []; nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; diff = nodeIndex; } // xml :nth-child(...) // or :nth-last-child(...) or :nth(-last)?-of-type(...) if ( diff === false ) { // Use the same loop as above to seek `elem` from the start while ( (node = ++nodeIndex && node && node[ dir ] || (diff = nodeIndex = 0) || start.pop()) ) { if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) { // Cache the index of each encountered element if ( useCache ) { outerCache = node[ expando ] || (node[ expando ] = {}); // Support: IE <9 only // Defend against cloned attroperties (jQuery gh-1709) uniqueCache = outerCache[ node.uniqueID ] || (outerCache[ node.uniqueID ] = {}); uniqueCache[ type ] = [ dirruns, diff ]; } if ( node === elem ) { break; } } } } } // Incorporate the offset, then check against cycle size diff -= last; return diff === first || ( diff % first === 0 && diff / first >= 0 ); } }; }, "PSEUDO": function( pseudo, argument ) { // pseudo-class names are case-insensitive // http://www.w3.org/TR/selectors/#pseudo-classes // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters // Remember that setFilters inherits from pseudos var args, fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || Sizzle.error( "unsupported pseudo: " + pseudo ); // The user may use createPseudo to indicate that // arguments are needed to create the filter function // just as Sizzle does if ( fn[ expando ] ) { return fn( argument ); } // But maintain support for old signatures if ( fn.length > 1 ) { args = [ pseudo, pseudo, "", argument ]; return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? markFunction(function( seed, matches ) { var idx, matched = fn( seed, argument ), i = matched.length; while ( i-- ) { idx = indexOf( seed, matched[i] ); seed[ idx ] = !( matches[ idx ] = matched[i] ); } }) : function( elem ) { return fn( elem, 0, args ); }; } return fn; } }, pseudos: { // Potentially complex pseudos "not": markFunction(function( selector ) { // Trim the selector passed to compile // to avoid treating leading and trailing // spaces as combinators var input = [], results = [], matcher = compile( selector.replace( rtrim, "$1" ) ); return matcher[ expando ] ? markFunction(function( seed, matches, context, xml ) { var elem, unmatched = matcher( seed, null, xml, [] ), i = seed.length; // Match elements unmatched by `matcher` while ( i-- ) { if ( (elem = unmatched[i]) ) { seed[i] = !(matches[i] = elem); } } }) : function( elem, context, xml ) { input[0] = elem; matcher( input, null, xml, results ); // Don't keep the element (issue #299) input[0] = null; return !results.pop(); }; }), "has": markFunction(function( selector ) { return function( elem ) { return Sizzle( selector, elem ).length > 0; }; }), "contains": markFunction(function( text ) { text = text.replace( runescape, funescape ); return function( elem ) { return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; }; }), // "Whether an element is represented by a :lang() selector // is based solely on the element's language value // being equal to the identifier C, // or beginning with the identifier C immediately followed by "-". // The matching of C against the element's language value is performed case-insensitively. // The identifier C does not have to be a valid language name." // http://www.w3.org/TR/selectors/#lang-pseudo "lang": markFunction( function( lang ) { // lang value must be a valid identifier if ( !ridentifier.test(lang || "") ) { Sizzle.error( "unsupported lang: " + lang ); } lang = lang.replace( runescape, funescape ).toLowerCase(); return function( elem ) { var elemLang; do { if ( (elemLang = documentIsHTML ? elem.lang : elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { elemLang = elemLang.toLowerCase(); return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; } } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); return false; }; }), // Miscellaneous "target": function( elem ) { var hash = window.location && window.location.hash; return hash && hash.slice( 1 ) === elem.id; }, "root": function( elem ) { return elem === docElem; }, "focus": function( elem ) { return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); }, // Boolean properties "enabled": createDisabledPseudo( false ), "disabled": createDisabledPseudo( true ), "checked": function( elem ) { // In CSS3, :checked should return both checked and selected elements // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked var nodeName = elem.nodeName.toLowerCase(); return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); }, "selected": function( elem ) { // Accessing this property makes selected-by-default // options in Safari work properly if ( elem.parentNode ) { elem.parentNode.selectedIndex; } return elem.selected === true; }, // Contents "empty": function( elem ) { // http://www.w3.org/TR/selectors/#empty-pseudo // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), // but not by others (comment: 8; processing instruction: 7; etc.) // nodeType < 6 works because attributes (2) do not appear as children for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { if ( elem.nodeType < 6 ) { return false; } } return true; }, "parent": function( elem ) { return !Expr.pseudos["empty"]( elem ); }, // Element/input types "header": function( elem ) { return rheader.test( elem.nodeName ); }, "input": function( elem ) { return rinputs.test( elem.nodeName ); }, "button": function( elem ) { var name = elem.nodeName.toLowerCase(); return name === "input" && elem.type === "button" || name === "button"; }, "text": function( elem ) { var attr; return elem.nodeName.toLowerCase() === "input" && elem.type === "text" && // Support: IE<8 // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" ); }, // Position-in-collection "first": createPositionalPseudo(function() { return [ 0 ]; }), "last": createPositionalPseudo(function( matchIndexes, length ) { return [ length - 1 ]; }), "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { return [ argument < 0 ? argument + length : argument ]; }), "even": createPositionalPseudo(function( matchIndexes, length ) { var i = 0; for ( ; i < length; i += 2 ) { matchIndexes.push( i ); } return matchIndexes; }), "odd": createPositionalPseudo(function( matchIndexes, length ) { var i = 1; for ( ; i < length; i += 2 ) { matchIndexes.push( i ); } return matchIndexes; }), "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { var i = argument < 0 ? argument + length : argument; for ( ; --i >= 0; ) { matchIndexes.push( i ); } return matchIndexes; }), "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { var i = argument < 0 ? argument + length : argument; for ( ; ++i < length; ) { matchIndexes.push( i ); } return matchIndexes; }) } }; Expr.pseudos["nth"] = Expr.pseudos["eq"]; // Add button/input type pseudos for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { Expr.pseudos[ i ] = createInputPseudo( i ); } for ( i in { submit: true, reset: true } ) { Expr.pseudos[ i ] = createButtonPseudo( i ); } // Easy API for creating new setFilters function setFilters() {} setFilters.prototype = Expr.filters = Expr.pseudos; Expr.setFilters = new setFilters(); tokenize = Sizzle.tokenize = function( selector, parseOnly ) { var matched, match, tokens, type, soFar, groups, preFilters, cached = tokenCache[ selector + " " ]; if ( cached ) { return parseOnly ? 0 : cached.slice( 0 ); } soFar = selector; groups = []; preFilters = Expr.preFilter; while ( soFar ) { // Comma and first run if ( !matched || (match = rcomma.exec( soFar )) ) { if ( match ) { // Don't consume trailing commas as valid soFar = soFar.slice( match[0].length ) || soFar; } groups.push( (tokens = []) ); } matched = false; // Combinators if ( (match = rcombinators.exec( soFar )) ) { matched = match.shift(); tokens.push({ value: matched, // Cast descendant combinators to space type: match[0].replace( rtrim, " " ) }); soFar = soFar.slice( matched.length ); } // Filters for ( type in Expr.filter ) { if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || (match = preFilters[ type ]( match ))) ) { matched = match.shift(); tokens.push({ value: matched, type: type, matches: match }); soFar = soFar.slice( matched.length ); } } if ( !matched ) { break; } } // Return the length of the invalid excess // if we're just parsing // Otherwise, throw an error or return tokens return parseOnly ? soFar.length : soFar ? Sizzle.error( selector ) : // Cache the tokens tokenCache( selector, groups ).slice( 0 ); }; function toSelector( tokens ) { var i = 0, len = tokens.length, selector = ""; for ( ; i < len; i++ ) { selector += tokens[i].value; } return selector; } function addCombinator( matcher, combinator, base ) { var dir = combinator.dir, skip = combinator.next, key = skip || dir, checkNonElements = base && key === "parentNode", doneName = done++; return combinator.first ? // Check against closest ancestor/preceding element function( elem, context, xml ) { while ( (elem = elem[ dir ]) ) { if ( elem.nodeType === 1 || checkNonElements ) { return matcher( elem, context, xml ); } } return false; } : // Check against all ancestor/preceding elements function( elem, context, xml ) { var oldCache, uniqueCache, outerCache, newCache = [ dirruns, doneName ]; // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching if ( xml ) { while ( (elem = elem[ dir ]) ) { if ( elem.nodeType === 1 || checkNonElements ) { if ( matcher( elem, context, xml ) ) { return true; } } } } else { while ( (elem = elem[ dir ]) ) { if ( elem.nodeType === 1 || checkNonElements ) { outerCache = elem[ expando ] || (elem[ expando ] = {}); // Support: IE <9 only // Defend against cloned attroperties (jQuery gh-1709) uniqueCache = outerCache[ elem.uniqueID ] || (outerCache[ elem.uniqueID ] = {}); if ( skip && skip === elem.nodeName.toLowerCase() ) { elem = elem[ dir ] || elem; } else if ( (oldCache = uniqueCache[ key ]) && oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { // Assign to newCache so results back-propagate to previous elements return (newCache[ 2 ] = oldCache[ 2 ]); } else { // Reuse newcache so results back-propagate to previous elements uniqueCache[ key ] = newCache; // A match means we're done; a fail means we have to keep checking if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { return true; } } } } } return false; }; } function elementMatcher( matchers ) { return matchers.length > 1 ? function( elem, context, xml ) { var i = matchers.length; while ( i-- ) { if ( !matchers[i]( elem, context, xml ) ) { return false; } } return true; } : matchers[0]; } function multipleContexts( selector, contexts, results ) { var i = 0, len = contexts.length; for ( ; i < len; i++ ) { Sizzle( selector, contexts[i], results ); } return results; } function condense( unmatched, map, filter, context, xml ) { var elem, newUnmatched = [], i = 0, len = unmatched.length, mapped = map != null; for ( ; i < len; i++ ) { if ( (elem = unmatched[i]) ) { if ( !filter || filter( elem, context, xml ) ) { newUnmatched.push( elem ); if ( mapped ) { map.push( i ); } } } } return newUnmatched; } function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { if ( postFilter && !postFilter[ expando ] ) { postFilter = setMatcher( postFilter ); } if ( postFinder && !postFinder[ expando ] ) { postFinder = setMatcher( postFinder, postSelector ); } return markFunction(function( seed, results, context, xml ) { var temp, i, elem, preMap = [], postMap = [], preexisting = results.length, // Get initial elements from seed or context elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), // Prefilter to get matcher input, preserving a map for seed-results synchronization matcherIn = preFilter && ( seed || !selector ) ? condense( elems, preMap, preFilter, context, xml ) : elems, matcherOut = matcher ? // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, postFinder || ( seed ? preFilter : preexisting || postFilter ) ? // ...intermediate processing is necessary [] : // ...otherwise use results directly results : matcherIn; // Find primary matches if ( matcher ) { matcher( matcherIn, matcherOut, context, xml ); } // Apply postFilter if ( postFilter ) { temp = condense( matcherOut, postMap ); postFilter( temp, [], context, xml ); // Un-match failing elements by moving them back to matcherIn i = temp.length; while ( i-- ) { if ( (elem = temp[i]) ) { matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); } } } if ( seed ) { if ( postFinder || preFilter ) { if ( postFinder ) { // Get the final matcherOut by condensing this intermediate into postFinder contexts temp = []; i = matcherOut.length; while ( i-- ) { if ( (elem = matcherOut[i]) ) { // Restore matcherIn since elem is not yet a final match temp.push( (matcherIn[i] = elem) ); } } postFinder( null, (matcherOut = []), temp, xml ); } // Move matched elements from seed to results to keep them synchronized i = matcherOut.length; while ( i-- ) { if ( (elem = matcherOut[i]) && (temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) { seed[temp] = !(results[temp] = elem); } } } // Add elements to results, through postFinder if defined } else { matcherOut = condense( matcherOut === results ? matcherOut.splice( preexisting, matcherOut.length ) : matcherOut ); if ( postFinder ) { postFinder( null, results, matcherOut, xml ); } else { push.apply( results, matcherOut ); } } }); } function matcherFromTokens( tokens ) { var checkContext, matcher, j, len = tokens.length, leadingRelative = Expr.relative[ tokens[0].type ], implicitRelative = leadingRelative || Expr.relative[" "], i = leadingRelative ? 1 : 0, // The foundational matcher ensures that elements are reachable from top-level context(s) matchContext = addCombinator( function( elem ) { return elem === checkContext; }, implicitRelative, true ), matchAnyContext = addCombinator( function( elem ) { return indexOf( checkContext, elem ) > -1; }, implicitRelative, true ), matchers = [ function( elem, context, xml ) { var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( (checkContext = context).nodeType ? matchContext( elem, context, xml ) : matchAnyContext( elem, context, xml ) ); // Avoid hanging onto element (issue #299) checkContext = null; return ret; } ]; for ( ; i < len; i++ ) { if ( (matcher = Expr.relative[ tokens[i].type ]) ) { matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; } else { matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); // Return special upon seeing a positional matcher if ( matcher[ expando ] ) { // Find the next relative operator (if any) for proper handling j = ++i; for ( ; j < len; j++ ) { if ( Expr.relative[ tokens[j].type ] ) { break; } } return setMatcher( i > 1 && elementMatcher( matchers ), i > 1 && toSelector( // If the preceding token was a descendant combinator, insert an implicit any-element `*` tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) ).replace( rtrim, "$1" ), matcher, i < j && matcherFromTokens( tokens.slice( i, j ) ), j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), j < len && toSelector( tokens ) ); } matchers.push( matcher ); } } return elementMatcher( matchers ); } function matcherFromGroupMatchers( elementMatchers, setMatchers ) { var bySet = setMatchers.length > 0, byElement = elementMatchers.length > 0, superMatcher = function( seed, context, xml, results, outermost ) { var elem, j, matcher, matchedCount = 0, i = "0", unmatched = seed && [], setMatched = [], contextBackup = outermostContext, // We must always have either seed elements or outermost context elems = seed || byElement && Expr.find["TAG"]( "*", outermost ), // Use integer dirruns iff this is the outermost matcher dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), len = elems.length; if ( outermost ) { outermostContext = context === document || context || outermost; } // Add elements passing elementMatchers directly to results // Support: IE<9, Safari // Tolerate NodeList properties (IE: "length"; Safari: <number>) matching elements by id for ( ; i !== len && (elem = elems[i]) != null; i++ ) { if ( byElement && elem ) { j = 0; if ( !context && elem.ownerDocument !== document ) { setDocument( elem ); xml = !documentIsHTML; } while ( (matcher = elementMatchers[j++]) ) { if ( matcher( elem, context || document, xml) ) { results.push( elem ); break; } } if ( outermost ) { dirruns = dirrunsUnique; } } // Track unmatched elements for set filters if ( bySet ) { // They will have gone through all possible matchers if ( (elem = !matcher && elem) ) { matchedCount--; } // Lengthen the array for every element, matched or not if ( seed ) { unmatched.push( elem ); } } } // `i` is now the count of elements visited above, and adding it to `matchedCount` // makes the latter nonnegative. matchedCount += i; // Apply set filters to unmatched elements // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount` // equals `i`), unless we didn't visit _any_ elements in the above loop because we have // no element matchers and no seed. // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that // case, which will result in a "00" `matchedCount` that differs from `i` but is also // numerically zero. if ( bySet && i !== matchedCount ) { j = 0; while ( (matcher = setMatchers[j++]) ) { matcher( unmatched, setMatched, context, xml ); } if ( seed ) { // Reintegrate element matches to eliminate the need for sorting if ( matchedCount > 0 ) { while ( i-- ) { if ( !(unmatched[i] || setMatched[i]) ) { setMatched[i] = pop.call( results ); } } } // Discard index placeholder values to get only actual matches setMatched = condense( setMatched ); } // Add matches to results push.apply( results, setMatched ); // Seedless set matches succeeding multiple successful matchers stipulate sorting if ( outermost && !seed && setMatched.length > 0 && ( matchedCount + setMatchers.length ) > 1 ) { Sizzle.uniqueSort( results ); } } // Override manipulation of globals by nested matchers if ( outermost ) { dirruns = dirrunsUnique; outermostContext = contextBackup; } return unmatched; }; return bySet ? markFunction( superMatcher ) : superMatcher; } compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { var i, setMatchers = [], elementMatchers = [], cached = compilerCache[ selector + " " ]; if ( !cached ) { // Generate a function of recursive functions that can be used to check each element if ( !match ) { match = tokenize( selector ); } i = match.length; while ( i-- ) { cached = matcherFromTokens( match[i] ); if ( cached[ expando ] ) { setMatchers.push( cached ); } else { elementMatchers.push( cached ); } } // Cache the compiled function cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); // Save selector and tokenization cached.selector = selector; } return cached; }; /** * A low-level selection function that works with Sizzle's compiled * selector functions * @param {String|Function} selector A selector or a pre-compiled * selector function built with Sizzle.compile * @param {Element} context * @param {Array} [results] * @param {Array} [seed] A set of elements to match against */ select = Sizzle.select = function( selector, context, results, seed ) { var i, tokens, token, type, find, compiled = typeof selector === "function" && selector, match = !seed && tokenize( (selector = compiled.selector || selector) ); results = results || []; // Try to minimize operations if there is only one selector in the list and no seed // (the latter of which guarantees us context) if ( match.length === 1 ) { // Reduce context if the leading compound selector is an ID tokens = match[0] = match[0].slice( 0 ); if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[1].type ] ) { context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; if ( !context ) { return results; // Precompiled matchers will still verify ancestry, so step up a level } else if ( compiled ) { context = context.parentNode; } selector = selector.slice( tokens.shift().value.length ); } // Fetch a seed set for right-to-left matching i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; while ( i-- ) { token = tokens[i]; // Abort if we hit a combinator if ( Expr.relative[ (type = token.type) ] ) { break; } if ( (find = Expr.find[ type ]) ) { // Search, expanding context for leading sibling combinators if ( (seed = find( token.matches[0].replace( runescape, funescape ), rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context )) ) { // If seed is empty or no tokens remain, we can return early tokens.splice( i, 1 ); selector = seed.length && toSelector( tokens ); if ( !selector ) { push.apply( results, seed ); return results; } break; } } } } // Compile and execute a filtering function if one is not provided // Provide `match` to avoid retokenization if we modified the selector above ( compiled || compile( selector, match ) )( seed, context, !documentIsHTML, results, !context || rsibling.test( selector ) && testContext( context.parentNode ) || context ); return results; }; // One-time assignments // Sort stability support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; // Support: Chrome 14-35+ // Always assume duplicates if they aren't passed to the comparison function support.detectDuplicates = !!hasDuplicate; // Initialize against the default document setDocument(); // Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) // Detached nodes confoundingly follow *each other* support.sortDetached = assert(function( el ) { // Should return 1, but returns 4 (following) return el.compareDocumentPosition( document.createElement("fieldset") ) & 1; }); // Support: IE<8 // Prevent attribute/property "interpolation" // https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx if ( !assert(function( el ) { el.innerHTML = "<a href='#'></a>"; return el.firstChild.getAttribute("href") === "#" ; }) ) { addHandle( "type|href|height|width", function( elem, name, isXML ) { if ( !isXML ) { return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); } }); } // Support: IE<9 // Use defaultValue in place of getAttribute("value") if ( !support.attributes || !assert(function( el ) { el.innerHTML = "<input/>"; el.firstChild.setAttribute( "value", "" ); return el.firstChild.getAttribute( "value" ) === ""; }) ) { addHandle( "value", function( elem, name, isXML ) { if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { return elem.defaultValue; } }); } // Support: IE<9 // Use getAttributeNode to fetch booleans when getAttribute lies if ( !assert(function( el ) { return el.getAttribute("disabled") == null; }) ) { addHandle( booleans, function( elem, name, isXML ) { var val; if ( !isXML ) { return elem[ name ] === true ? name.toLowerCase() : (val = elem.getAttributeNode( name )) && val.specified ? val.value : null; } }); } return Sizzle; })( window ); jQuery.find = Sizzle; jQuery.expr = Sizzle.selectors; // Deprecated jQuery.expr[ ":" ] = jQuery.expr.pseudos; jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort; jQuery.text = Sizzle.getText; jQuery.isXMLDoc = Sizzle.isXML; jQuery.contains = Sizzle.contains; jQuery.escapeSelector = Sizzle.escape; var dir = function( elem, dir, until ) { var matched = [], truncate = until !== undefined; while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) { if ( elem.nodeType === 1 ) { if ( truncate && jQuery( elem ).is( until ) ) { break; } matched.push( elem ); } } return matched; }; var siblings = function( n, elem ) { var matched = []; for ( ; n; n = n.nextSibling ) { if ( n.nodeType === 1 && n !== elem ) { matched.push( n ); } } return matched; }; var rneedsContext = jQuery.expr.match.needsContext; var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i ); var risSimple = /^.[^:#\[\.,]*$/; // Implement the identical functionality for filter and not function winnow( elements, qualifier, not ) { if ( jQuery.isFunction( qualifier ) ) { return jQuery.grep( elements, function( elem, i ) { return !!qualifier.call( elem, i, elem ) !== not; } ); } // Single element if ( qualifier.nodeType ) { return jQuery.grep( elements, function( elem ) { return ( elem === qualifier ) !== not; } ); } // Arraylike of elements (jQuery, arguments, Array) if ( typeof qualifier !== "string" ) { return jQuery.grep( elements, function( elem ) { return ( indexOf.call( qualifier, elem ) > -1 ) !== not; } ); } // Simple selector that can be filtered directly, removing non-Elements if ( risSimple.test( qualifier ) ) { return jQuery.filter( qualifier, elements, not ); } // Complex selector, compare the two sets, removing non-Elements qualifier = jQuery.filter( qualifier, elements ); return jQuery.grep( elements, function( elem ) { return ( indexOf.call( qualifier, elem ) > -1 ) !== not && elem.nodeType === 1; } ); } jQuery.filter = function( expr, elems, not ) { var elem = elems[ 0 ]; if ( not ) { expr = ":not(" + expr + ")"; } if ( elems.length === 1 && elem.nodeType === 1 ) { return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : []; } return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { return elem.nodeType === 1; } ) ); }; jQuery.fn.extend( { find: function( selector ) { var i, ret, len = this.length, self = this; if ( typeof selector !== "string" ) { return this.pushStack( jQuery( selector ).filter( function() { for ( i = 0; i < len; i++ ) { if ( jQuery.contains( self[ i ], this ) ) { return true; } } } ) ); } ret = this.pushStack( [] ); for ( i = 0; i < len; i++ ) { jQuery.find( selector, self[ i ], ret ); } return len > 1 ? jQuery.uniqueSort( ret ) : ret; }, filter: function( selector ) { return this.pushStack( winnow( this, selector || [], false ) ); }, not: function( selector ) { return this.pushStack( winnow( this, selector || [], true ) ); }, is: function( selector ) { return !!winnow( this, // If this is a positional/relative selector, check membership in the returned set // so $("p:first").is("p:last") won't return true for a doc with two "p". typeof selector === "string" && rneedsContext.test( selector ) ? jQuery( selector ) : selector || [], false ).length; } } ); // Initialize a jQuery object // A central reference to the root jQuery(document) var rootjQuery, // A simple way to check for HTML strings // Prioritize #id over <tag> to avoid XSS via location.hash (#9521) // Strict HTML recognition (#11290: must start with <) // Shortcut simple #id case for speed rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/, init = jQuery.fn.init = function( selector, context, root ) { var match, elem; // HANDLE: $(""), $(null), $(undefined), $(false) if ( !selector ) { return this; } // Method init() accepts an alternate rootjQuery // so migrate can support jQuery.sub (gh-2101) root = root || rootjQuery; // Handle HTML strings if ( typeof selector === "string" ) { if ( selector[ 0 ] === "<" && selector[ selector.length - 1 ] === ">" && selector.length >= 3 ) { // Assume that strings that start and end with <> are HTML and skip the regex check match = [ null, selector, null ]; } else { match = rquickExpr.exec( selector ); } // Match html or make sure no context is specified for #id if ( match && ( match[ 1 ] || !context ) ) { // HANDLE: $(html) -> $(array) if ( match[ 1 ] ) { context = context instanceof jQuery ? context[ 0 ] : context; // Option to run scripts is true for back-compat // Intentionally let the error be thrown if parseHTML is not present jQuery.merge( this, jQuery.parseHTML( match[ 1 ], context && context.nodeType ? context.ownerDocument || context : document, true ) ); // HANDLE: $(html, props) if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { for ( match in context ) { // Properties of context are called as methods if possible if ( jQuery.isFunction( this[ match ] ) ) { this[ match ]( context[ match ] ); // ...and otherwise set as attributes } else { this.attr( match, context[ match ] ); } } } return this; // HANDLE: $(#id) } else { elem = document.getElementById( match[ 2 ] ); if ( elem ) { // Inject the element directly into the jQuery object this[ 0 ] = elem; this.length = 1; } return this; } // HANDLE: $(expr, $(...)) } else if ( !context || context.jquery ) { return ( context || root ).find( selector ); // HANDLE: $(expr, context) // (which is just equivalent to: $(context).find(expr) } else { return this.constructor( context ).find( selector ); } // HANDLE: $(DOMElement) } else if ( selector.nodeType ) { this[ 0 ] = selector; this.length = 1; return this; // HANDLE: $(function) // Shortcut for document ready } else if ( jQuery.isFunction( selector ) ) { return root.ready !== undefined ? root.ready( selector ) : // Execute immediately if ready is not present selector( jQuery ); } return jQuery.makeArray( selector, this ); }; // Give the init function the jQuery prototype for later instantiation init.prototype = jQuery.fn; // Initialize central reference rootjQuery = jQuery( document ); var rparentsprev = /^(?:parents|prev(?:Until|All))/, // Methods guaranteed to produce a unique set when starting from a unique set guaranteedUnique = { children: true, contents: true, next: true, prev: true }; jQuery.fn.extend( { has: function( target ) { var targets = jQuery( target, this ), l = targets.length; return this.filter( function() { var i = 0; for ( ; i < l; i++ ) { if ( jQuery.contains( this, targets[ i ] ) ) { return true; } } } ); }, closest: function( selectors, context ) { var cur, i = 0, l = this.length, matched = [], targets = typeof selectors !== "string" && jQuery( selectors ); // Positional selectors never match, since there's no _selection_ context if ( !rneedsContext.test( selectors ) ) { for ( ; i < l; i++ ) { for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) { // Always skip document fragments if ( cur.nodeType < 11 && ( targets ? targets.index( cur ) > -1 : // Don't pass non-elements to Sizzle cur.nodeType === 1 && jQuery.find.matchesSelector( cur, selectors ) ) ) { matched.push( cur ); break; } } } } return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched ); }, // Determine the position of an element within the set index: function( elem ) { // No argument, return index in parent if ( !elem ) { return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; } // Index in selector if ( typeof elem === "string" ) { return indexOf.call( jQuery( elem ), this[ 0 ] ); } // Locate the position of the desired element return indexOf.call( this, // If it receives a jQuery object, the first element is used elem.jquery ? elem[ 0 ] : elem ); }, add: function( selector, context ) { return this.pushStack( jQuery.uniqueSort( jQuery.merge( this.get(), jQuery( selector, context ) ) ) ); }, addBack: function( selector ) { return this.add( selector == null ? this.prevObject : this.prevObject.filter( selector ) ); } } ); function sibling( cur, dir ) { while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {} return cur; } jQuery.each( { parent: function( elem ) { var parent = elem.parentNode; return parent && parent.nodeType !== 11 ? parent : null; }, parents: function( elem ) { return dir( elem, "parentNode" ); }, parentsUntil: function( elem, i, until ) { return dir( elem, "parentNode", until ); }, next: function( elem ) { return sibling( elem, "nextSibling" ); }, prev: function( elem ) { return sibling( elem, "previousSibling" ); }, nextAll: function( elem ) { return dir( elem, "nextSibling" ); }, prevAll: function( elem ) { return dir( elem, "previousSibling" ); }, nextUntil: function( elem, i, until ) { return dir( elem, "nextSibling", until ); }, prevUntil: function( elem, i, until ) { return dir( elem, "previousSibling", until ); }, siblings: function( elem ) { return siblings( ( elem.parentNode || {} ).firstChild, elem ); }, children: function( elem ) { return siblings( elem.firstChild ); }, contents: function( elem ) { return elem.contentDocument || jQuery.merge( [], elem.childNodes ); } }, function( name, fn ) { jQuery.fn[ name ] = function( until, selector ) { var matched = jQuery.map( this, fn, until ); if ( name.slice( -5 ) !== "Until" ) { selector = until; } if ( selector && typeof selector === "string" ) { matched = jQuery.filter( selector, matched ); } if ( this.length > 1 ) { // Remove duplicates if ( !guaranteedUnique[ name ] ) { jQuery.uniqueSort( matched ); } // Reverse order for parents* and prev-derivatives if ( rparentsprev.test( name ) ) { matched.reverse(); } } return this.pushStack( matched ); }; } ); var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g ); // Convert String-formatted options into Object-formatted ones function createOptions( options ) { var object = {}; jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) { object[ flag ] = true; } ); return object; } /* * Create a callback list using the following parameters: * * options: an optional list of space-separated options that will change how * the callback list behaves or a more traditional option object * * By default a callback list will act like an event callback list and can be * "fired" multiple times. * * Possible options: * * once: will ensure the callback list can only be fired once (like a Deferred) * * memory: will keep track of previous values and will call any callback added * after the list has been fired right away with the latest "memorized" * values (like a Deferred) * * unique: will ensure a callback can only be added once (no duplicate in the list) * * stopOnFalse: interrupt callings when a callback returns false * */ jQuery.Callbacks = function( options ) { // Convert options from String-formatted to Object-formatted if needed // (we check in cache first) options = typeof options === "string" ? createOptions( options ) : jQuery.extend( {}, options ); var // Flag to know if list is currently firing firing, // Last fire value for non-forgettable lists memory, // Flag to know if list was already fired fired, // Flag to prevent firing locked, // Actual callback list list = [], // Queue of execution data for repeatable lists queue = [], // Index of currently firing callback (modified by add/remove as needed) firingIndex = -1, // Fire callbacks fire = function() { // Enforce single-firing locked = options.once; // Execute callbacks for all pending executions, // respecting firingIndex overrides and runtime changes fired = firing = true; for ( ; queue.length; firingIndex = -1 ) { memory = queue.shift(); while ( ++firingIndex < list.length ) { // Run callback and check for early termination if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && options.stopOnFalse ) { // Jump to end and forget the data so .add doesn't re-fire firingIndex = list.length; memory = false; } } } // Forget the data if we're done with it if ( !options.memory ) { memory = false; } firing = false; // Clean up if we're done firing for good if ( locked ) { // Keep an empty list if we have data for future add calls if ( memory ) { list = []; // Otherwise, this object is spent } else { list = ""; } } }, // Actual Callbacks object self = { // Add a callback or a collection of callbacks to the list add: function() { if ( list ) { // If we have memory from a past run, we should fire after adding if ( memory && !firing ) { firingIndex = list.length - 1; queue.push( memory ); } ( function add( args ) { jQuery.each( args, function( _, arg ) { if ( jQuery.isFunction( arg ) ) { if ( !options.unique || !self.has( arg ) ) { list.push( arg ); } } else if ( arg && arg.length && jQuery.type( arg ) !== "string" ) { // Inspect recursively add( arg ); } } ); } )( arguments ); if ( memory && !firing ) { fire(); } } return this; }, // Remove a callback from the list remove: function() { jQuery.each( arguments, function( _, arg ) { var index; while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { list.splice( index, 1 ); // Handle firing indexes if ( index <= firingIndex ) { firingIndex--; } } } ); return this; }, // Check if a given callback is in the list. // If no argument is given, return whether or not list has callbacks attached. has: function( fn ) { return fn ? jQuery.inArray( fn, list ) > -1 : list.length > 0; }, // Remove all callbacks from the list empty: function() { if ( list ) { list = []; } return this; }, // Disable .fire and .add // Abort any current/pending executions // Clear all callbacks and values disable: function() { locked = queue = []; list = memory = ""; return this; }, disabled: function() { return !list; }, // Disable .fire // Also disable .add unless we have memory (since it would have no effect) // Abort any pending executions lock: function() { locked = queue = []; if ( !memory && !firing ) { list = memory = ""; } return this; }, locked: function() { return !!locked; }, // Call all callbacks with the given context and arguments fireWith: function( context, args ) { if ( !locked ) { args = args || []; args = [ context, args.slice ? args.slice() : args ]; queue.push( args ); if ( !firing ) { fire(); } } return this; }, // Call all the callbacks with the given arguments fire: function() { self.fireWith( this, arguments ); return this; }, // To know if the callbacks have already been called at least once fired: function() { return !!fired; } }; return self; }; function Identity( v ) { return v; } function Thrower( ex ) { throw ex; } function adoptValue( value, resolve, reject ) { var method; try { // Check for promise aspect first to privilege synchronous behavior if ( value && jQuery.isFunction( ( method = value.promise ) ) ) { method.call( value ).done( resolve ).fail( reject ); // Other thenables } else if ( value && jQuery.isFunction( ( method = value.then ) ) ) { method.call( value, resolve, reject ); // Other non-thenables } else { // Support: Android 4.0 only // Strict mode functions invoked without .call/.apply get global-object context resolve.call( undefined, value ); } // For Promises/A+, convert exceptions into rejections // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in // Deferred#then to conditionally suppress rejection. } catch ( value ) { // Support: Android 4.0 only // Strict mode functions invoked without .call/.apply get global-object context reject.call( undefined, value ); } } jQuery.extend( { Deferred: function( func ) { var tuples = [ // action, add listener, callbacks, // ... .then handlers, argument index, [final state] [ "notify", "progress", jQuery.Callbacks( "memory" ), jQuery.Callbacks( "memory" ), 2 ], [ "resolve", "done", jQuery.Callbacks( "once memory" ), jQuery.Callbacks( "once memory" ), 0, "resolved" ], [ "reject", "fail", jQuery.Callbacks( "once memory" ), jQuery.Callbacks( "once memory" ), 1, "rejected" ] ], state = "pending", promise = { state: function() { return state; }, always: function() { deferred.done( arguments ).fail( arguments ); return this; }, "catch": function( fn ) { return promise.then( null, fn ); }, // Keep pipe for back-compat pipe: function( /* fnDone, fnFail, fnProgress */ ) { var fns = arguments; return jQuery.Deferred( function( newDefer ) { jQuery.each( tuples, function( i, tuple ) { // Map tuples (progress, done, fail) to arguments (done, fail, progress) var fn = jQuery.isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ]; // deferred.progress(function() { bind to newDefer or newDefer.notify }) // deferred.done(function() { bind to newDefer or newDefer.resolve }) // deferred.fail(function() { bind to newDefer or newDefer.reject }) deferred[ tuple[ 1 ] ]( function() { var returned = fn && fn.apply( this, arguments ); if ( returned && jQuery.isFunction( returned.promise ) ) { returned.promise() .progress( newDefer.notify ) .done( newDefer.resolve ) .fail( newDefer.reject ); } else { newDefer[ tuple[ 0 ] + "With" ]( this, fn ? [ returned ] : arguments ); } } ); } ); fns = null; } ).promise(); }, then: function( onFulfilled, onRejected, onProgress ) { var maxDepth = 0; function resolve( depth, deferred, handler, special ) { return function() { var that = this, args = arguments, mightThrow = function() { var returned, then; // Support: Promises/A+ section 2.3.3.3.3 // https://promisesaplus.com/#point-59 // Ignore double-resolution attempts if ( depth < maxDepth ) { return; } returned = handler.apply( that, args ); // Support: Promises/A+ section 2.3.1 // https://promisesaplus.com/#point-48 if ( returned === deferred.promise() ) { throw new TypeError( "Thenable self-resolution" ); } // Support: Promises/A+ sections 2.3.3.1, 3.5 // https://promisesaplus.com/#point-54 // https://promisesaplus.com/#point-75 // Retrieve `then` only once then = returned && // Support: Promises/A+ section 2.3.4 // https://promisesaplus.com/#point-64 // Only check objects and functions for thenability ( typeof returned === "object" || typeof returned === "function" ) && returned.then; // Handle a returned thenable if ( jQuery.isFunction( then ) ) { // Special processors (notify) just wait for resolution if ( special ) { then.call( returned, resolve( maxDepth, deferred, Identity, special ), resolve( maxDepth, deferred, Thrower, special ) ); // Normal processors (resolve) also hook into progress } else { // ...and disregard older resolution values maxDepth++; then.call( returned, resolve( maxDepth, deferred, Identity, special ), resolve( maxDepth, deferred, Thrower, special ), resolve( maxDepth, deferred, Identity, deferred.notifyWith ) ); } // Handle all other returned values } else { // Only substitute handlers pass on context // and multiple values (non-spec behavior) if ( handler !== Identity ) { that = undefined; args = [ returned ]; } // Process the value(s) // Default process is resolve ( special || deferred.resolveWith )( that, args ); } }, // Only normal processors (resolve) catch and reject exceptions process = special ? mightThrow : function() { try { mightThrow(); } catch ( e ) { if ( jQuery.Deferred.exceptionHook ) { jQuery.Deferred.exceptionHook( e, process.stackTrace ); } // Support: Promises/A+ section 2.3.3.3.4.1 // https://promisesaplus.com/#point-61 // Ignore post-resolution exceptions if ( depth + 1 >= maxDepth ) { // Only substitute handlers pass on context // and multiple values (non-spec behavior) if ( handler !== Thrower ) { that = undefined; args = [ e ]; } deferred.rejectWith( that, args ); } } }; // Support: Promises/A+ section 2.3.3.3.1 // https://promisesaplus.com/#point-57 // Re-resolve promises immediately to dodge false rejection from // subsequent errors if ( depth ) { process(); } else { // Call an optional hook to record the stack, in case of exception // since it's otherwise lost when execution goes async if ( jQuery.Deferred.getStackHook ) { process.stackTrace = jQuery.Deferred.getStackHook(); } window.setTimeout( process ); } }; } return jQuery.Deferred( function( newDefer ) { // progress_handlers.add( ... ) tuples[ 0 ][ 3 ].add( resolve( 0, newDefer, jQuery.isFunction( onProgress ) ? onProgress : Identity, newDefer.notifyWith ) ); // fulfilled_handlers.add( ... ) tuples[ 1 ][ 3 ].add( resolve( 0, newDefer, jQuery.isFunction( onFulfilled ) ? onFulfilled : Identity ) ); // rejected_handlers.add( ... ) tuples[ 2 ][ 3 ].add( resolve( 0, newDefer, jQuery.isFunction( onRejected ) ? onRejected : Thrower ) ); } ).promise(); }, // Get a promise for this deferred // If obj is provided, the promise aspect is added to the object promise: function( obj ) { return obj != null ? jQuery.extend( obj, promise ) : promise; } }, deferred = {}; // Add list-specific methods jQuery.each( tuples, function( i, tuple ) { var list = tuple[ 2 ], stateString = tuple[ 5 ]; // promise.progress = list.add // promise.done = list.add // promise.fail = list.add promise[ tuple[ 1 ] ] = list.add; // Handle state if ( stateString ) { list.add( function() { // state = "resolved" (i.e., fulfilled) // state = "rejected" state = stateString; }, // rejected_callbacks.disable // fulfilled_callbacks.disable tuples[ 3 - i ][ 2 ].disable, // progress_callbacks.lock tuples[ 0 ][ 2 ].lock ); } // progress_handlers.fire // fulfilled_handlers.fire // rejected_handlers.fire list.add( tuple[ 3 ].fire ); // deferred.notify = function() { deferred.notifyWith(...) } // deferred.resolve = function() { deferred.resolveWith(...) } // deferred.reject = function() { deferred.rejectWith(...) } deferred[ tuple[ 0 ] ] = function() { deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments ); return this; }; // deferred.notifyWith = list.fireWith // deferred.resolveWith = list.fireWith // deferred.rejectWith = list.fireWith deferred[ tuple[ 0 ] + "With" ] = list.fireWith; } ); // Make the deferred a promise promise.promise( deferred ); // Call given func if any if ( func ) { func.call( deferred, deferred ); } // All done! return deferred; }, // Deferred helper when: function( singleValue ) { var // count of uncompleted subordinates remaining = arguments.length, // count of unprocessed arguments i = remaining, // subordinate fulfillment data resolveContexts = Array( i ), resolveValues = slice.call( arguments ), // the master Deferred master = jQuery.Deferred(), // subordinate callback factory updateFunc = function( i ) { return function( value ) { resolveContexts[ i ] = this; resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; if ( !( --remaining ) ) { master.resolveWith( resolveContexts, resolveValues ); } }; }; // Single- and empty arguments are adopted like Promise.resolve if ( remaining <= 1 ) { adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject ); // Use .then() to unwrap secondary thenables (cf. gh-3000) if ( master.state() === "pending" || jQuery.isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) { return master.then(); } } // Multiple arguments are aggregated like Promise.all array elements while ( i-- ) { adoptValue( resolveValues[ i ], updateFunc( i ), master.reject ); } return master.promise(); } } ); // These usually indicate a programmer mistake during development, // warn about them ASAP rather than swallowing them by default. var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/; jQuery.Deferred.exceptionHook = function( error, stack ) { // Support: IE 8 - 9 only // Console exists when dev tools are open, which can happen at any time if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) { window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack ); } }; jQuery.readyException = function( error ) { window.setTimeout( function() { throw error; } ); }; // The deferred used on DOM ready var readyList = jQuery.Deferred(); jQuery.fn.ready = function( fn ) { readyList .then( fn ) // Wrap jQuery.readyException in a function so that the lookup // happens at the time of error handling instead of callback // registration. .catch( function( error ) { jQuery.readyException( error ); } ); return this; }; jQuery.extend( { // Is the DOM ready to be used? Set to true once it occurs. isReady: false, // A counter to track how many items to wait for before // the ready event fires. See #6781 readyWait: 1, // Hold (or release) the ready event holdReady: function( hold ) { if ( hold ) { jQuery.readyWait++; } else { jQuery.ready( true ); } }, // Handle when the DOM is ready ready: function( wait ) { // Abort if there are pending holds or we're already ready if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { return; } // Remember that the DOM is ready jQuery.isReady = true; // If a normal DOM Ready event fired, decrement, and wait if need be if ( wait !== true && --jQuery.readyWait > 0 ) { return; } // If there are functions bound, to execute readyList.resolveWith( document, [ jQuery ] ); } } ); jQuery.ready.then = readyList.then; // The ready event handler and self cleanup method function completed() { document.removeEventListener( "DOMContentLoaded", completed ); window.removeEventListener( "load", completed ); jQuery.ready(); } // Catch cases where $(document).ready() is called // after the browser event has already occurred. // Support: IE <=9 - 10 only // Older IE sometimes signals "interactive" too soon if ( document.readyState === "complete" || ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { // Handle it asynchronously to allow scripts the opportunity to delay ready window.setTimeout( jQuery.ready ); } else { // Use the handy event callback document.addEventListener( "DOMContentLoaded", completed ); // A fallback to window.onload, that will always work window.addEventListener( "load", completed ); } // Multifunctional method to get and set values of a collection // The value/s can optionally be executed if it's a function var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { var i = 0, len = elems.length, bulk = key == null; // Sets many values if ( jQuery.type( key ) === "object" ) { chainable = true; for ( i in key ) { access( elems, fn, i, key[ i ], true, emptyGet, raw ); } // Sets one value } else if ( value !== undefined ) { chainable = true; if ( !jQuery.isFunction( value ) ) { raw = true; } if ( bulk ) { // Bulk operations run against the entire set if ( raw ) { fn.call( elems, value ); fn = null; // ...except when executing function values } else { bulk = fn; fn = function( elem, key, value ) { return bulk.call( jQuery( elem ), value ); }; } } if ( fn ) { for ( ; i < len; i++ ) { fn( elems[ i ], key, raw ? value : value.call( elems[ i ], i, fn( elems[ i ], key ) ) ); } } } if ( chainable ) { return elems; } // Gets if ( bulk ) { return fn.call( elems ); } return len ? fn( elems[ 0 ], key ) : emptyGet; }; var acceptData = function( owner ) { // Accepts only: // - Node // - Node.ELEMENT_NODE // - Node.DOCUMENT_NODE // - Object // - Any return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); }; function Data() { this.expando = jQuery.expando + Data.uid++; } Data.uid = 1; Data.prototype = { cache: function( owner ) { // Check if the owner object already has a cache var value = owner[ this.expando ]; // If not, create one if ( !value ) { value = {}; // We can accept data for non-element nodes in modern browsers, // but we should not, see #8335. // Always return an empty object. if ( acceptData( owner ) ) { // If it is a node unlikely to be stringify-ed or looped over // use plain assignment if ( owner.nodeType ) { owner[ this.expando ] = value; // Otherwise secure it in a non-enumerable property // configurable must be true to allow the property to be // deleted when data is removed } else { Object.defineProperty( owner, this.expando, { value: value, configurable: true } ); } } } return value; }, set: function( owner, data, value ) { var prop, cache = this.cache( owner ); // Handle: [ owner, key, value ] args // Always use camelCase key (gh-2257) if ( typeof data === "string" ) { cache[ jQuery.camelCase( data ) ] = value; // Handle: [ owner, { properties } ] args } else { // Copy the properties one-by-one to the cache object for ( prop in data ) { cache[ jQuery.camelCase( prop ) ] = data[ prop ]; } } return cache; }, get: function( owner, key ) { return key === undefined ? this.cache( owner ) : // Always use camelCase key (gh-2257) owner[ this.expando ] && owner[ this.expando ][ jQuery.camelCase( key ) ]; }, access: function( owner, key, value ) { // In cases where either: // // 1. No key was specified // 2. A string key was specified, but no value provided // // Take the "read" path and allow the get method to determine // which value to return, respectively either: // // 1. The entire cache object // 2. The data stored at the key // if ( key === undefined || ( ( key && typeof key === "string" ) && value === undefined ) ) { return this.get( owner, key ); } // When the key is not a string, or both a key and value // are specified, set or extend (existing objects) with either: // // 1. An object of properties // 2. A key and value // this.set( owner, key, value ); // Since the "set" path can have two possible entry points // return the expected data based on which path was taken[*] return value !== undefined ? value : key; }, remove: function( owner, key ) { var i, cache = owner[ this.expando ]; if ( cache === undefined ) { return; } if ( key !== undefined ) { // Support array or space separated string of keys if ( jQuery.isArray( key ) ) { // If key is an array of keys... // We always set camelCase keys, so remove that. key = key.map( jQuery.camelCase ); } else { key = jQuery.camelCase( key ); // If a key with the spaces exists, use it. // Otherwise, create an array by matching non-whitespace key = key in cache ? [ key ] : ( key.match( rnothtmlwhite ) || [] ); } i = key.length; while ( i-- ) { delete cache[ key[ i ] ]; } } // Remove the expando if there's no more data if ( key === undefined || jQuery.isEmptyObject( cache ) ) { // Support: Chrome <=35 - 45 // Webkit & Blink performance suffers when deleting properties // from DOM nodes, so set to undefined instead // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted) if ( owner.nodeType ) { owner[ this.expando ] = undefined; } else { delete owner[ this.expando ]; } } }, hasData: function( owner ) { var cache = owner[ this.expando ]; return cache !== undefined && !jQuery.isEmptyObject( cache ); } }; var dataPriv = new Data(); var dataUser = new Data(); // Implementation Summary // // 1. Enforce API surface and semantic compatibility with 1.9.x branch // 2. Improve the module's maintainability by reducing the storage // paths to a single mechanism. // 3. Use the same single mechanism to support "private" and "user" data. // 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) // 5. Avoid exposing implementation details on user objects (eg. expando properties) // 6. Provide a clear path for implementation upgrade to WeakMap in 2014 var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, rmultiDash = /[A-Z]/g; function getData( data ) { if ( data === "true" ) { return true; } if ( data === "false" ) { return false; } if ( data === "null" ) { return null; } // Only convert to a number if it doesn't change the string if ( data === +data + "" ) { return +data; } if ( rbrace.test( data ) ) { return JSON.parse( data ); } return data; } function dataAttr( elem, key, data ) { var name; // If nothing was found internally, try to fetch any // data from the HTML5 data-* attribute if ( data === undefined && elem.nodeType === 1 ) { name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase(); data = elem.getAttribute( name ); if ( typeof data === "string" ) { try { data = getData( data ); } catch ( e ) {} // Make sure we set the data so it isn't changed later dataUser.set( elem, key, data ); } else { data = undefined; } } return data; } jQuery.extend( { hasData: function( elem ) { return dataUser.hasData( elem ) || dataPriv.hasData( elem ); }, data: function( elem, name, data ) { return dataUser.access( elem, name, data ); }, removeData: function( elem, name ) { dataUser.remove( elem, name ); }, // TODO: Now that all calls to _data and _removeData have been replaced // with direct calls to dataPriv methods, these can be deprecated. _data: function( elem, name, data ) { return dataPriv.access( elem, name, data ); }, _removeData: function( elem, name ) { dataPriv.remove( elem, name ); } } ); jQuery.fn.extend( { data: function( key, value ) { var i, name, data, elem = this[ 0 ], attrs = elem && elem.attributes; // Gets all values if ( key === undefined ) { if ( this.length ) { data = dataUser.get( elem ); if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) { i = attrs.length; while ( i-- ) { // Support: IE 11 only // The attrs elements can be null (#14894) if ( attrs[ i ] ) { name = attrs[ i ].name; if ( name.indexOf( "data-" ) === 0 ) { name = jQuery.camelCase( name.slice( 5 ) ); dataAttr( elem, name, data[ name ] ); } } } dataPriv.set( elem, "hasDataAttrs", true ); } } return data; } // Sets multiple values if ( typeof key === "object" ) { return this.each( function() { dataUser.set( this, key ); } ); } return access( this, function( value ) { var data; // The calling jQuery object (element matches) is not empty // (and therefore has an element appears at this[ 0 ]) and the // `value` parameter was not undefined. An empty jQuery object // will result in `undefined` for elem = this[ 0 ] which will // throw an exception if an attempt to read a data cache is made. if ( elem && value === undefined ) { // Attempt to get data from the cache // The key will always be camelCased in Data data = dataUser.get( elem, key ); if ( data !== undefined ) { return data; } // Attempt to "discover" the data in // HTML5 custom data-* attrs data = dataAttr( elem, key ); if ( data !== undefined ) { return data; } // We tried really hard, but the data doesn't exist. return; } // Set the data... this.each( function() { // We always store the camelCased key dataUser.set( this, key, value ); } ); }, null, value, arguments.length > 1, null, true ); }, removeData: function( key ) { return this.each( function() { dataUser.remove( this, key ); } ); } } ); jQuery.extend( { queue: function( elem, type, data ) { var queue; if ( elem ) { type = ( type || "fx" ) + "queue"; queue = dataPriv.get( elem, type ); // Speed up dequeue by getting out quickly if this is just a lookup if ( data ) { if ( !queue || jQuery.isArray( data ) ) { queue = dataPriv.access( elem, type, jQuery.makeArray( data ) ); } else { queue.push( data ); } } return queue || []; } }, dequeue: function( elem, type ) { type = type || "fx"; var queue = jQuery.queue( elem, type ), startLength = queue.length, fn = queue.shift(), hooks = jQuery._queueHooks( elem, type ), next = function() { jQuery.dequeue( elem, type ); }; // If the fx queue is dequeued, always remove the progress sentinel if ( fn === "inprogress" ) { fn = queue.shift(); startLength--; } if ( fn ) { // Add a progress sentinel to prevent the fx queue from being // automatically dequeued if ( type === "fx" ) { queue.unshift( "inprogress" ); } // Clear up the last queue stop function delete hooks.stop; fn.call( elem, next, hooks ); } if ( !startLength && hooks ) { hooks.empty.fire(); } }, // Not public - generate a queueHooks object, or return the current one _queueHooks: function( elem, type ) { var key = type + "queueHooks"; return dataPriv.get( elem, key ) || dataPriv.access( elem, key, { empty: jQuery.Callbacks( "once memory" ).add( function() { dataPriv.remove( elem, [ type + "queue", key ] ); } ) } ); } } ); jQuery.fn.extend( { queue: function( type, data ) { var setter = 2; if ( typeof type !== "string" ) { data = type; type = "fx"; setter--; } if ( arguments.length < setter ) { return jQuery.queue( this[ 0 ], type ); } return data === undefined ? this : this.each( function() { var queue = jQuery.queue( this, type, data ); // Ensure a hooks for this queue jQuery._queueHooks( this, type ); if ( type === "fx" && queue[ 0 ] !== "inprogress" ) { jQuery.dequeue( this, type ); } } ); }, dequeue: function( type ) { return this.each( function() { jQuery.dequeue( this, type ); } ); }, clearQueue: function( type ) { return this.queue( type || "fx", [] ); }, // Get a promise resolved when queues of a certain type // are emptied (fx is the type by default) promise: function( type, obj ) { var tmp, count = 1, defer = jQuery.Deferred(), elements = this, i = this.length, resolve = function() { if ( !( --count ) ) { defer.resolveWith( elements, [ elements ] ); } }; if ( typeof type !== "string" ) { obj = type; type = undefined; } type = type || "fx"; while ( i-- ) { tmp = dataPriv.get( elements[ i ], type + "queueHooks" ); if ( tmp && tmp.empty ) { count++; tmp.empty.add( resolve ); } } resolve(); return defer.promise( obj ); } } ); var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source; var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; var isHiddenWithinTree = function( elem, el ) { // isHiddenWithinTree might be called from jQuery#filter function; // in that case, element will be second argument elem = el || elem; // Inline style trumps all return elem.style.display === "none" || elem.style.display === "" && // Otherwise, check computed style // Support: Firefox <=43 - 45 // Disconnected elements can have computed display: none, so first confirm that elem is // in the document. jQuery.contains( elem.ownerDocument, elem ) && jQuery.css( elem, "display" ) === "none"; }; var swap = function( elem, options, callback, args ) { var ret, name, old = {}; // Remember the old values, and insert the new ones for ( name in options ) { old[ name ] = elem.style[ name ]; elem.style[ name ] = options[ name ]; } ret = callback.apply( elem, args || [] ); // Revert the old values for ( name in options ) { elem.style[ name ] = old[ name ]; } return ret; }; function adjustCSS( elem, prop, valueParts, tween ) { var adjusted, scale = 1, maxIterations = 20, currentValue = tween ? function() { return tween.cur(); } : function() { return jQuery.css( elem, prop, "" ); }, initial = currentValue(), unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), // Starting value computation is required for potential unit mismatches initialInUnit = ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) && rcssNum.exec( jQuery.css( elem, prop ) ); if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { // Trust units reported by jQuery.css unit = unit || initialInUnit[ 3 ]; // Make sure we update the tween properties later on valueParts = valueParts || []; // Iteratively approximate from a nonzero starting point initialInUnit = +initial || 1; do { // If previous iteration zeroed out, double until we get *something*. // Use string for doubling so we don't accidentally see scale as unchanged below scale = scale || ".5"; // Adjust and apply initialInUnit = initialInUnit / scale; jQuery.style( elem, prop, initialInUnit + unit ); // Update scale, tolerating zero or NaN from tween.cur() // Break the loop if scale is unchanged or perfect, or if we've just had enough. } while ( scale !== ( scale = currentValue() / initial ) && scale !== 1 && --maxIterations ); } if ( valueParts ) { initialInUnit = +initialInUnit || +initial || 0; // Apply relative offset (+=/-=) if specified adjusted = valueParts[ 1 ] ? initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] : +valueParts[ 2 ]; if ( tween ) { tween.unit = unit; tween.start = initialInUnit; tween.end = adjusted; } } return adjusted; } var defaultDisplayMap = {}; function getDefaultDisplay( elem ) { var temp, doc = elem.ownerDocument, nodeName = elem.nodeName, display = defaultDisplayMap[ nodeName ]; if ( display ) { return display; } temp = doc.body.appendChild( doc.createElement( nodeName ) ); display = jQuery.css( temp, "display" ); temp.parentNode.removeChild( temp ); if ( display === "none" ) { display = "block"; } defaultDisplayMap[ nodeName ] = display; return display; } function showHide( elements, show ) { var display, elem, values = [], index = 0, length = elements.length; // Determine new display value for elements that need to change for ( ; index < length; index++ ) { elem = elements[ index ]; if ( !elem.style ) { continue; } display = elem.style.display; if ( show ) { // Since we force visibility upon cascade-hidden elements, an immediate (and slow) // check is required in this first loop unless we have a nonempty display value (either // inline or about-to-be-restored) if ( display === "none" ) { values[ index ] = dataPriv.get( elem, "display" ) || null; if ( !values[ index ] ) { elem.style.display = ""; } } if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) { values[ index ] = getDefaultDisplay( elem ); } } else { if ( display !== "none" ) { values[ index ] = "none"; // Remember what we're overwriting dataPriv.set( elem, "display", display ); } } } // Set the display of the elements in a second loop to avoid constant reflow for ( index = 0; index < length; index++ ) { if ( values[ index ] != null ) { elements[ index ].style.display = values[ index ]; } } return elements; } jQuery.fn.extend( { show: function() { return showHide( this, true ); }, hide: function() { return showHide( this ); }, toggle: function( state ) { if ( typeof state === "boolean" ) { return state ? this.show() : this.hide(); } return this.each( function() { if ( isHiddenWithinTree( this ) ) { jQuery( this ).show(); } else { jQuery( this ).hide(); } } ); } } ); var rcheckableType = ( /^(?:checkbox|radio)$/i ); var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]+)/i ); var rscriptType = ( /^$|\/(?:java|ecma)script/i ); // We have to close these tags to support XHTML (#13200) var wrapMap = { // Support: IE <=9 only option: [ 1, "<select multiple='multiple'>", "</select>" ], // XHTML parsers do not magically insert elements in the // same way that tag soup parsers do. So we cannot shorten // this by omitting <tbody> or other required elements. thead: [ 1, "<table>", "</table>" ], col: [ 2, "<table><colgroup>", "</colgroup></table>" ], tr: [ 2, "<table><tbody>", "</tbody></table>" ], td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ], _default: [ 0, "", "" ] }; // Support: IE <=9 only wrapMap.optgroup = wrapMap.option; wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; wrapMap.th = wrapMap.td; function getAll( context, tag ) { // Support: IE <=9 - 11 only // Use typeof to avoid zero-argument method invocation on host objects (#15151) var ret; if ( typeof context.getElementsByTagName !== "undefined" ) { ret = context.getElementsByTagName( tag || "*" ); } else if ( typeof context.querySelectorAll !== "undefined" ) { ret = context.querySelectorAll( tag || "*" ); } else { ret = []; } if ( tag === undefined || tag && jQuery.nodeName( context, tag ) ) { return jQuery.merge( [ context ], ret ); } return ret; } // Mark scripts as having already been evaluated function setGlobalEval( elems, refElements ) { var i = 0, l = elems.length; for ( ; i < l; i++ ) { dataPriv.set( elems[ i ], "globalEval", !refElements || dataPriv.get( refElements[ i ], "globalEval" ) ); } } var rhtml = /<|&#?\w+;/; function buildFragment( elems, context, scripts, selection, ignored ) { var elem, tmp, tag, wrap, contains, j, fragment = context.createDocumentFragment(), nodes = [], i = 0, l = elems.length; for ( ; i < l; i++ ) { elem = elems[ i ]; if ( elem || elem === 0 ) { // Add nodes directly if ( jQuery.type( elem ) === "object" ) { // Support: Android <=4.0 only, PhantomJS 1 only // push.apply(_, arraylike) throws on ancient WebKit jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); // Convert non-html into a text node } else if ( !rhtml.test( elem ) ) { nodes.push( context.createTextNode( elem ) ); // Convert html into DOM nodes } else { tmp = tmp || fragment.appendChild( context.createElement( "div" ) ); // Deserialize a standard representation tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); wrap = wrapMap[ tag ] || wrapMap._default; tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ]; // Descend through wrappers to the right content j = wrap[ 0 ]; while ( j-- ) { tmp = tmp.lastChild; } // Support: Android <=4.0 only, PhantomJS 1 only // push.apply(_, arraylike) throws on ancient WebKit jQuery.merge( nodes, tmp.childNodes ); // Remember the top-level container tmp = fragment.firstChild; // Ensure the created nodes are orphaned (#12392) tmp.textContent = ""; } } } // Remove wrapper from fragment fragment.textContent = ""; i = 0; while ( ( elem = nodes[ i++ ] ) ) { // Skip elements already in the context collection (trac-4087) if ( selection && jQuery.inArray( elem, selection ) > -1 ) { if ( ignored ) { ignored.push( elem ); } continue; } contains = jQuery.contains( elem.ownerDocument, elem ); // Append to fragment tmp = getAll( fragment.appendChild( elem ), "script" ); // Preserve script evaluation history if ( contains ) { setGlobalEval( tmp ); } // Capture executables if ( scripts ) { j = 0; while ( ( elem = tmp[ j++ ] ) ) { if ( rscriptType.test( elem.type || "" ) ) { scripts.push( elem ); } } } } return fragment; } ( function() { var fragment = document.createDocumentFragment(), div = fragment.appendChild( document.createElement( "div" ) ), input = document.createElement( "input" ); // Support: Android 4.0 - 4.3 only // Check state lost if the name is set (#11217) // Support: Windows Web Apps (WWA) // `name` and `type` must use .setAttribute for WWA (#14901) input.setAttribute( "type", "radio" ); input.setAttribute( "checked", "checked" ); input.setAttribute( "name", "t" ); div.appendChild( input ); // Support: Android <=4.1 only // Older WebKit doesn't clone checked state correctly in fragments support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; // Support: IE <=11 only // Make sure textarea (and checkbox) defaultValue is properly cloned div.innerHTML = "<textarea>x</textarea>"; support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; } )(); var documentElement = document.documentElement; var rkeyEvent = /^key/, rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/, rtypenamespace = /^([^.]*)(?:\.(.+)|)/; function returnTrue() { return true; } function returnFalse() { return false; } // Support: IE <=9 only // See #13393 for more info function safeActiveElement() { try { return document.activeElement; } catch ( err ) { } } function on( elem, types, selector, data, fn, one ) { var origFn, type; // Types can be a map of types/handlers if ( typeof types === "object" ) { // ( types-Object, selector, data ) if ( typeof selector !== "string" ) { // ( types-Object, data ) data = data || selector; selector = undefined; } for ( type in types ) { on( elem, type, selector, data, types[ type ], one ); } return elem; } if ( data == null && fn == null ) { // ( types, fn ) fn = selector; data = selector = undefined; } else if ( fn == null ) { if ( typeof selector === "string" ) { // ( types, selector, fn ) fn = data; data = undefined; } else { // ( types, data, fn ) fn = data; data = selector; selector = undefined; } } if ( fn === false ) { fn = returnFalse; } else if ( !fn ) { return elem; } if ( one === 1 ) { origFn = fn; fn = function( event ) { // Can use an empty set, since event contains the info jQuery().off( event ); return origFn.apply( this, arguments ); }; // Use same guid so caller can remove using origFn fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); } return elem.each( function() { jQuery.event.add( this, types, fn, data, selector ); } ); } /* * Helper functions for managing events -- not part of the public interface. * Props to Dean Edwards' addEvent library for many of the ideas. */ jQuery.event = { global: {}, add: function( elem, types, handler, data, selector ) { var handleObjIn, eventHandle, tmp, events, t, handleObj, special, handlers, type, namespaces, origType, elemData = dataPriv.get( elem ); // Don't attach events to noData or text/comment nodes (but allow plain objects) if ( !elemData ) { return; } // Caller can pass in an object of custom data in lieu of the handler if ( handler.handler ) { handleObjIn = handler; handler = handleObjIn.handler; selector = handleObjIn.selector; } // Ensure that invalid selectors throw exceptions at attach time // Evaluate against documentElement in case elem is a non-element node (e.g., document) if ( selector ) { jQuery.find.matchesSelector( documentElement, selector ); } // Make sure that the handler has a unique ID, used to find/remove it later if ( !handler.guid ) { handler.guid = jQuery.guid++; } // Init the element's event structure and main handler, if this is the first if ( !( events = elemData.events ) ) { events = elemData.events = {}; } if ( !( eventHandle = elemData.handle ) ) { eventHandle = elemData.handle = function( e ) { // Discard the second event of a jQuery.event.trigger() and // when an event is called after a page has unloaded return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? jQuery.event.dispatch.apply( elem, arguments ) : undefined; }; } // Handle multiple events separated by a space types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; t = types.length; while ( t-- ) { tmp = rtypenamespace.exec( types[ t ] ) || []; type = origType = tmp[ 1 ]; namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); // There *must* be a type, no attaching namespace-only handlers if ( !type ) { continue; } // If event changes its type, use the special event handlers for the changed type special = jQuery.event.special[ type ] || {}; // If selector defined, determine special event api type, otherwise given type type = ( selector ? special.delegateType : special.bindType ) || type; // Update special based on newly reset type special = jQuery.event.special[ type ] || {}; // handleObj is passed to all event handlers handleObj = jQuery.extend( { type: type, origType: origType, data: data, handler: handler, guid: handler.guid, selector: selector, needsContext: selector && jQuery.expr.match.needsContext.test( selector ), namespace: namespaces.join( "." ) }, handleObjIn ); // Init the event handler queue if we're the first if ( !( handlers = events[ type ] ) ) { handlers = events[ type ] = []; handlers.delegateCount = 0; // Only use addEventListener if the special events handler returns false if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { if ( elem.addEventListener ) { elem.addEventListener( type, eventHandle ); } } } if ( special.add ) { special.add.call( elem, handleObj ); if ( !handleObj.handler.guid ) { handleObj.handler.guid = handler.guid; } } // Add to the element's handler list, delegates in front if ( selector ) { handlers.splice( handlers.delegateCount++, 0, handleObj ); } else { handlers.push( handleObj ); } // Keep track of which events have ever been used, for event optimization jQuery.event.global[ type ] = true; } }, // Detach an event or set of events from an element remove: function( elem, types, handler, selector, mappedTypes ) { var j, origCount, tmp, events, t, handleObj, special, handlers, type, namespaces, origType, elemData = dataPriv.hasData( elem ) && dataPriv.get( elem ); if ( !elemData || !( events = elemData.events ) ) { return; } // Once for each type.namespace in types; type may be omitted types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; t = types.length; while ( t-- ) { tmp = rtypenamespace.exec( types[ t ] ) || []; type = origType = tmp[ 1 ]; namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); // Unbind all events (on this namespace, if provided) for the element if ( !type ) { for ( type in events ) { jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); } continue; } special = jQuery.event.special[ type ] || {}; type = ( selector ? special.delegateType : special.bindType ) || type; handlers = events[ type ] || []; tmp = tmp[ 2 ] && new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); // Remove matching events origCount = j = handlers.length; while ( j-- ) { handleObj = handlers[ j ]; if ( ( mappedTypes || origType === handleObj.origType ) && ( !handler || handler.guid === handleObj.guid ) && ( !tmp || tmp.test( handleObj.namespace ) ) && ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { handlers.splice( j, 1 ); if ( handleObj.selector ) { handlers.delegateCount--; } if ( special.remove ) { special.remove.call( elem, handleObj ); } } } // Remove generic event handler if we removed something and no more handlers exist // (avoids potential for endless recursion during removal of special event handlers) if ( origCount && !handlers.length ) { if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) { jQuery.removeEvent( elem, type, elemData.handle ); } delete events[ type ]; } } // Remove data and the expando if it's no longer used if ( jQuery.isEmptyObject( events ) ) { dataPriv.remove( elem, "handle events" ); } }, dispatch: function( nativeEvent ) { // Make a writable jQuery.Event from the native event object var event = jQuery.event.fix( nativeEvent ); var i, j, ret, matched, handleObj, handlerQueue, args = new Array( arguments.length ), handlers = ( dataPriv.get( this, "events" ) || {} )[ event.type ] || [], special = jQuery.event.special[ event.type ] || {}; // Use the fix-ed jQuery.Event rather than the (read-only) native event args[ 0 ] = event; for ( i = 1; i < arguments.length; i++ ) { args[ i ] = arguments[ i ]; } event.delegateTarget = this; // Call the preDispatch hook for the mapped type, and let it bail if desired if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { return; } // Determine handlers handlerQueue = jQuery.event.handlers.call( this, event, handlers ); // Run delegates first; they may want to stop propagation beneath us i = 0; while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { event.currentTarget = matched.elem; j = 0; while ( ( handleObj = matched.handlers[ j++ ] ) && !event.isImmediatePropagationStopped() ) { // Triggered event must either 1) have no namespace, or 2) have namespace(s) // a subset or equal to those in the bound event (both can have no namespace). if ( !event.rnamespace || event.rnamespace.test( handleObj.namespace ) ) { event.handleObj = handleObj; event.data = handleObj.data; ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || handleObj.handler ).apply( matched.elem, args ); if ( ret !== undefined ) { if ( ( event.result = ret ) === false ) { event.preventDefault(); event.stopPropagation(); } } } } } // Call the postDispatch hook for the mapped type if ( special.postDispatch ) { special.postDispatch.call( this, event ); } return event.result; }, handlers: function( event, handlers ) { var i, handleObj, sel, matchedHandlers, matchedSelectors, handlerQueue = [], delegateCount = handlers.delegateCount, cur = event.target; // Find delegate handlers if ( delegateCount && // Support: IE <=9 // Black-hole SVG <use> instance trees (trac-13180) cur.nodeType && // Support: Firefox <=42 // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861) // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click // Support: IE 11 only // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343) !( event.type === "click" && event.button >= 1 ) ) { for ( ; cur !== this; cur = cur.parentNode || this ) { // Don't check non-elements (#13208) // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) { matchedHandlers = []; matchedSelectors = {}; for ( i = 0; i < delegateCount; i++ ) { handleObj = handlers[ i ]; // Don't conflict with Object.prototype properties (#13203) sel = handleObj.selector + " "; if ( matchedSelectors[ sel ] === undefined ) { matchedSelectors[ sel ] = handleObj.needsContext ? jQuery( sel, this ).index( cur ) > -1 : jQuery.find( sel, this, null, [ cur ] ).length; } if ( matchedSelectors[ sel ] ) { matchedHandlers.push( handleObj ); } } if ( matchedHandlers.length ) { handlerQueue.push( { elem: cur, handlers: matchedHandlers } ); } } } } // Add the remaining (directly-bound) handlers cur = this; if ( delegateCount < handlers.length ) { handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } ); } return handlerQueue; }, addProp: function( name, hook ) { Object.defineProperty( jQuery.Event.prototype, name, { enumerable: true, configurable: true, get: jQuery.isFunction( hook ) ? function() { if ( this.originalEvent ) { return hook( this.originalEvent ); } } : function() { if ( this.originalEvent ) { return this.originalEvent[ name ]; } }, set: function( value ) { Object.defineProperty( this, name, { enumerable: true, configurable: true, writable: true, value: value } ); } } ); }, fix: function( originalEvent ) { return originalEvent[ jQuery.expando ] ? originalEvent : new jQuery.Event( originalEvent ); }, special: { load: { // Prevent triggered image.load events from bubbling to window.load noBubble: true }, focus: { // Fire native event if possible so blur/focus sequence is correct trigger: function() { if ( this !== safeActiveElement() && this.focus ) { this.focus(); return false; } }, delegateType: "focusin" }, blur: { trigger: function() { if ( this === safeActiveElement() && this.blur ) { this.blur(); return false; } }, delegateType: "focusout" }, click: { // For checkbox, fire native event so checked state will be right trigger: function() { if ( this.type === "checkbox" && this.click && jQuery.nodeName( this, "input" ) ) { this.click(); return false; } }, // For cross-browser consistency, don't fire native .click() on links _default: function( event ) { return jQuery.nodeName( event.target, "a" ); } }, beforeunload: { postDispatch: function( event ) { // Support: Firefox 20+ // Firefox doesn't alert if the returnValue field is not set. if ( event.result !== undefined && event.originalEvent ) { event.originalEvent.returnValue = event.result; } } } } }; jQuery.removeEvent = function( elem, type, handle ) { // This "if" is needed for plain objects if ( elem.removeEventListener ) { elem.removeEventListener( type, handle ); } }; jQuery.Event = function( src, props ) { // Allow instantiation without the 'new' keyword if ( !( this instanceof jQuery.Event ) ) { return new jQuery.Event( src, props ); } // Event object if ( src && src.type ) { this.originalEvent = src; this.type = src.type; // Events bubbling up the document may have been marked as prevented // by a handler lower down the tree; reflect the correct value. this.isDefaultPrevented = src.defaultPrevented || src.defaultPrevented === undefined && // Support: Android <=2.3 only src.returnValue === false ? returnTrue : returnFalse; // Create target properties // Support: Safari <=6 - 7 only // Target should not be a text node (#504, #13143) this.target = ( src.target && src.target.nodeType === 3 ) ? src.target.parentNode : src.target; this.currentTarget = src.currentTarget; this.relatedTarget = src.relatedTarget; // Event type } else { this.type = src; } // Put explicitly provided properties onto the event object if ( props ) { jQuery.extend( this, props ); } // Create a timestamp if incoming event doesn't have one this.timeStamp = src && src.timeStamp || jQuery.now(); // Mark it as fixed this[ jQuery.expando ] = true; }; // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding // https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html jQuery.Event.prototype = { constructor: jQuery.Event, isDefaultPrevented: returnFalse, isPropagationStopped: returnFalse, isImmediatePropagationStopped: returnFalse, isSimulated: false, preventDefault: function() { var e = this.originalEvent; this.isDefaultPrevented = returnTrue; if ( e && !this.isSimulated ) { e.preventDefault(); } }, stopPropagation: function() { var e = this.originalEvent; this.isPropagationStopped = returnTrue; if ( e && !this.isSimulated ) { e.stopPropagation(); } }, stopImmediatePropagation: function() { var e = this.originalEvent; this.isImmediatePropagationStopped = returnTrue; if ( e && !this.isSimulated ) { e.stopImmediatePropagation(); } this.stopPropagation(); } }; // Includes all common event props including KeyEvent and MouseEvent specific props jQuery.each( { altKey: true, bubbles: true, cancelable: true, changedTouches: true, ctrlKey: true, detail: true, eventPhase: true, metaKey: true, pageX: true, pageY: true, shiftKey: true, view: true, "char": true, charCode: true, key: true, keyCode: true, button: true, buttons: true, clientX: true, clientY: true, offsetX: true, offsetY: true, pointerId: true, pointerType: true, screenX: true, screenY: true, targetTouches: true, toElement: true, touches: true, which: function( event ) { var button = event.button; // Add which for key events if ( event.which == null && rkeyEvent.test( event.type ) ) { return event.charCode != null ? event.charCode : event.keyCode; } // Add which for click: 1 === left; 2 === middle; 3 === right if ( !event.which && button !== undefined && rmouseEvent.test( event.type ) ) { if ( button & 1 ) { return 1; } if ( button & 2 ) { return 3; } if ( button & 4 ) { return 2; } return 0; } return event.which; } }, jQuery.event.addProp ); // Create mouseenter/leave events using mouseover/out and event-time checks // so that event delegation works in jQuery. // Do the same for pointerenter/pointerleave and pointerover/pointerout // // Support: Safari 7 only // Safari sends mouseenter too often; see: // https://bugs.chromium.org/p/chromium/issues/detail?id=470258 // for the description of the bug (it existed in older Chrome versions as well). jQuery.each( { mouseenter: "mouseover", mouseleave: "mouseout", pointerenter: "pointerover", pointerleave: "pointerout" }, function( orig, fix ) { jQuery.event.special[ orig ] = { delegateType: fix, bindType: fix, handle: function( event ) { var ret, target = this, related = event.relatedTarget, handleObj = event.handleObj; // For mouseenter/leave call the handler if related is outside the target. // NB: No relatedTarget if the mouse left/entered the browser window if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { event.type = handleObj.origType; ret = handleObj.handler.apply( this, arguments ); event.type = fix; } return ret; } }; } ); jQuery.fn.extend( { on: function( types, selector, data, fn ) { return on( this, types, selector, data, fn ); }, one: function( types, selector, data, fn ) { return on( this, types, selector, data, fn, 1 ); }, off: function( types, selector, fn ) { var handleObj, type; if ( types && types.preventDefault && types.handleObj ) { // ( event ) dispatched jQuery.Event handleObj = types.handleObj; jQuery( types.delegateTarget ).off( handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, handleObj.selector, handleObj.handler ); return this; } if ( typeof types === "object" ) { // ( types-object [, selector] ) for ( type in types ) { this.off( type, selector, types[ type ] ); } return this; } if ( selector === false || typeof selector === "function" ) { // ( types [, fn] ) fn = selector; selector = undefined; } if ( fn === false ) { fn = returnFalse; } return this.each( function() { jQuery.event.remove( this, types, fn, selector ); } ); } } ); var /* eslint-disable max-len */ // See https://github.com/eslint/eslint/issues/3229 rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi, /* eslint-enable */ // Support: IE <=10 - 11, Edge 12 - 13 // In IE/Edge using regex groups here causes severe slowdowns. // See https://connect.microsoft.com/IE/feedback/details/1736512/ rnoInnerhtml = /<script|<style|<link/i, // checked="checked" or checked rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, rscriptTypeMasked = /^true\/(.*)/, rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g; function manipulationTarget( elem, content ) { if ( jQuery.nodeName( elem, "table" ) && jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) { return elem.getElementsByTagName( "tbody" )[ 0 ] || elem; } return elem; } // Replace/restore the type attribute of script elements for safe DOM manipulation function disableScript( elem ) { elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type; return elem; } function restoreScript( elem ) { var match = rscriptTypeMasked.exec( elem.type ); if ( match ) { elem.type = match[ 1 ]; } else { elem.removeAttribute( "type" ); } return elem; } function cloneCopyEvent( src, dest ) { var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events; if ( dest.nodeType !== 1 ) { return; } // 1. Copy private data: events, handlers, etc. if ( dataPriv.hasData( src ) ) { pdataOld = dataPriv.access( src ); pdataCur = dataPriv.set( dest, pdataOld ); events = pdataOld.events; if ( events ) { delete pdataCur.handle; pdataCur.events = {}; for ( type in events ) { for ( i = 0, l = events[ type ].length; i < l; i++ ) { jQuery.event.add( dest, type, events[ type ][ i ] ); } } } } // 2. Copy user data if ( dataUser.hasData( src ) ) { udataOld = dataUser.access( src ); udataCur = jQuery.extend( {}, udataOld ); dataUser.set( dest, udataCur ); } } // Fix IE bugs, see support tests function fixInput( src, dest ) { var nodeName = dest.nodeName.toLowerCase(); // Fails to persist the checked state of a cloned checkbox or radio button. if ( nodeName === "input" && rcheckableType.test( src.type ) ) { dest.checked = src.checked; // Fails to return the selected option to the default selected state when cloning options } else if ( nodeName === "input" || nodeName === "textarea" ) { dest.defaultValue = src.defaultValue; } } function domManip( collection, args, callback, ignored ) { // Flatten any nested arrays args = concat.apply( [], args ); var fragment, first, scripts, hasScripts, node, doc, i = 0, l = collection.length, iNoClone = l - 1, value = args[ 0 ], isFunction = jQuery.isFunction( value ); // We can't cloneNode fragments that contain checked, in WebKit if ( isFunction || ( l > 1 && typeof value === "string" && !support.checkClone && rchecked.test( value ) ) ) { return collection.each( function( index ) { var self = collection.eq( index ); if ( isFunction ) { args[ 0 ] = value.call( this, index, self.html() ); } domManip( self, args, callback, ignored ); } ); } if ( l ) { fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); first = fragment.firstChild; if ( fragment.childNodes.length === 1 ) { fragment = first; } // Require either new content or an interest in ignored elements to invoke the callback if ( first || ignored ) { scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); hasScripts = scripts.length; // Use the original fragment for the last item // instead of the first because it can end up // being emptied incorrectly in certain situations (#8070). for ( ; i < l; i++ ) { node = fragment; if ( i !== iNoClone ) { node = jQuery.clone( node, true, true ); // Keep references to cloned scripts for later restoration if ( hasScripts ) { // Support: Android <=4.0 only, PhantomJS 1 only // push.apply(_, arraylike) throws on ancient WebKit jQuery.merge( scripts, getAll( node, "script" ) ); } } callback.call( collection[ i ], node, i ); } if ( hasScripts ) { doc = scripts[ scripts.length - 1 ].ownerDocument; // Reenable scripts jQuery.map( scripts, restoreScript ); // Evaluate executable scripts on first document insertion for ( i = 0; i < hasScripts; i++ ) { node = scripts[ i ]; if ( rscriptType.test( node.type || "" ) && !dataPriv.access( node, "globalEval" ) && jQuery.contains( doc, node ) ) { if ( node.src ) { // Optional AJAX dependency, but won't run scripts if not present if ( jQuery._evalUrl ) { jQuery._evalUrl( node.src ); } } else { DOMEval( node.textContent.replace( rcleanScript, "" ), doc ); } } } } } } return collection; } function remove( elem, selector, keepData ) { var node, nodes = selector ? jQuery.filter( selector, elem ) : elem, i = 0; for ( ; ( node = nodes[ i ] ) != null; i++ ) { if ( !keepData && node.nodeType === 1 ) { jQuery.cleanData( getAll( node ) ); } if ( node.parentNode ) { if ( keepData && jQuery.contains( node.ownerDocument, node ) ) { setGlobalEval( getAll( node, "script" ) ); } node.parentNode.removeChild( node ); } } return elem; } jQuery.extend( { htmlPrefilter: function( html ) { return html.replace( rxhtmlTag, "<$1></$2>" ); }, clone: function( elem, dataAndEvents, deepDataAndEvents ) { var i, l, srcElements, destElements, clone = elem.cloneNode( true ), inPage = jQuery.contains( elem.ownerDocument, elem ); // Fix IE cloning issues if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && !jQuery.isXMLDoc( elem ) ) { // We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2 destElements = getAll( clone ); srcElements = getAll( elem ); for ( i = 0, l = srcElements.length; i < l; i++ ) { fixInput( srcElements[ i ], destElements[ i ] ); } } // Copy the events from the original to the clone if ( dataAndEvents ) { if ( deepDataAndEvents ) { srcElements = srcElements || getAll( elem ); destElements = destElements || getAll( clone ); for ( i = 0, l = srcElements.length; i < l; i++ ) { cloneCopyEvent( srcElements[ i ], destElements[ i ] ); } } else { cloneCopyEvent( elem, clone ); } } // Preserve script evaluation history destElements = getAll( clone, "script" ); if ( destElements.length > 0 ) { setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); } // Return the cloned set return clone; }, cleanData: function( elems ) { var data, elem, type, special = jQuery.event.special, i = 0; for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) { if ( acceptData( elem ) ) { if ( ( data = elem[ dataPriv.expando ] ) ) { if ( data.events ) { for ( type in data.events ) { if ( special[ type ] ) { jQuery.event.remove( elem, type ); // This is a shortcut to avoid jQuery.event.remove's overhead } else { jQuery.removeEvent( elem, type, data.handle ); } } } // Support: Chrome <=35 - 45+ // Assign undefined instead of using delete, see Data#remove elem[ dataPriv.expando ] = undefined; } if ( elem[ dataUser.expando ] ) { // Support: Chrome <=35 - 45+ // Assign undefined instead of using delete, see Data#remove elem[ dataUser.expando ] = undefined; } } } } } ); jQuery.fn.extend( { detach: function( selector ) { return remove( this, selector, true ); }, remove: function( selector ) { return remove( this, selector ); }, text: function( value ) { return access( this, function( value ) { return value === undefined ? jQuery.text( this ) : this.empty().each( function() { if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { this.textContent = value; } } ); }, null, value, arguments.length ); }, append: function() { return domManip( this, arguments, function( elem ) { if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { var target = manipulationTarget( this, elem ); target.appendChild( elem ); } } ); }, prepend: function() { return domManip( this, arguments, function( elem ) { if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { var target = manipulationTarget( this, elem ); target.insertBefore( elem, target.firstChild ); } } ); }, before: function() { return domManip( this, arguments, function( elem ) { if ( this.parentNode ) { this.parentNode.insertBefore( elem, this ); } } ); }, after: function() { return domManip( this, arguments, function( elem ) { if ( this.parentNode ) { this.parentNode.insertBefore( elem, this.nextSibling ); } } ); }, empty: function() { var elem, i = 0; for ( ; ( elem = this[ i ] ) != null; i++ ) { if ( elem.nodeType === 1 ) { // Prevent memory leaks jQuery.cleanData( getAll( elem, false ) ); // Remove any remaining nodes elem.textContent = ""; } } return this; }, clone: function( dataAndEvents, deepDataAndEvents ) { dataAndEvents = dataAndEvents == null ? false : dataAndEvents; deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; return this.map( function() { return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); } ); }, html: function( value ) { return access( this, function( value ) { var elem = this[ 0 ] || {}, i = 0, l = this.length; if ( value === undefined && elem.nodeType === 1 ) { return elem.innerHTML; } // See if we can take a shortcut and just use innerHTML if ( typeof value === "string" && !rnoInnerhtml.test( value ) && !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { value = jQuery.htmlPrefilter( value ); try { for ( ; i < l; i++ ) { elem = this[ i ] || {}; // Remove element nodes and prevent memory leaks if ( elem.nodeType === 1 ) { jQuery.cleanData( getAll( elem, false ) ); elem.innerHTML = value; } } elem = 0; // If using innerHTML throws an exception, use the fallback method } catch ( e ) {} } if ( elem ) { this.empty().append( value ); } }, null, value, arguments.length ); }, replaceWith: function() { var ignored = []; // Make the changes, replacing each non-ignored context element with the new content return domManip( this, arguments, function( elem ) { var parent = this.parentNode; if ( jQuery.inArray( this, ignored ) < 0 ) { jQuery.cleanData( getAll( this ) ); if ( parent ) { parent.replaceChild( elem, this ); } } // Force callback invocation }, ignored ); } } ); jQuery.each( { appendTo: "append", prependTo: "prepend", insertBefore: "before", insertAfter: "after", replaceAll: "replaceWith" }, function( name, original ) { jQuery.fn[ name ] = function( selector ) { var elems, ret = [], insert = jQuery( selector ), last = insert.length - 1, i = 0; for ( ; i <= last; i++ ) { elems = i === last ? this : this.clone( true ); jQuery( insert[ i ] )[ original ]( elems ); // Support: Android <=4.0 only, PhantomJS 1 only // .get() because push.apply(_, arraylike) throws on ancient WebKit push.apply( ret, elems.get() ); } return this.pushStack( ret ); }; } ); var rmargin = ( /^margin/ ); var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); var getStyles = function( elem ) { // Support: IE <=11 only, Firefox <=30 (#15098, #14150) // IE throws on elements created in popups // FF meanwhile throws on frame elements through "defaultView.getComputedStyle" var view = elem.ownerDocument.defaultView; if ( !view || !view.opener ) { view = window; } return view.getComputedStyle( elem ); }; ( function() { // Executing both pixelPosition & boxSizingReliable tests require only one layout // so they're executed at the same time to save the second computation. function computeStyleTests() { // This is a singleton, we need to execute it only once if ( !div ) { return; } div.style.cssText = "box-sizing:border-box;" + "position:relative;display:block;" + "margin:auto;border:1px;padding:1px;" + "top:1%;width:50%"; div.innerHTML = ""; documentElement.appendChild( container ); var divStyle = window.getComputedStyle( div ); pixelPositionVal = divStyle.top !== "1%"; // Support: Android 4.0 - 4.3 only, Firefox <=3 - 44 reliableMarginLeftVal = divStyle.marginLeft === "2px"; boxSizingReliableVal = divStyle.width === "4px"; // Support: Android 4.0 - 4.3 only // Some styles come back with percentage values, even though they shouldn't div.style.marginRight = "50%"; pixelMarginRightVal = divStyle.marginRight === "4px"; documentElement.removeChild( container ); // Nullify the div so it wouldn't be stored in the memory and // it will also be a sign that checks already performed div = null; } var pixelPositionVal, boxSizingReliableVal, pixelMarginRightVal, reliableMarginLeftVal, container = document.createElement( "div" ), div = document.createElement( "div" ); // Finish early in limited (non-browser) environments if ( !div.style ) { return; } // Support: IE <=9 - 11 only // Style of cloned element affects source element cloned (#8908) div.style.backgroundClip = "content-box"; div.cloneNode( true ).style.backgroundClip = ""; support.clearCloneStyle = div.style.backgroundClip === "content-box"; container.style.cssText = "border:0;width:8px;height:0;top:0;left:-9999px;" + "padding:0;margin-top:1px;position:absolute"; container.appendChild( div ); jQuery.extend( support, { pixelPosition: function() { computeStyleTests(); return pixelPositionVal; }, boxSizingReliable: function() { computeStyleTests(); return boxSizingReliableVal; }, pixelMarginRight: function() { computeStyleTests(); return pixelMarginRightVal; }, reliableMarginLeft: function() { computeStyleTests(); return reliableMarginLeftVal; } } ); } )(); function curCSS( elem, name, computed ) { var width, minWidth, maxWidth, ret, style = elem.style; computed = computed || getStyles( elem ); // Support: IE <=9 only // getPropertyValue is only needed for .css('filter') (#12537) if ( computed ) { ret = computed.getPropertyValue( name ) || computed[ name ]; if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) { ret = jQuery.style( elem, name ); } // A tribute to the "awesome hack by Dean Edwards" // Android Browser returns percentage for some values, // but width seems to be reliably pixels. // This is against the CSSOM draft spec: // https://drafts.csswg.org/cssom/#resolved-values if ( !support.pixelMarginRight() && rnumnonpx.test( ret ) && rmargin.test( name ) ) { // Remember the original values width = style.width; minWidth = style.minWidth; maxWidth = style.maxWidth; // Put in the new values to get a computed value out style.minWidth = style.maxWidth = style.width = ret; ret = computed.width; // Revert the changed values style.width = width; style.minWidth = minWidth; style.maxWidth = maxWidth; } } return ret !== undefined ? // Support: IE <=9 - 11 only // IE returns zIndex value as an integer. ret + "" : ret; } function addGetHookIf( conditionFn, hookFn ) { // Define the hook, we'll check on the first run if it's really needed. return { get: function() { if ( conditionFn() ) { // Hook not needed (or it's not possible to use it due // to missing dependency), remove it. delete this.get; return; } // Hook needed; redefine it so that the support test is not executed again. return ( this.get = hookFn ).apply( this, arguments ); } }; } var // Swappable if display is none or starts with table // except "table", "table-cell", or "table-caption" // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display rdisplayswap = /^(none|table(?!-c[ea]).+)/, cssShow = { position: "absolute", visibility: "hidden", display: "block" }, cssNormalTransform = { letterSpacing: "0", fontWeight: "400" }, cssPrefixes = [ "Webkit", "Moz", "ms" ], emptyStyle = document.createElement( "div" ).style; // Return a css property mapped to a potentially vendor prefixed property function vendorPropName( name ) { // Shortcut for names that are not vendor prefixed if ( name in emptyStyle ) { return name; } // Check for vendor prefixed names var capName = name[ 0 ].toUpperCase() + name.slice( 1 ), i = cssPrefixes.length; while ( i-- ) { name = cssPrefixes[ i ] + capName; if ( name in emptyStyle ) { return name; } } } function setPositiveNumber( elem, value, subtract ) { // Any relative (+/-) values have already been // normalized at this point var matches = rcssNum.exec( value ); return matches ? // Guard against undefined "subtract", e.g., when used as in cssHooks Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) : value; } function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) { var i, val = 0; // If we already have the right measurement, avoid augmentation if ( extra === ( isBorderBox ? "border" : "content" ) ) { i = 4; // Otherwise initialize for horizontal or vertical properties } else { i = name === "width" ? 1 : 0; } for ( ; i < 4; i += 2 ) { // Both box models exclude margin, so add it if we want it if ( extra === "margin" ) { val += jQuery.css( elem, extra + cssExpand[ i ], true, styles ); } if ( isBorderBox ) { // border-box includes padding, so remove it if we want content if ( extra === "content" ) { val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); } // At this point, extra isn't border nor margin, so remove border if ( extra !== "margin" ) { val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); } } else { // At this point, extra isn't content, so add padding val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); // At this point, extra isn't content nor padding, so add border if ( extra !== "padding" ) { val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); } } } return val; } function getWidthOrHeight( elem, name, extra ) { // Start with offset property, which is equivalent to the border-box value var val, valueIsBorderBox = true, styles = getStyles( elem ), isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; // Support: IE <=11 only // Running getBoundingClientRect on a disconnected node // in IE throws an error. if ( elem.getClientRects().length ) { val = elem.getBoundingClientRect()[ name ]; } // Some non-html elements return undefined for offsetWidth, so check for null/undefined // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285 // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668 if ( val <= 0 || val == null ) { // Fall back to computed then uncomputed css if necessary val = curCSS( elem, name, styles ); if ( val < 0 || val == null ) { val = elem.style[ name ]; } // Computed unit is not pixels. Stop here and return. if ( rnumnonpx.test( val ) ) { return val; } // Check for style in case a browser which returns unreliable values // for getComputedStyle silently falls back to the reliable elem.style valueIsBorderBox = isBorderBox && ( support.boxSizingReliable() || val === elem.style[ name ] ); // Normalize "", auto, and prepare for extra val = parseFloat( val ) || 0; } // Use the active box-sizing model to add/subtract irrelevant styles return ( val + augmentWidthOrHeight( elem, name, extra || ( isBorderBox ? "border" : "content" ), valueIsBorderBox, styles ) ) + "px"; } jQuery.extend( { // Add in style property hooks for overriding the default // behavior of getting and setting a style property cssHooks: { opacity: { get: function( elem, computed ) { if ( computed ) { // We should always get a number back from opacity var ret = curCSS( elem, "opacity" ); return ret === "" ? "1" : ret; } } } }, // Don't automatically add "px" to these possibly-unitless properties cssNumber: { "animationIterationCount": true, "columnCount": true, "fillOpacity": true, "flexGrow": true, "flexShrink": true, "fontWeight": true, "lineHeight": true, "opacity": true, "order": true, "orphans": true, "widows": true, "zIndex": true, "zoom": true }, // Add in properties whose names you wish to fix before // setting or getting the value cssProps: { "float": "cssFloat" }, // Get and set the style property on a DOM Node style: function( elem, name, value, extra ) { // Don't set styles on text and comment nodes if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { return; } // Make sure that we're working with the right name var ret, type, hooks, origName = jQuery.camelCase( name ), style = elem.style; name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( origName ) || origName ); // Gets hook for the prefixed version, then unprefixed version hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; // Check if we're setting a value if ( value !== undefined ) { type = typeof value; // Convert "+=" or "-=" to relative numbers (#7345) if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) { value = adjustCSS( elem, name, ret ); // Fixes bug #9237 type = "number"; } // Make sure that null and NaN values aren't set (#7116) if ( value == null || value !== value ) { return; } // If a number was passed in, add the unit (except for certain CSS properties) if ( type === "number" ) { value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" ); } // background-* props affect original clone's values if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) { style[ name ] = "inherit"; } // If a hook was provided, use that value, otherwise just set the specified value if ( !hooks || !( "set" in hooks ) || ( value = hooks.set( elem, value, extra ) ) !== undefined ) { style[ name ] = value; } } else { // If a hook was provided get the non-computed value from there if ( hooks && "get" in hooks && ( ret = hooks.get( elem, false, extra ) ) !== undefined ) { return ret; } // Otherwise just get the value from the style object return style[ name ]; } }, css: function( elem, name, extra, styles ) { var val, num, hooks, origName = jQuery.camelCase( name ); // Make sure that we're working with the right name name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( origName ) || origName ); // Try prefixed name followed by the unprefixed name hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; // If a hook was provided get the computed value from there if ( hooks && "get" in hooks ) { val = hooks.get( elem, true, extra ); } // Otherwise, if a way to get the computed value exists, use that if ( val === undefined ) { val = curCSS( elem, name, styles ); } // Convert "normal" to computed value if ( val === "normal" && name in cssNormalTransform ) { val = cssNormalTransform[ name ]; } // Make numeric if forced or a qualifier was provided and val looks numeric if ( extra === "" || extra ) { num = parseFloat( val ); return extra === true || isFinite( num ) ? num || 0 : val; } return val; } } ); jQuery.each( [ "height", "width" ], function( i, name ) { jQuery.cssHooks[ name ] = { get: function( elem, computed, extra ) { if ( computed ) { // Certain elements can have dimension info if we invisibly show them // but it must have a current display style that would benefit return rdisplayswap.test( jQuery.css( elem, "display" ) ) && // Support: Safari 8+ // Table columns in Safari have non-zero offsetWidth & zero // getBoundingClientRect().width unless display is changed. // Support: IE <=11 only // Running getBoundingClientRect on a disconnected node // in IE throws an error. ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ? swap( elem, cssShow, function() { return getWidthOrHeight( elem, name, extra ); } ) : getWidthOrHeight( elem, name, extra ); } }, set: function( elem, value, extra ) { var matches, styles = extra && getStyles( elem ), subtract = extra && augmentWidthOrHeight( elem, name, extra, jQuery.css( elem, "boxSizing", false, styles ) === "border-box", styles ); // Convert to pixels if value adjustment is needed if ( subtract && ( matches = rcssNum.exec( value ) ) && ( matches[ 3 ] || "px" ) !== "px" ) { elem.style[ name ] = value; value = jQuery.css( elem, name ); } return setPositiveNumber( elem, value, subtract ); } }; } ); jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft, function( elem, computed ) { if ( computed ) { return ( parseFloat( curCSS( elem, "marginLeft" ) ) || elem.getBoundingClientRect().left - swap( elem, { marginLeft: 0 }, function() { return elem.getBoundingClientRect().left; } ) ) + "px"; } } ); // These hooks are used by animate to expand properties jQuery.each( { margin: "", padding: "", border: "Width" }, function( prefix, suffix ) { jQuery.cssHooks[ prefix + suffix ] = { expand: function( value ) { var i = 0, expanded = {}, // Assumes a single number if not a string parts = typeof value === "string" ? value.split( " " ) : [ value ]; for ( ; i < 4; i++ ) { expanded[ prefix + cssExpand[ i ] + suffix ] = parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; } return expanded; } }; if ( !rmargin.test( prefix ) ) { jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; } } ); jQuery.fn.extend( { css: function( name, value ) { return access( this, function( elem, name, value ) { var styles, len, map = {}, i = 0; if ( jQuery.isArray( name ) ) { styles = getStyles( elem ); len = name.length; for ( ; i < len; i++ ) { map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); } return map; } return value !== undefined ? jQuery.style( elem, name, value ) : jQuery.css( elem, name ); }, name, value, arguments.length > 1 ); } } ); function Tween( elem, options, prop, end, easing ) { return new Tween.prototype.init( elem, options, prop, end, easing ); } jQuery.Tween = Tween; Tween.prototype = { constructor: Tween, init: function( elem, options, prop, end, easing, unit ) { this.elem = elem; this.prop = prop; this.easing = easing || jQuery.easing._default; this.options = options; this.start = this.now = this.cur(); this.end = end; this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); }, cur: function() { var hooks = Tween.propHooks[ this.prop ]; return hooks && hooks.get ? hooks.get( this ) : Tween.propHooks._default.get( this ); }, run: function( percent ) { var eased, hooks = Tween.propHooks[ this.prop ]; if ( this.options.duration ) { this.pos = eased = jQuery.easing[ this.easing ]( percent, this.options.duration * percent, 0, 1, this.options.duration ); } else { this.pos = eased = percent; } this.now = ( this.end - this.start ) * eased + this.start; if ( this.options.step ) { this.options.step.call( this.elem, this.now, this ); } if ( hooks && hooks.set ) { hooks.set( this ); } else { Tween.propHooks._default.set( this ); } return this; } }; Tween.prototype.init.prototype = Tween.prototype; Tween.propHooks = { _default: { get: function( tween ) { var result; // Use a property on the element directly when it is not a DOM element, // or when there is no matching style property that exists. if ( tween.elem.nodeType !== 1 || tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) { return tween.elem[ tween.prop ]; } // Passing an empty string as a 3rd parameter to .css will automatically // attempt a parseFloat and fallback to a string if the parse fails. // Simple values such as "10px" are parsed to Float; // complex values such as "rotate(1rad)" are returned as-is. result = jQuery.css( tween.elem, tween.prop, "" ); // Empty strings, null, undefined and "auto" are converted to 0. return !result || result === "auto" ? 0 : result; }, set: function( tween ) { // Use step hook for back compat. // Use cssHook if its there. // Use .style if available and use plain properties where available. if ( jQuery.fx.step[ tween.prop ] ) { jQuery.fx.step[ tween.prop ]( tween ); } else if ( tween.elem.nodeType === 1 && ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || jQuery.cssHooks[ tween.prop ] ) ) { jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); } else { tween.elem[ tween.prop ] = tween.now; } } } }; // Support: IE <=9 only // Panic based approach to setting things on disconnected nodes Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { set: function( tween ) { if ( tween.elem.nodeType && tween.elem.parentNode ) { tween.elem[ tween.prop ] = tween.now; } } }; jQuery.easing = { linear: function( p ) { return p; }, swing: function( p ) { return 0.5 - Math.cos( p * Math.PI ) / 2; }, _default: "swing" }; jQuery.fx = Tween.prototype.init; // Back compat <1.8 extension point jQuery.fx.step = {}; var fxNow, timerId, rfxtypes = /^(?:toggle|show|hide)$/, rrun = /queueHooks$/; function raf() { if ( timerId ) { window.requestAnimationFrame( raf ); jQuery.fx.tick(); } } // Animations created synchronously will run synchronously function createFxNow() { window.setTimeout( function() { fxNow = undefined; } ); return ( fxNow = jQuery.now() ); } // Generate parameters to create a standard animation function genFx( type, includeWidth ) { var which, i = 0, attrs = { height: type }; // If we include width, step value is 1 to do all cssExpand values, // otherwise step value is 2 to skip over Left and Right includeWidth = includeWidth ? 1 : 0; for ( ; i < 4; i += 2 - includeWidth ) { which = cssExpand[ i ]; attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; } if ( includeWidth ) { attrs.opacity = attrs.width = type; } return attrs; } function createTween( value, prop, animation ) { var tween, collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ), index = 0, length = collection.length; for ( ; index < length; index++ ) { if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) { // We're done with this property return tween; } } } function defaultPrefilter( elem, props, opts ) { var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display, isBox = "width" in props || "height" in props, anim = this, orig = {}, style = elem.style, hidden = elem.nodeType && isHiddenWithinTree( elem ), dataShow = dataPriv.get( elem, "fxshow" ); // Queue-skipping animations hijack the fx hooks if ( !opts.queue ) { hooks = jQuery._queueHooks( elem, "fx" ); if ( hooks.unqueued == null ) { hooks.unqueued = 0; oldfire = hooks.empty.fire; hooks.empty.fire = function() { if ( !hooks.unqueued ) { oldfire(); } }; } hooks.unqueued++; anim.always( function() { // Ensure the complete handler is called before this completes anim.always( function() { hooks.unqueued--; if ( !jQuery.queue( elem, "fx" ).length ) { hooks.empty.fire(); } } ); } ); } // Detect show/hide animations for ( prop in props ) { value = props[ prop ]; if ( rfxtypes.test( value ) ) { delete props[ prop ]; toggle = toggle || value === "toggle"; if ( value === ( hidden ? "hide" : "show" ) ) { // Pretend to be hidden if this is a "show" and // there is still data from a stopped show/hide if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) { hidden = true; // Ignore all other no-op show/hide data } else { continue; } } orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop ); } } // Bail out if this is a no-op like .hide().hide() propTween = !jQuery.isEmptyObject( props ); if ( !propTween && jQuery.isEmptyObject( orig ) ) { return; } // Restrict "overflow" and "display" styles during box animations if ( isBox && elem.nodeType === 1 ) { // Support: IE <=9 - 11, Edge 12 - 13 // Record all 3 overflow attributes because IE does not infer the shorthand // from identically-valued overflowX and overflowY opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; // Identify a display type, preferring old show/hide data over the CSS cascade restoreDisplay = dataShow && dataShow.display; if ( restoreDisplay == null ) { restoreDisplay = dataPriv.get( elem, "display" ); } display = jQuery.css( elem, "display" ); if ( display === "none" ) { if ( restoreDisplay ) { display = restoreDisplay; } else { // Get nonempty value(s) by temporarily forcing visibility showHide( [ elem ], true ); restoreDisplay = elem.style.display || restoreDisplay; display = jQuery.css( elem, "display" ); showHide( [ elem ] ); } } // Animate inline elements as inline-block if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) { if ( jQuery.css( elem, "float" ) === "none" ) { // Restore the original display value at the end of pure show/hide animations if ( !propTween ) { anim.done( function() { style.display = restoreDisplay; } ); if ( restoreDisplay == null ) { display = style.display; restoreDisplay = display === "none" ? "" : display; } } style.display = "inline-block"; } } } if ( opts.overflow ) { style.overflow = "hidden"; anim.always( function() { style.overflow = opts.overflow[ 0 ]; style.overflowX = opts.overflow[ 1 ]; style.overflowY = opts.overflow[ 2 ]; } ); } // Implement show/hide animations propTween = false; for ( prop in orig ) { // General show/hide setup for this element animation if ( !propTween ) { if ( dataShow ) { if ( "hidden" in dataShow ) { hidden = dataShow.hidden; } } else { dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } ); } // Store hidden/visible for toggle so `.stop().toggle()` "reverses" if ( toggle ) { dataShow.hidden = !hidden; } // Show elements before animating them if ( hidden ) { showHide( [ elem ], true ); } /* eslint-disable no-loop-func */ anim.done( function() { /* eslint-enable no-loop-func */ // The final step of a "hide" animation is actually hiding the element if ( !hidden ) { showHide( [ elem ] ); } dataPriv.remove( elem, "fxshow" ); for ( prop in orig ) { jQuery.style( elem, prop, orig[ prop ] ); } } ); } // Per-property setup propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim ); if ( !( prop in dataShow ) ) { dataShow[ prop ] = propTween.start; if ( hidden ) { propTween.end = propTween.start; propTween.start = 0; } } } } function propFilter( props, specialEasing ) { var index, name, easing, value, hooks; // camelCase, specialEasing and expand cssHook pass for ( index in props ) { name = jQuery.camelCase( index ); easing = specialEasing[ name ]; value = props[ index ]; if ( jQuery.isArray( value ) ) { easing = value[ 1 ]; value = props[ index ] = value[ 0 ]; } if ( index !== name ) { props[ name ] = value; delete props[ index ]; } hooks = jQuery.cssHooks[ name ]; if ( hooks && "expand" in hooks ) { value = hooks.expand( value ); delete props[ name ]; // Not quite $.extend, this won't overwrite existing keys. // Reusing 'index' because we have the correct "name" for ( index in value ) { if ( !( index in props ) ) { props[ index ] = value[ index ]; specialEasing[ index ] = easing; } } } else { specialEasing[ name ] = easing; } } } function Animation( elem, properties, options ) { var result, stopped, index = 0, length = Animation.prefilters.length, deferred = jQuery.Deferred().always( function() { // Don't match elem in the :animated selector delete tick.elem; } ), tick = function() { if ( stopped ) { return false; } var currentTime = fxNow || createFxNow(), remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), // Support: Android 2.3 only // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497) temp = remaining / animation.duration || 0, percent = 1 - temp, index = 0, length = animation.tweens.length; for ( ; index < length; index++ ) { animation.tweens[ index ].run( percent ); } deferred.notifyWith( elem, [ animation, percent, remaining ] ); if ( percent < 1 && length ) { return remaining; } else { deferred.resolveWith( elem, [ animation ] ); return false; } }, animation = deferred.promise( { elem: elem, props: jQuery.extend( {}, properties ), opts: jQuery.extend( true, { specialEasing: {}, easing: jQuery.easing._default }, options ), originalProperties: properties, originalOptions: options, startTime: fxNow || createFxNow(), duration: options.duration, tweens: [], createTween: function( prop, end ) { var tween = jQuery.Tween( elem, animation.opts, prop, end, animation.opts.specialEasing[ prop ] || animation.opts.easing ); animation.tweens.push( tween ); return tween; }, stop: function( gotoEnd ) { var index = 0, // If we are going to the end, we want to run all the tweens // otherwise we skip this part length = gotoEnd ? animation.tweens.length : 0; if ( stopped ) { return this; } stopped = true; for ( ; index < length; index++ ) { animation.tweens[ index ].run( 1 ); } // Resolve when we played the last frame; otherwise, reject if ( gotoEnd ) { deferred.notifyWith( elem, [ animation, 1, 0 ] ); deferred.resolveWith( elem, [ animation, gotoEnd ] ); } else { deferred.rejectWith( elem, [ animation, gotoEnd ] ); } return this; } } ), props = animation.props; propFilter( props, animation.opts.specialEasing ); for ( ; index < length; index++ ) { result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts ); if ( result ) { if ( jQuery.isFunction( result.stop ) ) { jQuery._queueHooks( animation.elem, animation.opts.queue ).stop = jQuery.proxy( result.stop, result ); } return result; } } jQuery.map( props, createTween, animation ); if ( jQuery.isFunction( animation.opts.start ) ) { animation.opts.start.call( elem, animation ); } jQuery.fx.timer( jQuery.extend( tick, { elem: elem, anim: animation, queue: animation.opts.queue } ) ); // attach callbacks from options return animation.progress( animation.opts.progress ) .done( animation.opts.done, animation.opts.complete ) .fail( animation.opts.fail ) .always( animation.opts.always ); } jQuery.Animation = jQuery.extend( Animation, { tweeners: { "*": [ function( prop, value ) { var tween = this.createTween( prop, value ); adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween ); return tween; } ] }, tweener: function( props, callback ) { if ( jQuery.isFunction( props ) ) { callback = props; props = [ "*" ]; } else { props = props.match( rnothtmlwhite ); } var prop, index = 0, length = props.length; for ( ; index < length; index++ ) { prop = props[ index ]; Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || []; Animation.tweeners[ prop ].unshift( callback ); } }, prefilters: [ defaultPrefilter ], prefilter: function( callback, prepend ) { if ( prepend ) { Animation.prefilters.unshift( callback ); } else { Animation.prefilters.push( callback ); } } } ); jQuery.speed = function( speed, easing, fn ) { var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { complete: fn || !fn && easing || jQuery.isFunction( speed ) && speed, duration: speed, easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing }; // Go to the end state if fx are off or if document is hidden if ( jQuery.fx.off || document.hidden ) { opt.duration = 0; } else { if ( typeof opt.duration !== "number" ) { if ( opt.duration in jQuery.fx.speeds ) { opt.duration = jQuery.fx.speeds[ opt.duration ]; } else { opt.duration = jQuery.fx.speeds._default; } } } // Normalize opt.queue - true/undefined/null -> "fx" if ( opt.queue == null || opt.queue === true ) { opt.queue = "fx"; } // Queueing opt.old = opt.complete; opt.complete = function() { if ( jQuery.isFunction( opt.old ) ) { opt.old.call( this ); } if ( opt.queue ) { jQuery.dequeue( this, opt.queue ); } }; return opt; }; jQuery.fn.extend( { fadeTo: function( speed, to, easing, callback ) { // Show any hidden elements after setting opacity to 0 return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show() // Animate to the value specified .end().animate( { opacity: to }, speed, easing, callback ); }, animate: function( prop, speed, easing, callback ) { var empty = jQuery.isEmptyObject( prop ), optall = jQuery.speed( speed, easing, callback ), doAnimation = function() { // Operate on a copy of prop so per-property easing won't be lost var anim = Animation( this, jQuery.extend( {}, prop ), optall ); // Empty animations, or finishing resolves immediately if ( empty || dataPriv.get( this, "finish" ) ) { anim.stop( true ); } }; doAnimation.finish = doAnimation; return empty || optall.queue === false ? this.each( doAnimation ) : this.queue( optall.queue, doAnimation ); }, stop: function( type, clearQueue, gotoEnd ) { var stopQueue = function( hooks ) { var stop = hooks.stop; delete hooks.stop; stop( gotoEnd ); }; if ( typeof type !== "string" ) { gotoEnd = clearQueue; clearQueue = type; type = undefined; } if ( clearQueue && type !== false ) { this.queue( type || "fx", [] ); } return this.each( function() { var dequeue = true, index = type != null && type + "queueHooks", timers = jQuery.timers, data = dataPriv.get( this ); if ( index ) { if ( data[ index ] && data[ index ].stop ) { stopQueue( data[ index ] ); } } else { for ( index in data ) { if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { stopQueue( data[ index ] ); } } } for ( index = timers.length; index--; ) { if ( timers[ index ].elem === this && ( type == null || timers[ index ].queue === type ) ) { timers[ index ].anim.stop( gotoEnd ); dequeue = false; timers.splice( index, 1 ); } } // Start the next in the queue if the last step wasn't forced. // Timers currently will call their complete callbacks, which // will dequeue but only if they were gotoEnd. if ( dequeue || !gotoEnd ) { jQuery.dequeue( this, type ); } } ); }, finish: function( type ) { if ( type !== false ) { type = type || "fx"; } return this.each( function() { var index, data = dataPriv.get( this ), queue = data[ type + "queue" ], hooks = data[ type + "queueHooks" ], timers = jQuery.timers, length = queue ? queue.length : 0; // Enable finishing flag on private data data.finish = true; // Empty the queue first jQuery.queue( this, type, [] ); if ( hooks && hooks.stop ) { hooks.stop.call( this, true ); } // Look for any active animations, and finish them for ( index = timers.length; index--; ) { if ( timers[ index ].elem === this && timers[ index ].queue === type ) { timers[ index ].anim.stop( true ); timers.splice( index, 1 ); } } // Look for any animations in the old queue and finish them for ( index = 0; index < length; index++ ) { if ( queue[ index ] && queue[ index ].finish ) { queue[ index ].finish.call( this ); } } // Turn off finishing flag delete data.finish; } ); } } ); jQuery.each( [ "toggle", "show", "hide" ], function( i, name ) { var cssFn = jQuery.fn[ name ]; jQuery.fn[ name ] = function( speed, easing, callback ) { return speed == null || typeof speed === "boolean" ? cssFn.apply( this, arguments ) : this.animate( genFx( name, true ), speed, easing, callback ); }; } ); // Generate shortcuts for custom animations jQuery.each( { slideDown: genFx( "show" ), slideUp: genFx( "hide" ), slideToggle: genFx( "toggle" ), fadeIn: { opacity: "show" }, fadeOut: { opacity: "hide" }, fadeToggle: { opacity: "toggle" } }, function( name, props ) { jQuery.fn[ name ] = function( speed, easing, callback ) { return this.animate( props, speed, easing, callback ); }; } ); jQuery.timers = []; jQuery.fx.tick = function() { var timer, i = 0, timers = jQuery.timers; fxNow = jQuery.now(); for ( ; i < timers.length; i++ ) { timer = timers[ i ]; // Checks the timer has not already been removed if ( !timer() && timers[ i ] === timer ) { timers.splice( i--, 1 ); } } if ( !timers.length ) { jQuery.fx.stop(); } fxNow = undefined; }; jQuery.fx.timer = function( timer ) { jQuery.timers.push( timer ); if ( timer() ) { jQuery.fx.start(); } else { jQuery.timers.pop(); } }; jQuery.fx.interval = 13; jQuery.fx.start = function() { if ( !timerId ) { timerId = window.requestAnimationFrame ? window.requestAnimationFrame( raf ) : window.setInterval( jQuery.fx.tick, jQuery.fx.interval ); } }; jQuery.fx.stop = function() { if ( window.cancelAnimationFrame ) { window.cancelAnimationFrame( timerId ); } else { window.clearInterval( timerId ); } timerId = null; }; jQuery.fx.speeds = { slow: 600, fast: 200, // Default speed _default: 400 }; // Based off of the plugin by Clint Helfers, with permission. // https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/ jQuery.fn.delay = function( time, type ) { time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; type = type || "fx"; return this.queue( type, function( next, hooks ) { var timeout = window.setTimeout( next, time ); hooks.stop = function() { window.clearTimeout( timeout ); }; } ); }; ( function() { var input = document.createElement( "input" ), select = document.createElement( "select" ), opt = select.appendChild( document.createElement( "option" ) ); input.type = "checkbox"; // Support: Android <=4.3 only // Default value for a checkbox should be "on" support.checkOn = input.value !== ""; // Support: IE <=11 only // Must access selectedIndex to make default options select support.optSelected = opt.selected; // Support: IE <=11 only // An input loses its value after becoming a radio input = document.createElement( "input" ); input.value = "t"; input.type = "radio"; support.radioValue = input.value === "t"; } )(); var boolHook, attrHandle = jQuery.expr.attrHandle; jQuery.fn.extend( { attr: function( name, value ) { return access( this, jQuery.attr, name, value, arguments.length > 1 ); }, removeAttr: function( name ) { return this.each( function() { jQuery.removeAttr( this, name ); } ); } } ); jQuery.extend( { attr: function( elem, name, value ) { var ret, hooks, nType = elem.nodeType; // Don't get/set attributes on text, comment and attribute nodes if ( nType === 3 || nType === 8 || nType === 2 ) { return; } // Fallback to prop when attributes are not supported if ( typeof elem.getAttribute === "undefined" ) { return jQuery.prop( elem, name, value ); } // Attribute hooks are determined by the lowercase version // Grab necessary hook if one is defined if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { hooks = jQuery.attrHooks[ name.toLowerCase() ] || ( jQuery.expr.match.bool.test( name ) ? boolHook : undefined ); } if ( value !== undefined ) { if ( value === null ) { jQuery.removeAttr( elem, name ); return; } if ( hooks && "set" in hooks && ( ret = hooks.set( elem, value, name ) ) !== undefined ) { return ret; } elem.setAttribute( name, value + "" ); return value; } if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { return ret; } ret = jQuery.find.attr( elem, name ); // Non-existent attributes return null, we normalize to undefined return ret == null ? undefined : ret; }, attrHooks: { type: { set: function( elem, value ) { if ( !support.radioValue && value === "radio" && jQuery.nodeName( elem, "input" ) ) { var val = elem.value; elem.setAttribute( "type", value ); if ( val ) { elem.value = val; } return value; } } } }, removeAttr: function( elem, value ) { var name, i = 0, // Attribute names can contain non-HTML whitespace characters // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 attrNames = value && value.match( rnothtmlwhite ); if ( attrNames && elem.nodeType === 1 ) { while ( ( name = attrNames[ i++ ] ) ) { elem.removeAttribute( name ); } } } } ); // Hooks for boolean attributes boolHook = { set: function( elem, value, name ) { if ( value === false ) { // Remove boolean attributes when set to false jQuery.removeAttr( elem, name ); } else { elem.setAttribute( name, name ); } return name; } }; jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) { var getter = attrHandle[ name ] || jQuery.find.attr; attrHandle[ name ] = function( elem, name, isXML ) { var ret, handle, lowercaseName = name.toLowerCase(); if ( !isXML ) { // Avoid an infinite loop by temporarily removing this function from the getter handle = attrHandle[ lowercaseName ]; attrHandle[ lowercaseName ] = ret; ret = getter( elem, name, isXML ) != null ? lowercaseName : null; attrHandle[ lowercaseName ] = handle; } return ret; }; } ); var rfocusable = /^(?:input|select|textarea|button)$/i, rclickable = /^(?:a|area)$/i; jQuery.fn.extend( { prop: function( name, value ) { return access( this, jQuery.prop, name, value, arguments.length > 1 ); }, removeProp: function( name ) { return this.each( function() { delete this[ jQuery.propFix[ name ] || name ]; } ); } } ); jQuery.extend( { prop: function( elem, name, value ) { var ret, hooks, nType = elem.nodeType; // Don't get/set properties on text, comment and attribute nodes if ( nType === 3 || nType === 8 || nType === 2 ) { return; } if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { // Fix name and attach hooks name = jQuery.propFix[ name ] || name; hooks = jQuery.propHooks[ name ]; } if ( value !== undefined ) { if ( hooks && "set" in hooks && ( ret = hooks.set( elem, value, name ) ) !== undefined ) { return ret; } return ( elem[ name ] = value ); } if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { return ret; } return elem[ name ]; }, propHooks: { tabIndex: { get: function( elem ) { // Support: IE <=9 - 11 only // elem.tabIndex doesn't always return the // correct value when it hasn't been explicitly set // https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ // Use proper attribute retrieval(#12072) var tabindex = jQuery.find.attr( elem, "tabindex" ); if ( tabindex ) { return parseInt( tabindex, 10 ); } if ( rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ) { return 0; } return -1; } } }, propFix: { "for": "htmlFor", "class": "className" } } ); // Support: IE <=11 only // Accessing the selectedIndex property // forces the browser to respect setting selected // on the option // The getter ensures a default option is selected // when in an optgroup // eslint rule "no-unused-expressions" is disabled for this code // since it considers such accessions noop if ( !support.optSelected ) { jQuery.propHooks.selected = { get: function( elem ) { /* eslint no-unused-expressions: "off" */ var parent = elem.parentNode; if ( parent && parent.parentNode ) { parent.parentNode.selectedIndex; } return null; }, set: function( elem ) { /* eslint no-unused-expressions: "off" */ var parent = elem.parentNode; if ( parent ) { parent.selectedIndex; if ( parent.parentNode ) { parent.parentNode.selectedIndex; } } } }; } jQuery.each( [ "tabIndex", "readOnly", "maxLength", "cellSpacing", "cellPadding", "rowSpan", "colSpan", "useMap", "frameBorder", "contentEditable" ], function() { jQuery.propFix[ this.toLowerCase() ] = this; } ); // Strip and collapse whitespace according to HTML spec // https://html.spec.whatwg.org/multipage/infrastructure.html#strip-and-collapse-whitespace function stripAndCollapse( value ) { var tokens = value.match( rnothtmlwhite ) || []; return tokens.join( " " ); } function getClass( elem ) { return elem.getAttribute && elem.getAttribute( "class" ) || ""; } jQuery.fn.extend( { addClass: function( value ) { var classes, elem, cur, curValue, clazz, j, finalValue, i = 0; if ( jQuery.isFunction( value ) ) { return this.each( function( j ) { jQuery( this ).addClass( value.call( this, j, getClass( this ) ) ); } ); } if ( typeof value === "string" && value ) { classes = value.match( rnothtmlwhite ) || []; while ( ( elem = this[ i++ ] ) ) { curValue = getClass( elem ); cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); if ( cur ) { j = 0; while ( ( clazz = classes[ j++ ] ) ) { if ( cur.indexOf( " " + clazz + " " ) < 0 ) { cur += clazz + " "; } } // Only assign if different to avoid unneeded rendering. finalValue = stripAndCollapse( cur ); if ( curValue !== finalValue ) { elem.setAttribute( "class", finalValue ); } } } } return this; }, removeClass: function( value ) { var classes, elem, cur, curValue, clazz, j, finalValue, i = 0; if ( jQuery.isFunction( value ) ) { return this.each( function( j ) { jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) ); } ); } if ( !arguments.length ) { return this.attr( "class", "" ); } if ( typeof value === "string" && value ) { classes = value.match( rnothtmlwhite ) || []; while ( ( elem = this[ i++ ] ) ) { curValue = getClass( elem ); // This expression is here for better compressibility (see addClass) cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); if ( cur ) { j = 0; while ( ( clazz = classes[ j++ ] ) ) { // Remove *all* instances while ( cur.indexOf( " " + clazz + " " ) > -1 ) { cur = cur.replace( " " + clazz + " ", " " ); } } // Only assign if different to avoid unneeded rendering. finalValue = stripAndCollapse( cur ); if ( curValue !== finalValue ) { elem.setAttribute( "class", finalValue ); } } } } return this; }, toggleClass: function( value, stateVal ) { var type = typeof value; if ( typeof stateVal === "boolean" && type === "string" ) { return stateVal ? this.addClass( value ) : this.removeClass( value ); } if ( jQuery.isFunction( value ) ) { return this.each( function( i ) { jQuery( this ).toggleClass( value.call( this, i, getClass( this ), stateVal ), stateVal ); } ); } return this.each( function() { var className, i, self, classNames; if ( type === "string" ) { // Toggle individual class names i = 0; self = jQuery( this ); classNames = value.match( rnothtmlwhite ) || []; while ( ( className = classNames[ i++ ] ) ) { // Check each className given, space separated list if ( self.hasClass( className ) ) { self.removeClass( className ); } else { self.addClass( className ); } } // Toggle whole class name } else if ( value === undefined || type === "boolean" ) { className = getClass( this ); if ( className ) { // Store className if set dataPriv.set( this, "__className__", className ); } // If the element has a class name or if we're passed `false`, // then remove the whole classname (if there was one, the above saved it). // Otherwise bring back whatever was previously saved (if anything), // falling back to the empty string if nothing was stored. if ( this.setAttribute ) { this.setAttribute( "class", className || value === false ? "" : dataPriv.get( this, "__className__" ) || "" ); } } } ); }, hasClass: function( selector ) { var className, elem, i = 0; className = " " + selector + " "; while ( ( elem = this[ i++ ] ) ) { if ( elem.nodeType === 1 && ( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) { return true; } } return false; } } ); var rreturn = /\r/g; jQuery.fn.extend( { val: function( value ) { var hooks, ret, isFunction, elem = this[ 0 ]; if ( !arguments.length ) { if ( elem ) { hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ]; if ( hooks && "get" in hooks && ( ret = hooks.get( elem, "value" ) ) !== undefined ) { return ret; } ret = elem.value; // Handle most common string cases if ( typeof ret === "string" ) { return ret.replace( rreturn, "" ); } // Handle cases where value is null/undef or number return ret == null ? "" : ret; } return; } isFunction = jQuery.isFunction( value ); return this.each( function( i ) { var val; if ( this.nodeType !== 1 ) { return; } if ( isFunction ) { val = value.call( this, i, jQuery( this ).val() ); } else { val = value; } // Treat null/undefined as ""; convert numbers to string if ( val == null ) { val = ""; } else if ( typeof val === "number" ) { val += ""; } else if ( jQuery.isArray( val ) ) { val = jQuery.map( val, function( value ) { return value == null ? "" : value + ""; } ); } hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; // If set returns undefined, fall back to normal setting if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) { this.value = val; } } ); } } ); jQuery.extend( { valHooks: { option: { get: function( elem ) { var val = jQuery.find.attr( elem, "value" ); return val != null ? val : // Support: IE <=10 - 11 only // option.text throws exceptions (#14686, #14858) // Strip and collapse whitespace // https://html.spec.whatwg.org/#strip-and-collapse-whitespace stripAndCollapse( jQuery.text( elem ) ); } }, select: { get: function( elem ) { var value, option, i, options = elem.options, index = elem.selectedIndex, one = elem.type === "select-one", values = one ? null : [], max = one ? index + 1 : options.length; if ( index < 0 ) { i = max; } else { i = one ? index : 0; } // Loop through all the selected options for ( ; i < max; i++ ) { option = options[ i ]; // Support: IE <=9 only // IE8-9 doesn't update selected after form reset (#2551) if ( ( option.selected || i === index ) && // Don't return options that are disabled or in a disabled optgroup !option.disabled && ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) { // Get the specific value for the option value = jQuery( option ).val(); // We don't need an array for one selects if ( one ) { return value; } // Multi-Selects return an array values.push( value ); } } return values; }, set: function( elem, value ) { var optionSet, option, options = elem.options, values = jQuery.makeArray( value ), i = options.length; while ( i-- ) { option = options[ i ]; /* eslint-disable no-cond-assign */ if ( option.selected = jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1 ) { optionSet = true; } /* eslint-enable no-cond-assign */ } // Force browsers to behave consistently when non-matching value is set if ( !optionSet ) { elem.selectedIndex = -1; } return values; } } } } ); // Radios and checkboxes getter/setter jQuery.each( [ "radio", "checkbox" ], function() { jQuery.valHooks[ this ] = { set: function( elem, value ) { if ( jQuery.isArray( value ) ) { return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 ); } } }; if ( !support.checkOn ) { jQuery.valHooks[ this ].get = function( elem ) { return elem.getAttribute( "value" ) === null ? "on" : elem.value; }; } } ); // Return jQuery for attributes-only inclusion var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/; jQuery.extend( jQuery.event, { trigger: function( event, data, elem, onlyHandlers ) { var i, cur, tmp, bubbleType, ontype, handle, special, eventPath = [ elem || document ], type = hasOwn.call( event, "type" ) ? event.type : event, namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; cur = tmp = elem = elem || document; // Don't do events on text and comment nodes if ( elem.nodeType === 3 || elem.nodeType === 8 ) { return; } // focus/blur morphs to focusin/out; ensure we're not firing them right now if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { return; } if ( type.indexOf( "." ) > -1 ) { // Namespaced trigger; create a regexp to match event type in handle() namespaces = type.split( "." ); type = namespaces.shift(); namespaces.sort(); } ontype = type.indexOf( ":" ) < 0 && "on" + type; // Caller can pass in a jQuery.Event object, Object, or just an event type string event = event[ jQuery.expando ] ? event : new jQuery.Event( type, typeof event === "object" && event ); // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) event.isTrigger = onlyHandlers ? 2 : 3; event.namespace = namespaces.join( "." ); event.rnamespace = event.namespace ? new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : null; // Clean up the event in case it is being reused event.result = undefined; if ( !event.target ) { event.target = elem; } // Clone any incoming data and prepend the event, creating the handler arg list data = data == null ? [ event ] : jQuery.makeArray( data, [ event ] ); // Allow special events to draw outside the lines special = jQuery.event.special[ type ] || {}; if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { return; } // Determine event propagation path in advance, per W3C events spec (#9951) // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { bubbleType = special.delegateType || type; if ( !rfocusMorph.test( bubbleType + type ) ) { cur = cur.parentNode; } for ( ; cur; cur = cur.parentNode ) { eventPath.push( cur ); tmp = cur; } // Only add window if we got to document (e.g., not plain obj or detached DOM) if ( tmp === ( elem.ownerDocument || document ) ) { eventPath.push( tmp.defaultView || tmp.parentWindow || window ); } } // Fire handlers on the event path i = 0; while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { event.type = i > 1 ? bubbleType : special.bindType || type; // jQuery handler handle = ( dataPriv.get( cur, "events" ) || {} )[ event.type ] && dataPriv.get( cur, "handle" ); if ( handle ) { handle.apply( cur, data ); } // Native handler handle = ontype && cur[ ontype ]; if ( handle && handle.apply && acceptData( cur ) ) { event.result = handle.apply( cur, data ); if ( event.result === false ) { event.preventDefault(); } } } event.type = type; // If nobody prevented the default action, do it now if ( !onlyHandlers && !event.isDefaultPrevented() ) { if ( ( !special._default || special._default.apply( eventPath.pop(), data ) === false ) && acceptData( elem ) ) { // Call a native DOM method on the target with the same name as the event. // Don't do default actions on window, that's where global variables be (#6170) if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) { // Don't re-trigger an onFOO event when we call its FOO() method tmp = elem[ ontype ]; if ( tmp ) { elem[ ontype ] = null; } // Prevent re-triggering of the same event, since we already bubbled it above jQuery.event.triggered = type; elem[ type ](); jQuery.event.triggered = undefined; if ( tmp ) { elem[ ontype ] = tmp; } } } } return event.result; }, // Piggyback on a donor event to simulate a different one // Used only for `focus(in | out)` events simulate: function( type, elem, event ) { var e = jQuery.extend( new jQuery.Event(), event, { type: type, isSimulated: true } ); jQuery.event.trigger( e, null, elem ); } } ); jQuery.fn.extend( { trigger: function( type, data ) { return this.each( function() { jQuery.event.trigger( type, data, this ); } ); }, triggerHandler: function( type, data ) { var elem = this[ 0 ]; if ( elem ) { return jQuery.event.trigger( type, data, elem, true ); } } } ); jQuery.each( ( "blur focus focusin focusout resize scroll click dblclick " + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + "change select submit keydown keypress keyup contextmenu" ).split( " " ), function( i, name ) { // Handle event binding jQuery.fn[ name ] = function( data, fn ) { return arguments.length > 0 ? this.on( name, null, data, fn ) : this.trigger( name ); }; } ); jQuery.fn.extend( { hover: function( fnOver, fnOut ) { return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); } } ); support.focusin = "onfocusin" in window; // Support: Firefox <=44 // Firefox doesn't have focus(in | out) events // Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 // // Support: Chrome <=48 - 49, Safari <=9.0 - 9.1 // focus(in | out) events fire after focus & blur events, // which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order // Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857 if ( !support.focusin ) { jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) { // Attach a single capturing handler on the document while someone wants focusin/focusout var handler = function( event ) { jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) ); }; jQuery.event.special[ fix ] = { setup: function() { var doc = this.ownerDocument || this, attaches = dataPriv.access( doc, fix ); if ( !attaches ) { doc.addEventListener( orig, handler, true ); } dataPriv.access( doc, fix, ( attaches || 0 ) + 1 ); }, teardown: function() { var doc = this.ownerDocument || this, attaches = dataPriv.access( doc, fix ) - 1; if ( !attaches ) { doc.removeEventListener( orig, handler, true ); dataPriv.remove( doc, fix ); } else { dataPriv.access( doc, fix, attaches ); } } }; } ); } var location = window.location; var nonce = jQuery.now(); var rquery = ( /\?/ ); // Cross-browser xml parsing jQuery.parseXML = function( data ) { var xml; if ( !data || typeof data !== "string" ) { return null; } // Support: IE 9 - 11 only // IE throws on parseFromString with invalid input. try { xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" ); } catch ( e ) { xml = undefined; } if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) { jQuery.error( "Invalid XML: " + data ); } return xml; }; var rbracket = /\[\]$/, rCRLF = /\r?\n/g, rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i, rsubmittable = /^(?:input|select|textarea|keygen)/i; function buildParams( prefix, obj, traditional, add ) { var name; if ( jQuery.isArray( obj ) ) { // Serialize array item. jQuery.each( obj, function( i, v ) { if ( traditional || rbracket.test( prefix ) ) { // Treat each array item as a scalar. add( prefix, v ); } else { // Item is non-scalar (array or object), encode its numeric index. buildParams( prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]", v, traditional, add ); } } ); } else if ( !traditional && jQuery.type( obj ) === "object" ) { // Serialize object item. for ( name in obj ) { buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); } } else { // Serialize scalar item. add( prefix, obj ); } } // Serialize an array of form elements or a set of // key/values into a query string jQuery.param = function( a, traditional ) { var prefix, s = [], add = function( key, valueOrFunction ) { // If value is a function, invoke it and use its return value var value = jQuery.isFunction( valueOrFunction ) ? valueOrFunction() : valueOrFunction; s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value == null ? "" : value ); }; // If an array was passed in, assume that it is an array of form elements. if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { // Serialize the form elements jQuery.each( a, function() { add( this.name, this.value ); } ); } else { // If traditional, encode the "old" way (the way 1.3.2 or older // did it), otherwise encode params recursively. for ( prefix in a ) { buildParams( prefix, a[ prefix ], traditional, add ); } } // Return the resulting serialization return s.join( "&" ); }; jQuery.fn.extend( { serialize: function() { return jQuery.param( this.serializeArray() ); }, serializeArray: function() { return this.map( function() { // Can add propHook for "elements" to filter or add form elements var elements = jQuery.prop( this, "elements" ); return elements ? jQuery.makeArray( elements ) : this; } ) .filter( function() { var type = this.type; // Use .is( ":disabled" ) so that fieldset[disabled] works return this.name && !jQuery( this ).is( ":disabled" ) && rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) && ( this.checked || !rcheckableType.test( type ) ); } ) .map( function( i, elem ) { var val = jQuery( this ).val(); if ( val == null ) { return null; } if ( jQuery.isArray( val ) ) { return jQuery.map( val, function( val ) { return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; } ); } return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; } ).get(); } } ); var r20 = /%20/g, rhash = /#.*$/, rantiCache = /([?&])_=[^&]*/, rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg, // #7653, #8125, #8152: local protocol detection rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, rnoContent = /^(?:GET|HEAD)$/, rprotocol = /^\/\//, /* Prefilters * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) * 2) These are called: * - BEFORE asking for a transport * - AFTER param serialization (s.data is a string if s.processData is true) * 3) key is the dataType * 4) the catchall symbol "*" can be used * 5) execution will start with transport dataType and THEN continue down to "*" if needed */ prefilters = {}, /* Transports bindings * 1) key is the dataType * 2) the catchall symbol "*" can be used * 3) selection will start with transport dataType and THEN go to "*" if needed */ transports = {}, // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression allTypes = "*/".concat( "*" ), // Anchor tag for parsing the document origin originAnchor = document.createElement( "a" ); originAnchor.href = location.href; // Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport function addToPrefiltersOrTransports( structure ) { // dataTypeExpression is optional and defaults to "*" return function( dataTypeExpression, func ) { if ( typeof dataTypeExpression !== "string" ) { func = dataTypeExpression; dataTypeExpression = "*"; } var dataType, i = 0, dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || []; if ( jQuery.isFunction( func ) ) { // For each dataType in the dataTypeExpression while ( ( dataType = dataTypes[ i++ ] ) ) { // Prepend if requested if ( dataType[ 0 ] === "+" ) { dataType = dataType.slice( 1 ) || "*"; ( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func ); // Otherwise append } else { ( structure[ dataType ] = structure[ dataType ] || [] ).push( func ); } } } }; } // Base inspection function for prefilters and transports function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) { var inspected = {}, seekingTransport = ( structure === transports ); function inspect( dataType ) { var selected; inspected[ dataType ] = true; jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) { var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR ); if ( typeof dataTypeOrTransport === "string" && !seekingTransport && !inspected[ dataTypeOrTransport ] ) { options.dataTypes.unshift( dataTypeOrTransport ); inspect( dataTypeOrTransport ); return false; } else if ( seekingTransport ) { return !( selected = dataTypeOrTransport ); } } ); return selected; } return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" ); } // A special extend for ajax options // that takes "flat" options (not to be deep extended) // Fixes #9887 function ajaxExtend( target, src ) { var key, deep, flatOptions = jQuery.ajaxSettings.flatOptions || {}; for ( key in src ) { if ( src[ key ] !== undefined ) { ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; } } if ( deep ) { jQuery.extend( true, target, deep ); } return target; } /* Handles responses to an ajax request: * - finds the right dataType (mediates between content-type and expected dataType) * - returns the corresponding response */ function ajaxHandleResponses( s, jqXHR, responses ) { var ct, type, finalDataType, firstDataType, contents = s.contents, dataTypes = s.dataTypes; // Remove auto dataType and get content-type in the process while ( dataTypes[ 0 ] === "*" ) { dataTypes.shift(); if ( ct === undefined ) { ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" ); } } // Check if we're dealing with a known content-type if ( ct ) { for ( type in contents ) { if ( contents[ type ] && contents[ type ].test( ct ) ) { dataTypes.unshift( type ); break; } } } // Check to see if we have a response for the expected dataType if ( dataTypes[ 0 ] in responses ) { finalDataType = dataTypes[ 0 ]; } else { // Try convertible dataTypes for ( type in responses ) { if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) { finalDataType = type; break; } if ( !firstDataType ) { firstDataType = type; } } // Or just use first one finalDataType = finalDataType || firstDataType; } // If we found a dataType // We add the dataType to the list if needed // and return the corresponding response if ( finalDataType ) { if ( finalDataType !== dataTypes[ 0 ] ) { dataTypes.unshift( finalDataType ); } return responses[ finalDataType ]; } } /* Chain conversions given the request and the original response * Also sets the responseXXX fields on the jqXHR instance */ function ajaxConvert( s, response, jqXHR, isSuccess ) { var conv2, current, conv, tmp, prev, converters = {}, // Work with a copy of dataTypes in case we need to modify it for conversion dataTypes = s.dataTypes.slice(); // Create converters map with lowercased keys if ( dataTypes[ 1 ] ) { for ( conv in s.converters ) { converters[ conv.toLowerCase() ] = s.converters[ conv ]; } } current = dataTypes.shift(); // Convert to each sequential dataType while ( current ) { if ( s.responseFields[ current ] ) { jqXHR[ s.responseFields[ current ] ] = response; } // Apply the dataFilter if provided if ( !prev && isSuccess && s.dataFilter ) { response = s.dataFilter( response, s.dataType ); } prev = current; current = dataTypes.shift(); if ( current ) { // There's only work to do if current dataType is non-auto if ( current === "*" ) { current = prev; // Convert response if prev dataType is non-auto and differs from current } else if ( prev !== "*" && prev !== current ) { // Seek a direct converter conv = converters[ prev + " " + current ] || converters[ "* " + current ]; // If none found, seek a pair if ( !conv ) { for ( conv2 in converters ) { // If conv2 outputs current tmp = conv2.split( " " ); if ( tmp[ 1 ] === current ) { // If prev can be converted to accepted input conv = converters[ prev + " " + tmp[ 0 ] ] || converters[ "* " + tmp[ 0 ] ]; if ( conv ) { // Condense equivalence converters if ( conv === true ) { conv = converters[ conv2 ]; // Otherwise, insert the intermediate dataType } else if ( converters[ conv2 ] !== true ) { current = tmp[ 0 ]; dataTypes.unshift( tmp[ 1 ] ); } break; } } } } // Apply converter (if not an equivalence) if ( conv !== true ) { // Unless errors are allowed to bubble, catch and return them if ( conv && s.throws ) { response = conv( response ); } else { try { response = conv( response ); } catch ( e ) { return { state: "parsererror", error: conv ? e : "No conversion from " + prev + " to " + current }; } } } } } } return { state: "success", data: response }; } jQuery.extend( { // Counter for holding the number of active queries active: 0, // Last-Modified header cache for next request lastModified: {}, etag: {}, ajaxSettings: { url: location.href, type: "GET", isLocal: rlocalProtocol.test( location.protocol ), global: true, processData: true, async: true, contentType: "application/x-www-form-urlencoded; charset=UTF-8", /* timeout: 0, data: null, dataType: null, username: null, password: null, cache: null, throws: false, traditional: false, headers: {}, */ accepts: { "*": allTypes, text: "text/plain", html: "text/html", xml: "application/xml, text/xml", json: "application/json, text/javascript" }, contents: { xml: /\bxml\b/, html: /\bhtml/, json: /\bjson\b/ }, responseFields: { xml: "responseXML", text: "responseText", json: "responseJSON" }, // Data converters // Keys separate source (or catchall "*") and destination types with a single space converters: { // Convert anything to text "* text": String, // Text to html (true = no transformation) "text html": true, // Evaluate text as a json expression "text json": JSON.parse, // Parse text as xml "text xml": jQuery.parseXML }, // For options that shouldn't be deep extended: // you can add your own custom options here if // and when you create one that shouldn't be // deep extended (see ajaxExtend) flatOptions: { url: true, context: true } }, // Creates a full fledged settings object into target // with both ajaxSettings and settings fields. // If target is omitted, writes into ajaxSettings. ajaxSetup: function( target, settings ) { return settings ? // Building a settings object ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) : // Extending ajaxSettings ajaxExtend( jQuery.ajaxSettings, target ); }, ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), ajaxTransport: addToPrefiltersOrTransports( transports ), // Main method ajax: function( url, options ) { // If url is an object, simulate pre-1.5 signature if ( typeof url === "object" ) { options = url; url = undefined; } // Force options to be an object options = options || {}; var transport, // URL without anti-cache param cacheURL, // Response headers responseHeadersString, responseHeaders, // timeout handle timeoutTimer, // Url cleanup var urlAnchor, // Request state (becomes false upon send and true upon completion) completed, // To know if global events are to be dispatched fireGlobals, // Loop variable i, // uncached part of the url uncached, // Create the final options object s = jQuery.ajaxSetup( {}, options ), // Callbacks context callbackContext = s.context || s, // Context for global events is callbackContext if it is a DOM node or jQuery collection globalEventContext = s.context && ( callbackContext.nodeType || callbackContext.jquery ) ? jQuery( callbackContext ) : jQuery.event, // Deferreds deferred = jQuery.Deferred(), completeDeferred = jQuery.Callbacks( "once memory" ), // Status-dependent callbacks statusCode = s.statusCode || {}, // Headers (they are sent all at once) requestHeaders = {}, requestHeadersNames = {}, // Default abort message strAbort = "canceled", // Fake xhr jqXHR = { readyState: 0, // Builds headers hashtable if needed getResponseHeader: function( key ) { var match; if ( completed ) { if ( !responseHeaders ) { responseHeaders = {}; while ( ( match = rheaders.exec( responseHeadersString ) ) ) { responseHeaders[ match[ 1 ].toLowerCase() ] = match[ 2 ]; } } match = responseHeaders[ key.toLowerCase() ]; } return match == null ? null : match; }, // Raw string getAllResponseHeaders: function() { return completed ? responseHeadersString : null; }, // Caches the header setRequestHeader: function( name, value ) { if ( completed == null ) { name = requestHeadersNames[ name.toLowerCase() ] = requestHeadersNames[ name.toLowerCase() ] || name; requestHeaders[ name ] = value; } return this; }, // Overrides response content-type header overrideMimeType: function( type ) { if ( completed == null ) { s.mimeType = type; } return this; }, // Status-dependent callbacks statusCode: function( map ) { var code; if ( map ) { if ( completed ) { // Execute the appropriate callbacks jqXHR.always( map[ jqXHR.status ] ); } else { // Lazy-add the new callbacks in a way that preserves old ones for ( code in map ) { statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; } } } return this; }, // Cancel the request abort: function( statusText ) { var finalText = statusText || strAbort; if ( transport ) { transport.abort( finalText ); } done( 0, finalText ); return this; } }; // Attach deferreds deferred.promise( jqXHR ); // Add protocol if not provided (prefilters might expect it) // Handle falsy url in the settings object (#10093: consistency with old signature) // We also use the url parameter if available s.url = ( ( url || s.url || location.href ) + "" ) .replace( rprotocol, location.protocol + "//" ); // Alias method option to type as per ticket #12004 s.type = options.method || options.type || s.method || s.type; // Extract dataTypes list s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ]; // A cross-domain request is in order when the origin doesn't match the current origin. if ( s.crossDomain == null ) { urlAnchor = document.createElement( "a" ); // Support: IE <=8 - 11, Edge 12 - 13 // IE throws exception on accessing the href property if url is malformed, // e.g. http://example.com:80x/ try { urlAnchor.href = s.url; // Support: IE <=8 - 11 only // Anchor's host property isn't correctly set when s.url is relative urlAnchor.href = urlAnchor.href; s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !== urlAnchor.protocol + "//" + urlAnchor.host; } catch ( e ) { // If there is an error parsing the URL, assume it is crossDomain, // it can be rejected by the transport if it is invalid s.crossDomain = true; } } // Convert data if not already a string if ( s.data && s.processData && typeof s.data !== "string" ) { s.data = jQuery.param( s.data, s.traditional ); } // Apply prefilters inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); // If request was aborted inside a prefilter, stop there if ( completed ) { return jqXHR; } // We can fire global events as of now if asked to // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118) fireGlobals = jQuery.event && s.global; // Watch for a new set of requests if ( fireGlobals && jQuery.active++ === 0 ) { jQuery.event.trigger( "ajaxStart" ); } // Uppercase the type s.type = s.type.toUpperCase(); // Determine if request has content s.hasContent = !rnoContent.test( s.type ); // Save the URL in case we're toying with the If-Modified-Since // and/or If-None-Match header later on // Remove hash to simplify url manipulation cacheURL = s.url.replace( rhash, "" ); // More options handling for requests with no content if ( !s.hasContent ) { // Remember the hash so we can put it back uncached = s.url.slice( cacheURL.length ); // If data is available, append data to url if ( s.data ) { cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data; // #9682: remove data so that it's not used in an eventual retry delete s.data; } // Add or update anti-cache param if needed if ( s.cache === false ) { cacheURL = cacheURL.replace( rantiCache, "$1" ); uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce++ ) + uncached; } // Put hash and anti-cache on the URL that will be requested (gh-1732) s.url = cacheURL + uncached; // Change '%20' to '+' if this is encoded form body content (gh-2658) } else if ( s.data && s.processData && ( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) { s.data = s.data.replace( r20, "+" ); } // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. if ( s.ifModified ) { if ( jQuery.lastModified[ cacheURL ] ) { jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] ); } if ( jQuery.etag[ cacheURL ] ) { jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] ); } } // Set the correct header, if data is being sent if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { jqXHR.setRequestHeader( "Content-Type", s.contentType ); } // Set the Accepts header for the server, depending on the dataType jqXHR.setRequestHeader( "Accept", s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ? s.accepts[ s.dataTypes[ 0 ] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : s.accepts[ "*" ] ); // Check for headers option for ( i in s.headers ) { jqXHR.setRequestHeader( i, s.headers[ i ] ); } // Allow custom headers/mimetypes and early abort if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) { // Abort if not done already and return return jqXHR.abort(); } // Aborting is no longer a cancellation strAbort = "abort"; // Install callbacks on deferreds completeDeferred.add( s.complete ); jqXHR.done( s.success ); jqXHR.fail( s.error ); // Get transport transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); // If no transport, we auto-abort if ( !transport ) { done( -1, "No Transport" ); } else { jqXHR.readyState = 1; // Send global event if ( fireGlobals ) { globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); } // If request was aborted inside ajaxSend, stop there if ( completed ) { return jqXHR; } // Timeout if ( s.async && s.timeout > 0 ) { timeoutTimer = window.setTimeout( function() { jqXHR.abort( "timeout" ); }, s.timeout ); } try { completed = false; transport.send( requestHeaders, done ); } catch ( e ) { // Rethrow post-completion exceptions if ( completed ) { throw e; } // Propagate others as results done( -1, e ); } } // Callback for when everything is done function done( status, nativeStatusText, responses, headers ) { var isSuccess, success, error, response, modified, statusText = nativeStatusText; // Ignore repeat invocations if ( completed ) { return; } completed = true; // Clear timeout if it exists if ( timeoutTimer ) { window.clearTimeout( timeoutTimer ); } // Dereference transport for early garbage collection // (no matter how long the jqXHR object will be used) transport = undefined; // Cache response headers responseHeadersString = headers || ""; // Set readyState jqXHR.readyState = status > 0 ? 4 : 0; // Determine if successful isSuccess = status >= 200 && status < 300 || status === 304; // Get response data if ( responses ) { response = ajaxHandleResponses( s, jqXHR, responses ); } // Convert no matter what (that way responseXXX fields are always set) response = ajaxConvert( s, response, jqXHR, isSuccess ); // If successful, handle type chaining if ( isSuccess ) { // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. if ( s.ifModified ) { modified = jqXHR.getResponseHeader( "Last-Modified" ); if ( modified ) { jQuery.lastModified[ cacheURL ] = modified; } modified = jqXHR.getResponseHeader( "etag" ); if ( modified ) { jQuery.etag[ cacheURL ] = modified; } } // if no content if ( status === 204 || s.type === "HEAD" ) { statusText = "nocontent"; // if not modified } else if ( status === 304 ) { statusText = "notmodified"; // If we have data, let's convert it } else { statusText = response.state; success = response.data; error = response.error; isSuccess = !error; } } else { // Extract error from statusText and normalize for non-aborts error = statusText; if ( status || !statusText ) { statusText = "error"; if ( status < 0 ) { status = 0; } } } // Set data for the fake xhr object jqXHR.status = status; jqXHR.statusText = ( nativeStatusText || statusText ) + ""; // Success/Error if ( isSuccess ) { deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); } else { deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); } // Status-dependent callbacks jqXHR.statusCode( statusCode ); statusCode = undefined; if ( fireGlobals ) { globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError", [ jqXHR, s, isSuccess ? success : error ] ); } // Complete completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); if ( fireGlobals ) { globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); // Handle the global AJAX counter if ( !( --jQuery.active ) ) { jQuery.event.trigger( "ajaxStop" ); } } } return jqXHR; }, getJSON: function( url, data, callback ) { return jQuery.get( url, data, callback, "json" ); }, getScript: function( url, callback ) { return jQuery.get( url, undefined, callback, "script" ); } } ); jQuery.each( [ "get", "post" ], function( i, method ) { jQuery[ method ] = function( url, data, callback, type ) { // Shift arguments if data argument was omitted if ( jQuery.isFunction( data ) ) { type = type || callback; callback = data; data = undefined; } // The url can be an options object (which then must have .url) return jQuery.ajax( jQuery.extend( { url: url, type: method, dataType: type, data: data, success: callback }, jQuery.isPlainObject( url ) && url ) ); }; } ); jQuery._evalUrl = function( url ) { return jQuery.ajax( { url: url, // Make this explicit, since user can override this through ajaxSetup (#11264) type: "GET", dataType: "script", cache: true, async: false, global: false, "throws": true } ); }; jQuery.fn.extend( { wrapAll: function( html ) { var wrap; if ( this[ 0 ] ) { if ( jQuery.isFunction( html ) ) { html = html.call( this[ 0 ] ); } // The elements to wrap the target around wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true ); if ( this[ 0 ].parentNode ) { wrap.insertBefore( this[ 0 ] ); } wrap.map( function() { var elem = this; while ( elem.firstElementChild ) { elem = elem.firstElementChild; } return elem; } ).append( this ); } return this; }, wrapInner: function( html ) { if ( jQuery.isFunction( html ) ) { return this.each( function( i ) { jQuery( this ).wrapInner( html.call( this, i ) ); } ); } return this.each( function() { var self = jQuery( this ), contents = self.contents(); if ( contents.length ) { contents.wrapAll( html ); } else { self.append( html ); } } ); }, wrap: function( html ) { var isFunction = jQuery.isFunction( html ); return this.each( function( i ) { jQuery( this ).wrapAll( isFunction ? html.call( this, i ) : html ); } ); }, unwrap: function( selector ) { this.parent( selector ).not( "body" ).each( function() { jQuery( this ).replaceWith( this.childNodes ); } ); return this; } } ); jQuery.expr.pseudos.hidden = function( elem ) { return !jQuery.expr.pseudos.visible( elem ); }; jQuery.expr.pseudos.visible = function( elem ) { return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ); }; jQuery.ajaxSettings.xhr = function() { try { return new window.XMLHttpRequest(); } catch ( e ) {} }; var xhrSuccessStatus = { // File protocol always yields status code 0, assume 200 0: 200, // Support: IE <=9 only // #1450: sometimes IE returns 1223 when it should be 204 1223: 204 }, xhrSupported = jQuery.ajaxSettings.xhr(); support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); support.ajax = xhrSupported = !!xhrSupported; jQuery.ajaxTransport( function( options ) { var callback, errorCallback; // Cross domain only allowed if supported through XMLHttpRequest if ( support.cors || xhrSupported && !options.crossDomain ) { return { send: function( headers, complete ) { var i, xhr = options.xhr(); xhr.open( options.type, options.url, options.async, options.username, options.password ); // Apply custom fields if provided if ( options.xhrFields ) { for ( i in options.xhrFields ) { xhr[ i ] = options.xhrFields[ i ]; } } // Override mime type if needed if ( options.mimeType && xhr.overrideMimeType ) { xhr.overrideMimeType( options.mimeType ); } // X-Requested-With header // For cross-domain requests, seeing as conditions for a preflight are // akin to a jigsaw puzzle, we simply never set it to be sure. // (it can always be set on a per-request basis or even using ajaxSetup) // For same-domain requests, won't change header if already provided. if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) { headers[ "X-Requested-With" ] = "XMLHttpRequest"; } // Set headers for ( i in headers ) { xhr.setRequestHeader( i, headers[ i ] ); } // Callback callback = function( type ) { return function() { if ( callback ) { callback = errorCallback = xhr.onload = xhr.onerror = xhr.onabort = xhr.onreadystatechange = null; if ( type === "abort" ) { xhr.abort(); } else if ( type === "error" ) { // Support: IE <=9 only // On a manual native abort, IE9 throws // errors on any property access that is not readyState if ( typeof xhr.status !== "number" ) { complete( 0, "error" ); } else { complete( // File: protocol always yields status 0; see #8605, #14207 xhr.status, xhr.statusText ); } } else { complete( xhrSuccessStatus[ xhr.status ] || xhr.status, xhr.statusText, // Support: IE <=9 only // IE9 has no XHR2 but throws on binary (trac-11426) // For XHR2 non-text, let the caller handle it (gh-2498) ( xhr.responseType || "text" ) !== "text" || typeof xhr.responseText !== "string" ? { binary: xhr.response } : { text: xhr.responseText }, xhr.getAllResponseHeaders() ); } } }; }; // Listen to events xhr.onload = callback(); errorCallback = xhr.onerror = callback( "error" ); // Support: IE 9 only // Use onreadystatechange to replace onabort // to handle uncaught aborts if ( xhr.onabort !== undefined ) { xhr.onabort = errorCallback; } else { xhr.onreadystatechange = function() { // Check readyState before timeout as it changes if ( xhr.readyState === 4 ) { // Allow onerror to be called first, // but that will not handle a native abort // Also, save errorCallback to a variable // as xhr.onerror cannot be accessed window.setTimeout( function() { if ( callback ) { errorCallback(); } } ); } }; } // Create the abort callback callback = callback( "abort" ); try { // Do send the request (this may raise an exception) xhr.send( options.hasContent && options.data || null ); } catch ( e ) { // #14683: Only rethrow if this hasn't been notified as an error yet if ( callback ) { throw e; } } }, abort: function() { if ( callback ) { callback(); } } }; } } ); // Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432) jQuery.ajaxPrefilter( function( s ) { if ( s.crossDomain ) { s.contents.script = false; } } ); // Install script dataType jQuery.ajaxSetup( { accepts: { script: "text/javascript, application/javascript, " + "application/ecmascript, application/x-ecmascript" }, contents: { script: /\b(?:java|ecma)script\b/ }, converters: { "text script": function( text ) { jQuery.globalEval( text ); return text; } } } ); // Handle cache's special case and crossDomain jQuery.ajaxPrefilter( "script", function( s ) { if ( s.cache === undefined ) { s.cache = false; } if ( s.crossDomain ) { s.type = "GET"; } } ); // Bind script tag hack transport jQuery.ajaxTransport( "script", function( s ) { // This transport only deals with cross domain requests if ( s.crossDomain ) { var script, callback; return { send: function( _, complete ) { script = jQuery( "<script>" ).prop( { charset: s.scriptCharset, src: s.url } ).on( "load error", callback = function( evt ) { script.remove(); callback = null; if ( evt ) { complete( evt.type === "error" ? 404 : 200, evt.type ); } } ); // Use native DOM manipulation to avoid our domManip AJAX trickery document.head.appendChild( script[ 0 ] ); }, abort: function() { if ( callback ) { callback(); } } }; } } ); var oldCallbacks = [], rjsonp = /(=)\?(?=&|$)|\?\?/; // Default jsonp settings jQuery.ajaxSetup( { jsonp: "callback", jsonpCallback: function() { var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce++ ) ); this[ callback ] = true; return callback; } } ); // Detect, normalize options and install callbacks for jsonp requests jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) { var callbackName, overwritten, responseContainer, jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ? "url" : typeof s.data === "string" && ( s.contentType || "" ) .indexOf( "application/x-www-form-urlencoded" ) === 0 && rjsonp.test( s.data ) && "data" ); // Handle iff the expected data type is "jsonp" or we have a parameter to set if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) { // Get callback name, remembering preexisting value associated with it callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ? s.jsonpCallback() : s.jsonpCallback; // Insert callback into url or form data if ( jsonProp ) { s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName ); } else if ( s.jsonp !== false ) { s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName; } // Use data converter to retrieve json after script execution s.converters[ "script json" ] = function() { if ( !responseContainer ) { jQuery.error( callbackName + " was not called" ); } return responseContainer[ 0 ]; }; // Force json dataType s.dataTypes[ 0 ] = "json"; // Install callback overwritten = window[ callbackName ]; window[ callbackName ] = function() { responseContainer = arguments; }; // Clean-up function (fires after converters) jqXHR.always( function() { // If previous value didn't exist - remove it if ( overwritten === undefined ) { jQuery( window ).removeProp( callbackName ); // Otherwise restore preexisting value } else { window[ callbackName ] = overwritten; } // Save back as free if ( s[ callbackName ] ) { // Make sure that re-using the options doesn't screw things around s.jsonpCallback = originalSettings.jsonpCallback; // Save the callback name for future use oldCallbacks.push( callbackName ); } // Call if it was a function and we have a response if ( responseContainer && jQuery.isFunction( overwritten ) ) { overwritten( responseContainer[ 0 ] ); } responseContainer = overwritten = undefined; } ); // Delegate to script return "script"; } } ); // Support: Safari 8 only // In Safari 8 documents created via document.implementation.createHTMLDocument // collapse sibling forms: the second one becomes a child of the first one. // Because of that, this security measure has to be disabled in Safari 8. // https://bugs.webkit.org/show_bug.cgi?id=137337 support.createHTMLDocument = ( function() { var body = document.implementation.createHTMLDocument( "" ).body; body.innerHTML = "<form></form><form></form>"; return body.childNodes.length === 2; } )(); // Argument "data" should be string of html // context (optional): If specified, the fragment will be created in this context, // defaults to document // keepScripts (optional): If true, will include scripts passed in the html string jQuery.parseHTML = function( data, context, keepScripts ) { if ( typeof data !== "string" ) { return []; } if ( typeof context === "boolean" ) { keepScripts = context; context = false; } var base, parsed, scripts; if ( !context ) { // Stop scripts or inline event handlers from being executed immediately // by using document.implementation if ( support.createHTMLDocument ) { context = document.implementation.createHTMLDocument( "" ); // Set the base href for the created document // so any parsed elements with URLs // are based on the document's URL (gh-2965) base = context.createElement( "base" ); base.href = document.location.href; context.head.appendChild( base ); } else { context = document; } } parsed = rsingleTag.exec( data ); scripts = !keepScripts && []; // Single tag if ( parsed ) { return [ context.createElement( parsed[ 1 ] ) ]; } parsed = buildFragment( [ data ], context, scripts ); if ( scripts && scripts.length ) { jQuery( scripts ).remove(); } return jQuery.merge( [], parsed.childNodes ); }; /** * Load a url into a page */ jQuery.fn.load = function( url, params, callback ) { var selector, type, response, self = this, off = url.indexOf( " " ); if ( off > -1 ) { selector = stripAndCollapse( url.slice( off ) ); url = url.slice( 0, off ); } // If it's a function if ( jQuery.isFunction( params ) ) { // We assume that it's the callback callback = params; params = undefined; // Otherwise, build a param string } else if ( params && typeof params === "object" ) { type = "POST"; } // If we have elements to modify, make the request if ( self.length > 0 ) { jQuery.ajax( { url: url, // If "type" variable is undefined, then "GET" method will be used. // Make value of this field explicit since // user can override it through ajaxSetup method type: type || "GET", dataType: "html", data: params } ).done( function( responseText ) { // Save response for use in complete callback response = arguments; self.html( selector ? // If a selector was specified, locate the right elements in a dummy div // Exclude scripts to avoid IE 'Permission Denied' errors jQuery( "<div>" ).append( jQuery.parseHTML( responseText ) ).find( selector ) : // Otherwise use the full result responseText ); // If the request succeeds, this function gets "data", "status", "jqXHR" // but they are ignored because response was set above. // If it fails, this function gets "jqXHR", "status", "error" } ).always( callback && function( jqXHR, status ) { self.each( function() { callback.apply( this, response || [ jqXHR.responseText, status, jqXHR ] ); } ); } ); } return this; }; // Attach a bunch of functions for handling common AJAX events jQuery.each( [ "ajaxStart", "ajaxStop", "ajaxComplete", "ajaxError", "ajaxSuccess", "ajaxSend" ], function( i, type ) { jQuery.fn[ type ] = function( fn ) { return this.on( type, fn ); }; } ); jQuery.expr.pseudos.animated = function( elem ) { return jQuery.grep( jQuery.timers, function( fn ) { return elem === fn.elem; } ).length; }; /** * Gets a window from an element */ function getWindow( elem ) { return jQuery.isWindow( elem ) ? elem : elem.nodeType === 9 && elem.defaultView; } jQuery.offset = { setOffset: function( elem, options, i ) { var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition, position = jQuery.css( elem, "position" ), curElem = jQuery( elem ), props = {}; // Set position first, in-case top/left are set even on static elem if ( position === "static" ) { elem.style.position = "relative"; } curOffset = curElem.offset(); curCSSTop = jQuery.css( elem, "top" ); curCSSLeft = jQuery.css( elem, "left" ); calculatePosition = ( position === "absolute" || position === "fixed" ) && ( curCSSTop + curCSSLeft ).indexOf( "auto" ) > -1; // Need to be able to calculate position if either // top or left is auto and position is either absolute or fixed if ( calculatePosition ) { curPosition = curElem.position(); curTop = curPosition.top; curLeft = curPosition.left; } else { curTop = parseFloat( curCSSTop ) || 0; curLeft = parseFloat( curCSSLeft ) || 0; } if ( jQuery.isFunction( options ) ) { // Use jQuery.extend here to allow modification of coordinates argument (gh-1848) options = options.call( elem, i, jQuery.extend( {}, curOffset ) ); } if ( options.top != null ) { props.top = ( options.top - curOffset.top ) + curTop; } if ( options.left != null ) { props.left = ( options.left - curOffset.left ) + curLeft; } if ( "using" in options ) { options.using.call( elem, props ); } else { curElem.css( props ); } } }; jQuery.fn.extend( { offset: function( options ) { // Preserve chaining for setter if ( arguments.length ) { return options === undefined ? this : this.each( function( i ) { jQuery.offset.setOffset( this, options, i ); } ); } var docElem, win, rect, doc, elem = this[ 0 ]; if ( !elem ) { return; } // Support: IE <=11 only // Running getBoundingClientRect on a // disconnected node in IE throws an error if ( !elem.getClientRects().length ) { return { top: 0, left: 0 }; } rect = elem.getBoundingClientRect(); // Make sure element is not hidden (display: none) if ( rect.width || rect.height ) { doc = elem.ownerDocument; win = getWindow( doc ); docElem = doc.documentElement; return { top: rect.top + win.pageYOffset - docElem.clientTop, left: rect.left + win.pageXOffset - docElem.clientLeft }; } // Return zeros for disconnected and hidden elements (gh-2310) return rect; }, position: function() { if ( !this[ 0 ] ) { return; } var offsetParent, offset, elem = this[ 0 ], parentOffset = { top: 0, left: 0 }; // Fixed elements are offset from window (parentOffset = {top:0, left: 0}, // because it is its only offset parent if ( jQuery.css( elem, "position" ) === "fixed" ) { // Assume getBoundingClientRect is there when computed position is fixed offset = elem.getBoundingClientRect(); } else { // Get *real* offsetParent offsetParent = this.offsetParent(); // Get correct offsets offset = this.offset(); if ( !jQuery.nodeName( offsetParent[ 0 ], "html" ) ) { parentOffset = offsetParent.offset(); } // Add offsetParent borders parentOffset = { top: parentOffset.top + jQuery.css( offsetParent[ 0 ], "borderTopWidth", true ), left: parentOffset.left + jQuery.css( offsetParent[ 0 ], "borderLeftWidth", true ) }; } // Subtract parent offsets and element margins return { top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ), left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true ) }; }, // This method will return documentElement in the following cases: // 1) For the element inside the iframe without offsetParent, this method will return // documentElement of the parent window // 2) For the hidden or detached element // 3) For body or html element, i.e. in case of the html node - it will return itself // // but those exceptions were never presented as a real life use-cases // and might be considered as more preferable results. // // This logic, however, is not guaranteed and can change at any point in the future offsetParent: function() { return this.map( function() { var offsetParent = this.offsetParent; while ( offsetParent && jQuery.css( offsetParent, "position" ) === "static" ) { offsetParent = offsetParent.offsetParent; } return offsetParent || documentElement; } ); } } ); // Create scrollLeft and scrollTop methods jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function( method, prop ) { var top = "pageYOffset" === prop; jQuery.fn[ method ] = function( val ) { return access( this, function( elem, method, val ) { var win = getWindow( elem ); if ( val === undefined ) { return win ? win[ prop ] : elem[ method ]; } if ( win ) { win.scrollTo( !top ? val : win.pageXOffset, top ? val : win.pageYOffset ); } else { elem[ method ] = val; } }, method, val, arguments.length ); }; } ); // Support: Safari <=7 - 9.1, Chrome <=37 - 49 // Add the top/left cssHooks using jQuery.fn.position // Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084 // Blink bug: https://bugs.chromium.org/p/chromium/issues/detail?id=589347 // getComputedStyle returns percent when specified for top/left/bottom/right; // rather than make the css module depend on the offset module, just check for it here jQuery.each( [ "top", "left" ], function( i, prop ) { jQuery.cssHooks[ prop ] = addGetHookIf( support.pixelPosition, function( elem, computed ) { if ( computed ) { computed = curCSS( elem, prop ); // If curCSS returns percentage, fallback to offset return rnumnonpx.test( computed ) ? jQuery( elem ).position()[ prop ] + "px" : computed; } } ); } ); // Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods jQuery.each( { Height: "height", Width: "width" }, function( name, type ) { jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) { // Margin is only for outerHeight, outerWidth jQuery.fn[ funcName ] = function( margin, value ) { var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ), extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" ); return access( this, function( elem, type, value ) { var doc; if ( jQuery.isWindow( elem ) ) { // $( window ).outerWidth/Height return w/h including scrollbars (gh-1729) return funcName.indexOf( "outer" ) === 0 ? elem[ "inner" + name ] : elem.document.documentElement[ "client" + name ]; } // Get document width or height if ( elem.nodeType === 9 ) { doc = elem.documentElement; // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height], // whichever is greatest return Math.max( elem.body[ "scroll" + name ], doc[ "scroll" + name ], elem.body[ "offset" + name ], doc[ "offset" + name ], doc[ "client" + name ] ); } return value === undefined ? // Get width or height on the element, requesting but not forcing parseFloat jQuery.css( elem, type, extra ) : // Set width or height on the element jQuery.style( elem, type, value, extra ); }, type, chainable ? margin : undefined, chainable ); }; } ); } ); jQuery.fn.extend( { bind: function( types, data, fn ) { return this.on( types, null, data, fn ); }, unbind: function( types, fn ) { return this.off( types, null, fn ); }, delegate: function( selector, types, data, fn ) { return this.on( types, selector, data, fn ); }, undelegate: function( selector, types, fn ) { // ( namespace ) or ( selector, types [, fn] ) return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn ); } } ); jQuery.parseJSON = JSON.parse; // Register as a named AMD module, since jQuery can be concatenated with other // files that may use define, but not via a proper concatenation script that // understands anonymous AMD modules. A named AMD is safest and most robust // way to register. Lowercase jquery is used because AMD module names are // derived from file names, and jQuery is normally delivered in a lowercase // file name. Do this after creating the global so that if an AMD module wants // to call noConflict to hide this version of jQuery, it will work. // Note that for maximum portability, libraries that are not jQuery should // declare themselves as anonymous modules, and avoid setting a global if an // AMD loader is present. jQuery is a special case. For more information, see // https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon if ( typeof define === "function" && define.amd ) { define( "jquery", [], function() { return jQuery; } ); } var // Map over jQuery in case of overwrite _jQuery = window.jQuery, // Map over the $ in case of overwrite _$ = window.$; jQuery.noConflict = function( deep ) { if ( window.$ === jQuery ) { window.$ = _$; } if ( deep && window.jQuery === jQuery ) { window.jQuery = _jQuery; } return jQuery; }; // Expose jQuery and $ identifiers, even in AMD // (#7102#comment:10, https://github.com/jquery/jquery/pull/557) // and CommonJS for browser emulators (#13566) if ( !noGlobal ) { window.jQuery = window.$ = jQuery; } return jQuery; } ); assets/js/tablesaw/stackonly/tablesaw.stackonly.js 0000644 00000144346 14736103451 0016461 0 ustar 00 /*! Tablesaw - v3.0.1 - 2017-02-16 * https://github.com/filamentgroup/tablesaw * Copyright (c) 2017 Filament Group; Licensed MIT */ /*! Shoestring - v2.0.0 - 2017-02-14 * http://github.com/filamentgroup/shoestring/ * Copyright (c) 2017 Scott Jehl, Filament Group, Inc; Licensed MIT & GPLv2 */ (function( factory ) { if( typeof define === 'function' && define.amd ) { // AMD. Register as an anonymous module. define( [ 'shoestring' ], factory ); } else if (typeof module === 'object' && module.exports) { // Node/CommonJS module.exports = factory(); } else { // Browser globals factory(); } }(function () { var win = typeof window !== "undefined" ? window : this; var doc = win.document; /** * The shoestring object constructor. * * @param {string,object} prim The selector to find or element to wrap. * @param {object} sec The context in which to match the `prim` selector. * @returns shoestring * @this window */ function shoestring( prim, sec ){ var pType = typeof( prim ), ret = [], sel; // return an empty shoestring object if( !prim ){ return new Shoestring( ret ); } // ready calls if( prim.call ){ return shoestring.ready( prim ); } // handle re-wrapping shoestring objects if( prim.constructor === Shoestring && !sec ){ return prim; } // if string starting with <, make html if( pType === "string" && prim.indexOf( "<" ) === 0 ){ var dfrag = doc.createElement( "div" ); dfrag.innerHTML = prim; // TODO depends on children (circular) return shoestring( dfrag ).children().each(function(){ dfrag.removeChild( this ); }); } // if string, it's a selector, use qsa if( pType === "string" ){ if( sec ){ return shoestring( sec ).find( prim ); } sel = doc.querySelectorAll( prim ); return new Shoestring( sel, prim ); } // array like objects or node lists if( Object.prototype.toString.call( pType ) === '[object Array]' || (win.NodeList && prim instanceof win.NodeList) ){ return new Shoestring( prim, prim ); } // if it's an array, use all the elements if( prim.constructor === Array ){ return new Shoestring( prim, prim ); } // otherwise assume it's an object the we want at an index return new Shoestring( [prim], prim ); } var Shoestring = function( ret, prim ) { this.length = 0; this.selector = prim; shoestring.merge(this, ret); }; // TODO only required for tests Shoestring.prototype.reverse = [].reverse; // For adding element set methods shoestring.fn = Shoestring.prototype; shoestring.Shoestring = Shoestring; // For extending objects // TODO move to separate module when we use prototypes shoestring.extend = function( first, second ){ for( var i in second ){ if( second.hasOwnProperty( i ) ){ first[ i ] = second[ i ]; } } return first; }; // taken directly from jQuery shoestring.merge = function( first, second ) { var len, j, i; len = +second.length, j = 0, i = first.length; for ( ; j < len; j++ ) { first[ i++ ] = second[ j ]; } first.length = i; return first; }; // expose win.shoestring = shoestring; /** * Iterates over `shoestring` collections. * * @param {function} callback The callback to be invoked on each element and index * @return shoestring * @this shoestring */ shoestring.fn.each = function( callback ){ return shoestring.each( this, callback ); }; shoestring.each = function( collection, callback ) { var val; for( var i = 0, il = collection.length; i < il; i++ ){ val = callback.call( collection[i], i, collection[i] ); if( val === false ){ break; } } return collection; }; /** * Check for array membership. * * @param {object} needle The thing to find. * @param {object} haystack The thing to find the needle in. * @return {boolean} * @this window */ shoestring.inArray = function( needle, haystack ){ var isin = -1; for( var i = 0, il = haystack.length; i < il; i++ ){ if( haystack.hasOwnProperty( i ) && haystack[ i ] === needle ){ isin = i; } } return isin; }; /** * Bind callbacks to be run when the DOM is "ready". * * @param {function} fn The callback to be run * @return shoestring * @this shoestring */ shoestring.ready = function( fn ){ if( ready && fn ){ fn.call( doc ); } else if( fn ){ readyQueue.push( fn ); } else { runReady(); } return [doc]; }; // TODO necessary? shoestring.fn.ready = function( fn ){ shoestring.ready( fn ); return this; }; // Empty and exec the ready queue var ready = false, readyQueue = [], runReady = function(){ if( !ready ){ while( readyQueue.length ){ readyQueue.shift().call( doc ); } ready = true; } }; // If DOM is already ready at exec time, depends on the browser. // From: https://github.com/mobify/mobifyjs/blob/526841be5509e28fc949038021799e4223479f8d/src/capture.js#L128 if (doc.attachEvent ? doc.readyState === "complete" : doc.readyState !== "loading") { runReady(); } else { doc.addEventListener( "DOMContentLoaded", runReady, false ); doc.addEventListener( "readystatechange", runReady, false ); win.addEventListener( "load", runReady, false ); } /** * Checks the current set of elements against the selector, if one matches return `true`. * * @param {string} selector The selector to check. * @return {boolean} * @this {shoestring} */ shoestring.fn.is = function( selector ){ var ret = false, self = this, parents, check; // assume a dom element if( typeof selector !== "string" ){ // array-like, ie shoestring objects or element arrays if( selector.length && selector[0] ){ check = selector; } else { check = [selector]; } return _checkElements(this, check); } parents = this.parent(); if( !parents.length ){ parents = shoestring( doc ); } parents.each(function( i, e ) { var children; children = e.querySelectorAll( selector ); ret = _checkElements( self, children ); }); return ret; }; function _checkElements(needles, haystack){ var ret = false; needles.each(function() { var j = 0; while( j < haystack.length ){ if( this === haystack[j] ){ ret = true; } j++; } }); return ret; } /** * Get data attached to the first element or set data values on all elements in the current set. * * @param {string} name The data attribute name. * @param {any} value The value assigned to the data attribute. * @return {any|shoestring} * @this shoestring */ shoestring.fn.data = function( name, value ){ if( name !== undefined ){ if( value !== undefined ){ return this.each(function(){ if( !this.shoestringData ){ this.shoestringData = {}; } this.shoestringData[ name ] = value; }); } else { if( this[ 0 ] ) { if( this[ 0 ].shoestringData ) { return this[ 0 ].shoestringData[ name ]; } } } } else { return this[ 0 ] ? this[ 0 ].shoestringData || {} : undefined; } }; /** * Remove data associated with `name` or all the data, for each element in the current set. * * @param {string} name The data attribute name. * @return shoestring * @this shoestring */ shoestring.fn.removeData = function( name ){ return this.each(function(){ if( name !== undefined && this.shoestringData ){ this.shoestringData[ name ] = undefined; delete this.shoestringData[ name ]; } else { this[ 0 ].shoestringData = {}; } }); }; /** * An alias for the `shoestring` constructor. */ win.$ = shoestring; /** * Add a class to each DOM element in the set of elements. * * @param {string} className The name of the class to be added. * @return shoestring * @this shoestring */ shoestring.fn.addClass = function( className ){ var classes = className.replace(/^\s+|\s+$/g, '').split( " " ); return this.each(function(){ for( var i = 0, il = classes.length; i < il; i++ ){ if( this.className !== undefined && (this.className === "" || !this.className.match( new RegExp( "(^|\\s)" + classes[ i ] + "($|\\s)"))) ){ this.className += " " + classes[ i ]; } } }); }; /** * Add elements matching the selector to the current set. * * @param {string} selector The selector for the elements to add from the DOM * @return shoestring * @this shoestring */ shoestring.fn.add = function( selector ){ var ret = []; this.each(function(){ ret.push( this ); }); shoestring( selector ).each(function(){ ret.push( this ); }); return shoestring( ret ); }; /** * Insert an element or HTML string as the last child of each element in the set. * * @param {string|HTMLElement} fragment The HTML or HTMLElement to insert. * @return shoestring * @this shoestring */ shoestring.fn.append = function( fragment ){ if( typeof( fragment ) === "string" || fragment.nodeType !== undefined ){ fragment = shoestring( fragment ); } return this.each(function( i ){ for( var j = 0, jl = fragment.length; j < jl; j++ ){ this.appendChild( i > 0 ? fragment[ j ].cloneNode( true ) : fragment[ j ] ); } }); }; /** * Insert the current set as the last child of the elements matching the selector. * * @param {string} selector The selector after which to append the current set. * @return shoestring * @this shoestring */ shoestring.fn.appendTo = function( selector ){ return this.each(function(){ shoestring( selector ).append( this ); }); }; /** * Get the value of the first element of the set or set the value of all the elements in the set. * * @param {string} name The attribute name. * @param {string} value The new value for the attribute. * @return {shoestring|string|undefined} * @this {shoestring} */ shoestring.fn.attr = function( name, value ){ var nameStr = typeof( name ) === "string"; if( value !== undefined || !nameStr ){ return this.each(function(){ if( nameStr ){ this.setAttribute( name, value ); } else { for( var i in name ){ if( name.hasOwnProperty( i ) ){ this.setAttribute( i, name[ i ] ); } } } }); } else { return this[ 0 ] ? this[ 0 ].getAttribute( name ) : undefined; } }; /** * Insert an element or HTML string before each element in the current set. * * @param {string|HTMLElement} fragment The HTML or HTMLElement to insert. * @return shoestring * @this shoestring */ shoestring.fn.before = function( fragment ){ if( typeof( fragment ) === "string" || fragment.nodeType !== undefined ){ fragment = shoestring( fragment ); } return this.each(function( i ){ for( var j = 0, jl = fragment.length; j < jl; j++ ){ this.parentNode.insertBefore( i > 0 ? fragment[ j ].cloneNode( true ) : fragment[ j ], this ); } }); }; /** * Get the children of the current collection. * @return shoestring * @this shoestring */ shoestring.fn.children = function(){ var ret = [], childs, j; this.each(function(){ childs = this.children; j = -1; while( j++ < childs.length-1 ){ if( shoestring.inArray( childs[ j ], ret ) === -1 ){ ret.push( childs[ j ] ); } } }); return shoestring(ret); }; /** * Find an element matching the selector in the set of the current element and its parents. * * @param {string} selector The selector used to identify the target element. * @return shoestring * @this shoestring */ shoestring.fn.closest = function( selector ){ var ret = []; if( !selector ){ return shoestring( ret ); } this.each(function(){ var element, $self = shoestring( element = this ); if( $self.is(selector) ){ ret.push( this ); return; } while( element.parentElement ) { if( shoestring(element.parentElement).is(selector) ){ ret.push( element.parentElement ); break; } element = element.parentElement; } }); return shoestring( ret ); }; shoestring.cssExceptions = { 'float': [ 'cssFloat' ] }; (function() { var cssExceptions = shoestring.cssExceptions; // IE8 uses marginRight instead of margin-right function convertPropertyName( str ) { return str.replace( /\-([A-Za-z])/g, function ( match, character ) { return character.toUpperCase(); }); } function _getStyle( element, property ) { return win.getComputedStyle( element, null ).getPropertyValue( property ); } var vendorPrefixes = [ '', '-webkit-', '-ms-', '-moz-', '-o-', '-khtml-' ]; /** * Private function for getting the computed style of an element. * * **NOTE** Please use the [css](../css.js.html) method instead. * * @method _getStyle * @param {HTMLElement} element The element we want the style property for. * @param {string} property The css property we want the style for. */ shoestring._getStyle = function( element, property ) { var convert, value, j, k; if( cssExceptions[ property ] ) { for( j = 0, k = cssExceptions[ property ].length; j < k; j++ ) { value = _getStyle( element, cssExceptions[ property ][ j ] ); if( value ) { return value; } } } for( j = 0, k = vendorPrefixes.length; j < k; j++ ) { convert = convertPropertyName( vendorPrefixes[ j ] + property ); // VendorprefixKeyName || key-name value = _getStyle( element, convert ); if( convert !== property ) { value = value || _getStyle( element, property ); } if( vendorPrefixes[ j ] ) { // -vendorprefix-key-name value = value || _getStyle( element, vendorPrefixes[ j ] + property ); } if( value ) { return value; } } return undefined; }; })(); (function() { var cssExceptions = shoestring.cssExceptions; // IE8 uses marginRight instead of margin-right function convertPropertyName( str ) { return str.replace( /\-([A-Za-z])/g, function ( match, character ) { return character.toUpperCase(); }); } /** * Private function for setting the style of an element. * * **NOTE** Please use the [css](../css.js.html) method instead. * * @method _setStyle * @param {HTMLElement} element The element we want to style. * @param {string} property The property being used to style the element. * @param {string} value The css value for the style property. */ shoestring._setStyle = function( element, property, value ) { var convertedProperty = convertPropertyName(property); element.style[ property ] = value; if( convertedProperty !== property ) { element.style[ convertedProperty ] = value; } if( cssExceptions[ property ] ) { for( var j = 0, k = cssExceptions[ property ].length; j<k; j++ ) { element.style[ cssExceptions[ property ][ j ] ] = value; } } }; })(); /** * Get the compute style property of the first element or set the value of a style property * on all elements in the set. * * @method _setStyle * @param {string} property The property being used to style the element. * @param {string|undefined} value The css value for the style property. * @return {string|shoestring} * @this shoestring */ shoestring.fn.css = function( property, value ){ if( !this[0] ){ return; } if( typeof property === "object" ) { return this.each(function() { for( var key in property ) { if( property.hasOwnProperty( key ) ) { shoestring._setStyle( this, key, property[key] ); } } }); } else { // assignment else retrieve first if( value !== undefined ){ return this.each(function(){ shoestring._setStyle( this, property, value ); }); } return shoestring._getStyle( this[0], property ); } }; /** * Returns the indexed element wrapped in a new `shoestring` object. * * @param {integer} index The index of the element to wrap and return. * @return shoestring * @this shoestring */ shoestring.fn.eq = function( index ){ if( this[index] ){ return shoestring( this[index] ); } return shoestring([]); }; /** * Filter out the current set if they do *not* match the passed selector or * the supplied callback returns false * * @param {string,function} selector The selector or boolean return value callback used to filter the elements. * @return shoestring * @this shoestring */ shoestring.fn.filter = function( selector ){ var ret = []; this.each(function( index ){ var wsel; if( typeof selector === 'function' ) { if( selector.call( this, index ) !== false ) { ret.push( this ); } } else { if( !this.parentNode ){ var context = shoestring( doc.createDocumentFragment() ); context[ 0 ].appendChild( this ); wsel = shoestring( selector, context ); } else { wsel = shoestring( selector, this.parentNode ); } if( shoestring.inArray( this, wsel ) > -1 ){ ret.push( this ); } } }); return shoestring( ret ); }; /** * Find descendant elements of the current collection. * * @param {string} selector The selector used to find the children * @return shoestring * @this shoestring */ shoestring.fn.find = function( selector ){ var ret = [], finds; this.each(function(){ finds = this.querySelectorAll( selector ); for( var i = 0, il = finds.length; i < il; i++ ){ ret = ret.concat( finds[i] ); } }); return shoestring( ret ); }; /** * Returns the first element of the set wrapped in a new `shoestring` object. * * @return shoestring * @this shoestring */ shoestring.fn.first = function(){ return this.eq( 0 ); }; /** * Returns the raw DOM node at the passed index. * * @param {integer} index The index of the element to wrap and return. * @return {HTMLElement|undefined|array} * @this shoestring */ shoestring.fn.get = function( index ){ // return an array of elements if index is undefined if( index === undefined ){ var elements = []; for( var i = 0; i < this.length; i++ ){ elements.push( this[ i ] ); } return elements; } else { return this[ index ]; } }; var set = function( html ){ if( typeof html === "string" || typeof html === "number" ){ return this.each(function(){ this.innerHTML = "" + html; }); } else { var h = ""; if( typeof html.length !== "undefined" ){ for( var i = 0, l = html.length; i < l; i++ ){ h += html[i].outerHTML; } } else { h = html.outerHTML; } return this.each(function(){ this.innerHTML = h; }); } }; /** * Gets or sets the `innerHTML` from all the elements in the set. * * @param {string|undefined} html The html to assign * @return {string|shoestring} * @this shoestring */ shoestring.fn.html = function( html ){ if( typeof html !== "undefined" ){ return set.call( this, html ); } else { // get var pile = ""; this.each(function(){ pile += this.innerHTML; }); return pile; } }; (function() { function _getIndex( set, test ) { var i, result, element; for( i = result = 0; i < set.length; i++ ) { element = set.item ? set.item(i) : set[i]; if( test(element) ){ return result; } // ignore text nodes, etc // NOTE may need to be more permissive if( element.nodeType === 1 ){ result++; } } return -1; } /** * Find the index in the current set for the passed selector. * Without a selector it returns the index of the first node within the array of its siblings. * * @param {string|undefined} selector The selector used to search for the index. * @return {integer} * @this {shoestring} */ shoestring.fn.index = function( selector ){ var self, children; self = this; // no arg? check the children, otherwise check each element that matches if( selector === undefined ){ children = ( ( this[ 0 ] && this[0].parentNode ) || doc.documentElement).childNodes; // check if the element matches the first of the set return _getIndex(children, function( element ) { return self[0] === element; }); } else { // check if the element matches the first selected node from the parent return _getIndex(self, function( element ) { return element === (shoestring( selector, element.parentNode )[ 0 ]); }); } }; })(); /** * Insert the current set before the elements matching the selector. * * @param {string} selector The selector before which to insert the current set. * @return shoestring * @this shoestring */ shoestring.fn.insertBefore = function( selector ){ return this.each(function(){ shoestring( selector ).before( this ); }); }; /** * Returns the last element of the set wrapped in a new `shoestring` object. * * @return shoestring * @this shoestring */ shoestring.fn.last = function(){ return this.eq( this.length - 1 ); }; /** * Returns a `shoestring` object with the set of siblings of each element in the original set. * * @return shoestring * @this shoestring */ shoestring.fn.next = function(){ var result = []; // TODO need to implement map this.each(function() { var children, item, found; // get the child nodes for this member of the set children = shoestring( this.parentNode )[0].childNodes; for( var i = 0; i < children.length; i++ ){ item = children.item( i ); // found the item we needed (found) which means current item value is // the next node in the list, as long as it's viable grab it // NOTE may need to be more permissive if( found && item.nodeType === 1 ){ result.push( item ); break; } // find the current item and mark it as found if( item === this ){ found = true; } } }); return shoestring( result ); }; /** * Removes elements from the current set. * * @param {string} selector The selector to use when removing the elements. * @return shoestring * @this shoestring */ shoestring.fn.not = function( selector ){ var ret = []; this.each(function(){ var found = shoestring( selector, this.parentNode ); if( shoestring.inArray(this, found) === -1 ){ ret.push( this ); } }); return shoestring( ret ); }; /** * Returns the set of first parents for each element in the current set. * * @return shoestring * @this shoestring */ shoestring.fn.parent = function(){ var ret = [], parent; this.each(function(){ // no parent node, assume top level // jQuery parent: return the document object for <html> or the parent node if it exists parent = (this === doc.documentElement ? doc : this.parentNode); // if there is a parent and it's not a document fragment if( parent && parent.nodeType !== 11 ){ ret.push( parent ); } }); return shoestring(ret); }; /** * Add an HTML string or element before the children of each element in the current set. * * @param {string|HTMLElement} fragment The HTML string or element to add. * @return shoestring * @this shoestring */ shoestring.fn.prepend = function( fragment ){ if( typeof( fragment ) === "string" || fragment.nodeType !== undefined ){ fragment = shoestring( fragment ); } return this.each(function( i ){ for( var j = 0, jl = fragment.length; j < jl; j++ ){ var insertEl = i > 0 ? fragment[ j ].cloneNode( true ) : fragment[ j ]; if ( this.firstChild ){ this.insertBefore( insertEl, this.firstChild ); } else { this.appendChild( insertEl ); } } }); }; /** * Returns a `shoestring` object with the set of *one* siblingx before each element in the original set. * * @return shoestring * @this shoestring */ shoestring.fn.prev = function(){ var result = []; // TODO need to implement map this.each(function() { var children, item, found; // get the child nodes for this member of the set children = shoestring( this.parentNode )[0].childNodes; for( var i = children.length -1; i >= 0; i-- ){ item = children.item( i ); // found the item we needed (found) which means current item value is // the next node in the list, as long as it's viable grab it // NOTE may need to be more permissive if( found && item.nodeType === 1 ){ result.push( item ); break; } // find the current item and mark it as found if( item === this ){ found = true; } } }); return shoestring( result ); }; /** * Returns a `shoestring` object with the set of *all* siblings before each element in the original set. * * @return shoestring * @this shoestring */ shoestring.fn.prevAll = function(){ var result = []; this.each(function() { var $previous = shoestring( this ).prev(); while( $previous.length ){ result.push( $previous[0] ); $previous = $previous.prev(); } }); return shoestring( result ); }; /** * Remove an attribute from each element in the current set. * * @param {string} name The name of the attribute. * @return shoestring * @this shoestring */ shoestring.fn.removeAttr = function( name ){ return this.each(function(){ this.removeAttribute( name ); }); }; /** * Remove a class from each DOM element in the set of elements. * * @param {string} className The name of the class to be removed. * @return shoestring * @this shoestring */ shoestring.fn.removeClass = function( cname ){ var classes = cname.replace(/^\s+|\s+$/g, '').split( " " ); return this.each(function(){ var newClassName, regex; for( var i = 0, il = classes.length; i < il; i++ ){ if( this.className !== undefined ){ regex = new RegExp( "(^|\\s)" + classes[ i ] + "($|\\s)", "gmi" ); newClassName = this.className.replace( regex, " " ); this.className = newClassName.replace(/^\s+|\s+$/g, ''); } } }); }; /** * Remove the current set of elements from the DOM. * * @return shoestring * @this shoestring */ shoestring.fn.remove = function(){ return this.each(function(){ if( this.parentNode ) { this.parentNode.removeChild( this ); } }); }; /** * Replace each element in the current set with that argument HTML string or HTMLElement. * * @param {string|HTMLElement} fragment The value to assign. * @return shoestring * @this shoestring */ shoestring.fn.replaceWith = function( fragment ){ if( typeof( fragment ) === "string" ){ fragment = shoestring( fragment ); } var ret = []; if( fragment.length > 1 ){ fragment = fragment.reverse(); } this.each(function( i ){ var clone = this.cloneNode( true ), insertEl; ret.push( clone ); // If there is no parentNode, this is pointless, drop it. if( !this.parentNode ){ return; } if( fragment.length === 1 ){ insertEl = i > 0 ? fragment[ 0 ].cloneNode( true ) : fragment[ 0 ]; this.parentNode.replaceChild( insertEl, this ); } else { for( var j = 0, jl = fragment.length; j < jl; j++ ){ insertEl = i > 0 ? fragment[ j ].cloneNode( true ) : fragment[ j ]; this.parentNode.insertBefore( insertEl, this.nextSibling ); } this.parentNode.removeChild( this ); } }); return shoestring( ret ); }; /** * Get all of the sibling elements for each element in the current set. * * @return shoestring * @this shoestring */ shoestring.fn.siblings = function(){ if( !this.length ) { return shoestring( [] ); } var sibs = [], el = this[ 0 ].parentNode.firstChild; do { if( el.nodeType === 1 && el !== this[ 0 ] ) { sibs.push( el ); } el = el.nextSibling; } while( el ); return shoestring( sibs ); }; var getText = function( elem ){ var node, ret = "", i = 0, nodeType = elem.nodeType; if ( !nodeType ) { // If no nodeType, this is expected to be an array while ( (node = elem[i++]) ) { // Do not traverse comment nodes ret += getText( node ); } } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { // Use textContent for elements // innerText usage removed for consistency of new lines (jQuery #11153) if ( typeof elem.textContent === "string" ) { return elem.textContent; } else { // Traverse its children for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { ret += getText( elem ); } } } else if ( nodeType === 3 || nodeType === 4 ) { return elem.nodeValue; } // Do not include comment or processing instruction nodes return ret; }; /** * Recursively retrieve the text content of the each element in the current set. * * @return shoestring * @this shoestring */ shoestring.fn.text = function() { return getText( this ); }; /** * Get the value of the first element or set the value of all elements in the current set. * * @param {string} value The value to set. * @return shoestring * @this shoestring */ shoestring.fn.val = function( value ){ var el; if( value !== undefined ){ return this.each(function(){ if( this.tagName === "SELECT" ){ var optionSet, option, options = this.options, values = [], i = options.length, newIndex; values[0] = value; while ( i-- ) { option = options[ i ]; if ( (option.selected = shoestring.inArray( option.value, values ) >= 0) ) { optionSet = true; newIndex = i; } } // force browsers to behave consistently when non-matching value is set if ( !optionSet ) { this.selectedIndex = -1; } else { this.selectedIndex = newIndex; } } else { this.value = value; } }); } else { el = this[0]; if( el.tagName === "SELECT" ){ if( el.selectedIndex < 0 ){ return ""; } return el.options[ el.selectedIndex ].value; } else { return el.value; } } }; /** * Private function for setting/getting the offset property for height/width. * * **NOTE** Please use the [width](width.js.html) or [height](height.js.html) methods instead. * * @param {shoestring} set The set of elements. * @param {string} name The string "height" or "width". * @param {float|undefined} value The value to assign. * @return shoestring * @this window */ shoestring._dimension = function( set, name, value ){ var offsetName; if( value === undefined ){ offsetName = name.replace(/^[a-z]/, function( letter ) { return letter.toUpperCase(); }); return set[ 0 ][ "offset" + offsetName ]; } else { // support integer values as pixels value = typeof value === "string" ? value : value + "px"; return set.each(function(){ this.style[ name ] = value; }); } }; /** * Gets the width value of the first element or sets the width for the whole set. * * @param {float|undefined} value The value to assign. * @return shoestring * @this shoestring */ shoestring.fn.width = function( value ){ return shoestring._dimension( this, "width", value ); }; /** * Wraps the child elements in the provided HTML. * * @param {string} html The wrapping HTML. * @return shoestring * @this shoestring */ shoestring.fn.wrapInner = function( html ){ return this.each(function(){ var inH = this.innerHTML; this.innerHTML = ""; shoestring( this ).append( shoestring( html ).html( inH ) ); }); }; function initEventCache( el, evt ) { if ( !el.shoestringData ) { el.shoestringData = {}; } if ( !el.shoestringData.events ) { el.shoestringData.events = {}; } if ( !el.shoestringData.loop ) { el.shoestringData.loop = {}; } if ( !el.shoestringData.events[ evt ] ) { el.shoestringData.events[ evt ] = []; } } function addToEventCache( el, evt, eventInfo ) { var obj = {}; obj.isCustomEvent = eventInfo.isCustomEvent; obj.callback = eventInfo.callfunc; obj.originalCallback = eventInfo.originalCallback; obj.namespace = eventInfo.namespace; el.shoestringData.events[ evt ].push( obj ); if( eventInfo.customEventLoop ) { el.shoestringData.loop[ evt ] = eventInfo.customEventLoop; } } /** * Bind a callback to an event for the currrent set of elements. * * @param {string} evt The event(s) to watch for. * @param {object,function} data Data to be included with each event or the callback. * @param {function} originalCallback Callback to be invoked when data is define.d. * @return shoestring * @this shoestring */ shoestring.fn.bind = function( evt, data, originalCallback ){ if( typeof data === "function" ){ originalCallback = data; data = null; } var evts = evt.split( " " ); // NOTE the `triggeredElement` is purely for custom events from IE function encasedCallback( e, namespace, triggeredElement ){ var result; if( e._namespace && e._namespace !== namespace ) { return; } e.data = data; e.namespace = e._namespace; var returnTrue = function(){ return true; }; e.isDefaultPrevented = function(){ return false; }; var originalPreventDefault = e.preventDefault; var preventDefaultConstructor = function(){ if( originalPreventDefault ) { return function(){ e.isDefaultPrevented = returnTrue; originalPreventDefault.call(e); }; } else { return function(){ e.isDefaultPrevented = returnTrue; e.returnValue = false; }; } }; // thanks https://github.com/jonathantneal/EventListener e.target = triggeredElement || e.target || e.srcElement; e.preventDefault = preventDefaultConstructor(); e.stopPropagation = e.stopPropagation || function () { e.cancelBubble = true; }; result = originalCallback.apply(this, [ e ].concat( e._args ) ); if( result === false ){ e.preventDefault(); e.stopPropagation(); } return result; } return this.each(function(){ var domEventCallback, customEventCallback, customEventLoop, oEl = this; for( var i = 0, il = evts.length; i < il; i++ ){ var split = evts[ i ].split( "." ), evt = split[ 0 ], namespace = split.length > 0 ? split[ 1 ] : null; domEventCallback = function( originalEvent ) { if( oEl.ssEventTrigger ) { originalEvent._namespace = oEl.ssEventTrigger._namespace; originalEvent._args = oEl.ssEventTrigger._args; oEl.ssEventTrigger = null; } return encasedCallback.call( oEl, originalEvent, namespace ); }; customEventCallback = null; customEventLoop = null; initEventCache( this, evt ); this.addEventListener( evt, domEventCallback, false ); addToEventCache( this, evt, { callfunc: customEventCallback || domEventCallback, isCustomEvent: !!customEventCallback, customEventLoop: customEventLoop, originalCallback: originalCallback, namespace: namespace }); } }); }; shoestring.fn.on = shoestring.fn.bind; /** * Unbind a previous bound callback for an event. * * @param {string} event The event(s) the callback was bound to.. * @param {function} callback Callback to unbind. * @return shoestring * @this shoestring */ shoestring.fn.unbind = function( event, callback ){ var evts = event ? event.split( " " ) : []; return this.each(function(){ if( !this.shoestringData || !this.shoestringData.events ) { return; } if( !evts.length ) { unbindAll.call( this ); } else { var split, evt, namespace; for( var i = 0, il = evts.length; i < il; i++ ){ split = evts[ i ].split( "." ), evt = split[ 0 ], namespace = split.length > 0 ? split[ 1 ] : null; if( evt ) { unbind.call( this, evt, namespace, callback ); } else { unbindAll.call( this, namespace, callback ); } } } }); }; function unbind( evt, namespace, callback ) { var bound = this.shoestringData.events[ evt ]; if( !(bound && bound.length) ) { return; } var matched = [], j, jl; for( j = 0, jl = bound.length; j < jl; j++ ) { if( !namespace || namespace === bound[ j ].namespace ) { if( callback === undefined || callback === bound[ j ].originalCallback ) { this.removeEventListener( evt, bound[ j ].callback, false ); matched.push( j ); } } } for( j = 0, jl = matched.length; j < jl; j++ ) { this.shoestringData.events[ evt ].splice( j, 1 ); } } function unbindAll( namespace, callback ) { for( var evtKey in this.shoestringData.events ) { unbind.call( this, evtKey, namespace, callback ); } } shoestring.fn.off = shoestring.fn.unbind; /** * Bind a callback to an event for the currrent set of elements, unbind after one occurence. * * @param {string} event The event(s) to watch for. * @param {function} callback Callback to invoke on the event. * @return shoestring * @this shoestring */ shoestring.fn.one = function( event, callback ){ var evts = event.split( " " ); return this.each(function(){ var thisevt, cbs = {}, $t = shoestring( this ); for( var i = 0, il = evts.length; i < il; i++ ){ thisevt = evts[ i ]; cbs[ thisevt ] = function( e ){ var $t = shoestring( this ); for( var j in cbs ) { $t.unbind( j, cbs[ j ] ); } return callback.apply( this, [ e ].concat( e._args ) ); }; $t.bind( thisevt, cbs[ thisevt ] ); } }); }; /** * Trigger an event on the first element in the set, no bubbling, no defaults. * * @param {string} event The event(s) to trigger. * @param {object} args Arguments to append to callback invocations. * @return shoestring * @this shoestring */ shoestring.fn.triggerHandler = function( event, args ){ var e = event.split( " " )[ 0 ], el = this[ 0 ], ret; // See this.fireEvent( 'on' + evts[ i ], document.createEventObject() ); instead of click() etc in trigger. if( doc.createEvent && el.shoestringData && el.shoestringData.events && el.shoestringData.events[ e ] ){ var bindings = el.shoestringData.events[ e ]; for (var i in bindings ){ if( bindings.hasOwnProperty( i ) ){ event = doc.createEvent( "Event" ); event.initEvent( e, true, true ); event._args = args; args.unshift( event ); ret = bindings[ i ].originalCallback.apply( event.target, args ); } } } return ret; }; /** * Trigger an event on each of the DOM elements in the current set. * * @param {string} event The event(s) to trigger. * @param {object} args Arguments to append to callback invocations. * @return shoestring * @this shoestring */ shoestring.fn.trigger = function( event, args ){ var evts = event.split( " " ); return this.each(function(){ var split, evt, namespace; for( var i = 0, il = evts.length; i < il; i++ ){ split = evts[ i ].split( "." ), evt = split[ 0 ], namespace = split.length > 0 ? split[ 1 ] : null; if( evt === "click" ){ if( this.tagName === "INPUT" && this.type === "checkbox" && this.click ){ this.click(); return false; } } if( doc.createEvent ){ var event = doc.createEvent( "Event" ); event.initEvent( evt, true, true ); event._args = args; event._namespace = namespace; this.dispatchEvent( event ); } } }); }; return shoestring; })); // UMD module definition // From: https://github.com/umdjs/umd/blob/master/templates/jqueryPlugin.js (function (factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module. define(['shoestring'], factory); } else if (typeof module === 'object' && module.exports) { // Node/CommonJS module.exports = function( root, shoestring ) { if ( shoestring === undefined ) { // require('shoestring') returns a factory that requires window to // build a shoestring instance, we normalize how we use modules // that require this pattern but the window provided is a noop // if it's defined (how jquery works) if ( typeof window !== 'undefined' ) { shoestring = require('shoestring'); } else { shoestring = require('shoestring')(root); } } factory(shoestring); return shoestring; }; } else { // Browser globals factory(shoestring); } }(function ($) { "use strict"; var win = typeof window !== "undefined" ? window : this; var Tablesaw = { i18n: { modes: [ 'Stack', 'Swipe', 'Toggle' ], columns: 'Col<span class=\"a11y-sm\">umn</span>s', columnBtnText: 'Columns', columnsDialogError: 'No eligible columns.', sort: 'Sort' }, // cut the mustard mustard: ( 'head' in document ) && // IE9+, Firefox 4+, Safari 5.1+, Mobile Safari 4.1+, Opera 11.5+, Android 2.3+ ( !window.blackberry || window.WebKitPoint ) && // only WebKit Blackberry (OS 6+) !window.operamini }; if( Tablesaw.mustard ) { $( document.documentElement ).addClass( 'tablesaw-enhanced' ); } (function() { var pluginName = "tablesaw"; var classes = { toolbar: "tablesaw-bar" }; var events = { create: "tablesawcreate", destroy: "tablesawdestroy", refresh: "tablesawrefresh", resize: "tablesawresize" }; var defaultMode = "stack"; var initSelector = "table[data-tablesaw-mode],table[data-tablesaw-sortable]"; var defaultConfig = { getHeaderCells: function() { return this.$table.find( "thead" ).children().filter( "tr" ).eq( 0 ).find( "th" ); } }; Tablesaw.events = events; var Table = function( element ) { if( !element ) { throw new Error( "Tablesaw requires an element." ); } this.table = element; this.$table = $( element ); this.mode = this.$table.attr( "data-tablesaw-mode" ) || defaultMode; this.init(); }; Table.prototype.init = function() { // assign an id if there is none if ( !this.$table.attr( "id" ) ) { this.$table.attr( "id", pluginName + "-" + Math.round( Math.random() * 10000 ) ); } this.createToolbar(); // TODO this is used inside stack table init for some reason? what does it do? this._initCells(); this.$table.trigger( events.create, [ this ] ); }; Table.prototype.getConfig = function( pluginSpecificConfig ) { // shoestring extend doesn’t support arbitrary args var configs = $.extend( defaultConfig, pluginSpecificConfig || {} ); return $.extend( configs, typeof TablesawConfig !== "undefined" ? TablesawConfig : {} ); }; Table.prototype._getPrimaryHeaderCells = function() { return this.getConfig().getHeaderCells.call( this ); }; Table.prototype._findHeadersForCell = function( cell ) { var $headers = this._getPrimaryHeaderCells(); var results = []; for( var rowNumber = 1; rowNumber < this.headerMapping.length; rowNumber++ ) { for( var colNumber = 0; colNumber < this.headerMapping[ rowNumber ].length; colNumber++ ) { if( this.headerMapping[ rowNumber ][ colNumber ] === cell ) { results.push( $headers[ colNumber ] ); } } } return results; }; Table.prototype._initCells = function() { var $rows = this.$table.find( "tr" ); var columnLookup = []; $rows.each(function( rowNumber ) { columnLookup[ rowNumber ] = []; }); $rows.each(function( rowNumber ) { var coltally = 0; var $t = $( this ); var children = $t.children(); // var isInHeader = $t.closest( "thead" ).length; children.each(function() { var colspan = parseInt( this.getAttribute( "colspan" ), 10 ); var rowspan = parseInt( this.getAttribute( "rowspan" ), 10 ); // set in a previous rowspan while( columnLookup[ rowNumber ][ coltally ] ) { coltally++; } columnLookup[ rowNumber ][ coltally ] = this; // TODO both colspan and rowspan if( colspan ) { for( var k = 0; k < colspan - 1; k++ ){ coltally++; columnLookup[ rowNumber ][ coltally ] = this; } } if( rowspan ) { for( var j = 1; j < rowspan; j++ ){ columnLookup[ rowNumber + j ][ coltally ] = this; } } coltally++; }); }); for( var colNumber = 0; colNumber < columnLookup[ 0 ].length; colNumber++ ) { var headerCol = columnLookup[ 0 ][ colNumber ]; var rowNumber = 0; var rowCell; if( !headerCol.cells ) { headerCol.cells = []; } while( rowNumber < columnLookup.length ) { rowCell = columnLookup[ rowNumber ][ colNumber ]; if( headerCol !== rowCell ) { headerCol.cells.push( rowCell ); } rowNumber++; } } this.headerMapping = columnLookup; }; Table.prototype.refresh = function() { this._initCells(); this.$table.trigger( events.refresh, [ this ] ); }; Table.prototype.createToolbar = function() { // Insert the toolbar // TODO move this into a separate component var $toolbar = this.$table.prev().filter( '.' + classes.toolbar ); if( !$toolbar.length ) { $toolbar = $( '<div>' ) .addClass( classes.toolbar ) .insertBefore( this.$table ); } this.$toolbar = $toolbar; if( this.mode ) { this.$toolbar.addClass( 'tablesaw-mode-' + this.mode ); } }; Table.prototype.destroy = function() { // Don’t remove the toolbar. Some of the table features are not yet destroy-friendly. this.$table.prev().filter( '.' + classes.toolbar ).each(function() { this.className = this.className.replace( /\btablesaw-mode\-\w*\b/gi, '' ); }); var tableId = this.$table.attr( 'id' ); $( document ).off( "." + tableId ); $( window ).off( "." + tableId ); // other plugins this.$table.trigger( events.destroy, [ this ] ); this.$table.removeData( pluginName ); }; // Collection method. $.fn[ pluginName ] = function() { return this.each( function() { var $t = $( this ); if( $t.data( pluginName ) ){ return; } var table = new Table( this ); $t.data( pluginName, table ); }); }; var $doc = $( win.document ); $doc.on( "enhance.tablesaw", function( e ) { // Cut the mustard if( Tablesaw.mustard ) { $( e.target ).find( initSelector )[ pluginName ](); } }); // Avoid a resize during scroll: // Some Mobile devices trigger a resize during scroll (sometimes when // doing elastic stretch at the end of the document or from the // location bar hide) var isScrolling = false; var scrollTimeout; $doc.on( "scroll.tablesaw", function() { isScrolling = true; win.clearTimeout( scrollTimeout ); scrollTimeout = win.setTimeout(function() { isScrolling = false; }, 300 ); }); $doc.on( "resize.tablesaw", function() { if( !isScrolling ) { $doc.trigger( events.resize ); } }); }()); (function(){ var classes = { stackTable: 'tablesaw-stack', cellLabels: 'tablesaw-cell-label', cellContentLabels: 'tablesaw-cell-content' }; var data = { key: 'tablesaw-stack' }; var attrs = { labelless: 'data-tablesaw-no-labels', hideempty: 'data-tablesaw-hide-empty' }; var Stack = function( element, tablesaw ) { this.tablesaw = tablesaw; this.$table = $( element ); this.labelless = this.$table.is( '[' + attrs.labelless + ']' ); this.hideempty = this.$table.is( '[' + attrs.hideempty + ']' ); this.$table.data( data.key, this ); }; Stack.prototype.init = function() { this.$table.addClass( classes.stackTable ); if( this.labelless ) { return; } var self = this; this.$table.find( "th, td" ).filter(function() { return !$( this ).closest( "thead" ).length; }).filter(function() { return !$( this ).closest( "tr" ).is( "[" + attrs.labelless + "]" ) && ( !self.hideempty || !!$( this ).html() ); }).each(function() { var html = []; var $cell = $( this ); // headers $( self.tablesaw._findHeadersForCell( this ) ).each(function() { var $header = $( this.cloneNode( true ) ); // TODO decouple from sortable better // Changed from .text() in https://github.com/filamentgroup/tablesaw/commit/b9c12a8f893ec192830ec3ba2d75f062642f935b // to preserve structural html in headers, like <a> var $sortableButton = $header.find( ".tablesaw-sortable-btn" ); $header.find( ".tablesaw-sortable-arrow" ).remove(); html.push( $sortableButton.length ? $sortableButton.html() : $header.html() ); }); if( !$cell.find( "." + classes.cellContentLabels ).length ) { $cell.wrapInner( "<span class='" + classes.cellContentLabels + "'></span>" ); } // Update if already exists. var $label = $cell.find( "." + classes.cellLabels ); var newHtml = html.join( ", " ); if( !$label.length ) { $cell.prepend( "<b class='" + classes.cellLabels + "'>" + newHtml + "</b>" ); } else if( $label.html() !== newHtml ) { // only if changed $label.html( newHtml ); } }); }; Stack.prototype.destroy = function() { this.$table.removeClass( classes.stackTable ); this.$table.find( '.' + classes.cellLabels ).remove(); this.$table.find( '.' + classes.cellContentLabels ).each(function() { $( this ).replaceWith( this.childNodes ); }); }; // on tablecreate, init $( document ).on( Tablesaw.events.create, function( e, tablesaw ){ if( tablesaw.mode === 'stack' ){ var table = new Stack( tablesaw.table, tablesaw ); table.init(); } }).on( Tablesaw.events.refresh, function( e, tablesaw ){ if( tablesaw.mode === 'stack' ){ $( tablesaw.table ).data( data.key ).init(); } }).on( Tablesaw.events.destroy, function( e, tablesaw ){ if( tablesaw.mode === 'stack' ){ $( tablesaw.table ).data( data.key ).destroy(); } }); }()); })); assets/js/tablesaw/stackonly/tablesaw.stackonly.scss 0000644 00000006735 14736103451 0017017 0 ustar 00 /*! Tablesaw - v3.0.1 - 2017-02-16 * https://github.com/filamentgroup/tablesaw * Copyright (c) 2017 Filament Group; Licensed MIT */ /*! Tablesaw - v3.0.1 - 2017-02-16 * https://github.com/filamentgroup/tablesaw * Copyright (c) 2017 Filament Group; Licensed MIT */ .tablesaw { width: 100%; max-width: 100%; empty-cells: show; border-collapse: collapse; border: 0; padding: 0; } .tablesaw * { box-sizing: border-box; } .tablesaw th, .tablesaw td { padding: .5em .7em; } .tablesaw thead tr:first-child th { padding-top: .9em; padding-bottom: .7em; } .tablesaw-stack td .tablesaw-cell-label, .tablesaw-stack th .tablesaw-cell-label { display: none; } /* Mobile first styles: Begin with the stacked presentation at narrow widths */ /* Support note IE9+: @media only all */ @media only all { /* Show the table cells as a block level element */ .tablesaw-stack { clear: both; } .tablesaw-stack td, .tablesaw-stack th { text-align: left; display: block; } .tablesaw-stack tr { clear: both; display: table-row; } /* Make the label elements a percentage width */ .tablesaw-stack td .tablesaw-cell-label, .tablesaw-stack th .tablesaw-cell-label { display: block; padding: 0 .6em 0 0; width: 30%; display: inline-block; } /* For grouped headers, have a different style to visually separate the levels by classing the first label in each col group */ .tablesaw-stack th .tablesaw-cell-label-top, .tablesaw-stack td .tablesaw-cell-label-top { display: block; padding: .4em 0; margin: .4em 0; } .tablesaw-cell-label { display: block; } /* Avoid double strokes when stacked */ .tablesaw-stack tbody th.group { margin-top: -1px; } /* Avoid double strokes when stacked */ .tablesaw-stack th.group b.tablesaw-cell-label { display: none !important; } } @mixin tablesaw-stack-max { /* Table rows have a gray bottom stroke by default */ .tablesaw-stack tbody tr { display: block; width: 100%; border-bottom: 1px solid #dfdfdf; } .tablesaw-stack thead td, .tablesaw-stack thead th { display: none; } .tablesaw-stack tbody td, .tablesaw-stack tbody th { display: block; float: left; clear: left; width: 100%; } .tablesaw-cell-label { vertical-align: top; } .tablesaw-cell-content { max-width: 67%; display: inline-block; } .tablesaw-stack td:empty, .tablesaw-stack th:empty { display: none; } } @mixin tablesaw-stack-min { .tablesaw-stack tr { display: table-row; } /* Show the table header rows */ .tablesaw-stack td, .tablesaw-stack th, .tablesaw-stack thead td, .tablesaw-stack thead th { display: table-cell; margin: 0; } /* Hide the labels in each cell */ .tablesaw-stack td .tablesaw-cell-label, .tablesaw-stack th .tablesaw-cell-label { display: none !important; } } $use-respond-mixins: false !default; @mixin tablesaw-stack( $breakpoint: 40em ) { @if $use-respond-mixins { @include respond-max($breakpoint - .0625) { @include tablesaw-stack-max; } @include respond-min($breakpoint) { @include tablesaw-stack-min; } } @else { @media (max-width: ($breakpoint - .0625)) { @include tablesaw-stack-max; } @media (min-width: $breakpoint) { @include tablesaw-stack-min; } } } assets/js/tablesaw/stackonly/tablesaw.stackonly.jquery.js 0000644 00000024335 14736103451 0017772 0 ustar 00 /*! Tablesaw - v3.0.1 - 2017-02-16 * https://github.com/filamentgroup/tablesaw * Copyright (c) 2017 Filament Group; Licensed MIT */ // UMD module definition // From: https://github.com/umdjs/umd/blob/master/templates/jqueryPlugin.js (function (factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module. define(['jquery'], factory); } else if (typeof module === 'object' && module.exports) { // Node/CommonJS module.exports = function( root, jQuery ) { if ( jQuery === undefined ) { // require('jQuery') returns a factory that requires window to // build a jQuery instance, we normalize how we use modules // that require this pattern but the window provided is a noop // if it's defined (how jquery works) if ( typeof window !== 'undefined' ) { jQuery = require('jquery'); } else { jQuery = require('jquery')(root); } } factory(jQuery); return jQuery; }; } else { // Browser globals factory(jQuery); } }(function ($) { "use strict"; var win = typeof window !== "undefined" ? window : this; var Tablesaw = { i18n: { modes: [ 'Stack', 'Swipe', 'Toggle' ], columns: 'Col<span class=\"a11y-sm\">umn</span>s', columnBtnText: 'Columns', columnsDialogError: 'No eligible columns.', sort: 'Sort' }, // cut the mustard mustard: ( 'head' in document ) && // IE9+, Firefox 4+, Safari 5.1+, Mobile Safari 4.1+, Opera 11.5+, Android 2.3+ ( !window.blackberry || window.WebKitPoint ) && // only WebKit Blackberry (OS 6+) !window.operamini }; if( Tablesaw.mustard ) { $( document.documentElement ).addClass( 'tablesaw-enhanced' ); } (function() { var pluginName = "tablesaw"; var classes = { toolbar: "tablesaw-bar" }; var events = { create: "tablesawcreate", destroy: "tablesawdestroy", refresh: "tablesawrefresh", resize: "tablesawresize" }; var defaultMode = "stack"; var initSelector = "table[data-tablesaw-mode],table[data-tablesaw-sortable]"; var defaultConfig = { getHeaderCells: function() { return this.$table.find( "thead" ).children().filter( "tr" ).eq( 0 ).find( "th" ); } }; Tablesaw.events = events; var Table = function( element ) { if( !element ) { throw new Error( "Tablesaw requires an element." ); } this.table = element; this.$table = $( element ); this.mode = this.$table.attr( "data-tablesaw-mode" ) || defaultMode; this.init(); }; Table.prototype.init = function() { // assign an id if there is none if ( !this.$table.attr( "id" ) ) { this.$table.attr( "id", pluginName + "-" + Math.round( Math.random() * 10000 ) ); } this.createToolbar(); // TODO this is used inside stack table init for some reason? what does it do? this._initCells(); this.$table.trigger( events.create, [ this ] ); }; Table.prototype.getConfig = function( pluginSpecificConfig ) { // shoestring extend doesn’t support arbitrary args var configs = $.extend( defaultConfig, pluginSpecificConfig || {} ); return $.extend( configs, typeof TablesawConfig !== "undefined" ? TablesawConfig : {} ); }; Table.prototype._getPrimaryHeaderCells = function() { return this.getConfig().getHeaderCells.call( this ); }; Table.prototype._findHeadersForCell = function( cell ) { var $headers = this._getPrimaryHeaderCells(); var results = []; for( var rowNumber = 1; rowNumber < this.headerMapping.length; rowNumber++ ) { for( var colNumber = 0; colNumber < this.headerMapping[ rowNumber ].length; colNumber++ ) { if( this.headerMapping[ rowNumber ][ colNumber ] === cell ) { results.push( $headers[ colNumber ] ); } } } return results; }; Table.prototype._initCells = function() { var $rows = this.$table.find( "tr" ); var columnLookup = []; $rows.each(function( rowNumber ) { columnLookup[ rowNumber ] = []; }); $rows.each(function( rowNumber ) { var coltally = 0; var $t = $( this ); var children = $t.children(); // var isInHeader = $t.closest( "thead" ).length; children.each(function() { var colspan = parseInt( this.getAttribute( "colspan" ), 10 ); var rowspan = parseInt( this.getAttribute( "rowspan" ), 10 ); // set in a previous rowspan while( columnLookup[ rowNumber ][ coltally ] ) { coltally++; } columnLookup[ rowNumber ][ coltally ] = this; // TODO both colspan and rowspan if( colspan ) { for( var k = 0; k < colspan - 1; k++ ){ coltally++; columnLookup[ rowNumber ][ coltally ] = this; } } if( rowspan ) { for( var j = 1; j < rowspan; j++ ){ columnLookup[ rowNumber + j ][ coltally ] = this; } } coltally++; }); }); for( var colNumber = 0; colNumber < columnLookup[ 0 ].length; colNumber++ ) { var headerCol = columnLookup[ 0 ][ colNumber ]; var rowNumber = 0; var rowCell; if( !headerCol.cells ) { headerCol.cells = []; } while( rowNumber < columnLookup.length ) { rowCell = columnLookup[ rowNumber ][ colNumber ]; if( headerCol !== rowCell ) { headerCol.cells.push( rowCell ); } rowNumber++; } } this.headerMapping = columnLookup; }; Table.prototype.refresh = function() { this._initCells(); this.$table.trigger( events.refresh, [ this ] ); }; Table.prototype.createToolbar = function() { // Insert the toolbar // TODO move this into a separate component var $toolbar = this.$table.prev().filter( '.' + classes.toolbar ); if( !$toolbar.length ) { $toolbar = $( '<div>' ) .addClass( classes.toolbar ) .insertBefore( this.$table ); } this.$toolbar = $toolbar; if( this.mode ) { this.$toolbar.addClass( 'tablesaw-mode-' + this.mode ); } }; Table.prototype.destroy = function() { // Don’t remove the toolbar. Some of the table features are not yet destroy-friendly. this.$table.prev().filter( '.' + classes.toolbar ).each(function() { this.className = this.className.replace( /\btablesaw-mode\-\w*\b/gi, '' ); }); var tableId = this.$table.attr( 'id' ); $( document ).off( "." + tableId ); $( window ).off( "." + tableId ); // other plugins this.$table.trigger( events.destroy, [ this ] ); this.$table.removeData( pluginName ); }; // Collection method. $.fn[ pluginName ] = function() { return this.each( function() { var $t = $( this ); if( $t.data( pluginName ) ){ return; } var table = new Table( this ); $t.data( pluginName, table ); }); }; var $doc = $( win.document ); $doc.on( "enhance.tablesaw", function( e ) { // Cut the mustard if( Tablesaw.mustard ) { $( e.target ).find( initSelector )[ pluginName ](); } }); // Avoid a resize during scroll: // Some Mobile devices trigger a resize during scroll (sometimes when // doing elastic stretch at the end of the document or from the // location bar hide) var isScrolling = false; var scrollTimeout; $doc.on( "scroll.tablesaw", function() { isScrolling = true; win.clearTimeout( scrollTimeout ); scrollTimeout = win.setTimeout(function() { isScrolling = false; }, 300 ); }); $doc.on( "resize.tablesaw", function() { if( !isScrolling ) { $doc.trigger( events.resize ); } }); }()); (function(){ var classes = { stackTable: 'tablesaw-stack', cellLabels: 'tablesaw-cell-label', cellContentLabels: 'tablesaw-cell-content' }; var data = { key: 'tablesaw-stack' }; var attrs = { labelless: 'data-tablesaw-no-labels', hideempty: 'data-tablesaw-hide-empty' }; var Stack = function( element, tablesaw ) { this.tablesaw = tablesaw; this.$table = $( element ); this.labelless = this.$table.is( '[' + attrs.labelless + ']' ); this.hideempty = this.$table.is( '[' + attrs.hideempty + ']' ); this.$table.data( data.key, this ); }; Stack.prototype.init = function() { this.$table.addClass( classes.stackTable ); if( this.labelless ) { return; } var self = this; this.$table.find( "th, td" ).filter(function() { return !$( this ).closest( "thead" ).length; }).filter(function() { return !$( this ).closest( "tr" ).is( "[" + attrs.labelless + "]" ) && ( !self.hideempty || !!$( this ).html() ); }).each(function() { var html = []; var $cell = $( this ); // headers $( self.tablesaw._findHeadersForCell( this ) ).each(function() { var $header = $( this.cloneNode( true ) ); // TODO decouple from sortable better // Changed from .text() in https://github.com/filamentgroup/tablesaw/commit/b9c12a8f893ec192830ec3ba2d75f062642f935b // to preserve structural html in headers, like <a> var $sortableButton = $header.find( ".tablesaw-sortable-btn" ); $header.find( ".tablesaw-sortable-arrow" ).remove(); html.push( $sortableButton.length ? $sortableButton.html() : $header.html() ); }); if( !$cell.find( "." + classes.cellContentLabels ).length ) { $cell.wrapInner( "<span class='" + classes.cellContentLabels + "'></span>" ); } // Update if already exists. var $label = $cell.find( "." + classes.cellLabels ); var newHtml = html.join( ", " ); if( !$label.length ) { $cell.prepend( "<b class='" + classes.cellLabels + "'>" + newHtml + "</b>" ); } else if( $label.html() !== newHtml ) { // only if changed $label.html( newHtml ); } }); }; Stack.prototype.destroy = function() { this.$table.removeClass( classes.stackTable ); this.$table.find( '.' + classes.cellLabels ).remove(); this.$table.find( '.' + classes.cellContentLabels ).each(function() { $( this ).replaceWith( this.childNodes ); }); }; // on tablecreate, init $( document ).on( Tablesaw.events.create, function( e, tablesaw ){ if( tablesaw.mode === 'stack' ){ var table = new Stack( tablesaw.table, tablesaw ); table.init(); } }).on( Tablesaw.events.refresh, function( e, tablesaw ){ if( tablesaw.mode === 'stack' ){ $( tablesaw.table ).data( data.key ).init(); } }).on( Tablesaw.events.destroy, function( e, tablesaw ){ if( tablesaw.mode === 'stack' ){ $( tablesaw.table ).data( data.key ).destroy(); } }); }()); })); assets/js/tablesaw/stackonly/tablesaw.stackonly.css 0000644 00000005727 14736103451 0016634 0 ustar 00 /*! Tablesaw - v3.0.1 - 2017-02-16 * https://github.com/filamentgroup/tablesaw * Copyright (c) 2017 Filament Group; Licensed MIT */ .tablesaw { width: 100%; max-width: 100%; empty-cells: show; border-collapse: collapse; border: 0; padding: 0; } .tablesaw * { box-sizing: border-box; } .tablesaw th, .tablesaw td { padding: .5em .7em; } .tablesaw thead tr:first-child th { padding-top: .9em; padding-bottom: .7em; } .tablesaw-stack td .tablesaw-cell-label, .tablesaw-stack th .tablesaw-cell-label { display: none; } /* Mobile first styles: Begin with the stacked presentation at narrow widths */ /* Support note IE9+: @media only all */ @media only all { /* Show the table cells as a block level element */ .tablesaw-stack { clear: both; } .tablesaw-stack td, .tablesaw-stack th { text-align: left; display: block; } .tablesaw-stack tr { clear: both; display: table-row; } /* Make the label elements a percentage width */ .tablesaw-stack td .tablesaw-cell-label, .tablesaw-stack th .tablesaw-cell-label { display: block; padding: 0 .6em 0 0; width: 30%; display: inline-block; } /* For grouped headers, have a different style to visually separate the levels by classing the first label in each col group */ .tablesaw-stack th .tablesaw-cell-label-top, .tablesaw-stack td .tablesaw-cell-label-top { display: block; padding: .4em 0; margin: .4em 0; } .tablesaw-cell-label { display: block; } /* Avoid double strokes when stacked */ .tablesaw-stack tbody th.group { margin-top: -1px; } /* Avoid double strokes when stacked */ .tablesaw-stack th.group b.tablesaw-cell-label { display: none !important; } } @media (max-width: 39.9375em) { /* Table rows have a gray bottom stroke by default */ .tablesaw-stack tbody tr { display: block; width: 100%; border-bottom: 1px solid #dfdfdf; } .tablesaw-stack thead td, .tablesaw-stack thead th { display: none; } .tablesaw-stack tbody td, .tablesaw-stack tbody th { display: block; float: left; clear: left; width: 100%; } .tablesaw-cell-label { vertical-align: top; } .tablesaw-cell-content { max-width: 67%; display: inline-block; } .tablesaw-stack td:empty, .tablesaw-stack th:empty { display: none; } } /* Media query to show as a standard table at 560px (35em x 16px) or wider */ @media (min-width: 40em) { .tablesaw-stack tr { display: table-row; } /* Show the table header rows */ .tablesaw-stack td, .tablesaw-stack th, .tablesaw-stack thead td, .tablesaw-stack thead th { display: table-cell; margin: 0; } /* Hide the labels in each cell */ .tablesaw-stack td .tablesaw-cell-label, .tablesaw-stack th .tablesaw-cell-label { display: none !important; } } assets/js/simplemde.css 0000644 00000037103 14736103451 0011163 0 ustar 00 /** * simplemde v1.11.2 * Copyright Next Step Webs, Inc. * @link https://github.com/NextStepWebs/simplemde-markdown-editor * @license MIT */ /* BASICS */ .CodeMirror { /* Set height, width, borders, and global font properties here */ font-family: monospace; height: 300px; color: black; } /* PADDING */ .CodeMirror-lines { padding: 4px 0; /* Vertical padding around content */ } .CodeMirror pre { padding: 0 4px; /* Horizontal padding of content */ } .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { background-color: white; /* The little square between H and V scrollbars */ } /* GUTTER */ .CodeMirror-gutters { border-right: 1px solid #ddd; background-color: #f7f7f7; white-space: nowrap; } .CodeMirror-linenumbers {} .CodeMirror-linenumber { padding: 0 3px 0 5px; min-width: 20px; text-align: right; color: #999; white-space: nowrap; } .CodeMirror-guttermarker { color: black; } .CodeMirror-guttermarker-subtle { color: #999; } /* CURSOR */ .CodeMirror-cursor { border-left: 1px solid black; border-right: none; width: 0; } /* Shown when moving in bi-directional text */ .CodeMirror div.CodeMirror-secondarycursor { border-left: 1px solid silver; } .cm-fat-cursor .CodeMirror-cursor { width: auto; border: 0 !important; background: #7e7; } .cm-fat-cursor div.CodeMirror-cursors { z-index: 1; } .cm-animate-fat-cursor { width: auto; border: 0; -webkit-animation: blink 1.06s steps(1) infinite; -moz-animation: blink 1.06s steps(1) infinite; animation: blink 1.06s steps(1) infinite; background-color: #7e7; } @-moz-keyframes blink { 0% {} 50% { background-color: transparent; } 100% {} } @-webkit-keyframes blink { 0% {} 50% { background-color: transparent; } 100% {} } @keyframes blink { 0% {} 50% { background-color: transparent; } 100% {} } /* Can style cursor different in overwrite (non-insert) mode */ .CodeMirror-overwrite .CodeMirror-cursor {} .cm-tab { display: inline-block; text-decoration: inherit; } .CodeMirror-ruler { border-left: 1px solid #ccc; position: absolute; } /* DEFAULT THEME */ .cm-s-default .cm-header {color: blue;} .cm-s-default .cm-quote {color: #090;} .cm-negative {color: #d44;} .cm-positive {color: #292;} .cm-header, .cm-strong {font-weight: bold;} .cm-em {font-style: italic;} .cm-link {text-decoration: underline;} .cm-strikethrough {text-decoration: line-through;} .cm-s-default .cm-keyword {color: #708;} .cm-s-default .cm-atom {color: #219;} .cm-s-default .cm-number {color: #164;} .cm-s-default .cm-def {color: #00f;} .cm-s-default .cm-variable, .cm-s-default .cm-punctuation, .cm-s-default .cm-property, .cm-s-default .cm-operator {} .cm-s-default .cm-variable-2 {color: #05a;} .cm-s-default .cm-variable-3 {color: #085;} .cm-s-default .cm-comment {color: #a50;} .cm-s-default .cm-string {color: #a11;} .cm-s-default .cm-string-2 {color: #f50;} .cm-s-default .cm-meta {color: #555;} .cm-s-default .cm-qualifier {color: #555;} .cm-s-default .cm-builtin {color: #30a;} .cm-s-default .cm-bracket {color: #997;} .cm-s-default .cm-tag {color: #170;} .cm-s-default .cm-attribute {color: #00c;} .cm-s-default .cm-hr {color: #999;} .cm-s-default .cm-link {color: #00c;} .cm-s-default .cm-error {color: #f00;} .cm-invalidchar {color: #f00;} .CodeMirror-composing { border-bottom: 2px solid; } /* Default styles for common addons */ div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } .CodeMirror-activeline-background {background: #e8f2ff;} /* STOP */ /* The rest of this file contains styles related to the mechanics of the editor. You probably shouldn't touch them. */ .CodeMirror { position: relative; overflow: hidden; background: white; } .CodeMirror-scroll { overflow: scroll !important; /* Things will break if this is overridden */ /* 30px is the magic margin used to hide the element's real scrollbars */ /* See overflow: hidden in .CodeMirror */ margin-bottom: -30px; margin-right: -30px; padding-bottom: 30px; height: 100%; outline: none; /* Prevent dragging from highlighting the element */ position: relative; } .CodeMirror-sizer { position: relative; border-right: 30px solid transparent; } /* The fake, visible scrollbars. Used to force redraw during scrolling before actual scrolling happens, thus preventing shaking and flickering artifacts. */ .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { position: absolute; z-index: 6; display: none; } .CodeMirror-vscrollbar { right: 0; top: 0; overflow-x: hidden; overflow-y: scroll; } .CodeMirror-hscrollbar { bottom: 0; left: 0; overflow-y: hidden; overflow-x: scroll; } .CodeMirror-scrollbar-filler { right: 0; bottom: 0; } .CodeMirror-gutter-filler { left: 0; bottom: 0; } .CodeMirror-gutters { position: absolute; left: 0; top: 0; min-height: 100%; z-index: 3; } .CodeMirror-gutter { white-space: normal; height: 100%; display: inline-block; vertical-align: top; margin-bottom: -30px; /* Hack to make IE7 behave */ *zoom:1; *display:inline; } .CodeMirror-gutter-wrapper { position: absolute; z-index: 4; background: none !important; border: none !important; } .CodeMirror-gutter-background { position: absolute; top: 0; bottom: 0; z-index: 4; } .CodeMirror-gutter-elt { position: absolute; cursor: default; z-index: 4; } .CodeMirror-gutter-wrapper { -webkit-user-select: none; -moz-user-select: none; user-select: none; } .CodeMirror-lines { cursor: text; min-height: 1px; /* prevents collapsing before first draw */ } .CodeMirror pre { /* Reset some styles that the rest of the page might have set */ -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; border-width: 0; background: transparent; font-family: inherit; font-size: inherit; margin: 0; white-space: pre; word-wrap: normal; line-height: inherit; color: inherit; z-index: 2; position: relative; overflow: visible; -webkit-tap-highlight-color: transparent; -webkit-font-variant-ligatures: none; font-variant-ligatures: none; } .CodeMirror-wrap pre { word-wrap: break-word; white-space: pre-wrap; word-break: normal; } .CodeMirror-linebackground { position: absolute; left: 0; right: 0; top: 0; bottom: 0; z-index: 0; } .CodeMirror-linewidget { position: relative; z-index: 2; overflow: auto; } .CodeMirror-widget {} .CodeMirror-code { outline: none; } /* Force content-box sizing for the elements where we expect it */ .CodeMirror-scroll, .CodeMirror-sizer, .CodeMirror-gutter, .CodeMirror-gutters, .CodeMirror-linenumber { -moz-box-sizing: content-box; box-sizing: content-box; } .CodeMirror-measure { position: absolute; width: 100%; height: 0; overflow: hidden; visibility: hidden; } .CodeMirror-cursor { position: absolute; } .CodeMirror-measure pre { position: static; } div.CodeMirror-cursors { visibility: hidden; position: relative; z-index: 3; } div.CodeMirror-dragcursors { visibility: visible; } .CodeMirror-focused div.CodeMirror-cursors { visibility: visible; } .CodeMirror-selected { background: #d9d9d9; } .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } .CodeMirror-crosshair { cursor: crosshair; } .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; } .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; } .cm-searching { background: #ffa; background: rgba(255, 255, 0, .4); } /* IE7 hack to prevent it from returning funny offsetTops on the spans */ .CodeMirror span { *vertical-align: text-bottom; } /* Used to force a border model for a node */ .cm-force-border { padding-right: .1px; } @media print { /* Hide the cursor when printing */ .CodeMirror div.CodeMirror-cursors { visibility: hidden; } } /* See issue #2901 */ .cm-tab-wrap-hack:after { content: ''; } /* Help users use markselection to safely style text background */ span.CodeMirror-selectedtext { background: none; } .CodeMirror { height: auto; min-height: 300px; border: 1px solid #ddd; border-bottom-left-radius: 4px; border-bottom-right-radius: 4px; padding: 10px; font: inherit; z-index: 1; } .CodeMirror-scroll { min-height: 300px } .CodeMirror-fullscreen { background: #fff; position: fixed !important; top: 50px; left: 0; right: 0; bottom: 0; height: auto; z-index: 9; } .CodeMirror-sided { width: 50% !important; } .editor-toolbar { position: relative; opacity: .6; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; -o-user-select: none; user-select: none; padding: 0 10px; border-top: 1px solid #bbb; border-left: 1px solid #bbb; border-right: 1px solid #bbb; border-top-left-radius: 4px; border-top-right-radius: 4px; } .editor-toolbar:after, .editor-toolbar:before { display: block; content: ' '; height: 1px; } .editor-toolbar:before { margin-bottom: 8px } .editor-toolbar:after { margin-top: 8px } .editor-toolbar:hover, .editor-wrapper input.title:focus, .editor-wrapper input.title:hover { opacity: .8 } .editor-toolbar.fullscreen { width: 100%; height: 50px; overflow-x: auto; overflow-y: hidden; white-space: nowrap; padding-top: 10px; padding-bottom: 10px; box-sizing: border-box; background: #fff; border: 0; position: fixed; top: 0; left: 0; opacity: 1; z-index: 9; } .editor-toolbar.fullscreen::before { width: 20px; height: 50px; background: -moz-linear-gradient(left, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0) 100%); background: -webkit-gradient(linear, left top, right top, color-stop(0%, rgba(255, 255, 255, 1)), color-stop(100%, rgba(255, 255, 255, 0))); background: -webkit-linear-gradient(left, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0) 100%); background: -o-linear-gradient(left, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0) 100%); background: -ms-linear-gradient(left, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0) 100%); background: linear-gradient(to right, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0) 100%); position: fixed; top: 0; left: 0; margin: 0; padding: 0; } .editor-toolbar.fullscreen::after { width: 20px; height: 50px; background: -moz-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 1) 100%); background: -webkit-gradient(linear, left top, right top, color-stop(0%, rgba(255, 255, 255, 0)), color-stop(100%, rgba(255, 255, 255, 1))); background: -webkit-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 1) 100%); background: -o-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 1) 100%); background: -ms-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 1) 100%); background: linear-gradient(to right, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 1) 100%); position: fixed; top: 0; right: 0; margin: 0; padding: 0; } .editor-toolbar a { display: inline-block; text-align: center; text-decoration: none!important; color: #2c3e50!important; width: 30px; height: 30px; margin: 0; border: 1px solid transparent; border-radius: 3px; cursor: pointer; } .editor-toolbar a.active, .editor-toolbar a:hover { background: #fcfcfc; border-color: #95a5a6; } .editor-toolbar a:before { line-height: 30px } .editor-toolbar i.separator { display: inline-block; width: 0; border-left: 1px solid #d9d9d9; border-right: 1px solid #fff; color: transparent; text-indent: -10px; margin: 0 6px; } .editor-toolbar a.fa-header-x:after { font-family: Arial, "Helvetica Neue", Helvetica, sans-serif; font-size: 65%; vertical-align: text-bottom; position: relative; top: 2px; } .editor-toolbar a.fa-header-1:after { content: "1"; } .editor-toolbar a.fa-header-2:after { content: "2"; } .editor-toolbar a.fa-header-3:after { content: "3"; } .editor-toolbar a.fa-header-bigger:after { content: "▲"; } .editor-toolbar a.fa-header-smaller:after { content: "▼"; } .editor-toolbar.disabled-for-preview a:not(.no-disable) { pointer-events: none; background: #fff; border-color: transparent; text-shadow: inherit; } @media only screen and (max-width: 700px) { .editor-toolbar a.no-mobile { display: none; } } .editor-statusbar { padding: 8px 10px; font-size: 12px; color: #959694; text-align: right; } .editor-statusbar span { display: inline-block; min-width: 4em; margin-left: 1em; } .editor-statusbar .lines:before { content: 'lines: ' } .editor-statusbar .words:before { content: 'words: ' } .editor-statusbar .characters:before { content: 'characters: ' } .editor-preview { padding: 10px; position: absolute; width: 100%; height: 100%; top: 0; left: 0; background: #fafafa; z-index: 7; overflow: auto; display: none; box-sizing: border-box; } .editor-preview-side { padding: 10px; position: fixed; bottom: 0; width: 50%; top: 50px; right: 0; background: #fafafa; z-index: 9; overflow: auto; display: none; box-sizing: border-box; border: 1px solid #ddd; } .editor-preview-active-side { display: block } .editor-preview-active { display: block } .editor-preview>p, .editor-preview-side>p { margin-top: 0 } .editor-preview pre, .editor-preview-side pre { background: #eee; margin-bottom: 10px; } .editor-preview table td, .editor-preview table th, .editor-preview-side table td, .editor-preview-side table th { border: 1px solid #ddd; padding: 5px; } .CodeMirror .CodeMirror-code .cm-tag { color: #63a35c; } .CodeMirror .CodeMirror-code .cm-attribute { color: #795da3; } .CodeMirror .CodeMirror-code .cm-string { color: #183691; } .CodeMirror .CodeMirror-selected { background: #d9d9d9; } .CodeMirror .CodeMirror-code .cm-header-1 { font-size: 200%; line-height: 200%; } .CodeMirror .CodeMirror-code .cm-header-2 { font-size: 160%; line-height: 160%; } .CodeMirror .CodeMirror-code .cm-header-3 { font-size: 125%; line-height: 125%; } .CodeMirror .CodeMirror-code .cm-header-4 { font-size: 110%; line-height: 110%; } .CodeMirror .CodeMirror-code .cm-comment { background: rgba(0, 0, 0, .05); border-radius: 2px; } .CodeMirror .CodeMirror-code .cm-link { color: #7f8c8d; } .CodeMirror .CodeMirror-code .cm-url { color: #aab2b3; } .CodeMirror .CodeMirror-code .cm-strikethrough { text-decoration: line-through; } .CodeMirror .CodeMirror-placeholder { opacity: .5; } .CodeMirror .cm-spell-error:not(.cm-url):not(.cm-comment):not(.cm-tag):not(.cm-word) { background: rgba(255, 0, 0, .15); } assets/js/scripts.js 0000644 00000360021 14736103451 0010515 0 ustar 00 var t0 = performance.now(); var current_request = null; var is_mobile = false; //initiate as false // device detection if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|ipad|iris|kindle|Android|Silk|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(navigator.userAgent) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(navigator.userAgent.substr(0, 4))) { is_mobile = true; } function base64_encode(stringToEncode) { var encodeUTF8string = function (str) { // first we use encodeURIComponent to get percent-encoded UTF-8, // then we convert the percent encodings into raw bytes which // can be fed into the base64 encoding algorithm. return encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function toSolidBytes(match, p1) { return String.fromCharCode('0x' + p1) }) } if (typeof window !== 'undefined') { if (typeof window.btoa !== 'undefined') { return window.btoa(encodeUTF8string(stringToEncode)) } } else { return new Buffer(stringToEncode).toString('base64') } var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=' var o1 var o2 var o3 var h1 var h2 var h3 var h4 var bits var i = 0 var ac = 0 var enc = '' var tmpArr = [] if (!stringToEncode) { return stringToEncode } stringToEncode = encodeUTF8string(stringToEncode) do { // pack three octets into four hexets o1 = stringToEncode.charCodeAt(i++) o2 = stringToEncode.charCodeAt(i++) o3 = stringToEncode.charCodeAt(i++) bits = o1 << 16 | o2 << 8 | o3 h1 = bits >> 18 & 0x3f h2 = bits >> 12 & 0x3f h3 = bits >> 6 & 0x3f h4 = bits & 0x3f // use hexets to index into b64, and append result to encoded string tmpArr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4) } while (i < stringToEncode.length) enc = tmpArr.join('') var r = stringToEncode.length % 3 return ( r ? enc.slice(0, r - 3) : enc ) + '==='.slice(r || 3) } function urlencode(str) { str = ( str + '' ) // Tilde should be allowed unescaped in future versions of PHP (as reflected below), // but if you want to reflect current // PHP behavior, you would need to add ".replace(/~/g, '%7E');" to the following. return encodeURIComponent(str) .replace(/!/g, '%21') .replace(/'/g, '%27') .replace(/\(/g, '%28') .replace(/\)/g, '%29') .replace(/\*/g, '%2A') .replace(/%20/g, '+') } function isset() { var a = arguments var l = a.length var i = 0 var undef if (l === 0) { throw new Error('Empty isset') } while (i !== l) { if (a[i] === undef || a[i] === null) { return false } i++ } return true } function print_r(array, returnVal) { var echo = require('../strings/echo') var output = '' var padChar = ' ' var padVal = 4 var _repeatChar = function (len, padChar) { var str = '' for (var i = 0; i < len; i++) { str += padChar } return str } var _formatArray = function (obj, curDepth, padVal, padChar) { if (curDepth > 0) { curDepth++ } var basePad = _repeatChar(padVal * curDepth, padChar) var thickPad = _repeatChar(padVal * ( curDepth + 1 ), padChar) var str = '' if (typeof obj === 'object' && obj !== null && obj.constructor) { str += 'Array\n' + basePad + '(\n' for (var key in obj) { if (Object.prototype.toString.call(obj[key]) === '[object Array]') { str += thickPad str += '[' str += key str += '] => ' str += _formatArray(obj[key], curDepth + 1, padVal, padChar) } else { str += thickPad str += '[' str += key str += '] => ' str += obj[key] str += '\n' } } str += basePad + ')\n' } else if (obj === null || obj === undefined) { str = '' } else { // for our "resource" class str = obj.toString() } return str } output = _formatArray(array, 0, padVal, padChar) if (returnVal !== true) { echo(output) return true } return output } function SubmitUpdateSecurityQuestions(event) { event.preventDefault(); jQuery("#success_message, #success_error").hide(); jQuery.post(wcap_ajaxurl, jQuery(this).serialize(), function (data) { if (data == "OK") { jQuery("#success_message").html("Success!").show(); } else { jQuery("#success_error").html(data).show(); } }) } //byfarooq function SubmitUpdateUpdateCreditCard(event) { event.preventDefault(); var k = jQuery("#update_credit_card_form").serialize(); var view_container = jQuery('.wcap_view_container'); var view_content = view_container.find('.wcap_view_content'); var view_response = view_container.find('.wcap_view_response'); view_response.html(wcap_spinner_icon).show(); jQuery.post(wcap_ajaxurl, k, function (data) { var res = JSON.parse(data); console.log(res['api_response']); view_response.show().html(res.message); if (res.status === "OK") { view_content.hide(); } }); } function count(mixedVar, mode) { // discuss at: http://locutus.io/php/count/ // original by: Kevin van Zonneveld (http://kvz.io) // input by: Waldo Malqui Silva (http://waldo.malqui.info) // input by: merabi // bugfixed by: Soren Hansen // bugfixed by: Olivier Louvignes (http://mg-crea.com/) // improved by: Brett Zamir (http://brett-zamir.me) // example 1: count([[0,0],[0,-4]], 'COUNT_RECURSIVE') // returns 1: 6 // example 2: count({'one' : [1,2,3,4,5]}, 'COUNT_RECURSIVE') // returns 2: 6 var key; var cnt = 0; if (mixedVar === null || typeof mixedVar === 'undefined') { return 0 } else if (mixedVar.constructor !== Array && mixedVar.constructor !== Object) { return 1 } if (mode === 'COUNT_RECURSIVE') { mode = 1 } if (mode !== 1) { mode = 0 } for (key in mixedVar) { if (mixedVar.hasOwnProperty(key)) { cnt++ if (mode === 1 && mixedVar[key] && ( mixedVar[key].constructor === Array || mixedVar[key].constructor === Object )) { cnt += count(mixedVar[key], 1) } } } return cnt } function rtrim(str, charlist) { // discuss at: http://locutus.io/php/rtrim/ // original by: Kevin van Zonneveld (http://kvz.io) // input by: Erkekjetter // input by: rem // improved by: Kevin van Zonneveld (http://kvz.io) // bugfixed by: Onno Marsman (https://twitter.com/onnomarsman) // bugfixed by: Brett Zamir (http://brett-zamir.me) // example 1: rtrim(' Kevin van Zonneveld ') // returns 1: ' Kevin van Zonneveld' charlist = !charlist ? ' \\s\u00A0' : ( charlist + '' ) .replace(/([[\]().?/*{}+$^:])/g, '\\$1') var re = new RegExp('[' + charlist + ']+$', 'g') return ( str + '' ).replace(re, '') } function trim(str, charlist) { var whitespace = [ ' ', '\n', '\r', '\t', '\f', '\x0b', '\xa0', '\u2000', '\u2001', '\u2002', '\u2003', '\u2004', '\u2005', '\u2006', '\u2007', '\u2008', '\u2009', '\u200a', '\u200b', '\u2028', '\u2029', '\u3000' ].join(''); var l = 0; var i = 0; str += ''; if (charlist) { whitespace = ( charlist + '' ).replace(/([[\]().?/*{}+$^:])/g, '$1') } l = str.length; for (i = 0; i < l; i++) { if (whitespace.indexOf(str.charAt(i)) === -1) { str = str.substring(i) break } } l = str.length; for (i = l - 1; i >= 0; i--) { if (whitespace.indexOf(str.charAt(i)) === -1) { str = str.substring(0, i + 1) break } } return whitespace.indexOf(str.charAt(0)) === -1 ? str : ''; } function unset() { // http://kevin.vanzonneveld.net // + original by: Brett Zamir (http://brett-zamir.me) // * example 1: var arr = ['a', 'b', 'c']; // * example 1: unset('arr[1]'); // * returns 1: undefined // Must pass in a STRING to indicate the variable, not the variable itself (whether or not that evaluates to a string) // Works only on globals var i = 0, arg = '', win = '', winRef = /^(?:this)?window[.[]/, arr = [], accessor = '', bracket = /\[['"]?(\d+)['"]?\]$/; for (i = 0; i < arguments.length; i++) { arg = arguments[i]; winRef.lastIndex = 0, bracket.lastIndex = 0; win = winRef.test(arg) ? '' : 'this.window.'; if (bracket.test(arg)) { accessor = arg.match(bracket)[1]; arr = eval(win + arg.replace(bracket, '')); arr.splice(accessor, 1); // We remove from the array entirely, rather than leaving a gap } else { eval('delete ' + win + arg); } } } function is_json(str) { str = jQuery.trim(str); if (str == "") { return false; } try { JSON.parse(str); } catch (e) { return false; } return true; } (function ($) { //Credit Abdul Waheed $(document).on('change', 'input[name="wcap_op_product_domain_option_selector"]', function () { $("#continue_domain_transfer_btn_container, #continue_domain_register_btn_container").hide(); $('.whcom_product_domain_option_form').hide(); $(".wcap_op_domain_response").hide(); $("#continue_domain_register_btn_container").hide(); $('#' + $(this).val()).show(); }); // Data tables pagination buttons jQuery.fn.dataTable.ext.classes.sPageButton = 'whcom_button whcom_button_small whcom_button_secondary'; jQuery.fn.dataTable.ext.classes.sPageButtonActive = 'whcom_button_primary'; CheckDomainAvailability = function (event) { event.preventDefault(); jQuery("#continue_btn").hide(); var k = jQuery("#ask_domain_form").serialize(); jQuery("#ask_domain_form button, #ask_domain_form input[name=domain]").prop("disabled", true); jQuery.post(wcap_ajaxurl, k, function (data) { jQuery("#ask_domain_form button, #ask_domain_form input[name=domain]").prop("disabled", false); if (is_json(data)) { data = JSON.parse(data); var paytype = jQuery("#ask_domain_form input[name=paytype]:checked").val(); if (data.result == "error") { jQuery("#asking_domain_response").html(data.message); } else if (data.status == "unavailable" && paytype == "domainregister") { jQuery("#asking_domain_response").html("Domain not available, Please select another domain"); } else if (data.status == "unavailable" && paytype == "domaintransfer") { jQuery("#asking_domain_response").html("Domain is Eligible."); jQuery("#continue_btn").show(); } else if (data.status == "available" && paytype == "domainregister") { jQuery("#asking_domain_response").html("Congratulation, Domain available"); jQuery("#continue_btn").show(); } else if (data.status == "available" && paytype == "domaintransfer") { jQuery("#asking_domain_response").html("Domain is not Eligible."); } } else { jQuery("#asking_domain_response").html(data); } }); }; OpenInvoice = function () { var $GET = GET(); invoiceid = $GET["invoiceid"]; jQuery.post(wcap_ajaxurl, {"action": "wcap_requests", "what": "whmcs_login"}, function (data) { jQuery("#i").html("<pre>" + data + "</pre>"); window.open("http://whmcs2.whmpress.com/viewinvoice.php?wcap_no_redirect=1&id=" + invoiceid); }); }; CheckOut1 = function (event) { event.preventDefault(); k = "action=wcap_requests&what=add_order_to_whmcs&paymentmethod=" + jQuery("[name=paymentmethod]").val(); jQuery.post(wcap_ajaxurl, k, function (data) { if (is_json(data)) { data = JSON.parse(data); if (data.invoiceid) { /*new_link = "<a style='display:none' href='invoiceid=" + data.invoiceid + "' class='wcap_load_page' data-page='viewinvoice' id='wcap_new_link'>Go</a>"; jQuery("#main_div_100").append(new_link); jQuery("#wcap_new_link").click();*/ set_url_parameter_value("whmpca", "viewinvoice"); set_url_parameter_value("invoiceid", data.invoiceid); LoadData(); } else if (data.message) { alert(data.message); } else { alert(JSON.stringify(data)); } } else { alert(data); } }); }; LoginToWHMCS = function () { }; DocumentReady = function () { jQuery('.wcap_progress_circle').circliful({ animationStep: 5, foregroundBorderWidth: 5, backgroundBorderWidth: 15, percent: 80, iconPosition: 'middle', halfCircle: 1, textBelow: true }); jQuery(document).find('.whcom_table:not(.wcap_not_datatable) table').each(function () { jQuery.fn.dataTable.ext.classes.sPageButton = 'whcom_button whcom_button_small whcom_button_secondary'; jQuery.fn.dataTable.ext.classes.sPageButtonActive = 'whcom_button_primary'; jQuery(this).DataTable(dataTablesConfig); }); }; SubmitResetPassword = function (event) { event.preventDefault(); var k = jQuery("#whmcs_reset_pwd_form").serialize(); k += "&action=wcap_requests&what=reset_password_email&url=" + page_url; jQuery("#reset_email").prop("disabled", true); jQuery("#reset_submit").val(WCAP_Working_text); jQuery.post(wcap_ajaxurl, k, function (data) { jQuery("#reset_email").prop("disabled", false); if (data == "OK") { jQuery("#success_message").show(); jQuery("#whmcs_reset_pwd_form").remove(); } else { jQuery("#error_message").html(data).show(); jQuery("#reset_submit").val("Submit"); } }); } /** Sample Code - Farooq **/ SubmitContactForm = function (event) { event.preventDefault(); var k = jQuery("#wcap_contactus_from").serialize(); k += "&action=wcap_requests&what=submit_contact_form&url=" + page_url; var view_container = jQuery('.wcap_view_container'); var view_content = view_container.find('.wcap_view_content'); var view_response = view_container.find('.wcap_response_div'); view_response.html(wcap_spinner_icon).show(); jQuery.post(wcap_ajaxurl, k, function (data) { var res = JSON.parse(data); console.log(res); view_response.show().html(res.message); if (res.status === "OK") { view_content.hide(); } }); }; SubmitValidationForm = function (event) { event.preventDefault(); var k = jQuery("#whmcs_validation_form").serialize(); k += "&action=wcap_requests&what=validate_login"; var old_html = jQuery("#whmcs_validation_form input[type=submit]").val(); jQuery("#whmcs_validation_form input[type=email]").prop("disabled", true); jQuery("#whmcs_validation_form input[type=password]").prop("disabled", true); jQuery("#whmcs_validation_form input[type=submit]").prop("disabled", true).val(WCAP_Working_text); jQuery("#error_message").hide(); jQuery.post(wcap_ajaxurl, k, function (data) { data = JSON.parse(data); console.log(data); if (data.status == "OK") { if (redirect_login != "0") { window.location.href = redirect_login; } else { window.location.reload(); } /*jQuery( "#wcap_main_div" ).html( data.substr( 2 ) ); if ( jQuery( "table.wcap_responsive_table" ).length > 0 ) { jQuery( "table.wcap_responsive_table" ).tablesaw(); }*/ } else { jQuery("#whmcs_validation_form input[type=email]").prop("disabled", false); jQuery("#whmcs_validation_form input[type=password]").prop("disabled", false); jQuery("#whmcs_validation_form input[type=submit]").prop("disabled", false).val(old_html); jQuery("#error_message").html(data.message).show(); } }); }; ModalOpener = function (e) { var dialogTitle = jQuery(this).data('dialog-title'); var dialogWidth = ( jQuery('.wcap').outerWidth(false) ) - 40; var dialogPosition = {my: "center top", at: "center top+30", of: "body"} var dialogID = jQuery(this).data('dialog-id'); e.preventDefault(); jQuery('.wcap_modal#' + dialogID).dialog({ dialogClass: 'wcap_dialog', title: dialogTitle, maxWidth: 600, width: dialogWidth, position: dialogPosition, open: function () { } }); }; LoadSingleTicket = function (event) { event.preventDefault(); var id = jQuery(this).attr("data-id"); var args = "action=wcap_requests&what=load_page&page=viewticket&tid=" + id; var html = '<div style="padding:20px;text-align: center;font-size:20px"><i class="whcom_icon_spinner-1"></i> ' + WCAP_Loading_text + '</div>'; jQuery("#wcap_main_div .wcap").html(html); jQuery.post(wcap_ajaxurl, args, function (data) { data += "<button style='display: none' class='wcap_load_single_ticket' data-id='" + id + "' id='load_ticket_btn'>Load Ticket</button>"; jQuery("#wcap_main_div .wcap").html(data); jQuery(document).find('.whcom_table:not(.wcap_not_datatable) table').each(function () { jQuery.fn.dataTable.ext.classes.sPageButton = 'whcom_button whcom_button_small whcom_button_secondary'; jQuery.fn.dataTable.ext.classes.sPageButtonActive = 'whcom_button_primary'; jQuery(this).DataTable(dataTablesConfig); }); /* var simplemde= new SimpleMDE({element: jQuery('#wcap_md_editor')});*/ /*jQuery( "textarea.summernote" ).summernote( { height: 250 } );*/ }); }; Logout = function (event) { event.preventDefault(); jQuery(this).html(WCAP_Working_text); jQuery(this).val(WCAP_Working_text); var k = "action=wcap_requests&what=whmcs_logout"; jQuery.post(wcap_ajaxurl, k, function (data) { console.log(data); data = JSON.parse(data); if (data.status == "OK") { if (redirect_logout != '0') { window.location.href = redirect_logout; } else { // set_url_parameter_value( "whmpca", "logged_out" ); // LoadData(); window.location.reload(); } } else { alert(data); } }); }; NoLoad = function (event) { event.preventDefault(); }; LoadPage = function (event) { event.preventDefault(); var tagName = jQuery(this).prop("tagName"); var page = jQuery(this).attr("data-page"); if (!page) { page = "dashboard"; } var href = jQuery(this).attr("href"); var $this = jQuery(this); var params = {}; var remove_params = new Array("a", "pid", "invoiceid", "wcap"); if (href && href != "#") { if (href.substr(0, 1) == "?") { href = trim(href, "?"); } //params = JSON.parse('{"' + decodeURI(href).replace(/"/g, '\\"').replace(/&/g, '","').replace(/=/g,'":"') + '"}'); params = href ? JSON.parse('{"' + href.replace(/&/g, '","').replace(/=/g, '":"') + '"}', function (key, value) { return key === "" ? value : decodeURIComponent(value) }) : {} jQuery.each(remove_params, function (i, v) { if (params[v]) { delete ( remove_params[i] ); } }); params["whmpca"] = page; set_multi_url_parameters_value(params, remove_params); } else { params["whmpca"] = page; //var remove_params = JSON.parse('{"0":"a"}'); set_multi_url_parameters_value(params, remove_params); } if (params["whmpca"] == "add_service_page" && !params["pid"]) { params["whmpca"] = "order_new_service"; } if (tagName == "A") { jQuery("li.current-menu-item").removeClass("current-menu-item"); jQuery("#primary_nav_wrap").find('a[data-page=' + page + ']').closest("li").addClass("current-menu-item"); var Parent = jQuery("#primary_nav_wrap").find("a[data-page=" + page + "]").closest("li") .addClass("current-menu-item").parent(); if (Parent.prop("tagName") != "LI") { Parent = Parent.parent(); if (Parent.prop("tagName") == "LI") { Parent.addClass("current-menu-item"); } } else { Parent.addClass("current-menu-item"); } } /* else { jQuery( this ).html( "<i class='fa fa-spin fa-spinner'></i> Loading ...." ); jQuery( this ).val( "<i class='fa fa-spin fa-spinner'></i> Loading ...." ); }*/ /*var html = `<div style="padding:20px;text-align: center;font-size:20px"><i class="fa fa-spin fa-spinner"></i> Loading ....</div>`; jQuery( "#wcap_main_div .wcap" ).html( html ); var args = "action=wcap_requests&what=load_page&page=" + page; jQuery.post( wcap_ajaxurl, args, function( data ) { jQuery( "#wcap_main_div .wcap" ).html( data ); if ( jQuery( "table.wcap_responsive_table" ).length > 0 ) { jQuery( "table.wcap_responsive_table" ).tablesaw(); } } );*/ LoadData(); }; LoadData = function () { get_params = GET(); t1 = performance.now(); var page = get_params.whmpca; if (!page || page === 'clientarea') { page = "dashboard"; } get_params["page"] = page; WMPCA_page = page; get_params["action"] = "wcap_requests"; get_params["what"] = "load_data"; var match, pl = /\+/g, // Regex for replacing addition symbol with a space search = /([^&=]+)=?([^&]*)/g, decode = function (s) { return decodeURIComponent(s.replace(pl, " ")); } if (jQuery(this).attr("href")) { var args = jQuery(this).attr("href"); if (args.substr(0, 1) == "?") { args = args.substr(1); } while (match = search.exec(args)) { get_params[decode(match[1])] = decode(match[2]); } } if (get_params.whmpca == "add_service_page" && ( !get_params.pid )) { get_params.whmpca = "order_new_service"; } if (get_params.whmpca == 'order_new_service' && get_params.pid != '') { var k = ''; if (get_params.order_type == "order_product") { if (get_params.showdomainoption == '1') { var k = "?whmpca=domain_service&pid=" + get_params.pid + "&billingcycle=" + get_params.billingcycle; } else { var k = "?whmpca=add_service_page&pid=" + get_params.pid + "&billingcycle=" + get_params.billingcycle; } // var k = "?whmpca=add_service_page&pid=" + get_params.pid + "&billingcycle=" + get_params.billingcycle; set_new_url(k); LoadData(); } } if (get_params.whmpca == 'order_new_service' && get_params.order_type == "order_domain") { var k = ''; if (get_params.domain == 'register') { var k = "?whmpca=domain_register&sld=" + get_params.sld + "&tld=" + get_params.tld; set_new_url(k); LoadData(); } else if (get_params.domain == "transfer") { var k = "?whmpca=domain_transfer&sld=" + get_params.sld + "&tld=" + get_params.tld; set_new_url(k); LoadData(); } } /*if ( jQuery( "#wcap_menu_div" ).length > 0 ) { jQuery( 'html,body' ).animate( { scrollTop: jQuery( "#wcap_menu_div" ).offset().top }, 'slow' ); }*/ var html = '<div style="padding:20px;text-align: center;font-size:20px">' + '<i class="whcom_icon_spinner-1 whcom_animate_spin"></i> ' + WCAP_Loading_text + '</div>'; jQuery("#wcap_main_div .wcap").html(html); current_request = jQuery.ajax({ type: 'POST', data: get_params, url: wcap_ajaxurl, beforeSend: function () { if (current_request != null) { current_request.abort(); } }, success: function (data) { // Remving url parameter status new_url = remove_url_parameter("status"); // If tickets page is opened then remove URL parameter id if (!( get_params["whmpca"] == "service" || get_params["whmpca"] == "ticket" || get_params["whmpca"] == "updowngrade" || get_params["whmpca"] == "productdetails" )) { new_url = remove_url_parameter("id", new_url); } //for all pages other than order_new_service remove parameter if (( get_params["whmpca"] == "order_new_service" )) { new_url = remove_url_parameter("active", new_url); } new_url = remove_url_parameter("emptycart", new_url); if (( get_params["whmpca"] == "upgrade_final" )) { //new_url = remove_url_parameter( "k", new_url ); } if (( get_params["whmpca"] == "mass_pay" )) { //new_url = remove_url_parameter( "k", new_url ); } //for all pages other than order_new_service remove parameter if (( get_params["whmpca"] == "domain_register" )) { new_url = remove_url_parameter("domain", new_url); } if (( get_params["whmpca"] == "cart" )) { new_url = remove_url_parameter("domain", new_url); } // Set new URL in browser. set_new_url(new_url); jQuery("#wcap_main_div .wcap").html(data); jQuery(document).find('.whcom_table:not(.wcap_not_datatable) table').each(function () { jQuery.fn.dataTable.ext.classes.sPageButton = 'whcom_button whcom_button_small whcom_button_secondary'; jQuery.fn.dataTable.ext.classes.sPageButtonActive = 'whcom_button_primary'; jQuery(this).DataTable(dataTablesConfig); }); jQuery(".fancybox").fancybox(); jQuery(".ifancybox").fancybox({ type: "iframe", scrolling: "yes", iframe: {'scrolling': 'yes'}, afterShow: function () { var customContent = '<a title="Close" class="whcom_op_thickbox_redirect_overlay" href="javascript:;">Close</a>'; jQuery('.fancybox-outer').append(customContent); } }); if (get_params["whmpca"] == "order_new_service" && get_params["gid"]) { jQuery("li[data-li=" + get_params["gid"] + "]").click(); } else if (get_params["whmpca"] == "viewinvoice" && jQuery("#view_invoice_link").length == 1) { jQuery("#view_invoice_link").click(); } renderWhcomTabs(); //Registration form submit if (get_params.whmpca == 'domain_register') { if (get_params.sld && get_params.tld) { jQuery("#domain_search_form").trigger('submit'); // console.log(get_params.sld); } } //Transfer form submit if (get_params.whmpca == 'domain_transfer' && get_params.sld && get_params.tld) { jQuery("#wcap_domain_transfer_submit").trigger('click'); } window.whcom_op_update_cart_summaries(); window.whcom_op_update_product_summary(); $('.whcom_op_submit_on_load').submit().removeClass('whcom_op_submit_on_load'); window.initWhcom(); }, error: function (jqXHR, textStatus) { console.log(textStatus); }, complete: function () { current_request = null; } }); }; LoadInvoiceLink = function (event) { event.preventDefault(); jQuery(this).html("<i class='whcom_icon_spinner-1 whcom_animate_spin'></i> " + WCAP_Loading_text); var k = "action=wcap_requests&what=load_invoices_page"; jQuery.post(wcap_ajaxurl, k, function (data) { jQuery("#wcap_main_div").html(data); jQuery(document).find('.whcom_table:not(.wcap_not_datatable) table').each(function () { jQuery.fn.dataTable.ext.classes.sPageButton = 'whcom_button whcom_button_small whcom_button_secondary'; jQuery.fn.dataTable.ext.classes.sPageButtonActive = 'whcom_button_primary'; jQuery(this).DataTable(dataTablesConfig); }); //jQuery( "table.wcap_responsive_table" ).tablesaw(); //jQuery( "table.whcom_table" ).tablesaw(); //jQuery( "table.wcap_responsive_table" ).trigger( "enhance.tablesaw" ); //jQuery('table#whcom_table').table().data( "table" ).refresh(); //jQuery('table#whcom_table').tablesaw().data( "tablesaw" ).refresh(); }); }; LoadTicketsLink = function (event) { event.preventDefault(); jQuery(this).html("<i class='whcom_icon_spinner-1 whcom_animate_spin'></i> " + WCAP_Loading_text); var k = "action=wcap_requests&what=load_tickets_page"; jQuery.post(wcap_ajaxurl, k, function (data) { jQuery("#wcap_main_div").html(data); jQuery(document).find('.whcom_table:not(.wcap_not_datatable) table').each(function () { jQuery.fn.dataTable.ext.classes.sPageButton = 'whcom_button whcom_button_small whcom_button_secondary'; jQuery.fn.dataTable.ext.classes.sPageButtonActive = 'whcom_button_primary'; jQuery(this).DataTable(dataTablesConfig); }); }); }; LoadDomainsLink = function (event) { event.preventDefault(); jQuery(this).html("<i class='whcom_icon_spinner-1 whcom_animate_spin'></i> " + WCAP_Loading_text); var k = "action=wcap_requests&what=load_domains_page"; jQuery.post(wcap_ajaxurl, k, function (data) { jQuery("#wcap_main_div").html(data); jQuery(document).find('.whcom_table:not(.wcap_not_datatable) table').each(function () { jQuery.fn.dataTable.ext.classes.sPageButton = 'whcom_button whcom_button_small whcom_button_secondary'; jQuery.fn.dataTable.ext.classes.sPageButtonActive = 'whcom_button_primary'; jQuery(this).DataTable(dataTablesConfig); }); }); }; LoadServiceLink = function (event) { event.preventDefault(); jQuery(this).html("<i class='whcom_icon_spinner-1 whcom_animate_spin'></i> " + WCAP_Loading_text); var k = "action=wcap_requests&what=load_services_page"; jQuery.post(wcap_ajaxurl, k, function (data) { jQuery("#wcap_main_div").html(data); jQuery(document).find('.whcom_table:not(.wcap_not_datatable) table').each(function () { jQuery.fn.dataTable.ext.classes.sPageButton = 'whcom_button whcom_button_small whcom_button_secondary'; jQuery.fn.dataTable.ext.classes.sPageButtonActive = 'whcom_button_primary'; jQuery(this).DataTable(dataTablesConfig); }); }); }; SubmitUpdateClient = function (event) { event.preventDefault(); var args = jQuery(this).serialize(); jQuery("#wcap_update_client_form input").prop("disabled", true); console.log(args); jQuery.post(wcap_ajaxurl, args, function (data) { jQuery("#wcap_update_client_form input").prop("disabled", false); data = JSON.parse(data); if (data.result == "success") { window.scrollTo(0, 0); jQuery("#profile_update_success").show(); jQuery("#profile_update_error").hide(); jQuery("#wcap_update_client_form").hide(); } else { console.log(data); jQuery("#profile_update_error").html(data).show(); } }); }; SubmitUpdatePassword = function (event) { event.preventDefault(); var args = jQuery(this).serialize(); jQuery("#wcap_update_password_form input").prop("disabled", true); jQuery.post(wcap_ajaxurl, args, function (data) { jQuery("#wcap_update_password_form input").prop("disabled", false); if (data['status'] == "success") { alert("Password updated successfully!"); jQuery("#wcap_update_password_form input[type=reset]").click(); } else { alert(data); } }); }; SubmitRegisterClient = function (event) { event.preventDefault(); var args = jQuery(this).serialize(); oldHTML = jQuery("#register_form_button").html(); var view_container = jQuery('.wcap_view_container'); var view_content = view_container.find('.wcap_vew_content'); var view_response = view_container.find('.wcap_view_response'); view_response.html(wcap_spinner_icon).show(); jQuery.post(wcap_ajaxurl, args, function (data) { data = JSON.parse(data); console.log(data); if (data.status == "OK") { jQuery('html, body').animate({ scrollTop: jQuery('#register_client_form').offset().top - 40 //#DIV_ID is an example. Use the id of your destination on the page }, 'slow'); jQuery("#register_message").removeClass(); jQuery("#register_client_form").fadeOut(); /*alert( "New User has been created" );*/ jQuery('.wcop_response').addClass('whcom_alert whcom_alert_success'); jQuery('.wcop_response').html("New User Created Successfull").show().delay(100); var oldURL = window.location.href; var index = 0; var newURL = oldURL; index = oldURL.indexOf('?'); if (index === -1) { index = oldURL.indexOf('#'); } if (index !== -1) { newURL = oldURL.substring(0, index); } setTimeout(function () { window.location.replace(newURL); }, 1000); } else { jQuery('html, body').animate({ scrollTop: jQuery('#register_client_form').offset().top - 40 //#DIV_ID is an example. Use the id of your destination on the page }, 'slow'); jQuery("#register_form_button").html(oldHTML); jQuery('.wcop_response').addClass('whcom_alert whcom_alert_danger'); jQuery('.wcop_response').html(data.message).show().delay(200); jQuery('#register_client_form :input:enabled:visible:first').focus(); } }); }; SubmitAddAccount = function (event) { event.preventDefault(); var args = jQuery(this).serialize(); var form = jQuery("#wcap_add_contact_form"); var response_container = form.find('.wcap_response_text'); response_container.removeClass('whcom_alert_success whcom_alert_info whcom_alert_warning whcom_alert_danger').slideUp(500); jQuery.post(wcap_ajaxurl, args, function (data) { var res = JSON.parse(data); console.log(res); response_container.slideDown(500).text(res.message); form.html(response_container); if (res.result === 'success') { response_container.addClass('whcom_alert_success'); } else { response_container.addClass('whcom_alert_danger'); } setTimeout(function () { jQuery("#wcap_choose_contact_form").submit(); }, 3000); }); }; SubmitSelectAccount = function (event) { event.preventDefault(); jQuery("#wcap_main_div .wcap").html('<div style="padding:20px;text-align: center;font-size:20px">' + '<i class="whcom_icon_spinner-1 whcom_animate_spin"></i> ' + WCAP_Loading_text + '</div>'); var args = jQuery(this).serialize(); jQuery.post(wcap_ajaxurl, args, function (data) { jQuery("#wcap_main_div .wcap").html(data); }); }; SubmitUpdateAccount = function (event) { event.preventDefault(); var k = jQuery("#wcap_update_contact_form").serialize(); console.log(k); var view_container = jQuery('.wcap_view_container'); var view_content = view_container.find('.wcap_view_content'); var view_response = view_container.find('.wcap_view_response'); view_response.html(wcap_spinner_icon).show(); jQuery.post(wcap_ajaxurl, k, function (data) { var res = JSON.parse(data); console.log(res['api_response']); view_response.show().html(res.message); if (res.status === "OK") { if (res.action_dont_hide != "YES") { view_content.hide(); } if (res.action_refresh === "YES") { //refresh here there is a command } } } ); }; OpenTicket = function (event) { event.preventDefault(); var id = jQuery(this).attr("data-id"); var args = 'id=' + id + '&action=wcap_requests&what=ticket_form'; var html = '<div style="padding:20px;text-align: center;font-size:20px">' + '<i class="whcom_icon_spinner-1 whcom_animate_spin"></i> ' + WCAP_Loading_text + '</div>'; jQuery("#wcap_main_div .wcap").html(html); jQuery.post(wcap_ajaxurl, args, function (data) { jQuery("#wcap_main_div .wcap").html(data); jQuery(document).find('.whcom_table:not(.wcap_not_datatable) table').each(function () { jQuery.fn.dataTable.ext.classes.sPageButton = 'whcom_button whcom_button_small whcom_button_secondary'; jQuery.fn.dataTable.ext.classes.sPageButtonActive = 'whcom_button_primary'; jQuery(this).DataTable(dataTablesConfig); }); //jQuery( "table.wcap_responsive_table" ).tablesaw(); //jQuery( "table.whcom_table" ).tablesaw(); //jQuery( "table.wcap_responsive_table" ).trigger( "enhance.tablesaw" ); //jQuery('table#whcom_table').table().data( "table" ).refresh(); //jQuery('table#whcom_table').tablesaw().data( "tablesaw" ).refresh(); }); }; SubmitOpenTicketForm = function (event) { event.preventDefault(); oldHtml = jQuery("#wcap_md_form_submit").html(); jQuery("#wcap_md_form_submit").html("<i class='whcom_icon_spinner-1 whcom_animate_spin'></i>"); var args = jQuery("#open_ticket_form").serialize(); jQuery("#open_ticket_form input").prop("disabled", true); jQuery("#open_ticket_form button").prop("disabled", true); jQuery("#open_ticket_form textarea").prop("disabled", true); jQuery.post(wcap_ajaxurl, args, function (data) { if (is_json(data)) { data = JSON.parse(data); console.log(data); if (data.result == "success") { jQuery("#open_ticket_form").hide(); var open_ticket_message_container = jQuery("#open_ticket_message"); open_ticket_message_container.removeClass(); jQuery('html, body').animate({ scrollTop: jQuery('#open_ticket_message').offset().top - 10000 //#DIV_ID is an example. Use the id of your destination on the page }, 'slow'); open_ticket_message_container.removeClass().html(data.response_html).show(); /* setTimeout(function () { jQuery("#wcap_main_div .wcap").html("<div style='padding:15px;text-align: center'>" + "<i class='whcom_icon_spinner-1 whcom_animate_spin'></i> Loading ...</div>"); var k = "action=wcap_requests&what=load_page&page=tickets"; jQuery.post(wcap_ajaxurl, k, function (data) { jQuery("#wcap_main_div").html(data); jQuery(document).find('.whcom_table:not(.wcap_not_datatable) table').each(function () { jQuery.fn.dataTable.ext.classes.sPageButton = 'whcom_button whcom_button_small whcom_button_secondary'; jQuery.fn.dataTable.ext.classes.sPageButtonActive = 'whcom_button_primary'; jQuery(this).DataTable(dataTablesConfig); } ); }); }, 2000);*/ } else { jQuery("#open_ticket_message").addClass("whcom_alert whcom_alert_warning").html("<span class='whcom_icon_cancel-circled'></span>" + data.result); jQuery('html, body').animate({ scrollTop: jQuery('#ticket_top').offset().top - 20 //#DIV_ID is an example. Use the id of your destination on the page }, 'slow'); jQuery("#open_ticket_form").show(); jQuery("#wcap_md_form_submit").html(oldHtml); } } else { jQuery("#open_ticket_message").addClass("whcom_alert whcom_alert_warning").html("<span class='whcom_icon_cancel-circled'></span> " + data); jQuery("#wcap_md_form_submit").html(oldHtml); jQuery("#open_ticket_form:input:enabled:visible:first:focus"); jQuery('html, body').animate({ scrollTop: jQuery('#open_ticket_message').offset().top - 10000 //#DIV_ID is an example. Use the id of your destination on the page }, 'slow'); setTimeout(function () { jQuery("#open_ticket_message").fadeOut(); }, 2000); jQuery("#open_ticket_form input").prop("disabled", false); jQuery("#open_ticket_form button").prop("disabled", false); jQuery("#open_ticket_form textarea").prop("disabled", false); } }); }; SubmitReplyTicket = function (event) { event.preventDefault(); var args = jQuery(this).serialize(); jQuery.post(wcap_ajaxurl, args, function (data) { data = JSON.parse(data); if (data.status == "OK") { //jQuery( "#reply_ticket_form textarea[name=message]" ).summernote( "code", "" ); jQuery('#reply_ticket_form').slideToggle(); jQuery("#load_ticket_btn").click(); } else { alert(data); } }); }; RemoveCartTR = function (event) { event.preventDefault(); key = jQuery(this).closest("tr").attr("data-key"); jQuery(this).closest("tr").remove(); jQuery("#total_price").html("<i class='whcom_icon_spinner-1 whcom_animate_spin'></i>"); jQuery.post(wcap_ajaxurl, { action: "wcap_requests", what: "remove_cart_time", key: key }, function (data) { jQuery("#total_price").html(data); }) }; SubmitRequestCancel = function (event) { event.preventDefault(); jQuery.post(wcap_ajaxurl, jQuery(this).serialize(), function (data) { if (is_json(data)) { data = JSON.parse(data); if (data.result == "success") { jQuery("#wcap_request_cancel").hide(); jQuery("#wcap_success_div").show(); } else if (data.result == "error") { jQuery("#wcap_request_cancel").hide(); jQuery("#wcap_error_div").html(data.message).show(); } } else { jQuery("#wcap_request_cancel").hide(); jQuery("#wcap_error_div").html(data).show(); } }) }; /*AddProduct = function( event ) { event.preventDefault(); var pid = jQuery( this ).attr( "data-pid" ); var page = "cart"; var args = jQuery( this ).attr( "href" ); if ( args.substr( 0, 1 ) == "?" ) { args = args.substr( 1 ); } args += '&action=wcap_requests&what=load_page&page=' + page; // var html = `<div style="padding:20px;text-align: center;font-size:20px"><i class="fa fa-spin fa-spinner"></i> Loading ....</div>`; // jQuery( "#wcap_main_div .wcap" ).html( html ); // // jQuery.post( wcap_ajaxurl, args, function( data ) { // jQuery( "#wcap_main_div .wcap" ).html( data ); // if ( jQuery( "table.wcap_responsive_table" ).length > 0 ) { // jQuery( "table.wcap_responsive_table" ).tablesaw(); // } // } ); }*/ PopState = function (event) { /*st = event.originalEvent.state; console.log( st ); console.log( st.url );*/ //console.log( window.location.href ); //console.log( GET() ); LoadData(); }; GET = function () { var urlParams; var match, pl = /\+/g, // Regex for replacing addition symbol with a space search = /([^&=]+)=?([^&]*)/g, decode = function (s) { return decodeURIComponent(s.replace(pl, " ")); }, query = window.location.search.substring(1); urlParams = {}; while (match = search.exec(query)) { urlParams[decode(match[1])] = decode(match[2]); } return urlParams; }; set_new_url = function ($new_url, title) { if (title) { window.history.pushState({}, title, $new_url); document.title = title; } else { window.history.pushState({}, "", $new_url); } }; remove_url_parameter = function (parameter, url, auto_change_url) { if (!url) { url = window.location.href; } //prefer to use l.search if you have a location/link object var urlparts = url.split('?'); if (urlparts.length >= 2) { var prefix = encodeURIComponent(parameter) + '='; var pars = urlparts[1].split(/[&;]/g); //reverse iteration as may be destructive for (var i = pars.length; i-- > 0;) { //idiom for string.startsWith if (pars[i].lastIndexOf(prefix, 0) !== -1) { pars.splice(i, 1); } } url = urlparts[0] + '?' + pars.join('&'); if (auto_change_url) { set_new_url(url); } else { return url; } } else { if (auto_change_url) { set_new_url(url); } else { return url; } } }; get_url_values = function () { // It will return all query string variable names. var vars = [], hash; var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&'); for (var i = 0; i < hashes.length; i++) { hash = hashes[i].split('='); vars.push(hash[0]); vars[hash[0]] = hash[1]; } return vars; }; remove_query_string = function () { var uri = window.location.toString(); if (uri.indexOf("?") > 0) { var clean_uri = uri.substring(0, uri.indexOf("?")); window.history.replaceState({}, document.title, clean_uri); } }; set_url_parameter_value = function (key, val) { var all_vars = GET(); all_vars[key] = val; var vars_string = ""; jQuery.each(all_vars, function (k, v) { vars_string += encodeURI(k) + "=" + encodeURI(v) + "&"; }); if (count(all_vars) > 0) { if (vars_string.substr(-1) == "&") { vars_string = rtrim(vars_string, "&"); } set_new_url("?" + vars_string); } }; set_multi_url_parameters_value = function (params_array, remove_params_array) { var all_vars = GET(); jQuery.each(params_array, function (key, val) { all_vars[key] = val; }); if (remove_params_array) { jQuery.each(remove_params_array, function (i, v) { if (all_vars[v]) { delete ( all_vars[v] ); } }); } var vars_string = ""; jQuery.each(all_vars, function (k, v) { vars_string += encodeURI(k) + "=" + encodeURI(v) + "&"; }); if (count(all_vars) > 0) { if (vars_string.substr(-1) == "&") { vars_string = rtrim(vars_string, "&"); } set_new_url("?" + vars_string); } }; UpdateConfigPrices = function (event) { jQuery("#spinner").show(); var k = "action=wcap_requests&what=configurable_options_html&billingcycle=" + jQuery(".wcap_billingcycle").val() + "&pid=" + PID + "&" + jQuery(".configoption").serialize(); jQuery.post(wcap_ajaxurl, k, function (data) { jQuery("#spinner").hide(); if (is_json(data)) { data = JSON.parse(data); jQuery("#config_div").html(data.config_html); jQuery("#summary_order_div").html(data.order_summary_html); jQuery("#order_form_cart input[name=price]").val(data.prices.price); jQuery("#order_form_cart input[name=setup]").val(data.prices.setupfee); } else { alert(data); } }); }; }) (jQuery); (function ($) { "use strict"; LoadData(); jQuery(window).on("popstate", PopState); jQuery(document).on('submit', '#ask_domain_form', function (event) { event.preventDefault(); }); jQuery(document).on('click', '#domain_whois_check_btn', CheckDomainAvailability); jQuery(document).on('click', '.wcap_modal_opener', ModalOpener); jQuery(document).on('ready', DocumentReady); jQuery(document).on("submit", "#whmcs_validation_form", SubmitValidationForm); jQuery(document).on("submit", "#whmcs_reset_pwd_form", SubmitResetPassword); jQuery(document).on("submit", "#wcap_contactus_from", SubmitContactForm); jQuery(document).on("submit", "#updowngrade_form_final", function (event) { event.preventDefault(); var k = jQuery("#updowngrade_form_final").serialize(); var view_container = jQuery('.wcap_view_container'); var view_content = view_container.find('.wcap_view_content'); var view_response = view_container.find('.wcap_view_response'); view_response.html(wcap_spinner_icon).show(); jQuery.post(wcap_ajaxurl, k, function (data) { var res = JSON.parse(data); console.log(res); view_response.show().html(res.message); if (res.status === "OK") { view_content.hide(); } }); }); // options upgrade finel : farooq jQuery(document).on("submit", "#updowngrade_options_form_final", function (event) { event.preventDefault(); var k = jQuery("#updowngrade_options_form_final").serialize(); var view_container = jQuery('.wcap_view_container'); var view_content = view_container.find('.wcap_view_content'); var view_response = view_container.find('.wcap_view_response'); view_response.html(wcap_spinner_icon).show(); jQuery.post(wcap_ajaxurl, k, function (data) { var res = JSON.parse(data); console.log(res); view_response.show().html(res.message); if (res.status === "OK") { view_content.hide(); } }); }); jQuery(document).on("click", "#whmcs_logout_btn", Logout); jQuery(document).on("submit", "#domain_ns_form", function (event) { event.preventDefault(); jQuery.post(wcap_ajaxurl, $("#domain_ns_form").serialize(), function (data) { if (data == "OK") { set_url_parameter_value("whmpca", "cart"); LoadData(); } else { alert(data); } }); }); jQuery(document).on("submit", "#mass_pay_form", function (event) { event.preventDefault(); jQuery.post(wcap_ajaxurl, $("#mass_pay_form").serialize(), function (data) { alert(data); }); }); jQuery(document).on("click", "#load_invoices_link", LoadInvoiceLink); jQuery(document).on("click", "#load_tickets_link", LoadTicketsLink); jQuery(document).on("click", "#load_domains_link", LoadDomainsLink); jQuery(document).on("click", "#load_service_link", LoadServiceLink); //jQuery( document ).on( "click", ".add_product_link", AddProduct ); jQuery(document).on("click", ".wcap_load_single_ticket", LoadSingleTicket); jQuery(document).on("click", ".no_load", NoLoad); jQuery(document).on("click", ".wcap_load_page", LoadPage); jQuery(document).on("submit", "#wcap_update_client_form", SubmitUpdateClient); jQuery(document).on("submit", "#wcap_update_password_form", SubmitUpdatePassword); jQuery(document).on("submit", "#register_client_form", SubmitRegisterClient); jQuery(document).on("submit", "#wcap_add_contact_form", SubmitAddAccount); jQuery(document).on("submit", "#wcap_choose_contact_form", SubmitSelectAccount); jQuery(document).on("submit", "#wcap_update_contact_form", SubmitUpdateAccount); jQuery(document).on("click", ".open_ticket", OpenTicket); jQuery(document).on("submit", "#open_ticket_form", SubmitOpenTicketForm); jQuery(document).on("submit", "#reply_ticket_form", SubmitReplyTicket); jQuery(document).on("click", ".wcap_check", CheckOut1); jQuery(document).on("click", ".remove_item", RemoveCartTR); jQuery(document).on("submit", "#wcap_request_cancel", SubmitRequestCancel); jQuery(document).on("submit", "#security_question", SubmitUpdateSecurityQuestions); jQuery(document).on("submit", "#security_question", SubmitUpdateSecurityQuestions); jQuery(document).on("submit", "#update_credit_card_form", SubmitUpdateUpdateCreditCard); jQuery(document).on("change", ".wcap_billingcycle", function () { var code = jQuery(this).val(); var pid = jQuery(this).attr("data-pid"); jQuery("#spinner").show(); jQuery(".wcap_billingcycle").prop("disabled", true); jQuery("button#button1").prop("disabled", true).html("Wait..."); jQuery.post(wcap_ajaxurl, { action: "wcap_requests", what: "configurable_options_html", pid: pid, billingcycle: code }, function (data) { jQuery("button#button1").prop("disabled", false).html("Continue <i class='whcom_icon_right-hand'></i>"); jQuery(".wcap_billingcycle").prop("disabled", false); jQuery("#spinner").hide(); if (is_json(data)) { data = JSON.parse(data); jQuery("#config_div").html(data.config_html); jQuery("#summary_order_div").html(data.order_summary_html); } else { alert(data); } }); /*jQuery( "#price_td, #price_td2" ).html( Currencies[ cur_code ].prefix + Product[ "pricing" ][ cur_code ][ code ] + " " + Currencies[ cur_code ].suffix ); jQuery( "#billing_cycle_td" ).html( BillingCycles[ code ] ); jQuery( "#setup_td" ).html( Currencies[ cur_code ].prefix + Product[ "pricing" ][ cur_code ][ code.substr( 0, 1 ) + "setupfee" ] + " " + Currencies[ cur_code ].suffix ); var fee = Number( Product[ "pricing" ][ cur_code ][ code ] ); var setup_fee = Number( Product[ "pricing" ][ cur_code ][ code.substr( 0, 1 ) + "setupfee" ] ); var total = fee + setup_fee; jQuery( "#order_form_cart input[name=price]" ).val( fee ); jQuery( "#order_form_cart input[name=setup]" ).val( setup_fee ); jQuery( "#total_td" ).html( Currencies[ cur_code ].prefix + total + " " + Currencies[ cur_code ].suffix )*/ }); jQuery(document).on("submit", "#order_form_cart", function (event) { event.preventDefault(); jQuery("#button1").html("Adding ...."); jQuery.post(wcap_ajaxurl, jQuery("#order_form_cart").serialize(), function (data) { if (data == "OK") { if ($("#order_form_cart input[name=service_with_domain]").length == 1) { set_url_parameter_value("whmpca", "confdomains"); LoadData(); } else { var new_link = "<a style='display:none' class='wcap_load_page' data-page='cart' id='wcap_new_link'>Go</a>"; jQuery("#order_form_cart").append(new_link); jQuery("#wcap_new_link").click(); } } else { jQuery("#button1").html("<i class='fa fa-plus'></i> Add Order"); alert(data); } }); /*jQuery.post( wcap_ajaxurl, jQuery( this ).serialize(), function( data ) { if ( is_json( data ) ) { data = JSON.parse( data ); if ( data.result == "success" ) { console.log( "remove_url_parameter" ); new_link = "<a style='display:none' class='wcap_load_page' data-page='viewinvoice' id='invoice_link' href='invoiceid=" + data.invoiceid + "'>Go</a>"; jQuery( "#order_form_cart" ).append( new_link ); jQuery( "#invoice_link" ).click(); } else if ( data.result ) { alert( "Error: " + data.result ); } else { alert( "Unknown Error" ); } } else { alert( "Error: ".data ); } } );*/ }); jQuery(document).on("click", ".wcap_ifancybox", function (event) { event.preventDefault(); var href = jQuery(this).attr("href"); jQuery.fancybox({ type: "iframe", href: href, minWidth: '90%', afterShow: function () { var customContent = '<a title="Close" class="whcom_op_thickbox_redirect_overlay" href="javascript:;">Close</a>'; jQuery('.fancybox-outer').append(customContent); } }); }); jQuery(document).on('click', '.whcom_op_thickbox_redirect_overlay', function () { jQuery.fancybox.close(); }); jQuery(document).on("change", "#config_div select, #config_div input[type=radio], #config_div input[type=checkbox], #config_div input[type=number]", UpdateConfigPrices); jQuery(document).on("change", "#wcap_service_status", function (event) { set_url_parameter_value("status", jQuery(this).val()); LoadData(); }); jQuery(document).on("click", "input[name=paytype]", function (event) { var val = jQuery(this).val(); if (val == "domainown") { jQuery("#domain_check_form").hide(); jQuery("#domain_own_check_div").show(); } else { jQuery("#domain_own_check_div").hide(); jQuery("#domain_check_form").show(); } }); jQuery(document).on("click", "#domain_own_check_btn", function (event) { event.preventDefault(); jQuery("#continue_btn").hide(); var val = jQuery("input[name=domain]").val(); jQuery("input[name=domain]").prop("disabled", true); jQuery.post(wcap_ajaxurl + "?checkown_domain", {action: "wcap_requests", domain: val, what: "domain_own_check"}, function (data) { jQuery("input[name=domain]").prop("disabled", false); if (data == "OK") { LoadData(); } else { alert(data); } }); }); // This function is called function function123(data) { if (is_json(data)) { data = JSON.parse(data); if (data.status == "unavailable") { //jQuery(".wcap_op_domain_response").html(data.domain_price_html).show(); jQuery("#continue_domain_transfer_btn_container").show(); } else { jQuery(".wcap_op_domain_response").html(data.status).show(); } } else { alert(data); } } jQuery(document).on("submit", "#transfer_domain_form", function (event) { event.preventDefault(); // Ajax post request. jQuery.post(wcap_ajaxurl, jQuery("#transfer_domain_form").serialize(), function123); }); jQuery(document).on("submit", "#domain_config_search_form", function (event) { event.preventDefault(); jQuery("#domain_search_success").hide(); jQuery("#continue_domain_register_btn_container").hide(); jQuery("#domain_register").val($("#search_domain").val() + '' + $("#extension").val()); var k = jQuery(this).serialize(); jQuery(this).find("input[name=domain]").prop("disabled", true); jQuery("#domain_config_search_form button[type=submit]").html("<i class='whcom_icon_spinner-1 whcom_animate_spin'></i>"); jQuery(".wcap_op_domain_response").html(""); jQuery.post(wcap_ajaxurl, k, function (data) { jQuery("#domain_config_search_form input[name=domain]").prop("disabled", false); jQuery("#domain_config_search_form button[type=submit]").html("Check"); if (is_json(data)) { data = JSON.parse(data); if (data.result == "success" && data.status == "available") { jQuery("#wcap_html").html(data.domain_price_html).show(); jQuery("#domain_search_success").show(); jQuery("#continue_domain_register_btn").show(); jQuery("#continue_domain_register_btn_container").show(); jQuery("#domain_search_error").hide(); } else { jQuery("#continue_domain_register_btn_container").show(); jQuery("#domain_search_error").show(); jQuery("#domain_search_success").hide(); jQuery("#continue_domain_register_btn").hide(); } } else { console.log(data); } }); }); jQuery(document).on("submit", "#domain_config_transfer", function (event) { event.preventDefault(); jQuery("#continue_domain_transfer_btn_container").hide(); var oldhtml = jQuery("#domain_config_transfer_button").html(); jQuery("#domain_config_transfer_button").html("<i class='whcom_icon_spinner-1 whcom_animate_spin'></i>"); jQuery("#domaint_transfer_text").val(jQuery("#search_transfer_domain").val() + '' + jQuery("#transfer_domain_ext").val()); var k = jQuery("#domain_config_transfer").serialize(); // Ajax post request. jQuery.post(wcap_ajaxurl, k, function (data) { if (is_json(data)) { data = JSON.parse(data); if (data.status == "unavailable") { //jQuery(".wcap_op_domain_response").html(data.domain_price_html).show(); jQuery("#continue_domain_transfer_btn_container").show(); jQuery("#continue_domain_transfer_btn").show(); jQuery("#domain_config_transfer_success").show(); jQuery("#domain_config_transfer_error").hide(); jQuery("#domain_config_transfer_button").html(oldhtml); } else { jQuery("#continue_domain_transfer_btn_container").show(); jQuery("#domain_config_transfer_error").show(); jQuery("#continue_domain_transfer_btn").hide(); jQuery("#domain_config_transfer_success").hide(); jQuery("#domain_config_transfer_button").html(oldhtml); } } else { alert(data); jQuery("#domain_config_transfer_button").html(oldhtml); } }); }); jQuery(document).on("submit", "#domain_search_form", function (event) { event.preventDefault(); jQuery("#wcap_domain_register").val(jQuery("#wcap_search_domain").val() + '' + jQuery("#wcap_extension").val()); var k = jQuery(this).serialize(); jQuery(this).find("input[name=domain]").prop("disabled", true); jQuery(".whcom_alert.whcom_alert_success, .whcom_alert.whcom_alert_danger, #continue_domain_register_btn, #continue_domain_register_btn_container").hide(); jQuery("#domain_search_form button[type=submit]").html("<i class='whcom_icon_spinner-1 whcom_animate_spin'></i> Checking"); jQuery("#wcap_html").html(""); jQuery.post(wcap_ajaxurl, k, function (data) { jQuery("#domain_search_form input[name=domain]").prop("disabled", false); jQuery("#domain_search_form button[type=submit]").html("Check"); if (is_json(data)) { data = JSON.parse(data); if (data.result == "success" && data.status == "available") { jQuery(".whcom_alert.whcom_alert_success").show(); jQuery("#continue_domain_register_btn").show(); jQuery("#continue_domain_register_btn_container").show(); jQuery("#wcap_html").html(data.domain_price_html); } else if (data.message) { jQuery(".whcom_alert.whcom_alert_danger").html("Error: " + data.message).show(); } else { jQuery(".whcom_alert.whcom_alert_danger").html("Domain not available for registration").show(); } } else { alert(data); } }); }); jQuery(document).on("click", "#continue_domain_transfer_btn", function (event) { event.preventDefault(); var old_html = jQuery(this).html(); jQuery(this).html(WCAP_Loading_text); //var k = "action=wcap_requests&what=transferdomain&domain=" + jQuery.trim(jQuery("#domain_attach").val()) + "." + jQuery.trim(jQuery("#domain_attach").val()); var k = "action=wcap_requests&what=transferdomain&domain=" + jQuery.trim(jQuery("#search_transfer_domain").val()) + jQuery.trim(jQuery("#transfer_domain_ext").val()); jQuery.post(wcap_ajaxurl, k, function (data) { if (data == "OK") { //set_url_parameter_value( "whmpca", "cart" ); var params = GET(); if (isset(params["pid"])) { set_url_parameter_value("whmpca", "add_service_page"); } else { set_url_parameter_value("whmpca", "confdomains"); } LoadData(); } else { jQuery("#continue_domain_register_btn").html(old_html); alert(data); } }); }); jQuery(document).on("click", "#continue_domain_register_btn", function (event) { event.preventDefault(); var old_html = jQuery(this).html(); jQuery(this).html(WCAP_Loading_text); var k = "action=wcap_requests&what=registerdomain&billingcycle=" + jQuery(".wcap_billingcycle").val(); k += "¤cy=" + jQuery("input[name=currency]").val(); jQuery.post(wcap_ajaxurl, k, function (data) { if (data == "OK") { //set_url_parameter_value( "whmpca", "cart" ); var params = GET(); if (isset(params["pid"])) { set_url_parameter_value("whmpca", "add_service_page"); } else { set_url_parameter_value("whmpca", "confdomains"); } LoadData(); } else { jQuery("#continue_domain_register_btn").html(old_html); alert(data); } }); }); jQuery(document).on("change", "#domain_actions_select", function (event) { event.preventDefault(); var val = jQuery(this).val(); var domain = jQuery(this).attr("data-domain"); if (val == "1") { jQuery.fancybox({ type: "ajax", ajax: { type: "POST", data: {action: "wcap_requests", what: "domain_renew_modal", domain: domain, domainid: val} }, href: wcap_ajaxurl }); } jQuery(this).val(0); }); jQuery(document).on("submit", "#domain_renew_form", function (event) { event.preventDefault(); jQuery.fancybox.close(); jQuery.post(wcap_ajaxurl, jQuery("#domain_renew_form").serialize(), function (data) { if (data == "OK") { alert("Domain renewed successfully"); } else { alert(data); } }); }); jQuery(document).on("submit", "#domain_config_attach", function (event) { event.preventDefault(); jQuery.post(wcap_ajaxurl, jQuery("#domain_config_attach").serialize(), function (data) { /*if (data == "OK") { $('.wcap_op_domain_response').html("Domain renewed successfully").show(); } else { $('.wcap_op_domain_response').html(data).show(); }*/ /*if (data=="OK") { set_url_parameter_value("whmpca", "add_service_page"); LoadData(); } else { alert (data); }*/ set_url_parameter_value("whmpca", "add_service_page"); set_url_parameter_value("domain", jQuery.trim(jQuery("#domain_attach").val()) + "." + jQuery.trim(jQuery("#domain_attach_ext").val())); LoadData(); }); }); jQuery(document).on("submit", "#domain_config_renew", function (event) { event.preventDefault(); $("#domain_attach").val($("#domain_attach_ext").val() + '.' + $("#search_renew_ext").val()); jQuery.post(wcap_ajaxurl, jQuery("#domain_config_renew").serialize(), function (data) { if (data == "OK") { $('.wcap_op_domain_response').html("Domain renewed successfully").show(); } else { $('.wcap_op_domain_response').html(data).show(); } }); }); jQuery(document).on("click", "#manage_domain_auto-renew_form", function (event) { event.preventDefault(); var k = jQuery("#manage_domain_auto-renew_form").serialize(); var view_container = jQuery('.wcap_view_container'); var view_content = view_container.find('.wcap_view_content'); var view_response = view_container.find('.wcap_view_response'); view_response.html(wcap_spinner_icon).show(); jQuery.post(wcap_ajaxurl, k, function (data) { var res = JSON.parse(data); console.log(res); view_response.show().html(res.message); if (res.status === "OK") { view_content.hide(); } }); /* var status = jQuery( this ).attr( "data-value" ); var id = jQuery( this ).attr( "data-id" ); var This = jQuery( this ); jQuery( "#wcap_loading" ).show(); jQuery.post( wcap_ajaxurl, { action: "wcap_requests", what: "update_donotrenew_status", domainid: id, status: status }, function ( data ) { jQuery( "#wcap_loading" ).hide(); if ( data == "OK" ) { if ( status == "1" ) { jQuery( This ).attr( "data-value", "0" ); jQuery( This ).html( "No" ); } else { jQuery( This ).attr( "data-value", "1" ); jQuery( This ).html( "Yes" ); } } else { alert( data ); } } ); */ }); jQuery(document).on("submit", "#addon_form", function (event) { event.preventDefault(); var Data = jQuery(this).serialize(); jQuery.post(wcap_ajaxurl, Data, function (data) { if (data.substr(0, 2) == "OK") { set_url_parameter_value("whmpca", "cart"); LoadData(); } else { alert(data); } }); }); jQuery(document).on("click", "#manage_registrar_lock_form", function (event) { event.preventDefault(); var k = jQuery("#manage_registrar_lock_form").serialize(); var view_container = jQuery('.wcap_view_container'); var view_content = view_container.find('.wcap_view_content'); var view_response = view_container.find('.wcap_view_response'); view_response.html(wcap_spinner_icon).show(); jQuery.post(wcap_ajaxurl, k, function (data) { var res = JSON.parse(data); console.log(res); view_response.show().html(res.message); if (res.status === "OK") { view_content.hide(); } }); }); jQuery(document).on("submit", "#update_dns_form", function (event) { event.preventDefault(); var k = jQuery("#update_dns_form").serialize(); var view_container = jQuery('.wcap_view_container'); var view_content = view_container.find('.wcap_view_content'); var view_response = view_container.find('.wcap_view_response'); view_response.html(wcap_spinner_icon).show(); jQuery.post(wcap_ajaxurl, k, function (data) { var res = JSON.parse(data); console.log(res); view_response.show().html(res.message); if (res.status === "OK") { view_content.hide(); } }); /* jQuery.post( wcap_ajaxurl, jQuery( "#update_dns_form" ).serialize(), function ( data ) { if ( data == "OK" ) { alert( "NS Servers Updated" ); } else { alert( data ); } } ); */ }); jQuery(document).on("submit", "#update_whois_form", function (event) { event.preventDefault(); var k = jQuery("#update_whois_form").serialize(); var view_container = jQuery('.wcap_view_container'); var view_content = view_container.find('.wcap_view_content'); var view_response = view_container.find('.wcap_view_response'); view_response.html(wcap_spinner_icon).show(); jQuery.post(wcap_ajaxurl, k, function (data) { var res = JSON.parse(data); console.log(res); view_response.show().html(res.message); if (res.status === "OK") { view_content.hide(); } }); }); jQuery(document).on("click", "#validate_promotion_code_btn", function (event) { event.preventDefault(); var val = jQuery("#promotion_code").val(); if (val) { jQuery("#validate_promotion_code_btn, #promotion_code").prop("disabled", true); jQuery.post(wcap_ajaxurl, { action: "wcap_requests", "what": "validate_coupon", code: val }, function (data) { jQuery("#validate_promotion_code_btn, #promotion_code").prop("disabled", false); if (data == "OK") { LoadData(); } else { alert(data); } }); } }); jQuery(document).on("click", "#remove_promotion_btn", function (event) { event.preventDefault(); jQuery.post(wcap_ajaxurl, {action: "wcap_requests", what: "remove_coupon"}, function (data) { LoadData(); }) }); jQuery(document).on("click", "#domain_epp_btn", function (event) { event.preventDefault(); var id = jQuery(this).attr("data-id"); jQuery(this).hide(); jQuery("#epp_code_div").html("Getting code ..."); jQuery.post(wcap_ajaxurl, {action: "wcap_requests", what: "epp_code", domainid: id}, function (data) { jQuery("#epp_code_div").html(data); }); }); jQuery(document).on("submit", ".updowngrade_form", function (event) { event.preventDefault(); var k = base64_encode(jQuery(this).serialize()); set_url_parameter_value("whmpca", "updowngrade_final"); set_url_parameter_value("k", k); LoadData(); }); //by farooq //todo: why not in post? jQuery(document).on("submit", "#updowngrade_options_form", function (event) { event.preventDefault(); var k = base64_encode(jQuery(this).serialize()); set_url_parameter_value("whmpca", "updowngrade_final"); set_url_parameter_value("k", k); LoadData(); }); //filter services jQuery(document).on("click", ".wcap_services_filter", function (event) { event.preventDefault(); var status = jQuery(this).attr("data-status"); console.log(status); if (status === "") { jQuery(".data_table tbody tr").show(); } else { jQuery(".data_table tbody tr").hide(); jQuery(".data_table tbody tr").each(function () { console.log(jQuery(this).attr("data-status")); if (jQuery(this).attr("data-status") == status) { jQuery(this).show(); } }); } }); //filter domains //filter domains jQuery(document).on("click", ".wcap_domains_filter", function (event) { event.preventDefault(); var status = jQuery(this).attr("data-status"); console.log(status); if (status === "") { jQuery(".data_table tbody tr").show(); } else { jQuery(".data_table tbody tr").hide(); jQuery(".data_table tbody tr").each(function () { console.log(jQuery(this).attr("data-status")); if (jQuery(this).attr("data-status") == status) { jQuery(this).show(); } }); } }); //filter invoices jQuery(document).on("click", ".wcap_invoices_filter", function (event) { event.preventDefault(); var status = jQuery(this).attr("data-status"); console.log(status); if (status === "") { jQuery(".data_table tbody tr").show(); } else { jQuery(".data_table tbody tr").hide(); jQuery(".data_table tbody tr").each(function () { console.log(jQuery(this).attr("data-status")); if (jQuery(this).attr("data-status") == status) { jQuery(this).show(); } }); } }); //filter tickets jQuery(document).on("click", ".wcap_tickets_filter", function (event) { event.preventDefault(); var status = jQuery(this).attr("data-status"); if (status === "") { jQuery(".data_table tbody tr").show(); } else { jQuery(".data_table tbody tr").hide(); jQuery(".data_table tbody tr").each(function () { console.log(jQuery(this).attr("data-status")); if (jQuery(this).attr("data-status") == status) { jQuery(this).show(); } }); } }); jQuery(document).on("submit", "#wcap_update_password_form1", function (event) { event.preventDefault(); jQuery("#success_message").hide(); jQuery("#error_message").hide(); var k = jQuery("#wcap_update_password_form1").serialize(); jQuery("#new_password, #new_password_2").prop("disabled", true); jQuery.post(wcap_ajaxurl, k, function (data) { jQuery("#new_password, #new_password_2").prop("disabled", false); console.log(data); data = JSON.parse(data); if (data.result == "success") { jQuery("#wcap_update_password_form1").hide(); jQuery("#error_message").hide(); jQuery("#success_message").html("Changes Saved Successfully!").show(); setTimeout(function () { jQuery("#success_message").fadeOut(); var k = "?whmpca=dashboard"; set_new_url(k); LoadData(); }, 5000) } else { jQuery("#wcap_update_password_form1").show(); jQuery("#error_message").html(data.message).show(); jQuery("#success_message").hide(); } }); }); /* @author Shakeel Ahmed Siddiqi <shakeel@shakeel.pk> Used in domain renewel page. */ jQuery(document).on("submit", "#domain_renew_multiple_form", function (event) { event.preventDefault(); if (jQuery("#domain_renew_multiple_form input[name*='domainrenewals[']:checked").length == 0) { alert("Please select 1 or more domains to renew"); return false; } jQuery.post(wcap_ajaxurl, jQuery("#domain_renew_multiple_form").serialize(), function (data) { if (data == "OK") { set_url_parameter_value("whmpca", "cart"); LoadData(); } else { alert(data); } }); }); //Nadeem //Start function jQuery(document).on("click", "#wcap_domain_transfer_submit", function (event) { event.preventDefault(); jQuery("#wcap_transfer_success").hide(); jQuery("#wcap_transfer_unsuccess").hide(); var oldhtml = jQuery(this).html(); jQuery(this).html("<i class='whcom_icon_spinner-1 whcom_animate_spin'></i>"); var i = jQuery("#wcap_transfer_domain").serialize(); jQuery.post(wcap_ajaxurl, i, function (data) { if (is_json(data)) { data = JSON.parse(data); if (data.status == "unavailable") { var k = "action=wcap_requests&what=transferdomain&domain=" + jQuery.trim(jQuery("#domain_name").val()) + "&auth_code=" + jQuery.trim(jQuery("#domain_epp_code").val()); //var k = jQuery("#wcap_transfer_domain").serialize(); jQuery.post(wcap_ajaxurl, k, function (data) { if (data == "OK") { //jQuery(".wcap_op_domain_response").html(data.domain_price_html).show(); jQuery("#wcap_transfer_success").show(); jQuery('#wcap_domain_transfer_submit').html(oldhtml); set_url_parameter_value("whmpca", "confdomains"); setTimeout(LoadData(), 500); } else { alert(data); } }); } else { jQuery("#wcap_transfer_unsuccess").show(); jQuery('#wcap_domain_transfer_submit').html(oldhtml); } } else { alert(data); } }); }); //End function // login_test(); })(jQuery); var t1 = performance.now(); //console.log( "Call to doSomething took " + (t1 - t0) + " milliseconds." ) function login_test() { setTimeout(function () { // Your code here var k = "action=wcap_requests&what=loggedin"; jQuery.post(wcap_ajaxurl, k, function (data) { if (data == "RELOAD") { window.location.reload(); } else { login_test(); } }); }, 5000); } // Order Process functionality starts from here (by Abdul Waheed) var wcap_spinner_icon = '<i class="whcom_icon_spinner-1 whcom_animate_spin"></i>'; var wcap_spinner_block = '<div class="whcom_text_center_xs"><i class="whcom_icon_spinner-1 whcom_animate_spin"></i></div>'; ( function ($) { // Main Repopulating Handler for Product and Domain Configuration pages $(document).on('change', '.wcap_op_main input, .wcap_op_main select', function () { var input = $(this); if (input.hasClass('wcap_op_input')) { wcap_op_update_product_summary(); wcap_op_sp_update_product_summary(); } if (input.hasClass('wcap_op_update_product_options')) { wcap_op_update_product_options(); } if (input.hasClass('wcap_op_update_cart_summaries')) { wcap_op_update_cart_summaries(); } }); // Document Ready functions $(document).on('ready', function () { $('.wcap_op_submit_on_load').submit().removeClass('wcap_op_submit_on_load'); wcap_op_update_cart_summaries(); wcap_op_update_product_summary(); wcap_op_sp_update_product_summary(); }); // Domain only related functions // Check Domain $(document).on('submit', '.wcap_op_check_domain', function (e) { e.preventDefault(); var form = $(this); var submit = form.find('button[type="submit"]'); var submit_html = submit.html(); var response_text = $('.wcap_op_domain_action_response_text'); var response_form = $('.wcap_op_domain_action_response_form'); var data = $(this).serializeArray(); submit.html(whcom_spinner_icon); response_text.empty().hide(); response_form.empty().hide(); jQuery.ajax({ url: wcap_op_ajax.ajax_url, type: 'post', data: data, success: function (response) { var res = JSON.parse(response); console.log(res); submit.html(submit_html); response_text.html(res.message).slideDown(500); if (res.status === "OK") { response_form.html(res.response_form).slideDown(500); } } }); }); // Select Domain action $(document).on('submit', '.wcap_op_domain_action_select', function (e) { e.preventDefault(); var form = $(this); var submit = form.find('button[type="submit"]'); var submit_html = submit.html(); var response_text = $('.wcap_op_domain_action_response_text'); var response_form = $('.wcap_op_domain_action_response_form'); var data = $(this).serializeArray(); submit.html(whcom_spinner_icon); jQuery.ajax({ url: wcap_op_ajax.ajax_url, type: 'post', data: data, success: function (response) { var res = JSON.parse(response); console.log(res); response_text.empty().hide(); response_form.empty().hide(); submit.html(submit_html); response_text.html(res.message).slideDown(500); if (res.status === "OK") { $('.wcap_op_domain_search').slideUp(500).empty(); response_form.html(res.response_form).slideDown(500); } } }); }); // Add Domain to local cart Form $(document).on('submit', '.wcap_op_add_domain_to_cart', function (e) { e.preventDefault(); var $form = $(this); var response_field = $form.find('.wcap_op_domain_config_response_text'); var redirect_url = $form.find('input[name="landing_page"]').val(); var data = $(this).serializeArray(); response_field.show(); response_field.html(whcom_spinner_icon); jQuery.ajax({ url: wcap_op_ajax.ajax_url, type: 'post', data: data, success: function (response) { var res = JSON.parse(response); console.log(res); response_field.text(res.message); if (res.status === "OK") { window.location.href = redirect_url; } response_field.removeClass('alert-info'); } }); }); // Product related functions // Select Product Domain Option $(document).on('change', 'input[name="wcap_op_product_domain_option_selector"]', function () { $('.whcom_product_domain_option_form').hide(); $('#' + $(this).val()).show(); }); // Check Product Domain $(document).on('submit', '.wcap_op_check_product_domain', function (e) { e.preventDefault(); var form = $(this); var response_container = $('.wcap_op_domain_response'); var submit = form.find('button[type="submit"]'); var submit_val = submit.html(); var data = $(this).serializeArray(); if (form.hasClass('domain_already_in_cart')) { var tld = form.find('select[name="domain"] :selected').data('domain-tld'); var domain_type = form.find('select[name="domain"] :selected').data('domain-type'); var cart_index = form.find('select[name="domain"] :selected').data('cart-index'); data.push({'name': 'ext', 'value': tld}); data.push({'name': 'cart_index', 'value': cart_index}); data.push({'name': 'type', 'value': domain_type}); } console.log(data); submit.html(whcom_spinner_icon); response_container.html(whcom_spinner_block); jQuery.ajax({ url: wcap_op_ajax.ajax_url, type: 'post', data: data, success: function (response) { var res = JSON.parse(response); submit.html(submit_val); console.log(res); if (res.status === "OK") { response_container.html(res.domain_attachment_form); if (res.type === 'existing') { $('.wcap_op_attach_product_domain').submit(); } if (form.hasClass('domain_already_in_cart')) { $('.wcap_op_attach_product_domain').submit(); } } else { response_container.html(res.message); } } }); }); // Attach Product Domain $(document).on('submit', '.wcap_op_attach_product_domain', function (e) { e.preventDefault(); var form = $(this); var product_container = $('.wcap_op_product_container'); var product_domain_container = $('.wcap_op_domain_container'); var domain_options_container = $('.wcap_op_product_domain_config_container'); var domain_free_tlds_info = $('.wcap_op_free_tlds'); var submit = form.find('button[type="submit"]'); var submit_val = submit.html(); var data = $(this).serializeArray(); console.log(data); submit.html(whcom_spinner_icon); domain_options_container.slideUp(300).html(''); domain_free_tlds_info.hide(); jQuery.ajax({ url: wcap_op_ajax.ajax_url, type: 'post', data: data, success: function (response) { var res = JSON.parse(response); submit.html(submit_val); console.log(res); if (res.status === "OK") { domain_options_container.html(res.domain_config_form).show(); product_container.slideDown(500); domain_free_tlds_info.show(); product_domain_container.html(res.message).fadeOut(2000); wcap_op_update_product_summary(); } else { form.html(res.message); window.location.reload(); } } }); }); // Add Product to local cart Form $(document).on('submit', '.wcap_op_add_product', function (e) { e.preventDefault(); var $form = $(this); var response_field = $form.find('.wcap_op_response'); var submit_field = $form.find('.wcap_op_product_submit'); var redirect_url = $form.find('input[name="landing_page"]').val(); var data = $(this).serializeArray(); response_field.show(); response_field.html(whcom_spinner_icon); jQuery.ajax({ url: wcap_op_ajax.ajax_url, type: 'post', data: data, success: function (response) { var res = JSON.parse(response); console.log(res); response_field.text(res.message); if (res.status === "OK") { submit_field.find('button').text('Redirecting...'); if (redirect_url !== undefined) { window.location.href = redirect_url; } } else { } response_field.removeClass('alert-info'); } }); }); // Client related functions // Login Form $(document).on('click', '.wcap_op_sp_nav > ul > li', function (e) { $(this).addClass('active').siblings('li').removeClass('active'); }); $(window).on('scroll', function () { var scrollPos = $(document).scrollTop(); $('.wcap_op_sp_nav a.whcom_smooth_scroll').each(function () { var currLink = $(this); var refElement = $(currLink.attr("href")); var topGap = $('.wcap_op_sp_nav').data('scroll-top-gap') || 20; var elemPositionTop = refElement.position().top; var elemPositionBottom = refElement.position().top + parseInt(refElement.outerHeight(true)) - topGap; if (elemPositionTop <= scrollPos && elemPositionBottom > scrollPos) { currLink.closest('li').trigger("click"); } }); }); $(document).on('submit', '.wcap_op_login_form', function (e) { e.preventDefault(); var $form = $(this); var response_field = $form.find('.wcap_op_response'); var data = $(this).serializeArray(); response_field.show(); response_field.html(whcom_spinner_icon); response_field.addClass('alert-info'); jQuery.ajax({ url: wcap_op_ajax.ajax_url, type: 'post', data: data, success: function (response) { console.log(response); var res = JSON.parse(response); console.log(res); response_field.text(res.message); response_field.removeClass('alert-info'); if (res.status === 'OK') { $('.whcom_login_register_response_container').submit(); } } }); }); // Logout Form $(document).on('submit', '.wcap_op_logout_form', function (e) { e.preventDefault(); var data = $(this).serializeArray(); jQuery.ajax({ url: wcap_op_ajax.ajax_url, type: 'post', data: data, success: function (response) { var res = JSON.parse(response); if (res.status === 'OK') { window.location.reload(); } } }); }); // Registration Form $(document).on('submit', '.wcap_op_registration_form', function (e) { e.preventDefault(); var $form = $(this); var response_field = $form.find('.wcap_op_response'); var data = $(this).serializeArray(); response_field.html(whcom_spinner_icon).show().removeClass('whcom_alert whcom_alert_success whcom_alert_danger'); jQuery.ajax({ url: wcap_op_ajax.ajax_url, type: 'post', data: data, success: function (response) { var res = JSON.parse(response); console.log(res); response_field.html(res.message); if (res.status === "OK") { $('.whcom_login_register_response_container').submit(); response_field.addClass('whcom_alert whcom_alert_success'); } else { response_field.addClass('whcom_alert whcom_alert_danger'); } } }); }); // Cart related functions // Delete cart item $(document).on('click', '.wcap_op_delete_cart_item', function (e) { e.preventDefault(); var confirm_delete = confirm('Are you sure you want to remove this item from cart?'); if (!confirm_delete) { console.log('not deleting'); return; } console.log('deleting'); var button = $(this); var data = {}; data.action = "wcap_op_delete_cart_item"; data.cart_index = button.data('cart-index'); button.html(whcom_spinner_icon); jQuery.ajax({ url: wcap_op_ajax.ajax_url, type: 'post', data: data, success: function (response) { var res = JSON.parse(response); console.log(res); if (res.status === "OK") { wcap_op_update_cart_summaries(); } else { alert(res.message); //window.location.reload(); } } }); }); // Empty Cart $(document).on('submit', '.wcap_op_reset_cart_form', function (e) { e.preventDefault(); var $form = $(this); var response_field = $form.find('.wcap_op_response'); var confirm_string = $form.find('input[name="confirm_string"]').val(); var confirm = window.confirm(confirm_string); if (confirm === true) { var data = $(this).serializeArray(); response_field.show(); response_field.html(whcom_spinner_icon); response_field.addClass('alert-info'); jQuery.ajax({ url: wcap_op_ajax.ajax_url, type: 'post', data: data, success: function (response) { var res = JSON.parse(response); console.log(res); response_field.text(res.message); if (res.status === "OK") { window.location.reload(); } } }); } }); // Submit Order $(document).on('submit', '.wcap_op_submit_order', function (e) { e.preventDefault(); var form = $(this); var response_text = form.find('.wcap_op_response'); var response_form = $('.whcom_checkout_form'); var submit_container = form.find('.wcap_op_submit_container'); var submit = submit_container.find('button'); var submit_val = submit.html(); var data = $(this).serializeArray(); submit.html(whcom_spinner_icon); jQuery.ajax({ url: wcap_op_ajax.ajax_url, type: 'post', data: data, success: function (response) { var res = JSON.parse(response); console.log(res); submit.html(submit_val); response_text.show(); if (res.status === "OK") { if (res.show_cc === 'yes') { response_form.html(res.response_form); } else { $('.whcom_page_heading').empty().removeClass('whcom_page_heading').addClass('whcom_margin_bottom_45'); response_form.html(res.redirect_link).addClass('whcom_text_center whcom_form_field').append(res.invoice_link); $('.wcap_op_view_invoice_button').trigger('click'); } } else { response_text.text(res.message); } } }); }); // Capture Payment $(document).on('submit', '.wcap_op_capture_invoice_payment', function (e) { e.preventDefault(); var form = $(this); var submit = form.find('button[type="submit"]'); var submit_val = submit.html(); var response_text = form.find('.wcap_op_response'); var response_form = $('.whcom_checkout_form'); var data = $(this).serializeArray(); submit.html(whcom_spinner_icon); jQuery.ajax({ url: wcap_op_ajax.ajax_url, type: 'post', data: data, success: function (response) { var res = JSON.parse(response); console.log(res); submit.html(submit_val); if (res.status === "OK") { response_form.html(res.response_html); } else { response_text.html(res.message); } } }); }); // Apply Promo Code $(document).on('submit', '.wcap_op_apply_promo_code_form', function (e) { e.preventDefault(); var $form = $(this); var response_field = $('.wcap_op_promo_response'); response_field.slideUp().removeClass().addClass('wcap_op_promo_response whcom_alert'); var data = $(this).serializeArray(); jQuery.ajax({ url: wcap_op_ajax.ajax_url, type: 'post', data: data, success: function (response) { var res = JSON.parse(response); console.log(res); response_field.text(res.message).slideDown(); if (res.status === "OK") { response_field.addClass('whcom_alert_success'); window.location.reload(); } else { response_field.addClass('whcom_alert_info'); } } }); }); // Remove Promo Code $(document).on('submit', '.wcap_op_remove_promo_code_form', function (e) { e.preventDefault(); var $form = $(this); var response_field = $('.wcap_op_promo_response'); response_field.slideUp().removeClass().addClass('wcap_op_promo_response whcom_alert'); var data = $(this).serializeArray(); jQuery.ajax({ url: wcap_op_ajax.ajax_url, type: 'post', data: data, success: function (response) { var res = JSON.parse(response); console.log(res); response_field.text(res.message).slideDown(); if (res.status === "OK") { response_field.addClass('whcom_alert_success'); window.location.reload(); } else { response_field.addClass('whcom_alert_info'); } } }); }); window.wcap_op_update_cart_summaries = function () { var side_summaries = []; var short_summaries = []; var buttons_summaries = []; var detailed_summaries = []; $('.wcap_op_universal_cart_summary_side').each(function () { $(this).html(whcom_spinner_block); side_summaries.push($(this)); }); $('.wcap_op_universal_cart_summary_short').each(function () { $(this).html(whcom_spinner_block); short_summaries.push($(this)); }); $('.wcap_op_universal_cart_summary_button').each(function () { $(this).html(whcom_spinner_block); buttons_summaries.push($(this)); }); $('.wcap_op_universal_cart_summary_detailed').each(function () { $(this).html(whcom_spinner_block); detailed_summaries.push($(this)); }); if (( buttons_summaries.length + short_summaries.length + detailed_summaries.length ) > 0) { console.log('Calculating Summaries'); var data = {}; data.action = "wcap_op_generate_cart_summaries"; data.cart_index = $('input[name="cart_index"]').val(); data.product_id = $('input[name="pid"]').val(); data.billingcycle = $('[name="billingcycle"]').val(); $.ajax({ url: wcap_op_ajax.ajax_url, type: 'post', data: data, success: function (response) { var res = JSON.parse(response); console.log(res); if (res.status === "OK") { if (side_summaries.length) { $(side_summaries).each(function () { $(this).html(res.side); }); } if (short_summaries.length) { $(short_summaries).each(function () { $(this).html(res.short) }); } if (buttons_summaries.length) { $(buttons_summaries).each(function () { $(this).html(res.button) }); } if (detailed_summaries.length) { $(detailed_summaries).each(function () { $(this).html(res.detailed) }); } if (res.total_items > 0) { $('.whcom_universal_checkout_button').prop('disabled', false); } else { $('.whcom_universal_checkout_button').prop('disabled', true); } $('.whcom_summary_sidebar').whcom_sticky({ 'parent': '.whcom_main', 'offset_top': 80, 'recalc_every': 1 }); } else { } } }); } }; window.wcap_op_update_product_options = function () { var options_container = $('.wcap_op_product_options_container'); if (options_container[0]) { var data = {}; data.action = "wcap_op_get_product_config_options"; data.cart_index = $('input[name="cart_index"]').val(); data.product_id = $('input[name="pid"]').val(); data.billingcycle = $('[name="billingcycle"]').val(); options_container.html(whcom_spinner_block); $.ajax({ url: wcap_op_ajax.ajax_url, type: 'post', data: data, success: function (response) { var res = JSON.parse(response); console.log(res); if (res.status === "OK") { options_container.html(res.options_html); } else { } } }); } }; window.wcap_op_update_product_summary = function () { var prd_form = $('form.wcap_op_add_product'); if (prd_form[0]) { var prd_summary = $('.wcap_op_summary_sidebar'); var prd_summary_spinner = $('.wcap_op_product_summary .whcom_icon_spinner-1'); var prd_submit = $('.wcap_op_product_submit button'); prd_submit.prop('disabled', true); prd_summary_spinner.show(); if (prd_form) { var data = prd_form.serializeArray(); data.push({'name': 'action', 'value': 'wcap_op_generate_current_product_summery'}); $.ajax({ url: wcap_op_ajax.ajax_url, type: 'post', data: data, success: function (response) { var res = JSON.parse(response); console.log(res); if (res.status === "OK") { prd_submit.prop('disabled', false); prd_summary_spinner.fadeOut(500); prd_summary.html(res.summary_html.side); } else { prd_summary.html(res.message); } } }); } } }; window.wcap_op_sp_update_product_summary = function () { var prd_form = $('form.wcap_op_sp_add_product'); if (prd_form[0]) { var prd_summary = $('.wcap_op_sp_order_summary'); var prd_summary_spinner = $('.wcap_op_sp_product_summary_spinner'); var prd_submit = $('.wcap_op_sp_product_submit'); prd_submit.hide(); prd_summary_spinner.show(); if (prd_form) { var data = prd_form.serializeArray(); data.push({'name': 'action', 'value': 'wcap_op_sp_process'}); data.push({'name': 'wcap_op_sp_what', 'value': 'wcap_op_sp_generate_summary'}); $.ajax({ url: wcap_op_ajax.ajax_url, type: 'post', data: data, success: function (response) { var res = JSON.parse(response); console.log(res); if (res.status === "OK") { prd_submit.show(); prd_summary_spinner.fadeOut(500); prd_summary.html(res.summary_html.side); if (res.summary_html.free_domain) { console.log('Free Domain'); $('[name="regperiod"]').prop('disabled', true).find('option').each(function () { $(this).prop('selected', true); return false; }); } else { $('[name="regperiod"]').prop('disabled', false); } if (res.summary_html.no_options) { $('.wcap_op_sp_product_no_options').show(); } else { $('.wcap_op_sp_product_no_options').hide(); } } else { prd_summary.html(res.message); } } }); } } }; }(jQuery) ); assets/js/dataTables.responsive.js 0000644 00000062336 14736103451 0013276 0 ustar 00 /*! Responsive 1.0.4 * 2014 SpryMedia Ltd - datatables.net/license */ /** * @summary Responsive * @description Responsive tables plug-in for DataTables * @version 1.0.4 * @file dataTables.responsive.js * @author SpryMedia Ltd (www.sprymedia.co.uk) * @contact www.sprymedia.co.uk/contact * @copyright Copyright 2014 SpryMedia Ltd. * * This source file is free software, available under the following license: * MIT license - http://datatables.net/license/mit * * This source file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details. * * For details please refer to: http://www.datatables.net */ (function(window, document, undefined) { var factory = function( $, DataTable ) { "use strict"; /** * Responsive is a plug-in for the DataTables library that makes use of * DataTables' ability to change the visibility of columns, changing the * visibility of columns so the displayed columns fit into the table container. * The end result is that complex tables will be dynamically adjusted to fit * into the viewport, be it on a desktop, tablet or mobile browser. * * Responsive for DataTables has two modes of operation, which can used * individually or combined: * * * Class name based control - columns assigned class names that match the * breakpoint logic can be shown / hidden as required for each breakpoint. * * Automatic control - columns are automatically hidden when there is no * room left to display them. Columns removed from the right. * * In additional to column visibility control, Responsive also has built into * options to use DataTables' child row display to show / hide the information * from the table that has been hidden. There are also two modes of operation * for this child row display: * * * Inline - when the control element that the user can use to show / hide * child rows is displayed inside the first column of the table. * * Column - where a whole column is dedicated to be the show / hide control. * * Initialisation of Responsive is performed by: * * * Adding the class `responsive` or `dt-responsive` to the table. In this case * Responsive will automatically be initialised with the default configuration * options when the DataTable is created. * * Using the `responsive` option in the DataTables configuration options. This * can also be used to specify the configuration options, or simply set to * `true` to use the defaults. * * @class * @param {object} settings DataTables settings object for the host table * @param {object} [opts] Configuration options * @requires jQuery 1.7+ * @requires DataTables 1.10.1+ * * @example * $('#example').DataTable( { * responsive: true * } ); * } ); */ var Responsive = function ( settings, opts ) { // Sanity check that we are using DataTables 1.10 or newer if ( ! DataTable.versionCheck || ! DataTable.versionCheck( '1.10.1' ) ) { throw 'DataTables Responsive requires DataTables 1.10.1 or newer'; } this.s = { dt: new DataTable.Api( settings ), columns: [] }; // Check if responsive has already been initialised on this table if ( this.s.dt.settings()[0].responsive ) { return; } // details is an object, but for simplicity the user can give it as a string if ( opts && typeof opts.details === 'string' ) { opts.details = { type: opts.details }; } this.c = $.extend( true, {}, Responsive.defaults, DataTable.defaults.responsive, opts ); settings.responsive = this; this._constructor(); }; Responsive.prototype = { /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Constructor */ /** * Initialise the Responsive instance * * @private */ _constructor: function () { var that = this; var dt = this.s.dt; dt.settings()[0]._responsive = this; // Use DataTables' private throttle function to avoid processor thrashing $(window).on( 'resize.dtr orientationchange.dtr', dt.settings()[0].oApi._fnThrottle( function () { that._resize(); } ) ); // Destroy event handler dt.on( 'destroy.dtr', function () { $(window).off( 'resize.dtr orientationchange.dtr draw.dtr' ); } ); // Reorder the breakpoints array here in case they have been added out // of order this.c.breakpoints.sort( function (a, b) { return a.width < b.width ? 1 : a.width > b.width ? -1 : 0; } ); // Determine which columns are already hidden, and should therefore // remain hidden. TODO - should this be done? See thread 22677 // // this.s.alwaysHidden = dt.columns(':hidden').indexes(); this._classLogic(); this._resizeAuto(); // First pass - draw the table for the current viewport size this._resize(); // Details handler var details = this.c.details; if ( details.type ) { that._detailsInit(); this._detailsVis(); dt.on( 'column-visibility.dtr', function () { that._detailsVis(); } ); // Redraw the details box on each draw. This is used until // DataTables implements a native `updated` event for rows dt.on( 'draw.dtr', function () { dt.rows().iterator( 'row', function ( settings, idx ) { var row = dt.row( idx ); if ( row.child.isShown() ) { var info = that.c.details.renderer( dt, idx ); row.child( info, 'child' ).show(); } } ); } ); $(dt.table().node()).addClass( 'dtr-'+details.type ); } }, /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Private methods */ /** * Calculate the visibility for the columns in a table for a given * breakpoint. The result is pre-determined based on the class logic if * class names are used to control all columns, but the width of the table * is also used if there are columns which are to be automatically shown * and hidden. * * @param {string} breakpoint Breakpoint name to use for the calculation * @return {array} Array of boolean values initiating the visibility of each * column. * @private */ _columnsVisiblity: function ( breakpoint ) { var dt = this.s.dt; var columns = this.s.columns; var i, ien; // Class logic - determine which columns are in this breakpoint based // on the classes. If no class control (i.e. `auto`) then `-` is used // to indicate this to the rest of the function var display = $.map( columns, function ( col ) { return col.auto && col.minWidth === null ? false : col.auto === true ? '-' : $.inArray( breakpoint, col.includeIn ) !== -1; } ); // Auto column control - first pass: how much width is taken by the // ones that must be included from the non-auto columns var requiredWidth = 0; for ( i=0, ien=display.length ; i<ien ; i++ ) { if ( display[i] === true ) { requiredWidth += columns[i].minWidth; } } // Second pass, use up any remaining width for other columns var widthAvailable = dt.table().container().offsetWidth; var usedWidth = widthAvailable - requiredWidth; // Control column needs to always be included. This makes it sub- // optimal in terms of using the available with, but to stop layout // thrashing or overflow. Also we need to account for the control column // width first so we know how much width is available for the other // columns, since the control column might not be the first one shown for ( i=0, ien=display.length ; i<ien ; i++ ) { if ( columns[i].control ) { usedWidth -= columns[i].minWidth; } } // Allow columns to be shown (counting from the left) until we run out // of room for ( i=0, ien=display.length ; i<ien ; i++ ) { if ( display[i] === '-' && ! columns[i].control ) { display[i] = usedWidth - columns[i].minWidth < 0 ? false : true; usedWidth -= columns[i].minWidth; } } // Determine if the 'control' column should be shown (if there is one). // This is the case when there is a hidden column (that is not the // control column). The two loops look inefficient here, but they are // trivial and will fly through. We need to know the outcome from the // first , before the action in the second can be taken var showControl = false; for ( i=0, ien=columns.length ; i<ien ; i++ ) { if ( ! columns[i].control && ! columns[i].never && ! display[i] ) { showControl = true; break; } } for ( i=0, ien=columns.length ; i<ien ; i++ ) { if ( columns[i].control ) { display[i] = showControl; } } // Finally we need to make sure that there is at least one column that // is visible if ( $.inArray( true, display ) === -1 ) { display[0] = true; } return display; }, /** * Create the internal `columns` array with information about the columns * for the table. This includes determining which breakpoints the column * will appear in, based upon class names in the column, which makes up the * vast majority of this method. * * @private */ _classLogic: function () { var that = this; var calc = {}; var breakpoints = this.c.breakpoints; var columns = this.s.dt.columns().eq(0).map( function (i) { var className = this.column(i).header().className; return { className: className, includeIn: [], auto: false, control: false, never: className.match(/\bnever\b/) ? true : false }; } ); // Simply add a breakpoint to `includeIn` array, ensuring that there are // no duplicates var add = function ( colIdx, name ) { var includeIn = columns[ colIdx ].includeIn; if ( $.inArray( name, includeIn ) === -1 ) { includeIn.push( name ); } }; var column = function ( colIdx, name, operator, matched ) { var size, i, ien; if ( ! operator ) { columns[ colIdx ].includeIn.push( name ); } else if ( operator === 'max-' ) { // Add this breakpoint and all smaller size = that._find( name ).width; for ( i=0, ien=breakpoints.length ; i<ien ; i++ ) { if ( breakpoints[i].width <= size ) { add( colIdx, breakpoints[i].name ); } } } else if ( operator === 'min-' ) { // Add this breakpoint and all larger size = that._find( name ).width; for ( i=0, ien=breakpoints.length ; i<ien ; i++ ) { if ( breakpoints[i].width >= size ) { add( colIdx, breakpoints[i].name ); } } } else if ( operator === 'not-' ) { // Add all but this breakpoint (xxx need extra information) for ( i=0, ien=breakpoints.length ; i<ien ; i++ ) { if ( breakpoints[i].name.indexOf( matched ) === -1 ) { add( colIdx, breakpoints[i].name ); } } } }; // Loop over each column and determine if it has a responsive control // class columns.each( function ( col, i ) { var classNames = col.className.split(' '); var hasClass = false; // Split the class name up so multiple rules can be applied if needed for ( var k=0, ken=classNames.length ; k<ken ; k++ ) { var className = $.trim( classNames[k] ); if ( className === 'all' ) { // Include in all hasClass = true; col.includeIn = $.map( breakpoints, function (a) { return a.name; } ); return; } else if ( className === 'none' || className === 'never' ) { // Include in none (default) and no auto hasClass = true; return; } else if ( className === 'control' ) { // Special column that is only visible, when one of the other // columns is hidden. This is used for the details control hasClass = true; col.control = true; return; } $.each( breakpoints, function ( j, breakpoint ) { // Does this column have a class that matches this breakpoint? var brokenPoint = breakpoint.name.split('-'); var re = new RegExp( '(min\\-|max\\-|not\\-)?('+brokenPoint[0]+')(\\-[_a-zA-Z0-9])?' ); var match = className.match( re ); if ( match ) { hasClass = true; if ( match[2] === brokenPoint[0] && match[3] === '-'+brokenPoint[1] ) { // Class name matches breakpoint name fully column( i, breakpoint.name, match[1], match[2]+match[3] ); } else if ( match[2] === brokenPoint[0] && ! match[3] ) { // Class name matched primary breakpoint name with no qualifier column( i, breakpoint.name, match[1], match[2] ); } } } ); } // If there was no control class, then automatic sizing is used if ( ! hasClass ) { col.auto = true; } } ); this.s.columns = columns; }, /** * Initialisation for the details handler * * @private */ _detailsInit: function () { var that = this; var dt = this.s.dt; var details = this.c.details; // The inline type always uses the first child as the target if ( details.type === 'inline' ) { details.target = 'td:first-child'; } // type.target can be a string jQuery selector or a column index var target = details.target; var selector = typeof target === 'string' ? target : 'td'; // Click handler to show / hide the details rows when they are available $( dt.table().body() ).on( 'click', selector, function (e) { // If the table is not collapsed (i.e. there is no hidden columns) // then take no action if ( ! $(dt.table().node()).hasClass('collapsed' ) ) { return; } // Check that the row is actually a DataTable's controlled node if ( ! dt.row( $(this).closest('tr') ).length ) { return; } // For column index, we determine if we should act or not in the // handler - otherwise it is already okay if ( typeof target === 'number' ) { var targetIdx = target < 0 ? dt.columns().eq(0).length + target : target; if ( dt.cell( this ).index().column !== targetIdx ) { return; } } // $().closest() includes itself in its check var row = dt.row( $(this).closest('tr') ); if ( row.child.isShown() ) { row.child( false ); $( row.node() ).removeClass( 'parent' ); } else { var info = that.c.details.renderer( dt, row[0] ); row.child( info, 'child' ).show(); $( row.node() ).addClass( 'parent' ); } } ); }, /** * Update the child rows in the table whenever the column visibility changes * * @private */ _detailsVis: function () { var that = this; var dt = this.s.dt; // Find how many columns are hidden var hiddenColumns = dt.columns().indexes().filter( function ( idx ) { var col = dt.column( idx ); if ( col.visible() ) { return null; } // Only counts as hidden if it doesn't have the `never` class return $( col.header() ).hasClass( 'never' ) ? null : idx; } ); var haveHidden = true; if ( hiddenColumns.length === 0 || ( hiddenColumns.length === 1 && this.s.columns[ hiddenColumns[0] ].control ) ) { haveHidden = false; } if ( haveHidden ) { // Got hidden columns $( dt.table().node() ).addClass('collapsed'); // Show all existing child rows dt.rows().eq(0).each( function (idx) { var row = dt.row( idx ); if ( row.child() ) { var info = that.c.details.renderer( dt, row[0] ); // The renderer can return false to have no child row if ( info === false ) { row.child.hide(); } else { row.child( info, 'child' ).show(); } } } ); } else { // No hidden columns $( dt.table().node() ).removeClass('collapsed'); // Hide all existing child rows dt.rows().eq(0).each( function (idx) { dt.row( idx ).child.hide(); } ); } }, /** * Find a breakpoint object from a name * @param {string} name Breakpoint name to find * @return {object} Breakpoint description object */ _find: function ( name ) { var breakpoints = this.c.breakpoints; for ( var i=0, ien=breakpoints.length ; i<ien ; i++ ) { if ( breakpoints[i].name === name ) { return breakpoints[i]; } } }, /** * Alter the table display for a resized viewport. This involves first * determining what breakpoint the window currently is in, getting the * column visibilities to apply and then setting them. * * @private */ _resize: function () { var dt = this.s.dt; var width = $(window).width(); var breakpoints = this.c.breakpoints; var breakpoint = breakpoints[0].name; // Determine what breakpoint we are currently at for ( var i=breakpoints.length-1 ; i>=0 ; i-- ) { if ( width <= breakpoints[i].width ) { breakpoint = breakpoints[i].name; break; } } // Show the columns for that break point var columns = this._columnsVisiblity( breakpoint ); dt.columns().eq(0).each( function ( colIdx, i ) { dt.column( colIdx ).visible( columns[i] ); } ); }, /** * Determine the width of each column in the table so the auto column hiding * has that information to work with. This method is never going to be 100% * perfect since column widths can change slightly per page, but without * seriously compromising performance this is quite effective. * * @private */ _resizeAuto: function () { var dt = this.s.dt; var columns = this.s.columns; // Are we allowed to do auto sizing? if ( ! this.c.auto ) { return; } // Are there any columns that actually need auto-sizing, or do they all // have classes defined if ( $.inArray( true, $.map( columns, function (c) { return c.auto; } ) ) === -1 ) { return; } // Clone the table with the current data in it var tableWidth = dt.table().node().offsetWidth; var columnWidths = dt.columns; var clonedTable = dt.table().node().cloneNode( false ); var clonedHeader = $( dt.table().header().cloneNode( false ) ).appendTo( clonedTable ); var clonedBody = $( dt.table().body().cloneNode( false ) ).appendTo( clonedTable ); // This is a bit slow, but we need to get a clone of each row that // includes all columns. As such, try to do this as little as possible. dt.rows( { page: 'current' } ).indexes().flatten().each( function ( idx ) { var clone = dt.row( idx ).node().cloneNode( true ); if ( dt.columns( ':hidden' ).flatten().length ) { $(clone).append( dt.cells( idx, ':hidden' ).nodes().to$().clone() ); } $(clone).appendTo( clonedBody ); } ); var cells = dt.columns().header().to$().clone( false ); $('<tr/>') .append( cells ) .appendTo( clonedHeader ); var inserted = $('<div/>') .css( { width: 1, height: 1, overflow: 'hidden' } ) .append( clonedTable ) .insertBefore( dt.table().node() ); // The cloned header now contains the smallest that each column can be dt.columns().eq(0).each( function ( idx ) { columns[idx].minWidth = cells[ idx ].offsetWidth || 0; } ); inserted.remove(); } }; /** * List of default breakpoints. Each item in the array is an object with two * properties: * * * `name` - the breakpoint name. * * `width` - the breakpoint width * * @name Responsive.breakpoints * @static */ Responsive.breakpoints = [ { name: 'desktop', width: Infinity }, { name: 'tablet-l', width: 1024 }, { name: 'tablet-p', width: 768 }, { name: 'mobile-l', width: 480 }, { name: 'mobile-p', width: 320 } ]; /** * Responsive default settings for initialisation * * @namespace * @name Responsive.defaults * @static */ Responsive.defaults = { /** * List of breakpoints for the instance. Note that this means that each * instance can have its own breakpoints. Additionally, the breakpoints * cannot be changed once an instance has been creased. * * @type {Array} * @default Takes the value of `Responsive.breakpoints` */ breakpoints: Responsive.breakpoints, /** * Enable / disable auto hiding calculations. It can help to increase * performance slightly if you disable this option, but all columns would * need to have breakpoint classes assigned to them * * @type {Boolean} * @default `true` */ auto: true, /** * Details control. If given as a string value, the `type` property of the * default object is set to that value, and the defaults used for the rest * of the object - this is for ease of implementation. * * The object consists of the following properties: * * * `renderer` - function that is called for display of the child row data. * The default function will show the data from the hidden columns * * `target` - Used as the selector for what objects to attach the child * open / close to * * `type` - `false` to disable the details display, `inline` or `column` * for the two control types * * @type {Object|string} */ details: { renderer: function ( api, rowIdx ) { var data = api.cells( rowIdx, ':hidden' ).eq(0).map( function ( cell ) { var header = $( api.column( cell.column ).header() ); var idx = api.cell( cell ).index(); if ( header.hasClass( 'control' ) || header.hasClass( 'never' ) ) { return ''; } // Use a non-public DT API method to render the data for display // This needs to be updated when DT adds a suitable method for // this type of data retrieval var dtPrivate = api.settings()[0]; var cellData = dtPrivate.oApi._fnGetCellData( dtPrivate, idx.row, idx.column, 'display' ); var title = header.text(); if ( title ) { title = title + ':'; } return '<li data-dtr-index="'+idx.column+'">'+ '<span class="dtr-title">'+ title+ '</span> '+ '<span class="dtr-data">'+ cellData+ '</span>'+ '</li>'; } ).toArray().join(''); return data ? $('<ul data-dtr-index="'+rowIdx+'"/>').append( data ) : false; }, target: 0, type: 'inline' } }; /* * API */ var Api = $.fn.dataTable.Api; // Doesn't do anything - work around for a bug in DT... Not documented Api.register( 'responsive()', function () { return this; } ); Api.register( 'responsive.index()', function ( li ) { li = $(li); return { column: li.data('dtr-index'), row: li.parent().data('dtr-index') }; } ); Api.register( 'responsive.rebuild()', function () { return this.iterator( 'table', function ( ctx ) { if ( ctx._responsive ) { ctx._responsive._classLogic(); } } ); } ); Api.register( 'responsive.recalc()', function () { return this.iterator( 'table', function ( ctx ) { if ( ctx._responsive ) { ctx._responsive._resizeAuto(); ctx._responsive._resize(); } } ); } ); /** * Version information * * @name Responsive.version * @static */ Responsive.version = '1.0.4'; $.fn.dataTable.Responsive = Responsive; $.fn.DataTable.Responsive = Responsive; // Attach a listener to the document which listens for DataTables initialisation // events so we can automatically initialise $(document).on( 'init.dt.dtr', function (e, settings, json) { if ( $(settings.nTable).hasClass( 'responsive' ) || $(settings.nTable).hasClass( 'dt-responsive' ) || //settings.oInit.responsive || DataTable.defaults.responsive ) { var init = settings.oInit.responsive; if ( init !== false ) { new Responsive( settings, $.isPlainObject( init ) ? init : {} ); } } } ); return Responsive; }; // /factory // Define as an AMD module if possible if ( typeof define === 'function' && define.amd ) { define( ['jquery', 'datatables'], factory ); } else if ( typeof exports === 'object' ) { // Node/CommonJS factory( require('jquery'), require('datatables') ); } else if ( jQuery && !jQuery.fn.dataTable.Responsive ) { // Otherwise simply initialise as normal, stopping multiple evaluation factory( jQuery, jQuery.fn.dataTable ); } })(window, document); assets/js/index.php 0000644 00000000000 14736103451 0010274 0 ustar 00 assets/js/simplemde.js 0000644 00002231405 14736103451 0011012 0 ustar 00 /** * simplemde v1.11.2 * Copyright Next Step Webs, Inc. * @link https://github.com/NextStepWebs/simplemde-markdown-editor * @license MIT */ (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.SimpleMDE = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ 'use strict' exports.toByteArray = toByteArray exports.fromByteArray = fromByteArray var lookup = [] var revLookup = [] var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array function init () { var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' for (var i = 0, len = code.length; i < len; ++i) { lookup[i] = code[i] revLookup[code.charCodeAt(i)] = i } revLookup['-'.charCodeAt(0)] = 62 revLookup['_'.charCodeAt(0)] = 63 } init() function toByteArray (b64) { var i, j, l, tmp, placeHolders, arr var len = b64.length if (len % 4 > 0) { throw new Error('Invalid string. Length must be a multiple of 4') } // the number of equal signs (place holders) // if there are two placeholders, than the two characters before it // represent one byte // if there is only one, then the three characters before it represent 2 bytes // this is just a cheap hack to not do indexOf twice placeHolders = b64[len - 2] === '=' ? 2 : b64[len - 1] === '=' ? 1 : 0 // base64 is 4/3 + up to two characters of the original data arr = new Arr(len * 3 / 4 - placeHolders) // if there are placeholders, only get up to the last complete 4 chars l = placeHolders > 0 ? len - 4 : len var L = 0 for (i = 0, j = 0; i < l; i += 4, j += 3) { tmp = (revLookup[b64.charCodeAt(i)] << 18) | (revLookup[b64.charCodeAt(i + 1)] << 12) | (revLookup[b64.charCodeAt(i + 2)] << 6) | revLookup[b64.charCodeAt(i + 3)] arr[L++] = (tmp >> 16) & 0xFF arr[L++] = (tmp >> 8) & 0xFF arr[L++] = tmp & 0xFF } if (placeHolders === 2) { tmp = (revLookup[b64.charCodeAt(i)] << 2) | (revLookup[b64.charCodeAt(i + 1)] >> 4) arr[L++] = tmp & 0xFF } else if (placeHolders === 1) { tmp = (revLookup[b64.charCodeAt(i)] << 10) | (revLookup[b64.charCodeAt(i + 1)] << 4) | (revLookup[b64.charCodeAt(i + 2)] >> 2) arr[L++] = (tmp >> 8) & 0xFF arr[L++] = tmp & 0xFF } return arr } function tripletToBase64 (num) { return lookup[num >> 18 & 0x3F] + lookup[num >> 12 & 0x3F] + lookup[num >> 6 & 0x3F] + lookup[num & 0x3F] } function encodeChunk (uint8, start, end) { var tmp var output = [] for (var i = start; i < end; i += 3) { tmp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2]) output.push(tripletToBase64(tmp)) } return output.join('') } function fromByteArray (uint8) { var tmp var len = uint8.length var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes var output = '' var parts = [] var maxChunkLength = 16383 // must be multiple of 3 // go through the array every three bytes, we'll deal with trailing stuff later for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) { parts.push(encodeChunk(uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength))) } // pad the end with zeros, but make sure to not forget the extra bytes if (extraBytes === 1) { tmp = uint8[len - 1] output += lookup[tmp >> 2] output += lookup[(tmp << 4) & 0x3F] output += '==' } else if (extraBytes === 2) { tmp = (uint8[len - 2] << 8) + (uint8[len - 1]) output += lookup[tmp >> 10] output += lookup[(tmp >> 4) & 0x3F] output += lookup[(tmp << 2) & 0x3F] output += '=' } parts.push(output) return parts.join('') } },{}],2:[function(require,module,exports){ },{}],3:[function(require,module,exports){ (function (global){ /*! * The buffer module from node.js, for the browser. * * @author Feross Aboukhadijeh <feross@feross.org> <http://feross.org> * @license MIT */ /* eslint-disable no-proto */ 'use strict' var base64 = require('base64-js') var ieee754 = require('ieee754') var isArray = require('isarray') exports.Buffer = Buffer exports.SlowBuffer = SlowBuffer exports.INSPECT_MAX_BYTES = 50 /** * If `Buffer.TYPED_ARRAY_SUPPORT`: * === true Use Uint8Array implementation (fastest) * === false Use Object implementation (most compatible, even IE6) * * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+, * Opera 11.6+, iOS 4.2+. * * Due to various browser bugs, sometimes the Object implementation will be used even * when the browser supports typed arrays. * * Note: * * - Firefox 4-29 lacks support for adding new properties to `Uint8Array` instances, * See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438. * * - Chrome 9-10 is missing the `TypedArray.prototype.subarray` function. * * - IE10 has a broken `TypedArray.prototype.subarray` function which returns arrays of * incorrect length in some situations. * We detect these buggy browsers and set `Buffer.TYPED_ARRAY_SUPPORT` to `false` so they * get the Object implementation, which is slower but behaves correctly. */ Buffer.TYPED_ARRAY_SUPPORT = global.TYPED_ARRAY_SUPPORT !== undefined ? global.TYPED_ARRAY_SUPPORT : typedArraySupport() /* * Export kMaxLength after typed array support is determined. */ exports.kMaxLength = kMaxLength() function typedArraySupport () { try { var arr = new Uint8Array(1) arr.foo = function () { return 42 } return arr.foo() === 42 && // typed array instances can be augmented typeof arr.subarray === 'function' && // chrome 9-10 lack `subarray` arr.subarray(1, 1).byteLength === 0 // ie10 has broken `subarray` } catch (e) { return false } } function kMaxLength () { return Buffer.TYPED_ARRAY_SUPPORT ? 0x7fffffff : 0x3fffffff } function createBuffer (that, length) { if (kMaxLength() < length) { throw new RangeError('Invalid typed array length') } if (Buffer.TYPED_ARRAY_SUPPORT) { // Return an augmented `Uint8Array` instance, for best performance that = new Uint8Array(length) that.__proto__ = Buffer.prototype } else { // Fallback: Return an object instance of the Buffer class if (that === null) { that = new Buffer(length) } that.length = length } return that } /** * The Buffer constructor returns instances of `Uint8Array` that have their * prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of * `Uint8Array`, so the returned instances will have all the node `Buffer` methods * and the `Uint8Array` methods. Square bracket notation works as expected -- it * returns a single octet. * * The `Uint8Array` prototype remains unmodified. */ function Buffer (arg, encodingOrOffset, length) { if (!Buffer.TYPED_ARRAY_SUPPORT && !(this instanceof Buffer)) { return new Buffer(arg, encodingOrOffset, length) } // Common case. if (typeof arg === 'number') { if (typeof encodingOrOffset === 'string') { throw new Error( 'If encoding is specified then the first argument must be a string' ) } return allocUnsafe(this, arg) } return from(this, arg, encodingOrOffset, length) } Buffer.poolSize = 8192 // not used by this implementation // TODO: Legacy, not needed anymore. Remove in next major version. Buffer._augment = function (arr) { arr.__proto__ = Buffer.prototype return arr } function from (that, value, encodingOrOffset, length) { if (typeof value === 'number') { throw new TypeError('"value" argument must not be a number') } if (typeof ArrayBuffer !== 'undefined' && value instanceof ArrayBuffer) { return fromArrayBuffer(that, value, encodingOrOffset, length) } if (typeof value === 'string') { return fromString(that, value, encodingOrOffset) } return fromObject(that, value) } /** * Functionally equivalent to Buffer(arg, encoding) but throws a TypeError * if value is a number. * Buffer.from(str[, encoding]) * Buffer.from(array) * Buffer.from(buffer) * Buffer.from(arrayBuffer[, byteOffset[, length]]) **/ Buffer.from = function (value, encodingOrOffset, length) { return from(null, value, encodingOrOffset, length) } if (Buffer.TYPED_ARRAY_SUPPORT) { Buffer.prototype.__proto__ = Uint8Array.prototype Buffer.__proto__ = Uint8Array if (typeof Symbol !== 'undefined' && Symbol.species && Buffer[Symbol.species] === Buffer) { // Fix subarray() in ES2016. See: https://github.com/feross/buffer/pull/97 Object.defineProperty(Buffer, Symbol.species, { value: null, configurable: true }) } } function assertSize (size) { if (typeof size !== 'number') { throw new TypeError('"size" argument must be a number') } } function alloc (that, size, fill, encoding) { assertSize(size) if (size <= 0) { return createBuffer(that, size) } if (fill !== undefined) { // Only pay attention to encoding if it's a string. This // prevents accidentally sending in a number that would // be interpretted as a start offset. return typeof encoding === 'string' ? createBuffer(that, size).fill(fill, encoding) : createBuffer(that, size).fill(fill) } return createBuffer(that, size) } /** * Creates a new filled Buffer instance. * alloc(size[, fill[, encoding]]) **/ Buffer.alloc = function (size, fill, encoding) { return alloc(null, size, fill, encoding) } function allocUnsafe (that, size) { assertSize(size) that = createBuffer(that, size < 0 ? 0 : checked(size) | 0) if (!Buffer.TYPED_ARRAY_SUPPORT) { for (var i = 0; i < size; i++) { that[i] = 0 } } return that } /** * Equivalent to Buffer(num), by default creates a non-zero-filled Buffer instance. * */ Buffer.allocUnsafe = function (size) { return allocUnsafe(null, size) } /** * Equivalent to SlowBuffer(num), by default creates a non-zero-filled Buffer instance. */ Buffer.allocUnsafeSlow = function (size) { return allocUnsafe(null, size) } function fromString (that, string, encoding) { if (typeof encoding !== 'string' || encoding === '') { encoding = 'utf8' } if (!Buffer.isEncoding(encoding)) { throw new TypeError('"encoding" must be a valid string encoding') } var length = byteLength(string, encoding) | 0 that = createBuffer(that, length) that.write(string, encoding) return that } function fromArrayLike (that, array) { var length = checked(array.length) | 0 that = createBuffer(that, length) for (var i = 0; i < length; i += 1) { that[i] = array[i] & 255 } return that } function fromArrayBuffer (that, array, byteOffset, length) { array.byteLength // this throws if `array` is not a valid ArrayBuffer if (byteOffset < 0 || array.byteLength < byteOffset) { throw new RangeError('\'offset\' is out of bounds') } if (array.byteLength < byteOffset + (length || 0)) { throw new RangeError('\'length\' is out of bounds') } if (length === undefined) { array = new Uint8Array(array, byteOffset) } else { array = new Uint8Array(array, byteOffset, length) } if (Buffer.TYPED_ARRAY_SUPPORT) { // Return an augmented `Uint8Array` instance, for best performance that = array that.__proto__ = Buffer.prototype } else { // Fallback: Return an object instance of the Buffer class that = fromArrayLike(that, array) } return that } function fromObject (that, obj) { if (Buffer.isBuffer(obj)) { var len = checked(obj.length) | 0 that = createBuffer(that, len) if (that.length === 0) { return that } obj.copy(that, 0, 0, len) return that } if (obj) { if ((typeof ArrayBuffer !== 'undefined' && obj.buffer instanceof ArrayBuffer) || 'length' in obj) { if (typeof obj.length !== 'number' || isnan(obj.length)) { return createBuffer(that, 0) } return fromArrayLike(that, obj) } if (obj.type === 'Buffer' && isArray(obj.data)) { return fromArrayLike(that, obj.data) } } throw new TypeError('First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.') } function checked (length) { // Note: cannot use `length < kMaxLength` here because that fails when // length is NaN (which is otherwise coerced to zero.) if (length >= kMaxLength()) { throw new RangeError('Attempt to allocate Buffer larger than maximum ' + 'size: 0x' + kMaxLength().toString(16) + ' bytes') } return length | 0 } function SlowBuffer (length) { if (+length != length) { // eslint-disable-line eqeqeq length = 0 } return Buffer.alloc(+length) } Buffer.isBuffer = function isBuffer (b) { return !!(b != null && b._isBuffer) } Buffer.compare = function compare (a, b) { if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) { throw new TypeError('Arguments must be Buffers') } if (a === b) return 0 var x = a.length var y = b.length for (var i = 0, len = Math.min(x, y); i < len; ++i) { if (a[i] !== b[i]) { x = a[i] y = b[i] break } } if (x < y) return -1 if (y < x) return 1 return 0 } Buffer.isEncoding = function isEncoding (encoding) { switch (String(encoding).toLowerCase()) { case 'hex': case 'utf8': case 'utf-8': case 'ascii': case 'binary': case 'base64': case 'raw': case 'ucs2': case 'ucs-2': case 'utf16le': case 'utf-16le': return true default: return false } } Buffer.concat = function concat (list, length) { if (!isArray(list)) { throw new TypeError('"list" argument must be an Array of Buffers') } if (list.length === 0) { return Buffer.alloc(0) } var i if (length === undefined) { length = 0 for (i = 0; i < list.length; i++) { length += list[i].length } } var buffer = Buffer.allocUnsafe(length) var pos = 0 for (i = 0; i < list.length; i++) { var buf = list[i] if (!Buffer.isBuffer(buf)) { throw new TypeError('"list" argument must be an Array of Buffers') } buf.copy(buffer, pos) pos += buf.length } return buffer } function byteLength (string, encoding) { if (Buffer.isBuffer(string)) { return string.length } if (typeof ArrayBuffer !== 'undefined' && typeof ArrayBuffer.isView === 'function' && (ArrayBuffer.isView(string) || string instanceof ArrayBuffer)) { return string.byteLength } if (typeof string !== 'string') { string = '' + string } var len = string.length if (len === 0) return 0 // Use a for loop to avoid recursion var loweredCase = false for (;;) { switch (encoding) { case 'ascii': case 'binary': // Deprecated case 'raw': case 'raws': return len case 'utf8': case 'utf-8': case undefined: return utf8ToBytes(string).length case 'ucs2': case 'ucs-2': case 'utf16le': case 'utf-16le': return len * 2 case 'hex': return len >>> 1 case 'base64': return base64ToBytes(string).length default: if (loweredCase) return utf8ToBytes(string).length // assume utf8 encoding = ('' + encoding).toLowerCase() loweredCase = true } } } Buffer.byteLength = byteLength function slowToString (encoding, start, end) { var loweredCase = false // No need to verify that "this.length <= MAX_UINT32" since it's a read-only // property of a typed array. // This behaves neither like String nor Uint8Array in that we set start/end // to their upper/lower bounds if the value passed is out of range. // undefined is handled specially as per ECMA-262 6th Edition, // Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization. if (start === undefined || start < 0) { start = 0 } // Return early if start > this.length. Done here to prevent potential uint32 // coercion fail below. if (start > this.length) { return '' } if (end === undefined || end > this.length) { end = this.length } if (end <= 0) { return '' } // Force coersion to uint32. This will also coerce falsey/NaN values to 0. end >>>= 0 start >>>= 0 if (end <= start) { return '' } if (!encoding) encoding = 'utf8' while (true) { switch (encoding) { case 'hex': return hexSlice(this, start, end) case 'utf8': case 'utf-8': return utf8Slice(this, start, end) case 'ascii': return asciiSlice(this, start, end) case 'binary': return binarySlice(this, start, end) case 'base64': return base64Slice(this, start, end) case 'ucs2': case 'ucs-2': case 'utf16le': case 'utf-16le': return utf16leSlice(this, start, end) default: if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) encoding = (encoding + '').toLowerCase() loweredCase = true } } } // The property is used by `Buffer.isBuffer` and `is-buffer` (in Safari 5-7) to detect // Buffer instances. Buffer.prototype._isBuffer = true function swap (b, n, m) { var i = b[n] b[n] = b[m] b[m] = i } Buffer.prototype.swap16 = function swap16 () { var len = this.length if (len % 2 !== 0) { throw new RangeError('Buffer size must be a multiple of 16-bits') } for (var i = 0; i < len; i += 2) { swap(this, i, i + 1) } return this } Buffer.prototype.swap32 = function swap32 () { var len = this.length if (len % 4 !== 0) { throw new RangeError('Buffer size must be a multiple of 32-bits') } for (var i = 0; i < len; i += 4) { swap(this, i, i + 3) swap(this, i + 1, i + 2) } return this } Buffer.prototype.toString = function toString () { var length = this.length | 0 if (length === 0) return '' if (arguments.length === 0) return utf8Slice(this, 0, length) return slowToString.apply(this, arguments) } Buffer.prototype.equals = function equals (b) { if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer') if (this === b) return true return Buffer.compare(this, b) === 0 } Buffer.prototype.inspect = function inspect () { var str = '' var max = exports.INSPECT_MAX_BYTES if (this.length > 0) { str = this.toString('hex', 0, max).match(/.{2}/g).join(' ') if (this.length > max) str += ' ... ' } return '<Buffer ' + str + '>' } Buffer.prototype.compare = function compare (target, start, end, thisStart, thisEnd) { if (!Buffer.isBuffer(target)) { throw new TypeError('Argument must be a Buffer') } if (start === undefined) { start = 0 } if (end === undefined) { end = target ? target.length : 0 } if (thisStart === undefined) { thisStart = 0 } if (thisEnd === undefined) { thisEnd = this.length } if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) { throw new RangeError('out of range index') } if (thisStart >= thisEnd && start >= end) { return 0 } if (thisStart >= thisEnd) { return -1 } if (start >= end) { return 1 } start >>>= 0 end >>>= 0 thisStart >>>= 0 thisEnd >>>= 0 if (this === target) return 0 var x = thisEnd - thisStart var y = end - start var len = Math.min(x, y) var thisCopy = this.slice(thisStart, thisEnd) var targetCopy = target.slice(start, end) for (var i = 0; i < len; ++i) { if (thisCopy[i] !== targetCopy[i]) { x = thisCopy[i] y = targetCopy[i] break } } if (x < y) return -1 if (y < x) return 1 return 0 } function arrayIndexOf (arr, val, byteOffset, encoding) { var indexSize = 1 var arrLength = arr.length var valLength = val.length if (encoding !== undefined) { encoding = String(encoding).toLowerCase() if (encoding === 'ucs2' || encoding === 'ucs-2' || encoding === 'utf16le' || encoding === 'utf-16le') { if (arr.length < 2 || val.length < 2) { return -1 } indexSize = 2 arrLength /= 2 valLength /= 2 byteOffset /= 2 } } function read (buf, i) { if (indexSize === 1) { return buf[i] } else { return buf.readUInt16BE(i * indexSize) } } var foundIndex = -1 for (var i = 0; byteOffset + i < arrLength; i++) { if (read(arr, byteOffset + i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) { if (foundIndex === -1) foundIndex = i if (i - foundIndex + 1 === valLength) return (byteOffset + foundIndex) * indexSize } else { if (foundIndex !== -1) i -= i - foundIndex foundIndex = -1 } } return -1 } Buffer.prototype.indexOf = function indexOf (val, byteOffset, encoding) { if (typeof byteOffset === 'string') { encoding = byteOffset byteOffset = 0 } else if (byteOffset > 0x7fffffff) { byteOffset = 0x7fffffff } else if (byteOffset < -0x80000000) { byteOffset = -0x80000000 } byteOffset >>= 0 if (this.length === 0) return -1 if (byteOffset >= this.length) return -1 // Negative offsets start from the end of the buffer if (byteOffset < 0) byteOffset = Math.max(this.length + byteOffset, 0) if (typeof val === 'string') { val = Buffer.from(val, encoding) } if (Buffer.isBuffer(val)) { // special case: looking for empty string/buffer always fails if (val.length === 0) { return -1 } return arrayIndexOf(this, val, byteOffset, encoding) } if (typeof val === 'number') { if (Buffer.TYPED_ARRAY_SUPPORT && Uint8Array.prototype.indexOf === 'function') { return Uint8Array.prototype.indexOf.call(this, val, byteOffset) } return arrayIndexOf(this, [ val ], byteOffset, encoding) } throw new TypeError('val must be string, number or Buffer') } Buffer.prototype.includes = function includes (val, byteOffset, encoding) { return this.indexOf(val, byteOffset, encoding) !== -1 } function hexWrite (buf, string, offset, length) { offset = Number(offset) || 0 var remaining = buf.length - offset if (!length) { length = remaining } else { length = Number(length) if (length > remaining) { length = remaining } } // must be an even number of digits var strLen = string.length if (strLen % 2 !== 0) throw new Error('Invalid hex string') if (length > strLen / 2) { length = strLen / 2 } for (var i = 0; i < length; i++) { var parsed = parseInt(string.substr(i * 2, 2), 16) if (isNaN(parsed)) return i buf[offset + i] = parsed } return i } function utf8Write (buf, string, offset, length) { return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length) } function asciiWrite (buf, string, offset, length) { return blitBuffer(asciiToBytes(string), buf, offset, length) } function binaryWrite (buf, string, offset, length) { return asciiWrite(buf, string, offset, length) } function base64Write (buf, string, offset, length) { return blitBuffer(base64ToBytes(string), buf, offset, length) } function ucs2Write (buf, string, offset, length) { return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length) } Buffer.prototype.write = function write (string, offset, length, encoding) { // Buffer#write(string) if (offset === undefined) { encoding = 'utf8' length = this.length offset = 0 // Buffer#write(string, encoding) } else if (length === undefined && typeof offset === 'string') { encoding = offset length = this.length offset = 0 // Buffer#write(string, offset[, length][, encoding]) } else if (isFinite(offset)) { offset = offset | 0 if (isFinite(length)) { length = length | 0 if (encoding === undefined) encoding = 'utf8' } else { encoding = length length = undefined } // legacy write(string, encoding, offset, length) - remove in v0.13 } else { throw new Error( 'Buffer.write(string, encoding, offset[, length]) is no longer supported' ) } var remaining = this.length - offset if (length === undefined || length > remaining) length = remaining if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) { throw new RangeError('Attempt to write outside buffer bounds') } if (!encoding) encoding = 'utf8' var loweredCase = false for (;;) { switch (encoding) { case 'hex': return hexWrite(this, string, offset, length) case 'utf8': case 'utf-8': return utf8Write(this, string, offset, length) case 'ascii': return asciiWrite(this, string, offset, length) case 'binary': return binaryWrite(this, string, offset, length) case 'base64': // Warning: maxLength not taken into account in base64Write return base64Write(this, string, offset, length) case 'ucs2': case 'ucs-2': case 'utf16le': case 'utf-16le': return ucs2Write(this, string, offset, length) default: if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) encoding = ('' + encoding).toLowerCase() loweredCase = true } } } Buffer.prototype.toJSON = function toJSON () { return { type: 'Buffer', data: Array.prototype.slice.call(this._arr || this, 0) } } function base64Slice (buf, start, end) { if (start === 0 && end === buf.length) { return base64.fromByteArray(buf) } else { return base64.fromByteArray(buf.slice(start, end)) } } function utf8Slice (buf, start, end) { end = Math.min(buf.length, end) var res = [] var i = start while (i < end) { var firstByte = buf[i] var codePoint = null var bytesPerSequence = (firstByte > 0xEF) ? 4 : (firstByte > 0xDF) ? 3 : (firstByte > 0xBF) ? 2 : 1 if (i + bytesPerSequence <= end) { var secondByte, thirdByte, fourthByte, tempCodePoint switch (bytesPerSequence) { case 1: if (firstByte < 0x80) { codePoint = firstByte } break case 2: secondByte = buf[i + 1] if ((secondByte & 0xC0) === 0x80) { tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F) if (tempCodePoint > 0x7F) { codePoint = tempCodePoint } } break case 3: secondByte = buf[i + 1] thirdByte = buf[i + 2] if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) { tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F) if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) { codePoint = tempCodePoint } } break case 4: secondByte = buf[i + 1] thirdByte = buf[i + 2] fourthByte = buf[i + 3] if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) { tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F) if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) { codePoint = tempCodePoint } } } } if (codePoint === null) { // we did not generate a valid codePoint so insert a // replacement char (U+FFFD) and advance only 1 byte codePoint = 0xFFFD bytesPerSequence = 1 } else if (codePoint > 0xFFFF) { // encode to utf16 (surrogate pair dance) codePoint -= 0x10000 res.push(codePoint >>> 10 & 0x3FF | 0xD800) codePoint = 0xDC00 | codePoint & 0x3FF } res.push(codePoint) i += bytesPerSequence } return decodeCodePointsArray(res) } // Based on http://stackoverflow.com/a/22747272/680742, the browser with // the lowest limit is Chrome, with 0x10000 args. // We go 1 magnitude less, for safety var MAX_ARGUMENTS_LENGTH = 0x1000 function decodeCodePointsArray (codePoints) { var len = codePoints.length if (len <= MAX_ARGUMENTS_LENGTH) { return String.fromCharCode.apply(String, codePoints) // avoid extra slice() } // Decode in chunks to avoid "call stack size exceeded". var res = '' var i = 0 while (i < len) { res += String.fromCharCode.apply( String, codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH) ) } return res } function asciiSlice (buf, start, end) { var ret = '' end = Math.min(buf.length, end) for (var i = start; i < end; i++) { ret += String.fromCharCode(buf[i] & 0x7F) } return ret } function binarySlice (buf, start, end) { var ret = '' end = Math.min(buf.length, end) for (var i = start; i < end; i++) { ret += String.fromCharCode(buf[i]) } return ret } function hexSlice (buf, start, end) { var len = buf.length if (!start || start < 0) start = 0 if (!end || end < 0 || end > len) end = len var out = '' for (var i = start; i < end; i++) { out += toHex(buf[i]) } return out } function utf16leSlice (buf, start, end) { var bytes = buf.slice(start, end) var res = '' for (var i = 0; i < bytes.length; i += 2) { res += String.fromCharCode(bytes[i] + bytes[i + 1] * 256) } return res } Buffer.prototype.slice = function slice (start, end) { var len = this.length start = ~~start end = end === undefined ? len : ~~end if (start < 0) { start += len if (start < 0) start = 0 } else if (start > len) { start = len } if (end < 0) { end += len if (end < 0) end = 0 } else if (end > len) { end = len } if (end < start) end = start var newBuf if (Buffer.TYPED_ARRAY_SUPPORT) { newBuf = this.subarray(start, end) newBuf.__proto__ = Buffer.prototype } else { var sliceLen = end - start newBuf = new Buffer(sliceLen, undefined) for (var i = 0; i < sliceLen; i++) { newBuf[i] = this[i + start] } } return newBuf } /* * Need to make sure that buffer isn't trying to write out of bounds. */ function checkOffset (offset, ext, length) { if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint') if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length') } Buffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) { offset = offset | 0 byteLength = byteLength | 0 if (!noAssert) checkOffset(offset, byteLength, this.length) var val = this[offset] var mul = 1 var i = 0 while (++i < byteLength && (mul *= 0x100)) { val += this[offset + i] * mul } return val } Buffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) { offset = offset | 0 byteLength = byteLength | 0 if (!noAssert) { checkOffset(offset, byteLength, this.length) } var val = this[offset + --byteLength] var mul = 1 while (byteLength > 0 && (mul *= 0x100)) { val += this[offset + --byteLength] * mul } return val } Buffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) { if (!noAssert) checkOffset(offset, 1, this.length) return this[offset] } Buffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) { if (!noAssert) checkOffset(offset, 2, this.length) return this[offset] | (this[offset + 1] << 8) } Buffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) { if (!noAssert) checkOffset(offset, 2, this.length) return (this[offset] << 8) | this[offset + 1] } Buffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) { if (!noAssert) checkOffset(offset, 4, this.length) return ((this[offset]) | (this[offset + 1] << 8) | (this[offset + 2] << 16)) + (this[offset + 3] * 0x1000000) } Buffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) { if (!noAssert) checkOffset(offset, 4, this.length) return (this[offset] * 0x1000000) + ((this[offset + 1] << 16) | (this[offset + 2] << 8) | this[offset + 3]) } Buffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) { offset = offset | 0 byteLength = byteLength | 0 if (!noAssert) checkOffset(offset, byteLength, this.length) var val = this[offset] var mul = 1 var i = 0 while (++i < byteLength && (mul *= 0x100)) { val += this[offset + i] * mul } mul *= 0x80 if (val >= mul) val -= Math.pow(2, 8 * byteLength) return val } Buffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) { offset = offset | 0 byteLength = byteLength | 0 if (!noAssert) checkOffset(offset, byteLength, this.length) var i = byteLength var mul = 1 var val = this[offset + --i] while (i > 0 && (mul *= 0x100)) { val += this[offset + --i] * mul } mul *= 0x80 if (val >= mul) val -= Math.pow(2, 8 * byteLength) return val } Buffer.prototype.readInt8 = function readInt8 (offset, noAssert) { if (!noAssert) checkOffset(offset, 1, this.length) if (!(this[offset] & 0x80)) return (this[offset]) return ((0xff - this[offset] + 1) * -1) } Buffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) { if (!noAssert) checkOffset(offset, 2, this.length) var val = this[offset] | (this[offset + 1] << 8) return (val & 0x8000) ? val | 0xFFFF0000 : val } Buffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) { if (!noAssert) checkOffset(offset, 2, this.length) var val = this[offset + 1] | (this[offset] << 8) return (val & 0x8000) ? val | 0xFFFF0000 : val } Buffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) { if (!noAssert) checkOffset(offset, 4, this.length) return (this[offset]) | (this[offset + 1] << 8) | (this[offset + 2] << 16) | (this[offset + 3] << 24) } Buffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) { if (!noAssert) checkOffset(offset, 4, this.length) return (this[offset] << 24) | (this[offset + 1] << 16) | (this[offset + 2] << 8) | (this[offset + 3]) } Buffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) { if (!noAssert) checkOffset(offset, 4, this.length) return ieee754.read(this, offset, true, 23, 4) } Buffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) { if (!noAssert) checkOffset(offset, 4, this.length) return ieee754.read(this, offset, false, 23, 4) } Buffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) { if (!noAssert) checkOffset(offset, 8, this.length) return ieee754.read(this, offset, true, 52, 8) } Buffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) { if (!noAssert) checkOffset(offset, 8, this.length) return ieee754.read(this, offset, false, 52, 8) } function checkInt (buf, value, offset, ext, max, min) { if (!Buffer.isBuffer(buf)) throw new TypeError('"buffer" argument must be a Buffer instance') if (value > max || value < min) throw new RangeError('"value" argument is out of bounds') if (offset + ext > buf.length) throw new RangeError('Index out of range') } Buffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) { value = +value offset = offset | 0 byteLength = byteLength | 0 if (!noAssert) { var maxBytes = Math.pow(2, 8 * byteLength) - 1 checkInt(this, value, offset, byteLength, maxBytes, 0) } var mul = 1 var i = 0 this[offset] = value & 0xFF while (++i < byteLength && (mul *= 0x100)) { this[offset + i] = (value / mul) & 0xFF } return offset + byteLength } Buffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) { value = +value offset = offset | 0 byteLength = byteLength | 0 if (!noAssert) { var maxBytes = Math.pow(2, 8 * byteLength) - 1 checkInt(this, value, offset, byteLength, maxBytes, 0) } var i = byteLength - 1 var mul = 1 this[offset + i] = value & 0xFF while (--i >= 0 && (mul *= 0x100)) { this[offset + i] = (value / mul) & 0xFF } return offset + byteLength } Buffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) { value = +value offset = offset | 0 if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0) if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value) this[offset] = (value & 0xff) return offset + 1 } function objectWriteUInt16 (buf, value, offset, littleEndian) { if (value < 0) value = 0xffff + value + 1 for (var i = 0, j = Math.min(buf.length - offset, 2); i < j; i++) { buf[offset + i] = (value & (0xff << (8 * (littleEndian ? i : 1 - i)))) >>> (littleEndian ? i : 1 - i) * 8 } } Buffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) { value = +value offset = offset | 0 if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) if (Buffer.TYPED_ARRAY_SUPPORT) { this[offset] = (value & 0xff) this[offset + 1] = (value >>> 8) } else { objectWriteUInt16(this, value, offset, true) } return offset + 2 } Buffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) { value = +value offset = offset | 0 if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) if (Buffer.TYPED_ARRAY_SUPPORT) { this[offset] = (value >>> 8) this[offset + 1] = (value & 0xff) } else { objectWriteUInt16(this, value, offset, false) } return offset + 2 } function objectWriteUInt32 (buf, value, offset, littleEndian) { if (value < 0) value = 0xffffffff + value + 1 for (var i = 0, j = Math.min(buf.length - offset, 4); i < j; i++) { buf[offset + i] = (value >>> (littleEndian ? i : 3 - i) * 8) & 0xff } } Buffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) { value = +value offset = offset | 0 if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0) if (Buffer.TYPED_ARRAY_SUPPORT) { this[offset + 3] = (value >>> 24) this[offset + 2] = (value >>> 16) this[offset + 1] = (value >>> 8) this[offset] = (value & 0xff) } else { objectWriteUInt32(this, value, offset, true) } return offset + 4 } Buffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) { value = +value offset = offset | 0 if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0) if (Buffer.TYPED_ARRAY_SUPPORT) { this[offset] = (value >>> 24) this[offset + 1] = (value >>> 16) this[offset + 2] = (value >>> 8) this[offset + 3] = (value & 0xff) } else { objectWriteUInt32(this, value, offset, false) } return offset + 4 } Buffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) { value = +value offset = offset | 0 if (!noAssert) { var limit = Math.pow(2, 8 * byteLength - 1) checkInt(this, value, offset, byteLength, limit - 1, -limit) } var i = 0 var mul = 1 var sub = 0 this[offset] = value & 0xFF while (++i < byteLength && (mul *= 0x100)) { if (value < 0 && sub === 0 && this[offset + i - 1] !== 0) { sub = 1 } this[offset + i] = ((value / mul) >> 0) - sub & 0xFF } return offset + byteLength } Buffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) { value = +value offset = offset | 0 if (!noAssert) { var limit = Math.pow(2, 8 * byteLength - 1) checkInt(this, value, offset, byteLength, limit - 1, -limit) } var i = byteLength - 1 var mul = 1 var sub = 0 this[offset + i] = value & 0xFF while (--i >= 0 && (mul *= 0x100)) { if (value < 0 && sub === 0 && this[offset + i + 1] !== 0) { sub = 1 } this[offset + i] = ((value / mul) >> 0) - sub & 0xFF } return offset + byteLength } Buffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) { value = +value offset = offset | 0 if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80) if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value) if (value < 0) value = 0xff + value + 1 this[offset] = (value & 0xff) return offset + 1 } Buffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) { value = +value offset = offset | 0 if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) if (Buffer.TYPED_ARRAY_SUPPORT) { this[offset] = (value & 0xff) this[offset + 1] = (value >>> 8) } else { objectWriteUInt16(this, value, offset, true) } return offset + 2 } Buffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) { value = +value offset = offset | 0 if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) if (Buffer.TYPED_ARRAY_SUPPORT) { this[offset] = (value >>> 8) this[offset + 1] = (value & 0xff) } else { objectWriteUInt16(this, value, offset, false) } return offset + 2 } Buffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) { value = +value offset = offset | 0 if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000) if (Buffer.TYPED_ARRAY_SUPPORT) { this[offset] = (value & 0xff) this[offset + 1] = (value >>> 8) this[offset + 2] = (value >>> 16) this[offset + 3] = (value >>> 24) } else { objectWriteUInt32(this, value, offset, true) } return offset + 4 } Buffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) { value = +value offset = offset | 0 if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000) if (value < 0) value = 0xffffffff + value + 1 if (Buffer.TYPED_ARRAY_SUPPORT) { this[offset] = (value >>> 24) this[offset + 1] = (value >>> 16) this[offset + 2] = (value >>> 8) this[offset + 3] = (value & 0xff) } else { objectWriteUInt32(this, value, offset, false) } return offset + 4 } function checkIEEE754 (buf, value, offset, ext, max, min) { if (offset + ext > buf.length) throw new RangeError('Index out of range') if (offset < 0) throw new RangeError('Index out of range') } function writeFloat (buf, value, offset, littleEndian, noAssert) { if (!noAssert) { checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38) } ieee754.write(buf, value, offset, littleEndian, 23, 4) return offset + 4 } Buffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) { return writeFloat(this, value, offset, true, noAssert) } Buffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) { return writeFloat(this, value, offset, false, noAssert) } function writeDouble (buf, value, offset, littleEndian, noAssert) { if (!noAssert) { checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308) } ieee754.write(buf, value, offset, littleEndian, 52, 8) return offset + 8 } Buffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) { return writeDouble(this, value, offset, true, noAssert) } Buffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) { return writeDouble(this, value, offset, false, noAssert) } // copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length) Buffer.prototype.copy = function copy (target, targetStart, start, end) { if (!start) start = 0 if (!end && end !== 0) end = this.length if (targetStart >= target.length) targetStart = target.length if (!targetStart) targetStart = 0 if (end > 0 && end < start) end = start // Copy 0 bytes; we're done if (end === start) return 0 if (target.length === 0 || this.length === 0) return 0 // Fatal error conditions if (targetStart < 0) { throw new RangeError('targetStart out of bounds') } if (start < 0 || start >= this.length) throw new RangeError('sourceStart out of bounds') if (end < 0) throw new RangeError('sourceEnd out of bounds') // Are we oob? if (end > this.length) end = this.length if (target.length - targetStart < end - start) { end = target.length - targetStart + start } var len = end - start var i if (this === target && start < targetStart && targetStart < end) { // descending copy from end for (i = len - 1; i >= 0; i--) { target[i + targetStart] = this[i + start] } } else if (len < 1000 || !Buffer.TYPED_ARRAY_SUPPORT) { // ascending copy from start for (i = 0; i < len; i++) { target[i + targetStart] = this[i + start] } } else { Uint8Array.prototype.set.call( target, this.subarray(start, start + len), targetStart ) } return len } // Usage: // buffer.fill(number[, offset[, end]]) // buffer.fill(buffer[, offset[, end]]) // buffer.fill(string[, offset[, end]][, encoding]) Buffer.prototype.fill = function fill (val, start, end, encoding) { // Handle string cases: if (typeof val === 'string') { if (typeof start === 'string') { encoding = start start = 0 end = this.length } else if (typeof end === 'string') { encoding = end end = this.length } if (val.length === 1) { var code = val.charCodeAt(0) if (code < 256) { val = code } } if (encoding !== undefined && typeof encoding !== 'string') { throw new TypeError('encoding must be a string') } if (typeof encoding === 'string' && !Buffer.isEncoding(encoding)) { throw new TypeError('Unknown encoding: ' + encoding) } } else if (typeof val === 'number') { val = val & 255 } // Invalid ranges are not set to a default, so can range check early. if (start < 0 || this.length < start || this.length < end) { throw new RangeError('Out of range index') } if (end <= start) { return this } start = start >>> 0 end = end === undefined ? this.length : end >>> 0 if (!val) val = 0 var i if (typeof val === 'number') { for (i = start; i < end; i++) { this[i] = val } } else { var bytes = Buffer.isBuffer(val) ? val : utf8ToBytes(new Buffer(val, encoding).toString()) var len = bytes.length for (i = 0; i < end - start; i++) { this[i + start] = bytes[i % len] } } return this } // HELPER FUNCTIONS // ================ var INVALID_BASE64_RE = /[^+\/0-9A-Za-z-_]/g function base64clean (str) { // Node strips out invalid characters like \n and \t from the string, base64-js does not str = stringtrim(str).replace(INVALID_BASE64_RE, '') // Node converts strings with length < 2 to '' if (str.length < 2) return '' // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not while (str.length % 4 !== 0) { str = str + '=' } return str } function stringtrim (str) { if (str.trim) return str.trim() return str.replace(/^\s+|\s+$/g, '') } function toHex (n) { if (n < 16) return '0' + n.toString(16) return n.toString(16) } function utf8ToBytes (string, units) { units = units || Infinity var codePoint var length = string.length var leadSurrogate = null var bytes = [] for (var i = 0; i < length; i++) { codePoint = string.charCodeAt(i) // is surrogate component if (codePoint > 0xD7FF && codePoint < 0xE000) { // last char was a lead if (!leadSurrogate) { // no lead yet if (codePoint > 0xDBFF) { // unexpected trail if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) continue } else if (i + 1 === length) { // unpaired lead if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) continue } // valid lead leadSurrogate = codePoint continue } // 2 leads in a row if (codePoint < 0xDC00) { if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) leadSurrogate = codePoint continue } // valid surrogate pair codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000 } else if (leadSurrogate) { // valid bmp char, but last char was a lead if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) } leadSurrogate = null // encode utf8 if (codePoint < 0x80) { if ((units -= 1) < 0) break bytes.push(codePoint) } else if (codePoint < 0x800) { if ((units -= 2) < 0) break bytes.push( codePoint >> 0x6 | 0xC0, codePoint & 0x3F | 0x80 ) } else if (codePoint < 0x10000) { if ((units -= 3) < 0) break bytes.push( codePoint >> 0xC | 0xE0, codePoint >> 0x6 & 0x3F | 0x80, codePoint & 0x3F | 0x80 ) } else if (codePoint < 0x110000) { if ((units -= 4) < 0) break bytes.push( codePoint >> 0x12 | 0xF0, codePoint >> 0xC & 0x3F | 0x80, codePoint >> 0x6 & 0x3F | 0x80, codePoint & 0x3F | 0x80 ) } else { throw new Error('Invalid code point') } } return bytes } function asciiToBytes (str) { var byteArray = [] for (var i = 0; i < str.length; i++) { // Node's code seems to be doing this and not & 0x7F.. byteArray.push(str.charCodeAt(i) & 0xFF) } return byteArray } function utf16leToBytes (str, units) { var c, hi, lo var byteArray = [] for (var i = 0; i < str.length; i++) { if ((units -= 2) < 0) break c = str.charCodeAt(i) hi = c >> 8 lo = c % 256 byteArray.push(lo) byteArray.push(hi) } return byteArray } function base64ToBytes (str) { return base64.toByteArray(base64clean(str)) } function blitBuffer (src, dst, offset, length) { for (var i = 0; i < length; i++) { if ((i + offset >= dst.length) || (i >= src.length)) break dst[i + offset] = src[i] } return i } function isnan (val) { return val !== val // eslint-disable-line no-self-compare } }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{"base64-js":1,"ieee754":15,"isarray":16}],4:[function(require,module,exports){ // Use strict mode (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode) "use strict"; // Requires var Typo = require("typo-js"); // Create function function CodeMirrorSpellChecker(options) { // Initialize options = options || {}; // Verify if(typeof options.codeMirrorInstance !== "function" || typeof options.codeMirrorInstance.defineMode !== "function") { console.log("CodeMirror Spell Checker: You must provide an instance of CodeMirror via the option `codeMirrorInstance`"); return; } // Because some browsers don't support this functionality yet if(!String.prototype.includes) { String.prototype.includes = function() { "use strict"; return String.prototype.indexOf.apply(this, arguments) !== -1; }; } // Define the new mode options.codeMirrorInstance.defineMode("spell-checker", function(config) { // Load AFF/DIC data if(!CodeMirrorSpellChecker.aff_loading) { CodeMirrorSpellChecker.aff_loading = true; var xhr_aff = new XMLHttpRequest(); xhr_aff.open("GET", "https://cdn.jsdelivr.net/codemirror.spell-checker/latest/en_US.aff", true); xhr_aff.onload = function() { if(xhr_aff.readyState === 4 && xhr_aff.status === 200) { CodeMirrorSpellChecker.aff_data = xhr_aff.responseText; CodeMirrorSpellChecker.num_loaded++; if(CodeMirrorSpellChecker.num_loaded == 2) { CodeMirrorSpellChecker.typo = new Typo("en_US", CodeMirrorSpellChecker.aff_data, CodeMirrorSpellChecker.dic_data, { platform: "any" }); } } }; xhr_aff.send(null); } if(!CodeMirrorSpellChecker.dic_loading) { CodeMirrorSpellChecker.dic_loading = true; var xhr_dic = new XMLHttpRequest(); xhr_dic.open("GET", "https://cdn.jsdelivr.net/codemirror.spell-checker/latest/en_US.dic", true); xhr_dic.onload = function() { if(xhr_dic.readyState === 4 && xhr_dic.status === 200) { CodeMirrorSpellChecker.dic_data = xhr_dic.responseText; CodeMirrorSpellChecker.num_loaded++; if(CodeMirrorSpellChecker.num_loaded == 2) { CodeMirrorSpellChecker.typo = new Typo("en_US", CodeMirrorSpellChecker.aff_data, CodeMirrorSpellChecker.dic_data, { platform: "any" }); } } }; xhr_dic.send(null); } // Define what separates a word var rx_word = "!\"#$%&()*+,-./:;<=>?@[\\]^_`{|}~ "; // Create the overlay and such var overlay = { token: function(stream) { var ch = stream.peek(); var word = ""; if(rx_word.includes(ch)) { stream.next(); return null; } while((ch = stream.peek()) != null && !rx_word.includes(ch)) { word += ch; stream.next(); } if(CodeMirrorSpellChecker.typo && !CodeMirrorSpellChecker.typo.check(word)) return "spell-error"; // CSS class: cm-spell-error return null; } }; var mode = options.codeMirrorInstance.getMode( config, config.backdrop || "text/plain" ); return options.codeMirrorInstance.overlayMode(mode, overlay, true); }); } // Initialize data globally to reduce memory consumption CodeMirrorSpellChecker.num_loaded = 0; CodeMirrorSpellChecker.aff_loading = false; CodeMirrorSpellChecker.dic_loading = false; CodeMirrorSpellChecker.aff_data = ""; CodeMirrorSpellChecker.dic_data = ""; CodeMirrorSpellChecker.typo; // Export module.exports = CodeMirrorSpellChecker; },{"typo-js":18}],5:[function(require,module,exports){ // CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: http://codemirror.net/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); else if (typeof define == "function" && define.amd) // AMD define(["../../lib/codemirror"], mod); else // Plain browser env mod(CodeMirror); })(function(CodeMirror) { "use strict"; CodeMirror.defineOption("fullScreen", false, function(cm, val, old) { if (old == CodeMirror.Init) old = false; if (!old == !val) return; if (val) setFullscreen(cm); else setNormal(cm); }); function setFullscreen(cm) { var wrap = cm.getWrapperElement(); cm.state.fullScreenRestore = {scrollTop: window.pageYOffset, scrollLeft: window.pageXOffset, width: wrap.style.width, height: wrap.style.height}; wrap.style.width = ""; wrap.style.height = "auto"; wrap.className += " CodeMirror-fullscreen"; document.documentElement.style.overflow = "hidden"; cm.refresh(); } function setNormal(cm) { var wrap = cm.getWrapperElement(); wrap.className = wrap.className.replace(/\s*CodeMirror-fullscreen\b/, ""); document.documentElement.style.overflow = ""; var info = cm.state.fullScreenRestore; wrap.style.width = info.width; wrap.style.height = info.height; window.scrollTo(info.scrollLeft, info.scrollTop); cm.refresh(); } }); },{"../../lib/codemirror":10}],6:[function(require,module,exports){ // CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: http://codemirror.net/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); else if (typeof define == "function" && define.amd) // AMD define(["../../lib/codemirror"], mod); else // Plain browser env mod(CodeMirror); })(function(CodeMirror) { CodeMirror.defineOption("placeholder", "", function(cm, val, old) { var prev = old && old != CodeMirror.Init; if (val && !prev) { cm.on("blur", onBlur); cm.on("change", onChange); cm.on("swapDoc", onChange); onChange(cm); } else if (!val && prev) { cm.off("blur", onBlur); cm.off("change", onChange); cm.off("swapDoc", onChange); clearPlaceholder(cm); var wrapper = cm.getWrapperElement(); wrapper.className = wrapper.className.replace(" CodeMirror-empty", ""); } if (val && !cm.hasFocus()) onBlur(cm); }); function clearPlaceholder(cm) { if (cm.state.placeholder) { cm.state.placeholder.parentNode.removeChild(cm.state.placeholder); cm.state.placeholder = null; } } function setPlaceholder(cm) { clearPlaceholder(cm); var elt = cm.state.placeholder = document.createElement("pre"); elt.style.cssText = "height: 0; overflow: visible"; elt.className = "CodeMirror-placeholder"; var placeHolder = cm.getOption("placeholder") if (typeof placeHolder == "string") placeHolder = document.createTextNode(placeHolder) elt.appendChild(placeHolder) cm.display.lineSpace.insertBefore(elt, cm.display.lineSpace.firstChild); } function onBlur(cm) { if (isEmpty(cm)) setPlaceholder(cm); } function onChange(cm) { var wrapper = cm.getWrapperElement(), empty = isEmpty(cm); wrapper.className = wrapper.className.replace(" CodeMirror-empty", "") + (empty ? " CodeMirror-empty" : ""); if (empty) setPlaceholder(cm); else clearPlaceholder(cm); } function isEmpty(cm) { return (cm.lineCount() === 1) && (cm.getLine(0) === ""); } }); },{"../../lib/codemirror":10}],7:[function(require,module,exports){ // CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: http://codemirror.net/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); else if (typeof define == "function" && define.amd) // AMD define(["../../lib/codemirror"], mod); else // Plain browser env mod(CodeMirror); })(function(CodeMirror) { "use strict"; var listRE = /^(\s*)(>[> ]*|[*+-]\s|(\d+)([.)]))(\s*)/, emptyListRE = /^(\s*)(>[> ]*|[*+-]|(\d+)[.)])(\s*)$/, unorderedListRE = /[*+-]\s/; CodeMirror.commands.newlineAndIndentContinueMarkdownList = function(cm) { if (cm.getOption("disableInput")) return CodeMirror.Pass; var ranges = cm.listSelections(), replacements = []; for (var i = 0; i < ranges.length; i++) { var pos = ranges[i].head; var eolState = cm.getStateAfter(pos.line); var inList = eolState.list !== false; var inQuote = eolState.quote !== 0; var line = cm.getLine(pos.line), match = listRE.exec(line); if (!ranges[i].empty() || (!inList && !inQuote) || !match) { cm.execCommand("newlineAndIndent"); return; } if (emptyListRE.test(line)) { cm.replaceRange("", { line: pos.line, ch: 0 }, { line: pos.line, ch: pos.ch + 1 }); replacements[i] = "\n"; } else { var indent = match[1], after = match[5]; var bullet = unorderedListRE.test(match[2]) || match[2].indexOf(">") >= 0 ? match[2] : (parseInt(match[3], 10) + 1) + match[4]; replacements[i] = "\n" + indent + bullet + after; } } cm.replaceSelections(replacements); }; }); },{"../../lib/codemirror":10}],8:[function(require,module,exports){ // CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: http://codemirror.net/LICENSE // Utility function that allows modes to be combined. The mode given // as the base argument takes care of most of the normal mode // functionality, but a second (typically simple) mode is used, which // can override the style of text. Both modes get to parse all of the // text, but when both assign a non-null style to a piece of code, the // overlay wins, unless the combine argument was true and not overridden, // or state.overlay.combineTokens was true, in which case the styles are // combined. (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); else if (typeof define == "function" && define.amd) // AMD define(["../../lib/codemirror"], mod); else // Plain browser env mod(CodeMirror); })(function(CodeMirror) { "use strict"; CodeMirror.overlayMode = function(base, overlay, combine) { return { startState: function() { return { base: CodeMirror.startState(base), overlay: CodeMirror.startState(overlay), basePos: 0, baseCur: null, overlayPos: 0, overlayCur: null, streamSeen: null }; }, copyState: function(state) { return { base: CodeMirror.copyState(base, state.base), overlay: CodeMirror.copyState(overlay, state.overlay), basePos: state.basePos, baseCur: null, overlayPos: state.overlayPos, overlayCur: null }; }, token: function(stream, state) { if (stream != state.streamSeen || Math.min(state.basePos, state.overlayPos) < stream.start) { state.streamSeen = stream; state.basePos = state.overlayPos = stream.start; } if (stream.start == state.basePos) { state.baseCur = base.token(stream, state.base); state.basePos = stream.pos; } if (stream.start == state.overlayPos) { stream.pos = stream.start; state.overlayCur = overlay.token(stream, state.overlay); state.overlayPos = stream.pos; } stream.pos = Math.min(state.basePos, state.overlayPos); // state.overlay.combineTokens always takes precedence over combine, // unless set to null if (state.overlayCur == null) return state.baseCur; else if (state.baseCur != null && state.overlay.combineTokens || combine && state.overlay.combineTokens == null) return state.baseCur + " " + state.overlayCur; else return state.overlayCur; }, indent: base.indent && function(state, textAfter) { return base.indent(state.base, textAfter); }, electricChars: base.electricChars, innerMode: function(state) { return {state: state.base, mode: base}; }, blankLine: function(state) { if (base.blankLine) base.blankLine(state.base); if (overlay.blankLine) overlay.blankLine(state.overlay); } }; }; }); },{"../../lib/codemirror":10}],9:[function(require,module,exports){ // CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: http://codemirror.net/LICENSE // Because sometimes you need to mark the selected *text*. // // Adds an option 'styleSelectedText' which, when enabled, gives // selected text the CSS class given as option value, or // "CodeMirror-selectedtext" when the value is not a string. (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); else if (typeof define == "function" && define.amd) // AMD define(["../../lib/codemirror"], mod); else // Plain browser env mod(CodeMirror); })(function(CodeMirror) { "use strict"; CodeMirror.defineOption("styleSelectedText", false, function(cm, val, old) { var prev = old && old != CodeMirror.Init; if (val && !prev) { cm.state.markedSelection = []; cm.state.markedSelectionStyle = typeof val == "string" ? val : "CodeMirror-selectedtext"; reset(cm); cm.on("cursorActivity", onCursorActivity); cm.on("change", onChange); } else if (!val && prev) { cm.off("cursorActivity", onCursorActivity); cm.off("change", onChange); clear(cm); cm.state.markedSelection = cm.state.markedSelectionStyle = null; } }); function onCursorActivity(cm) { cm.operation(function() { update(cm); }); } function onChange(cm) { if (cm.state.markedSelection.length) cm.operation(function() { clear(cm); }); } var CHUNK_SIZE = 8; var Pos = CodeMirror.Pos; var cmp = CodeMirror.cmpPos; function coverRange(cm, from, to, addAt) { if (cmp(from, to) == 0) return; var array = cm.state.markedSelection; var cls = cm.state.markedSelectionStyle; for (var line = from.line;;) { var start = line == from.line ? from : Pos(line, 0); var endLine = line + CHUNK_SIZE, atEnd = endLine >= to.line; var end = atEnd ? to : Pos(endLine, 0); var mark = cm.markText(start, end, {className: cls}); if (addAt == null) array.push(mark); else array.splice(addAt++, 0, mark); if (atEnd) break; line = endLine; } } function clear(cm) { var array = cm.state.markedSelection; for (var i = 0; i < array.length; ++i) array[i].clear(); array.length = 0; } function reset(cm) { clear(cm); var ranges = cm.listSelections(); for (var i = 0; i < ranges.length; i++) coverRange(cm, ranges[i].from(), ranges[i].to()); } function update(cm) { if (!cm.somethingSelected()) return clear(cm); if (cm.listSelections().length > 1) return reset(cm); var from = cm.getCursor("start"), to = cm.getCursor("end"); var array = cm.state.markedSelection; if (!array.length) return coverRange(cm, from, to); var coverStart = array[0].find(), coverEnd = array[array.length - 1].find(); if (!coverStart || !coverEnd || to.line - from.line < CHUNK_SIZE || cmp(from, coverEnd.to) >= 0 || cmp(to, coverStart.from) <= 0) return reset(cm); while (cmp(from, coverStart.from) > 0) { array.shift().clear(); coverStart = array[0].find(); } if (cmp(from, coverStart.from) < 0) { if (coverStart.to.line - from.line < CHUNK_SIZE) { array.shift().clear(); coverRange(cm, from, coverStart.to, 0); } else { coverRange(cm, from, coverStart.from, 0); } } while (cmp(to, coverEnd.to) < 0) { array.pop().clear(); coverEnd = array[array.length - 1].find(); } if (cmp(to, coverEnd.to) > 0) { if (to.line - coverEnd.from.line < CHUNK_SIZE) { array.pop().clear(); coverRange(cm, coverEnd.from, to); } else { coverRange(cm, coverEnd.to, to); } } } }); },{"../../lib/codemirror":10}],10:[function(require,module,exports){ // CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: http://codemirror.net/LICENSE // This is CodeMirror (http://codemirror.net), a code editor // implemented in JavaScript on top of the browser's DOM. // // You can find some technical background for some of the code below // at http://marijnhaverbeke.nl/blog/#cm-internals . (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS module.exports = mod(); else if (typeof define == "function" && define.amd) // AMD return define([], mod); else // Plain browser env (this || window).CodeMirror = mod(); })(function() { "use strict"; // BROWSER SNIFFING // Kludges for bugs and behavior differences that can't be feature // detected are enabled based on userAgent etc sniffing. var userAgent = navigator.userAgent; var platform = navigator.platform; var gecko = /gecko\/\d/i.test(userAgent); var ie_upto10 = /MSIE \d/.test(userAgent); var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent); var ie = ie_upto10 || ie_11up; var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : ie_11up[1]); var webkit = /WebKit\//.test(userAgent); var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent); var chrome = /Chrome\//.test(userAgent); var presto = /Opera\//.test(userAgent); var safari = /Apple Computer/.test(navigator.vendor); var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent); var phantom = /PhantomJS/.test(userAgent); var ios = /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent); // This is woefully incomplete. Suggestions for alternative methods welcome. var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent); var mac = ios || /Mac/.test(platform); var chromeOS = /\bCrOS\b/.test(userAgent); var windows = /win/i.test(platform); var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/); if (presto_version) presto_version = Number(presto_version[1]); if (presto_version && presto_version >= 15) { presto = false; webkit = true; } // Some browsers use the wrong event properties to signal cmd/ctrl on OS X var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11)); var captureRightClick = gecko || (ie && ie_version >= 9); // Optimize some code when these features are not used. var sawReadOnlySpans = false, sawCollapsedSpans = false; // EDITOR CONSTRUCTOR // A CodeMirror instance represents an editor. This is the object // that user code is usually dealing with. function CodeMirror(place, options) { if (!(this instanceof CodeMirror)) return new CodeMirror(place, options); this.options = options = options ? copyObj(options) : {}; // Determine effective options based on given values and defaults. copyObj(defaults, options, false); setGuttersForLineNumbers(options); var doc = options.value; if (typeof doc == "string") doc = new Doc(doc, options.mode, null, options.lineSeparator); this.doc = doc; var input = new CodeMirror.inputStyles[options.inputStyle](this); var display = this.display = new Display(place, doc, input); display.wrapper.CodeMirror = this; updateGutters(this); themeChanged(this); if (options.lineWrapping) this.display.wrapper.className += " CodeMirror-wrap"; if (options.autofocus && !mobile) display.input.focus(); initScrollbars(this); this.state = { keyMaps: [], // stores maps added by addKeyMap overlays: [], // highlighting overlays, as added by addOverlay modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info overwrite: false, delayingBlurEvent: false, focused: false, suppressEdits: false, // used to disable editing during key handlers when in readOnly mode pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in input.poll selectingText: false, draggingText: false, highlight: new Delayed(), // stores highlight worker timeout keySeq: null, // Unfinished key sequence specialChars: null }; var cm = this; // Override magic textarea content restore that IE sometimes does // on our hidden textarea on reload if (ie && ie_version < 11) setTimeout(function() { cm.display.input.reset(true); }, 20); registerEventHandlers(this); ensureGlobalHandlers(); startOperation(this); this.curOp.forceUpdate = true; attachDoc(this, doc); if ((options.autofocus && !mobile) || cm.hasFocus()) setTimeout(bind(onFocus, this), 20); else onBlur(this); for (var opt in optionHandlers) if (optionHandlers.hasOwnProperty(opt)) optionHandlers[opt](this, options[opt], Init); maybeUpdateLineNumberWidth(this); if (options.finishInit) options.finishInit(this); for (var i = 0; i < initHooks.length; ++i) initHooks[i](this); endOperation(this); // Suppress optimizelegibility in Webkit, since it breaks text // measuring on line wrapping boundaries. if (webkit && options.lineWrapping && getComputedStyle(display.lineDiv).textRendering == "optimizelegibility") display.lineDiv.style.textRendering = "auto"; } // DISPLAY CONSTRUCTOR // The display handles the DOM integration, both for input reading // and content drawing. It holds references to DOM nodes and // display-related state. function Display(place, doc, input) { var d = this; this.input = input; // Covers bottom-right square when both scrollbars are present. d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler"); d.scrollbarFiller.setAttribute("cm-not-content", "true"); // Covers bottom of gutter when coverGutterNextToScrollbar is on // and h scrollbar is present. d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler"); d.gutterFiller.setAttribute("cm-not-content", "true"); // Will contain the actual code, positioned to cover the viewport. d.lineDiv = elt("div", null, "CodeMirror-code"); // Elements are added to these to represent selection and cursors. d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1"); d.cursorDiv = elt("div", null, "CodeMirror-cursors"); // A visibility: hidden element used to find the size of things. d.measure = elt("div", null, "CodeMirror-measure"); // When lines outside of the viewport are measured, they are drawn in this. d.lineMeasure = elt("div", null, "CodeMirror-measure"); // Wraps everything that needs to exist inside the vertically-padded coordinate system d.lineSpace = elt("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv], null, "position: relative; outline: none"); // Moved around its parent to cover visible view. d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative"); // Set to the height of the document, allowing scrolling. d.sizer = elt("div", [d.mover], "CodeMirror-sizer"); d.sizerWidth = null; // Behavior of elts with overflow: auto and padding is // inconsistent across browsers. This is used to ensure the // scrollable area is big enough. d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;"); // Will contain the gutters, if any. d.gutters = elt("div", null, "CodeMirror-gutters"); d.lineGutter = null; // Actual scrollable element. d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll"); d.scroller.setAttribute("tabIndex", "-1"); // The element in which the editor lives. d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror"); // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported) if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; } if (!webkit && !(gecko && mobile)) d.scroller.draggable = true; if (place) { if (place.appendChild) place.appendChild(d.wrapper); else place(d.wrapper); } // Current rendered range (may be bigger than the view window). d.viewFrom = d.viewTo = doc.first; d.reportedViewFrom = d.reportedViewTo = doc.first; // Information about the rendered lines. d.view = []; d.renderedView = null; // Holds info about a single rendered line when it was rendered // for measurement, while not in view. d.externalMeasured = null; // Empty space (in pixels) above the view d.viewOffset = 0; d.lastWrapHeight = d.lastWrapWidth = 0; d.updateLineNumbers = null; d.nativeBarWidth = d.barHeight = d.barWidth = 0; d.scrollbarsClipped = false; // Used to only resize the line number gutter when necessary (when // the amount of lines crosses a boundary that makes its width change) d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null; // Set to true when a non-horizontal-scrolling line widget is // added. As an optimization, line widget aligning is skipped when // this is false. d.alignWidgets = false; d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; // Tracks the maximum line length so that the horizontal scrollbar // can be kept static when scrolling. d.maxLine = null; d.maxLineLength = 0; d.maxLineChanged = false; // Used for measuring wheel scrolling granularity d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null; // True when shift is held down. d.shift = false; // Used to track whether anything happened since the context menu // was opened. d.selForContextMenu = null; d.activeTouch = null; input.init(d); } // STATE UPDATES // Used to get the editor into a consistent state again when options change. function loadMode(cm) { cm.doc.mode = CodeMirror.getMode(cm.options, cm.doc.modeOption); resetModeState(cm); } function resetModeState(cm) { cm.doc.iter(function(line) { if (line.stateAfter) line.stateAfter = null; if (line.styles) line.styles = null; }); cm.doc.frontier = cm.doc.first; startWorker(cm, 100); cm.state.modeGen++; if (cm.curOp) regChange(cm); } function wrappingChanged(cm) { if (cm.options.lineWrapping) { addClass(cm.display.wrapper, "CodeMirror-wrap"); cm.display.sizer.style.minWidth = ""; cm.display.sizerWidth = null; } else { rmClass(cm.display.wrapper, "CodeMirror-wrap"); findMaxLine(cm); } estimateLineHeights(cm); regChange(cm); clearCaches(cm); setTimeout(function(){updateScrollbars(cm);}, 100); } // Returns a function that estimates the height of a line, to use as // first approximation until the line becomes visible (and is thus // properly measurable). function estimateHeight(cm) { var th = textHeight(cm.display), wrapping = cm.options.lineWrapping; var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3); return function(line) { if (lineIsHidden(cm.doc, line)) return 0; var widgetsHeight = 0; if (line.widgets) for (var i = 0; i < line.widgets.length; i++) { if (line.widgets[i].height) widgetsHeight += line.widgets[i].height; } if (wrapping) return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th; else return widgetsHeight + th; }; } function estimateLineHeights(cm) { var doc = cm.doc, est = estimateHeight(cm); doc.iter(function(line) { var estHeight = est(line); if (estHeight != line.height) updateLineHeight(line, estHeight); }); } function themeChanged(cm) { cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") + cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-"); clearCaches(cm); } function guttersChanged(cm) { updateGutters(cm); regChange(cm); setTimeout(function(){alignHorizontally(cm);}, 20); } // Rebuild the gutter elements, ensure the margin to the left of the // code matches their width. function updateGutters(cm) { var gutters = cm.display.gutters, specs = cm.options.gutters; removeChildren(gutters); for (var i = 0; i < specs.length; ++i) { var gutterClass = specs[i]; var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass)); if (gutterClass == "CodeMirror-linenumbers") { cm.display.lineGutter = gElt; gElt.style.width = (cm.display.lineNumWidth || 1) + "px"; } } gutters.style.display = i ? "" : "none"; updateGutterSpace(cm); } function updateGutterSpace(cm) { var width = cm.display.gutters.offsetWidth; cm.display.sizer.style.marginLeft = width + "px"; } // Compute the character length of a line, taking into account // collapsed ranges (see markText) that might hide parts, and join // other lines onto it. function lineLength(line) { if (line.height == 0) return 0; var len = line.text.length, merged, cur = line; while (merged = collapsedSpanAtStart(cur)) { var found = merged.find(0, true); cur = found.from.line; len += found.from.ch - found.to.ch; } cur = line; while (merged = collapsedSpanAtEnd(cur)) { var found = merged.find(0, true); len -= cur.text.length - found.from.ch; cur = found.to.line; len += cur.text.length - found.to.ch; } return len; } // Find the longest line in the document. function findMaxLine(cm) { var d = cm.display, doc = cm.doc; d.maxLine = getLine(doc, doc.first); d.maxLineLength = lineLength(d.maxLine); d.maxLineChanged = true; doc.iter(function(line) { var len = lineLength(line); if (len > d.maxLineLength) { d.maxLineLength = len; d.maxLine = line; } }); } // Make sure the gutters options contains the element // "CodeMirror-linenumbers" when the lineNumbers option is true. function setGuttersForLineNumbers(options) { var found = indexOf(options.gutters, "CodeMirror-linenumbers"); if (found == -1 && options.lineNumbers) { options.gutters = options.gutters.concat(["CodeMirror-linenumbers"]); } else if (found > -1 && !options.lineNumbers) { options.gutters = options.gutters.slice(0); options.gutters.splice(found, 1); } } // SCROLLBARS // Prepare DOM reads needed to update the scrollbars. Done in one // shot to minimize update/measure roundtrips. function measureForScrollbars(cm) { var d = cm.display, gutterW = d.gutters.offsetWidth; var docH = Math.round(cm.doc.height + paddingVert(cm.display)); return { clientHeight: d.scroller.clientHeight, viewHeight: d.wrapper.clientHeight, scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth, viewWidth: d.wrapper.clientWidth, barLeft: cm.options.fixedGutter ? gutterW : 0, docHeight: docH, scrollHeight: docH + scrollGap(cm) + d.barHeight, nativeBarWidth: d.nativeBarWidth, gutterWidth: gutterW }; } function NativeScrollbars(place, scroll, cm) { this.cm = cm; var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar"); var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar"); place(vert); place(horiz); on(vert, "scroll", function() { if (vert.clientHeight) scroll(vert.scrollTop, "vertical"); }); on(horiz, "scroll", function() { if (horiz.clientWidth) scroll(horiz.scrollLeft, "horizontal"); }); this.checkedZeroWidth = false; // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8). if (ie && ie_version < 8) this.horiz.style.minHeight = this.vert.style.minWidth = "18px"; } NativeScrollbars.prototype = copyObj({ update: function(measure) { var needsH = measure.scrollWidth > measure.clientWidth + 1; var needsV = measure.scrollHeight > measure.clientHeight + 1; var sWidth = measure.nativeBarWidth; if (needsV) { this.vert.style.display = "block"; this.vert.style.bottom = needsH ? sWidth + "px" : "0"; var totalHeight = measure.viewHeight - (needsH ? sWidth : 0); // A bug in IE8 can cause this value to be negative, so guard it. this.vert.firstChild.style.height = Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px"; } else { this.vert.style.display = ""; this.vert.firstChild.style.height = "0"; } if (needsH) { this.horiz.style.display = "block"; this.horiz.style.right = needsV ? sWidth + "px" : "0"; this.horiz.style.left = measure.barLeft + "px"; var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0); this.horiz.firstChild.style.width = (measure.scrollWidth - measure.clientWidth + totalWidth) + "px"; } else { this.horiz.style.display = ""; this.horiz.firstChild.style.width = "0"; } if (!this.checkedZeroWidth && measure.clientHeight > 0) { if (sWidth == 0) this.zeroWidthHack(); this.checkedZeroWidth = true; } return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0}; }, setScrollLeft: function(pos) { if (this.horiz.scrollLeft != pos) this.horiz.scrollLeft = pos; if (this.disableHoriz) this.enableZeroWidthBar(this.horiz, this.disableHoriz); }, setScrollTop: function(pos) { if (this.vert.scrollTop != pos) this.vert.scrollTop = pos; if (this.disableVert) this.enableZeroWidthBar(this.vert, this.disableVert); }, zeroWidthHack: function() { var w = mac && !mac_geMountainLion ? "12px" : "18px"; this.horiz.style.height = this.vert.style.width = w; this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none"; this.disableHoriz = new Delayed; this.disableVert = new Delayed; }, enableZeroWidthBar: function(bar, delay) { bar.style.pointerEvents = "auto"; function maybeDisable() { // To find out whether the scrollbar is still visible, we // check whether the element under the pixel in the bottom // left corner of the scrollbar box is the scrollbar box // itself (when the bar is still visible) or its filler child // (when the bar is hidden). If it is still visible, we keep // it enabled, if it's hidden, we disable pointer events. var box = bar.getBoundingClientRect(); var elt = document.elementFromPoint(box.left + 1, box.bottom - 1); if (elt != bar) bar.style.pointerEvents = "none"; else delay.set(1000, maybeDisable); } delay.set(1000, maybeDisable); }, clear: function() { var parent = this.horiz.parentNode; parent.removeChild(this.horiz); parent.removeChild(this.vert); } }, NativeScrollbars.prototype); function NullScrollbars() {} NullScrollbars.prototype = copyObj({ update: function() { return {bottom: 0, right: 0}; }, setScrollLeft: function() {}, setScrollTop: function() {}, clear: function() {} }, NullScrollbars.prototype); CodeMirror.scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars}; function initScrollbars(cm) { if (cm.display.scrollbars) { cm.display.scrollbars.clear(); if (cm.display.scrollbars.addClass) rmClass(cm.display.wrapper, cm.display.scrollbars.addClass); } cm.display.scrollbars = new CodeMirror.scrollbarModel[cm.options.scrollbarStyle](function(node) { cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller); // Prevent clicks in the scrollbars from killing focus on(node, "mousedown", function() { if (cm.state.focused) setTimeout(function() { cm.display.input.focus(); }, 0); }); node.setAttribute("cm-not-content", "true"); }, function(pos, axis) { if (axis == "horizontal") setScrollLeft(cm, pos); else setScrollTop(cm, pos); }, cm); if (cm.display.scrollbars.addClass) addClass(cm.display.wrapper, cm.display.scrollbars.addClass); } function updateScrollbars(cm, measure) { if (!measure) measure = measureForScrollbars(cm); var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight; updateScrollbarsInner(cm, measure); for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) { if (startWidth != cm.display.barWidth && cm.options.lineWrapping) updateHeightsInViewport(cm); updateScrollbarsInner(cm, measureForScrollbars(cm)); startWidth = cm.display.barWidth; startHeight = cm.display.barHeight; } } // Re-synchronize the fake scrollbars with the actual size of the // content. function updateScrollbarsInner(cm, measure) { var d = cm.display; var sizes = d.scrollbars.update(measure); d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px"; d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px"; d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent" if (sizes.right && sizes.bottom) { d.scrollbarFiller.style.display = "block"; d.scrollbarFiller.style.height = sizes.bottom + "px"; d.scrollbarFiller.style.width = sizes.right + "px"; } else d.scrollbarFiller.style.display = ""; if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) { d.gutterFiller.style.display = "block"; d.gutterFiller.style.height = sizes.bottom + "px"; d.gutterFiller.style.width = measure.gutterWidth + "px"; } else d.gutterFiller.style.display = ""; } // Compute the lines that are visible in a given viewport (defaults // the the current scroll position). viewport may contain top, // height, and ensure (see op.scrollToPos) properties. function visibleLines(display, doc, viewport) { var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop; top = Math.floor(top - paddingTop(display)); var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight; var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom); // Ensure is a {from: {line, ch}, to: {line, ch}} object, and // forces those lines into the viewport (if possible). if (viewport && viewport.ensure) { var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line; if (ensureFrom < from) { from = ensureFrom; to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight); } else if (Math.min(ensureTo, doc.lastLine()) >= to) { from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight); to = ensureTo; } } return {from: from, to: Math.max(to, from + 1)}; } // LINE NUMBERS // Re-align line numbers and gutter marks to compensate for // horizontal scrolling. function alignHorizontally(cm) { var display = cm.display, view = display.view; if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return; var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft; var gutterW = display.gutters.offsetWidth, left = comp + "px"; for (var i = 0; i < view.length; i++) if (!view[i].hidden) { if (cm.options.fixedGutter && view[i].gutter) view[i].gutter.style.left = left; var align = view[i].alignable; if (align) for (var j = 0; j < align.length; j++) align[j].style.left = left; } if (cm.options.fixedGutter) display.gutters.style.left = (comp + gutterW) + "px"; } // Used to ensure that the line number gutter is still the right // size for the current document size. Returns true when an update // is needed. function maybeUpdateLineNumberWidth(cm) { if (!cm.options.lineNumbers) return false; var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display; if (last.length != display.lineNumChars) { var test = display.measure.appendChild(elt("div", [elt("div", last)], "CodeMirror-linenumber CodeMirror-gutter-elt")); var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW; display.lineGutter.style.width = ""; display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1; display.lineNumWidth = display.lineNumInnerWidth + padding; display.lineNumChars = display.lineNumInnerWidth ? last.length : -1; display.lineGutter.style.width = display.lineNumWidth + "px"; updateGutterSpace(cm); return true; } return false; } function lineNumberFor(options, i) { return String(options.lineNumberFormatter(i + options.firstLineNumber)); } // Computes display.scroller.scrollLeft + display.gutters.offsetWidth, // but using getBoundingClientRect to get a sub-pixel-accurate // result. function compensateForHScroll(display) { return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left; } // DISPLAY DRAWING function DisplayUpdate(cm, viewport, force) { var display = cm.display; this.viewport = viewport; // Store some values that we'll need later (but don't want to force a relayout for) this.visible = visibleLines(display, cm.doc, viewport); this.editorIsHidden = !display.wrapper.offsetWidth; this.wrapperHeight = display.wrapper.clientHeight; this.wrapperWidth = display.wrapper.clientWidth; this.oldDisplayWidth = displayWidth(cm); this.force = force; this.dims = getDimensions(cm); this.events = []; } DisplayUpdate.prototype.signal = function(emitter, type) { if (hasHandler(emitter, type)) this.events.push(arguments); }; DisplayUpdate.prototype.finish = function() { for (var i = 0; i < this.events.length; i++) signal.apply(null, this.events[i]); }; function maybeClipScrollbars(cm) { var display = cm.display; if (!display.scrollbarsClipped && display.scroller.offsetWidth) { display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth; display.heightForcer.style.height = scrollGap(cm) + "px"; display.sizer.style.marginBottom = -display.nativeBarWidth + "px"; display.sizer.style.borderRightWidth = scrollGap(cm) + "px"; display.scrollbarsClipped = true; } } // Does the actual updating of the line display. Bails out // (returning false) when there is nothing to be done and forced is // false. function updateDisplayIfNeeded(cm, update) { var display = cm.display, doc = cm.doc; if (update.editorIsHidden) { resetView(cm); return false; } // Bail out if the visible area is already rendered and nothing changed. if (!update.force && update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo && (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) && display.renderedView == display.view && countDirtyView(cm) == 0) return false; if (maybeUpdateLineNumberWidth(cm)) { resetView(cm); update.dims = getDimensions(cm); } // Compute a suitable new viewport (from & to) var end = doc.first + doc.size; var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first); var to = Math.min(end, update.visible.to + cm.options.viewportMargin); if (display.viewFrom < from && from - display.viewFrom < 20) from = Math.max(doc.first, display.viewFrom); if (display.viewTo > to && display.viewTo - to < 20) to = Math.min(end, display.viewTo); if (sawCollapsedSpans) { from = visualLineNo(cm.doc, from); to = visualLineEndNo(cm.doc, to); } var different = from != display.viewFrom || to != display.viewTo || display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth; adjustView(cm, from, to); display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom)); // Position the mover div to align with the current scroll position cm.display.mover.style.top = display.viewOffset + "px"; var toUpdate = countDirtyView(cm); if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view && (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo)) return false; // For big changes, we hide the enclosing element during the // update, since that speeds up the operations on most browsers. var focused = activeElt(); if (toUpdate > 4) display.lineDiv.style.display = "none"; patchDisplay(cm, display.updateLineNumbers, update.dims); if (toUpdate > 4) display.lineDiv.style.display = ""; display.renderedView = display.view; // There might have been a widget with a focused element that got // hidden or updated, if so re-focus it. if (focused && activeElt() != focused && focused.offsetHeight) focused.focus(); // Prevent selection and cursors from interfering with the scroll // width and height. removeChildren(display.cursorDiv); removeChildren(display.selectionDiv); display.gutters.style.height = display.sizer.style.minHeight = 0; if (different) { display.lastWrapHeight = update.wrapperHeight; display.lastWrapWidth = update.wrapperWidth; startWorker(cm, 400); } display.updateLineNumbers = null; return true; } function postUpdateDisplay(cm, update) { var viewport = update.viewport; for (var first = true;; first = false) { if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) { // Clip forced viewport to actual scrollable area. if (viewport && viewport.top != null) viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)}; // Updated line heights might result in the drawn area not // actually covering the viewport. Keep looping until it does. update.visible = visibleLines(cm.display, cm.doc, viewport); if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo) break; } if (!updateDisplayIfNeeded(cm, update)) break; updateHeightsInViewport(cm); var barMeasure = measureForScrollbars(cm); updateSelection(cm); updateScrollbars(cm, barMeasure); setDocumentHeight(cm, barMeasure); } update.signal(cm, "update", cm); if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) { update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo); cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo; } } function updateDisplaySimple(cm, viewport) { var update = new DisplayUpdate(cm, viewport); if (updateDisplayIfNeeded(cm, update)) { updateHeightsInViewport(cm); postUpdateDisplay(cm, update); var barMeasure = measureForScrollbars(cm); updateSelection(cm); updateScrollbars(cm, barMeasure); setDocumentHeight(cm, barMeasure); update.finish(); } } function setDocumentHeight(cm, measure) { cm.display.sizer.style.minHeight = measure.docHeight + "px"; cm.display.heightForcer.style.top = measure.docHeight + "px"; cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px"; } // Read the actual heights of the rendered lines, and update their // stored heights to match. function updateHeightsInViewport(cm) { var display = cm.display; var prevBottom = display.lineDiv.offsetTop; for (var i = 0; i < display.view.length; i++) { var cur = display.view[i], height; if (cur.hidden) continue; if (ie && ie_version < 8) { var bot = cur.node.offsetTop + cur.node.offsetHeight; height = bot - prevBottom; prevBottom = bot; } else { var box = cur.node.getBoundingClientRect(); height = box.bottom - box.top; } var diff = cur.line.height - height; if (height < 2) height = textHeight(display); if (diff > .001 || diff < -.001) { updateLineHeight(cur.line, height); updateWidgetHeight(cur.line); if (cur.rest) for (var j = 0; j < cur.rest.length; j++) updateWidgetHeight(cur.rest[j]); } } } // Read and store the height of line widgets associated with the // given line. function updateWidgetHeight(line) { if (line.widgets) for (var i = 0; i < line.widgets.length; ++i) line.widgets[i].height = line.widgets[i].node.parentNode.offsetHeight; } // Do a bulk-read of the DOM positions and sizes needed to draw the // view, so that we don't interleave reading and writing to the DOM. function getDimensions(cm) { var d = cm.display, left = {}, width = {}; var gutterLeft = d.gutters.clientLeft; for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) { left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft; width[cm.options.gutters[i]] = n.clientWidth; } return {fixedPos: compensateForHScroll(d), gutterTotalWidth: d.gutters.offsetWidth, gutterLeft: left, gutterWidth: width, wrapperWidth: d.wrapper.clientWidth}; } // Sync the actual display DOM structure with display.view, removing // nodes for lines that are no longer in view, and creating the ones // that are not there yet, and updating the ones that are out of // date. function patchDisplay(cm, updateNumbersFrom, dims) { var display = cm.display, lineNumbers = cm.options.lineNumbers; var container = display.lineDiv, cur = container.firstChild; function rm(node) { var next = node.nextSibling; // Works around a throw-scroll bug in OS X Webkit if (webkit && mac && cm.display.currentWheelTarget == node) node.style.display = "none"; else node.parentNode.removeChild(node); return next; } var view = display.view, lineN = display.viewFrom; // Loop over the elements in the view, syncing cur (the DOM nodes // in display.lineDiv) with the view as we go. for (var i = 0; i < view.length; i++) { var lineView = view[i]; if (lineView.hidden) { } else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet var node = buildLineElement(cm, lineView, lineN, dims); container.insertBefore(node, cur); } else { // Already drawn while (cur != lineView.node) cur = rm(cur); var updateNumber = lineNumbers && updateNumbersFrom != null && updateNumbersFrom <= lineN && lineView.lineNumber; if (lineView.changes) { if (indexOf(lineView.changes, "gutter") > -1) updateNumber = false; updateLineForChanges(cm, lineView, lineN, dims); } if (updateNumber) { removeChildren(lineView.lineNumber); lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN))); } cur = lineView.node.nextSibling; } lineN += lineView.size; } while (cur) cur = rm(cur); } // When an aspect of a line changes, a string is added to // lineView.changes. This updates the relevant part of the line's // DOM structure. function updateLineForChanges(cm, lineView, lineN, dims) { for (var j = 0; j < lineView.changes.length; j++) { var type = lineView.changes[j]; if (type == "text") updateLineText(cm, lineView); else if (type == "gutter") updateLineGutter(cm, lineView, lineN, dims); else if (type == "class") updateLineClasses(lineView); else if (type == "widget") updateLineWidgets(cm, lineView, dims); } lineView.changes = null; } // Lines with gutter elements, widgets or a background class need to // be wrapped, and have the extra elements added to the wrapper div function ensureLineWrapped(lineView) { if (lineView.node == lineView.text) { lineView.node = elt("div", null, null, "position: relative"); if (lineView.text.parentNode) lineView.text.parentNode.replaceChild(lineView.node, lineView.text); lineView.node.appendChild(lineView.text); if (ie && ie_version < 8) lineView.node.style.zIndex = 2; } return lineView.node; } function updateLineBackground(lineView) { var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass; if (cls) cls += " CodeMirror-linebackground"; if (lineView.background) { if (cls) lineView.background.className = cls; else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; } } else if (cls) { var wrap = ensureLineWrapped(lineView); lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild); } } // Wrapper around buildLineContent which will reuse the structure // in display.externalMeasured when possible. function getLineContent(cm, lineView) { var ext = cm.display.externalMeasured; if (ext && ext.line == lineView.line) { cm.display.externalMeasured = null; lineView.measure = ext.measure; return ext.built; } return buildLineContent(cm, lineView); } // Redraw the line's text. Interacts with the background and text // classes because the mode may output tokens that influence these // classes. function updateLineText(cm, lineView) { var cls = lineView.text.className; var built = getLineContent(cm, lineView); if (lineView.text == lineView.node) lineView.node = built.pre; lineView.text.parentNode.replaceChild(built.pre, lineView.text); lineView.text = built.pre; if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) { lineView.bgClass = built.bgClass; lineView.textClass = built.textClass; updateLineClasses(lineView); } else if (cls) { lineView.text.className = cls; } } function updateLineClasses(lineView) { updateLineBackground(lineView); if (lineView.line.wrapClass) ensureLineWrapped(lineView).className = lineView.line.wrapClass; else if (lineView.node != lineView.text) lineView.node.className = ""; var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass; lineView.text.className = textClass || ""; } function updateLineGutter(cm, lineView, lineN, dims) { if (lineView.gutter) { lineView.node.removeChild(lineView.gutter); lineView.gutter = null; } if (lineView.gutterBackground) { lineView.node.removeChild(lineView.gutterBackground); lineView.gutterBackground = null; } if (lineView.line.gutterClass) { var wrap = ensureLineWrapped(lineView); lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass, "left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + dims.gutterTotalWidth + "px"); wrap.insertBefore(lineView.gutterBackground, lineView.text); } var markers = lineView.line.gutterMarkers; if (cm.options.lineNumbers || markers) { var wrap = ensureLineWrapped(lineView); var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", "left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"); cm.display.input.setUneditable(gutterWrap); wrap.insertBefore(gutterWrap, lineView.text); if (lineView.line.gutterClass) gutterWrap.className += " " + lineView.line.gutterClass; if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"])) lineView.lineNumber = gutterWrap.appendChild( elt("div", lineNumberFor(cm.options, lineN), "CodeMirror-linenumber CodeMirror-gutter-elt", "left: " + dims.gutterLeft["CodeMirror-linenumbers"] + "px; width: " + cm.display.lineNumInnerWidth + "px")); if (markers) for (var k = 0; k < cm.options.gutters.length; ++k) { var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id]; if (found) gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "left: " + dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px")); } } } function updateLineWidgets(cm, lineView, dims) { if (lineView.alignable) lineView.alignable = null; for (var node = lineView.node.firstChild, next; node; node = next) { var next = node.nextSibling; if (node.className == "CodeMirror-linewidget") lineView.node.removeChild(node); } insertLineWidgets(cm, lineView, dims); } // Build a line's DOM representation from scratch function buildLineElement(cm, lineView, lineN, dims) { var built = getLineContent(cm, lineView); lineView.text = lineView.node = built.pre; if (built.bgClass) lineView.bgClass = built.bgClass; if (built.textClass) lineView.textClass = built.textClass; updateLineClasses(lineView); updateLineGutter(cm, lineView, lineN, dims); insertLineWidgets(cm, lineView, dims); return lineView.node; } // A lineView may contain multiple logical lines (when merged by // collapsed spans). The widgets for all of them need to be drawn. function insertLineWidgets(cm, lineView, dims) { insertLineWidgetsFor(cm, lineView.line, lineView, dims, true); if (lineView.rest) for (var i = 0; i < lineView.rest.length; i++) insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false); } function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) { if (!line.widgets) return; var wrap = ensureLineWrapped(lineView); for (var i = 0, ws = line.widgets; i < ws.length; ++i) { var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget"); if (!widget.handleMouseEvents) node.setAttribute("cm-ignore-events", "true"); positionLineWidget(widget, node, lineView, dims); cm.display.input.setUneditable(node); if (allowAbove && widget.above) wrap.insertBefore(node, lineView.gutter || lineView.text); else wrap.appendChild(node); signalLater(widget, "redraw"); } } function positionLineWidget(widget, node, lineView, dims) { if (widget.noHScroll) { (lineView.alignable || (lineView.alignable = [])).push(node); var width = dims.wrapperWidth; node.style.left = dims.fixedPos + "px"; if (!widget.coverGutter) { width -= dims.gutterTotalWidth; node.style.paddingLeft = dims.gutterTotalWidth + "px"; } node.style.width = width + "px"; } if (widget.coverGutter) { node.style.zIndex = 5; node.style.position = "relative"; if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px"; } } // POSITION OBJECT // A Pos instance represents a position within the text. var Pos = CodeMirror.Pos = function(line, ch) { if (!(this instanceof Pos)) return new Pos(line, ch); this.line = line; this.ch = ch; }; // Compare two positions, return 0 if they are the same, a negative // number when a is less, and a positive number otherwise. var cmp = CodeMirror.cmpPos = function(a, b) { return a.line - b.line || a.ch - b.ch; }; function copyPos(x) {return Pos(x.line, x.ch);} function maxPos(a, b) { return cmp(a, b) < 0 ? b : a; } function minPos(a, b) { return cmp(a, b) < 0 ? a : b; } // INPUT HANDLING function ensureFocus(cm) { if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); } } // This will be set to a {lineWise: bool, text: [string]} object, so // that, when pasting, we know what kind of selections the copied // text was made out of. var lastCopied = null; function applyTextInput(cm, inserted, deleted, sel, origin) { var doc = cm.doc; cm.display.shift = false; if (!sel) sel = doc.sel; var paste = cm.state.pasteIncoming || origin == "paste"; var textLines = doc.splitLines(inserted), multiPaste = null // When pasing N lines into N selections, insert one line per selection if (paste && sel.ranges.length > 1) { if (lastCopied && lastCopied.text.join("\n") == inserted) { if (sel.ranges.length % lastCopied.text.length == 0) { multiPaste = []; for (var i = 0; i < lastCopied.text.length; i++) multiPaste.push(doc.splitLines(lastCopied.text[i])); } } else if (textLines.length == sel.ranges.length) { multiPaste = map(textLines, function(l) { return [l]; }); } } // Normal behavior is to insert the new text into every selection for (var i = sel.ranges.length - 1; i >= 0; i--) { var range = sel.ranges[i]; var from = range.from(), to = range.to(); if (range.empty()) { if (deleted && deleted > 0) // Handle deletion from = Pos(from.line, from.ch - deleted); else if (cm.state.overwrite && !paste) // Handle overwrite to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); else if (lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted) from = to = Pos(from.line, 0) } var updateInput = cm.curOp.updateInput; var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i % multiPaste.length] : textLines, origin: origin || (paste ? "paste" : cm.state.cutIncoming ? "cut" : "+input")}; makeChange(cm.doc, changeEvent); signalLater(cm, "inputRead", cm, changeEvent); } if (inserted && !paste) triggerElectric(cm, inserted); ensureCursorVisible(cm); cm.curOp.updateInput = updateInput; cm.curOp.typing = true; cm.state.pasteIncoming = cm.state.cutIncoming = false; } function handlePaste(e, cm) { var pasted = e.clipboardData && e.clipboardData.getData("text/plain"); if (pasted) { e.preventDefault(); if (!cm.isReadOnly() && !cm.options.disableInput) runInOp(cm, function() { applyTextInput(cm, pasted, 0, null, "paste"); }); return true; } } function triggerElectric(cm, inserted) { // When an 'electric' character is inserted, immediately trigger a reindent if (!cm.options.electricChars || !cm.options.smartIndent) return; var sel = cm.doc.sel; for (var i = sel.ranges.length - 1; i >= 0; i--) { var range = sel.ranges[i]; if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) continue; var mode = cm.getModeAt(range.head); var indented = false; if (mode.electricChars) { for (var j = 0; j < mode.electricChars.length; j++) if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) { indented = indentLine(cm, range.head.line, "smart"); break; } } else if (mode.electricInput) { if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice(0, range.head.ch))) indented = indentLine(cm, range.head.line, "smart"); } if (indented) signalLater(cm, "electricInput", cm, range.head.line); } } function copyableRanges(cm) { var text = [], ranges = []; for (var i = 0; i < cm.doc.sel.ranges.length; i++) { var line = cm.doc.sel.ranges[i].head.line; var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)}; ranges.push(lineRange); text.push(cm.getRange(lineRange.anchor, lineRange.head)); } return {text: text, ranges: ranges}; } function disableBrowserMagic(field) { field.setAttribute("autocorrect", "off"); field.setAttribute("autocapitalize", "off"); field.setAttribute("spellcheck", "false"); } // TEXTAREA INPUT STYLE function TextareaInput(cm) { this.cm = cm; // See input.poll and input.reset this.prevInput = ""; // Flag that indicates whether we expect input to appear real soon // now (after some event like 'keypress' or 'input') and are // polling intensively. this.pollingFast = false; // Self-resetting timeout for the poller this.polling = new Delayed(); // Tracks when input.reset has punted to just putting a short // string into the textarea instead of the full selection. this.inaccurateSelection = false; // Used to work around IE issue with selection being forgotten when focus moves away from textarea this.hasSelection = false; this.composing = null; }; function hiddenTextarea() { var te = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none"); var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;"); // The textarea is kept positioned near the cursor to prevent the // fact that it'll be scrolled into view on input from scrolling // our fake cursor out of view. On webkit, when wrap=off, paste is // very slow. So make the area wide instead. if (webkit) te.style.width = "1000px"; else te.setAttribute("wrap", "off"); // If border: 0; -- iOS fails to open keyboard (issue #1287) if (ios) te.style.border = "1px solid black"; disableBrowserMagic(te); return div; } TextareaInput.prototype = copyObj({ init: function(display) { var input = this, cm = this.cm; // Wraps and hides input textarea var div = this.wrapper = hiddenTextarea(); // The semihidden textarea that is focused when the editor is // focused, and receives input. var te = this.textarea = div.firstChild; display.wrapper.insertBefore(div, display.wrapper.firstChild); // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore) if (ios) te.style.width = "0px"; on(te, "input", function() { if (ie && ie_version >= 9 && input.hasSelection) input.hasSelection = null; input.poll(); }); on(te, "paste", function(e) { if (signalDOMEvent(cm, e) || handlePaste(e, cm)) return cm.state.pasteIncoming = true; input.fastPoll(); }); function prepareCopyCut(e) { if (signalDOMEvent(cm, e)) return if (cm.somethingSelected()) { lastCopied = {lineWise: false, text: cm.getSelections()}; if (input.inaccurateSelection) { input.prevInput = ""; input.inaccurateSelection = false; te.value = lastCopied.text.join("\n"); selectInput(te); } } else if (!cm.options.lineWiseCopyCut) { return; } else { var ranges = copyableRanges(cm); lastCopied = {lineWise: true, text: ranges.text}; if (e.type == "cut") { cm.setSelections(ranges.ranges, null, sel_dontScroll); } else { input.prevInput = ""; te.value = ranges.text.join("\n"); selectInput(te); } } if (e.type == "cut") cm.state.cutIncoming = true; } on(te, "cut", prepareCopyCut); on(te, "copy", prepareCopyCut); on(display.scroller, "paste", function(e) { if (eventInWidget(display, e) || signalDOMEvent(cm, e)) return; cm.state.pasteIncoming = true; input.focus(); }); // Prevent normal selection in the editor (we handle our own) on(display.lineSpace, "selectstart", function(e) { if (!eventInWidget(display, e)) e_preventDefault(e); }); on(te, "compositionstart", function() { var start = cm.getCursor("from"); if (input.composing) input.composing.range.clear() input.composing = { start: start, range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"}) }; }); on(te, "compositionend", function() { if (input.composing) { input.poll(); input.composing.range.clear(); input.composing = null; } }); }, prepareSelection: function() { // Redraw the selection and/or cursor var cm = this.cm, display = cm.display, doc = cm.doc; var result = prepareSelection(cm); // Move the hidden textarea near the cursor to prevent scrolling artifacts if (cm.options.moveInputWithCursor) { var headPos = cursorCoords(cm, doc.sel.primary().head, "div"); var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect(); result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10, headPos.top + lineOff.top - wrapOff.top)); result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10, headPos.left + lineOff.left - wrapOff.left)); } return result; }, showSelection: function(drawn) { var cm = this.cm, display = cm.display; removeChildrenAndAdd(display.cursorDiv, drawn.cursors); removeChildrenAndAdd(display.selectionDiv, drawn.selection); if (drawn.teTop != null) { this.wrapper.style.top = drawn.teTop + "px"; this.wrapper.style.left = drawn.teLeft + "px"; } }, // Reset the input to correspond to the selection (or to be empty, // when not typing and nothing is selected) reset: function(typing) { if (this.contextMenuPending) return; var minimal, selected, cm = this.cm, doc = cm.doc; if (cm.somethingSelected()) { this.prevInput = ""; var range = doc.sel.primary(); minimal = hasCopyEvent && (range.to().line - range.from().line > 100 || (selected = cm.getSelection()).length > 1000); var content = minimal ? "-" : selected || cm.getSelection(); this.textarea.value = content; if (cm.state.focused) selectInput(this.textarea); if (ie && ie_version >= 9) this.hasSelection = content; } else if (!typing) { this.prevInput = this.textarea.value = ""; if (ie && ie_version >= 9) this.hasSelection = null; } this.inaccurateSelection = minimal; }, getField: function() { return this.textarea; }, supportsTouch: function() { return false; }, focus: function() { if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) { try { this.textarea.focus(); } catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM } }, blur: function() { this.textarea.blur(); }, resetPosition: function() { this.wrapper.style.top = this.wrapper.style.left = 0; }, receivedFocus: function() { this.slowPoll(); }, // Poll for input changes, using the normal rate of polling. This // runs as long as the editor is focused. slowPoll: function() { var input = this; if (input.pollingFast) return; input.polling.set(this.cm.options.pollInterval, function() { input.poll(); if (input.cm.state.focused) input.slowPoll(); }); }, // When an event has just come in that is likely to add or change // something in the input textarea, we poll faster, to ensure that // the change appears on the screen quickly. fastPoll: function() { var missed = false, input = this; input.pollingFast = true; function p() { var changed = input.poll(); if (!changed && !missed) {missed = true; input.polling.set(60, p);} else {input.pollingFast = false; input.slowPoll();} } input.polling.set(20, p); }, // Read input from the textarea, and update the document to match. // When something is selected, it is present in the textarea, and // selected (unless it is huge, in which case a placeholder is // used). When nothing is selected, the cursor sits after previously // seen text (can be empty), which is stored in prevInput (we must // not reset the textarea when typing, because that breaks IME). poll: function() { var cm = this.cm, input = this.textarea, prevInput = this.prevInput; // Since this is called a *lot*, try to bail out as cheaply as // possible when it is clear that nothing happened. hasSelection // will be the case when there is a lot of text in the textarea, // in which case reading its value would be expensive. if (this.contextMenuPending || !cm.state.focused || (hasSelection(input) && !prevInput && !this.composing) || cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq) return false; var text = input.value; // If nothing changed, bail. if (text == prevInput && !cm.somethingSelected()) return false; // Work around nonsensical selection resetting in IE9/10, and // inexplicable appearance of private area unicode characters on // some key combos in Mac (#2689). if (ie && ie_version >= 9 && this.hasSelection === text || mac && /[\uf700-\uf7ff]/.test(text)) { cm.display.input.reset(); return false; } if (cm.doc.sel == cm.display.selForContextMenu) { var first = text.charCodeAt(0); if (first == 0x200b && !prevInput) prevInput = "\u200b"; if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo"); } } // Find the part of the input that is actually new var same = 0, l = Math.min(prevInput.length, text.length); while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same; var self = this; runInOp(cm, function() { applyTextInput(cm, text.slice(same), prevInput.length - same, null, self.composing ? "*compose" : null); // Don't leave long text in the textarea, since it makes further polling slow if (text.length > 1000 || text.indexOf("\n") > -1) input.value = self.prevInput = ""; else self.prevInput = text; if (self.composing) { self.composing.range.clear(); self.composing.range = cm.markText(self.composing.start, cm.getCursor("to"), {className: "CodeMirror-composing"}); } }); return true; }, ensurePolled: function() { if (this.pollingFast && this.poll()) this.pollingFast = false; }, onKeyPress: function() { if (ie && ie_version >= 9) this.hasSelection = null; this.fastPoll(); }, onContextMenu: function(e) { var input = this, cm = input.cm, display = cm.display, te = input.textarea; var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop; if (!pos || presto) return; // Opera is difficult. // Reset the current text selection only if the click is done outside of the selection // and 'resetSelectionOnContextMenu' option is true. var reset = cm.options.resetSelectionOnContextMenu; if (reset && cm.doc.sel.contains(pos) == -1) operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll); var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText; input.wrapper.style.cssText = "position: absolute" var wrapperBox = input.wrapper.getBoundingClientRect() te.style.cssText = "position: absolute; width: 30px; height: 30px; top: " + (e.clientY - wrapperBox.top - 5) + "px; left: " + (e.clientX - wrapperBox.left - 5) + "px; z-index: 1000; background: " + (ie ? "rgba(255, 255, 255, .05)" : "transparent") + "; outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);"; if (webkit) var oldScrollY = window.scrollY; // Work around Chrome issue (#2712) display.input.focus(); if (webkit) window.scrollTo(null, oldScrollY); display.input.reset(); // Adds "Select all" to context menu in FF if (!cm.somethingSelected()) te.value = input.prevInput = " "; input.contextMenuPending = true; display.selForContextMenu = cm.doc.sel; clearTimeout(display.detectingSelectAll); // Select-all will be greyed out if there's nothing to select, so // this adds a zero-width space so that we can later check whether // it got selected. function prepareSelectAllHack() { if (te.selectionStart != null) { var selected = cm.somethingSelected(); var extval = "\u200b" + (selected ? te.value : ""); te.value = "\u21da"; // Used to catch context-menu undo te.value = extval; input.prevInput = selected ? "" : "\u200b"; te.selectionStart = 1; te.selectionEnd = extval.length; // Re-set this, in case some other handler touched the // selection in the meantime. display.selForContextMenu = cm.doc.sel; } } function rehide() { input.contextMenuPending = false; input.wrapper.style.cssText = oldWrapperCSS te.style.cssText = oldCSS; if (ie && ie_version < 9) display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos); // Try to detect the user choosing select-all if (te.selectionStart != null) { if (!ie || (ie && ie_version < 9)) prepareSelectAllHack(); var i = 0, poll = function() { if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 && te.selectionEnd > 0 && input.prevInput == "\u200b") operation(cm, commands.selectAll)(cm); else if (i++ < 10) display.detectingSelectAll = setTimeout(poll, 500); else display.input.reset(); }; display.detectingSelectAll = setTimeout(poll, 200); } } if (ie && ie_version >= 9) prepareSelectAllHack(); if (captureRightClick) { e_stop(e); var mouseup = function() { off(window, "mouseup", mouseup); setTimeout(rehide, 20); }; on(window, "mouseup", mouseup); } else { setTimeout(rehide, 50); } }, readOnlyChanged: function(val) { if (!val) this.reset(); }, setUneditable: nothing, needsContentAttribute: false }, TextareaInput.prototype); // CONTENTEDITABLE INPUT STYLE function ContentEditableInput(cm) { this.cm = cm; this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null; this.polling = new Delayed(); this.gracePeriod = false; } ContentEditableInput.prototype = copyObj({ init: function(display) { var input = this, cm = input.cm; var div = input.div = display.lineDiv; disableBrowserMagic(div); on(div, "paste", function(e) { if (!signalDOMEvent(cm, e)) handlePaste(e, cm); }) on(div, "compositionstart", function(e) { var data = e.data; input.composing = {sel: cm.doc.sel, data: data, startData: data}; if (!data) return; var prim = cm.doc.sel.primary(); var line = cm.getLine(prim.head.line); var found = line.indexOf(data, Math.max(0, prim.head.ch - data.length)); if (found > -1 && found <= prim.head.ch) input.composing.sel = simpleSelection(Pos(prim.head.line, found), Pos(prim.head.line, found + data.length)); }); on(div, "compositionupdate", function(e) { input.composing.data = e.data; }); on(div, "compositionend", function(e) { var ours = input.composing; if (!ours) return; if (e.data != ours.startData && !/\u200b/.test(e.data)) ours.data = e.data; // Need a small delay to prevent other code (input event, // selection polling) from doing damage when fired right after // compositionend. setTimeout(function() { if (!ours.handled) input.applyComposition(ours); if (input.composing == ours) input.composing = null; }, 50); }); on(div, "touchstart", function() { input.forceCompositionEnd(); }); on(div, "input", function() { if (input.composing) return; if (cm.isReadOnly() || !input.pollContent()) runInOp(input.cm, function() {regChange(cm);}); }); function onCopyCut(e) { if (signalDOMEvent(cm, e)) return if (cm.somethingSelected()) { lastCopied = {lineWise: false, text: cm.getSelections()}; if (e.type == "cut") cm.replaceSelection("", null, "cut"); } else if (!cm.options.lineWiseCopyCut) { return; } else { var ranges = copyableRanges(cm); lastCopied = {lineWise: true, text: ranges.text}; if (e.type == "cut") { cm.operation(function() { cm.setSelections(ranges.ranges, 0, sel_dontScroll); cm.replaceSelection("", null, "cut"); }); } } // iOS exposes the clipboard API, but seems to discard content inserted into it if (e.clipboardData && !ios) { e.preventDefault(); e.clipboardData.clearData(); e.clipboardData.setData("text/plain", lastCopied.text.join("\n")); } else { // Old-fashioned briefly-focus-a-textarea hack var kludge = hiddenTextarea(), te = kludge.firstChild; cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild); te.value = lastCopied.text.join("\n"); var hadFocus = document.activeElement; selectInput(te); setTimeout(function() { cm.display.lineSpace.removeChild(kludge); hadFocus.focus(); }, 50); } } on(div, "copy", onCopyCut); on(div, "cut", onCopyCut); }, prepareSelection: function() { var result = prepareSelection(this.cm, false); result.focus = this.cm.state.focused; return result; }, showSelection: function(info, takeFocus) { if (!info || !this.cm.display.view.length) return; if (info.focus || takeFocus) this.showPrimarySelection(); this.showMultipleSelections(info); }, showPrimarySelection: function() { var sel = window.getSelection(), prim = this.cm.doc.sel.primary(); var curAnchor = domToPos(this.cm, sel.anchorNode, sel.anchorOffset); var curFocus = domToPos(this.cm, sel.focusNode, sel.focusOffset); if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad && cmp(minPos(curAnchor, curFocus), prim.from()) == 0 && cmp(maxPos(curAnchor, curFocus), prim.to()) == 0) return; var start = posToDOM(this.cm, prim.from()); var end = posToDOM(this.cm, prim.to()); if (!start && !end) return; var view = this.cm.display.view; var old = sel.rangeCount && sel.getRangeAt(0); if (!start) { start = {node: view[0].measure.map[2], offset: 0}; } else if (!end) { // FIXME dangerously hacky var measure = view[view.length - 1].measure; var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map; end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]}; } try { var rng = range(start.node, start.offset, end.offset, end.node); } catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible if (rng) { if (!gecko && this.cm.state.focused) { sel.collapse(start.node, start.offset); if (!rng.collapsed) sel.addRange(rng); } else { sel.removeAllRanges(); sel.addRange(rng); } if (old && sel.anchorNode == null) sel.addRange(old); else if (gecko) this.startGracePeriod(); } this.rememberSelection(); }, startGracePeriod: function() { var input = this; clearTimeout(this.gracePeriod); this.gracePeriod = setTimeout(function() { input.gracePeriod = false; if (input.selectionChanged()) input.cm.operation(function() { input.cm.curOp.selectionChanged = true; }); }, 20); }, showMultipleSelections: function(info) { removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors); removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection); }, rememberSelection: function() { var sel = window.getSelection(); this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset; this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset; }, selectionInEditor: function() { var sel = window.getSelection(); if (!sel.rangeCount) return false; var node = sel.getRangeAt(0).commonAncestorContainer; return contains(this.div, node); }, focus: function() { if (this.cm.options.readOnly != "nocursor") this.div.focus(); }, blur: function() { this.div.blur(); }, getField: function() { return this.div; }, supportsTouch: function() { return true; }, receivedFocus: function() { var input = this; if (this.selectionInEditor()) this.pollSelection(); else runInOp(this.cm, function() { input.cm.curOp.selectionChanged = true; }); function poll() { if (input.cm.state.focused) { input.pollSelection(); input.polling.set(input.cm.options.pollInterval, poll); } } this.polling.set(this.cm.options.pollInterval, poll); }, selectionChanged: function() { var sel = window.getSelection(); return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset || sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset; }, pollSelection: function() { if (!this.composing && !this.gracePeriod && this.selectionChanged()) { var sel = window.getSelection(), cm = this.cm; this.rememberSelection(); var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset); var head = domToPos(cm, sel.focusNode, sel.focusOffset); if (anchor && head) runInOp(cm, function() { setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll); if (anchor.bad || head.bad) cm.curOp.selectionChanged = true; }); } }, pollContent: function() { var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary(); var from = sel.from(), to = sel.to(); if (from.line < display.viewFrom || to.line > display.viewTo - 1) return false; var fromIndex; if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) { var fromLine = lineNo(display.view[0].line); var fromNode = display.view[0].node; } else { var fromLine = lineNo(display.view[fromIndex].line); var fromNode = display.view[fromIndex - 1].node.nextSibling; } var toIndex = findViewIndex(cm, to.line); if (toIndex == display.view.length - 1) { var toLine = display.viewTo - 1; var toNode = display.lineDiv.lastChild; } else { var toLine = lineNo(display.view[toIndex + 1].line) - 1; var toNode = display.view[toIndex + 1].node.previousSibling; } var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine)); var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length)); while (newText.length > 1 && oldText.length > 1) { if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; } else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; } else break; } var cutFront = 0, cutEnd = 0; var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length); while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront)) ++cutFront; var newBot = lst(newText), oldBot = lst(oldText); var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0), oldBot.length - (oldText.length == 1 ? cutFront : 0)); while (cutEnd < maxCutEnd && newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) ++cutEnd; newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd); newText[0] = newText[0].slice(cutFront); var chFrom = Pos(fromLine, cutFront); var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0); if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) { replaceRange(cm.doc, newText, chFrom, chTo, "+input"); return true; } }, ensurePolled: function() { this.forceCompositionEnd(); }, reset: function() { this.forceCompositionEnd(); }, forceCompositionEnd: function() { if (!this.composing || this.composing.handled) return; this.applyComposition(this.composing); this.composing.handled = true; this.div.blur(); this.div.focus(); }, applyComposition: function(composing) { if (this.cm.isReadOnly()) operation(this.cm, regChange)(this.cm) else if (composing.data && composing.data != composing.startData) operation(this.cm, applyTextInput)(this.cm, composing.data, 0, composing.sel); }, setUneditable: function(node) { node.contentEditable = "false" }, onKeyPress: function(e) { e.preventDefault(); if (!this.cm.isReadOnly()) operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0); }, readOnlyChanged: function(val) { this.div.contentEditable = String(val != "nocursor") }, onContextMenu: nothing, resetPosition: nothing, needsContentAttribute: true }, ContentEditableInput.prototype); function posToDOM(cm, pos) { var view = findViewForLine(cm, pos.line); if (!view || view.hidden) return null; var line = getLine(cm.doc, pos.line); var info = mapFromLineView(view, line, pos.line); var order = getOrder(line), side = "left"; if (order) { var partPos = getBidiPartAt(order, pos.ch); side = partPos % 2 ? "right" : "left"; } var result = nodeAndOffsetInLineMap(info.map, pos.ch, side); result.offset = result.collapse == "right" ? result.end : result.start; return result; } function badPos(pos, bad) { if (bad) pos.bad = true; return pos; } function domToPos(cm, node, offset) { var lineNode; if (node == cm.display.lineDiv) { lineNode = cm.display.lineDiv.childNodes[offset]; if (!lineNode) return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true); node = null; offset = 0; } else { for (lineNode = node;; lineNode = lineNode.parentNode) { if (!lineNode || lineNode == cm.display.lineDiv) return null; if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) break; } } for (var i = 0; i < cm.display.view.length; i++) { var lineView = cm.display.view[i]; if (lineView.node == lineNode) return locateNodeInLineView(lineView, node, offset); } } function locateNodeInLineView(lineView, node, offset) { var wrapper = lineView.text.firstChild, bad = false; if (!node || !contains(wrapper, node)) return badPos(Pos(lineNo(lineView.line), 0), true); if (node == wrapper) { bad = true; node = wrapper.childNodes[offset]; offset = 0; if (!node) { var line = lineView.rest ? lst(lineView.rest) : lineView.line; return badPos(Pos(lineNo(line), line.text.length), bad); } } var textNode = node.nodeType == 3 ? node : null, topNode = node; if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) { textNode = node.firstChild; if (offset) offset = textNode.nodeValue.length; } while (topNode.parentNode != wrapper) topNode = topNode.parentNode; var measure = lineView.measure, maps = measure.maps; function find(textNode, topNode, offset) { for (var i = -1; i < (maps ? maps.length : 0); i++) { var map = i < 0 ? measure.map : maps[i]; for (var j = 0; j < map.length; j += 3) { var curNode = map[j + 2]; if (curNode == textNode || curNode == topNode) { var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]); var ch = map[j] + offset; if (offset < 0 || curNode != textNode) ch = map[j + (offset ? 1 : 0)]; return Pos(line, ch); } } } } var found = find(textNode, topNode, offset); if (found) return badPos(found, bad); // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) { found = find(after, after.firstChild, 0); if (found) return badPos(Pos(found.line, found.ch - dist), bad); else dist += after.textContent.length; } for (var before = topNode.previousSibling, dist = offset; before; before = before.previousSibling) { found = find(before, before.firstChild, -1); if (found) return badPos(Pos(found.line, found.ch + dist), bad); else dist += after.textContent.length; } } function domTextBetween(cm, from, to, fromLine, toLine) { var text = "", closing = false, lineSep = cm.doc.lineSeparator(); function recognizeMarker(id) { return function(marker) { return marker.id == id; }; } function walk(node) { if (node.nodeType == 1) { var cmText = node.getAttribute("cm-text"); if (cmText != null) { if (cmText == "") cmText = node.textContent.replace(/\u200b/g, ""); text += cmText; return; } var markerID = node.getAttribute("cm-marker"), range; if (markerID) { var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID)); if (found.length && (range = found[0].find())) text += getBetween(cm.doc, range.from, range.to).join(lineSep); return; } if (node.getAttribute("contenteditable") == "false") return; for (var i = 0; i < node.childNodes.length; i++) walk(node.childNodes[i]); if (/^(pre|div|p)$/i.test(node.nodeName)) closing = true; } else if (node.nodeType == 3) { var val = node.nodeValue; if (!val) return; if (closing) { text += lineSep; closing = false; } text += val; } } for (;;) { walk(from); if (from == to) break; from = from.nextSibling; } return text; } CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput}; // SELECTION / CURSOR // Selection objects are immutable. A new one is created every time // the selection changes. A selection is one or more non-overlapping // (and non-touching) ranges, sorted, and an integer that indicates // which one is the primary selection (the one that's scrolled into // view, that getCursor returns, etc). function Selection(ranges, primIndex) { this.ranges = ranges; this.primIndex = primIndex; } Selection.prototype = { primary: function() { return this.ranges[this.primIndex]; }, equals: function(other) { if (other == this) return true; if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) return false; for (var i = 0; i < this.ranges.length; i++) { var here = this.ranges[i], there = other.ranges[i]; if (cmp(here.anchor, there.anchor) != 0 || cmp(here.head, there.head) != 0) return false; } return true; }, deepCopy: function() { for (var out = [], i = 0; i < this.ranges.length; i++) out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head)); return new Selection(out, this.primIndex); }, somethingSelected: function() { for (var i = 0; i < this.ranges.length; i++) if (!this.ranges[i].empty()) return true; return false; }, contains: function(pos, end) { if (!end) end = pos; for (var i = 0; i < this.ranges.length; i++) { var range = this.ranges[i]; if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0) return i; } return -1; } }; function Range(anchor, head) { this.anchor = anchor; this.head = head; } Range.prototype = { from: function() { return minPos(this.anchor, this.head); }, to: function() { return maxPos(this.anchor, this.head); }, empty: function() { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch; } }; // Take an unsorted, potentially overlapping set of ranges, and // build a selection out of it. 'Consumes' ranges array (modifying // it). function normalizeSelection(ranges, primIndex) { var prim = ranges[primIndex]; ranges.sort(function(a, b) { return cmp(a.from(), b.from()); }); primIndex = indexOf(ranges, prim); for (var i = 1; i < ranges.length; i++) { var cur = ranges[i], prev = ranges[i - 1]; if (cmp(prev.to(), cur.from()) >= 0) { var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to()); var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head; if (i <= primIndex) --primIndex; ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to)); } } return new Selection(ranges, primIndex); } function simpleSelection(anchor, head) { return new Selection([new Range(anchor, head || anchor)], 0); } // Most of the external API clips given positions to make sure they // actually exist within the document. function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1));} function clipPos(doc, pos) { if (pos.line < doc.first) return Pos(doc.first, 0); var last = doc.first + doc.size - 1; if (pos.line > last) return Pos(last, getLine(doc, last).text.length); return clipToLen(pos, getLine(doc, pos.line).text.length); } function clipToLen(pos, linelen) { var ch = pos.ch; if (ch == null || ch > linelen) return Pos(pos.line, linelen); else if (ch < 0) return Pos(pos.line, 0); else return pos; } function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size;} function clipPosArray(doc, array) { for (var out = [], i = 0; i < array.length; i++) out[i] = clipPos(doc, array[i]); return out; } // SELECTION UPDATES // The 'scroll' parameter given to many of these indicated whether // the new cursor position should be scrolled into view after // modifying the selection. // If shift is held or the extend flag is set, extends a range to // include a given position (and optionally a second position). // Otherwise, simply returns the range between the given positions. // Used for cursor motion and such. function extendRange(doc, range, head, other) { if (doc.cm && doc.cm.display.shift || doc.extend) { var anchor = range.anchor; if (other) { var posBefore = cmp(head, anchor) < 0; if (posBefore != (cmp(other, anchor) < 0)) { anchor = head; head = other; } else if (posBefore != (cmp(head, other) < 0)) { head = other; } } return new Range(anchor, head); } else { return new Range(other || head, head); } } // Extend the primary selection range, discard the rest. function extendSelection(doc, head, other, options) { setSelection(doc, new Selection([extendRange(doc, doc.sel.primary(), head, other)], 0), options); } // Extend all selections (pos is an array of selections with length // equal the number of selections) function extendSelections(doc, heads, options) { for (var out = [], i = 0; i < doc.sel.ranges.length; i++) out[i] = extendRange(doc, doc.sel.ranges[i], heads[i], null); var newSel = normalizeSelection(out, doc.sel.primIndex); setSelection(doc, newSel, options); } // Updates a single range in the selection. function replaceOneSelection(doc, i, range, options) { var ranges = doc.sel.ranges.slice(0); ranges[i] = range; setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), options); } // Reset the selection to a single range. function setSimpleSelection(doc, anchor, head, options) { setSelection(doc, simpleSelection(anchor, head), options); } // Give beforeSelectionChange handlers a change to influence a // selection update. function filterSelectionChange(doc, sel, options) { var obj = { ranges: sel.ranges, update: function(ranges) { this.ranges = []; for (var i = 0; i < ranges.length; i++) this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor), clipPos(doc, ranges[i].head)); }, origin: options && options.origin }; signal(doc, "beforeSelectionChange", doc, obj); if (doc.cm) signal(doc.cm, "beforeSelectionChange", doc.cm, obj); if (obj.ranges != sel.ranges) return normalizeSelection(obj.ranges, obj.ranges.length - 1); else return sel; } function setSelectionReplaceHistory(doc, sel, options) { var done = doc.history.done, last = lst(done); if (last && last.ranges) { done[done.length - 1] = sel; setSelectionNoUndo(doc, sel, options); } else { setSelection(doc, sel, options); } } // Set a new selection. function setSelection(doc, sel, options) { setSelectionNoUndo(doc, sel, options); addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options); } function setSelectionNoUndo(doc, sel, options) { if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange")) sel = filterSelectionChange(doc, sel, options); var bias = options && options.bias || (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1); setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true)); if (!(options && options.scroll === false) && doc.cm) ensureCursorVisible(doc.cm); } function setSelectionInner(doc, sel) { if (sel.equals(doc.sel)) return; doc.sel = sel; if (doc.cm) { doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true; signalCursorActivity(doc.cm); } signalLater(doc, "cursorActivity", doc); } // Verify that the selection does not partially select any atomic // marked ranges. function reCheckSelection(doc) { setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false), sel_dontScroll); } // Return a selection that does not partially select any atomic // ranges. function skipAtomicInSelection(doc, sel, bias, mayClear) { var out; for (var i = 0; i < sel.ranges.length; i++) { var range = sel.ranges[i]; var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i]; var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear); var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear); if (out || newAnchor != range.anchor || newHead != range.head) { if (!out) out = sel.ranges.slice(0, i); out[i] = new Range(newAnchor, newHead); } } return out ? normalizeSelection(out, sel.primIndex) : sel; } function skipAtomicInner(doc, pos, oldPos, dir, mayClear) { var line = getLine(doc, pos.line); if (line.markedSpans) for (var i = 0; i < line.markedSpans.length; ++i) { var sp = line.markedSpans[i], m = sp.marker; if ((sp.from == null || (m.inclusiveLeft ? sp.from <= pos.ch : sp.from < pos.ch)) && (sp.to == null || (m.inclusiveRight ? sp.to >= pos.ch : sp.to > pos.ch))) { if (mayClear) { signal(m, "beforeCursorEnter"); if (m.explicitlyCleared) { if (!line.markedSpans) break; else {--i; continue;} } } if (!m.atomic) continue; if (oldPos) { var near = m.find(dir < 0 ? 1 : -1), diff; if (dir < 0 ? m.inclusiveRight : m.inclusiveLeft) near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null); if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0)) return skipAtomicInner(doc, near, pos, dir, mayClear); } var far = m.find(dir < 0 ? -1 : 1); if (dir < 0 ? m.inclusiveLeft : m.inclusiveRight) far = movePos(doc, far, dir, far.line == pos.line ? line : null); return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null; } } return pos; } // Ensure a given position is not inside an atomic range. function skipAtomic(doc, pos, oldPos, bias, mayClear) { var dir = bias || 1; var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) || (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) || skipAtomicInner(doc, pos, oldPos, -dir, mayClear) || (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true)); if (!found) { doc.cantEdit = true; return Pos(doc.first, 0); } return found; } function movePos(doc, pos, dir, line) { if (dir < 0 && pos.ch == 0) { if (pos.line > doc.first) return clipPos(doc, Pos(pos.line - 1)); else return null; } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) { if (pos.line < doc.first + doc.size - 1) return Pos(pos.line + 1, 0); else return null; } else { return new Pos(pos.line, pos.ch + dir); } } // SELECTION DRAWING function updateSelection(cm) { cm.display.input.showSelection(cm.display.input.prepareSelection()); } function prepareSelection(cm, primary) { var doc = cm.doc, result = {}; var curFragment = result.cursors = document.createDocumentFragment(); var selFragment = result.selection = document.createDocumentFragment(); for (var i = 0; i < doc.sel.ranges.length; i++) { if (primary === false && i == doc.sel.primIndex) continue; var range = doc.sel.ranges[i]; if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.viewFrom) continue; var collapsed = range.empty(); if (collapsed || cm.options.showCursorWhenSelecting) drawSelectionCursor(cm, range.head, curFragment); if (!collapsed) drawSelectionRange(cm, range, selFragment); } return result; } // Draws a cursor for the given range function drawSelectionCursor(cm, head, output) { var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine); var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor")); cursor.style.left = pos.left + "px"; cursor.style.top = pos.top + "px"; cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px"; if (pos.other) { // Secondary cursor, shown when on a 'jump' in bi-directional text var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor")); otherCursor.style.display = ""; otherCursor.style.left = pos.other.left + "px"; otherCursor.style.top = pos.other.top + "px"; otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px"; } } // Draws the given range as a highlighted selection function drawSelectionRange(cm, range, output) { var display = cm.display, doc = cm.doc; var fragment = document.createDocumentFragment(); var padding = paddingH(cm.display), leftSide = padding.left; var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right; function add(left, top, width, bottom) { if (top < 0) top = 0; top = Math.round(top); bottom = Math.round(bottom); fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left + "px; top: " + top + "px; width: " + (width == null ? rightSide - left : width) + "px; height: " + (bottom - top) + "px")); } function drawForLine(line, fromArg, toArg) { var lineObj = getLine(doc, line); var lineLen = lineObj.text.length; var start, end; function coords(ch, bias) { return charCoords(cm, Pos(line, ch), "div", lineObj, bias); } iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) { var leftPos = coords(from, "left"), rightPos, left, right; if (from == to) { rightPos = leftPos; left = right = leftPos.left; } else { rightPos = coords(to - 1, "right"); if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp; } left = leftPos.left; right = rightPos.right; } if (fromArg == null && from == 0) left = leftSide; if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part add(left, leftPos.top, null, leftPos.bottom); left = leftSide; if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top); } if (toArg == null && to == lineLen) right = rightSide; if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left) start = leftPos; if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right) end = rightPos; if (left < leftSide + 1) left = leftSide; add(left, rightPos.top, right - left, rightPos.bottom); }); return {start: start, end: end}; } var sFrom = range.from(), sTo = range.to(); if (sFrom.line == sTo.line) { drawForLine(sFrom.line, sFrom.ch, sTo.ch); } else { var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line); var singleVLine = visualLine(fromLine) == visualLine(toLine); var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end; var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start; if (singleVLine) { if (leftEnd.top < rightStart.top - 2) { add(leftEnd.right, leftEnd.top, null, leftEnd.bottom); add(leftSide, rightStart.top, rightStart.left, rightStart.bottom); } else { add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom); } } if (leftEnd.bottom < rightStart.top) add(leftSide, leftEnd.bottom, null, rightStart.top); } output.appendChild(fragment); } // Cursor-blinking function restartBlink(cm) { if (!cm.state.focused) return; var display = cm.display; clearInterval(display.blinker); var on = true; display.cursorDiv.style.visibility = ""; if (cm.options.cursorBlinkRate > 0) display.blinker = setInterval(function() { display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; }, cm.options.cursorBlinkRate); else if (cm.options.cursorBlinkRate < 0) display.cursorDiv.style.visibility = "hidden"; } // HIGHLIGHT WORKER function startWorker(cm, time) { if (cm.doc.mode.startState && cm.doc.frontier < cm.display.viewTo) cm.state.highlight.set(time, bind(highlightWorker, cm)); } function highlightWorker(cm) { var doc = cm.doc; if (doc.frontier < doc.first) doc.frontier = doc.first; if (doc.frontier >= cm.display.viewTo) return; var end = +new Date + cm.options.workTime; var state = copyState(doc.mode, getStateBefore(cm, doc.frontier)); var changedLines = []; doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function(line) { if (doc.frontier >= cm.display.viewFrom) { // Visible var oldStyles = line.styles, tooLong = line.text.length > cm.options.maxHighlightLength; var highlighted = highlightLine(cm, line, tooLong ? copyState(doc.mode, state) : state, true); line.styles = highlighted.styles; var oldCls = line.styleClasses, newCls = highlighted.classes; if (newCls) line.styleClasses = newCls; else if (oldCls) line.styleClasses = null; var ischange = !oldStyles || oldStyles.length != line.styles.length || oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass); for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i]; if (ischange) changedLines.push(doc.frontier); line.stateAfter = tooLong ? state : copyState(doc.mode, state); } else { if (line.text.length <= cm.options.maxHighlightLength) processLine(cm, line.text, state); line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null; } ++doc.frontier; if (+new Date > end) { startWorker(cm, cm.options.workDelay); return true; } }); if (changedLines.length) runInOp(cm, function() { for (var i = 0; i < changedLines.length; i++) regLineChange(cm, changedLines[i], "text"); }); } // Finds the line to start with when starting a parse. Tries to // find a line with a stateAfter, so that it can start with a // valid state. If that fails, it returns the line with the // smallest indentation, which tends to need the least context to // parse correctly. function findStartLine(cm, n, precise) { var minindent, minline, doc = cm.doc; var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100); for (var search = n; search > lim; --search) { if (search <= doc.first) return doc.first; var line = getLine(doc, search - 1); if (line.stateAfter && (!precise || search <= doc.frontier)) return search; var indented = countColumn(line.text, null, cm.options.tabSize); if (minline == null || minindent > indented) { minline = search - 1; minindent = indented; } } return minline; } function getStateBefore(cm, n, precise) { var doc = cm.doc, display = cm.display; if (!doc.mode.startState) return true; var pos = findStartLine(cm, n, precise), state = pos > doc.first && getLine(doc, pos-1).stateAfter; if (!state) state = startState(doc.mode); else state = copyState(doc.mode, state); doc.iter(pos, n, function(line) { processLine(cm, line.text, state); var save = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo; line.stateAfter = save ? copyState(doc.mode, state) : null; ++pos; }); if (precise) doc.frontier = pos; return state; } // POSITION MEASUREMENT function paddingTop(display) {return display.lineSpace.offsetTop;} function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight;} function paddingH(display) { if (display.cachedPaddingH) return display.cachedPaddingH; var e = removeChildrenAndAdd(display.measure, elt("pre", "x")); var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle; var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)}; if (!isNaN(data.left) && !isNaN(data.right)) display.cachedPaddingH = data; return data; } function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth; } function displayWidth(cm) { return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth; } function displayHeight(cm) { return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight; } // Ensure the lineView.wrapping.heights array is populated. This is // an array of bottom offsets for the lines that make up a drawn // line. When lineWrapping is on, there might be more than one // height. function ensureLineHeights(cm, lineView, rect) { var wrapping = cm.options.lineWrapping; var curWidth = wrapping && displayWidth(cm); if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) { var heights = lineView.measure.heights = []; if (wrapping) { lineView.measure.width = curWidth; var rects = lineView.text.firstChild.getClientRects(); for (var i = 0; i < rects.length - 1; i++) { var cur = rects[i], next = rects[i + 1]; if (Math.abs(cur.bottom - next.bottom) > 2) heights.push((cur.bottom + next.top) / 2 - rect.top); } } heights.push(rect.bottom - rect.top); } } // Find a line map (mapping character offsets to text nodes) and a // measurement cache for the given line number. (A line view might // contain multiple lines when collapsed ranges are present.) function mapFromLineView(lineView, line, lineN) { if (lineView.line == line) return {map: lineView.measure.map, cache: lineView.measure.cache}; for (var i = 0; i < lineView.rest.length; i++) if (lineView.rest[i] == line) return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]}; for (var i = 0; i < lineView.rest.length; i++) if (lineNo(lineView.rest[i]) > lineN) return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i], before: true}; } // Render a line into the hidden node display.externalMeasured. Used // when measurement is needed for a line that's not in the viewport. function updateExternalMeasurement(cm, line) { line = visualLine(line); var lineN = lineNo(line); var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN); view.lineN = lineN; var built = view.built = buildLineContent(cm, view); view.text = built.pre; removeChildrenAndAdd(cm.display.lineMeasure, built.pre); return view; } // Get a {top, bottom, left, right} box (in line-local coordinates) // for a given character. function measureChar(cm, line, ch, bias) { return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias); } // Find a line view that corresponds to the given line number. function findViewForLine(cm, lineN) { if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo) return cm.display.view[findViewIndex(cm, lineN)]; var ext = cm.display.externalMeasured; if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size) return ext; } // Measurement can be split in two steps, the set-up work that // applies to the whole line, and the measurement of the actual // character. Functions like coordsChar, that need to do a lot of // measurements in a row, can thus ensure that the set-up work is // only done once. function prepareMeasureForLine(cm, line) { var lineN = lineNo(line); var view = findViewForLine(cm, lineN); if (view && !view.text) { view = null; } else if (view && view.changes) { updateLineForChanges(cm, view, lineN, getDimensions(cm)); cm.curOp.forceUpdate = true; } if (!view) view = updateExternalMeasurement(cm, line); var info = mapFromLineView(view, line, lineN); return { line: line, view: view, rect: null, map: info.map, cache: info.cache, before: info.before, hasHeights: false }; } // Given a prepared measurement object, measures the position of an // actual character (or fetches it from the cache). function measureCharPrepared(cm, prepared, ch, bias, varHeight) { if (prepared.before) ch = -1; var key = ch + (bias || ""), found; if (prepared.cache.hasOwnProperty(key)) { found = prepared.cache[key]; } else { if (!prepared.rect) prepared.rect = prepared.view.text.getBoundingClientRect(); if (!prepared.hasHeights) { ensureLineHeights(cm, prepared.view, prepared.rect); prepared.hasHeights = true; } found = measureCharInner(cm, prepared, ch, bias); if (!found.bogus) prepared.cache[key] = found; } return {left: found.left, right: found.right, top: varHeight ? found.rtop : found.top, bottom: varHeight ? found.rbottom : found.bottom}; } var nullRect = {left: 0, right: 0, top: 0, bottom: 0}; function nodeAndOffsetInLineMap(map, ch, bias) { var node, start, end, collapse; // First, search the line map for the text node corresponding to, // or closest to, the target character. for (var i = 0; i < map.length; i += 3) { var mStart = map[i], mEnd = map[i + 1]; if (ch < mStart) { start = 0; end = 1; collapse = "left"; } else if (ch < mEnd) { start = ch - mStart; end = start + 1; } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) { end = mEnd - mStart; start = end - 1; if (ch >= mEnd) collapse = "right"; } if (start != null) { node = map[i + 2]; if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right")) collapse = bias; if (bias == "left" && start == 0) while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) { node = map[(i -= 3) + 2]; collapse = "left"; } if (bias == "right" && start == mEnd - mStart) while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) { node = map[(i += 3) + 2]; collapse = "right"; } break; } } return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd}; } function measureCharInner(cm, prepared, ch, bias) { var place = nodeAndOffsetInLineMap(prepared.map, ch, bias); var node = place.node, start = place.start, end = place.end, collapse = place.collapse; var rect; if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates. for (var i = 0; i < 4; i++) { // Retry a maximum of 4 times when nonsense rectangles are returned while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) --start; while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) ++end; if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart) { rect = node.parentNode.getBoundingClientRect(); } else if (ie && cm.options.lineWrapping) { var rects = range(node, start, end).getClientRects(); if (rects.length) rect = rects[bias == "right" ? rects.length - 1 : 0]; else rect = nullRect; } else { rect = range(node, start, end).getBoundingClientRect() || nullRect; } if (rect.left || rect.right || start == 0) break; end = start; start = start - 1; collapse = "right"; } if (ie && ie_version < 11) rect = maybeUpdateRectForZooming(cm.display.measure, rect); } else { // If it is a widget, simply get the box for the whole widget. if (start > 0) collapse = bias = "right"; var rects; if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1) rect = rects[bias == "right" ? rects.length - 1 : 0]; else rect = node.getBoundingClientRect(); } if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) { var rSpan = node.parentNode.getClientRects()[0]; if (rSpan) rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom}; else rect = nullRect; } var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top; var mid = (rtop + rbot) / 2; var heights = prepared.view.measure.heights; for (var i = 0; i < heights.length - 1; i++) if (mid < heights[i]) break; var top = i ? heights[i - 1] : 0, bot = heights[i]; var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left, right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left, top: top, bottom: bot}; if (!rect.left && !rect.right) result.bogus = true; if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; } return result; } // Work around problem with bounding client rects on ranges being // returned incorrectly when zoomed on IE10 and below. function maybeUpdateRectForZooming(measure, rect) { if (!window.screen || screen.logicalXDPI == null || screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure)) return rect; var scaleX = screen.logicalXDPI / screen.deviceXDPI; var scaleY = screen.logicalYDPI / screen.deviceYDPI; return {left: rect.left * scaleX, right: rect.right * scaleX, top: rect.top * scaleY, bottom: rect.bottom * scaleY}; } function clearLineMeasurementCacheFor(lineView) { if (lineView.measure) { lineView.measure.cache = {}; lineView.measure.heights = null; if (lineView.rest) for (var i = 0; i < lineView.rest.length; i++) lineView.measure.caches[i] = {}; } } function clearLineMeasurementCache(cm) { cm.display.externalMeasure = null; removeChildren(cm.display.lineMeasure); for (var i = 0; i < cm.display.view.length; i++) clearLineMeasurementCacheFor(cm.display.view[i]); } function clearCaches(cm) { clearLineMeasurementCache(cm); cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null; if (!cm.options.lineWrapping) cm.display.maxLineChanged = true; cm.display.lineNumChars = null; } function pageScrollX() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft; } function pageScrollY() { return window.pageYOffset || (document.documentElement || document.body).scrollTop; } // Converts a {top, bottom, left, right} box from line-local // coordinates into another coordinate system. Context may be one of // "line", "div" (display.lineDiv), "local"/null (editor), "window", // or "page". function intoCoordSystem(cm, lineObj, rect, context) { if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) { var size = widgetHeight(lineObj.widgets[i]); rect.top += size; rect.bottom += size; } if (context == "line") return rect; if (!context) context = "local"; var yOff = heightAtLine(lineObj); if (context == "local") yOff += paddingTop(cm.display); else yOff -= cm.display.viewOffset; if (context == "page" || context == "window") { var lOff = cm.display.lineSpace.getBoundingClientRect(); yOff += lOff.top + (context == "window" ? 0 : pageScrollY()); var xOff = lOff.left + (context == "window" ? 0 : pageScrollX()); rect.left += xOff; rect.right += xOff; } rect.top += yOff; rect.bottom += yOff; return rect; } // Coverts a box from "div" coords to another coordinate system. // Context may be "window", "page", "div", or "local"/null. function fromCoordSystem(cm, coords, context) { if (context == "div") return coords; var left = coords.left, top = coords.top; // First move into "page" coordinate system if (context == "page") { left -= pageScrollX(); top -= pageScrollY(); } else if (context == "local" || !context) { var localBox = cm.display.sizer.getBoundingClientRect(); left += localBox.left; top += localBox.top; } var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect(); return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top}; } function charCoords(cm, pos, context, lineObj, bias) { if (!lineObj) lineObj = getLine(cm.doc, pos.line); return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context); } // Returns a box for a given cursor position, which may have an // 'other' property containing the position of the secondary cursor // on a bidi boundary. function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) { lineObj = lineObj || getLine(cm.doc, pos.line); if (!preparedMeasure) preparedMeasure = prepareMeasureForLine(cm, lineObj); function get(ch, right) { var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight); if (right) m.left = m.right; else m.right = m.left; return intoCoordSystem(cm, lineObj, m, context); } function getBidi(ch, partPos) { var part = order[partPos], right = part.level % 2; if (ch == bidiLeft(part) && partPos && part.level < order[partPos - 1].level) { part = order[--partPos]; ch = bidiRight(part) - (part.level % 2 ? 0 : 1); right = true; } else if (ch == bidiRight(part) && partPos < order.length - 1 && part.level < order[partPos + 1].level) { part = order[++partPos]; ch = bidiLeft(part) - part.level % 2; right = false; } if (right && ch == part.to && ch > part.from) return get(ch - 1); return get(ch, right); } var order = getOrder(lineObj), ch = pos.ch; if (!order) return get(ch); var partPos = getBidiPartAt(order, ch); var val = getBidi(ch, partPos); if (bidiOther != null) val.other = getBidi(ch, bidiOther); return val; } // Used to cheaply estimate the coordinates for a position. Used for // intermediate scroll updates. function estimateCoords(cm, pos) { var left = 0, pos = clipPos(cm.doc, pos); if (!cm.options.lineWrapping) left = charWidth(cm.display) * pos.ch; var lineObj = getLine(cm.doc, pos.line); var top = heightAtLine(lineObj) + paddingTop(cm.display); return {left: left, right: left, top: top, bottom: top + lineObj.height}; } // Positions returned by coordsChar contain some extra information. // xRel is the relative x position of the input coordinates compared // to the found position (so xRel > 0 means the coordinates are to // the right of the character position, for example). When outside // is true, that means the coordinates lie outside the line's // vertical range. function PosWithInfo(line, ch, outside, xRel) { var pos = Pos(line, ch); pos.xRel = xRel; if (outside) pos.outside = true; return pos; } // Compute the character position closest to the given coordinates. // Input must be lineSpace-local ("div" coordinate system). function coordsChar(cm, x, y) { var doc = cm.doc; y += cm.display.viewOffset; if (y < 0) return PosWithInfo(doc.first, 0, true, -1); var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1; if (lineN > last) return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, true, 1); if (x < 0) x = 0; var lineObj = getLine(doc, lineN); for (;;) { var found = coordsCharInner(cm, lineObj, lineN, x, y); var merged = collapsedSpanAtEnd(lineObj); var mergedPos = merged && merged.find(0, true); if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0)) lineN = lineNo(lineObj = mergedPos.to.line); else return found; } } function coordsCharInner(cm, lineObj, lineNo, x, y) { var innerOff = y - heightAtLine(lineObj); var wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth; var preparedMeasure = prepareMeasureForLine(cm, lineObj); function getX(ch) { var sp = cursorCoords(cm, Pos(lineNo, ch), "line", lineObj, preparedMeasure); wrongLine = true; if (innerOff > sp.bottom) return sp.left - adjust; else if (innerOff < sp.top) return sp.left + adjust; else wrongLine = false; return sp.left; } var bidi = getOrder(lineObj), dist = lineObj.text.length; var from = lineLeft(lineObj), to = lineRight(lineObj); var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine; if (x > toX) return PosWithInfo(lineNo, to, toOutside, 1); // Do a binary search between these bounds. for (;;) { if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) { var ch = x < fromX || x - fromX <= toX - x ? from : to; var xDiff = x - (ch == from ? fromX : toX); while (isExtendingChar(lineObj.text.charAt(ch))) ++ch; var pos = PosWithInfo(lineNo, ch, ch == from ? fromOutside : toOutside, xDiff < -1 ? -1 : xDiff > 1 ? 1 : 0); return pos; } var step = Math.ceil(dist / 2), middle = from + step; if (bidi) { middle = from; for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1); } var middleX = getX(middle); if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) toX += 1000; dist = step;} else {from = middle; fromX = middleX; fromOutside = wrongLine; dist -= step;} } } var measureText; // Compute the default text height. function textHeight(display) { if (display.cachedTextHeight != null) return display.cachedTextHeight; if (measureText == null) { measureText = elt("pre"); // Measure a bunch of lines, for browsers that compute // fractional heights. for (var i = 0; i < 49; ++i) { measureText.appendChild(document.createTextNode("x")); measureText.appendChild(elt("br")); } measureText.appendChild(document.createTextNode("x")); } removeChildrenAndAdd(display.measure, measureText); var height = measureText.offsetHeight / 50; if (height > 3) display.cachedTextHeight = height; removeChildren(display.measure); return height || 1; } // Compute the default character width. function charWidth(display) { if (display.cachedCharWidth != null) return display.cachedCharWidth; var anchor = elt("span", "xxxxxxxxxx"); var pre = elt("pre", [anchor]); removeChildrenAndAdd(display.measure, pre); var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10; if (width > 2) display.cachedCharWidth = width; return width || 10; } // OPERATIONS // Operations are used to wrap a series of changes to the editor // state in such a way that each change won't have to update the // cursor and display (which would be awkward, slow, and // error-prone). Instead, display updates are batched and then all // combined and executed at once. var operationGroup = null; var nextOpId = 0; // Start a new operation. function startOperation(cm) { cm.curOp = { cm: cm, viewChanged: false, // Flag that indicates that lines might need to be redrawn startHeight: cm.doc.height, // Used to detect need to update scrollbar forceUpdate: false, // Used to force a redraw updateInput: null, // Whether to reset the input textarea typing: false, // Whether this reset should be careful to leave existing text (for compositing) changeObjs: null, // Accumulated changes, for firing change events cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already selectionChanged: false, // Whether the selection needs to be redrawn updateMaxLine: false, // Set when the widest line needs to be determined anew scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet scrollToPos: null, // Used to scroll to a specific position focus: false, id: ++nextOpId // Unique ID }; if (operationGroup) { operationGroup.ops.push(cm.curOp); } else { cm.curOp.ownsGroup = operationGroup = { ops: [cm.curOp], delayedCallbacks: [] }; } } function fireCallbacksForOps(group) { // Calls delayed callbacks and cursorActivity handlers until no // new ones appear var callbacks = group.delayedCallbacks, i = 0; do { for (; i < callbacks.length; i++) callbacks[i].call(null); for (var j = 0; j < group.ops.length; j++) { var op = group.ops[j]; if (op.cursorActivityHandlers) while (op.cursorActivityCalled < op.cursorActivityHandlers.length) op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm); } } while (i < callbacks.length); } // Finish an operation, updating the display and signalling delayed events function endOperation(cm) { var op = cm.curOp, group = op.ownsGroup; if (!group) return; try { fireCallbacksForOps(group); } finally { operationGroup = null; for (var i = 0; i < group.ops.length; i++) group.ops[i].cm.curOp = null; endOperations(group); } } // The DOM updates done when an operation finishes are batched so // that the minimum number of relayouts are required. function endOperations(group) { var ops = group.ops; for (var i = 0; i < ops.length; i++) // Read DOM endOperation_R1(ops[i]); for (var i = 0; i < ops.length; i++) // Write DOM (maybe) endOperation_W1(ops[i]); for (var i = 0; i < ops.length; i++) // Read DOM endOperation_R2(ops[i]); for (var i = 0; i < ops.length; i++) // Write DOM (maybe) endOperation_W2(ops[i]); for (var i = 0; i < ops.length; i++) // Read DOM endOperation_finish(ops[i]); } function endOperation_R1(op) { var cm = op.cm, display = cm.display; maybeClipScrollbars(cm); if (op.updateMaxLine) findMaxLine(cm); op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null || op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom || op.scrollToPos.to.line >= display.viewTo) || display.maxLineChanged && cm.options.lineWrapping; op.update = op.mustUpdate && new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate); } function endOperation_W1(op) { op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update); } function endOperation_R2(op) { var cm = op.cm, display = cm.display; if (op.updatedDisplay) updateHeightsInViewport(cm); op.barMeasure = measureForScrollbars(cm); // If the max line changed since it was last measured, measure it, // and ensure the document's width matches it. // updateDisplay_W2 will use these properties to do the actual resizing if (display.maxLineChanged && !cm.options.lineWrapping) { op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3; cm.display.sizerWidth = op.adjustWidthTo; op.barMeasure.scrollWidth = Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth); op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm)); } if (op.updatedDisplay || op.selectionChanged) op.preparedSelection = display.input.prepareSelection(op.focus); } function endOperation_W2(op) { var cm = op.cm; if (op.adjustWidthTo != null) { cm.display.sizer.style.minWidth = op.adjustWidthTo + "px"; if (op.maxScrollLeft < cm.doc.scrollLeft) setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true); cm.display.maxLineChanged = false; } var takeFocus = op.focus && op.focus == activeElt() && (!document.hasFocus || document.hasFocus()) if (op.preparedSelection) cm.display.input.showSelection(op.preparedSelection, takeFocus); if (op.updatedDisplay || op.startHeight != cm.doc.height) updateScrollbars(cm, op.barMeasure); if (op.updatedDisplay) setDocumentHeight(cm, op.barMeasure); if (op.selectionChanged) restartBlink(cm); if (cm.state.focused && op.updateInput) cm.display.input.reset(op.typing); if (takeFocus) ensureFocus(op.cm); } function endOperation_finish(op) { var cm = op.cm, display = cm.display, doc = cm.doc; if (op.updatedDisplay) postUpdateDisplay(cm, op.update); // Abort mouse wheel delta measurement, when scrolling explicitly if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos)) display.wheelStartX = display.wheelStartY = null; // Propagate the scroll position to the actual DOM scroller if (op.scrollTop != null && (display.scroller.scrollTop != op.scrollTop || op.forceScroll)) { doc.scrollTop = Math.max(0, Math.min(display.scroller.scrollHeight - display.scroller.clientHeight, op.scrollTop)); display.scrollbars.setScrollTop(doc.scrollTop); display.scroller.scrollTop = doc.scrollTop; } if (op.scrollLeft != null && (display.scroller.scrollLeft != op.scrollLeft || op.forceScroll)) { doc.scrollLeft = Math.max(0, Math.min(display.scroller.scrollWidth - display.scroller.clientWidth, op.scrollLeft)); display.scrollbars.setScrollLeft(doc.scrollLeft); display.scroller.scrollLeft = doc.scrollLeft; alignHorizontally(cm); } // If we need to scroll a specific position into view, do so. if (op.scrollToPos) { var coords = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from), clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin); if (op.scrollToPos.isCursor && cm.state.focused) maybeScrollWindow(cm, coords); } // Fire events for markers that are hidden/unidden by editing or // undoing var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers; if (hidden) for (var i = 0; i < hidden.length; ++i) if (!hidden[i].lines.length) signal(hidden[i], "hide"); if (unhidden) for (var i = 0; i < unhidden.length; ++i) if (unhidden[i].lines.length) signal(unhidden[i], "unhide"); if (display.wrapper.offsetHeight) doc.scrollTop = cm.display.scroller.scrollTop; // Fire change events, and delayed event handlers if (op.changeObjs) signal(cm, "changes", cm, op.changeObjs); if (op.update) op.update.finish(); } // Run the given function in an operation function runInOp(cm, f) { if (cm.curOp) return f(); startOperation(cm); try { return f(); } finally { endOperation(cm); } } // Wraps a function in an operation. Returns the wrapped function. function operation(cm, f) { return function() { if (cm.curOp) return f.apply(cm, arguments); startOperation(cm); try { return f.apply(cm, arguments); } finally { endOperation(cm); } }; } // Used to add methods to editor and doc instances, wrapping them in // operations. function methodOp(f) { return function() { if (this.curOp) return f.apply(this, arguments); startOperation(this); try { return f.apply(this, arguments); } finally { endOperation(this); } }; } function docMethodOp(f) { return function() { var cm = this.cm; if (!cm || cm.curOp) return f.apply(this, arguments); startOperation(cm); try { return f.apply(this, arguments); } finally { endOperation(cm); } }; } // VIEW TRACKING // These objects are used to represent the visible (currently drawn) // part of the document. A LineView may correspond to multiple // logical lines, if those are connected by collapsed ranges. function LineView(doc, line, lineN) { // The starting line this.line = line; // Continuing lines, if any this.rest = visualLineContinued(line); // Number of logical lines in this visual line this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1; this.node = this.text = null; this.hidden = lineIsHidden(doc, line); } // Create a range of LineView objects for the given lines. function buildViewArray(cm, from, to) { var array = [], nextPos; for (var pos = from; pos < to; pos = nextPos) { var view = new LineView(cm.doc, getLine(cm.doc, pos), pos); nextPos = pos + view.size; array.push(view); } return array; } // Updates the display.view data structure for a given change to the // document. From and to are in pre-change coordinates. Lendiff is // the amount of lines added or subtracted by the change. This is // used for changes that span multiple lines, or change the way // lines are divided into visual lines. regLineChange (below) // registers single-line changes. function regChange(cm, from, to, lendiff) { if (from == null) from = cm.doc.first; if (to == null) to = cm.doc.first + cm.doc.size; if (!lendiff) lendiff = 0; var display = cm.display; if (lendiff && to < display.viewTo && (display.updateLineNumbers == null || display.updateLineNumbers > from)) display.updateLineNumbers = from; cm.curOp.viewChanged = true; if (from >= display.viewTo) { // Change after if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo) resetView(cm); } else if (to <= display.viewFrom) { // Change before if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) { resetView(cm); } else { display.viewFrom += lendiff; display.viewTo += lendiff; } } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap resetView(cm); } else if (from <= display.viewFrom) { // Top overlap var cut = viewCuttingPoint(cm, to, to + lendiff, 1); if (cut) { display.view = display.view.slice(cut.index); display.viewFrom = cut.lineN; display.viewTo += lendiff; } else { resetView(cm); } } else if (to >= display.viewTo) { // Bottom overlap var cut = viewCuttingPoint(cm, from, from, -1); if (cut) { display.view = display.view.slice(0, cut.index); display.viewTo = cut.lineN; } else { resetView(cm); } } else { // Gap in the middle var cutTop = viewCuttingPoint(cm, from, from, -1); var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1); if (cutTop && cutBot) { display.view = display.view.slice(0, cutTop.index) .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN)) .concat(display.view.slice(cutBot.index)); display.viewTo += lendiff; } else { resetView(cm); } } var ext = display.externalMeasured; if (ext) { if (to < ext.lineN) ext.lineN += lendiff; else if (from < ext.lineN + ext.size) display.externalMeasured = null; } } // Register a change to a single line. Type must be one of "text", // "gutter", "class", "widget" function regLineChange(cm, line, type) { cm.curOp.viewChanged = true; var display = cm.display, ext = cm.display.externalMeasured; if (ext && line >= ext.lineN && line < ext.lineN + ext.size) display.externalMeasured = null; if (line < display.viewFrom || line >= display.viewTo) return; var lineView = display.view[findViewIndex(cm, line)]; if (lineView.node == null) return; var arr = lineView.changes || (lineView.changes = []); if (indexOf(arr, type) == -1) arr.push(type); } // Clear the view. function resetView(cm) { cm.display.viewFrom = cm.display.viewTo = cm.doc.first; cm.display.view = []; cm.display.viewOffset = 0; } // Find the view element corresponding to a given line. Return null // when the line isn't visible. function findViewIndex(cm, n) { if (n >= cm.display.viewTo) return null; n -= cm.display.viewFrom; if (n < 0) return null; var view = cm.display.view; for (var i = 0; i < view.length; i++) { n -= view[i].size; if (n < 0) return i; } } function viewCuttingPoint(cm, oldN, newN, dir) { var index = findViewIndex(cm, oldN), diff, view = cm.display.view; if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size) return {index: index, lineN: newN}; for (var i = 0, n = cm.display.viewFrom; i < index; i++) n += view[i].size; if (n != oldN) { if (dir > 0) { if (index == view.length - 1) return null; diff = (n + view[index].size) - oldN; index++; } else { diff = n - oldN; } oldN += diff; newN += diff; } while (visualLineNo(cm.doc, newN) != newN) { if (index == (dir < 0 ? 0 : view.length - 1)) return null; newN += dir * view[index - (dir < 0 ? 1 : 0)].size; index += dir; } return {index: index, lineN: newN}; } // Force the view to cover a given range, adding empty view element // or clipping off existing ones as needed. function adjustView(cm, from, to) { var display = cm.display, view = display.view; if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) { display.view = buildViewArray(cm, from, to); display.viewFrom = from; } else { if (display.viewFrom > from) display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view); else if (display.viewFrom < from) display.view = display.view.slice(findViewIndex(cm, from)); display.viewFrom = from; if (display.viewTo < to) display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)); else if (display.viewTo > to) display.view = display.view.slice(0, findViewIndex(cm, to)); } display.viewTo = to; } // Count the number of lines in the view whose DOM representation is // out of date (or nonexistent). function countDirtyView(cm) { var view = cm.display.view, dirty = 0; for (var i = 0; i < view.length; i++) { var lineView = view[i]; if (!lineView.hidden && (!lineView.node || lineView.changes)) ++dirty; } return dirty; } // EVENT HANDLERS // Attach the necessary event handlers when initializing the editor function registerEventHandlers(cm) { var d = cm.display; on(d.scroller, "mousedown", operation(cm, onMouseDown)); // Older IE's will not fire a second mousedown for a double click if (ie && ie_version < 11) on(d.scroller, "dblclick", operation(cm, function(e) { if (signalDOMEvent(cm, e)) return; var pos = posFromMouse(cm, e); if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return; e_preventDefault(e); var word = cm.findWordAt(pos); extendSelection(cm.doc, word.anchor, word.head); })); else on(d.scroller, "dblclick", function(e) { signalDOMEvent(cm, e) || e_preventDefault(e); }); // Some browsers fire contextmenu *after* opening the menu, at // which point we can't mess with it anymore. Context menu is // handled in onMouseDown for these browsers. if (!captureRightClick) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);}); // Used to suppress mouse event handling when a touch happens var touchFinished, prevTouch = {end: 0}; function finishTouch() { if (d.activeTouch) { touchFinished = setTimeout(function() {d.activeTouch = null;}, 1000); prevTouch = d.activeTouch; prevTouch.end = +new Date; } }; function isMouseLikeTouchEvent(e) { if (e.touches.length != 1) return false; var touch = e.touches[0]; return touch.radiusX <= 1 && touch.radiusY <= 1; } function farAway(touch, other) { if (other.left == null) return true; var dx = other.left - touch.left, dy = other.top - touch.top; return dx * dx + dy * dy > 20 * 20; } on(d.scroller, "touchstart", function(e) { if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e)) { clearTimeout(touchFinished); var now = +new Date; d.activeTouch = {start: now, moved: false, prev: now - prevTouch.end <= 300 ? prevTouch : null}; if (e.touches.length == 1) { d.activeTouch.left = e.touches[0].pageX; d.activeTouch.top = e.touches[0].pageY; } } }); on(d.scroller, "touchmove", function() { if (d.activeTouch) d.activeTouch.moved = true; }); on(d.scroller, "touchend", function(e) { var touch = d.activeTouch; if (touch && !eventInWidget(d, e) && touch.left != null && !touch.moved && new Date - touch.start < 300) { var pos = cm.coordsChar(d.activeTouch, "page"), range; if (!touch.prev || farAway(touch, touch.prev)) // Single tap range = new Range(pos, pos); else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap range = cm.findWordAt(pos); else // Triple tap range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))); cm.setSelection(range.anchor, range.head); cm.focus(); e_preventDefault(e); } finishTouch(); }); on(d.scroller, "touchcancel", finishTouch); // Sync scrolling between fake scrollbars and real scrollable // area, ensure viewport is updated when scrolling. on(d.scroller, "scroll", function() { if (d.scroller.clientHeight) { setScrollTop(cm, d.scroller.scrollTop); setScrollLeft(cm, d.scroller.scrollLeft, true); signal(cm, "scroll", cm); } }); // Listen to wheel events in order to try and update the viewport on time. on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);}); on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);}); // Prevent wrapper from ever scrolling on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; }); d.dragFunctions = { enter: function(e) {if (!signalDOMEvent(cm, e)) e_stop(e);}, over: function(e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e); }}, start: function(e){onDragStart(cm, e);}, drop: operation(cm, onDrop), leave: function(e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm); }} }; var inp = d.input.getField(); on(inp, "keyup", function(e) { onKeyUp.call(cm, e); }); on(inp, "keydown", operation(cm, onKeyDown)); on(inp, "keypress", operation(cm, onKeyPress)); on(inp, "focus", bind(onFocus, cm)); on(inp, "blur", bind(onBlur, cm)); } function dragDropChanged(cm, value, old) { var wasOn = old && old != CodeMirror.Init; if (!value != !wasOn) { var funcs = cm.display.dragFunctions; var toggle = value ? on : off; toggle(cm.display.scroller, "dragstart", funcs.start); toggle(cm.display.scroller, "dragenter", funcs.enter); toggle(cm.display.scroller, "dragover", funcs.over); toggle(cm.display.scroller, "dragleave", funcs.leave); toggle(cm.display.scroller, "drop", funcs.drop); } } // Called when the window resizes function onResize(cm) { var d = cm.display; if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper.clientWidth) return; // Might be a text scaling operation, clear size caches. d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; d.scrollbarsClipped = false; cm.setSize(); } // MOUSE EVENTS // Return true when the given mouse event happened in a widget function eventInWidget(display, e) { for (var n = e_target(e); n != display.wrapper; n = n.parentNode) { if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") || (n.parentNode == display.sizer && n != display.mover)) return true; } } // Given a mouse event, find the corresponding position. If liberal // is false, it checks whether a gutter or scrollbar was clicked, // and returns null if it was. forRect is used by rectangular // selections, and tries to estimate a character position even for // coordinates beyond the right of the text. function posFromMouse(cm, e, liberal, forRect) { var display = cm.display; if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") return null; var x, y, space = display.lineSpace.getBoundingClientRect(); // Fails unpredictably on IE[67] when mouse is dragged around quickly. try { x = e.clientX - space.left; y = e.clientY - space.top; } catch (e) { return null; } var coords = coordsChar(cm, x, y), line; if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) { var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length; coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff)); } return coords; } // A mouse down can be a single click, double click, triple click, // start of selection drag, start of text drag, new cursor // (ctrl-click), rectangle drag (alt-drag), or xwin // middle-click-paste. Or it might be a click on something we should // not interfere with, such as a scrollbar or widget. function onMouseDown(e) { var cm = this, display = cm.display; if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) return; display.shift = e.shiftKey; if (eventInWidget(display, e)) { if (!webkit) { // Briefly turn off draggability, to allow widgets to do // normal dragging things. display.scroller.draggable = false; setTimeout(function(){display.scroller.draggable = true;}, 100); } return; } if (clickInGutter(cm, e)) return; var start = posFromMouse(cm, e); window.focus(); switch (e_button(e)) { case 1: // #3261: make sure, that we're not starting a second selection if (cm.state.selectingText) cm.state.selectingText(e); else if (start) leftButtonDown(cm, e, start); else if (e_target(e) == display.scroller) e_preventDefault(e); break; case 2: if (webkit) cm.state.lastMiddleDown = +new Date; if (start) extendSelection(cm.doc, start); setTimeout(function() {display.input.focus();}, 20); e_preventDefault(e); break; case 3: if (captureRightClick) onContextMenu(cm, e); else delayBlurEvent(cm); break; } } var lastClick, lastDoubleClick; function leftButtonDown(cm, e, start) { if (ie) setTimeout(bind(ensureFocus, cm), 0); else cm.curOp.focus = activeElt(); var now = +new Date, type; if (lastDoubleClick && lastDoubleClick.time > now - 400 && cmp(lastDoubleClick.pos, start) == 0) { type = "triple"; } else if (lastClick && lastClick.time > now - 400 && cmp(lastClick.pos, start) == 0) { type = "double"; lastDoubleClick = {time: now, pos: start}; } else { type = "single"; lastClick = {time: now, pos: start}; } var sel = cm.doc.sel, modifier = mac ? e.metaKey : e.ctrlKey, contained; if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() && type == "single" && (contained = sel.contains(start)) > -1 && (cmp((contained = sel.ranges[contained]).from(), start) < 0 || start.xRel > 0) && (cmp(contained.to(), start) > 0 || start.xRel < 0)) leftButtonStartDrag(cm, e, start, modifier); else leftButtonSelect(cm, e, start, type, modifier); } // Start a text drag. When it ends, see if any dragging actually // happen, and treat as a click if it didn't. function leftButtonStartDrag(cm, e, start, modifier) { var display = cm.display, startTime = +new Date; var dragEnd = operation(cm, function(e2) { if (webkit) display.scroller.draggable = false; cm.state.draggingText = false; off(document, "mouseup", dragEnd); off(display.scroller, "drop", dragEnd); if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) { e_preventDefault(e2); if (!modifier && +new Date - 200 < startTime) extendSelection(cm.doc, start); // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081) if (webkit || ie && ie_version == 9) setTimeout(function() {document.body.focus(); display.input.focus();}, 20); else display.input.focus(); } }); // Let the drag handler handle this. if (webkit) display.scroller.draggable = true; cm.state.draggingText = dragEnd; // IE's approach to draggable if (display.scroller.dragDrop) display.scroller.dragDrop(); on(document, "mouseup", dragEnd); on(display.scroller, "drop", dragEnd); } // Normal selection, as opposed to text dragging. function leftButtonSelect(cm, e, start, type, addNew) { var display = cm.display, doc = cm.doc; e_preventDefault(e); var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges; if (addNew && !e.shiftKey) { ourIndex = doc.sel.contains(start); if (ourIndex > -1) ourRange = ranges[ourIndex]; else ourRange = new Range(start, start); } else { ourRange = doc.sel.primary(); ourIndex = doc.sel.primIndex; } if (chromeOS ? e.shiftKey && e.metaKey : e.altKey) { type = "rect"; if (!addNew) ourRange = new Range(start, start); start = posFromMouse(cm, e, true, true); ourIndex = -1; } else if (type == "double") { var word = cm.findWordAt(start); if (cm.display.shift || doc.extend) ourRange = extendRange(doc, ourRange, word.anchor, word.head); else ourRange = word; } else if (type == "triple") { var line = new Range(Pos(start.line, 0), clipPos(doc, Pos(start.line + 1, 0))); if (cm.display.shift || doc.extend) ourRange = extendRange(doc, ourRange, line.anchor, line.head); else ourRange = line; } else { ourRange = extendRange(doc, ourRange, start); } if (!addNew) { ourIndex = 0; setSelection(doc, new Selection([ourRange], 0), sel_mouse); startSel = doc.sel; } else if (ourIndex == -1) { ourIndex = ranges.length; setSelection(doc, normalizeSelection(ranges.concat([ourRange]), ourIndex), {scroll: false, origin: "*mouse"}); } else if (ranges.length > 1 && ranges[ourIndex].empty() && type == "single" && !e.shiftKey) { setSelection(doc, normalizeSelection(ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0), {scroll: false, origin: "*mouse"}); startSel = doc.sel; } else { replaceOneSelection(doc, ourIndex, ourRange, sel_mouse); } var lastPos = start; function extendTo(pos) { if (cmp(lastPos, pos) == 0) return; lastPos = pos; if (type == "rect") { var ranges = [], tabSize = cm.options.tabSize; var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize); var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize); var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol); for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line)); line <= end; line++) { var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize); if (left == right) ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))); else if (text.length > leftPos) ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))); } if (!ranges.length) ranges.push(new Range(start, start)); setSelection(doc, normalizeSelection(startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex), {origin: "*mouse", scroll: false}); cm.scrollIntoView(pos); } else { var oldRange = ourRange; var anchor = oldRange.anchor, head = pos; if (type != "single") { if (type == "double") var range = cm.findWordAt(pos); else var range = new Range(Pos(pos.line, 0), clipPos(doc, Pos(pos.line + 1, 0))); if (cmp(range.anchor, anchor) > 0) { head = range.head; anchor = minPos(oldRange.from(), range.anchor); } else { head = range.anchor; anchor = maxPos(oldRange.to(), range.head); } } var ranges = startSel.ranges.slice(0); ranges[ourIndex] = new Range(clipPos(doc, anchor), head); setSelection(doc, normalizeSelection(ranges, ourIndex), sel_mouse); } } var editorSize = display.wrapper.getBoundingClientRect(); // Used to ensure timeout re-tries don't fire when another extend // happened in the meantime (clearTimeout isn't reliable -- at // least on Chrome, the timeouts still happen even when cleared, // if the clear happens after their scheduled firing time). var counter = 0; function extend(e) { var curCount = ++counter; var cur = posFromMouse(cm, e, true, type == "rect"); if (!cur) return; if (cmp(cur, lastPos) != 0) { cm.curOp.focus = activeElt(); extendTo(cur); var visible = visibleLines(display, doc); if (cur.line >= visible.to || cur.line < visible.from) setTimeout(operation(cm, function(){if (counter == curCount) extend(e);}), 150); } else { var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0; if (outside) setTimeout(operation(cm, function() { if (counter != curCount) return; display.scroller.scrollTop += outside; extend(e); }), 50); } } function done(e) { cm.state.selectingText = false; counter = Infinity; e_preventDefault(e); display.input.focus(); off(document, "mousemove", move); off(document, "mouseup", up); doc.history.lastSelOrigin = null; } var move = operation(cm, function(e) { if (!e_button(e)) done(e); else extend(e); }); var up = operation(cm, done); cm.state.selectingText = up; on(document, "mousemove", move); on(document, "mouseup", up); } // Determines whether an event happened in the gutter, and fires the // handlers for the corresponding event. function gutterEvent(cm, e, type, prevent) { try { var mX = e.clientX, mY = e.clientY; } catch(e) { return false; } if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) return false; if (prevent) e_preventDefault(e); var display = cm.display; var lineBox = display.lineDiv.getBoundingClientRect(); if (mY > lineBox.bottom || !hasHandler(cm, type)) return e_defaultPrevented(e); mY -= lineBox.top - display.viewOffset; for (var i = 0; i < cm.options.gutters.length; ++i) { var g = display.gutters.childNodes[i]; if (g && g.getBoundingClientRect().right >= mX) { var line = lineAtHeight(cm.doc, mY); var gutter = cm.options.gutters[i]; signal(cm, type, cm, line, gutter, e); return e_defaultPrevented(e); } } } function clickInGutter(cm, e) { return gutterEvent(cm, e, "gutterClick", true); } // Kludge to work around strange IE behavior where it'll sometimes // re-fire a series of drag-related events right after the drop (#1551) var lastDrop = 0; function onDrop(e) { var cm = this; clearDragCursor(cm); if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return; e_preventDefault(e); if (ie) lastDrop = +new Date; var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files; if (!pos || cm.isReadOnly()) return; // Might be a file drop, in which case we simply extract the text // and insert it. if (files && files.length && window.FileReader && window.File) { var n = files.length, text = Array(n), read = 0; var loadFile = function(file, i) { if (cm.options.allowDropFileTypes && indexOf(cm.options.allowDropFileTypes, file.type) == -1) return; var reader = new FileReader; reader.onload = operation(cm, function() { var content = reader.result; if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) content = ""; text[i] = content; if (++read == n) { pos = clipPos(cm.doc, pos); var change = {from: pos, to: pos, text: cm.doc.splitLines(text.join(cm.doc.lineSeparator())), origin: "paste"}; makeChange(cm.doc, change); setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change))); } }); reader.readAsText(file); }; for (var i = 0; i < n; ++i) loadFile(files[i], i); } else { // Normal drop // Don't do a replace if the drop happened inside of the selected text. if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) { cm.state.draggingText(e); // Ensure the editor is re-focused setTimeout(function() {cm.display.input.focus();}, 20); return; } try { var text = e.dataTransfer.getData("Text"); if (text) { if (cm.state.draggingText && !(mac ? e.altKey : e.ctrlKey)) var selected = cm.listSelections(); setSelectionNoUndo(cm.doc, simpleSelection(pos, pos)); if (selected) for (var i = 0; i < selected.length; ++i) replaceRange(cm.doc, "", selected[i].anchor, selected[i].head, "drag"); cm.replaceSelection(text, "around", "paste"); cm.display.input.focus(); } } catch(e){} } } function onDragStart(cm, e) { if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return; } if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return; e.dataTransfer.setData("Text", cm.getSelection()); e.dataTransfer.effectAllowed = "copyMove" // Use dummy image instead of default browsers image. // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there. if (e.dataTransfer.setDragImage && !safari) { var img = elt("img", null, null, "position: fixed; left: 0; top: 0;"); img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="; if (presto) { img.width = img.height = 1; cm.display.wrapper.appendChild(img); // Force a relayout, or Opera won't use our image for some obscure reason img._top = img.offsetTop; } e.dataTransfer.setDragImage(img, 0, 0); if (presto) img.parentNode.removeChild(img); } } function onDragOver(cm, e) { var pos = posFromMouse(cm, e); if (!pos) return; var frag = document.createDocumentFragment(); drawSelectionCursor(cm, pos, frag); if (!cm.display.dragCursor) { cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors"); cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv); } removeChildrenAndAdd(cm.display.dragCursor, frag); } function clearDragCursor(cm) { if (cm.display.dragCursor) { cm.display.lineSpace.removeChild(cm.display.dragCursor); cm.display.dragCursor = null; } } // SCROLL EVENTS // Sync the scrollable area and scrollbars, ensure the viewport // covers the visible area. function setScrollTop(cm, val) { if (Math.abs(cm.doc.scrollTop - val) < 2) return; cm.doc.scrollTop = val; if (!gecko) updateDisplaySimple(cm, {top: val}); if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val; cm.display.scrollbars.setScrollTop(val); if (gecko) updateDisplaySimple(cm); startWorker(cm, 100); } // Sync scroller and scrollbar, ensure the gutter elements are // aligned. function setScrollLeft(cm, val, isScroller) { if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) return; val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth); cm.doc.scrollLeft = val; alignHorizontally(cm); if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val; cm.display.scrollbars.setScrollLeft(val); } // Since the delta values reported on mouse wheel events are // unstandardized between browsers and even browser versions, and // generally horribly unpredictable, this code starts by measuring // the scroll effect that the first few mouse wheel events have, // and, from that, detects the way it can convert deltas to pixel // offsets afterwards. // // The reason we want to know the amount a wheel event will scroll // is that it gives us a chance to update the display before the // actual scrolling happens, reducing flickering. var wheelSamples = 0, wheelPixelsPerUnit = null; // Fill in a browser-detected starting value on browsers where we // know one. These don't have to be accurate -- the result of them // being wrong would just be a slight flicker on the first wheel // scroll (if it is large enough). if (ie) wheelPixelsPerUnit = -.53; else if (gecko) wheelPixelsPerUnit = 15; else if (chrome) wheelPixelsPerUnit = -.7; else if (safari) wheelPixelsPerUnit = -1/3; var wheelEventDelta = function(e) { var dx = e.wheelDeltaX, dy = e.wheelDeltaY; if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) dx = e.detail; if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail; else if (dy == null) dy = e.wheelDelta; return {x: dx, y: dy}; }; CodeMirror.wheelEventPixels = function(e) { var delta = wheelEventDelta(e); delta.x *= wheelPixelsPerUnit; delta.y *= wheelPixelsPerUnit; return delta; }; function onScrollWheel(cm, e) { var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y; var display = cm.display, scroll = display.scroller; // Quit if there's nothing to scroll here var canScrollX = scroll.scrollWidth > scroll.clientWidth; var canScrollY = scroll.scrollHeight > scroll.clientHeight; if (!(dx && canScrollX || dy && canScrollY)) return; // Webkit browsers on OS X abort momentum scrolls when the target // of the scroll event is removed from the scrollable element. // This hack (see related code in patchDisplay) makes sure the // element is kept around. if (dy && mac && webkit) { outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) { for (var i = 0; i < view.length; i++) { if (view[i].node == cur) { cm.display.currentWheelTarget = cur; break outer; } } } } // On some browsers, horizontal scrolling will cause redraws to // happen before the gutter has been realigned, causing it to // wriggle around in a most unseemly way. When we have an // estimated pixels/delta value, we just handle horizontal // scrolling entirely here. It'll be slightly off from native, but // better than glitching out. if (dx && !gecko && !presto && wheelPixelsPerUnit != null) { if (dy && canScrollY) setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight))); setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth))); // Only prevent default scrolling if vertical scrolling is // actually possible. Otherwise, it causes vertical scroll // jitter on OSX trackpads when deltaX is small and deltaY // is large (issue #3579) if (!dy || (dy && canScrollY)) e_preventDefault(e); display.wheelStartX = null; // Abort measurement, if in progress return; } // 'Project' the visible viewport to cover the area that is being // scrolled into view (if we know enough to estimate it). if (dy && wheelPixelsPerUnit != null) { var pixels = dy * wheelPixelsPerUnit; var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight; if (pixels < 0) top = Math.max(0, top + pixels - 50); else bot = Math.min(cm.doc.height, bot + pixels + 50); updateDisplaySimple(cm, {top: top, bottom: bot}); } if (wheelSamples < 20) { if (display.wheelStartX == null) { display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop; display.wheelDX = dx; display.wheelDY = dy; setTimeout(function() { if (display.wheelStartX == null) return; var movedX = scroll.scrollLeft - display.wheelStartX; var movedY = scroll.scrollTop - display.wheelStartY; var sample = (movedY && display.wheelDY && movedY / display.wheelDY) || (movedX && display.wheelDX && movedX / display.wheelDX); display.wheelStartX = display.wheelStartY = null; if (!sample) return; wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1); ++wheelSamples; }, 200); } else { display.wheelDX += dx; display.wheelDY += dy; } } } // KEY EVENTS // Run a handler that was bound to a key. function doHandleBinding(cm, bound, dropShift) { if (typeof bound == "string") { bound = commands[bound]; if (!bound) return false; } // Ensure previous input has been read, so that the handler sees a // consistent view of the document cm.display.input.ensurePolled(); var prevShift = cm.display.shift, done = false; try { if (cm.isReadOnly()) cm.state.suppressEdits = true; if (dropShift) cm.display.shift = false; done = bound(cm) != Pass; } finally { cm.display.shift = prevShift; cm.state.suppressEdits = false; } return done; } function lookupKeyForEditor(cm, name, handle) { for (var i = 0; i < cm.state.keyMaps.length; i++) { var result = lookupKey(name, cm.state.keyMaps[i], handle, cm); if (result) return result; } return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm)) || lookupKey(name, cm.options.keyMap, handle, cm); } var stopSeq = new Delayed; function dispatchKey(cm, name, e, handle) { var seq = cm.state.keySeq; if (seq) { if (isModifierKey(name)) return "handled"; stopSeq.set(50, function() { if (cm.state.keySeq == seq) { cm.state.keySeq = null; cm.display.input.reset(); } }); name = seq + " " + name; } var result = lookupKeyForEditor(cm, name, handle); if (result == "multi") cm.state.keySeq = name; if (result == "handled") signalLater(cm, "keyHandled", cm, name, e); if (result == "handled" || result == "multi") { e_preventDefault(e); restartBlink(cm); } if (seq && !result && /\'$/.test(name)) { e_preventDefault(e); return true; } return !!result; } // Handle a key from the keydown event. function handleKeyBinding(cm, e) { var name = keyName(e, true); if (!name) return false; if (e.shiftKey && !cm.state.keySeq) { // First try to resolve full name (including 'Shift-'). Failing // that, see if there is a cursor-motion command (starting with // 'go') bound to the keyname without 'Shift-'. return dispatchKey(cm, "Shift-" + name, e, function(b) {return doHandleBinding(cm, b, true);}) || dispatchKey(cm, name, e, function(b) { if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion) return doHandleBinding(cm, b); }); } else { return dispatchKey(cm, name, e, function(b) { return doHandleBinding(cm, b); }); } } // Handle a key from the keypress event function handleCharBinding(cm, e, ch) { return dispatchKey(cm, "'" + ch + "'", e, function(b) { return doHandleBinding(cm, b, true); }); } var lastStoppedKey = null; function onKeyDown(e) { var cm = this; cm.curOp.focus = activeElt(); if (signalDOMEvent(cm, e)) return; // IE does strange things with escape. if (ie && ie_version < 11 && e.keyCode == 27) e.returnValue = false; var code = e.keyCode; cm.display.shift = code == 16 || e.shiftKey; var handled = handleKeyBinding(cm, e); if (presto) { lastStoppedKey = handled ? code : null; // Opera has no cut event... we try to at least catch the key combo if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey)) cm.replaceSelection("", null, "cut"); } // Turn mouse into crosshair when Alt is held on Mac. if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className)) showCrossHair(cm); } function showCrossHair(cm) { var lineDiv = cm.display.lineDiv; addClass(lineDiv, "CodeMirror-crosshair"); function up(e) { if (e.keyCode == 18 || !e.altKey) { rmClass(lineDiv, "CodeMirror-crosshair"); off(document, "keyup", up); off(document, "mouseover", up); } } on(document, "keyup", up); on(document, "mouseover", up); } function onKeyUp(e) { if (e.keyCode == 16) this.doc.sel.shift = false; signalDOMEvent(this, e); } function onKeyPress(e) { var cm = this; if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) return; var keyCode = e.keyCode, charCode = e.charCode; if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;} if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) return; var ch = String.fromCharCode(charCode == null ? keyCode : charCode); if (handleCharBinding(cm, e, ch)) return; cm.display.input.onKeyPress(e); } // FOCUS/BLUR EVENTS function delayBlurEvent(cm) { cm.state.delayingBlurEvent = true; setTimeout(function() { if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false; onBlur(cm); } }, 100); } function onFocus(cm) { if (cm.state.delayingBlurEvent) cm.state.delayingBlurEvent = false; if (cm.options.readOnly == "nocursor") return; if (!cm.state.focused) { signal(cm, "focus", cm); cm.state.focused = true; addClass(cm.display.wrapper, "CodeMirror-focused"); // This test prevents this from firing when a context // menu is closed (since the input reset would kill the // select-all detection hack) if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) { cm.display.input.reset(); if (webkit) setTimeout(function() { cm.display.input.reset(true); }, 20); // Issue #1730 } cm.display.input.receivedFocus(); } restartBlink(cm); } function onBlur(cm) { if (cm.state.delayingBlurEvent) return; if (cm.state.focused) { signal(cm, "blur", cm); cm.state.focused = false; rmClass(cm.display.wrapper, "CodeMirror-focused"); } clearInterval(cm.display.blinker); setTimeout(function() {if (!cm.state.focused) cm.display.shift = false;}, 150); } // CONTEXT MENU HANDLING // To make the context menu work, we need to briefly unhide the // textarea (making it as unobtrusive as possible) to let the // right-click take effect on it. function onContextMenu(cm, e) { if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) return; if (signalDOMEvent(cm, e, "contextmenu")) return; cm.display.input.onContextMenu(e); } function contextMenuInGutter(cm, e) { if (!hasHandler(cm, "gutterContextMenu")) return false; return gutterEvent(cm, e, "gutterContextMenu", false); } // UPDATING // Compute the position of the end of a change (its 'to' property // refers to the pre-change end). var changeEnd = CodeMirror.changeEnd = function(change) { if (!change.text) return change.to; return Pos(change.from.line + change.text.length - 1, lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0)); }; // Adjust a position to refer to the post-change position of the // same text, or the end of the change if the change covers it. function adjustForChange(pos, change) { if (cmp(pos, change.from) < 0) return pos; if (cmp(pos, change.to) <= 0) return changeEnd(change); var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch; if (pos.line == change.to.line) ch += changeEnd(change).ch - change.to.ch; return Pos(line, ch); } function computeSelAfterChange(doc, change) { var out = []; for (var i = 0; i < doc.sel.ranges.length; i++) { var range = doc.sel.ranges[i]; out.push(new Range(adjustForChange(range.anchor, change), adjustForChange(range.head, change))); } return normalizeSelection(out, doc.sel.primIndex); } function offsetPos(pos, old, nw) { if (pos.line == old.line) return Pos(nw.line, pos.ch - old.ch + nw.ch); else return Pos(nw.line + (pos.line - old.line), pos.ch); } // Used by replaceSelections to allow moving the selection to the // start or around the replaced test. Hint may be "start" or "around". function computeReplacedSel(doc, changes, hint) { var out = []; var oldPrev = Pos(doc.first, 0), newPrev = oldPrev; for (var i = 0; i < changes.length; i++) { var change = changes[i]; var from = offsetPos(change.from, oldPrev, newPrev); var to = offsetPos(changeEnd(change), oldPrev, newPrev); oldPrev = change.to; newPrev = to; if (hint == "around") { var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0; out[i] = new Range(inv ? to : from, inv ? from : to); } else { out[i] = new Range(from, from); } } return new Selection(out, doc.sel.primIndex); } // Allow "beforeChange" event handlers to influence a change function filterChange(doc, change, update) { var obj = { canceled: false, from: change.from, to: change.to, text: change.text, origin: change.origin, cancel: function() { this.canceled = true; } }; if (update) obj.update = function(from, to, text, origin) { if (from) this.from = clipPos(doc, from); if (to) this.to = clipPos(doc, to); if (text) this.text = text; if (origin !== undefined) this.origin = origin; }; signal(doc, "beforeChange", doc, obj); if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj); if (obj.canceled) return null; return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin}; } // Apply a change to a document, and add it to the document's // history, and propagating it to all linked documents. function makeChange(doc, change, ignoreReadOnly) { if (doc.cm) { if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly); if (doc.cm.state.suppressEdits) return; } if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) { change = filterChange(doc, change, true); if (!change) return; } // Possibly split or suppress the update based on the presence // of read-only spans in its range. var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to); if (split) { for (var i = split.length - 1; i >= 0; --i) makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text}); } else { makeChangeInner(doc, change); } } function makeChangeInner(doc, change) { if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) return; var selAfter = computeSelAfterChange(doc, change); addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN); makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change)); var rebased = []; linkedDocs(doc, function(doc, sharedHist) { if (!sharedHist && indexOf(rebased, doc.history) == -1) { rebaseHist(doc.history, change); rebased.push(doc.history); } makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change)); }); } // Revert a change stored in a document's history. function makeChangeFromHistory(doc, type, allowSelectionOnly) { if (doc.cm && doc.cm.state.suppressEdits) return; var hist = doc.history, event, selAfter = doc.sel; var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done; // Verify that there is a useable event (so that ctrl-z won't // needlessly clear selection events) for (var i = 0; i < source.length; i++) { event = source[i]; if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges) break; } if (i == source.length) return; hist.lastOrigin = hist.lastSelOrigin = null; for (;;) { event = source.pop(); if (event.ranges) { pushSelectionToHistory(event, dest); if (allowSelectionOnly && !event.equals(doc.sel)) { setSelection(doc, event, {clearRedo: false}); return; } selAfter = event; } else break; } // Build up a reverse change object to add to the opposite history // stack (redo when undoing, and vice versa). var antiChanges = []; pushSelectionToHistory(selAfter, dest); dest.push({changes: antiChanges, generation: hist.generation}); hist.generation = event.generation || ++hist.maxGeneration; var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange"); for (var i = event.changes.length - 1; i >= 0; --i) { var change = event.changes[i]; change.origin = type; if (filter && !filterChange(doc, change, false)) { source.length = 0; return; } antiChanges.push(historyChangeFromChange(doc, change)); var after = i ? computeSelAfterChange(doc, change) : lst(source); makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change)); if (!i && doc.cm) doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}); var rebased = []; // Propagate to the linked documents linkedDocs(doc, function(doc, sharedHist) { if (!sharedHist && indexOf(rebased, doc.history) == -1) { rebaseHist(doc.history, change); rebased.push(doc.history); } makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change)); }); } } // Sub-views need their line numbers shifted when text is added // above or below them in the parent document. function shiftDoc(doc, distance) { if (distance == 0) return; doc.first += distance; doc.sel = new Selection(map(doc.sel.ranges, function(range) { return new Range(Pos(range.anchor.line + distance, range.anchor.ch), Pos(range.head.line + distance, range.head.ch)); }), doc.sel.primIndex); if (doc.cm) { regChange(doc.cm, doc.first, doc.first - distance, distance); for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++) regLineChange(doc.cm, l, "gutter"); } } // More lower-level change function, handling only a single document // (not linked ones). function makeChangeSingleDoc(doc, change, selAfter, spans) { if (doc.cm && !doc.cm.curOp) return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans); if (change.to.line < doc.first) { shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line)); return; } if (change.from.line > doc.lastLine()) return; // Clip the change to the size of this doc if (change.from.line < doc.first) { var shift = change.text.length - 1 - (doc.first - change.from.line); shiftDoc(doc, shift); change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch), text: [lst(change.text)], origin: change.origin}; } var last = doc.lastLine(); if (change.to.line > last) { change = {from: change.from, to: Pos(last, getLine(doc, last).text.length), text: [change.text[0]], origin: change.origin}; } change.removed = getBetween(doc, change.from, change.to); if (!selAfter) selAfter = computeSelAfterChange(doc, change); if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans); else updateDoc(doc, change, spans); setSelectionNoUndo(doc, selAfter, sel_dontScroll); } // Handle the interaction of a change to a document with the editor // that this document is part of. function makeChangeSingleDocInEditor(cm, change, spans) { var doc = cm.doc, display = cm.display, from = change.from, to = change.to; var recomputeMaxLength = false, checkWidthStart = from.line; if (!cm.options.lineWrapping) { checkWidthStart = lineNo(visualLine(getLine(doc, from.line))); doc.iter(checkWidthStart, to.line + 1, function(line) { if (line == display.maxLine) { recomputeMaxLength = true; return true; } }); } if (doc.sel.contains(change.from, change.to) > -1) signalCursorActivity(cm); updateDoc(doc, change, spans, estimateHeight(cm)); if (!cm.options.lineWrapping) { doc.iter(checkWidthStart, from.line + change.text.length, function(line) { var len = lineLength(line); if (len > display.maxLineLength) { display.maxLine = line; display.maxLineLength = len; display.maxLineChanged = true; recomputeMaxLength = false; } }); if (recomputeMaxLength) cm.curOp.updateMaxLine = true; } // Adjust frontier, schedule worker doc.frontier = Math.min(doc.frontier, from.line); startWorker(cm, 400); var lendiff = change.text.length - (to.line - from.line) - 1; // Remember that these lines changed, for updating the display if (change.full) regChange(cm); else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change)) regLineChange(cm, from.line, "text"); else regChange(cm, from.line, to.line + 1, lendiff); var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change"); if (changeHandler || changesHandler) { var obj = { from: from, to: to, text: change.text, removed: change.removed, origin: change.origin }; if (changeHandler) signalLater(cm, "change", cm, obj); if (changesHandler) (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj); } cm.display.selForContextMenu = null; } function replaceRange(doc, code, from, to, origin) { if (!to) to = from; if (cmp(to, from) < 0) { var tmp = to; to = from; from = tmp; } if (typeof code == "string") code = doc.splitLines(code); makeChange(doc, {from: from, to: to, text: code, origin: origin}); } // SCROLLING THINGS INTO VIEW // If an editor sits on the top or bottom of the window, partially // scrolled out of view, this ensures that the cursor is visible. function maybeScrollWindow(cm, coords) { if (signalDOMEvent(cm, "scrollCursorIntoView")) return; var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null; if (coords.top + box.top < 0) doScroll = true; else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false; if (doScroll != null && !phantom) { var scrollNode = elt("div", "\u200b", null, "position: absolute; top: " + (coords.top - display.viewOffset - paddingTop(cm.display)) + "px; height: " + (coords.bottom - coords.top + scrollGap(cm) + display.barHeight) + "px; left: " + coords.left + "px; width: 2px;"); cm.display.lineSpace.appendChild(scrollNode); scrollNode.scrollIntoView(doScroll); cm.display.lineSpace.removeChild(scrollNode); } } // Scroll a given position into view (immediately), verifying that // it actually became visible (as line heights are accurately // measured, the position of something may 'drift' during drawing). function scrollPosIntoView(cm, pos, end, margin) { if (margin == null) margin = 0; for (var limit = 0; limit < 5; limit++) { var changed = false, coords = cursorCoords(cm, pos); var endCoords = !end || end == pos ? coords : cursorCoords(cm, end); var scrollPos = calculateScrollPos(cm, Math.min(coords.left, endCoords.left), Math.min(coords.top, endCoords.top) - margin, Math.max(coords.left, endCoords.left), Math.max(coords.bottom, endCoords.bottom) + margin); var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft; if (scrollPos.scrollTop != null) { setScrollTop(cm, scrollPos.scrollTop); if (Math.abs(cm.doc.scrollTop - startTop) > 1) changed = true; } if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft); if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) changed = true; } if (!changed) break; } return coords; } // Scroll a given set of coordinates into view (immediately). function scrollIntoView(cm, x1, y1, x2, y2) { var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2); if (scrollPos.scrollTop != null) setScrollTop(cm, scrollPos.scrollTop); if (scrollPos.scrollLeft != null) setScrollLeft(cm, scrollPos.scrollLeft); } // Calculate a new scroll position needed to scroll the given // rectangle into view. Returns an object with scrollTop and // scrollLeft properties. When these are undefined, the // vertical/horizontal position does not need to be adjusted. function calculateScrollPos(cm, x1, y1, x2, y2) { var display = cm.display, snapMargin = textHeight(cm.display); if (y1 < 0) y1 = 0; var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop; var screen = displayHeight(cm), result = {}; if (y2 - y1 > screen) y2 = y1 + screen; var docBottom = cm.doc.height + paddingVert(display); var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin; if (y1 < screentop) { result.scrollTop = atTop ? 0 : y1; } else if (y2 > screentop + screen) { var newTop = Math.min(y1, (atBottom ? docBottom : y2) - screen); if (newTop != screentop) result.scrollTop = newTop; } var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft; var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0); var tooWide = x2 - x1 > screenw; if (tooWide) x2 = x1 + screenw; if (x1 < 10) result.scrollLeft = 0; else if (x1 < screenleft) result.scrollLeft = Math.max(0, x1 - (tooWide ? 0 : 10)); else if (x2 > screenw + screenleft - 3) result.scrollLeft = x2 + (tooWide ? 0 : 10) - screenw; return result; } // Store a relative adjustment to the scroll position in the current // operation (to be applied when the operation finishes). function addToScrollPos(cm, left, top) { if (left != null || top != null) resolveScrollToPos(cm); if (left != null) cm.curOp.scrollLeft = (cm.curOp.scrollLeft == null ? cm.doc.scrollLeft : cm.curOp.scrollLeft) + left; if (top != null) cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top; } // Make sure that at the end of the operation the current cursor is // shown. function ensureCursorVisible(cm) { resolveScrollToPos(cm); var cur = cm.getCursor(), from = cur, to = cur; if (!cm.options.lineWrapping) { from = cur.ch ? Pos(cur.line, cur.ch - 1) : cur; to = Pos(cur.line, cur.ch + 1); } cm.curOp.scrollToPos = {from: from, to: to, margin: cm.options.cursorScrollMargin, isCursor: true}; } // When an operation has its scrollToPos property set, and another // scroll action is applied before the end of the operation, this // 'simulates' scrolling that position into view in a cheap way, so // that the effect of intermediate scroll commands is not ignored. function resolveScrollToPos(cm) { var range = cm.curOp.scrollToPos; if (range) { cm.curOp.scrollToPos = null; var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to); var sPos = calculateScrollPos(cm, Math.min(from.left, to.left), Math.min(from.top, to.top) - range.margin, Math.max(from.right, to.right), Math.max(from.bottom, to.bottom) + range.margin); cm.scrollTo(sPos.scrollLeft, sPos.scrollTop); } } // API UTILITIES // Indent the given line. The how parameter can be "smart", // "add"/null, "subtract", or "prev". When aggressive is false // (typically set to true for forced single-line indents), empty // lines are not indented, and places where the mode returns Pass // are left alone. function indentLine(cm, n, how, aggressive) { var doc = cm.doc, state; if (how == null) how = "add"; if (how == "smart") { // Fall back to "prev" when the mode doesn't have an indentation // method. if (!doc.mode.indent) how = "prev"; else state = getStateBefore(cm, n); } var tabSize = cm.options.tabSize; var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize); if (line.stateAfter) line.stateAfter = null; var curSpaceString = line.text.match(/^\s*/)[0], indentation; if (!aggressive && !/\S/.test(line.text)) { indentation = 0; how = "not"; } else if (how == "smart") { indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text); if (indentation == Pass || indentation > 150) { if (!aggressive) return; how = "prev"; } } if (how == "prev") { if (n > doc.first) indentation = countColumn(getLine(doc, n-1).text, null, tabSize); else indentation = 0; } else if (how == "add") { indentation = curSpace + cm.options.indentUnit; } else if (how == "subtract") { indentation = curSpace - cm.options.indentUnit; } else if (typeof how == "number") { indentation = curSpace + how; } indentation = Math.max(0, indentation); var indentString = "", pos = 0; if (cm.options.indentWithTabs) for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} if (pos < indentation) indentString += spaceStr(indentation - pos); if (indentString != curSpaceString) { replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input"); line.stateAfter = null; return true; } else { // Ensure that, if the cursor was in the whitespace at the start // of the line, it is moved to the end of that space. for (var i = 0; i < doc.sel.ranges.length; i++) { var range = doc.sel.ranges[i]; if (range.head.line == n && range.head.ch < curSpaceString.length) { var pos = Pos(n, curSpaceString.length); replaceOneSelection(doc, i, new Range(pos, pos)); break; } } } } // Utility for applying a change to a line by handle or number, // returning the number and optionally registering the line as // changed. function changeLine(doc, handle, changeType, op) { var no = handle, line = handle; if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle)); else no = lineNo(handle); if (no == null) return null; if (op(line, no) && doc.cm) regLineChange(doc.cm, no, changeType); return line; } // Helper for deleting text near the selection(s), used to implement // backspace, delete, and similar functionality. function deleteNearSelection(cm, compute) { var ranges = cm.doc.sel.ranges, kill = []; // Build up a set of ranges to kill first, merging overlapping // ranges. for (var i = 0; i < ranges.length; i++) { var toKill = compute(ranges[i]); while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) { var replaced = kill.pop(); if (cmp(replaced.from, toKill.from) < 0) { toKill.from = replaced.from; break; } } kill.push(toKill); } // Next, remove those actual ranges. runInOp(cm, function() { for (var i = kill.length - 1; i >= 0; i--) replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete"); ensureCursorVisible(cm); }); } // Used for horizontal relative motion. Dir is -1 or 1 (left or // right), unit can be "char", "column" (like char, but doesn't // cross line boundaries), "word" (across next word), or "group" (to // the start of next group of word or non-word-non-whitespace // chars). The visually param controls whether, in right-to-left // text, direction 1 means to move towards the next index in the // string, or towards the character to the right of the current // position. The resulting position will have a hitSide=true // property if it reached the end of the document. function findPosH(doc, pos, dir, unit, visually) { var line = pos.line, ch = pos.ch, origDir = dir; var lineObj = getLine(doc, line); function findNextLine() { var l = line + dir; if (l < doc.first || l >= doc.first + doc.size) return false line = l; return lineObj = getLine(doc, l); } function moveOnce(boundToLine) { var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true); if (next == null) { if (!boundToLine && findNextLine()) { if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj); else ch = dir < 0 ? lineObj.text.length : 0; } else return false } else ch = next; return true; } if (unit == "char") { moveOnce() } else if (unit == "column") { moveOnce(true) } else if (unit == "word" || unit == "group") { var sawType = null, group = unit == "group"; var helper = doc.cm && doc.cm.getHelper(pos, "wordChars"); for (var first = true;; first = false) { if (dir < 0 && !moveOnce(!first)) break; var cur = lineObj.text.charAt(ch) || "\n"; var type = isWordChar(cur, helper) ? "w" : group && cur == "\n" ? "n" : !group || /\s/.test(cur) ? null : "p"; if (group && !first && !type) type = "s"; if (sawType && sawType != type) { if (dir < 0) {dir = 1; moveOnce();} break; } if (type) sawType = type; if (dir > 0 && !moveOnce(!first)) break; } } var result = skipAtomic(doc, Pos(line, ch), pos, origDir, true); if (!cmp(pos, result)) result.hitSide = true; return result; } // For relative vertical movement. Dir may be -1 or 1. Unit can be // "page" or "line". The resulting position will have a hitSide=true // property if it reached the end of the document. function findPosV(cm, pos, dir, unit) { var doc = cm.doc, x = pos.left, y; if (unit == "page") { var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight); y = pos.top + dir * (pageSize - (dir < 0 ? 1.5 : .5) * textHeight(cm.display)); } else if (unit == "line") { y = dir > 0 ? pos.bottom + 3 : pos.top - 3; } for (;;) { var target = coordsChar(cm, x, y); if (!target.outside) break; if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break; } y += dir * 5; } return target; } // EDITOR METHODS // The publicly visible API. Note that methodOp(f) means // 'wrap f in an operation, performed on its `this` parameter'. // This is not the complete set of editor methods. Most of the // methods defined on the Doc type are also injected into // CodeMirror.prototype, for backwards compatibility and // convenience. CodeMirror.prototype = { constructor: CodeMirror, focus: function(){window.focus(); this.display.input.focus();}, setOption: function(option, value) { var options = this.options, old = options[option]; if (options[option] == value && option != "mode") return; options[option] = value; if (optionHandlers.hasOwnProperty(option)) operation(this, optionHandlers[option])(this, value, old); }, getOption: function(option) {return this.options[option];}, getDoc: function() {return this.doc;}, addKeyMap: function(map, bottom) { this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map)); }, removeKeyMap: function(map) { var maps = this.state.keyMaps; for (var i = 0; i < maps.length; ++i) if (maps[i] == map || maps[i].name == map) { maps.splice(i, 1); return true; } }, addOverlay: methodOp(function(spec, options) { var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec); if (mode.startState) throw new Error("Overlays may not be stateful."); this.state.overlays.push({mode: mode, modeSpec: spec, opaque: options && options.opaque}); this.state.modeGen++; regChange(this); }), removeOverlay: methodOp(function(spec) { var overlays = this.state.overlays; for (var i = 0; i < overlays.length; ++i) { var cur = overlays[i].modeSpec; if (cur == spec || typeof spec == "string" && cur.name == spec) { overlays.splice(i, 1); this.state.modeGen++; regChange(this); return; } } }), indentLine: methodOp(function(n, dir, aggressive) { if (typeof dir != "string" && typeof dir != "number") { if (dir == null) dir = this.options.smartIndent ? "smart" : "prev"; else dir = dir ? "add" : "subtract"; } if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive); }), indentSelection: methodOp(function(how) { var ranges = this.doc.sel.ranges, end = -1; for (var i = 0; i < ranges.length; i++) { var range = ranges[i]; if (!range.empty()) { var from = range.from(), to = range.to(); var start = Math.max(end, from.line); end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1; for (var j = start; j < end; ++j) indentLine(this, j, how); var newRanges = this.doc.sel.ranges; if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0) replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); } else if (range.head.line > end) { indentLine(this, range.head.line, how, true); end = range.head.line; if (i == this.doc.sel.primIndex) ensureCursorVisible(this); } } }), // Fetch the parser token for a given character. Useful for hacks // that want to inspect the mode state (say, for completion). getTokenAt: function(pos, precise) { return takeToken(this, pos, precise); }, getLineTokens: function(line, precise) { return takeToken(this, Pos(line), precise, true); }, getTokenTypeAt: function(pos) { pos = clipPos(this.doc, pos); var styles = getLineStyles(this, getLine(this.doc, pos.line)); var before = 0, after = (styles.length - 1) / 2, ch = pos.ch; var type; if (ch == 0) type = styles[2]; else for (;;) { var mid = (before + after) >> 1; if ((mid ? styles[mid * 2 - 1] : 0) >= ch) after = mid; else if (styles[mid * 2 + 1] < ch) before = mid + 1; else { type = styles[mid * 2 + 2]; break; } } var cut = type ? type.indexOf("cm-overlay ") : -1; return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1); }, getModeAt: function(pos) { var mode = this.doc.mode; if (!mode.innerMode) return mode; return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode; }, getHelper: function(pos, type) { return this.getHelpers(pos, type)[0]; }, getHelpers: function(pos, type) { var found = []; if (!helpers.hasOwnProperty(type)) return found; var help = helpers[type], mode = this.getModeAt(pos); if (typeof mode[type] == "string") { if (help[mode[type]]) found.push(help[mode[type]]); } else if (mode[type]) { for (var i = 0; i < mode[type].length; i++) { var val = help[mode[type][i]]; if (val) found.push(val); } } else if (mode.helperType && help[mode.helperType]) { found.push(help[mode.helperType]); } else if (help[mode.name]) { found.push(help[mode.name]); } for (var i = 0; i < help._global.length; i++) { var cur = help._global[i]; if (cur.pred(mode, this) && indexOf(found, cur.val) == -1) found.push(cur.val); } return found; }, getStateAfter: function(line, precise) { var doc = this.doc; line = clipLine(doc, line == null ? doc.first + doc.size - 1: line); return getStateBefore(this, line + 1, precise); }, cursorCoords: function(start, mode) { var pos, range = this.doc.sel.primary(); if (start == null) pos = range.head; else if (typeof start == "object") pos = clipPos(this.doc, start); else pos = start ? range.from() : range.to(); return cursorCoords(this, pos, mode || "page"); }, charCoords: function(pos, mode) { return charCoords(this, clipPos(this.doc, pos), mode || "page"); }, coordsChar: function(coords, mode) { coords = fromCoordSystem(this, coords, mode || "page"); return coordsChar(this, coords.left, coords.top); }, lineAtHeight: function(height, mode) { height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top; return lineAtHeight(this.doc, height + this.display.viewOffset); }, heightAtLine: function(line, mode) { var end = false, lineObj; if (typeof line == "number") { var last = this.doc.first + this.doc.size - 1; if (line < this.doc.first) line = this.doc.first; else if (line > last) { line = last; end = true; } lineObj = getLine(this.doc, line); } else { lineObj = line; } return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page").top + (end ? this.doc.height - heightAtLine(lineObj) : 0); }, defaultTextHeight: function() { return textHeight(this.display); }, defaultCharWidth: function() { return charWidth(this.display); }, setGutterMarker: methodOp(function(line, gutterID, value) { return changeLine(this.doc, line, "gutter", function(line) { var markers = line.gutterMarkers || (line.gutterMarkers = {}); markers[gutterID] = value; if (!value && isEmpty(markers)) line.gutterMarkers = null; return true; }); }), clearGutter: methodOp(function(gutterID) { var cm = this, doc = cm.doc, i = doc.first; doc.iter(function(line) { if (line.gutterMarkers && line.gutterMarkers[gutterID]) { line.gutterMarkers[gutterID] = null; regLineChange(cm, i, "gutter"); if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null; } ++i; }); }), lineInfo: function(line) { if (typeof line == "number") { if (!isLine(this.doc, line)) return null; var n = line; line = getLine(this.doc, line); if (!line) return null; } else { var n = lineNo(line); if (n == null) return null; } return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers, textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass, widgets: line.widgets}; }, getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo};}, addWidget: function(pos, node, scroll, vert, horiz) { var display = this.display; pos = cursorCoords(this, clipPos(this.doc, pos)); var top = pos.bottom, left = pos.left; node.style.position = "absolute"; node.setAttribute("cm-ignore-events", "true"); this.display.input.setUneditable(node); display.sizer.appendChild(node); if (vert == "over") { top = pos.top; } else if (vert == "above" || vert == "near") { var vspace = Math.max(display.wrapper.clientHeight, this.doc.height), hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth); // Default to positioning above (if specified and possible); otherwise default to positioning below if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight) top = pos.top - node.offsetHeight; else if (pos.bottom + node.offsetHeight <= vspace) top = pos.bottom; if (left + node.offsetWidth > hspace) left = hspace - node.offsetWidth; } node.style.top = top + "px"; node.style.left = node.style.right = ""; if (horiz == "right") { left = display.sizer.clientWidth - node.offsetWidth; node.style.right = "0px"; } else { if (horiz == "left") left = 0; else if (horiz == "middle") left = (display.sizer.clientWidth - node.offsetWidth) / 2; node.style.left = left + "px"; } if (scroll) scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight); }, triggerOnKeyDown: methodOp(onKeyDown), triggerOnKeyPress: methodOp(onKeyPress), triggerOnKeyUp: onKeyUp, execCommand: function(cmd) { if (commands.hasOwnProperty(cmd)) return commands[cmd].call(null, this); }, triggerElectric: methodOp(function(text) { triggerElectric(this, text); }), findPosH: function(from, amount, unit, visually) { var dir = 1; if (amount < 0) { dir = -1; amount = -amount; } for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) { cur = findPosH(this.doc, cur, dir, unit, visually); if (cur.hitSide) break; } return cur; }, moveH: methodOp(function(dir, unit) { var cm = this; cm.extendSelectionsBy(function(range) { if (cm.display.shift || cm.doc.extend || range.empty()) return findPosH(cm.doc, range.head, dir, unit, cm.options.rtlMoveVisually); else return dir < 0 ? range.from() : range.to(); }, sel_move); }), deleteH: methodOp(function(dir, unit) { var sel = this.doc.sel, doc = this.doc; if (sel.somethingSelected()) doc.replaceSelection("", null, "+delete"); else deleteNearSelection(this, function(range) { var other = findPosH(doc, range.head, dir, unit, false); return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other}; }); }), findPosV: function(from, amount, unit, goalColumn) { var dir = 1, x = goalColumn; if (amount < 0) { dir = -1; amount = -amount; } for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) { var coords = cursorCoords(this, cur, "div"); if (x == null) x = coords.left; else coords.left = x; cur = findPosV(this, coords, dir, unit); if (cur.hitSide) break; } return cur; }, moveV: methodOp(function(dir, unit) { var cm = this, doc = this.doc, goals = []; var collapse = !cm.display.shift && !doc.extend && doc.sel.somethingSelected(); doc.extendSelectionsBy(function(range) { if (collapse) return dir < 0 ? range.from() : range.to(); var headPos = cursorCoords(cm, range.head, "div"); if (range.goalColumn != null) headPos.left = range.goalColumn; goals.push(headPos.left); var pos = findPosV(cm, headPos, dir, unit); if (unit == "page" && range == doc.sel.primary()) addToScrollPos(cm, null, charCoords(cm, pos, "div").top - headPos.top); return pos; }, sel_move); if (goals.length) for (var i = 0; i < doc.sel.ranges.length; i++) doc.sel.ranges[i].goalColumn = goals[i]; }), // Find the word at the given position (as returned by coordsChar). findWordAt: function(pos) { var doc = this.doc, line = getLine(doc, pos.line).text; var start = pos.ch, end = pos.ch; if (line) { var helper = this.getHelper(pos, "wordChars"); if ((pos.xRel < 0 || end == line.length) && start) --start; else ++end; var startChar = line.charAt(start); var check = isWordChar(startChar, helper) ? function(ch) { return isWordChar(ch, helper); } : /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);} : function(ch) {return !/\s/.test(ch) && !isWordChar(ch);}; while (start > 0 && check(line.charAt(start - 1))) --start; while (end < line.length && check(line.charAt(end))) ++end; } return new Range(Pos(pos.line, start), Pos(pos.line, end)); }, toggleOverwrite: function(value) { if (value != null && value == this.state.overwrite) return; if (this.state.overwrite = !this.state.overwrite) addClass(this.display.cursorDiv, "CodeMirror-overwrite"); else rmClass(this.display.cursorDiv, "CodeMirror-overwrite"); signal(this, "overwriteToggle", this, this.state.overwrite); }, hasFocus: function() { return this.display.input.getField() == activeElt(); }, isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit); }, scrollTo: methodOp(function(x, y) { if (x != null || y != null) resolveScrollToPos(this); if (x != null) this.curOp.scrollLeft = x; if (y != null) this.curOp.scrollTop = y; }), getScrollInfo: function() { var scroller = this.display.scroller; return {left: scroller.scrollLeft, top: scroller.scrollTop, height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight, width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth, clientHeight: displayHeight(this), clientWidth: displayWidth(this)}; }, scrollIntoView: methodOp(function(range, margin) { if (range == null) { range = {from: this.doc.sel.primary().head, to: null}; if (margin == null) margin = this.options.cursorScrollMargin; } else if (typeof range == "number") { range = {from: Pos(range, 0), to: null}; } else if (range.from == null) { range = {from: range, to: null}; } if (!range.to) range.to = range.from; range.margin = margin || 0; if (range.from.line != null) { resolveScrollToPos(this); this.curOp.scrollToPos = range; } else { var sPos = calculateScrollPos(this, Math.min(range.from.left, range.to.left), Math.min(range.from.top, range.to.top) - range.margin, Math.max(range.from.right, range.to.right), Math.max(range.from.bottom, range.to.bottom) + range.margin); this.scrollTo(sPos.scrollLeft, sPos.scrollTop); } }), setSize: methodOp(function(width, height) { var cm = this; function interpret(val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; } if (width != null) cm.display.wrapper.style.width = interpret(width); if (height != null) cm.display.wrapper.style.height = interpret(height); if (cm.options.lineWrapping) clearLineMeasurementCache(this); var lineNo = cm.display.viewFrom; cm.doc.iter(lineNo, cm.display.viewTo, function(line) { if (line.widgets) for (var i = 0; i < line.widgets.length; i++) if (line.widgets[i].noHScroll) { regLineChange(cm, lineNo, "widget"); break; } ++lineNo; }); cm.curOp.forceUpdate = true; signal(cm, "refresh", this); }), operation: function(f){return runInOp(this, f);}, refresh: methodOp(function() { var oldHeight = this.display.cachedTextHeight; regChange(this); this.curOp.forceUpdate = true; clearCaches(this); this.scrollTo(this.doc.scrollLeft, this.doc.scrollTop); updateGutterSpace(this); if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5) estimateLineHeights(this); signal(this, "refresh", this); }), swapDoc: methodOp(function(doc) { var old = this.doc; old.cm = null; attachDoc(this, doc); clearCaches(this); this.display.input.reset(); this.scrollTo(doc.scrollLeft, doc.scrollTop); this.curOp.forceScroll = true; signalLater(this, "swapDoc", this, old); return old; }), getInputField: function(){return this.display.input.getField();}, getWrapperElement: function(){return this.display.wrapper;}, getScrollerElement: function(){return this.display.scroller;}, getGutterElement: function(){return this.display.gutters;} }; eventMixin(CodeMirror); // OPTION DEFAULTS // The default configuration options. var defaults = CodeMirror.defaults = {}; // Functions to run when options are changed. var optionHandlers = CodeMirror.optionHandlers = {}; function option(name, deflt, handle, notOnInit) { CodeMirror.defaults[name] = deflt; if (handle) optionHandlers[name] = notOnInit ? function(cm, val, old) {if (old != Init) handle(cm, val, old);} : handle; } // Passed to option handlers when there is no old value. var Init = CodeMirror.Init = {toString: function(){return "CodeMirror.Init";}}; // These two are, on init, called from the constructor because they // have to be initialized before the editor can start at all. option("value", "", function(cm, val) { cm.setValue(val); }, true); option("mode", null, function(cm, val) { cm.doc.modeOption = val; loadMode(cm); }, true); option("indentUnit", 2, loadMode, true); option("indentWithTabs", false); option("smartIndent", true); option("tabSize", 4, function(cm) { resetModeState(cm); clearCaches(cm); regChange(cm); }, true); option("lineSeparator", null, function(cm, val) { cm.doc.lineSep = val; if (!val) return; var newBreaks = [], lineNo = cm.doc.first; cm.doc.iter(function(line) { for (var pos = 0;;) { var found = line.text.indexOf(val, pos); if (found == -1) break; pos = found + val.length; newBreaks.push(Pos(lineNo, found)); } lineNo++; }); for (var i = newBreaks.length - 1; i >= 0; i--) replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)) }); option("specialChars", /[\u0000-\u001f\u007f\u00ad\u200b-\u200f\u2028\u2029\ufeff]/g, function(cm, val, old) { cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g"); if (old != CodeMirror.Init) cm.refresh(); }); option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function(cm) {cm.refresh();}, true); option("electricChars", true); option("inputStyle", mobile ? "contenteditable" : "textarea", function() { throw new Error("inputStyle can not (yet) be changed in a running editor"); // FIXME }, true); option("rtlMoveVisually", !windows); option("wholeLineUpdateBefore", true); option("theme", "default", function(cm) { themeChanged(cm); guttersChanged(cm); }, true); option("keyMap", "default", function(cm, val, old) { var next = getKeyMap(val); var prev = old != CodeMirror.Init && getKeyMap(old); if (prev && prev.detach) prev.detach(cm, next); if (next.attach) next.attach(cm, prev || null); }); option("extraKeys", null); option("lineWrapping", false, wrappingChanged, true); option("gutters", [], function(cm) { setGuttersForLineNumbers(cm.options); guttersChanged(cm); }, true); option("fixedGutter", true, function(cm, val) { cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0"; cm.refresh(); }, true); option("coverGutterNextToScrollbar", false, function(cm) {updateScrollbars(cm);}, true); option("scrollbarStyle", "native", function(cm) { initScrollbars(cm); updateScrollbars(cm); cm.display.scrollbars.setScrollTop(cm.doc.scrollTop); cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft); }, true); option("lineNumbers", false, function(cm) { setGuttersForLineNumbers(cm.options); guttersChanged(cm); }, true); option("firstLineNumber", 1, guttersChanged, true); option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true); option("showCursorWhenSelecting", false, updateSelection, true); option("resetSelectionOnContextMenu", true); option("lineWiseCopyCut", true); option("readOnly", false, function(cm, val) { if (val == "nocursor") { onBlur(cm); cm.display.input.blur(); cm.display.disabled = true; } else { cm.display.disabled = false; } cm.display.input.readOnlyChanged(val) }); option("disableInput", false, function(cm, val) {if (!val) cm.display.input.reset();}, true); option("dragDrop", true, dragDropChanged); option("allowDropFileTypes", null); option("cursorBlinkRate", 530); option("cursorScrollMargin", 0); option("cursorHeight", 1, updateSelection, true); option("singleCursorHeightPerLine", true, updateSelection, true); option("workTime", 100); option("workDelay", 100); option("flattenSpans", true, resetModeState, true); option("addModeClass", false, resetModeState, true); option("pollInterval", 100); option("undoDepth", 200, function(cm, val){cm.doc.history.undoDepth = val;}); option("historyEventDelay", 1250); option("viewportMargin", 10, function(cm){cm.refresh();}, true); option("maxHighlightLength", 10000, resetModeState, true); option("moveInputWithCursor", true, function(cm, val) { if (!val) cm.display.input.resetPosition(); }); option("tabindex", null, function(cm, val) { cm.display.input.getField().tabIndex = val || ""; }); option("autofocus", null); // MODE DEFINITION AND QUERYING // Known modes, by name and by MIME var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {}; // Extra arguments are stored as the mode's dependencies, which is // used by (legacy) mechanisms like loadmode.js to automatically // load a mode. (Preferred mechanism is the require/define calls.) CodeMirror.defineMode = function(name, mode) { if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name; if (arguments.length > 2) mode.dependencies = Array.prototype.slice.call(arguments, 2); modes[name] = mode; }; CodeMirror.defineMIME = function(mime, spec) { mimeModes[mime] = spec; }; // Given a MIME type, a {name, ...options} config object, or a name // string, return a mode config object. CodeMirror.resolveMode = function(spec) { if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) { spec = mimeModes[spec]; } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) { var found = mimeModes[spec.name]; if (typeof found == "string") found = {name: found}; spec = createObj(found, spec); spec.name = found.name; } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) { return CodeMirror.resolveMode("application/xml"); } if (typeof spec == "string") return {name: spec}; else return spec || {name: "null"}; }; // Given a mode spec (anything that resolveMode accepts), find and // initialize an actual mode object. CodeMirror.getMode = function(options, spec) { var spec = CodeMirror.resolveMode(spec); var mfactory = modes[spec.name]; if (!mfactory) return CodeMirror.getMode(options, "text/plain"); var modeObj = mfactory(options, spec); if (modeExtensions.hasOwnProperty(spec.name)) { var exts = modeExtensions[spec.name]; for (var prop in exts) { if (!exts.hasOwnProperty(prop)) continue; if (modeObj.hasOwnProperty(prop)) modeObj["_" + prop] = modeObj[prop]; modeObj[prop] = exts[prop]; } } modeObj.name = spec.name; if (spec.helperType) modeObj.helperType = spec.helperType; if (spec.modeProps) for (var prop in spec.modeProps) modeObj[prop] = spec.modeProps[prop]; return modeObj; }; // Minimal default mode. CodeMirror.defineMode("null", function() { return {token: function(stream) {stream.skipToEnd();}}; }); CodeMirror.defineMIME("text/plain", "null"); // This can be used to attach properties to mode objects from // outside the actual mode definition. var modeExtensions = CodeMirror.modeExtensions = {}; CodeMirror.extendMode = function(mode, properties) { var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {}); copyObj(properties, exts); }; // EXTENSIONS CodeMirror.defineExtension = function(name, func) { CodeMirror.prototype[name] = func; }; CodeMirror.defineDocExtension = function(name, func) { Doc.prototype[name] = func; }; CodeMirror.defineOption = option; var initHooks = []; CodeMirror.defineInitHook = function(f) {initHooks.push(f);}; var helpers = CodeMirror.helpers = {}; CodeMirror.registerHelper = function(type, name, value) { if (!helpers.hasOwnProperty(type)) helpers[type] = CodeMirror[type] = {_global: []}; helpers[type][name] = value; }; CodeMirror.registerGlobalHelper = function(type, name, predicate, value) { CodeMirror.registerHelper(type, name, value); helpers[type]._global.push({pred: predicate, val: value}); }; // MODE STATE HANDLING // Utility functions for working with state. Exported because nested // modes need to do this for their inner modes. var copyState = CodeMirror.copyState = function(mode, state) { if (state === true) return state; if (mode.copyState) return mode.copyState(state); var nstate = {}; for (var n in state) { var val = state[n]; if (val instanceof Array) val = val.concat([]); nstate[n] = val; } return nstate; }; var startState = CodeMirror.startState = function(mode, a1, a2) { return mode.startState ? mode.startState(a1, a2) : true; }; // Given a mode and a state (for that mode), find the inner mode and // state at the position that the state refers to. CodeMirror.innerMode = function(mode, state) { while (mode.innerMode) { var info = mode.innerMode(state); if (!info || info.mode == mode) break; state = info.state; mode = info.mode; } return info || {mode: mode, state: state}; }; // STANDARD COMMANDS // Commands are parameter-less actions that can be performed on an // editor, mostly used for keybindings. var commands = CodeMirror.commands = { selectAll: function(cm) {cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll);}, singleSelection: function(cm) { cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); }, killLine: function(cm) { deleteNearSelection(cm, function(range) { if (range.empty()) { var len = getLine(cm.doc, range.head.line).text.length; if (range.head.ch == len && range.head.line < cm.lastLine()) return {from: range.head, to: Pos(range.head.line + 1, 0)}; else return {from: range.head, to: Pos(range.head.line, len)}; } else { return {from: range.from(), to: range.to()}; } }); }, deleteLine: function(cm) { deleteNearSelection(cm, function(range) { return {from: Pos(range.from().line, 0), to: clipPos(cm.doc, Pos(range.to().line + 1, 0))}; }); }, delLineLeft: function(cm) { deleteNearSelection(cm, function(range) { return {from: Pos(range.from().line, 0), to: range.from()}; }); }, delWrappedLineLeft: function(cm) { deleteNearSelection(cm, function(range) { var top = cm.charCoords(range.head, "div").top + 5; var leftPos = cm.coordsChar({left: 0, top: top}, "div"); return {from: leftPos, to: range.from()}; }); }, delWrappedLineRight: function(cm) { deleteNearSelection(cm, function(range) { var top = cm.charCoords(range.head, "div").top + 5; var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div"); return {from: range.from(), to: rightPos }; }); }, undo: function(cm) {cm.undo();}, redo: function(cm) {cm.redo();}, undoSelection: function(cm) {cm.undoSelection();}, redoSelection: function(cm) {cm.redoSelection();}, goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));}, goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()));}, goLineStart: function(cm) { cm.extendSelectionsBy(function(range) { return lineStart(cm, range.head.line); }, {origin: "+move", bias: 1}); }, goLineStartSmart: function(cm) { cm.extendSelectionsBy(function(range) { return lineStartSmart(cm, range.head); }, {origin: "+move", bias: 1}); }, goLineEnd: function(cm) { cm.extendSelectionsBy(function(range) { return lineEnd(cm, range.head.line); }, {origin: "+move", bias: -1}); }, goLineRight: function(cm) { cm.extendSelectionsBy(function(range) { var top = cm.charCoords(range.head, "div").top + 5; return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div"); }, sel_move); }, goLineLeft: function(cm) { cm.extendSelectionsBy(function(range) { var top = cm.charCoords(range.head, "div").top + 5; return cm.coordsChar({left: 0, top: top}, "div"); }, sel_move); }, goLineLeftSmart: function(cm) { cm.extendSelectionsBy(function(range) { var top = cm.charCoords(range.head, "div").top + 5; var pos = cm.coordsChar({left: 0, top: top}, "div"); if (pos.ch < cm.getLine(pos.line).search(/\S/)) return lineStartSmart(cm, range.head); return pos; }, sel_move); }, goLineUp: function(cm) {cm.moveV(-1, "line");}, goLineDown: function(cm) {cm.moveV(1, "line");}, goPageUp: function(cm) {cm.moveV(-1, "page");}, goPageDown: function(cm) {cm.moveV(1, "page");}, goCharLeft: function(cm) {cm.moveH(-1, "char");}, goCharRight: function(cm) {cm.moveH(1, "char");}, goColumnLeft: function(cm) {cm.moveH(-1, "column");}, goColumnRight: function(cm) {cm.moveH(1, "column");}, goWordLeft: function(cm) {cm.moveH(-1, "word");}, goGroupRight: function(cm) {cm.moveH(1, "group");}, goGroupLeft: function(cm) {cm.moveH(-1, "group");}, goWordRight: function(cm) {cm.moveH(1, "word");}, delCharBefore: function(cm) {cm.deleteH(-1, "char");}, delCharAfter: function(cm) {cm.deleteH(1, "char");}, delWordBefore: function(cm) {cm.deleteH(-1, "word");}, delWordAfter: function(cm) {cm.deleteH(1, "word");}, delGroupBefore: function(cm) {cm.deleteH(-1, "group");}, delGroupAfter: function(cm) {cm.deleteH(1, "group");}, indentAuto: function(cm) {cm.indentSelection("smart");}, indentMore: function(cm) {cm.indentSelection("add");}, indentLess: function(cm) {cm.indentSelection("subtract");}, insertTab: function(cm) {cm.replaceSelection("\t");}, insertSoftTab: function(cm) { var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize; for (var i = 0; i < ranges.length; i++) { var pos = ranges[i].from(); var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize); spaces.push(spaceStr(tabSize - col % tabSize)); } cm.replaceSelections(spaces); }, defaultTab: function(cm) { if (cm.somethingSelected()) cm.indentSelection("add"); else cm.execCommand("insertTab"); }, transposeChars: function(cm) { runInOp(cm, function() { var ranges = cm.listSelections(), newSel = []; for (var i = 0; i < ranges.length; i++) { var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text; if (line) { if (cur.ch == line.length) cur = new Pos(cur.line, cur.ch - 1); if (cur.ch > 0) { cur = new Pos(cur.line, cur.ch + 1); cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2), Pos(cur.line, cur.ch - 2), cur, "+transpose"); } else if (cur.line > cm.doc.first) { var prev = getLine(cm.doc, cur.line - 1).text; if (prev) cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() + prev.charAt(prev.length - 1), Pos(cur.line - 1, prev.length - 1), Pos(cur.line, 1), "+transpose"); } } newSel.push(new Range(cur, cur)); } cm.setSelections(newSel); }); }, newlineAndIndent: function(cm) { runInOp(cm, function() { var len = cm.listSelections().length; for (var i = 0; i < len; i++) { var range = cm.listSelections()[i]; cm.replaceRange(cm.doc.lineSeparator(), range.anchor, range.head, "+input"); cm.indentLine(range.from().line + 1, null, true); } ensureCursorVisible(cm); }); }, openLine: function(cm) {cm.replaceSelection("\n", "start")}, toggleOverwrite: function(cm) {cm.toggleOverwrite();} }; // STANDARD KEYMAPS var keyMap = CodeMirror.keyMap = {}; keyMap.basic = { "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown", "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown", "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore", "Tab": "defaultTab", "Shift-Tab": "indentAuto", "Enter": "newlineAndIndent", "Insert": "toggleOverwrite", "Esc": "singleSelection" }; // Note that the save and find-related commands aren't defined by // default. User code or addons can define them. Unknown commands // are simply ignored. keyMap.pcDefault = { "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo", "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown", "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd", "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find", "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll", "Ctrl-[": "indentLess", "Ctrl-]": "indentMore", "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection", fallthrough: "basic" }; // Very basic readline/emacs-style bindings, which are standard on Mac. keyMap.emacsy = { "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown", "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd", "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore", "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars", "Ctrl-O": "openLine" }; keyMap.macDefault = { "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo", "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft", "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore", "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find", "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll", "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight", "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd", fallthrough: ["basic", "emacsy"] }; keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault; // KEYMAP DISPATCH function normalizeKeyName(name) { var parts = name.split(/-(?!$)/), name = parts[parts.length - 1]; var alt, ctrl, shift, cmd; for (var i = 0; i < parts.length - 1; i++) { var mod = parts[i]; if (/^(cmd|meta|m)$/i.test(mod)) cmd = true; else if (/^a(lt)?$/i.test(mod)) alt = true; else if (/^(c|ctrl|control)$/i.test(mod)) ctrl = true; else if (/^s(hift)$/i.test(mod)) shift = true; else throw new Error("Unrecognized modifier name: " + mod); } if (alt) name = "Alt-" + name; if (ctrl) name = "Ctrl-" + name; if (cmd) name = "Cmd-" + name; if (shift) name = "Shift-" + name; return name; } // This is a kludge to keep keymaps mostly working as raw objects // (backwards compatibility) while at the same time support features // like normalization and multi-stroke key bindings. It compiles a // new normalized keymap, and then updates the old object to reflect // this. CodeMirror.normalizeKeyMap = function(keymap) { var copy = {}; for (var keyname in keymap) if (keymap.hasOwnProperty(keyname)) { var value = keymap[keyname]; if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) continue; if (value == "...") { delete keymap[keyname]; continue; } var keys = map(keyname.split(" "), normalizeKeyName); for (var i = 0; i < keys.length; i++) { var val, name; if (i == keys.length - 1) { name = keys.join(" "); val = value; } else { name = keys.slice(0, i + 1).join(" "); val = "..."; } var prev = copy[name]; if (!prev) copy[name] = val; else if (prev != val) throw new Error("Inconsistent bindings for " + name); } delete keymap[keyname]; } for (var prop in copy) keymap[prop] = copy[prop]; return keymap; }; var lookupKey = CodeMirror.lookupKey = function(key, map, handle, context) { map = getKeyMap(map); var found = map.call ? map.call(key, context) : map[key]; if (found === false) return "nothing"; if (found === "...") return "multi"; if (found != null && handle(found)) return "handled"; if (map.fallthrough) { if (Object.prototype.toString.call(map.fallthrough) != "[object Array]") return lookupKey(key, map.fallthrough, handle, context); for (var i = 0; i < map.fallthrough.length; i++) { var result = lookupKey(key, map.fallthrough[i], handle, context); if (result) return result; } } }; // Modifier key presses don't count as 'real' key presses for the // purpose of keymap fallthrough. var isModifierKey = CodeMirror.isModifierKey = function(value) { var name = typeof value == "string" ? value : keyNames[value.keyCode]; return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"; }; // Look up the name of a key as indicated by an event object. var keyName = CodeMirror.keyName = function(event, noShift) { if (presto && event.keyCode == 34 && event["char"]) return false; var base = keyNames[event.keyCode], name = base; if (name == null || event.altGraphKey) return false; if (event.altKey && base != "Alt") name = "Alt-" + name; if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") name = "Ctrl-" + name; if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") name = "Cmd-" + name; if (!noShift && event.shiftKey && base != "Shift") name = "Shift-" + name; return name; }; function getKeyMap(val) { return typeof val == "string" ? keyMap[val] : val; } // FROMTEXTAREA CodeMirror.fromTextArea = function(textarea, options) { options = options ? copyObj(options) : {}; options.value = textarea.value; if (!options.tabindex && textarea.tabIndex) options.tabindex = textarea.tabIndex; if (!options.placeholder && textarea.placeholder) options.placeholder = textarea.placeholder; // Set autofocus to true if this textarea is focused, or if it has // autofocus and no other element is focused. if (options.autofocus == null) { var hasFocus = activeElt(); options.autofocus = hasFocus == textarea || textarea.getAttribute("autofocus") != null && hasFocus == document.body; } function save() {textarea.value = cm.getValue();} if (textarea.form) { on(textarea.form, "submit", save); // Deplorable hack to make the submit method do the right thing. if (!options.leaveSubmitMethodAlone) { var form = textarea.form, realSubmit = form.submit; try { var wrappedSubmit = form.submit = function() { save(); form.submit = realSubmit; form.submit(); form.submit = wrappedSubmit; }; } catch(e) {} } } options.finishInit = function(cm) { cm.save = save; cm.getTextArea = function() { return textarea; }; cm.toTextArea = function() { cm.toTextArea = isNaN; // Prevent this from being ran twice save(); textarea.parentNode.removeChild(cm.getWrapperElement()); textarea.style.display = ""; if (textarea.form) { off(textarea.form, "submit", save); if (typeof textarea.form.submit == "function") textarea.form.submit = realSubmit; } }; }; textarea.style.display = "none"; var cm = CodeMirror(function(node) { textarea.parentNode.insertBefore(node, textarea.nextSibling); }, options); return cm; }; // STRING STREAM // Fed to the mode parsers, provides helper functions to make // parsers more succinct. var StringStream = CodeMirror.StringStream = function(string, tabSize) { this.pos = this.start = 0; this.string = string; this.tabSize = tabSize || 8; this.lastColumnPos = this.lastColumnValue = 0; this.lineStart = 0; }; StringStream.prototype = { eol: function() {return this.pos >= this.string.length;}, sol: function() {return this.pos == this.lineStart;}, peek: function() {return this.string.charAt(this.pos) || undefined;}, next: function() { if (this.pos < this.string.length) return this.string.charAt(this.pos++); }, eat: function(match) { var ch = this.string.charAt(this.pos); if (typeof match == "string") var ok = ch == match; else var ok = ch && (match.test ? match.test(ch) : match(ch)); if (ok) {++this.pos; return ch;} }, eatWhile: function(match) { var start = this.pos; while (this.eat(match)){} return this.pos > start; }, eatSpace: function() { var start = this.pos; while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos; return this.pos > start; }, skipToEnd: function() {this.pos = this.string.length;}, skipTo: function(ch) { var found = this.string.indexOf(ch, this.pos); if (found > -1) {this.pos = found; return true;} }, backUp: function(n) {this.pos -= n;}, column: function() { if (this.lastColumnPos < this.start) { this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue); this.lastColumnPos = this.start; } return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0); }, indentation: function() { return countColumn(this.string, null, this.tabSize) - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0); }, match: function(pattern, consume, caseInsensitive) { if (typeof pattern == "string") { var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;}; var substr = this.string.substr(this.pos, pattern.length); if (cased(substr) == cased(pattern)) { if (consume !== false) this.pos += pattern.length; return true; } } else { var match = this.string.slice(this.pos).match(pattern); if (match && match.index > 0) return null; if (match && consume !== false) this.pos += match[0].length; return match; } }, current: function(){return this.string.slice(this.start, this.pos);}, hideFirstChars: function(n, inner) { this.lineStart += n; try { return inner(); } finally { this.lineStart -= n; } } }; // TEXTMARKERS // Created with markText and setBookmark methods. A TextMarker is a // handle that can be used to clear or find a marked position in the // document. Line objects hold arrays (markedSpans) containing // {from, to, marker} object pointing to such marker objects, and // indicating that such a marker is present on that line. Multiple // lines may point to the same marker when it spans across lines. // The spans will have null for their from/to properties when the // marker continues beyond the start/end of the line. Markers have // links back to the lines they currently touch. var nextMarkerId = 0; var TextMarker = CodeMirror.TextMarker = function(doc, type) { this.lines = []; this.type = type; this.doc = doc; this.id = ++nextMarkerId; }; eventMixin(TextMarker); // Clear the marker. TextMarker.prototype.clear = function() { if (this.explicitlyCleared) return; var cm = this.doc.cm, withOp = cm && !cm.curOp; if (withOp) startOperation(cm); if (hasHandler(this, "clear")) { var found = this.find(); if (found) signalLater(this, "clear", found.from, found.to); } var min = null, max = null; for (var i = 0; i < this.lines.length; ++i) { var line = this.lines[i]; var span = getMarkedSpanFor(line.markedSpans, this); if (cm && !this.collapsed) regLineChange(cm, lineNo(line), "text"); else if (cm) { if (span.to != null) max = lineNo(line); if (span.from != null) min = lineNo(line); } line.markedSpans = removeMarkedSpan(line.markedSpans, span); if (span.from == null && this.collapsed && !lineIsHidden(this.doc, line) && cm) updateLineHeight(line, textHeight(cm.display)); } if (cm && this.collapsed && !cm.options.lineWrapping) for (var i = 0; i < this.lines.length; ++i) { var visual = visualLine(this.lines[i]), len = lineLength(visual); if (len > cm.display.maxLineLength) { cm.display.maxLine = visual; cm.display.maxLineLength = len; cm.display.maxLineChanged = true; } } if (min != null && cm && this.collapsed) regChange(cm, min, max + 1); this.lines.length = 0; this.explicitlyCleared = true; if (this.atomic && this.doc.cantEdit) { this.doc.cantEdit = false; if (cm) reCheckSelection(cm.doc); } if (cm) signalLater(cm, "markerCleared", cm, this); if (withOp) endOperation(cm); if (this.parent) this.parent.clear(); }; // Find the position of the marker in the document. Returns a {from, // to} object by default. Side can be passed to get a specific side // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the // Pos objects returned contain a line object, rather than a line // number (used to prevent looking up the same line twice). TextMarker.prototype.find = function(side, lineObj) { if (side == null && this.type == "bookmark") side = 1; var from, to; for (var i = 0; i < this.lines.length; ++i) { var line = this.lines[i]; var span = getMarkedSpanFor(line.markedSpans, this); if (span.from != null) { from = Pos(lineObj ? line : lineNo(line), span.from); if (side == -1) return from; } if (span.to != null) { to = Pos(lineObj ? line : lineNo(line), span.to); if (side == 1) return to; } } return from && {from: from, to: to}; }; // Signals that the marker's widget changed, and surrounding layout // should be recomputed. TextMarker.prototype.changed = function() { var pos = this.find(-1, true), widget = this, cm = this.doc.cm; if (!pos || !cm) return; runInOp(cm, function() { var line = pos.line, lineN = lineNo(pos.line); var view = findViewForLine(cm, lineN); if (view) { clearLineMeasurementCacheFor(view); cm.curOp.selectionChanged = cm.curOp.forceUpdate = true; } cm.curOp.updateMaxLine = true; if (!lineIsHidden(widget.doc, line) && widget.height != null) { var oldHeight = widget.height; widget.height = null; var dHeight = widgetHeight(widget) - oldHeight; if (dHeight) updateLineHeight(line, line.height + dHeight); } }); }; TextMarker.prototype.attachLine = function(line) { if (!this.lines.length && this.doc.cm) { var op = this.doc.cm.curOp; if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1) (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this); } this.lines.push(line); }; TextMarker.prototype.detachLine = function(line) { this.lines.splice(indexOf(this.lines, line), 1); if (!this.lines.length && this.doc.cm) { var op = this.doc.cm.curOp; (op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this); } }; // Collapsed markers have unique ids, in order to be able to order // them, which is needed for uniquely determining an outer marker // when they overlap (they may nest, but not partially overlap). var nextMarkerId = 0; // Create a marker, wire it up to the right lines, and function markText(doc, from, to, options, type) { // Shared markers (across linked documents) are handled separately // (markTextShared will call out to this again, once per // document). if (options && options.shared) return markTextShared(doc, from, to, options, type); // Ensure we are in an operation. if (doc.cm && !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, to, options, type); var marker = new TextMarker(doc, type), diff = cmp(from, to); if (options) copyObj(options, marker, false); // Don't connect empty markers unless clearWhenEmpty is false if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false) return marker; if (marker.replacedWith) { // Showing up as a widget implies collapsed (widget replaces text) marker.collapsed = true; marker.widgetNode = elt("span", [marker.replacedWith], "CodeMirror-widget"); if (!options.handleMouseEvents) marker.widgetNode.setAttribute("cm-ignore-events", "true"); if (options.insertLeft) marker.widgetNode.insertLeft = true; } if (marker.collapsed) { if (conflictingCollapsedRange(doc, from.line, from, to, marker) || from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker)) throw new Error("Inserting collapsed marker partially overlapping an existing one"); sawCollapsedSpans = true; } if (marker.addToHistory) addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN); var curLine = from.line, cm = doc.cm, updateMaxLine; doc.iter(curLine, to.line + 1, function(line) { if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine) updateMaxLine = true; if (marker.collapsed && curLine != from.line) updateLineHeight(line, 0); addMarkedSpan(line, new MarkedSpan(marker, curLine == from.line ? from.ch : null, curLine == to.line ? to.ch : null)); ++curLine; }); // lineIsHidden depends on the presence of the spans, so needs a second pass if (marker.collapsed) doc.iter(from.line, to.line + 1, function(line) { if (lineIsHidden(doc, line)) updateLineHeight(line, 0); }); if (marker.clearOnEnter) on(marker, "beforeCursorEnter", function() { marker.clear(); }); if (marker.readOnly) { sawReadOnlySpans = true; if (doc.history.done.length || doc.history.undone.length) doc.clearHistory(); } if (marker.collapsed) { marker.id = ++nextMarkerId; marker.atomic = true; } if (cm) { // Sync editor state if (updateMaxLine) cm.curOp.updateMaxLine = true; if (marker.collapsed) regChange(cm, from.line, to.line + 1); else if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.css) for (var i = from.line; i <= to.line; i++) regLineChange(cm, i, "text"); if (marker.atomic) reCheckSelection(cm.doc); signalLater(cm, "markerAdded", cm, marker); } return marker; } // SHARED TEXTMARKERS // A shared marker spans multiple linked documents. It is // implemented as a meta-marker-object controlling multiple normal // markers. var SharedTextMarker = CodeMirror.SharedTextMarker = function(markers, primary) { this.markers = markers; this.primary = primary; for (var i = 0; i < markers.length; ++i) markers[i].parent = this; }; eventMixin(SharedTextMarker); SharedTextMarker.prototype.clear = function() { if (this.explicitlyCleared) return; this.explicitlyCleared = true; for (var i = 0; i < this.markers.length; ++i) this.markers[i].clear(); signalLater(this, "clear"); }; SharedTextMarker.prototype.find = function(side, lineObj) { return this.primary.find(side, lineObj); }; function markTextShared(doc, from, to, options, type) { options = copyObj(options); options.shared = false; var markers = [markText(doc, from, to, options, type)], primary = markers[0]; var widget = options.widgetNode; linkedDocs(doc, function(doc) { if (widget) options.widgetNode = widget.cloneNode(true); markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type)); for (var i = 0; i < doc.linked.length; ++i) if (doc.linked[i].isParent) return; primary = lst(markers); }); return new SharedTextMarker(markers, primary); } function findSharedMarkers(doc) { return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function(m) { return m.parent; }); } function copySharedMarkers(doc, markers) { for (var i = 0; i < markers.length; i++) { var marker = markers[i], pos = marker.find(); var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to); if (cmp(mFrom, mTo)) { var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type); marker.markers.push(subMark); subMark.parent = marker; } } } function detachSharedMarkers(markers) { for (var i = 0; i < markers.length; i++) { var marker = markers[i], linked = [marker.primary.doc];; linkedDocs(marker.primary.doc, function(d) { linked.push(d); }); for (var j = 0; j < marker.markers.length; j++) { var subMarker = marker.markers[j]; if (indexOf(linked, subMarker.doc) == -1) { subMarker.parent = null; marker.markers.splice(j--, 1); } } } } // TEXTMARKER SPANS function MarkedSpan(marker, from, to) { this.marker = marker; this.from = from; this.to = to; } // Search an array of spans for a span matching the given marker. function getMarkedSpanFor(spans, marker) { if (spans) for (var i = 0; i < spans.length; ++i) { var span = spans[i]; if (span.marker == marker) return span; } } // Remove a span from an array, returning undefined if no spans are // left (we don't store arrays for lines without spans). function removeMarkedSpan(spans, span) { for (var r, i = 0; i < spans.length; ++i) if (spans[i] != span) (r || (r = [])).push(spans[i]); return r; } // Add a span to a line. function addMarkedSpan(line, span) { line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span]; span.marker.attachLine(line); } // Used for the algorithm that adjusts markers for a change in the // document. These functions cut an array of spans at a given // character position, returning an array of remaining chunks (or // undefined if nothing remains). function markedSpansBefore(old, startCh, isInsert) { if (old) for (var i = 0, nw; i < old.length; ++i) { var span = old[i], marker = span.marker; var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh); if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) { var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh); (nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to)); } } return nw; } function markedSpansAfter(old, endCh, isInsert) { if (old) for (var i = 0, nw; i < old.length; ++i) { var span = old[i], marker = span.marker; var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh); if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) { var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh); (nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh, span.to == null ? null : span.to - endCh)); } } return nw; } // Given a change object, compute the new set of marker spans that // cover the line in which the change took place. Removes spans // entirely within the change, reconnects spans belonging to the // same marker that appear on both sides of the change, and cuts off // spans partially within the change. Returns an array of span // arrays with one element for each line in (after) the change. function stretchSpansOverChange(doc, change) { if (change.full) return null; var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans; var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans; if (!oldFirst && !oldLast) return null; var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0; // Get the spans that 'stick out' on both sides var first = markedSpansBefore(oldFirst, startCh, isInsert); var last = markedSpansAfter(oldLast, endCh, isInsert); // Next, merge those two ends var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0); if (first) { // Fix up .to properties of first for (var i = 0; i < first.length; ++i) { var span = first[i]; if (span.to == null) { var found = getMarkedSpanFor(last, span.marker); if (!found) span.to = startCh; else if (sameLine) span.to = found.to == null ? null : found.to + offset; } } } if (last) { // Fix up .from in last (or move them into first in case of sameLine) for (var i = 0; i < last.length; ++i) { var span = last[i]; if (span.to != null) span.to += offset; if (span.from == null) { var found = getMarkedSpanFor(first, span.marker); if (!found) { span.from = offset; if (sameLine) (first || (first = [])).push(span); } } else { span.from += offset; if (sameLine) (first || (first = [])).push(span); } } } // Make sure we didn't create any zero-length spans if (first) first = clearEmptySpans(first); if (last && last != first) last = clearEmptySpans(last); var newMarkers = [first]; if (!sameLine) { // Fill gap with whole-line-spans var gap = change.text.length - 2, gapMarkers; if (gap > 0 && first) for (var i = 0; i < first.length; ++i) if (first[i].to == null) (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i].marker, null, null)); for (var i = 0; i < gap; ++i) newMarkers.push(gapMarkers); newMarkers.push(last); } return newMarkers; } // Remove spans that are empty and don't have a clearWhenEmpty // option of false. function clearEmptySpans(spans) { for (var i = 0; i < spans.length; ++i) { var span = spans[i]; if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false) spans.splice(i--, 1); } if (!spans.length) return null; return spans; } // Used for un/re-doing changes from the history. Combines the // result of computing the existing spans with the set of spans that // existed in the history (so that deleting around a span and then // undoing brings back the span). function mergeOldSpans(doc, change) { var old = getOldSpans(doc, change); var stretched = stretchSpansOverChange(doc, change); if (!old) return stretched; if (!stretched) return old; for (var i = 0; i < old.length; ++i) { var oldCur = old[i], stretchCur = stretched[i]; if (oldCur && stretchCur) { spans: for (var j = 0; j < stretchCur.length; ++j) { var span = stretchCur[j]; for (var k = 0; k < oldCur.length; ++k) if (oldCur[k].marker == span.marker) continue spans; oldCur.push(span); } } else if (stretchCur) { old[i] = stretchCur; } } return old; } // Used to 'clip' out readOnly ranges when making a change. function removeReadOnlyRanges(doc, from, to) { var markers = null; doc.iter(from.line, to.line + 1, function(line) { if (line.markedSpans) for (var i = 0; i < line.markedSpans.length; ++i) { var mark = line.markedSpans[i].marker; if (mark.readOnly && (!markers || indexOf(markers, mark) == -1)) (markers || (markers = [])).push(mark); } }); if (!markers) return null; var parts = [{from: from, to: to}]; for (var i = 0; i < markers.length; ++i) { var mk = markers[i], m = mk.find(0); for (var j = 0; j < parts.length; ++j) { var p = parts[j]; if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) continue; var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to); if (dfrom < 0 || !mk.inclusiveLeft && !dfrom) newParts.push({from: p.from, to: m.from}); if (dto > 0 || !mk.inclusiveRight && !dto) newParts.push({from: m.to, to: p.to}); parts.splice.apply(parts, newParts); j += newParts.length - 1; } } return parts; } // Connect or disconnect spans from a line. function detachMarkedSpans(line) { var spans = line.markedSpans; if (!spans) return; for (var i = 0; i < spans.length; ++i) spans[i].marker.detachLine(line); line.markedSpans = null; } function attachMarkedSpans(line, spans) { if (!spans) return; for (var i = 0; i < spans.length; ++i) spans[i].marker.attachLine(line); line.markedSpans = spans; } // Helpers used when computing which overlapping collapsed span // counts as the larger one. function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0; } function extraRight(marker) { return marker.inclusiveRight ? 1 : 0; } // Returns a number indicating which of two overlapping collapsed // spans is larger (and thus includes the other). Falls back to // comparing ids when the spans cover exactly the same range. function compareCollapsedMarkers(a, b) { var lenDiff = a.lines.length - b.lines.length; if (lenDiff != 0) return lenDiff; var aPos = a.find(), bPos = b.find(); var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b); if (fromCmp) return -fromCmp; var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b); if (toCmp) return toCmp; return b.id - a.id; } // Find out whether a line ends or starts in a collapsed span. If // so, return the marker for that span. function collapsedSpanAtSide(line, start) { var sps = sawCollapsedSpans && line.markedSpans, found; if (sps) for (var sp, i = 0; i < sps.length; ++i) { sp = sps[i]; if (sp.marker.collapsed && (start ? sp.from : sp.to) == null && (!found || compareCollapsedMarkers(found, sp.marker) < 0)) found = sp.marker; } return found; } function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true); } function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false); } // Test whether there exists a collapsed span that partially // overlaps (covers the start or end, but not both) of a new span. // Such overlap is not allowed. function conflictingCollapsedRange(doc, lineNo, from, to, marker) { var line = getLine(doc, lineNo); var sps = sawCollapsedSpans && line.markedSpans; if (sps) for (var i = 0; i < sps.length; ++i) { var sp = sps[i]; if (!sp.marker.collapsed) continue; var found = sp.marker.find(0); var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker); var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker); if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) continue; if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) || fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0)) return true; } } // A visual line is a line as drawn on the screen. Folding, for // example, can cause multiple logical lines to appear on the same // visual line. This finds the start of the visual line that the // given line is part of (usually that is the line itself). function visualLine(line) { var merged; while (merged = collapsedSpanAtStart(line)) line = merged.find(-1, true).line; return line; } // Returns an array of logical lines that continue the visual line // started by the argument, or undefined if there are no such lines. function visualLineContinued(line) { var merged, lines; while (merged = collapsedSpanAtEnd(line)) { line = merged.find(1, true).line; (lines || (lines = [])).push(line); } return lines; } // Get the line number of the start of the visual line that the // given line number is part of. function visualLineNo(doc, lineN) { var line = getLine(doc, lineN), vis = visualLine(line); if (line == vis) return lineN; return lineNo(vis); } // Get the line number of the start of the next visual line after // the given line. function visualLineEndNo(doc, lineN) { if (lineN > doc.lastLine()) return lineN; var line = getLine(doc, lineN), merged; if (!lineIsHidden(doc, line)) return lineN; while (merged = collapsedSpanAtEnd(line)) line = merged.find(1, true).line; return lineNo(line) + 1; } // Compute whether a line is hidden. Lines count as hidden when they // are part of a visual line that starts with another line, or when // they are entirely covered by collapsed, non-widget span. function lineIsHidden(doc, line) { var sps = sawCollapsedSpans && line.markedSpans; if (sps) for (var sp, i = 0; i < sps.length; ++i) { sp = sps[i]; if (!sp.marker.collapsed) continue; if (sp.from == null) return true; if (sp.marker.widgetNode) continue; if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp)) return true; } } function lineIsHiddenInner(doc, line, span) { if (span.to == null) { var end = span.marker.find(1, true); return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker)); } if (span.marker.inclusiveRight && span.to == line.text.length) return true; for (var sp, i = 0; i < line.markedSpans.length; ++i) { sp = line.markedSpans[i]; if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to && (sp.to == null || sp.to != span.from) && (sp.marker.inclusiveLeft || span.marker.inclusiveRight) && lineIsHiddenInner(doc, line, sp)) return true; } } // LINE WIDGETS // Line widgets are block elements displayed above or below a line. var LineWidget = CodeMirror.LineWidget = function(doc, node, options) { if (options) for (var opt in options) if (options.hasOwnProperty(opt)) this[opt] = options[opt]; this.doc = doc; this.node = node; }; eventMixin(LineWidget); function adjustScrollWhenAboveVisible(cm, line, diff) { if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop)) addToScrollPos(cm, null, diff); } LineWidget.prototype.clear = function() { var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line); if (no == null || !ws) return; for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1); if (!ws.length) line.widgets = null; var height = widgetHeight(this); updateLineHeight(line, Math.max(0, line.height - height)); if (cm) runInOp(cm, function() { adjustScrollWhenAboveVisible(cm, line, -height); regLineChange(cm, no, "widget"); }); }; LineWidget.prototype.changed = function() { var oldH = this.height, cm = this.doc.cm, line = this.line; this.height = null; var diff = widgetHeight(this) - oldH; if (!diff) return; updateLineHeight(line, line.height + diff); if (cm) runInOp(cm, function() { cm.curOp.forceUpdate = true; adjustScrollWhenAboveVisible(cm, line, diff); }); }; function widgetHeight(widget) { if (widget.height != null) return widget.height; var cm = widget.doc.cm; if (!cm) return 0; if (!contains(document.body, widget.node)) { var parentStyle = "position: relative;"; if (widget.coverGutter) parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;"; if (widget.noHScroll) parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;"; removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle)); } return widget.height = widget.node.parentNode.offsetHeight; } function addLineWidget(doc, handle, node, options) { var widget = new LineWidget(doc, node, options); var cm = doc.cm; if (cm && widget.noHScroll) cm.display.alignWidgets = true; changeLine(doc, handle, "widget", function(line) { var widgets = line.widgets || (line.widgets = []); if (widget.insertAt == null) widgets.push(widget); else widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget); widget.line = line; if (cm && !lineIsHidden(doc, line)) { var aboveVisible = heightAtLine(line) < doc.scrollTop; updateLineHeight(line, line.height + widgetHeight(widget)); if (aboveVisible) addToScrollPos(cm, null, widget.height); cm.curOp.forceUpdate = true; } return true; }); return widget; } // LINE DATA STRUCTURE // Line objects. These hold state related to a line, including // highlighting info (the styles array). var Line = CodeMirror.Line = function(text, markedSpans, estimateHeight) { this.text = text; attachMarkedSpans(this, markedSpans); this.height = estimateHeight ? estimateHeight(this) : 1; }; eventMixin(Line); Line.prototype.lineNo = function() { return lineNo(this); }; // Change the content (text, markers) of a line. Automatically // invalidates cached information and tries to re-estimate the // line's height. function updateLine(line, text, markedSpans, estimateHeight) { line.text = text; if (line.stateAfter) line.stateAfter = null; if (line.styles) line.styles = null; if (line.order != null) line.order = null; detachMarkedSpans(line); attachMarkedSpans(line, markedSpans); var estHeight = estimateHeight ? estimateHeight(line) : 1; if (estHeight != line.height) updateLineHeight(line, estHeight); } // Detach a line from the document tree and its markers. function cleanUpLine(line) { line.parent = null; detachMarkedSpans(line); } function extractLineClasses(type, output) { if (type) for (;;) { var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/); if (!lineClass) break; type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length); var prop = lineClass[1] ? "bgClass" : "textClass"; if (output[prop] == null) output[prop] = lineClass[2]; else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop])) output[prop] += " " + lineClass[2]; } return type; } function callBlankLine(mode, state) { if (mode.blankLine) return mode.blankLine(state); if (!mode.innerMode) return; var inner = CodeMirror.innerMode(mode, state); if (inner.mode.blankLine) return inner.mode.blankLine(inner.state); } function readToken(mode, stream, state, inner) { for (var i = 0; i < 10; i++) { if (inner) inner[0] = CodeMirror.innerMode(mode, state).mode; var style = mode.token(stream, state); if (stream.pos > stream.start) return style; } throw new Error("Mode " + mode.name + " failed to advance stream."); } // Utility for getTokenAt and getLineTokens function takeToken(cm, pos, precise, asArray) { function getObj(copy) { return {start: stream.start, end: stream.pos, string: stream.current(), type: style || null, state: copy ? copyState(doc.mode, state) : state}; } var doc = cm.doc, mode = doc.mode, style; pos = clipPos(doc, pos); var line = getLine(doc, pos.line), state = getStateBefore(cm, pos.line, precise); var stream = new StringStream(line.text, cm.options.tabSize), tokens; if (asArray) tokens = []; while ((asArray || stream.pos < pos.ch) && !stream.eol()) { stream.start = stream.pos; style = readToken(mode, stream, state); if (asArray) tokens.push(getObj(true)); } return asArray ? tokens : getObj(); } // Run the given mode's parser over a line, calling f for each token. function runMode(cm, text, mode, state, f, lineClasses, forceToEnd) { var flattenSpans = mode.flattenSpans; if (flattenSpans == null) flattenSpans = cm.options.flattenSpans; var curStart = 0, curStyle = null; var stream = new StringStream(text, cm.options.tabSize), style; var inner = cm.options.addModeClass && [null]; if (text == "") extractLineClasses(callBlankLine(mode, state), lineClasses); while (!stream.eol()) { if (stream.pos > cm.options.maxHighlightLength) { flattenSpans = false; if (forceToEnd) processLine(cm, text, state, stream.pos); stream.pos = text.length; style = null; } else { style = extractLineClasses(readToken(mode, stream, state, inner), lineClasses); } if (inner) { var mName = inner[0].name; if (mName) style = "m-" + (style ? mName + " " + style : mName); } if (!flattenSpans || curStyle != style) { while (curStart < stream.start) { curStart = Math.min(stream.start, curStart + 50000); f(curStart, curStyle); } curStyle = style; } stream.start = stream.pos; } while (curStart < stream.pos) { // Webkit seems to refuse to render text nodes longer than 57444 characters var pos = Math.min(stream.pos, curStart + 50000); f(pos, curStyle); curStart = pos; } } // Compute a style array (an array starting with a mode generation // -- for invalidation -- followed by pairs of end positions and // style strings), which is used to highlight the tokens on the // line. function highlightLine(cm, line, state, forceToEnd) { // A styles array always starts with a number identifying the // mode/overlays that it is based on (for easy invalidation). var st = [cm.state.modeGen], lineClasses = {}; // Compute the base array of styles runMode(cm, line.text, cm.doc.mode, state, function(end, style) { st.push(end, style); }, lineClasses, forceToEnd); // Run overlays, adjust style array. for (var o = 0; o < cm.state.overlays.length; ++o) { var overlay = cm.state.overlays[o], i = 1, at = 0; runMode(cm, line.text, overlay.mode, true, function(end, style) { var start = i; // Ensure there's a token end at the current position, and that i points at it while (at < end) { var i_end = st[i]; if (i_end > end) st.splice(i, 1, end, st[i+1], i_end); i += 2; at = Math.min(end, i_end); } if (!style) return; if (overlay.opaque) { st.splice(start, i - start, end, "cm-overlay " + style); i = start + 2; } else { for (; start < i; start += 2) { var cur = st[start+1]; st[start+1] = (cur ? cur + " " : "") + "cm-overlay " + style; } } }, lineClasses); } return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null}; } function getLineStyles(cm, line, updateFrontier) { if (!line.styles || line.styles[0] != cm.state.modeGen) { var state = getStateBefore(cm, lineNo(line)); var result = highlightLine(cm, line, line.text.length > cm.options.maxHighlightLength ? copyState(cm.doc.mode, state) : state); line.stateAfter = state; line.styles = result.styles; if (result.classes) line.styleClasses = result.classes; else if (line.styleClasses) line.styleClasses = null; if (updateFrontier === cm.doc.frontier) cm.doc.frontier++; } return line.styles; } // Lightweight form of highlight -- proceed over this line and // update state, but don't save a style array. Used for lines that // aren't currently visible. function processLine(cm, text, state, startAt) { var mode = cm.doc.mode; var stream = new StringStream(text, cm.options.tabSize); stream.start = stream.pos = startAt || 0; if (text == "") callBlankLine(mode, state); while (!stream.eol()) { readToken(mode, stream, state); stream.start = stream.pos; } } // Convert a style as returned by a mode (either null, or a string // containing one or more styles) to a CSS style. This is cached, // and also looks for line-wide styles. var styleToClassCache = {}, styleToClassCacheWithMode = {}; function interpretTokenStyle(style, options) { if (!style || /^\s*$/.test(style)) return null; var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache; return cache[style] || (cache[style] = style.replace(/\S+/g, "cm-$&")); } // Render the DOM representation of the text of a line. Also builds // up a 'line map', which points at the DOM nodes that represent // specific stretches of text, and is used by the measuring code. // The returned object contains the DOM node, this map, and // information about line-wide styles that were set by the mode. function buildLineContent(cm, lineView) { // The padding-right forces the element to have a 'border', which // is needed on Webkit to be able to get line-level bounding // rectangles for it (in measureChar). var content = elt("span", null, null, webkit ? "padding-right: .1px" : null); var builder = {pre: elt("pre", [content], "CodeMirror-line"), content: content, col: 0, pos: 0, cm: cm, splitSpaces: (ie || webkit) && cm.getOption("lineWrapping")}; lineView.measure = {}; // Iterate over the logical lines that make up this visual line. for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) { var line = i ? lineView.rest[i - 1] : lineView.line, order; builder.pos = 0; builder.addToken = buildToken; // Optionally wire in some hacks into the token-rendering // algorithm, to deal with browser quirks. if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line))) builder.addToken = buildTokenBadBidi(builder.addToken, order); builder.map = []; var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line); insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate)); if (line.styleClasses) { if (line.styleClasses.bgClass) builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || ""); if (line.styleClasses.textClass) builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || ""); } // Ensure at least a single node is present, for measuring. if (builder.map.length == 0) builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))); // Store the map and a cache object for the current logical line if (i == 0) { lineView.measure.map = builder.map; lineView.measure.cache = {}; } else { (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map); (lineView.measure.caches || (lineView.measure.caches = [])).push({}); } } // See issue #2901 if (webkit) { var last = builder.content.lastChild if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab"))) builder.content.className = "cm-tab-wrap-hack"; } signal(cm, "renderLine", cm, lineView.line, builder.pre); if (builder.pre.className) builder.textClass = joinClasses(builder.pre.className, builder.textClass || ""); return builder; } function defaultSpecialCharPlaceholder(ch) { var token = elt("span", "\u2022", "cm-invalidchar"); token.title = "\\u" + ch.charCodeAt(0).toString(16); token.setAttribute("aria-label", token.title); return token; } // Build up the DOM representation for a single token, and add it to // the line map. Takes care to render special characters separately. function buildToken(builder, text, style, startStyle, endStyle, title, css) { if (!text) return; var displayText = builder.splitSpaces ? text.replace(/ {3,}/g, splitSpaces) : text; var special = builder.cm.state.specialChars, mustWrap = false; if (!special.test(text)) { builder.col += text.length; var content = document.createTextNode(displayText); builder.map.push(builder.pos, builder.pos + text.length, content); if (ie && ie_version < 9) mustWrap = true; builder.pos += text.length; } else { var content = document.createDocumentFragment(), pos = 0; while (true) { special.lastIndex = pos; var m = special.exec(text); var skipped = m ? m.index - pos : text.length - pos; if (skipped) { var txt = document.createTextNode(displayText.slice(pos, pos + skipped)); if (ie && ie_version < 9) content.appendChild(elt("span", [txt])); else content.appendChild(txt); builder.map.push(builder.pos, builder.pos + skipped, txt); builder.col += skipped; builder.pos += skipped; } if (!m) break; pos += skipped + 1; if (m[0] == "\t") { var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize; var txt = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab")); txt.setAttribute("role", "presentation"); txt.setAttribute("cm-text", "\t"); builder.col += tabWidth; } else if (m[0] == "\r" || m[0] == "\n") { var txt = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar")); txt.setAttribute("cm-text", m[0]); builder.col += 1; } else { var txt = builder.cm.options.specialCharPlaceholder(m[0]); txt.setAttribute("cm-text", m[0]); if (ie && ie_version < 9) content.appendChild(elt("span", [txt])); else content.appendChild(txt); builder.col += 1; } builder.map.push(builder.pos, builder.pos + 1, txt); builder.pos++; } } if (style || startStyle || endStyle || mustWrap || css) { var fullStyle = style || ""; if (startStyle) fullStyle += startStyle; if (endStyle) fullStyle += endStyle; var token = elt("span", [content], fullStyle, css); if (title) token.title = title; return builder.content.appendChild(token); } builder.content.appendChild(content); } function splitSpaces(old) { var out = " "; for (var i = 0; i < old.length - 2; ++i) out += i % 2 ? " " : "\u00a0"; out += " "; return out; } // Work around nonsense dimensions being reported for stretches of // right-to-left text. function buildTokenBadBidi(inner, order) { return function(builder, text, style, startStyle, endStyle, title, css) { style = style ? style + " cm-force-border" : "cm-force-border"; var start = builder.pos, end = start + text.length; for (;;) { // Find the part that overlaps with the start of this text for (var i = 0; i < order.length; i++) { var part = order[i]; if (part.to > start && part.from <= start) break; } if (part.to >= end) return inner(builder, text, style, startStyle, endStyle, title, css); inner(builder, text.slice(0, part.to - start), style, startStyle, null, title, css); startStyle = null; text = text.slice(part.to - start); start = part.to; } }; } function buildCollapsedSpan(builder, size, marker, ignoreWidget) { var widget = !ignoreWidget && marker.widgetNode; if (widget) builder.map.push(builder.pos, builder.pos + size, widget); if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) { if (!widget) widget = builder.content.appendChild(document.createElement("span")); widget.setAttribute("cm-marker", marker.id); } if (widget) { builder.cm.display.input.setUneditable(widget); builder.content.appendChild(widget); } builder.pos += size; } // Outputs a number of spans to make up a line, taking highlighting // and marked text into account. function insertLineContent(line, builder, styles) { var spans = line.markedSpans, allText = line.text, at = 0; if (!spans) { for (var i = 1; i < styles.length; i+=2) builder.addToken(builder, allText.slice(at, at = styles[i]), interpretTokenStyle(styles[i+1], builder.cm.options)); return; } var len = allText.length, pos = 0, i = 1, text = "", style, css; var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed; for (;;) { if (nextChange == pos) { // Update current marker set spanStyle = spanEndStyle = spanStartStyle = title = css = ""; collapsed = null; nextChange = Infinity; var foundBookmarks = [], endStyles for (var j = 0; j < spans.length; ++j) { var sp = spans[j], m = sp.marker; if (m.type == "bookmark" && sp.from == pos && m.widgetNode) { foundBookmarks.push(m); } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) { if (sp.to != null && sp.to != pos && nextChange > sp.to) { nextChange = sp.to; spanEndStyle = ""; } if (m.className) spanStyle += " " + m.className; if (m.css) css = (css ? css + ";" : "") + m.css; if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle; if (m.endStyle && sp.to == nextChange) (endStyles || (endStyles = [])).push(m.endStyle, sp.to) if (m.title && !title) title = m.title; if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0)) collapsed = sp; } else if (sp.from > pos && nextChange > sp.from) { nextChange = sp.from; } } if (endStyles) for (var j = 0; j < endStyles.length; j += 2) if (endStyles[j + 1] == nextChange) spanEndStyle += " " + endStyles[j] if (!collapsed || collapsed.from == pos) for (var j = 0; j < foundBookmarks.length; ++j) buildCollapsedSpan(builder, 0, foundBookmarks[j]); if (collapsed && (collapsed.from || 0) == pos) { buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos, collapsed.marker, collapsed.from == null); if (collapsed.to == null) return; if (collapsed.to == pos) collapsed = false; } } if (pos >= len) break; var upto = Math.min(len, nextChange); while (true) { if (text) { var end = pos + text.length; if (!collapsed) { var tokenText = end > upto ? text.slice(0, upto - pos) : text; builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle, spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title, css); } if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;} pos = end; spanStartStyle = ""; } text = allText.slice(at, at = styles[i++]); style = interpretTokenStyle(styles[i++], builder.cm.options); } } } // DOCUMENT DATA STRUCTURE // By default, updates that start and end at the beginning of a line // are treated specially, in order to make the association of line // widgets and marker elements with the text behave more intuitive. function isWholeLineUpdate(doc, change) { return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" && (!doc.cm || doc.cm.options.wholeLineUpdateBefore); } // Perform a change on the document data structure. function updateDoc(doc, change, markedSpans, estimateHeight) { function spansFor(n) {return markedSpans ? markedSpans[n] : null;} function update(line, text, spans) { updateLine(line, text, spans, estimateHeight); signalLater(line, "change", line, change); } function linesFor(start, end) { for (var i = start, result = []; i < end; ++i) result.push(new Line(text[i], spansFor(i), estimateHeight)); return result; } var from = change.from, to = change.to, text = change.text; var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line); var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line; // Adjust the line structure if (change.full) { doc.insert(0, linesFor(0, text.length)); doc.remove(text.length, doc.size - text.length); } else if (isWholeLineUpdate(doc, change)) { // This is a whole-line replace. Treated specially to make // sure line objects move the way they are supposed to. var added = linesFor(0, text.length - 1); update(lastLine, lastLine.text, lastSpans); if (nlines) doc.remove(from.line, nlines); if (added.length) doc.insert(from.line, added); } else if (firstLine == lastLine) { if (text.length == 1) { update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans); } else { var added = linesFor(1, text.length - 1); added.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight)); update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); doc.insert(from.line + 1, added); } } else if (text.length == 1) { update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0)); doc.remove(from.line + 1, nlines); } else { update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans); var added = linesFor(1, text.length - 1); if (nlines > 1) doc.remove(from.line + 1, nlines - 1); doc.insert(from.line + 1, added); } signalLater(doc, "change", doc, change); } // The document is represented as a BTree consisting of leaves, with // chunk of lines in them, and branches, with up to ten leaves or // other branch nodes below them. The top node is always a branch // node, and is the document object itself (meaning it has // additional methods and properties). // // All nodes have parent links. The tree is used both to go from // line numbers to line objects, and to go from objects to numbers. // It also indexes by height, and is used to convert between height // and line object, and to find the total height of the document. // // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html function LeafChunk(lines) { this.lines = lines; this.parent = null; for (var i = 0, height = 0; i < lines.length; ++i) { lines[i].parent = this; height += lines[i].height; } this.height = height; } LeafChunk.prototype = { chunkSize: function() { return this.lines.length; }, // Remove the n lines at offset 'at'. removeInner: function(at, n) { for (var i = at, e = at + n; i < e; ++i) { var line = this.lines[i]; this.height -= line.height; cleanUpLine(line); signalLater(line, "delete"); } this.lines.splice(at, n); }, // Helper used to collapse a small branch into a single leaf. collapse: function(lines) { lines.push.apply(lines, this.lines); }, // Insert the given array of lines at offset 'at', count them as // having the given height. insertInner: function(at, lines, height) { this.height += height; this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at)); for (var i = 0; i < lines.length; ++i) lines[i].parent = this; }, // Used to iterate over a part of the tree. iterN: function(at, n, op) { for (var e = at + n; at < e; ++at) if (op(this.lines[at])) return true; } }; function BranchChunk(children) { this.children = children; var size = 0, height = 0; for (var i = 0; i < children.length; ++i) { var ch = children[i]; size += ch.chunkSize(); height += ch.height; ch.parent = this; } this.size = size; this.height = height; this.parent = null; } BranchChunk.prototype = { chunkSize: function() { return this.size; }, removeInner: function(at, n) { this.size -= n; for (var i = 0; i < this.children.length; ++i) { var child = this.children[i], sz = child.chunkSize(); if (at < sz) { var rm = Math.min(n, sz - at), oldHeight = child.height; child.removeInner(at, rm); this.height -= oldHeight - child.height; if (sz == rm) { this.children.splice(i--, 1); child.parent = null; } if ((n -= rm) == 0) break; at = 0; } else at -= sz; } // If the result is smaller than 25 lines, ensure that it is a // single leaf node. if (this.size - n < 25 && (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) { var lines = []; this.collapse(lines); this.children = [new LeafChunk(lines)]; this.children[0].parent = this; } }, collapse: function(lines) { for (var i = 0; i < this.children.length; ++i) this.children[i].collapse(lines); }, insertInner: function(at, lines, height) { this.size += lines.length; this.height += height; for (var i = 0; i < this.children.length; ++i) { var child = this.children[i], sz = child.chunkSize(); if (at <= sz) { child.insertInner(at, lines, height); if (child.lines && child.lines.length > 50) { // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced. // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest. var remaining = child.lines.length % 25 + 25 for (var pos = remaining; pos < child.lines.length;) { var leaf = new LeafChunk(child.lines.slice(pos, pos += 25)); child.height -= leaf.height; this.children.splice(++i, 0, leaf); leaf.parent = this; } child.lines = child.lines.slice(0, remaining); this.maybeSpill(); } break; } at -= sz; } }, // When a node has grown, check whether it should be split. maybeSpill: function() { if (this.children.length <= 10) return; var me = this; do { var spilled = me.children.splice(me.children.length - 5, 5); var sibling = new BranchChunk(spilled); if (!me.parent) { // Become the parent node var copy = new BranchChunk(me.children); copy.parent = me; me.children = [copy, sibling]; me = copy; } else { me.size -= sibling.size; me.height -= sibling.height; var myIndex = indexOf(me.parent.children, me); me.parent.children.splice(myIndex + 1, 0, sibling); } sibling.parent = me.parent; } while (me.children.length > 10); me.parent.maybeSpill(); }, iterN: function(at, n, op) { for (var i = 0; i < this.children.length; ++i) { var child = this.children[i], sz = child.chunkSize(); if (at < sz) { var used = Math.min(n, sz - at); if (child.iterN(at, used, op)) return true; if ((n -= used) == 0) break; at = 0; } else at -= sz; } } }; var nextDocId = 0; var Doc = CodeMirror.Doc = function(text, mode, firstLine, lineSep) { if (!(this instanceof Doc)) return new Doc(text, mode, firstLine, lineSep); if (firstLine == null) firstLine = 0; BranchChunk.call(this, [new LeafChunk([new Line("", null)])]); this.first = firstLine; this.scrollTop = this.scrollLeft = 0; this.cantEdit = false; this.cleanGeneration = 1; this.frontier = firstLine; var start = Pos(firstLine, 0); this.sel = simpleSelection(start); this.history = new History(null); this.id = ++nextDocId; this.modeOption = mode; this.lineSep = lineSep; this.extend = false; if (typeof text == "string") text = this.splitLines(text); updateDoc(this, {from: start, to: start, text: text}); setSelection(this, simpleSelection(start), sel_dontScroll); }; Doc.prototype = createObj(BranchChunk.prototype, { constructor: Doc, // Iterate over the document. Supports two forms -- with only one // argument, it calls that for each line in the document. With // three, it iterates over the range given by the first two (with // the second being non-inclusive). iter: function(from, to, op) { if (op) this.iterN(from - this.first, to - from, op); else this.iterN(this.first, this.first + this.size, from); }, // Non-public interface for adding and removing lines. insert: function(at, lines) { var height = 0; for (var i = 0; i < lines.length; ++i) height += lines[i].height; this.insertInner(at - this.first, lines, height); }, remove: function(at, n) { this.removeInner(at - this.first, n); }, // From here, the methods are part of the public interface. Most // are also available from CodeMirror (editor) instances. getValue: function(lineSep) { var lines = getLines(this, this.first, this.first + this.size); if (lineSep === false) return lines; return lines.join(lineSep || this.lineSeparator()); }, setValue: docMethodOp(function(code) { var top = Pos(this.first, 0), last = this.first + this.size - 1; makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length), text: this.splitLines(code), origin: "setValue", full: true}, true); setSelection(this, simpleSelection(top)); }), replaceRange: function(code, from, to, origin) { from = clipPos(this, from); to = to ? clipPos(this, to) : from; replaceRange(this, code, from, to, origin); }, getRange: function(from, to, lineSep) { var lines = getBetween(this, clipPos(this, from), clipPos(this, to)); if (lineSep === false) return lines; return lines.join(lineSep || this.lineSeparator()); }, getLine: function(line) {var l = this.getLineHandle(line); return l && l.text;}, getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line);}, getLineNumber: function(line) {return lineNo(line);}, getLineHandleVisualStart: function(line) { if (typeof line == "number") line = getLine(this, line); return visualLine(line); }, lineCount: function() {return this.size;}, firstLine: function() {return this.first;}, lastLine: function() {return this.first + this.size - 1;}, clipPos: function(pos) {return clipPos(this, pos);}, getCursor: function(start) { var range = this.sel.primary(), pos; if (start == null || start == "head") pos = range.head; else if (start == "anchor") pos = range.anchor; else if (start == "end" || start == "to" || start === false) pos = range.to(); else pos = range.from(); return pos; }, listSelections: function() { return this.sel.ranges; }, somethingSelected: function() {return this.sel.somethingSelected();}, setCursor: docMethodOp(function(line, ch, options) { setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options); }), setSelection: docMethodOp(function(anchor, head, options) { setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options); }), extendSelection: docMethodOp(function(head, other, options) { extendSelection(this, clipPos(this, head), other && clipPos(this, other), options); }), extendSelections: docMethodOp(function(heads, options) { extendSelections(this, clipPosArray(this, heads), options); }), extendSelectionsBy: docMethodOp(function(f, options) { var heads = map(this.sel.ranges, f); extendSelections(this, clipPosArray(this, heads), options); }), setSelections: docMethodOp(function(ranges, primary, options) { if (!ranges.length) return; for (var i = 0, out = []; i < ranges.length; i++) out[i] = new Range(clipPos(this, ranges[i].anchor), clipPos(this, ranges[i].head)); if (primary == null) primary = Math.min(ranges.length - 1, this.sel.primIndex); setSelection(this, normalizeSelection(out, primary), options); }), addSelection: docMethodOp(function(anchor, head, options) { var ranges = this.sel.ranges.slice(0); ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor))); setSelection(this, normalizeSelection(ranges, ranges.length - 1), options); }), getSelection: function(lineSep) { var ranges = this.sel.ranges, lines; for (var i = 0; i < ranges.length; i++) { var sel = getBetween(this, ranges[i].from(), ranges[i].to()); lines = lines ? lines.concat(sel) : sel; } if (lineSep === false) return lines; else return lines.join(lineSep || this.lineSeparator()); }, getSelections: function(lineSep) { var parts = [], ranges = this.sel.ranges; for (var i = 0; i < ranges.length; i++) { var sel = getBetween(this, ranges[i].from(), ranges[i].to()); if (lineSep !== false) sel = sel.join(lineSep || this.lineSeparator()); parts[i] = sel; } return parts; }, replaceSelection: function(code, collapse, origin) { var dup = []; for (var i = 0; i < this.sel.ranges.length; i++) dup[i] = code; this.replaceSelections(dup, collapse, origin || "+input"); }, replaceSelections: docMethodOp(function(code, collapse, origin) { var changes = [], sel = this.sel; for (var i = 0; i < sel.ranges.length; i++) { var range = sel.ranges[i]; changes[i] = {from: range.from(), to: range.to(), text: this.splitLines(code[i]), origin: origin}; } var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse); for (var i = changes.length - 1; i >= 0; i--) makeChange(this, changes[i]); if (newSel) setSelectionReplaceHistory(this, newSel); else if (this.cm) ensureCursorVisible(this.cm); }), undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}), redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}), undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}), redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}), setExtending: function(val) {this.extend = val;}, getExtending: function() {return this.extend;}, historySize: function() { var hist = this.history, done = 0, undone = 0; for (var i = 0; i < hist.done.length; i++) if (!hist.done[i].ranges) ++done; for (var i = 0; i < hist.undone.length; i++) if (!hist.undone[i].ranges) ++undone; return {undo: done, redo: undone}; }, clearHistory: function() {this.history = new History(this.history.maxGeneration);}, markClean: function() { this.cleanGeneration = this.changeGeneration(true); }, changeGeneration: function(forceSplit) { if (forceSplit) this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null; return this.history.generation; }, isClean: function (gen) { return this.history.generation == (gen || this.cleanGeneration); }, getHistory: function() { return {done: copyHistoryArray(this.history.done), undone: copyHistoryArray(this.history.undone)}; }, setHistory: function(histData) { var hist = this.history = new History(this.history.maxGeneration); hist.done = copyHistoryArray(histData.done.slice(0), null, true); hist.undone = copyHistoryArray(histData.undone.slice(0), null, true); }, addLineClass: docMethodOp(function(handle, where, cls) { return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function(line) { var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : where == "gutter" ? "gutterClass" : "wrapClass"; if (!line[prop]) line[prop] = cls; else if (classTest(cls).test(line[prop])) return false; else line[prop] += " " + cls; return true; }); }), removeLineClass: docMethodOp(function(handle, where, cls) { return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function(line) { var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : where == "gutter" ? "gutterClass" : "wrapClass"; var cur = line[prop]; if (!cur) return false; else if (cls == null) line[prop] = null; else { var found = cur.match(classTest(cls)); if (!found) return false; var end = found.index + found[0].length; line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null; } return true; }); }), addLineWidget: docMethodOp(function(handle, node, options) { return addLineWidget(this, handle, node, options); }), removeLineWidget: function(widget) { widget.clear(); }, markText: function(from, to, options) { return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range"); }, setBookmark: function(pos, options) { var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options), insertLeft: options && options.insertLeft, clearWhenEmpty: false, shared: options && options.shared, handleMouseEvents: options && options.handleMouseEvents}; pos = clipPos(this, pos); return markText(this, pos, pos, realOpts, "bookmark"); }, findMarksAt: function(pos) { pos = clipPos(this, pos); var markers = [], spans = getLine(this, pos.line).markedSpans; if (spans) for (var i = 0; i < spans.length; ++i) { var span = spans[i]; if ((span.from == null || span.from <= pos.ch) && (span.to == null || span.to >= pos.ch)) markers.push(span.marker.parent || span.marker); } return markers; }, findMarks: function(from, to, filter) { from = clipPos(this, from); to = clipPos(this, to); var found = [], lineNo = from.line; this.iter(from.line, to.line + 1, function(line) { var spans = line.markedSpans; if (spans) for (var i = 0; i < spans.length; i++) { var span = spans[i]; if (!(span.to != null && lineNo == from.line && from.ch >= span.to || span.from == null && lineNo != from.line || span.from != null && lineNo == to.line && span.from >= to.ch) && (!filter || filter(span.marker))) found.push(span.marker.parent || span.marker); } ++lineNo; }); return found; }, getAllMarks: function() { var markers = []; this.iter(function(line) { var sps = line.markedSpans; if (sps) for (var i = 0; i < sps.length; ++i) if (sps[i].from != null) markers.push(sps[i].marker); }); return markers; }, posFromIndex: function(off) { var ch, lineNo = this.first, sepSize = this.lineSeparator().length; this.iter(function(line) { var sz = line.text.length + sepSize; if (sz > off) { ch = off; return true; } off -= sz; ++lineNo; }); return clipPos(this, Pos(lineNo, ch)); }, indexFromPos: function (coords) { coords = clipPos(this, coords); var index = coords.ch; if (coords.line < this.first || coords.ch < 0) return 0; var sepSize = this.lineSeparator().length; this.iter(this.first, coords.line, function (line) { index += line.text.length + sepSize; }); return index; }, copy: function(copyHistory) { var doc = new Doc(getLines(this, this.first, this.first + this.size), this.modeOption, this.first, this.lineSep); doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft; doc.sel = this.sel; doc.extend = false; if (copyHistory) { doc.history.undoDepth = this.history.undoDepth; doc.setHistory(this.getHistory()); } return doc; }, linkedDoc: function(options) { if (!options) options = {}; var from = this.first, to = this.first + this.size; if (options.from != null && options.from > from) from = options.from; if (options.to != null && options.to < to) to = options.to; var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep); if (options.sharedHist) copy.history = this.history; (this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist}); copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}]; copySharedMarkers(copy, findSharedMarkers(this)); return copy; }, unlinkDoc: function(other) { if (other instanceof CodeMirror) other = other.doc; if (this.linked) for (var i = 0; i < this.linked.length; ++i) { var link = this.linked[i]; if (link.doc != other) continue; this.linked.splice(i, 1); other.unlinkDoc(this); detachSharedMarkers(findSharedMarkers(this)); break; } // If the histories were shared, split them again if (other.history == this.history) { var splitIds = [other.id]; linkedDocs(other, function(doc) {splitIds.push(doc.id);}, true); other.history = new History(null); other.history.done = copyHistoryArray(this.history.done, splitIds); other.history.undone = copyHistoryArray(this.history.undone, splitIds); } }, iterLinkedDocs: function(f) {linkedDocs(this, f);}, getMode: function() {return this.mode;}, getEditor: function() {return this.cm;}, splitLines: function(str) { if (this.lineSep) return str.split(this.lineSep); return splitLinesAuto(str); }, lineSeparator: function() { return this.lineSep || "\n"; } }); // Public alias. Doc.prototype.eachLine = Doc.prototype.iter; // Set up methods on CodeMirror's prototype to redirect to the editor's document. var dontDelegate = "iter insert remove copy getEditor constructor".split(" "); for (var prop in Doc.prototype) if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0) CodeMirror.prototype[prop] = (function(method) { return function() {return method.apply(this.doc, arguments);}; })(Doc.prototype[prop]); eventMixin(Doc); // Call f for all linked documents. function linkedDocs(doc, f, sharedHistOnly) { function propagate(doc, skip, sharedHist) { if (doc.linked) for (var i = 0; i < doc.linked.length; ++i) { var rel = doc.linked[i]; if (rel.doc == skip) continue; var shared = sharedHist && rel.sharedHist; if (sharedHistOnly && !shared) continue; f(rel.doc, shared); propagate(rel.doc, doc, shared); } } propagate(doc, null, true); } // Attach a document to an editor. function attachDoc(cm, doc) { if (doc.cm) throw new Error("This document is already in use."); cm.doc = doc; doc.cm = cm; estimateLineHeights(cm); loadMode(cm); if (!cm.options.lineWrapping) findMaxLine(cm); cm.options.mode = doc.modeOption; regChange(cm); } // LINE UTILITIES // Find the line object corresponding to the given line number. function getLine(doc, n) { n -= doc.first; if (n < 0 || n >= doc.size) throw new Error("There is no line " + (n + doc.first) + " in the document."); for (var chunk = doc; !chunk.lines;) { for (var i = 0;; ++i) { var child = chunk.children[i], sz = child.chunkSize(); if (n < sz) { chunk = child; break; } n -= sz; } } return chunk.lines[n]; } // Get the part of a document between two positions, as an array of // strings. function getBetween(doc, start, end) { var out = [], n = start.line; doc.iter(start.line, end.line + 1, function(line) { var text = line.text; if (n == end.line) text = text.slice(0, end.ch); if (n == start.line) text = text.slice(start.ch); out.push(text); ++n; }); return out; } // Get the lines between from and to, as array of strings. function getLines(doc, from, to) { var out = []; doc.iter(from, to, function(line) { out.push(line.text); }); return out; } // Update the height of a line, propagating the height change // upwards to parent nodes. function updateLineHeight(line, height) { var diff = height - line.height; if (diff) for (var n = line; n; n = n.parent) n.height += diff; } // Given a line object, find its line number by walking up through // its parent links. function lineNo(line) { if (line.parent == null) return null; var cur = line.parent, no = indexOf(cur.lines, line); for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) { for (var i = 0;; ++i) { if (chunk.children[i] == cur) break; no += chunk.children[i].chunkSize(); } } return no + cur.first; } // Find the line at the given vertical position, using the height // information in the document tree. function lineAtHeight(chunk, h) { var n = chunk.first; outer: do { for (var i = 0; i < chunk.children.length; ++i) { var child = chunk.children[i], ch = child.height; if (h < ch) { chunk = child; continue outer; } h -= ch; n += child.chunkSize(); } return n; } while (!chunk.lines); for (var i = 0; i < chunk.lines.length; ++i) { var line = chunk.lines[i], lh = line.height; if (h < lh) break; h -= lh; } return n + i; } // Find the height above the given line. function heightAtLine(lineObj) { lineObj = visualLine(lineObj); var h = 0, chunk = lineObj.parent; for (var i = 0; i < chunk.lines.length; ++i) { var line = chunk.lines[i]; if (line == lineObj) break; else h += line.height; } for (var p = chunk.parent; p; chunk = p, p = chunk.parent) { for (var i = 0; i < p.children.length; ++i) { var cur = p.children[i]; if (cur == chunk) break; else h += cur.height; } } return h; } // Get the bidi ordering for the given line (and cache it). Returns // false for lines that are fully left-to-right, and an array of // BidiSpan objects otherwise. function getOrder(line) { var order = line.order; if (order == null) order = line.order = bidiOrdering(line.text); return order; } // HISTORY function History(startGen) { // Arrays of change events and selections. Doing something adds an // event to done and clears undo. Undoing moves events from done // to undone, redoing moves them in the other direction. this.done = []; this.undone = []; this.undoDepth = Infinity; // Used to track when changes can be merged into a single undo // event this.lastModTime = this.lastSelTime = 0; this.lastOp = this.lastSelOp = null; this.lastOrigin = this.lastSelOrigin = null; // Used by the isClean() method this.generation = this.maxGeneration = startGen || 1; } // Create a history change event from an updateDoc-style change // object. function historyChangeFromChange(doc, change) { var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)}; attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); linkedDocs(doc, function(doc) {attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);}, true); return histChange; } // Pop all selection events off the end of a history array. Stop at // a change event. function clearSelectionEvents(array) { while (array.length) { var last = lst(array); if (last.ranges) array.pop(); else break; } } // Find the top change event in the history. Pop off selection // events that are in the way. function lastChangeEvent(hist, force) { if (force) { clearSelectionEvents(hist.done); return lst(hist.done); } else if (hist.done.length && !lst(hist.done).ranges) { return lst(hist.done); } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) { hist.done.pop(); return lst(hist.done); } } // Register a change in the history. Merges changes that are within // a single operation, ore are close together with an origin that // allows merging (starting with "+") into a single event. function addChangeToHistory(doc, change, selAfter, opId) { var hist = doc.history; hist.undone.length = 0; var time = +new Date, cur; if ((hist.lastOp == opId || hist.lastOrigin == change.origin && change.origin && ((change.origin.charAt(0) == "+" && doc.cm && hist.lastModTime > time - doc.cm.options.historyEventDelay) || change.origin.charAt(0) == "*")) && (cur = lastChangeEvent(hist, hist.lastOp == opId))) { // Merge this change into the last event var last = lst(cur.changes); if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) { // Optimized case for simple insertion -- don't want to add // new changesets for every character typed last.to = changeEnd(change); } else { // Add new sub-event cur.changes.push(historyChangeFromChange(doc, change)); } } else { // Can not be merged, start a new event. var before = lst(hist.done); if (!before || !before.ranges) pushSelectionToHistory(doc.sel, hist.done); cur = {changes: [historyChangeFromChange(doc, change)], generation: hist.generation}; hist.done.push(cur); while (hist.done.length > hist.undoDepth) { hist.done.shift(); if (!hist.done[0].ranges) hist.done.shift(); } } hist.done.push(selAfter); hist.generation = ++hist.maxGeneration; hist.lastModTime = hist.lastSelTime = time; hist.lastOp = hist.lastSelOp = opId; hist.lastOrigin = hist.lastSelOrigin = change.origin; if (!last) signal(doc, "historyAdded"); } function selectionEventCanBeMerged(doc, origin, prev, sel) { var ch = origin.charAt(0); return ch == "*" || ch == "+" && prev.ranges.length == sel.ranges.length && prev.somethingSelected() == sel.somethingSelected() && new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500); } // Called whenever the selection changes, sets the new selection as // the pending selection in the history, and pushes the old pending // selection into the 'done' array when it was significantly // different (in number of selected ranges, emptiness, or time). function addSelectionToHistory(doc, sel, opId, options) { var hist = doc.history, origin = options && options.origin; // A new event is started when the previous origin does not match // the current, or the origins don't allow matching. Origins // starting with * are always merged, those starting with + are // merged when similar and close together in time. if (opId == hist.lastSelOp || (origin && hist.lastSelOrigin == origin && (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin || selectionEventCanBeMerged(doc, origin, lst(hist.done), sel)))) hist.done[hist.done.length - 1] = sel; else pushSelectionToHistory(sel, hist.done); hist.lastSelTime = +new Date; hist.lastSelOrigin = origin; hist.lastSelOp = opId; if (options && options.clearRedo !== false) clearSelectionEvents(hist.undone); } function pushSelectionToHistory(sel, dest) { var top = lst(dest); if (!(top && top.ranges && top.equals(sel))) dest.push(sel); } // Used to store marked span information in the history. function attachLocalSpans(doc, change, from, to) { var existing = change["spans_" + doc.id], n = 0; doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function(line) { if (line.markedSpans) (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans; ++n; }); } // When un/re-doing restores text containing marked spans, those // that have been explicitly cleared should not be restored. function removeClearedSpans(spans) { if (!spans) return null; for (var i = 0, out; i < spans.length; ++i) { if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i); } else if (out) out.push(spans[i]); } return !out ? spans : out.length ? out : null; } // Retrieve and filter the old marked spans stored in a change event. function getOldSpans(doc, change) { var found = change["spans_" + doc.id]; if (!found) return null; for (var i = 0, nw = []; i < change.text.length; ++i) nw.push(removeClearedSpans(found[i])); return nw; } // Used both to provide a JSON-safe object in .getHistory, and, when // detaching a document, to split the history in two function copyHistoryArray(events, newGroup, instantiateSel) { for (var i = 0, copy = []; i < events.length; ++i) { var event = events[i]; if (event.ranges) { copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event); continue; } var changes = event.changes, newChanges = []; copy.push({changes: newChanges}); for (var j = 0; j < changes.length; ++j) { var change = changes[j], m; newChanges.push({from: change.from, to: change.to, text: change.text}); if (newGroup) for (var prop in change) if (m = prop.match(/^spans_(\d+)$/)) { if (indexOf(newGroup, Number(m[1])) > -1) { lst(newChanges)[prop] = change[prop]; delete change[prop]; } } } } return copy; } // Rebasing/resetting history to deal with externally-sourced changes function rebaseHistSelSingle(pos, from, to, diff) { if (to < pos.line) { pos.line += diff; } else if (from < pos.line) { pos.line = from; pos.ch = 0; } } // Tries to rebase an array of history events given a change in the // document. If the change touches the same lines as the event, the // event, and everything 'behind' it, is discarded. If the change is // before the event, the event's positions are updated. Uses a // copy-on-write scheme for the positions, to avoid having to // reallocate them all on every rebase, but also avoid problems with // shared position objects being unsafely updated. function rebaseHistArray(array, from, to, diff) { for (var i = 0; i < array.length; ++i) { var sub = array[i], ok = true; if (sub.ranges) { if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; } for (var j = 0; j < sub.ranges.length; j++) { rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff); rebaseHistSelSingle(sub.ranges[j].head, from, to, diff); } continue; } for (var j = 0; j < sub.changes.length; ++j) { var cur = sub.changes[j]; if (to < cur.from.line) { cur.from = Pos(cur.from.line + diff, cur.from.ch); cur.to = Pos(cur.to.line + diff, cur.to.ch); } else if (from <= cur.to.line) { ok = false; break; } } if (!ok) { array.splice(0, i + 1); i = 0; } } } function rebaseHist(hist, change) { var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1; rebaseHistArray(hist.done, from, to, diff); rebaseHistArray(hist.undone, from, to, diff); } // EVENT UTILITIES // Due to the fact that we still support jurassic IE versions, some // compatibility wrappers are needed. var e_preventDefault = CodeMirror.e_preventDefault = function(e) { if (e.preventDefault) e.preventDefault(); else e.returnValue = false; }; var e_stopPropagation = CodeMirror.e_stopPropagation = function(e) { if (e.stopPropagation) e.stopPropagation(); else e.cancelBubble = true; }; function e_defaultPrevented(e) { return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false; } var e_stop = CodeMirror.e_stop = function(e) {e_preventDefault(e); e_stopPropagation(e);}; function e_target(e) {return e.target || e.srcElement;} function e_button(e) { var b = e.which; if (b == null) { if (e.button & 1) b = 1; else if (e.button & 2) b = 3; else if (e.button & 4) b = 2; } if (mac && e.ctrlKey && b == 1) b = 3; return b; } // EVENT HANDLING // Lightweight event framework. on/off also work on DOM nodes, // registering native DOM handlers. var on = CodeMirror.on = function(emitter, type, f) { if (emitter.addEventListener) emitter.addEventListener(type, f, false); else if (emitter.attachEvent) emitter.attachEvent("on" + type, f); else { var map = emitter._handlers || (emitter._handlers = {}); var arr = map[type] || (map[type] = []); arr.push(f); } }; var noHandlers = [] function getHandlers(emitter, type, copy) { var arr = emitter._handlers && emitter._handlers[type] if (copy) return arr && arr.length > 0 ? arr.slice() : noHandlers else return arr || noHandlers } var off = CodeMirror.off = function(emitter, type, f) { if (emitter.removeEventListener) emitter.removeEventListener(type, f, false); else if (emitter.detachEvent) emitter.detachEvent("on" + type, f); else { var handlers = getHandlers(emitter, type, false) for (var i = 0; i < handlers.length; ++i) if (handlers[i] == f) { handlers.splice(i, 1); break; } } }; var signal = CodeMirror.signal = function(emitter, type /*, values...*/) { var handlers = getHandlers(emitter, type, true) if (!handlers.length) return; var args = Array.prototype.slice.call(arguments, 2); for (var i = 0; i < handlers.length; ++i) handlers[i].apply(null, args); }; var orphanDelayedCallbacks = null; // Often, we want to signal events at a point where we are in the // middle of some work, but don't want the handler to start calling // other methods on the editor, which might be in an inconsistent // state or simply not expect any other events to happen. // signalLater looks whether there are any handlers, and schedules // them to be executed when the last operation ends, or, if no // operation is active, when a timeout fires. function signalLater(emitter, type /*, values...*/) { var arr = getHandlers(emitter, type, false) if (!arr.length) return; var args = Array.prototype.slice.call(arguments, 2), list; if (operationGroup) { list = operationGroup.delayedCallbacks; } else if (orphanDelayedCallbacks) { list = orphanDelayedCallbacks; } else { list = orphanDelayedCallbacks = []; setTimeout(fireOrphanDelayed, 0); } function bnd(f) {return function(){f.apply(null, args);};}; for (var i = 0; i < arr.length; ++i) list.push(bnd(arr[i])); } function fireOrphanDelayed() { var delayed = orphanDelayedCallbacks; orphanDelayedCallbacks = null; for (var i = 0; i < delayed.length; ++i) delayed[i](); } // The DOM events that CodeMirror handles can be overridden by // registering a (non-DOM) handler on the editor for the event name, // and preventDefault-ing the event in that handler. function signalDOMEvent(cm, e, override) { if (typeof e == "string") e = {type: e, preventDefault: function() { this.defaultPrevented = true; }}; signal(cm, override || e.type, cm, e); return e_defaultPrevented(e) || e.codemirrorIgnore; } function signalCursorActivity(cm) { var arr = cm._handlers && cm._handlers.cursorActivity; if (!arr) return; var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []); for (var i = 0; i < arr.length; ++i) if (indexOf(set, arr[i]) == -1) set.push(arr[i]); } function hasHandler(emitter, type) { return getHandlers(emitter, type).length > 0 } // Add on and off methods to a constructor's prototype, to make // registering events on such objects more convenient. function eventMixin(ctor) { ctor.prototype.on = function(type, f) {on(this, type, f);}; ctor.prototype.off = function(type, f) {off(this, type, f);}; } // MISC UTILITIES // Number of pixels added to scroller and sizer to hide scrollbar var scrollerGap = 30; // Returned or thrown by various protocols to signal 'I'm not // handling this'. var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}}; // Reused option objects for setSelection & friends var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"}; function Delayed() {this.id = null;} Delayed.prototype.set = function(ms, f) { clearTimeout(this.id); this.id = setTimeout(f, ms); }; // Counts the column offset in a string, taking tabs into account. // Used mostly to find indentation. var countColumn = CodeMirror.countColumn = function(string, end, tabSize, startIndex, startValue) { if (end == null) { end = string.search(/[^\s\u00a0]/); if (end == -1) end = string.length; } for (var i = startIndex || 0, n = startValue || 0;;) { var nextTab = string.indexOf("\t", i); if (nextTab < 0 || nextTab >= end) return n + (end - i); n += nextTab - i; n += tabSize - (n % tabSize); i = nextTab + 1; } }; // The inverse of countColumn -- find the offset that corresponds to // a particular column. var findColumn = CodeMirror.findColumn = function(string, goal, tabSize) { for (var pos = 0, col = 0;;) { var nextTab = string.indexOf("\t", pos); if (nextTab == -1) nextTab = string.length; var skipped = nextTab - pos; if (nextTab == string.length || col + skipped >= goal) return pos + Math.min(skipped, goal - col); col += nextTab - pos; col += tabSize - (col % tabSize); pos = nextTab + 1; if (col >= goal) return pos; } } var spaceStrs = [""]; function spaceStr(n) { while (spaceStrs.length <= n) spaceStrs.push(lst(spaceStrs) + " "); return spaceStrs[n]; } function lst(arr) { return arr[arr.length-1]; } var selectInput = function(node) { node.select(); }; if (ios) // Mobile Safari apparently has a bug where select() is broken. selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; }; else if (ie) // Suppress mysterious IE10 errors selectInput = function(node) { try { node.select(); } catch(_e) {} }; function indexOf(array, elt) { for (var i = 0; i < array.length; ++i) if (array[i] == elt) return i; return -1; } function map(array, f) { var out = []; for (var i = 0; i < array.length; i++) out[i] = f(array[i], i); return out; } function nothing() {} function createObj(base, props) { var inst; if (Object.create) { inst = Object.create(base); } else { nothing.prototype = base; inst = new nothing(); } if (props) copyObj(props, inst); return inst; }; function copyObj(obj, target, overwrite) { if (!target) target = {}; for (var prop in obj) if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop))) target[prop] = obj[prop]; return target; } function bind(f) { var args = Array.prototype.slice.call(arguments, 1); return function(){return f.apply(null, args);}; } var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/; var isWordCharBasic = CodeMirror.isWordChar = function(ch) { return /\w/.test(ch) || ch > "\x80" && (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch)); }; function isWordChar(ch, helper) { if (!helper) return isWordCharBasic(ch); if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) return true; return helper.test(ch); } function isEmpty(obj) { for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) return false; return true; } // Extending unicode characters. A series of a non-extending char + // any number of extending chars is treated as a single unit as far // as editing and measuring is concerned. This is not fully correct, // since some scripts/fonts/browsers also treat other configurations // of code points as a group. var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/; function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch); } // DOM UTILITIES function elt(tag, content, className, style) { var e = document.createElement(tag); if (className) e.className = className; if (style) e.style.cssText = style; if (typeof content == "string") e.appendChild(document.createTextNode(content)); else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]); return e; } var range; if (document.createRange) range = function(node, start, end, endNode) { var r = document.createRange(); r.setEnd(endNode || node, end); r.setStart(node, start); return r; }; else range = function(node, start, end) { var r = document.body.createTextRange(); try { r.moveToElementText(node.parentNode); } catch(e) { return r; } r.collapse(true); r.moveEnd("character", end); r.moveStart("character", start); return r; }; function removeChildren(e) { for (var count = e.childNodes.length; count > 0; --count) e.removeChild(e.firstChild); return e; } function removeChildrenAndAdd(parent, e) { return removeChildren(parent).appendChild(e); } var contains = CodeMirror.contains = function(parent, child) { if (child.nodeType == 3) // Android browser always returns false when child is a textnode child = child.parentNode; if (parent.contains) return parent.contains(child); do { if (child.nodeType == 11) child = child.host; if (child == parent) return true; } while (child = child.parentNode); }; function activeElt() { var activeElement = document.activeElement; while (activeElement && activeElement.root && activeElement.root.activeElement) activeElement = activeElement.root.activeElement; return activeElement; } // Older versions of IE throws unspecified error when touching // document.activeElement in some cases (during loading, in iframe) if (ie && ie_version < 11) activeElt = function() { try { return document.activeElement; } catch(e) { return document.body; } }; function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*"); } var rmClass = CodeMirror.rmClass = function(node, cls) { var current = node.className; var match = classTest(cls).exec(current); if (match) { var after = current.slice(match.index + match[0].length); node.className = current.slice(0, match.index) + (after ? match[1] + after : ""); } }; var addClass = CodeMirror.addClass = function(node, cls) { var current = node.className; if (!classTest(cls).test(current)) node.className += (current ? " " : "") + cls; }; function joinClasses(a, b) { var as = a.split(" "); for (var i = 0; i < as.length; i++) if (as[i] && !classTest(as[i]).test(b)) b += " " + as[i]; return b; } // WINDOW-WIDE EVENTS // These must be handled carefully, because naively registering a // handler for each editor will cause the editors to never be // garbage collected. function forEachCodeMirror(f) { if (!document.body.getElementsByClassName) return; var byClass = document.body.getElementsByClassName("CodeMirror"); for (var i = 0; i < byClass.length; i++) { var cm = byClass[i].CodeMirror; if (cm) f(cm); } } var globalsRegistered = false; function ensureGlobalHandlers() { if (globalsRegistered) return; registerGlobalHandlers(); globalsRegistered = true; } function registerGlobalHandlers() { // When the window resizes, we need to refresh active editors. var resizeTimer; on(window, "resize", function() { if (resizeTimer == null) resizeTimer = setTimeout(function() { resizeTimer = null; forEachCodeMirror(onResize); }, 100); }); // When the window loses focus, we want to show the editor as blurred on(window, "blur", function() { forEachCodeMirror(onBlur); }); } // FEATURE DETECTION // Detect drag-and-drop var dragAndDrop = function() { // There is *some* kind of drag-and-drop support in IE6-8, but I // couldn't get it to work yet. if (ie && ie_version < 9) return false; var div = elt('div'); return "draggable" in div || "dragDrop" in div; }(); var zwspSupported; function zeroWidthElement(measure) { if (zwspSupported == null) { var test = elt("span", "\u200b"); removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")])); if (measure.firstChild.offsetHeight != 0) zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8); } var node = zwspSupported ? elt("span", "\u200b") : elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px"); node.setAttribute("cm-text", ""); return node; } // Feature-detect IE's crummy client rect reporting for bidi text var badBidiRects; function hasBadBidiRects(measure) { if (badBidiRects != null) return badBidiRects; var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA")); var r0 = range(txt, 0, 1).getBoundingClientRect(); if (!r0 || r0.left == r0.right) return false; // Safari returns null in some cases (#2780) var r1 = range(txt, 1, 2).getBoundingClientRect(); return badBidiRects = (r1.right - r0.right < 3); } // See if "".split is the broken IE version, if so, provide an // alternative way to split lines. var splitLinesAuto = CodeMirror.splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) { var pos = 0, result = [], l = string.length; while (pos <= l) { var nl = string.indexOf("\n", pos); if (nl == -1) nl = string.length; var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl); var rt = line.indexOf("\r"); if (rt != -1) { result.push(line.slice(0, rt)); pos += rt + 1; } else { result.push(line); pos = nl + 1; } } return result; } : function(string){return string.split(/\r\n?|\n/);}; var hasSelection = window.getSelection ? function(te) { try { return te.selectionStart != te.selectionEnd; } catch(e) { return false; } } : function(te) { try {var range = te.ownerDocument.selection.createRange();} catch(e) {} if (!range || range.parentElement() != te) return false; return range.compareEndPoints("StartToEnd", range) != 0; }; var hasCopyEvent = (function() { var e = elt("div"); if ("oncopy" in e) return true; e.setAttribute("oncopy", "return;"); return typeof e.oncopy == "function"; })(); var badZoomedRects = null; function hasBadZoomedRects(measure) { if (badZoomedRects != null) return badZoomedRects; var node = removeChildrenAndAdd(measure, elt("span", "x")); var normal = node.getBoundingClientRect(); var fromRange = range(node, 0, 1).getBoundingClientRect(); return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1; } // KEY NAMES var keyNames = CodeMirror.keyNames = { 3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt", 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End", 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert", 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod", 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 127: "Delete", 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete", 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert" }; (function() { // Number keys for (var i = 0; i < 10; i++) keyNames[i + 48] = keyNames[i + 96] = String(i); // Alphabetic keys for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i); // Function keys for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i; })(); // BIDI HELPERS function iterateBidiSections(order, from, to, f) { if (!order) return f(from, to, "ltr"); var found = false; for (var i = 0; i < order.length; ++i) { var part = order[i]; if (part.from < to && part.to > from || from == to && part.to == from) { f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr"); found = true; } } if (!found) f(from, to, "ltr"); } function bidiLeft(part) { return part.level % 2 ? part.to : part.from; } function bidiRight(part) { return part.level % 2 ? part.from : part.to; } function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0; } function lineRight(line) { var order = getOrder(line); if (!order) return line.text.length; return bidiRight(lst(order)); } function lineStart(cm, lineN) { var line = getLine(cm.doc, lineN); var visual = visualLine(line); if (visual != line) lineN = lineNo(visual); var order = getOrder(visual); var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual); return Pos(lineN, ch); } function lineEnd(cm, lineN) { var merged, line = getLine(cm.doc, lineN); while (merged = collapsedSpanAtEnd(line)) { line = merged.find(1, true).line; lineN = null; } var order = getOrder(line); var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line); return Pos(lineN == null ? lineNo(line) : lineN, ch); } function lineStartSmart(cm, pos) { var start = lineStart(cm, pos.line); var line = getLine(cm.doc, start.line); var order = getOrder(line); if (!order || order[0].level == 0) { var firstNonWS = Math.max(0, line.text.search(/\S/)); var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch; return Pos(start.line, inWS ? 0 : firstNonWS); } return start; } function compareBidiLevel(order, a, b) { var linedir = order[0].level; if (a == linedir) return true; if (b == linedir) return false; return a < b; } var bidiOther; function getBidiPartAt(order, pos) { bidiOther = null; for (var i = 0, found; i < order.length; ++i) { var cur = order[i]; if (cur.from < pos && cur.to > pos) return i; if ((cur.from == pos || cur.to == pos)) { if (found == null) { found = i; } else if (compareBidiLevel(order, cur.level, order[found].level)) { if (cur.from != cur.to) bidiOther = found; return i; } else { if (cur.from != cur.to) bidiOther = i; return found; } } } return found; } function moveInLine(line, pos, dir, byUnit) { if (!byUnit) return pos + dir; do pos += dir; while (pos > 0 && isExtendingChar(line.text.charAt(pos))); return pos; } // This is needed in order to move 'visually' through bi-directional // text -- i.e., pressing left should make the cursor go left, even // when in RTL text. The tricky part is the 'jumps', where RTL and // LTR text touch each other. This often requires the cursor offset // to move more than one unit, in order to visually move one unit. function moveVisually(line, start, dir, byUnit) { var bidi = getOrder(line); if (!bidi) return moveLogically(line, start, dir, byUnit); var pos = getBidiPartAt(bidi, start), part = bidi[pos]; var target = moveInLine(line, start, part.level % 2 ? -dir : dir, byUnit); for (;;) { if (target > part.from && target < part.to) return target; if (target == part.from || target == part.to) { if (getBidiPartAt(bidi, target) == pos) return target; part = bidi[pos += dir]; return (dir > 0) == part.level % 2 ? part.to : part.from; } else { part = bidi[pos += dir]; if (!part) return null; if ((dir > 0) == part.level % 2) target = moveInLine(line, part.to, -1, byUnit); else target = moveInLine(line, part.from, 1, byUnit); } } } function moveLogically(line, start, dir, byUnit) { var target = start + dir; if (byUnit) while (target > 0 && isExtendingChar(line.text.charAt(target))) target += dir; return target < 0 || target > line.text.length ? null : target; } // Bidirectional ordering algorithm // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm // that this (partially) implements. // One-char codes used for character types: // L (L): Left-to-Right // R (R): Right-to-Left // r (AL): Right-to-Left Arabic // 1 (EN): European Number // + (ES): European Number Separator // % (ET): European Number Terminator // n (AN): Arabic Number // , (CS): Common Number Separator // m (NSM): Non-Spacing Mark // b (BN): Boundary Neutral // s (B): Paragraph Separator // t (S): Segment Separator // w (WS): Whitespace // N (ON): Other Neutrals // Returns null if characters are ordered as they appear // (left-to-right), or an array of sections ({from, to, level} // objects) in the order in which they occur visually. var bidiOrdering = (function() { // Character types for codepoints 0 to 0xff var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN"; // Character types for codepoints 0x600 to 0x6ff var arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmm"; function charType(code) { if (code <= 0xf7) return lowTypes.charAt(code); else if (0x590 <= code && code <= 0x5f4) return "R"; else if (0x600 <= code && code <= 0x6ed) return arabicTypes.charAt(code - 0x600); else if (0x6ee <= code && code <= 0x8ac) return "r"; else if (0x2000 <= code && code <= 0x200b) return "w"; else if (code == 0x200c) return "b"; else return "L"; } var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/; var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/; // Browsers seem to always treat the boundaries of block elements as being L. var outerType = "L"; function BidiSpan(level, from, to) { this.level = level; this.from = from; this.to = to; } return function(str) { if (!bidiRE.test(str)) return false; var len = str.length, types = []; for (var i = 0, type; i < len; ++i) types.push(type = charType(str.charCodeAt(i))); // W1. Examine each non-spacing mark (NSM) in the level run, and // change the type of the NSM to the type of the previous // character. If the NSM is at the start of the level run, it will // get the type of sor. for (var i = 0, prev = outerType; i < len; ++i) { var type = types[i]; if (type == "m") types[i] = prev; else prev = type; } // W2. Search backwards from each instance of a European number // until the first strong type (R, L, AL, or sor) is found. If an // AL is found, change the type of the European number to Arabic // number. // W3. Change all ALs to R. for (var i = 0, cur = outerType; i < len; ++i) { var type = types[i]; if (type == "1" && cur == "r") types[i] = "n"; else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "R"; } } // W4. A single European separator between two European numbers // changes to a European number. A single common separator between // two numbers of the same type changes to that type. for (var i = 1, prev = types[0]; i < len - 1; ++i) { var type = types[i]; if (type == "+" && prev == "1" && types[i+1] == "1") types[i] = "1"; else if (type == "," && prev == types[i+1] && (prev == "1" || prev == "n")) types[i] = prev; prev = type; } // W5. A sequence of European terminators adjacent to European // numbers changes to all European numbers. // W6. Otherwise, separators and terminators change to Other // Neutral. for (var i = 0; i < len; ++i) { var type = types[i]; if (type == ",") types[i] = "N"; else if (type == "%") { for (var end = i + 1; end < len && types[end] == "%"; ++end) {} var replace = (i && types[i-1] == "!") || (end < len && types[end] == "1") ? "1" : "N"; for (var j = i; j < end; ++j) types[j] = replace; i = end - 1; } } // W7. Search backwards from each instance of a European number // until the first strong type (R, L, or sor) is found. If an L is // found, then change the type of the European number to L. for (var i = 0, cur = outerType; i < len; ++i) { var type = types[i]; if (cur == "L" && type == "1") types[i] = "L"; else if (isStrong.test(type)) cur = type; } // N1. A sequence of neutrals takes the direction of the // surrounding strong text if the text on both sides has the same // direction. European and Arabic numbers act as if they were R in // terms of their influence on neutrals. Start-of-level-run (sor) // and end-of-level-run (eor) are used at level run boundaries. // N2. Any remaining neutrals take the embedding direction. for (var i = 0; i < len; ++i) { if (isNeutral.test(types[i])) { for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {} var before = (i ? types[i-1] : outerType) == "L"; var after = (end < len ? types[end] : outerType) == "L"; var replace = before || after ? "L" : "R"; for (var j = i; j < end; ++j) types[j] = replace; i = end - 1; } } // Here we depart from the documented algorithm, in order to avoid // building up an actual levels array. Since there are only three // levels (0, 1, 2) in an implementation that doesn't take // explicit embedding into account, we can build up the order on // the fly, without following the level-based algorithm. var order = [], m; for (var i = 0; i < len;) { if (countsAsLeft.test(types[i])) { var start = i; for (++i; i < len && countsAsLeft.test(types[i]); ++i) {} order.push(new BidiSpan(0, start, i)); } else { var pos = i, at = order.length; for (++i; i < len && types[i] != "L"; ++i) {} for (var j = pos; j < i;) { if (countsAsNum.test(types[j])) { if (pos < j) order.splice(at, 0, new BidiSpan(1, pos, j)); var nstart = j; for (++j; j < i && countsAsNum.test(types[j]); ++j) {} order.splice(at, 0, new BidiSpan(2, nstart, j)); pos = j; } else ++j; } if (pos < i) order.splice(at, 0, new BidiSpan(1, pos, i)); } } if (order[0].level == 1 && (m = str.match(/^\s+/))) { order[0].from = m[0].length; order.unshift(new BidiSpan(0, 0, m[0].length)); } if (lst(order).level == 1 && (m = str.match(/\s+$/))) { lst(order).to -= m[0].length; order.push(new BidiSpan(0, len - m[0].length, len)); } if (order[0].level == 2) order.unshift(new BidiSpan(1, order[0].to, order[0].to)); if (order[0].level != lst(order).level) order.push(new BidiSpan(order[0].level, len, len)); return order; }; })(); // THE END CodeMirror.version = "5.15.2"; return CodeMirror; }); },{}],11:[function(require,module,exports){ // CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: http://codemirror.net/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror"), require("../markdown/markdown"), require("../../addon/mode/overlay")); else if (typeof define == "function" && define.amd) // AMD define(["../../lib/codemirror", "../markdown/markdown", "../../addon/mode/overlay"], mod); else // Plain browser env mod(CodeMirror); })(function(CodeMirror) { "use strict"; var urlRE = /^((?:(?:aaas?|about|acap|adiumxtra|af[ps]|aim|apt|attachment|aw|beshare|bitcoin|bolo|callto|cap|chrome(?:-extension)?|cid|coap|com-eventbrite-attendee|content|crid|cvs|data|dav|dict|dlna-(?:playcontainer|playsingle)|dns|doi|dtn|dvb|ed2k|facetime|feed|file|finger|fish|ftp|geo|gg|git|gizmoproject|go|gopher|gtalk|h323|hcp|https?|iax|icap|icon|im|imap|info|ipn|ipp|irc[6s]?|iris(?:\.beep|\.lwz|\.xpc|\.xpcs)?|itms|jar|javascript|jms|keyparc|lastfm|ldaps?|magnet|mailto|maps|market|message|mid|mms|ms-help|msnim|msrps?|mtqp|mumble|mupdate|mvn|news|nfs|nih?|nntp|notes|oid|opaquelocktoken|palm|paparazzi|platform|pop|pres|proxy|psyc|query|res(?:ource)?|rmi|rsync|rtmp|rtsp|secondlife|service|session|sftp|sgn|shttp|sieve|sips?|skype|sm[bs]|snmp|soap\.beeps?|soldat|spotify|ssh|steam|svn|tag|teamspeak|tel(?:net)?|tftp|things|thismessage|tip|tn3270|tv|udp|unreal|urn|ut2004|vemmi|ventrilo|view-source|webcal|wss?|wtai|wyciwyg|xcon(?:-userid)?|xfire|xmlrpc\.beeps?|xmpp|xri|ymsgr|z39\.50[rs]?):(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]|\([^\s()<>]*\))+(?:\([^\s()<>]*\)|[^\s`*!()\[\]{};:'".,<>?«»“”‘’]))/i CodeMirror.defineMode("gfm", function(config, modeConfig) { var codeDepth = 0; function blankLine(state) { state.code = false; return null; } var gfmOverlay = { startState: function() { return { code: false, codeBlock: false, ateSpace: false }; }, copyState: function(s) { return { code: s.code, codeBlock: s.codeBlock, ateSpace: s.ateSpace }; }, token: function(stream, state) { state.combineTokens = null; // Hack to prevent formatting override inside code blocks (block and inline) if (state.codeBlock) { if (stream.match(/^```+/)) { state.codeBlock = false; return null; } stream.skipToEnd(); return null; } if (stream.sol()) { state.code = false; } if (stream.sol() && stream.match(/^```+/)) { stream.skipToEnd(); state.codeBlock = true; return null; } // If this block is changed, it may need to be updated in Markdown mode if (stream.peek() === '`') { stream.next(); var before = stream.pos; stream.eatWhile('`'); var difference = 1 + stream.pos - before; if (!state.code) { codeDepth = difference; state.code = true; } else { if (difference === codeDepth) { // Must be exact state.code = false; } } return null; } else if (state.code) { stream.next(); return null; } // Check if space. If so, links can be formatted later on if (stream.eatSpace()) { state.ateSpace = true; return null; } if (stream.sol() || state.ateSpace) { state.ateSpace = false; if (modeConfig.gitHubSpice !== false) { if(stream.match(/^(?:[a-zA-Z0-9\-_]+\/)?(?:[a-zA-Z0-9\-_]+@)?(?:[a-f0-9]{7,40}\b)/)) { // User/Project@SHA // User@SHA // SHA state.combineTokens = true; return "link"; } else if (stream.match(/^(?:[a-zA-Z0-9\-_]+\/)?(?:[a-zA-Z0-9\-_]+)?#[0-9]+\b/)) { // User/Project#Num // User#Num // #Num state.combineTokens = true; return "link"; } } } if (stream.match(urlRE) && stream.string.slice(stream.start - 2, stream.start) != "](" && (stream.start == 0 || /\W/.test(stream.string.charAt(stream.start - 1)))) { // URLs // Taken from http://daringfireball.net/2010/07/improved_regex_for_matching_urls // And then (issue #1160) simplified to make it not crash the Chrome Regexp engine // And then limited url schemes to the CommonMark list, so foo:bar isn't matched as a URL state.combineTokens = true; return "link"; } stream.next(); return null; }, blankLine: blankLine }; var markdownConfig = { underscoresBreakWords: false, taskLists: true, fencedCodeBlocks: '```', strikethrough: true }; for (var attr in modeConfig) { markdownConfig[attr] = modeConfig[attr]; } markdownConfig.name = "markdown"; return CodeMirror.overlayMode(CodeMirror.getMode(config, markdownConfig), gfmOverlay); }, "markdown"); CodeMirror.defineMIME("text/x-gfm", "gfm"); }); },{"../../addon/mode/overlay":8,"../../lib/codemirror":10,"../markdown/markdown":12}],12:[function(require,module,exports){ // CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: http://codemirror.net/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror"), require("../xml/xml"), require("../meta")); else if (typeof define == "function" && define.amd) // AMD define(["../../lib/codemirror", "../xml/xml", "../meta"], mod); else // Plain browser env mod(CodeMirror); })(function(CodeMirror) { "use strict"; CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { var htmlMode = CodeMirror.getMode(cmCfg, "text/html"); var htmlModeMissing = htmlMode.name == "null" function getMode(name) { if (CodeMirror.findModeByName) { var found = CodeMirror.findModeByName(name); if (found) name = found.mime || found.mimes[0]; } var mode = CodeMirror.getMode(cmCfg, name); return mode.name == "null" ? null : mode; } // Should characters that affect highlighting be highlighted separate? // Does not include characters that will be output (such as `1.` and `-` for lists) if (modeCfg.highlightFormatting === undefined) modeCfg.highlightFormatting = false; // Maximum number of nested blockquotes. Set to 0 for infinite nesting. // Excess `>` will emit `error` token. if (modeCfg.maxBlockquoteDepth === undefined) modeCfg.maxBlockquoteDepth = 0; // Should underscores in words open/close em/strong? if (modeCfg.underscoresBreakWords === undefined) modeCfg.underscoresBreakWords = true; // Use `fencedCodeBlocks` to configure fenced code blocks. false to // disable, string to specify a precise regexp that the fence should // match, and true to allow three or more backticks or tildes (as // per CommonMark). // Turn on task lists? ("- [ ] " and "- [x] ") if (modeCfg.taskLists === undefined) modeCfg.taskLists = false; // Turn on strikethrough syntax if (modeCfg.strikethrough === undefined) modeCfg.strikethrough = false; // Allow token types to be overridden by user-provided token types. if (modeCfg.tokenTypeOverrides === undefined) modeCfg.tokenTypeOverrides = {}; var tokenTypes = { header: "header", code: "comment", quote: "quote", list1: "variable-2", list2: "variable-3", list3: "keyword", hr: "hr", image: "tag", formatting: "formatting", linkInline: "link", linkEmail: "link", linkText: "link", linkHref: "string", em: "em", strong: "strong", strikethrough: "strikethrough" }; for (var tokenType in tokenTypes) { if (tokenTypes.hasOwnProperty(tokenType) && modeCfg.tokenTypeOverrides[tokenType]) { tokenTypes[tokenType] = modeCfg.tokenTypeOverrides[tokenType]; } } var hrRE = /^([*\-_])(?:\s*\1){2,}\s*$/ , ulRE = /^[*\-+]\s+/ , olRE = /^[0-9]+([.)])\s+/ , taskListRE = /^\[(x| )\](?=\s)/ // Must follow ulRE or olRE , atxHeaderRE = modeCfg.allowAtxHeaderWithoutSpace ? /^(#+)/ : /^(#+)(?: |$)/ , setextHeaderRE = /^ *(?:\={1,}|-{1,})\s*$/ , textRE = /^[^#!\[\]*_\\<>` "'(~]+/ , fencedCodeRE = new RegExp("^(" + (modeCfg.fencedCodeBlocks === true ? "~~~+|```+" : modeCfg.fencedCodeBlocks) + ")[ \\t]*([\\w+#\-]*)"); function switchInline(stream, state, f) { state.f = state.inline = f; return f(stream, state); } function switchBlock(stream, state, f) { state.f = state.block = f; return f(stream, state); } function lineIsEmpty(line) { return !line || !/\S/.test(line.string) } // Blocks function blankLine(state) { // Reset linkTitle state state.linkTitle = false; // Reset EM state state.em = false; // Reset STRONG state state.strong = false; // Reset strikethrough state state.strikethrough = false; // Reset state.quote state.quote = 0; // Reset state.indentedCode state.indentedCode = false; if (htmlModeMissing && state.f == htmlBlock) { state.f = inlineNormal; state.block = blockNormal; } // Reset state.trailingSpace state.trailingSpace = 0; state.trailingSpaceNewLine = false; // Mark this line as blank state.prevLine = state.thisLine state.thisLine = null return null; } function blockNormal(stream, state) { var sol = stream.sol(); var prevLineIsList = state.list !== false, prevLineIsIndentedCode = state.indentedCode; state.indentedCode = false; if (prevLineIsList) { if (state.indentationDiff >= 0) { // Continued list if (state.indentationDiff < 4) { // Only adjust indentation if *not* a code block state.indentation -= state.indentationDiff; } state.list = null; } else if (state.indentation > 0) { state.list = null; } else { // No longer a list state.list = false; } } var match = null; if (state.indentationDiff >= 4) { stream.skipToEnd(); if (prevLineIsIndentedCode || lineIsEmpty(state.prevLine)) { state.indentation -= 4; state.indentedCode = true; return tokenTypes.code; } else { return null; } } else if (stream.eatSpace()) { return null; } else if ((match = stream.match(atxHeaderRE)) && match[1].length <= 6) { state.header = match[1].length; if (modeCfg.highlightFormatting) state.formatting = "header"; state.f = state.inline; return getType(state); } else if (!lineIsEmpty(state.prevLine) && !state.quote && !prevLineIsList && !prevLineIsIndentedCode && (match = stream.match(setextHeaderRE))) { state.header = match[0].charAt(0) == '=' ? 1 : 2; if (modeCfg.highlightFormatting) state.formatting = "header"; state.f = state.inline; return getType(state); } else if (stream.eat('>')) { state.quote = sol ? 1 : state.quote + 1; if (modeCfg.highlightFormatting) state.formatting = "quote"; stream.eatSpace(); return getType(state); } else if (stream.peek() === '[') { return switchInline(stream, state, footnoteLink); } else if (stream.match(hrRE, true)) { state.hr = true; return tokenTypes.hr; } else if ((lineIsEmpty(state.prevLine) || prevLineIsList) && (stream.match(ulRE, false) || stream.match(olRE, false))) { var listType = null; if (stream.match(ulRE, true)) { listType = 'ul'; } else { stream.match(olRE, true); listType = 'ol'; } state.indentation = stream.column() + stream.current().length; state.list = true; // While this list item's marker's indentation // is less than the deepest list item's content's indentation, // pop the deepest list item indentation off the stack. while (state.listStack && stream.column() < state.listStack[state.listStack.length - 1]) { state.listStack.pop(); } // Add this list item's content's indentation to the stack state.listStack.push(state.indentation); if (modeCfg.taskLists && stream.match(taskListRE, false)) { state.taskList = true; } state.f = state.inline; if (modeCfg.highlightFormatting) state.formatting = ["list", "list-" + listType]; return getType(state); } else if (modeCfg.fencedCodeBlocks && (match = stream.match(fencedCodeRE, true))) { state.fencedChars = match[1] // try switching mode state.localMode = getMode(match[2]); if (state.localMode) state.localState = CodeMirror.startState(state.localMode); state.f = state.block = local; if (modeCfg.highlightFormatting) state.formatting = "code-block"; state.code = -1 return getType(state); } return switchInline(stream, state, state.inline); } function htmlBlock(stream, state) { var style = htmlMode.token(stream, state.htmlState); if (!htmlModeMissing) { var inner = CodeMirror.innerMode(htmlMode, state.htmlState) if ((inner.mode.name == "xml" && inner.state.tagStart === null && (!inner.state.context && inner.state.tokenize.isInText)) || (state.md_inside && stream.current().indexOf(">") > -1)) { state.f = inlineNormal; state.block = blockNormal; state.htmlState = null; } } return style; } function local(stream, state) { if (state.fencedChars && stream.match(state.fencedChars, false)) { state.localMode = state.localState = null; state.f = state.block = leavingLocal; return null; } else if (state.localMode) { return state.localMode.token(stream, state.localState); } else { stream.skipToEnd(); return tokenTypes.code; } } function leavingLocal(stream, state) { stream.match(state.fencedChars); state.block = blockNormal; state.f = inlineNormal; state.fencedChars = null; if (modeCfg.highlightFormatting) state.formatting = "code-block"; state.code = 1 var returnType = getType(state); state.code = 0 return returnType; } // Inline function getType(state) { var styles = []; if (state.formatting) { styles.push(tokenTypes.formatting); if (typeof state.formatting === "string") state.formatting = [state.formatting]; for (var i = 0; i < state.formatting.length; i++) { styles.push(tokenTypes.formatting + "-" + state.formatting[i]); if (state.formatting[i] === "header") { styles.push(tokenTypes.formatting + "-" + state.formatting[i] + "-" + state.header); } // Add `formatting-quote` and `formatting-quote-#` for blockquotes // Add `error` instead if the maximum blockquote nesting depth is passed if (state.formatting[i] === "quote") { if (!modeCfg.maxBlockquoteDepth || modeCfg.maxBlockquoteDepth >= state.quote) { styles.push(tokenTypes.formatting + "-" + state.formatting[i] + "-" + state.quote); } else { styles.push("error"); } } } } if (state.taskOpen) { styles.push("meta"); return styles.length ? styles.join(' ') : null; } if (state.taskClosed) { styles.push("property"); return styles.length ? styles.join(' ') : null; } if (state.linkHref) { styles.push(tokenTypes.linkHref, "url"); } else { // Only apply inline styles to non-url text if (state.strong) { styles.push(tokenTypes.strong); } if (state.em) { styles.push(tokenTypes.em); } if (state.strikethrough) { styles.push(tokenTypes.strikethrough); } if (state.linkText) { styles.push(tokenTypes.linkText); } if (state.code) { styles.push(tokenTypes.code); } } if (state.header) { styles.push(tokenTypes.header, tokenTypes.header + "-" + state.header); } if (state.quote) { styles.push(tokenTypes.quote); // Add `quote-#` where the maximum for `#` is modeCfg.maxBlockquoteDepth if (!modeCfg.maxBlockquoteDepth || modeCfg.maxBlockquoteDepth >= state.quote) { styles.push(tokenTypes.quote + "-" + state.quote); } else { styles.push(tokenTypes.quote + "-" + modeCfg.maxBlockquoteDepth); } } if (state.list !== false) { var listMod = (state.listStack.length - 1) % 3; if (!listMod) { styles.push(tokenTypes.list1); } else if (listMod === 1) { styles.push(tokenTypes.list2); } else { styles.push(tokenTypes.list3); } } if (state.trailingSpaceNewLine) { styles.push("trailing-space-new-line"); } else if (state.trailingSpace) { styles.push("trailing-space-" + (state.trailingSpace % 2 ? "a" : "b")); } return styles.length ? styles.join(' ') : null; } function handleText(stream, state) { if (stream.match(textRE, true)) { return getType(state); } return undefined; } function inlineNormal(stream, state) { var style = state.text(stream, state); if (typeof style !== 'undefined') return style; if (state.list) { // List marker (*, +, -, 1., etc) state.list = null; return getType(state); } if (state.taskList) { var taskOpen = stream.match(taskListRE, true)[1] !== "x"; if (taskOpen) state.taskOpen = true; else state.taskClosed = true; if (modeCfg.highlightFormatting) state.formatting = "task"; state.taskList = false; return getType(state); } state.taskOpen = false; state.taskClosed = false; if (state.header && stream.match(/^#+$/, true)) { if (modeCfg.highlightFormatting) state.formatting = "header"; return getType(state); } // Get sol() value now, before character is consumed var sol = stream.sol(); var ch = stream.next(); // Matches link titles present on next line if (state.linkTitle) { state.linkTitle = false; var matchCh = ch; if (ch === '(') { matchCh = ')'; } matchCh = (matchCh+'').replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1"); var regex = '^\\s*(?:[^' + matchCh + '\\\\]+|\\\\\\\\|\\\\.)' + matchCh; if (stream.match(new RegExp(regex), true)) { return tokenTypes.linkHref; } } // If this block is changed, it may need to be updated in GFM mode if (ch === '`') { var previousFormatting = state.formatting; if (modeCfg.highlightFormatting) state.formatting = "code"; stream.eatWhile('`'); var count = stream.current().length if (state.code == 0) { state.code = count return getType(state) } else if (count == state.code) { // Must be exact var t = getType(state) state.code = 0 return t } else { state.formatting = previousFormatting return getType(state) } } else if (state.code) { return getType(state); } if (ch === '\\') { stream.next(); if (modeCfg.highlightFormatting) { var type = getType(state); var formattingEscape = tokenTypes.formatting + "-escape"; return type ? type + " " + formattingEscape : formattingEscape; } } if (ch === '!' && stream.match(/\[[^\]]*\] ?(?:\(|\[)/, false)) { stream.match(/\[[^\]]*\]/); state.inline = state.f = linkHref; return tokenTypes.image; } if (ch === '[' && stream.match(/[^\]]*\](\(.*\)| ?\[.*?\])/, false)) { state.linkText = true; if (modeCfg.highlightFormatting) state.formatting = "link"; return getType(state); } if (ch === ']' && state.linkText && stream.match(/\(.*?\)| ?\[.*?\]/, false)) { if (modeCfg.highlightFormatting) state.formatting = "link"; var type = getType(state); state.linkText = false; state.inline = state.f = linkHref; return type; } if (ch === '<' && stream.match(/^(https?|ftps?):\/\/(?:[^\\>]|\\.)+>/, false)) { state.f = state.inline = linkInline; if (modeCfg.highlightFormatting) state.formatting = "link"; var type = getType(state); if (type){ type += " "; } else { type = ""; } return type + tokenTypes.linkInline; } if (ch === '<' && stream.match(/^[^> \\]+@(?:[^\\>]|\\.)+>/, false)) { state.f = state.inline = linkInline; if (modeCfg.highlightFormatting) state.formatting = "link"; var type = getType(state); if (type){ type += " "; } else { type = ""; } return type + tokenTypes.linkEmail; } if (ch === '<' && stream.match(/^(!--|\w)/, false)) { var end = stream.string.indexOf(">", stream.pos); if (end != -1) { var atts = stream.string.substring(stream.start, end); if (/markdown\s*=\s*('|"){0,1}1('|"){0,1}/.test(atts)) state.md_inside = true; } stream.backUp(1); state.htmlState = CodeMirror.startState(htmlMode); return switchBlock(stream, state, htmlBlock); } if (ch === '<' && stream.match(/^\/\w*?>/)) { state.md_inside = false; return "tag"; } var ignoreUnderscore = false; if (!modeCfg.underscoresBreakWords) { if (ch === '_' && stream.peek() !== '_' && stream.match(/(\w)/, false)) { var prevPos = stream.pos - 2; if (prevPos >= 0) { var prevCh = stream.string.charAt(prevPos); if (prevCh !== '_' && prevCh.match(/(\w)/, false)) { ignoreUnderscore = true; } } } } if (ch === '*' || (ch === '_' && !ignoreUnderscore)) { if (sol && stream.peek() === ' ') { // Do nothing, surrounded by newline and space } else if (state.strong === ch && stream.eat(ch)) { // Remove STRONG if (modeCfg.highlightFormatting) state.formatting = "strong"; var t = getType(state); state.strong = false; return t; } else if (!state.strong && stream.eat(ch)) { // Add STRONG state.strong = ch; if (modeCfg.highlightFormatting) state.formatting = "strong"; return getType(state); } else if (state.em === ch) { // Remove EM if (modeCfg.highlightFormatting) state.formatting = "em"; var t = getType(state); state.em = false; return t; } else if (!state.em) { // Add EM state.em = ch; if (modeCfg.highlightFormatting) state.formatting = "em"; return getType(state); } } else if (ch === ' ') { if (stream.eat('*') || stream.eat('_')) { // Probably surrounded by spaces if (stream.peek() === ' ') { // Surrounded by spaces, ignore return getType(state); } else { // Not surrounded by spaces, back up pointer stream.backUp(1); } } } if (modeCfg.strikethrough) { if (ch === '~' && stream.eatWhile(ch)) { if (state.strikethrough) {// Remove strikethrough if (modeCfg.highlightFormatting) state.formatting = "strikethrough"; var t = getType(state); state.strikethrough = false; return t; } else if (stream.match(/^[^\s]/, false)) {// Add strikethrough state.strikethrough = true; if (modeCfg.highlightFormatting) state.formatting = "strikethrough"; return getType(state); } } else if (ch === ' ') { if (stream.match(/^~~/, true)) { // Probably surrounded by space if (stream.peek() === ' ') { // Surrounded by spaces, ignore return getType(state); } else { // Not surrounded by spaces, back up pointer stream.backUp(2); } } } } if (ch === ' ') { if (stream.match(/ +$/, false)) { state.trailingSpace++; } else if (state.trailingSpace) { state.trailingSpaceNewLine = true; } } return getType(state); } function linkInline(stream, state) { var ch = stream.next(); if (ch === ">") { state.f = state.inline = inlineNormal; if (modeCfg.highlightFormatting) state.formatting = "link"; var type = getType(state); if (type){ type += " "; } else { type = ""; } return type + tokenTypes.linkInline; } stream.match(/^[^>]+/, true); return tokenTypes.linkInline; } function linkHref(stream, state) { // Check if space, and return NULL if so (to avoid marking the space) if(stream.eatSpace()){ return null; } var ch = stream.next(); if (ch === '(' || ch === '[') { state.f = state.inline = getLinkHrefInside(ch === "(" ? ")" : "]", 0); if (modeCfg.highlightFormatting) state.formatting = "link-string"; state.linkHref = true; return getType(state); } return 'error'; } var linkRE = { ")": /^(?:[^\\\(\)]|\\.|\((?:[^\\\(\)]|\\.)*\))*?(?=\))/, "]": /^(?:[^\\\[\]]|\\.|\[(?:[^\\\[\\]]|\\.)*\])*?(?=\])/ } function getLinkHrefInside(endChar) { return function(stream, state) { var ch = stream.next(); if (ch === endChar) { state.f = state.inline = inlineNormal; if (modeCfg.highlightFormatting) state.formatting = "link-string"; var returnState = getType(state); state.linkHref = false; return returnState; } stream.match(linkRE[endChar]) state.linkHref = true; return getType(state); }; } function footnoteLink(stream, state) { if (stream.match(/^([^\]\\]|\\.)*\]:/, false)) { state.f = footnoteLinkInside; stream.next(); // Consume [ if (modeCfg.highlightFormatting) state.formatting = "link"; state.linkText = true; return getType(state); } return switchInline(stream, state, inlineNormal); } function footnoteLinkInside(stream, state) { if (stream.match(/^\]:/, true)) { state.f = state.inline = footnoteUrl; if (modeCfg.highlightFormatting) state.formatting = "link"; var returnType = getType(state); state.linkText = false; return returnType; } stream.match(/^([^\]\\]|\\.)+/, true); return tokenTypes.linkText; } function footnoteUrl(stream, state) { // Check if space, and return NULL if so (to avoid marking the space) if(stream.eatSpace()){ return null; } // Match URL stream.match(/^[^\s]+/, true); // Check for link title if (stream.peek() === undefined) { // End of line, set flag to check next line state.linkTitle = true; } else { // More content on line, check if link title stream.match(/^(?:\s+(?:"(?:[^"\\]|\\\\|\\.)+"|'(?:[^'\\]|\\\\|\\.)+'|\((?:[^)\\]|\\\\|\\.)+\)))?/, true); } state.f = state.inline = inlineNormal; return tokenTypes.linkHref + " url"; } var mode = { startState: function() { return { f: blockNormal, prevLine: null, thisLine: null, block: blockNormal, htmlState: null, indentation: 0, inline: inlineNormal, text: handleText, formatting: false, linkText: false, linkHref: false, linkTitle: false, code: 0, em: false, strong: false, header: 0, hr: false, taskList: false, list: false, listStack: [], quote: 0, trailingSpace: 0, trailingSpaceNewLine: false, strikethrough: false, fencedChars: null }; }, copyState: function(s) { return { f: s.f, prevLine: s.prevLine, thisLine: s.thisLine, block: s.block, htmlState: s.htmlState && CodeMirror.copyState(htmlMode, s.htmlState), indentation: s.indentation, localMode: s.localMode, localState: s.localMode ? CodeMirror.copyState(s.localMode, s.localState) : null, inline: s.inline, text: s.text, formatting: false, linkTitle: s.linkTitle, code: s.code, em: s.em, strong: s.strong, strikethrough: s.strikethrough, header: s.header, hr: s.hr, taskList: s.taskList, list: s.list, listStack: s.listStack.slice(0), quote: s.quote, indentedCode: s.indentedCode, trailingSpace: s.trailingSpace, trailingSpaceNewLine: s.trailingSpaceNewLine, md_inside: s.md_inside, fencedChars: s.fencedChars }; }, token: function(stream, state) { // Reset state.formatting state.formatting = false; if (stream != state.thisLine) { var forceBlankLine = state.header || state.hr; // Reset state.header and state.hr state.header = 0; state.hr = false; if (stream.match(/^\s*$/, true) || forceBlankLine) { blankLine(state); if (!forceBlankLine) return null state.prevLine = null } state.prevLine = state.thisLine state.thisLine = stream // Reset state.taskList state.taskList = false; // Reset state.trailingSpace state.trailingSpace = 0; state.trailingSpaceNewLine = false; state.f = state.block; var indentation = stream.match(/^\s*/, true)[0].replace(/\t/g, ' ').length; state.indentationDiff = Math.min(indentation - state.indentation, 4); state.indentation = state.indentation + state.indentationDiff; if (indentation > 0) return null; } return state.f(stream, state); }, innerMode: function(state) { if (state.block == htmlBlock) return {state: state.htmlState, mode: htmlMode}; if (state.localState) return {state: state.localState, mode: state.localMode}; return {state: state, mode: mode}; }, blankLine: blankLine, getType: getType, fold: "markdown" }; return mode; }, "xml"); CodeMirror.defineMIME("text/x-markdown", "markdown"); }); },{"../../lib/codemirror":10,"../meta":13,"../xml/xml":14}],13:[function(require,module,exports){ // CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: http://codemirror.net/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../lib/codemirror")); else if (typeof define == "function" && define.amd) // AMD define(["../lib/codemirror"], mod); else // Plain browser env mod(CodeMirror); })(function(CodeMirror) { "use strict"; CodeMirror.modeInfo = [ {name: "APL", mime: "text/apl", mode: "apl", ext: ["dyalog", "apl"]}, {name: "PGP", mimes: ["application/pgp", "application/pgp-keys", "application/pgp-signature"], mode: "asciiarmor", ext: ["pgp"]}, {name: "ASN.1", mime: "text/x-ttcn-asn", mode: "asn.1", ext: ["asn", "asn1"]}, {name: "Asterisk", mime: "text/x-asterisk", mode: "asterisk", file: /^extensions\.conf$/i}, {name: "Brainfuck", mime: "text/x-brainfuck", mode: "brainfuck", ext: ["b", "bf"]}, {name: "C", mime: "text/x-csrc", mode: "clike", ext: ["c", "h"]}, {name: "C++", mime: "text/x-c++src", mode: "clike", ext: ["cpp", "c++", "cc", "cxx", "hpp", "h++", "hh", "hxx"], alias: ["cpp"]}, {name: "Cobol", mime: "text/x-cobol", mode: "cobol", ext: ["cob", "cpy"]}, {name: "C#", mime: "text/x-csharp", mode: "clike", ext: ["cs"], alias: ["csharp"]}, {name: "Clojure", mime: "text/x-clojure", mode: "clojure", ext: ["clj", "cljc", "cljx"]}, {name: "ClojureScript", mime: "text/x-clojurescript", mode: "clojure", ext: ["cljs"]}, {name: "Closure Stylesheets (GSS)", mime: "text/x-gss", mode: "css", ext: ["gss"]}, {name: "CMake", mime: "text/x-cmake", mode: "cmake", ext: ["cmake", "cmake.in"], file: /^CMakeLists.txt$/}, {name: "CoffeeScript", mime: "text/x-coffeescript", mode: "coffeescript", ext: ["coffee"], alias: ["coffee", "coffee-script"]}, {name: "Common Lisp", mime: "text/x-common-lisp", mode: "commonlisp", ext: ["cl", "lisp", "el"], alias: ["lisp"]}, {name: "Cypher", mime: "application/x-cypher-query", mode: "cypher", ext: ["cyp", "cypher"]}, {name: "Cython", mime: "text/x-cython", mode: "python", ext: ["pyx", "pxd", "pxi"]}, {name: "Crystal", mime: "text/x-crystal", mode: "crystal", ext: ["cr"]}, {name: "CSS", mime: "text/css", mode: "css", ext: ["css"]}, {name: "CQL", mime: "text/x-cassandra", mode: "sql", ext: ["cql"]}, {name: "D", mime: "text/x-d", mode: "d", ext: ["d"]}, {name: "Dart", mimes: ["application/dart", "text/x-dart"], mode: "dart", ext: ["dart"]}, {name: "diff", mime: "text/x-diff", mode: "diff", ext: ["diff", "patch"]}, {name: "Django", mime: "text/x-django", mode: "django"}, {name: "Dockerfile", mime: "text/x-dockerfile", mode: "dockerfile", file: /^Dockerfile$/}, {name: "DTD", mime: "application/xml-dtd", mode: "dtd", ext: ["dtd"]}, {name: "Dylan", mime: "text/x-dylan", mode: "dylan", ext: ["dylan", "dyl", "intr"]}, {name: "EBNF", mime: "text/x-ebnf", mode: "ebnf"}, {name: "ECL", mime: "text/x-ecl", mode: "ecl", ext: ["ecl"]}, {name: "edn", mime: "application/edn", mode: "clojure", ext: ["edn"]}, {name: "Eiffel", mime: "text/x-eiffel", mode: "eiffel", ext: ["e"]}, {name: "Elm", mime: "text/x-elm", mode: "elm", ext: ["elm"]}, {name: "Embedded Javascript", mime: "application/x-ejs", mode: "htmlembedded", ext: ["ejs"]}, {name: "Embedded Ruby", mime: "application/x-erb", mode: "htmlembedded", ext: ["erb"]}, {name: "Erlang", mime: "text/x-erlang", mode: "erlang", ext: ["erl"]}, {name: "Factor", mime: "text/x-factor", mode: "factor", ext: ["factor"]}, {name: "FCL", mime: "text/x-fcl", mode: "fcl"}, {name: "Forth", mime: "text/x-forth", mode: "forth", ext: ["forth", "fth", "4th"]}, {name: "Fortran", mime: "text/x-fortran", mode: "fortran", ext: ["f", "for", "f77", "f90"]}, {name: "F#", mime: "text/x-fsharp", mode: "mllike", ext: ["fs"], alias: ["fsharp"]}, {name: "Gas", mime: "text/x-gas", mode: "gas", ext: ["s"]}, {name: "Gherkin", mime: "text/x-feature", mode: "gherkin", ext: ["feature"]}, {name: "GitHub Flavored Markdown", mime: "text/x-gfm", mode: "gfm", file: /^(readme|contributing|history).md$/i}, {name: "Go", mime: "text/x-go", mode: "go", ext: ["go"]}, {name: "Groovy", mime: "text/x-groovy", mode: "groovy", ext: ["groovy", "gradle"]}, {name: "HAML", mime: "text/x-haml", mode: "haml", ext: ["haml"]}, {name: "Haskell", mime: "text/x-haskell", mode: "haskell", ext: ["hs"]}, {name: "Haskell (Literate)", mime: "text/x-literate-haskell", mode: "haskell-literate", ext: ["lhs"]}, {name: "Haxe", mime: "text/x-haxe", mode: "haxe", ext: ["hx"]}, {name: "HXML", mime: "text/x-hxml", mode: "haxe", ext: ["hxml"]}, {name: "ASP.NET", mime: "application/x-aspx", mode: "htmlembedded", ext: ["aspx"], alias: ["asp", "aspx"]}, {name: "HTML", mime: "text/html", mode: "htmlmixed", ext: ["html", "htm"], alias: ["xhtml"]}, {name: "HTTP", mime: "message/http", mode: "http"}, {name: "IDL", mime: "text/x-idl", mode: "idl", ext: ["pro"]}, {name: "Jade", mime: "text/x-jade", mode: "jade", ext: ["jade"]}, {name: "Java", mime: "text/x-java", mode: "clike", ext: ["java"]}, {name: "Java Server Pages", mime: "application/x-jsp", mode: "htmlembedded", ext: ["jsp"], alias: ["jsp"]}, {name: "JavaScript", mimes: ["text/javascript", "text/ecmascript", "application/javascript", "application/x-javascript", "application/ecmascript"], mode: "javascript", ext: ["js"], alias: ["ecmascript", "js", "node"]}, {name: "JSON", mimes: ["application/json", "application/x-json"], mode: "javascript", ext: ["json", "map"], alias: ["json5"]}, {name: "JSON-LD", mime: "application/ld+json", mode: "javascript", ext: ["jsonld"], alias: ["jsonld"]}, {name: "JSX", mime: "text/jsx", mode: "jsx", ext: ["jsx"]}, {name: "Jinja2", mime: "null", mode: "jinja2"}, {name: "Julia", mime: "text/x-julia", mode: "julia", ext: ["jl"]}, {name: "Kotlin", mime: "text/x-kotlin", mode: "clike", ext: ["kt"]}, {name: "LESS", mime: "text/x-less", mode: "css", ext: ["less"]}, {name: "LiveScript", mime: "text/x-livescript", mode: "livescript", ext: ["ls"], alias: ["ls"]}, {name: "Lua", mime: "text/x-lua", mode: "lua", ext: ["lua"]}, {name: "Markdown", mime: "text/x-markdown", mode: "markdown", ext: ["markdown", "md", "mkd"]}, {name: "mIRC", mime: "text/mirc", mode: "mirc"}, {name: "MariaDB SQL", mime: "text/x-mariadb", mode: "sql"}, {name: "Mathematica", mime: "text/x-mathematica", mode: "mathematica", ext: ["m", "nb"]}, {name: "Modelica", mime: "text/x-modelica", mode: "modelica", ext: ["mo"]}, {name: "MUMPS", mime: "text/x-mumps", mode: "mumps", ext: ["mps"]}, {name: "MS SQL", mime: "text/x-mssql", mode: "sql"}, {name: "mbox", mime: "application/mbox", mode: "mbox", ext: ["mbox"]}, {name: "MySQL", mime: "text/x-mysql", mode: "sql"}, {name: "Nginx", mime: "text/x-nginx-conf", mode: "nginx", file: /nginx.*\.conf$/i}, {name: "NSIS", mime: "text/x-nsis", mode: "nsis", ext: ["nsh", "nsi"]}, {name: "NTriples", mime: "text/n-triples", mode: "ntriples", ext: ["nt"]}, {name: "Objective C", mime: "text/x-objectivec", mode: "clike", ext: ["m", "mm"], alias: ["objective-c", "objc"]}, {name: "OCaml", mime: "text/x-ocaml", mode: "mllike", ext: ["ml", "mli", "mll", "mly"]}, {name: "Octave", mime: "text/x-octave", mode: "octave", ext: ["m"]}, {name: "Oz", mime: "text/x-oz", mode: "oz", ext: ["oz"]}, {name: "Pascal", mime: "text/x-pascal", mode: "pascal", ext: ["p", "pas"]}, {name: "PEG.js", mime: "null", mode: "pegjs", ext: ["jsonld"]}, {name: "Perl", mime: "text/x-perl", mode: "perl", ext: ["pl", "pm"]}, {name: "PHP", mime: "application/x-httpd-php", mode: "php", ext: ["php", "php3", "php4", "php5", "phtml"]}, {name: "Pig", mime: "text/x-pig", mode: "pig", ext: ["pig"]}, {name: "Plain Text", mime: "text/plain", mode: "null", ext: ["txt", "text", "conf", "def", "list", "log"]}, {name: "PLSQL", mime: "text/x-plsql", mode: "sql", ext: ["pls"]}, {name: "PowerShell", mime: "application/x-powershell", mode: "powershell", ext: ["ps1", "psd1", "psm1"]}, {name: "Properties files", mime: "text/x-properties", mode: "properties", ext: ["properties", "ini", "in"], alias: ["ini", "properties"]}, {name: "ProtoBuf", mime: "text/x-protobuf", mode: "protobuf", ext: ["proto"]}, {name: "Python", mime: "text/x-python", mode: "python", ext: ["BUILD", "bzl", "py", "pyw"], file: /^(BUCK|BUILD)$/}, {name: "Puppet", mime: "text/x-puppet", mode: "puppet", ext: ["pp"]}, {name: "Q", mime: "text/x-q", mode: "q", ext: ["q"]}, {name: "R", mime: "text/x-rsrc", mode: "r", ext: ["r"], alias: ["rscript"]}, {name: "reStructuredText", mime: "text/x-rst", mode: "rst", ext: ["rst"], alias: ["rst"]}, {name: "RPM Changes", mime: "text/x-rpm-changes", mode: "rpm"}, {name: "RPM Spec", mime: "text/x-rpm-spec", mode: "rpm", ext: ["spec"]}, {name: "Ruby", mime: "text/x-ruby", mode: "ruby", ext: ["rb"], alias: ["jruby", "macruby", "rake", "rb", "rbx"]}, {name: "Rust", mime: "text/x-rustsrc", mode: "rust", ext: ["rs"]}, {name: "SAS", mime: "text/x-sas", mode: "sas", ext: ["sas"]}, {name: "Sass", mime: "text/x-sass", mode: "sass", ext: ["sass"]}, {name: "Scala", mime: "text/x-scala", mode: "clike", ext: ["scala"]}, {name: "Scheme", mime: "text/x-scheme", mode: "scheme", ext: ["scm", "ss"]}, {name: "SCSS", mime: "text/x-scss", mode: "css", ext: ["scss"]}, {name: "Shell", mime: "text/x-sh", mode: "shell", ext: ["sh", "ksh", "bash"], alias: ["bash", "sh", "zsh"], file: /^PKGBUILD$/}, {name: "Sieve", mime: "application/sieve", mode: "sieve", ext: ["siv", "sieve"]}, {name: "Slim", mimes: ["text/x-slim", "application/x-slim"], mode: "slim", ext: ["slim"]}, {name: "Smalltalk", mime: "text/x-stsrc", mode: "smalltalk", ext: ["st"]}, {name: "Smarty", mime: "text/x-smarty", mode: "smarty", ext: ["tpl"]}, {name: "Solr", mime: "text/x-solr", mode: "solr"}, {name: "Soy", mime: "text/x-soy", mode: "soy", ext: ["soy"], alias: ["closure template"]}, {name: "SPARQL", mime: "application/sparql-query", mode: "sparql", ext: ["rq", "sparql"], alias: ["sparul"]}, {name: "Spreadsheet", mime: "text/x-spreadsheet", mode: "spreadsheet", alias: ["excel", "formula"]}, {name: "SQL", mime: "text/x-sql", mode: "sql", ext: ["sql"]}, {name: "Squirrel", mime: "text/x-squirrel", mode: "clike", ext: ["nut"]}, {name: "Swift", mime: "text/x-swift", mode: "swift", ext: ["swift"]}, {name: "sTeX", mime: "text/x-stex", mode: "stex"}, {name: "LaTeX", mime: "text/x-latex", mode: "stex", ext: ["text", "ltx"], alias: ["tex"]}, {name: "SystemVerilog", mime: "text/x-systemverilog", mode: "verilog", ext: ["v"]}, {name: "Tcl", mime: "text/x-tcl", mode: "tcl", ext: ["tcl"]}, {name: "Textile", mime: "text/x-textile", mode: "textile", ext: ["textile"]}, {name: "TiddlyWiki ", mime: "text/x-tiddlywiki", mode: "tiddlywiki"}, {name: "Tiki wiki", mime: "text/tiki", mode: "tiki"}, {name: "TOML", mime: "text/x-toml", mode: "toml", ext: ["toml"]}, {name: "Tornado", mime: "text/x-tornado", mode: "tornado"}, {name: "troff", mime: "text/troff", mode: "troff", ext: ["1", "2", "3", "4", "5", "6", "7", "8", "9"]}, {name: "TTCN", mime: "text/x-ttcn", mode: "ttcn", ext: ["ttcn", "ttcn3", "ttcnpp"]}, {name: "TTCN_CFG", mime: "text/x-ttcn-cfg", mode: "ttcn-cfg", ext: ["cfg"]}, {name: "Turtle", mime: "text/turtle", mode: "turtle", ext: ["ttl"]}, {name: "TypeScript", mime: "application/typescript", mode: "javascript", ext: ["ts"], alias: ["ts"]}, {name: "Twig", mime: "text/x-twig", mode: "twig"}, {name: "Web IDL", mime: "text/x-webidl", mode: "webidl", ext: ["webidl"]}, {name: "VB.NET", mime: "text/x-vb", mode: "vb", ext: ["vb"]}, {name: "VBScript", mime: "text/vbscript", mode: "vbscript", ext: ["vbs"]}, {name: "Velocity", mime: "text/velocity", mode: "velocity", ext: ["vtl"]}, {name: "Verilog", mime: "text/x-verilog", mode: "verilog", ext: ["v"]}, {name: "VHDL", mime: "text/x-vhdl", mode: "vhdl", ext: ["vhd", "vhdl"]}, {name: "XML", mimes: ["application/xml", "text/xml"], mode: "xml", ext: ["xml", "xsl", "xsd"], alias: ["rss", "wsdl", "xsd"]}, {name: "XQuery", mime: "application/xquery", mode: "xquery", ext: ["xy", "xquery"]}, {name: "Yacas", mime: "text/x-yacas", mode: "yacas", ext: ["ys"]}, {name: "YAML", mime: "text/x-yaml", mode: "yaml", ext: ["yaml", "yml"], alias: ["yml"]}, {name: "Z80", mime: "text/x-z80", mode: "z80", ext: ["z80"]}, {name: "mscgen", mime: "text/x-mscgen", mode: "mscgen", ext: ["mscgen", "mscin", "msc"]}, {name: "xu", mime: "text/x-xu", mode: "mscgen", ext: ["xu"]}, {name: "msgenny", mime: "text/x-msgenny", mode: "mscgen", ext: ["msgenny"]} ]; // Ensure all modes have a mime property for backwards compatibility for (var i = 0; i < CodeMirror.modeInfo.length; i++) { var info = CodeMirror.modeInfo[i]; if (info.mimes) info.mime = info.mimes[0]; } CodeMirror.findModeByMIME = function(mime) { mime = mime.toLowerCase(); for (var i = 0; i < CodeMirror.modeInfo.length; i++) { var info = CodeMirror.modeInfo[i]; if (info.mime == mime) return info; if (info.mimes) for (var j = 0; j < info.mimes.length; j++) if (info.mimes[j] == mime) return info; } }; CodeMirror.findModeByExtension = function(ext) { for (var i = 0; i < CodeMirror.modeInfo.length; i++) { var info = CodeMirror.modeInfo[i]; if (info.ext) for (var j = 0; j < info.ext.length; j++) if (info.ext[j] == ext) return info; } }; CodeMirror.findModeByFileName = function(filename) { for (var i = 0; i < CodeMirror.modeInfo.length; i++) { var info = CodeMirror.modeInfo[i]; if (info.file && info.file.test(filename)) return info; } var dot = filename.lastIndexOf("."); var ext = dot > -1 && filename.substring(dot + 1, filename.length); if (ext) return CodeMirror.findModeByExtension(ext); }; CodeMirror.findModeByName = function(name) { name = name.toLowerCase(); for (var i = 0; i < CodeMirror.modeInfo.length; i++) { var info = CodeMirror.modeInfo[i]; if (info.name.toLowerCase() == name) return info; if (info.alias) for (var j = 0; j < info.alias.length; j++) if (info.alias[j].toLowerCase() == name) return info; } }; }); },{"../lib/codemirror":10}],14:[function(require,module,exports){ // CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: http://codemirror.net/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); else if (typeof define == "function" && define.amd) // AMD define(["../../lib/codemirror"], mod); else // Plain browser env mod(CodeMirror); })(function(CodeMirror) { "use strict"; var htmlConfig = { autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true, 'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true, 'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true, 'track': true, 'wbr': true, 'menuitem': true}, implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true, 'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true, 'th': true, 'tr': true}, contextGrabbers: { 'dd': {'dd': true, 'dt': true}, 'dt': {'dd': true, 'dt': true}, 'li': {'li': true}, 'option': {'option': true, 'optgroup': true}, 'optgroup': {'optgroup': true}, 'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true, 'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true, 'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true, 'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true, 'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true}, 'rp': {'rp': true, 'rt': true}, 'rt': {'rp': true, 'rt': true}, 'tbody': {'tbody': true, 'tfoot': true}, 'td': {'td': true, 'th': true}, 'tfoot': {'tbody': true}, 'th': {'td': true, 'th': true}, 'thead': {'tbody': true, 'tfoot': true}, 'tr': {'tr': true} }, doNotIndent: {"pre": true}, allowUnquoted: true, allowMissing: true, caseFold: true } var xmlConfig = { autoSelfClosers: {}, implicitlyClosed: {}, contextGrabbers: {}, doNotIndent: {}, allowUnquoted: false, allowMissing: false, caseFold: false } CodeMirror.defineMode("xml", function(editorConf, config_) { var indentUnit = editorConf.indentUnit var config = {} var defaults = config_.htmlMode ? htmlConfig : xmlConfig for (var prop in defaults) config[prop] = defaults[prop] for (var prop in config_) config[prop] = config_[prop] // Return variables for tokenizers var type, setStyle; function inText(stream, state) { function chain(parser) { state.tokenize = parser; return parser(stream, state); } var ch = stream.next(); if (ch == "<") { if (stream.eat("!")) { if (stream.eat("[")) { if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>")); else return null; } else if (stream.match("--")) { return chain(inBlock("comment", "-->")); } else if (stream.match("DOCTYPE", true, true)) { stream.eatWhile(/[\w\._\-]/); return chain(doctype(1)); } else { return null; } } else if (stream.eat("?")) { stream.eatWhile(/[\w\._\-]/); state.tokenize = inBlock("meta", "?>"); return "meta"; } else { type = stream.eat("/") ? "closeTag" : "openTag"; state.tokenize = inTag; return "tag bracket"; } } else if (ch == "&") { var ok; if (stream.eat("#")) { if (stream.eat("x")) { ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";"); } else { ok = stream.eatWhile(/[\d]/) && stream.eat(";"); } } else { ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";"); } return ok ? "atom" : "error"; } else { stream.eatWhile(/[^&<]/); return null; } } inText.isInText = true; function inTag(stream, state) { var ch = stream.next(); if (ch == ">" || (ch == "/" && stream.eat(">"))) { state.tokenize = inText; type = ch == ">" ? "endTag" : "selfcloseTag"; return "tag bracket"; } else if (ch == "=") { type = "equals"; return null; } else if (ch == "<") { state.tokenize = inText; state.state = baseState; state.tagName = state.tagStart = null; var next = state.tokenize(stream, state); return next ? next + " tag error" : "tag error"; } else if (/[\'\"]/.test(ch)) { state.tokenize = inAttribute(ch); state.stringStartCol = stream.column(); return state.tokenize(stream, state); } else { stream.match(/^[^\s\u00a0=<>\"\']*[^\s\u00a0=<>\"\'\/]/); return "word"; } } function inAttribute(quote) { var closure = function(stream, state) { while (!stream.eol()) { if (stream.next() == quote) { state.tokenize = inTag; break; } } return "string"; }; closure.isInAttribute = true; return closure; } function inBlock(style, terminator) { return function(stream, state) { while (!stream.eol()) { if (stream.match(terminator)) { state.tokenize = inText; break; } stream.next(); } return style; }; } function doctype(depth) { return function(stream, state) { var ch; while ((ch = stream.next()) != null) { if (ch == "<") { state.tokenize = doctype(depth + 1); return state.tokenize(stream, state); } else if (ch == ">") { if (depth == 1) { state.tokenize = inText; break; } else { state.tokenize = doctype(depth - 1); return state.tokenize(stream, state); } } } return "meta"; }; } function Context(state, tagName, startOfLine) { this.prev = state.context; this.tagName = tagName; this.indent = state.indented; this.startOfLine = startOfLine; if (config.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent)) this.noIndent = true; } function popContext(state) { if (state.context) state.context = state.context.prev; } function maybePopContext(state, nextTagName) { var parentTagName; while (true) { if (!state.context) { return; } parentTagName = state.context.tagName; if (!config.contextGrabbers.hasOwnProperty(parentTagName) || !config.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) { return; } popContext(state); } } function baseState(type, stream, state) { if (type == "openTag") { state.tagStart = stream.column(); return tagNameState; } else if (type == "closeTag") { return closeTagNameState; } else { return baseState; } } function tagNameState(type, stream, state) { if (type == "word") { state.tagName = stream.current(); setStyle = "tag"; return attrState; } else { setStyle = "error"; return tagNameState; } } function closeTagNameState(type, stream, state) { if (type == "word") { var tagName = stream.current(); if (state.context && state.context.tagName != tagName && config.implicitlyClosed.hasOwnProperty(state.context.tagName)) popContext(state); if ((state.context && state.context.tagName == tagName) || config.matchClosing === false) { setStyle = "tag"; return closeState; } else { setStyle = "tag error"; return closeStateErr; } } else { setStyle = "error"; return closeStateErr; } } function closeState(type, _stream, state) { if (type != "endTag") { setStyle = "error"; return closeState; } popContext(state); return baseState; } function closeStateErr(type, stream, state) { setStyle = "error"; return closeState(type, stream, state); } function attrState(type, _stream, state) { if (type == "word") { setStyle = "attribute"; return attrEqState; } else if (type == "endTag" || type == "selfcloseTag") { var tagName = state.tagName, tagStart = state.tagStart; state.tagName = state.tagStart = null; if (type == "selfcloseTag" || config.autoSelfClosers.hasOwnProperty(tagName)) { maybePopContext(state, tagName); } else { maybePopContext(state, tagName); state.context = new Context(state, tagName, tagStart == state.indented); } return baseState; } setStyle = "error"; return attrState; } function attrEqState(type, stream, state) { if (type == "equals") return attrValueState; if (!config.allowMissing) setStyle = "error"; return attrState(type, stream, state); } function attrValueState(type, stream, state) { if (type == "string") return attrContinuedState; if (type == "word" && config.allowUnquoted) {setStyle = "string"; return attrState;} setStyle = "error"; return attrState(type, stream, state); } function attrContinuedState(type, stream, state) { if (type == "string") return attrContinuedState; return attrState(type, stream, state); } return { startState: function(baseIndent) { var state = {tokenize: inText, state: baseState, indented: baseIndent || 0, tagName: null, tagStart: null, context: null} if (baseIndent != null) state.baseIndent = baseIndent return state }, token: function(stream, state) { if (!state.tagName && stream.sol()) state.indented = stream.indentation(); if (stream.eatSpace()) return null; type = null; var style = state.tokenize(stream, state); if ((style || type) && style != "comment") { setStyle = null; state.state = state.state(type || style, stream, state); if (setStyle) style = setStyle == "error" ? style + " error" : setStyle; } return style; }, indent: function(state, textAfter, fullLine) { var context = state.context; // Indent multi-line strings (e.g. css). if (state.tokenize.isInAttribute) { if (state.tagStart == state.indented) return state.stringStartCol + 1; else return state.indented + indentUnit; } if (context && context.noIndent) return CodeMirror.Pass; if (state.tokenize != inTag && state.tokenize != inText) return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0; // Indent the starts of attribute names. if (state.tagName) { if (config.multilineTagIndentPastTag !== false) return state.tagStart + state.tagName.length + 2; else return state.tagStart + indentUnit * (config.multilineTagIndentFactor || 1); } if (config.alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0; var tagAfter = textAfter && /^<(\/)?([\w_:\.-]*)/.exec(textAfter); if (tagAfter && tagAfter[1]) { // Closing tag spotted while (context) { if (context.tagName == tagAfter[2]) { context = context.prev; break; } else if (config.implicitlyClosed.hasOwnProperty(context.tagName)) { context = context.prev; } else { break; } } } else if (tagAfter) { // Opening tag spotted while (context) { var grabbers = config.contextGrabbers[context.tagName]; if (grabbers && grabbers.hasOwnProperty(tagAfter[2])) context = context.prev; else break; } } while (context && context.prev && !context.startOfLine) context = context.prev; if (context) return context.indent + indentUnit; else return state.baseIndent || 0; }, electricInput: /<\/[\s\w:]+>$/, blockCommentStart: "<!--", blockCommentEnd: "-->", configuration: config.htmlMode ? "html" : "xml", helperType: config.htmlMode ? "html" : "xml", skipAttribute: function(state) { if (state.state == attrValueState) state.state = attrState } }; }); CodeMirror.defineMIME("text/xml", "xml"); CodeMirror.defineMIME("application/xml", "xml"); if (!CodeMirror.mimeModes.hasOwnProperty("text/html")) CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true}); }); },{"../../lib/codemirror":10}],15:[function(require,module,exports){ exports.read = function (buffer, offset, isLE, mLen, nBytes) { var e, m var eLen = nBytes * 8 - mLen - 1 var eMax = (1 << eLen) - 1 var eBias = eMax >> 1 var nBits = -7 var i = isLE ? (nBytes - 1) : 0 var d = isLE ? -1 : 1 var s = buffer[offset + i] i += d e = s & ((1 << (-nBits)) - 1) s >>= (-nBits) nBits += eLen for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8) {} m = e & ((1 << (-nBits)) - 1) e >>= (-nBits) nBits += mLen for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8) {} if (e === 0) { e = 1 - eBias } else if (e === eMax) { return m ? NaN : ((s ? -1 : 1) * Infinity) } else { m = m + Math.pow(2, mLen) e = e - eBias } return (s ? -1 : 1) * m * Math.pow(2, e - mLen) } exports.write = function (buffer, value, offset, isLE, mLen, nBytes) { var e, m, c var eLen = nBytes * 8 - mLen - 1 var eMax = (1 << eLen) - 1 var eBias = eMax >> 1 var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0) var i = isLE ? 0 : (nBytes - 1) var d = isLE ? 1 : -1 var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0 value = Math.abs(value) if (isNaN(value) || value === Infinity) { m = isNaN(value) ? 1 : 0 e = eMax } else { e = Math.floor(Math.log(value) / Math.LN2) if (value * (c = Math.pow(2, -e)) < 1) { e-- c *= 2 } if (e + eBias >= 1) { value += rt / c } else { value += rt * Math.pow(2, 1 - eBias) } if (value * c >= 2) { e++ c /= 2 } if (e + eBias >= eMax) { m = 0 e = eMax } else if (e + eBias >= 1) { m = (value * c - 1) * Math.pow(2, mLen) e = e + eBias } else { m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen) e = 0 } } for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {} e = (e << mLen) | m eLen += mLen for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {} buffer[offset + i - d] |= s * 128 } },{}],16:[function(require,module,exports){ var toString = {}.toString; module.exports = Array.isArray || function (arr) { return toString.call(arr) == '[object Array]'; }; },{}],17:[function(require,module,exports){ (function (global){ /** * marked - a markdown parser * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed) * https://github.com/chjj/marked */ ;(function() { /** * Block-Level Grammar */ var block = { newline: /^\n+/, code: /^( {4}[^\n]+\n*)+/, fences: noop, hr: /^( *[-*_]){3,} *(?:\n+|$)/, heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/, nptable: noop, lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/, blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/, list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/, html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/, def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/, table: noop, paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/, text: /^[^\n]+/ }; block.bullet = /(?:[*+-]|\d+\.)/; block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/; block.item = replace(block.item, 'gm') (/bull/g, block.bullet) (); block.list = replace(block.list) (/bull/g, block.bullet) ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))') ('def', '\\n+(?=' + block.def.source + ')') (); block.blockquote = replace(block.blockquote) ('def', block.def) (); block._tag = '(?!(?:' + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code' + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo' + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b'; block.html = replace(block.html) ('comment', /<!--[\s\S]*?-->/) ('closed', /<(tag)[\s\S]+?<\/\1>/) ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/) (/tag/g, block._tag) (); block.paragraph = replace(block.paragraph) ('hr', block.hr) ('heading', block.heading) ('lheading', block.lheading) ('blockquote', block.blockquote) ('tag', '<' + block._tag) ('def', block.def) (); /** * Normal Block Grammar */ block.normal = merge({}, block); /** * GFM Block Grammar */ block.gfm = merge({}, block.normal, { fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/, paragraph: /^/, heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/ }); block.gfm.paragraph = replace(block.paragraph) ('(?!', '(?!' + block.gfm.fences.source.replace('\\1', '\\2') + '|' + block.list.source.replace('\\1', '\\3') + '|') (); /** * GFM + Tables Block Grammar */ block.tables = merge({}, block.gfm, { nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/, table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/ }); /** * Block Lexer */ function Lexer(options) { this.tokens = []; this.tokens.links = {}; this.options = options || marked.defaults; this.rules = block.normal; if (this.options.gfm) { if (this.options.tables) { this.rules = block.tables; } else { this.rules = block.gfm; } } } /** * Expose Block Rules */ Lexer.rules = block; /** * Static Lex Method */ Lexer.lex = function(src, options) { var lexer = new Lexer(options); return lexer.lex(src); }; /** * Preprocessing */ Lexer.prototype.lex = function(src) { src = src .replace(/\r\n|\r/g, '\n') .replace(/\t/g, ' ') .replace(/\u00a0/g, ' ') .replace(/\u2424/g, '\n'); return this.token(src, true); }; /** * Lexing */ Lexer.prototype.token = function(src, top, bq) { var src = src.replace(/^ +$/gm, '') , next , loose , cap , bull , b , item , space , i , l; while (src) { // newline if (cap = this.rules.newline.exec(src)) { src = src.substring(cap[0].length); if (cap[0].length > 1) { this.tokens.push({ type: 'space' }); } } // code if (cap = this.rules.code.exec(src)) { src = src.substring(cap[0].length); cap = cap[0].replace(/^ {4}/gm, ''); this.tokens.push({ type: 'code', text: !this.options.pedantic ? cap.replace(/\n+$/, '') : cap }); continue; } // fences (gfm) if (cap = this.rules.fences.exec(src)) { src = src.substring(cap[0].length); this.tokens.push({ type: 'code', lang: cap[2], text: cap[3] || '' }); continue; } // heading if (cap = this.rules.heading.exec(src)) { src = src.substring(cap[0].length); this.tokens.push({ type: 'heading', depth: cap[1].length, text: cap[2] }); continue; } // table no leading pipe (gfm) if (top && (cap = this.rules.nptable.exec(src))) { src = src.substring(cap[0].length); item = { type: 'table', header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */), align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), cells: cap[3].replace(/\n$/, '').split('\n') }; for (i = 0; i < item.align.length; i++) { if (/^ *-+: *$/.test(item.align[i])) { item.align[i] = 'right'; } else if (/^ *:-+: *$/.test(item.align[i])) { item.align[i] = 'center'; } else if (/^ *:-+ *$/.test(item.align[i])) { item.align[i] = 'left'; } else { item.align[i] = null; } } for (i = 0; i < item.cells.length; i++) { item.cells[i] = item.cells[i].split(/ *\| */); } this.tokens.push(item); continue; } // lheading if (cap = this.rules.lheading.exec(src)) { src = src.substring(cap[0].length); this.tokens.push({ type: 'heading', depth: cap[2] === '=' ? 1 : 2, text: cap[1] }); continue; } // hr if (cap = this.rules.hr.exec(src)) { src = src.substring(cap[0].length); this.tokens.push({ type: 'hr' }); continue; } // blockquote if (cap = this.rules.blockquote.exec(src)) { src = src.substring(cap[0].length); this.tokens.push({ type: 'blockquote_start' }); cap = cap[0].replace(/^ *> ?/gm, ''); // Pass `top` to keep the current // "toplevel" state. This is exactly // how markdown.pl works. this.token(cap, top, true); this.tokens.push({ type: 'blockquote_end' }); continue; } // list if (cap = this.rules.list.exec(src)) { src = src.substring(cap[0].length); bull = cap[2]; this.tokens.push({ type: 'list_start', ordered: bull.length > 1 }); // Get each top-level item. cap = cap[0].match(this.rules.item); next = false; l = cap.length; i = 0; for (; i < l; i++) { item = cap[i]; // Remove the list item's bullet // so it is seen as the next token. space = item.length; item = item.replace(/^ *([*+-]|\d+\.) +/, ''); // Outdent whatever the // list item contains. Hacky. if (~item.indexOf('\n ')) { space -= item.length; item = !this.options.pedantic ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '') : item.replace(/^ {1,4}/gm, ''); } // Determine whether the next list item belongs here. // Backpedal if it does not belong in this list. if (this.options.smartLists && i !== l - 1) { b = block.bullet.exec(cap[i + 1])[0]; if (bull !== b && !(bull.length > 1 && b.length > 1)) { src = cap.slice(i + 1).join('\n') + src; i = l - 1; } } // Determine whether item is loose or not. // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/ // for discount behavior. loose = next || /\n\n(?!\s*$)/.test(item); if (i !== l - 1) { next = item.charAt(item.length - 1) === '\n'; if (!loose) loose = next; } this.tokens.push({ type: loose ? 'loose_item_start' : 'list_item_start' }); // Recurse. this.token(item, false, bq); this.tokens.push({ type: 'list_item_end' }); } this.tokens.push({ type: 'list_end' }); continue; } // html if (cap = this.rules.html.exec(src)) { src = src.substring(cap[0].length); this.tokens.push({ type: this.options.sanitize ? 'paragraph' : 'html', pre: !this.options.sanitizer && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'), text: cap[0] }); continue; } // def if ((!bq && top) && (cap = this.rules.def.exec(src))) { src = src.substring(cap[0].length); this.tokens.links[cap[1].toLowerCase()] = { href: cap[2], title: cap[3] }; continue; } // table (gfm) if (top && (cap = this.rules.table.exec(src))) { src = src.substring(cap[0].length); item = { type: 'table', header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */), align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n') }; for (i = 0; i < item.align.length; i++) { if (/^ *-+: *$/.test(item.align[i])) { item.align[i] = 'right'; } else if (/^ *:-+: *$/.test(item.align[i])) { item.align[i] = 'center'; } else if (/^ *:-+ *$/.test(item.align[i])) { item.align[i] = 'left'; } else { item.align[i] = null; } } for (i = 0; i < item.cells.length; i++) { item.cells[i] = item.cells[i] .replace(/^ *\| *| *\| *$/g, '') .split(/ *\| */); } this.tokens.push(item); continue; } // top-level paragraph if (top && (cap = this.rules.paragraph.exec(src))) { src = src.substring(cap[0].length); this.tokens.push({ type: 'paragraph', text: cap[1].charAt(cap[1].length - 1) === '\n' ? cap[1].slice(0, -1) : cap[1] }); continue; } // text if (cap = this.rules.text.exec(src)) { // Top-level should never reach here. src = src.substring(cap[0].length); this.tokens.push({ type: 'text', text: cap[0] }); continue; } if (src) { throw new Error('Infinite loop on byte: ' + src.charCodeAt(0)); } } return this.tokens; }; /** * Inline-Level Grammar */ var inline = { escape: /^\\([\\`*{}\[\]()#+\-.!_>])/, autolink: /^<([^ >]+(@|:\/)[^ >]+)>/, url: noop, tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/, link: /^!?\[(inside)\]\(href\)/, reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/, nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/, strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/, em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/, code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/, br: /^ {2,}\n(?!\s*$)/, del: noop, text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/ }; inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/; inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/; inline.link = replace(inline.link) ('inside', inline._inside) ('href', inline._href) (); inline.reflink = replace(inline.reflink) ('inside', inline._inside) (); /** * Normal Inline Grammar */ inline.normal = merge({}, inline); /** * Pedantic Inline Grammar */ inline.pedantic = merge({}, inline.normal, { strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/, em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/ }); /** * GFM Inline Grammar */ inline.gfm = merge({}, inline.normal, { escape: replace(inline.escape)('])', '~|])')(), url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/, del: /^~~(?=\S)([\s\S]*?\S)~~/, text: replace(inline.text) (']|', '~]|') ('|', '|https?://|') () }); /** * GFM + Line Breaks Inline Grammar */ inline.breaks = merge({}, inline.gfm, { br: replace(inline.br)('{2,}', '*')(), text: replace(inline.gfm.text)('{2,}', '*')() }); /** * Inline Lexer & Compiler */ function InlineLexer(links, options) { this.options = options || marked.defaults; this.links = links; this.rules = inline.normal; this.renderer = this.options.renderer || new Renderer; this.renderer.options = this.options; if (!this.links) { throw new Error('Tokens array requires a `links` property.'); } if (this.options.gfm) { if (this.options.breaks) { this.rules = inline.breaks; } else { this.rules = inline.gfm; } } else if (this.options.pedantic) { this.rules = inline.pedantic; } } /** * Expose Inline Rules */ InlineLexer.rules = inline; /** * Static Lexing/Compiling Method */ InlineLexer.output = function(src, links, options) { var inline = new InlineLexer(links, options); return inline.output(src); }; /** * Lexing/Compiling */ InlineLexer.prototype.output = function(src) { var out = '' , link , text , href , cap; while (src) { // escape if (cap = this.rules.escape.exec(src)) { src = src.substring(cap[0].length); out += cap[1]; continue; } // autolink if (cap = this.rules.autolink.exec(src)) { src = src.substring(cap[0].length); if (cap[2] === '@') { text = cap[1].charAt(6) === ':' ? this.mangle(cap[1].substring(7)) : this.mangle(cap[1]); href = this.mangle('mailto:') + text; } else { text = escape(cap[1]); href = text; } out += this.renderer.link(href, null, text); continue; } // url (gfm) if (!this.inLink && (cap = this.rules.url.exec(src))) { src = src.substring(cap[0].length); text = escape(cap[1]); href = text; out += this.renderer.link(href, null, text); continue; } // tag if (cap = this.rules.tag.exec(src)) { if (!this.inLink && /^<a /i.test(cap[0])) { this.inLink = true; } else if (this.inLink && /^<\/a>/i.test(cap[0])) { this.inLink = false; } src = src.substring(cap[0].length); out += this.options.sanitize ? this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape(cap[0]) : cap[0] continue; } // link if (cap = this.rules.link.exec(src)) { src = src.substring(cap[0].length); this.inLink = true; out += this.outputLink(cap, { href: cap[2], title: cap[3] }); this.inLink = false; continue; } // reflink, nolink if ((cap = this.rules.reflink.exec(src)) || (cap = this.rules.nolink.exec(src))) { src = src.substring(cap[0].length); link = (cap[2] || cap[1]).replace(/\s+/g, ' '); link = this.links[link.toLowerCase()]; if (!link || !link.href) { out += cap[0].charAt(0); src = cap[0].substring(1) + src; continue; } this.inLink = true; out += this.outputLink(cap, link); this.inLink = false; continue; } // strong if (cap = this.rules.strong.exec(src)) { src = src.substring(cap[0].length); out += this.renderer.strong(this.output(cap[2] || cap[1])); continue; } // em if (cap = this.rules.em.exec(src)) { src = src.substring(cap[0].length); out += this.renderer.em(this.output(cap[2] || cap[1])); continue; } // code if (cap = this.rules.code.exec(src)) { src = src.substring(cap[0].length); out += this.renderer.codespan(escape(cap[2], true)); continue; } // br if (cap = this.rules.br.exec(src)) { src = src.substring(cap[0].length); out += this.renderer.br(); continue; } // del (gfm) if (cap = this.rules.del.exec(src)) { src = src.substring(cap[0].length); out += this.renderer.del(this.output(cap[1])); continue; } // text if (cap = this.rules.text.exec(src)) { src = src.substring(cap[0].length); out += this.renderer.text(escape(this.smartypants(cap[0]))); continue; } if (src) { throw new Error('Infinite loop on byte: ' + src.charCodeAt(0)); } } return out; }; /** * Compile Link */ InlineLexer.prototype.outputLink = function(cap, link) { var href = escape(link.href) , title = link.title ? escape(link.title) : null; return cap[0].charAt(0) !== '!' ? this.renderer.link(href, title, this.output(cap[1])) : this.renderer.image(href, title, escape(cap[1])); }; /** * Smartypants Transformations */ InlineLexer.prototype.smartypants = function(text) { if (!this.options.smartypants) return text; return text // em-dashes .replace(/---/g, '\u2014') // en-dashes .replace(/--/g, '\u2013') // opening singles .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018') // closing singles & apostrophes .replace(/'/g, '\u2019') // opening doubles .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c') // closing doubles .replace(/"/g, '\u201d') // ellipses .replace(/\.{3}/g, '\u2026'); }; /** * Mangle Links */ InlineLexer.prototype.mangle = function(text) { if (!this.options.mangle) return text; var out = '' , l = text.length , i = 0 , ch; for (; i < l; i++) { ch = text.charCodeAt(i); if (Math.random() > 0.5) { ch = 'x' + ch.toString(16); } out += '&#' + ch + ';'; } return out; }; /** * Renderer */ function Renderer(options) { this.options = options || {}; } Renderer.prototype.code = function(code, lang, escaped) { if (this.options.highlight) { var out = this.options.highlight(code, lang); if (out != null && out !== code) { escaped = true; code = out; } } if (!lang) { return '<pre><code>' + (escaped ? code : escape(code, true)) + '\n</code></pre>'; } return '<pre><code class="' + this.options.langPrefix + escape(lang, true) + '">' + (escaped ? code : escape(code, true)) + '\n</code></pre>\n'; }; Renderer.prototype.blockquote = function(quote) { return '<blockquote>\n' + quote + '</blockquote>\n'; }; Renderer.prototype.html = function(html) { return html; }; Renderer.prototype.heading = function(text, level, raw) { return '<h' + level + ' id="' + this.options.headerPrefix + raw.toLowerCase().replace(/[^\w]+/g, '-') + '">' + text + '</h' + level + '>\n'; }; Renderer.prototype.hr = function() { return this.options.xhtml ? '<hr/>\n' : '<hr>\n'; }; Renderer.prototype.list = function(body, ordered) { var type = ordered ? 'ol' : 'ul'; return '<' + type + '>\n' + body + '</' + type + '>\n'; }; Renderer.prototype.listitem = function(text) { return '<li>' + text + '</li>\n'; }; Renderer.prototype.paragraph = function(text) { return '<p>' + text + '</p>\n'; }; Renderer.prototype.table = function(header, body) { return '<table>\n' + '<thead>\n' + header + '</thead>\n' + '<tbody>\n' + body + '</tbody>\n' + '</table>\n'; }; Renderer.prototype.tablerow = function(content) { return '<tr>\n' + content + '</tr>\n'; }; Renderer.prototype.tablecell = function(content, flags) { var type = flags.header ? 'th' : 'td'; var tag = flags.align ? '<' + type + ' style="text-align:' + flags.align + '">' : '<' + type + '>'; return tag + content + '</' + type + '>\n'; }; // span level renderer Renderer.prototype.strong = function(text) { return '<strong>' + text + '</strong>'; }; Renderer.prototype.em = function(text) { return '<em>' + text + '</em>'; }; Renderer.prototype.codespan = function(text) { return '<code>' + text + '</code>'; }; Renderer.prototype.br = function() { return this.options.xhtml ? '<br/>' : '<br>'; }; Renderer.prototype.del = function(text) { return '<del>' + text + '</del>'; }; Renderer.prototype.link = function(href, title, text) { if (this.options.sanitize) { try { var prot = decodeURIComponent(unescape(href)) .replace(/[^\w:]/g, '') .toLowerCase(); } catch (e) { return ''; } if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) { return ''; } } var out = '<a href="' + href + '"'; if (title) { out += ' title="' + title + '"'; } out += '>' + text + '</a>'; return out; }; Renderer.prototype.image = function(href, title, text) { var out = '<img src="' + href + '" alt="' + text + '"'; if (title) { out += ' title="' + title + '"'; } out += this.options.xhtml ? '/>' : '>'; return out; }; Renderer.prototype.text = function(text) { return text; }; /** * Parsing & Compiling */ function Parser(options) { this.tokens = []; this.token = null; this.options = options || marked.defaults; this.options.renderer = this.options.renderer || new Renderer; this.renderer = this.options.renderer; this.renderer.options = this.options; } /** * Static Parse Method */ Parser.parse = function(src, options, renderer) { var parser = new Parser(options, renderer); return parser.parse(src); }; /** * Parse Loop */ Parser.prototype.parse = function(src) { this.inline = new InlineLexer(src.links, this.options, this.renderer); this.tokens = src.reverse(); var out = ''; while (this.next()) { out += this.tok(); } return out; }; /** * Next Token */ Parser.prototype.next = function() { return this.token = this.tokens.pop(); }; /** * Preview Next Token */ Parser.prototype.peek = function() { return this.tokens[this.tokens.length - 1] || 0; }; /** * Parse Text Tokens */ Parser.prototype.parseText = function() { var body = this.token.text; while (this.peek().type === 'text') { body += '\n' + this.next().text; } return this.inline.output(body); }; /** * Parse Current Token */ Parser.prototype.tok = function() { switch (this.token.type) { case 'space': { return ''; } case 'hr': { return this.renderer.hr(); } case 'heading': { return this.renderer.heading( this.inline.output(this.token.text), this.token.depth, this.token.text); } case 'code': { return this.renderer.code(this.token.text, this.token.lang, this.token.escaped); } case 'table': { var header = '' , body = '' , i , row , cell , flags , j; // header cell = ''; for (i = 0; i < this.token.header.length; i++) { flags = { header: true, align: this.token.align[i] }; cell += this.renderer.tablecell( this.inline.output(this.token.header[i]), { header: true, align: this.token.align[i] } ); } header += this.renderer.tablerow(cell); for (i = 0; i < this.token.cells.length; i++) { row = this.token.cells[i]; cell = ''; for (j = 0; j < row.length; j++) { cell += this.renderer.tablecell( this.inline.output(row[j]), { header: false, align: this.token.align[j] } ); } body += this.renderer.tablerow(cell); } return this.renderer.table(header, body); } case 'blockquote_start': { var body = ''; while (this.next().type !== 'blockquote_end') { body += this.tok(); } return this.renderer.blockquote(body); } case 'list_start': { var body = '' , ordered = this.token.ordered; while (this.next().type !== 'list_end') { body += this.tok(); } return this.renderer.list(body, ordered); } case 'list_item_start': { var body = ''; while (this.next().type !== 'list_item_end') { body += this.token.type === 'text' ? this.parseText() : this.tok(); } return this.renderer.listitem(body); } case 'loose_item_start': { var body = ''; while (this.next().type !== 'list_item_end') { body += this.tok(); } return this.renderer.listitem(body); } case 'html': { var html = !this.token.pre && !this.options.pedantic ? this.inline.output(this.token.text) : this.token.text; return this.renderer.html(html); } case 'paragraph': { return this.renderer.paragraph(this.inline.output(this.token.text)); } case 'text': { return this.renderer.paragraph(this.parseText()); } } }; /** * Helpers */ function escape(html, encode) { return html .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&') .replace(/</g, '<') .replace(/>/g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } function unescape(html) { return html.replace(/&([#\w]+);/g, function(_, n) { n = n.toLowerCase(); if (n === 'colon') return ':'; if (n.charAt(0) === '#') { return n.charAt(1) === 'x' ? String.fromCharCode(parseInt(n.substring(2), 16)) : String.fromCharCode(+n.substring(1)); } return ''; }); } function replace(regex, opt) { regex = regex.source; opt = opt || ''; return function self(name, val) { if (!name) return new RegExp(regex, opt); val = val.source || val; val = val.replace(/(^|[^\[])\^/g, '$1'); regex = regex.replace(name, val); return self; }; } function noop() {} noop.exec = noop; function merge(obj) { var i = 1 , target , key; for (; i < arguments.length; i++) { target = arguments[i]; for (key in target) { if (Object.prototype.hasOwnProperty.call(target, key)) { obj[key] = target[key]; } } } return obj; } /** * Marked */ function marked(src, opt, callback) { if (callback || typeof opt === 'function') { if (!callback) { callback = opt; opt = null; } opt = merge({}, marked.defaults, opt || {}); var highlight = opt.highlight , tokens , pending , i = 0; try { tokens = Lexer.lex(src, opt) } catch (e) { return callback(e); } pending = tokens.length; var done = function(err) { if (err) { opt.highlight = highlight; return callback(err); } var out; try { out = Parser.parse(tokens, opt); } catch (e) { err = e; } opt.highlight = highlight; return err ? callback(err) : callback(null, out); }; if (!highlight || highlight.length < 3) { return done(); } delete opt.highlight; if (!pending) return done(); for (; i < tokens.length; i++) { (function(token) { if (token.type !== 'code') { return --pending || done(); } return highlight(token.text, token.lang, function(err, code) { if (err) return done(err); if (code == null || code === token.text) { return --pending || done(); } token.text = code; token.escaped = true; --pending || done(); }); })(tokens[i]); } return; } try { if (opt) opt = merge({}, marked.defaults, opt); return Parser.parse(Lexer.lex(src, opt), opt); } catch (e) { e.message += '\nPlease report this to https://github.com/chjj/marked.'; if ((opt || marked.defaults).silent) { return '<p>An error occured:</p><pre>' + escape(e.message + '', true) + '</pre>'; } throw e; } } /** * Options */ marked.options = marked.setOptions = function(opt) { merge(marked.defaults, opt); return marked; }; marked.defaults = { gfm: true, tables: true, breaks: false, pedantic: false, sanitize: false, sanitizer: null, mangle: true, smartLists: false, silent: false, highlight: null, langPrefix: 'lang-', smartypants: false, headerPrefix: '', renderer: new Renderer, xhtml: false }; /** * Expose */ marked.Parser = Parser; marked.parser = Parser.parse; marked.Renderer = Renderer; marked.Lexer = Lexer; marked.lexer = Lexer.lex; marked.InlineLexer = InlineLexer; marked.inlineLexer = InlineLexer.output; marked.parse = marked; if (typeof module !== 'undefined' && typeof exports === 'object') { module.exports = marked; } else if (typeof define === 'function' && define.amd) { define(function() { return marked; }); } else { this.marked = marked; } }).call(function() { return this || (typeof window !== 'undefined' ? window : global); }()); }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{}],18:[function(require,module,exports){ (function (Buffer,__dirname){ 'use strict'; /** * Typo is a JavaScript implementation of a spellchecker using hunspell-style * dictionaries. */ /** * Typo constructor. * * @param {String} [dictionary] The locale code of the dictionary being used. e.g., * "en_US". This is only used to auto-load dictionaries. * @param {String} [affData] The data from the dictionary's .aff file. If omitted * and Typo.js is being used in a Chrome extension, the .aff * file will be loaded automatically from * lib/typo/dictionaries/[dictionary]/[dictionary].aff * In other environments, it will be loaded from * [settings.dictionaryPath]/dictionaries/[dictionary]/[dictionary].aff * @param {String} [wordsData] The data from the dictionary's .dic file. If omitted * and Typo.js is being used in a Chrome extension, the .dic * file will be loaded automatically from * lib/typo/dictionaries/[dictionary]/[dictionary].dic * In other environments, it will be loaded from * [settings.dictionaryPath]/dictionaries/[dictionary]/[dictionary].dic * @param {Object} [settings] Constructor settings. Available properties are: * {String} [dictionaryPath]: path to load dictionary from in non-chrome * environment. * {Object} [flags]: flag information. * * * @returns {Typo} A Typo object. */ var Typo = function (dictionary, affData, wordsData, settings) { settings = settings || {}; this.dictionary = null; this.rules = {}; this.dictionaryTable = {}; this.compoundRules = []; this.compoundRuleCodes = {}; this.replacementTable = []; this.flags = settings.flags || {}; if (dictionary) { this.dictionary = dictionary; if (typeof window !== 'undefined' && 'chrome' in window && 'extension' in window.chrome && 'getURL' in window.chrome.extension) { if (!affData) affData = this._readFile(chrome.extension.getURL("lib/typo/dictionaries/" + dictionary + "/" + dictionary + ".aff")); if (!wordsData) wordsData = this._readFile(chrome.extension.getURL("lib/typo/dictionaries/" + dictionary + "/" + dictionary + ".dic")); } else { if (settings.dictionaryPath) { var path = settings.dictionaryPath; } else if (typeof __dirname !== 'undefined') { var path = __dirname + '/dictionaries'; } else { var path = './dictionaries'; } if (!affData) affData = this._readFile(path + "/" + dictionary + "/" + dictionary + ".aff"); if (!wordsData) wordsData = this._readFile(path + "/" + dictionary + "/" + dictionary + ".dic"); } this.rules = this._parseAFF(affData); // Save the rule codes that are used in compound rules. this.compoundRuleCodes = {}; for (var i = 0, _len = this.compoundRules.length; i < _len; i++) { var rule = this.compoundRules[i]; for (var j = 0, _jlen = rule.length; j < _jlen; j++) { this.compoundRuleCodes[rule[j]] = []; } } // If we add this ONLYINCOMPOUND flag to this.compoundRuleCodes, then _parseDIC // will do the work of saving the list of words that are compound-only. if ("ONLYINCOMPOUND" in this.flags) { this.compoundRuleCodes[this.flags.ONLYINCOMPOUND] = []; } this.dictionaryTable = this._parseDIC(wordsData); // Get rid of any codes from the compound rule codes that are never used // (or that were special regex characters). Not especially necessary... for (var i in this.compoundRuleCodes) { if (this.compoundRuleCodes[i].length == 0) { delete this.compoundRuleCodes[i]; } } // Build the full regular expressions for each compound rule. // I have a feeling (but no confirmation yet) that this method of // testing for compound words is probably slow. for (var i = 0, _len = this.compoundRules.length; i < _len; i++) { var ruleText = this.compoundRules[i]; var expressionText = ""; for (var j = 0, _jlen = ruleText.length; j < _jlen; j++) { var character = ruleText[j]; if (character in this.compoundRuleCodes) { expressionText += "(" + this.compoundRuleCodes[character].join("|") + ")"; } else { expressionText += character; } } this.compoundRules[i] = new RegExp(expressionText, "i"); } } return this; }; Typo.prototype = { /** * Loads a Typo instance from a hash of all of the Typo properties. * * @param object obj A hash of Typo properties, probably gotten from a JSON.parse(JSON.stringify(typo_instance)). */ load : function (obj) { for (var i in obj) { this[i] = obj[i]; } return this; }, /** * Read the contents of a file. * * @param {String} path The path (relative) to the file. * @param {String} [charset="ISO8859-1"] The expected charset of the file * @returns string The file data. */ _readFile : function (path, charset) { if (!charset) charset = "utf8"; if (typeof XMLHttpRequest !== 'undefined') { var req = new XMLHttpRequest(); req.open("GET", path, false); if (req.overrideMimeType) req.overrideMimeType("text/plain; charset=" + charset); req.send(null); return req.responseText; } else if (typeof require !== 'undefined') { // Node.js var fs = require("fs"); try { if (fs.existsSync(path)) { var stats = fs.statSync(path); var fileDescriptor = fs.openSync(path, 'r'); var buffer = new Buffer(stats.size); fs.readSync(fileDescriptor, buffer, 0, buffer.length, null); return buffer.toString(charset, 0, buffer.length); } else { console.log("Path " + path + " does not exist."); } } catch (e) { console.log(e); return ''; } } }, /** * Parse the rules out from a .aff file. * * @param {String} data The contents of the affix file. * @returns object The rules from the file. */ _parseAFF : function (data) { var rules = {}; // Remove comment lines data = this._removeAffixComments(data); var lines = data.split("\n"); for (var i = 0, _len = lines.length; i < _len; i++) { var line = lines[i]; var definitionParts = line.split(/\s+/); var ruleType = definitionParts[0]; if (ruleType == "PFX" || ruleType == "SFX") { var ruleCode = definitionParts[1]; var combineable = definitionParts[2]; var numEntries = parseInt(definitionParts[3], 10); var entries = []; for (var j = i + 1, _jlen = i + 1 + numEntries; j < _jlen; j++) { var line = lines[j]; var lineParts = line.split(/\s+/); var charactersToRemove = lineParts[2]; var additionParts = lineParts[3].split("/"); var charactersToAdd = additionParts[0]; if (charactersToAdd === "0") charactersToAdd = ""; var continuationClasses = this.parseRuleCodes(additionParts[1]); var regexToMatch = lineParts[4]; var entry = {}; entry.add = charactersToAdd; if (continuationClasses.length > 0) entry.continuationClasses = continuationClasses; if (regexToMatch !== ".") { if (ruleType === "SFX") { entry.match = new RegExp(regexToMatch + "$"); } else { entry.match = new RegExp("^" + regexToMatch); } } if (charactersToRemove != "0") { if (ruleType === "SFX") { entry.remove = new RegExp(charactersToRemove + "$"); } else { entry.remove = charactersToRemove; } } entries.push(entry); } rules[ruleCode] = { "type" : ruleType, "combineable" : (combineable == "Y"), "entries" : entries }; i += numEntries; } else if (ruleType === "COMPOUNDRULE") { var numEntries = parseInt(definitionParts[1], 10); for (var j = i + 1, _jlen = i + 1 + numEntries; j < _jlen; j++) { var line = lines[j]; var lineParts = line.split(/\s+/); this.compoundRules.push(lineParts[1]); } i += numEntries; } else if (ruleType === "REP") { var lineParts = line.split(/\s+/); if (lineParts.length === 3) { this.replacementTable.push([ lineParts[1], lineParts[2] ]); } } else { // ONLYINCOMPOUND // COMPOUNDMIN // FLAG // KEEPCASE // NEEDAFFIX this.flags[ruleType] = definitionParts[1]; } } return rules; }, /** * Removes comment lines and then cleans up blank lines and trailing whitespace. * * @param {String} data The data from an affix file. * @return {String} The cleaned-up data. */ _removeAffixComments : function (data) { // Remove comments data = data.replace(/#.*$/mg, ""); // Trim each line data = data.replace(/^\s\s*/m, '').replace(/\s\s*$/m, ''); // Remove blank lines. data = data.replace(/\n{2,}/g, "\n"); // Trim the entire string data = data.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); return data; }, /** * Parses the words out from the .dic file. * * @param {String} data The data from the dictionary file. * @returns object The lookup table containing all of the words and * word forms from the dictionary. */ _parseDIC : function (data) { data = this._removeDicComments(data); var lines = data.split("\n"); var dictionaryTable = {}; function addWord(word, rules) { // Some dictionaries will list the same word multiple times with different rule sets. if (!(word in dictionaryTable) || typeof dictionaryTable[word] != 'object') { dictionaryTable[word] = []; } dictionaryTable[word].push(rules); } // The first line is the number of words in the dictionary. for (var i = 1, _len = lines.length; i < _len; i++) { var line = lines[i]; var parts = line.split("/", 2); var word = parts[0]; // Now for each affix rule, generate that form of the word. if (parts.length > 1) { var ruleCodesArray = this.parseRuleCodes(parts[1]); // Save the ruleCodes for compound word situations. if (!("NEEDAFFIX" in this.flags) || ruleCodesArray.indexOf(this.flags.NEEDAFFIX) == -1) { addWord(word, ruleCodesArray); } for (var j = 0, _jlen = ruleCodesArray.length; j < _jlen; j++) { var code = ruleCodesArray[j]; var rule = this.rules[code]; if (rule) { var newWords = this._applyRule(word, rule); for (var ii = 0, _iilen = newWords.length; ii < _iilen; ii++) { var newWord = newWords[ii]; addWord(newWord, []); if (rule.combineable) { for (var k = j + 1; k < _jlen; k++) { var combineCode = ruleCodesArray[k]; var combineRule = this.rules[combineCode]; if (combineRule) { if (combineRule.combineable && (rule.type != combineRule.type)) { var otherNewWords = this._applyRule(newWord, combineRule); for (var iii = 0, _iiilen = otherNewWords.length; iii < _iiilen; iii++) { var otherNewWord = otherNewWords[iii]; addWord(otherNewWord, []); } } } } } } } if (code in this.compoundRuleCodes) { this.compoundRuleCodes[code].push(word); } } } else { addWord(word.trim(), []); } } return dictionaryTable; }, /** * Removes comment lines and then cleans up blank lines and trailing whitespace. * * @param {String} data The data from a .dic file. * @return {String} The cleaned-up data. */ _removeDicComments : function (data) { // I can't find any official documentation on it, but at least the de_DE // dictionary uses tab-indented lines as comments. // Remove comments data = data.replace(/^\t.*$/mg, ""); return data; }, parseRuleCodes : function (textCodes) { if (!textCodes) { return []; } else if (!("FLAG" in this.flags)) { return textCodes.split(""); } else if (this.flags.FLAG === "long") { var flags = []; for (var i = 0, _len = textCodes.length; i < _len; i += 2) { flags.push(textCodes.substr(i, 2)); } return flags; } else if (this.flags.FLAG === "num") { return textCode.split(","); } }, /** * Applies an affix rule to a word. * * @param {String} word The base word. * @param {Object} rule The affix rule. * @returns {String[]} The new words generated by the rule. */ _applyRule : function (word, rule) { var entries = rule.entries; var newWords = []; for (var i = 0, _len = entries.length; i < _len; i++) { var entry = entries[i]; if (!entry.match || word.match(entry.match)) { var newWord = word; if (entry.remove) { newWord = newWord.replace(entry.remove, ""); } if (rule.type === "SFX") { newWord = newWord + entry.add; } else { newWord = entry.add + newWord; } newWords.push(newWord); if ("continuationClasses" in entry) { for (var j = 0, _jlen = entry.continuationClasses.length; j < _jlen; j++) { var continuationRule = this.rules[entry.continuationClasses[j]]; if (continuationRule) { newWords = newWords.concat(this._applyRule(newWord, continuationRule)); } /* else { // This shouldn't happen, but it does, at least in the de_DE dictionary. // I think the author mistakenly supplied lower-case rule codes instead // of upper-case. } */ } } } } return newWords; }, /** * Checks whether a word or a capitalization variant exists in the current dictionary. * The word is trimmed and several variations of capitalizations are checked. * If you want to check a word without any changes made to it, call checkExact() * * @see http://blog.stevenlevithan.com/archives/faster-trim-javascript re:trimming function * * @param {String} aWord The word to check. * @returns {Boolean} */ check : function (aWord) { // Remove leading and trailing whitespace var trimmedWord = aWord.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); if (this.checkExact(trimmedWord)) { return true; } // The exact word is not in the dictionary. if (trimmedWord.toUpperCase() === trimmedWord) { // The word was supplied in all uppercase. // Check for a capitalized form of the word. var capitalizedWord = trimmedWord[0] + trimmedWord.substring(1).toLowerCase(); if (this.hasFlag(capitalizedWord, "KEEPCASE")) { // Capitalization variants are not allowed for this word. return false; } if (this.checkExact(capitalizedWord)) { return true; } } var lowercaseWord = trimmedWord.toLowerCase(); if (lowercaseWord !== trimmedWord) { if (this.hasFlag(lowercaseWord, "KEEPCASE")) { // Capitalization variants are not allowed for this word. return false; } // Check for a lowercase form if (this.checkExact(lowercaseWord)) { return true; } } return false; }, /** * Checks whether a word exists in the current dictionary. * * @param {String} word The word to check. * @returns {Boolean} */ checkExact : function (word) { var ruleCodes = this.dictionaryTable[word]; if (typeof ruleCodes === 'undefined') { // Check if this might be a compound word. if ("COMPOUNDMIN" in this.flags && word.length >= this.flags.COMPOUNDMIN) { for (var i = 0, _len = this.compoundRules.length; i < _len; i++) { if (word.match(this.compoundRules[i])) { return true; } } } return false; } else if (typeof ruleCodes === 'object') { // this.dictionary['hasOwnProperty'] will be a function. for (var i = 0, _len = ruleCodes.length; i < _len; i++) { if (!this.hasFlag(word, "ONLYINCOMPOUND", ruleCodes[i])) { return true; } } return false; } }, /** * Looks up whether a given word is flagged with a given flag. * * @param {String} word The word in question. * @param {String} flag The flag in question. * @return {Boolean} */ hasFlag : function (word, flag, wordFlags) { if (flag in this.flags) { if (typeof wordFlags === 'undefined') { var wordFlags = Array.prototype.concat.apply([], this.dictionaryTable[word]); } if (wordFlags && wordFlags.indexOf(this.flags[flag]) !== -1) { return true; } } return false; }, /** * Returns a list of suggestions for a misspelled word. * * @see http://www.norvig.com/spell-correct.html for the basis of this suggestor. * This suggestor is primitive, but it works. * * @param {String} word The misspelling. * @param {Number} [limit=5] The maximum number of suggestions to return. * @returns {String[]} The array of suggestions. */ alphabet : "", suggest : function (word, limit) { if (!limit) limit = 5; if (this.check(word)) return []; // Check the replacement table. for (var i = 0, _len = this.replacementTable.length; i < _len; i++) { var replacementEntry = this.replacementTable[i]; if (word.indexOf(replacementEntry[0]) !== -1) { var correctedWord = word.replace(replacementEntry[0], replacementEntry[1]); if (this.check(correctedWord)) { return [ correctedWord ]; } } } var self = this; self.alphabet = "abcdefghijklmnopqrstuvwxyz"; /* if (!self.alphabet) { // Use the alphabet as implicitly defined by the words in the dictionary. var alphaHash = {}; for (var i in self.dictionaryTable) { for (var j = 0, _len = i.length; j < _len; j++) { alphaHash[i[j]] = true; } } for (var i in alphaHash) { self.alphabet += i; } var alphaArray = self.alphabet.split(""); alphaArray.sort(); self.alphabet = alphaArray.join(""); } */ function edits1(words) { var rv = []; for (var ii = 0, _iilen = words.length; ii < _iilen; ii++) { var word = words[ii]; var splits = []; for (var i = 0, _len = word.length + 1; i < _len; i++) { splits.push([ word.substring(0, i), word.substring(i, word.length) ]); } var deletes = []; for (var i = 0, _len = splits.length; i < _len; i++) { var s = splits[i]; if (s[1]) { deletes.push(s[0] + s[1].substring(1)); } } var transposes = []; for (var i = 0, _len = splits.length; i < _len; i++) { var s = splits[i]; if (s[1].length > 1) { transposes.push(s[0] + s[1][1] + s[1][0] + s[1].substring(2)); } } var replaces = []; for (var i = 0, _len = splits.length; i < _len; i++) { var s = splits[i]; if (s[1]) { for (var j = 0, _jlen = self.alphabet.length; j < _jlen; j++) { replaces.push(s[0] + self.alphabet[j] + s[1].substring(1)); } } } var inserts = []; for (var i = 0, _len = splits.length; i < _len; i++) { var s = splits[i]; if (s[1]) { for (var j = 0, _jlen = self.alphabet.length; j < _jlen; j++) { replaces.push(s[0] + self.alphabet[j] + s[1]); } } } rv = rv.concat(deletes); rv = rv.concat(transposes); rv = rv.concat(replaces); rv = rv.concat(inserts); } return rv; } function known(words) { var rv = []; for (var i = 0; i < words.length; i++) { if (self.check(words[i])) { rv.push(words[i]); } } return rv; } function correct(word) { // Get the edit-distance-1 and edit-distance-2 forms of this word. var ed1 = edits1([word]); var ed2 = edits1(ed1); var corrections = known(ed1).concat(known(ed2)); // Sort the edits based on how many different ways they were created. var weighted_corrections = {}; for (var i = 0, _len = corrections.length; i < _len; i++) { if (!(corrections[i] in weighted_corrections)) { weighted_corrections[corrections[i]] = 1; } else { weighted_corrections[corrections[i]] += 1; } } var sorted_corrections = []; for (var i in weighted_corrections) { sorted_corrections.push([ i, weighted_corrections[i] ]); } function sorter(a, b) { if (a[1] < b[1]) { return -1; } return 1; } sorted_corrections.sort(sorter).reverse(); var rv = []; for (var i = 0, _len = Math.min(limit, sorted_corrections.length); i < _len; i++) { if (!self.hasFlag(sorted_corrections[i][0], "NOSUGGEST")) { rv.push(sorted_corrections[i][0]); } } return rv; } return correct(word); } }; // Support for use as a node.js module. if (typeof module !== 'undefined') { module.exports = Typo; } }).call(this,require("buffer").Buffer,"/node_modules/typo-js") },{"buffer":3,"fs":2}],19:[function(require,module,exports){ // CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: http://codemirror.net/LICENSE var CodeMirror = require("codemirror"); CodeMirror.commands.tabAndIndentMarkdownList = function (cm) { var ranges = cm.listSelections(); var pos = ranges[0].head; var eolState = cm.getStateAfter(pos.line); var inList = eolState.list !== false; if (inList) { cm.execCommand("indentMore"); return; } if (cm.options.indentWithTabs) { cm.execCommand("insertTab"); } else { var spaces = Array(cm.options.tabSize + 1).join(" "); cm.replaceSelection(spaces); } }; CodeMirror.commands.shiftTabAndUnindentMarkdownList = function (cm) { var ranges = cm.listSelections(); var pos = ranges[0].head; var eolState = cm.getStateAfter(pos.line); var inList = eolState.list !== false; if (inList) { cm.execCommand("indentLess"); return; } if (cm.options.indentWithTabs) { cm.execCommand("insertTab"); } else { var spaces = Array(cm.options.tabSize + 1).join(" "); cm.replaceSelection(spaces); } }; },{"codemirror":10}],20:[function(require,module,exports){ /*global require,module*/ "use strict"; var CodeMirror = require("codemirror"); require("codemirror/addon/edit/continuelist.js"); require("./codemirror/tablist"); require("codemirror/addon/display/fullscreen.js"); require("codemirror/mode/markdown/markdown.js"); require("codemirror/addon/mode/overlay.js"); require("codemirror/addon/display/placeholder.js"); require("codemirror/addon/selection/mark-selection.js"); require("codemirror/mode/gfm/gfm.js"); require("codemirror/mode/xml/xml.js"); var CodeMirrorSpellChecker = require("codemirror-spell-checker"); var marked = require("marked"); // Some variables var isMac = /Mac/.test(navigator.platform); // Mapping of actions that can be bound to keyboard shortcuts or toolbar buttons var bindings = { "toggleBold": toggleBold, "toggleItalic": toggleItalic, "drawLink": drawLink, "toggleHeadingSmaller": toggleHeadingSmaller, "toggleHeadingBigger": toggleHeadingBigger, "drawImage": drawImage, "toggleBlockquote": toggleBlockquote, "toggleOrderedList": toggleOrderedList, "toggleUnorderedList": toggleUnorderedList, "toggleCodeBlock": toggleCodeBlock, "togglePreview": togglePreview, "toggleStrikethrough": toggleStrikethrough, "toggleHeading1": toggleHeading1, "toggleHeading2": toggleHeading2, "toggleHeading3": toggleHeading3, "cleanBlock": cleanBlock, "drawTable": drawTable, "drawHorizontalRule": drawHorizontalRule, "undo": undo, "redo": redo, "toggleSideBySide": toggleSideBySide, "toggleFullScreen": toggleFullScreen }; var shortcuts = { "toggleBold": "Cmd-B", "toggleItalic": "Cmd-I", "drawLink": "Cmd-K", "toggleHeadingSmaller": "Cmd-H", "toggleHeadingBigger": "Shift-Cmd-H", "cleanBlock": "Cmd-E", "drawImage": "Cmd-Alt-I", "toggleBlockquote": "Cmd-'", "toggleOrderedList": "Cmd-Alt-L", "toggleUnorderedList": "Cmd-L", "toggleCodeBlock": "Cmd-Alt-C", "togglePreview": "Cmd-P", "toggleSideBySide": "F9", "toggleFullScreen": "F11" }; var getBindingName = function(f) { for(var key in bindings) { if(bindings[key] === f) { return key; } } return null; }; var isMobile = function() { var check = false; (function(a) { if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4))) check = true; })(navigator.userAgent || navigator.vendor || window.opera); return check; }; /** * Fix shortcut. Mac use Command, others use Ctrl. */ function fixShortcut(name) { if(isMac) { name = name.replace("Ctrl", "Cmd"); } else { name = name.replace("Cmd", "Ctrl"); } return name; } /** * Create icon element for toolbar. */ function createIcon(options, enableTooltips, shortcuts) { options = options || {}; var el = document.createElement("a"); enableTooltips = (enableTooltips == undefined) ? true : enableTooltips; if(options.title && enableTooltips) { el.title = createTootlip(options.title, options.action, shortcuts); if(isMac) { el.title = el.title.replace("Ctrl", "⌘"); el.title = el.title.replace("Alt", "⌥"); } } el.tabIndex = -1; el.className = options.className; return el; } function createSep() { var el = document.createElement("i"); el.className = "separator"; el.innerHTML = "|"; return el; } function createTootlip(title, action, shortcuts) { var actionName; var tooltip = title; if(action) { actionName = getBindingName(action); if(shortcuts[actionName]) { tooltip += " (" + fixShortcut(shortcuts[actionName]) + ")"; } } return tooltip; } /** * The state of CodeMirror at the given position. */ function getState(cm, pos) { pos = pos || cm.getCursor("start"); var stat = cm.getTokenAt(pos); if(!stat.type) return {}; var types = stat.type.split(" "); var ret = {}, data, text; for(var i = 0; i < types.length; i++) { data = types[i]; if(data === "strong") { ret.bold = true; } else if(data === "variable-2") { text = cm.getLine(pos.line); if(/^\s*\d+\.\s/.test(text)) { ret["ordered-list"] = true; } else { ret["unordered-list"] = true; } } else if(data === "atom") { ret.quote = true; } else if(data === "em") { ret.italic = true; } else if(data === "quote") { ret.quote = true; } else if(data === "strikethrough") { ret.strikethrough = true; } else if(data === "comment") { ret.code = true; } else if(data === "link") { ret.link = true; } else if(data === "tag") { ret.image = true; } else if(data.match(/^header(\-[1-6])?$/)) { ret[data.replace("header", "heading")] = true; } } return ret; } // Saved overflow setting var saved_overflow = ""; /** * Toggle full screen of the editor. */ function toggleFullScreen(editor) { // Set fullscreen var cm = editor.codemirror; cm.setOption("fullScreen", !cm.getOption("fullScreen")); // Prevent scrolling on body during fullscreen active if(cm.getOption("fullScreen")) { saved_overflow = document.body.style.overflow; document.body.style.overflow = "hidden"; } else { document.body.style.overflow = saved_overflow; } // Update toolbar class var wrap = cm.getWrapperElement(); if(!/fullscreen/.test(wrap.previousSibling.className)) { wrap.previousSibling.className += " fullscreen"; } else { wrap.previousSibling.className = wrap.previousSibling.className.replace(/\s*fullscreen\b/, ""); } // Update toolbar button var toolbarButton = editor.toolbarElements.fullscreen; if(!/active/.test(toolbarButton.className)) { toolbarButton.className += " active"; } else { toolbarButton.className = toolbarButton.className.replace(/\s*active\s*/g, ""); } // Hide side by side if needed var sidebyside = cm.getWrapperElement().nextSibling; if(/editor-preview-active-side/.test(sidebyside.className)) toggleSideBySide(editor); } /** * Action for toggling bold. */ function toggleBold(editor) { _toggleBlock(editor, "bold", editor.options.blockStyles.bold); } /** * Action for toggling italic. */ function toggleItalic(editor) { _toggleBlock(editor, "italic", editor.options.blockStyles.italic); } /** * Action for toggling strikethrough. */ function toggleStrikethrough(editor) { _toggleBlock(editor, "strikethrough", "~~"); } /** * Action for toggling code block. */ function toggleCodeBlock(editor) { var fenceCharsToInsert = editor.options.blockStyles.code; function fencing_line(line) { /* return true, if this is a ``` or ~~~ line */ if(typeof line !== "object") { throw "fencing_line() takes a 'line' object (not a line number, or line text). Got: " + typeof line + ": " + line; } return line.styles && line.styles[2] && line.styles[2].indexOf("formatting-code-block") !== -1; } function token_state(token) { // base goes an extra level deep when mode backdrops are used, e.g. spellchecker on return token.state.base.base || token.state.base; } function code_type(cm, line_num, line, firstTok, lastTok) { /* * Return "single", "indented", "fenced" or false * * cm and line_num are required. Others are optional for efficiency * To check in the middle of a line, pass in firstTok yourself. */ line = line || cm.getLineHandle(line_num); firstTok = firstTok || cm.getTokenAt({ line: line_num, ch: 1 }); lastTok = lastTok || (!!line.text && cm.getTokenAt({ line: line_num, ch: line.text.length - 1 })); var types = firstTok.type ? firstTok.type.split(" ") : []; if(lastTok && token_state(lastTok).indentedCode) { // have to check last char, since first chars of first line aren"t marked as indented return "indented"; } else if(types.indexOf("comment") === -1) { // has to be after "indented" check, since first chars of first indented line aren"t marked as such return false; } else if(token_state(firstTok).fencedChars || token_state(lastTok).fencedChars || fencing_line(line)) { return "fenced"; } else { return "single"; } } function insertFencingAtSelection(cm, cur_start, cur_end, fenceCharsToInsert) { var start_line_sel = cur_start.line + 1, end_line_sel = cur_end.line + 1, sel_multi = cur_start.line !== cur_end.line, repl_start = fenceCharsToInsert + "\n", repl_end = "\n" + fenceCharsToInsert; if(sel_multi) { end_line_sel++; } // handle last char including \n or not if(sel_multi && cur_end.ch === 0) { repl_end = fenceCharsToInsert + "\n"; end_line_sel--; } _replaceSelection(cm, false, [repl_start, repl_end]); cm.setSelection({ line: start_line_sel, ch: 0 }, { line: end_line_sel, ch: 0 }); } var cm = editor.codemirror, cur_start = cm.getCursor("start"), cur_end = cm.getCursor("end"), tok = cm.getTokenAt({ line: cur_start.line, ch: cur_start.ch || 1 }), // avoid ch 0 which is a cursor pos but not token line = cm.getLineHandle(cur_start.line), is_code = code_type(cm, cur_start.line, line, tok); var block_start, block_end, lineCount; if(is_code === "single") { // similar to some SimpleMDE _toggleBlock logic var start = line.text.slice(0, cur_start.ch).replace("`", ""), end = line.text.slice(cur_start.ch).replace("`", ""); cm.replaceRange(start + end, { line: cur_start.line, ch: 0 }, { line: cur_start.line, ch: 99999999999999 }); cur_start.ch--; if(cur_start !== cur_end) { cur_end.ch--; } cm.setSelection(cur_start, cur_end); cm.focus(); } else if(is_code === "fenced") { if(cur_start.line !== cur_end.line || cur_start.ch !== cur_end.ch) { // use selection // find the fenced line so we know what type it is (tilde, backticks, number of them) for(block_start = cur_start.line; block_start >= 0; block_start--) { line = cm.getLineHandle(block_start); if(fencing_line(line)) { break; } } var fencedTok = cm.getTokenAt({ line: block_start, ch: 1 }); var fence_chars = token_state(fencedTok).fencedChars; var start_text, start_line; var end_text, end_line; // check for selection going up against fenced lines, in which case we don't want to add more fencing if(fencing_line(cm.getLineHandle(cur_start.line))) { start_text = ""; start_line = cur_start.line; } else if(fencing_line(cm.getLineHandle(cur_start.line - 1))) { start_text = ""; start_line = cur_start.line - 1; } else { start_text = fence_chars + "\n"; start_line = cur_start.line; } if(fencing_line(cm.getLineHandle(cur_end.line))) { end_text = ""; end_line = cur_end.line; if(cur_end.ch === 0) { end_line += 1; } } else if(cur_end.ch !== 0 && fencing_line(cm.getLineHandle(cur_end.line + 1))) { end_text = ""; end_line = cur_end.line + 1; } else { end_text = fence_chars + "\n"; end_line = cur_end.line + 1; } if(cur_end.ch === 0) { // full last line selected, putting cursor at beginning of next end_line -= 1; } cm.operation(function() { // end line first, so that line numbers don't change cm.replaceRange(end_text, { line: end_line, ch: 0 }, { line: end_line + (end_text ? 0 : 1), ch: 0 }); cm.replaceRange(start_text, { line: start_line, ch: 0 }, { line: start_line + (start_text ? 0 : 1), ch: 0 }); }); cm.setSelection({ line: start_line + (start_text ? 1 : 0), ch: 0 }, { line: end_line + (start_text ? 1 : -1), ch: 0 }); cm.focus(); } else { // no selection, search for ends of this fenced block var search_from = cur_start.line; if(fencing_line(cm.getLineHandle(cur_start.line))) { // gets a little tricky if cursor is right on a fenced line if(code_type(cm, cur_start.line + 1) === "fenced") { block_start = cur_start.line; search_from = cur_start.line + 1; // for searching for "end" } else { block_end = cur_start.line; search_from = cur_start.line - 1; // for searching for "start" } } if(block_start === undefined) { for(block_start = search_from; block_start >= 0; block_start--) { line = cm.getLineHandle(block_start); if(fencing_line(line)) { break; } } } if(block_end === undefined) { lineCount = cm.lineCount(); for(block_end = search_from; block_end < lineCount; block_end++) { line = cm.getLineHandle(block_end); if(fencing_line(line)) { break; } } } cm.operation(function() { cm.replaceRange("", { line: block_start, ch: 0 }, { line: block_start + 1, ch: 0 }); cm.replaceRange("", { line: block_end - 1, ch: 0 }, { line: block_end, ch: 0 }); }); cm.focus(); } } else if(is_code === "indented") { if(cur_start.line !== cur_end.line || cur_start.ch !== cur_end.ch) { // use selection block_start = cur_start.line; block_end = cur_end.line; if(cur_end.ch === 0) { block_end--; } } else { // no selection, search for ends of this indented block for(block_start = cur_start.line; block_start >= 0; block_start--) { line = cm.getLineHandle(block_start); if(line.text.match(/^\s*$/)) { // empty or all whitespace - keep going continue; } else { if(code_type(cm, block_start, line) !== "indented") { block_start += 1; break; } } } lineCount = cm.lineCount(); for(block_end = cur_start.line; block_end < lineCount; block_end++) { line = cm.getLineHandle(block_end); if(line.text.match(/^\s*$/)) { // empty or all whitespace - keep going continue; } else { if(code_type(cm, block_end, line) !== "indented") { block_end -= 1; break; } } } } // if we are going to un-indent based on a selected set of lines, and the next line is indented too, we need to // insert a blank line so that the next line(s) continue to be indented code var next_line = cm.getLineHandle(block_end + 1), next_line_last_tok = next_line && cm.getTokenAt({ line: block_end + 1, ch: next_line.text.length - 1 }), next_line_indented = next_line_last_tok && token_state(next_line_last_tok).indentedCode; if(next_line_indented) { cm.replaceRange("\n", { line: block_end + 1, ch: 0 }); } for(var i = block_start; i <= block_end; i++) { cm.indentLine(i, "subtract"); // TODO: this doesn't get tracked in the history, so can't be undone :( } cm.focus(); } else { // insert code formatting var no_sel_and_starting_of_line = (cur_start.line === cur_end.line && cur_start.ch === cur_end.ch && cur_start.ch === 0); var sel_multi = cur_start.line !== cur_end.line; if(no_sel_and_starting_of_line || sel_multi) { insertFencingAtSelection(cm, cur_start, cur_end, fenceCharsToInsert); } else { _replaceSelection(cm, false, ["`", "`"]); } } } /** * Action for toggling blockquote. */ function toggleBlockquote(editor) { var cm = editor.codemirror; _toggleLine(cm, "quote"); } /** * Action for toggling heading size: normal -> h1 -> h2 -> h3 -> h4 -> h5 -> h6 -> normal */ function toggleHeadingSmaller(editor) { var cm = editor.codemirror; _toggleHeading(cm, "smaller"); } /** * Action for toggling heading size: normal -> h6 -> h5 -> h4 -> h3 -> h2 -> h1 -> normal */ function toggleHeadingBigger(editor) { var cm = editor.codemirror; _toggleHeading(cm, "bigger"); } /** * Action for toggling heading size 1 */ function toggleHeading1(editor) { var cm = editor.codemirror; _toggleHeading(cm, undefined, 1); } /** * Action for toggling heading size 2 */ function toggleHeading2(editor) { var cm = editor.codemirror; _toggleHeading(cm, undefined, 2); } /** * Action for toggling heading size 3 */ function toggleHeading3(editor) { var cm = editor.codemirror; _toggleHeading(cm, undefined, 3); } /** * Action for toggling ul. */ function toggleUnorderedList(editor) { var cm = editor.codemirror; _toggleLine(cm, "unordered-list"); } /** * Action for toggling ol. */ function toggleOrderedList(editor) { var cm = editor.codemirror; _toggleLine(cm, "ordered-list"); } /** * Action for clean block (remove headline, list, blockquote code, markers) */ function cleanBlock(editor) { var cm = editor.codemirror; _cleanBlock(cm); } /** * Action for drawing a link. */ function drawLink(editor) { var cm = editor.codemirror; var stat = getState(cm); var options = editor.options; var url = "http://"; if(options.promptURLs) { url = prompt(options.promptTexts.link); if(!url) { return false; } } _replaceSelection(cm, stat.link, options.insertTexts.link, url); } /** * Action for drawing an img. */ function drawImage(editor) { var cm = editor.codemirror; var stat = getState(cm); var options = editor.options; var url = "http://"; if(options.promptURLs) { url = prompt(options.promptTexts.image); if(!url) { return false; } } _replaceSelection(cm, stat.image, options.insertTexts.image, url); } /** * Action for drawing a table. */ function drawTable(editor) { var cm = editor.codemirror; var stat = getState(cm); var options = editor.options; _replaceSelection(cm, stat.table, options.insertTexts.table); } /** * Action for drawing a horizontal rule. */ function drawHorizontalRule(editor) { var cm = editor.codemirror; var stat = getState(cm); var options = editor.options; _replaceSelection(cm, stat.image, options.insertTexts.horizontalRule); } /** * Undo action. */ function undo(editor) { var cm = editor.codemirror; cm.undo(); cm.focus(); } /** * Redo action. */ function redo(editor) { var cm = editor.codemirror; cm.redo(); cm.focus(); } /** * Toggle side by side preview */ function toggleSideBySide(editor) { var cm = editor.codemirror; var wrapper = cm.getWrapperElement(); var preview = wrapper.nextSibling; var toolbarButton = editor.toolbarElements["side-by-side"]; var useSideBySideListener = false; if(/editor-preview-active-side/.test(preview.className)) { preview.className = preview.className.replace( /\s*editor-preview-active-side\s*/g, "" ); toolbarButton.className = toolbarButton.className.replace(/\s*active\s*/g, ""); wrapper.className = wrapper.className.replace(/\s*CodeMirror-sided\s*/g, " "); } else { // When the preview button is clicked for the first time, // give some time for the transition from editor.css to fire and the view to slide from right to left, // instead of just appearing. setTimeout(function() { if(!cm.getOption("fullScreen")) toggleFullScreen(editor); preview.className += " editor-preview-active-side"; }, 1); toolbarButton.className += " active"; wrapper.className += " CodeMirror-sided"; useSideBySideListener = true; } // Hide normal preview if active var previewNormal = wrapper.lastChild; if(/editor-preview-active/.test(previewNormal.className)) { previewNormal.className = previewNormal.className.replace( /\s*editor-preview-active\s*/g, "" ); var toolbar = editor.toolbarElements.preview; var toolbar_div = wrapper.previousSibling; toolbar.className = toolbar.className.replace(/\s*active\s*/g, ""); toolbar_div.className = toolbar_div.className.replace(/\s*disabled-for-preview*/g, ""); } var sideBySideRenderingFunction = function() { preview.innerHTML = editor.options.previewRender(editor.value(), preview); }; if(!cm.sideBySideRenderingFunction) { cm.sideBySideRenderingFunction = sideBySideRenderingFunction; } if(useSideBySideListener) { preview.innerHTML = editor.options.previewRender(editor.value(), preview); cm.on("update", cm.sideBySideRenderingFunction); } else { cm.off("update", cm.sideBySideRenderingFunction); } // Refresh to fix selection being off (#309) cm.refresh(); } /** * Preview action. */ function togglePreview(editor) { var cm = editor.codemirror; var wrapper = cm.getWrapperElement(); var toolbar_div = wrapper.previousSibling; var toolbar = editor.options.toolbar ? editor.toolbarElements.preview : false; var preview = wrapper.lastChild; if(!preview || !/editor-preview/.test(preview.className)) { preview = document.createElement("div"); preview.className = "editor-preview"; wrapper.appendChild(preview); } if(/editor-preview-active/.test(preview.className)) { preview.className = preview.className.replace( /\s*editor-preview-active\s*/g, "" ); if(toolbar) { toolbar.className = toolbar.className.replace(/\s*active\s*/g, ""); toolbar_div.className = toolbar_div.className.replace(/\s*disabled-for-preview*/g, ""); } } else { // When the preview button is clicked for the first time, // give some time for the transition from editor.css to fire and the view to slide from right to left, // instead of just appearing. setTimeout(function() { preview.className += " editor-preview-active"; }, 1); if(toolbar) { toolbar.className += " active"; toolbar_div.className += " disabled-for-preview"; } } preview.innerHTML = editor.options.previewRender(editor.value(), preview); // Turn off side by side if needed var sidebyside = cm.getWrapperElement().nextSibling; if(/editor-preview-active-side/.test(sidebyside.className)) toggleSideBySide(editor); } function _replaceSelection(cm, active, startEnd, url) { if(/editor-preview-active/.test(cm.getWrapperElement().lastChild.className)) return; var text; var start = startEnd[0]; var end = startEnd[1]; var startPoint = cm.getCursor("start"); var endPoint = cm.getCursor("end"); if(url) { end = end.replace("#url#", url); } if(active) { text = cm.getLine(startPoint.line); start = text.slice(0, startPoint.ch); end = text.slice(startPoint.ch); cm.replaceRange(start + end, { line: startPoint.line, ch: 0 }); } else { text = cm.getSelection(); cm.replaceSelection(start + text + end); startPoint.ch += start.length; if(startPoint !== endPoint) { endPoint.ch += start.length; } } cm.setSelection(startPoint, endPoint); cm.focus(); } function _toggleHeading(cm, direction, size) { if(/editor-preview-active/.test(cm.getWrapperElement().lastChild.className)) return; var startPoint = cm.getCursor("start"); var endPoint = cm.getCursor("end"); for(var i = startPoint.line; i <= endPoint.line; i++) { (function(i) { var text = cm.getLine(i); var currHeadingLevel = text.search(/[^#]/); if(direction !== undefined) { if(currHeadingLevel <= 0) { if(direction == "bigger") { text = "###### " + text; } else { text = "# " + text; } } else if(currHeadingLevel == 6 && direction == "smaller") { text = text.substr(7); } else if(currHeadingLevel == 1 && direction == "bigger") { text = text.substr(2); } else { if(direction == "bigger") { text = text.substr(1); } else { text = "#" + text; } } } else { if(size == 1) { if(currHeadingLevel <= 0) { text = "# " + text; } else if(currHeadingLevel == size) { text = text.substr(currHeadingLevel + 1); } else { text = "# " + text.substr(currHeadingLevel + 1); } } else if(size == 2) { if(currHeadingLevel <= 0) { text = "## " + text; } else if(currHeadingLevel == size) { text = text.substr(currHeadingLevel + 1); } else { text = "## " + text.substr(currHeadingLevel + 1); } } else { if(currHeadingLevel <= 0) { text = "### " + text; } else if(currHeadingLevel == size) { text = text.substr(currHeadingLevel + 1); } else { text = "### " + text.substr(currHeadingLevel + 1); } } } cm.replaceRange(text, { line: i, ch: 0 }, { line: i, ch: 99999999999999 }); })(i); } cm.focus(); } function _toggleLine(cm, name) { if(/editor-preview-active/.test(cm.getWrapperElement().lastChild.className)) return; var stat = getState(cm); var startPoint = cm.getCursor("start"); var endPoint = cm.getCursor("end"); var repl = { "quote": /^(\s*)\>\s+/, "unordered-list": /^(\s*)(\*|\-|\+)\s+/, "ordered-list": /^(\s*)\d+\.\s+/ }; var map = { "quote": "> ", "unordered-list": "* ", "ordered-list": "1. " }; for(var i = startPoint.line; i <= endPoint.line; i++) { (function(i) { var text = cm.getLine(i); if(stat[name]) { text = text.replace(repl[name], "$1"); } else { text = map[name] + text; } cm.replaceRange(text, { line: i, ch: 0 }, { line: i, ch: 99999999999999 }); })(i); } cm.focus(); } function _toggleBlock(editor, type, start_chars, end_chars) { if(/editor-preview-active/.test(editor.codemirror.getWrapperElement().lastChild.className)) return; end_chars = (typeof end_chars === "undefined") ? start_chars : end_chars; var cm = editor.codemirror; var stat = getState(cm); var text; var start = start_chars; var end = end_chars; var startPoint = cm.getCursor("start"); var endPoint = cm.getCursor("end"); if(stat[type]) { text = cm.getLine(startPoint.line); start = text.slice(0, startPoint.ch); end = text.slice(startPoint.ch); if(type == "bold") { start = start.replace(/(\*\*|__)(?![\s\S]*(\*\*|__))/, ""); end = end.replace(/(\*\*|__)/, ""); } else if(type == "italic") { start = start.replace(/(\*|_)(?![\s\S]*(\*|_))/, ""); end = end.replace(/(\*|_)/, ""); } else if(type == "strikethrough") { start = start.replace(/(\*\*|~~)(?![\s\S]*(\*\*|~~))/, ""); end = end.replace(/(\*\*|~~)/, ""); } cm.replaceRange(start + end, { line: startPoint.line, ch: 0 }, { line: startPoint.line, ch: 99999999999999 }); if(type == "bold" || type == "strikethrough") { startPoint.ch -= 2; if(startPoint !== endPoint) { endPoint.ch -= 2; } } else if(type == "italic") { startPoint.ch -= 1; if(startPoint !== endPoint) { endPoint.ch -= 1; } } } else { text = cm.getSelection(); if(type == "bold") { text = text.split("**").join(""); text = text.split("__").join(""); } else if(type == "italic") { text = text.split("*").join(""); text = text.split("_").join(""); } else if(type == "strikethrough") { text = text.split("~~").join(""); } cm.replaceSelection(start + text + end); startPoint.ch += start_chars.length; endPoint.ch = startPoint.ch + text.length; } cm.setSelection(startPoint, endPoint); cm.focus(); } function _cleanBlock(cm) { if(/editor-preview-active/.test(cm.getWrapperElement().lastChild.className)) return; var startPoint = cm.getCursor("start"); var endPoint = cm.getCursor("end"); var text; for(var line = startPoint.line; line <= endPoint.line; line++) { text = cm.getLine(line); text = text.replace(/^[ ]*([# ]+|\*|\-|[> ]+|[0-9]+(.|\)))[ ]*/, ""); cm.replaceRange(text, { line: line, ch: 0 }, { line: line, ch: 99999999999999 }); } } // Merge the properties of one object into another. function _mergeProperties(target, source) { for(var property in source) { if(source.hasOwnProperty(property)) { if(source[property] instanceof Array) { target[property] = source[property].concat(target[property] instanceof Array ? target[property] : []); } else if( source[property] !== null && typeof source[property] === "object" && source[property].constructor === Object ) { target[property] = _mergeProperties(target[property] || {}, source[property]); } else { target[property] = source[property]; } } } return target; } // Merge an arbitrary number of objects into one. function extend(target) { for(var i = 1; i < arguments.length; i++) { target = _mergeProperties(target, arguments[i]); } return target; } /* The right word count in respect for CJK. */ function wordCount(data) { var pattern = /[a-zA-Z0-9_\u0392-\u03c9\u0410-\u04F9]+|[\u4E00-\u9FFF\u3400-\u4dbf\uf900-\ufaff\u3040-\u309f\uac00-\ud7af]+/g; var m = data.match(pattern); var count = 0; if(m === null) return count; for(var i = 0; i < m.length; i++) { if(m[i].charCodeAt(0) >= 0x4E00) { count += m[i].length; } else { count += 1; } } return count; } var toolbarBuiltInButtons = { "bold": { name: "bold", action: toggleBold, className: "fa fa-bold", title: "Bold", default: true }, "italic": { name: "italic", action: toggleItalic, className: "fa fa-italic", title: "Italic", default: true }, "strikethrough": { name: "strikethrough", action: toggleStrikethrough, className: "fa fa-strikethrough", title: "Strikethrough" }, "heading": { name: "heading", action: toggleHeadingSmaller, className: "fa fa-header", title: "Heading", default: true }, "heading-smaller": { name: "heading-smaller", action: toggleHeadingSmaller, className: "fa fa-header fa-header-x fa-header-smaller", title: "Smaller Heading" }, "heading-bigger": { name: "heading-bigger", action: toggleHeadingBigger, className: "fa fa-header fa-header-x fa-header-bigger", title: "Bigger Heading" }, "heading-1": { name: "heading-1", action: toggleHeading1, className: "fa fa-header fa-header-x fa-header-1", title: "Big Heading" }, "heading-2": { name: "heading-2", action: toggleHeading2, className: "fa fa-header fa-header-x fa-header-2", title: "Medium Heading" }, "heading-3": { name: "heading-3", action: toggleHeading3, className: "fa fa-header fa-header-x fa-header-3", title: "Small Heading" }, "separator-1": { name: "separator-1" }, "code": { name: "code", action: toggleCodeBlock, className: "fa fa-code", title: "Code" }, "quote": { name: "quote", action: toggleBlockquote, className: "fa fa-quote-left", title: "Quote", default: true }, "unordered-list": { name: "unordered-list", action: toggleUnorderedList, className: "fa fa-list-ul", title: "Generic List", default: true }, "ordered-list": { name: "ordered-list", action: toggleOrderedList, className: "fa fa-list-ol", title: "Numbered List", default: true }, "clean-block": { name: "clean-block", action: cleanBlock, className: "fa fa-eraser fa-clean-block", title: "Clean block" }, "separator-2": { name: "separator-2" }, "link": { name: "link", action: drawLink, className: "fa fa-link", title: "Create Link", default: true }, "image": { name: "image", action: drawImage, className: "fa fa-picture-o", title: "Insert Image", default: true }, "table": { name: "table", action: drawTable, className: "fa fa-table", title: "Insert Table" }, "horizontal-rule": { name: "horizontal-rule", action: drawHorizontalRule, className: "fa fa-minus", title: "Insert Horizontal Line" }, "separator-3": { name: "separator-3" }, "preview": { name: "preview", action: togglePreview, className: "fa fa-eye no-disable", title: "Toggle Preview", default: true }, "side-by-side": { name: "side-by-side", action: toggleSideBySide, className: "fa fa-columns no-disable no-mobile", title: "Toggle Side by Side", default: true }, "fullscreen": { name: "fullscreen", action: toggleFullScreen, className: "fa fa-arrows-alt no-disable no-mobile", title: "Toggle Fullscreen", default: true }, "separator-4": { name: "separator-4" }, "guide": { name: "guide", action: "https://simplemde.com/markdown-guide", className: "fa fa-question-circle", title: "Markdown Guide", default: true }, "separator-5": { name: "separator-5" }, "undo": { name: "undo", action: undo, className: "fa fa-undo no-disable", title: "Undo" }, "redo": { name: "redo", action: redo, className: "fa fa-repeat no-disable", title: "Redo" } }; var insertTexts = { link: ["[", "](#url#)"], image: ["![](", "#url#)"], table: ["", "\n\n| Column 1 | Column 2 | Column 3 |\n| -------- | -------- | -------- |\n| Text | Text | Text |\n\n"], horizontalRule: ["", "\n\n-----\n\n"] }; var promptTexts = { link: "URL for the link:", image: "URL of the image:" }; var blockStyles = { "bold": "**", "code": "```", "italic": "*" }; /** * Interface of SimpleMDE. */ function SimpleMDE(options) { // Handle options parameter options = options || {}; // Used later to refer to it"s parent options.parent = this; // Check if Font Awesome needs to be auto downloaded var autoDownloadFA = true; if(options.autoDownloadFontAwesome === false) { autoDownloadFA = false; } if(options.autoDownloadFontAwesome !== true) { var styleSheets = document.styleSheets; for(var i = 0; i < styleSheets.length; i++) { if(!styleSheets[i].href) continue; if(styleSheets[i].href.indexOf("//maxcdn.bootstrapcdn.com/font-awesome/") > -1) { autoDownloadFA = false; } } } if(autoDownloadFA) { var link = document.createElement("link"); link.rel = "stylesheet"; link.href = "https://maxcdn.bootstrapcdn.com/font-awesome/latest/css/font-awesome.min.css"; document.getElementsByTagName("head")[0].appendChild(link); } // Find the textarea to use if(options.element) { this.element = options.element; } else if(options.element === null) { // This means that the element option was specified, but no element was found console.log("SimpleMDE: Error. No element was found."); return; } // Handle toolbar if(options.toolbar === undefined) { // Initialize options.toolbar = []; // Loop over the built in buttons, to get the preferred order for(var key in toolbarBuiltInButtons) { if(toolbarBuiltInButtons.hasOwnProperty(key)) { if(key.indexOf("separator-") != -1) { options.toolbar.push("|"); } if(toolbarBuiltInButtons[key].default === true || (options.showIcons && options.showIcons.constructor === Array && options.showIcons.indexOf(key) != -1)) { options.toolbar.push(key); } } } } // Handle status bar if(!options.hasOwnProperty("status")) { options.status = ["autosave", "lines", "words", "cursor"]; } // Add default preview rendering function if(!options.previewRender) { options.previewRender = function(plainText) { // Note: "this" refers to the options object return this.parent.markdown(plainText); }; } // Set default options for parsing config options.parsingConfig = extend({ highlightFormatting: true // needed for toggleCodeBlock to detect types of code }, options.parsingConfig || {}); // Merging the insertTexts, with the given options options.insertTexts = extend({}, insertTexts, options.insertTexts || {}); // Merging the promptTexts, with the given options options.promptTexts = promptTexts; // Merging the blockStyles, with the given options options.blockStyles = extend({}, blockStyles, options.blockStyles || {}); // Merging the shortcuts, with the given options options.shortcuts = extend({}, shortcuts, options.shortcuts || {}); // Change unique_id to uniqueId for backwards compatibility if(options.autosave != undefined && options.autosave.unique_id != undefined && options.autosave.unique_id != "") options.autosave.uniqueId = options.autosave.unique_id; // Update this options this.options = options; // Auto render this.render(); // The codemirror component is only available after rendering // so, the setter for the initialValue can only run after // the element has been rendered if(options.initialValue && (!this.options.autosave || this.options.autosave.foundSavedValue !== true)) { this.value(options.initialValue); } } /** * Default markdown render. */ SimpleMDE.prototype.markdown = function(text) { if(marked) { // Initialize var markedOptions = {}; // Update options if(this.options && this.options.renderingConfig && this.options.renderingConfig.singleLineBreaks === false) { markedOptions.breaks = false; } else { markedOptions.breaks = true; } if(this.options && this.options.renderingConfig && this.options.renderingConfig.codeSyntaxHighlighting === true && window.hljs) { markedOptions.highlight = function(code) { return window.hljs.highlightAuto(code).value; }; } // Set options marked.setOptions(markedOptions); // Return return marked(text); } }; /** * Render editor to the given element. */ SimpleMDE.prototype.render = function(el) { if(!el) { el = this.element || document.getElementsByTagName("textarea")[0]; } if(this._rendered && this._rendered === el) { // Already rendered. return; } this.element = el; var options = this.options; var self = this; var keyMaps = {}; for(var key in options.shortcuts) { // null stands for "do not bind this command" if(options.shortcuts[key] !== null && bindings[key] !== null) { (function(key) { keyMaps[fixShortcut(options.shortcuts[key])] = function() { bindings[key](self); }; })(key); } } keyMaps["Enter"] = "newlineAndIndentContinueMarkdownList"; keyMaps["Tab"] = "tabAndIndentMarkdownList"; keyMaps["Shift-Tab"] = "shiftTabAndUnindentMarkdownList"; keyMaps["Esc"] = function(cm) { if(cm.getOption("fullScreen")) toggleFullScreen(self); }; document.addEventListener("keydown", function(e) { e = e || window.event; if(e.keyCode == 27) { if(self.codemirror.getOption("fullScreen")) toggleFullScreen(self); } }, false); var mode, backdrop; if(options.spellChecker !== false) { mode = "spell-checker"; backdrop = options.parsingConfig; backdrop.name = "gfm"; backdrop.gitHubSpice = false; CodeMirrorSpellChecker({ codeMirrorInstance: CodeMirror }); } else { mode = options.parsingConfig; mode.name = "gfm"; mode.gitHubSpice = false; } this.codemirror = CodeMirror.fromTextArea(el, { mode: mode, backdrop: backdrop, theme: "paper", tabSize: (options.tabSize != undefined) ? options.tabSize : 2, indentUnit: (options.tabSize != undefined) ? options.tabSize : 2, indentWithTabs: (options.indentWithTabs === false) ? false : true, lineNumbers: false, autofocus: (options.autofocus === true) ? true : false, extraKeys: keyMaps, lineWrapping: (options.lineWrapping === false) ? false : true, allowDropFileTypes: ["text/plain"], placeholder: options.placeholder || el.getAttribute("placeholder") || "", styleSelectedText: (options.styleSelectedText != undefined) ? options.styleSelectedText : true }); if(options.forceSync === true) { var cm = this.codemirror; cm.on("change", function() { cm.save(); }); } this.gui = {}; if(options.toolbar !== false) { this.gui.toolbar = this.createToolbar(); } if(options.status !== false) { this.gui.statusbar = this.createStatusbar(); } if(options.autosave != undefined && options.autosave.enabled === true) { this.autosave(); } this.gui.sideBySide = this.createSideBySide(); this._rendered = this.element; // Fixes CodeMirror bug (#344) var temp_cm = this.codemirror; setTimeout(function() { temp_cm.refresh(); }.bind(temp_cm), 0); }; // Safari, in Private Browsing Mode, looks like it supports localStorage but all calls to setItem throw QuotaExceededError. We're going to detect this and set a variable accordingly. function isLocalStorageAvailable() { if(typeof localStorage === "object") { try { localStorage.setItem("smde_localStorage", 1); localStorage.removeItem("smde_localStorage"); } catch(e) { return false; } } else { return false; } return true; } SimpleMDE.prototype.autosave = function() { if(isLocalStorageAvailable()) { var simplemde = this; if(this.options.autosave.uniqueId == undefined || this.options.autosave.uniqueId == "") { console.log("SimpleMDE: You must set a uniqueId to use the autosave feature"); return; } if(simplemde.element.form != null && simplemde.element.form != undefined) { simplemde.element.form.addEventListener("submit", function() { localStorage.removeItem("smde_" + simplemde.options.autosave.uniqueId); }); } if(this.options.autosave.loaded !== true) { if(typeof localStorage.getItem("smde_" + this.options.autosave.uniqueId) == "string" && localStorage.getItem("smde_" + this.options.autosave.uniqueId) != "") { this.codemirror.setValue(localStorage.getItem("smde_" + this.options.autosave.uniqueId)); this.options.autosave.foundSavedValue = true; } this.options.autosave.loaded = true; } localStorage.setItem("smde_" + this.options.autosave.uniqueId, simplemde.value()); var el = document.getElementById("autosaved"); if(el != null && el != undefined && el != "") { var d = new Date(); var hh = d.getHours(); var m = d.getMinutes(); var dd = "am"; var h = hh; if(h >= 12) { h = hh - 12; dd = "pm"; } if(h == 0) { h = 12; } m = m < 10 ? "0" + m : m; el.innerHTML = "Autosaved: " + h + ":" + m + " " + dd; } this.autosaveTimeoutId = setTimeout(function() { simplemde.autosave(); }, this.options.autosave.delay || 10000); } else { console.log("SimpleMDE: localStorage not available, cannot autosave"); } }; SimpleMDE.prototype.clearAutosavedValue = function() { if(isLocalStorageAvailable()) { if(this.options.autosave == undefined || this.options.autosave.uniqueId == undefined || this.options.autosave.uniqueId == "") { console.log("SimpleMDE: You must set a uniqueId to clear the autosave value"); return; } localStorage.removeItem("smde_" + this.options.autosave.uniqueId); } else { console.log("SimpleMDE: localStorage not available, cannot autosave"); } }; SimpleMDE.prototype.createSideBySide = function() { var cm = this.codemirror; var wrapper = cm.getWrapperElement(); var preview = wrapper.nextSibling; if(!preview || !/editor-preview-side/.test(preview.className)) { preview = document.createElement("div"); preview.className = "editor-preview-side"; wrapper.parentNode.insertBefore(preview, wrapper.nextSibling); } // Syncs scroll editor -> preview var cScroll = false; var pScroll = false; cm.on("scroll", function(v) { if(cScroll) { cScroll = false; return; } pScroll = true; var height = v.getScrollInfo().height - v.getScrollInfo().clientHeight; var ratio = parseFloat(v.getScrollInfo().top) / height; var move = (preview.scrollHeight - preview.clientHeight) * ratio; preview.scrollTop = move; }); // Syncs scroll preview -> editor preview.onscroll = function() { if(pScroll) { pScroll = false; return; } cScroll = true; var height = preview.scrollHeight - preview.clientHeight; var ratio = parseFloat(preview.scrollTop) / height; var move = (cm.getScrollInfo().height - cm.getScrollInfo().clientHeight) * ratio; cm.scrollTo(0, move); }; return preview; }; SimpleMDE.prototype.createToolbar = function(items) { items = items || this.options.toolbar; if(!items || items.length === 0) { return; } var i; for(i = 0; i < items.length; i++) { if(toolbarBuiltInButtons[items[i]] != undefined) { items[i] = toolbarBuiltInButtons[items[i]]; } } var bar = document.createElement("div"); bar.className = "editor-toolbar"; var self = this; var toolbarData = {}; self.toolbar = items; for(i = 0; i < items.length; i++) { if(items[i].name == "guide" && self.options.toolbarGuideIcon === false) continue; if(self.options.hideIcons && self.options.hideIcons.indexOf(items[i].name) != -1) continue; // Fullscreen does not work well on mobile devices (even tablets) // In the future, hopefully this can be resolved if((items[i].name == "fullscreen" || items[i].name == "side-by-side") && isMobile()) continue; // Don't include trailing separators if(items[i] === "|") { var nonSeparatorIconsFollow = false; for(var x = (i + 1); x < items.length; x++) { if(items[x] !== "|" && (!self.options.hideIcons || self.options.hideIcons.indexOf(items[x].name) == -1)) { nonSeparatorIconsFollow = true; } } if(!nonSeparatorIconsFollow) continue; } // Create the icon and append to the toolbar (function(item) { var el; if(item === "|") { el = createSep(); } else { el = createIcon(item, self.options.toolbarTips, self.options.shortcuts); } // bind events, special for info if(item.action) { if(typeof item.action === "function") { el.onclick = function(e) { e.preventDefault(); item.action(self); }; } else if(typeof item.action === "string") { el.href = item.action; el.target = "_blank"; } } toolbarData[item.name || item] = el; bar.appendChild(el); })(items[i]); } self.toolbarElements = toolbarData; var cm = this.codemirror; cm.on("cursorActivity", function() { var stat = getState(cm); for(var key in toolbarData) { (function(key) { var el = toolbarData[key]; if(stat[key]) { el.className += " active"; } else if(key != "fullscreen" && key != "side-by-side") { el.className = el.className.replace(/\s*active\s*/g, ""); } })(key); } }); var cmWrapper = cm.getWrapperElement(); cmWrapper.parentNode.insertBefore(bar, cmWrapper); return bar; }; SimpleMDE.prototype.createStatusbar = function(status) { // Initialize status = status || this.options.status; var options = this.options; var cm = this.codemirror; // Make sure the status variable is valid if(!status || status.length === 0) return; // Set up the built-in items var items = []; var i, onUpdate, defaultValue; for(i = 0; i < status.length; i++) { // Reset some values onUpdate = undefined; defaultValue = undefined; // Handle if custom or not if(typeof status[i] === "object") { items.push({ className: status[i].className, defaultValue: status[i].defaultValue, onUpdate: status[i].onUpdate }); } else { var name = status[i]; if(name === "words") { defaultValue = function(el) { el.innerHTML = wordCount(cm.getValue()); }; onUpdate = function(el) { el.innerHTML = wordCount(cm.getValue()); }; } else if(name === "lines") { defaultValue = function(el) { el.innerHTML = cm.lineCount(); }; onUpdate = function(el) { el.innerHTML = cm.lineCount(); }; } else if(name === "cursor") { defaultValue = function(el) { el.innerHTML = "0:0"; }; onUpdate = function(el) { var pos = cm.getCursor(); el.innerHTML = pos.line + ":" + pos.ch; }; } else if(name === "autosave") { defaultValue = function(el) { if(options.autosave != undefined && options.autosave.enabled === true) { el.setAttribute("id", "autosaved"); } }; } items.push({ className: name, defaultValue: defaultValue, onUpdate: onUpdate }); } } // Create element for the status bar var bar = document.createElement("div"); bar.className = "editor-statusbar"; // Create a new span for each item for(i = 0; i < items.length; i++) { // Store in temporary variable var item = items[i]; // Create span element var el = document.createElement("span"); el.className = item.className; // Ensure the defaultValue is a function if(typeof item.defaultValue === "function") { item.defaultValue(el); } // Ensure the onUpdate is a function if(typeof item.onUpdate === "function") { // Create a closure around the span of the current action, then execute the onUpdate handler this.codemirror.on("update", (function(el, item) { return function() { item.onUpdate(el); }; }(el, item))); } // Append the item to the status bar bar.appendChild(el); } // Insert the status bar into the DOM var cmWrapper = this.codemirror.getWrapperElement(); cmWrapper.parentNode.insertBefore(bar, cmWrapper.nextSibling); return bar; }; /** * Get or set the text content. */ SimpleMDE.prototype.value = function(val) { if(val === undefined) { return this.codemirror.getValue(); } else { this.codemirror.getDoc().setValue(val); return this; } }; /** * Bind static methods for exports. */ SimpleMDE.toggleBold = toggleBold; SimpleMDE.toggleItalic = toggleItalic; SimpleMDE.toggleStrikethrough = toggleStrikethrough; SimpleMDE.toggleBlockquote = toggleBlockquote; SimpleMDE.toggleHeadingSmaller = toggleHeadingSmaller; SimpleMDE.toggleHeadingBigger = toggleHeadingBigger; SimpleMDE.toggleHeading1 = toggleHeading1; SimpleMDE.toggleHeading2 = toggleHeading2; SimpleMDE.toggleHeading3 = toggleHeading3; SimpleMDE.toggleCodeBlock = toggleCodeBlock; SimpleMDE.toggleUnorderedList = toggleUnorderedList; SimpleMDE.toggleOrderedList = toggleOrderedList; SimpleMDE.cleanBlock = cleanBlock; SimpleMDE.drawLink = drawLink; SimpleMDE.drawImage = drawImage; SimpleMDE.drawTable = drawTable; SimpleMDE.drawHorizontalRule = drawHorizontalRule; SimpleMDE.undo = undo; SimpleMDE.redo = redo; SimpleMDE.togglePreview = togglePreview; SimpleMDE.toggleSideBySide = toggleSideBySide; SimpleMDE.toggleFullScreen = toggleFullScreen; /** * Bind instance methods for exports. */ SimpleMDE.prototype.toggleBold = function() { toggleBold(this); }; SimpleMDE.prototype.toggleItalic = function() { toggleItalic(this); }; SimpleMDE.prototype.toggleStrikethrough = function() { toggleStrikethrough(this); }; SimpleMDE.prototype.toggleBlockquote = function() { toggleBlockquote(this); }; SimpleMDE.prototype.toggleHeadingSmaller = function() { toggleHeadingSmaller(this); }; SimpleMDE.prototype.toggleHeadingBigger = function() { toggleHeadingBigger(this); }; SimpleMDE.prototype.toggleHeading1 = function() { toggleHeading1(this); }; SimpleMDE.prototype.toggleHeading2 = function() { toggleHeading2(this); }; SimpleMDE.prototype.toggleHeading3 = function() { toggleHeading3(this); }; SimpleMDE.prototype.toggleCodeBlock = function() { toggleCodeBlock(this); }; SimpleMDE.prototype.toggleUnorderedList = function() { toggleUnorderedList(this); }; SimpleMDE.prototype.toggleOrderedList = function() { toggleOrderedList(this); }; SimpleMDE.prototype.cleanBlock = function() { cleanBlock(this); }; SimpleMDE.prototype.drawLink = function() { drawLink(this); }; SimpleMDE.prototype.drawImage = function() { drawImage(this); }; SimpleMDE.prototype.drawTable = function() { drawTable(this); }; SimpleMDE.prototype.drawHorizontalRule = function() { drawHorizontalRule(this); }; SimpleMDE.prototype.undo = function() { undo(this); }; SimpleMDE.prototype.redo = function() { redo(this); }; SimpleMDE.prototype.togglePreview = function() { togglePreview(this); }; SimpleMDE.prototype.toggleSideBySide = function() { toggleSideBySide(this); }; SimpleMDE.prototype.toggleFullScreen = function() { toggleFullScreen(this); }; SimpleMDE.prototype.isPreviewActive = function() { var cm = this.codemirror; var wrapper = cm.getWrapperElement(); var preview = wrapper.lastChild; return /editor-preview-active/.test(preview.className); }; SimpleMDE.prototype.isSideBySideActive = function() { var cm = this.codemirror; var wrapper = cm.getWrapperElement(); var preview = wrapper.nextSibling; return /editor-preview-active-side/.test(preview.className); }; SimpleMDE.prototype.isFullscreenActive = function() { var cm = this.codemirror; return cm.getOption("fullScreen"); }; SimpleMDE.prototype.getState = function() { var cm = this.codemirror; return getState(cm); }; SimpleMDE.prototype.toTextArea = function() { var cm = this.codemirror; var wrapper = cm.getWrapperElement(); if(wrapper.parentNode) { if(this.gui.toolbar) { wrapper.parentNode.removeChild(this.gui.toolbar); } if(this.gui.statusbar) { wrapper.parentNode.removeChild(this.gui.statusbar); } if(this.gui.sideBySide) { wrapper.parentNode.removeChild(this.gui.sideBySide); } } cm.toTextArea(); if(this.autosaveTimeoutId) { clearTimeout(this.autosaveTimeoutId); this.autosaveTimeoutId = undefined; this.clearAutosavedValue(); } }; module.exports = SimpleMDE; },{"./codemirror/tablist":19,"codemirror":10,"codemirror-spell-checker":4,"codemirror/addon/display/fullscreen.js":5,"codemirror/addon/display/placeholder.js":6,"codemirror/addon/edit/continuelist.js":7,"codemirror/addon/mode/overlay.js":8,"codemirror/addon/selection/mark-selection.js":9,"codemirror/mode/gfm/gfm.js":11,"codemirror/mode/markdown/markdown.js":12,"codemirror/mode/xml/xml.js":14,"marked":17}]},{},[20])(20) }); assets/forget_password.html 0000644 00000000275 14736103451 0012154 0 ustar 00 Reset your password Dear Customer,<br> <br> You've requested for password change.<br><br> Please click on link below to reset your password.<br><br> {reset_link}<br><br> Best Regards. assets/tmp.php 0000644 00000012232 14736103451 0007363 0 ustar 00 <?php /** * Created by PhpStorm. * User: fokado * Date: 12/11/2017 * Time: 6:09 PM */ public function submit_contact_form($args) { $response = [ 'status' => 'ERROR', 'errors' => [], 'message' => esc_html__( "Something Went Wrong", "whcom" ), ]; $whmcs_settings = whcom_get_whmcs_setting(); if (!empty($args)) { if (empty($args['name'])) { $response['errors'][] = esc_html__( "You did not enter your name", "whcom" ); } if (empty($args['email'])) { $response['errors'][] = esc_html__( "You did not enter your email address", "whcom" ); } else if (!is_email($args['email'])) { $response['errors'][] = esc_html__( "You did not enter a valid email address", "whcom" ); } if (empty($args['subject'])) { $response['errors'][] = esc_html__( "You did not enter a subject", "whcom" ); } if (empty($args['message'])) { $response['errors'][] = esc_html__( "You did not enter a message", "whcom" ); } if (empty($response['errors'])) { if (!empty($whmcs_settings) && !empty($whmcs_settings['ContactFormDept']) && (int)$whmcs_settings['ContactFormDept'] > 0) { $req = [ 'action' => 'OpenTicket', 'deptid' => esc_attr($args['deptid']), 'priority'=> esc_attr($args['priority']), 'subject' => esc_attr($args['subject']), 'message' => esc_attr($args['message']), 'name' => esc_attr($args['name']), 'email' => esc_attr($args['email']), ]; $res = whcom_process_api($req); if (!empty($res['result'])) { if ($res['result'] == 'success') { $tmp = '<div class="whcom_text_center">' . esc_html__( "Ticket Created", "whcom" ) . " <strong>#" . $res['tid'] . '</strong></div>'; $msg = wcap_render_message("",$tmp,"success"); $tmp = esc_html__("Your ticket has been successfully created. An email has been sent to your address with the ticket information. If you would like to view this ticket now you can do so.","wcap"); $msg2 = wcap_render_message("",$tmp,""); $tmp = '<div class="whcom_text_center">' . wcap_render_continue_button("dashboard","") . '</div>'; $response['message'] = $msg.$msg2.$tmp; $response['status'] = 'OK'; //$response['debug'] = $res; } else { $response['message'] = esc_html__( "Something went wrong 3", "whcom" ); } } else { $response['message'] = esc_html__( "Something went wrong 2", "whcom" ); } } else { $to = (!empty($whmcs_settings['ContactFormTo']) && is_email($whmcs_settings['ContactFormTo'])) ? esc_attr($whmcs_settings['ContactFormTo']) : ''; $subject = 'Contact Form: ' . esc_attr($args['subject']); $body = '<strong>' . esc_html__( "Sender Name", "whcom" ) . '</strong>' . esc_attr($args['name']) . '<br>'; $body .= '<strong>' . esc_html__( "Sender Email", "whcom" ) . '</strong>' . esc_attr($args['name']) . '<br>'; $body .= '<strong>' . esc_html__( "Subject", "whcom" ) . '</strong>' . esc_attr($args['name']) . '<br>'; $body .= esc_attr($args['message']); $headers = array('Content-Type: text/html; charset=UTF-8'); if (wp_mail( $to, $subject, $body, $headers )) { $response['message'] = esc_html__( "Your Message has been Sent", "whcom" ); } else { $response['message'] = esc_html__( "Something went wrong 4", "whcom" ); } } } } /* if ($response['status'] == 'OK') { $response['message'] = '<div class="whcom_alert whcom_alert_success">' . $response['message'] . '</div>' ; } else { if (!empty($response['errors'])) { $response['message'] = '<div class="whcom_alert whcom_alert_danger">'; $response['message'] .= '<div class="whcom_margin_bottom_15"> ' . esc_html__( "The following errors occurred:", "whcom" ) . ' </div>' ; $response['message'] .= '<ul class="whcom_list_padded_narrow">' ; foreach ($response['errors'] as $error) { $response['message'] .= '<li>' .$error. '</li>' ; } $response['message'] .= '</ul>' ; $response['message'] .= '</div>'; } else { $response['message'] = '<div class="whcom_alert whcom_alert_danger">' . $response['message'] . '</div>'; } }*/ return $response; } common/shortcodes/list_products.php 0000644 00000010431 14736103451 0013623 0 ustar 00 <?php defined( 'ABSPATH' ) or die( "Cannot access pages directly." ); $currency_id = $gids = $pids = $domain_products = ''; extract( shortcode_atts( [ 'currency_id' => ( isset( $_REQUEST['currency'] ) && is_integer( intval( $_REQUEST['currency'] ) ) ) ? $_REQUEST['currency'] : whcom_get_current_currency_id(), 'gids' => '', 'pids' => '', 'domain_products' => (!empty($_REQUEST['dp']) && strtolower($_REQUEST['dp']) == 'yes') ? 'yes' : 'no' ], $atts ) ); $currency_id = whcom_validate_currency_id($currency_id); whcom_update_current_currency($currency_id); $groups = whcom_get_all_products( $gids, $pids ); $groups = ( empty( $groups['groups'] ) ) ? [] : $groups['groups']; if ( empty( $groups ) ) { esc_html_e( 'No Groups/Products Found', 'whcom' ); return; } if ( strtolower( $domain_products ) == 'yes' ) { foreach ( $groups as $key => $group ) { if ( ! empty( $group['products'] ) ) { foreach ( $group['products'] as $p_key => $product ) { if ( $product['showdomainoptions'] != '1' ) { unset( $groups[ $key ]['products'][ $p_key ] ); } } } } } ?> <div class="whcom_op_products whcom_main"> <div class="whcom_tabs_container whcom_tabs_vertical"> <div class="whcom_row"> <div class="whcom_col_sm_3"> <div class="whcom_panel"> <div class="whcom_panel_header whcom_has_icon"> <i class="whcom_icon_basket-1"></i> <?php esc_html_e( "Categories", 'whcom' ) ?> </div> <div class="whcom_panel_body whcom_has_list"> <ul class="whcom_tab_links whcom_list_fancy whcom_list_tabbed whcom_list_padded whcom_list_bordered"> <?php $active = true; foreach ( $groups as $key => $group ) { if ( ! empty( $group['products'] ) ) { $group_unique_id = 'whcom_products_group_' . $key; $active = ( $active ) ? 'active' : ''; echo '<li data-tab="' . $group_unique_id . '" class="whcom_tab_link ' . $active . '">' . $group["name"] . '</li>'; $active = false; } } ?> </ul> </div> </div> </div> <div class="whcom_col_sm_9"> <?php $active = true; foreach ( $groups as $key => $group ) { ?> <?php if ( ! empty( $group['products'] ) ) { ?> <?php $group_unique_id = 'whcom_products_group_' . $key; ?> <?php $active = ( $active ) ? 'active' : ''; ?> <div class="whcom_tabs_content whcom_padding_0_10 <?php echo $active; ?>" id="<?php echo $group_unique_id; ?>"> <div class="whcom_page_heading"><?php echo $group['name']; ?></div> <div class="whcom_op_product_boxes whcom_row"> <?php foreach ( $group['products'] as $product ) { ?> <?php $price = $product['lowest_price']; if ( ( empty( $billing_cycle ) ) && ( ! empty( $product['lowest_price'] ) ) ) { reset( $product['lowest_price'] ); $billing_cycle = key( $product['lowest_price'] ); $price = $product['lowest_price'][$billing_cycle]['price']; } $duration = $billing_cycle; $price = whcom_format_amount( [ 'amount' => $price ] ) ?> <div class="whcom_col_lg_4 whcom_col_md_4 whcom_col_sm_6 whcom_col_xs_12"> <div class="whcom_op_product_box whcom_panel"> <div class="whcom_text_center whcom_panel_header"> <span><?php echo $product["name"] ?></span> </div> <div class="whcom_panel_body"> <div class="whcom_op_product_info"> <?php echo whcom_render_product_price($product); ?> </div> <div class="whcom_op_product_description"> <?php echo nl2br( strip_tags( $product["description"] ) ) ?><br> </div> </div> <div class="whcom_panel_footer whcom_text_center_xs"> <a class="whcom_button whcom_button_success" href="<?php echo whcom_get_order_url(); ?>&a=add&pid=<?php echo $product["id"] ?>"> <i class="whcom_icon_basket"></i> <?php esc_html_e('Order Now', 'whcom')?> </a> </div> </div> </div> <?php } ?> </div> </div> <?php } ?> <?php $active = false; ?> <?php } ?> </div> </div> </div> </div> common/shortcodes/client_update.php 0000644 00000000107 14736103451 0013544 0 ustar 00 <?php defined( 'ABSPATH' ) or die( "Cannot access pages directly." ); common/shortcodes/client_logout.php 0000644 00000000107 14736103451 0013573 0 ustar 00 <?php defined( 'ABSPATH' ) or die( "Cannot access pages directly." ); common/shortcodes/client_login.php 0000644 00000000107 14736103451 0013372 0 ustar 00 <?php defined( 'ABSPATH' ) or die( "Cannot access pages directly." ); common/shortcodes/order_process/02_product.php 0000644 00000022047 14736103451 0015565 0 ustar 00 <?php defined( 'ABSPATH' ) or die( "Cannot access pages directly." ); $product_id = ( ! empty( $product_id ) ) ? (int) $product_id : 0; $billing_cycle = ( ! empty( $_REQUEST['billingcycle'] ) ) ? strtolower( $_REQUEST['billingcycle'] ) : ''; $page_url = ( empty( $page_url ) ) ? home_url( '/' ) : $page_url; if ( $product_id < 1 ) { esc_html_e( 'No valid product_id provided', 'whcom' ); return; } $final_array = whcom_get_product_details( $product_id ); if ( ! $final_array ) { esc_html_e( 'No valid product_id provided', 'whcom' ); return; } if ( ! empty( $final_array['all_prices'] ) && ! empty( $final_array['all_prices'][ $billing_cycle ] ) && ( $final_array['all_prices'][ $billing_cycle ]['price'] < ! 0 ) ) { } else if ( ( empty( $billing_cycle ) ) && ( ! empty( $final_array['lowest_price'] ) ) ) { reset( $final_array['lowest_price'] ); $billing_cycle = key( $final_array['lowest_price'] ); } if ( empty( $billing_cycle ) ) { esc_html_e( 'Product is not valid', 'whcom' ); return; } $free_domain_billingcycles = []; if ( ! empty ( $final_array['freedomainpaymentterms'] ) ) { $free_domain_billingcycles = explode( ',', $final_array['freedomainpaymentterms'] ); } $gids = ( ! empty( $gids ) ) ? $gids : ''; $pids = ( ! empty( $pids ) ) ? $pids : ''; ?> <div id="whcom_page_container" class="whcom_page_container whcom_product_page"> <?php if ( $final_array['showdomainoptions'] == '1' ) { echo whcom_render_product_domain_form(); } ?> <div class="whcom_op_product_container" <?php echo ( $final_array['showdomainoptions'] == '1' ) ? 'style="display: none;"' : ''; ?>> <div class="whcom_page_heading "> <?php esc_html_e( 'Configure', 'whcom' ) ?> </div> <div class="whcom_margin_bottom_15"> <?php esc_html_e( 'Configure your desired options and continue to checkout.', 'whcom' ) ?> </div> <form class="whcom_op_add_product" method="post"> <div class="whcom_row"> <div class="whcom_col_sm_8"> <input type="hidden" name="cart_index" value="-1"> <input type="hidden" name="pid" value="<?php echo $product_id; ?>"> <input type="hidden" name="cid" value="<?php echo whcom_get_current_currency_id(); ?>"> <input type="hidden" name="action" value="whcom_op"> <input type="hidden" name="whcom_op_what" value="add_product"> <input type="hidden" name="required_domain" value="no"> <?php if ( $final_array['showdomainoptions'] == '1' ) { ?> <input type="hidden" name="required_domain" value="yes"> <?php } ?> <div class="whcom_product_details"> <div class="whcom_product_details_container"> <div class="whcom_product_config_description whcom_product_config_block"> <div> <strong><?php echo $final_array['name']; ?></strong> </div> <?php echo $final_array['description']; ?> <?php //$current = ''; //foreach ( $final_array['description_array'] as $des => $det ) { // echo '<div class="whcom_product_feature">'; // echo '<div class="whcom_product_feature_title">' . $det['feature_title'] . '</div>'; // echo '<div class="whcom_product_feature_value">' . $det['feature_value'] . '</div>'; // echo '</div>'; //} ?> </div> <div class="whcom_clearfix"></div> <?php if ( $final_array['paytype'] == 'recurring' ) { ?> <div class="whcom_product_config_block"> <div class="whcom_product_billingcycle whcom_form_field"> <?php $all_prices = $final_array['all_prices']; ?> <?php $billing_cycle_class = 'whcom_form_control whcom_op_input'; if ( ! empty( $final_array['prd_configoptions'] ) ) { $billing_cycle_class .= ' whcom_op_update_product_options'; } ?> <label for="whcom_product_billingcycle" class="main_label"><?php esc_html_e( 'Choose Billing Cycle', 'whcom' ) ?></label> <select id="whcom_product_billingcycle" name="billingcycle" class="<?php echo $billing_cycle_class ?>"> <?php $current = ''; foreach ( $all_prices as $key => $price ) { ( $billing_cycle == $key ) ? $current = 'selected' : $current = ''; $option_string = '<option value="' . $key . '" ' . $current . '>'; $option_string .= whcom_format_amount( [ 'amount' => $price['price'] ] ) . ' ' . whcom_convert_billingcycle($key) . ' + ' . whcom_format_amount( [ 'amount' => $price['setup'] ] ) . ' ' . esc_html__( 'Setup Fee', 'whcom' ); if ( in_array( $key, $free_domain_billingcycles ) ) { $option_string .= ' (' . esc_html__( 'Free Domain', 'whcom' ) . ') '; } $option_string .= '</option>'; echo $option_string; } ?> </select> </div> </div> <?php } ?> <?php if ( $final_array['paytype'] == 'free' ) { ?> <input type="hidden" name="billingcycle" value="<?php echo $billing_cycle ?>"> <?php } ?> <?php if ( $final_array['paytype'] == 'onetime' ) { ?> <input type="hidden" name="billingcycle" value="<?php echo $billing_cycle ?>"> <?php } ?> <?php if ( $final_array['type'] == 'server' ) { ?> <div class="whcom_sub_heading_style_1"> <span><?php esc_html_e( 'Server Options', 'whcom' ) ?></span> </div> <div class="whcom_server_options_container whcom_product_config_block"> <?php echo whcom_render_server_specific_fields(); ?> </div> <?php } ?> <?php if ( ! empty( $final_array['prd_configoptions'] ) ) { ?> <div class="whcom_product_config_block"> <div class="whcom_sub_heading_style_1"> <span><?php esc_html_e( 'Configurable Options', 'whcom' ) ?></span> </div> <div class="whcom_op_product_options_container"> <?php echo whcom_render_product_config_options( $final_array, - 1, $billing_cycle ) ?> </div> </div> <?php } ?> <?php if ( ! empty( $final_array['custom_fields'] ) && is_array( $final_array['custom_fields'] ) ) { ?> <div class="whcom_product_config_block"> <div class="whcom_sub_heading_style_1"> <span> <?php esc_html_e( 'Additional Required Information', 'whcom' ) ?> </span> </div> <div class="whcom_product_custom_fields_container"> <?php echo whcom_render_product_custom_fields( $final_array ); ?> </div> </div> <?php } ?> <?php if ( ! empty( $final_array['prd_addons'] ) && is_array( $final_array['prd_addons'] ) ) { ?> <div class="whcom_product_config_block"> <div class="whcom_sub_heading_style_1"> <span> <?php esc_html_e( 'Available Addons', 'whcom' ) ?> </span> </div> <?php echo whcom_render_product_addons( $final_array ); ?> </div> <?php } ?> <?php if ( $final_array['showdomainoptions'] == '1' ) { ?> <div class="whcom_product_config_block"> <div class="whcom_op_product_domain_config_container"> </div> <div class="whcom_free_tlds" style="display: none;"> <?php if ( ! empty( $final_array['freedomainpaymentterms'] ) && ! empty( $final_array['freedomaintlds'] ) ) { ?> <div class="whcom_alert whcom_alert_info"> <div> <?php esc_html_e( 'Free Domain is only available for following TLD\'s', 'whcom' ) ?> </div> <div><strong><?php echo $final_array['freedomaintlds']; ?></strong> </div> </div> <div class="whcom_alert whcom_alert_info"> <div> <?php esc_html_e( 'Free Domain is only available for following billingcycles...', 'whcom' ) ?> </div> <div> <strong><?php echo whcom_render_free_domain_billingcycles($final_array['freedomainpaymentterms']); ?></strong> </div> </div> <?php } ?> </div> </div> <?php } ?> </div> </div> </div> <div class="whcom_col_sm_4 whcom_sticky_item whcom_padding_0_10"> <div class="whcom_op_product_summary whcom_panel"> <div class="whcom_panel_header"> <?php esc_html_e( 'Order Summary', 'whcom' ) ?> <span class="whcom_icon_spinner-1 whcom_animate_spin whcom_pull_right"></span> </div> <div class="whcom_panel_body"> <div class="whmpress_cart_product_name whcom_text_center"> <strong><?php echo $final_array['name']; ?></strong> </div> <div class="whcom_op_summary_sidebar"> </div> <div class="whcom_text_center whcom_response"> </div> </div> <div class="whcom_panel_footer"> <div class="whcom_op_product_submit whcom_text_center"> <button type="submit" class="whcom_button whcom_button_big"><?php esc_html_e( 'Continue', 'whcom' ) ?></button> </div> </div> </div> </div> </div> </form> </div> </div> common/shortcodes/order_process/01_domains_config.php 0000644 00000004526 14736103451 0017065 0 ustar 00 <?php defined( 'ABSPATH' ) or die( "Cannot access pages directly." ); $curr_domains = []; $curr_cart = whcom_get_cart()['all_items']; $show_ns = false; foreach ( $curr_cart as $index => $item ) { if ( ! empty( $item['domain'] ) ) { $curr_domains[] = [ 'cart_index' => $index, 'domain_name' => $item['domain'], 'domain_type' => $item['domaintype'], 'domain_period' => $item['regperiod'], 'domain_tld' => whcom_get_tld_from_domain( $item['domain'] ), 'has_hosting' => (!empty($item['pid']) && (int)$item['pid'] > 0) ? true : false, 'cart_item' => $item ]; if (empty($item['pid'])) { $show_ns = true; } } } ?> <?php if ( empty( $curr_domains ) ) { ?> <script> var old_url = window.location.href; var new_url = old_url.replace("a=confdomains", "a=view"); window.location.href = new_url </script> <?php } else { ?> <div class="whcom_page_heading "> <?php esc_html_e( 'Domains Configuration', 'whcom' ) ?> </div> <div class="whcom_margin_bottom_15"> <?php esc_html_e( 'Please review your domain name selections and any addons that are available for them.', 'whcom' ) ?> </div> <div class="whcom_op_domains_config_form_container"> <form class="whcom_op_domains_config_form" method="post"> <input type="hidden" name="action" value="whcom_op"> <input type="hidden" name="whcom_op_what" value="domains_config"> <?php foreach ( $curr_domains as $cart_index => $curr_domain ) { ?> <?php $tld_details = whcom_get_tld_details( $curr_domain['domain_tld'] ); $tld_register_prices = $tld_transfer_prices = []; foreach ( $tld_details['register_price'] as $ry => $rp ) { $tld_register_prices[] = [ 'duration' => $ry, 'price' => $rp, ]; } foreach ( $tld_details['transfer_price'] as $ty => $tp ) { $tld_transfer_prices[] = [ 'duration' => $ty, 'price' => $tp, ]; } include WHCOM_PATH . '/shortcodes/order_process/01_domain_config.php'; ?> <?php } ?> <?php if ($show_ns) { echo whcom_render_nameservers(); } ?> <div class="whcom_form_field whcom_form_field_horizontal whcom_text_center_xs"> <button type="submit" class="whcom_op_domains_submit"><?php esc_html_e( 'Continue', 'whcom' ) ?></button> </div> </form> </div> <?php } ?> common/shortcodes/order_process/01_domain.php 0000644 00000007431 14736103451 0015353 0 ustar 00 <?php defined( 'ABSPATH' ) or die( "Cannot access pages directly." ); $domain_price = []; $active_tab = 'register'; $sld = ( isset( $_REQUEST['sld'] ) ) ? esc_attr( $_REQUEST['sld'] ) : ''; $tld = ( isset( $_REQUEST['tld'] ) ) ? esc_attr( $_REQUEST['tld'] ) : ''; $typ = ( isset( $_REQUEST['domain'] ) ) ? esc_attr( $_REQUEST['domain'] ) : ''; $domain = $sld . $tld; if ('transfer' != strtolower($typ)) { $typ = 'register'; } $domains_list = whcom_get_all_tlds(); if ( strtolower($typ) == 'transfer' ) { $active_tab = 'transfer'; } ?> <?php if ( $active_tab == 'register' ) { ?> <div class="whcom_op_domain_search whcom_margin_bottom_45"> <div class="whcom_page_heading "> <?php esc_html_e( 'Register Domain', 'whcom' ) ?> </div> <form class="whcom_op_check_domain <?php echo ( $domain == '' ) ? '' : 'whcom_op_submit_on_load'; ?>" method="post"> <input type="hidden" name="check_domain" value="1"> <input type="hidden" name="domaintype" value="register"> <input type="hidden" name="action" value="whcom_op"> <input type="hidden" name="whcom_op_what" value="check_domain"> <div class="whcom_row whcom_row_no_gap"> <div class="whcom_col_sm_7"> <div class="whcom_form_field"> <input required="required" type="search" name="domain" id="search_domain" title="Domain Name" value="<?php echo $sld; ?>"> </div> </div> <div class="whcom_col_sm_2"> <div class="whcom_form_field"> <select name="ext" title="Select TLD"> <?php if ( is_array( $domains_list ) && ! empty ( $domains_list ) ) { foreach ( $domains_list as $i => $domain ) { $selected = ( $i == $tld ) ? 'selected' : ''; echo '<option value="' . $i . '" ' . $selected . '>' . $i . '</option>'; } } ?> </select> </div> </div> <div class="whcom_col_sm_3"> <div class="whcom_form_field"> <button type="submit" class="whcom_button whcom_button_block"><?php esc_html_e( 'Search', 'whcom' ) ?></button> </div> </div> </div> </form> </div> <?php } else { ?> <div class="whcom_op_domain_search whcom_margin_bottom_45"> <div class="whcom_page_heading "> <?php esc_html_e( 'Transfer Domain', 'whcom' ) ?> </div> <form class="whcom_op_check_domain <?php echo ( $domain == '' ) ? '' : 'whcom_op_submit_on_load'; ?>" method="post"> <input type="hidden" name="check_domain" value="1"> <input type="hidden" name="domaintype" value="transfer"> <input type="hidden" name="action" value="whcom_op"> <input type="hidden" name="whcom_op_what" value="check_domain"> <div class="whcom_row whcom_row_no_gap"> <div class="whcom_col_sm_7"> <div class="whcom_form_field"> <input required="required" type="search" name="domain" id="search_domain" title="Search Domain" value="<?php echo $sld; ?>"> </div> </div> <div class="whcom_col_sm_2"> <div class="whcom_form_field"> <select name="ext" id="search_ext" title="Select TLD"> <?php if ( is_array( $domains_list ) && ! empty ( $domains_list ) ) { foreach ( $domains_list as $i => $domain ) { $selected = ( $i == $tld ) ? 'selected' : ''; echo '<option value="' . $i . '" ' . $selected . '>' . $i . '</option>'; } } ?> </select> </div> </div> <div class="whcom_col_sm_3"> <div class="whcom_form_field"> <button type="submit" class="whcom_button whcom_button_block"><?php esc_html_e( 'Transfer', 'whcom' ) ?></button> </div> </div> </div> </form> </div> <?php } ?> <div class="whcom_op_domain_action_response_text whcom_max_width_640"></div> <div class="whcom_op_domain_action_response_form"></div> common/shortcodes/order_process/01_domain_config.php 0000644 00000006222 14736103451 0016675 0 ustar 00 <?php defined( 'ABSPATH' ) or die( "Cannot access pages directly." ); $curr_domain = ( empty( $curr_domain ) ) ? [] : $curr_domain; $cart_index = ( ! empty( $curr_domain ) && isset( $curr_domain['cart_index'] ) ) ? (int)$curr_domain['cart_index'] : - 1; ?> <?php if ( ! empty( $curr_domain ) && is_array( $curr_domain ) && $cart_index > -1 ) { ?> <div class="whcom_sub_heading_style_1"> <span><?php echo $curr_domain['domain_name']; ?></span> </div> <div class="whcom_op_domain_config_container"> <input type="hidden" name="cart_index" value="<?php echo $cart_index; ?>"> <div class="whcom_row"> <div class="whcom_col_sm_6"> <div class="whcom_form_field whcom_form_field_horizontal"> <label for="whcom_op_product_domainduration" class="main_label"><?php esc_html_e( 'Registration Period', 'whcom' ); ?></label> <select id="whcom_op_product_domainduration" name="domainduration[<?php echo $cart_index; ?>]"> <?php foreach ( $tld_register_prices as $dur ) { $dur_txt = esc_html__( ' For', 'whcom' ); $dur_txt .= $dur['duration']; $dur_txt .= ( $dur['duration'] == 1 ) ? esc_html__( ' Year', 'whcom' ) : esc_html__( ' Years', 'whcom' ); if ( $dur['duration'] == $curr_domain['domain_period'] ) { $selected = 'selected'; } else { $selected = ''; } if ( $dur['price'] < 0 ) { continue; } echo '<option value="' . $dur['duration'] . '" ' . $selected . '>' . whcom_format_amount( [ 'amount' => $dur['price'] ] ) . $dur_txt . '</option>'; if ( $curr_domain['domain_type'] == 'transfer' ) { break; } } ?> </select> </div> </div> <div class="whcom_col_sm_6 whcom_text_small"> <div>Hosting</div> <?php if ( $curr_domain['has_hosting'] ) { ?> <div class="whcom_text_success"> [<?php esc_html_e( "Has Hosting", "whcom" ) ?>] </div> <?php } else { ?> <div class=""> <a href="<?php echo whcom_get_order_url() ?>&dp=yes"> <span class="whcom_text_danger">[<?php esc_html_e( "No Hosting! Click to Add", "whcom" ) ?>]</span> </a> </div> <?php } ?> </div> </div> <div class="whcom_row"> <?php if ( $curr_domain['domain_type'] == 'transfer' ) { ?> <div class="whcom_op_domain_epp whcom_col_sm_6"> <div class="whcom_form_field whcom_form_field_horizontal"> <label for="inputAuthCode" class="main_label">Authorization Code</label> <input type="text" class="form-control" name="eppcode[<?php echo $cart_index; ?>]" id="inputAuthCode" placeholder="Epp Code / Auth Code" required="required" value="<?php echo (!empty($curr_domain['cart_item']['eppcode'])) ? (string)$curr_domain['cart_item']['eppcode'] : ''; ?>"> </div> </div> <?php } ?> </div> <?php if ( $curr_domain['domain_type'] == 'register' ) { echo whcom_render_tld_specific_fields( $curr_domain['domain_tld'], $cart_index, $curr_domain['cart_item'] ); } ?> <?php echo whcom_render_tld_specific_addons( $curr_domain['domain_tld'], $curr_domain['cart_index'], $curr_domain['domain_type'] ) ?> </div> <?php } common/shortcodes/order_process/03_summary_generator.php 0000644 00000102512 14736103451 0017645 0 ustar 00 <?php defined( 'ABSPATH' ) or die( "Cannot access pages directly." ); $cart = whcom_get_cart(); $tax_settings = whcom_get_whmcs_setting(); $tax_rates = whcom_get_tax_levels(); $domain_addons = whcom_get_domain_addons(); $current_discount = whcom_get_current_promo(); $counter = 0; $totals = [ 'discount' => 0.00, 'monthly' => false, 'quarterly' => false, 'semiannually' => false, 'annually' => false, 'biennially' => false, 'triennially' => false, 'base_price' => 0.00, 'l1_amount' => 0.00, 'l2_amount' => 0.00, 'final_price' => 0.00, ]; $summary_html = [ 'full' => '', 'side' => 'Side Summary Start', 'short' => 'Short Summary Started ', 'button' => '', 'discount_message' => '' ]; ob_start(); ?> <ul class="whcom_list whcom_list_padded whcom_list_stripped"> <?php if ( ! empty( $cart['all_items'] ) ) { ?> <?php foreach ( $cart['all_items'] as $cart_index => $cart_item ) { $found_item = $product = $addon = $service = false; $domain_text = $domain_details = []; $tld = ''; $found_array = [ 'product' => false, 'product_domain' => false, 'product_addons' => false, 'product_options' => false, 'product_domain_free' => false, 'addon' => false, 'domain' => false, 'renew_domain' => false, ]; // Check if domain is found if ( ! empty( $cart_item['domain'] ) ) { $tld = whcom_get_tld_from_domain( $cart_item['domain'] ); $domain_details = whcom_get_tld_details( $tld ); if ( ! empty( $domain_details ) ) { if ( ( ! empty( $cart_item['pid'] ) && (int) $cart_item['pid'] > 0 ) ) { $found_array['product_domain'] = true; } else { $found_array['domain'] = true; } $found_item = true; } } // Check if product is found if ( ! empty( $cart_item['pid'] ) && (int) $cart_item['pid'] > 0 ) { $product = whcom_get_product_details( $cart_item['pid'] ); if ( $product ) { $found_array['product'] = $found_item = true; if ( ! empty( $cart_item['configoptions'] ) ) { $found_array['product_options'] = ( empty( $cart_item['configoptions'] ) ) ? [] : $cart_item['configoptions']; } if ( ! empty( $cart_item['addons'] ) ) { $found_array['product_addons'] = ( empty( $cart_item['addons'] ) ) ? [] : explode( ',', (string) $cart_item['addons'] ); } if ( $found_array['product_domain'] ) { $free_domain_billingcycles = explode( ',', $product['freedomainpaymentterms'] ); $free_domain_tlds = explode( ',', $product['freedomaintlds'] ); if ( ( (string) $product['freedomain'] == 'on' || (string) $product['freedomain'] == 'once') && ( in_array( $tld, $free_domain_tlds ) ) && ( in_array( $cart_item['billingcycle'], $free_domain_billingcycles ) ) && ( in_array( $cart_item['billingcycle'], $free_domain_billingcycles ) ) ) { $found_array['product_domain_free'] = true; } } } } if ( ! $found_item ) { continue; } ?> <?php // Product ?> <?php if ( $found_array['product'] ) { ?> <?php $product_price = (float) $product['all_prices'][ $cart_item['billingcycle'] ]['price']; $product_setup = (float) $product['all_prices'][ $cart_item['billingcycle'] ]['setup']; ?> <li> <div class="whcom_op_summary_item_container" id="whcom_op_summary_item_product_<?php echo $cart_item['pid'] ?>"> <a href="#" class="whcom_op_delete_cart_item" data-cart-index="<?php echo $cart_index ?>"><span class="whcom_icon_cancel"></span></a> <div class="whcom_row"> <div class="whcom_col_sm_7"> <strong class="whcom_summary_product_title"><?php echo $product['name']; ?></strong><br> <span class="whcom_text_small whcom_summary_product_group_title"><?php echo $product['group_name']; ?></span> <?php if ( ! empty( $cart_item['domain'] ) ) { ?> <div class="whcom_text_small whcom_summary_product_domain"> <a href="<?php echo $cart_item['domain']; ?>" target="_blank"><?php echo $cart_item['domain']; ?></a> </div> <?php } ?> <?php if ( ! empty( $cart_item['hostname'] ) ) { ?> <div class="whcom_text_small whcom_summary_product_hostname"> <?php echo $cart_item['hostname']; ?> </div> <?php } ?> <?php if ( $found_array['product_options'] ) { ?> <div class="whcom_text_small whcom_summary_product_configoptions whcom_padding_0_10"> <?php foreach ( $found_array['product_options'] as $option_id => $sub_option_id ) { ?> <?php $curr_option = $product['prd_configoptions'][ $option_id ]; $curr_option_html = $curr_option['optionname'] . ' '; $configoption_amount = 0.00; $configoption_setup = 0.00; ?> <?php switch ( $curr_option['optiontype'] ) { case '1' : { $curr_sub_option = $curr_option['sub_options'][ $sub_option_id ]; $curr_option_html .= '(' . whcom_format_amount( $curr_sub_option['all_prices'][ $cart_item['billingcycle'] ]['price'] ) . ')'; $configoption_amount = (float) $curr_sub_option['all_prices'][ $cart_item['billingcycle'] ]['price']; $configoption_setup = (float) $curr_sub_option['all_prices'][ $cart_item['billingcycle'] ]['setup']; break; } case '2' : { $curr_sub_option = $curr_option['sub_options'][ $sub_option_id ]; $curr_option_html .= '(' . whcom_format_amount( $curr_sub_option['all_prices'][ $cart_item['billingcycle'] ]['price'] ) . ')'; $configoption_amount = (float) $curr_sub_option['all_prices'][ $cart_item['billingcycle'] ]['price']; $configoption_setup = (float) $curr_sub_option['all_prices'][ $cart_item['billingcycle'] ]['setup']; break; } case '3' : { $curr_sub_option = reset( $curr_option['sub_options'] ); if ( $sub_option_id > 0 ) { $curr_option_html .= '(' . whcom_format_amount( $curr_sub_option['all_prices'][ $cart_item['billingcycle'] ]['price'] ) . ')'; $configoption_amount = (float) $curr_sub_option['all_prices'][ $cart_item['billingcycle'] ]['price']; $configoption_setup = (float) $curr_sub_option['all_prices'][ $cart_item['billingcycle'] ]['setup']; } break; } case '4' : { $curr_sub_option = reset( $curr_option['sub_options'] ); $curr_option_html .= '('; $curr_option_html .= whcom_format_amount( $curr_sub_option['all_prices'][ $cart_item['billingcycle'] ]['price'] ) . ' x '; $curr_option_html .= $sub_option_id; $curr_option_html .= ')'; $configoption_amount = (float) ( $curr_sub_option['all_prices'][ $cart_item['billingcycle'] ]['price'] * $sub_option_id ); $configoption_setup = (float) ( $curr_sub_option['all_prices'][ $cart_item['billingcycle'] ]['setup'] * $sub_option_id ); break; } default : { $curr_sub_option = []; } } ?> <div class="whcom_summary_product_configoption"> <i class="whcom_icon_angle-double-right"></i> <?php echo $curr_option_html ?> </div> <?php $product_price = $product_price + $configoption_amount; ?> <?php $product_setup = $product_setup + $configoption_setup; ?> <?php } ?> </div> <?php } ?> </div> <div class="whcom_col_sm_5 whcom_text_right"> <div class="whcom_op_summary_item_price whcom_summary_product_price <?php echo ( (float) $product_price > 0 ) ? '' : 'free'; ?>"> <strong><?php echo whcom_format_amount( $product_price ) ?></strong> </div> <div class="whcom_text_small whcom_summary_product_billingcycle"> <?php echo whcom_convert_billingcycle( $cart_item['billingcycle'] ); ?> </div> <div class="whcom_text_small whcom_summary_product_setup <?php echo ( (float) $product_setup > 0 ) ? '' : 'free'; ?>"> <?php echo whcom_format_amount( $product_setup ) ?> <?php esc_html_e( 'Setup Fee', 'whcom' ) ?> </div> </div> <?php // Final Array Population -> product // If product is discounted if ( whcom_validate_item_promotion( 'product', $product['id'], $cart_item['billingcycle'] ) ) { $product_discounts = whcom_apply_item_discount( $product_price, $product_setup ); $product_price = $product_discounts['price']; $product_setup = $product_discounts['setup']; $totals['discount'] = $totals['discount'] + $product_discounts['discount']; } $totals[ $cart_item['billingcycle'] ] = $product_price; $product_total = $product_price + $product_setup; if ( ! empty( $product['tax'] ) && $product['tax'] == '1' ) { $product_total = whcom_calculate_tax( $product_total, $tax_settings ); $totals['base_price'] = $totals['base_price'] + $product_total['base_price']; $totals['l1_amount'] = $totals['l1_amount'] + $product_total['l1_amount']; $totals['l2_amount'] = $totals['l2_amount'] + $product_total['l2_amount']; $totals['final_price'] = $totals['final_price'] + $product_total['final_price']; } else { $totals['base_price'] = $totals['base_price'] + $product_total; $totals['final_price'] = $totals['final_price'] + $product_total; } ?> </div> </div> </li> <?php } ?> <?php // Addons with product ?> <?php if ( $found_array['product_addons'] ) { ?> <?php foreach ( $found_array['product_addons'] as $addon_id ) { ?> <li> <div class="whcom_op_summary_item_container" id="whcom_op_summary_item_product_addon_<?php echo $addon_id ?>"> <div class="whcom_row"> <div class="whcom_col_sm_7"> <?php $curr_addon = $product['prd_addons'][ $addon_id ]; // Addon price logic $addon_billingcycle = strtolower( $curr_addon['billingcycle'] ); if ( $addon_billingcycle == 'recurring' ) { if ( isset( $curr_addon[ $cart_item['billingcycle'] ] ) && $curr_addon[ $cart_item['billingcycle'] ] >= 0 ) { $addon_billingcycle = $cart_item['billingcycle']; $curr_addon_price = $curr_addon['all_prices'][ $addon_billingcycle ]['price']; $curr_addon_setup = $curr_addon['all_prices'][ $addon_billingcycle ]['setup']; } else { reset( $curr_addon['lowest_price'] ); $addon_billingcycle = key( $curr_addon['lowest_price'] ); $curr_addon_price = $curr_addon['lowest_price'][ $addon_billingcycle ]['price']; $curr_addon_setup = $curr_addon['lowest_price'][ $addon_billingcycle ]['setup']; } } elseif ( $addon_billingcycle == 'free' ) { $curr_addon_price = 0.00; $curr_addon_setup = 0.00; } else { $curr_addon_price = $curr_addon['monthly']; $curr_addon_setup = $curr_addon['msetupfee']; } ?> <div> <strong class="whcom_op_summary_item_product_addon_title"><?php echo $curr_addon['name']; ?></strong> <div class="whcom_text_small whcom_op_summary_item_product_addon_label"> <?php esc_html_e( "Addon", "whcom" ); ?> </div> <div class="whcom_text_small whcom_op_summary_item_product_addon_setup"> <?php echo whcom_format_amount( $curr_addon_setup ); ?> <?php esc_html_e( "Setup Fee", "whcom" ); ?> </div> </div> </div> <div class="whcom_col_sm_5 whcom_text_right whcom_op_summary_item_product_addon_price"> <?php echo whcom_format_amount( $curr_addon_price ); ?> <div class="whcom_text_small whcom_op_summary_item_product_addon_billingcycle"> <?php echo whcom_convert_billingcycle( $addon_billingcycle ); ?> </div> </div> <?php // If Product -> Addons is discounted if ( whcom_validate_item_promotion( 'addon', $addon_id, $addon_billingcycle ) ) { $curr_addon_discounts = whcom_apply_item_discount( $curr_addon_price, $curr_addon_setup ); $curr_addon_price = $curr_addon_discounts['price']; $curr_addon_setup = $curr_addon_discounts['setup']; $totals['discount'] = $totals['discount'] + $curr_addon_discounts['discount']; } // Final Array Population -> product-addons $totals[ $addon_billingcycle ] = $totals[ $addon_billingcycle ] + $curr_addon_price; $curr_addon_total = $curr_addon_price + $curr_addon_setup; if ( ! empty( $curr_addon['tax'] ) && $curr_addon['tax'] == '1' ) { $curr_addon_total = whcom_calculate_tax( $curr_addon_total, $tax_settings ); $totals['base_price'] = $totals['base_price'] + $curr_addon_total['base_price']; $totals['l1_amount'] = $totals['l1_amount'] + $curr_addon_total['l1_amount']; $totals['l2_amount'] = $totals['l2_amount'] + $curr_addon_total['l2_amount']; $totals['final_price'] = $totals['final_price'] + $curr_addon_total['final_price']; } else { $totals['base_price'] = $totals['base_price'] + $curr_addon_total; $totals['final_price'] = $totals['final_price'] + $curr_addon_total; } ?> </div> </div> </li> <?php } ?> <?php } ?> <?php // Domain, with or without product ?> <?php if ( ! empty( $domain_details ) ) { ?> <li> <div class="whcom_op_summary_item_container whcom_op_summary_item_domain"> <a href="#" class="whcom_op_delete_cart_item" data-cart-index="<?php echo $cart_index ?>"><span class="whcom_icon_cancel"></span></a> <?php $domain_text = whcom_generate_domain_text( $domain_details, $cart_item['domaintype'], $cart_item['regperiod'], $found_array['product_domain_free'], $cart_index ); $domain_price = (float) $domain_text['price']; ?> <div class="whcom_op_summary_item_domains"> <div class="whcom_row"> <div class="whcom_col_sm_7"> <strong class="whcom_op_summary_item_domain_label"><?php esc_html_e( 'Domain ', 'whcom' ) ?><?php echo ( $cart_item['domaintype'] == 'register' ) ? esc_html__( "Registration", "whcom" ) : ucfirst( $cart_item['domaintype'] ); ?></strong> <div class="whcom_text_small whcom_op_summary_item_domain_link"> <a href="<?php echo $cart_item['domain']; ?>" target="_blank"><?php echo $cart_item['domain']; ?></a> </div> <div class="whcom_text_small whcom_padding_0_10 whcom_op_summary_item_domain_addons"> <?php if ( ! empty( $cart_item['dnsmanagement'] ) ) { ?> <?php $domain_addon_price = (float) $domain_addons['dnsmanagement'] * (float) $cart_item['regperiod']; ?> <div class="whcom_op_summary_item_domain_dnsmanagement"><i class="whcom_icon_angle-double-right"></i> <?php esc_html_e( "DNS Management", 'whcom' ) ?> - <?php echo whcom_format_amount( $domain_addon_price ) . ' ' . $domain_text['text']; ?> </div> <?php $domain_price = $domain_price + (float) $domain_addon_price; ?> <?php } ?> <?php if ( ! empty( $cart_item['emailforwarding'] ) ) { ?> <?php $domain_addon_price = (float) $domain_addons['emailforwarding'] * (float) $cart_item['regperiod']; ?> <div class="whcom_op_summary_item_domain_emailforwarding"><i class="whcom_icon_angle-double-right"></i> <?php esc_html_e( "Email Forwarding", 'whcom' ) ?> - <?php echo whcom_format_amount( $domain_addon_price ) . ' ' . $domain_text['text']; ?> </div> <?php $domain_price = $domain_price + (float) $domain_addon_price; ?> <?php } ?> <?php if ( ! empty( $cart_item['idprotection'] ) ) { ?> <?php $domain_addon_price = (float) $domain_addons['idprotection'] * (float) $cart_item['regperiod']; ?> <div class="whcom_op_summary_item_domain_idprotection"><i class="whcom_icon_angle-double-right"></i> <?php esc_html_e( "ID Protection", 'whcom' ) ?> - <?php echo whcom_format_amount( $domain_addon_price ) . ' ' . $domain_text['text']; ?> </div> <?php $domain_price = $domain_price + (float) $domain_addon_price; ?> <?php } ?> </div> </div> <div class="whcom_col_sm_5 whcom_text_right whcom_op_summary_item_domain_price"> <?php echo whcom_format_amount( $domain_price ); ?> <div class="whcom_op_summary_item_domain_text"> <?php echo strtolower( $domain_text['text'] ); ?> </div> </div> </div> </div> <?php // If Domain is discounted if ( whcom_validate_item_promotion( 'domain', $domain_details['extension'], $cart_item['regperiod'] ) ) { $domain_discounts = whcom_apply_item_discount( $domain_price, 0.00 ); $domain_price = $domain_discounts['price']; $totals['discount'] = $totals['discount'] + $domain_discounts['discount']; } // Final Array Population -> domain if ( ! empty( $tax_settings['TaxDomains'] ) && $tax_settings['TaxDomains'] == 'on' ) { $domain_price = whcom_calculate_tax( $domain_price, $tax_settings ); $totals['base_price'] = $totals['base_price'] + $domain_price['base_price']; $totals['l1_amount'] = $totals['l1_amount'] + $domain_price['l1_amount']; $totals['l2_amount'] = $totals['l2_amount'] + $domain_price['l2_amount']; $totals['final_price'] = $totals['final_price'] + $domain_price['final_price']; } else { $totals['base_price'] = $totals['base_price'] + $domain_price; $totals['final_price'] = $totals['final_price'] + $domain_price; } ?> </div> </li> <?php } ?> <?php $counter ++; ?> <?php ob_start() ?> Sidebar Short Appended <?php $summary_html['short'] .= ob_get_clean(); ?> <?php } ?> <?php } ?> <?php if ( ! empty( $cart['order_specific']['domainrenewals'] ) ) { ?> <?php foreach ( $cart['order_specific']['domainrenewals'] as $renew_domain => $renew_period ) { $renew_domain_tld = whcom_get_tld_from_domain( $renew_domain ); $renew_domain_details = whcom_get_tld_details( $renew_domain_tld ); $domain_args = [ "action" => "GetClientsDomains", "domain" => $renew_domain, ]; $domain_service_details = false; $domain_service_details_raw = whcom_process_api($domain_args); if (!empty($domain_service_details_raw['result']) && (string)$domain_service_details_raw['result'] == 'success') { foreach ($domain_service_details_raw['domains']['domain'] as $domain_service_details_raw_domain) { if ($domain_service_details_raw_domain['status'] == 'Active') { $domain_service_details = $domain_service_details_raw_domain; break; } } } ?> <?php if ( $renew_domain_details && $domain_service_details) { ?> <li> <div class="whcom_op_summary_item_container whcom_op_summary_item_domain_renewal" > <?php $domain_renewal_text = whcom_generate_domain_text( $renew_domain_details, 'renew', $renew_period, false, 0 ); $domain_renewal_price = (float) $domain_renewal_text['price']; ?> <div class="whcom_op_summary_item_domains"> <div class="whcom_row"> <div class="whcom_col_sm_7"> <strong class="whcom_op_summary_item_domain_renewal_label"><?php esc_html_e( 'Domain Renewal', 'whcom' ) ?></strong> <div class="whcom_text_small whcom_op_summary_item_domain_renewal_link"> <a href="<?php echo $renew_domain; ?>" target="_blank"><?php echo $renew_domain; ?></a> </div> <div class="whcom_text_small whcom_op_summary_item_domain_renewal_addons whcom_padding_0_10"> <?php if ( ! empty( $domain_service_details['dnsmanagement'] ) ) { ?> <?php $domain_addon_price = (float) $domain_addons['dnsmanagement'] * (float) $renew_period; ?> <div class="whcom_op_summary_item_domain_renewal_dnsmanagement"><i class="whcom_icon_angle-double-right"></i> <?php esc_html_e( "DNS Management", 'whcom' ) ?> - <?php echo whcom_format_amount( $domain_addon_price ) . ' ' . $domain_text['text']; ?> </div> <?php $domain_renewal_price = $domain_renewal_price + (float) $domain_addon_price; ?> <?php } ?> <?php if ( ! empty( $domain_service_details['emailforwarding'] ) ) { ?> <?php $domain_addon_price = (float) $domain_addons['emailforwarding'] * (float) $renew_period; ?> <div class="whcom_op_summary_item_domain_renewal_emailforwarding"><i class="whcom_icon_angle-double-right"></i> <?php esc_html_e( "Email Forwarding", 'whcom' ) ?> - <?php echo whcom_format_amount( $domain_addon_price ) . ' ' . $domain_text['text']; ?> </div> <?php $domain_renewal_price = $domain_renewal_price + (float) $domain_addon_price; ?> <?php } ?> <?php if ( ! empty( $domain_service_details['idprotection'] ) ) { ?> <?php $domain_addon_price = (float) $domain_addons['idprotection'] * (float) $renew_period; ?> <div class="whcom_op_summary_item_domain_renewal_idprotection"><i class="whcom_icon_angle-double-right"></i> <?php esc_html_e( "ID Protection", 'whcom' ) ?> - <?php echo whcom_format_amount( $domain_addon_price ) . ' ' . $domain_text['text']; ?> </div> <?php $domain_renewal_price = $domain_renewal_price + (float) $domain_addon_price; ?> <?php } ?> </div> </div> <div class="whcom_col_sm_5 whcom_text_right whcom_op_summary_item_domain_renewal_price"> <?php echo whcom_format_amount( $domain_renewal_price ); ?> <div class="whcom_op_summary_item_domain_renewal_text"> <?php echo strtolower( $domain_renewal_text['text'] ); ?> </div> </div> </div> </div> <?php // If Domain is discounted if ( whcom_validate_item_promotion( 'domain', $renew_domain_tld, $renew_period ) ) { $domain_renewal_discounts = whcom_apply_item_discount( $domain_renewal_price, 0.00 ); $domain_renewal_price = $domain_renewal_discounts['price']; $totals['discount'] = $totals['discount'] + $domain_renewal_discounts['discount']; } // Final Array Population -> domain_renewal if ( ! empty( $tax_settings['TaxDomains'] ) && $tax_settings['TaxDomains'] == 'on' ) { $domain_renewal_price = whcom_calculate_tax( $domain_renewal_price, $tax_settings ); $totals['base_price'] = $totals['base_price'] + $domain_renewal_price['base_price']; $totals['l1_amount'] = $totals['l1_amount'] + $domain_renewal_price['l1_amount']; $totals['l2_amount'] = $totals['l2_amount'] + $domain_renewal_price['l2_amount']; $totals['final_price'] = $totals['final_price'] + $domain_renewal_price['final_price']; } else { $totals['base_price'] = $totals['base_price'] + $domain_renewal_price; $totals['final_price'] = $totals['final_price'] + $domain_renewal_price; } ?> </div> <?php $counter ++; ?> </li> <?php } ?> <?php } ?> <?php } ?> <?php // Addon only order if ( ! empty( $cart['order_specific']['addonids'] ) && is_array( $cart['order_specific']['addonids'] ) && ! empty( $cart['order_specific']['serviceids'] ) && is_array( $cart['order_specific']['serviceids'] )) { $addon_ids = $cart['order_specific']['addonids']; $service_ids = $cart['order_specific']['serviceids']; if (count($addon_ids) == count($service_ids)) { foreach ($addon_ids as $addon_index => $addon_id) { $addon = whcom_get_addon_details( $addon_id ); $service = whcom_get_service_details( $service_ids[$addon_index] ); if ( ! empty( $addon ) && ! empty( $service ) ) { ?> <?php // Addon only ?> <li> <?php // Addon price logic $addon_duration = strtolower( $addon['billingcycle'] ); $service_duration = strtolower( $service['billingcycle'] ); if ( $addon_duration == 'recurring' ) { if ( isset( $curr_addon[ $cart_item['billingcycle'] ] ) && $addon[ $service_duration ] >= 0 ) { $addon_duration = $service_duration; $addon_price = $addon['all_prices'][ $addon_duration ]['price']; $addon_setup = $addon['all_prices'][ $addon_duration ]['setup']; } else { reset( $addon['lowest_price'] ); $addon_duration = key( $addon['lowest_price'] ); $addon_price = $addon['lowest_price'][ $addon_duration ]['price']; $addon_setup = $addon['lowest_price'][ $addon_duration ]['setup']; } } elseif ( $addon_duration == 'free' ) { $addon_price = 0.00; $addon_setup = 0.00; } else { $addon_price = $addon['monthly']; $addon_setup = $addon['msetupfee']; } ?> <div class="whcom_op_summary_item_container" id="whcom_op_summary_item_addon_<?php echo $addon_id ?>"> <div class="whcom_row"> <div class="whcom_col_sm_7"> <div class="whcom_op_summary_item_addon_title"><strong><?php echo $addon['name']; ?></strong></div> <?php if ( ! empty( $service['name'] ) ) { ?> <div class="whcom_text_small whcom_op_summary_item_addon_service_title"><?php echo $service['name']; ?></div> <?php } ?> <?php if ( ! empty( $service['domain'] ) ) { ?> <div class="whcom_text_small whcom_op_summary_item_addon_domain"><?php echo $service['domain']; ?></div> <?php } ?> </div> <div class="whcom_col_sm_5 whcom_text_right"> <div class="whcom_op_summary_item_price whcom_op_summary_item_addon_price"> <strong class=""> <?php echo whcom_format_amount( $addon_price ) ?> + <?php echo whcom_format_amount( $addon_setup ) ?> <?php esc_html_e( 'Setup Fee', 'whcom' ) ?> </strong> </div> <div class="whcom_text_small whcom_op_summary_item_addon_billingcycle"> <?php echo whcom_convert_billingcycle( $addon_duration ); ?> </div> </div> </div> </div> <?php // If Addons (Addon only) is discounted if ( whcom_validate_item_promotion( 'addon', $addon['id'], $addon_duration ) ) { $addon_discounts = whcom_apply_item_discount( $addon_price, $addon_setup ); $addon_price = $addon_discounts['price']; $addon_setup = $addon_discounts['setup']; $totals['discount'] = $totals['discount'] + $addon_discounts['discount']; } // Final Array Population -> addon-only $totals[ $addon_duration ] = $totals[ $addon_duration ] + $addon_price; $addon_total = $addon_price + $addon_setup; if ( ! empty( $addon['tax'] ) && $addon['tax'] == '1' ) { $addon_total = whcom_calculate_tax( $addon_total, $tax_settings ); $totals['base_price'] = $totals['base_price'] + $addon_total['base_price']; $totals['l1_amount'] = $totals['l1_amount'] + $addon_total['l1_amount']; $totals['l2_amount'] = $totals['l2_amount'] + $addon_total['l2_amount']; $totals['final_price'] = $totals['final_price'] + $addon_total['final_price']; } else { $totals['base_price'] = $totals['base_price'] + $addon_total; $totals['final_price'] = $totals['final_price'] + $addon_total; } ?> </li> <?php $counter ++; ?> <?php } } } } ?> </ul> <?php if ( $counter < 1 ) { ?> <div class="whcom_alert whcom_alert_info"> <?php esc_html_e( 'Your Shopping Cart is Empty', 'whcom' ); ?> </div> <?php } ?> <?php ob_start() ?> <div class="whcom_cart_summary_sidebar"> <div class="whcom_clearfix whcom_margin_bottom_5 whcom_padding_5_0 whcom_bordered_bottom"> <span class="whcom_pull_left"><?php esc_html_e( 'Subtotal', 'whcom' ) ?></span> <span class="whcom_pull_right"><?php echo whcom_format_amount( $totals['base_price'] + $totals['discount'] ); ?></span> </div> <?php if ( $totals['l1_amount'] > 0 || $totals['l2_amount'] > 0 || $totals['discount'] > 0 ) { ?> <div class="whcom_padding_10_0 whcom_bordered_bottom"> <?php if ( $totals['discount'] > 0 ) { ?> <?php $discount_value = $current_discount['value']; $discount_type = esc_html__( "One Time Discount", "whcom" ); if ( $current_discount['recurring'] == '1' ) { $discount_type = esc_html__( "Recurring Discount", "whcom" ); } switch ( $current_discount['type'] ) { case 'Free Setup' : { $discount_value = esc_html__( "Free Setup", "whcom" ); break; } case 'Fixed Amount' : { $discount_value = whcom_format_amount( $current_discount['value'] ); break; } case 'Price Override' : { $discount_value = whcom_format_amount( $current_discount['value'] ) . ' ' . esc_html__( "Price Override", "whcom" ); break; } case 'Percentage' : { $discount_value = $current_discount['value'] . esc_html__( "%", "whcom" ); break; } default : { } } ?> <?php ?> <div class="whcom_clearfix"> <span class="whcom_pull_left"> <?php echo $discount_value; ?> <?php echo $discount_type; ?> </span> <span class="whcom_pull_right"><?php echo whcom_format_amount( $totals['discount'] ); ?></span> </div> <?php } ?> <?php if ( $totals['l1_amount'] > 0 ) { ?> <div class="whcom_clearfix"> <span class="whcom_pull_left"><?php echo $tax_rates['level1_title'] ?> @ <?php echo $tax_rates['level1_rate'] ?>%</span> <span class="whcom_pull_right"><?php echo whcom_format_amount( $totals['l1_amount'] ); ?></span> </div> <?php } ?> <?php if ( $totals['l2_amount'] > 0 ) { ?> <div class="whcom_clearfix"> <span class="whcom_pull_left"><?php echo $tax_rates['level2_title'] ?> @ <?php echo $tax_rates['level2_rate'] ?>%</span> <span class="whcom_pull_right"><?php echo whcom_format_amount( $totals['l2_amount'] ); ?></span> </div> <?php } ?> </div> <?php } ?> <div class="whcom_clearfix whcom_margin_bottom_30"> <span class="whcom_pull_left"><?php esc_html_e( 'Totals', 'whcom' ) ?></span> <?php $billingcycles = [ 'monthly', 'quarterly', 'semiannually', 'annually', 'biennially', 'triennially', ]; ?> <?php foreach ( $totals as $key => $total ) { ?> <?php if ( $total && in_array( $key, $billingcycles ) ) { ?> <div class="whcom_clearfix"> <div class="whcom_pull_right"> <span><?php echo whcom_format_amount( $total ); ?></span> <span><?php echo whcom_convert_billingcycle( $key ) ?></span> </div> </div> <?php } ?> <?php } ?> </div> <div class="whcom_clearfix"> <div class="whcom_text_right whcom_text_2x"> <?php echo whcom_format_amount( ['amount' => $totals['final_price'], 'add_suffix' => 'yes'] ); ?> </div> <div class="whcom_text_right"><?php esc_html_e( 'Total Due Today', 'whcom' ) ?></div> </div> </div> <?php $summary_html['side'] = ob_get_clean(); ?> <?php ob_start(); ?> <?php if ( ! empty ( $cart['order_specific']['promocode'] ) ) { ?> <?php if ( (float) $totals['discount'] > 0.00 ) { ?> <div class="whcom_alert whcom_alert_success"> <?php esc_html_e( "Promotion Code Accepted! Your order total has been updated.", "whcom" ) ?> </div> <?php } else if ( ! empty( $current_discount ) ) { ?> <div class="whcom_alert whcom_alert_info"> <?php esc_html_e( "The promotion code you entered has been applied to your cart but no items qualify for the discount yet - please check the promotion terms", "whcom" ) ?> </div> <?php } else { ?> <div class="whcom_alert whcom_alert_warning"> <?php esc_html_e( "The promotion code entered does not exist", "whcom" ) ?> </div> <?php } ?> <?php } ?> <?php $summary_html['discount_message'] = ob_get_clean(); ?> <?php $summary_html['status'] = 'OK'; $summary_html['message'] = esc_html__( 'Updating Cart Summaries', 'whcom' ); $summary_html['detailed'] = ob_get_clean(); $summary_html['total_items'] = $counter; $summary_html['grand_total'] = whcom_format_amount( $totals['final_price'] ); ?> common/shortcodes/order_process/03_summary_generator_backup.php 0000644 00000062213 14736103451 0021175 0 ustar 00 <?php defined( 'ABSPATH' ) or die( "Cannot access pages directly." ); $cart = whcom_get_cart(); $tax_settings = whcom_get_whmcs_setting(); $domain_addons = whcom_get_domain_addons(); $counter = 0; $totals = [ 'prod_total' => 0.00, 'prod_setup' => 0.00, 'addon_setup' => 0.00, // Addon only 'addon_total' => 0.00, // Addon only 'addons_setup' => 0.00, // Product Addons 'addons_total' => 0.00, // Product Addons 'domain' => 0.00, 'grand' => 0.00, 'discount' => 0.00, // recurring 'monthly' => false, 'quarterly' => false, 'semiannually' => false, 'annually' => false, 'biennially' => false, 'triennially' => false, 'base_price' => 0.00, 'l1_rate' => 0.00, 'l2_rate' => 0.00, 'l1_amount' => 0.00, 'l2_amount' => 0.00, 'final_price' => 0.00, ]; $summary_html = [ 'full' => '', 'side' => 'Side Summary Start', 'short' => 'Short Summary Started ', 'button' => '', ]; ob_start(); ?> <ul class="whcom_list whcom_list_padded whcom_list_stripped"> <?php if ( ! empty( $cart['all_items'] ) ) { ?> <?php foreach ( $cart['all_items'] as $cart_index => $cart_item ) { $found_item = $product = $addons = $addon = $service = false; $domain_text = $domain_details = []; $item_totals = [ 'prod_total' => 0.00, 'prod_setup' => 0.00, 'addon_setup' => 0.00, // Addon only 'addon_total' => 0.00, // Addon only 'addons_setup' => 0.00, // Product Addons 'addons_total' => 0.00, // Product Addons 'domain' => 0.00, 'grand' => 0.00, 'discount' => 0.00, // recurring 'monthly' => false, 'quarterly' => false, 'semiannually' => false, 'annually' => false, 'biennially' => false, 'triennially' => false, ]; $tld = ''; $found_array = [ 'product' => false, 'product_domain' => false, 'product_addons' => false, 'product_options' => false, 'product_domain_free' => false, 'addon' => false, 'domain' => false, 'renew_domain' => false, ]; // Check if domain is found if ( ! empty( $cart_item['domain'] ) ) { $tld = whcom_get_tld_from_domain( $cart_item['domain'] ); $domain_details = whcom_get_tld_details( $tld ); if ( ! empty( $domain_details ) ) { if ( ( ! empty( $cart_item['pid'] ) && (int) $cart_item['pid'] > 0 ) ) { $found_array['product_domain'] = true; } else { $found_array['domain'] = true; } $found_item = true; } } // Check if product is found if ( ! empty( $cart_item['pid'] ) && (int) $cart_item['pid'] > 0 ) { $product = whcom_get_product_details( $cart_item['pid'] ); if ( $product ) { $found_array['product'] = $found_item = true; if ( ! empty( $cart_item['configoptions'] ) ) { $found_array['product_options'] = ( empty( $cart_item['configoptions'] ) ) ? [] : $cart_item['configoptions']; } if ( ! empty( $cart_item['addons'] ) ) { $found_array['product_addons'] = ( empty( $cart_item['addons'] ) ) ? [] : explode( ',', (string) $cart_item['addons'] ); } if ( $found_array['product_domain'] ) { $free_domain_billingcycles = explode( ',', $product['freedomainpaymentterms'] ); $free_domain_tlds = explode( ',', $product['freedomaintlds'] ); if ( ( (string) $product['freedomain'] == 'on' || (string) $product['freedomain'] == 'once') && ( in_array( $tld, $free_domain_tlds ) ) && ( in_array( $cart_item['billingcycle'], $free_domain_billingcycles ) ) && ( in_array( $cart_item['billingcycle'], $free_domain_billingcycles ) ) ) { $found_array['product_domain_free'] = true; } } } } // Addon only order if ( ! empty( $cart_item['addonids'] ) && ! empty( $cart_item['serviceids'] ) ) { $addon = whcom_get_addon_details( $cart_item['addonids'] ); $service = whcom_get_service_details( $cart_item['serviceids'] ); if ( ! empty( $addon ) && ! empty( $service ) ) { $addon_duration = ( ! empty( $service['billingcycle'] ) ) ? strtolower( $service['billingcycle'] ) : ''; if ( empty( $addon['all_prices'][ $addon_duration ] ) ) { reset( $addon['all_prices'] ); $addon_duration = key( $product['all_prices'] ); } $addon_duration = ( $addon_duration == 'recurring' ) ? 'monthly' : $addon_duration; $found_array['addon'] = $found_item = true; } } if ( ! $found_item ) { continue; } $cart_item = whcom_calculate_promotion( $cart_item ); $discounted_domain = ( ! empty( $cart_item['domain_discount'] ) && (float) $cart_item['domain_discount']['value'] > 0.00 ) ? true : false; $discounted_product = ( ! empty( $cart_item['product_discount'] ) && (float) $cart_item['product_discount']['value'] > 0.00 ) ? true : false; ?> <?php // Product ?> <?php if ( $found_array['product'] ) { ?> <?php $item_totals['prod_total'] = $product['all_prices'][ $cart_item['billingcycle'] ]['price']; $item_totals['prod_setup'] = $product['all_prices'][ $cart_item['billingcycle'] ]['setup']; ?> <li> <div class="whcom_op_summary_item_container"> <a href="#" class="whcom_op_delete_cart_item" data-cart-index="<?php echo $cart_index ?>"><span class="whcom_icon_cancel"></span></a> <div class="whcom_row"> <div class="whcom_col_sm_7"> <strong><?php echo $product['name']; ?></strong><br> <span class="whcom_text_small"><?php echo $product['group_name']; ?></span> <?php if ( ! empty( $cart_item['domain'] ) ) { ?> <div class="whcom_text_small"> <a href="<?php echo $cart_item['domain']; ?>" target="_blank"><?php echo $cart_item['domain']; ?></a> </div> <?php } ?> <?php if ( ! empty( $cart_item['hostname'] ) ) { ?> <div class="whcom_text_small"> <?php echo $cart_item['hostname']; ?> </div> <?php } ?> <?php if ( $found_array['product_options'] ) { ?> <div class="whcom_text_small whcom_padding_0_10"> <?php foreach ( $found_array['product_options'] as $option_id => $sub_option_id ) { ?> <?php $curr_option = $product['prd_configoptions'][ $option_id ]; $curr_option_html = $curr_option['optionname'] . ' '; $configoption_amount = 0.00; $configoption_setup = 0.00; ?> <?php switch ( $curr_option['optiontype'] ) { case '1' : { $curr_sub_option = $curr_option['sub_options'][ $sub_option_id ]; $curr_option_html .= '(' . whcom_format_amount( $curr_sub_option['all_prices'][ $cart_item['billingcycle'] ]['price'] ) . ')'; $configoption_amount = (float) $curr_sub_option['all_prices'][ $cart_item['billingcycle'] ]['price']; $configoption_setup = (float) $curr_sub_option['all_prices'][ $cart_item['billingcycle'] ]['setup']; break; } case '2' : { $curr_sub_option = $curr_option['sub_options'][ $sub_option_id ]; $curr_option_html .= '(' . whcom_format_amount( $curr_sub_option['all_prices'][ $cart_item['billingcycle'] ]['price'] ) . ')'; $configoption_amount = (float) $curr_sub_option['all_prices'][ $cart_item['billingcycle'] ]['price']; $configoption_setup = (float) $curr_sub_option['all_prices'][ $cart_item['billingcycle'] ]['setup']; break; } case '3' : { $curr_sub_option = reset( $curr_option['sub_options'] ); if ( $sub_option_id > 0 ) { $curr_option_html .= '(' . whcom_format_amount( $curr_sub_option['all_prices'][ $cart_item['billingcycle'] ]['price'] ) . ')'; $configoption_amount = (float) $curr_sub_option['all_prices'][ $cart_item['billingcycle'] ]['price']; $configoption_setup = (float) $curr_sub_option['all_prices'][ $cart_item['billingcycle'] ]['setup']; } break; } case '4' : { $curr_sub_option = reset( $curr_option['sub_options'] ); $curr_option_html .= '('; $curr_option_html .= whcom_format_amount( $curr_sub_option['all_prices'][ $cart_item['billingcycle'] ]['price'] ) . ' x '; $curr_option_html .= $sub_option_id; $curr_option_html .= ')'; $configoption_amount = (float) ( $curr_sub_option['all_prices'][ $cart_item['billingcycle'] ]['price'] * $sub_option_id ); $configoption_setup = (float) ( $curr_sub_option['all_prices'][ $cart_item['billingcycle'] ]['setup'] * $sub_option_id ); break; } default : { $curr_sub_option = []; } } ?> <div> <i class="whcom_icon_angle-double-right"></i> <?php echo $curr_option_html ?> </div> <?php $item_totals['prod_total'] = $item_totals['prod_total'] + $configoption_amount; ?> <?php $item_totals['prod_setup'] = $item_totals['prod_setup'] + $configoption_setup; ?> <?php } ?> </div> <?php } ?> </div> <div class="whcom_col_sm_5 whcom_text_right"> <div class="whcom_op_summary_item_price whcom_summary_product_price <?php echo ((float) $setup_price > 0) ? '' : 'free'; ?>"> <strong><?php echo whcom_format_amount( $item_totals['prod_total'] ) ?></strong> </div> <div class="whcom_text_small"> <?php echo whcom_convert_billingcycle( $cart_item['billingcycle'] ); ?> </div> <div class="whcom_text_small whcom_summary_product_setup <?php echo ((float)$setup_price > 0) ? '' : 'free'; ?>"> <?php echo whcom_format_amount( $item_totals['prod_setup'] ) ?> <?php esc_html_e( 'Setup Fee', 'whcom' ) ?> </div> </div> <?php $item_totals[$cart_item['billingcycle']] = $item_totals['prod_total'] ?> </div> </div> </li> <?php } ?> <?php // Addons with product ?> <?php if ( $found_array['product_addons'] ) { ?> <?php foreach ( $found_array['product_addons'] as $addon_id ) { ?> <li> <div class="whcom_op_summary_item_container"> <div class="whcom_row"> <div class="whcom_col_sm_7"> <?php $curr_addon = $product['prd_addons'][ $addon_id ]; // Addon price logic $addon_billingcycle = strtolower( $curr_addon['billingcycle'] ); if ( $addon_billingcycle == 'recurring' ) { if ( isset( $curr_addon[ $cart_item['billingcycle'] ] ) && $curr_addon[ $cart_item['billingcycle'] ] >= 0 ) { $addon_billingcycle = $cart_item['billingcycle']; $curr_addon_price = $curr_addon['all_prices'][ $addon_billingcycle ]['price']; $curr_addon_setup = $curr_addon['all_prices'][ $addon_billingcycle ]['setup']; } else { reset( $curr_addon['lowest_price'] ); $addon_billingcycle = key( $curr_addon['lowest_price'] ); $curr_addon_price = $curr_addon['lowest_price'][ $addon_billingcycle ]['price']; $curr_addon_setup = $curr_addon['lowest_price'][ $addon_billingcycle ]['setup']; } } elseif ($addon_billingcycle == 'free') { $curr_addon_price = 0.00; $curr_addon_setup = 0.00; } else { $curr_addon_price = $curr_addon['monthly']; $curr_addon_setup = $curr_addon['msetupfee']; } $item_totals['addons_total'] = $item_totals['addons_total'] + $curr_addon_price; $item_totals['addons_setup'] = $item_totals['addons_setup'] + $curr_addon_setup; $item_totals[$addon_billingcycle] = $item_totals[$addon_billingcycle] + $curr_addon_price; ?> <div> <strong><?php echo $curr_addon['name']; ?></strong> <div class="whcom_text_small"> <?php esc_html_e( "Addon", "whcom" ); ?> </div> <div class="whcom_text_small"> <?php echo whcom_format_amount( $curr_addon_setup ); ?> <?php esc_html_e( "Setup Fee", "whcom" ); ?> </div> </div> </div> <div class="whcom_col_sm_5 whcom_text_right"> <?php echo whcom_format_amount( $curr_addon_price ); ?> <div class="whcom_text_small"> <?php echo whcom_convert_billingcycle( $addon_billingcycle ); ?> </div> </div> </div> </div> </li> <?php } ?> <?php } ?> <?php // Addon only ?> <?php if ( $found_array['addon'] ) { ?> <li> <?php // Addon price logic $addon_duration = strtolower( $addon['billingcycle'] ); $service_duration = strtolower( $service['billingcycle'] ); if ( $addon_duration == 'recurring' ) { if ( isset( $addon[ $service_duration ] ) && $addon[ $service_duration ] >= 0 ) { $addon_price = $addon['all_prices'][ $service_duration ]['price']; $addon_setup = $addon['all_prices'][ $service_duration ]['setup']; } else { reset( $addon['lowest_price'] ); $addon_duration = key( $addon['lowest_price'] ); $addon_price = $addon['lowest_price']['price']; $addon_setup = $addon['lowest_price']['setup']; } } else { $addon_price = $addon['monthly']; $addon_setup = $addon['msetupfee']; } $item_totals['addon_total'] = $item_totals['addon_total'] + $addon_price; $item_totals['addon_setup'] = $item_totals['addon_setup'] + $addon_setup; $item_totals[$addon_duration] = $item_totals[$addon_duration] + $addon_price; ?> <div class="whcom_op_summary_item_container"> <a href="#" class="whcom_op_delete_cart_item" data-cart-index="<?php echo $cart_index ?>"><span class="whcom_icon_cancel"></span></a> <div class="whcom_row"> <div class="whcom_col_sm_7"> <div><strong><?php echo $addon['name']; ?></strong></div> <?php if ( ! empty( $service['name'] ) ) { ?> <div class="whcom_text_small"><?php echo $service['name']; ?></div> <?php } ?> <?php if ( ! empty( $service['domain'] ) ) { ?> <div class="whcom_text_small"><?php echo $service['domain']; ?></div> <?php } ?> </div> <div class="whcom_col_sm_5 whcom_text_right"> <div class="whcom_op_summary_item_price"> <strong> <?php echo whcom_format_amount( $addon_price ) ?> + <?php echo whcom_format_amount( $addon_setup ) ?> <?php esc_html_e( 'Setup Fee', 'whcom' ) ?> </strong> </div> <div class="whcom_text_small"> <?php echo whcom_convert_billingcycle( $service_duration ); ?> </div> </div> </div> </div> </li> <?php } ?> <?php // Domain, with or without product ?> <?php if ( ! empty( $domain_details ) ) { ?> <li> <div class="whcom_op_summary_item_container"> <a href="#" class="whcom_op_delete_cart_item" data-cart-index="<?php echo $cart_index ?>"><span class="whcom_icon_cancel"></span></a> <?php $domain_text = whcom_generate_domain_text( $domain_details, $cart_item['domaintype'], $cart_item['regperiod'], $found_array['product_domain_free'], $cart_index ); $item_totals['domain'] = (float) $domain_text['price']; ?> <div class="whcom_op_summary_item_domains"> <div class="whcom_row"> <div class="whcom_col_sm_7"> <strong><?php esc_html_e( 'Domain ', 'whcom' ) ?><?php echo ( $cart_item['domaintype'] == 'register' ) ? esc_html__( "Registration", "whcom" ) : ucfirst( $cart_item['domaintype'] ); ?></strong> <div class="whcom_text_small"> <a href="<?php echo $cart_item['domain']; ?>" target="_blank"><?php echo $cart_item['domain']; ?></a> </div> <div class="whcom_text_small whcom_padding_0_10"> <?php if ( ! empty( $cart_item['dnsmanagement'] ) ) { ?> <?php $domain_addon_price = (float) $domain_addons['dnsmanagement'] * (float) $cart_item['regperiod']; ?> <div><i class="whcom_icon_angle-double-right"></i> <?php esc_html_e( "DNS Management", 'whcom' ) ?> - <?php echo whcom_format_amount( $domain_addon_price ) . ' ' . $domain_text['text']; ?> </div> <?php $item_totals['domain'] = $item_totals['domain'] + (float) $domain_addon_price; ?> <?php } ?> <?php if ( ! empty( $cart_item['emailforwarding'] ) ) { ?> <?php $domain_addon_price = (float) $domain_addons['emailforwarding'] * (float) $cart_item['regperiod']; ?> <div><i class="whcom_icon_angle-double-right"></i> <?php esc_html_e( "Email Forwarding", 'whcom' ) ?> - <?php echo whcom_format_amount( $domain_addon_price ) . ' ' . $domain_text['text']; ?> </div> <?php $item_totals['domain'] = $item_totals['domain'] + (float) $domain_addon_price; ?> <?php } ?> <?php if ( ! empty( $cart_item['idprotection'] ) ) { ?> <?php $domain_addon_price = (float) $domain_addons['idprotection'] * (float) $cart_item['regperiod']; ?> <div><i class="whcom_icon_angle-double-right"></i> <?php esc_html_e( "ID Protection", 'whcom' ) ?> - <?php echo whcom_format_amount( $domain_addon_price ) . ' ' . $domain_text['text']; ?> </div> <?php $item_totals['domain'] = $item_totals['domain'] + (float) $domain_addon_price; ?> <?php } ?> </div> </div> <div class="whcom_col_sm_5 whcom_text_right"> <?php echo whcom_format_amount( $item_totals['domain'] ); ?> <div> <?php echo strtolower( $domain_text['text'] ); ?> </div> </div> </div> </div> </div> </li> <?php } ?> <?php //$item_totals = whcom_apply_item_discount( $item_totals, $cart_item ); ?> <?php //$item_totals = whcom_calculate_tax( $item_totals, $tax_settings ); ?> <?php // Base $totals['prod_total'] = (float) $totals['prod_total'] + (float) $item_totals['prod_total']; $totals['prod_setup'] = (float) $totals['prod_setup'] + (float) $item_totals['prod_setup']; $totals['addon_setup'] = (float) $totals['addon_setup'] + (float) $item_totals['addon_setup']; $totals['addon_total'] = (float) $totals['addon_total'] + (float) $item_totals['addon_total']; $totals['addons_setup'] = (float) $totals['addons_setup'] + (float) $item_totals['addons_setup']; $totals['addons_total'] = (float) $totals['addons_total'] + (float) $item_totals['addons_total']; $totals['domain'] = (float) $totals['domain'] + (float) $item_totals['domain']; // Recurring $totals['monthly'] = (float) $totals['monthly'] + (float) $item_totals['monthly']; $totals['quarterly'] = (float) $totals['quarterly'] + (float) $item_totals['quarterly']; $totals['semiannually'] = (float) $totals['semiannually'] + (float) $item_totals['semiannually']; $totals['annually'] = (float) $totals['annually'] + (float) $item_totals['annually']; $totals['biennially'] = (float) $totals['biennially'] + (float) $item_totals['biennially']; $totals['triennially'] = (float) $totals['triennially'] + (float) $item_totals['triennially']; // Totals $totals['grand'] = (float) $totals['grand'] + (float) $item_totals['grand']; $totals['discount'] = (float) $totals['discount'] + (float) $item_totals['discount']; // Taxes $totals['base_price'] = (float) $totals['base_price'] + (float) $item_totals['base_price']; $totals['l1_rate'] = (float) $totals['l1_rate'] + (float) $item_totals['l1_rate']; $totals['l2_rate'] = (float) $totals['l2_rate'] + (float) $item_totals['l2_rate']; $totals['l1_amount'] = (float) $totals['l1_amount'] + (float) $item_totals['l1_amount']; $totals['l2_amount'] = (float) $totals['l2_amount'] + (float) $item_totals['l2_amount']; $totals['final_price'] = (float) $totals['final_price'] + (float) $item_totals['final_price']; ?> <?php $counter ++; ?> <?php ob_start() ?> Sidebar Short Appended <?php $summary_html['short'] .= ob_get_clean(); ?> <?php } ?> <?php } ?> <?php if ( ! empty( $cart['order_specific']['domainrenewals'] ) ) { ?> <?php foreach ( $cart['order_specific']['domainrenewals'] as $domainrenewal ) { ?> <li>Domain Renewal here,</li> <?php } ?> <?php } ?> </ul> <?php if ( $counter < 1 ) { ?> <div class="whcom_alert whcom_alert_info"> <?php esc_html_e( 'Your Shopping Cart is Empty', 'whcom' ); ?> </div> <?php } ?> <?php ob_start() ?> <div class="whcom_cart_summary_sidebar"> <?php $totals['base_price'] = $totals['prod_total'] + $totals['prod_setup'] + $totals['addons_total'] + $totals['addons_setup'] + $totals['addon_total'] + $totals['addon_setup'] + $totals['domain']; $totals['base_price'] = $totals['prod_total'] + $totals['prod_setup'] + $totals['addons_total'] + $totals['addons_setup'] + $totals['addon_total'] + $totals['addon_setup'] + $totals['domain']; $subtotal = $totals['base_price'] + $totals['discount']; $totals['final_price'] = $subtotal + $totals['l1_amount'] - $totals['l2_amount']; ?> <div class="whcom_clearfix whcom_margin_bottom_5 whcom_padding_5_0 whcom_bordered_bottom"> <span class="whcom_pull_left"><?php esc_html_e( 'Subtotal', 'whcom' ) ?></span> <span class="whcom_pull_right"><?php echo whcom_format_amount( $subtotal ); ?></span> </div> <?php if ( $totals['l1_amount'] > 0 || $totals['l2_amount'] > 0 || $totals['discount'] > 0 ) { ?> <div class="whcom_padding_10_0 whcom_bordered_top_bottom"> <?php if ( $totals['discount'] > 0 ) { ?> <?php $current_discount = whcom_get_current_promo(); ?> <?php $discount_value = $current_discount['value']; $discount_type = esc_html__( "One Time Discount", "whcom" ); if ( $current_discount['recurring'] == '1' ) { $discount_type = esc_html__( "Recurring Discount", "whcom" ); } switch ( $current_discount['type'] ) { case 'Free Setup' : { $discount_value = esc_html__( "Free Setup", "whcom" ); break; } case 'Fixed Amount' : { $discount_value = whcom_format_amount( $current_discount['value'] ); break; } case 'Price Override' : { $discount_value = whcom_format_amount( $current_discount['value'] ) . ' ' . esc_html__( "Price Override", "whcom" ); break; } case 'Percentage' : { $discount_value = $current_discount['value'] . esc_html__( "%", "whcom" ); break; } default : { } } ?> <?php ?> <div class="whcom_clearfix"> <span class="whcom_pull_left"> <?php echo $discount_value; ?> <?php echo $discount_type; ?> </span> <span class="whcom_pull_right"><?php echo whcom_format_amount( $totals['discount'] ); ?></span> </div> <?php } ?> <?php if ( $totals['l1_amount'] > 0 ) { ?> <div class="whcom_clearfix"> <span class="whcom_pull_left"><?php esc_html_e( "L1 Tax rule, @ ", 'whcom' ) ?><?php echo $totals['l1_rate'] ?> %</span> <span class="whcom_pull_right"><?php echo whcom_format_amount( $totals['l1_amount'] ); ?></span> </div> <?php } ?> <?php if ( $totals['l2_amount'] > 0 ) { ?> <div class="whcom_clearfix"> <span class="whcom_pull_left"><?php esc_html_e( "L2 Tax rule, @ ", 'whcom' ) ?><?php echo $totals['l2_rate'] ?> %</span> <span class="whcom_pull_right"><?php echo whcom_format_amount( $totals['l2_amount'] ); ?></span> </div> <?php } ?> </div> <?php } ?> <div class="whcom_clearfix whcom_margin_bottom_30"> <span class="whcom_pull_left"><?php esc_html_e( 'Totals', 'whcom' ) ?></span> <?php $billingcycles = [ 'monthly', 'quarterly', 'semiannually', 'annually', 'biennially', 'triennially', ]; ?> <?php foreach ( $totals as $key => $total ) { ?> <?php if ( $total && in_array($key, $billingcycles) ) { ?> <div class="whcom_clearfix"> <div class="whcom_pull_right"> <span><?php echo whcom_format_amount( $total ); ?></span> <span><?php echo whcom_convert_billingcycle( $key ) ?></span> </div> </div> <?php } ?> <?php } ?> </div> <div class="whcom_clearfix"> <div class="whcom_text_right"><strong><?php echo whcom_format_amount( $totals['final_price'] ); ?></strong></div> <div class="whcom_text_right"><?php esc_html_e( 'Total Due Today', 'whcom' ) ?></div> </div> </div> <?php $summary_html['side'] = ob_get_clean(); ?> <?php $summary_html['status'] = 'OK'; $summary_html['message'] = esc_html__( 'Updating Cart Summaries', 'whcom' ); $summary_html['detailed'] = ob_get_clean(); $summary_html['total_items'] = $counter; $summary_html['grand_total'] = whcom_format_amount( $totals['final_price'] ); ?> common/shortcodes/order_process/index.php 0000644 00000000107 14736103451 0014704 0 ustar 00 <?php defined( 'ABSPATH' ) or die( "Cannot access pages directly." ); common/shortcodes/order_process/02_product_sidebar.php 0000644 00000035201 14736103451 0017252 0 ustar 00 <?php defined( 'ABSPATH' ) or die( "Cannot access pages directly." ); $curr_product = ( ! empty( $product_array ) ) ? $product_array : []; $tax_settings = whcom_get_whmcs_setting(); $tax_rates = whcom_get_tax_levels(); $summary_html = [ 'side' => 'Something went wrong', ]; ob_start(); $pid = ( empty( $curr_product['pid'] ) ) ? 0 : (int) $curr_product['pid']; $billing_cycle = ( empty( $curr_product['billingcycle'] ) ) ? 'monthly' : (string) $curr_product['billingcycle']; $product = whcom_get_product_details( $pid ); $domain_details['is_free'] = false; $no_options = true; ?> <?php if ( $product ) { $product_price = $product['all_prices'][ $billing_cycle ]['price']; $setup_price = $product['all_prices'][ $billing_cycle ]['setup']; $amount = [ 'setup' => 0.00, 'monthly' => false, 'quarterly' => false, 'semiannually' => false, 'annually' => false, 'biennially' => false, 'triennially' => false, 'free_domain' => false, 'base_price' => 0.00, 'l1_amount' => 0.00, 'l2_amount' => 0.00, 'final_price' => 0.00, ]; $total_amount = 0.00; if ( $product['type'] == 'server' ) { $no_options = false; } if ( ! empty( $product['prd_addons'] ) && is_array( $product['prd_addons'] ) ) { $no_options = false; } if ( ! empty( $product['custom_fields'] ) && is_array( $product['custom_fields'] ) ) { $no_options = false; } if ( ! empty( $product['prd_configoptions'] ) && is_array( $product['prd_configoptions'] ) ) { $no_options = false; } ?> <div class="whcom_sticky_item whcom_text_small"> <div class="wcop_product_summary_product whcom_margin_bottom_10 whcom_bordered_bottom whcom_padding_bottom_5"> <div class="whcom_clearfix"> <span class="whcom_pull_left"><?php echo $product['name'] ?></span> <span class="whcom_pull_right"><?php echo whcom_format_amount( $product_price ); ?></span> <?php $amount[ $billing_cycle ] = $product_price; ?> </div> <div class="whcom_clearfix"> <span class="whcom_pull_left"><?php esc_html_e( 'Setup Price', 'whcom' ) ?></span> <span class="whcom_pull_right"><?php echo whcom_format_amount( $setup_price ); ?></span> <?php $amount['setup'] = $setup_price; ?> </div> <?php if ( ( ! empty( $curr_product['configoptions'] ) ) && ( is_array( $curr_product['configoptions'] ) ) ) { ?> <div class="whcom_op_product_summary_configoptions"> <?php foreach ( $curr_product['configoptions'] as $opt_id => $opt ) { $opt_details = $product['prd_configoptions'][ $opt_id ]; if ( ! empty( $opt_details ) ) { ?> <?php switch ( $opt_details['optiontype'] ) { case '1' : { $sub_opt_details = $opt_details['sub_options'][ $opt ]; $opt_amount = (float) $sub_opt_details['all_prices'][ $billing_cycle ]['price']; $opt_setup = (float) $sub_opt_details['all_prices'][ $billing_cycle ]['setup']; ?> <div class="whcom_clearfix"> <span class="whcom_pull_left whcom_padding_0_10"> <i class="whcom_icon_angle-double-right"></i> <?php echo $opt_details['optionname']; ?>: <?php echo $sub_opt_details['optionname']; ?></span> <span class="whcom_pull_right"> <?php if ($opt_amount > 0) { ?> <?php echo whcom_format_amount( $opt_amount ) ?> <?php } ?> <?php if ($opt_amount > 0 && $opt_setup > 0) { ?> + <?php } ?> <?php if ($opt_setup > 0) { ?> <?php echo whcom_format_amount( $opt_setup ) ?> <?php esc_html_e( "Setup Fee", "whcom" ) ?> <?php } ?> </span> </div> <?php $amount[ $billing_cycle ] = (float) $amount[ $billing_cycle ] + (float) $sub_opt_details['all_prices'][ $billing_cycle ]['price']; $amount['setup'] = (float) $amount['setup'] + (float) $sub_opt_details['all_prices'][ $billing_cycle ]['setup']; break; } case '2' : { $sub_opt_details = $opt_details['sub_options'][ $opt ]; $opt_amount = (float) $sub_opt_details['all_prices'][ $billing_cycle ]['price']; $opt_setup = (float) $sub_opt_details['all_prices'][ $billing_cycle ]['setup']; ?> <div class="whcom_clearfix"> <span class="whcom_pull_left whcom_padding_0_10"> <i class="whcom_icon_angle-double-right"></i> <?php echo $opt_details['optionname']; ?>: <?php echo $sub_opt_details['optionname']; ?></span> <span class="whcom_pull_right"> <?php if ($opt_amount > 0) { ?> <?php echo whcom_format_amount( $opt_amount ) ?> <?php } ?> <?php if ($opt_amount > 0 && $opt_setup > 0) { ?> + <?php } ?> <?php if ($opt_setup > 0) { ?> <?php echo whcom_format_amount( $opt_setup ) ?> <?php esc_html_e( "Setup Fee", "whcom" ) ?> <?php } ?> </span> </div> <?php $amount[ $billing_cycle ] = (float) $amount[ $billing_cycle ] + (float) $sub_opt_details['all_prices'][ $billing_cycle ]['price']; $amount['setup'] = (float) $amount['setup'] + (float) $sub_opt_details['all_prices'][ $billing_cycle ]['setup']; break; } case '3' : { $sub_opt_details = reset( $opt_details['sub_options'] ); $opt_amount = (float) $sub_opt_details['all_prices'][ $billing_cycle ]['price']; $opt_setup = (float) $sub_opt_details['all_prices'][ $billing_cycle ]['setup']; ?> <div class="whcom_clearfix"> <span class="whcom_pull_left whcom_padding_0_10"> <i class="whcom_icon_angle-double-right"></i> <?php echo $opt_details['optionname']; ?>: <?php esc_html_e( 'Yes', 'whcom' ) ?> </span> <span class="whcom_pull_right"> <?php if ($opt_amount > 0) { ?> <?php echo whcom_format_amount( $opt_amount ) ?> <?php } ?> <?php if ($opt_amount > 0 && $opt_setup > 0) { ?> + <?php } ?> <?php if ($opt_setup > 0) { ?> <?php echo whcom_format_amount( $opt_setup ) ?> <?php esc_html_e( "Setup Fee", "whcom" ) ?> <?php } ?> </span> </div> <?php $amount[ $billing_cycle ] = (float) $amount[ $billing_cycle ] + (float) $sub_opt_details['all_prices'][ $billing_cycle ]['price']; $amount['setup'] = (float) $amount['setup'] + (float) $sub_opt_details['all_prices'][ $billing_cycle ]['setup']; break; } case '4' : { $sub_opt_details = reset( $opt_details['sub_options'] ); $opt_amount = (float) $sub_opt_details['all_prices'][ $billing_cycle ]['price'] * (float) $opt; $opt_setup = (float) $sub_opt_details['all_prices'][ $billing_cycle ]['setup'] * (float) $opt; ?> <div class="whcom_clearfix"> <span class="whcom_pull_left whcom_padding_0_10"> <i class="whcom_icon_angle-double-right"></i> <?php echo $opt_details['optionname']; ?>: <?php echo $opt; ?> </span> <span class="whcom_pull_right"> <?php if ($opt_amount > 0) { ?> <?php echo whcom_format_amount( $opt_amount ) ?> <?php } ?> <?php if ($opt_amount > 0 && $opt_setup > 0) { ?> + <?php } ?> <?php if ($opt_setup > 0) { ?> <?php echo whcom_format_amount( $opt_setup ) ?> <?php esc_html_e( "Setup Fee", "whcom" ) ?> <?php } ?> </span> </div> <?php $amount[ $billing_cycle ] = (float) $amount[ $billing_cycle ] + (float) $opt_amount; $amount['setup'] = (float) $amount['setup'] + (float) $opt_setup; break; } } ?> <?php } ?> <?php } ?> </div> <?php } ?> <?php $product_price = $amount[ $billing_cycle ]; $product_setup = $amount['setup']; $product_total = $product_price + $product_setup; if ( ! empty( $product['tax'] ) && $product['tax'] == '1' ) { $product_total = whcom_calculate_tax( $product_total, $tax_settings ); $totals['base_price'] = $totals['base_price'] + $product_total['base_price']; $totals['l1_amount'] = $totals['l1_amount'] + $product_total['l1_amount']; $totals['l2_amount'] = $totals['l2_amount'] + $product_total['l2_amount']; $totals['final_price'] = $totals['final_price'] + $product_total['final_price']; } else { $totals['base_price'] = $totals['base_price'] + $product_total; $totals['final_price'] = $totals['final_price'] + $product_total; } ?> <?php if ( ( ! empty( $curr_product['addons'] ) ) && ( is_array( $curr_product['addons'] ) ) ) { ?> <div class="whcom_op_product_summary_addons"> <?php foreach ( $curr_product['addons'] as $adn ) { $curr_addon = $product['prd_addons'][ $adn ]; // Addon price logic $addon_billingcycle = strtolower( $curr_addon['billingcycle'] ); if ( $addon_billingcycle == 'recurring' ) { if ( !empty($curr_addon['all_prices'][ $curr_product['billingcycle'] ]) ) { $addon_billingcycle = $curr_product['billingcycle']; $curr_addon_price = $curr_addon['all_prices'][ $addon_billingcycle ]['price']; $curr_addon_setup = $curr_addon['all_prices'][ $addon_billingcycle ]['setup']; } else { reset( $curr_addon['lowest_price'] ); $addon_billingcycle = key( $curr_addon['lowest_price'] ); $curr_addon_price = $curr_addon['lowest_price'][ $addon_billingcycle ]['price']; $curr_addon_setup = $curr_addon['lowest_price'][ $addon_billingcycle ]['setup']; } } elseif ($addon_billingcycle == 'free') { $curr_addon_price = 0.00; $curr_addon_setup = 0.00; } else { $curr_addon_price = $curr_addon['monthly']; $curr_addon_setup = $curr_addon['msetupfee']; } $amount[ $addon_billingcycle ] = $amount[ $addon_billingcycle ] + $curr_addon_price; $amount['setup'] = $amount['setup'] + $curr_addon_setup; if ( ! empty( $curr_addon ) ) { ?> <div class="whcom_clearfix"> <span class="whcom_pull_left whcom_padding_0_10">+ <?php echo $curr_addon['name']; ?></span> <span class="whcom_pull_right"> <?php echo whcom_format_amount( $curr_addon_price ) ?> </span> </div> <?php ?> <?php $curr_addon_total = $curr_addon_price + $curr_addon_setup; if ( ! empty( $curr_addon['tax'] ) && $curr_addon['tax'] == '1' ) { $curr_addon_total = whcom_calculate_tax( $curr_addon_total, $tax_settings ); $totals['base_price'] = $totals['base_price'] + $curr_addon_total['base_price']; $totals['l1_amount'] = $totals['l1_amount'] + $curr_addon_total['l1_amount']; $totals['l2_amount'] = $totals['l2_amount'] + $curr_addon_total['l2_amount']; $totals['final_price'] = $totals['final_price'] + $curr_addon_total['final_price']; } else { $totals['base_price'] = $totals['base_price'] + $curr_addon_total; $totals['final_price'] = $totals['final_price'] + $curr_addon_total; } ?> <?php } ?> <?php } ?> </div> <?php } ?> </div> <?php if ( ! empty( $curr_product['domain'] ) ) { $domain_type = ( $curr_product['domaintype'] == 'transfer' ) ? 'transfer' : 'register'; $domain_details = whcom_op_get_current_domain_details( $curr_product['domain'], $domain_type, $curr_product['regperiod'], $product, $billing_cycle ); ?> <div class="whcom_clearfix"> <strong class="whcom_pull_left"><?php echo $curr_product['domain'] ?></strong> <span class="whcom_pull_right"><?php echo whcom_format_amount( $domain_details['domain_price'] ); ?></span> <?php $total_amount = $total_amount + (float) $domain_details['domain_price']; ?> </div> <div class="whcom_clearfix"> <span class="whcom_pull_left"><?php echo $domain_details['domain_type']; ?></span> <span class="whcom_pull_right"><?php echo $domain_details['domain_duration'] ?></span> </div> <?php $no_options = false; ?> <?php } ?> <div class="whcom_op_product_summary_totals whcom_margin_bottom_10 whcom_bordered_bottom whcom_padding_bottom_5"> <?php foreach ( $amount as $key => $amt ) { ?> <?php if ( $amt || ( $key == 'setup' ) ) { $total_amount = $total_amount + $amt; if ( $key == 'onetime' ) { continue; } ?> <div class="whcom_clearfix <?php echo $key; ?> <?php echo ( ( $key != 'setup' ) ) ? 'price' : ''; ?> <?php echo ( ( $amt <= 0 ) ) ? 'free' : ''; ?>"> <span class="whcom_pull_left"><?php echo whcom_convert_billingcycle( $key ); ?></span> <span class="whcom_pull_right"><?php echo whcom_format_amount( [ 'amount' => $amt, 'add_suffix' => 'yes' ] ); ?></span> </div> <?php } ?> <?php } ?> <?php if ( $totals['l1_amount'] > 0 ) { ?> <div class="whcom_clearfix"> <span class="whcom_pull_left"><?php echo $tax_rates['level1_title'] ?> @ <?php echo $tax_rates['level1_rate'] ?>%</span> <span class="whcom_pull_right"><?php echo whcom_format_amount( $totals['l1_amount'] ); ?></span> </div> <?php } ?> <?php if ( $totals['l2_amount'] > 0 ) { ?> <div class="whcom_clearfix"> <span class="whcom_pull_left"><?php echo $tax_rates['level2_title'] ?> @ <?php echo $tax_rates['level2_rate'] ?>%</span> <span class="whcom_pull_right"><?php echo whcom_format_amount( $totals['l2_amount'] ); ?></span> </div> <?php } ?> </div> <div class="whcom_op_product_summary_grand_total"> <div class="whcom_clearfix whcom_text_2x"> <span class="whcom_pull_right"><?php echo whcom_format_amount( [ 'amount' => $totals['final_price'], 'add_suffix' => 'yes' ] ); ?></span> </div> <div class="whcom_clearfix"> <span class="whcom_pull_right"><?php esc_html_e( 'Total Due Today', 'whcom' ) ?></span> </div> </div> </div> <?php } ?> <?php $summary_html = [ 'side' => ob_get_clean(), 'free_domain' => $domain_details['is_free'], 'no_options' => $no_options, ]; common/shortcodes/order_process/04_checkout.php 0000644 00000012002 14736103451 0015702 0 ustar 00 <?php defined( 'ABSPATH' ) or die( "Cannot access pages directly." ); $payment_gateways = whcom_get_payment_gateways()['payment_gateways']; $currencies = whcom_get_all_currencies(); $countries = whcom_get_countries_array(); $cop_tags = [ 'whmpress_store', 'whmpress_cart_list_products', 'whmpress_cart_config_product' ]; $is_wcop = ( ! empty( $tag ) && in_array( $tag, $cop_tags ) ) ? 'yes' : 'no'; $merchant_gateway = ''; if (function_exists('wcop_use_merchant_gateway')) { $merchant_gateway = ( wcop_use_merchant_gateway() && get_option( 'merchant_gateway_key', '' ) != '' ) ? (string) get_option( 'merchant_gateway_key' ) : false; } ?> <?php ob_start(); $checked = 'checked'; $show_cc = 'none'; $checked_class = 'whcom_checked'; $selected_gateway = ''; ?> <div class="whcom_form_field whcom_text_center"> <div class="whcom_radio_container"> <?php foreach ( $payment_gateways as $payment_gateway ) { ?> <label class="whcom_radio <?php echo $checked_class; ?>"> <input type="radio" name="paymentmethod" class="whcom_sp_cc_switcher" value="<?php echo $payment_gateway['module'] ?>" <?php echo $checked; ?> data-merchent-gateway="<?php echo $merchant_gateway; ?>"> <?php echo $payment_gateway['displayname']; ?> </label> <?php if ( $checked == 'checked' ) { $selected_gateway = (string) $payment_gateway['module']; } ?> <?php $checked = $checked_class = ''; ?> <?php } ?> </div> </div> <?php $show_cc = ( ( $merchant_gateway ) && ( $selected_gateway == $merchant_gateway ) ) ? 'block' : 'none'; ?> <?php if ( $merchant_gateway && $is_wcop == 'yes') { ?> <div class="whcom_sp_cc_fields" style="display: <?php echo $show_cc; ?>;"> <?php echo whcom_render_cc_form(); ?> </div> <?php } ?> <?php $payment_selection = ob_get_clean(); ?> <div class="whcom_op_checkout_container"> <div class="whcom_page_heading "> <?php esc_html_e( 'Checkout', 'whcom' ) ?> </div> <div class="whcom_margin_bottom_15 whcom_page_sub_heading"> <?php esc_html_e( 'Please enter your personal details and billing information to checkout.', 'whcom' ) ?> </div> <form method="post" class="whcom_op_checkout_form" novalidate> <input type="hidden" name="action" value="whcom_op"> <input type="hidden" name="whcom_op_what" value="checkout"> <input type="hidden" name="is_wcop" value="<?php echo $is_wcop; ?>"> <?php if ( whcom_is_client_logged_in() ) { ?> <?php echo whcom_render_logged_in_client_form(); ?> <?php } else { ?> <?php echo whcom_render_client_register_JS(); ?> <div class="whcom_op_section_content"> <div class="whcom_op_register_login_container"> <div class="whcom_text_right"> <span class="whcom_op_register_link whcom_button whcom_button_warning whcom_op_register_login_link" style="display: none" id="whcom_op_register_link"> <?php esc_html_e( 'Register New Account', 'whcom' ) ?> </span> <span class="whcom_op_login_link whcom_button whcom_button_info whcom_op_register_login_link" style="display: inline-block" id="whcom_op_login_link"> <?php esc_html_e( 'Already Registered?', 'whcom' ) ?> </span> </div> <div class="whcom_tabs_content active" id="whcom_op_register_container" style="display: block"> <input type="hidden" name="currency" value="<?php echo whcom_get_current_currency_id(); ?>"> <?php echo whcom_render_register_form_fields(); ?> </div> <div class="whcom_tabs_content whcom_op_login_form" id="whcom_op_login_container" style="display: none"> <div class="whcom_sub_heading_style_1"> <span><?php esc_html_e( "Existing Customer Login", "whcom" ) ?></span> </div> <?php echo whcom_render_login_form_fields(); ?> </div> </div> <input type="hidden" name="whcom_op_client_type" value="register" id="whcom_op_client_type"> </div> <?php } ?> <div class="whcom_op_payment_method"> <div class="whcom_sub_heading_style_1"> <span><?php esc_html_e( "Payment Details", "whcom" ) ?></span> </div> <?php echo $payment_selection;?> </div> <div class="whcom_op_tos_container"> <?php echo whcom_render_tos_fields(); ?> </div> <div class="whcom_op_response"> </div> <div class="whcom_op_submit_container whcom_text_center"> <button type="submit"><?php esc_html_e( "Complete Order", "whcom" ) ?></button> </div> </form> </div> common/shortcodes/order_process/03_summary.php 0000644 00000005041 14736103451 0015576 0 ustar 00 <?php defined( 'ABSPATH' ) or die( "Cannot access pages directly." ); $current_promo = whcom_get_current_promo(); ?> <div class="whcom_page_heading"> <?php esc_html_e('Review & Checkout', 'whcom')?> </div> <div class="whcom_op_cart_list whcom_row"> <div class="whcom_col_sm_8 whcom_op_cart_list_main"> <div class="whcom_op_promo_response"> </div> <div class="whcom_panel whcom_panel_fancy_2 whcom_panel_primary"> <div class="whcom_panel_header"> <div class="whcom_row"> <div class="whcom_col_sm_7"> <?php esc_html_e('Product/Options', 'whcom')?> </div> <div class="whcom_col_sm_5 whcom_text_right_sm"> <?php esc_html_e('Price/Cycle', 'whcom')?> </div> </div> </div> <div class="whcom_panel_body" style="padding: 0 !important; background: white !important;"> <div class="whcom_op_universal_cart_summary_detailed"> <div class="whcom_text_center_xs"><i class="whcom_icon_spinner-1 whcom_animate_spin"></i></div> </div> </div> </div> <div class="whcom_text_right whcom_op_cart_list_reset_button whcom_margin_bottom_45"> <?php echo whcom_render_reset_cart(); ?> </div> <div class="whcom_op_promo_container"> <?php echo whcom_op_render_promo_form(); ?> </div> </div> <div class="whcom_col_sm_4 whcom_summary_sidebar" style="padding: 0 10px;"> <div class="whcom_panel"> <div class="whcom_panel_header"> <span><?php esc_html_e('Order Summary', 'whcom')?></span> </div> <div class="whcom_op_universal_cart_summary_side whcom_panel_body whcom_text_small"> <div class="whcom_text_center_xs"><i class="whcom_icon_spinner-1 whcom_animate_spin"></i></div> </div> <div class="whcom_op_summary_footer whcom_panel_footer whcom_text_right"> <div class="whcom_op_response"> </div> <div class="whcom_margin_bottom_15"> <form class="whcom_op_review_form" method="post"> <input type="hidden" name="checkout" value="1"> <input type="hidden" name="action" value="whcom_op"> <input type="hidden" name="whcom_op_what" value="review"> <button type="submit" class="whcom_universal_checkout_button whcom_button whcom_button_success whcom_button_big" disabled="disabled"><?php esc_html_e( 'Checkout', 'whcom' ) ?> <i class="whcom_icon_right-big"></i></button> </form> </div> <div class="whcom_text_small"> <a class="wcap_load_page" data-page="order_new_service" href="" id=""><?php esc_html_e('Continue Shopping', 'whcom')?></a> </div> </div> </div> </div> </div> common/shortcodes/order_process.php 0000644 00000005150 14736103451 0013600 0 ustar 00 <?php defined( 'ABSPATH' ) or die( "Cannot access pages directly." ); $atts = shortcode_atts( [ 'currency_id' => ( isset( $_REQUEST['currency'] ) && is_integer( intval( $_REQUEST['currency'] ) ) ) ? $_REQUEST['currency'] : whcom_get_current_currency_id(), 'gids' => '', 'pids' => '', 'domain_products' => (!empty($_REQUEST['dp']) && strtolower($_REQUEST['dp']) == 'yes') ? 'yes' : 'no' ], $atts ); extract($atts); if ( ! empty( $_REQUEST['a'] ) ) { $action = (string) $_REQUEST['a']; ?> <div class="whcom_op_main whcom_main"> <?php switch ( $action ) { case 'add' : { // Register/Transfer/Order domain if ( ! empty( $_REQUEST['domain'] ) && empty($_REQUEST['pid'])) { ob_start(); include_once WHCOM_PATH . '/shortcodes/order_process/01_domain.php'; echo ob_get_clean(); break; } else if ( ! empty( $_REQUEST['pid'] ) && (int) $_REQUEST['pid'] > 0 ) { $product_id = $_REQUEST['pid']; ob_start(); include_once WHCOM_PATH . '/shortcodes/order_process/02_product.php'; echo ob_get_clean(); break; } else if (!empty($_REQUEST['serviceids']) && (int) $_REQUEST['serviceids'] > 0 && !empty($_REQUEST['addonids']) && (int) $_REQUEST['addonids'] > 0 ) { whcom_add_update_cart_item($_REQUEST); ob_start(); include_once WHCOM_PATH . '/shortcodes/order_process/03_summary.php'; echo ob_get_clean(); break; } continue; } case 'confdomains' : { ob_start(); include_once WHCOM_PATH . '/shortcodes/order_process/01_domains_config.php'; echo ob_get_clean(); break; } case 'view' : { ob_start(); include_once WHCOM_PATH . '/shortcodes/order_process/03_summary.php'; echo ob_get_clean(); break; } case 'checkout' : { ob_start(); if (whcom_is_cart_empty()) { include_once WHCOM_PATH . '/shortcodes/order_process/03_summary.php'; } else { include_once WHCOM_PATH . '/shortcodes/order_process/04_checkout.php'; } echo ob_get_clean(); break; } case 'viewinvoice' : { if ( ! empty( $_REQUEST['id'] ) && (int) $_REQUEST['id'] > 0 ) { $order_complete_url = get_option('wcapfield_client_area_url' . whcom_get_current_language(), '?whmpca=dashboard'); echo whcom_generate_invoice_iframe((int)$_REQUEST['id'], $order_complete_url); break; } continue; } default : { echo whcom_order_list_products_render($atts, $content, $tag); } } ?> </div> <?php } else { echo whcom_order_list_products_render($atts, $content, $tag); } common/shortcodes/index.php 0000644 00000000107 14736103451 0012033 0 ustar 00 <?php defined( 'ABSPATH' ) or die( "Cannot access pages directly." ); common/shortcodes/icons.php 0000644 00000450750 14736103451 0012054 0 ustar 00 <style> .i-code { display: none; } </style> <div class="whcom_main"> <h2>Icons</h2> <div id="icons" class="container"> <div class="whcom_row"> <div title="Code: 0xe800" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_basket"></i> <span class="i-name">whcom_icon_basket</span><span class="i-code">0xe800</span></div> <div title="Code: 0xe801" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_th"></i> <span class="i-name">whcom_icon_th</span><span class="i-code">0xe801</span></div> <div title="Code: 0xe802" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_like"></i> <span class="i-name">whcom_icon_like</span><span class="i-code">0xe802</span></div> <div title="Code: 0xe803" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_mail"></i> <span class="i-name">whcom_icon_mail</span><span class="i-code">0xe803</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe804" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_rocket"></i> <span class="i-name">whcom_icon_rocket</span><span class="i-code">0xe804</span></div> <div title="Code: 0xe805" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_rocket-basket"></i> <span class="i-name">whcom_icon_rocket-basket</span><span class="i-code">0xe805</span></div> <div title="Code: 0xe806" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_user"></i> <span class="i-name">whcom_icon_user</span><span class="i-code">0xe806</span></div> <div title="Code: 0xe807" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_www"></i> <span class="i-name">whcom_icon_www</span><span class="i-code">0xe807</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe808" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_card"></i> <span class="i-name">whcom_icon_card</span><span class="i-code">0xe808</span></div> <div title="Code: 0xe809" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_user-2"></i> <span class="i-name">whcom_icon_user-2</span><span class="i-code">0xe809</span></div> <div title="Code: 0xe80a" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_ok"></i> <span class="i-name">whcom_icon_ok</span><span class="i-code">0xe80a</span></div> <div title="Code: 0xe80b" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_wrench"></i> <span class="i-name">whcom_icon_wrench</span><span class="i-code">0xe80b</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe80c" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_ok-circled"></i> <span class="i-name">whcom_icon_ok-circled</span><span class="i-code">0xe80c</span></div> <div title="Code: 0xe80d" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_ok-circled2"></i> <span class="i-name">whcom_icon_ok-circled2</span><span class="i-code">0xe80d</span></div> <div title="Code: 0xe80e" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_cancel"></i> <span class="i-name">whcom_icon_cancel</span><span class="i-code">0xe80e</span></div> <div title="Code: 0xe80f" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_plus"></i> <span class="i-name">whcom_icon_plus</span><span class="i-code">0xe80f</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe810" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_cancel-circled"></i> <span class="i-name">whcom_icon_cancel-circled</span><span class="i-code">0xe810</span></div> <div title="Code: 0xe811" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_cancel-circled2"></i> <span class="i-name">whcom_icon_cancel-circled2</span><span class="i-code">0xe811</span></div> <div title="Code: 0xe812" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_minus"></i> <span class="i-name">whcom_icon_minus</span><span class="i-code">0xe812</span></div> <div title="Code: 0xe813" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_tags"></i> <span class="i-name">whcom_icon_tags</span><span class="i-code">0xe813</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe814" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_plus-circled"></i> <span class="i-name">whcom_icon_plus-circled</span><span class="i-code">0xe814</span></div> <div title="Code: 0xe815" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_bookmark"></i> <span class="i-name">whcom_icon_bookmark</span><span class="i-code">0xe815</span></div> <div title="Code: 0xe816" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_minus-circled"></i> <span class="i-name">whcom_icon_minus-circled</span><span class="i-code">0xe816</span></div> <div title="Code: 0xe817" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_home"></i> <span class="i-name">whcom_icon_home</span><span class="i-code">0xe817</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe818" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_thumbs-up"></i> <span class="i-name">whcom_icon_thumbs-up</span><span class="i-code">0xe818</span></div> <div title="Code: 0xe819" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_thumbs-down"></i> <span class="i-name">whcom_icon_thumbs-down</span><span class="i-code">0xe819</span></div> <div title="Code: 0xe81a" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_attention-circled"></i> <span class="i-name">whcom_icon_attention-circled</span><span class="i-code">0xe81a</span></div> <div title="Code: 0xe81b" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_down-open"></i> <span class="i-name">whcom_icon_down-open</span><span class="i-code">0xe81b</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe81c" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_attention"></i> <span class="i-name">whcom_icon_attention</span><span class="i-code">0xe81c</span></div> <div title="Code: 0xe81d" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_wrench-1"></i> <span class="i-name">whcom_icon_wrench-1</span><span class="i-code">0xe81d</span></div> <div title="Code: 0xe81e" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_cog-alt"></i> <span class="i-name">whcom_icon_cog-alt</span><span class="i-code">0xe81e</span></div> <div title="Code: 0xe81f" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_cog"></i> <span class="i-name">whcom_icon_cog</span><span class="i-code">0xe81f</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe820" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_paper-plane"></i> <span class="i-name">whcom_icon_paper-plane</span><span class="i-code">0xe820</span></div> <div title="Code: 0xe821" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_database"></i> <span class="i-name">whcom_icon_database</span><span class="i-code">0xe821</span></div> <div title="Code: 0xe822" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_trash"></i> <span class="i-name">whcom_icon_trash</span><span class="i-code">0xe822</span></div> <div title="Code: 0xe823" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_cog-1"></i> <span class="i-name">whcom_icon_cog-1</span><span class="i-code">0xe823</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe824" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_params"></i> <span class="i-name">whcom_icon_params</span><span class="i-code">0xe824</span></div> <div title="Code: 0xe825" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_diamond"></i> <span class="i-name">whcom_icon_diamond</span><span class="i-code">0xe825</span></div> <div title="Code: 0xe826" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_megaphone"></i> <span class="i-name">whcom_icon_megaphone</span><span class="i-code">0xe826</span></div> <div title="Code: 0xe827" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_thumbs-up-1"></i> <span class="i-name">whcom_icon_thumbs-up-1</span><span class="i-code">0xe827</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe828" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_trash-empty"></i> <span class="i-name">whcom_icon_trash-empty</span><span class="i-code">0xe828</span></div> <div title="Code: 0xe829" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_right-open"></i> <span class="i-name">whcom_icon_right-open</span><span class="i-code">0xe829</span></div> <div title="Code: 0xe82a" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_left-open"></i> <span class="i-name">whcom_icon_left-open</span><span class="i-code">0xe82a</span></div> <div title="Code: 0xe82b" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_down-dir"></i> <span class="i-name">whcom_icon_down-dir</span><span class="i-code">0xe82b</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe82c" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_up-dir"></i> <span class="i-name">whcom_icon_up-dir</span><span class="i-code">0xe82c</span></div> <div title="Code: 0xe82d" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_left-dir"></i> <span class="i-name">whcom_icon_left-dir</span><span class="i-code">0xe82d</span></div> <div title="Code: 0xe82e" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_right-dir"></i> <span class="i-name">whcom_icon_right-dir</span><span class="i-code">0xe82e</span></div> <div title="Code: 0xe82f" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_star"></i> <span class="i-name">whcom_icon_star</span><span class="i-code">0xe82f</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe830" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_heart"></i> <span class="i-name">whcom_icon_heart</span><span class="i-code">0xe830</span></div> <div title="Code: 0xe831" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_lightbulb"></i> <span class="i-name">whcom_icon_lightbulb</span><span class="i-code">0xe831</span></div> <div title="Code: 0xe832" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_user-1"></i> <span class="i-name">whcom_icon_user-1</span><span class="i-code">0xe832</span></div> <div title="Code: 0xe833" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_globe"></i> <span class="i-name">whcom_icon_globe</span><span class="i-code">0xe833</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe834" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_pencil"></i> <span class="i-name">whcom_icon_pencil</span><span class="i-code">0xe834</span></div> <div title="Code: 0xe835" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_comment"></i> <span class="i-name">whcom_icon_comment</span><span class="i-code">0xe835</span></div> <div title="Code: 0xe836" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_doc"></i> <span class="i-name">whcom_icon_doc</span><span class="i-code">0xe836</span></div> <div title="Code: 0xe837" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_mail-1"></i> <span class="i-name">whcom_icon_mail-1</span><span class="i-code">0xe837</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe838" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_spin4 animate-spin"></i> <span class="i-name">whcom_icon_spin4</span><span class="i-code">0xe838</span></div> <div title="Code: 0xe839" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_spinner animate-spin"></i> <span class="i-name">whcom_icon_spinner</span><span class="i-code">0xe839</span></div> <div title="Code: 0xe83a" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_star-empty"></i> <span class="i-name">whcom_icon_star-empty</span><span class="i-code">0xe83a</span></div> <div title="Code: 0xe83b" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_heart-empty"></i> <span class="i-name">whcom_icon_heart-empty</span><span class="i-code">0xe83b</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe83c" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_star-1"></i> <span class="i-name">whcom_icon_star-1</span><span class="i-code">0xe83c</span></div> <div title="Code: 0xe83d" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_heart-1"></i> <span class="i-name">whcom_icon_heart-1</span><span class="i-code">0xe83d</span></div> <div title="Code: 0xe83e" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_star-half"></i> <span class="i-name">whcom_icon_star-half</span><span class="i-code">0xe83e</span></div> <div title="Code: 0xe83f" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_users"></i> <span class="i-name">whcom_icon_users</span><span class="i-code">0xe83f</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe840" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_user-3"></i> <span class="i-name">whcom_icon_user-3</span><span class="i-code">0xe840</span></div> <div title="Code: 0xe841" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_th-list"></i> <span class="i-name">whcom_icon_th-list</span><span class="i-code">0xe841</span></div> <div title="Code: 0xe842" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_lock-open"></i> <span class="i-name">whcom_icon_lock-open</span><span class="i-code">0xe842</span></div> <div title="Code: 0xe843" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_lock"></i> <span class="i-name">whcom_icon_lock</span><span class="i-code">0xe843</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe844" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_pin"></i> <span class="i-name">whcom_icon_pin</span><span class="i-code">0xe844</span></div> <div title="Code: 0xe845" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_eye-off"></i> <span class="i-name">whcom_icon_eye-off</span><span class="i-code">0xe845</span></div> <div title="Code: 0xe846" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_eye"></i> <span class="i-name">whcom_icon_eye</span><span class="i-code">0xe846</span></div> <div title="Code: 0xe847" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_tag"></i> <span class="i-name">whcom_icon_tag</span><span class="i-code">0xe847</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe848" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_edit"></i> <span class="i-name">whcom_icon_edit</span><span class="i-code">0xe848</span></div> <div title="Code: 0xe849" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_pencil-1"></i> <span class="i-name">whcom_icon_pencil-1</span><span class="i-code">0xe849</span></div> <div title="Code: 0xe84a" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_chat"></i> <span class="i-name">whcom_icon_chat</span><span class="i-code">0xe84a</span></div> <div title="Code: 0xe84b" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_comment-1"></i> <span class="i-name">whcom_icon_comment-1</span><span class="i-code">0xe84b</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe84c" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_basket-1"></i> <span class="i-name">whcom_icon_basket-1</span><span class="i-code">0xe84c</span></div> <div title="Code: 0xe84d" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_up-open"></i> <span class="i-name">whcom_icon_up-open</span><span class="i-code">0xe84d</span></div> <div title="Code: 0xe84e" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_flight"></i> <span class="i-name">whcom_icon_flight</span><span class="i-code">0xe84e</span></div> <div title="Code: 0xe84f" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_gift"></i> <span class="i-name">whcom_icon_gift</span><span class="i-code">0xe84f</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe850" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_megaphone-1"></i> <span class="i-name">whcom_icon_megaphone-1</span><span class="i-code">0xe850</span></div> <div title="Code: 0xe851" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_book"></i> <span class="i-name">whcom_icon_book</span><span class="i-code">0xe851</span></div> <div title="Code: 0xe852" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_chart-bar"></i> <span class="i-name">whcom_icon_chart-bar</span><span class="i-code">0xe852</span></div> <div title="Code: 0xe853" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_credit-card"></i> <span class="i-name">whcom_icon_credit-card</span><span class="i-code">0xe853</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe854" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_floppy"></i> <span class="i-name">whcom_icon_floppy</span><span class="i-code">0xe854</span></div> <div title="Code: 0xe855" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_doc-1"></i> <span class="i-name">whcom_icon_doc-1</span><span class="i-code">0xe855</span></div> <div title="Code: 0xe856" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_list"></i> <span class="i-name">whcom_icon_list</span><span class="i-code">0xe856</span></div> <div title="Code: 0xe857" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_align-left"></i> <span class="i-name">whcom_icon_align-left</span><span class="i-code">0xe857</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe858" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_align-center"></i> <span class="i-name">whcom_icon_align-center</span><span class="i-code">0xe858</span></div> <div title="Code: 0xe859" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_align-right"></i> <span class="i-name">whcom_icon_align-right</span><span class="i-code">0xe859</span></div> <div title="Code: 0xe85a" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_align-justify"></i> <span class="i-name">whcom_icon_align-justify</span><span class="i-code">0xe85a</span></div> <div title="Code: 0xe85b" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_indent-left"></i> <span class="i-name">whcom_icon_indent-left</span><span class="i-code">0xe85b</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe85c" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_indent-right"></i> <span class="i-name">whcom_icon_indent-right</span><span class="i-code">0xe85c</span></div> <div title="Code: 0xe85d" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_glass"></i> <span class="i-name">whcom_icon_glass</span><span class="i-code">0xe85d</span></div> <div title="Code: 0xe85e" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_music"></i> <span class="i-name">whcom_icon_music</span><span class="i-code">0xe85e</span></div> <div title="Code: 0xe85f" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_search"></i> <span class="i-name">whcom_icon_search</span><span class="i-code">0xe85f</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe860" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_mail-2"></i> <span class="i-name">whcom_icon_mail-2</span><span class="i-code">0xe860</span></div> <div title="Code: 0xe861" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_video"></i> <span class="i-name">whcom_icon_video</span><span class="i-code">0xe861</span></div> <div title="Code: 0xe862" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_videocam"></i> <span class="i-name">whcom_icon_videocam</span><span class="i-code">0xe862</span></div> <div title="Code: 0xe863" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_picture"></i> <span class="i-name">whcom_icon_picture</span><span class="i-code">0xe863</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe864" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_camera"></i> <span class="i-name">whcom_icon_camera</span><span class="i-code">0xe864</span></div> <div title="Code: 0xe865" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_camera-alt"></i> <span class="i-name">whcom_icon_camera-alt</span><span class="i-code">0xe865</span></div> <div title="Code: 0xe866" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_th-large"></i> <span class="i-name">whcom_icon_th-large</span><span class="i-code">0xe866</span></div> <div title="Code: 0xe867" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_flag"></i> <span class="i-name">whcom_icon_flag</span><span class="i-code">0xe867</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe868" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_export"></i> <span class="i-name">whcom_icon_export</span><span class="i-code">0xe868</span></div> <div title="Code: 0xe869" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_help-circled"></i> <span class="i-name">whcom_icon_help-circled</span><span class="i-code">0xe869</span></div> <div title="Code: 0xe86a" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_info-circled"></i> <span class="i-name">whcom_icon_info-circled</span><span class="i-code">0xe86a</span></div> <div title="Code: 0xe86b" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_link"></i> <span class="i-name">whcom_icon_link</span><span class="i-code">0xe86b</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe86c" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_attach"></i> <span class="i-name">whcom_icon_attach</span><span class="i-code">0xe86c</span></div> <div title="Code: 0xe86d" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_download"></i> <span class="i-name">whcom_icon_download</span><span class="i-code">0xe86d</span></div> <div title="Code: 0xe86e" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_upload"></i> <span class="i-name">whcom_icon_upload</span><span class="i-code">0xe86e</span></div> <div title="Code: 0xe86f" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_forward"></i> <span class="i-name">whcom_icon_forward</span><span class="i-code">0xe86f</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe870" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_bell"></i> <span class="i-name">whcom_icon_bell</span><span class="i-code">0xe870</span></div> <div title="Code: 0xe871" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_print"></i> <span class="i-name">whcom_icon_print</span><span class="i-code">0xe871</span></div> <div title="Code: 0xe872" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_retweet"></i> <span class="i-name">whcom_icon_retweet</span><span class="i-code">0xe872</span></div> <div title="Code: 0xe873" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_location"></i> <span class="i-name">whcom_icon_location</span><span class="i-code">0xe873</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe874" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_folder"></i> <span class="i-name">whcom_icon_folder</span><span class="i-code">0xe874</span></div> <div title="Code: 0xe875" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_folder-open"></i> <span class="i-name">whcom_icon_folder-open</span><span class="i-code">0xe875</span></div> <div title="Code: 0xe876" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_login"></i> <span class="i-name">whcom_icon_login</span><span class="i-code">0xe876</span></div> <div title="Code: 0xe877" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_logout"></i> <span class="i-name">whcom_icon_logout</span><span class="i-code">0xe877</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe878" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_down-circled2"></i> <span class="i-name">whcom_icon_down-circled2</span><span class="i-code">0xe878</span></div> <div title="Code: 0xe879" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_up-circled2"></i> <span class="i-name">whcom_icon_up-circled2</span><span class="i-code">0xe879</span></div> <div title="Code: 0xe87a" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_volume-off"></i> <span class="i-name">whcom_icon_volume-off</span><span class="i-code">0xe87a</span></div> <div title="Code: 0xe87b" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_volume-down"></i> <span class="i-name">whcom_icon_volume-down</span><span class="i-code">0xe87b</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe87c" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_volume-up"></i> <span class="i-name">whcom_icon_volume-up</span><span class="i-code">0xe87c</span></div> <div title="Code: 0xe87d" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_headphones"></i> <span class="i-name">whcom_icon_headphones</span><span class="i-code">0xe87d</span></div> <div title="Code: 0xe87e" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_clock"></i> <span class="i-name">whcom_icon_clock</span><span class="i-code">0xe87e</span></div> <div title="Code: 0xe87f" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_block"></i> <span class="i-name">whcom_icon_block</span><span class="i-code">0xe87f</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe880" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_resize-full"></i> <span class="i-name">whcom_icon_resize-full</span><span class="i-code">0xe880</span></div> <div title="Code: 0xe881" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_resize-small"></i> <span class="i-name">whcom_icon_resize-small</span><span class="i-code">0xe881</span></div> <div title="Code: 0xe882" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_resize-vertical"></i> <span class="i-name">whcom_icon_resize-vertical</span><span class="i-code">0xe882</span></div> <div title="Code: 0xe883" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_resize-horizontal"></i> <span class="i-name">whcom_icon_resize-horizontal</span><span class="i-code">0xe883</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe884" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_zoom-in"></i> <span class="i-name">whcom_icon_zoom-in</span><span class="i-code">0xe884</span></div> <div title="Code: 0xe885" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_zoom-out"></i> <span class="i-name">whcom_icon_zoom-out</span><span class="i-code">0xe885</span></div> <div title="Code: 0xe886" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_calendar"></i> <span class="i-name">whcom_icon_calendar</span><span class="i-code">0xe886</span></div> <div title="Code: 0xe887" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_phone"></i> <span class="i-name">whcom_icon_phone</span><span class="i-code">0xe887</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe888" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_down-big"></i> <span class="i-name">whcom_icon_down-big</span><span class="i-code">0xe888</span></div> <div title="Code: 0xe889" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_left-big"></i> <span class="i-name">whcom_icon_left-big</span><span class="i-code">0xe889</span></div> <div title="Code: 0xe88a" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_right-big"></i> <span class="i-name">whcom_icon_right-big</span><span class="i-code">0xe88a</span></div> <div title="Code: 0xe88b" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_up-big"></i> <span class="i-name">whcom_icon_up-big</span><span class="i-code">0xe88b</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe88c" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_right-hand"></i> <span class="i-name">whcom_icon_right-hand</span><span class="i-code">0xe88c</span></div> <div title="Code: 0xe88d" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_left-hand"></i> <span class="i-name">whcom_icon_left-hand</span><span class="i-code">0xe88d</span></div> <div title="Code: 0xe88e" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_up-hand"></i> <span class="i-name">whcom_icon_up-hand</span><span class="i-code">0xe88e</span></div> <div title="Code: 0xe88f" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_down-hand"></i> <span class="i-name">whcom_icon_down-hand</span><span class="i-code">0xe88f</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe890" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_shuffle"></i> <span class="i-name">whcom_icon_shuffle</span><span class="i-code">0xe890</span></div> <div title="Code: 0xe891" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_play"></i> <span class="i-name">whcom_icon_play</span><span class="i-code">0xe891</span></div> <div title="Code: 0xe892" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_play-circled2"></i> <span class="i-name">whcom_icon_play-circled2</span><span class="i-code">0xe892</span></div> <div title="Code: 0xe893" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_stop"></i> <span class="i-name">whcom_icon_stop</span><span class="i-code">0xe893</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe894" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_pause"></i> <span class="i-name">whcom_icon_pause</span><span class="i-code">0xe894</span></div> <div title="Code: 0xe895" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_to-end"></i> <span class="i-name">whcom_icon_to-end</span><span class="i-code">0xe895</span></div> <div title="Code: 0xe896" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_to-end-alt"></i> <span class="i-name">whcom_icon_to-end-alt</span><span class="i-code">0xe896</span></div> <div title="Code: 0xe897" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_to-start"></i> <span class="i-name">whcom_icon_to-start</span><span class="i-code">0xe897</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe898" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_cw"></i> <span class="i-name">whcom_icon_cw</span><span class="i-code">0xe898</span></div> <div title="Code: 0xe899" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_ccw"></i> <span class="i-name">whcom_icon_ccw</span><span class="i-code">0xe899</span></div> <div title="Code: 0xe89a" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_arrows-cw"></i> <span class="i-name">whcom_icon_arrows-cw</span><span class="i-code">0xe89a</span></div> <div title="Code: 0xe89b" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_to-start-alt"></i> <span class="i-name">whcom_icon_to-start-alt</span><span class="i-code">0xe89b</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe89c" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_fast-fw"></i> <span class="i-name">whcom_icon_fast-fw</span><span class="i-code">0xe89c</span></div> <div title="Code: 0xe89d" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_fast-bw"></i> <span class="i-name">whcom_icon_fast-bw</span><span class="i-code">0xe89d</span></div> <div title="Code: 0xe89e" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_eject"></i> <span class="i-name">whcom_icon_eject</span><span class="i-code">0xe89e</span></div> <div title="Code: 0xe89f" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_target"></i> <span class="i-name">whcom_icon_target</span><span class="i-code">0xe89f</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe8a0" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_signal"></i> <span class="i-name">whcom_icon_signal</span><span class="i-code">0xe8a0</span></div> <div title="Code: 0xe8a1" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_award"></i> <span class="i-name">whcom_icon_award</span><span class="i-code">0xe8a1</span></div> <div title="Code: 0xe8a2" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_inbox"></i> <span class="i-name">whcom_icon_inbox</span><span class="i-code">0xe8a2</span></div> <div title="Code: 0xe8a3" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_globe-1"></i> <span class="i-name">whcom_icon_globe-1</span><span class="i-code">0xe8a3</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe8a4" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_cloud"></i> <span class="i-name">whcom_icon_cloud</span><span class="i-code">0xe8a4</span></div> <div title="Code: 0xe8a5" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_flash"></i> <span class="i-name">whcom_icon_flash</span><span class="i-code">0xe8a5</span></div> <div title="Code: 0xe8a6" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_umbrella"></i> <span class="i-name">whcom_icon_umbrella</span><span class="i-code">0xe8a6</span></div> <div title="Code: 0xe8a7" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_leaf"></i> <span class="i-name">whcom_icon_leaf</span><span class="i-code">0xe8a7</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe8a8" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_font"></i> <span class="i-name">whcom_icon_font</span><span class="i-code">0xe8a8</span></div> <div title="Code: 0xe8a9" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_bold"></i> <span class="i-name">whcom_icon_bold</span><span class="i-code">0xe8a9</span></div> <div title="Code: 0xe8aa" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_italic"></i> <span class="i-name">whcom_icon_italic</span><span class="i-code">0xe8aa</span></div> <div title="Code: 0xe8ab" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_text-height"></i> <span class="i-name">whcom_icon_text-height</span><span class="i-code">0xe8ab</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe8ac" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_text-width"></i> <span class="i-name">whcom_icon_text-width</span><span class="i-code">0xe8ac</span></div> <div title="Code: 0xe8ad" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_scissors"></i> <span class="i-name">whcom_icon_scissors</span><span class="i-code">0xe8ad</span></div> <div title="Code: 0xe8ae" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_briefcase"></i> <span class="i-name">whcom_icon_briefcase</span><span class="i-code">0xe8ae</span></div> <div title="Code: 0xe8af" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_off"></i> <span class="i-name">whcom_icon_off</span><span class="i-code">0xe8af</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe8b0" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_road"></i> <span class="i-name">whcom_icon_road</span><span class="i-code">0xe8b0</span></div> <div title="Code: 0xe8b1" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_list-alt"></i> <span class="i-name">whcom_icon_list-alt</span><span class="i-code">0xe8b1</span></div> <div title="Code: 0xe8b2" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_qrcode"></i> <span class="i-name">whcom_icon_qrcode</span><span class="i-code">0xe8b2</span></div> <div title="Code: 0xe8b3" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_barcode"></i> <span class="i-name">whcom_icon_barcode</span><span class="i-code">0xe8b3</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe8b4" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_adjust"></i> <span class="i-name">whcom_icon_adjust</span><span class="i-code">0xe8b4</span></div> <div title="Code: 0xe8b5" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_tint"></i> <span class="i-name">whcom_icon_tint</span><span class="i-code">0xe8b5</span></div> <div title="Code: 0xe8b6" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_check"></i> <span class="i-name">whcom_icon_check</span><span class="i-code">0xe8b6</span></div> <div title="Code: 0xe8b7" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_asterisk"></i> <span class="i-name">whcom_icon_asterisk</span><span class="i-code">0xe8b7</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe8b8" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_fire"></i> <span class="i-name">whcom_icon_fire</span><span class="i-code">0xe8b8</span></div> <div title="Code: 0xe8b9" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_magnet"></i> <span class="i-name">whcom_icon_magnet</span><span class="i-code">0xe8b9</span></div> <div title="Code: 0xe8ba" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_key"></i> <span class="i-name">whcom_icon_key</span><span class="i-code">0xe8ba</span></div> <div title="Code: 0xe8bb" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_truck"></i> <span class="i-name">whcom_icon_truck</span><span class="i-code">0xe8bb</span></div> </div> <div class="whcom_row"> <div title="Code: 0xe8bc" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_hammer"></i> <span class="i-name">whcom_icon_hammer</span><span class="i-code">0xe8bc</span></div> <div title="Code: 0xe8bd" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_lemon"></i> <span class="i-name">whcom_icon_lemon</span><span class="i-code">0xe8bd</span></div> <div title="Code: 0xf047" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_move"></i> <span class="i-name">whcom_icon_move</span><span class="i-code">0xf047</span></div> <div title="Code: 0xf08e" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_link-ext"></i> <span class="i-name">whcom_icon_link-ext</span><span class="i-code">0xf08e</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf096" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_check-empty"></i> <span class="i-name">whcom_icon_check-empty</span><span class="i-code">0xf096</span></div> <div title="Code: 0xf097" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_bookmark-empty"></i> <span class="i-name">whcom_icon_bookmark-empty</span><span class="i-code">0xf097</span></div> <div title="Code: 0xf098" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_phone-squared"></i> <span class="i-name">whcom_icon_phone-squared</span><span class="i-code">0xf098</span></div> <div title="Code: 0xf099" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_twitter"></i> <span class="i-name">whcom_icon_twitter</span><span class="i-code">0xf099</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf09a" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_facebook"></i> <span class="i-name">whcom_icon_facebook</span><span class="i-code">0xf09a</span></div> <div title="Code: 0xf09b" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_github-circled"></i> <span class="i-name">whcom_icon_github-circled</span><span class="i-code">0xf09b</span></div> <div title="Code: 0xf09e" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_rss"></i> <span class="i-name">whcom_icon_rss</span><span class="i-code">0xf09e</span></div> <div title="Code: 0xf0a0" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_hdd"></i> <span class="i-name">whcom_icon_hdd</span><span class="i-code">0xf0a0</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf0a3" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_certificate"></i> <span class="i-name">whcom_icon_certificate</span><span class="i-code">0xf0a3</span></div> <div title="Code: 0xf0a8" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_left-circled"></i> <span class="i-name">whcom_icon_left-circled</span><span class="i-code">0xf0a8</span></div> <div title="Code: 0xf0a9" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_right-circled"></i> <span class="i-name">whcom_icon_right-circled</span><span class="i-code">0xf0a9</span></div> <div title="Code: 0xf0aa" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_up-circled"></i> <span class="i-name">whcom_icon_up-circled</span><span class="i-code">0xf0aa</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf0ab" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_down-circled"></i> <span class="i-name">whcom_icon_down-circled</span><span class="i-code">0xf0ab</span></div> <div title="Code: 0xf0ae" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_tasks"></i> <span class="i-name">whcom_icon_tasks</span><span class="i-code">0xf0ae</span></div> <div title="Code: 0xf0b0" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_filter"></i> <span class="i-name">whcom_icon_filter</span><span class="i-code">0xf0b0</span></div> <div title="Code: 0xf0b2" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_resize-full-alt"></i> <span class="i-name">whcom_icon_resize-full-alt</span><span class="i-code">0xf0b2</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf0c3" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_beaker"></i> <span class="i-name">whcom_icon_beaker</span><span class="i-code">0xf0c3</span></div> <div title="Code: 0xf0c5" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_docs"></i> <span class="i-name">whcom_icon_docs</span><span class="i-code">0xf0c5</span></div> <div title="Code: 0xf0c8" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_blank"></i> <span class="i-name">whcom_icon_blank</span><span class="i-code">0xf0c8</span></div> <div title="Code: 0xf0c9" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_menu"></i> <span class="i-name">whcom_icon_menu</span><span class="i-code">0xf0c9</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf0ca" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_list-bullet"></i> <span class="i-name">whcom_icon_list-bullet</span><span class="i-code">0xf0ca</span></div> <div title="Code: 0xf0cb" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_list-numbered"></i> <span class="i-name">whcom_icon_list-numbered</span><span class="i-code">0xf0cb</span></div> <div title="Code: 0xf0cc" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_strike"></i> <span class="i-name">whcom_icon_strike</span><span class="i-code">0xf0cc</span></div> <div title="Code: 0xf0cd" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_underline"></i> <span class="i-name">whcom_icon_underline</span><span class="i-code">0xf0cd</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf0ce" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_table"></i> <span class="i-name">whcom_icon_table</span><span class="i-code">0xf0ce</span></div> <div title="Code: 0xf0d0" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_magic"></i> <span class="i-name">whcom_icon_magic</span><span class="i-code">0xf0d0</span></div> <div title="Code: 0xf0d2" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_pinterest-circled"></i> <span class="i-name">whcom_icon_pinterest-circled</span><span class="i-code">0xf0d2</span></div> <div title="Code: 0xf0d3" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_pinterest-squared"></i> <span class="i-name">whcom_icon_pinterest-squared</span><span class="i-code">0xf0d3</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf0d4" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_gplus-squared"></i> <span class="i-name">whcom_icon_gplus-squared</span><span class="i-code">0xf0d4</span></div> <div title="Code: 0xf0d5" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_gplus"></i> <span class="i-name">whcom_icon_gplus</span><span class="i-code">0xf0d5</span></div> <div title="Code: 0xf0d6" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_money"></i> <span class="i-name">whcom_icon_money</span><span class="i-code">0xf0d6</span></div> <div title="Code: 0xf0db" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_columns"></i> <span class="i-name">whcom_icon_columns</span><span class="i-code">0xf0db</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf0dc" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_sort"></i> <span class="i-name">whcom_icon_sort</span><span class="i-code">0xf0dc</span></div> <div title="Code: 0xf0dd" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_sort-down"></i> <span class="i-name">whcom_icon_sort-down</span><span class="i-code">0xf0dd</span></div> <div title="Code: 0xf0de" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_sort-up"></i> <span class="i-name">whcom_icon_sort-up</span><span class="i-code">0xf0de</span></div> <div title="Code: 0xf0e0" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_mail-alt"></i> <span class="i-name">whcom_icon_mail-alt</span><span class="i-code">0xf0e0</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf0e1" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_linkedin"></i> <span class="i-name">whcom_icon_linkedin</span><span class="i-code">0xf0e1</span></div> <div title="Code: 0xf0e4" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_gauge"></i> <span class="i-name">whcom_icon_gauge</span><span class="i-code">0xf0e4</span></div> <div title="Code: 0xf0e5" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_comment-empty"></i> <span class="i-name">whcom_icon_comment-empty</span><span class="i-code">0xf0e5</span></div> <div title="Code: 0xf0e6" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_chat-empty"></i> <span class="i-name">whcom_icon_chat-empty</span><span class="i-code">0xf0e6</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf0e8" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_sitemap"></i> <span class="i-name">whcom_icon_sitemap</span><span class="i-code">0xf0e8</span></div> <div title="Code: 0xf0ea" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_paste"></i> <span class="i-name">whcom_icon_paste</span><span class="i-code">0xf0ea</span></div> <div title="Code: 0xf0eb" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_lightbulb-1"></i> <span class="i-name">whcom_icon_lightbulb-1</span><span class="i-code">0xf0eb</span></div> <div title="Code: 0xf0ec" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_exchange"></i> <span class="i-name">whcom_icon_exchange</span><span class="i-code">0xf0ec</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf0ed" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_download-cloud"></i> <span class="i-name">whcom_icon_download-cloud</span><span class="i-code">0xf0ed</span></div> <div title="Code: 0xf0ee" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_upload-cloud"></i> <span class="i-name">whcom_icon_upload-cloud</span><span class="i-code">0xf0ee</span></div> <div title="Code: 0xf0f0" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_user-md"></i> <span class="i-name">whcom_icon_user-md</span><span class="i-code">0xf0f0</span></div> <div title="Code: 0xf0f1" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_stethoscope"></i> <span class="i-name">whcom_icon_stethoscope</span><span class="i-code">0xf0f1</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf0f2" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_suitcase"></i> <span class="i-name">whcom_icon_suitcase</span><span class="i-code">0xf0f2</span></div> <div title="Code: 0xf0f3" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_bell-alt"></i> <span class="i-name">whcom_icon_bell-alt</span><span class="i-code">0xf0f3</span></div> <div title="Code: 0xf0f4" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_coffee"></i> <span class="i-name">whcom_icon_coffee</span><span class="i-code">0xf0f4</span></div> <div title="Code: 0xf0f5" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_food"></i> <span class="i-name">whcom_icon_food</span><span class="i-code">0xf0f5</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf0f6" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_doc-text"></i> <span class="i-name">whcom_icon_doc-text</span><span class="i-code">0xf0f6</span></div> <div title="Code: 0xf0f7" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_building"></i> <span class="i-name">whcom_icon_building</span><span class="i-code">0xf0f7</span></div> <div title="Code: 0xf0f8" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_hospital"></i> <span class="i-name">whcom_icon_hospital</span><span class="i-code">0xf0f8</span></div> <div title="Code: 0xf0f9" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_ambulance"></i> <span class="i-name">whcom_icon_ambulance</span><span class="i-code">0xf0f9</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf0fa" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_medkit"></i> <span class="i-name">whcom_icon_medkit</span><span class="i-code">0xf0fa</span></div> <div title="Code: 0xf0fb" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_fighter-jet"></i> <span class="i-name">whcom_icon_fighter-jet</span><span class="i-code">0xf0fb</span></div> <div title="Code: 0xf0fc" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_beer"></i> <span class="i-name">whcom_icon_beer</span><span class="i-code">0xf0fc</span></div> <div title="Code: 0xf0fd" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_h-sigh"></i> <span class="i-name">whcom_icon_h-sigh</span><span class="i-code">0xf0fd</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf0fe" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_plus-squared"></i> <span class="i-name">whcom_icon_plus-squared</span><span class="i-code">0xf0fe</span></div> <div title="Code: 0xf100" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_angle-double-left"></i> <span class="i-name">whcom_icon_angle-double-left</span><span class="i-code">0xf100</span></div> <div title="Code: 0xf101" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_angle-double-right"></i> <span class="i-name">whcom_icon_angle-double-right</span><span class="i-code">0xf101</span></div> <div title="Code: 0xf102" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_angle-double-up"></i> <span class="i-name">whcom_icon_angle-double-up</span><span class="i-code">0xf102</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf103" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_angle-double-down"></i> <span class="i-name">whcom_icon_angle-double-down</span><span class="i-code">0xf103</span></div> <div title="Code: 0xf104" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_angle-left"></i> <span class="i-name">whcom_icon_angle-left</span><span class="i-code">0xf104</span></div> <div title="Code: 0xf105" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_angle-right"></i> <span class="i-name">whcom_icon_angle-right</span><span class="i-code">0xf105</span></div> <div title="Code: 0xf106" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_angle-up"></i> <span class="i-name">whcom_icon_angle-up</span><span class="i-code">0xf106</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf107" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_angle-down"></i> <span class="i-name">whcom_icon_angle-down</span><span class="i-code">0xf107</span></div> <div title="Code: 0xf108" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_desktop"></i> <span class="i-name">whcom_icon_desktop</span><span class="i-code">0xf108</span></div> <div title="Code: 0xf109" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_laptop"></i> <span class="i-name">whcom_icon_laptop</span><span class="i-code">0xf109</span></div> <div title="Code: 0xf10a" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_tablet"></i> <span class="i-name">whcom_icon_tablet</span><span class="i-code">0xf10a</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf10b" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_mobile"></i> <span class="i-name">whcom_icon_mobile</span><span class="i-code">0xf10b</span></div> <div title="Code: 0xf10c" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_circle-empty"></i> <span class="i-name">whcom_icon_circle-empty</span><span class="i-code">0xf10c</span></div> <div title="Code: 0xf10d" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_quote-left"></i> <span class="i-name">whcom_icon_quote-left</span><span class="i-code">0xf10d</span></div> <div title="Code: 0xf10e" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_quote-right"></i> <span class="i-name">whcom_icon_quote-right</span><span class="i-code">0xf10e</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf110" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_spinner-1"></i> <span class="i-name">whcom_icon_spinner-1</span><span class="i-code">0xf110</span></div> <div title="Code: 0xf111" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_circle"></i> <span class="i-name">whcom_icon_circle</span><span class="i-code">0xf111</span></div> <div title="Code: 0xf112" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_reply"></i> <span class="i-name">whcom_icon_reply</span><span class="i-code">0xf112</span></div> <div title="Code: 0xf113" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_github"></i> <span class="i-name">whcom_icon_github</span><span class="i-code">0xf113</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf114" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_folder-empty"></i> <span class="i-name">whcom_icon_folder-empty</span><span class="i-code">0xf114</span></div> <div title="Code: 0xf115" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_folder-open-empty"></i> <span class="i-name">whcom_icon_folder-open-empty</span><span class="i-code">0xf115</span></div> <div title="Code: 0xf118" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_smile"></i> <span class="i-name">whcom_icon_smile</span><span class="i-code">0xf118</span></div> <div title="Code: 0xf119" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_frown"></i> <span class="i-name">whcom_icon_frown</span><span class="i-code">0xf119</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf11a" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_meh"></i> <span class="i-name">whcom_icon_meh</span><span class="i-code">0xf11a</span></div> <div title="Code: 0xf11b" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_gamepad"></i> <span class="i-name">whcom_icon_gamepad</span><span class="i-code">0xf11b</span></div> <div title="Code: 0xf11c" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_keyboard"></i> <span class="i-name">whcom_icon_keyboard</span><span class="i-code">0xf11c</span></div> <div title="Code: 0xf11d" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_flag-empty"></i> <span class="i-name">whcom_icon_flag-empty</span><span class="i-code">0xf11d</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf11e" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_flag-checkered"></i> <span class="i-name">whcom_icon_flag-checkered</span><span class="i-code">0xf11e</span></div> <div title="Code: 0xf120" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_terminal"></i> <span class="i-name">whcom_icon_terminal</span><span class="i-code">0xf120</span></div> <div title="Code: 0xf121" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_code"></i> <span class="i-name">whcom_icon_code</span><span class="i-code">0xf121</span></div> <div title="Code: 0xf122" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_reply-all"></i> <span class="i-name">whcom_icon_reply-all</span><span class="i-code">0xf122</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf123" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_star-half-alt"></i> <span class="i-name">whcom_icon_star-half-alt</span><span class="i-code">0xf123</span></div> <div title="Code: 0xf124" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_direction"></i> <span class="i-name">whcom_icon_direction</span><span class="i-code">0xf124</span></div> <div title="Code: 0xf125" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_crop"></i> <span class="i-name">whcom_icon_crop</span><span class="i-code">0xf125</span></div> <div title="Code: 0xf126" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_fork"></i> <span class="i-name">whcom_icon_fork</span><span class="i-code">0xf126</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf127" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_unlink"></i> <span class="i-name">whcom_icon_unlink</span><span class="i-code">0xf127</span></div> <div title="Code: 0xf128" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_help"></i> <span class="i-name">whcom_icon_help</span><span class="i-code">0xf128</span></div> <div title="Code: 0xf129" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_info"></i> <span class="i-name">whcom_icon_info</span><span class="i-code">0xf129</span></div> <div title="Code: 0xf12a" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_attention-alt"></i> <span class="i-name">whcom_icon_attention-alt</span><span class="i-code">0xf12a</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf12b" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_superscript"></i> <span class="i-name">whcom_icon_superscript</span><span class="i-code">0xf12b</span></div> <div title="Code: 0xf12c" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_subscript"></i> <span class="i-name">whcom_icon_subscript</span><span class="i-code">0xf12c</span></div> <div title="Code: 0xf12d" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_eraser"></i> <span class="i-name">whcom_icon_eraser</span><span class="i-code">0xf12d</span></div> <div title="Code: 0xf12e" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_puzzle"></i> <span class="i-name">whcom_icon_puzzle</span><span class="i-code">0xf12e</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf130" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_mic"></i> <span class="i-name">whcom_icon_mic</span><span class="i-code">0xf130</span></div> <div title="Code: 0xf131" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_mute"></i> <span class="i-name">whcom_icon_mute</span><span class="i-code">0xf131</span></div> <div title="Code: 0xf132" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_shield"></i> <span class="i-name">whcom_icon_shield</span><span class="i-code">0xf132</span></div> <div title="Code: 0xf133" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_calendar-empty"></i> <span class="i-name">whcom_icon_calendar-empty</span><span class="i-code">0xf133</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf134" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_extinguisher"></i> <span class="i-name">whcom_icon_extinguisher</span><span class="i-code">0xf134</span></div> <div title="Code: 0xf135" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_rocket-1"></i> <span class="i-name">whcom_icon_rocket-1</span><span class="i-code">0xf135</span></div> <div title="Code: 0xf136" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_maxcdn"></i> <span class="i-name">whcom_icon_maxcdn</span><span class="i-code">0xf136</span></div> <div title="Code: 0xf137" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_angle-circled-left"></i> <span class="i-name">whcom_icon_angle-circled-left</span><span class="i-code">0xf137</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf138" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_angle-circled-right"></i> <span class="i-name">whcom_icon_angle-circled-right</span><span class="i-code">0xf138</span></div> <div title="Code: 0xf139" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_angle-circled-up"></i> <span class="i-name">whcom_icon_angle-circled-up</span><span class="i-code">0xf139</span></div> <div title="Code: 0xf13a" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_angle-circled-down"></i> <span class="i-name">whcom_icon_angle-circled-down</span><span class="i-code">0xf13a</span></div> <div title="Code: 0xf13b" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_html5"></i> <span class="i-name">whcom_icon_html5</span><span class="i-code">0xf13b</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf13c" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_css3"></i> <span class="i-name">whcom_icon_css3</span><span class="i-code">0xf13c</span></div> <div title="Code: 0xf13d" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_anchor"></i> <span class="i-name">whcom_icon_anchor</span><span class="i-code">0xf13d</span></div> <div title="Code: 0xf13e" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_lock-open-alt"></i> <span class="i-name">whcom_icon_lock-open-alt</span><span class="i-code">0xf13e</span></div> <div title="Code: 0xf140" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_bullseye"></i> <span class="i-name">whcom_icon_bullseye</span><span class="i-code">0xf140</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf141" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_ellipsis"></i> <span class="i-name">whcom_icon_ellipsis</span><span class="i-code">0xf141</span></div> <div title="Code: 0xf142" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_ellipsis-vert"></i> <span class="i-name">whcom_icon_ellipsis-vert</span><span class="i-code">0xf142</span></div> <div title="Code: 0xf143" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_rss-squared"></i> <span class="i-name">whcom_icon_rss-squared</span><span class="i-code">0xf143</span></div> <div title="Code: 0xf144" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_play-circled"></i> <span class="i-name">whcom_icon_play-circled</span><span class="i-code">0xf144</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf145" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_ticket"></i> <span class="i-name">whcom_icon_ticket</span><span class="i-code">0xf145</span></div> <div title="Code: 0xf146" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_minus-squared"></i> <span class="i-name">whcom_icon_minus-squared</span><span class="i-code">0xf146</span></div> <div title="Code: 0xf147" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_minus-squared-alt"></i> <span class="i-name">whcom_icon_minus-squared-alt</span><span class="i-code">0xf147</span></div> <div title="Code: 0xf148" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_level-up"></i> <span class="i-name">whcom_icon_level-up</span><span class="i-code">0xf148</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf149" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_level-down"></i> <span class="i-name">whcom_icon_level-down</span><span class="i-code">0xf149</span></div> <div title="Code: 0xf14a" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_ok-squared"></i> <span class="i-name">whcom_icon_ok-squared</span><span class="i-code">0xf14a</span></div> <div title="Code: 0xf14b" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_pencil-squared"></i> <span class="i-name">whcom_icon_pencil-squared</span><span class="i-code">0xf14b</span></div> <div title="Code: 0xf14c" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_link-ext-alt"></i> <span class="i-name">whcom_icon_link-ext-alt</span><span class="i-code">0xf14c</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf14d" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_export-alt"></i> <span class="i-name">whcom_icon_export-alt</span><span class="i-code">0xf14d</span></div> <div title="Code: 0xf14e" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_compass"></i> <span class="i-name">whcom_icon_compass</span><span class="i-code">0xf14e</span></div> <div title="Code: 0xf150" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_expand"></i> <span class="i-name">whcom_icon_expand</span><span class="i-code">0xf150</span></div> <div title="Code: 0xf151" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_collapse"></i> <span class="i-name">whcom_icon_collapse</span><span class="i-code">0xf151</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf152" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_expand-right"></i> <span class="i-name">whcom_icon_expand-right</span><span class="i-code">0xf152</span></div> <div title="Code: 0xf153" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_euro"></i> <span class="i-name">whcom_icon_euro</span><span class="i-code">0xf153</span></div> <div title="Code: 0xf154" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_pound"></i> <span class="i-name">whcom_icon_pound</span><span class="i-code">0xf154</span></div> <div title="Code: 0xf155" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_dollar"></i> <span class="i-name">whcom_icon_dollar</span><span class="i-code">0xf155</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf156" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_rupee"></i> <span class="i-name">whcom_icon_rupee</span><span class="i-code">0xf156</span></div> <div title="Code: 0xf157" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_yen"></i> <span class="i-name">whcom_icon_yen</span><span class="i-code">0xf157</span></div> <div title="Code: 0xf158" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_rouble"></i> <span class="i-name">whcom_icon_rouble</span><span class="i-code">0xf158</span></div> <div title="Code: 0xf159" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_won"></i> <span class="i-name">whcom_icon_won</span><span class="i-code">0xf159</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf15a" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_bitcoin"></i> <span class="i-name">whcom_icon_bitcoin</span><span class="i-code">0xf15a</span></div> <div title="Code: 0xf15b" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_doc-inv"></i> <span class="i-name">whcom_icon_doc-inv</span><span class="i-code">0xf15b</span></div> <div title="Code: 0xf15c" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_doc-text-inv"></i> <span class="i-name">whcom_icon_doc-text-inv</span><span class="i-code">0xf15c</span></div> <div title="Code: 0xf15d" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_sort-name-up"></i> <span class="i-name">whcom_icon_sort-name-up</span><span class="i-code">0xf15d</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf15e" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_sort-name-down"></i> <span class="i-name">whcom_icon_sort-name-down</span><span class="i-code">0xf15e</span></div> <div title="Code: 0xf160" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_sort-alt-up"></i> <span class="i-name">whcom_icon_sort-alt-up</span><span class="i-code">0xf160</span></div> <div title="Code: 0xf161" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_sort-alt-down"></i> <span class="i-name">whcom_icon_sort-alt-down</span><span class="i-code">0xf161</span></div> <div title="Code: 0xf162" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_sort-number-up"></i> <span class="i-name">whcom_icon_sort-number-up</span><span class="i-code">0xf162</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf163" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_sort-number-down"></i> <span class="i-name">whcom_icon_sort-number-down</span><span class="i-code">0xf163</span></div> <div title="Code: 0xf164" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_thumbs-up-alt"></i> <span class="i-name">whcom_icon_thumbs-up-alt</span><span class="i-code">0xf164</span></div> <div title="Code: 0xf165" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_thumbs-down-alt"></i> <span class="i-name">whcom_icon_thumbs-down-alt</span><span class="i-code">0xf165</span></div> <div title="Code: 0xf166" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_youtube-squared"></i> <span class="i-name">whcom_icon_youtube-squared</span><span class="i-code">0xf166</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf167" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_youtube"></i> <span class="i-name">whcom_icon_youtube</span><span class="i-code">0xf167</span></div> <div title="Code: 0xf168" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_xing"></i> <span class="i-name">whcom_icon_xing</span><span class="i-code">0xf168</span></div> <div title="Code: 0xf169" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_xing-squared"></i> <span class="i-name">whcom_icon_xing-squared</span><span class="i-code">0xf169</span></div> <div title="Code: 0xf16a" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_youtube-play"></i> <span class="i-name">whcom_icon_youtube-play</span><span class="i-code">0xf16a</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf16b" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_dropbox"></i> <span class="i-name">whcom_icon_dropbox</span><span class="i-code">0xf16b</span></div> <div title="Code: 0xf16c" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_stackoverflow"></i> <span class="i-name">whcom_icon_stackoverflow</span><span class="i-code">0xf16c</span></div> <div title="Code: 0xf16d" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_instagram"></i> <span class="i-name">whcom_icon_instagram</span><span class="i-code">0xf16d</span></div> <div title="Code: 0xf16e" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_flickr"></i> <span class="i-name">whcom_icon_flickr</span><span class="i-code">0xf16e</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf170" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_adn"></i> <span class="i-name">whcom_icon_adn</span><span class="i-code">0xf170</span></div> <div title="Code: 0xf171" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_bitbucket"></i> <span class="i-name">whcom_icon_bitbucket</span><span class="i-code">0xf171</span></div> <div title="Code: 0xf172" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_bitbucket-squared"></i> <span class="i-name">whcom_icon_bitbucket-squared</span><span class="i-code">0xf172</span></div> <div title="Code: 0xf173" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_tumblr"></i> <span class="i-name">whcom_icon_tumblr</span><span class="i-code">0xf173</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf174" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_tumblr-squared"></i> <span class="i-name">whcom_icon_tumblr-squared</span><span class="i-code">0xf174</span></div> <div title="Code: 0xf175" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_down"></i> <span class="i-name">whcom_icon_down</span><span class="i-code">0xf175</span></div> <div title="Code: 0xf176" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_up"></i> <span class="i-name">whcom_icon_up</span><span class="i-code">0xf176</span></div> <div title="Code: 0xf177" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_left"></i> <span class="i-name">whcom_icon_left</span><span class="i-code">0xf177</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf178" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_right"></i> <span class="i-name">whcom_icon_right</span><span class="i-code">0xf178</span></div> <div title="Code: 0xf179" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_apple"></i> <span class="i-name">whcom_icon_apple</span><span class="i-code">0xf179</span></div> <div title="Code: 0xf17a" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_windows"></i> <span class="i-name">whcom_icon_windows</span><span class="i-code">0xf17a</span></div> <div title="Code: 0xf17b" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_android"></i> <span class="i-name">whcom_icon_android</span><span class="i-code">0xf17b</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf17c" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_linux"></i> <span class="i-name">whcom_icon_linux</span><span class="i-code">0xf17c</span></div> <div title="Code: 0xf17d" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_dribbble"></i> <span class="i-name">whcom_icon_dribbble</span><span class="i-code">0xf17d</span></div> <div title="Code: 0xf17e" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_skype"></i> <span class="i-name">whcom_icon_skype</span><span class="i-code">0xf17e</span></div> <div title="Code: 0xf180" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_foursquare"></i> <span class="i-name">whcom_icon_foursquare</span><span class="i-code">0xf180</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf181" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_trello"></i> <span class="i-name">whcom_icon_trello</span><span class="i-code">0xf181</span></div> <div title="Code: 0xf182" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_female"></i> <span class="i-name">whcom_icon_female</span><span class="i-code">0xf182</span></div> <div title="Code: 0xf183" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_male"></i> <span class="i-name">whcom_icon_male</span><span class="i-code">0xf183</span></div> <div title="Code: 0xf184" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_gittip"></i> <span class="i-name">whcom_icon_gittip</span><span class="i-code">0xf184</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf185" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_sun"></i> <span class="i-name">whcom_icon_sun</span><span class="i-code">0xf185</span></div> <div title="Code: 0xf186" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_moon"></i> <span class="i-name">whcom_icon_moon</span><span class="i-code">0xf186</span></div> <div title="Code: 0xf187" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_box"></i> <span class="i-name">whcom_icon_box</span><span class="i-code">0xf187</span></div> <div title="Code: 0xf188" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_bug"></i> <span class="i-name">whcom_icon_bug</span><span class="i-code">0xf188</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf189" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_vkontakte"></i> <span class="i-name">whcom_icon_vkontakte</span><span class="i-code">0xf189</span></div> <div title="Code: 0xf18a" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_weibo"></i> <span class="i-name">whcom_icon_weibo</span><span class="i-code">0xf18a</span></div> <div title="Code: 0xf18b" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_renren"></i> <span class="i-name">whcom_icon_renren</span><span class="i-code">0xf18b</span></div> <div title="Code: 0xf18c" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_pagelines"></i> <span class="i-name">whcom_icon_pagelines</span><span class="i-code">0xf18c</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf18d" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_stackexchange"></i> <span class="i-name">whcom_icon_stackexchange</span><span class="i-code">0xf18d</span></div> <div title="Code: 0xf18e" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_right-circled2"></i> <span class="i-name">whcom_icon_right-circled2</span><span class="i-code">0xf18e</span></div> <div title="Code: 0xf190" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_left-circled2"></i> <span class="i-name">whcom_icon_left-circled2</span><span class="i-code">0xf190</span></div> <div title="Code: 0xf191" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_collapse-left"></i> <span class="i-name">whcom_icon_collapse-left</span><span class="i-code">0xf191</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf192" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_dot-circled"></i> <span class="i-name">whcom_icon_dot-circled</span><span class="i-code">0xf192</span></div> <div title="Code: 0xf193" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_wheelchair"></i> <span class="i-name">whcom_icon_wheelchair</span><span class="i-code">0xf193</span></div> <div title="Code: 0xf194" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_vimeo-squared"></i> <span class="i-name">whcom_icon_vimeo-squared</span><span class="i-code">0xf194</span></div> <div title="Code: 0xf195" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_try"></i> <span class="i-name">whcom_icon_try</span><span class="i-code">0xf195</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf196" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_plus-squared-alt"></i> <span class="i-name">whcom_icon_plus-squared-alt</span><span class="i-code">0xf196</span></div> <div title="Code: 0xf197" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_space-shuttle"></i> <span class="i-name">whcom_icon_space-shuttle</span><span class="i-code">0xf197</span></div> <div title="Code: 0xf198" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_slack"></i> <span class="i-name">whcom_icon_slack</span><span class="i-code">0xf198</span></div> <div title="Code: 0xf199" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_mail-squared"></i> <span class="i-name">whcom_icon_mail-squared</span><span class="i-code">0xf199</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf19a" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_wordpress"></i> <span class="i-name">whcom_icon_wordpress</span><span class="i-code">0xf19a</span></div> <div title="Code: 0xf19b" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_openid"></i> <span class="i-name">whcom_icon_openid</span><span class="i-code">0xf19b</span></div> <div title="Code: 0xf19c" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_bank"></i> <span class="i-name">whcom_icon_bank</span><span class="i-code">0xf19c</span></div> <div title="Code: 0xf19d" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_graduation-cap"></i> <span class="i-name">whcom_icon_graduation-cap</span><span class="i-code">0xf19d</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf19e" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_yahoo"></i> <span class="i-name">whcom_icon_yahoo</span><span class="i-code">0xf19e</span></div> <div title="Code: 0xf1a0" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_google"></i> <span class="i-name">whcom_icon_google</span><span class="i-code">0xf1a0</span></div> <div title="Code: 0xf1a1" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_reddit"></i> <span class="i-name">whcom_icon_reddit</span><span class="i-code">0xf1a1</span></div> <div title="Code: 0xf1a2" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_reddit-squared"></i> <span class="i-name">whcom_icon_reddit-squared</span><span class="i-code">0xf1a2</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf1a3" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_stumbleupon-circled"></i> <span class="i-name">whcom_icon_stumbleupon-circled</span><span class="i-code">0xf1a3</span></div> <div title="Code: 0xf1a4" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_stumbleupon"></i> <span class="i-name">whcom_icon_stumbleupon</span><span class="i-code">0xf1a4</span></div> <div title="Code: 0xf1a5" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_delicious"></i> <span class="i-name">whcom_icon_delicious</span><span class="i-code">0xf1a5</span></div> <div title="Code: 0xf1a6" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_digg"></i> <span class="i-name">whcom_icon_digg</span><span class="i-code">0xf1a6</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf1a7" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_pied-piper-squared"></i> <span class="i-name">whcom_icon_pied-piper-squared</span><span class="i-code">0xf1a7</span></div> <div title="Code: 0xf1a8" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_pied-piper-alt"></i> <span class="i-name">whcom_icon_pied-piper-alt</span><span class="i-code">0xf1a8</span></div> <div title="Code: 0xf1a9" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_drupal"></i> <span class="i-name">whcom_icon_drupal</span><span class="i-code">0xf1a9</span></div> <div title="Code: 0xf1aa" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_joomla"></i> <span class="i-name">whcom_icon_joomla</span><span class="i-code">0xf1aa</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf1ab" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_language"></i> <span class="i-name">whcom_icon_language</span><span class="i-code">0xf1ab</span></div> <div title="Code: 0xf1ac" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_fax"></i> <span class="i-name">whcom_icon_fax</span><span class="i-code">0xf1ac</span></div> <div title="Code: 0xf1ad" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_building-filled"></i> <span class="i-name">whcom_icon_building-filled</span><span class="i-code">0xf1ad</span></div> <div title="Code: 0xf1ae" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_child"></i> <span class="i-name">whcom_icon_child</span><span class="i-code">0xf1ae</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf1b0" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_paw"></i> <span class="i-name">whcom_icon_paw</span><span class="i-code">0xf1b0</span></div> <div title="Code: 0xf1b1" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_spoon"></i> <span class="i-name">whcom_icon_spoon</span><span class="i-code">0xf1b1</span></div> <div title="Code: 0xf1b2" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_cube"></i> <span class="i-name">whcom_icon_cube</span><span class="i-code">0xf1b2</span></div> <div title="Code: 0xf1b3" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_cubes"></i> <span class="i-name">whcom_icon_cubes</span><span class="i-code">0xf1b3</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf1b4" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_behance"></i> <span class="i-name">whcom_icon_behance</span><span class="i-code">0xf1b4</span></div> <div title="Code: 0xf1b5" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_behance-squared"></i> <span class="i-name">whcom_icon_behance-squared</span><span class="i-code">0xf1b5</span></div> <div title="Code: 0xf1b6" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_steam"></i> <span class="i-name">whcom_icon_steam</span><span class="i-code">0xf1b6</span></div> <div title="Code: 0xf1b7" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_steam-squared"></i> <span class="i-name">whcom_icon_steam-squared</span><span class="i-code">0xf1b7</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf1b8" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_recycle"></i> <span class="i-name">whcom_icon_recycle</span><span class="i-code">0xf1b8</span></div> <div title="Code: 0xf1b9" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_cab"></i> <span class="i-name">whcom_icon_cab</span><span class="i-code">0xf1b9</span></div> <div title="Code: 0xf1ba" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_taxi"></i> <span class="i-name">whcom_icon_taxi</span><span class="i-code">0xf1ba</span></div> <div title="Code: 0xf1bb" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_tree"></i> <span class="i-name">whcom_icon_tree</span><span class="i-code">0xf1bb</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf1bc" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_spotify"></i> <span class="i-name">whcom_icon_spotify</span><span class="i-code">0xf1bc</span></div> <div title="Code: 0xf1bd" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_deviantart"></i> <span class="i-name">whcom_icon_deviantart</span><span class="i-code">0xf1bd</span></div> <div title="Code: 0xf1be" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_soundcloud"></i> <span class="i-name">whcom_icon_soundcloud</span><span class="i-code">0xf1be</span></div> <div title="Code: 0xf1c0" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_database-1"></i> <span class="i-name">whcom_icon_database-1</span><span class="i-code">0xf1c0</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf1c1" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_file-pdf"></i> <span class="i-name">whcom_icon_file-pdf</span><span class="i-code">0xf1c1</span></div> <div title="Code: 0xf1c2" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_file-word"></i> <span class="i-name">whcom_icon_file-word</span><span class="i-code">0xf1c2</span></div> <div title="Code: 0xf1c3" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_file-excel"></i> <span class="i-name">whcom_icon_file-excel</span><span class="i-code">0xf1c3</span></div> <div title="Code: 0xf1c4" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_file-powerpoint"></i> <span class="i-name">whcom_icon_file-powerpoint</span><span class="i-code">0xf1c4</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf1c5" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_file-image"></i> <span class="i-name">whcom_icon_file-image</span><span class="i-code">0xf1c5</span></div> <div title="Code: 0xf1c6" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_file-archive"></i> <span class="i-name">whcom_icon_file-archive</span><span class="i-code">0xf1c6</span></div> <div title="Code: 0xf1c7" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_file-audio"></i> <span class="i-name">whcom_icon_file-audio</span><span class="i-code">0xf1c7</span></div> <div title="Code: 0xf1c8" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_file-video"></i> <span class="i-name">whcom_icon_file-video</span><span class="i-code">0xf1c8</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf1c9" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_file-code"></i> <span class="i-name">whcom_icon_file-code</span><span class="i-code">0xf1c9</span></div> <div title="Code: 0xf1ca" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_vine"></i> <span class="i-name">whcom_icon_vine</span><span class="i-code">0xf1ca</span></div> <div title="Code: 0xf1cb" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_codeopen"></i> <span class="i-name">whcom_icon_codeopen</span><span class="i-code">0xf1cb</span></div> <div title="Code: 0xf1cc" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_jsfiddle"></i> <span class="i-name">whcom_icon_jsfiddle</span><span class="i-code">0xf1cc</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf1cd" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_lifebuoy"></i> <span class="i-name">whcom_icon_lifebuoy</span><span class="i-code">0xf1cd</span></div> <div title="Code: 0xf1ce" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_circle-notch"></i> <span class="i-name">whcom_icon_circle-notch</span><span class="i-code">0xf1ce</span></div> <div title="Code: 0xf1d0" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_rebel"></i> <span class="i-name">whcom_icon_rebel</span><span class="i-code">0xf1d0</span></div> <div title="Code: 0xf1d1" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_empire"></i> <span class="i-name">whcom_icon_empire</span><span class="i-code">0xf1d1</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf1d2" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_git-squared"></i> <span class="i-name">whcom_icon_git-squared</span><span class="i-code">0xf1d2</span></div> <div title="Code: 0xf1d3" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_git"></i> <span class="i-name">whcom_icon_git</span><span class="i-code">0xf1d3</span></div> <div title="Code: 0xf1d4" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_hacker-news"></i> <span class="i-name">whcom_icon_hacker-news</span><span class="i-code">0xf1d4</span></div> <div title="Code: 0xf1d5" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_tencent-weibo"></i> <span class="i-name">whcom_icon_tencent-weibo</span><span class="i-code">0xf1d5</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf1d6" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_qq"></i> <span class="i-name">whcom_icon_qq</span><span class="i-code">0xf1d6</span></div> <div title="Code: 0xf1d7" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_wechat"></i> <span class="i-name">whcom_icon_wechat</span><span class="i-code">0xf1d7</span></div> <div title="Code: 0xf1d8" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_paper-plane-1"></i> <span class="i-name">whcom_icon_paper-plane-1</span><span class="i-code">0xf1d8</span></div> <div title="Code: 0xf1d9" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_paper-plane-empty"></i> <span class="i-name">whcom_icon_paper-plane-empty</span><span class="i-code">0xf1d9</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf1da" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_history"></i> <span class="i-name">whcom_icon_history</span><span class="i-code">0xf1da</span></div> <div title="Code: 0xf1db" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_circle-thin"></i> <span class="i-name">whcom_icon_circle-thin</span><span class="i-code">0xf1db</span></div> <div title="Code: 0xf1dc" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_header"></i> <span class="i-name">whcom_icon_header</span><span class="i-code">0xf1dc</span></div> <div title="Code: 0xf1dd" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_paragraph"></i> <span class="i-name">whcom_icon_paragraph</span><span class="i-code">0xf1dd</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf1de" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_sliders"></i> <span class="i-name">whcom_icon_sliders</span><span class="i-code">0xf1de</span></div> <div title="Code: 0xf1e0" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_share"></i> <span class="i-name">whcom_icon_share</span><span class="i-code">0xf1e0</span></div> <div title="Code: 0xf1e1" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_share-squared"></i> <span class="i-name">whcom_icon_share-squared</span><span class="i-code">0xf1e1</span></div> <div title="Code: 0xf1e2" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_bomb"></i> <span class="i-name">whcom_icon_bomb</span><span class="i-code">0xf1e2</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf1e3" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_soccer-ball"></i> <span class="i-name">whcom_icon_soccer-ball</span><span class="i-code">0xf1e3</span></div> <div title="Code: 0xf1e4" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_tty"></i> <span class="i-name">whcom_icon_tty</span><span class="i-code">0xf1e4</span></div> <div title="Code: 0xf1e5" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_binoculars"></i> <span class="i-name">whcom_icon_binoculars</span><span class="i-code">0xf1e5</span></div> <div title="Code: 0xf1e6" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_plug"></i> <span class="i-name">whcom_icon_plug</span><span class="i-code">0xf1e6</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf1e7" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_slideshare"></i> <span class="i-name">whcom_icon_slideshare</span><span class="i-code">0xf1e7</span></div> <div title="Code: 0xf1e8" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_twitch"></i> <span class="i-name">whcom_icon_twitch</span><span class="i-code">0xf1e8</span></div> <div title="Code: 0xf1e9" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_yelp"></i> <span class="i-name">whcom_icon_yelp</span><span class="i-code">0xf1e9</span></div> <div title="Code: 0xf1ea" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_newspaper"></i> <span class="i-name">whcom_icon_newspaper</span><span class="i-code">0xf1ea</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf1eb" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_wifi"></i> <span class="i-name">whcom_icon_wifi</span><span class="i-code">0xf1eb</span></div> <div title="Code: 0xf1ec" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_calc"></i> <span class="i-name">whcom_icon_calc</span><span class="i-code">0xf1ec</span></div> <div title="Code: 0xf1ed" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_paypal"></i> <span class="i-name">whcom_icon_paypal</span><span class="i-code">0xf1ed</span></div> <div title="Code: 0xf1ee" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_gwallet"></i> <span class="i-name">whcom_icon_gwallet</span><span class="i-code">0xf1ee</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf1f0" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_cc-visa"></i> <span class="i-name">whcom_icon_cc-visa</span><span class="i-code">0xf1f0</span></div> <div title="Code: 0xf1f1" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_cc-mastercard"></i> <span class="i-name">whcom_icon_cc-mastercard</span><span class="i-code">0xf1f1</span></div> <div title="Code: 0xf1f2" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_cc-discover"></i> <span class="i-name">whcom_icon_cc-discover</span><span class="i-code">0xf1f2</span></div> <div title="Code: 0xf1f3" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_cc-amex"></i> <span class="i-name">whcom_icon_cc-amex</span><span class="i-code">0xf1f3</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf1f4" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_cc-paypal"></i> <span class="i-name">whcom_icon_cc-paypal</span><span class="i-code">0xf1f4</span></div> <div title="Code: 0xf1f5" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_cc-stripe"></i> <span class="i-name">whcom_icon_cc-stripe</span><span class="i-code">0xf1f5</span></div> <div title="Code: 0xf1f6" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_bell-off"></i> <span class="i-name">whcom_icon_bell-off</span><span class="i-code">0xf1f6</span></div> <div title="Code: 0xf1f7" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_bell-off-empty"></i> <span class="i-name">whcom_icon_bell-off-empty</span><span class="i-code">0xf1f7</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf1f8" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_trash-1"></i> <span class="i-name">whcom_icon_trash-1</span><span class="i-code">0xf1f8</span></div> <div title="Code: 0xf1f9" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_copyright"></i> <span class="i-name">whcom_icon_copyright</span><span class="i-code">0xf1f9</span></div> <div title="Code: 0xf1fa" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_at"></i> <span class="i-name">whcom_icon_at</span><span class="i-code">0xf1fa</span></div> <div title="Code: 0xf1fb" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_eyedropper"></i> <span class="i-name">whcom_icon_eyedropper</span><span class="i-code">0xf1fb</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf1fc" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_brush"></i> <span class="i-name">whcom_icon_brush</span><span class="i-code">0xf1fc</span></div> <div title="Code: 0xf1fd" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_birthday"></i> <span class="i-name">whcom_icon_birthday</span><span class="i-code">0xf1fd</span></div> <div title="Code: 0xf1fe" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_chart-area"></i> <span class="i-name">whcom_icon_chart-area</span><span class="i-code">0xf1fe</span></div> <div title="Code: 0xf200" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_chart-pie"></i> <span class="i-name">whcom_icon_chart-pie</span><span class="i-code">0xf200</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf201" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_chart-line"></i> <span class="i-name">whcom_icon_chart-line</span><span class="i-code">0xf201</span></div> <div title="Code: 0xf202" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_lastfm"></i> <span class="i-name">whcom_icon_lastfm</span><span class="i-code">0xf202</span></div> <div title="Code: 0xf203" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_lastfm-squared"></i> <span class="i-name">whcom_icon_lastfm-squared</span><span class="i-code">0xf203</span></div> <div title="Code: 0xf204" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_toggle-off"></i> <span class="i-name">whcom_icon_toggle-off</span><span class="i-code">0xf204</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf205" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_toggle-on"></i> <span class="i-name">whcom_icon_toggle-on</span><span class="i-code">0xf205</span></div> <div title="Code: 0xf206" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_bicycle"></i> <span class="i-name">whcom_icon_bicycle</span><span class="i-code">0xf206</span></div> <div title="Code: 0xf207" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_bus"></i> <span class="i-name">whcom_icon_bus</span><span class="i-code">0xf207</span></div> <div title="Code: 0xf208" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_ioxhost"></i> <span class="i-name">whcom_icon_ioxhost</span><span class="i-code">0xf208</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf209" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_angellist"></i> <span class="i-name">whcom_icon_angellist</span><span class="i-code">0xf209</span></div> <div title="Code: 0xf20a" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_cc"></i> <span class="i-name">whcom_icon_cc</span><span class="i-code">0xf20a</span></div> <div title="Code: 0xf20b" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_shekel"></i> <span class="i-name">whcom_icon_shekel</span><span class="i-code">0xf20b</span></div> <div title="Code: 0xf20c" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_meanpath"></i> <span class="i-name">whcom_icon_meanpath</span><span class="i-code">0xf20c</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf20d" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_buysellads"></i> <span class="i-name">whcom_icon_buysellads</span><span class="i-code">0xf20d</span></div> <div title="Code: 0xf20e" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_connectdevelop"></i> <span class="i-name">whcom_icon_connectdevelop</span><span class="i-code">0xf20e</span></div> <div title="Code: 0xf210" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_dashcube"></i> <span class="i-name">whcom_icon_dashcube</span><span class="i-code">0xf210</span></div> <div title="Code: 0xf211" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_forumbee"></i> <span class="i-name">whcom_icon_forumbee</span><span class="i-code">0xf211</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf212" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_leanpub"></i> <span class="i-name">whcom_icon_leanpub</span><span class="i-code">0xf212</span></div> <div title="Code: 0xf213" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_sellsy"></i> <span class="i-name">whcom_icon_sellsy</span><span class="i-code">0xf213</span></div> <div title="Code: 0xf214" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_shirtsinbulk"></i> <span class="i-name">whcom_icon_shirtsinbulk</span><span class="i-code">0xf214</span></div> <div title="Code: 0xf215" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_simplybuilt"></i> <span class="i-name">whcom_icon_simplybuilt</span><span class="i-code">0xf215</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf216" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_skyatlas"></i> <span class="i-name">whcom_icon_skyatlas</span><span class="i-code">0xf216</span></div> <div title="Code: 0xf217" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_cart-plus"></i> <span class="i-name">whcom_icon_cart-plus</span><span class="i-code">0xf217</span></div> <div title="Code: 0xf218" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_cart-arrow-down"></i> <span class="i-name">whcom_icon_cart-arrow-down</span><span class="i-code">0xf218</span></div> <div title="Code: 0xf219" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_diamond-1"></i> <span class="i-name">whcom_icon_diamond-1</span><span class="i-code">0xf219</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf21a" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_ship"></i> <span class="i-name">whcom_icon_ship</span><span class="i-code">0xf21a</span></div> <div title="Code: 0xf21b" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_user-secret"></i> <span class="i-name">whcom_icon_user-secret</span><span class="i-code">0xf21b</span></div> <div title="Code: 0xf21c" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_motorcycle"></i> <span class="i-name">whcom_icon_motorcycle</span><span class="i-code">0xf21c</span></div> <div title="Code: 0xf21d" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_street-view"></i> <span class="i-name">whcom_icon_street-view</span><span class="i-code">0xf21d</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf21e" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_heartbeat"></i> <span class="i-name">whcom_icon_heartbeat</span><span class="i-code">0xf21e</span></div> <div title="Code: 0xf221" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_venus"></i> <span class="i-name">whcom_icon_venus</span><span class="i-code">0xf221</span></div> <div title="Code: 0xf222" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_mars"></i> <span class="i-name">whcom_icon_mars</span><span class="i-code">0xf222</span></div> <div title="Code: 0xf223" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_mercury"></i> <span class="i-name">whcom_icon_mercury</span><span class="i-code">0xf223</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf224" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_transgender"></i> <span class="i-name">whcom_icon_transgender</span><span class="i-code">0xf224</span></div> <div title="Code: 0xf225" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_transgender-alt"></i> <span class="i-name">whcom_icon_transgender-alt</span><span class="i-code">0xf225</span></div> <div title="Code: 0xf226" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_venus-double"></i> <span class="i-name">whcom_icon_venus-double</span><span class="i-code">0xf226</span></div> <div title="Code: 0xf227" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_mars-double"></i> <span class="i-name">whcom_icon_mars-double</span><span class="i-code">0xf227</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf228" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_venus-mars"></i> <span class="i-name">whcom_icon_venus-mars</span><span class="i-code">0xf228</span></div> <div title="Code: 0xf229" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_mars-stroke"></i> <span class="i-name">whcom_icon_mars-stroke</span><span class="i-code">0xf229</span></div> <div title="Code: 0xf22a" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_mars-stroke-v"></i> <span class="i-name">whcom_icon_mars-stroke-v</span><span class="i-code">0xf22a</span></div> <div title="Code: 0xf22b" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_mars-stroke-h"></i> <span class="i-name">whcom_icon_mars-stroke-h</span><span class="i-code">0xf22b</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf22c" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_neuter"></i> <span class="i-name">whcom_icon_neuter</span><span class="i-code">0xf22c</span></div> <div title="Code: 0xf22d" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_genderless"></i> <span class="i-name">whcom_icon_genderless</span><span class="i-code">0xf22d</span></div> <div title="Code: 0xf230" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_facebook-official"></i> <span class="i-name">whcom_icon_facebook-official</span><span class="i-code">0xf230</span></div> <div title="Code: 0xf231" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_pinterest"></i> <span class="i-name">whcom_icon_pinterest</span><span class="i-code">0xf231</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf232" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_whatsapp"></i> <span class="i-name">whcom_icon_whatsapp</span><span class="i-code">0xf232</span></div> <div title="Code: 0xf233" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_server"></i> <span class="i-name">whcom_icon_server</span><span class="i-code">0xf233</span></div> <div title="Code: 0xf234" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_user-plus"></i> <span class="i-name">whcom_icon_user-plus</span><span class="i-code">0xf234</span></div> <div title="Code: 0xf235" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_user-times"></i> <span class="i-name">whcom_icon_user-times</span><span class="i-code">0xf235</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf236" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_bed"></i> <span class="i-name">whcom_icon_bed</span><span class="i-code">0xf236</span></div> <div title="Code: 0xf237" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_viacoin"></i> <span class="i-name">whcom_icon_viacoin</span><span class="i-code">0xf237</span></div> <div title="Code: 0xf238" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_train"></i> <span class="i-name">whcom_icon_train</span><span class="i-code">0xf238</span></div> <div title="Code: 0xf239" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_subway"></i> <span class="i-name">whcom_icon_subway</span><span class="i-code">0xf239</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf23a" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_medium"></i> <span class="i-name">whcom_icon_medium</span><span class="i-code">0xf23a</span></div> <div title="Code: 0xf23b" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_y-combinator"></i> <span class="i-name">whcom_icon_y-combinator</span><span class="i-code">0xf23b</span></div> <div title="Code: 0xf23c" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_optin-monster"></i> <span class="i-name">whcom_icon_optin-monster</span><span class="i-code">0xf23c</span></div> <div title="Code: 0xf23d" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_opencart"></i> <span class="i-name">whcom_icon_opencart</span><span class="i-code">0xf23d</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf23e" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_expeditedssl"></i> <span class="i-name">whcom_icon_expeditedssl</span><span class="i-code">0xf23e</span></div> <div title="Code: 0xf240" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_battery-4"></i> <span class="i-name">whcom_icon_battery-4</span><span class="i-code">0xf240</span></div> <div title="Code: 0xf241" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_battery-3"></i> <span class="i-name">whcom_icon_battery-3</span><span class="i-code">0xf241</span></div> <div title="Code: 0xf242" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_battery-2"></i> <span class="i-name">whcom_icon_battery-2</span><span class="i-code">0xf242</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf243" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_battery-1"></i> <span class="i-name">whcom_icon_battery-1</span><span class="i-code">0xf243</span></div> <div title="Code: 0xf244" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_battery-0"></i> <span class="i-name">whcom_icon_battery-0</span><span class="i-code">0xf244</span></div> <div title="Code: 0xf245" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_mouse-pointer"></i> <span class="i-name">whcom_icon_mouse-pointer</span><span class="i-code">0xf245</span></div> <div title="Code: 0xf246" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_i-cursor"></i> <span class="i-name">whcom_icon_i-cursor</span><span class="i-code">0xf246</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf247" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_object-group"></i> <span class="i-name">whcom_icon_object-group</span><span class="i-code">0xf247</span></div> <div title="Code: 0xf248" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_object-ungroup"></i> <span class="i-name">whcom_icon_object-ungroup</span><span class="i-code">0xf248</span></div> <div title="Code: 0xf249" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_sticky-note"></i> <span class="i-name">whcom_icon_sticky-note</span><span class="i-code">0xf249</span></div> <div title="Code: 0xf24a" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_sticky-note-o"></i> <span class="i-name">whcom_icon_sticky-note-o</span><span class="i-code">0xf24a</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf24b" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_cc-jcb"></i> <span class="i-name">whcom_icon_cc-jcb</span><span class="i-code">0xf24b</span></div> <div title="Code: 0xf24c" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_cc-diners-club"></i> <span class="i-name">whcom_icon_cc-diners-club</span><span class="i-code">0xf24c</span></div> <div title="Code: 0xf24d" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_clone"></i> <span class="i-name">whcom_icon_clone</span><span class="i-code">0xf24d</span></div> <div title="Code: 0xf24e" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_balance-scale"></i> <span class="i-name">whcom_icon_balance-scale</span><span class="i-code">0xf24e</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf250" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_hourglass-o"></i> <span class="i-name">whcom_icon_hourglass-o</span><span class="i-code">0xf250</span></div> <div title="Code: 0xf251" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_hourglass-1"></i> <span class="i-name">whcom_icon_hourglass-1</span><span class="i-code">0xf251</span></div> <div title="Code: 0xf252" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_hourglass-2"></i> <span class="i-name">whcom_icon_hourglass-2</span><span class="i-code">0xf252</span></div> <div title="Code: 0xf253" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_hourglass-3"></i> <span class="i-name">whcom_icon_hourglass-3</span><span class="i-code">0xf253</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf254" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_hourglass"></i> <span class="i-name">whcom_icon_hourglass</span><span class="i-code">0xf254</span></div> <div title="Code: 0xf255" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_hand-grab-o"></i> <span class="i-name">whcom_icon_hand-grab-o</span><span class="i-code">0xf255</span></div> <div title="Code: 0xf256" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_hand-paper-o"></i> <span class="i-name">whcom_icon_hand-paper-o</span><span class="i-code">0xf256</span></div> <div title="Code: 0xf257" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_hand-scissors-o"></i> <span class="i-name">whcom_icon_hand-scissors-o</span><span class="i-code">0xf257</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf258" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_hand-lizard-o"></i> <span class="i-name">whcom_icon_hand-lizard-o</span><span class="i-code">0xf258</span></div> <div title="Code: 0xf259" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_hand-spock-o"></i> <span class="i-name">whcom_icon_hand-spock-o</span><span class="i-code">0xf259</span></div> <div title="Code: 0xf25a" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_hand-pointer-o"></i> <span class="i-name">whcom_icon_hand-pointer-o</span><span class="i-code">0xf25a</span></div> <div title="Code: 0xf25b" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_hand-peace-o"></i> <span class="i-name">whcom_icon_hand-peace-o</span><span class="i-code">0xf25b</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf25c" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_trademark"></i> <span class="i-name">whcom_icon_trademark</span><span class="i-code">0xf25c</span></div> <div title="Code: 0xf25d" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_registered"></i> <span class="i-name">whcom_icon_registered</span><span class="i-code">0xf25d</span></div> <div title="Code: 0xf25e" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_creative-commons"></i> <span class="i-name">whcom_icon_creative-commons</span><span class="i-code">0xf25e</span></div> <div title="Code: 0xf260" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_gg"></i> <span class="i-name">whcom_icon_gg</span><span class="i-code">0xf260</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf261" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_gg-circle"></i> <span class="i-name">whcom_icon_gg-circle</span><span class="i-code">0xf261</span></div> <div title="Code: 0xf262" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_tripadvisor"></i> <span class="i-name">whcom_icon_tripadvisor</span><span class="i-code">0xf262</span></div> <div title="Code: 0xf263" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_odnoklassniki"></i> <span class="i-name">whcom_icon_odnoklassniki</span><span class="i-code">0xf263</span></div> <div title="Code: 0xf264" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_odnoklassniki-square"></i> <span class="i-name">whcom_icon_odnoklassniki-square</span><span class="i-code">0xf264</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf265" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_get-pocket"></i> <span class="i-name">whcom_icon_get-pocket</span><span class="i-code">0xf265</span></div> <div title="Code: 0xf266" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_wikipedia-w"></i> <span class="i-name">whcom_icon_wikipedia-w</span><span class="i-code">0xf266</span></div> <div title="Code: 0xf267" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_safari"></i> <span class="i-name">whcom_icon_safari</span><span class="i-code">0xf267</span></div> <div title="Code: 0xf268" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_chrome"></i> <span class="i-name">whcom_icon_chrome</span><span class="i-code">0xf268</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf269" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_firefox"></i> <span class="i-name">whcom_icon_firefox</span><span class="i-code">0xf269</span></div> <div title="Code: 0xf26a" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_opera"></i> <span class="i-name">whcom_icon_opera</span><span class="i-code">0xf26a</span></div> <div title="Code: 0xf26b" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_internet-explorer"></i> <span class="i-name">whcom_icon_internet-explorer</span><span class="i-code">0xf26b</span></div> <div title="Code: 0xf26c" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_television"></i> <span class="i-name">whcom_icon_television</span><span class="i-code">0xf26c</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf26d" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_contao"></i> <span class="i-name">whcom_icon_contao</span><span class="i-code">0xf26d</span></div> <div title="Code: 0xf26e" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_500px"></i> <span class="i-name">whcom_icon_500px</span><span class="i-code">0xf26e</span></div> <div title="Code: 0xf270" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_amazon"></i> <span class="i-name">whcom_icon_amazon</span><span class="i-code">0xf270</span></div> <div title="Code: 0xf271" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_calendar-plus-o"></i> <span class="i-name">whcom_icon_calendar-plus-o</span><span class="i-code">0xf271</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf272" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_calendar-minus-o"></i> <span class="i-name">whcom_icon_calendar-minus-o</span><span class="i-code">0xf272</span></div> <div title="Code: 0xf273" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_calendar-times-o"></i> <span class="i-name">whcom_icon_calendar-times-o</span><span class="i-code">0xf273</span></div> <div title="Code: 0xf274" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_calendar-check-o"></i> <span class="i-name">whcom_icon_calendar-check-o</span><span class="i-code">0xf274</span></div> <div title="Code: 0xf275" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_industry"></i> <span class="i-name">whcom_icon_industry</span><span class="i-code">0xf275</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf276" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_map-pin"></i> <span class="i-name">whcom_icon_map-pin</span><span class="i-code">0xf276</span></div> <div title="Code: 0xf277" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_map-signs"></i> <span class="i-name">whcom_icon_map-signs</span><span class="i-code">0xf277</span></div> <div title="Code: 0xf278" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_map-o"></i> <span class="i-name">whcom_icon_map-o</span><span class="i-code">0xf278</span></div> <div title="Code: 0xf279" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_map"></i> <span class="i-name">whcom_icon_map</span><span class="i-code">0xf279</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf27a" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_commenting"></i> <span class="i-name">whcom_icon_commenting</span><span class="i-code">0xf27a</span></div> <div title="Code: 0xf27b" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_commenting-o"></i> <span class="i-name">whcom_icon_commenting-o</span><span class="i-code">0xf27b</span></div> <div title="Code: 0xf27c" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_houzz"></i> <span class="i-name">whcom_icon_houzz</span><span class="i-code">0xf27c</span></div> <div title="Code: 0xf27d" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_vimeo"></i> <span class="i-name">whcom_icon_vimeo</span><span class="i-code">0xf27d</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf27e" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_black-tie"></i> <span class="i-name">whcom_icon_black-tie</span><span class="i-code">0xf27e</span></div> <div title="Code: 0xf280" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_fonticons"></i> <span class="i-name">whcom_icon_fonticons</span><span class="i-code">0xf280</span></div> <div title="Code: 0xf281" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_reddit-alien"></i> <span class="i-name">whcom_icon_reddit-alien</span><span class="i-code">0xf281</span></div> <div title="Code: 0xf282" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_edge"></i> <span class="i-name">whcom_icon_edge</span><span class="i-code">0xf282</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf283" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_credit-card-alt"></i> <span class="i-name">whcom_icon_credit-card-alt</span><span class="i-code">0xf283</span></div> <div title="Code: 0xf284" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_codiepie"></i> <span class="i-name">whcom_icon_codiepie</span><span class="i-code">0xf284</span></div> <div title="Code: 0xf285" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_modx"></i> <span class="i-name">whcom_icon_modx</span><span class="i-code">0xf285</span></div> <div title="Code: 0xf286" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_fort-awesome"></i> <span class="i-name">whcom_icon_fort-awesome</span><span class="i-code">0xf286</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf287" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_usb"></i> <span class="i-name">whcom_icon_usb</span><span class="i-code">0xf287</span></div> <div title="Code: 0xf288" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_product-hunt"></i> <span class="i-name">whcom_icon_product-hunt</span><span class="i-code">0xf288</span></div> <div title="Code: 0xf289" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_mixcloud"></i> <span class="i-name">whcom_icon_mixcloud</span><span class="i-code">0xf289</span></div> <div title="Code: 0xf28a" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_scribd"></i> <span class="i-name">whcom_icon_scribd</span><span class="i-code">0xf28a</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf28b" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_pause-circle"></i> <span class="i-name">whcom_icon_pause-circle</span><span class="i-code">0xf28b</span></div> <div title="Code: 0xf28c" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_pause-circle-o"></i> <span class="i-name">whcom_icon_pause-circle-o</span><span class="i-code">0xf28c</span></div> <div title="Code: 0xf28d" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_stop-circle"></i> <span class="i-name">whcom_icon_stop-circle</span><span class="i-code">0xf28d</span></div> <div title="Code: 0xf28e" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_stop-circle-o"></i> <span class="i-name">whcom_icon_stop-circle-o</span><span class="i-code">0xf28e</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf290" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_shopping-bag"></i> <span class="i-name">whcom_icon_shopping-bag</span><span class="i-code">0xf290</span></div> <div title="Code: 0xf291" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_shopping-basket"></i> <span class="i-name">whcom_icon_shopping-basket</span><span class="i-code">0xf291</span></div> <div title="Code: 0xf292" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_hashtag"></i> <span class="i-name">whcom_icon_hashtag</span><span class="i-code">0xf292</span></div> <div title="Code: 0xf293" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_bluetooth"></i> <span class="i-name">whcom_icon_bluetooth</span><span class="i-code">0xf293</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf294" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_bluetooth-b"></i> <span class="i-name">whcom_icon_bluetooth-b</span><span class="i-code">0xf294</span></div> <div title="Code: 0xf295" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_percent"></i> <span class="i-name">whcom_icon_percent</span><span class="i-code">0xf295</span></div> <div title="Code: 0xf296" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_gitlab"></i> <span class="i-name">whcom_icon_gitlab</span><span class="i-code">0xf296</span></div> <div title="Code: 0xf297" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_wpbeginner"></i> <span class="i-name">whcom_icon_wpbeginner</span><span class="i-code">0xf297</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf298" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_wpforms"></i> <span class="i-name">whcom_icon_wpforms</span><span class="i-code">0xf298</span></div> <div title="Code: 0xf299" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_envira"></i> <span class="i-name">whcom_icon_envira</span><span class="i-code">0xf299</span></div> <div title="Code: 0xf29a" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_universal-access"></i> <span class="i-name">whcom_icon_universal-access</span><span class="i-code">0xf29a</span></div> <div title="Code: 0xf29b" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_wheelchair-alt"></i> <span class="i-name">whcom_icon_wheelchair-alt</span><span class="i-code">0xf29b</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf29c" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_question-circle-o"></i> <span class="i-name">whcom_icon_question-circle-o</span><span class="i-code">0xf29c</span></div> <div title="Code: 0xf29d" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_blind"></i> <span class="i-name">whcom_icon_blind</span><span class="i-code">0xf29d</span></div> <div title="Code: 0xf29e" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_audio-description"></i> <span class="i-name">whcom_icon_audio-description</span><span class="i-code">0xf29e</span></div> <div title="Code: 0xf2a0" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_volume-control-phone"></i> <span class="i-name">whcom_icon_volume-control-phone</span><span class="i-code">0xf2a0</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf2a1" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_braille"></i> <span class="i-name">whcom_icon_braille</span><span class="i-code">0xf2a1</span></div> <div title="Code: 0xf2a2" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_assistive-listening-systems"></i> <span class="i-name">whcom_icon_assistive-listening-systems</span><span class="i-code">0xf2a2</span></div> <div title="Code: 0xf2a3" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_american-sign-language-interpreting"></i> <span class="i-name">whcom_icon_american-sign-language-interpreting</span><span class="i-code">0xf2a3</span></div> <div title="Code: 0xf2a4" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_asl-interpreting"></i> <span class="i-name">whcom_icon_asl-interpreting</span><span class="i-code">0xf2a4</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf2a5" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_glide"></i> <span class="i-name">whcom_icon_glide</span><span class="i-code">0xf2a5</span></div> <div title="Code: 0xf2a6" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_glide-g"></i> <span class="i-name">whcom_icon_glide-g</span><span class="i-code">0xf2a6</span></div> <div title="Code: 0xf2a7" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_sign-language"></i> <span class="i-name">whcom_icon_sign-language</span><span class="i-code">0xf2a7</span></div> <div title="Code: 0xf2a8" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_low-vision"></i> <span class="i-name">whcom_icon_low-vision</span><span class="i-code">0xf2a8</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf2a9" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_viadeo"></i> <span class="i-name">whcom_icon_viadeo</span><span class="i-code">0xf2a9</span></div> <div title="Code: 0xf2aa" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_viadeo-square"></i> <span class="i-name">whcom_icon_viadeo-square</span><span class="i-code">0xf2aa</span></div> <div title="Code: 0xf2ab" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_snapchat"></i> <span class="i-name">whcom_icon_snapchat</span><span class="i-code">0xf2ab</span></div> <div title="Code: 0xf2ac" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_snapchat-ghost"></i> <span class="i-name">whcom_icon_snapchat-ghost</span><span class="i-code">0xf2ac</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf2ad" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_snapchat-square"></i> <span class="i-name">whcom_icon_snapchat-square</span><span class="i-code">0xf2ad</span></div> <div title="Code: 0xf2ae" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_pied-piper"></i> <span class="i-name">whcom_icon_pied-piper</span><span class="i-code">0xf2ae</span></div> <div title="Code: 0xf2b0" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_first-order"></i> <span class="i-name">whcom_icon_first-order</span><span class="i-code">0xf2b0</span></div> <div title="Code: 0xf2b1" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_yoast"></i> <span class="i-name">whcom_icon_yoast</span><span class="i-code">0xf2b1</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf2b2" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_themeisle"></i> <span class="i-name">whcom_icon_themeisle</span><span class="i-code">0xf2b2</span></div> <div title="Code: 0xf2b3" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_google-plus-circle"></i> <span class="i-name">whcom_icon_google-plus-circle</span><span class="i-code">0xf2b3</span></div> <div title="Code: 0xf2b4" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_font-awesome"></i> <span class="i-name">whcom_icon_font-awesome</span><span class="i-code">0xf2b4</span></div> <div title="Code: 0xf2b5" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_handshake-o"></i> <span class="i-name">whcom_icon_handshake-o</span><span class="i-code">0xf2b5</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf2b6" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_envelope-open"></i> <span class="i-name">whcom_icon_envelope-open</span><span class="i-code">0xf2b6</span></div> <div title="Code: 0xf2b7" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_envelope-open-o"></i> <span class="i-name">whcom_icon_envelope-open-o</span><span class="i-code">0xf2b7</span></div> <div title="Code: 0xf2b8" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_linode"></i> <span class="i-name">whcom_icon_linode</span><span class="i-code">0xf2b8</span></div> <div title="Code: 0xf2b9" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_address-book"></i> <span class="i-name">whcom_icon_address-book</span><span class="i-code">0xf2b9</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf2ba" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_address-book-o"></i> <span class="i-name">whcom_icon_address-book-o</span><span class="i-code">0xf2ba</span></div> <div title="Code: 0xf2bb" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_address-card"></i> <span class="i-name">whcom_icon_address-card</span><span class="i-code">0xf2bb</span></div> <div title="Code: 0xf2bc" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_address-card-o"></i> <span class="i-name">whcom_icon_address-card-o</span><span class="i-code">0xf2bc</span></div> <div title="Code: 0xf2bd" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_user-circle"></i> <span class="i-name">whcom_icon_user-circle</span><span class="i-code">0xf2bd</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf2be" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_user-circle-o"></i> <span class="i-name">whcom_icon_user-circle-o</span><span class="i-code">0xf2be</span></div> <div title="Code: 0xf2c0" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_user-o"></i> <span class="i-name">whcom_icon_user-o</span><span class="i-code">0xf2c0</span></div> <div title="Code: 0xf2c1" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_id-badge"></i> <span class="i-name">whcom_icon_id-badge</span><span class="i-code">0xf2c1</span></div> <div title="Code: 0xf2c2" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_id-card"></i> <span class="i-name">whcom_icon_id-card</span><span class="i-code">0xf2c2</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf2c3" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_id-card-o"></i> <span class="i-name">whcom_icon_id-card-o</span><span class="i-code">0xf2c3</span></div> <div title="Code: 0xf2c4" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_quora"></i> <span class="i-name">whcom_icon_quora</span><span class="i-code">0xf2c4</span></div> <div title="Code: 0xf2c5" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_free-code-camp"></i> <span class="i-name">whcom_icon_free-code-camp</span><span class="i-code">0xf2c5</span></div> <div title="Code: 0xf2c6" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_telegram"></i> <span class="i-name">whcom_icon_telegram</span><span class="i-code">0xf2c6</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf2c7" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_thermometer"></i> <span class="i-name">whcom_icon_thermometer</span><span class="i-code">0xf2c7</span></div> <div title="Code: 0xf2c8" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_thermometer-3"></i> <span class="i-name">whcom_icon_thermometer-3</span><span class="i-code">0xf2c8</span></div> <div title="Code: 0xf2c9" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_thermometer-2"></i> <span class="i-name">whcom_icon_thermometer-2</span><span class="i-code">0xf2c9</span></div> <div title="Code: 0xf2ca" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_thermometer-quarter"></i> <span class="i-name">whcom_icon_thermometer-quarter</span><span class="i-code">0xf2ca</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf2cb" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_thermometer-0"></i> <span class="i-name">whcom_icon_thermometer-0</span><span class="i-code">0xf2cb</span></div> <div title="Code: 0xf2cc" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_shower"></i> <span class="i-name">whcom_icon_shower</span><span class="i-code">0xf2cc</span></div> <div title="Code: 0xf2cd" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_bath"></i> <span class="i-name">whcom_icon_bath</span><span class="i-code">0xf2cd</span></div> <div title="Code: 0xf2ce" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_podcast"></i> <span class="i-name">whcom_icon_podcast</span><span class="i-code">0xf2ce</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf2d0" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_window-maximize"></i> <span class="i-name">whcom_icon_window-maximize</span><span class="i-code">0xf2d0</span></div> <div title="Code: 0xf2d1" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_window-minimize"></i> <span class="i-name">whcom_icon_window-minimize</span><span class="i-code">0xf2d1</span></div> <div title="Code: 0xf2d2" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_window-restore"></i> <span class="i-name">whcom_icon_window-restore</span><span class="i-code">0xf2d2</span></div> <div title="Code: 0xf2d3" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_window-close"></i> <span class="i-name">whcom_icon_window-close</span><span class="i-code">0xf2d3</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf2d4" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_window-close-o"></i> <span class="i-name">whcom_icon_window-close-o</span><span class="i-code">0xf2d4</span></div> <div title="Code: 0xf2d5" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_bandcamp"></i> <span class="i-name">whcom_icon_bandcamp</span><span class="i-code">0xf2d5</span></div> <div title="Code: 0xf2d6" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_grav"></i> <span class="i-name">whcom_icon_grav</span><span class="i-code">0xf2d6</span></div> <div title="Code: 0xf2d7" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_etsy"></i> <span class="i-name">whcom_icon_etsy</span><span class="i-code">0xf2d7</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf2d8" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_imdb"></i> <span class="i-name">whcom_icon_imdb</span><span class="i-code">0xf2d8</span></div> <div title="Code: 0xf2d9" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_ravelry"></i> <span class="i-name">whcom_icon_ravelry</span><span class="i-code">0xf2d9</span></div> <div title="Code: 0xf2da" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_eercast"></i> <span class="i-name">whcom_icon_eercast</span><span class="i-code">0xf2da</span></div> <div title="Code: 0xf2db" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_microchip"></i> <span class="i-name">whcom_icon_microchip</span><span class="i-code">0xf2db</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf2dc" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_snowflake-o"></i> <span class="i-name">whcom_icon_snowflake-o</span><span class="i-code">0xf2dc</span></div> <div title="Code: 0xf2dd" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_superpowers"></i> <span class="i-name">whcom_icon_superpowers</span><span class="i-code">0xf2dd</span></div> <div title="Code: 0xf2de" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_wpexplorer"></i> <span class="i-name">whcom_icon_wpexplorer</span><span class="i-code">0xf2de</span></div> <div title="Code: 0xf2e0" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_meetup"></i> <span class="i-name">whcom_icon_meetup</span><span class="i-code">0xf2e0</span></div> </div> <div class="whcom_row"> <div title="Code: 0xf300" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_github-squared"></i> <span class="i-name">whcom_icon_github-squared</span><span class="i-code">0xf300</span></div> <div title="Code: 0xf304" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_twitter-squared"></i> <span class="i-name">whcom_icon_twitter-squared</span><span class="i-code">0xf304</span></div> <div title="Code: 0xf308" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_facebook-squared"></i> <span class="i-name">whcom_icon_facebook-squared</span><span class="i-code">0xf308</span></div> <div title="Code: 0xf30c" class="the-icons whcom_col_sm_3"><i class="demo-icon whcom_icon_linkedin-squared"></i> <span class="i-name">whcom_icon_linkedin-squared</span><span class="i-code">0xf30c</span></div> </div> </div> </div>