├── .DS_Store ├── .php-cs-fixer.dist.php ├── .phpcs ├── .phpunit.cache └── test-results ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── composer.json ├── dist ├── magic_test.js └── magic_test.js.LICENSE.txt ├── js ├── AttributeParser.js ├── Context.js ├── Events │ ├── Click.js │ ├── Key.js │ └── Keypress.js ├── Finders.js ├── Helpers.js ├── Mutation.js └── magic_test.js ├── mix-manifest.json ├── package-lock.json ├── package.json ├── src ├── .DS_Store ├── Commands │ └── MagicTestCommand.php ├── Controllers │ └── MagicTestController.php ├── Element │ └── Attribute.php ├── Exceptions │ └── InvalidFileException.php ├── FileEditor.php ├── Grammar │ ├── Click.php │ ├── Fill.php │ ├── Grammar.php │ ├── Pause.php │ └── See.php ├── MagicTest.php ├── MagicTestManager.php ├── MagicTestServiceProvider.php ├── Middleware │ ├── MagicTestMiddleware.php │ └── NullMagicTestMiddleware.php ├── Parser │ ├── .DS_Store │ ├── File.php │ ├── Printer │ │ └── PrettyPrinter.php │ └── Visitors │ │ ├── GrammarBuilderVisitor.php │ │ └── MagicRemoverVisitor.php ├── PendingOk.php ├── ShellCommands │ ├── Finish.php │ └── Ok.php ├── Support │ └── AttributeCollection.php ├── Traits │ └── UsesMagicTest.php └── tests │ ├── .DS_Store │ └── Grammar │ └── .DS_Store └── webpack.mix.js /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magic-test/magic-test-laravel/a2c2452c413ca17db276f51f86d2b9f42fca805f/.DS_Store -------------------------------------------------------------------------------- /.php-cs-fixer.dist.php: -------------------------------------------------------------------------------- 1 | notPath('bootstrap/*') 5 | ->notPath('storage/*') 6 | ->notPath('resources/view/mail/*') 7 | ->in([ 8 | __DIR__ . '/src', 9 | __DIR__ . '/tests', 10 | ]) 11 | ->name('*.php') 12 | ->notName('*.blade.php') 13 | ->ignoreDotFiles(true) 14 | ->ignoreVCS(true); 15 | 16 | return (new PhpCsFixer\Config()) 17 | ->setRules([ 18 | '@PSR2' => true, 19 | 'array_syntax' => ['syntax' => 'short'], 20 | 'ordered_imports' => ['sort_algorithm' => 'alpha'], 21 | 'no_unused_imports' => true, 22 | 'not_operator_with_successor_space' => true, 23 | 'trailing_comma_in_multiline' => [ 24 | 'elements' => ['arrays'] 25 | ], 26 | 'phpdoc_scalar' => true, 27 | 'unary_operator_spaces' => true, 28 | 'binary_operator_spaces' => true, 29 | 'blank_line_before_statement' => [ 30 | 'statements' => ['break', 'continue', 'declare', 'return', 'throw', 'try'], 31 | ], 32 | 'phpdoc_single_line_var_spacing' => true, 33 | 'phpdoc_var_without_name' => true, 34 | 'class_attributes_separation' => [ 35 | 'elements' => [ 36 | 'method' => 'one', 37 | ], 38 | ], 39 | 'method_argument_space' => [ 40 | 'on_multiline' => 'ensure_fully_multiline', 41 | 'keep_multiple_spaces_after_comma' => true, 42 | ], 43 | 'single_trait_insert_per_statement' => true, 44 | ]) 45 | ->setFinder($finder); 46 | -------------------------------------------------------------------------------- /.phpcs: -------------------------------------------------------------------------------- 1 | notPath('bootstrap/*') 5 | ->notPath('storage/*') 6 | ->notPath('resources/view/mail/*') 7 | ->in([ 8 | __DIR__ . '/src', 9 | __DIR__ . '/tests', 10 | ]) 11 | ->name('*.php') 12 | ->notName('*.blade.php') 13 | ->ignoreDotFiles(true) 14 | ->ignoreVCS(true); 15 | 16 | return PhpCsFixer\Config::create() 17 | ->setRules([ 18 | '@PSR2' => true, 19 | 'array_syntax' => ['syntax' => 'short'], 20 | 'ordered_imports' => ['sortAlgorithm' => 'alpha'], 21 | 'no_unused_imports' => true, 22 | 'not_operator_with_successor_space' => true, 23 | 'trailing_comma_in_multiline_array' => true, 24 | 'phpdoc_scalar' => true, 25 | 'unary_operator_spaces' => true, 26 | 'binary_operator_spaces' => true, 27 | 'blank_line_before_statement' => [ 28 | 'statements' => ['break', 'continue', 'declare', 'return', 'throw', 'try'], 29 | ], 30 | 'phpdoc_single_line_var_spacing' => true, 31 | 'phpdoc_var_without_name' => true, 32 | 'class_attributes_separation' => [ 33 | 'elements' => [ 34 | 'method', 35 | ], 36 | ], 37 | 'method_argument_space' => [ 38 | 'on_multiline' => 'ensure_fully_multiline', 39 | 'keep_multiple_spaces_after_comma' => true, 40 | ], 41 | 'single_trait_insert_per_statement' => true, 42 | ]) 43 | ->setFinder($finder); 44 | -------------------------------------------------------------------------------- /.phpunit.cache/test-results: -------------------------------------------------------------------------------- 1 | {"version":1,"defects":[],"times":{"MagicTest\\MagicTest\\Tests\\Element\\AttributeTest::it_parses_a_livewire_name_field":0.006,"MagicTest\\MagicTest\\Tests\\FileEditorTest::it_properly_replaces_the_method_content_when_it_does_not_have_actions":0.001,"MagicTest\\MagicTest\\Tests\\FileEditorTest::it_properly_parses_a_file_that_uses_the_magic_macro":0.016,"MagicTest\\MagicTest\\Tests\\FileEditorTest::it_properly_adds_fills_to_a_livewire_test":0.001,"MagicTest\\MagicTest\\Tests\\FileEditorTest::it_finishes_a_test_using_the_macro":0.001,"MagicTest\\MagicTest\\Tests\\FileEditorTest::it_properly_adds_methods_to_a_file_using_inline_code":0.001,"MagicTest\\MagicTest\\Tests\\FileEditorTest::it_properly_adds_content_to_a_file_with_two_closures":0.001,"MagicTest\\MagicTest\\Tests\\FileEditorTest::it_properly_escapes_a_string":0.001,"MagicTest\\MagicTest\\Tests\\Grammar\\ClickTest::it_properly_builds_a_select":0,"MagicTest\\MagicTest\\Tests\\Grammar\\ClickTest::it_properly_builds_a_radio":0.004,"MagicTest\\MagicTest\\Tests\\Grammar\\FillTest::it_properly_builds_an_action":0,"MagicTest\\MagicTest\\Tests\\Grammar\\FillTest::it_properly_adds_a_pause_to_a_livewire_input":0,"MagicTest\\MagicTest\\Tests\\Grammar\\FillTest::it_uses_the_livewire_selector_when_name_is_not_unique":0,"MagicTest\\MagicTest\\Tests\\Grammar\\FillTest::it_gives_priority_to_the_name_attribute":0,"MagicTest\\MagicTest\\Tests\\Grammar\\SeeTest::it_properly_builds_a_see_grammar":0,"MagicTest\\MagicTest\\Tests\\MagicTestManagerTest::it_replaces_the_content_of_a_file_with_actions":0.001,"MagicTest\\MagicTest\\Tests\\MagicTestManagerTest::it_replaces_the_content_of_a_file_without_actions":0.001,"MagicTest\\MagicTest\\Tests\\Parser\\FileTest::it_validates_a_class_missing_a_method":0.001,"MagicTest\\MagicTest\\Tests\\Parser\\FileTest::it_validates_a_class_missing_the_method_Call":0.001,"MagicTest\\MagicTest\\Tests\\Parser\\FileTest::it_validates_a_class_missing_the_closure":0.001,"MagicTest\\MagicTest\\Tests\\Support\\AttributeCollectionTest::it_reorders_attributes_including_a_name":0,"MagicTest\\MagicTest\\Tests\\Support\\AttributeCollectionTest::it_reorders_items_and_gives_preference_to_dusk":0}} -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to `magic-test-laravel` will be documented in this file. 4 | 5 | ## 1.0.0 - 202X-XX-XX 6 | 7 | - initial release 8 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) mateusjatenee 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Magic Test for Laravel 2 | 3 | [![Latest Version on Packagist](https://img.shields.io/packagist/v/magic-test/magic-test-laravel.svg?style=flat-square)](https://packagist.org/packages/magic-test/magic-test-laravel) 4 | [![GitHub Tests Action Status](https://img.shields.io/github/workflow/status/magic-test/magic-test-laravel/run-tests?label=tests)](https://github.com/magic-test/magic-test-laravel/actions?query=workflow%3ATests+branch%3Amaster) 5 | [![GitHub Code Style Action Status](https://img.shields.io/github/workflow/status/magic-test/magic-test-laravel/Check%20&%20fix%20styling?label=code%20style)](https://github.com/magic-test/magic-test-laravel/actions?query=workflow%3A"Check+%26+fix+styling"+branch%3Amaster) 6 | [![Total Downloads](https://img.shields.io/packagist/dt/magic-test/magic-test-laravel.svg?style=flat-square)](https://packagist.org/packages/magic-test/magic-test-laravel) 7 | 8 | Magic Test allows you to write browser tests by simply clicking around on the application being tested, all without the slowness of constantly restarting the testing environment. 9 | It inverts the test-writing experience and avoids all the back and forth between tests, your terminal and your template files. [See it in action here.](https://twitter.com/mateusjatenee/status/1368905554790334464) 10 | The easiest way to explain Magic Test is through a video. [Check it out here](https://twitter.com/mateusjatenee/status/1368905554790334464). 11 | 12 | Magic Test was originally created by [Andrew Culver](http://twitter.com/andrewculver) and [Adam Pallozi](https://twitter.com/adampallozzi) for Ruby on Rails. 13 | Laravel Magic Test was created by [Mateus Guimarães](https://twitter.com/mateusjatenee). 14 | 15 | > Magic Test is still in early development, and that includes the documentation. Any questions you have that aren't already address in the documentation should be opened as issues so they can be appropriately addressed in the documentation. 16 | 17 | > :warning: **If you are using Windows/Laragon**: This package won't work with Windows/Laragon, since this doesn't support TTY. This package DOES work with WSL. 18 | 19 | ## Installation 20 | 21 | You can install the package via composer: 22 | 23 | ```bash 24 | composer require magic-test/magic-test-laravel --dev 25 | ``` 26 | 27 | ## Usage 28 | On your Laravel Dusk tests, simply add `magic()` at the end of your method chain. For example: 29 | 30 | ```php 31 | public function testBasicExample() 32 | { 33 | $this->browse(function (Browser $browser) { 34 | $browser->visit('/') 35 | ->magic(); 36 | }); 37 | } 38 | ``` 39 | 40 | To run Magic Test, you must simply run the command `php artisan magic`. Behind the scenes, it is the same as running `php artisan dusk`, but it will maintain the browser window open. 41 | 42 | This will leave you with two or three windows: 43 | - The browser 44 | - An interactive Shell 45 | - Your text editor if you had it open 46 | 47 | For the Magic Experience™️, we suggest you organize the three windows to fit your screen. That way, you can see tests being generated in real-time. 48 | 49 | ## Recording Actions 50 | Once the browser is open, Magic Test will already be capturing all of your actions. You can click around, fill inputs, checkboxes, selects and radios just like you would do manually testing an application. 51 | 52 | ## Generating Assertions 53 | Additionally, you can generate text assertions by selecting a given text and then pressing ControlShift + A. You'll see a dialog box confirming the assertion has been recorded. 54 | 55 | ## Saving the new actions to the test file 56 | To save the actions that were recorded, simply go to the Shell and type `ok`. You are free to close it and come back to your Magic Sessiona any time, or just keep recording more actions. 57 | If you're satisfied with your test, you can type `finish` on the Shell and it'll remove the `magic()` call from your test, leaving you with a clean, working test. 58 | 59 | See how it works [on this video](https://twitter.com/mateusjatenee/status/1368905554790334464) 60 | 61 | Magic Test is still in it's early days, so you might find that the output is not exactly what you wanted. In that case, [feel free to submit an issue](https://github.com/magic-test/magic-test-laravel/issues/new) and we'll try to improve it ASAP. 62 | 63 | ## Known issues 64 | 65 | Magic Test does not work well with Inertia.js assertions. If you're using Inertia in an integration test, please disable Magic Test by add the following code to your `setUp` method: 66 | 67 | ```php 68 | {var e={755:function(e,t){var n;!function(t,n){"use strict";"object"==typeof e.exports?e.exports=t.document?n(t,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return n(e)}:n(t)}("undefined"!=typeof window?window:this,(function(r,i){"use strict";var o=[],a=Object.getPrototypeOf,s=o.slice,u=o.flat?function(e){return o.flat.call(e)}:function(e){return o.concat.apply([],e)},l=o.push,c=o.indexOf,f={},p=f.toString,d=f.hasOwnProperty,h=d.toString,g=h.call(Object),v={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},y=function(e){return null!=e&&e===e.window},x=r.document,b={type:!0,src:!0,nonce:!0,noModule:!0};function w(e,t,n){var r,i,o=(n=n||x).createElement("script");if(o.text=e,t)for(r in b)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function T(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?f[p.call(e)]||"object":typeof e}var C="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function E(e){var t=!!e&&"length"in e&&e.length,n=T(e);return!m(e)&&!y(e)&&("array"===n||0===t||"number"==typeof t&&t>0&&t-1 in e)}S.fn=S.prototype={jquery:C,constructor:S,length:0,toArray:function(){return s.call(this)},get:function(e){return null==e?s.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=S.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return S.each(this,e)},map:function(e){return this.pushStack(S.map(this,(function(t,n){return e.call(t,n,t)})))},slice:function(){return this.pushStack(s.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},even:function(){return this.pushStack(S.grep(this,(function(e,t){return(t+1)%2})))},odd:function(){return this.pushStack(S.grep(this,(function(e,t){return t%2})))},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(n>=0&&n+~]|[\\x20\\t\\r\\n\\f])[\\x20\\t\\r\\n\\f]*"),z=new RegExp(M+"|>"),X=new RegExp(F),J=new RegExp("^"+R+"$"),V={ID:new RegExp("^#("+R+")"),CLASS:new RegExp("^\\.("+R+")"),TAG:new RegExp("^("+R+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\([\\x20\\t\\r\\n\\f]*(even|odd|(([+-]|)(\\d*)n|)[\\x20\\t\\r\\n\\f]*(?:([+-]|)[\\x20\\t\\r\\n\\f]*(\\d+)|))[\\x20\\t\\r\\n\\f]*\\)|)","i"),bool:new RegExp("^(?:"+P+")$","i"),needsContext:new RegExp("^[\\x20\\t\\r\\n\\f]*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\([\\x20\\t\\r\\n\\f]*((?:-\\d)?\\d*)[\\x20\\t\\r\\n\\f]*\\)|)(?=[^-]|$)","i")},Q=/HTML$/i,G=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}[\\x20\\t\\r\\n\\f]?|\\\\([^\\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?"�":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){p()},ae=be((function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()}),{dir:"parentNode",next:"legend"});try{O.apply(j=H.call(w.childNodes),w.childNodes),j[w.childNodes.length].nodeType}catch(e){O={apply:j.length?function(e,t){q.apply(e,H.call(t))}:function(e,t){for(var n=e.length,r=0;e[n++]=t[r++];);e.length=n-1}}}function se(e,t,r,i){var o,s,l,c,f,h,m,y=t&&t.ownerDocument,w=t?t.nodeType:9;if(r=r||[],"string"!=typeof e||!e||1!==w&&9!==w&&11!==w)return r;if(!i&&(p(t),t=t||d,g)){if(11!==w&&(f=Z.exec(e)))if(o=f[1]){if(9===w){if(!(l=t.getElementById(o)))return r;if(l.id===o)return r.push(l),r}else if(y&&(l=y.getElementById(o))&&x(t,l)&&l.id===o)return r.push(l),r}else{if(f[2])return O.apply(r,t.getElementsByTagName(e)),r;if((o=f[3])&&n.getElementsByClassName&&t.getElementsByClassName)return O.apply(r,t.getElementsByClassName(o)),r}if(n.qsa&&!k[e+" "]&&(!v||!v.test(e))&&(1!==w||"object"!==t.nodeName.toLowerCase())){if(m=e,y=t,1===w&&(z.test(e)||U.test(e))){for((y=ee.test(e)&&me(t.parentNode)||t)===t&&n.scope||((c=t.getAttribute("id"))?c=c.replace(re,ie):t.setAttribute("id",c=b)),s=(h=a(e)).length;s--;)h[s]=(c?"#"+c:":scope")+" "+xe(h[s]);m=h.join(",")}try{return O.apply(r,y.querySelectorAll(m)),r}catch(t){k(e,!0)}finally{c===b&&t.removeAttribute("id")}}}return u(e.replace($,"$1"),t,r,i)}function ue(){var e=[];return function t(n,i){return e.push(n+" ")>r.cacheLength&&delete t[e.shift()],t[n+" "]=i}}function le(e){return e[b]=!0,e}function ce(e){var t=d.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){for(var n=e.split("|"),i=n.length;i--;)r.attrHandle[n[i]]=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)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function de(e){return function(t){return"input"===t.nodeName.toLowerCase()&&t.type===e}}function he(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function ge(e){return function(t){return"form"in t?t.parentNode&&!1===t.disabled?"label"in t?"label"in t.parentNode?t.parentNode.disabled===e:t.disabled===e:t.isDisabled===e||t.isDisabled!==!e&&ae(t)===e:t.disabled===e:"label"in t&&t.disabled===e}}function ve(e){return le((function(t){return t=+t,le((function(n,r){for(var i,o=e([],n.length,t),a=o.length;a--;)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))}))}))}function me(e){return e&&void 0!==e.getElementsByTagName&&e}for(t in n=se.support={},o=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Q.test(t||n&&n.nodeName||"HTML")},p=se.setDocument=function(e){var t,i,a=e?e.ownerDocument||e:w;return a!=d&&9===a.nodeType&&a.documentElement?(h=(d=a).documentElement,g=!o(d),w!=d&&(i=d.defaultView)&&i.top!==i&&(i.addEventListener?i.addEventListener("unload",oe,!1):i.attachEvent&&i.attachEvent("onunload",oe)),n.scope=ce((function(e){return h.appendChild(e).appendChild(d.createElement("div")),void 0!==e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length})),n.attributes=ce((function(e){return e.className="i",!e.getAttribute("className")})),n.getElementsByTagName=ce((function(e){return e.appendChild(d.createComment("")),!e.getElementsByTagName("*").length})),n.getElementsByClassName=K.test(d.getElementsByClassName),n.getById=ce((function(e){return h.appendChild(e).id=b,!d.getElementsByName||!d.getElementsByName(b).length})),n.getById?(r.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},r.find.ID=function(e,t){if(void 0!==t.getElementById&&g){var n=t.getElementById(e);return n?[n]:[]}}):(r.filter.ID=function(e){var t=e.replace(te,ne);return function(e){var n=void 0!==e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}},r.find.ID=function(e,t){if(void 0!==t.getElementById&&g){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];for(i=t.getElementsByName(e),r=0;o=i[r++];)if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),r.find.TAG=n.getElementsByTagName?function(e,t){return void 0!==t.getElementsByTagName?t.getElementsByTagName(e):n.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){for(;n=o[i++];)1===n.nodeType&&r.push(n);return r}return o},r.find.CLASS=n.getElementsByClassName&&function(e,t){if(void 0!==t.getElementsByClassName&&g)return t.getElementsByClassName(e)},m=[],v=[],(n.qsa=K.test(d.querySelectorAll))&&(ce((function(e){var t;h.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]=[\\x20\\t\\r\\n\\f]*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\[[\\x20\\t\\r\\n\\f]*(?:value|"+P+")"),e.querySelectorAll("[id~="+b+"-]").length||v.push("~="),(t=d.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\[[\\x20\\t\\r\\n\\f]*name[\\x20\\t\\r\\n\\f]*=[\\x20\\t\\r\\n\\f]*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+b+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")})),ce((function(e){e.innerHTML="";var t=d.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name[\\x20\\t\\r\\n\\f]*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),h.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")}))),(n.matchesSelector=K.test(y=h.matches||h.webkitMatchesSelector||h.mozMatchesSelector||h.oMatchesSelector||h.msMatchesSelector))&&ce((function(e){n.disconnectedMatch=y.call(e,"*"),y.call(e,"[s!='']:x"),m.push("!=",F)})),v=v.length&&new RegExp(v.join("|")),m=m.length&&new RegExp(m.join("|")),t=K.test(h.compareDocumentPosition),x=t||K.test(h.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)for(;t=t.parentNode;)if(t===e)return!0;return!1},A=t?function(e,t){if(e===t)return f=!0,0;var r=!e.compareDocumentPosition-!t.compareDocumentPosition;return r||(1&(r=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!n.sortDetached&&t.compareDocumentPosition(e)===r?e==d||e.ownerDocument==w&&x(w,e)?-1:t==d||t.ownerDocument==w&&x(w,t)?1:c?I(c,e)-I(c,t):0:4&r?-1:1)}:function(e,t){if(e===t)return f=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==d?-1:t==d?1:i?-1:o?1:c?I(c,e)-I(c,t):0;if(i===o)return pe(e,t);for(n=e;n=n.parentNode;)a.unshift(n);for(n=t;n=n.parentNode;)s.unshift(n);for(;a[r]===s[r];)r++;return r?pe(a[r],s[r]):a[r]==w?-1:s[r]==w?1:0},d):d},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(p(e),n.matchesSelector&&g&&!k[t+" "]&&(!m||!m.test(t))&&(!v||!v.test(t)))try{var r=y.call(e,t);if(r||n.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(e){k(t,!0)}return se(t,d,null,[e]).length>0},se.contains=function(e,t){return(e.ownerDocument||e)!=d&&p(e),x(e,t)},se.attr=function(e,t){(e.ownerDocument||e)!=d&&p(e);var i=r.attrHandle[t.toLowerCase()],o=i&&D.call(r.attrHandle,t.toLowerCase())?i(e,t,!g):void 0;return void 0!==o?o:n.attributes||!g?e.getAttribute(t):(o=e.getAttributeNode(t))&&o.specified?o.value:null},se.escape=function(e){return(e+"").replace(re,ie)},se.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},se.uniqueSort=function(e){var t,r=[],i=0,o=0;if(f=!n.detectDuplicates,c=!n.sortStable&&e.slice(0),e.sort(A),f){for(;t=e[o++];)t===e[o]&&(i=r.push(o));for(;i--;)e.splice(r[i],1)}return c=null,e},i=se.getText=function(e){var t,n="",r=0,o=e.nodeType;if(o){if(1===o||9===o||11===o){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=i(e)}else if(3===o||4===o)return e.nodeValue}else for(;t=e[r++];)n+=i(t);return n},r=se.selectors={cacheLength:50,createPseudo:le,match:V,attrHandle:{},find:{},relative:{">":{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 V.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=a(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=S[e+" "];return t||(t=new RegExp("(^|[\\x20\\t\\r\\n\\f])"+e+"("+M+"|$)"))&&S(e,(function(e){return t.test("string"==typeof e.className&&e.className||void 0!==e.getAttribute&&e.getAttribute("class")||"")}))},ATTR:function(e,t,n){return function(r){var i=se.attr(r,e);return null==i?"!="===t:!t||(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i.replace(B," ")+" ").indexOf(n)>-1:"|="===t&&(i===n||i.slice(0,n.length+1)===n+"-"))}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,p,d,h,g=o!==a?"nextSibling":"previousSibling",v=t.parentNode,m=s&&t.nodeName.toLowerCase(),y=!u&&!s,x=!1;if(v){if(o){for(;g;){for(p=t;p=p[g];)if(s?p.nodeName.toLowerCase()===m:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?v.firstChild:v.lastChild],a&&y){for(x=(d=(l=(c=(f=(p=v)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1])&&l[2],p=d&&v.childNodes[d];p=++d&&p&&p[g]||(x=d=0)||h.pop();)if(1===p.nodeType&&++x&&p===t){c[e]=[T,d,x];break}}else if(y&&(x=d=(l=(c=(f=(p=t)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1]),!1===x)for(;(p=++d&&p&&p[g]||(x=d=0)||h.pop())&&((s?p.nodeName.toLowerCase()!==m:1!==p.nodeType)||!++x||(y&&((c=(f=p[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]=[T,x]),p!==t)););return(x-=i)===r||x%r==0&&x/r>=0}}},PSEUDO:function(e,t){var n,i=r.pseudos[e]||r.setFilters[e.toLowerCase()]||se.error("unsupported pseudo: "+e);return i[b]?i(t):i.length>1?(n=[e,e,"",t],r.setFilters.hasOwnProperty(e.toLowerCase())?le((function(e,n){for(var r,o=i(e,t),a=o.length;a--;)e[r=I(e,o[a])]=!(n[r]=o[a])})):function(e){return i(e,0,n)}):i}},pseudos:{not:le((function(e){var t=[],n=[],r=s(e.replace($,"$1"));return r[b]?le((function(e,t,n,i){for(var o,a=r(e,null,i,[]),s=e.length;s--;)(o=a[s])&&(e[s]=!(t[s]=o))})):function(e,i,o){return t[0]=e,r(t,null,o,n),t[0]=null,!n.pop()}})),has:le((function(e){return function(t){return se(e,t).length>0}})),contains:le((function(e){return e=e.replace(te,ne),function(t){return(t.textContent||i(t)).indexOf(e)>-1}})),lang:le((function(e){return J.test(e||"")||se.error("unsupported lang: "+e),e=e.replace(te,ne).toLowerCase(),function(t){var n;do{if(n=g?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return(n=n.toLowerCase())===e||0===n.indexOf(e+"-")}while((t=t.parentNode)&&1===t.nodeType);return!1}})),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===h},focus:function(e){return e===d.activeElement&&(!d.hasFocus||d.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:ge(!1),disabled:ge(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!r.pseudos.empty(e)},header:function(e){return Y.test(e.nodeName)},input:function(e){return G.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:ve((function(){return[0]})),last:ve((function(e,t){return[t-1]})),eq:ve((function(e,t,n){return[n<0?n+t:n]})),even:ve((function(e,t){for(var n=0;nt?t:n;--r>=0;)e.push(r);return e})),gt:ve((function(e,t,n){for(var r=n<0?n+t:n;++r1?function(t,n,r){for(var i=e.length;i--;)if(!e[i](t,n,r))return!1;return!0}:e[0]}function Te(e,t,n,r,i){for(var o,a=[],s=0,u=e.length,l=null!=t;s-1&&(o[l]=!(a[l]=f))}}else m=Te(m===a?m.splice(h,m.length):m),i?i(null,a,m,u):O.apply(a,m)}))}function Se(e){for(var t,n,i,o=e.length,a=r.relative[e[0].type],s=a||r.relative[" "],u=a?1:0,c=be((function(e){return e===t}),s,!0),f=be((function(e){return I(t,e)>-1}),s,!0),p=[function(e,n,r){var i=!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):f(e,n,r));return t=null,i}];u1&&we(p),u>1&&xe(e.slice(0,u-1).concat({value:" "===e[u-2].type?"*":""})).replace($,"$1"),n,u0,i=e.length>0,o=function(o,a,s,u,c){var f,h,v,m=0,y="0",x=o&&[],b=[],w=l,C=o||i&&r.find.TAG("*",c),S=T+=null==w?1:Math.random()||.1,E=C.length;for(c&&(l=a==d||a||c);y!==E&&null!=(f=C[y]);y++){if(i&&f){for(h=0,a||f.ownerDocument==d||(p(f),s=!g);v=e[h++];)if(v(f,a||d,s)){u.push(f);break}c&&(T=S)}n&&((f=!v&&f)&&m--,o&&x.push(f))}if(m+=y,n&&y!==m){for(h=0;v=t[h++];)v(x,b,a,s);if(o){if(m>0)for(;y--;)x[y]||b[y]||(b[y]=L.call(u));b=Te(b)}O.apply(u,b),c&&!o&&b.length>0&&m+t.length>1&&se.uniqueSort(u)}return c&&(T=S,l=w),x};return n?le(o):o}(o,i)),s.selector=e}return s},u=se.select=function(e,t,n,i){var o,u,l,c,f,p="function"==typeof e&&e,d=!i&&a(e=p.selector||e);if(n=n||[],1===d.length){if((u=d[0]=d[0].slice(0)).length>2&&"ID"===(l=u[0]).type&&9===t.nodeType&&g&&r.relative[u[1].type]){if(!(t=(r.find.ID(l.matches[0].replace(te,ne),t)||[])[0]))return n;p&&(t=t.parentNode),e=e.slice(u.shift().value.length)}for(o=V.needsContext.test(e)?0:u.length;o--&&(l=u[o],!r.relative[c=l.type]);)if((f=r.find[c])&&(i=f(l.matches[0].replace(te,ne),ee.test(u[0].type)&&me(t.parentNode)||t))){if(u.splice(o,1),!(e=i.length&&xe(u)))return O.apply(n,i),n;break}}return(p||s(e,d))(i,t,!g,n,!t||ee.test(e)&&me(t.parentNode)||t),n},n.sortStable=b.split("").sort(A).join("")===b,n.detectDuplicates=!!f,p(),n.sortDetached=ce((function(e){return 1&e.compareDocumentPosition(d.createElement("fieldset"))})),ce((function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")}))||fe("type|href|height|width",(function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)})),n.attributes&&ce((function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")}))||fe("value",(function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue})),ce((function(e){return null==e.getAttribute("disabled")}))||fe(P,(function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null})),se}(r);S.find=N,S.expr=N.selectors,S.expr[":"]=S.expr.pseudos,S.uniqueSort=S.unique=N.uniqueSort,S.text=N.getText,S.isXMLDoc=N.isXML,S.contains=N.contains,S.escapeSelector=N.escape;var k=function(e,t,n){for(var r=[],i=void 0!==n;(e=e[t])&&9!==e.nodeType;)if(1===e.nodeType){if(i&&S(e).is(n))break;r.push(e)}return r},A=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},D=S.expr.match.needsContext;function j(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var L=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function q(e,t,n){return m(t)?S.grep(e,(function(e,r){return!!t.call(e,r,e)!==n})):t.nodeType?S.grep(e,(function(e){return e===t!==n})):"string"!=typeof t?S.grep(e,(function(e){return c.call(t,e)>-1!==n})):S.filter(t,e,n)}S.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?S.find.matchesSelector(r,e)?[r]:[]:S.find.matches(e,S.grep(t,(function(e){return 1===e.nodeType})))},S.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(S(e).filter((function(){for(t=0;t1?S.uniqueSort(n):n},filter:function(e){return this.pushStack(q(this,e||[],!1))},not:function(e){return this.pushStack(q(this,e||[],!0))},is:function(e){return!!q(this,"string"==typeof e&&D.test(e)?S(e):e||[],!1).length}});var O,H=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||O,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&e.length>=3?[null,e,null]:H.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:x,!0)),L.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=x.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,O=S(x);var I=/^(?:parents|prev(?:Until|All))/,P={children:!0,contents:!0,next:!0,prev:!0};function M(e,t){for(;(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-1:1===n.nodeType&&S.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?S.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?c.call(S(e),this[0]):c.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(S.uniqueSort(S.merge(this.get(),S(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),S.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return k(e,"parentNode")},parentsUntil:function(e,t,n){return k(e,"parentNode",n)},next:function(e){return M(e,"nextSibling")},prev:function(e){return M(e,"previousSibling")},nextAll:function(e){return k(e,"nextSibling")},prevAll:function(e){return k(e,"previousSibling")},nextUntil:function(e,t,n){return k(e,"nextSibling",n)},prevUntil:function(e,t,n){return k(e,"previousSibling",n)},siblings:function(e){return A((e.parentNode||{}).firstChild,e)},children:function(e){return A(e.firstChild)},contents:function(e){return null!=e.contentDocument&&a(e.contentDocument)?e.contentDocument:(j(e,"template")&&(e=e.content||e),S.merge([],e.childNodes))}},(function(e,t){S.fn[e]=function(n,r){var i=S.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=S.filter(r,i)),this.length>1&&(P[e]||S.uniqueSort(i),I.test(e)&&i.reverse()),this.pushStack(i)}}));var R=/[^\x20\t\r\n\f]+/g;function W(e){return e}function F(e){throw e}function B(e,t,n,r){var i;try{e&&m(i=e.promise)?i.call(e).done(t).fail(n):e&&m(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}S.Callbacks=function(e){e="string"==typeof e?function(e){var t={};return S.each(e.match(R)||[],(function(e,n){t[n]=!0})),t}(e):S.extend({},e);var t,n,r,i,o=[],a=[],s=-1,u=function(){for(i=i||e.once,r=t=!0;a.length;s=-1)for(n=a.shift();++s-1;)o.splice(n,1),n<=s&&s--})),this},has:function(e){return e?S.inArray(e,o)>-1:o.length>0},empty:function(){return o&&(o=[]),this},disable:function(){return i=a=[],o=n="",this},disabled:function(){return!o},lock:function(){return i=a=[],n||t||(o=n=""),this},locked:function(){return!!i},fireWith:function(e,n){return i||(n=[e,(n=n||[]).slice?n.slice():n],a.push(n),t||u()),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!r}};return l},S.extend({Deferred:function(e){var t=[["notify","progress",S.Callbacks("memory"),S.Callbacks("memory"),2],["resolve","done",S.Callbacks("once memory"),S.Callbacks("once memory"),0,"resolved"],["reject","fail",S.Callbacks("once memory"),S.Callbacks("once memory"),1,"rejected"]],n="pending",i={state:function(){return n},always:function(){return o.done(arguments).fail(arguments),this},catch:function(e){return i.then(null,e)},pipe:function(){var e=arguments;return S.Deferred((function(n){S.each(t,(function(t,r){var i=m(e[r[4]])&&e[r[4]];o[r[1]]((function(){var e=i&&i.apply(this,arguments);e&&m(e.promise)?e.promise().progress(n.notify).done(n.resolve).fail(n.reject):n[r[0]+"With"](this,i?[e]:arguments)}))})),e=null})).promise()},then:function(e,n,i){var o=0;function a(e,t,n,i){return function(){var s=this,u=arguments,l=function(){var r,l;if(!(e=o&&(n!==F&&(s=void 0,u=[r]),t.rejectWith(s,u))}};e?c():(S.Deferred.getStackHook&&(c.stackTrace=S.Deferred.getStackHook()),r.setTimeout(c))}}return S.Deferred((function(r){t[0][3].add(a(0,r,m(i)?i:W,r.notifyWith)),t[1][3].add(a(0,r,m(e)?e:W)),t[2][3].add(a(0,r,m(n)?n:F))})).promise()},promise:function(e){return null!=e?S.extend(e,i):i}},o={};return S.each(t,(function(e,r){var a=r[2],s=r[5];i[r[1]]=a.add,s&&a.add((function(){n=s}),t[3-e][2].disable,t[3-e][3].disable,t[0][2].lock,t[0][3].lock),a.add(r[3].fire),o[r[0]]=function(){return o[r[0]+"With"](this===o?void 0:this,arguments),this},o[r[0]+"With"]=a.fireWith})),i.promise(o),e&&e.call(o,o),o},when:function(e){var t=arguments.length,n=t,r=Array(n),i=s.call(arguments),o=S.Deferred(),a=function(e){return function(n){r[e]=this,i[e]=arguments.length>1?s.call(arguments):n,--t||o.resolveWith(r,i)}};if(t<=1&&(B(e,o.done(a(n)).resolve,o.reject,!t),"pending"===o.state()||m(i[n]&&i[n].then)))return o.then();for(;n--;)B(i[n],a(n),o.reject);return o.promise()}});var $=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;S.Deferred.exceptionHook=function(e,t){r.console&&r.console.warn&&e&&$.test(e.name)&&r.console.warn("jQuery.Deferred exception: "+e.message,e.stack,t)},S.readyException=function(e){r.setTimeout((function(){throw e}))};var _=S.Deferred();function U(){x.removeEventListener("DOMContentLoaded",U),r.removeEventListener("load",U),S.ready()}S.fn.ready=function(e){return _.then(e).catch((function(e){S.readyException(e)})),this},S.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--S.readyWait:S.isReady)||(S.isReady=!0,!0!==e&&--S.readyWait>0||_.resolveWith(x,[S]))}}),S.ready.then=_.then,"complete"===x.readyState||"loading"!==x.readyState&&!x.documentElement.doScroll?r.setTimeout(S.ready):(x.addEventListener("DOMContentLoaded",U),r.addEventListener("load",U));var z=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===T(n))for(s in i=!0,n)z(e,t,s,n[s],!0,o,a);else if(void 0!==r&&(i=!0,m(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(S(e),n)})),t))for(;s1,null,!0)},removeData:function(e){return this.each((function(){Z.remove(this,e)}))}}),S.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=K.get(e,t),n&&(!r||Array.isArray(n)?r=K.access(e,t,S.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=S.queue(e,t),r=n.length,i=n.shift(),o=S._queueHooks(e,t);"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,(function(){S.dequeue(e,t)}),o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return K.get(e,n)||K.access(e,n,{empty:S.Callbacks("once memory").add((function(){K.remove(e,[t+"queue",n])}))})}}),S.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length\x20\t\r\n\f]*)/i,ye=/^$|^module$|\/(?:java|ecma)script/i;he=x.createDocumentFragment().appendChild(x.createElement("div")),(ge=x.createElement("input")).setAttribute("type","radio"),ge.setAttribute("checked","checked"),ge.setAttribute("name","t"),he.appendChild(ge),v.checkClone=he.cloneNode(!0).cloneNode(!0).lastChild.checked,he.innerHTML="",v.noCloneChecked=!!he.cloneNode(!0).lastChild.defaultValue,he.innerHTML="",v.option=!!he.lastChild;var xe={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function be(e,t){var n;return n=void 0!==e.getElementsByTagName?e.getElementsByTagName(t||"*"):void 0!==e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&j(e,t)?S.merge([e],n):n}function we(e,t){for(var n=0,r=e.length;n",""]);var Te=/<|&#?\w+;/;function Ce(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d-1)i&&i.push(o);else if(l=se(o),a=be(f.appendChild(o),"script"),l&&we(a),n)for(c=0;o=a[c++];)ye.test(o.type||"")&&n.push(o);return f}var Se=/^([^.]*)(?:\.(.+)|)/;function Ee(){return!0}function Ne(){return!1}function ke(e,t){return e===function(){try{return x.activeElement}catch(e){}}()==("focus"===t)}function Ae(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)Ae(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Ne;else if(!i)return e;return 1===o&&(a=i,i=function(e){return S().off(e),a.apply(this,arguments)},i.guid=a.guid||(a.guid=S.guid++)),e.each((function(){S.event.add(this,t,i,r,n)}))}function De(e,t,n){n?(K.set(e,t,!1),S.event.add(e,t,{namespace:!1,handler:function(e){var r,i,o=K.get(this,t);if(1&e.isTrigger&&this[t]){if(o.length)(S.event.special[t]||{}).delegateType&&e.stopPropagation();else if(o=s.call(arguments),K.set(this,t,o),r=n(this,t),this[t](),o!==(i=K.get(this,t))||r?K.set(this,t,!1):i={},o!==i)return e.stopImmediatePropagation(),e.preventDefault(),i&&i.value}else o.length&&(K.set(this,t,{value:S.event.trigger(S.extend(o[0],S.Event.prototype),o.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===K.get(e,t)&&S.event.add(e,t,Ee)}S.event={global:{},add:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=K.get(e);if(G(e))for(n.handler&&(n=(o=n).handler,i=o.selector),i&&S.find.matchesSelector(ae,i),n.guid||(n.guid=S.guid++),(u=v.events)||(u=v.events=Object.create(null)),(a=v.handle)||(a=v.handle=function(t){return void 0!==S&&S.event.triggered!==t.type?S.event.dispatch.apply(e,arguments):void 0}),l=(t=(t||"").match(R)||[""]).length;l--;)d=g=(s=Se.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=S.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=S.event.special[d]||{},c=S.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&S.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(e,r,h,a)||e.addEventListener&&e.addEventListener(d,a)),f.add&&(f.add.call(e,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),S.event.global[d]=!0)},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=K.hasData(e)&&K.get(e);if(v&&(u=v.events)){for(l=(t=(t||"").match(R)||[""]).length;l--;)if(d=g=(s=Se.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){for(f=S.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;o--;)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||S.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)S.event.remove(e,d+t[l],n,r,!0);S.isEmptyObject(u)&&K.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=new Array(arguments.length),u=S.event.fix(e),l=(K.get(this,"events")||Object.create(null))[u.type]||[],c=S.event.special[u.type]||{};for(s[0]=u,t=1;t=1))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n-1:S.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u\s*$/g;function Oe(e,t){return j(e,"table")&&j(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function He(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Ie(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Pe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(K.hasData(e)&&(s=K.get(e).events))for(i in K.remove(t,"handle events"),s)for(n=0,r=s[i].length;n1&&"string"==typeof h&&!v.checkClone&&Le.test(h))return e.each((function(i){var o=e.eq(i);g&&(t[0]=h.call(this,i,o.html())),Re(o,t,n,r)}));if(p&&(o=(i=Ce(t,e[0].ownerDocument,!1,e,r)).firstChild,1===i.childNodes.length&&(i=o),o||r)){for(s=(a=S.map(be(i,"script"),He)).length;f0&&we(a,!u&&be(e,"script")),s},cleanData:function(e){for(var t,n,r,i=S.event.special,o=0;void 0!==(n=e[o]);o++)if(G(n)){if(t=n[K.expando]){if(t.events)for(r in t.events)i[r]?S.event.remove(n,r):S.removeEvent(n,r,t.handle);n[K.expando]=void 0}n[Z.expando]&&(n[Z.expando]=void 0)}}}),S.fn.extend({detach:function(e){return We(this,e,!0)},remove:function(e){return We(this,e)},text:function(e){return z(this,(function(e){return void 0===e?S.text(this):this.empty().each((function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)}))}),null,e,arguments.length)},append:function(){return Re(this,arguments,(function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||Oe(this,e).appendChild(e)}))},prepend:function(){return Re(this,arguments,(function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Oe(this,e);t.insertBefore(e,t.firstChild)}}))},before:function(){return Re(this,arguments,(function(e){this.parentNode&&this.parentNode.insertBefore(e,this)}))},after:function(){return Re(this,arguments,(function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)}))},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(S.cleanData(be(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map((function(){return S.clone(this,e,t)}))},html:function(e){return z(this,(function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!je.test(e)&&!xe[(me.exec(e)||["",""])[1].toLowerCase()]){e=S.htmlPrefilter(e);try{for(;n=0&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))||0),u}function nt(e,t,n){var r=Be(e),i=(!v.boxSizingReliable()||n)&&"border-box"===S.css(e,"boxSizing",!1,r),o=i,a=Ue(e,t,r),s="offset"+t[0].toUpperCase()+t.slice(1);if(Fe.test(a)){if(!n)return a;a="auto"}return(!v.boxSizingReliable()&&i||!v.reliableTrDimensions()&&j(e,"tr")||"auto"===a||!parseFloat(a)&&"inline"===S.css(e,"display",!1,r))&&e.getClientRects().length&&(i="border-box"===S.css(e,"boxSizing",!1,r),(o=s in e)&&(a=e[s])),(a=parseFloat(a)||0)+tt(e,t,n||(i?"border":"content"),o,r,a)+"px"}function rt(e,t,n,r,i){return new rt.prototype.init(e,t,n,r,i)}S.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Ue(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,gridArea:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnStart:!0,gridRow:!0,gridRowEnd:!0,gridRowStart:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=Q(t),u=Ye.test(t),l=e.style;if(u||(t=Qe(s)),a=S.cssHooks[t]||S.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];"string"===(o=typeof n)&&(i=ie.exec(n))&&i[1]&&(n=ce(e,t,i),o="number"),null!=n&&n==n&&("number"!==o||u||(n+=i&&i[3]||(S.cssNumber[s]?"":"px")),v.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=Q(t);return Ye.test(t)||(t=Qe(s)),(a=S.cssHooks[t]||S.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Ue(e,t,r)),"normal"===i&&t in Ze&&(i=Ze[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),S.each(["height","width"],(function(e,t){S.cssHooks[t]={get:function(e,n,r){if(n)return!Ge.test(S.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?nt(e,t,r):$e(e,Ke,(function(){return nt(e,t,r)}))},set:function(e,n,r){var i,o=Be(e),a=!v.scrollboxSize()&&"absolute"===o.position,s=(a||r)&&"border-box"===S.css(e,"boxSizing",!1,o),u=r?tt(e,t,r,s,o):0;return s&&a&&(u-=Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-parseFloat(o[t])-tt(e,t,"border",!1,o)-.5)),u&&(i=ie.exec(n))&&"px"!==(i[3]||"px")&&(e.style[t]=n,n=S.css(e,t)),et(0,n,u)}}})),S.cssHooks.marginLeft=ze(v.reliableMarginLeft,(function(e,t){if(t)return(parseFloat(Ue(e,"marginLeft"))||e.getBoundingClientRect().left-$e(e,{marginLeft:0},(function(){return e.getBoundingClientRect().left})))+"px"})),S.each({margin:"",padding:"",border:"Width"},(function(e,t){S.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];r<4;r++)i[e+oe[r]+t]=o[r]||o[r-2]||o[0];return i}},"margin"!==e&&(S.cssHooks[e+t].set=et)})),S.fn.extend({css:function(e,t){return z(this,(function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=Be(e),i=t.length;a1)}}),S.Tween=rt,rt.prototype={constructor:rt,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||S.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(S.cssNumber[n]?"":"px")},cur:function(){var e=rt.propHooks[this.prop];return e&&e.get?e.get(this):rt.propHooks._default.get(this)},run:function(e){var t,n=rt.propHooks[this.prop];return this.options.duration?this.pos=t=S.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):rt.propHooks._default.set(this),this}},rt.prototype.init.prototype=rt.prototype,rt.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=S.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){S.fx.step[e.prop]?S.fx.step[e.prop](e):1!==e.elem.nodeType||!S.cssHooks[e.prop]&&null==e.elem.style[Qe(e.prop)]?e.elem[e.prop]=e.now:S.style(e.elem,e.prop,e.now+e.unit)}}},rt.propHooks.scrollTop=rt.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},S.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},S.fx=rt.prototype.init,S.fx.step={};var it,ot,at=/^(?:toggle|show|hide)$/,st=/queueHooks$/;function ut(){ot&&(!1===x.hidden&&r.requestAnimationFrame?r.requestAnimationFrame(ut):r.setTimeout(ut,S.fx.interval),S.fx.tick())}function lt(){return r.setTimeout((function(){it=void 0})),it=Date.now()}function ct(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=oe[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function ft(e,t,n){for(var r,i=(pt.tweeners[t]||[]).concat(pt.tweeners["*"]),o=0,a=i.length;o1)},removeAttr:function(e){return this.each((function(){S.removeAttr(this,e)}))}}),S.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return void 0===e.getAttribute?S.prop(e,t,n):(1===o&&S.isXMLDoc(e)||(i=S.attrHooks[t.toLowerCase()]||(S.expr.match.bool.test(t)?dt:void 0)),void 0!==n?null===n?void S.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=S.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!v.radioValue&&"radio"===t&&j(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(R);if(i&&1===e.nodeType)for(;n=i[r++];)e.removeAttribute(n)}}),dt={set:function(e,t,n){return!1===t?S.removeAttr(e,n):e.setAttribute(n,n),n}},S.each(S.expr.match.bool.source.match(/\w+/g),(function(e,t){var n=ht[t]||S.find.attr;ht[t]=function(e,t,r){var i,o,a=t.toLowerCase();return r||(o=ht[a],ht[a]=i,i=null!=n(e,t,r)?a:null,ht[a]=o),i}}));var gt=/^(?:input|select|textarea|button)$/i,vt=/^(?:a|area)$/i;function mt(e){return(e.match(R)||[]).join(" ")}function yt(e){return e.getAttribute&&e.getAttribute("class")||""}function xt(e){return Array.isArray(e)?e:"string"==typeof e&&e.match(R)||[]}S.fn.extend({prop:function(e,t){return z(this,S.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each((function(){delete this[S.propFix[e]||e]}))}}),S.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&S.isXMLDoc(e)||(t=S.propFix[t]||t,i=S.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=S.find.attr(e,"tabindex");return t?parseInt(t,10):gt.test(e.nodeName)||vt.test(e.nodeName)&&e.href?0:-1}}},propFix:{for:"htmlFor",class:"className"}}),v.optSelected||(S.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),S.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],(function(){S.propFix[this.toLowerCase()]=this})),S.fn.extend({addClass:function(e){var t,n,r,i,o,a,s,u=0;if(m(e))return this.each((function(t){S(this).addClass(e.call(this,t,yt(this)))}));if((t=xt(e)).length)for(;n=this[u++];)if(i=yt(n),r=1===n.nodeType&&" "+mt(i)+" "){for(a=0;o=t[a++];)r.indexOf(" "+o+" ")<0&&(r+=o+" ");i!==(s=mt(r))&&n.setAttribute("class",s)}return this},removeClass:function(e){var t,n,r,i,o,a,s,u=0;if(m(e))return this.each((function(t){S(this).removeClass(e.call(this,t,yt(this)))}));if(!arguments.length)return this.attr("class","");if((t=xt(e)).length)for(;n=this[u++];)if(i=yt(n),r=1===n.nodeType&&" "+mt(i)+" "){for(a=0;o=t[a++];)for(;r.indexOf(" "+o+" ")>-1;)r=r.replace(" "+o+" "," ");i!==(s=mt(r))&&n.setAttribute("class",s)}return this},toggleClass:function(e,t){var n=typeof e,r="string"===n||Array.isArray(e);return"boolean"==typeof t&&r?t?this.addClass(e):this.removeClass(e):m(e)?this.each((function(n){S(this).toggleClass(e.call(this,n,yt(this),t),t)})):this.each((function(){var t,i,o,a;if(r)for(i=0,o=S(this),a=xt(e);t=a[i++];)o.hasClass(t)?o.removeClass(t):o.addClass(t);else void 0!==e&&"boolean"!==n||((t=yt(this))&&K.set(this,"__className__",t),this.setAttribute&&this.setAttribute("class",t||!1===e?"":K.get(this,"__className__")||""))}))},hasClass:function(e){var t,n,r=0;for(t=" "+e+" ";n=this[r++];)if(1===n.nodeType&&(" "+mt(yt(n))+" ").indexOf(t)>-1)return!0;return!1}});var bt=/\r/g;S.fn.extend({val:function(e){var t,n,r,i=this[0];return arguments.length?(r=m(e),this.each((function(n){var i;1===this.nodeType&&(null==(i=r?e.call(this,n,S(this).val()):e)?i="":"number"==typeof i?i+="":Array.isArray(i)&&(i=S.map(i,(function(e){return null==e?"":e+""}))),(t=S.valHooks[this.type]||S.valHooks[this.nodeName.toLowerCase()])&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))}))):i?(t=S.valHooks[i.type]||S.valHooks[i.nodeName.toLowerCase()])&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:"string"==typeof(n=i.value)?n.replace(bt,""):null==n?"":n:void 0}}),S.extend({valHooks:{option:{get:function(e){var t=S.find.attr(e,"value");return null!=t?t:mt(S.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a="select-one"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r-1)&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),S.each(["radio","checkbox"],(function(){S.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=S.inArray(S(e).val(),t)>-1}},v.checkOn||(S.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})})),v.focusin="onfocusin"in r;var wt=/^(?:focusinfocus|focusoutblur)$/,Tt=function(e){e.stopPropagation()};S.extend(S.event,{trigger:function(e,t,n,i){var o,a,s,u,l,c,f,p,h=[n||x],g=d.call(e,"type")?e.type:e,v=d.call(e,"namespace")?e.namespace.split("."):[];if(a=p=s=n=n||x,3!==n.nodeType&&8!==n.nodeType&&!wt.test(g+S.event.triggered)&&(g.indexOf(".")>-1&&(v=g.split("."),g=v.shift(),v.sort()),l=g.indexOf(":")<0&&"on"+g,(e=e[S.expando]?e:new S.Event(g,"object"==typeof e&&e)).isTrigger=i?2:3,e.namespace=v.join("."),e.rnamespace=e.namespace?new RegExp("(^|\\.)"+v.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,e.result=void 0,e.target||(e.target=n),t=null==t?[e]:S.makeArray(t,[e]),f=S.event.special[g]||{},i||!f.trigger||!1!==f.trigger.apply(n,t))){if(!i&&!f.noBubble&&!y(n)){for(u=f.delegateType||g,wt.test(u+g)||(a=a.parentNode);a;a=a.parentNode)h.push(a),s=a;s===(n.ownerDocument||x)&&h.push(s.defaultView||s.parentWindow||r)}for(o=0;(a=h[o++])&&!e.isPropagationStopped();)p=a,e.type=o>1?u:f.bindType||g,(c=(K.get(a,"events")||Object.create(null))[e.type]&&K.get(a,"handle"))&&c.apply(a,t),(c=l&&a[l])&&c.apply&&G(a)&&(e.result=c.apply(a,t),!1===e.result&&e.preventDefault());return e.type=g,i||e.isDefaultPrevented()||f._default&&!1!==f._default.apply(h.pop(),t)||!G(n)||l&&m(n[g])&&!y(n)&&((s=n[l])&&(n[l]=null),S.event.triggered=g,e.isPropagationStopped()&&p.addEventListener(g,Tt),n[g](),e.isPropagationStopped()&&p.removeEventListener(g,Tt),S.event.triggered=void 0,s&&(n[l]=s)),e.result}},simulate:function(e,t,n){var r=S.extend(new S.Event,n,{type:e,isSimulated:!0});S.event.trigger(r,null,t)}}),S.fn.extend({trigger:function(e,t){return this.each((function(){S.event.trigger(e,t,this)}))},triggerHandler:function(e,t){var n=this[0];if(n)return S.event.trigger(e,t,n,!0)}}),v.focusin||S.each({focus:"focusin",blur:"focusout"},(function(e,t){var n=function(e){S.event.simulate(t,e.target,S.event.fix(e))};S.event.special[t]={setup:function(){var r=this.ownerDocument||this.document||this,i=K.access(r,t);i||r.addEventListener(e,n,!0),K.access(r,t,(i||0)+1)},teardown:function(){var r=this.ownerDocument||this.document||this,i=K.access(r,t)-1;i?K.access(r,t,i):(r.removeEventListener(e,n,!0),K.remove(r,t))}}}));var Ct=r.location,St={guid:Date.now()},Et=/\?/;S.parseXML=function(e){var t,n;if(!e||"string"!=typeof e)return null;try{t=(new r.DOMParser).parseFromString(e,"text/xml")}catch(e){}return n=t&&t.getElementsByTagName("parsererror")[0],t&&!n||S.error("Invalid XML: "+(n?S.map(n.childNodes,(function(e){return e.textContent})).join("\n"):e)),t};var Nt=/\[\]$/,kt=/\r?\n/g,At=/^(?:submit|button|image|reset|file)$/i,Dt=/^(?:input|select|textarea|keygen)/i;function jt(e,t,n,r){var i;if(Array.isArray(t))S.each(t,(function(t,i){n||Nt.test(e)?r(e,i):jt(e+"["+("object"==typeof i&&null!=i?t:"")+"]",i,n,r)}));else if(n||"object"!==T(t))r(e,t);else for(i in t)jt(e+"["+i+"]",t[i],n,r)}S.param=function(e,t){var n,r=[],i=function(e,t){var n=m(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(null==e)return"";if(Array.isArray(e)||e.jquery&&!S.isPlainObject(e))S.each(e,(function(){i(this.name,this.value)}));else for(n in e)jt(n,e[n],t,i);return r.join("&")},S.fn.extend({serialize:function(){return S.param(this.serializeArray())},serializeArray:function(){return this.map((function(){var e=S.prop(this,"elements");return e?S.makeArray(e):this})).filter((function(){var e=this.type;return this.name&&!S(this).is(":disabled")&&Dt.test(this.nodeName)&&!At.test(e)&&(this.checked||!ve.test(e))})).map((function(e,t){var n=S(this).val();return null==n?null:Array.isArray(n)?S.map(n,(function(e){return{name:t.name,value:e.replace(kt,"\r\n")}})):{name:t.name,value:n.replace(kt,"\r\n")}})).get()}});var Lt=/%20/g,qt=/#.*$/,Ot=/([?&])_=[^&]*/,Ht=/^(.*?):[ \t]*([^\r\n]*)$/gm,It=/^(?:GET|HEAD)$/,Pt=/^\/\//,Mt={},Rt={},Wt="*/".concat("*"),Ft=x.createElement("a");function Bt(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(R)||[];if(m(n))for(;r=o[i++];)"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function $t(e,t,n,r){var i={},o=e===Rt;function a(s){var u;return i[s]=!0,S.each(e[s]||[],(function(e,s){var l=s(t,n,r);return"string"!=typeof l||o||i[l]?o?!(u=l):void 0:(t.dataTypes.unshift(l),a(l),!1)})),u}return a(t.dataTypes[0])||!i["*"]&&a("*")}function _t(e,t){var n,r,i=S.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&S.extend(!0,e,r),e}Ft.href=Ct.href,S.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Ct.href,type:"GET",isLocal:/^(?:about|app|app-storage|.+-extension|file|res|widget):$/.test(Ct.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Wt,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"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":S.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?_t(_t(e,S.ajaxSettings),t):_t(S.ajaxSettings,e)},ajaxPrefilter:Bt(Mt),ajaxTransport:Bt(Rt),ajax:function(e,t){"object"==typeof e&&(t=e,e=void 0),t=t||{};var n,i,o,a,s,u,l,c,f,p,d=S.ajaxSetup({},t),h=d.context||d,g=d.context&&(h.nodeType||h.jquery)?S(h):S.event,v=S.Deferred(),m=S.Callbacks("once memory"),y=d.statusCode||{},b={},w={},T="canceled",C={readyState:0,getResponseHeader:function(e){var t;if(l){if(!a)for(a={};t=Ht.exec(o);)a[t[1].toLowerCase()+" "]=(a[t[1].toLowerCase()+" "]||[]).concat(t[2]);t=a[e.toLowerCase()+" "]}return null==t?null:t.join(", ")},getAllResponseHeaders:function(){return l?o:null},setRequestHeader:function(e,t){return null==l&&(e=w[e.toLowerCase()]=w[e.toLowerCase()]||e,b[e]=t),this},overrideMimeType:function(e){return null==l&&(d.mimeType=e),this},statusCode:function(e){var t;if(e)if(l)C.always(e[C.status]);else for(t in e)y[t]=[y[t],e[t]];return this},abort:function(e){var t=e||T;return n&&n.abort(t),E(0,t),this}};if(v.promise(C),d.url=((e||d.url||Ct.href)+"").replace(Pt,Ct.protocol+"//"),d.type=t.method||t.type||d.method||d.type,d.dataTypes=(d.dataType||"*").toLowerCase().match(R)||[""],null==d.crossDomain){u=x.createElement("a");try{u.href=d.url,u.href=u.href,d.crossDomain=Ft.protocol+"//"+Ft.host!=u.protocol+"//"+u.host}catch(e){d.crossDomain=!0}}if(d.data&&d.processData&&"string"!=typeof d.data&&(d.data=S.param(d.data,d.traditional)),$t(Mt,d,t,C),l)return C;for(f in(c=S.event&&d.global)&&0==S.active++&&S.event.trigger("ajaxStart"),d.type=d.type.toUpperCase(),d.hasContent=!It.test(d.type),i=d.url.replace(qt,""),d.hasContent?d.data&&d.processData&&0===(d.contentType||"").indexOf("application/x-www-form-urlencoded")&&(d.data=d.data.replace(Lt,"+")):(p=d.url.slice(i.length),d.data&&(d.processData||"string"==typeof d.data)&&(i+=(Et.test(i)?"&":"?")+d.data,delete d.data),!1===d.cache&&(i=i.replace(Ot,"$1"),p=(Et.test(i)?"&":"?")+"_="+St.guid+++p),d.url=i+p),d.ifModified&&(S.lastModified[i]&&C.setRequestHeader("If-Modified-Since",S.lastModified[i]),S.etag[i]&&C.setRequestHeader("If-None-Match",S.etag[i])),(d.data&&d.hasContent&&!1!==d.contentType||t.contentType)&&C.setRequestHeader("Content-Type",d.contentType),C.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+("*"!==d.dataTypes[0]?", "+Wt+"; q=0.01":""):d.accepts["*"]),d.headers)C.setRequestHeader(f,d.headers[f]);if(d.beforeSend&&(!1===d.beforeSend.call(h,C,d)||l))return C.abort();if(T="abort",m.add(d.complete),C.done(d.success),C.fail(d.error),n=$t(Rt,d,t,C)){if(C.readyState=1,c&&g.trigger("ajaxSend",[C,d]),l)return C;d.async&&d.timeout>0&&(s=r.setTimeout((function(){C.abort("timeout")}),d.timeout));try{l=!1,n.send(b,E)}catch(e){if(l)throw e;E(-1,e)}}else E(-1,"No Transport");function E(e,t,a,u){var f,p,x,b,w,T=t;l||(l=!0,s&&r.clearTimeout(s),n=void 0,o=u||"",C.readyState=e>0?4:0,f=e>=200&&e<300||304===e,a&&(b=function(e,t,n){for(var r,i,o,a,s=e.contents,u=e.dataTypes;"*"===u[0];)u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}(d,C,a)),!f&&S.inArray("script",d.dataTypes)>-1&&S.inArray("json",d.dataTypes)<0&&(d.converters["text script"]=function(){}),b=function(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];for(o=c.shift();o;)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if((s=i.split(" "))[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e.throws)t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}(d,b,C,f),f?(d.ifModified&&((w=C.getResponseHeader("Last-Modified"))&&(S.lastModified[i]=w),(w=C.getResponseHeader("etag"))&&(S.etag[i]=w)),204===e||"HEAD"===d.type?T="nocontent":304===e?T="notmodified":(T=b.state,p=b.data,f=!(x=b.error))):(x=T,!e&&T||(T="error",e<0&&(e=0))),C.status=e,C.statusText=(t||T)+"",f?v.resolveWith(h,[p,T,C]):v.rejectWith(h,[C,T,x]),C.statusCode(y),y=void 0,c&&g.trigger(f?"ajaxSuccess":"ajaxError",[C,d,f?p:x]),m.fireWith(h,[C,T]),c&&(g.trigger("ajaxComplete",[C,d]),--S.active||S.event.trigger("ajaxStop")))}return C},getJSON:function(e,t,n){return S.get(e,t,n,"json")},getScript:function(e,t){return S.get(e,void 0,t,"script")}}),S.each(["get","post"],(function(e,t){S[t]=function(e,n,r,i){return m(n)&&(i=i||r,r=n,n=void 0),S.ajax(S.extend({url:e,type:t,dataType:i,data:n,success:r},S.isPlainObject(e)&&e))}})),S.ajaxPrefilter((function(e){var t;for(t in e.headers)"content-type"===t.toLowerCase()&&(e.contentType=e.headers[t]||"")})),S._evalUrl=function(e,t,n){return S.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,converters:{"text script":function(){}},dataFilter:function(e){S.globalEval(e,t,n)}})},S.fn.extend({wrapAll:function(e){var t;return this[0]&&(m(e)&&(e=e.call(this[0])),t=S(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map((function(){for(var e=this;e.firstElementChild;)e=e.firstElementChild;return e})).append(this)),this},wrapInner:function(e){return m(e)?this.each((function(t){S(this).wrapInner(e.call(this,t))})):this.each((function(){var t=S(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)}))},wrap:function(e){var t=m(e);return this.each((function(n){S(this).wrapAll(t?e.call(this,n):e)}))},unwrap:function(e){return this.parent(e).not("body").each((function(){S(this).replaceWith(this.childNodes)})),this}}),S.expr.pseudos.hidden=function(e){return!S.expr.pseudos.visible(e)},S.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},S.ajaxSettings.xhr=function(){try{return new r.XMLHttpRequest}catch(e){}};var Ut={0:200,1223:204},zt=S.ajaxSettings.xhr();v.cors=!!zt&&"withCredentials"in zt,v.ajax=zt=!!zt,S.ajaxTransport((function(e){var t,n;if(v.cors||zt&&!e.crossDomain)return{send:function(i,o){var a,s=e.xhr();if(s.open(e.type,e.url,e.async,e.username,e.password),e.xhrFields)for(a in e.xhrFields)s[a]=e.xhrFields[a];for(a in e.mimeType&&s.overrideMimeType&&s.overrideMimeType(e.mimeType),e.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest"),i)s.setRequestHeader(a,i[a]);t=function(e){return function(){t&&(t=n=s.onload=s.onerror=s.onabort=s.ontimeout=s.onreadystatechange=null,"abort"===e?s.abort():"error"===e?"number"!=typeof s.status?o(0,"error"):o(s.status,s.statusText):o(Ut[s.status]||s.status,s.statusText,"text"!==(s.responseType||"text")||"string"!=typeof s.responseText?{binary:s.response}:{text:s.responseText},s.getAllResponseHeaders()))}},s.onload=t(),n=s.onerror=s.ontimeout=t("error"),void 0!==s.onabort?s.onabort=n:s.onreadystatechange=function(){4===s.readyState&&r.setTimeout((function(){t&&n()}))},t=t("abort");try{s.send(e.hasContent&&e.data||null)}catch(e){if(t)throw e}},abort:function(){t&&t()}}})),S.ajaxPrefilter((function(e){e.crossDomain&&(e.contents.script=!1)})),S.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return S.globalEval(e),e}}}),S.ajaxPrefilter("script",(function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")})),S.ajaxTransport("script",(function(e){var t,n;if(e.crossDomain||e.scriptAttrs)return{send:function(r,i){t=S("'; 42 | 43 | return implode("\n", $html); 44 | } 45 | 46 | public static function disable(): void 47 | { 48 | app()->bind(MagicTestMiddleware::class, NullMagicTestMiddleware::class); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/MagicTestManager.php: -------------------------------------------------------------------------------- 1 | withArguments()->limit(10)->frames()); 20 | 21 | // this means it was called with the magic() macro 22 | if ($backtrace[3]->method === '__call') { 23 | $caller = $backtrace[6]; 24 | $testMethod = $caller->method; 25 | } else { 26 | $callerKey = $backtrace[1]->method === 'magic_test' ? 2 : 1; 27 | $caller = $backtrace[$callerKey]; 28 | $testMethod = $backtrace[$callerKey + 2]->method; 29 | } 30 | 31 | MagicTest::setBrowserInstance($browser); 32 | MagicTest::setTestMethod($testMethod); 33 | MagicTest::setOpenFile($caller->file); 34 | 35 | $browser->script('MagicTest.run()'); 36 | 37 | $shell = new Shell(new Configuration([ 38 | 'startupMessage' => 39 | "\n" 40 | . '*** Welcome to your 🧙 Magic Test session! ***' 41 | . "\n" 42 | . "\n* To make a assertion press Ctrl + Shift + A on your browser." 43 | . "\n* Type ok to magically write it to your test file." 44 | . "\n (make as many assertions as you wish)" 45 | . "\n* Type finish to finalize and save your test file." 46 | . "\n* Type exit to leave." 47 | . "\n" 48 | . "\n💡 Tip: Do not close your browser window before finalizing this session here.", 49 | ])); 50 | 51 | $shell->addCommands([ 52 | new Ok, 53 | new Finish, 54 | ]); 55 | $shell->run(); 56 | } 57 | 58 | public function runScripts(): string 59 | { 60 | $browser = MagicTest::$browser; 61 | 62 | $output = json_decode($browser->driver->executeScript('return MagicTest.getData()'), true); 63 | $grammar = collect($output)->map(fn ($command) => Grammar::for($command)); 64 | 65 | if (is_null($grammar) || $grammar->isEmpty()) { 66 | return "No actions were added to " . MagicTest::$file . '::' . MagicTest::$method; 67 | } 68 | 69 | $this->buildTest($grammar); 70 | 71 | $browser->script('MagicTest.clear()'); 72 | 73 | return '🧙 ' . $grammar->count() . ' new ' . Str::plural('action', $grammar->count()) . ($grammar->count() > 1 ? ' were' : ' was') . ' added to ' . MagicTest::$file . '::' . MagicTest::$method . ''; 74 | } 75 | 76 | public function finish(): string 77 | { 78 | $content = file_get_contents(MagicTest::$file); 79 | $method = MagicTest::$method; 80 | 81 | file_put_contents( 82 | MagicTest::$file, 83 | (new FileEditor)->finish($content, $method) 84 | ); 85 | 86 | return 'Your 🧙 Magic Test session has finished. See you later! 👋 '; 87 | } 88 | 89 | public function buildTest(Collection $grammar): void 90 | { 91 | $content = file_get_contents(MagicTest::$file); 92 | $method = MagicTest::$method; 93 | 94 | file_put_contents( 95 | MagicTest::$file, 96 | (new FileEditor)->process($content, $grammar, $method) 97 | ); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/MagicTestServiceProvider.php: -------------------------------------------------------------------------------- 1 | name('magic-test-laravel') 20 | ->hasCommand(MagicTestCommand::class); 21 | } 22 | 23 | public function boot() 24 | { 25 | parent::boot(); 26 | 27 | $this->app->singleton('magic-test-laravel', fn ($app) => new MagicTest); 28 | 29 | $this->app['router']->pushMiddlewareToGroup('web', MagicTestMiddleware::class); 30 | 31 | Browser::macro('magic', fn () => MagicTestManager::run($this)); 32 | Browser::macro('clickElement', function ($selector, $value) { 33 | foreach ($this->resolver->all($selector) as $element) { 34 | if (Str::contains($element->getText(), $value)) { 35 | $element->click(); 36 | 37 | return $this; 38 | } 39 | } 40 | 41 | throw new InvalidArgumentException( 42 | "Unable to locate element [${selector}] with content [{$value}]." 43 | ); 44 | }); 45 | 46 | Blade::directive('magicTestScripts', [MagicTest::class, 'scripts']); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Middleware/MagicTestMiddleware.php: -------------------------------------------------------------------------------- 1 | environment(['local', 'testing', 'dusk'])) { 23 | return $next($request); 24 | } 25 | 26 | /** @var \Illuminate\Http\Response $response */ 27 | $response = $next($request); 28 | 29 | if (! $this->responseContainsClosingHtmlTag($response)) { 30 | return $response; 31 | } 32 | 33 | return tap($response)->setContent( 34 | $this->addMagicTestScriptsToResponseContent($response->getContent()) 35 | ); 36 | } 37 | 38 | protected function responseContainsClosingHtmlTag(Response $response): bool 39 | { 40 | return mb_strpos($response->getContent(), '') !== false; 41 | } 42 | 43 | /** 44 | * @param string $responseContent 45 | * @return string 46 | */ 47 | protected function addMagicTestScriptsToResponseContent(string $responseContent): string 48 | { 49 | $scripts = MagicTest::scripts(); 50 | 51 | return Str::replaceLast( 52 | '', 53 | "{$scripts} \n ", 54 | $responseContent 55 | ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Middleware/NullMagicTestMiddleware.php: -------------------------------------------------------------------------------- 1 | lexer = new \PhpParser\Lexer\Emulative([ 40 | 'usedAttributes' => [ 41 | 'comments', 42 | 'startLine', 'endLine', 43 | 'startTokenPos', 'endTokenPos', 44 | ], 45 | ]); 46 | 47 | $this->parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7, $this->lexer); 48 | $this->ast = (array) $this->parser->parse($content)[0]; 49 | $this->initialStatements = $this->ast['stmts']; 50 | $this->newStatements = $this->getNewStatements(); 51 | $this->closure = $this->getClosure($method); 52 | } 53 | 54 | public static function fromContent(string $content, string $method) 55 | { 56 | return new static($content, $method); 57 | } 58 | 59 | public function addMethods(Collection $grammar): string 60 | { 61 | $traverser = new NodeTraverser; 62 | $traverser->addVisitor(new ParentConnectingVisitor); 63 | $traverser->addVisitor(new GrammarBuilderVisitor($grammar)); 64 | $traverser->traverse($this->closure->stmts); 65 | 66 | return $this->print(); 67 | } 68 | 69 | public function finish(): string 70 | { 71 | $traverser = new NodeTraverser; 72 | $traverser->addVisitor(new ParentConnectingVisitor); 73 | $traverser->addVisitor(new MagicRemoverVisitor); 74 | $traverser->traverse($this->closure->stmts); 75 | 76 | return $this->print(); 77 | } 78 | 79 | protected function print(): string 80 | { 81 | return (new PrettyPrinter)->printFormatPreserving( 82 | $this->newStatements, 83 | $this->initialStatements, 84 | $this->lexer->getTokens() 85 | ); 86 | } 87 | 88 | /** 89 | * Clone the statements to leave the starting ones untouched so they can be diffed by the printer later. 90 | * 91 | * @return array 92 | */ 93 | protected function getNewStatements(): array 94 | { 95 | $traverser = new NodeTraverser; 96 | $traverser->addVisitor(new CloningVisitor); 97 | 98 | return $traverser->traverse($this->initialStatements); 99 | } 100 | 101 | protected function getClassMethod(string $method): ?ClassMethod 102 | { 103 | return (new NodeFinder)->findFirst( 104 | $this->newStatements, 105 | fn (Node $node) => $node instanceof ClassMethod && $node->name->__toString() === $method 106 | ); 107 | } 108 | 109 | /** 110 | * Finds the first valid method call inside a class method. 111 | * A valid method call is one that is both a MethodCall instance and 112 | * that also has a node that is a Identifier and ahs the name magic. 113 | * 114 | * @param \PhpParser\Node\Stmt\ClassMethod $classMethod 115 | * @return \PhpParser\Node\Expr\MethodCall|null 116 | */ 117 | protected function getMethodCall(ClassMethod $classMethod): ?MethodCall 118 | { 119 | return (new NodeFinder)->findFirst($classMethod->stmts, function (Node $node) { 120 | return $node instanceof MethodCall && 121 | (new NodeFinder)->find( 122 | $node->args, 123 | fn (Node $node) => $node instanceof Identifier && $node->name === 'magic' 124 | ); 125 | }); 126 | } 127 | 128 | /** 129 | * Get the closure object 130 | * 131 | * @param string $method 132 | * @throws \MagicTest\MagicTest\Exceptions\InvalidFileException 133 | * @return Closure 134 | */ 135 | protected function getClosure(string $method): ?Closure 136 | { 137 | $classMethod = $this->getClassMethod($method); 138 | throw_if(! $classMethod, new InvalidFileException("Could not find method {$method} on file.")); 139 | 140 | $methodCall = $this->getMethodCall($classMethod); 141 | throw_if(! $methodCall, new InvalidFileException("Could not find the browse call on file.")); 142 | 143 | $closure = (new NodeFinder)->findFirst($methodCall->args, fn (Node $node) => $node->value instanceof Closure); 144 | throw_if(! $closure, new InvalidFileException("Could not find the closure on file.")); 145 | 146 | return $closure->value; 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/Parser/Printer/PrettyPrinter.php: -------------------------------------------------------------------------------- 1 | ' . $this->pObjectProperty($node->name) 16 | . '(' . $this->pMaybeMultiline($node->args) . ')', 1); 17 | 18 | return $this->pDereferenceLhs($node->var) . $this->nl . $call; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Parser/Visitors/GrammarBuilderVisitor.php: -------------------------------------------------------------------------------- 1 | grammar = $grammar; 17 | } 18 | 19 | public function leaveNode(Node $node): void 20 | { 21 | if ($node instanceof MethodCall && $node->name->__toString() === 'magic') { 22 | $this->buildNodes($node); 23 | } 24 | } 25 | 26 | /** 27 | * Builds the necessary methods with their arguments, 28 | * properly adds pauses between them when needed 29 | * and then adds the necessary nodes. 30 | * 31 | * @param \PhpParser\Node $node 32 | * @return void 33 | */ 34 | public function buildNodes($node): void 35 | { 36 | $previousMethod = $this->getPreviousMethodInChain($node); 37 | $grammar = $this->grammar 38 | ->map(function ($grammar) { 39 | return [$grammar, $grammar->pause()]; 40 | })->flatten()->filter(); 41 | 42 | 43 | // if the last item of a grammar is a pause, it is unnecessary because 44 | // right now we are in the "magic" call node, so we remove it. 45 | // That happens unless the second to last grammar is a Livewire field. 46 | // In that case we have to add an extra pause regardless. 47 | $secondToLastGrammar = $grammar->count() > 1 ? $grammar[$grammar->count() - 2] : null; 48 | if ( 49 | $grammar->last() instanceof Pause && 50 | ! $secondToLastGrammar->isLivewire() 51 | ) { 52 | $grammar->pop(); 53 | } 54 | 55 | // If the previous method was a method 56 | // that needs a pause, we prepend it. 57 | if (in_array($previousMethod->name->__toString(), [ 58 | 'clickLink', 'press', 59 | ])) { 60 | $grammar->prepend(new Pause(500)); 61 | } 62 | 63 | 64 | // I'll be honest and say I don't entirely understand the folllowing block of code, 65 | // even though I wrote it. 66 | // I'm still a bit confused as to why there are nested method call objects inside each var 67 | // (so each method call's var is another method call) instead of them being siblings. 68 | foreach ($grammar as $gram) { 69 | $previousNode = clone $node; 70 | $node->var = new MethodCall( 71 | $previousNode->var, 72 | $gram->nameForParser(), 73 | $this->buildArguments($gram->arguments()) 74 | ); 75 | } 76 | } 77 | 78 | public function buildArguments(array $arguments): array 79 | { 80 | return array_map(fn ($argument) => new Arg($argument), $arguments); 81 | } 82 | 83 | public function getPreviousMethodInChain(Node $node): Expr 84 | { 85 | $parentExpression = $node->getAttribute('parent')->expr; 86 | 87 | // now we return the first var, which *should* be the previous method. 88 | return $parentExpression->var; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/Parser/Visitors/MagicRemoverVisitor.php: -------------------------------------------------------------------------------- 1 | name->__toString() === 'magic') { 14 | $previousMethod = $this->getPreviousMethodInChain($node); 15 | 16 | // We are mutating the currenet method (magic) to have the properties 17 | // of the previous method. That way we get rid of it. Is this the 18 | // correct way? No fucking clue. But it's working. I think. 19 | $node->name = $previousMethod->name; 20 | $node->var = $previousMethod->var; 21 | $node->args = $previousMethod->args; 22 | } 23 | } 24 | 25 | public function getPreviousMethodInChain(Node $node): Expr 26 | { 27 | $parentExpression = $node->getAttribute('parent')->expr; 28 | 29 | // now we return the first var, which *should* be the previous method. 30 | return $parentExpression->var; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/PendingOk.php: -------------------------------------------------------------------------------- 1 | runScripts(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/ShellCommands/Finish.php: -------------------------------------------------------------------------------- 1 | setName('finish') 15 | ->setDefinition([]); 16 | } 17 | 18 | protected function execute(InputInterface $input, OutputInterface $output) 19 | { 20 | $actionOutput = (new MagicTestManager)->finish(); 21 | 22 | $output->write("{$actionOutput}", true); 23 | 24 | return 0; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/ShellCommands/Ok.php: -------------------------------------------------------------------------------- 1 | setName('ok') 15 | ->setDefinition([]); 16 | } 17 | 18 | protected function execute(InputInterface $input, OutputInterface $output) 19 | { 20 | $scriptOutput = (new MagicTestManager)->runScripts(); 21 | 22 | $output->write("{$scriptOutput}", true); 23 | 24 | return 0; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Support/AttributeCollection.php: -------------------------------------------------------------------------------- 1 | where('name', $name)->isNotEmpty(); 14 | } 15 | 16 | public function getAttribute(string $name): string 17 | { 18 | return $this->where('name', $name)->first()->value; 19 | } 20 | 21 | /** 22 | * The "dusk" and "name" attributes should always be the priority. 23 | * 24 | * @return self 25 | */ 26 | public function reorderItems(): self 27 | { 28 | $newCollection = new static; 29 | 30 | foreach ($this->attributeOrder as $attribute) { 31 | if ($this->hasAttribute($attribute)) { 32 | $newCollection->push( 33 | $this->where('name', $attribute)->first() 34 | ); 35 | } 36 | } 37 | 38 | return $newCollection->merge( 39 | $this->whereNotIn('name', $this->attributeOrder) 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Traits/UsesMagicTest.php: -------------------------------------------------------------------------------- 1 |