├── tests ├── server-acf.json ├── tinytest │ ├── design │ │ ├── design.png │ │ └── design2.png │ ├── Application.cfc │ ├── lib │ │ ├── TestResults.cfc │ │ ├── TestSuite.cfc │ │ ├── Error.cfc │ │ └── TestCase.cfc │ ├── assets │ │ ├── app │ │ │ ├── main.js │ │ │ ├── less │ │ │ │ └── test-suite.less │ │ │ └── css │ │ │ │ └── test-suite.css │ │ └── jquery │ │ │ └── jquery-1.9.1.min.js │ └── templates │ │ └── test-suite.cfm ├── server-lucee.json ├── Application.cfc ├── index.cfm ├── specs │ ├── TestCase.cfc │ ├── JRegExListTest.cfc │ └── JRegExTest.cfc ├── LICENSE.md └── README.md ├── LICENSE.md ├── README.md └── lib ├── JRegExList.cfc └── JRegEx.cfc /tests/server-acf.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"jregex-acf", 3 | "app":{ 4 | "cfengine":"adobe@2021" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /tests/tinytest/design/design.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bennadel/ColdFusion-JRegEx/HEAD/tests/tinytest/design/design.png -------------------------------------------------------------------------------- /tests/tinytest/design/design2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bennadel/ColdFusion-JRegEx/HEAD/tests/tinytest/design/design2.png -------------------------------------------------------------------------------- /tests/server-lucee.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"jregex-lucee", 3 | "app":{ 4 | "cfengine":"lucee" 5 | }, 6 | "jvm":{ 7 | "args":"-Dlucee.preserve.case=true" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tests/Application.cfc: -------------------------------------------------------------------------------- 1 | component 2 | extends = "tinytest.Application" 3 | output = false 4 | hint = "I provide the application settings and event handlers for your testing." 5 | { 6 | 7 | // Add any mappings that you need in order to load your modules from within 8 | // the unit test specifications. 9 | // 10 | // NOTE: You can use the evaluatePathTraversal() method to navigate upto your application, 11 | // and then down into any ColdFusion libraries you need to reference. 12 | this.mappings[ "/lib" ] = evaluatePathTraversal( "../lib/" ); 13 | 14 | 15 | } -------------------------------------------------------------------------------- /tests/index.cfm: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /tests/specs/TestCase.cfc: -------------------------------------------------------------------------------- 1 | component 2 | extends = "tinytest.lib.TestCase" 3 | output = false 4 | hint = "I provide a hook for adding shared methods to your test cases; such as custom assertion methods, or commong setup/teardown methods." 5 | { 6 | 7 | // Example of a custom assertion that you provide for your test cases. All assertions should 8 | // return void and should either run without exception; or, make one call to fail() with a 9 | // required error message. 10 | private void function assertIsValidEmail( 11 | required string email, 12 | string additionalInfo = "" 13 | ) { 14 | 15 | if ( ! isValid( "email", email ) ) { 16 | 17 | // fail() will end the test with an error. 18 | fail( "Expected [#email#] to be a valid email address.", additionalInfo ); 19 | 20 | } 21 | 22 | } 23 | 24 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | 2 | # The MIT License (MIT) 3 | 4 | Copyright (c) 2013 [Ben Nadel][1] 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | this software and associated documentation files (the "Software"), to deal in 8 | the Software without restriction, including without limitation the rights to 9 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | the Software, and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | 23 | [1]: http://www.bennadel.com -------------------------------------------------------------------------------- /tests/LICENSE.md: -------------------------------------------------------------------------------- 1 | 2 | # The MIT License (MIT) 3 | 4 | Copyright (c) 2013 [Ben Nadel][1] 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | this software and associated documentation files (the "Software"), to deal in 8 | the Software without restriction, including without limitation the rights to 9 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | the Software, and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | 23 | [1]: http://www.bennadel.com -------------------------------------------------------------------------------- /tests/tinytest/Application.cfc: -------------------------------------------------------------------------------- 1 | 2 | component 3 | output = false 4 | hint = "I define the application settings and event handlers." 5 | { 6 | 7 | 8 | // Define the application settings. 9 | this.name = ( "TinyTest:" & hash( getCurrentTemplatePath() ) ); 10 | 11 | // Keep the application timeout very low since this application isn't meant to be 12 | // persistent - it has no state and will be spun-up every time the tests run (more 13 | // or less). 14 | this.applicationTimeout = createTimeSpan( 0, 0, 5, 0 ); 15 | 16 | // Turn off session management - you don't need session management to run unit 17 | // tests since the functionality can be tested with successive assertions. 18 | this.sessionManagement = false; 19 | 20 | // Turn off client cookies - you don't need cookes to run unit tests since the 21 | // functionality can be tested with successive assertions. 22 | this.setClientCookies = false; 23 | 24 | // The root of the current tiny test directory. 25 | this.mappings[ "/tinytest" ] = getDirectoryFromPath( getCurrentTemplatePath() ); 26 | 27 | // The root of the testing application (ie. parent directory). 28 | this.mappings[ "/specs" ] = ( this.mappings[ "/tinytest" ] & "../specs/" ); 29 | 30 | 31 | // --- 32 | // PUBLIC METHODS. 33 | // --- 34 | 35 | 36 | // I execute the page request. 37 | public void function onRequest() { 38 | 39 | // Override any existing output and just execute the test suite template. 40 | include "./templates/test-suite.cfm"; 41 | 42 | } 43 | 44 | 45 | // --- 46 | // PRIVATE METHODS. 47 | // --- 48 | 49 | 50 | // I evaluate the given path traversal against the PARENT directory. This is intended 51 | // to be called from the sub-class Application.cfc. 52 | private string function evaluatePathTraversal( required string traversal ) { 53 | 54 | var currentDirectory = createObject( "java", "java.net.URI" ).init( 55 | javaCast( "string", this.mappings[ "/tinytest" ] ) 56 | ); 57 | 58 | var parentDirectory = currentDirectory.resolve( javaCast( "string", "../" ) ); 59 | 60 | var targetPath = parentDirectory.resolve( javaCast( "string", traversal ) ); 61 | 62 | return( targetPath.toString() ); 63 | 64 | } 65 | 66 | 67 | } -------------------------------------------------------------------------------- /tests/tinytest/lib/TestResults.cfc: -------------------------------------------------------------------------------- 1 | component 2 | output = false 3 | hint = "I represent the results of a test suite." 4 | { 5 | 6 | public any function init() { 7 | 8 | // For duration of testing. 9 | startTime = getTickCount(); 10 | endTime = 0; 11 | 12 | // The number of test methods that have been executed. 13 | testCount = 0; 14 | 15 | // Will contain an instance of the Error.cfc if there is an error. 16 | error = ""; 17 | 18 | return( this ); 19 | 20 | } 21 | 22 | 23 | // --- 24 | // PUBLIC METHODS. 25 | // --- 26 | 27 | 28 | // I end the tests with a successful conclusion. 29 | public void function endTestingWithSuccess() { 30 | 31 | if ( isComplete() ) { 32 | 33 | throw( type = "InvalidState", message = "Testing is already complete." ); 34 | 35 | } 36 | 37 | endTime = getTickCount(); 38 | 39 | } 40 | 41 | 42 | public void function endTestingWithError( required tinytest.lib.Error error ) { 43 | 44 | if ( isComplete() ) { 45 | 46 | throw( type = "InvalidState", message = "Testing is already complete." ); 47 | 48 | } 49 | 50 | endTime = getTickCount(); 51 | 52 | variables.error = error; 53 | 54 | } 55 | 56 | 57 | public numeric function getDuration() { 58 | 59 | if ( isComplete() ) { 60 | 61 | return( endTime - startTime ); 62 | 63 | } 64 | 65 | return( getTickCount() - startTime ); 66 | 67 | } 68 | 69 | 70 | public numeric function getTestCount() { 71 | 72 | return( testCount ); 73 | 74 | } 75 | 76 | 77 | public void function incrementTestCount() { 78 | 79 | testCount++; 80 | 81 | } 82 | 83 | 84 | public boolean function isComplete() { 85 | 86 | return( endTime != 0 ); 87 | 88 | } 89 | 90 | 91 | public any function getError() { 92 | 93 | if ( isSimpleValue( error ) ) { 94 | 95 | return; 96 | 97 | } 98 | 99 | return( error ); 100 | 101 | } 102 | 103 | 104 | public boolean function isFailed() { 105 | 106 | if ( ! isComplete() ) { 107 | 108 | throw( type = "InvalidState", message = "Testing is not complete." ); 109 | 110 | } 111 | 112 | return( ! isSimpleValue( error ) ); 113 | 114 | } 115 | 116 | 117 | public boolean function isPassed() { 118 | 119 | if ( ! isComplete() ) { 120 | 121 | throw( type = "InvalidState", message = "Testing is not complete." ); 122 | 123 | } 124 | 125 | return( !! isSimpleValue( error ) ); 126 | 127 | } 128 | 129 | 130 | // --- 131 | // PRIVATE METHODS. 132 | // --- 133 | 134 | } -------------------------------------------------------------------------------- /tests/tinytest/lib/TestSuite.cfc: -------------------------------------------------------------------------------- 1 | 2 | component 3 | output = false 4 | hint = "I run a suite of test cases." 5 | { 6 | 7 | // I initialize the component. 8 | public any function init( required string testDirectory ) { 9 | 10 | variables.testDirectory = testDirectory; 11 | 12 | results = ""; 13 | 14 | return( this ); 15 | 16 | } 17 | 18 | 19 | // --- 20 | // PUBLIC METHODS. 21 | // --- 22 | 23 | 24 | // I get the list of tests that this runner knows about (based on the directory). 25 | public array function getTestCaseNames() { 26 | 27 | var names = []; 28 | 29 | var files = directoryList( testDirectory, false, "name", "*Test.cfc" ); 30 | 31 | for ( var file in files ) { 32 | 33 | arrayAppend( names, listFirst( file, "." ) ); 34 | 35 | } 36 | 37 | return( names ); 38 | 39 | } 40 | 41 | 42 | public any function runTestCases( required string testCaseList ) { 43 | 44 | results = new TestResults(); 45 | 46 | var testCases = getTestCaseNames(); 47 | 48 | try { 49 | 50 | for ( var testCaseName in testCases ) { 51 | 52 | if ( ! listFind( testCaseList, testCaseName ) ) { 53 | 54 | continue; 55 | 56 | } 57 | 58 | runTestsInTestCase( new "specs.#testCaseName#"() ); 59 | 60 | } 61 | 62 | results.endTestingWithSuccess(); 63 | 64 | } catch ( any error ) { 65 | 66 | results.endTestingWithError( new tinytest.lib.Error( error ) ); 67 | 68 | } 69 | 70 | return( results ); 71 | 72 | } 73 | 74 | 75 | // --- 76 | // PRIVATE METHODS. 77 | // --- 78 | 79 | 80 | private array function getTestMethodNames( required any testCase ) { 81 | 82 | var methodNames = []; 83 | 84 | // structKeyArray() will make sure that only public methods are picked up. 85 | for ( var methodName in structKeyArray( testCase ) ) { 86 | 87 | if ( isTestMethodName( methodName ) ) { 88 | 89 | arrayAppend( methodNames, methodName ); 90 | 91 | } 92 | 93 | } 94 | 95 | return( methodNames ); 96 | 97 | } 98 | 99 | 100 | private boolean function isTestMethodName( required string methodName ) { 101 | 102 | // All test methods must start with the term, "test". 103 | return( !! reFindNoCase( "^test", methodName ) ); 104 | 105 | } 106 | 107 | 108 | private void function runTestMethod( 109 | required any testCase, 110 | required string methodName 111 | ) { 112 | 113 | testCase.setup(); 114 | 115 | evaluate( "testCase.#methodName#()" ); 116 | 117 | testCase.teardown(); 118 | 119 | } 120 | 121 | 122 | public void function runTestsInTestCase( required any testCase ) { 123 | 124 | testCase.beforeTests(); 125 | 126 | // When running the tests, we want to stop on failure; however, we also want to make 127 | // sure that we always run the after-tests teardown. 128 | try { 129 | 130 | var testMethods = getTestMethodNames( testCase ); 131 | 132 | for ( var methodName in testMethods ) { 133 | 134 | results.incrementTestCount(); 135 | 136 | runTestMethod( testCase, methodName ); 137 | 138 | } 139 | 140 | // Rethrow errors - we want to prevent future tests. 141 | } catch( any error ) { 142 | 143 | rethrow; 144 | 145 | // Guarantee that after tests run. 146 | } finally { 147 | 148 | // Execute after 149 | testCase.afterTests(); 150 | 151 | } 152 | 153 | } 154 | 155 | } -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Tiny Test - ColdFusion Unit Testing Framework 3 | 4 | by [Ben Nadel][1] 5 | 6 | Tiny Test is a ColdFusion unit testing framework that I built for personal use as a means 7 | to become more comfortable with the idea of unit testing and TDD (Test Drive Development). 8 | The feature is set is intended to be incredibly limited; and, it's meant to work with an 9 | HTML page that comes pre-packaged with the framework. You just drop it in, point it at 10 | the test specifications, and open it up in the browser. 11 | 12 | If you want a more full-featured unit testing framework, I would suggest looking into 13 | [MXUnit][2]; it's a robust unit testing framework that has been battle-hardened for years 14 | by the ColdFusion community. 15 | 16 | ## Railo Compatibility 17 | 18 | Railo's ColdFusion engine has some settings that require you to use explicit Arguments 19 | and Variables scope references. Since I am doubtful that I can be mindful of this, I 20 | have created a separate [Railo branch][3] branch. As features are added here, they will 21 | be cherry-picked into the Railo branch to keep the two in sync. 22 | 23 | ## Specs Directory 24 | 25 | Tiny Test will look in the "specs" directory for your test cases. It will attempt to run 26 | any ColdFusion component whose name ends with, "Test.cfc". For example, the following are 27 | all valid test case file names: 28 | 29 | * AccountTest.cfc 30 | * PrimeNumberGeneratorTest.cfc 31 | * UserServiceTest.cfc 32 | 33 | While Tiny Test will examine all of these ColdFusion components, it will only invoke the 34 | ones that you specify in the HTML web page served up by the Tiny Test framework. 35 | 36 | ## Test Cases 37 | 38 | Inside of your test cases, Tiny Test will attempt to invoke every public method that 39 | starts with, "test". For example, the following are all valid test method names: 40 | 41 | * testThatThatWorks(); 42 | * testThatThisWorks(); 43 | 44 | Within each test case can define optional methods that run before and / or after each 45 | test method: 46 | 47 | * beforeTests() 48 | * setup() 49 | * teardown() 50 | * afterTests() 51 | 52 | In these methods, you can reset the private variables of your test case to be "pristine" 53 | for each invocation of the provided test methods. 54 | 55 | Each of your test cases should extend the TestCase.cfc component that ships in the specs 56 | directory. This is your bridge into the core functionality provided by Tiny Test. 57 | 58 | * The 'beforeTests()' method executes **once** before all tests 59 | * The 'setup()' method executes before **any** test case 60 | * The 'teardown' method executes after **any** test case 61 | * The 'afterTests()' method executes **once** after all tests execute, even if they produced failures or exceptions. 62 | 63 | ## Assertions 64 | 65 | Each of your test methods will probably make some assertion based on the state of your 66 | components. Out of the box, Tiny Test provide only the most basic assertions: 67 | 68 | * assert( truthy ) 69 | * assertTrue( truthy ) 70 | * assertFalse( falsey ) 71 | * assertEquals( simpleValue, simpleValue ) 72 | * assertNotEquals( simpleValue, simpleValue ) 73 | 74 | If you want to add your own custom assertions, feel free to add them to the TestCase.cfc 75 | provided in the specs directory. Since each of your test cases extends this base 76 | component, each of your test cases will have access to the custom methods that you define 77 | within TestCase.cfc. 78 | 79 | Inside of your custom assertions, you can make use of the private method, fail(), which 80 | is how the Tiny Test tracks exceptions: 81 | 82 | * fail( errorMessage ) 83 | 84 | Hopefully you've found some of this vaguely interesting. 85 | 86 | ## JavaScript Version - Tiny Test JS 87 | 88 | I have ported the Tiny Test unit testing framework (as best as possible) over to 89 | JavaScript. I call it (no surprise) [Tiny Test JS][tinytestjs]. It is an AngularJS 90 | application that is loaded by RequireJS and then, subsequently, uses RequireJS to load 91 | all of the test case modules. 92 | 93 | 94 | [1]: http://www.bennadel.com 95 | [2]: http://mxunit.org 96 | [3]: https://github.com/bennadel/TinyTest/tree/railo 97 | [tinytestjs]: https://github.com/bennadel/TinyTestJS 98 | -------------------------------------------------------------------------------- /tests/tinytest/lib/Error.cfc: -------------------------------------------------------------------------------- 1 | component 2 | output = false 3 | hint = "I represent the error that was thrown during a TestCase execution." 4 | { 5 | 6 | public any function init( required any exception ) { 7 | 8 | errorMessage = getErrorFromException( exception ); 9 | 10 | stackTrace = getStackTraceFromException( exception ); 11 | 12 | fullStackTrace = getFullStackTraceFromException( exception ); 13 | 14 | return( this ); 15 | 16 | } 17 | 18 | 19 | // --- 20 | // PUBLIC METHODS. 21 | // --- 22 | 23 | 24 | public string function getErrorMessage() { 25 | 26 | return( errorMessage ); 27 | 28 | } 29 | 30 | 31 | public string function getFullStackTrace() { 32 | 33 | return( fullStackTrace ); 34 | 35 | } 36 | 37 | 38 | public array function getStackTrace() { 39 | 40 | return( stackTrace ); 41 | 42 | } 43 | 44 | 45 | // --- 46 | // PRIVATE METHODS. 47 | // --- 48 | 49 | 50 | private string function getErrorFromException( required any exception ) { 51 | 52 | if ( 53 | ! structKeyExists( exception, "message" ) || 54 | ! len( exception.message ) 55 | ) { 56 | 57 | return( "An unexpected error has occurred." ); 58 | 59 | } 60 | 61 | return( exception.message ); 62 | 63 | } 64 | 65 | 66 | private string function getFullStackTraceFromException( required any exception ) { 67 | 68 | if ( ! structKeyExists( exception, "stackTrace" ) ) { 69 | 70 | return( "" ); 71 | 72 | } 73 | 74 | return( exception.stackTrace ); 75 | 76 | } 77 | 78 | 79 | private string function getMethodNameFromContext( 80 | required string rawTrace, 81 | required string filepath 82 | ) { 83 | 84 | var methodNameCallout = reMatch( "\$func[^.]+", rawTrace ); 85 | 86 | // If no method was mentioned in the raw stack trace, return the empty string. 87 | if ( ! arrayLen( methodNameCallout ) ) { 88 | 89 | return( "" ); 90 | 91 | } 92 | 93 | // Remove the non-method-name portions of the method name. 94 | var methodName = lcase( replace( methodNameCallout[ 1 ], "$func", "", "one" ) ); 95 | 96 | // At this point, the method name will be all-caps since ColdFusion reports it without 97 | // case-sensitivity. To get around this, let's see if we can get the proper casing of 98 | // the method name directly from the file that contains the method definition. 99 | try { 100 | 101 | var fileContent = fileRead( filepath ); 102 | 103 | var escapedMethodName = reReplace( methodName, "([\\$])", "\\\1", "all" ); 104 | 105 | // Look for both the script-based and tag-based definitions. 106 | var methodDefinitions = reMatch( "(?i)\b#escapedMethodName#\s*\(|""#escapedMethodName#""", fileContent ); 107 | 108 | // If we found the definition, return the true-cased name, less the other method 109 | // definition markers we needed for the regex match. 110 | if ( arrayLen( methodDefinitions ) ) { 111 | 112 | return( 113 | reReplace( methodDefinitions[ 1 ], "[\s(""]+", "", "all" ) 114 | ); 115 | 116 | } 117 | 118 | // Ingore any file I/O errors. 119 | } catch ( any fileIOError ) { 120 | 121 | // ... 122 | 123 | } 124 | 125 | // If we couldn't find the true-cased method name, just return the fake one. 126 | return( methodName ); 127 | 128 | } 129 | 130 | 131 | private array function getStackTraceFromException( required any exception ) { 132 | 133 | if ( ! structKeyExists( exception, "tagContext" ) ) { 134 | 135 | return( arrayNew( 1 ) ); 136 | 137 | } 138 | 139 | var simplifiedContext = []; 140 | 141 | for ( var tagContext in exception.tagContext ) { 142 | 143 | // For generated templates, this will not be a file path. This is caused by the 144 | // use of the evaluate() method to invoke a compoonent method. 145 | if ( isPhysicalTemplatePath( tagContext.template ) ) { 146 | 147 | arrayAppend( 148 | simplifiedContext, 149 | { 150 | fileName = getFileFromPath( tagContext.template ), 151 | filePath = tagContext.template, 152 | lineNumber = tagContext.line, 153 | methodName = getMethodNameFromContext( tagContext.raw_trace, tagContext.template ) 154 | } 155 | ); 156 | 157 | } 158 | 159 | } 160 | 161 | return( simplifiedContext ); 162 | 163 | } 164 | 165 | 166 | private boolean function isPhysicalTemplatePath( required string templatePath ) { 167 | 168 | return( templatePath != "" ); 169 | 170 | } 171 | 172 | } -------------------------------------------------------------------------------- /tests/tinytest/lib/TestCase.cfc: -------------------------------------------------------------------------------- 1 | component 2 | output = false 3 | hint = "I provide the base class for unit test cases." 4 | { 5 | 6 | /** 7 | * @hint I initialize the component. 8 | * @output false 9 | */ 10 | public any function init() { 11 | 12 | return( this ); 13 | 14 | } 15 | 16 | 17 | // --- 18 | // PUBLIC METHODS. 19 | // --- 20 | 21 | 22 | /** 23 | * @hint I get called once after all the test methods have executed. 24 | * @output false 25 | */ 26 | public void function afterTests() { 27 | 28 | // Abstract method... 29 | 30 | } 31 | 32 | 33 | /** 34 | * @hint I get called once before any tests methods have executed. 35 | * @output false 36 | */ 37 | public void function beforeTests() { 38 | 39 | // Abstract method... 40 | 41 | } 42 | 43 | 44 | /** 45 | * @hint I get called before every test method is executed. 46 | * @output false 47 | */ 48 | public void function setup() { 49 | 50 | // Abstract method... 51 | 52 | } 53 | 54 | 55 | /** 56 | * @hint I get called after every test method has executed. 57 | * @output false 58 | */ 59 | public void function teardown() { 60 | 61 | // Abstract method... 62 | 63 | } 64 | 65 | 66 | // --- 67 | // PRIVATE METHODS. 68 | // --- 69 | 70 | 71 | /** 72 | * @hint I am a short-hand for the assertTrue() method. 73 | * @output false 74 | * @value A True or False value that will be passed on to the assertTrue method. 75 | * @additionalInfo If provided, this value will be output along with the failure message (if failure occurs). 76 | */ 77 | private void function assert( 78 | required boolean value, 79 | string additionalInfo = "" 80 | ) { 81 | 82 | assertTrue( value, additionalInfo ); 83 | 84 | } 85 | 86 | 87 | /** 88 | * @hint I test to see if the supplied values are equal or not. 89 | * @output false 90 | * @valueA The first value to be compared. 91 | * @valueB The value to compare against valueA. 92 | * @additionalInfo If provided, this value will be output along with the failure message (if failure occurs). 93 | */ 94 | private void function assertEquals( 95 | required string valueA, 96 | required string valueB, 97 | string additionalInfo = "" 98 | ) { 99 | 100 | if ( valueA != valueB ) { 101 | 102 | fail( "Expected [#valueA#] to equal [#valueB#].", additionalInfo ); 103 | 104 | } 105 | 106 | } 107 | 108 | 109 | /** 110 | * @hint I test to see if the supplied value is equal to False. 111 | * @output false 112 | * @value A True or False value to test. 113 | * @additionalInfo If provided, this value will be output along with the failure message (if failure occurs). 114 | */ 115 | private void function assertFalse( 116 | required boolean value, 117 | string additionalInfo = "" 118 | ) { 119 | 120 | if ( value ) { 121 | 122 | fail( "Expected [#value#] to be falsey.", additionalInfo ); 123 | 124 | } 125 | 126 | } 127 | 128 | 129 | /** 130 | * @hint I test to see if the supplied values are equal or not. 131 | * @output false 132 | * @valueA The first value to be compared. 133 | * @valueB The value to compare against valueA. 134 | * @additionalInfo If provided, this value will be output along with the failure message (if failure occurs). 135 | */ 136 | private void function assertNotEquals( 137 | required string valueA, 138 | required string valueB, 139 | string additionalInfo = "" 140 | ) { 141 | 142 | if ( valueA == valueB ) { 143 | 144 | fail( "Expected [#valueA#] to not equal [#valueB#].", additionalInfo ); 145 | 146 | } 147 | 148 | } 149 | 150 | 151 | /** 152 | * @hint I test to see if the supplied value is equal to True. 153 | * @output false 154 | * @value A True or False value to test. 155 | * @additionalInfo If provided, this value will be output along with the failure message (if failure occurs). 156 | */ 157 | private void function assertTrue( 158 | required boolean value, 159 | string additionalInfo = "" 160 | ) { 161 | 162 | if ( ! value ) { 163 | 164 | fail( "Expected [#value#] to be truthy.", additionalInfo ); 165 | } 166 | 167 | } 168 | 169 | 170 | /** 171 | * @hint I send a failure message back to the calling application. 172 | * @output false 173 | * @message The message provided from the assert method. 174 | * @additionalInfo If provided, this value will be output along with the failure message (if failure occurs). 175 | */ 176 | private void function fail( 177 | required string message, 178 | string additionalInfo = "" 179 | ) { 180 | 181 | if ( len( additionalInfo ) ) { 182 | 183 | message &= ( " " & additionalInfo ); 184 | 185 | } 186 | 187 | throw( type = "tinytest.AssertionFailed", message = message ); 188 | 189 | } 190 | 191 | } 192 | -------------------------------------------------------------------------------- /tests/tinytest/assets/app/main.js: -------------------------------------------------------------------------------- 1 | (function( $ ) { 2 | 3 | "use strict"; 4 | 5 | 6 | function hideAllTestCases() { 7 | 8 | dom.testCases.children().hide(); 9 | 10 | } 11 | 12 | 13 | function getSelectedTestCases() { 14 | 15 | return( 16 | dom.testCases.children().has( "input:checked" ) 17 | ); 18 | 19 | } 20 | 21 | 22 | function initializeSearchValues() { 23 | 24 | dom.testCases.children().attr( 25 | "data-search-value", 26 | function( index ) { 27 | 28 | var textValue = $( this ).text(); 29 | 30 | return( $.trim( textValue ).toLowerCase() ); 31 | 32 | } 33 | ); 34 | 35 | } 36 | 37 | 38 | function isRefreshEvent( event ) { 39 | 40 | return( ( event.which === 114 ) && event.metaKey ); 41 | 42 | } 43 | 44 | 45 | function isEnterKeyEvent( event ) { 46 | 47 | return( event.which === 13 ); 48 | 49 | } 50 | 51 | 52 | function searchTestCases( value ) { 53 | 54 | value = value.toLowerCase(); 55 | 56 | var matchingTestCases = dom.testCases.children().filter( 57 | function() { 58 | 59 | return( $( this ).attr( "data-search-value" ).indexOf( value ) >= 0 ); 60 | 61 | } 62 | ); 63 | 64 | return( matchingTestCases ); 65 | 66 | } 67 | 68 | 69 | function selectAllTestsCases() { 70 | 71 | dom.filter.val( "" ); 72 | 73 | showAllTestCases(); 74 | 75 | selectVisibleTestCases(); 76 | 77 | } 78 | 79 | 80 | function selectVisibleTestCases() { 81 | 82 | dom.testCases 83 | .children( ":visible" ) 84 | .find( "input" ) 85 | .prop( "checked", true ) 86 | ; 87 | 88 | } 89 | 90 | 91 | function showAllTestCases() { 92 | 93 | dom.testCases.children().show(); 94 | 95 | } 96 | 97 | 98 | function showMatchingTestsCases( value ) { 99 | 100 | if ( value ) { 101 | 102 | hideAllTestCases(); 103 | 104 | searchTestCases( value ) 105 | .show() 106 | ; 107 | 108 | } else { 109 | 110 | showAllTestCases(); 111 | 112 | } 113 | 114 | } 115 | 116 | 117 | function showProcessingOverlay( testCaseCount ) { 118 | 119 | dom.processingCount.text( testCaseCount ); 120 | 121 | dom.processingOverlay 122 | .delay( 500 ) 123 | .fadeIn( 200 ) 124 | ; 125 | 126 | } 127 | 128 | 129 | // Cache DOM node references. 130 | var dom = { 131 | form: $( "form" ), 132 | testStatus: $( "form input[ name = 'testStatus' ]"), 133 | filter: $( "input.filter" ), 134 | selectAll: $( "span.selectAll a" ), 135 | testCases: $( "div.testList ol.tests" ), 136 | autorun: $( "label.autorun input" ), 137 | processingOverlay: $( "div.processingOverlay" ), 138 | processingCount: $( "div.processingOverlay span.count" ), 139 | window: $( window ) 140 | }; 141 | 142 | 143 | initializeSearchValues(); 144 | 145 | 146 | dom.selectAll.click( 147 | function( event ) { 148 | 149 | event.preventDefault(); 150 | 151 | selectAllTestsCases(); 152 | 153 | } 154 | ); 155 | 156 | 157 | // Listen for the keypress event which will give us access to the Enter key. 158 | dom.filter.keypress( 159 | function( event ) { 160 | 161 | if ( isEnterKeyEvent( event ) ) { 162 | 163 | selectVisibleTestCases(); 164 | 165 | // NOTE: We are not cancelling the event since we want it to trigger a form submission. 166 | 167 | } 168 | 169 | } 170 | ); 171 | 172 | 173 | // Listen to filter changes so we can update the list of test cases. 174 | dom.filter.keyup( 175 | function( event ) { 176 | 177 | showMatchingTestsCases( this.value ); 178 | 179 | } 180 | ); 181 | 182 | 183 | dom.autorun.click( 184 | function( event ) { 185 | 186 | var label = dom.autorun.closest( "label" ); 187 | 188 | if ( this.checked ) { 189 | 190 | label.addClass( "on" ); 191 | 192 | } else { 193 | 194 | label.removeClass( "on" ); 195 | 196 | } 197 | 198 | } 199 | ); 200 | 201 | 202 | // We want to prevent double-submission, so we have to track the active submission. 203 | var isFormSubmitting = false; 204 | 205 | dom.form.submit( 206 | function( event ) { 207 | 208 | // If the form is already submitting, completely cancel the event. 209 | if ( isFormSubmitting ) { 210 | 211 | return( false ); 212 | 213 | } 214 | 215 | var selectedTestCases = getSelectedTestCases(); 216 | 217 | if ( selectedTestCases.length ) { 218 | 219 | showProcessingOverlay( selectedTestCases.length ); 220 | 221 | isFormSubmitting = true; 222 | 223 | } else { 224 | 225 | event.preventDefault(); 226 | 227 | } 228 | 229 | } 230 | ); 231 | 232 | 233 | dom.window.keypress( 234 | function( event ) { 235 | 236 | // We don't care about keypresses while on the start screen. 237 | if ( dom.testStatus.val() === "start" ) { 238 | 239 | return; 240 | 241 | } 242 | 243 | if ( isRefreshEvent( event ) ) { 244 | 245 | // This will prevent the browser from asking if the form data should be 246 | // resubmitted (for security reasons). Since we just want to resubmit the 247 | // data, this will remove that friction. 248 | event.preventDefault(); 249 | 250 | dom.form.submit(); 251 | 252 | } 253 | 254 | } 255 | ); 256 | 257 | 258 | // When the window refreshes, it will "gain focus" on load. As such, we want to ignore the 259 | // first focus event or we will get stuck in a crazy form-submission loop. 260 | var isFirstFocusEvent = true; 261 | 262 | dom.window.focus( 263 | function( event ) { 264 | 265 | // We only care about re-running the tests if the form has already been submitted. 266 | if ( dom.testStatus.val() === "start" ) { 267 | 268 | return; 269 | 270 | } 271 | 272 | if ( isFirstFocusEvent ) { 273 | 274 | isFirstFocusEvent = false; 275 | return; 276 | 277 | } 278 | 279 | if ( dom.autorun.is( ":checked" ) ) { 280 | 281 | dom.form.submit(); 282 | 283 | } 284 | 285 | } 286 | ); 287 | 288 | 289 | })( jQuery ); -------------------------------------------------------------------------------- /tests/specs/JRegExListTest.cfc: -------------------------------------------------------------------------------- 1 | component 2 | extends = "TestCase" 3 | output = false 4 | hint = "I test JRegExList." 5 | { 6 | 7 | public void function test_jreListFirst() { 8 | 9 | var jreList = new lib.JRegExList(); 10 | 11 | assert( jreList.jreListFirst( "", "-" ) == "" ); 12 | assert( jreList.jreListFirst( "-------", "-" ) == "" ); 13 | assert( jreList.jreListFirst( "-------", "-", true ) == "" ); 14 | assert( jreList.jreListFirst( "--abc--", "-" ) == "abc" ); 15 | assert( jreList.jreListFirst( "--abc--", "-+" ) == "abc" ); 16 | assert( jreList.jreListFirst( "--abc--", "-", true ) == "" ); 17 | assert( jreList.jreListFirst( "--abc--", "-+", true ) == "" ); 18 | assert( jreList.jreListFirst( "abc----", "-" ) == "abc" ); 19 | assert( jreList.jreListFirst( "abc----", "-", true ) == "abc" ); 20 | 21 | } 22 | 23 | 24 | public void function test_jreListGetAt() { 25 | 26 | var jreList = new lib.JRegExList(); 27 | 28 | assert( jreList.jreListGetAt( "", 1, "-" ) == "" ); 29 | assert( jreList.jreListGetAt( "", 2, "-" ) == "" ); 30 | assert( jreList.jreListGetAt( "", 1, "-", true ) == "" ); 31 | assert( jreList.jreListGetAt( "", 2, "-", true ) == "" ); 32 | assert( jreList.jreListGetAt( "a", 1, "-" ) == "a" ); 33 | assert( jreList.jreListGetAt( "a", 2, "-" ) == "" ); 34 | assert( jreList.jreListGetAt( "---", 1, "-" ) == "" ); 35 | assert( jreList.jreListGetAt( "---", 1, "-", true ) == "" ); 36 | assert( jreList.jreListGetAt( "-a-", 1, "-" ) == "a" ); 37 | assert( jreList.jreListGetAt( "-a-", 1, "-", true ) == "" ); 38 | assert( jreList.jreListGetAt( "---a---", 1, "-+" ) == "a" ); 39 | assert( jreList.jreListGetAt( "---a---", 1, "-+", true ) == "" ); 40 | 41 | } 42 | 43 | 44 | public void function test_jreListLast() { 45 | 46 | var jreList = new lib.JRegExList(); 47 | 48 | assert( jreList.jreListLast( "", "-" ) == "" ); 49 | assert( jreList.jreListLast( "-", "-" ) == "" ); 50 | assert( jreList.jreListLast( "-", "-", true ) == "" ); 51 | assert( jreList.jreListLast( "a-", "-" ) == "a" ); 52 | assert( jreList.jreListLast( "a-", "-", true ) == "" ); 53 | assert( jreList.jreListLast( "a-b-c", "-" ) == "c" ); 54 | 55 | } 56 | 57 | 58 | public void function test_jreListLen() { 59 | 60 | var jreList = new lib.JRegExList(); 61 | 62 | assert( jreList.jreListLen( "", "-" ) == 0 ); 63 | assert( jreList.jreListLen( "", "-", true ) == 1 ); 64 | assert( jreList.jreListLen( "-", "-" ) == 0 ); 65 | assert( jreList.jreListLen( "-", "-", true ) == 2 ); 66 | assert( jreList.jreListLen( "--abc--", "-" ) == 1 ); 67 | assert( jreList.jreListLen( "--abc--", "-", true ) == 5 ); 68 | assert( jreList.jreListLen( "--abc--", "-+", true ) == 3 ); 69 | assert( jreList.jreListLen( "-a--b--c-", "-" ) == 3 ); 70 | assert( jreList.jreListLen( "-a--b--c-", "-", true ) == 7 ); 71 | assert( jreList.jreListLen( "-a--b--c-", "-+", true ) == 5 ); 72 | 73 | } 74 | 75 | 76 | public void function test_jreListMap() { 77 | 78 | var jreList = new lib.JRegExList(); 79 | 80 | var op = ( item ) => { 81 | return( item.len() ); 82 | }; 83 | 84 | assert( jreList.jreListMap( "", op, "-" ) == "" ); 85 | assert( jreList.jreListMap( "", op, "-", true ) == "0" ); 86 | assert( jreList.jreListMap( "-a--bb--ccc-", op, "-+" ) == "-1--2--3-" ); 87 | assert( jreList.jreListMap( "-a--bb--ccc-", op, "-+", true ) == "0-1--2--3-0" ); 88 | assert( jreList.jreListMap( "-a--bb--ccc-", op, "-", true ) == "0-1-0-2-0-3-0" ); 89 | 90 | } 91 | 92 | 93 | public void function test_jreListRest() { 94 | 95 | var jreList = new lib.JRegExList(); 96 | 97 | assert( jreList.jreListRest( "", "-" ) == "" ); 98 | assert( jreList.jreListRest( "-------", "-" ) == "" ); 99 | assert( jreList.jreListRest( "-------", "-", true ) == "------" ); 100 | assert( jreList.jreListRest( "-------", "-+", true ) == "" ); 101 | assert( jreList.jreListRest( "--abc--", "-" ) == "" ); 102 | assert( jreList.jreListRest( "--abc--", "-", true ) == "-abc--" ); 103 | assert( jreList.jreListRest( "--abc--", "-+", true ) == "abc--" ); 104 | assert( jreList.jreListRest( "abc----", "-" ) == "" ); 105 | assert( jreList.jreListRest( "abc----", "-", true ) == "---" ); 106 | assert( jreList.jreListRest( "----abc", "-" ) == "" ); 107 | assert( jreList.jreListRest( "----abc", "-", true ) == "---abc" ); 108 | assert( jreList.jreListRest( "--abc--def--", "-" ) == "def--" ); 109 | assert( jreList.jreListRest( "--abc--def--", "-", true ) == "-abc--def--" ); 110 | assert( jreList.jreListRest( "--abc--def--", "-+", true ) == "abc--def--" ); 111 | assert( jreList.jreListRest( "abc", "-", true ) == "" ); 112 | 113 | } 114 | 115 | 116 | public void function test_jreListSetAt() { 117 | 118 | var jreList = new lib.JRegExList(); 119 | 120 | assert( jreList.jreListSetAt( "", 1, "a", "-" ) == "" ); 121 | assert( jreList.jreListSetAt( "", 1, "a", "-", true ) == "a" ); 122 | assert( jreList.jreListSetAt( "", 2, "a", "-", true ) == "" ); 123 | assert( jreList.jreListSetAt( "--", 1, "a", "-" ) == "--" ); 124 | assert( jreList.jreListSetAt( "--", 1, "a", "-", true ) == "a--" ); 125 | assert( jreList.jreListSetAt( "-a-", 1, "b", "-" ) == "-b-" ); 126 | assert( jreList.jreListSetAt( "-a-", 1, "b", "-", true ) == "b-a-" ); 127 | assert( jreList.jreListSetAt( "-a-", 10, "b", "-", true ) == "-a-" ); 128 | 129 | } 130 | 131 | 132 | public void function test_jreListToArray() { 133 | 134 | var jreList = new lib.JRegExList(); 135 | 136 | assert( serializeJson( jreList.jreListToArray( "--", "-" ) ) == '[]' ); 137 | assert( serializeJson( jreList.jreListToArray( "--", "-+", true ) ) == '["",""]' ); 138 | assert( serializeJson( jreList.jreListToArray( "-a-", "-" ) ) == '["a"]' ); 139 | assert( serializeJson( jreList.jreListToArray( "-a-", "-", true ) ) == '["","a",""]' ); 140 | assert( serializeJson( jreList.jreListToArray( "-a-----b-", "-" ) ) == '["a","b"]' ); 141 | assert( serializeJson( jreList.jreListToArray( "-a-----b-", "-", true ) ) == '["","a","","","","","b",""]' ); 142 | 143 | } 144 | 145 | } 146 | -------------------------------------------------------------------------------- /tests/tinytest/templates/test-suite.cfm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | // Param the form fields. 6 | param name="form.submitted" type="boolean" default=false; 7 | param name="form.selectedTestCases" type="string" default=""; 8 | param name="form.autorun" type="string" default="off"; 9 | 10 | 11 | // NOTE: Since this template is included INTO the Application.cfc, we have access to the "this" scope; 12 | // and, therefore, have direct access to the mappings collection. 13 | testSuite = new tinytest.lib.TestSuite( this.mappings[ "/specs" ] ); 14 | 15 | testCaseNames = testSuite.getTestCaseNames(); 16 | 17 | testStatus = "start"; 18 | 19 | 20 | // Only run the tests if at least one test was selected. 21 | if ( form.submitted && len( form.selectedTestCases ) ) { 22 | 23 | testResults = testSuite.runTestCases( form.selectedTestCases ); 24 | 25 | testStatus = ( testResults.isPassed() ? "pass" : "fail" ); 26 | 27 | } 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | Tiny Test : Test-Driven Development 49 | 50 | 51 | 52 | 53 | 54 | 55 |
56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 |
66 | 67 | 68 | TinyTest 69 | 70 | 71 | 72 | Red - Green - Refactor 73 | 74 | 75 | 76 | by Ben Nadel 77 | 78 | 79 |
80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 |
89 | 90 | 105 | 106 |
107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 |
115 | 116 | 131 | 132 |
133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 |
141 | 142 | 196 | 197 |
198 | 199 | 200 | 201 |
202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 |
211 | 212 |
213 | 214 |
215 | You Have #arrayLen( testCaseNames )# Test Cases 216 | ( Select All ) 217 |
218 | 219 | 220 | 221 |
222 | 223 |
    224 | 225 | 228 | 229 |
  1. 230 | 231 | 245 | 246 |
  2. 247 | 248 |
    249 | 250 |
251 | 252 |
253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 |
262 | 263 | Oops: There are no test cases in your "specs" directory.
264 | 265 | See the Readme.md file for instructions. 266 | 267 |
268 | 269 | 270 | 271 |
272 | 273 | 274 | 275 | 285 | 286 | 287 | 288 |
289 | 290 | 291 | 292 | 293 |
294 | 295 |
296 | 297 |
298 | Running 0 Test Cases 299 |
300 | 301 |
302 | Get ready to refactor... 303 |
304 | 305 |
306 | 307 |
308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 |
-------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # JRegEx - A ColdFusion Wrapper Around Java's Regular Expression Patterns 3 | 4 | by [Ben Nadel][bennadel] 5 | 6 | This is a ColdFusion wrapper around Java's Regular Expression pattern matching and 7 | replacing functionality. The native ColdFusion POSIX Regular Expression support is 8 | slow and somewhat limited. By using Java's Patterns, we can create faster, more robust 9 | pattern-matching solutions. 10 | 11 | Since ColdFusion uses 1-based offsets and Java uses 0-based offsets, any method that 12 | returns an Offset will be adjusted to use ColdFusion's 1-based offsets. 13 | 14 | There are no `*NoCase()` versions of these methods. If you want to run a case-insensitive 15 | pattern match, simply prepend the case-insensitivity flag `(?i)` to your Regular 16 | Expression pattern. 17 | 18 | ## `JRegEx.cfc` 19 | 20 | This ColdFusion component provides generic Regular Expression support. 21 | 22 | ### `jreAfter( targetText, patternText )` :: String 23 | 24 | I return the trailing portion of the string starting after the first match of the given pattern. If the pattern cannot be matched, the empty string is returned. 25 | 26 | ### `jreAfterLast( targetText, patternText )` :: String 27 | 28 | I return the trailing portion of the string starting after the last match of the given pattern. If the pattern cannot be matched, the empty string is returned. 29 | 30 | ### `jreBefore( targetText, patternText )` :: String 31 | 32 | I return the leading portion of the string up until first match of the given pattern. If the pattern cannot be matched, the entire string is returned. 33 | 34 | ### `jreBeforeLast( targetText, patternText )` :: String 35 | 36 | I return the leading portion of the string up until last match of the given pattern. If the pattern cannot be matched, the entire string is returned. 37 | 38 | ### `jreEndingWith( targetText, patternText )` :: String 39 | 40 | I return the leading portion of the string ending with the first match of the given pattern. If the pattern cannot be matched, the empty string is returned. 41 | 42 | ### `jreEndsWith( targetText, patternText )` :: Boolean 43 | 44 | I determine if the given pattern can be matched at the end of the input. 45 | 46 | ### `jreEscape( patternText )` :: string 47 | 48 | This takes a Java Regular Expression pattern and returns a literal version of it. This 49 | literal version can then be used in other JRegEx methods. This is essentially what the 50 | `quoteReplacement` argument is doing in some of the other methods. 51 | 52 | ### `jreExtract( targetText, patternText )` :: Struct 53 | 54 | This takes the given Java Regular Expression pattern and returns a struct that holds all 55 | of the captured groups in the first match within the target text. If no match can be 56 | found, an empty struct is returned. 57 | 58 | ### `jreFind( targetText, patternText [, startingAt = 1 ] )` :: number 59 | 60 | This finds the offset of the first Java Regular Expression pattern match in the given 61 | target text. Returns zero if no match is found. 62 | 63 | ### `jreForEach( targetText, patternText, operator )` :: void 64 | 65 | This iterates over each match of the given Java Regular Expression pattern found within 66 | the given target text. Each match and its captured groups are passed to the operator 67 | function. All operator function return values are ignored. 68 | 69 | ```cfc 70 | // Count the number of matched patterns. 71 | var count = 0; 72 | jre.jreForEach( 73 | "hello there", 74 | "\b\w+\b", 75 | function( $0, start, targetText ) { 76 | 77 | count++; 78 | 79 | } 80 | ); 81 | ``` 82 | 83 | ### `jreMap( targetText, patternText, operator )` :: array[any] 84 | 85 | This iterates over each match of the given Java Regular Expression pattern found within 86 | the given target text. Each match and its captured groups are passed to the operator 87 | function which can return a mapped value. The mapped values are then aggregated and 88 | returned in an array. If the operator returns an undefined value, the match is omitted 89 | from the results. 90 | 91 | ```cfc 92 | // Capture each word and return it with an upper-case first letter. 93 | var results = jre.jreMap( 94 | "hello there", 95 | "(\w)(\w*)", 96 | function( $0, head, tail, start, targetText ) { 97 | 98 | return( ucase( head ) & tail ); 99 | 100 | } 101 | ); 102 | ``` 103 | 104 | ### `jreMatch( targetText, patternText )` :: array[string] 105 | 106 | This takes the given Java Regular Expression pattern and collects all matches of it 107 | that can be found within the given target text. That matches are returned as an array 108 | of strings. 109 | 110 | ### `jreMatchGroups( targetText, patternText )` :: array[struct] 111 | 112 | This takes the given Java Regular Expression pattern and collects all matches of it 113 | that can be found within the given target text. That matches are returned as an array 114 | of structs in which each struct holds the captured groups of the match. The struct is 115 | keyed based on the index of the captured group, within the pattern, with the `0` key 116 | containing the entire match text. 117 | 118 | ### `jreMatchFirst( targetText, patternText )` :: string 119 | 120 | This takes the given Java Regular Expression pattern and returns the first match of it that can be found within the given target text. If no match is found, the empty string is returned. 121 | 122 | ### `jreReplace( targetText, patternText, operator )` :: string 123 | 124 | This iterates over each match of the given Java Regular Expression pattern found within 125 | the given target text and passes the match to the operator function / closure. The 126 | operator function / closure can then return a replacement value that will be merged into 127 | the resultant text. This is based on JavaScript's String.prototype.replace() method. 128 | 129 | ```cfc 130 | // Replace each first letter with an upper-case letter. 131 | var result = jre.jreReplace( 132 | "hello there", 133 | "\b[a-z]", 134 | function( $0, start, targetText ) { 135 | 136 | return( ucase( $0 ) ); 137 | 138 | } 139 | ); 140 | ``` 141 | 142 | ### `jreReplaceAll( targetText, patternText, replacementText [, quoteReplacement = false ] )` :: string 143 | 144 | I replace all matches of the given Java Regular Expression pattern found within the given 145 | target text with the given replacement text. The replacement text can contain `$N` 146 | references to captured groups within the pattern. 147 | 148 | ### `jreReplaceFirst( targetText, patternText, replacementText [, quoteReplacement = false ] )` :: string 149 | 150 | I replace the first match of the given Java Regular Expression pattern found within the 151 | given target text with the given replacement text. The replacement text can contain `$N` 152 | references to captured groups within the pattern. 153 | 154 | ### `jreSegment( targetText, patternText )` :: array[struct] 155 | 156 | I use the given Java Regular Expression pattern to break the given target text up into 157 | segments. Unlike the `jreSplit()` method, this method returns both the pattern matches as 158 | well as the text in between the matches. The resultant array contains a heterogeneous mix 159 | of match and non-match structs. Each struct contains the following properties: 160 | 161 | * `Match`: Boolean 162 | * `Offset`: Numeric 163 | * `Text`: String 164 | 165 | Match structs also contain the property: 166 | 167 | * `Groups`: Struct[Numeric] :: String 168 | 169 | ... which contains the captured groups of the match. 170 | 171 | ### `jreSplit( targetText, patternText [, limit = 0 ] )` :: array[string] 172 | 173 | I use the given Java Regular Expression pattern to split the given target text. The 174 | resultant portions of the target text are returned as an array of strings. 175 | 176 | ### `jreStartingWith( targetText, patternText )` :: String 177 | 178 | I return the trailing portion of the string starting with the first match of the given pattern. If the pattern cannot be matched, the empty string is returned. 179 | 180 | ### `jreStartsWith( targetText, patternText )` :: Boolean 181 | 182 | I determine if the given pattern can be matched at the start of the input. 183 | 184 | ### `jreTest( targetText, patternText )` :: boolean 185 | 186 | I test to see if the given Java Regular Expression pattern matches the entire target 187 | text. You can think of this as wrapping your pattern with `^` and `$` boundary sequences. 188 | 189 | 190 | ## `JRegExList.cfc` 191 | 192 | This ColdFusion component provides list parsing functions in which the list delimiter is provided as a Regular Expression. 193 | 194 | * `jreListFirst( list, delimiterPattern [, includeEmptyFields] )` :: String 195 | * `jreListGetAt( list, position, delimiterPattern [, includeEmptyFields] )` :: String 196 | * `jreListLast( list, delimiterPattern [, includeEmptyFields] )` :: String 197 | * `jreListLen( list, delimiterPattern [, includeEmptyFields] )` :: Number 198 | * `jreListMap( list, callback, delimiterPattern [, includeEmptyFields] )` :: String 199 | * `jreListRest( list, delimiterPattern [, includeEmptyFields] )` :: String 200 | * `jreListSetAt( list, position, value, delimiterPattern [, includeEmptyFields] )` :: String 201 | * `jreListToArray( list, delimiterPattern [, includeEmptyFields] )` :: Array 202 | * `jreSegment( list, delimiterPattern )` :: Array 203 | 204 | 205 | [bennadel]: https://www.bennadel.com/ 206 | -------------------------------------------------------------------------------- /tests/tinytest/assets/app/less/test-suite.less: -------------------------------------------------------------------------------- 1 | 2 | // -- Mixins ------------------------------------------------ // 3 | 4 | 5 | // Border Radius 6 | .border-radius( @radius ) { 7 | -webkit-border-radius: @radius ; 8 | -moz-border-radius: @radius ; 9 | border-radius: @radius ; 10 | } 11 | 12 | // Drop shadows 13 | .box-shadow( @shadow ) { 14 | -webkit-box-shadow: @shadow ; 15 | -moz-box-shadow: @shadow ; 16 | box-shadow: @shadow ; 17 | } 18 | 19 | .font-family-default() { 20 | font-family: "lucida sans unicode", "lucida grande", sans-serif ; 21 | } 22 | 23 | .font-family-heavy() { 24 | font-family: "arial black", gadget, sans-serif ; 25 | } 26 | 27 | .opacity( @opacity ) { 28 | opacity: @opacity / 100 ; 29 | filter: ~"alpha( opacity=@{opacity} )" ; 30 | } 31 | 32 | .placeholder( @color: #AAAAAA, @font-style: italic ) { 33 | &:-moz-placeholder { 34 | color: @color ; 35 | font-style: @font-style ; 36 | font-weight: 100 ; 37 | } 38 | 39 | &::-moz-placeholder { 40 | color: @color ; 41 | font-style: @font-style ; 42 | font-weight: 100 ; 43 | } 44 | 45 | &:-ms-input-placeholder { 46 | color: @color ; 47 | font-style: @font-style ; 48 | font-weight: 100 ; 49 | } 50 | 51 | &::-webkit-input-placeholder { 52 | color: @color ; 53 | font-style: @font-style ; 54 | font-weight: 100 ; 55 | } 56 | } 57 | 58 | .user-select( @select ) { 59 | -webkit-user-select: @select ; 60 | -moz-user-select: @select ; 61 | -ms-user-select: @select ; 62 | -o-user-select: @select ; 63 | user-select: @select ; 64 | } 65 | 66 | 67 | // -- Core -------------------------------------------------- // 68 | 69 | 70 | body { 71 | background-color: #FFFFFF ; 72 | color: #333333 ; 73 | .font-family-default ; 74 | margin: 0px 0px 0px 0px ; 75 | } 76 | 77 | form { 78 | margin: 0px 0px 0px 0px ; 79 | } 80 | 81 | div.siteInfo { 82 | background-color: #161616 ; 83 | color: #C4CBCE ; 84 | font-size: 14px ; 85 | font-weight: 100 ; 86 | height: 30px ; 87 | line-height: 30px ; 88 | position: relative ; 89 | 90 | a.name { 91 | color: #C4CBCE ; 92 | left: 20px ; 93 | position: absolute ; 94 | text-decoration: none ; 95 | text-transform: uppercase ; 96 | top: 0px ; 97 | z-index: 2 ; 98 | 99 | &:hover { 100 | text-decoration: underline ; 101 | } 102 | } 103 | 104 | span.tddMentality { 105 | display: block ; 106 | text-align: center ; 107 | text-transform: uppercase ; 108 | } 109 | 110 | span.author { 111 | position: absolute ; 112 | right: 20px ; 113 | top: 0px ; 114 | z-index: 2 ; 115 | 116 | a { 117 | color: inherit ; 118 | text-decoration: none ; 119 | text-transform: uppercase ; 120 | 121 | &:hover { 122 | text-decoration: underline ; 123 | } 124 | } 125 | } 126 | } 127 | 128 | div.testStatus { 129 | border-bottom: 30px solid #EDF3F3 ; 130 | margin-bottom: 30px ; 131 | 132 | button.callToAction { 133 | border: none ; 134 | cursor: pointer ; 135 | display: block ; 136 | margin: 0px 0px 0px 0px ; 137 | padding: 70px 0px 75px 0px ; 138 | text-align: center ; 139 | width: 100% ; 140 | 141 | div.subtitle { 142 | font-size: 22px ; 143 | .font-family-heavy ; 144 | height: 27px ; 145 | line-height: 27px ; 146 | margin: 0px auto 30px auto ; 147 | position: relative ; 148 | text-transform: uppercase ; 149 | width: 650px ; 150 | 151 | &:before { 152 | content: "" ; 153 | height: 1px ; 154 | left: 0px ; 155 | position: absolute ; 156 | right: 0px ; 157 | top: 15px ; 158 | } 159 | 160 | span { 161 | display: inline-block ; 162 | padding: 0px 20px 0px 20px ; 163 | position: relative ; // Needed for proper zIndex ordering. 164 | z-index: 2 ; 165 | } 166 | } 167 | 168 | div.status { 169 | color: #FFFFFF ; 170 | .font-family-heavy ; 171 | font-size: 135px ; 172 | line-height: 135px ; 173 | margin: 0px 0px 35px 0px ; 174 | text-transform: uppercase ; 175 | } 176 | 177 | div.button { 178 | .border-radius( 6px 6px 6px 6px ) ; 179 | display: inline-block ; 180 | font-size: 22px ; 181 | font-weight: bold ; 182 | line-height: 25px ; 183 | padding: 20px 25px 20px 25px ; 184 | } 185 | 186 | div.errorInfo { 187 | margin: 0px auto 0px auto ; 188 | padding-top: 40px ; 189 | width: 650px ; 190 | 191 | div.subtitle { 192 | margin-bottom: 35px ; 193 | } 194 | 195 | div.file { 196 | font-size: 20px ; 197 | font-weight: bold ; 198 | line-height: 25px ; 199 | margin-bottom: 15px ; 200 | } 201 | 202 | div.message { 203 | font-size: 20px ; 204 | line-height: 25px ; 205 | } 206 | } 207 | } 208 | 209 | // div.testStatus -- variations. 210 | 211 | &.start { 212 | button.callToAction { 213 | background-color: #709DB0 ; 214 | 215 | div.subtitle { 216 | color: #121A1D ; 217 | 218 | &:before { 219 | background-color: #5E8494 ; 220 | } 221 | 222 | span { 223 | background-color: #709DB0 ; 224 | } 225 | } 226 | 227 | div.button { 228 | background-color: #FFC618 ; 229 | } 230 | } 231 | } 232 | 233 | &.pass { 234 | button.callToAction { 235 | background-color: #88934B ; 236 | 237 | div.subtitle { 238 | color: #181A0C ; 239 | 240 | &:before { 241 | background-color: #6F783D ; 242 | } 243 | 244 | span { 245 | background-color: #88934B ; 246 | } 247 | } 248 | 249 | div.button { 250 | background-color: #FFC618 ; 251 | } 252 | } 253 | } 254 | 255 | &.fail { 256 | button.callToAction { 257 | background-color: #B75757 ; 258 | 259 | div.subtitle { 260 | color: #221010 ; 261 | 262 | &:before { 263 | background-color: #993300 ; 264 | } 265 | 266 | span { 267 | background-color: #B75757 ; 268 | } 269 | } 270 | 271 | div.button { 272 | background-color: #FFC618 ; 273 | } 274 | } 275 | 276 | div.errorInfo { 277 | div.file { 278 | color: #FFFFFF ; 279 | } 280 | 281 | div.message { 282 | color: #FFFFFF ; 283 | } 284 | } 285 | } 286 | } // END: div.testStatus 287 | 288 | div.testList { 289 | margin: 0px auto 100px auto ; 290 | width: 650px ; 291 | 292 | div.header { 293 | margin-bottom: 30px ; 294 | position: relative ; 295 | 296 | div.title { 297 | .font-family-heavy ; 298 | font-size: 22px ; 299 | line-height: 27px ; 300 | 301 | span.text { 302 | text-transform: uppercase ; 303 | } 304 | } 305 | 306 | .selectAll { 307 | color: #999999 ; 308 | display: inline-block ; 309 | .font-family-default ; 310 | font-size: 13px ; 311 | line-height: 27px ; 312 | vertical-align: middle ; 313 | 314 | a { 315 | color: #999999 ; 316 | } 317 | } 318 | 319 | input.filter { 320 | border: 1px solid #999999 ; 321 | .border-radius( 3px 3px 3px 3px ) ; 322 | font-size: 16px ; 323 | padding: 4px 0px 4px 3px ; 324 | .placeholder ; 325 | position: absolute ; 326 | right: 0px ; 327 | top: -2px ; 328 | width: 180px ; 329 | } 330 | } 331 | 332 | ol.tests { 333 | font-size: 16px ; 334 | font-weight: 100 ; 335 | line-height: 25px ; 336 | list-style-type: none ; 337 | margin: 0px 0px 30px 0px ; 338 | padding: 0px 0px 0px 0px ; 339 | 340 | li.test { 341 | background-color: #F5F5F5 ; 342 | margin-bottom: 2px ; 343 | 344 | &:hover { 345 | background-color: #E5E5E5 ; 346 | } 347 | 348 | // li.test -- variations. 349 | 350 | &:first-child { 351 | .border-radius( 4px 4px 0px 0px ) ; 352 | } 353 | 354 | &:last-child { 355 | .border-radius( 0px 0px 4px 4px ) ; 356 | } 357 | 358 | &:first-child:last-child { 359 | .border-radius( 4px 4px 4px 4px ) ; 360 | } 361 | } 362 | 363 | label { 364 | cursor: pointer ; 365 | display: block ; 366 | padding: 5px 5px 4px 5px ; 367 | } 368 | 369 | input { 370 | display: inline-block ; 371 | margin-right: 7px ; 372 | vertical-align: middle ; 373 | } 374 | } 375 | } // END: div.testList 376 | 377 | div.noTestList { 378 | color: #CC1717 ; 379 | font-size: 22px ; 380 | line-height: 40px ; 381 | margin: 0px 20px 100px 20px ; 382 | text-align: center ; 383 | font-style: italic ; 384 | 385 | a { 386 | border-bottom: 1px dotted #CC1717 ; 387 | color: #CC1717 ; 388 | text-decoration: none ; 389 | } 390 | } // END: div.noTestList 391 | 392 | label.autorun { 393 | background-color: #F0F0F0 ; 394 | .border-radius( 0px 4px 0px 0px ) ; 395 | bottom: 0px ; 396 | color: #666666 ; 397 | cursor: pointer ; 398 | font-size: 11px ; 399 | left: 0px ; 400 | line-height: 15px ; 401 | padding: 4px 12px 3px 3px ; 402 | position: fixed ; 403 | z-index: 2 ; 404 | 405 | &:hover { 406 | background-color: #333333 ; 407 | color: #FFFFFF ; 408 | } 409 | 410 | // label.autorun -- variations. 411 | 412 | &.on { 413 | background-color: #333333 ; 414 | color: #FFFFFF ; 415 | } 416 | } 417 | 418 | div.processingOverlay { 419 | background-color: rgba( 255, 255, 255, .95 ) ; 420 | bottom: 0px ; 421 | display: none ; 422 | left: 0px ; 423 | position: fixed ; 424 | right: 0px ; 425 | top: 0px ; 426 | z-index: 101 ; 427 | 428 | div.message { 429 | background-color: #709DB0 ; 430 | .border-radius( 7px 7px 7px 7px ) ; 431 | color: #FFFFFF ; 432 | left: 50% ; 433 | margin: -48px 0px 0px -192px ; 434 | padding: 18px 0px 18px 0px ; 435 | position: absolute ; 436 | text-align: center ; 437 | top: 50% ; 438 | width: 385px ; 439 | } 440 | 441 | div.plan { 442 | font-size: 22px ; 443 | .font-family-heavy ; 444 | line-height: 27px ; 445 | margin: 0px 0px 8px 0px ; 446 | text-transform: uppercase ; 447 | } 448 | 449 | div.patience { 450 | font-size: 16px ; 451 | line-height: 21px ; 452 | } 453 | } // END: div.processingOverlay -------------------------------------------------------------------------------- /tests/tinytest/assets/app/css/test-suite.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #FFFFFF ; 3 | color: #333333 ; 4 | font-family: "lucida sans unicode", "lucida grande", sans-serif; 5 | margin: 0px 0px 0px 0px ; 6 | } 7 | form { 8 | margin: 0px 0px 0px 0px ; 9 | } 10 | div.siteInfo { 11 | background-color: #161616 ; 12 | color: #C4CBCE ; 13 | font-size: 14px ; 14 | font-weight: 100 ; 15 | height: 30px ; 16 | line-height: 30px ; 17 | position: relative ; 18 | } 19 | div.siteInfo a.name { 20 | color: #C4CBCE ; 21 | left: 20px ; 22 | position: absolute ; 23 | text-decoration: none ; 24 | text-transform: uppercase ; 25 | top: 0px ; 26 | z-index: 2 ; 27 | } 28 | div.siteInfo a.name:hover { 29 | text-decoration: underline ; 30 | } 31 | div.siteInfo span.tddMentality { 32 | display: block ; 33 | text-align: center ; 34 | text-transform: uppercase ; 35 | } 36 | div.siteInfo span.author { 37 | position: absolute ; 38 | right: 20px ; 39 | top: 0px ; 40 | z-index: 2 ; 41 | } 42 | div.siteInfo span.author a { 43 | color: inherit ; 44 | text-decoration: none ; 45 | text-transform: uppercase ; 46 | } 47 | div.siteInfo span.author a:hover { 48 | text-decoration: underline ; 49 | } 50 | div.testStatus { 51 | border-bottom: 30px solid #EDF3F3 ; 52 | margin-bottom: 30px ; 53 | } 54 | div.testStatus button.callToAction { 55 | border: none ; 56 | cursor: pointer ; 57 | display: block ; 58 | margin: 0px 0px 0px 0px ; 59 | padding: 70px 0px 75px 0px ; 60 | text-align: center ; 61 | width: 100% ; 62 | } 63 | div.testStatus button.callToAction div.subtitle { 64 | font-size: 22px ; 65 | font-family: "arial black", gadget, sans-serif; 66 | height: 27px ; 67 | line-height: 27px ; 68 | margin: 0px auto 30px auto ; 69 | position: relative ; 70 | text-transform: uppercase ; 71 | width: 650px ; 72 | } 73 | div.testStatus button.callToAction div.subtitle:before { 74 | content: ""; 75 | height: 1px ; 76 | left: 0px ; 77 | position: absolute ; 78 | right: 0px ; 79 | top: 15px ; 80 | } 81 | div.testStatus button.callToAction div.subtitle span { 82 | display: inline-block; 83 | padding: 0px 20px 0px 20px ; 84 | position: relative ; 85 | z-index: 2 ; 86 | } 87 | div.testStatus button.callToAction div.status { 88 | color: #FFFFFF ; 89 | font-family: "arial black", gadget, sans-serif; 90 | font-size: 135px ; 91 | line-height: 135px ; 92 | margin: 0px 0px 35px 0px ; 93 | text-transform: uppercase ; 94 | } 95 | div.testStatus button.callToAction div.button { 96 | -webkit-border-radius: 6px 6px 6px 6px; 97 | -moz-border-radius: 6px 6px 6px 6px; 98 | border-radius: 6px 6px 6px 6px; 99 | display: inline-block; 100 | font-size: 22px ; 101 | font-weight: bold ; 102 | line-height: 25px ; 103 | padding: 20px 25px 20px 25px ; 104 | } 105 | div.testStatus button.callToAction div.errorInfo { 106 | margin: 0px auto 0px auto ; 107 | padding-top: 40px ; 108 | width: 650px ; 109 | } 110 | div.testStatus button.callToAction div.errorInfo div.subtitle { 111 | margin-bottom: 35px ; 112 | } 113 | div.testStatus button.callToAction div.errorInfo div.file { 114 | font-size: 20px ; 115 | font-weight: bold ; 116 | line-height: 25px ; 117 | margin-bottom: 15px ; 118 | } 119 | div.testStatus button.callToAction div.errorInfo div.message { 120 | font-size: 20px ; 121 | line-height: 25px ; 122 | } 123 | div.testStatus.start button.callToAction { 124 | background-color: #709DB0 ; 125 | } 126 | div.testStatus.start button.callToAction div.subtitle { 127 | color: #121A1D ; 128 | } 129 | div.testStatus.start button.callToAction div.subtitle:before { 130 | background-color: #5E8494 ; 131 | } 132 | div.testStatus.start button.callToAction div.subtitle span { 133 | background-color: #709DB0 ; 134 | } 135 | div.testStatus.start button.callToAction div.button { 136 | background-color: #FFC618 ; 137 | } 138 | div.testStatus.pass button.callToAction { 139 | background-color: #88934B ; 140 | } 141 | div.testStatus.pass button.callToAction div.subtitle { 142 | color: #181A0C ; 143 | } 144 | div.testStatus.pass button.callToAction div.subtitle:before { 145 | background-color: #6F783D ; 146 | } 147 | div.testStatus.pass button.callToAction div.subtitle span { 148 | background-color: #88934B ; 149 | } 150 | div.testStatus.pass button.callToAction div.button { 151 | background-color: #FFC618 ; 152 | } 153 | div.testStatus.fail button.callToAction { 154 | background-color: #B75757 ; 155 | } 156 | div.testStatus.fail button.callToAction div.subtitle { 157 | color: #221010 ; 158 | } 159 | div.testStatus.fail button.callToAction div.subtitle:before { 160 | background-color: #993300 ; 161 | } 162 | div.testStatus.fail button.callToAction div.subtitle span { 163 | background-color: #B75757 ; 164 | } 165 | div.testStatus.fail button.callToAction div.button { 166 | background-color: #FFC618 ; 167 | } 168 | div.testStatus.fail div.errorInfo div.file { 169 | color: #FFFFFF ; 170 | } 171 | div.testStatus.fail div.errorInfo div.message { 172 | color: #FFFFFF ; 173 | } 174 | div.testList { 175 | margin: 0px auto 100px auto ; 176 | width: 650px ; 177 | } 178 | div.testList div.header { 179 | margin-bottom: 30px ; 180 | position: relative ; 181 | } 182 | div.testList div.header div.title { 183 | font-family: "arial black", gadget, sans-serif; 184 | font-size: 22px ; 185 | line-height: 27px ; 186 | } 187 | div.testList div.header div.title span.text { 188 | text-transform: uppercase ; 189 | } 190 | div.testList div.header .selectAll { 191 | color: #999999 ; 192 | display: inline-block; 193 | font-family: "lucida sans unicode", "lucida grande", sans-serif; 194 | font-size: 13px ; 195 | line-height: 27px ; 196 | vertical-align: middle ; 197 | } 198 | div.testList div.header .selectAll a { 199 | color: #999999 ; 200 | } 201 | div.testList div.header input.filter { 202 | border: 1px solid #999999 ; 203 | -webkit-border-radius: 3px 3px 3px 3px; 204 | -moz-border-radius: 3px 3px 3px 3px; 205 | border-radius: 3px 3px 3px 3px; 206 | font-size: 16px ; 207 | padding: 4px 0px 4px 3px ; 208 | position: absolute ; 209 | right: 0px ; 210 | top: -2px; 211 | width: 180px ; 212 | } 213 | div.testList div.header input.filter:-moz-placeholder { 214 | color: #aaaaaa; 215 | font-style: italic; 216 | font-weight: 100 ; 217 | } 218 | div.testList div.header input.filter::-moz-placeholder { 219 | color: #aaaaaa; 220 | font-style: italic; 221 | font-weight: 100 ; 222 | } 223 | div.testList div.header input.filter:-ms-input-placeholder { 224 | color: #aaaaaa; 225 | font-style: italic; 226 | font-weight: 100 ; 227 | } 228 | div.testList div.header input.filter::-webkit-input-placeholder { 229 | color: #aaaaaa; 230 | font-style: italic; 231 | font-weight: 100 ; 232 | } 233 | div.testList ol.tests { 234 | font-size: 16px ; 235 | font-weight: 100 ; 236 | line-height: 25px ; 237 | list-style-type: none ; 238 | margin: 0px 0px 30px 0px ; 239 | padding: 0px 0px 0px 0px ; 240 | } 241 | div.testList ol.tests li.test { 242 | background-color: #F5F5F5 ; 243 | margin-bottom: 2px ; 244 | } 245 | div.testList ol.tests li.test:hover { 246 | background-color: #E5E5E5 ; 247 | } 248 | div.testList ol.tests li.test:first-child { 249 | -webkit-border-radius: 4px 4px 0px 0px; 250 | -moz-border-radius: 4px 4px 0px 0px; 251 | border-radius: 4px 4px 0px 0px; 252 | } 253 | div.testList ol.tests li.test:last-child { 254 | -webkit-border-radius: 0px 0px 4px 4px; 255 | -moz-border-radius: 0px 0px 4px 4px; 256 | border-radius: 0px 0px 4px 4px; 257 | } 258 | div.testList ol.tests li.test:first-child:last-child { 259 | -webkit-border-radius: 4px 4px 4px 4px; 260 | -moz-border-radius: 4px 4px 4px 4px; 261 | border-radius: 4px 4px 4px 4px; 262 | } 263 | div.testList ol.tests label { 264 | cursor: pointer ; 265 | display: block ; 266 | padding: 5px 5px 4px 5px ; 267 | } 268 | div.testList ol.tests input { 269 | display: inline-block; 270 | margin-right: 7px ; 271 | vertical-align: middle ; 272 | } 273 | div.noTestList { 274 | color: #CC1717 ; 275 | font-size: 22px ; 276 | line-height: 40px ; 277 | margin: 0px 20px 100px 20px ; 278 | text-align: center ; 279 | font-style: italic ; 280 | } 281 | div.noTestList a { 282 | border-bottom: 1px dotted #CC1717 ; 283 | color: #CC1717 ; 284 | text-decoration: none ; 285 | } 286 | label.autorun { 287 | background-color: #F0F0F0 ; 288 | -webkit-border-radius: 0px 4px 0px 0px; 289 | -moz-border-radius: 0px 4px 0px 0px; 290 | border-radius: 0px 4px 0px 0px; 291 | bottom: 0px ; 292 | color: #666666 ; 293 | cursor: pointer ; 294 | font-size: 11px ; 295 | left: 0px ; 296 | line-height: 15px ; 297 | padding: 4px 12px 3px 3px ; 298 | position: fixed ; 299 | z-index: 2 ; 300 | } 301 | label.autorun:hover { 302 | background-color: #333333 ; 303 | color: #FFFFFF ; 304 | } 305 | label.autorun.on { 306 | background-color: #333333 ; 307 | color: #FFFFFF ; 308 | } 309 | div.processingOverlay { 310 | background-color: rgba(255, 255, 255, 0.95); 311 | bottom: 0px ; 312 | display: none ; 313 | left: 0px ; 314 | position: fixed ; 315 | right: 0px ; 316 | top: 0px ; 317 | z-index: 101 ; 318 | } 319 | div.processingOverlay div.message { 320 | background-color: #709DB0 ; 321 | -webkit-border-radius: 7px 7px 7px 7px; 322 | -moz-border-radius: 7px 7px 7px 7px; 323 | border-radius: 7px 7px 7px 7px; 324 | color: #FFFFFF ; 325 | left: 50% ; 326 | margin: -48px 0px 0px -192px; 327 | padding: 18px 0px 18px 0px ; 328 | position: absolute ; 329 | text-align: center ; 330 | top: 50% ; 331 | width: 385px ; 332 | } 333 | div.processingOverlay div.plan { 334 | font-size: 22px ; 335 | font-family: "arial black", gadget, sans-serif; 336 | line-height: 27px ; 337 | margin: 0px 0px 8px 0px ; 338 | text-transform: uppercase ; 339 | } 340 | div.processingOverlay div.patience { 341 | font-size: 16px ; 342 | line-height: 21px ; 343 | } 344 | -------------------------------------------------------------------------------- /lib/JRegExList.cfc: -------------------------------------------------------------------------------- 1 | component 2 | output = false 3 | hint = "I provide methods for parsing lists using Java-based Regular Expression delimiters. There are no 'NoCase' methods here since Java's RegEx Patterns can all be made case-insensitive by starting them with a case-insensitive (?i) flag." 4 | { 5 | 6 | /** 7 | * I initialize the component. 8 | */ 9 | public void function init() { 10 | 11 | variables.CollectionsClass = createObject( "java", "java.util.Collections" ); 12 | 13 | } 14 | 15 | // --- 16 | // PUBLIC METHODS. 17 | // --- 18 | 19 | /** 20 | * I return the first item in the list using the given RegEx delimiter pattern. 21 | */ 22 | public string function jreListFirst( 23 | required string list, 24 | required string delimiterPattern, 25 | boolean includeEmptyFields = false 26 | ) { 27 | 28 | for ( var segment in jreSegment( list, delimiterPattern ) ) { 29 | 30 | if ( segment.isItem && ( segment.text.len() || includeEmptyFields ) ) { 31 | 32 | return( segment.text ); 33 | 34 | } 35 | 36 | } 37 | 38 | return( "" ); 39 | 40 | } 41 | 42 | 43 | /** 44 | * I return the item at the given position in the list using the given RegEx delimiter 45 | * pattern. If the position is out-of-bounds, the empty string is returned. 46 | * 47 | * CAUTION: ColdFusion's native listGetAt() function throws an error for out-of-bounds 48 | * positions. As such, this is an intentional deviation from that approach. 49 | */ 50 | public string function jreListGetAt( 51 | required string list, 52 | required numeric position, 53 | required string delimiterPattern, 54 | boolean includeEmptyFields = false 55 | ) { 56 | 57 | var itemCount = 0; 58 | 59 | for ( var segment in jreSegment( list, delimiterPattern ) ) { 60 | 61 | if ( 62 | segment.isItem && 63 | ( segment.text.len() || includeEmptyFields ) && 64 | ( ++itemCount == position ) 65 | ) { 66 | 67 | return( segment.text ); 68 | 69 | } 70 | 71 | } 72 | 73 | return( "" ); 74 | 75 | } 76 | 77 | 78 | /** 79 | * I return the last item in the list using the given RegEx delimiter pattern. If the 80 | * list is empty, the empty string is returned. 81 | */ 82 | public string function jreListLast( 83 | required string list, 84 | required string delimiterPattern, 85 | boolean includeEmptyFields = false 86 | ) { 87 | 88 | // CAUTION: We are calling reverseOrder() on the segments. This means that by 89 | // finding the FIRST item in the resultant array, we're actually finding the LAST 90 | // item in the original array. 91 | for ( var segment in reverseOrder( jreSegment( list, delimiterPattern ) ) ) { 92 | 93 | if ( segment.isItem && ( segment.text.len() || includeEmptyFields ) ) { 94 | 95 | return( segment.text ); 96 | 97 | } 98 | 99 | } 100 | 101 | return( "" ); 102 | 103 | } 104 | 105 | 106 | /** 107 | * I return the length of the list using the given RegEx delimiter pattern. 108 | */ 109 | public numeric function jreListLen( 110 | required string list, 111 | required string delimiterPattern, 112 | boolean includeEmptyFields = false 113 | ) { 114 | 115 | var itemCount = 0; 116 | 117 | for ( var segment in jreSegment( list, delimiterPattern ) ) { 118 | 119 | if ( segment.isItem && ( segment.text.len() || includeEmptyFields ) ) { 120 | 121 | itemCount++; 122 | 123 | } 124 | 125 | } 126 | 127 | return( itemCount ); 128 | 129 | } 130 | 131 | 132 | /** 133 | * I return the mapped list using the given RegEx delimiter pattern and operator. 134 | */ 135 | public string function jreListMap( 136 | required string list, 137 | required function callback, 138 | required string delimiterPattern, 139 | boolean includeEmptyFields = false 140 | ) { 141 | 142 | var segments = jreSegment( list, delimiterPattern ); 143 | var itemCount = 0; 144 | 145 | for ( var segment in segments ) { 146 | 147 | if ( segment.isItem && ( segment.text.len() || includeEmptyFields ) ) { 148 | 149 | segment.text = callback( segment.text, ++itemCount, list, delimiterPattern, includeEmptyFields ); 150 | 151 | } 152 | 153 | } 154 | 155 | return( joinSegments( segments ) ); 156 | 157 | } 158 | 159 | 160 | /** 161 | * I return the list without the first item (and its surrounding delimiters) using the 162 | * given RegEx delimiter pattern. 163 | */ 164 | public string function jreListRest( 165 | required string list, 166 | required string delimiterPattern, 167 | boolean includeEmptyFields = false 168 | ) { 169 | 170 | var itemCount = 0; 171 | // When getting the "rest" of the list items, we want to drop the first item and 172 | // its surrounding delimiters. As such, we only want to start collecting segments 173 | // when we find our SECOND item. 174 | var restOfSegments = jreSegment( list, delimiterPattern ).filter( 175 | ( segment ) => { 176 | 177 | if ( segment.isItem && ( segment.text.len() || includeEmptyFields ) ) { 178 | 179 | itemCount++; 180 | 181 | } 182 | 183 | return( itemCount >= 2 ); 184 | 185 | } 186 | ); 187 | 188 | return( joinSegments( restOfSegments ) ); 189 | 190 | } 191 | 192 | 193 | /** 194 | * I set the item at the given position in the list using the given RegEx delimiter 195 | * pattern. If the position is out-of-bounds, the original list is returned. 196 | * 197 | * CAUTION: ColdFusion's native listSetAt() function throws an error for out-of-bounds 198 | * positions. As such, this is an intentional deviation from that approach. 199 | */ 200 | public string function jreListSetAt( 201 | required string list, 202 | required numeric position, 203 | required string value, 204 | required string delimiterPattern, 205 | boolean includeEmptyFields = false 206 | ) { 207 | 208 | var segments = jreSegment( list, delimiterPattern ); 209 | var itemCount = 0; 210 | 211 | for ( var segment in segments ) { 212 | 213 | if ( 214 | segment.isItem && 215 | ( segment.text.len() || includeEmptyFields ) && 216 | ( ++itemCount == position ) 217 | ) { 218 | 219 | segment.text = value; 220 | break; 221 | 222 | } 223 | 224 | } 225 | 226 | return( joinSegments( segments ) ); 227 | 228 | } 229 | 230 | 231 | /** 232 | * I extract the items from the list using the given RegEx delimiter pattern. 233 | */ 234 | public array function jreListToArray( 235 | required string list, 236 | required string delimiterPattern, 237 | boolean includeEmptyFields = false 238 | ) { 239 | 240 | var items = []; 241 | 242 | for ( var segment in jreSegment( list, delimiterPattern ) ) { 243 | 244 | if ( segment.isItem && ( segment.text.len() || includeEmptyFields ) ) { 245 | 246 | items.append( segment.text ); 247 | 248 | } 249 | 250 | } 251 | 252 | return( items ); 253 | 254 | } 255 | 256 | 257 | /** 258 | * I split the given list into segments using the given delimiter RegEx pattern. Empty 259 | * items are always collected between neighboring delimiters. It is up to the calling 260 | * context to exclude empty items if desired. 261 | */ 262 | public array function jreSegment( 263 | required string list, 264 | required string delimiterPattern 265 | ) { 266 | 267 | testDelimiterPattern( delimiterPattern ); 268 | 269 | var matcher = createObject( "java", "java.util.regex.Pattern" ) 270 | .compile( delimiterPattern ) 271 | .matcher( list ) 272 | ; 273 | var segments = []; 274 | 275 | // NOTE: Technically, CFML Strings are Java Strings; however, since we're going to 276 | // dip down into the Java layer methods, it's comforting to explicitly cast the 277 | // value to the native Java type if nothing else to provide some documentation as 278 | // to where those method are coming from. 279 | var input = javaCast( "string", list ); 280 | var endOfLastMatch = 0; 281 | 282 | // Search for list delimiters. 283 | while ( matcher.find() ) { 284 | 285 | // If the start of the current match lines-up with the end of the previous 286 | // match, it means that there was no non-delimiter value between the two 287 | // matched delimiters. As such, we need to insert an empty-item. 288 | if ( matcher.start() == endOfLastMatch ) { 289 | 290 | segments.append({ 291 | isItem: true, 292 | isDelimiter: false, 293 | text: "" 294 | }); 295 | 296 | // If the start of the current match does NOT line-up with the end of the 297 | // previous match, it means we have a non-delimiter value to collect from 298 | // between the two matched delimiters. 299 | } else { 300 | 301 | segments.append({ 302 | isItem: true, 303 | isDelimiter: false, 304 | text: input.substring( endOfLastMatch, matcher.start() ) 305 | }); 306 | 307 | } 308 | 309 | segments.append({ 310 | isItem: false, 311 | isDelimiter: true, 312 | text: matcher.group() 313 | }); 314 | 315 | endOfLastMatch = matcher.end(); 316 | 317 | } 318 | 319 | // If the last match ended before the end of the input, it means that we have some 320 | // non-delimiter text at the end of the list to collect. 321 | if ( endOfLastMatch < input.length() ) { 322 | 323 | segments.append({ 324 | isItem: true, 325 | isDelimiter: false, 326 | text: input.substring( endOfLastMatch ) 327 | }); 328 | 329 | // If the last delimiter match also hit the end of the input, it means that we 330 | // have to append an empty item after the last delimiter. 331 | } else { 332 | 333 | segments.append({ 334 | isItem: true, 335 | isDelimiter: false, 336 | text: "" 337 | }); 338 | 339 | } 340 | 341 | return( segments ); 342 | 343 | } 344 | 345 | // --- 346 | // PRIVATE METHODS. 347 | // --- 348 | 349 | /** 350 | * I collapse the given list segments down into a single string value. 351 | */ 352 | private string function joinSegments( required array segments ) { 353 | 354 | var list = segments 355 | .map( 356 | ( segment ) => { 357 | 358 | return( segment.text ); 359 | 360 | } 361 | ) 362 | .toList( "" ) 363 | ; 364 | 365 | return( list ); 366 | 367 | } 368 | 369 | 370 | /** 371 | * I reverse the order of the items in the given array. 372 | */ 373 | private array function reverseOrder( required array values ) { 374 | 375 | CollectionsClass.reverse( values ); 376 | return( values ); 377 | 378 | } 379 | 380 | 381 | /** 382 | * I test the given delimiter pattern. If it is empty, an error is thrown. 383 | */ 384 | private void function testDelimiterPattern( required string delimiterPattern ) { 385 | 386 | if ( ! delimiterPattern.len() ) { 387 | 388 | throw( 389 | type = "JRegExList.EmptyPattern", 390 | message = "The Regular Expression pattern for delimiters cannot be empty." 391 | ); 392 | 393 | } 394 | 395 | } 396 | 397 | } 398 | -------------------------------------------------------------------------------- /tests/specs/JRegExTest.cfc: -------------------------------------------------------------------------------- 1 | component 2 | extends = "TestCase" 3 | output = false 4 | hint = "I test JRegEx." 5 | { 6 | 7 | public void function test_jreAfter() { 8 | 9 | var jre = new lib.JRegEx(); 10 | 11 | assert( jre.jreAfter( "abcdef", "c" ) == "def" ); 12 | assert( jre.jreAfter( "abcdef", "[cd]+" ) == "ef" ); 13 | assert( jre.jreAfter( "abcdef", "abcdef" ) == "" ); 14 | assert( jre.jreAfter( "abcdef", "-" ) == "" ); 15 | assert( jre.jreAfter( "https://www.bennadel.com/foo/bar/baz", "https?://[^/]+" ) == "/foo/bar/baz" ); 16 | 17 | } 18 | 19 | 20 | public void function test_jreAfterLast() { 21 | 22 | var jre = new lib.JRegEx(); 23 | 24 | assert( jre.jreAfterLast( "abcabc", "-" ) == "" ); 25 | assert( jre.jreAfterLast( "abcabc", "abc" ) == "" ); 26 | assert( jre.jreAfterLast( "abcabc", "b" ) == "c" ); 27 | assert( jre.jreAfterLast( "abcabc", "[ab]" ) == "c" ); 28 | assert( jre.jreAfterLast( "abcabc", "abcabc" ) == "" ); 29 | assert( jre.jreAfterLast( "https://www.bennadel.com/foo/bar/baz", "[\\/]+" ) == "baz" ); 30 | 31 | } 32 | 33 | 34 | public void function test_jreBefore() { 35 | 36 | var jre = new lib.JRegEx(); 37 | 38 | assert( jre.jreBefore( "abcabc", "-" ) == "abcabc" ); 39 | assert( jre.jreBefore( "abcabc", "a" ) == "" ); 40 | assert( jre.jreBefore( "abcabc", "b" ) == "a" ); 41 | assert( jre.jreBefore( "abcabc", "c" ) == "ab" ); 42 | assert( jre.jreBefore( "abcabc", "\Babc" ) == "abc" ); 43 | 44 | } 45 | 46 | 47 | public void function test_jreBeforeLast() { 48 | 49 | var jre = new lib.JRegEx(); 50 | 51 | assert( jre.jreBeforeLast( "abcabc", "-" ) == "abcabc" ); 52 | assert( jre.jreBeforeLast( "abcabc", "b+" ) == "abca" ); 53 | assert( jre.jreBeforeLast( "abcabc", "abcabc" ) == "" ); 54 | assert( jre.jreBeforeLast( "abcabc", "a" ) == "abc" ); 55 | assert( jre.jreBeforeLast( "abcabcabcabc", "abc" ) == "abcabcabc" ); 56 | assert( jre.jreBeforeLast( "https://www.bennadel.com/foo/bar/baz", "/" ) == "https://www.bennadel.com/foo/bar" ); 57 | 58 | } 59 | 60 | 61 | public void function test_jreEndingWith() { 62 | 63 | var jre = new lib.JRegEx(); 64 | 65 | assert( jre.jreEndingWith( "abcdef", "z" ) == "" ); 66 | assert( jre.jreEndingWith( "abcdef", "d" ) == "abcd" ); 67 | assert( jre.jreEndingWith( "abcdef", "[def]+" ) == "abcdef" ); 68 | assert( jre.jreEndingWith( "abcdef", "[def]" ) == "abcd" ); 69 | assert( jre.jreEndingWith( "abcdef", "a" ) == "a" ); 70 | 71 | } 72 | 73 | 74 | public void function test_jreEndsWith() { 75 | 76 | var jre = new lib.JRegEx(); 77 | 78 | assert( jre.jreEndsWith( "abcdef", "f" ) == true ); 79 | assert( jre.jreEndsWith( "abcdef", "f$" ) == true ); 80 | assert( jre.jreEndsWith( "abcdef", "f\z" ) == true ); 81 | assert( jre.jreEndsWith( "abcdef", "(?m)f\b\Z" ) == true ); 82 | assert( jre.jreEndsWith( "abcdef", "def" ) == true ); 83 | assert( jre.jreEndsWith( "abcdef", "abc" ) == false ); 84 | assert( jre.jreEndsWith( "abcdef", "abcdef" ) == true ); 85 | assert( jre.jreEndsWith( "abcdef", "[a-z]+" ) == true ); 86 | 87 | } 88 | 89 | 90 | public void function test_jreEscape() { 91 | 92 | var jre = new lib.JRegEx(); 93 | 94 | assert( jre.jreFind( "**++??", jre.jreEscape( "**++??" ) ) == 1 ); 95 | 96 | } 97 | 98 | 99 | public void function test_jreExtract() { 100 | 101 | var jre = new lib.JRegEx(); 102 | 103 | var extraction = jre.jreExtract( "abc", "(.)(.)(.)(.)?" ); 104 | 105 | assert( extraction[ 0 ] == "abc" ); 106 | assert( extraction[ 1 ] == "a" ); 107 | assert( extraction[ 2 ] == "b" ); 108 | assert( extraction[ 3 ] == "c" ); 109 | assert( extraction[ 4 ] == "" ); 110 | 111 | var extraction = jre.jreExtract( "abc", "(.)(.)(.)(.)" ); 112 | 113 | assert( structIsEmpty( extraction ) ); 114 | 115 | } 116 | 117 | 118 | public void function test_jreFind() { 119 | 120 | var jre = new lib.JRegEx(); 121 | 122 | assert( jre.jreFind( "abc", "d" ) == 0 ); 123 | assert( jre.jreFind( "abc", "a" ) == 1 ); 124 | assert( jre.jreFind( "abc", "b" ) == 2 ); 125 | assert( jre.jreFind( "abc", "c" ) == 3 ); 126 | assert( jre.jreFind( "abc", "^abc$" ) == 1 ); 127 | assert( jre.jreFind( "aabbbcc", "b{3}" ) == 3 ); 128 | 129 | assert( jre.jreFind( "abc", "d" ) == reFind( "d", "abc" ) ); 130 | assert( jre.jreFind( "abc", "a" ) == reFind( "a", "abc" ) ); 131 | assert( jre.jreFind( "abc", "b" ) == reFind( "b", "abc" ) ); 132 | assert( jre.jreFind( "abc", "c" ) == reFind( "c", "abc" ) ); 133 | assert( jre.jreFind( "abc", "^abc$" ) == reFind( "^abc$", "abc" ) ); 134 | assert( jre.jreFind( "aabbbcc", "b{3}" ) == reFind( "b{3}", "aabbbcc" ) ); 135 | 136 | } 137 | 138 | 139 | public void function test_jreForEach() { 140 | 141 | var jre = new lib.JRegEx(); 142 | 143 | var count = 0; 144 | var results = jre.jreForEach( 145 | "abc", 146 | "(\w)", 147 | function( $0, $1, start, targetText ) { 148 | 149 | count++; 150 | 151 | switch ( start ) { 152 | case 1: 153 | assert( $0 == "a" ); 154 | break; 155 | case 2: 156 | assert( $0 == "b" ); 157 | break; 158 | case 3: 159 | assert( $0 == "c" ); 160 | break; 161 | default: 162 | fail(); 163 | break; 164 | } 165 | 166 | assert( $0 == $1 ); 167 | assert( targetText == "abc" ); 168 | 169 | } 170 | ); 171 | 172 | assert( count == 3 ); 173 | 174 | } 175 | 176 | 177 | public void function test_jreMap() { 178 | 179 | var jre = new lib.JRegEx(); 180 | 181 | var results = jre.jreMap( 182 | "abcdef", 183 | "(\w)", 184 | function( $0, $1, start, targetText ) { 185 | 186 | assert( $0 == $1 ); 187 | assert( targetText == "abcdef" ); 188 | 189 | if ( jre.jreTest( $1, "[aeiouy]" ) ) { 190 | 191 | return( ucase( $1 ) ); 192 | 193 | } 194 | 195 | } 196 | ); 197 | 198 | assert( arrayLen( results ) == 2 ); 199 | assert( ! compare( results[ 1 ], "A" ) ); 200 | assert( ! compare( results[ 2 ], "E" ) ); 201 | 202 | } 203 | 204 | 205 | public void function test_jreMatch() { 206 | 207 | var jre = new lib.JRegEx(); 208 | 209 | var matches = jre.jreMatch( "aabbcc", "(\w)\1" ); 210 | 211 | assert( arrayLen( matches ) == 3 ); 212 | assert( matches[ 1 ] == "aa" ); 213 | assert( matches[ 2 ] == "bb" ); 214 | assert( matches[ 3 ] == "cc" ); 215 | 216 | } 217 | 218 | 219 | public void function test_jreMatchGroups() { 220 | 221 | var jre = new lib.JRegEx(); 222 | 223 | var matches = jre.jreMatchGroups( "aabbccz", "(\w)\1(z)?" ); 224 | 225 | assert( arrayLen( matches ) == 3 ); 226 | assert( matches[ 1 ][ 0 ] == "aa" ); 227 | assert( matches[ 1 ][ 1 ] == "a" ); 228 | assert( matches[ 1 ][ 2 ] == "" ); 229 | 230 | assert( matches[ 2 ][ 0 ] == "bb" ); 231 | assert( matches[ 2 ][ 1 ] == "b" ); 232 | assert( matches[ 2 ][ 2 ] == "" ); 233 | 234 | assert( matches[ 3 ][ 0 ] == "ccz" ); 235 | assert( matches[ 3 ][ 1 ] == "c" ); 236 | assert( matches[ 3 ][ 2 ] == "z" ); 237 | 238 | } 239 | 240 | 241 | public void function test_jreMatchFirst() { 242 | 243 | var jre = new lib.JRegEx(); 244 | 245 | assert( jre.jreMatchFirst( "aabbaaaa", "z" ) == "" ); 246 | assert( jre.jreMatchFirst( "aabbaaaa", "a+" ) == "aa" ); 247 | assert( jre.jreMatchFirst( "aabbaaaa", "bb" ) == "bb" ); 248 | assert( jre.jreMatchFirst( "aabbaaaa", "bb{3,}" ) == "" ); 249 | assert( jre.jreMatchFirst( "aabbaaaa", "aa{3,}" ) == "aaaa" ); 250 | 251 | } 252 | 253 | 254 | public void function test_jreReplace() { 255 | 256 | var jre = new lib.JRegEx(); 257 | 258 | var result = jre.jreReplace( 259 | "abcde", 260 | "(b)|(d)", 261 | function( $0, $1, $2, start, targetText ) { 262 | 263 | if ( $0 == "b" ) { 264 | 265 | assert( $0 == "b" ); 266 | assert( $1 == "b" ); 267 | assert( isNull( $2 ) ); 268 | assert( start == 2 ); 269 | assert( targetText == "abcde" ); 270 | 271 | } else { 272 | 273 | assert( $0 == "d" ); 274 | assert( isNull( $1 ) ); 275 | assert( $2 == "d" ); 276 | assert( start == 4 ); 277 | assert( targetText == "abcde" ); 278 | 279 | } 280 | 281 | return( ucase( $0 ) ); 282 | 283 | } 284 | ); 285 | 286 | assert( ! compare( result, "aBcDe" ) ); 287 | 288 | var result = jre.jreReplace( 289 | "abc", 290 | ".", 291 | function( $0, start, targetText ) { 292 | 293 | if ( $0 == "a" ) { 294 | 295 | assert( start == 1 ); 296 | assert( targetText == "abc" ); 297 | 298 | } else if ( $0 == "b" ) { 299 | 300 | assert( start == 2 ); 301 | assert( targetText == "abc" ); 302 | 303 | } else { 304 | 305 | assert( start == 3 ); 306 | assert( targetText == "abc" ); 307 | 308 | } 309 | 310 | } 311 | ); 312 | 313 | assert( result == "" ); 314 | 315 | } 316 | 317 | 318 | public void function test_jreReplaceAll() { 319 | 320 | var jre = new lib.JRegEx(); 321 | 322 | assert( jre.jreReplaceAll( "abbcdde", "(b|d)\1", "$1" ) == "abcde" ); 323 | assert( jre.jreReplaceAll( "abbcdde", "(b|d)\1", "[$1]" ) == "a[b]c[d]e" ); 324 | 325 | assert( jre.jreReplaceAll( "abbcdde", "(b|d)\1", "$1", true ) == "a$1c$1e" ); 326 | assert( jre.jreReplaceAll( "abbcdde", "(b|d)\1", "[$1]", true ) == "a[$1]c[$1]e" ); 327 | 328 | } 329 | 330 | 331 | public void function test_jreReplaceFirst() { 332 | 333 | var jre = new lib.JRegEx(); 334 | 335 | assert( jre.jreReplaceFirst( "abbcdde", "(b|d)\1", "$1" ) == "abcdde" ); 336 | assert( jre.jreReplaceFirst( "abbcdde", "(b|d)\1", "[$1]" ) == "a[b]cdde" ); 337 | 338 | assert( jre.jreReplaceFirst( "abbcdde", "(b|d)\1", "$1", true ) == "a$1cdde" ); 339 | assert( jre.jreReplaceFirst( "abbcdde", "(b|d)\1", "[$1]", true ) == "a[$1]cdde" ); 340 | 341 | } 342 | 343 | 344 | public void function test_jreSegment() { 345 | 346 | var jre = new lib.JRegEx(); 347 | 348 | var targetText = "abcde"; 349 | var segments = jre.jreSegment( targetText, "[aeiou]" ); 350 | 351 | assert( arrayLen( segments ) == 3 ); 352 | assert( segments[ 1 ].text == "a" ); 353 | assert( mid( targetText, segments[ 1 ].offset, len( segments[ 1 ].text ) ) == "a" ); 354 | assert( segments[ 2 ].text == "bcd" ); 355 | assert( mid( targetText, segments[ 2 ].offset, len( segments[ 2 ].text ) ) == "bcd" ); 356 | assert( segments[ 3 ].text == "e" ); 357 | assert( mid( targetText, segments[ 3 ].offset, len( segments[ 3 ].text ) ) == "e" ); 358 | 359 | var targetText = "zaabcdeez"; 360 | var segments = jre.jreSegment( targetText, "[aeiou]" ); 361 | 362 | assert( arrayLen( segments ) == 7 ); 363 | assert( segments[ 1 ].text == "z" ); 364 | assert( mid( targetText, segments[ 1 ].offset, len( segments[ 1 ].text ) ) == "z" ); 365 | assert( segments[ 2 ].text == "a" ); 366 | assert( mid( targetText, segments[ 2 ].offset, len( segments[ 2 ].text ) ) == "a" ); 367 | assert( segments[ 3 ].text == "a" ); 368 | assert( mid( targetText, segments[ 3 ].offset, len( segments[ 3 ].text ) ) == "a" ); 369 | assert( segments[ 4 ].text == "bcd" ); 370 | assert( mid( targetText, segments[ 4 ].offset, len( segments[ 4 ].text ) ) == "bcd" ); 371 | assert( segments[ 5 ].text == "e" ); 372 | assert( mid( targetText, segments[ 5 ].offset, len( segments[ 5 ].text ) ) == "e" ); 373 | assert( segments[ 6 ].text == "e" ); 374 | assert( mid( targetText, segments[ 6 ].offset, len( segments[ 6 ].text ) ) == "e" ); 375 | assert( segments[ 7 ].text == "z" ); 376 | assert( mid( targetText, segments[ 7 ].offset, len( segments[ 7 ].text ) ) == "z" ); 377 | 378 | } 379 | 380 | 381 | public void function test_jreSplit() { 382 | 383 | var jre = new lib.JRegEx(); 384 | 385 | var results = jre.jreSplit( "abcde", "[bd]" ); 386 | 387 | assert( arrayLen( results ) == 3 ); 388 | assert( results[ 1 ] == "a" ); 389 | assert( results[ 2 ] == "c" ); 390 | assert( results[ 3 ] == "e" ); 391 | 392 | var results = jre.jreSplit( "abcde", "[bd]", 2 ); 393 | 394 | assert( arrayLen( results ) == 2 ); 395 | assert( results[ 1 ] == "a" ); 396 | assert( results[ 2 ] == "cde" ); 397 | 398 | } 399 | 400 | 401 | public void function test_jreStartingWith() { 402 | 403 | var jre = new lib.JRegEx(); 404 | 405 | assert( jre.jreStartingWith( "abcdef", "abcdef" ) == "abcdef" ); 406 | assert( jre.jreStartingWith( "abcdef", "[cd]+" ) == "cdef" ); 407 | assert( jre.jreStartingWith( "abcdef", "-" ) == "" ); 408 | assert( jre.jreStartingWith( "abcabcd", "\Babc" ) == "abcd" ); 409 | 410 | } 411 | 412 | 413 | public void function test_jreStartsWith() { 414 | 415 | var jre = new lib.JRegEx(); 416 | 417 | assert( jre.jreStartsWith( "abcdef", "a" ) == true ); 418 | assert( jre.jreStartsWith( "abcdef", "^a" ) == true ); 419 | assert( jre.jreStartsWith( "abcdef", "\Aa" ) == true ); 420 | assert( jre.jreStartsWith( "abcdef", "(?m)^a" ) == true ); 421 | assert( jre.jreStartsWith( "abcdef", "\ba" ) == true ); 422 | assert( jre.jreStartsWith( "abcdef", "[cab]+" ) == true ); 423 | assert( jre.jreStartsWith( "abcdef", "[cbd]+" ) == false ); 424 | assert( jre.jreStartsWith( "abcdef", "b" ) == false ); 425 | 426 | } 427 | 428 | 429 | public void function test_jreTest() { 430 | 431 | var jre = new lib.JRegEx(); 432 | 433 | assert( jre.jreTest( "", "" ) ); 434 | assert( jre.jreTest( "abc", ".{3}" ) ); 435 | assert( ! jre.jreTest( "abc", ".{4}" ) ); 436 | 437 | assert( jre.jreTest( "aabbcc", "aa\w{2}cc" ) ); 438 | assert( ! jre.jreTest( "aabbcc", "bb" ) ); 439 | 440 | } 441 | 442 | } 443 | -------------------------------------------------------------------------------- /lib/JRegEx.cfc: -------------------------------------------------------------------------------- 1 | component 2 | output = false 3 | semvar = "0.0.4" 4 | hint = "I provide a simplified API around the Regular Expression power of the underlying Java platform. There are no 'NoCase' method since Java RegEx Patterns can be made case-insensitive by start with a (?i) flag." 5 | { 6 | 7 | /** 8 | * I return an initialized Java RegEx utility library. 9 | */ 10 | public any function init() { 11 | 12 | variables.PatternClass = createObject( "java", "java.util.regex.Pattern" ); 13 | 14 | } 15 | 16 | // --- 17 | // PUBLIC METHODS. 18 | // --- 19 | 20 | /** 21 | * I return the trailing portion of the string starting after the first match of the 22 | * given pattern. If the pattern cannot be matched, the empty string is returned. 23 | */ 24 | public string function jreAfter( 25 | required string targetText, 26 | required string patternText 27 | ) { 28 | 29 | // NOTE: Technically, CFML Strings are Java Strings; however, since we're going to 30 | // dip down into the Java layer methods, it's comforting to explicitly cast the 31 | // value to the native Java type, if for no other reason to provide some 32 | // documentation as to where these method are coming from. 33 | var parts = javaCast( "string", targetText ) 34 | .split( patternText, 2 ) 35 | ; 36 | 37 | return( ( arrayLen( parts ) == 2 ) ? parts[ 2 ] : "" ); 38 | 39 | } 40 | 41 | 42 | /** 43 | * I return the trailing portion of the string starting after the last match of the 44 | * given pattern. If the pattern cannot be matched, the empty string is returned. 45 | */ 46 | public string function jreAfterLast( 47 | required string targetText, 48 | required string patternText 49 | ) { 50 | 51 | var matcher = createMatcher( targetText, patternText ); 52 | 53 | if ( ! matcher.find() ) { 54 | 55 | return( "" ); 56 | 57 | } 58 | 59 | var previousEnd = matcher.end(); 60 | 61 | while ( matcher.find() ) { 62 | 63 | previousEnd = matcher.end(); 64 | 65 | } 66 | 67 | // NOTE: Technically, CFML Strings are Java Strings; however, since we're going to 68 | // dip down into the Java layer methods, it's comforting to explicitly cast the 69 | // value to the native Java type, if for no other reason to provide some 70 | // documentation as to where these method are coming from. 71 | return( javaCast( "string", targetText ).substring( previousEnd ) ); 72 | 73 | } 74 | 75 | 76 | /** 77 | * I return the leading portion of the string up until first match of the given 78 | * pattern. If the pattern cannot be matched, the entire string is returned. 79 | */ 80 | public string function jreBefore( 81 | required string targetText, 82 | required string patternText 83 | ) { 84 | 85 | // NOTE: Technically, CFML Strings are Java Strings; however, since we're going to 86 | // dip down into the Java layer methods, it's comforting to explicitly cast the 87 | // value to the native Java type, if for no other reason to provide some 88 | // documentation as to where these method are coming from. 89 | var parts = javaCast( "string", targetText ) 90 | .split( patternText, 2 ) 91 | ; 92 | 93 | return( parts[ 1 ] ); 94 | 95 | } 96 | 97 | 98 | /** 99 | * I return the leading portion of the string up until last match of the given pattern. 100 | * If the pattern cannot be matched, the entire string is returned. 101 | */ 102 | public string function jreBeforeLast( 103 | required string targetText, 104 | required string patternText 105 | ) { 106 | 107 | var matcher = createMatcher( targetText, patternText ); 108 | 109 | if ( ! matcher.find() ) { 110 | 111 | return( targetText ); 112 | 113 | } 114 | 115 | var previousStart = matcher.start(); 116 | 117 | while ( matcher.find() ) { 118 | 119 | previousStart = matcher.start(); 120 | 121 | } 122 | 123 | // NOTE: Technically, CFML Strings are Java Strings; however, since we're going to 124 | // dip down into the Java layer methods, it's comforting to explicitly cast the 125 | // value to the native Java type, if for no other reason to provide some 126 | // documentation as to where these method are coming from. 127 | return( javaCast( "string", targetText ).substring( 0, previousStart ) ); 128 | 129 | } 130 | 131 | 132 | /** 133 | * I return the leading portion of the string ending with the first match of the given 134 | * pattern. If the pattern cannot be matched, the empty string is returned. 135 | */ 136 | public string function jreEndingWith( 137 | required string targetText, 138 | required string patternText 139 | ) { 140 | 141 | var matcher = createMatcher( targetText, patternText ); 142 | 143 | if ( ! matcher.find() ) { 144 | 145 | return( "" ); 146 | 147 | } 148 | 149 | // NOTE: Technically, CFML Strings are Java Strings; however, since we're going to 150 | // dip down into the Java layer methods, it's comforting to explicitly cast the 151 | // value to the native Java type, if for no other reason to provide some 152 | // documentation as to where these method are coming from. 153 | return( javaCast( "string", targetText ).substring( 0, matcher.end() ) ); 154 | 155 | } 156 | 157 | 158 | /** 159 | * I determine if the given pattern can be matched at the end of the input. 160 | */ 161 | public boolean function jreEndsWith( 162 | required string targetText, 163 | required string patternText 164 | ) { 165 | 166 | // In order to limit the amount of text that we have to search, let's anchor the 167 | // pattern to the end of the input. It's OK if the pattern already has anchor 168 | // boundaries - they can be doubled-up without consequence. 169 | var matcher = createMatcher( targetText, ( patternText & "\z" ) ); 170 | 171 | return( matcher.find() ); 172 | 173 | } 174 | 175 | 176 | /** 177 | * I escape the given Java Regular Expression pattern such that it will match as a 178 | * literal expression in a Java Regular Expression search. This is equivalent to what 179 | * the "quoteReplacement" arguments are doing in the other methods. 180 | * 181 | * @patternText I am the Java Regular Expression pattern being escaped. 182 | * @output false 183 | */ 184 | public string function jreEscape( required string patternText ) { 185 | 186 | return( PatternClass.quote( javaCast( "string", patternText ) ) ); 187 | 188 | } 189 | 190 | 191 | /** 192 | * I return all of the captured groups in the first match of the given Java's Pattern. 193 | * If not groups were captured, an empty struct is returned. 194 | */ 195 | public struct function jreExtract( 196 | required string targetText, 197 | required string patternText 198 | ) { 199 | 200 | var matcher = createMatcher( targetText, patternText ); 201 | 202 | if ( matcher.find() ) { 203 | 204 | return( createCapturedGroups( matcher ) ); 205 | 206 | } 207 | 208 | return( structNew() ); 209 | 210 | } 211 | 212 | 213 | /** 214 | * I determine the index of the first Java Regular Expression pattern within the given 215 | * target text. If no match can be found, returns zero. 216 | * 217 | * @targetText I am the text being scanned. 218 | * @patternText I am the Java Regular Expression pattern used to locate the first match. 219 | * @startingAt I am the offset (default 1) at which to start searching. 220 | * @output false 221 | */ 222 | public numeric function jreFind( 223 | required string targetText, 224 | required string patternText, 225 | numeric startingAt = 1 226 | ) { 227 | 228 | var matcher = createMatcher( targetText, patternText ); 229 | 230 | // NOTE: We're subtracting -1 to comply with Java's 0-based indexing. 231 | if ( matcher.find( javaCast( "int", ( startingAt - 1 ) ) ) ) { 232 | 233 | // NOTE: We're adding +1 to comply with ColdFusion's 1-based indexing. 234 | return( matcher.start() + 1 ); 235 | 236 | } 237 | 238 | return( 0 ); 239 | 240 | } 241 | 242 | 243 | /** 244 | * I use Java's Pattern / Matcher libraries to iterate over each matched pattern in 245 | * the given target text. Each match, including its captured groups, is passed to the 246 | * operator. Any return value is ignored. 247 | * 248 | * @targetText I am the text being scanned. 249 | * @patternText I am the Java Regular Expression pattern used to locate matches. 250 | * @operator I am the Function or Closure used to provide the match iteration. 251 | * @output false 252 | */ 253 | public void function jreForEach( 254 | required string targetText, 255 | required string patternText, 256 | required function operator 257 | ) { 258 | 259 | var matcher = createMatcher( targetText, patternText ); 260 | 261 | // Iterate over each pattern match in the target text. 262 | while ( matcher.find() ) { 263 | 264 | // Pass the matched groups, offset, and original text to the operator. This 265 | // creates a signature like operator( $0, ...$N, start, targetText ). 266 | operator( argumentCollection = createOperatorArguments( matcher, targetText ) ); 267 | 268 | } 269 | 270 | } 271 | 272 | 273 | /** 274 | * I use Java's Pattern / Matcher libraries to map matched patterns onto a resultant 275 | * array using the given operator function or closure. If the operator returns an 276 | * undefined value, nothing is added to the results for the given match. 277 | * 278 | * @targetText I am the text being scanned. 279 | * @patternText I am the Java Regular Expression pattern used to locate matches. 280 | * @operator I am the Function or Closure used to provide the match mapping. 281 | * @output false 282 | */ 283 | public array function jreMap( 284 | required string targetText, 285 | required string patternText, 286 | required function operator 287 | ) { 288 | 289 | var matcher = createMatcher( targetText, patternText ); 290 | var results = []; 291 | 292 | // Iterate over each pattern match in the target text. 293 | while ( matcher.find() ) { 294 | 295 | // Pass the matched groups, offset, and original text to the operator. This 296 | // creates a signature like operator( $0, ...$N, start, targetText ). 297 | var mappedMatch = operator( argumentCollection = createOperatorArguments( matcher, targetText ) ); 298 | 299 | // The operator can return an undefined value to exclude the given match from 300 | // the results. As such, only append the value if it is defined. 301 | if ( ! isNull( mappedMatch ) ) { 302 | 303 | arrayAppend( results, mappedMatch ); 304 | 305 | } 306 | 307 | } 308 | 309 | return( results ); 310 | 311 | } 312 | 313 | 314 | /** 315 | * I return all of the Java Regular Expression pattern matches in the given target 316 | * text. The matches are returned as an array of strings. 317 | * 318 | * @targetText I am the text being scanned. 319 | * @patternText I am the Java Regular Expression pattern used to locate matches. 320 | * @output false 321 | */ 322 | public array function jreMatch( 323 | required string targetText, 324 | required string patternText 325 | ) { 326 | 327 | var matcher = createMatcher( targetText, patternText ); 328 | var results = []; 329 | 330 | // Iterate over each pattern match in the target text. 331 | while ( matcher.find() ) { 332 | 333 | arrayAppend( results, matcher.group() ); 334 | 335 | } 336 | 337 | return( results ); 338 | 339 | } 340 | 341 | 342 | /** 343 | * I return all of the Java Regular Expression pattern matches in the given target 344 | * text. Unlike the .jreMatch() method, this method returns the matches as an array 345 | * of structures in which the captured-group index (0..N) is the key of the struct. 346 | * If a captured-group failed to match, its index will hold an empty string. 347 | * 348 | * @targetText I am the text being scanned. 349 | * @patternText I am the Java Regular Expression pattern used to locate matches. 350 | * @output false 351 | */ 352 | public array function jreMatchGroups( 353 | required string targetText, 354 | required string patternText 355 | ) { 356 | 357 | var matcher = createMatcher( targetText, patternText ); 358 | var results = []; 359 | 360 | // Iterate over each pattern match in the target text. 361 | while ( matcher.find() ) { 362 | 363 | arrayAppend( results, createCapturedGroups( matcher ) ); 364 | 365 | } 366 | 367 | return( results ); 368 | 369 | } 370 | 371 | 372 | /** 373 | * I return the first match of the given Java Regular Expression pattern against the 374 | * given target text. If there are no matches, an empty string is returned. 375 | */ 376 | public string function jreMatchFirst( 377 | required string targetText, 378 | required string patternText 379 | ) { 380 | 381 | var matcher = createMatcher( targetText, patternText ); 382 | 383 | if ( matcher.find() ) { 384 | 385 | return( matcher.group() ); 386 | 387 | } 388 | 389 | return( "" ); 390 | 391 | } 392 | 393 | 394 | /** 395 | * I use Java's Pattern / Matcher libraries to replace matched patterns using the 396 | * given operator function or closure. 397 | * 398 | * @targetText I am the text being scanned. 399 | * @patternText I am the Java Regular Expression pattern used to locate matches. 400 | * @operator I am the Function or Closure used to provide the match replacements. 401 | * @output false 402 | */ 403 | public string function jreReplace( 404 | required string targetText, 405 | required string patternText, 406 | required function operator 407 | ) { 408 | 409 | var matcher = createMatcher( targetText, patternText ); 410 | var buffer = createBuffer(); 411 | 412 | // Iterate over each pattern match in the target text. 413 | while ( matcher.find() ) { 414 | 415 | // Pass the matched groups, offset, and original text to the operator. This 416 | // creates a signature like operator( $0, ...$N, start, targetText ). 417 | var replacement = operator( argumentCollection = createOperatorArguments( matcher, targetText ) ); 418 | 419 | // In the event the operator doesn't return a value, we'll assume that the 420 | // intention is to replace the match with nothing. 421 | if ( isNull( replacement ) ) { 422 | 423 | replacement = ""; 424 | 425 | } 426 | 427 | // Since the operator is providing the replacement text based on the 428 | // individual parts found in the match, we are going to assume that any 429 | // embedded group reference is coincidental and should be consumed as a 430 | // string literal. 431 | matcher.appendReplacement( 432 | buffer, 433 | matcher.quoteReplacement( javaCast( "string", replacement ) ) 434 | ); 435 | 436 | } 437 | 438 | matcher.appendTail( buffer ); 439 | 440 | return( buffer.toString() ); 441 | 442 | } 443 | 444 | 445 | /** 446 | * I replace all instances of the given Java Regular Expression pattern matches with 447 | * the given replacement text. 448 | * 449 | * @targetText I am the text being scanned. 450 | * @patternText I am the Java Regular Expression pattern used to locate matches. 451 | * @replacementText I am the text replacing the pattern matches. I can use $ to reference captured groups. 452 | * @quoteReplacement I determine if the replacement text should be escaped (so as not to accidentally reference captured groups). 453 | * @output false 454 | */ 455 | public string function jreReplaceAll( 456 | required string targetText, 457 | required string patternText, 458 | required string replacementText, 459 | boolean quoteReplacement = false 460 | ) { 461 | 462 | var matcher = createMatcher( targetText, patternText ); 463 | 464 | var result = quoteReplacement 465 | ? matcher.replaceAll( matcher.quoteReplacement( javaCast( "string", replacementText ) ) ) 466 | : matcher.replaceAll( javaCast( "string", replacementText ) ) 467 | ; 468 | 469 | return( result ); 470 | 471 | } 472 | 473 | 474 | /** 475 | * I replace the first instance of the given Java Regular Expression pattern match 476 | * with the given replacement text, 477 | * 478 | * @targetText I am the text being scanned. 479 | * @patternText I am the Java Regular Expression pattern used to locate matches. 480 | * @replacementText I am the text replacing the first pattern match. I can use $ to reference captured groups. 481 | * @quoteReplacement I determine if the replacement text should be escaped (so as not to accidentally reference captured groups). 482 | * @output false 483 | */ 484 | public string function jreReplaceFirst( 485 | required string targetText, 486 | required string patternText, 487 | required string replacementText, 488 | boolean quoteReplacement = false 489 | ) { 490 | 491 | var matcher = createMatcher( targetText, patternText ); 492 | 493 | var result = quoteReplacement 494 | ? matcher.replaceFirst( matcher.quoteReplacement( javaCast( "string", replacementText ) ) ) 495 | : matcher.replaceFirst( javaCast( "string", replacementText ) ) 496 | ; 497 | 498 | return( result ); 499 | 500 | } 501 | 502 | 503 | /** 504 | * I segment the given target text using the given Java Regular Expression pattern. 505 | * This results in an array of both matches and non-matches, each of which have the 506 | * following properties: 507 | * 508 | * - Match: Boolean 509 | * - Offset: Numeric 510 | * - Text: String 511 | * 512 | * ... where "match" segments will also have the following property that contains 513 | * the captured groups of the given segment (similar to the jreMatchGroups() method): 514 | * 515 | * - Groups: Struct[Numeric]:String 516 | * 517 | * @targetText I am the text being segmented. 518 | * @patternText I am the Java Regular Expression pattern used to segment. 519 | * @output false 520 | */ 521 | public array function jreSegment( 522 | required string targetText, 523 | required string patternText 524 | ) { 525 | 526 | var matcher = createMatcher( targetText, patternText ); 527 | var segments = []; 528 | 529 | // As we iterate through the matches, we're going to need to slice the portions 530 | // of the target text that exist in between each match (as well as the leading 531 | // and trailing sections). 532 | var slicer = javaCast( "string", targetText ); 533 | var sliceStart = 0; 534 | 535 | // Iterate over each pattern match in the target text. 536 | while ( matcher.find() ) { 537 | 538 | // If the start of the current match is different than the next slice start, 539 | // it means there is some interstitial text between this match and the 540 | // previous match (or the start of the string). 541 | if ( matcher.start() != sliceStart ) { 542 | 543 | arrayAppend( 544 | segments, 545 | { 546 | match: false, 547 | text: slicer.substring( javaCast( "int", sliceStart ), matcher.start() ), 548 | offset: ( sliceStart + 1 ) // Adjust for ColdFusion 1-based offsets. 549 | } 550 | ); 551 | 552 | } 553 | 554 | arrayAppend( 555 | segments, 556 | { 557 | match: true, 558 | text: matcher.group(), 559 | offset: ( matcher.start() + 1 ), // Adjust for ColdFusion 1-based offsets. 560 | groups: createCapturedGroups( matcher ) 561 | } 562 | ); 563 | 564 | // Adjust slicer to account for the length of the current match. 565 | sliceStart = matcher.end(); 566 | 567 | } 568 | 569 | // If the last match didn't contain the tail-end of the target text, then our 570 | // last non-match will be the tail of the target text. 571 | if ( sliceStart < slicer.length() ) { 572 | 573 | arrayAppend( 574 | segments, 575 | { 576 | match: false, 577 | text: slicer.substring( javaCast( "int", sliceStart ) ), 578 | offset: ( sliceStart + 1 ) // Adjust for ColdFusion 1-based offsets. 579 | } 580 | ); 581 | 582 | } 583 | 584 | return( segments ); 585 | 586 | } 587 | 588 | 589 | /** 590 | * I split the given target text using the given Java Regular Expression pattern. 591 | * 592 | * @targetText I am the text being split. 593 | * @patternText I am the Java Regular Expression pattern used to split. 594 | * @limit I am the limit of the splits (zero means no limit). 595 | * @output false 596 | */ 597 | public array function jreSplit( 598 | required string targetText, 599 | required string patternText, 600 | numeric limit = 0 601 | ) { 602 | 603 | var results = limit 604 | ? javaCast( "string", targetText ).split( javaCast( "string", patternText ), javaCast( "int", limit ) ) 605 | : javaCast( "string", targetText ).split( javaCast( "string", patternText ) ) 606 | ; 607 | 608 | return( results ); 609 | 610 | } 611 | 612 | 613 | /** 614 | * I return the trailing portion of the string starting with the first match of the 615 | * given pattern. If the pattern cannot be matched, the empty string is returned. 616 | */ 617 | public string function jreStartingWith( 618 | required string targetText, 619 | required string patternText 620 | ) { 621 | 622 | var matcher = createMatcher( targetText, patternText ); 623 | 624 | if ( ! matcher.find() ) { 625 | 626 | return( "" ); 627 | 628 | } 629 | 630 | // NOTE: Technically, CFML Strings are Java Strings; however, since we're going to 631 | // dip down into the Java layer methods, it's comforting to explicitly cast the 632 | // value to the native Java type, if for no other reason to provide some 633 | // documentation as to where these method are coming from. 634 | return( javaCast( "string", targetText ).substring( matcher.start() ) ); 635 | 636 | } 637 | 638 | 639 | /** 640 | * I determine if the given pattern can be matched at the start of the input. 641 | */ 642 | public boolean function jreStartsWith( 643 | required string targetText, 644 | required string patternText 645 | ) { 646 | 647 | // In order to limit the amount of text that we have to search, let's anchor the 648 | // pattern to the beginning of the input. It's OK if the pattern already has 649 | // anchor boundaries - they can be doubled-up without consequence. 650 | var matcher = createMatcher( targetText, ( "\A" & patternText ) ); 651 | 652 | return( matcher.find() ); 653 | 654 | } 655 | 656 | 657 | /** 658 | * I determine if the entire target text can be matched by the given Java Regular 659 | * Expression pattern. 660 | * 661 | * @targetText I am the text being tested. 662 | * @patternText I am the Java Regular Expression pattern used to test. 663 | * @output false 664 | */ 665 | public boolean function jreTest( 666 | required string targetText, 667 | required string patternText 668 | ) { 669 | 670 | return( createMatcher( targetText, patternText ).matches() ); 671 | 672 | } 673 | 674 | // --- 675 | // PRIVATE METHODS. 676 | // --- 677 | 678 | /** 679 | * I create a Java String Buffer used to incrementally gather pattern replacements. 680 | * 681 | * @output false 682 | */ 683 | private any function createBuffer() { 684 | 685 | return( createObject( "java", "java.lang.StringBuffer" ).init() ); 686 | 687 | } 688 | 689 | 690 | /** 691 | * I translate the current match into a struct of captured groups, with the entire 692 | * match as the zero key. 693 | * 694 | * @matcher I am the matcher that is mid-iteration of matched patterns. 695 | * @output false 696 | */ 697 | private struct function createCapturedGroups( required any matcher ) { 698 | 699 | // Each set of captured groups will be a structure in which the keys correspond 700 | // to the index of the captured group with the zero-key being the entire match. 701 | var groups = {}; 702 | var groupCount = matcher.groupCount(); 703 | 704 | // Add each captured group to the resultant struct. 705 | for ( var i = 0 ; i <= groupCount ; i++ ) { 706 | 707 | groups[ i ] = matcher.group( javaCast( "int", i ) ); 708 | 709 | // If the group failed to match, it will be undefined. In order to make this 710 | // easier to consume, let's report it as the empty string. 711 | if ( ! structKeyExists( groups, i ) ) { 712 | 713 | groups[ i ] = ""; 714 | 715 | } 716 | 717 | } 718 | 719 | return( groups ); 720 | 721 | } 722 | 723 | 724 | /** 725 | * I create a Java Pattern Matcher for the given target text and pattern text. 726 | * 727 | * @targetText I am the text being scanned. 728 | * @patternText I am the Java Regular Expression pattern used to locate matches. 729 | * @output false 730 | */ 731 | private any function createMatcher( 732 | required string targetText, 733 | required string patternText 734 | ) { 735 | 736 | var matcher = PatternClass 737 | .compile( javaCast( "string", patternText ) ) 738 | .matcher( javaCast( "string", targetText ) ) 739 | ; 740 | 741 | return( matcher ); 742 | 743 | } 744 | 745 | 746 | /** 747 | * I create the argumentCollection that can be used to invoke a common operator() 748 | * function / closure. This will create a signature with the following arguments: 749 | * 750 | * operator( $0, ...$N, start, targetText ) 751 | * 752 | * @matcher I am the matcher that is mid-iteration of matched patterns. 753 | * @targetText I am the text being scanned. 754 | * @output false 755 | */ 756 | private struct function createOperatorArguments( 757 | required any matcher, 758 | required string targetText 759 | ) { 760 | 761 | // When preparing the arguments for the operator, we need to construct an 762 | // argumentCollection structure in which the argument index is the numeric key 763 | // of the argument offset. In order to simplify overlaying the pattern group 764 | // matching over the arguments array, we're simply going to keep an incremented 765 | // offset every time we add an argument. 766 | var operatorArguments = {}; 767 | var operatorArgumentOffset = 1; // Will be incremented with each argument. 768 | 769 | var groupCount = matcher.groupCount(); 770 | 771 | // NOTE: Calling .group(0) is equivalent to calling .group(), which will return 772 | // the entire match, not just a capturing group. 773 | for ( var i = 0 ; i <= groupCount ; i++ ) { 774 | 775 | operatorArguments[ operatorArgumentOffset++ ] = matcher.group( javaCast( "int", i ) ); 776 | 777 | } 778 | 779 | // Including the match offset and the original content for parity with the 780 | // JavaScript String.replace() function on which this algorithm is based. 781 | // -- 782 | // NOTE: We're adding 1 to the offset since ColdFusion starts offsets at 1 where 783 | // as Java starts offsets at 0. 784 | operatorArguments[ operatorArgumentOffset++ ] = ( matcher.start() + 1 ); 785 | operatorArguments[ operatorArgumentOffset++ ] = targetText; 786 | 787 | return( operatorArguments ); 788 | 789 | } 790 | 791 | } 792 | -------------------------------------------------------------------------------- /tests/tinytest/assets/jquery/jquery-1.9.1.min.js: -------------------------------------------------------------------------------- 1 | /*! jQuery v1.9.1 | (c) 2005, 2012 jQuery Foundation, Inc. | jquery.org/license 2 | //@ sourceMappingURL=jquery.min.map 3 | */(function(e,t){var n,r,i=typeof t,o=e.document,a=e.location,s=e.jQuery,u=e.$,l={},c=[],p="1.9.1",f=c.concat,d=c.push,h=c.slice,g=c.indexOf,m=l.toString,y=l.hasOwnProperty,v=p.trim,b=function(e,t){return new b.fn.init(e,t,r)},x=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,w=/\S+/g,T=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,N=/^(?:(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,k=/^[\],:{}\s]*$/,E=/(?:^|:|,)(?:\s*\[)+/g,S=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,A=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,j=/^-ms-/,D=/-([\da-z])/gi,L=function(e,t){return t.toUpperCase()},H=function(e){(o.addEventListener||"load"===e.type||"complete"===o.readyState)&&(q(),b.ready())},q=function(){o.addEventListener?(o.removeEventListener("DOMContentLoaded",H,!1),e.removeEventListener("load",H,!1)):(o.detachEvent("onreadystatechange",H),e.detachEvent("onload",H))};b.fn=b.prototype={jquery:p,constructor:b,init:function(e,n,r){var i,a;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof b?n[0]:n,b.merge(this,b.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:o,!0)),C.test(i[1])&&b.isPlainObject(n))for(i in n)b.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(a=o.getElementById(i[2]),a&&a.parentNode){if(a.id!==i[2])return r.find(e);this.length=1,this[0]=a}return this.context=o,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):b.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),b.makeArray(e,this))},selector:"",length:0,size:function(){return this.length},toArray:function(){return h.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=b.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return b.each(this,e,t)},ready:function(e){return b.ready.promise().done(e),this},slice:function(){return this.pushStack(h.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(b.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:d,sort:[].sort,splice:[].splice},b.fn.init.prototype=b.fn,b.extend=b.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},u=1,l=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},u=2),"object"==typeof s||b.isFunction(s)||(s={}),l===u&&(s=this,--u);l>u;u++)if(null!=(o=arguments[u]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(b.isPlainObject(r)||(n=b.isArray(r)))?(n?(n=!1,a=e&&b.isArray(e)?e:[]):a=e&&b.isPlainObject(e)?e:{},s[i]=b.extend(c,a,r)):r!==t&&(s[i]=r));return s},b.extend({noConflict:function(t){return e.$===b&&(e.$=u),t&&e.jQuery===b&&(e.jQuery=s),b},isReady:!1,readyWait:1,holdReady:function(e){e?b.readyWait++:b.ready(!0)},ready:function(e){if(e===!0?!--b.readyWait:!b.isReady){if(!o.body)return setTimeout(b.ready);b.isReady=!0,e!==!0&&--b.readyWait>0||(n.resolveWith(o,[b]),b.fn.trigger&&b(o).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===b.type(e)},isArray:Array.isArray||function(e){return"array"===b.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[m.call(e)]||"object":typeof e},isPlainObject:function(e){if(!e||"object"!==b.type(e)||e.nodeType||b.isWindow(e))return!1;try{if(e.constructor&&!y.call(e,"constructor")&&!y.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}var r;for(r in e);return r===t||y.call(e,r)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||o;var r=C.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=b.buildFragment([e],t,i),i&&b(i).remove(),b.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=b.trim(n),n&&k.test(n.replace(S,"@").replace(A,"]").replace(E,"")))?Function("return "+n)():(b.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||b.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&b.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(j,"ms-").replace(D,L)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,a=M(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:v&&!v.call("\ufeff\u00a0")?function(e){return null==e?"":v.call(e)}:function(e){return null==e?"":(e+"").replace(T,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?b.merge(n,"string"==typeof e?[e]:e):d.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(g)return g.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if("number"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=M(e),s=[];if(a)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return f.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(o=e[n],n=e,e=o),b.isFunction(e)?(r=h.call(arguments,2),i=function(){return e.apply(n||this,r.concat(h.call(arguments)))},i.guid=e.guid=e.guid||b.guid++,i):t},access:function(e,n,r,i,o,a,s){var u=0,l=e.length,c=null==r;if("object"===b.type(r)){o=!0;for(u in r)b.access(e,n,u,r[u],!0,a,s)}else if(i!==t&&(o=!0,b.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(b(e),n)})),n))for(;l>u;u++)n(e[u],r,s?i:i.call(e[u],u,n(e[u],r)));return o?e:c?n.call(e):l?n(e[0],r):a},now:function(){return(new Date).getTime()}}),b.ready.promise=function(t){if(!n)if(n=b.Deferred(),"complete"===o.readyState)setTimeout(b.ready);else if(o.addEventListener)o.addEventListener("DOMContentLoaded",H,!1),e.addEventListener("load",H,!1);else{o.attachEvent("onreadystatechange",H),e.attachEvent("onload",H);var r=!1;try{r=null==e.frameElement&&o.documentElement}catch(i){}r&&r.doScroll&&function a(){if(!b.isReady){try{r.doScroll("left")}catch(e){return setTimeout(a,50)}q(),b.ready()}}()}return n.promise(t)},b.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){l["[object "+t+"]"]=t.toLowerCase()});function M(e){var t=e.length,n=b.type(e);return b.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}r=b(o);var _={};function F(e){var t=_[e]={};return b.each(e.match(w)||[],function(e,n){t[n]=!0}),t}b.Callbacks=function(e){e="string"==typeof e?_[e]||F(e):b.extend({},e);var n,r,i,o,a,s,u=[],l=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=u.length,n=!0;u&&o>a;a++)if(u[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,u&&(l?l.length&&c(l.shift()):r?u=[]:p.disable())},p={add:function(){if(u){var t=u.length;(function i(t){b.each(t,function(t,n){var r=b.type(n);"function"===r?e.unique&&p.has(n)||u.push(n):n&&n.length&&"string"!==r&&i(n)})})(arguments),n?o=u.length:r&&(s=t,c(r))}return this},remove:function(){return u&&b.each(arguments,function(e,t){var r;while((r=b.inArray(t,u,r))>-1)u.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?b.inArray(e,u)>-1:!(!u||!u.length)},empty:function(){return u=[],this},disable:function(){return u=l=r=t,this},disabled:function(){return!u},lock:function(){return l=t,r||p.disable(),this},locked:function(){return!l},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],!u||i&&!l||(n?l.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},b.extend({Deferred:function(e){var t=[["resolve","done",b.Callbacks("once memory"),"resolved"],["reject","fail",b.Callbacks("once memory"),"rejected"],["notify","progress",b.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return b.Deferred(function(n){b.each(t,function(t,o){var a=o[0],s=b.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&b.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?b.extend(e,r):r}},i={};return r.pipe=r.then,b.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=h.call(arguments),r=n.length,i=1!==r||e&&b.isFunction(e.promise)?r:0,o=1===i?e:b.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?h.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,u,l;if(r>1)for(s=Array(r),u=Array(r),l=Array(r);r>t;t++)n[t]&&b.isFunction(n[t].promise)?n[t].promise().done(a(t,l,n)).fail(o.reject).progress(a(t,u,s)):--i;return i||o.resolveWith(l,n),o.promise()}}),b.support=function(){var t,n,r,a,s,u,l,c,p,f,d=o.createElement("div");if(d.setAttribute("className","t"),d.innerHTML="
a",n=d.getElementsByTagName("*"),r=d.getElementsByTagName("a")[0],!n||!r||!n.length)return{};s=o.createElement("select"),l=s.appendChild(o.createElement("option")),a=d.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t={getSetAttribute:"t"!==d.className,leadingWhitespace:3===d.firstChild.nodeType,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/top/.test(r.getAttribute("style")),hrefNormalized:"/a"===r.getAttribute("href"),opacity:/^0.5/.test(r.style.opacity),cssFloat:!!r.style.cssFloat,checkOn:!!a.value,optSelected:l.selected,enctype:!!o.createElement("form").enctype,html5Clone:"<:nav>"!==o.createElement("nav").cloneNode(!0).outerHTML,boxModel:"CSS1Compat"===o.compatMode,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},a.checked=!0,t.noCloneChecked=a.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!l.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}a=o.createElement("input"),a.setAttribute("value",""),t.input=""===a.getAttribute("value"),a.value="t",a.setAttribute("type","radio"),t.radioValue="t"===a.value,a.setAttribute("checked","t"),a.setAttribute("name","t"),u=o.createDocumentFragment(),u.appendChild(a),t.appendChecked=a.checked,t.checkClone=u.cloneNode(!0).cloneNode(!0).lastChild.checked,d.attachEvent&&(d.attachEvent("onclick",function(){t.noCloneEvent=!1}),d.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})d.setAttribute(c="on"+f,"t"),t[f+"Bubbles"]=c in e||d.attributes[c].expando===!1;return d.style.backgroundClip="content-box",d.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===d.style.backgroundClip,b(function(){var n,r,a,s="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",u=o.getElementsByTagName("body")[0];u&&(n=o.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",u.appendChild(n).appendChild(d),d.innerHTML="
t
",a=d.getElementsByTagName("td"),a[0].style.cssText="padding:0;margin:0;border:0;display:none",p=0===a[0].offsetHeight,a[0].style.display="",a[1].style.display="none",t.reliableHiddenOffsets=p&&0===a[0].offsetHeight,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",t.boxSizing=4===d.offsetWidth,t.doesNotIncludeMarginInBodyOffset=1!==u.offsetTop,e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(d,null)||{width:"4px"}).width,r=d.appendChild(o.createElement("div")),r.style.cssText=d.style.cssText=s,r.style.marginRight=r.style.width="0",d.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof d.style.zoom!==i&&(d.innerHTML="",d.style.cssText=s+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=3===d.offsetWidth,d.style.display="block",d.innerHTML="
",d.firstChild.style.width="5px",t.shrinkWrapBlocks=3!==d.offsetWidth,t.inlineBlockNeedsLayout&&(u.style.zoom=1)),u.removeChild(n),n=d=a=r=null)}),n=s=u=l=r=a=null,t}();var O=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,B=/([A-Z])/g;function P(e,n,r,i){if(b.acceptData(e)){var o,a,s=b.expando,u="string"==typeof n,l=e.nodeType,p=l?b.cache:e,f=l?e[s]:e[s]&&s;if(f&&p[f]&&(i||p[f].data)||!u||r!==t)return f||(l?e[s]=f=c.pop()||b.guid++:f=s),p[f]||(p[f]={},l||(p[f].toJSON=b.noop)),("object"==typeof n||"function"==typeof n)&&(i?p[f]=b.extend(p[f],n):p[f].data=b.extend(p[f].data,n)),o=p[f],i||(o.data||(o.data={}),o=o.data),r!==t&&(o[b.camelCase(n)]=r),u?(a=o[n],null==a&&(a=o[b.camelCase(n)])):a=o,a}}function R(e,t,n){if(b.acceptData(e)){var r,i,o,a=e.nodeType,s=a?b.cache:e,u=a?e[b.expando]:b.expando;if(s[u]){if(t&&(o=n?s[u]:s[u].data)){b.isArray(t)?t=t.concat(b.map(t,b.camelCase)):t in o?t=[t]:(t=b.camelCase(t),t=t in o?[t]:t.split(" "));for(r=0,i=t.length;i>r;r++)delete o[t[r]];if(!(n?$:b.isEmptyObject)(o))return}(n||(delete s[u].data,$(s[u])))&&(a?b.cleanData([e],!0):b.support.deleteExpando||s!=s.window?delete s[u]:s[u]=null)}}}b.extend({cache:{},expando:"jQuery"+(p+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(e){return e=e.nodeType?b.cache[e[b.expando]]:e[b.expando],!!e&&!$(e)},data:function(e,t,n){return P(e,t,n)},removeData:function(e,t){return R(e,t)},_data:function(e,t,n){return P(e,t,n,!0)},_removeData:function(e,t){return R(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&b.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),b.fn.extend({data:function(e,n){var r,i,o=this[0],a=0,s=null;if(e===t){if(this.length&&(s=b.data(o),1===o.nodeType&&!b._data(o,"parsedAttrs"))){for(r=o.attributes;r.length>a;a++)i=r[a].name,i.indexOf("data-")||(i=b.camelCase(i.slice(5)),W(o,i,s[i]));b._data(o,"parsedAttrs",!0)}return s}return"object"==typeof e?this.each(function(){b.data(this,e)}):b.access(this,function(n){return n===t?o?W(o,e,b.data(o,e)):null:(this.each(function(){b.data(this,e,n)}),t)},null,n,arguments.length>1,null,!0)},removeData:function(e){return this.each(function(){b.removeData(this,e)})}});function W(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(B,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:O.test(r)?b.parseJSON(r):r}catch(o){}b.data(e,n,r)}else r=t}return r}function $(e){var t;for(t in e)if(("data"!==t||!b.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}b.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=b._data(e,n),r&&(!i||b.isArray(r)?i=b._data(e,n,b.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=b.queue(e,t),r=n.length,i=n.shift(),o=b._queueHooks(e,t),a=function(){b.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),o.cur=i,i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return b._data(e,n)||b._data(e,n,{empty:b.Callbacks("once memory").add(function(){b._removeData(e,t+"queue"),b._removeData(e,n)})})}}),b.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?b.queue(this[0],e):n===t?this:this.each(function(){var t=b.queue(this,e,n);b._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&b.dequeue(this,e)})},dequeue:function(e){return this.each(function(){b.dequeue(this,e)})},delay:function(e,t){return e=b.fx?b.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,o=b.Deferred(),a=this,s=this.length,u=function(){--i||o.resolveWith(a,[a])};"string"!=typeof e&&(n=e,e=t),e=e||"fx";while(s--)r=b._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(u));return u(),o.promise(n)}});var I,z,X=/[\t\r\n]/g,U=/\r/g,V=/^(?:input|select|textarea|button|object)$/i,Y=/^(?:a|area)$/i,J=/^(?:checked|selected|autofocus|autoplay|async|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped)$/i,G=/^(?:checked|selected)$/i,Q=b.support.getSetAttribute,K=b.support.input;b.fn.extend({attr:function(e,t){return b.access(this,b.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){b.removeAttr(this,e)})},prop:function(e,t){return b.access(this,b.prop,e,t,arguments.length>1)},removeProp:function(e){return e=b.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,u="string"==typeof e&&e;if(b.isFunction(e))return this.each(function(t){b(this).addClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(X," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=b.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,u=0===arguments.length||"string"==typeof e&&e;if(b.isFunction(e))return this.each(function(t){b(this).removeClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(X," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?b.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e,r="boolean"==typeof t;return b.isFunction(e)?this.each(function(n){b(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var o,a=0,s=b(this),u=t,l=e.match(w)||[];while(o=l[a++])u=r?u:!s.hasClass(o),s[u?"addClass":"removeClass"](o)}else(n===i||"boolean"===n)&&(this.className&&b._data(this,"__className__",this.className),this.className=this.className||e===!1?"":b._data(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(X," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=b.isFunction(e),this.each(function(n){var o,a=b(this);1===this.nodeType&&(o=i?e.call(this,n,a.val()):e,null==o?o="":"number"==typeof o?o+="":b.isArray(o)&&(o=b.map(o,function(e){return null==e?"":e+""})),r=b.valHooks[this.type]||b.valHooks[this.nodeName.toLowerCase()],r&&"set"in r&&r.set(this,o,"value")!==t||(this.value=o))});if(o)return r=b.valHooks[o.type]||b.valHooks[o.nodeName.toLowerCase()],r&&"get"in r&&(n=r.get(o,"value"))!==t?n:(n=o.value,"string"==typeof n?n.replace(U,""):null==n?"":n)}}}),b.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,u=0>i?s:o?i:0;for(;s>u;u++)if(n=r[u],!(!n.selected&&u!==i||(b.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&b.nodeName(n.parentNode,"optgroup"))){if(t=b(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n=b.makeArray(t);return b(e).find("option").each(function(){this.selected=b.inArray(b(this).val(),n)>=0}),n.length||(e.selectedIndex=-1),n}}},attr:function(e,n,r){var o,a,s,u=e.nodeType;if(e&&3!==u&&8!==u&&2!==u)return typeof e.getAttribute===i?b.prop(e,n,r):(a=1!==u||!b.isXMLDoc(e),a&&(n=n.toLowerCase(),o=b.attrHooks[n]||(J.test(n)?z:I)),r===t?o&&a&&"get"in o&&null!==(s=o.get(e,n))?s:(typeof e.getAttribute!==i&&(s=e.getAttribute(n)),null==s?t:s):null!==r?o&&a&&"set"in o&&(s=o.set(e,r,n))!==t?s:(e.setAttribute(n,r+""),r):(b.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(w);if(o&&1===e.nodeType)while(n=o[i++])r=b.propFix[n]||n,J.test(n)?!Q&&G.test(n)?e[b.camelCase("default-"+n)]=e[r]=!1:e[r]=!1:b.attr(e,n,""),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!b.support.radioValue&&"radio"===t&&b.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!b.isXMLDoc(e),a&&(n=b.propFix[n]||n,o=b.propHooks[n]),r!==t?o&&"set"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&"get"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var n=e.getAttributeNode("tabindex");return n&&n.specified?parseInt(n.value,10):V.test(e.nodeName)||Y.test(e.nodeName)&&e.href?0:t}}}}),z={get:function(e,n){var r=b.prop(e,n),i="boolean"==typeof r&&e.getAttribute(n),o="boolean"==typeof r?K&&Q?null!=i:G.test(n)?e[b.camelCase("default-"+n)]:!!i:e.getAttributeNode(n);return o&&o.value!==!1?n.toLowerCase():t},set:function(e,t,n){return t===!1?b.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&b.propFix[n]||n,n):e[b.camelCase("default-"+n)]=e[n]=!0,n}},K&&Q||(b.attrHooks.value={get:function(e,n){var r=e.getAttributeNode(n);return b.nodeName(e,"input")?e.defaultValue:r&&r.specified?r.value:t},set:function(e,n,r){return b.nodeName(e,"input")?(e.defaultValue=n,t):I&&I.set(e,n,r)}}),Q||(I=b.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&("id"===n||"name"===n||"coords"===n?""!==r.value:r.specified)?r.value:t},set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+="","value"===r||n===e.getAttribute(r)?n:t}},b.attrHooks.contenteditable={get:I.get,set:function(e,t,n){I.set(e,""===t?!1:t,n)}},b.each(["width","height"],function(e,n){b.attrHooks[n]=b.extend(b.attrHooks[n],{set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}})})),b.support.hrefNormalized||(b.each(["href","src","width","height"],function(e,n){b.attrHooks[n]=b.extend(b.attrHooks[n],{get:function(e){var r=e.getAttribute(n,2);return null==r?t:r}})}),b.each(["href","src"],function(e,t){b.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}})),b.support.style||(b.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),b.support.optSelected||(b.propHooks.selected=b.extend(b.propHooks.selected,{get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}})),b.support.enctype||(b.propFix.enctype="encoding"),b.support.checkOn||b.each(["radio","checkbox"],function(){b.valHooks[this]={get:function(e){return null===e.getAttribute("value")?"on":e.value}}}),b.each(["radio","checkbox"],function(){b.valHooks[this]=b.extend(b.valHooks[this],{set:function(e,n){return b.isArray(n)?e.checked=b.inArray(b(e).val(),n)>=0:t}})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\.(.+)|)$/;function it(){return!0}function ot(){return!1}b.event={global:{},add:function(e,n,r,o,a){var s,u,l,c,p,f,d,h,g,m,y,v=b._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=b.guid++),(u=v.events)||(u=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof b===i||e&&b.event.triggered===e.type?t:b.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||"").match(w)||[""],l=n.length;while(l--)s=rt.exec(n[l])||[],g=y=s[1],m=(s[2]||"").split(".").sort(),p=b.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=b.event.special[g]||{},d=b.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&b.expr.match.needsContext.test(a),namespace:m.join(".")},c),(h=u[g])||(h=u[g]=[],h.delegateCount=0,p.setup&&p.setup.call(e,o,m,f)!==!1||(e.addEventListener?e.addEventListener(g,f,!1):e.attachEvent&&e.attachEvent("on"+g,f))),p.add&&(p.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,d):h.push(d),b.event.global[g]=!0;e=null}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,p,f,d,h,g,m=b.hasData(e)&&b._data(e);if(m&&(c=m.events)){t=(t||"").match(w)||[""],l=t.length;while(l--)if(s=rt.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){p=b.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),u=o=f.length;while(o--)a=f[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,p.remove&&p.remove.call(e,a));u&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||b.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)b.event.remove(e,d+t[l],n,r,!0);b.isEmptyObject(c)&&(delete m.handle,b._removeData(e,"events"))}},trigger:function(n,r,i,a){var s,u,l,c,p,f,d,h=[i||o],g=y.call(n,"type")?n.type:n,m=y.call(n,"namespace")?n.namespace.split("."):[];if(l=f=i=i||o,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+b.event.triggered)&&(g.indexOf(".")>=0&&(m=g.split("."),g=m.shift(),m.sort()),u=0>g.indexOf(":")&&"on"+g,n=n[b.expando]?n:new b.Event(g,"object"==typeof n&&n),n.isTrigger=!0,n.namespace=m.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:b.makeArray(r,[n]),p=b.event.special[g]||{},a||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!a&&!p.noBubble&&!b.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(l=l.parentNode);l;l=l.parentNode)h.push(l),f=l;f===(i.ownerDocument||o)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((l=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(b._data(l,"events")||{})[n.type]&&b._data(l,"handle"),s&&s.apply(l,r),s=u&&l[u],s&&b.acceptData(l)&&s.apply&&s.apply(l,r)===!1&&n.preventDefault();if(n.type=g,!(a||n.isDefaultPrevented()||p._default&&p._default.apply(i.ownerDocument,r)!==!1||"click"===g&&b.nodeName(i,"a")||!b.acceptData(i)||!u||!i[g]||b.isWindow(i))){f=i[u],f&&(i[u]=null),b.event.triggered=g;try{i[g]()}catch(v){}b.event.triggered=t,f&&(i[u]=f)}return n.result}},dispatch:function(e){e=b.event.fix(e);var n,r,i,o,a,s=[],u=h.call(arguments),l=(b._data(this,"events")||{})[e.type]||[],c=b.event.special[e.type]||{};if(u[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=b.event.handlers.call(this,e,l),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((b.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,u),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],u=n.delegateCount,l=e.target;if(u&&l.nodeType&&(!e.button||"click"!==e.type))for(;l!=this;l=l.parentNode||this)if(1===l.nodeType&&(l.disabled!==!0||"click"!==e.type)){for(o=[],a=0;u>a;a++)i=n[a],r=i.selector+" ",o[r]===t&&(o[r]=i.needsContext?b(r,this).index(l)>=0:b.find(r,this,null,[l]).length),o[r]&&o.push(i);o.length&&s.push({elem:l,handlers:o})}return n.length>u&&s.push({elem:this,handlers:n.slice(u)}),s},fix:function(e){if(e[b.expando])return e;var t,n,r,i=e.type,a=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new b.Event(a),t=r.length;while(t--)n=r[t],e[n]=a[n];return e.target||(e.target=a.srcElement||o),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,a):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,i,a,s=n.button,u=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||o,a=i.documentElement,r=i.body,e.pageX=n.clientX+(a&&a.scrollLeft||r&&r.scrollLeft||0)-(a&&a.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(a&&a.scrollTop||r&&r.scrollTop||0)-(a&&a.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&u&&(e.relatedTarget=u===e.target?n.toElement:u),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},click:{trigger:function(){return b.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t}},focus:{trigger:function(){if(this!==o.activeElement&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===o.activeElement&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=b.extend(new b.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?b.event.trigger(i,null,t):b.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},b.removeEvent=o.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},b.Event=function(e,n){return this instanceof b.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&b.extend(this,n),this.timeStamp=e&&e.timeStamp||b.now(),this[b.expando]=!0,t):new b.Event(e,n)},b.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},b.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){b.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj; 4 | return(!i||i!==r&&!b.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),b.support.submitBubbles||(b.event.special.submit={setup:function(){return b.nodeName(this,"form")?!1:(b.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=b.nodeName(n,"input")||b.nodeName(n,"button")?n.form:t;r&&!b._data(r,"submitBubbles")&&(b.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),b._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&b.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return b.nodeName(this,"form")?!1:(b.event.remove(this,"._submit"),t)}}),b.support.changeBubbles||(b.event.special.change={setup:function(){return Z.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(b.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),b.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),b.event.simulate("change",this,e,!0)})),!1):(b.event.add(this,"beforeactivate._change",function(e){var t=e.target;Z.test(t.nodeName)&&!b._data(t,"changeBubbles")&&(b.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||b.event.simulate("change",this.parentNode,e,!0)}),b._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return b.event.remove(this,"._change"),!Z.test(this.nodeName)}}),b.support.focusinBubbles||b.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){b.event.simulate(t,e.target,b.event.fix(e),!0)};b.event.special[t]={setup:function(){0===n++&&o.addEventListener(e,r,!0)},teardown:function(){0===--n&&o.removeEventListener(e,r,!0)}}}),b.fn.extend({on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return b().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=b.guid++)),this.each(function(){b.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,b(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){b.event.remove(this,e,r,n)})},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)},trigger:function(e,t){return this.each(function(){b.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?b.event.trigger(e,n,r,!0):t}}),function(e,t){var n,r,i,o,a,s,u,l,c,p,f,d,h,g,m,y,v,x="sizzle"+-new Date,w=e.document,T={},N=0,C=0,k=it(),E=it(),S=it(),A=typeof t,j=1<<31,D=[],L=D.pop,H=D.push,q=D.slice,M=D.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},_="[\\x20\\t\\r\\n\\f]",F="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",O=F.replace("w","w#"),B="([*^$|!~]?=)",P="\\["+_+"*("+F+")"+_+"*(?:"+B+_+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+O+")|)|)"+_+"*\\]",R=":("+F+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+P.replace(3,8)+")*)|.*)\\)|)",W=RegExp("^"+_+"+|((?:^|[^\\\\])(?:\\\\.)*)"+_+"+$","g"),$=RegExp("^"+_+"*,"+_+"*"),I=RegExp("^"+_+"*([\\x20\\t\\r\\n\\f>+~])"+_+"*"),z=RegExp(R),X=RegExp("^"+O+"$"),U={ID:RegExp("^#("+F+")"),CLASS:RegExp("^\\.("+F+")"),NAME:RegExp("^\\[name=['\"]?("+F+")['\"]?\\]"),TAG:RegExp("^("+F.replace("w","w*")+")"),ATTR:RegExp("^"+P),PSEUDO:RegExp("^"+R),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+_+"*(even|odd|(([+-]|)(\\d*)n|)"+_+"*(?:([+-]|)"+_+"*(\\d+)|))"+_+"*\\)|)","i"),needsContext:RegExp("^"+_+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+_+"*((?:-\\d)?\\d*)"+_+"*\\)|)(?=[^-]|$)","i")},V=/[\x20\t\r\n\f]*[+~]/,Y=/^[^{]+\{\s*\[native code/,J=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,G=/^(?:input|select|textarea|button)$/i,Q=/^h\d$/i,K=/'|\\/g,Z=/\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,et=/\\([\da-fA-F]{1,6}[\x20\t\r\n\f]?|.)/g,tt=function(e,t){var n="0x"+t-65536;return n!==n?t:0>n?String.fromCharCode(n+65536):String.fromCharCode(55296|n>>10,56320|1023&n)};try{q.call(w.documentElement.childNodes,0)[0].nodeType}catch(nt){q=function(e){var t,n=[];while(t=this[e++])n.push(t);return n}}function rt(e){return Y.test(e+"")}function it(){var e,t=[];return e=function(n,r){return t.push(n+=" ")>i.cacheLength&&delete e[t.shift()],e[n]=r}}function ot(e){return e[x]=!0,e}function at(e){var t=p.createElement("div");try{return e(t)}catch(n){return!1}finally{t=null}}function st(e,t,n,r){var i,o,a,s,u,l,f,g,m,v;if((t?t.ownerDocument||t:w)!==p&&c(t),t=t||p,n=n||[],!e||"string"!=typeof e)return n;if(1!==(s=t.nodeType)&&9!==s)return[];if(!d&&!r){if(i=J.exec(e))if(a=i[1]){if(9===s){if(o=t.getElementById(a),!o||!o.parentNode)return n;if(o.id===a)return n.push(o),n}else if(t.ownerDocument&&(o=t.ownerDocument.getElementById(a))&&y(t,o)&&o.id===a)return n.push(o),n}else{if(i[2])return H.apply(n,q.call(t.getElementsByTagName(e),0)),n;if((a=i[3])&&T.getByClassName&&t.getElementsByClassName)return H.apply(n,q.call(t.getElementsByClassName(a),0)),n}if(T.qsa&&!h.test(e)){if(f=!0,g=x,m=t,v=9===s&&e,1===s&&"object"!==t.nodeName.toLowerCase()){l=ft(e),(f=t.getAttribute("id"))?g=f.replace(K,"\\$&"):t.setAttribute("id",g),g="[id='"+g+"'] ",u=l.length;while(u--)l[u]=g+dt(l[u]);m=V.test(e)&&t.parentNode||t,v=l.join(",")}if(v)try{return H.apply(n,q.call(m.querySelectorAll(v),0)),n}catch(b){}finally{f||t.removeAttribute("id")}}}return wt(e.replace(W,"$1"),t,n,r)}a=st.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},c=st.setDocument=function(e){var n=e?e.ownerDocument||e:w;return n!==p&&9===n.nodeType&&n.documentElement?(p=n,f=n.documentElement,d=a(n),T.tagNameNoComments=at(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),T.attributes=at(function(e){e.innerHTML="";var t=typeof e.lastChild.getAttribute("multiple");return"boolean"!==t&&"string"!==t}),T.getByClassName=at(function(e){return e.innerHTML="",e.getElementsByClassName&&e.getElementsByClassName("e").length?(e.lastChild.className="e",2===e.getElementsByClassName("e").length):!1}),T.getByName=at(function(e){e.id=x+0,e.innerHTML="
",f.insertBefore(e,f.firstChild);var t=n.getElementsByName&&n.getElementsByName(x).length===2+n.getElementsByName(x+0).length;return T.getIdNotName=!n.getElementById(x),f.removeChild(e),t}),i.attrHandle=at(function(e){return e.innerHTML="",e.firstChild&&typeof e.firstChild.getAttribute!==A&&"#"===e.firstChild.getAttribute("href")})?{}:{href:function(e){return e.getAttribute("href",2)},type:function(e){return e.getAttribute("type")}},T.getIdNotName?(i.find.ID=function(e,t){if(typeof t.getElementById!==A&&!d){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},i.filter.ID=function(e){var t=e.replace(et,tt);return function(e){return e.getAttribute("id")===t}}):(i.find.ID=function(e,n){if(typeof n.getElementById!==A&&!d){var r=n.getElementById(e);return r?r.id===e||typeof r.getAttributeNode!==A&&r.getAttributeNode("id").value===e?[r]:t:[]}},i.filter.ID=function(e){var t=e.replace(et,tt);return function(e){var n=typeof e.getAttributeNode!==A&&e.getAttributeNode("id");return n&&n.value===t}}),i.find.TAG=T.tagNameNoComments?function(e,n){return typeof n.getElementsByTagName!==A?n.getElementsByTagName(e):t}: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},i.find.NAME=T.getByName&&function(e,n){return typeof n.getElementsByName!==A?n.getElementsByName(name):t},i.find.CLASS=T.getByClassName&&function(e,n){return typeof n.getElementsByClassName===A||d?t:n.getElementsByClassName(e)},g=[],h=[":focus"],(T.qsa=rt(n.querySelectorAll))&&(at(function(e){e.innerHTML="",e.querySelectorAll("[selected]").length||h.push("\\["+_+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),e.querySelectorAll(":checked").length||h.push(":checked")}),at(function(e){e.innerHTML="",e.querySelectorAll("[i^='']").length&&h.push("[*^$]="+_+"*(?:\"\"|'')"),e.querySelectorAll(":enabled").length||h.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),h.push(",.*:")})),(T.matchesSelector=rt(m=f.matchesSelector||f.mozMatchesSelector||f.webkitMatchesSelector||f.oMatchesSelector||f.msMatchesSelector))&&at(function(e){T.disconnectedMatch=m.call(e,"div"),m.call(e,"[s!='']:x"),g.push("!=",R)}),h=RegExp(h.join("|")),g=RegExp(g.join("|")),y=rt(f.contains)||f.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},v=f.compareDocumentPosition?function(e,t){var r;return e===t?(u=!0,0):(r=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t))?1&r||e.parentNode&&11===e.parentNode.nodeType?e===n||y(w,e)?-1:t===n||y(w,t)?1:0:4&r?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return u=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:0;if(o===a)return ut(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++;return i?ut(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},u=!1,[0,0].sort(v),T.detectDuplicates=u,p):p},st.matches=function(e,t){return st(e,null,null,t)},st.matchesSelector=function(e,t){if((e.ownerDocument||e)!==p&&c(e),t=t.replace(Z,"='$1']"),!(!T.matchesSelector||d||g&&g.test(t)||h.test(t)))try{var n=m.call(e,t);if(n||T.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(r){}return st(t,p,null,[e]).length>0},st.contains=function(e,t){return(e.ownerDocument||e)!==p&&c(e),y(e,t)},st.attr=function(e,t){var n;return(e.ownerDocument||e)!==p&&c(e),d||(t=t.toLowerCase()),(n=i.attrHandle[t])?n(e):d||T.attributes?e.getAttribute(t):((n=e.getAttributeNode(t))||e.getAttribute(t))&&e[t]===!0?t:n&&n.specified?n.value:null},st.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},st.uniqueSort=function(e){var t,n=[],r=1,i=0;if(u=!T.detectDuplicates,e.sort(v),u){for(;t=e[r];r++)t===e[r-1]&&(i=n.push(r));while(i--)e.splice(n[i],1)}return e};function ut(e,t){var n=t&&e,r=n&&(~t.sourceIndex||j)-(~e.sourceIndex||j);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function lt(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function ct(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function pt(e){return ot(function(t){return t=+t,ot(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}o=st.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=o(t);return n},i=st.selectors={cacheLength:50,createPseudo:ot,match:U,find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(et,tt),e[3]=(e[4]||e[5]||"").replace(et,tt),"~="===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]||st.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]&&st.error(e[0]),e},PSEUDO:function(e){var t,n=!e[5]&&e[2];return U.CHILD.test(e[0])?null:(e[4]?e[2]=e[4]:n&&z.test(n)&&(t=ft(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){return"*"===e?function(){return!0}:(e=e.replace(et,tt).toLowerCase(),function(t){return t.nodeName&&t.nodeName.toLowerCase()===e})},CLASS:function(e){var t=k[e+" "];return t||(t=RegExp("(^|"+_+")"+e+"("+_+"|$)"))&&k(e,function(e){return t.test(e.className||typeof e.getAttribute!==A&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=st.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,p,f,d,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!u&&!s;if(m){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[x]||(m[x]={}),l=c[e]||[],d=l[0]===N&&l[1],f=l[0]===N&&l[2],p=d&&m.childNodes[d];while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[N,d,f];break}}else if(v&&(l=(t[x]||(t[x]={}))[e])&&l[0]===N)f=l[1];else while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(v&&((p[x]||(p[x]={}))[e]=[N,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=i.pseudos[e]||i.setFilters[e.toLowerCase()]||st.error("unsupported pseudo: "+e);return r[x]?r(t):r.length>1?(n=[e,e,"",t],i.setFilters.hasOwnProperty(e.toLowerCase())?ot(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=M.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:ot(function(e){var t=[],n=[],r=s(e.replace(W,"$1"));return r[x]?ot(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:ot(function(e){return function(t){return st(e,t).length>0}}),contains:ot(function(e){return function(t){return(t.textContent||t.innerText||o(t)).indexOf(e)>-1}}),lang:ot(function(e){return X.test(e||"")||st.error("unsupported lang: "+e),e=e.replace(et,tt).toLowerCase(),function(t){var n;do if(n=d?t.getAttribute("xml:lang")||t.getAttribute("lang"):t.lang)return n=n.toLowerCase(),n===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===f},focus:function(e){return e===p.activeElement&&(!p.hasFocus||p.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!i.pseudos.empty(e)},header:function(e){return Q.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"))||t.toLowerCase()===e.type)},first:pt(function(){return[0]}),last:pt(function(e,t){return[t-1]}),eq:pt(function(e,t,n){return[0>n?n+t:n]}),even:pt(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:pt(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:pt(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:pt(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}};for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})i.pseudos[n]=lt(n);for(n in{submit:!0,reset:!0})i.pseudos[n]=ct(n);function ft(e,t){var n,r,o,a,s,u,l,c=E[e+" "];if(c)return t?0:c.slice(0);s=e,u=[],l=i.preFilter;while(s){(!n||(r=$.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),u.push(o=[])),n=!1,(r=I.exec(s))&&(n=r.shift(),o.push({value:n,type:r[0].replace(W," ")}),s=s.slice(n.length));for(a in i.filter)!(r=U[a].exec(s))||l[a]&&!(r=l[a](r))||(n=r.shift(),o.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?st.error(e):E(e,u).slice(0)}function dt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function ht(e,t,n){var i=t.dir,o=n&&"parentNode"===i,a=C++;return t.first?function(t,n,r){while(t=t[i])if(1===t.nodeType||o)return e(t,n,r)}:function(t,n,s){var u,l,c,p=N+" "+a;if(s){while(t=t[i])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[i])if(1===t.nodeType||o)if(c=t[x]||(t[x]={}),(l=c[i])&&l[0]===p){if((u=l[1])===!0||u===r)return u===!0}else if(l=c[i]=[p],l[1]=e(t,n,s)||r,l[1]===!0)return!0}}function gt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function mt(e,t,n,r,i){var o,a=[],s=0,u=e.length,l=null!=t;for(;u>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),l&&t.push(s));return a}function yt(e,t,n,r,i,o){return r&&!r[x]&&(r=yt(r)),i&&!i[x]&&(i=yt(i,o)),ot(function(o,a,s,u){var l,c,p,f=[],d=[],h=a.length,g=o||xt(t||"*",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:mt(g,f,e,s,u),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,u),r){l=mt(y,d),r(l,[],s,u),c=l.length;while(c--)(p=l[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){l=[],c=y.length;while(c--)(p=y[c])&&l.push(m[c]=p);i(null,y=[],l,u)}c=y.length;while(c--)(p=y[c])&&(l=i?M.call(o,p):f[c])>-1&&(o[l]=!(a[l]=p))}}else y=mt(y===a?y.splice(h,y.length):y),i?i(null,a,y,u):H.apply(a,y)})}function vt(e){var t,n,r,o=e.length,a=i.relative[e[0].type],s=a||i.relative[" "],u=a?1:0,c=ht(function(e){return e===t},s,!0),p=ht(function(e){return M.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;o>u;u++)if(n=i.relative[e[u].type])f=[ht(gt(f),n)];else{if(n=i.filter[e[u].type].apply(null,e[u].matches),n[x]){for(r=++u;o>r;r++)if(i.relative[e[r].type])break;return yt(u>1&>(f),u>1&&dt(e.slice(0,u-1)).replace(W,"$1"),n,r>u&&vt(e.slice(u,r)),o>r&&vt(e=e.slice(r)),o>r&&dt(e))}f.push(n)}return gt(f)}function bt(e,t){var n=0,o=t.length>0,a=e.length>0,s=function(s,u,c,f,d){var h,g,m,y=[],v=0,b="0",x=s&&[],w=null!=d,T=l,C=s||a&&i.find.TAG("*",d&&u.parentNode||u),k=N+=null==T?1:Math.random()||.1;for(w&&(l=u!==p&&u,r=n);null!=(h=C[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,u,c)){f.push(h);break}w&&(N=k,r=++n)}o&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,o&&b!==v){g=0;while(m=t[g++])m(x,y,u,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=L.call(f));y=mt(y)}H.apply(f,y),w&&!s&&y.length>0&&v+t.length>1&&st.uniqueSort(f)}return w&&(N=k,l=T),x};return o?ot(s):s}s=st.compile=function(e,t){var n,r=[],i=[],o=S[e+" "];if(!o){t||(t=ft(e)),n=t.length;while(n--)o=vt(t[n]),o[x]?r.push(o):i.push(o);o=S(e,bt(i,r))}return o};function xt(e,t,n){var r=0,i=t.length;for(;i>r;r++)st(e,t[r],n);return n}function wt(e,t,n,r){var o,a,u,l,c,p=ft(e);if(!r&&1===p.length){if(a=p[0]=p[0].slice(0),a.length>2&&"ID"===(u=a[0]).type&&9===t.nodeType&&!d&&i.relative[a[1].type]){if(t=i.find.ID(u.matches[0].replace(et,tt),t)[0],!t)return n;e=e.slice(a.shift().value.length)}o=U.needsContext.test(e)?0:a.length;while(o--){if(u=a[o],i.relative[l=u.type])break;if((c=i.find[l])&&(r=c(u.matches[0].replace(et,tt),V.test(a[0].type)&&t.parentNode||t))){if(a.splice(o,1),e=r.length&&dt(a),!e)return H.apply(n,q.call(r,0)),n;break}}}return s(e,p)(r,t,d,n,V.test(e)),n}i.pseudos.nth=i.pseudos.eq;function Tt(){}i.filters=Tt.prototype=i.pseudos,i.setFilters=new Tt,c(),st.attr=b.attr,b.find=st,b.expr=st.selectors,b.expr[":"]=b.expr.pseudos,b.unique=st.uniqueSort,b.text=st.getText,b.isXMLDoc=st.isXML,b.contains=st.contains}(e);var at=/Until$/,st=/^(?:parents|prev(?:Until|All))/,ut=/^.[^:#\[\.,]*$/,lt=b.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};b.fn.extend({find:function(e){var t,n,r,i=this.length;if("string"!=typeof e)return r=this,this.pushStack(b(e).filter(function(){for(t=0;i>t;t++)if(b.contains(r[t],this))return!0}));for(n=[],t=0;i>t;t++)b.find(e,this[t],n);return n=this.pushStack(i>1?b.unique(n):n),n.selector=(this.selector?this.selector+" ":"")+e,n},has:function(e){var t,n=b(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(b.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e,!1))},filter:function(e){return this.pushStack(ft(this,e,!0))},is:function(e){return!!e&&("string"==typeof e?lt.test(e)?b(e,this.context).index(this[0])>=0:b.filter(e,this).length>0:this.filter(e).length>0)},closest:function(e,t){var n,r=0,i=this.length,o=[],a=lt.test(e)||"string"!=typeof e?b(e,t||this.context):0;for(;i>r;r++){n=this[r];while(n&&n.ownerDocument&&n!==t&&11!==n.nodeType){if(a?a.index(n)>-1:b.find.matchesSelector(n,e)){o.push(n);break}n=n.parentNode}}return this.pushStack(o.length>1?b.unique(o):o)},index:function(e){return e?"string"==typeof e?b.inArray(this[0],b(e)):b.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?b(e,t):b.makeArray(e&&e.nodeType?[e]:e),r=b.merge(this.get(),n);return this.pushStack(b.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),b.fn.andSelf=b.fn.addBack;function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}b.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return b.dir(e,"parentNode")},parentsUntil:function(e,t,n){return b.dir(e,"parentNode",n)},next:function(e){return pt(e,"nextSibling")},prev:function(e){return pt(e,"previousSibling")},nextAll:function(e){return b.dir(e,"nextSibling")},prevAll:function(e){return b.dir(e,"previousSibling")},nextUntil:function(e,t,n){return b.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return b.dir(e,"previousSibling",n)},siblings:function(e){return b.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return b.sibling(e.firstChild)},contents:function(e){return b.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:b.merge([],e.childNodes)}},function(e,t){b.fn[e]=function(n,r){var i=b.map(this,t,n);return at.test(e)||(r=n),r&&"string"==typeof r&&(i=b.filter(r,i)),i=this.length>1&&!ct[e]?b.unique(i):i,this.length>1&&st.test(e)&&(i=i.reverse()),this.pushStack(i)}}),b.extend({filter:function(e,t,n){return n&&(e=":not("+e+")"),1===t.length?b.find.matchesSelector(t[0],e)?[t[0]]:[]:b.find.matches(e,t)},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!b(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function ft(e,t,n){if(t=t||0,b.isFunction(t))return b.grep(e,function(e,r){var i=!!t.call(e,r,e);return i===n});if(t.nodeType)return b.grep(e,function(e){return e===t===n});if("string"==typeof t){var r=b.grep(e,function(e){return 1===e.nodeType});if(ut.test(t))return b.filter(t,r,!n);t=b.filter(t,r)}return b.grep(e,function(e){return b.inArray(e,t)>=0===n})}function dt(e){var t=ht.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",gt=/ jQuery\d+="(?:null|\d+)"/g,mt=RegExp("<(?:"+ht+")[\\s/>]","i"),yt=/^\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bt=/<([\w:]+)/,xt=/\s*$/g,At={option:[1,""],legend:[1,"
","
"],area:[1,"",""],param:[1,"",""],thead:[1,"","
"],tr:[2,"","
"],col:[2,"","
"],td:[3,"","
"],_default:b.support.htmlSerialize?[0,"",""]:[1,"X
","
"]},jt=dt(o),Dt=jt.appendChild(o.createElement("div"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,b.fn.extend({text:function(e){return b.access(this,function(e){return e===t?b.text(this):this.empty().append((this[0]&&this[0].ownerDocument||o).createTextNode(e))},null,e,arguments.length)},wrapAll:function(e){if(b.isFunction(e))return this.each(function(t){b(this).wrapAll(e.call(this,t))});if(this[0]){var t=b(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return b.isFunction(e)?this.each(function(t){b(this).wrapInner(e.call(this,t))}):this.each(function(){var t=b(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=b.isFunction(e);return this.each(function(n){b(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){b.nodeName(this,"body")||b(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&this.appendChild(e)})},prepend:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&this.insertBefore(e,this.firstChild)})},before:function(){return this.domManip(arguments,!1,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,!1,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=0;for(;null!=(n=this[r]);r++)(!e||b.filter(e,[n]).length>0)&&(t||1!==n.nodeType||b.cleanData(Ot(n)),n.parentNode&&(t&&b.contains(n.ownerDocument,n)&&Mt(Ot(n,"script")),n.parentNode.removeChild(n)));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&b.cleanData(Ot(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&b.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return b.clone(this,e,t)})},html:function(e){return b.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,""):t;if(!("string"!=typeof e||Tt.test(e)||!b.support.htmlSerialize&&mt.test(e)||!b.support.leadingWhitespace&&yt.test(e)||At[(bt.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(vt,"<$1>");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(b.cleanData(Ot(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(e){var t=b.isFunction(e);return t||"string"==typeof e||(e=b(e).not(this).detach()),this.domManip([e],!0,function(e){var t=this.nextSibling,n=this.parentNode;n&&(b(this).remove(),n.insertBefore(e,t))})},detach:function(e){return this.remove(e,!0)},domManip:function(e,n,r){e=f.apply([],e);var i,o,a,s,u,l,c=0,p=this.length,d=this,h=p-1,g=e[0],m=b.isFunction(g);if(m||!(1>=p||"string"!=typeof g||b.support.checkClone)&&Ct.test(g))return this.each(function(i){var o=d.eq(i);m&&(e[0]=g.call(this,i,n?o.html():t)),o.domManip(e,n,r)});if(p&&(l=b.buildFragment(e,this[0].ownerDocument,!1,this),i=l.firstChild,1===l.childNodes.length&&(l=i),i)){for(n=n&&b.nodeName(i,"tr"),s=b.map(Ot(l,"script"),Ht),a=s.length;p>c;c++)o=l,c!==h&&(o=b.clone(o,!0,!0),a&&b.merge(s,Ot(o,"script"))),r.call(n&&b.nodeName(this[c],"table")?Lt(this[c],"tbody"):this[c],o,c);if(a)for(u=s[s.length-1].ownerDocument,b.map(s,qt),c=0;a>c;c++)o=s[c],kt.test(o.type||"")&&!b._data(o,"globalEval")&&b.contains(u,o)&&(o.src?b.ajax({url:o.src,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0}):b.globalEval((o.text||o.textContent||o.innerHTML||"").replace(St,"")));l=i=null}return this}});function Lt(e,t){return e.getElementsByTagName(t)[0]||e.appendChild(e.ownerDocument.createElement(t))}function Ht(e){var t=e.getAttributeNode("type");return e.type=(t&&t.specified)+"/"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function Mt(e,t){var n,r=0;for(;null!=(n=e[r]);r++)b._data(n,"globalEval",!t||b._data(t[r],"globalEval"))}function _t(e,t){if(1===t.nodeType&&b.hasData(e)){var n,r,i,o=b._data(e),a=b._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)b.event.add(t,n,s[n][r])}a.data&&(a.data=b.extend({},a.data))}}function Ft(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!b.support.noCloneEvent&&t[b.expando]){i=b._data(t);for(r in i.events)b.removeEvent(t,r,i.handle);t.removeAttribute(b.expando)}"script"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),b.support.html5Clone&&e.innerHTML&&!b.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Nt.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}b.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){b.fn[e]=function(e){var n,r=0,i=[],o=b(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),b(o[r])[t](n),d.apply(i,n.get());return this.pushStack(i)}});function Ot(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||"*"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||"*"):t;if(!s)for(s=[],r=e.childNodes||e;null!=(o=r[a]);a++)!n||b.nodeName(o,n)?s.push(o):b.merge(s,Ot(o,n));return n===t||n&&b.nodeName(e,n)?b.merge([e],s):s}function Bt(e){Nt.test(e.type)&&(e.defaultChecked=e.checked)}b.extend({clone:function(e,t,n){var r,i,o,a,s,u=b.contains(e.ownerDocument,e);if(b.support.html5Clone||b.isXMLDoc(e)||!mt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(b.support.noCloneEvent&&b.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||b.isXMLDoc(e)))for(r=Ot(o),s=Ot(e),a=0;null!=(i=s[a]);++a)r[a]&&Ft(i,r[a]);if(t)if(n)for(s=s||Ot(e),r=r||Ot(o),a=0;null!=(i=s[a]);a++)_t(i,r[a]);else _t(e,o);return r=Ot(o,"script"),r.length>0&&Mt(r,!u&&Ot(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,u,l,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if("object"===b.type(o))b.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement("div")),u=(bt.exec(o)||["",""])[1].toLowerCase(),c=At[u]||At._default,s.innerHTML=c[1]+o.replace(vt,"<$1>")+c[2],i=c[0];while(i--)s=s.lastChild;if(!b.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!b.support.tbody){o="table"!==u||xt.test(o)?""!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)b.nodeName(l=o.childNodes[i],"tbody")&&!l.childNodes.length&&o.removeChild(l) 5 | }b.merge(d,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),b.support.appendChecked||b.grep(Ot(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===b.inArray(o,r))&&(a=b.contains(o.ownerDocument,o),s=Ot(f.appendChild(o),"script"),a&&Mt(s),n)){i=0;while(o=s[i++])kt.test(o.type||"")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,u=b.expando,l=b.cache,p=b.support.deleteExpando,f=b.event.special;for(;null!=(n=e[s]);s++)if((t||b.acceptData(n))&&(o=n[u],a=o&&l[o])){if(a.events)for(r in a.events)f[r]?b.event.remove(n,r):b.removeEvent(n,r,a.handle);l[o]&&(delete l[o],p?delete n[u]:typeof n.removeAttribute!==i?n.removeAttribute(u):n[u]=null,c.push(o))}}});var Pt,Rt,Wt,$t=/alpha\([^)]*\)/i,It=/opacity\s*=\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp("^("+x+")(.*)$","i"),Yt=RegExp("^("+x+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+-])=("+x+")","i"),Gt={BODY:"block"},Qt={position:"absolute",visibility:"hidden",display:"block"},Kt={letterSpacing:0,fontWeight:400},Zt=["Top","Right","Bottom","Left"],en=["Webkit","O","Moz","ms"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,"none"===b.css(e,"display")||!b.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=b._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=b._data(r,"olddisplay",un(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&b._data(r,"olddisplay",i?n:b.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}b.fn.extend({css:function(e,n){return b.access(this,function(e,n,r){var i,o,a={},s=0;if(b.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=b.css(e,n[s],!1,o);return a}return r!==t?b.style(e,n,r):b.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){var t="boolean"==typeof e;return this.each(function(){(t?e:nn(this))?b(this).show():b(this).hide()})}}),b.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":b.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,u=b.camelCase(n),l=e.style;if(n=b.cssProps[u]||(b.cssProps[u]=tn(l,u)),s=b.cssHooks[n]||b.cssHooks[u],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:l[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(b.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||b.cssNumber[u]||(r+="px"),b.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(l[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{l[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,u=b.camelCase(n);return n=b.cssProps[u]||(b.cssProps[u]=tn(e.style,u)),s=b.cssHooks[n]||b.cssHooks[u],s&&"get"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),"normal"===a&&n in Kt&&(a=Kt[n]),""===r||r?(o=parseFloat(a),r===!0||b.isNumeric(o)?o||0:a):a},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),u=s?s.getPropertyValue(n)||s[n]:t,l=e.style;return s&&(""!==u||b.contains(e.ownerDocument,e)||(u=b.style(e,n)),Yt.test(u)&&Ut.test(n)&&(i=l.width,o=l.minWidth,a=l.maxWidth,l.minWidth=l.maxWidth=l.width=u,u=s.width,l.width=i,l.minWidth=o,l.maxWidth=a)),u}):o.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),u=s?s[n]:t,l=e.style;return null==u&&l&&l[n]&&(u=l[n]),Yt.test(u)&&!zt.test(n)&&(i=l.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),l.left="fontSize"===n?"1em":u,u=l.pixelLeft+"px",l.left=i,a&&(o.left=a)),""===u?"auto":u});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function an(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;for(;4>o;o+=2)"margin"===n&&(a+=b.css(e,n+Zt[o],!0,i)),r?("content"===n&&(a-=b.css(e,"padding"+Zt[o],!0,i)),"margin"!==n&&(a-=b.css(e,"border"+Zt[o]+"Width",!0,i))):(a+=b.css(e,"padding"+Zt[o],!0,i),"padding"!==n&&(a+=b.css(e,"border"+Zt[o]+"Width",!0,i)));return a}function sn(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=b.support.boxSizing&&"border-box"===b.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(b.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?"border":"content"),r,o)+"px"}function un(e){var t=o,n=Gt[e];return n||(n=ln(e,t),"none"!==n&&n||(Pt=(Pt||b("