├── src ├── files │ ├── test_data.json │ ├── dummy.pdf │ └── dummy.zip ├── js │ ├── footer.js │ ├── loans.js │ ├── savings.js │ ├── index.js │ └── jquery-3.6.0.min.js ├── css │ └── custom.css ├── index.html ├── savings.html └── loans.html ├── .gitignore ├── playwright.config.ts ├── server.js ├── tests └── m2-setup │ └── _0setup_verification.js ├── package.json └── README.md /src/files/test_data.json: -------------------------------------------------------------------------------- 1 | { 2 | "Key 1": "Route FulFill Demo", 3 | "Key 2": "Key 2" 4 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Node.js 2 | node_modules/ 3 | test-results/ 4 | 5 | .idea/ 6 | playwright-report/ -------------------------------------------------------------------------------- /src/files/dummy.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrejs-ps/playwright-nodejs-starter/HEAD/src/files/dummy.pdf -------------------------------------------------------------------------------- /src/files/dummy.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrejs-ps/playwright-nodejs-starter/HEAD/src/files/dummy.zip -------------------------------------------------------------------------------- /playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from '@playwright/test'; 2 | 3 | export default defineConfig({ 4 | testDir: './tests', 5 | reporter: 'html' 6 | }); 7 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var app = express(); 3 | 4 | app.use(express.static(__dirname + '/src')); 5 | 6 | app.listen('3000'); 7 | console.log('working on 3000'); -------------------------------------------------------------------------------- /src/js/footer.js: -------------------------------------------------------------------------------- 1 | (() => { 2 | 'use strict' 3 | 4 | setTimeout(() => { 5 | document.getElementById('location').innerText = `You are visiting us from New York`; 6 | }, "2000"); 7 | 8 | })() 9 | -------------------------------------------------------------------------------- /tests/m2-setup/_0setup_verification.js: -------------------------------------------------------------------------------- 1 | const { chromium } = require('@playwright/test'); 2 | 3 | (async function firstScript() { 4 | const browser = await chromium.launch(); 5 | await browser.close(); 6 | console.log("we reached this line"); 7 | })(); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "playwright-javascript", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node server.js", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "@playwright/test": "^1.40.0", 15 | "@types/node": "^20.9.0" 16 | }, 17 | "dependencies": { 18 | "express": "^4.18.2" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Demo code for my Pluralsight course: Playwright in Node.js Fundamentals 2 | 3 | Starting a new end-to-end test automation project? My course will teach you how to test web applications using the open-source tool Playwright with Node.js. Try it and never look back! 4 | 5 | > [!IMPORTANT] 6 | > This is a "starter" repo, meaning it contains only the minimal setup to enable learners to code along with the video course. 7 | 8 | You can find all my courses at: https://www.pluralsight.com/authors/andrejs-doronins 9 | -------------------------------------------------------------------------------- /src/js/loans.js: -------------------------------------------------------------------------------- 1 | (() => { 2 | 'use strict' 3 | 4 | const applyBtn = document.getElementById('apply'); 5 | 6 | applyBtn.addEventListener('click', () => { 7 | const msg = document.getElementById('message'); 8 | msg.classList.add("alert"); 9 | msg.innerText = 'Thank you for applying' 10 | 11 | }) 12 | 13 | let input = document.getElementById('borrow'); 14 | let period = document.getElementById('period'); 15 | let result = document.getElementById('result'); 16 | 17 | input.addEventListener('input', event => { 18 | setTimeout(displayResult, 3000); 19 | }); 20 | 21 | period.addEventListener('change', event => { 22 | setTimeout(displayResult, 3000); 23 | }); 24 | 25 | function displayResult() { 26 | let sum = input.value; 27 | if (sum < 1) { return; } 28 | result.removeAttribute("style"); 29 | result.innerText = `You will pays us back ${sum * 2}`; 30 | } 31 | 32 | })() 33 | -------------------------------------------------------------------------------- /src/css/custom.css: -------------------------------------------------------------------------------- 1 | main > .container { 2 | padding: 60px 15px 0; 3 | } 4 | .bd-placeholder-img { 5 | font-size: 1.125rem; 6 | text-anchor: middle; 7 | -webkit-user-select: none; 8 | -moz-user-select: none; 9 | user-select: none; 10 | } 11 | 12 | @media (min-width: 768px) { 13 | .bd-placeholder-img-lg { 14 | font-size: 3.5rem; 15 | } 16 | } 17 | 18 | .b-example-divider { 19 | height: 3rem; 20 | background-color: rgba(0, 0, 0, .1); 21 | border: solid rgba(0, 0, 0, .15); 22 | border-width: 1px 0; 23 | box-shadow: inset 0 .5em 1.5em rgba(0, 0, 0, .1), inset 0 .125em .5em rgba(0, 0, 0, .15); 24 | } 25 | 26 | .b-example-vr { 27 | flex-shrink: 0; 28 | width: 1.5rem; 29 | height: 100vh; 30 | } 31 | 32 | .bi { 33 | vertical-align: -.125em; 34 | fill: currentColor; 35 | } 36 | 37 | .nav-scroller { 38 | position: relative; 39 | z-index: 2; 40 | height: 2.75rem; 41 | overflow-y: hidden; 42 | } 43 | 44 | .nav-scroller .nav { 45 | display: flex; 46 | flex-wrap: nowrap; 47 | padding-bottom: 1rem; 48 | margin-top: -1px; 49 | overflow-x: auto; 50 | text-align: center; 51 | white-space: nowrap; 52 | -webkit-overflow-scrolling: touch; 53 | } -------------------------------------------------------------------------------- /src/js/savings.js: -------------------------------------------------------------------------------- 1 | 2 | (() => { 3 | 'use strict' 4 | const periodYields = new Map([ 5 | [0.5, 0.04], 6 | [1, 0.05], 7 | [2, 0.06], 8 | ]); 9 | 10 | let input = document.getElementById('deposit'); 11 | let period = document.getElementById('period'); 12 | let result = document.getElementById('result'); 13 | 14 | input.addEventListener('input', event => { 15 | displayReturn(); 16 | }); 17 | 18 | period.addEventListener('change', event => { 19 | displayReturn(); 20 | }); 21 | 22 | function displayReturn() { 23 | let sum = input.value; 24 | if (sum < 1) { return; } 25 | let selected = period.options[period.selectedIndex].value; 26 | let periodInYears = convertPeriod(selected); 27 | let expectedReturn = calculateReturn(sum, periodInYears); 28 | let roundedReturn = parseFloat(expectedReturn).toFixed(2); 29 | result.classList.add("alert"); 30 | result.innerText = `After ${selected} you will earn $${roundedReturn} on your deposit`; 31 | } 32 | 33 | function convertPeriod(option) { 34 | let periodKeys = Array.from(periodYields.keys()); 35 | switch (option) { 36 | case '6 Months': 37 | return periodKeys[0]; 38 | case '1 Year': 39 | return periodKeys[1]; 40 | case '2 Years': 41 | return periodKeys[2]; 42 | } 43 | } 44 | 45 | function calculateReturn(sum, periodInYears) { 46 | let yearlyYield = periodYields.get(periodInYears); 47 | return sum * periodInYears * yearlyYield; 48 | } 49 | 50 | 51 | })() 52 | -------------------------------------------------------------------------------- /src/js/index.js: -------------------------------------------------------------------------------- 1 | (() => { 2 | 'use strict' 3 | const forms = document.querySelectorAll('.needs-validation'); 4 | 5 | // Prevent submission 6 | Array.from(forms).forEach(form => { 7 | form.addEventListener('submit', event => { 8 | if (!form.checkValidity()) { 9 | event.preventDefault() 10 | event.stopPropagation() 11 | } 12 | }, false); 13 | }) 14 | 15 | const registerBtn = document.getElementById('register'); 16 | registerBtn.addEventListener('click', () => { 17 | Array.from(forms).forEach(form => { 18 | form.classList.add('was-validated') 19 | }) 20 | printToConsole(); 21 | }) 22 | 23 | function printToConsole() { 24 | console.log("Application submitted"); 25 | console.warn("Connection is slow"); 26 | console.error("Something went wrong"); 27 | 28 | throw new Error("Deliberate error"); 29 | } 30 | 31 | const inputs = document.getElementsByClassName('form-control'); 32 | const clearBtn = document.getElementById('clear'); 33 | clearBtn.addEventListener('click', () => { 34 | if (confirm('This will clear all inputs. Continue?')) { 35 | Array.from(inputs).forEach(input => input.value = ''); 36 | } 37 | 38 | }); 39 | 40 | const saveBtn = document.getElementById('save'); 41 | saveBtn.addEventListener('click', () => { 42 | Array.from(inputs).forEach(input => { 43 | localStorage.setItem(input.getAttribute('id'), input.value); 44 | }); 45 | }); 46 | 47 | document.addEventListener("DOMContentLoaded", function () { 48 | 49 | Array.from(inputs).forEach(input => { 50 | let storedValue = localStorage.getItem(input.getAttribute('id')); 51 | if (storedValue) { 52 | input.value = storedValue; 53 | } 54 | }); 55 | 56 | }); 57 | })() 58 | 59 | 60 | 61 | $(document).ready(function () { 62 | 63 | $('#heard-about').click(function () { 64 | if ($("#textarea").attr("disabled")) { 65 | $('#textarea').removeAttr("disabled"); 66 | } else { 67 | $('#textarea').prop('disabled', true); 68 | $('#textarea').val(""); 69 | } 70 | }); 71 | }); -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Credit Association 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 35 |
36 | 37 | 38 |
39 |
40 | 41 |
42 |
43 |

44 |

Register to become a member

45 |
46 |
47 |
48 | 49 | 50 | 51 |
52 | Valid first name is required 53 |
54 |
55 | 56 |
57 | 58 | 59 |
60 | Valid last name is required 61 |
62 |
63 | 64 | 65 |
66 | 67 | 68 |
69 | Please enter a valid email address 70 |
71 |
72 | 73 |
74 | 75 | 76 |
77 | 78 | 79 |
80 | 81 |
82 | 83 |
84 | 85 | 86 | 87 |
88 | 89 |
90 | 91 |
92 | 93 | 94 |
95 | 96 | 97 | 98 | 99 |
100 |
101 |
102 |
103 |
104 | 105 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /src/savings.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Save with us 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 33 |
34 | 35 | 36 |
37 |
38 | 39 | 40 |
41 | 42 |
43 |

44 |

Deposit your money with us to earn a great yield

45 |
46 |
47 |
48 | 49 | 50 |
51 | 52 |
53 | 54 | 59 |
60 | 61 | 64 |
65 |

Current rates (yearly yield)

66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 |
6 months1 year2 years
Us4%5%6%
Competition2%3%4%
90 |
91 | 92 |
93 | 97 |
98 | 99 |
100 | 101 |
102 |
103 |
104 |
105 |
106 | 107 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /src/loans.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Credit Association 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 33 |
34 | 35 | 36 |
37 |
38 |
39 | 40 |
41 |

42 |

Get a loan with us!

43 |
44 |
45 |
46 | 47 | 48 |
49 | 50 |
51 | 52 | 57 |
58 |
59 | 60 |
61 | 64 | 65 |
66 |
67 | 68 | 69 |
70 |
71 | 72 |
73 | 74 |
75 |

Terms and conditions

76 |
77 |
78 |

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque eu vestibulum dui. Proin aliquet tortor non aliquet congue. Nunc in blandit eros, et pretium diam. Nulla facilisi. Nullam ligula nulla, laoreet in suscipit sit amet, finibus eget odio. Vivamus varius malesuada magna in interdum. Fusce lacinia condimentum sem, ac feugiat velit. Morbi vulputate eros sit amet fringilla rhoncus. Cras sed pulvinar dolor. 79 | 80 | Sed placerat lectus et mollis fermentum. Aliquam suscipit rutrum neque, a tristique erat fermentum nec. Cras a aliquet nisl. Cras tincidunt laoreet massa, eget dictum risus pellentesque eu. Cras eget laoreet risus. Aenean sed est maximus, vestibulum mauris suscipit, hendrerit quam. In hendrerit neque eu enim tristique, vel faucibus dolor dapibus. 81 | 82 | Nunc eu leo aliquam, feugiat diam vitae, auctor enim. Donec interdum odio vitae leo bibendum, id scelerisque mi dignissim. Integer condimentum dictum risus luctus posuere. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Suspendisse vel vulputate risus. Pellentesque hendrerit non lacus vitae semper. Nulla semper molestie magna, non tempus turpis luctus non. Sed odio dolor, efficitur at efficitur ut, finibus at libero. Pellentesque vitae risus a risus vestibulum mollis. Nam ullamcorper turpis id risus tincidunt malesuada. Cras aliquet leo feugiat justo varius, efficitur facilisis velit viverra. Cras augue est, suscipit id nisl sed, faucibus aliquam elit. Aliquam nec aliquet neque. Maecenas euismod mi fermentum vehicula suscipit. 83 | 84 | Aenean blandit, sapien in aliquam ornare, eros nisi pellentesque metus, et laoreet mi dui vel ex. Donec ullamcorper leo mi, eget ultricies nisl ultrices sed. Morbi auctor sapien a elit vestibulum, sit amet ullamcorper justo viverra. Morbi faucibus mattis malesuada. Praesent bibendum malesuada turpis, a maximus risus egestas ac. Vestibulum laoreet nisl orci, ac rutrum sapien sollicitudin a. Integer commodo fringilla rhoncus. Aenean arcu sem, luctus ut porttitor in, bibendum sit amet magna. Aenean et ultrices sapien. Praesent sit amet pellentesque ligula. 85 | 86 | Morbi gravida, tortor sit amet suscipit vestibulum, nulla lectus sagittis orci, et dignissim ipsum urna et augue. Vivamus et quam non magna fringilla aliquam id eu lorem. Aliquam aliquam blandit dui, ac porta ante rhoncus et. In at vulputate ligula, in blandit augue. Pellentesque faucibus elit vitae odio lobortis, ac bibendum nibh placerat. Phasellus cursus nisl porta nisi tempor aliquet. Etiam accumsan ut mi malesuada vestibulum. Maecenas dapibus mauris nec nunc laoreet iaculis. Nunc ut neque feugiat, ultricies purus id, placerat purus. Nullam sollicitudin ante et consequat lobortis. Praesent lacinia, velit nec malesuada varius, nibh turpis porta velit, ut imperdiet nibh tellus egestas mauris. Nulla efficitur imperdiet fringilla. 87 |

88 |

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque eu vestibulum dui. Proin aliquet tortor non aliquet congue. Nunc in blandit eros, et pretium diam. Nulla facilisi. Nullam ligula nulla, laoreet in suscipit sit amet, finibus eget odio. Vivamus varius malesuada magna in interdum. Fusce lacinia condimentum sem, ac feugiat velit. Morbi vulputate eros sit amet fringilla rhoncus. Cras sed pulvinar dolor. 89 | 90 | Sed placerat lectus et mollis fermentum. Aliquam suscipit rutrum neque, a tristique erat fermentum nec. Cras a aliquet nisl. Cras tincidunt laoreet massa, eget dictum risus pellentesque eu. Cras eget laoreet risus. Aenean sed est maximus, vestibulum mauris suscipit, hendrerit quam. In hendrerit neque eu enim tristique, vel faucibus dolor dapibus. 91 | 92 | Nunc eu leo aliquam, feugiat diam vitae, auctor enim. Donec interdum odio vitae leo bibendum, id scelerisque mi dignissim. Integer condimentum dictum risus luctus posuere. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Suspendisse vel vulputate risus. Pellentesque hendrerit non lacus vitae semper. Nulla semper molestie magna, non tempus turpis luctus non. Sed odio dolor, efficitur at efficitur ut, finibus at libero. Pellentesque vitae risus a risus vestibulum mollis. Nam ullamcorper turpis id risus tincidunt malesuada. Cras aliquet leo feugiat justo varius, efficitur facilisis velit viverra. Cras augue est, suscipit id nisl sed, faucibus aliquam elit. Aliquam nec aliquet neque. Maecenas euismod mi fermentum vehicula suscipit. 93 | 94 | Aenean blandit, sapien in aliquam ornare, eros nisi pellentesque metus, et laoreet mi dui vel ex. Donec ullamcorper leo mi, eget ultricies nisl ultrices sed. Morbi auctor sapien a elit vestibulum, sit amet ullamcorper justo viverra. Morbi faucibus mattis malesuada. Praesent bibendum malesuada turpis, a maximus risus egestas ac. Vestibulum laoreet nisl orci, ac rutrum sapien sollicitudin a. Integer commodo fringilla rhoncus. Aenean arcu sem, luctus ut porttitor in, bibendum sit amet magna. Aenean et ultrices sapien. Praesent sit amet pellentesque ligula. 95 | 96 | Morbi gravida, tortor sit amet suscipit vestibulum, nulla lectus sagittis orci, et dignissim ipsum urna et augue. Vivamus et quam non magna fringilla aliquam id eu lorem. Aliquam aliquam blandit dui, ac porta ante rhoncus et. In at vulputate ligula, in blandit augue. Pellentesque faucibus elit vitae odio lobortis, ac bibendum nibh placerat. Phasellus cursus nisl porta nisi tempor aliquet. Etiam accumsan ut mi malesuada vestibulum. Maecenas dapibus mauris nec nunc laoreet iaculis. Nunc ut neque feugiat, ultricies purus id, placerat purus. Nullam sollicitudin ante et consequat lobortis. Praesent lacinia, velit nec malesuada varius, nibh turpis porta velit, ut imperdiet nibh tellus egestas mauris. Nulla efficitur imperdiet fringilla. 97 |

98 | 99 |

100 | 103 | 104 |
105 |
106 |
107 | 108 | 109 |
110 |
111 | 112 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /src/js/jquery-3.6.0.min.js: -------------------------------------------------------------------------------- 1 | /*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */ 2 | !function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="
",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.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(e,n){S.fn[n]=function(e,t){return 0