From 9338cdb472d3bcdd48ea2f0d833463a034096b28 Mon Sep 17 00:00:00 2001 From: gmorel Date: Mon, 16 Sep 2013 22:05:33 +0200 Subject: [PATCH 001/100] Merge branch 'master' of https://github.com/thelia/thelia into coupon # By Manuel Raynaud (19) and others # Via Manuel Raynaud (5) and others * 'master' of https://github.com/thelia/thelia: (37 commits) add PropelDataCollector to debugbar, still missing some informations remove unused class integrate debugbar module remove htmlpurifier modify insert.sql including debugBar module add new event and new tag Started category modification Factorized modal dialogs javascript Factorized creation and confirmation modal dialogs create controller adding delivery module in session create Delivery loop Working : Continuing categories administration... Working : create base loop for delivery and payment entities remove htmlpurifier dependency country id can be an argument for calculate method Working : Working : fix typo ... Conflicts: composer.lock core/lib/Thelia/Controller/Admin/CategoryController.php templates/admin/default/assets/less/thelia/variables.less --- local/modules/Colissimo/Colissimo.php | 0 local/modules/Colissimo/Config/config.xml | 0 local/modules/Colissimo/Config/plugin.xml | 0 local/modules/Colissimo/Config/schema.xml | 0 local/modules/DebugBar/Config/config.xml | 0 local/modules/DebugBar/Config/plugin.xml | 0 local/modules/DebugBar/Config/schema.xml | 0 local/modules/DebugBar/DataCollector/PropelCollector.php | 0 local/modules/DebugBar/DebugBar.php | 0 local/modules/DebugBar/Listeners/DebugBarListeners.php | 0 local/modules/DebugBar/Smarty/Plugin/DebugBar.php | 0 11 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 local/modules/Colissimo/Colissimo.php mode change 100644 => 100755 local/modules/Colissimo/Config/config.xml mode change 100644 => 100755 local/modules/Colissimo/Config/plugin.xml mode change 100644 => 100755 local/modules/Colissimo/Config/schema.xml mode change 100644 => 100755 local/modules/DebugBar/Config/config.xml mode change 100644 => 100755 local/modules/DebugBar/Config/plugin.xml mode change 100644 => 100755 local/modules/DebugBar/Config/schema.xml mode change 100644 => 100755 local/modules/DebugBar/DataCollector/PropelCollector.php mode change 100644 => 100755 local/modules/DebugBar/DebugBar.php mode change 100644 => 100755 local/modules/DebugBar/Listeners/DebugBarListeners.php mode change 100644 => 100755 local/modules/DebugBar/Smarty/Plugin/DebugBar.php diff --git a/local/modules/Colissimo/Colissimo.php b/local/modules/Colissimo/Colissimo.php old mode 100644 new mode 100755 diff --git a/local/modules/Colissimo/Config/config.xml b/local/modules/Colissimo/Config/config.xml old mode 100644 new mode 100755 diff --git a/local/modules/Colissimo/Config/plugin.xml b/local/modules/Colissimo/Config/plugin.xml old mode 100644 new mode 100755 diff --git a/local/modules/Colissimo/Config/schema.xml b/local/modules/Colissimo/Config/schema.xml old mode 100644 new mode 100755 diff --git a/local/modules/DebugBar/Config/config.xml b/local/modules/DebugBar/Config/config.xml old mode 100644 new mode 100755 diff --git a/local/modules/DebugBar/Config/plugin.xml b/local/modules/DebugBar/Config/plugin.xml old mode 100644 new mode 100755 diff --git a/local/modules/DebugBar/Config/schema.xml b/local/modules/DebugBar/Config/schema.xml old mode 100644 new mode 100755 diff --git a/local/modules/DebugBar/DataCollector/PropelCollector.php b/local/modules/DebugBar/DataCollector/PropelCollector.php old mode 100644 new mode 100755 diff --git a/local/modules/DebugBar/DebugBar.php b/local/modules/DebugBar/DebugBar.php old mode 100644 new mode 100755 diff --git a/local/modules/DebugBar/Listeners/DebugBarListeners.php b/local/modules/DebugBar/Listeners/DebugBarListeners.php old mode 100644 new mode 100755 diff --git a/local/modules/DebugBar/Smarty/Plugin/DebugBar.php b/local/modules/DebugBar/Smarty/Plugin/DebugBar.php old mode 100644 new mode 100755 From 6f1c77a57ede3ccc358710d2b752fbee540b2965 Mon Sep 17 00:00:00 2001 From: gmorel Date: Mon, 16 Sep 2013 22:41:17 +0200 Subject: [PATCH 002/100] WIP : Coupon functional tests --- .../functionnal/casperjs/exe/00_parameters.js | 5 - .../casperjs/exe/31_coupons_rule.js | 618 +++++++++--------- 2 files changed, 309 insertions(+), 314 deletions(-) diff --git a/tests/functionnal/casperjs/exe/00_parameters.js b/tests/functionnal/casperjs/exe/00_parameters.js index df5470229..29bf20a25 100644 --- a/tests/functionnal/casperjs/exe/00_parameters.js +++ b/tests/functionnal/casperjs/exe/00_parameters.js @@ -7,11 +7,6 @@ var thelia2_login_coupon_create_url = thelia2_base_url + 'admin/coupon/create/'; var thelia2_login_coupon_read_url = thelia2_base_url + 'admin/coupon/read/1'; var thelia2_login_coupon_update_url = thelia2_base_url + 'admin/coupon/update/1'; - - -//var findMyId = /([0-9]+)$/; -//var currentId; - casper.test.comment('Variables are set'); casper.test.done(0); diff --git a/tests/functionnal/casperjs/exe/31_coupons_rule.js b/tests/functionnal/casperjs/exe/31_coupons_rule.js index 66706aada..4b9ada4ba 100644 --- a/tests/functionnal/casperjs/exe/31_coupons_rule.js +++ b/tests/functionnal/casperjs/exe/31_coupons_rule.js @@ -1,312 +1,312 @@ -//// -////var casper = require('casper').create({ -//// viewportSize:{ -//// width:1024, height:768 -//// }, -//// pageSettings:{ -//// userAgent:'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.79 Safari/535.11' -//// }, -//// verbose:true -////}); // -//casper.test.comment('Testing coupons rules'); -// -////UPDATE COUPON RULE -//casper.start(thelia2_login_coupon_update_url, function() { -// this.test.assertHttpStatus(200); -// this.test.comment('Now on : ' + this.getCurrentUrl()); -// this.capture('tests/functionnal/casperjs/screenshot/coupons/init.png'); -// this.test.comment('COUPON RULE - EDIT'); -// this.test.assertTitle('Update coupon - Thelia Back Office', 'Web page title OK'); -// this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(1)', 'If cart total amount is superior to 40 EUR','1) 1st default rule found'); -// this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(2)', 'If cart total amount is inferior to 400 EUR','1) 2nd default rule found'); -// -// // Create rule -// this.evaluate(function() { -// $('#category-rule').val('thelia.constraint.rule.available_for_x_articles').change(); -// return true; +//var casper = require('casper').create({ +// viewportSize:{ +// width:1024, height:768 +// }, +// pageSettings:{ +// userAgent:'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.79 Safari/535.11' +// }, +// verbose:true +//}); + +casper.test.comment('Testing coupons rules'); + +//UPDATE COUPON RULE +casper.start(thelia2_login_coupon_update_url, function() { + this.test.assertHttpStatus(200); + this.test.comment('Now on : ' + this.getCurrentUrl()); + this.capture('tests/functionnal/casperjs/screenshot/coupons/init.png'); + this.test.comment('COUPON RULE - EDIT'); + this.test.assertTitle('Update coupon - Thelia Back Office', 'Web page title OK'); + this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(1)', 'If cart total amount is superior to 40 EUR','1) 1st default rule found'); + this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(2)', 'If cart total amount is inferior to 400 EUR','1) 2nd default rule found'); + + // Create rule + this.evaluate(function() { + $('#category-rule').val('thelia.constraint.rule.available_for_x_articles').change(); + return true; + }); + this.capture('tests/functionnal/casperjs/screenshot/coupons/rule-selected.png'); +}); + +casper.wait(1000, function() { + this.echo("\nWaiting...."); +}); + +// Test Rule updating +casper.then(function(){ + this.evaluate(function() { + $('#quantity-operator').val('>=').change(); + return true; + }); + this.sendKeys('input#quantity-value', '4'); + this.click('#constraint-save-btn'); +}); + +casper.wait(1000, function() { + this.echo("\nWaiting...."); +}); + +casper.then(function(){ + this.capture('tests/functionnal/casperjs/screenshot/coupons/rule-added.png'); + this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(1)', 'If cart total amount is superior to 40 EUR','2) 1st default rule found'); + this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(2)', 'If cart total amount is inferior to 400 EUR','2) 2nd default rule found'); + this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(3)', ' If cart products quantity is superior or equal to 4','2) 3rd rule found'); + + // Click on Edit button + this.click('tbody#constraint-list tr:nth-child(3) .constraint-update-btn'); +}); + +casper.wait(2000, function() { + this.echo("\nWaiting...."); +}); + +casper.then(function(){ + this.evaluate(function() { + $('#quantity-operator').val('==').change(); + return true; + }); + + // Removing old value +// casper.evaluate(function triggerKeyDownEvent() { +// var e = $.Event("keydown"); +// e.which = 8; +// e.keyCode = 8; +// $("#quantity-value").trigger(e); // }); -// this.capture('tests/functionnal/casperjs/screenshot/coupons/rule-selected.png'); -//}); -// -//casper.wait(1000, function() { -// this.echo("\nWaiting...."); -//}); -// -//// Test Rule updating -//casper.then(function(){ -// this.evaluate(function() { -// $('#quantity-operator').val('>=').change(); -// return true; + this.evaluate(function() { + $("#quantity-value").val('').change(); + return true; + }); + + // Adding new value + this.sendKeys('#quantity-value', '5'); + this.capture('tests/functionnal/casperjs/screenshot/coupons/rule-being-edited.png'); + this.click('#constraint-save-btn'); +}); + +casper.wait(2000, function() { + this.echo("\nWaiting...."); +}); +// Check if updated rule has been saved and list refreshed +casper.then(function(){ + this.capture('tests/functionnal/casperjs/screenshot/coupons/rule-edited.png'); + this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(1)', 'If cart total amount is superior to 40 EUR','3) 1st default rule found'); + this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(2)', 'If cart total amount is inferior to 400 EUR','3) 2nd default rule found'); + this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(3)', 'If cart products quantity is equal to 5','3) 3rd rule updated found'); +}); + +// Check if updated rule has been well saved +casper.thenOpen(thelia2_login_coupon_update_url, function() { + this.test.assertHttpStatus(200); + this.test.comment('Now on : ' + this.getCurrentUrl()); + this.capture('tests/functionnal/casperjs/screenshot/coupons/rule-edited-refreshed.png'); + this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(1)', 'If cart total amount is superior to 40 EUR','4) 1st default rule found'); + this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(2)', 'If cart total amount is inferior to 400 EUR','4) 2nd default rule found'); + this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(3)', 'If cart products quantity is equal to 5','4) 3rd rule updated found'); + + // Click on Delete button + this.click('tbody#constraint-list tr:nth-child(2) .constraint-delete-btn'); +}); + +casper.wait(2000, function() { + this.echo("\nWaiting...."); +}); + +casper.then(function(){ + this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(1)', 'If cart total amount is superior to 40 EUR','5) 1st default rule found'); + this.test.assertSelectorDoesntHaveText('tbody#constraint-list tr:nth-child(2)', 'If cart total amount is inferior to 400 EUR','5) 2nd default rule found'); + this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(2)', 'If cart products quantity is equal to 5','5) 3rd rule updated found'); +}); + +// Check if updated rule has been well saved +casper.thenOpen(thelia2_login_coupon_update_url, function() { + this.test.assertHttpStatus(200); + this.test.comment('Now on : ' + this.getCurrentUrl()); + this.capture('tests/functionnal/casperjs/screenshot/coupons/rule-deleted-refreshed.png'); + this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(1)', 'If cart total amount is superior to 40 EUR','6) 1st default rule found'); + this.test.assertSelectorDoesntHaveText('tbody#constraint-list tr:nth-child(2)', 'If cart total amount is inferior to 400 EUR','6) 2nd default rule found'); + this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(2)', 'If cart products quantity is equal to 5','6) 3rd rule updated found'); +}); + +// Test creating rule that won't be edited +casper.then(function(){ +// Create rule + this.evaluate(function() { + $('#category-rule').val('thelia.constraint.rule.available_for_total_amount').change(); + return true; + }); + this.capture('tests/functionnal/casperjs/screenshot/coupons/rule-selected2.png'); +}); + +casper.wait(2000, function() { + this.echo("\nWaiting...."); +}); + +// Test Rule creation +casper.then(function(){ + this.evaluate(function() { + $('#price-operator').val('<=').change(); + return true; + }); + // Removing old value +// casper.evaluate(function triggerKeyDownEvent() { +// var e = $.Event("keydown"); +// e.which = 8; +// e.keyCode = 8; +// $("input#price-value").trigger(e); // }); -// this.sendKeys('input#quantity-value', '4'); -// this.click('#constraint-save-btn'); -//}); -// -//casper.wait(1000, function() { -// this.echo("\nWaiting...."); -//}); -// -//casper.then(function(){ -// this.capture('tests/functionnal/casperjs/screenshot/coupons/rule-added.png'); -// this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(1)', 'If cart total amount is superior to 40 EUR','2) 1st default rule found'); -// this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(2)', 'If cart total amount is inferior to 400 EUR','2) 2nd default rule found'); -// this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(3)', ' If cart products quantity is superior or equal to 4','2) 3rd rule found'); -// -// // Click on Edit button -// this.click('tbody#constraint-list tr:nth-child(3) .constraint-update-btn'); -//}); -// -//casper.wait(2000, function() { -// this.echo("\nWaiting...."); -//}); -// -//casper.then(function(){ -// this.evaluate(function() { -// $('#quantity-operator').val('==').change(); -// return true; -// }); -// -// // Removing old value -//// casper.evaluate(function triggerKeyDownEvent() { -//// var e = $.Event("keydown"); -//// e.which = 8; -//// e.keyCode = 8; -//// $("#quantity-value").trigger(e); -//// }); -// this.evaluate(function() { -// $("#quantity-value").val('').change(); -// return true; -// }); -// -// // Adding new value -// this.sendKeys('#quantity-value', '5'); -// this.capture('tests/functionnal/casperjs/screenshot/coupons/rule-being-edited.png'); -// this.click('#constraint-save-btn'); -//}); -// -//casper.wait(2000, function() { -// this.echo("\nWaiting...."); -//}); -//// Check if updated rule has been saved and list refreshed -//casper.then(function(){ -// this.capture('tests/functionnal/casperjs/screenshot/coupons/rule-edited.png'); -// this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(1)', 'If cart total amount is superior to 40 EUR','3) 1st default rule found'); -// this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(2)', 'If cart total amount is inferior to 400 EUR','3) 2nd default rule found'); -// this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(3)', 'If cart products quantity is equal to 5','3) 3rd rule updated found'); -//}); -// -//// Check if updated rule has been well saved -//casper.thenOpen(thelia2_login_coupon_update_url, function() { -// this.test.assertHttpStatus(200); -// this.test.comment('Now on : ' + this.getCurrentUrl()); -// this.capture('tests/functionnal/casperjs/screenshot/coupons/rule-edited-refreshed.png'); -// this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(1)', 'If cart total amount is superior to 40 EUR','4) 1st default rule found'); -// this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(2)', 'If cart total amount is inferior to 400 EUR','4) 2nd default rule found'); -// this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(3)', 'If cart products quantity is equal to 5','4) 3rd rule updated found'); -// -// // Click on Delete button -// this.click('tbody#constraint-list tr:nth-child(2) .constraint-delete-btn'); -//}); -// -//casper.wait(2000, function() { -// this.echo("\nWaiting...."); -//}); -// -//casper.then(function(){ -// this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(1)', 'If cart total amount is superior to 40 EUR','5) 1st default rule found'); -// this.test.assertSelectorDoesntHaveText('tbody#constraint-list tr:nth-child(2)', 'If cart total amount is inferior to 400 EUR','5) 2nd default rule found'); -// this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(2)', 'If cart products quantity is equal to 5','5) 3rd rule updated found'); -//}); -// -//// Check if updated rule has been well saved -//casper.thenOpen(thelia2_login_coupon_update_url, function() { -// this.test.assertHttpStatus(200); -// this.test.comment('Now on : ' + this.getCurrentUrl()); -// this.capture('tests/functionnal/casperjs/screenshot/coupons/rule-deleted-refreshed.png'); -// this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(1)', 'If cart total amount is superior to 40 EUR','6) 1st default rule found'); -// this.test.assertSelectorDoesntHaveText('tbody#constraint-list tr:nth-child(2)', 'If cart total amount is inferior to 400 EUR','6) 2nd default rule found'); -// this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(2)', 'If cart products quantity is equal to 5','6) 3rd rule updated found'); -//}); -// -//// Test creating rule that won't be edited -//casper.then(function(){ -//// Create rule -// this.evaluate(function() { -// $('#category-rule').val('thelia.constraint.rule.available_for_total_amount').change(); -// return true; -// }); -// this.capture('tests/functionnal/casperjs/screenshot/coupons/rule-selected2.png'); -//}); -// -//casper.wait(2000, function() { -// this.echo("\nWaiting...."); -//}); -// -//// Test Rule creation -//casper.then(function(){ -// this.evaluate(function() { -// $('#price-operator').val('<=').change(); -// return true; -// }); -// // Removing old value -//// casper.evaluate(function triggerKeyDownEvent() { -//// var e = $.Event("keydown"); -//// e.which = 8; -//// e.keyCode = 8; -//// $("input#price-value").trigger(e); -//// }); -// this.evaluate(function() { -// $("input#price-value").val('').change(); -// return true; -// }); -// -// // Changing 400 to 401 -// this.sendKeys('input#price-value', '401'); -// this.evaluate(function() { -// $('#currency-value').val('GBP').change(); -// return true; -// }); -// this.capture('tests/functionnal/casperjs/screenshot/coupons/rule-saved-edited-before-click-save.png'); -// this.click('#constraint-save-btn'); -//}); -// -//casper.wait(2000, function() { -// this.echo("\nWaiting...."); -//}); -// -//casper.then(function(){ -// this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(1)', 'If cart total amount is superior to 40 EUR','7) 1st default rule found'); -// this.test.assertSelectorDoesntHaveText('tbody#constraint-list tr:nth-child(2)', 'If cart total amount is inferior to 400 EUR','7) 2nd default rule found'); -// this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(2)', 'If cart products quantity is equal to 5','7) 3rd rule updated found'); -// this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(3)', 'If cart total amount is inferior or equal to 401 GBP','7) 4rd rule created found'); -//}); -// -//// Check if created rule has been well saved -//casper.thenOpen(thelia2_login_coupon_update_url, function() { -// this.test.assertHttpStatus(200); -// this.test.comment('Now on : ' + this.getCurrentUrl()); -// this.capture('tests/functionnal/casperjs/screenshot/coupons/rule-added-refreshed.png'); -// this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(1)', 'If cart total amount is superior to 40 EUR','8) 1st default rule found'); -// this.test.assertSelectorDoesntHaveText('tbody#constraint-list tr:nth-child(2)', 'If cart total amount is inferior to 400 EUR','8) 2nd default rule found'); -// this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(2)', 'If cart products quantity is equal to 5','8) 3rd rule updated found'); -// this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(3)', 'If cart total amount is inferior or equal to 401 GBP','8) 4rd rule created found'); -//}); -// -//// Testing deleting all rules -//casper.then(function(){ -//// Click on Delete button -// this.click('tbody#constraint-list tr:nth-child(1) .constraint-delete-btn'); -//}); -//casper.wait(1000, function() { -// this.echo("\nWaiting...."); -//}); -//casper.then(function(){ -//// Click on Delete button -// this.click('tbody#constraint-list tr:nth-child(1) .constraint-delete-btn'); -//}); -//casper.wait(1000, function() { -// this.echo("\nWaiting...."); -//}); -//casper.then(function(){ -//// Click on Delete button -// this.click('tbody#constraint-list tr:nth-child(1) .constraint-delete-btn'); -//}); -//casper.wait(1000, function() { -// this.echo("\nWaiting...."); -//}); -//casper.then(function(){ -// this.capture('tests/functionnal/casperjs/screenshot/coupons/rule-all-deleted.png'); -// this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(1)', 'No conditions','9) 1st default rule found'); -// test.assertDoesntExist('tbody#constraint-list tr:nth-child(2)'); -//}); -// -//// Check if created rule has been well saved -//casper.thenOpen(thelia2_login_coupon_update_url, function() { -// this.test.assertHttpStatus(200); -// this.test.comment('Now on : ' + this.getCurrentUrl()); -// this.capture('tests/functionnal/casperjs/screenshot/coupons/rule-all-deleted-refreshed.png'); -// this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(1)', 'No conditions','10) 1st default rule found'); -// test.assertDoesntExist('tbody#constraint-list tr:nth-child(2)'); -//}); -// -// -//// Test add no condition rule -//casper.then(function(){ -// this.evaluate(function() { -// $('#category-rule').val('thelia.constraint.rule.available_for_x_articles').change(); -// return true; -// }); -//}); -// -//casper.wait(1000, function() { -// this.echo("\nWaiting...."); -//}); -// -//// Test Rule updating -//casper.then(function(){ -// this.evaluate(function() { -// $('#quantity-operator').val('>').change(); -// return true; -// }); -// this.sendKeys('input#quantity-value', '4'); -// this.click('#constraint-save-btn'); -//}); -// -//casper.wait(1000, function() { -// this.echo("\nWaiting...."); -//}); -// -//casper.then(function(){ -// this.capture('tests/functionnal/casperjs/screenshot/coupons/rule-all-deleted.png'); -// this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(1)', 'If cart products quantity is superior to 4', '11) 1st default rule found'); -// test.assertDoesntExist('tbody#constraint-list tr:nth-child(2)'); -//}); -// -//// Check if created rule has been well saved -//casper.thenOpen(thelia2_login_coupon_update_url, function() { -// this.test.assertHttpStatus(200); -// this.test.comment('Now on : ' + this.getCurrentUrl()); -// this.capture('tests/functionnal/casperjs/screenshot/coupons/rule-all-deleted-refreshed.png'); -// this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(1)', 'If cart products quantity is superior to 4','12) 1st default rule found'); -// test.assertDoesntExist('tbody#constraint-list tr:nth-child(2)'); -//}); -// -//casper.then(function(){ -// this.evaluate(function() { -// $('#category-rule').val('thelia.constraint.rule.available_for_everyone').change(); -// return true; -// }); -//}); -// -//casper.wait(1000, function() { -// this.echo("\nWaiting...."); -//}); -// -//// Test Rule updating -//casper.then(function(){ -// this.click('#constraint-save-btn'); -//}); -// -//casper.wait(1000, function() { -// this.echo("\nWaiting...."); -//}); -//casper.then(function(){ -// this.capture('tests/functionnal/casperjs/screenshot/coupons/rule-all-deleted.png'); -// this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(1)', 'No conditions','13) 1st default rule found'); -// test.assertDoesntExist('tbody#constraint-list tr:nth-child(2)'); -//}); -// -//// Check if created rule has been well saved -//casper.thenOpen(thelia2_login_coupon_update_url, function() { -// this.test.assertHttpStatus(200); -// this.test.comment('Now on : ' + this.getCurrentUrl()); -// this.capture('tests/functionnal/casperjs/screenshot/coupons/rule-all-deleted-refreshed.png'); -// this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(1)', 'No conditions','14) 1st default rule found'); -// test.assertDoesntExist('tbody#constraint-list tr:nth-child(2)'); -//}); -// -////RUN -//casper.run(function() { -// this.test.done(); -//}); \ No newline at end of file + this.evaluate(function() { + $("input#price-value").val('').change(); + return true; + }); + + // Changing 400 to 401 + this.sendKeys('input#price-value', '401'); + this.evaluate(function() { + $('#currency-value').val('GBP').change(); + return true; + }); + this.capture('tests/functionnal/casperjs/screenshot/coupons/rule-saved-edited-before-click-save.png'); + this.click('#constraint-save-btn'); +}); + +casper.wait(2000, function() { + this.echo("\nWaiting...."); +}); + +casper.then(function(){ + this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(1)', 'If cart total amount is superior to 40 EUR','7) 1st default rule found'); + this.test.assertSelectorDoesntHaveText('tbody#constraint-list tr:nth-child(2)', 'If cart total amount is inferior to 400 EUR','7) 2nd default rule found'); + this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(2)', 'If cart products quantity is equal to 5','7) 3rd rule updated found'); + this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(3)', 'If cart total amount is inferior or equal to 401 GBP','7) 4rd rule created found'); +}); + +// Check if created rule has been well saved +casper.thenOpen(thelia2_login_coupon_update_url, function() { + this.test.assertHttpStatus(200); + this.test.comment('Now on : ' + this.getCurrentUrl()); + this.capture('tests/functionnal/casperjs/screenshot/coupons/rule-added-refreshed.png'); + this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(1)', 'If cart total amount is superior to 40 EUR','8) 1st default rule found'); + this.test.assertSelectorDoesntHaveText('tbody#constraint-list tr:nth-child(2)', 'If cart total amount is inferior to 400 EUR','8) 2nd default rule found'); + this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(2)', 'If cart products quantity is equal to 5','8) 3rd rule updated found'); + this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(3)', 'If cart total amount is inferior or equal to 401 GBP','8) 4rd rule created found'); +}); + +// Testing deleting all rules +casper.then(function(){ +// Click on Delete button + this.click('tbody#constraint-list tr:nth-child(1) .constraint-delete-btn'); +}); +casper.wait(1000, function() { + this.echo("\nWaiting...."); +}); +casper.then(function(){ +// Click on Delete button + this.click('tbody#constraint-list tr:nth-child(1) .constraint-delete-btn'); +}); +casper.wait(1000, function() { + this.echo("\nWaiting...."); +}); +casper.then(function(){ +// Click on Delete button + this.click('tbody#constraint-list tr:nth-child(1) .constraint-delete-btn'); +}); +casper.wait(1000, function() { + this.echo("\nWaiting...."); +}); +casper.then(function(){ + this.capture('tests/functionnal/casperjs/screenshot/coupons/rule-all-deleted.png'); + this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(1)', 'No conditions','9) 1st default rule found'); + test.assertDoesntExist('tbody#constraint-list tr:nth-child(2)'); +}); + +// Check if created rule has been well saved +casper.thenOpen(thelia2_login_coupon_update_url, function() { + this.test.assertHttpStatus(200); + this.test.comment('Now on : ' + this.getCurrentUrl()); + this.capture('tests/functionnal/casperjs/screenshot/coupons/rule-all-deleted-refreshed.png'); + this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(1)', 'No conditions','10) 1st default rule found'); + test.assertDoesntExist('tbody#constraint-list tr:nth-child(2)'); +}); + + +// Test add no condition rule +casper.then(function(){ + this.evaluate(function() { + $('#category-rule').val('thelia.constraint.rule.available_for_x_articles').change(); + return true; + }); +}); + +casper.wait(1000, function() { + this.echo("\nWaiting...."); +}); + +// Test Rule updating +casper.then(function(){ + this.evaluate(function() { + $('#quantity-operator').val('>').change(); + return true; + }); + this.sendKeys('input#quantity-value', '4'); + this.click('#constraint-save-btn'); +}); + +casper.wait(1000, function() { + this.echo("\nWaiting...."); +}); + +casper.then(function(){ + this.capture('tests/functionnal/casperjs/screenshot/coupons/rule-all-deleted.png'); + this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(1)', 'If cart products quantity is superior to 4', '11) 1st default rule found'); + test.assertDoesntExist('tbody#constraint-list tr:nth-child(2)'); +}); + +// Check if created rule has been well saved +casper.thenOpen(thelia2_login_coupon_update_url, function() { + this.test.assertHttpStatus(200); + this.test.comment('Now on : ' + this.getCurrentUrl()); + this.capture('tests/functionnal/casperjs/screenshot/coupons/rule-all-deleted-refreshed.png'); + this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(1)', 'If cart products quantity is superior to 4','12) 1st default rule found'); + test.assertDoesntExist('tbody#constraint-list tr:nth-child(2)'); +}); + +casper.then(function(){ + this.evaluate(function() { + $('#category-rule').val('thelia.constraint.rule.available_for_everyone').change(); + return true; + }); +}); + +casper.wait(1000, function() { + this.echo("\nWaiting...."); +}); + +// Test Rule updating +casper.then(function(){ + this.click('#constraint-save-btn'); +}); + +casper.wait(1000, function() { + this.echo("\nWaiting...."); +}); +casper.then(function(){ + this.capture('tests/functionnal/casperjs/screenshot/coupons/rule-all-deleted.png'); + this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(1)', 'No conditions','13) 1st default rule found'); + test.assertDoesntExist('tbody#constraint-list tr:nth-child(2)'); +}); + +// Check if created rule has been well saved +casper.thenOpen(thelia2_login_coupon_update_url, function() { + this.test.assertHttpStatus(200); + this.test.comment('Now on : ' + this.getCurrentUrl()); + this.capture('tests/functionnal/casperjs/screenshot/coupons/rule-all-deleted-refreshed.png'); + this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(1)', 'No conditions','14) 1st default rule found'); + test.assertDoesntExist('tbody#constraint-list tr:nth-child(2)'); +}); + +//RUN +casper.run(function() { + this.test.done(); +}); \ No newline at end of file From 73c0a96979501b5c9a3f44e4dd69e6aed824954f Mon Sep 17 00:00:00 2001 From: gmorel Date: Mon, 16 Sep 2013 23:05:46 +0200 Subject: [PATCH 003/100] WIP : Coupon : unit tests on Operator --- .../Tests/Constraint/Rule/OperatorsTest.php | 62 +++++++++++++++++-- 1 file changed, 56 insertions(+), 6 deletions(-) diff --git a/core/lib/Thelia/Tests/Constraint/Rule/OperatorsTest.php b/core/lib/Thelia/Tests/Constraint/Rule/OperatorsTest.php index 0b29baa62..d5a340d2b 100644 --- a/core/lib/Thelia/Tests/Constraint/Rule/OperatorsTest.php +++ b/core/lib/Thelia/Tests/Constraint/Rule/OperatorsTest.php @@ -23,7 +23,6 @@ namespace Thelia\Coupon; -use Thelia\Constraint\Validator\QuantityParam; use Thelia\Constraint\Rule\Operators; /** @@ -48,14 +47,58 @@ class OperatorsTest extends \PHPUnit_Framework_TestCase { } - public function testSomething() +// public function testSomething() +// { +// // Stop here and mark this test as incomplete. +// $this->markTestIncomplete( +// 'This test has not been implemented yet.' +// ); +// } + + public function testOperatorI18n() { - // Stop here and mark this test as incomplete. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); + $stubTranslator = $this->getMockBuilder('\Symfony\Component\Translation\Translator') + ->disableOriginalConstructor() + ->getMock(); + + $stubTranslator->expects($this->any()) + ->method('trans') + ->will($this->returnCallback((array($this, 'callbackI18n')))); + + $actual = Operators::getI18n($stubTranslator, Operators::INFERIOR); + $expected = 'inferior to'; + $this->assertEquals($expected, $actual); + + $actual = Operators::getI18n($stubTranslator, Operators::INFERIOR_OR_EQUAL); + $expected = 'inferior or equal to'; + $this->assertEquals($expected, $actual); + + $actual = Operators::getI18n($stubTranslator, Operators::EQUAL); + $expected = 'equal to'; + $this->assertEquals($expected, $actual); + + $actual = Operators::getI18n($stubTranslator, Operators::SUPERIOR_OR_EQUAL); + $expected = 'superior or equal to'; + $this->assertEquals($expected, $actual); + + $actual = Operators::getI18n($stubTranslator, Operators::SUPERIOR); + $expected = 'superior to'; + $this->assertEquals($expected, $actual); + + $actual = Operators::getI18n($stubTranslator, Operators::DIFFERENT); + $expected = 'different from'; + $this->assertEquals($expected, $actual); + + $actual = Operators::getI18n($stubTranslator, Operators::IN); + $expected = 'in'; + $this->assertEquals($expected, $actual); + + $actual = Operators::getI18n($stubTranslator, Operators::OUT); + $expected = 'not in'; + $this->assertEquals($expected, $actual); } + // /** // * // * @covers Thelia\Coupon\Rule\Operator::isValidAccordingToOperator @@ -424,4 +467,11 @@ class OperatorsTest extends \PHPUnit_Framework_TestCase { } + function callbackI18n() { + $args = func_get_args(); + + return $args[0]; + } } + + From ad48823b4819fdfe0a7a615fce8fabd38b9a991a Mon Sep 17 00:00:00 2001 From: gmorel Date: Mon, 16 Sep 2013 23:51:47 +0200 Subject: [PATCH 004/100] WIP : Coupon : unit tests on Rules --- ...=> AvailableForTotalAmountManagerTest.php} | 259 +++++++++++------- ...p => AvailableForXArticlesManagerTest.php} | 122 +++++---- .../Tests/Constraint/Rule/OperatorsTest.php | 4 + 3 files changed, 236 insertions(+), 149 deletions(-) rename core/lib/Thelia/Tests/Constraint/Rule/{AvailableForTotalAmountTest.php => AvailableForTotalAmountManagerTest.php} (78%) rename core/lib/Thelia/Tests/Constraint/Rule/{AvailableForXArticlesTest.php => AvailableForXArticlesManagerTest.php} (89%) diff --git a/core/lib/Thelia/Tests/Constraint/Rule/AvailableForTotalAmountTest.php b/core/lib/Thelia/Tests/Constraint/Rule/AvailableForTotalAmountManagerTest.php similarity index 78% rename from core/lib/Thelia/Tests/Constraint/Rule/AvailableForTotalAmountTest.php rename to core/lib/Thelia/Tests/Constraint/Rule/AvailableForTotalAmountManagerTest.php index c3f7249df..4ad790a5b 100644 --- a/core/lib/Thelia/Tests/Constraint/Rule/AvailableForTotalAmountTest.php +++ b/core/lib/Thelia/Tests/Constraint/Rule/AvailableForTotalAmountManagerTest.php @@ -26,19 +26,20 @@ namespace Thelia\Coupon; use Thelia\Constraint\ConstraintValidator; use Thelia\Constraint\Rule\AvailableForTotalAmountManager; use Thelia\Constraint\Rule\Operators; +use Thelia\Exception\InvalidRuleValueException; /** * Created by JetBrains PhpStorm. * Date: 8/19/13 * Time: 3:24 PM * - * Unit Test AvailableForTotalAmount Class + * Unit Test AvailableForTotalAmountManager Class * * @package Constraint * @author Guillaume MOREL * */ -class AvailableForTotalAmountTest extends \PHPUnit_Framework_TestCase +class AvailableForTotalAmountManagerTest extends \PHPUnit_Framework_TestCase { /** @var CouponAdapterInterface $stubTheliaAdapter */ protected $stubTheliaAdapter = null; @@ -49,108 +50,168 @@ class AvailableForTotalAmountTest extends \PHPUnit_Framework_TestCase */ protected function setUp() { -// /** @var CouponAdapterInterface $stubTheliaAdapter */ -// $this->stubTheliaAdapter = $this->generateValidCouponBaseAdapterMock(); + } -// /** -// * Generate valid CouponBaseAdapter -// * -// * @param float $cartTotalPrice Total amount of the current Cart -// * -// * @return CouponAdapterInterface -// */ -// protected function generateValidCouponBaseAdapterMock($cartTotalPrice = 421.23) -// { -// /** @var CouponAdapterInterface $stubTheliaAdapter */ -// $stubTheliaAdapter = $this->getMock( -// 'Thelia\Coupon\CouponBaseAdapter', -// array('getCartTotalPrice'), -// array() -// ); -// $stubTheliaAdapter->expects($this->any()) -// ->method('getCartTotalPrice') -// ->will($this->returnValue($cartTotalPrice)); -// -// return $stubTheliaAdapter; -// } + /** + * Check if validity test on BackOffice inputs are working + * + * @covers Thelia\Coupon\Rule\AvailableForTotalAmountManager::setValidators + * @expectedException \Thelia\Exception\InvalidRuleOperatorException + * + */ + public function testInValidBackOfficeInputOperator() + { + $stubAdapter = $this->getMockBuilder('\Thelia\Coupon\CouponBaseAdapter') + ->disableOriginalConstructor() + ->getMock(); -// /** -// * Check if validity test on BackOffice inputs are working -// * -// * @covers Thelia\Coupon\Rule\AvailableForTotalAmount::checkBackOfficeInput -// * -// */ -// public function testValidBackOfficeInput() -// { -// $adapter = new CouponBaseAdapter(); -// -// $validators = array( -// AvailableForTotalAmount::PARAM1_PRICE => new RuleValidator( -// Operators::SUPERIOR, -// new PriceParam( -// $adapter, 421.23, 'EUR' -// ) -// ) -// ); -// $rule = new AvailableForTotalAmount($adapter, $validators); -// -// $expected = true; -// $actual = $rule->checkBackOfficeInput(); -// $this->assertEquals($expected, $actual); -// } + $stubAdapter->expects($this->any()) + ->method('getCartTotalPrice') + ->will($this->returnValue(399)); + $stubAdapter->expects($this->any()) + ->method('getCheckoutCurrency') + ->will($this->returnValue('EUR')); + $stubAdapter->expects($this->any()) + ->method('getConstraintValidator') + ->will($this->returnValue(new ConstraintValidator())); -// /** -// * Check if validity test on BackOffice inputs are working -// * -// * @covers Thelia\Coupon\Rule\AvailableForTotalAmount::checkBackOfficeInput -// * @expectedException \Thelia\Exception\InvalidRuleOperatorException -// * -// */ -// public function testInValidBackOfficeInputOperator() -// { -// $adapter = new CouponBaseAdapter(); -// -// $validators = array( -// AvailableForTotalAmount::PARAM1_PRICE => new RuleValidator( -// 'X', -// new PriceParam( -// $adapter, 421.23, 'EUR' -// ) -// ) -// ); -// -// $rule = new AvailableForTotalAmount($adapter, $validators); -// -// $expected = false; -// $actual = $rule->checkBackOfficeInput(); -// $this->assertEquals($expected, $actual); -// } + $rule1 = new AvailableForTotalAmountManager($stubAdapter); + $operators = array( + AvailableForTotalAmountManager::INPUT1 => Operators::IN, + AvailableForTotalAmountManager::INPUT2 => Operators::EQUAL + ); + $values = array( + AvailableForTotalAmountManager::INPUT1 => '400', + AvailableForTotalAmountManager::INPUT2 => 'EUR'); + $rule1->setValidatorsFromForm($operators, $values); -// /** -// * Check if validity test on BackOffice inputs are working -// * -// * @covers Thelia\Coupon\Rule\AvailableForTotalAmount::checkBackOfficeInput -// * @expectedException \ErrorException -// * -// */ -// public function testInValidBackOfficeInputValue() -// { -// $adapter = $this->generateValidCouponBaseAdapterMock(); -// -// $validators = array( -// AvailableForTotalAmount::PARAM1_PRICE => new RuleValidator( -// Operators::SUPERIOR, -// 421 -// ) -// ); -// -// $rule = new AvailableForTotalAmount($adapter, $validators); -// -// $expected = false; -// $actual = $rule->checkBackOfficeInput(); -// $this->assertEquals($expected, $actual); -// } + $isValid = $rule1->isMatching(); + + $expected = true; + $actual =$isValid; + $this->assertEquals($expected, $actual); + } + + /** + * Check if validity test on BackOffice inputs are working + * + * @covers Thelia\Coupon\Rule\AvailableForTotalAmountManager::setValidators + * @expectedException \Thelia\Exception\InvalidRuleOperatorException + * + */ + public function testInValidBackOfficeInputOperator2() + { + $stubAdapter = $this->getMockBuilder('\Thelia\Coupon\CouponBaseAdapter') + ->disableOriginalConstructor() + ->getMock(); + + $stubAdapter->expects($this->any()) + ->method('getCartTotalPrice') + ->will($this->returnValue(399)); + $stubAdapter->expects($this->any()) + ->method('getCheckoutCurrency') + ->will($this->returnValue('EUR')); + $stubAdapter->expects($this->any()) + ->method('getConstraintValidator') + ->will($this->returnValue(new ConstraintValidator())); + + $rule1 = new AvailableForTotalAmountManager($stubAdapter); + $operators = array( + AvailableForTotalAmountManager::INPUT1 => Operators::SUPERIOR, + AvailableForTotalAmountManager::INPUT2 => Operators::INFERIOR + ); + $values = array( + AvailableForTotalAmountManager::INPUT1 => '400', + AvailableForTotalAmountManager::INPUT2 => 'EUR'); + $rule1->setValidatorsFromForm($operators, $values); + + $isValid = $rule1->isMatching(); + + $expected = true; + $actual =$isValid; + $this->assertEquals($expected, $actual); + } + + /** + * Check if validity test on BackOffice inputs are working + * + * @covers Thelia\Coupon\Rule\AvailableForTotalAmountManager::setValidators + * @expectedException \Thelia\Exception\InvalidRuleValueException + * + */ + public function testInValidBackOfficeInputValue() + { + $stubAdapter = $this->getMockBuilder('\Thelia\Coupon\CouponBaseAdapter') + ->disableOriginalConstructor() + ->getMock(); + + $stubAdapter->expects($this->any()) + ->method('getCartTotalPrice') + ->will($this->returnValue(399)); + $stubAdapter->expects($this->any()) + ->method('getCheckoutCurrency') + ->will($this->returnValue('EUR')); + $stubAdapter->expects($this->any()) + ->method('getConstraintValidator') + ->will($this->returnValue(new ConstraintValidator())); + + $rule1 = new AvailableForTotalAmountManager($stubAdapter); + $operators = array( + AvailableForTotalAmountManager::INPUT1 => Operators::SUPERIOR, + AvailableForTotalAmountManager::INPUT2 => Operators::EQUAL + ); + $values = array( + AvailableForTotalAmountManager::INPUT1 => 'X', + AvailableForTotalAmountManager::INPUT2 => 'EUR'); + $rule1->setValidatorsFromForm($operators, $values); + + $isValid = $rule1->isMatching(); + + $expected = true; + $actual =$isValid; + $this->assertEquals($expected, $actual); + } + + /** + * Check if validity test on BackOffice inputs are working + * + * @covers Thelia\Coupon\Rule\AvailableForTotalAmountManager::setValidators + * @expectedException \Thelia\Exception\InvalidRuleValueException + * + */ + public function testInValidBackOfficeInputValue2() + { + $stubAdapter = $this->getMockBuilder('\Thelia\Coupon\CouponBaseAdapter') + ->disableOriginalConstructor() + ->getMock(); + + $stubAdapter->expects($this->any()) + ->method('getCartTotalPrice') + ->will($this->returnValue(399)); + $stubAdapter->expects($this->any()) + ->method('getCheckoutCurrency') + ->will($this->returnValue('EUR')); + $stubAdapter->expects($this->any()) + ->method('getConstraintValidator') + ->will($this->returnValue(new ConstraintValidator())); + + $rule1 = new AvailableForTotalAmountManager($stubAdapter); + $operators = array( + AvailableForTotalAmountManager::INPUT1 => Operators::SUPERIOR, + AvailableForTotalAmountManager::INPUT2 => Operators::EQUAL + ); + $values = array( + AvailableForTotalAmountManager::INPUT1 => 400, + AvailableForTotalAmountManager::INPUT2 => 'FLA'); + $rule1->setValidatorsFromForm($operators, $values); + + $isValid = $rule1->isMatching(); + + $expected = true; + $actual =$isValid; + $this->assertEquals($expected, $actual); + } /** * Check if test inferior operator is working diff --git a/core/lib/Thelia/Tests/Constraint/Rule/AvailableForXArticlesTest.php b/core/lib/Thelia/Tests/Constraint/Rule/AvailableForXArticlesManagerTest.php similarity index 89% rename from core/lib/Thelia/Tests/Constraint/Rule/AvailableForXArticlesTest.php rename to core/lib/Thelia/Tests/Constraint/Rule/AvailableForXArticlesManagerTest.php index 4ecbcb8ac..12deb34b8 100644 --- a/core/lib/Thelia/Tests/Constraint/Rule/AvailableForXArticlesTest.php +++ b/core/lib/Thelia/Tests/Constraint/Rule/AvailableForXArticlesManagerTest.php @@ -33,13 +33,13 @@ use Thelia\Constraint\Rule\SerializableRule; * Date: 8/19/13 * Time: 3:24 PM * - * Unit Test AvailableForXArticles Class + * Unit Test AvailableForXArticlesManager Class * * @package Constraint * @author Guillaume MOREL * */ -class AvailableForXArticlesTest extends \PHPUnit_Framework_TestCase +class AvailableForXArticlesManagerTest extends \PHPUnit_Framework_TestCase { // /** @var CouponAdapterInterface $stubTheliaAdapter */ @@ -51,30 +51,8 @@ class AvailableForXArticlesTest extends \PHPUnit_Framework_TestCase */ protected function setUp() { -// /** @var CouponAdapterInterface $stubTheliaAdapter */ -// $this->stubTheliaAdapter = $this->generateValidCouponBaseAdapterMock(); - } -// /** -// * Generate valid CouponBaseAdapter -// * -// * @param int $nbArticlesInCart Total articles in the current Cart -// * -// * @return CouponAdapterInterface -// */ -// protected function generateValidCouponBaseAdapterMock($nbArticlesInCart = 4) -// { -// /** @var CouponAdapterInterface $stubTheliaAdapter */ -// $stubTheliaAdapter = $this->getMockBuilder('\Thelia\Coupon\CouponBaseAdapter') -// ->disableOriginalConstructor() -// ->setMethods(array('getNbArticlesInCart')) -// ->getMock(); -// $stubTheliaAdapter->expects($this->any()) -// ->method('getNbArticlesInCart') -// ->will($this->returnValue($nbArticlesInCart)); -// -// return $stubTheliaAdapter; -// } + } // /** // * Check if validity test on BackOffice inputs are working @@ -100,31 +78,75 @@ class AvailableForXArticlesTest extends \PHPUnit_Framework_TestCase // $this->assertEquals($expected, $actual); // } -// /** -// * Check if validity test on BackOffice inputs are working -// * -// * @covers Thelia\Coupon\Rule\AvailableForXArticles::checkBackOfficeInput -// * @expectedException \Thelia\Exception\InvalidRuleValueException -// */ -// public function testInValidBackOfficeInputFloat() -// { -// $adapter = $this->stubTheliaAdapter; -// -// $validators = array( -// AvailableForXArticles::PARAM1_QUANTITY => new RuleValidator( -// Operators::SUPERIOR, -// new QuantityParam( -// $adapter, -// 4.5 -// ) -// ) -// ); -// $rule = new AvailableForXArticles($adapter, $validators); -// -// $expected = false; -// $actual = $rule->checkBackOfficeInput(); -// $this->assertEquals($expected, $actual); -// } + /** + * Check if validity test on BackOffice inputs are working + * + * @covers Thelia\Coupon\Rule\AvailableForXArticles::checkBackOfficeInput + * @expectedException \Thelia\Exception\InvalidRuleOperatorException + */ + public function testInValidBackOfficeInputOperator() + { + $stubAdapter = $this->getMockBuilder('\Thelia\Coupon\CouponBaseAdapter') + ->disableOriginalConstructor() + ->getMock(); + + $stubAdapter->expects($this->any()) + ->method('getNbArticlesInCart') + ->will($this->returnValue(4)); + $stubAdapter->expects($this->any()) + ->method('getConstraintValidator') + ->will($this->returnValue(new ConstraintValidator())); + + $rule1 = new AvailableForXArticlesManager($stubAdapter); + $operators = array( + AvailableForXArticlesManager::INPUT1 => Operators::IN + ); + $values = array( + AvailableForXArticlesManager::INPUT1 => 5 + ); + $rule1->setValidatorsFromForm($operators, $values); + + $isValid = $rule1->isMatching(); + + $expected = true; + $actual =$isValid; + $this->assertEquals($expected, $actual); + } + + /** + * Check if validity test on BackOffice inputs are working + * + * @covers Thelia\Coupon\Rule\AvailableForXArticles::checkBackOfficeInput + * @expectedException \Thelia\Exception\InvalidRuleValueException + */ + public function testInValidBackOfficeInputValue() + { + $stubAdapter = $this->getMockBuilder('\Thelia\Coupon\CouponBaseAdapter') + ->disableOriginalConstructor() + ->getMock(); + + $stubAdapter->expects($this->any()) + ->method('getNbArticlesInCart') + ->will($this->returnValue(4)); + $stubAdapter->expects($this->any()) + ->method('getConstraintValidator') + ->will($this->returnValue(new ConstraintValidator())); + + $rule1 = new AvailableForXArticlesManager($stubAdapter); + $operators = array( + AvailableForXArticlesManager::INPUT1 => Operators::SUPERIOR + ); + $values = array( + AvailableForXArticlesManager::INPUT1 => 'X' + ); + $rule1->setValidatorsFromForm($operators, $values); + + $isValid = $rule1->isMatching(); + + $expected = true; + $actual =$isValid; + $this->assertEquals($expected, $actual); + } // /** // * Check if validity test on BackOffice inputs are working diff --git a/core/lib/Thelia/Tests/Constraint/Rule/OperatorsTest.php b/core/lib/Thelia/Tests/Constraint/Rule/OperatorsTest.php index d5a340d2b..6cdcb3430 100644 --- a/core/lib/Thelia/Tests/Constraint/Rule/OperatorsTest.php +++ b/core/lib/Thelia/Tests/Constraint/Rule/OperatorsTest.php @@ -96,6 +96,10 @@ class OperatorsTest extends \PHPUnit_Framework_TestCase $actual = Operators::getI18n($stubTranslator, Operators::OUT); $expected = 'not in'; $this->assertEquals($expected, $actual); + + $actual = Operators::getI18n($stubTranslator, 'unexpected operator'); + $expected = 'unexpected operator'; + $this->assertEquals($expected, $actual); } From a7ef4300aa16ba189a9d383ed1d2f2ba5f8c374b Mon Sep 17 00:00:00 2001 From: gmorel Date: Mon, 16 Sep 2013 23:56:31 +0200 Subject: [PATCH 005/100] WIP : Coupon : unit tests on Rules --- .../Rule/AvailableForTotalAmountManager.php | 12 +-- .../Rule/AvailableForXArticlesManager.php | 9 ++- core/lib/Thelia/Constraint/Rule/Operators.php | 56 ------------- .../Rule/AvailableForXArticlesManagerTest.php | 80 +------------------ 4 files changed, 13 insertions(+), 144 deletions(-) diff --git a/core/lib/Thelia/Constraint/Rule/AvailableForTotalAmountManager.php b/core/lib/Thelia/Constraint/Rule/AvailableForTotalAmountManager.php index 30bc86ad6..d8a4755f6 100644 --- a/core/lib/Thelia/Constraint/Rule/AvailableForTotalAmountManager.php +++ b/core/lib/Thelia/Constraint/Rule/AvailableForTotalAmountManager.php @@ -111,8 +111,8 @@ class AvailableForTotalAmountManager extends CouponRuleAbstract $this->availableOperators[self::INPUT1] ); if (!$isOperator1Legit) { - throw new \InvalidArgumentException( - 'Operator for price field is not legit' + throw new InvalidRuleOperatorException( + get_class(), 'price' ); } @@ -121,15 +121,15 @@ class AvailableForTotalAmountManager extends CouponRuleAbstract $this->availableOperators[self::INPUT2] ); if (!$isOperator1Legit) { - throw new \InvalidArgumentException( - 'Operator for currency field is not legit' + throw new InvalidRuleOperatorException( + get_class(), 'price' ); } $floatType = new FloatType(); if (!$floatType->isValid($priceValue) || $priceValue <= 0) { - throw new \InvalidArgumentException( - 'Value for price field is not legit' + throw new InvalidRuleValueException( + get_class(), 'price' ); } diff --git a/core/lib/Thelia/Constraint/Rule/AvailableForXArticlesManager.php b/core/lib/Thelia/Constraint/Rule/AvailableForXArticlesManager.php index 572d39b1d..f73d5e2be 100644 --- a/core/lib/Thelia/Constraint/Rule/AvailableForXArticlesManager.php +++ b/core/lib/Thelia/Constraint/Rule/AvailableForXArticlesManager.php @@ -30,6 +30,7 @@ use Thelia\Constraint\Validator\QuantityParam; use Thelia\Constraint\Validator\RuleValidator; use Thelia\Coupon\CouponAdapterInterface; use Thelia\Exception\InvalidRuleException; +use Thelia\Exception\InvalidRuleOperatorException; use Thelia\Exception\InvalidRuleValueException; use Thelia\Type\FloatType; @@ -98,14 +99,14 @@ class AvailableForXArticlesManager extends CouponRuleAbstract $this->availableOperators[self::INPUT1] ); if (!$isOperator1Legit) { - throw new \InvalidArgumentException( - 'Operator for quantity field is not legit' + throw new InvalidRuleOperatorException( + get_class(), 'quantity' ); } if ((int) $quantityValue <= 0) { - throw new \InvalidArgumentException( - 'Value for quantity field is not legit' + throw new InvalidRuleValueException( + get_class(), 'quantity' ); } diff --git a/core/lib/Thelia/Constraint/Rule/Operators.php b/core/lib/Thelia/Constraint/Rule/Operators.php index 2ed5c2909..a6c457c75 100644 --- a/core/lib/Thelia/Constraint/Rule/Operators.php +++ b/core/lib/Thelia/Constraint/Rule/Operators.php @@ -56,62 +56,6 @@ abstract class Operators /** Param1 is not in Param2 */ CONST OUT = 'out'; -// /** -// * Check if a parameter is valid against a ComparableInterface from its operator -// * -// * @param mixed $a Parameter to validate -// * @param string $operator Operator to validate against -// * @param ComparableInterface $b Comparable to validate against -// * -// * @return bool -// */ -// public static function isValid($a, $operator, ComparableInterface $b) -// { -// $ret = false; -// -// try { -// $comparison = $b->compareTo($a); -// } catch (\Exception $e) { -// return false; -// } -// -// switch ($operator) { -// case self::INFERIOR: -// if ($comparison == 1) { -// return true; -// } -// break; -// case self::INFERIOR_OR_EQUAL: -// if ($comparison == 1 || $comparison == 0) { -// return true; -// } -// break; -// case self::EQUAL: -// if ($comparison == 0) { -// return true; -// } -// break; -// case self::SUPERIOR_OR_EQUAL: -// if ($comparison == -1 || $comparison == 0) { -// return true; -// } -// break; -// case self::SUPERIOR: -// if ($comparison == -1) { -// return true; -// } -// break; -// case self::DIFFERENT: -// if ($comparison != 0) { -// return true; -// } -// break; -// default: -// } -// -// return $ret; -// } - /** * Get operator translation * diff --git a/core/lib/Thelia/Tests/Constraint/Rule/AvailableForXArticlesManagerTest.php b/core/lib/Thelia/Tests/Constraint/Rule/AvailableForXArticlesManagerTest.php index 12deb34b8..4937826b6 100644 --- a/core/lib/Thelia/Tests/Constraint/Rule/AvailableForXArticlesManagerTest.php +++ b/core/lib/Thelia/Tests/Constraint/Rule/AvailableForXArticlesManagerTest.php @@ -54,34 +54,10 @@ class AvailableForXArticlesManagerTest extends \PHPUnit_Framework_TestCase } -// /** -// * Check if validity test on BackOffice inputs are working -// * -// * @covers Thelia\Coupon\Rule\AvailableForXArticles::checkBackOfficeInput -// * -// */ -// public function testValidBackOfficeInput() -// { -// $translator = $this->getMockBuilder('\Thelia\Core\Translation\Translator') -// ->disableOriginalConstructor() -// ->getMock(); -// -// $rule = new AvailableForXArticles($translator); -// $operators = array(AvailableForXArticles::PARAM1_QUANTITY => Operators::SUPERIOR); -// $values = array( -// AvailableForXArticles::PARAM1_QUANTITY => 4 -// ); -// $rule->populateFromForm($operators, $values); -// -// $expected = true; -// $actual = $rule->checkBackOfficeInput(); -// $this->assertEquals($expected, $actual); -// } - /** * Check if validity test on BackOffice inputs are working * - * @covers Thelia\Coupon\Rule\AvailableForXArticles::checkBackOfficeInput + * @covers Thelia\Coupon\Rule\AvailableForXArticlesManager::setValidators * @expectedException \Thelia\Exception\InvalidRuleOperatorException */ public function testInValidBackOfficeInputOperator() @@ -116,7 +92,7 @@ class AvailableForXArticlesManagerTest extends \PHPUnit_Framework_TestCase /** * Check if validity test on BackOffice inputs are working * - * @covers Thelia\Coupon\Rule\AvailableForXArticles::checkBackOfficeInput + * @covers Thelia\Coupon\Rule\AvailableForXArticlesManager::setValidators * @expectedException \Thelia\Exception\InvalidRuleValueException */ public function testInValidBackOfficeInputValue() @@ -148,58 +124,6 @@ class AvailableForXArticlesManagerTest extends \PHPUnit_Framework_TestCase $this->assertEquals($expected, $actual); } -// /** -// * Check if validity test on BackOffice inputs are working -// * -// * @covers Thelia\Coupon\Rule\AvailableForXArticles::checkBackOfficeInput -// * @expectedException \Thelia\Exception\InvalidRuleValueException -// */ -// public function testInValidBackOfficeInputNegative() -// { -// $adapter = $this->stubTheliaAdapter; -// -// $validators = array( -// AvailableForXArticles::PARAM1_QUANTITY => new RuleValidator( -// Operators::SUPERIOR, -// new QuantityParam( -// $adapter, -// -1 -// ) -// ) -// ); -// $rule = new AvailableForXArticles($adapter, $validators); -// -// $expected = false; -// $actual = $rule->checkBackOfficeInput(); -// $this->assertEquals($expected, $actual); -// } - -// /** -// * Check if validity test on BackOffice inputs are working -// * -// * @covers Thelia\Coupon\Rule\AvailableForXArticles::checkBackOfficeInput -// * @expectedException \Thelia\Exception\InvalidRuleValueException -// */ -// public function testInValidBackOfficeInputString() -// { -// $adapter = $this->stubTheliaAdapter; -// -// $validators = array( -// AvailableForXArticles::PARAM1_QUANTITY => new RuleValidator( -// Operators::SUPERIOR, -// new QuantityParam( -// $adapter, -// 'bad' -// ) -// ) -// ); -// $rule = new AvailableForXArticles($adapter, $validators); -// -// $expected = false; -// $actual = $rule->checkBackOfficeInput(); -// $this->assertEquals($expected, $actual); -// } - /** * Check if test inferior operator is working * From 3b249dc21c65bea8836a275d867b1d00d516cb4e Mon Sep 17 00:00:00 2001 From: gmorel Date: Tue, 17 Sep 2013 11:41:09 +0200 Subject: [PATCH 006/100] Working : Coupon : Unit testing --- .../Rule/AvailableForTotalAmountManager.php | 11 +- .../Constraint/Rule/CouponRuleAbstract.php | 51 +++ .../Controller/Admin/CouponController.php | 2 + .../Thelia/Coupon/CouponAdapterInterface.php | 7 + core/lib/Thelia/Coupon/CouponBaseAdapter.php | 15 + .../AvailableForTotalAmountManagerTest.php | 290 ++++-------------- 6 files changed, 135 insertions(+), 241 deletions(-) diff --git a/core/lib/Thelia/Constraint/Rule/AvailableForTotalAmountManager.php b/core/lib/Thelia/Constraint/Rule/AvailableForTotalAmountManager.php index d8a4755f6..c4087e734 100644 --- a/core/lib/Thelia/Constraint/Rule/AvailableForTotalAmountManager.php +++ b/core/lib/Thelia/Constraint/Rule/AvailableForTotalAmountManager.php @@ -126,14 +126,11 @@ class AvailableForTotalAmountManager extends CouponRuleAbstract ); } - $floatType = new FloatType(); - if (!$floatType->isValid($priceValue) || $priceValue <= 0) { - throw new InvalidRuleValueException( - get_class(), 'price' - ); - } + $this->isPriceValid($priceValue); + + + $this->IsCurrencyValid($currencyValue); - // @todo check currency is legit or not $this->operators = array( self::INPUT1 => $priceOperator, diff --git a/core/lib/Thelia/Constraint/Rule/CouponRuleAbstract.php b/core/lib/Thelia/Constraint/Rule/CouponRuleAbstract.php index 0986daa50..ffd1eb0f3 100644 --- a/core/lib/Thelia/Constraint/Rule/CouponRuleAbstract.php +++ b/core/lib/Thelia/Constraint/Rule/CouponRuleAbstract.php @@ -31,6 +31,9 @@ use Thelia\Constraint\Validator\ComparableInterface; use Thelia\Constraint\Validator\RuleValidator; use Thelia\Exception\InvalidRuleException; use Thelia\Exception\InvalidRuleOperatorException; +use Thelia\Exception\InvalidRuleValueException; +use Thelia\Model\Currency; +use Thelia\Type\FloatType; /** * Created by JetBrains PhpStorm. @@ -268,4 +271,52 @@ abstract class CouponRuleAbstract implements CouponRuleInterface return $serializableRule; } + + /** + * Check if currency if valid or not + * + * @param string $currencyValue Currency EUR|USD|.. + * + * @return bool + * @throws \Thelia\Exception\InvalidRuleValueException + */ + protected function IsCurrencyValid($currencyValue) + { + $availableCurrencies = $this->adapter->getAvailableCurrencies(); + /** @var Currency $currency */ + $currencyFound = false; + foreach ($availableCurrencies as $key => $currency) { + if ($currencyValue == $currency->getCode()) { + $currencyFound = true; + } + } + if (!$currencyFound) { + throw new InvalidRuleValueException( + get_class(), 'currency' + ); + } + + return true; + } + + /** + * Check if price is valid + * + * @param float $priceValue Price value to check + * + * @return bool + * @throws \Thelia\Exception\InvalidRuleValueException + */ + protected function isPriceValid($priceValue) + { + $floatType = new FloatType(); + if (!$floatType->isValid($priceValue) || $priceValue <= 0) { + throw new InvalidRuleValueException( + get_class(), 'price' + ); + } + + return true; + } + } \ No newline at end of file diff --git a/core/lib/Thelia/Controller/Admin/CouponController.php b/core/lib/Thelia/Controller/Admin/CouponController.php index 6803addfd..f50c58307 100755 --- a/core/lib/Thelia/Controller/Admin/CouponController.php +++ b/core/lib/Thelia/Controller/Admin/CouponController.php @@ -49,7 +49,9 @@ use Thelia\Form\Exception\FormValidationException; use Thelia\Log\Tlog; use Thelia\Model\Coupon; use Thelia\Model\CouponQuery; +use Thelia\Model\CurrencyQuery; use Thelia\Model\Lang; +use Thelia\Model\LangQuery; use Thelia\Tools\I18n; /** diff --git a/core/lib/Thelia/Coupon/CouponAdapterInterface.php b/core/lib/Thelia/Coupon/CouponAdapterInterface.php index b2c168186..413fc3df6 100644 --- a/core/lib/Thelia/Coupon/CouponAdapterInterface.php +++ b/core/lib/Thelia/Coupon/CouponAdapterInterface.php @@ -169,4 +169,11 @@ interface CouponAdapterInterface */ public function getConstraintValidator(); + /** + * Return all available currencies + * + * @return array of Currency + */ + public function getAvailableCurrencies(); + } \ No newline at end of file diff --git a/core/lib/Thelia/Coupon/CouponBaseAdapter.php b/core/lib/Thelia/Coupon/CouponBaseAdapter.php index f9fae8651..cb5162090 100644 --- a/core/lib/Thelia/Coupon/CouponBaseAdapter.php +++ b/core/lib/Thelia/Coupon/CouponBaseAdapter.php @@ -35,6 +35,8 @@ use Thelia\Model\Coupon; use Thelia\Model\CouponQuery; use Thelia\Cart\CartTrait; use Thelia\Model\Currency; +use Thelia\Model\CurrencyQuery; +use Thelia\Model\LangQuery; /** * Created by JetBrains PhpStorm. @@ -266,4 +268,17 @@ class CouponBaseAdapter implements CouponAdapterInterface { return $this->container->get('thelia.constraint.validator'); } + + + /** + * Return all available currencies + * + * @return array of Currency + */ + public function getAvailableCurrencies() + { + $currencies = CurrencyQuery::create(); + + return $currencies->find(); + } } diff --git a/core/lib/Thelia/Tests/Constraint/Rule/AvailableForTotalAmountManagerTest.php b/core/lib/Thelia/Tests/Constraint/Rule/AvailableForTotalAmountManagerTest.php index 4ad790a5b..a883a0712 100644 --- a/core/lib/Thelia/Tests/Constraint/Rule/AvailableForTotalAmountManagerTest.php +++ b/core/lib/Thelia/Tests/Constraint/Rule/AvailableForTotalAmountManagerTest.php @@ -27,6 +27,7 @@ use Thelia\Constraint\ConstraintValidator; use Thelia\Constraint\Rule\AvailableForTotalAmountManager; use Thelia\Constraint\Rule\Operators; use Thelia\Exception\InvalidRuleValueException; +use Thelia\Model\Currency; /** * Created by JetBrains PhpStorm. @@ -44,6 +45,43 @@ class AvailableForTotalAmountManagerTest extends \PHPUnit_Framework_TestCase /** @var CouponAdapterInterface $stubTheliaAdapter */ protected $stubTheliaAdapter = null; + /** + * Generate adapter stub + * + * @param int $cartTotalPrice Cart total price + * @param string $checkoutCurrency Checkout currency + * + * @return \PHPUnit_Framework_MockObject_MockObject + */ + public function generateAdapterStub($cartTotalPrice = 400, $checkoutCurrency = 'EUR') + { + $stubAdapter = $this->getMockBuilder('\Thelia\Coupon\CouponBaseAdapter') + ->disableOriginalConstructor() + ->getMock(); + + $stubAdapter->expects($this->any()) + ->method('getCartTotalPrice') + ->will($this->returnValue($cartTotalPrice)); + + $stubAdapter->expects($this->any()) + ->method('getCheckoutCurrency') + ->will($this->returnValue($checkoutCurrency)); + + $stubAdapter->expects($this->any()) + ->method('getConstraintValidator') + ->will($this->returnValue(new ConstraintValidator())); + + $currency1 = new Currency(); + $currency1->setCode('EUR'); + $currency2 = new Currency(); + $currency2->setCode('USD'); + $stubAdapter->expects($this->any()) + ->method('getAvailableCurrencies') + ->will($this->returnValue(array($currency1, $currency2))); + + return $stubAdapter; + } + /** * Sets up the fixture, for example, opens a network connection. * This method is called before a test is executed. @@ -62,19 +100,7 @@ class AvailableForTotalAmountManagerTest extends \PHPUnit_Framework_TestCase */ public function testInValidBackOfficeInputOperator() { - $stubAdapter = $this->getMockBuilder('\Thelia\Coupon\CouponBaseAdapter') - ->disableOriginalConstructor() - ->getMock(); - - $stubAdapter->expects($this->any()) - ->method('getCartTotalPrice') - ->will($this->returnValue(399)); - $stubAdapter->expects($this->any()) - ->method('getCheckoutCurrency') - ->will($this->returnValue('EUR')); - $stubAdapter->expects($this->any()) - ->method('getConstraintValidator') - ->will($this->returnValue(new ConstraintValidator())); + $stubAdapter = $this->generateAdapterStub(399, 'EUR'); $rule1 = new AvailableForTotalAmountManager($stubAdapter); $operators = array( @@ -102,19 +128,7 @@ class AvailableForTotalAmountManagerTest extends \PHPUnit_Framework_TestCase */ public function testInValidBackOfficeInputOperator2() { - $stubAdapter = $this->getMockBuilder('\Thelia\Coupon\CouponBaseAdapter') - ->disableOriginalConstructor() - ->getMock(); - - $stubAdapter->expects($this->any()) - ->method('getCartTotalPrice') - ->will($this->returnValue(399)); - $stubAdapter->expects($this->any()) - ->method('getCheckoutCurrency') - ->will($this->returnValue('EUR')); - $stubAdapter->expects($this->any()) - ->method('getConstraintValidator') - ->will($this->returnValue(new ConstraintValidator())); + $stubAdapter = $this->generateAdapterStub(399, 'EUR'); $rule1 = new AvailableForTotalAmountManager($stubAdapter); $operators = array( @@ -142,19 +156,7 @@ class AvailableForTotalAmountManagerTest extends \PHPUnit_Framework_TestCase */ public function testInValidBackOfficeInputValue() { - $stubAdapter = $this->getMockBuilder('\Thelia\Coupon\CouponBaseAdapter') - ->disableOriginalConstructor() - ->getMock(); - - $stubAdapter->expects($this->any()) - ->method('getCartTotalPrice') - ->will($this->returnValue(399)); - $stubAdapter->expects($this->any()) - ->method('getCheckoutCurrency') - ->will($this->returnValue('EUR')); - $stubAdapter->expects($this->any()) - ->method('getConstraintValidator') - ->will($this->returnValue(new ConstraintValidator())); + $stubAdapter = $this->generateAdapterStub(399, 'EUR'); $rule1 = new AvailableForTotalAmountManager($stubAdapter); $operators = array( @@ -182,19 +184,7 @@ class AvailableForTotalAmountManagerTest extends \PHPUnit_Framework_TestCase */ public function testInValidBackOfficeInputValue2() { - $stubAdapter = $this->getMockBuilder('\Thelia\Coupon\CouponBaseAdapter') - ->disableOriginalConstructor() - ->getMock(); - - $stubAdapter->expects($this->any()) - ->method('getCartTotalPrice') - ->will($this->returnValue(399)); - $stubAdapter->expects($this->any()) - ->method('getCheckoutCurrency') - ->will($this->returnValue('EUR')); - $stubAdapter->expects($this->any()) - ->method('getConstraintValidator') - ->will($this->returnValue(new ConstraintValidator())); + $stubAdapter = $this->generateAdapterStub(399, 'EUR'); $rule1 = new AvailableForTotalAmountManager($stubAdapter); $operators = array( @@ -221,19 +211,7 @@ class AvailableForTotalAmountManagerTest extends \PHPUnit_Framework_TestCase */ public function testMatchingRuleInferior() { - $stubAdapter = $this->getMockBuilder('\Thelia\Coupon\CouponBaseAdapter') - ->disableOriginalConstructor() - ->getMock(); - - $stubAdapter->expects($this->any()) - ->method('getCartTotalPrice') - ->will($this->returnValue(399)); - $stubAdapter->expects($this->any()) - ->method('getCheckoutCurrency') - ->will($this->returnValue('EUR')); - $stubAdapter->expects($this->any()) - ->method('getConstraintValidator') - ->will($this->returnValue(new ConstraintValidator())); + $stubAdapter = $this->generateAdapterStub(399, 'EUR'); $rule1 = new AvailableForTotalAmountManager($stubAdapter); $operators = array( @@ -260,19 +238,7 @@ class AvailableForTotalAmountManagerTest extends \PHPUnit_Framework_TestCase */ public function testNotMatchingRuleInferior() { - $stubAdapter = $this->getMockBuilder('\Thelia\Coupon\CouponBaseAdapter') - ->disableOriginalConstructor() - ->getMock(); - - $stubAdapter->expects($this->any()) - ->method('getCartTotalPrice') - ->will($this->returnValue(400)); - $stubAdapter->expects($this->any()) - ->method('getCheckoutCurrency') - ->will($this->returnValue('EUR')); - $stubAdapter->expects($this->any()) - ->method('getConstraintValidator') - ->will($this->returnValue(new ConstraintValidator())); + $stubAdapter = $this->generateAdapterStub(400, 'EUR'); $rule1 = new AvailableForTotalAmountManager($stubAdapter); $operators = array( @@ -299,19 +265,7 @@ class AvailableForTotalAmountManagerTest extends \PHPUnit_Framework_TestCase */ public function testMatchingRuleInferiorEquals() { - $stubAdapter = $this->getMockBuilder('\Thelia\Coupon\CouponBaseAdapter') - ->disableOriginalConstructor() - ->getMock(); - - $stubAdapter->expects($this->any()) - ->method('getCartTotalPrice') - ->will($this->returnValue(400)); - $stubAdapter->expects($this->any()) - ->method('getCheckoutCurrency') - ->will($this->returnValue('EUR')); - $stubAdapter->expects($this->any()) - ->method('getConstraintValidator') - ->will($this->returnValue(new ConstraintValidator())); + $stubAdapter = $this->generateAdapterStub(400, 'EUR'); $rule1 = new AvailableForTotalAmountManager($stubAdapter); $operators = array( @@ -338,19 +292,7 @@ class AvailableForTotalAmountManagerTest extends \PHPUnit_Framework_TestCase */ public function testMatchingRuleInferiorEquals2() { - $stubAdapter = $this->getMockBuilder('\Thelia\Coupon\CouponBaseAdapter') - ->disableOriginalConstructor() - ->getMock(); - - $stubAdapter->expects($this->any()) - ->method('getCartTotalPrice') - ->will($this->returnValue(399)); - $stubAdapter->expects($this->any()) - ->method('getCheckoutCurrency') - ->will($this->returnValue('EUR')); - $stubAdapter->expects($this->any()) - ->method('getConstraintValidator') - ->will($this->returnValue(new ConstraintValidator())); + $stubAdapter = $this->generateAdapterStub(399, 'EUR'); $rule1 = new AvailableForTotalAmountManager($stubAdapter); $operators = array( @@ -377,19 +319,7 @@ class AvailableForTotalAmountManagerTest extends \PHPUnit_Framework_TestCase */ public function testNotMatchingRuleInferiorEquals() { - $stubAdapter = $this->getMockBuilder('\Thelia\Coupon\CouponBaseAdapter') - ->disableOriginalConstructor() - ->getMock(); - - $stubAdapter->expects($this->any()) - ->method('getCartTotalPrice') - ->will($this->returnValue(401)); - $stubAdapter->expects($this->any()) - ->method('getCheckoutCurrency') - ->will($this->returnValue('EUR')); - $stubAdapter->expects($this->any()) - ->method('getConstraintValidator') - ->will($this->returnValue(new ConstraintValidator())); + $stubAdapter = $this->generateAdapterStub(401, 'EUR'); $rule1 = new AvailableForTotalAmountManager($stubAdapter); $operators = array( @@ -416,19 +346,7 @@ class AvailableForTotalAmountManagerTest extends \PHPUnit_Framework_TestCase */ public function testMatchingRuleEqual() { - $stubAdapter = $this->getMockBuilder('\Thelia\Coupon\CouponBaseAdapter') - ->disableOriginalConstructor() - ->getMock(); - - $stubAdapter->expects($this->any()) - ->method('getCartTotalPrice') - ->will($this->returnValue(400)); - $stubAdapter->expects($this->any()) - ->method('getCheckoutCurrency') - ->will($this->returnValue('EUR')); - $stubAdapter->expects($this->any()) - ->method('getConstraintValidator') - ->will($this->returnValue(new ConstraintValidator())); + $stubAdapter = $this->generateAdapterStub(400, 'EUR'); $rule1 = new AvailableForTotalAmountManager($stubAdapter); $operators = array( @@ -455,19 +373,7 @@ class AvailableForTotalAmountManagerTest extends \PHPUnit_Framework_TestCase */ public function testNotMatchingRuleEqual() { - $stubAdapter = $this->getMockBuilder('\Thelia\Coupon\CouponBaseAdapter') - ->disableOriginalConstructor() - ->getMock(); - - $stubAdapter->expects($this->any()) - ->method('getCartTotalPrice') - ->will($this->returnValue(399)); - $stubAdapter->expects($this->any()) - ->method('getCheckoutCurrency') - ->will($this->returnValue('EUR')); - $stubAdapter->expects($this->any()) - ->method('getConstraintValidator') - ->will($this->returnValue(new ConstraintValidator())); + $stubAdapter = $this->generateAdapterStub(399, 'EUR'); $rule1 = new AvailableForTotalAmountManager($stubAdapter); $operators = array( @@ -494,19 +400,7 @@ class AvailableForTotalAmountManagerTest extends \PHPUnit_Framework_TestCase */ public function testMatchingRuleSuperiorEquals() { - $stubAdapter = $this->getMockBuilder('\Thelia\Coupon\CouponBaseAdapter') - ->disableOriginalConstructor() - ->getMock(); - - $stubAdapter->expects($this->any()) - ->method('getCartTotalPrice') - ->will($this->returnValue(401)); - $stubAdapter->expects($this->any()) - ->method('getCheckoutCurrency') - ->will($this->returnValue('EUR')); - $stubAdapter->expects($this->any()) - ->method('getConstraintValidator') - ->will($this->returnValue(new ConstraintValidator())); + $stubAdapter = $this->generateAdapterStub(401, 'EUR'); $rule1 = new AvailableForTotalAmountManager($stubAdapter); $operators = array( @@ -533,19 +427,7 @@ class AvailableForTotalAmountManagerTest extends \PHPUnit_Framework_TestCase */ public function testMatchingRuleSuperiorEquals2() { - $stubAdapter = $this->getMockBuilder('\Thelia\Coupon\CouponBaseAdapter') - ->disableOriginalConstructor() - ->getMock(); - - $stubAdapter->expects($this->any()) - ->method('getCartTotalPrice') - ->will($this->returnValue(400)); - $stubAdapter->expects($this->any()) - ->method('getCheckoutCurrency') - ->will($this->returnValue('EUR')); - $stubAdapter->expects($this->any()) - ->method('getConstraintValidator') - ->will($this->returnValue(new ConstraintValidator())); + $stubAdapter = $this->generateAdapterStub(400, 'EUR'); $rule1 = new AvailableForTotalAmountManager($stubAdapter); $operators = array( @@ -572,19 +454,7 @@ class AvailableForTotalAmountManagerTest extends \PHPUnit_Framework_TestCase */ public function testNotMatchingRuleSuperiorEquals() { - $stubAdapter = $this->getMockBuilder('\Thelia\Coupon\CouponBaseAdapter') - ->disableOriginalConstructor() - ->getMock(); - - $stubAdapter->expects($this->any()) - ->method('getCartTotalPrice') - ->will($this->returnValue(399.00)); - $stubAdapter->expects($this->any()) - ->method('getCheckoutCurrency') - ->will($this->returnValue('EUR')); - $stubAdapter->expects($this->any()) - ->method('getConstraintValidator') - ->will($this->returnValue(new ConstraintValidator())); + $stubAdapter = $this->generateAdapterStub(399, 'EUR'); $rule1 = new AvailableForTotalAmountManager($stubAdapter); $operators = array( @@ -611,19 +481,7 @@ class AvailableForTotalAmountManagerTest extends \PHPUnit_Framework_TestCase */ public function testMatchingRuleSuperior() { - $stubAdapter = $this->getMockBuilder('\Thelia\Coupon\CouponBaseAdapter') - ->disableOriginalConstructor() - ->getMock(); - - $stubAdapter->expects($this->any()) - ->method('getCartTotalPrice') - ->will($this->returnValue(401)); - $stubAdapter->expects($this->any()) - ->method('getCheckoutCurrency') - ->will($this->returnValue('EUR')); - $stubAdapter->expects($this->any()) - ->method('getConstraintValidator') - ->will($this->returnValue(new ConstraintValidator())); + $stubAdapter = $this->generateAdapterStub(401, 'EUR'); $rule1 = new AvailableForTotalAmountManager($stubAdapter); $operators = array( @@ -650,19 +508,7 @@ class AvailableForTotalAmountManagerTest extends \PHPUnit_Framework_TestCase */ public function testNotMatchingRuleSuperior() { - $stubAdapter = $this->getMockBuilder('\Thelia\Coupon\CouponBaseAdapter') - ->disableOriginalConstructor() - ->getMock(); - - $stubAdapter->expects($this->any()) - ->method('getCartTotalPrice') - ->will($this->returnValue(399.00)); - $stubAdapter->expects($this->any()) - ->method('getCheckoutCurrency') - ->will($this->returnValue('EUR')); - $stubAdapter->expects($this->any()) - ->method('getConstraintValidator') - ->will($this->returnValue(new ConstraintValidator())); + $stubAdapter = $this->generateAdapterStub(399, 'EUR'); $rule1 = new AvailableForTotalAmountManager($stubAdapter); $operators = array( @@ -689,19 +535,7 @@ class AvailableForTotalAmountManagerTest extends \PHPUnit_Framework_TestCase */ public function testMatchingRuleCurrency() { - $stubAdapter = $this->getMockBuilder('\Thelia\Coupon\CouponBaseAdapter') - ->disableOriginalConstructor() - ->getMock(); - - $stubAdapter->expects($this->any()) - ->method('getCartTotalPrice') - ->will($this->returnValue(400.00)); - $stubAdapter->expects($this->any()) - ->method('getCheckoutCurrency') - ->will($this->returnValue('EUR')); - $stubAdapter->expects($this->any()) - ->method('getConstraintValidator') - ->will($this->returnValue(new ConstraintValidator())); + $stubAdapter = $this->generateAdapterStub(400, 'EUR'); $rule1 = new AvailableForTotalAmountManager($stubAdapter); $operators = array( @@ -728,19 +562,7 @@ class AvailableForTotalAmountManagerTest extends \PHPUnit_Framework_TestCase */ public function testNotMatchingRuleCurrency() { - $stubAdapter = $this->getMockBuilder('\Thelia\Coupon\CouponBaseAdapter') - ->disableOriginalConstructor() - ->getMock(); - - $stubAdapter->expects($this->any()) - ->method('getCartTotalPrice') - ->will($this->returnValue(400.00)); - $stubAdapter->expects($this->any()) - ->method('getCheckoutCurrency') - ->will($this->returnValue('EUR')); - $stubAdapter->expects($this->any()) - ->method('getConstraintValidator') - ->will($this->returnValue(new ConstraintValidator())); + $stubAdapter = $this->generateAdapterStub(400.00, 'EUR'); $rule1 = new AvailableForTotalAmountManager($stubAdapter); $operators = array( From 5326ab1341549c1020ff4235abda6e03386d703a Mon Sep 17 00:00:00 2001 From: gmorel Date: Tue, 17 Sep 2013 22:54:49 +0200 Subject: [PATCH 007/100] Working : Coupon : fixture add old coupon --- install/faker.php | 57 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 43 insertions(+), 14 deletions(-) diff --git a/install/faker.php b/install/faker.php index 66303f05a..25112d904 100755 --- a/install/faker.php +++ b/install/faker.php @@ -1,5 +1,6 @@ setIsUsed(1); $coupon1->setIsEnabled(1); $date = new \DateTime(); - $coupon1->setExpirationDate($date->setTimestamp(strtotime("today + 2 months"))); + $coupon1->setExpirationDate($date->setTimestamp(strtotime("today + 3 months"))); $rule1 = new AvailableForTotalAmountManager($adapter); $operators = array( @@ -588,27 +589,18 @@ Sed facilisis pellentesque nisl, eu tincidunt erat scelerisque a. Nullam malesua $rules = new CouponRuleCollection(); $rules->add($rule1); $rules->add($rule2); - /** @var ConstraintFactory $constraintFactory */ $constraintFactory = $container->get('thelia.constraint.factory'); $serializedRules = $constraintFactory->serializeCouponRuleCollection($rules); $coupon1->setSerializedRules($serializedRules); - $coupon1->setMaxUsage(40); $coupon1->setIsCumulative(1); $coupon1->setIsRemovingPostage(0); $coupon1->setIsAvailableOnSpecialOffers(1); - $coupon1->save(); - - - - - - // Coupons $coupon2 = new Thelia\Model\Coupon(); $coupon2->setCode('SPRINGBREAK'); @@ -628,7 +620,7 @@ Sed facilisis pellentesque nisl, eu tincidunt erat scelerisque a. Nullam malesua $coupon2->setIsUsed(1); $coupon2->setIsEnabled(1); $date = new \DateTime(); - $coupon2->setExpirationDate($date->setTimestamp(strtotime("today + 2 months"))); + $coupon2->setExpirationDate($date->setTimestamp(strtotime("today + 1 months"))); $rule1 = new AvailableForXArticlesManager($adapter); $operators = array( @@ -638,7 +630,6 @@ Sed facilisis pellentesque nisl, eu tincidunt erat scelerisque a. Nullam malesua AvailableForXArticlesManager::INPUT1 => 4, ); $rule1->setValidatorsFromForm($operators, $values); - $rules = new CouponRuleCollection(); $rules->add($rule1); @@ -647,11 +638,49 @@ Sed facilisis pellentesque nisl, eu tincidunt erat scelerisque a. Nullam malesua $serializedRules = $constraintFactory->serializeCouponRuleCollection($rules); $coupon2->setSerializedRules($serializedRules); - $coupon2->setMaxUsage(-1); $coupon2->setIsCumulative(0); $coupon2->setIsRemovingPostage(1); $coupon2->setIsAvailableOnSpecialOffers(1); - $coupon2->save(); + + + // Coupons + $coupon3 = new Thelia\Model\Coupon(); + $coupon3->setCode('OLD'); + $coupon3->setType('thelia.coupon.type.remove_x_percent'); + $coupon3->setTitle('Old coupon'); + $coupon3->setShortDescription('Coupon for Springbreak removing 10% if you have more than 4 articles in your cart'); + $coupon3->setDescription('Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras at luctus tellus. Integer turpis mauris, aliquet vitae risus tristique, pellentesque vestibulum urna. Vestibulum sodales laoreet lectus dictum suscipit. Praesent vulputate, sem id varius condimentum, quam magna tempor elit, quis venenatis ligula nulla eget libero. Cras egestas euismod tellus, id pharetra leo suscipit quis. Donec lacinia ac lacus et ultricies. Nunc in porttitor neque. Proin at quam congue, consectetur orci sed, congue nulla. Nulla eleifend nunc ligula, nec pharetra elit tempus quis. Vivamus vel mauris sed est dictum blandit. Maecenas blandit dapibus velit ut sollicitudin. In in euismod mauris, consequat viverra magna. Cras velit velit, sollicitudin commodo tortor gravida, tempus varius nulla. + +Donec rhoncus leo mauris, id porttitor ante luctus tempus. Curabitur quis augue feugiat, ullamcorper mauris ac, interdum mi. Quisque aliquam lorem vitae felis lobortis, id interdum turpis mattis. Vestibulum diam massa, ornare congue blandit quis, facilisis at nisl. In tortor metus, venenatis non arcu nec, sollicitudin ornare nisl. Nunc erat risus, varius nec urna at, iaculis lacinia elit. Aenean ut felis tempus, tincidunt odio non, sagittis nisl. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec vitae hendrerit elit. Nunc sit amet gravida risus, euismod lobortis massa. Nam a erat mauris. Nam a malesuada lorem. Nulla id accumsan dolor, sed rhoncus tellus. Quisque dictum felis sed leo auctor, at volutpat lectus viverra. Morbi rutrum, est ac aliquam imperdiet, nibh sem sagittis justo, ac mattis magna lacus eu nulla. + +Duis interdum lectus nulla, nec pellentesque sapien condimentum at. Suspendisse potenti. Sed eu purus tellus. Nunc quis rhoncus metus. Fusce vitae tellus enim. Interdum et malesuada fames ac ante ipsum primis in faucibus. Etiam tempor porttitor erat vitae iaculis. Sed est elit, consequat non ornare vitae, vehicula eget lectus. Etiam consequat sapien mauris, eget consectetur magna imperdiet eget. Nunc sollicitudin luctus velit, in commodo nulla adipiscing fermentum. Fusce nisi sapien, posuere vitae metus sit amet, facilisis sollicitudin dui. Fusce ultricies auctor enim sit amet iaculis. Morbi at vestibulum enim, eget adipiscing eros. + +Praesent ligula lorem, faucibus ut metus quis, fermentum iaculis erat. Pellentesque elit erat, lacinia sed semper ac, sagittis vel elit. Nam eu convallis est. Curabitur rhoncus odio vitae consectetur pellentesque. Nam vitae arcu nec ante scelerisque dignissim vel nec neque. Suspendisse augue nulla, mollis eget dui et, tempor facilisis erat. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi ac diam ipsum. Donec convallis dui ultricies velit auctor, non lobortis nulla ultrices. Morbi vitae dignissim ante, sit amet lobortis tortor. Nunc dapibus condimentum augue, in molestie neque congue non. + +Sed facilisis pellentesque nisl, eu tincidunt erat scelerisque a. Nullam malesuada tortor vel erat volutpat tincidunt. In vehicula diam est, a convallis eros scelerisque ut. Donec aliquet venenatis iaculis. Ut a arcu gravida, placerat dui eu, iaculis nisl. Quisque adipiscing orci sit amet dui dignissim lacinia. Sed vulputate lorem non dolor adipiscing ornare. Morbi ornare id nisl id aliquam. Ut fringilla elit ante, nec lacinia enim fermentum sit amet. Aenean rutrum lorem eu convallis pharetra. Cras malesuada varius metus, vitae gravida velit. Nam a varius ipsum, ac commodo dolor. Phasellus nec elementum elit. Etiam vel adipiscing leo.'); + $coupon3->setAmount(10.00); + $coupon3->setIsUsed(1); + $coupon3->setIsEnabled(0); + $date = new \DateTime(); + $coupon3->setExpirationDate($date->setTimestamp(strtotime("today + 2 months"))); + + $rule1 = new AvailableForEveryoneManager($adapter); + $operators = array(); + $values = array(); + $rule1->setValidatorsFromForm($operators, $values); + $rules = new CouponRuleCollection(); + $rules->add($rule1); + + /** @var ConstraintFactory $constraintFactory */ + $constraintFactory = $container->get('thelia.constraint.factory'); + + $serializedRules = $constraintFactory->serializeCouponRuleCollection($rules); + $coupon3->setSerializedRules($serializedRules); + $coupon3->setMaxUsage(-1); + $coupon3->setIsCumulative(0); + $coupon3->setIsRemovingPostage(1); + $coupon3->setIsAvailableOnSpecialOffers(1); + $coupon3->save(); } From ce72a2007476b368f900cd18e986890d6426f453 Mon Sep 17 00:00:00 2001 From: gmorel Date: Tue, 17 Sep 2013 23:09:55 +0200 Subject: [PATCH 008/100] Working : Coupon : fix loop --- core/lib/Thelia/Core/Template/Loop/Coupon.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/lib/Thelia/Core/Template/Loop/Coupon.php b/core/lib/Thelia/Core/Template/Loop/Coupon.php index 4e8ca3e71..dff5e782b 100755 --- a/core/lib/Thelia/Core/Template/Loop/Coupon.php +++ b/core/lib/Thelia/Core/Template/Loop/Coupon.php @@ -88,8 +88,8 @@ class Coupon extends BaseI18nLoop $search->filterById($id, Criteria::IN); } - if ($isEnabled != BooleanOrBothType::ANY) { - $search->filterByIsEnabled($isEnabled ? 1 : 0); + if (isset($isEnabled)) { + $search->filterByIsEnabled($isEnabled ? true : false); } // Perform search From 542a1423298431263c72d3d0f6a835b13971c247 Mon Sep 17 00:00:00 2001 From: gmorel Date: Tue, 17 Sep 2013 23:19:09 +0200 Subject: [PATCH 009/100] Working : Coupon : fix loop, fix read display --- core/lib/Thelia/Core/Template/Loop/Coupon.php | 2 +- install/faker.php | 8 ++++---- templates/admin/default/coupon-read.html | 6 +++++- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/core/lib/Thelia/Core/Template/Loop/Coupon.php b/core/lib/Thelia/Core/Template/Loop/Coupon.php index dff5e782b..063ffa397 100755 --- a/core/lib/Thelia/Core/Template/Loop/Coupon.php +++ b/core/lib/Thelia/Core/Template/Loop/Coupon.php @@ -63,7 +63,7 @@ class Coupon extends BaseI18nLoop { return new ArgumentCollection( Argument::createIntListTypeArgument('id'), - Argument::createBooleanOrBothTypeArgument('is_enabled', 1) + Argument::createBooleanOrBothTypeArgument('is_enabled') ); } diff --git a/install/faker.php b/install/faker.php index 94c507d02..9a1d3d480 100755 --- a/install/faker.php +++ b/install/faker.php @@ -742,7 +742,7 @@ Praesent ligula lorem, faucibus ut metus quis, fermentum iaculis erat. Pellentes Sed facilisis pellentesque nisl, eu tincidunt erat scelerisque a. Nullam malesuada tortor vel erat volutpat tincidunt. In vehicula diam est, a convallis eros scelerisque ut. Donec aliquet venenatis iaculis. Ut a arcu gravida, placerat dui eu, iaculis nisl. Quisque adipiscing orci sit amet dui dignissim lacinia. Sed vulputate lorem non dolor adipiscing ornare. Morbi ornare id nisl id aliquam. Ut fringilla elit ante, nec lacinia enim fermentum sit amet. Aenean rutrum lorem eu convallis pharetra. Cras malesuada varius metus, vitae gravida velit. Nam a varius ipsum, ac commodo dolor. Phasellus nec elementum elit. Etiam vel adipiscing leo.'); $coupon3->setAmount(10.00); - $coupon3->setIsUsed(1); + $coupon3->setIsUsed(0); $coupon3->setIsEnabled(0); $date = new \DateTime(); $coupon3->setExpirationDate($date->setTimestamp(strtotime("today + 2 months"))); @@ -760,8 +760,8 @@ Sed facilisis pellentesque nisl, eu tincidunt erat scelerisque a. Nullam malesua $serializedRules = $constraintFactory->serializeCouponRuleCollection($rules); $coupon3->setSerializedRules($serializedRules); $coupon3->setMaxUsage(-1); - $coupon3->setIsCumulative(0); - $coupon3->setIsRemovingPostage(1); - $coupon3->setIsAvailableOnSpecialOffers(1); + $coupon3->setIsCumulative(1); + $coupon3->setIsRemovingPostage(0); + $coupon3->setIsAvailableOnSpecialOffers(0); $coupon3->save(); } diff --git a/templates/admin/default/coupon-read.html b/templates/admin/default/coupon-read.html index 4ae5e5c68..859163fc8 100755 --- a/templates/admin/default/coupon-read.html +++ b/templates/admin/default/coupon-read.html @@ -63,7 +63,11 @@ {intl l='Usage left'} - {if $USAGE_LEFT} + {if $USAGE_LEFT == -1} + + {intl l='Unlimited'} + + {elseif $USAGE_LEFT} {$USAGE_LEFT} From be665cba56d7ed3da0caf3d8d65e1ffea79625ef Mon Sep 17 00:00:00 2001 From: gmorel Date: Tue, 17 Sep 2013 23:25:38 +0200 Subject: [PATCH 010/100] Working : Coupon : fix loop, fix read display --- templates/admin/default/coupon-list.html | 36 ++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/templates/admin/default/coupon-list.html b/templates/admin/default/coupon-list.html index 2eafde586..a17d01e96 100755 --- a/templates/admin/default/coupon-list.html +++ b/templates/admin/default/coupon-list.html @@ -41,7 +41,23 @@ {block name="coupon-code"}{$CODE}{/block} {block name="coupon-title"}{$TITLE}{/block} {block name="coupon-expiration-date"}{$EXPIRATION_DATE}{/block} - {block name="coupon-usage-left"}{$USAGE_LEFT}{/block} + + {block name="coupon-usage-left"} + {if $USAGE_LEFT == -1} + + {intl l='Unlimited'} + + {elseif $USAGE_LEFT} + + {$USAGE_LEFT} + + {else} + + 0 + + {/if} + {/block} + {block name="coupon-action"} @@ -77,7 +93,23 @@ {block name="coupon-code"}{$CODE}{/block} {block name="coupon-title"}{$TITLE}{/block} {block name="coupon-expiration-date"}{$EXPIRATION_DATE}{/block} - {block name="coupon-usage-left"}{$USAGE_LEFT}{/block} + + {block name="coupon-usage-left"} + {if $USAGE_LEFT == -1} + + {intl l='Unlimited'} + + {elseif $USAGE_LEFT} + + {$USAGE_LEFT} + + {else} + + 0 + + {/if} + {/block} + {block name="coupon-action"} From b545e1c012f1d3ef609612021b34c812ca5d36a3 Mon Sep 17 00:00:00 2001 From: gmorel Date: Tue, 17 Sep 2013 23:29:25 +0200 Subject: [PATCH 011/100] Working : Coupon : fix loop, fix read display --- core/lib/Thelia/Core/Template/Loop/Coupon.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/core/lib/Thelia/Core/Template/Loop/Coupon.php b/core/lib/Thelia/Core/Template/Loop/Coupon.php index 063ffa397..9b720ef13 100755 --- a/core/lib/Thelia/Core/Template/Loop/Coupon.php +++ b/core/lib/Thelia/Core/Template/Loop/Coupon.php @@ -31,13 +31,11 @@ use Thelia\Core\HttpFoundation\Request; use Thelia\Core\Template\Element\BaseI18nLoop; use Thelia\Core\Template\Element\LoopResult; use Thelia\Core\Template\Element\LoopResultRow; - -use Thelia\Core\Template\Loop\Argument\ArgumentCollection; use Thelia\Core\Template\Loop\Argument\Argument; - +use Thelia\Core\Template\Loop\Argument\ArgumentCollection; use Thelia\Coupon\Type\CouponInterface; -use Thelia\Model\CouponQuery; use Thelia\Model\Coupon as MCoupon; +use Thelia\Model\CouponQuery; use Thelia\Type; use Thelia\Type\BooleanOrBothType; From 8e308d21e99dc44a247213c74b69400400125d4c Mon Sep 17 00:00:00 2001 From: gmorel Date: Thu, 19 Sep 2013 23:25:01 +0200 Subject: [PATCH 012/100] Working : Coupon : fix update effect (becomes type), update max usage --- .../Thelia/Config/Resources/routing/admin.xml | 19 ++++++++++++------- .../Controller/Admin/CouponController.php | 7 ++++--- .../Coupon/CouponCreateOrUpdateEvent.php | 12 ++++++------ core/lib/Thelia/Form/CouponCreationForm.php | 2 +- core/lib/Thelia/Model/Coupon.php | 6 +++--- .../Thelia/Tests/Coupon/RuleOrganizerTest.php | 3 ++- templates/admin/default/assets/js/coupon.js | 17 ++++++++++++----- templates/admin/default/coupon/form.html | 18 ++++++++++-------- 8 files changed, 50 insertions(+), 34 deletions(-) diff --git a/core/lib/Thelia/Config/Resources/routing/admin.xml b/core/lib/Thelia/Config/Resources/routing/admin.xml index af7c950af..15e450c01 100755 --- a/core/lib/Thelia/Config/Resources/routing/admin.xml +++ b/core/lib/Thelia/Config/Resources/routing/admin.xml @@ -160,26 +160,31 @@ - + Thelia\Controller\Admin\CouponController::browseAction - + Thelia\Controller\Admin\CouponController::createAction - + Thelia\Controller\Admin\CouponController::updateAction + \d+ - + Thelia\Controller\Admin\CouponController::readAction + \d+ - + Thelia\Controller\Admin\CouponController::getRuleInputAction + .* - + Thelia\Controller\Admin\CouponController::updateRulesAction + \d+ - + Thelia\Controller\Admin\CouponController::consumeAction + .* diff --git a/core/lib/Thelia/Controller/Admin/CouponController.php b/core/lib/Thelia/Controller/Admin/CouponController.php index f50c58307..f7804de8e 100755 --- a/core/lib/Thelia/Controller/Admin/CouponController.php +++ b/core/lib/Thelia/Controller/Admin/CouponController.php @@ -201,7 +201,7 @@ class CouponController extends BaseAdminController $lang = $this->getSession()->getLang(); $eventToDispatch = TheliaEvents::COUPON_UPDATE; - // Create + // Update if ($this->getRequest()->isMethod('POST')) { $this->validateCreateOrUpdateForm( $i18n, @@ -210,7 +210,7 @@ class CouponController extends BaseAdminController 'updated', 'update' ); - } else { // Update + } else { // Display // Prepare the data that will hydrate the form /** @var ConstraintFactory $constraintFactory */ @@ -495,11 +495,12 @@ class CouponController extends BaseAdminController // Get the form field values $data = $form->getData(); + $couponEvent = new CouponCreateOrUpdateEvent( $data['code'], $data['title'], $data['amount'], - $data['effect'], + $data['type'], $data['shortDescription'], $data['description'], $data['isEnabled'], diff --git a/core/lib/Thelia/Core/Event/Coupon/CouponCreateOrUpdateEvent.php b/core/lib/Thelia/Core/Event/Coupon/CouponCreateOrUpdateEvent.php index 2e004b818..e8f54fffb 100644 --- a/core/lib/Thelia/Core/Event/Coupon/CouponCreateOrUpdateEvent.php +++ b/core/lib/Thelia/Core/Event/Coupon/CouponCreateOrUpdateEvent.php @@ -90,7 +90,7 @@ class CouponCreateOrUpdateEvent extends ActionEvent * @param string $code Coupon Code * @param string $title Coupon title * @param float $amount Amount removed from the Total Checkout - * @param string $effect Coupon effect + * @param string $type Coupon type * @param string $shortDescription Coupon short description * @param string $description Coupon description * @param boolean $isEnabled Enable/Disable @@ -106,7 +106,7 @@ class CouponCreateOrUpdateEvent extends ActionEvent $code, $title, $amount, - $effect, + $type, $shortDescription, $description, $isEnabled, @@ -130,7 +130,7 @@ class CouponCreateOrUpdateEvent extends ActionEvent $this->rules = $rules; $this->shortDescription = $shortDescription; $this->title = $title; - $this->effect = $effect; + $this->type = $type; $this->locale = $locale; } @@ -264,13 +264,13 @@ class CouponCreateOrUpdateEvent extends ActionEvent } /** - * Get Coupon effect + * Get Coupon type (effect) * * @return string */ - public function getEffect() + public function getType() { - return $this->effect; + return $this->type; } /** diff --git a/core/lib/Thelia/Form/CouponCreationForm.php b/core/lib/Thelia/Form/CouponCreationForm.php index 1625ab685..f3ee0df63 100755 --- a/core/lib/Thelia/Form/CouponCreationForm.php +++ b/core/lib/Thelia/Form/CouponCreationForm.php @@ -87,7 +87,7 @@ class CouponCreationForm extends BaseForm ) ) ->add( - 'effect', + 'type', 'text', array( 'constraints' => array( diff --git a/core/lib/Thelia/Model/Coupon.php b/core/lib/Thelia/Model/Coupon.php index 032de412a..e40fd9078 100755 --- a/core/lib/Thelia/Model/Coupon.php +++ b/core/lib/Thelia/Model/Coupon.php @@ -54,7 +54,7 @@ class Coupon extends BaseCoupon * @param string $code Coupon Code * @param string $title Coupon title * @param float $amount Amount removed from the Total Checkout - * @param string $effect Coupon effect + * @param string $type Coupon type * @param bool $isRemovingPostage Is removing Postage * @param string $shortDescription Coupon short description * @param string $description Coupon description @@ -67,13 +67,13 @@ class Coupon extends BaseCoupon * * @throws \Exception */ - function createOrUpdate($code, $title, $amount, $effect, $isRemovingPostage, $shortDescription, $description, $isEnabled, $expirationDate, $isAvailableOnSpecialOffers, $isCumulative, $maxUsage, $locale = null) + function createOrUpdate($code, $title, $amount, $type, $isRemovingPostage, $shortDescription, $description, $isEnabled, $expirationDate, $isAvailableOnSpecialOffers, $isCumulative, $maxUsage, $locale = null) { $this->setCode($code) ->setTitle($title) ->setShortDescription($shortDescription) ->setDescription($description) - ->setType($effect) + ->setType($type) ->setAmount($amount) ->setIsRemovingPostage($isRemovingPostage) ->setIsEnabled($isEnabled) diff --git a/core/lib/Thelia/Tests/Coupon/RuleOrganizerTest.php b/core/lib/Thelia/Tests/Coupon/RuleOrganizerTest.php index 3300cb19b..d13184a8c 100644 --- a/core/lib/Thelia/Tests/Coupon/RuleOrganizerTest.php +++ b/core/lib/Thelia/Tests/Coupon/RuleOrganizerTest.php @@ -23,6 +23,7 @@ namespace Thelia\Coupon; +use Thelia\Coupon\RuleOrganizer; /** * Created by JetBrains PhpStorm. * Date: 8/19/13 @@ -47,7 +48,7 @@ class RuleOrganizerTest extends \PHPUnit_Framework_TestCase */ protected function setUp() { - $this->object = new RuleOrganizer; + $this->object = new RuleOrganizer(); } /** diff --git a/templates/admin/default/assets/js/coupon.js b/templates/admin/default/assets/js/coupon.js index 41e0c1430..dae26d4b4 100644 --- a/templates/admin/default/assets/js/coupon.js +++ b/templates/admin/default/assets/js/coupon.js @@ -138,17 +138,24 @@ $(function($){ // Set max usage to unlimited or not couponManager.onUsageUnlimitedChange = function() { - if (!$('#max-usage').parent().hasClass('has-error')) { - $('#max-usage').hide().attr('value', '-1'); + var isUnlimited = $('#is-unlimited'); + if ($('#max-usage').val() == -1) { + isUnlimited.prop('checked', true); + $('#max-usage').hide(); $('#max-usage-label').hide(); + } else { + $isUnlimited.prop('checked', false); + $('#max-usage').show(); + $('#max-usage-label').show(); } - $('#is-unlimited').change(function(){ + + isUnlimited.change(function(){ var $this = $(this); if ($this.is(':checked')) { - $('#max-usage').hide().attr('value', '-1'); + $('#max-usage').hide().val('-1'); $('#max-usage-label').hide(); } else { - $('#max-usage').show().val('').attr('value', ''); + $('#max-usage').show().val(''); $('#max-usage-label').show(); } }); diff --git a/templates/admin/default/coupon/form.html b/templates/admin/default/coupon/form.html index a4ec8b14a..68c74bee2 100644 --- a/templates/admin/default/coupon/form.html +++ b/templates/admin/default/coupon/form.html @@ -88,11 +88,11 @@
- + {if $error}{$message}{/if}
{/form_field} @@ -101,17 +101,19 @@
- {form_field form=$form field='effect'} + {form_field form=$form field='type'}
- - + {foreach from=$availableCoupons item=availableCoupon} - + {/foreach} {if $error}{$message}{/if} - {$availableCoupons.0.toolTip} + {$availableCoupons.0.toolTip}
{/form_field}
From 194073fa4273aeacd65e38d0032eab4fed83c99e Mon Sep 17 00:00:00 2001 From: Etienne Roudeix Date: Mon, 23 Sep 2013 11:12:41 +0200 Subject: [PATCH 013/100] order action test --- core/lib/Thelia/Tests/Action/OrderTest.php | 161 +++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 core/lib/Thelia/Tests/Action/OrderTest.php diff --git a/core/lib/Thelia/Tests/Action/OrderTest.php b/core/lib/Thelia/Tests/Action/OrderTest.php new file mode 100644 index 000000000..881da19f6 --- /dev/null +++ b/core/lib/Thelia/Tests/Action/OrderTest.php @@ -0,0 +1,161 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Tests\Action; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage; +use Thelia\Action\Customer; +use Thelia\Core\Event\CustomerCreateOrUpdateEvent; +use Thelia\Core\Event\OrderEvent; +use Thelia\Core\HttpFoundation\Request; +use Thelia\Core\HttpFoundation\Session\Session; +use Thelia\Core\Security\SecurityContext; +use Thelia\Model\Base\AddressQuery; +use Thelia\Model\CustomerQuery; +use Thelia\Model\ModuleQuery; +use Thelia\Model\Order as OrderModel; +use Thelia\Action\Order; +use Thelia\Module\BaseModule; + +/** + * Class CustomerTest + * @package Thelia\Tests\Action + * @author Etienne Roudeix + */ +class OrderTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var ContainerBuilder $container + */ + protected $container; + + /** + * @var Order $orderAction + */ + protected $orderAction; + + /** + * @var OrderEvent $orderEvent + */ + protected $orderEvent; + + protected $customer; + + public function setUp() + { + $container = new ContainerBuilder(); + +// $session = new Session(new MockArraySessionStorage()); +// $request = new Request(); +// $dispatcher = $this->getMock("Symfony\Component\EventDispatcher\EventDispatcherInterface"); + +// $request->setSession($session); + +// $container->set("event_dispatcher", $dispatcher); +// $container->set('request', $request); +// $container->set('thelia.securityContext', new SecurityContext($request)); + + $this->container = $container; + + $this->orderEvent = new OrderEvent(new OrderModel()); + + $this->orderAction = new Order($this->container); + + /* load customer */ + /*$this->customer = $this->loadCustomer(); + if(null === $this->customer) { + return; + }*/ + } + + public function loadCustomer() + { + $customer = CustomerQuery::create()->findOne(); + if(null === $customer) { + return null; + } + + $this->container->get('thelia.securityContext')->setCustomerUser($customer); + + return $customer; + } + + public function testSetDeliveryAddress() + { + //$validAddressId = AddressQuery::create()->findOneByCustomerId($this->customer->getId()); + + $this->orderEvent->setDeliveryAddress(321); + + $this->orderAction->setDeliveryAddress($this->orderEvent); + + $this->assertEquals( + 321, + $this->orderEvent->getOrder()->chosenDeliveryAddress + ); + } + + public function testSetinvoiceAddress() + { + $this->orderEvent->setInvoiceAddress(654); + + $this->orderAction->setInvoiceAddress($this->orderEvent); + + $this->assertEquals( + 654, + $this->orderEvent->getOrder()->chosenInvoiceAddress + ); + } + + public function testSetDeliveryModule() + { + /*$deliveryModule = ModuleQuery::create() + ->filterByType(BaseModule::DELIVERY_MODULE_TYPE) + ->filterByActivate(1) + ->findOne(); + + if(null === $deliveryModule) { + return; + }*/ + + $this->orderEvent->setDeliveryModule(123); + + $this->orderAction->setDeliveryModule($this->orderEvent); + + $this->assertEquals( + 123, + $this->orderEvent->getOrder()->getDeliveryModuleId() + ); + } + + public function testSetPaymentModule() + { + $this->orderEvent->setPaymentModule(456); + + $this->orderAction->setPaymentModule($this->orderEvent); + + $this->assertEquals( + 456, + $this->orderEvent->getOrder()->getPaymentModuleId() + ); + } +} \ No newline at end of file From 49f79071a6d68f359f500f7bed5110ffad349152 Mon Sep 17 00:00:00 2001 From: Manuel Raynaud Date: Mon, 23 Sep 2013 11:39:53 +0200 Subject: [PATCH 014/100] allow update content position number --- core/lib/Thelia/Action/Content.php | 20 +++++++++++++ .../Thelia/Config/Resources/routing/admin.xml | 4 +++ .../Controller/Admin/ContentController.php | 28 +++++++++++++++++++ templates/admin/default/content-edit.html | 2 +- templates/admin/default/folders.html | 2 +- 5 files changed, 54 insertions(+), 2 deletions(-) diff --git a/core/lib/Thelia/Action/Content.php b/core/lib/Thelia/Action/Content.php index 4c8810a40..a76664955 100644 --- a/core/lib/Thelia/Action/Content.php +++ b/core/lib/Thelia/Action/Content.php @@ -26,6 +26,7 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Thelia\Core\Event\Content\ContentCreateEvent; use Thelia\Core\Event\Content\ContentUpdateEvent; use Thelia\Core\Event\TheliaEvents; +use Thelia\Core\Event\UpdatePositionEvent; use Thelia\Model\ContentQuery; use Thelia\Model\Content as ContentModel; use Thelia\Model\FolderQuery; @@ -77,6 +78,25 @@ class Content extends BaseAction implements EventSubscriberInterface } } + public function updatePosition(UpdatePositionEvent $event) + { + if(null !== $content = ContentQuery::create()->findPk($event->getObjectId())) { + $content->setDispatcher($this->getDispatcher()); + + switch($event->getMode()) + { + case UpdatePositionEvent::POSITION_ABSOLUTE: + $content->changeAbsolutePosition($event->getPosition()); + break; + case UpdatePositionEvent::POSITION_DOWN: + $content->movePositionDown(); + break; + case UpdatePositionEvent::POSITION_UP: + $content->movePositionUp(); + break; + } + } + } /** * Returns an array of event names this subscriber wants to listen to. * diff --git a/core/lib/Thelia/Config/Resources/routing/admin.xml b/core/lib/Thelia/Config/Resources/routing/admin.xml index 838bd6da8..b4beb9799 100755 --- a/core/lib/Thelia/Config/Resources/routing/admin.xml +++ b/core/lib/Thelia/Config/Resources/routing/admin.xml @@ -235,6 +235,10 @@ Thelia\Controller\Admin\ContentController::processUpdateAction + + Thelia\Controller\Admin\ContentController::updatePositionAction + + diff --git a/core/lib/Thelia/Controller/Admin/ContentController.php b/core/lib/Thelia/Controller/Admin/ContentController.php index 9d20830a8..d91596ac0 100644 --- a/core/lib/Thelia/Controller/Admin/ContentController.php +++ b/core/lib/Thelia/Controller/Admin/ContentController.php @@ -25,6 +25,7 @@ namespace Thelia\Controller\Admin; use Thelia\Core\Event\Content\ContentCreateEvent; use Thelia\Core\Event\Content\ContentUpdateEvent; use Thelia\Core\Event\TheliaEvents; +use Thelia\Core\Event\UpdatePositionEvent; use Thelia\Form\ContentCreationForm; use Thelia\Form\ContentModificationForm; use Thelia\Model\ContentQuery; @@ -286,4 +287,31 @@ class ContentController extends AbstractCrudController ); } } + + /** + * @param $event \Thelia\Core\Event\UpdatePositionEvent + * @return null|Response + */ + protected function performAdditionalUpdatePositionAction($event) + { + + if (null !== $content = ContentQuery::create()->findPk($event->getObjectId())) { + // Redirect to parent category list + $this->redirectToRoute( + 'admin.folders.default', + array('parent' => $content->getDefaultFolderId()) + ); + } + + return null; + } + + protected function createUpdatePositionEvent($positionChangeMode, $positionValue) + { + return new UpdatePositionEvent( + $this->getRequest()->get('content_id', null), + $positionChangeMode, + $positionValue + ); + } } \ No newline at end of file diff --git a/templates/admin/default/content-edit.html b/templates/admin/default/content-edit.html index a7b55dfb8..f15718f91 100644 --- a/templates/admin/default/content-edit.html +++ b/templates/admin/default/content-edit.html @@ -57,7 +57,7 @@ {form name="thelia.admin.content.modification"}
- {include file="includes/inner-form-toolbar.html" close_url="{url path='/admin/folders' parent=0}"} + {include file="includes/inner-form-toolbar.html" close_url="{url path='/admin/folders' parent=$DEFAULT_FOLDER}"} {* Be sure to get the folder ID, even if the form could not be validated *} diff --git a/templates/admin/default/folders.html b/templates/admin/default/folders.html index fca34ba21..a4e1c0fc7 100644 --- a/templates/admin/default/folders.html +++ b/templates/admin/default/folders.html @@ -288,7 +288,7 @@ {admin_position_block permission="admin.content.edit" - path={url path='/admin/contents/update-position' content_id=$ID} + path={url path='/admin/content/update-position' content_id=$ID} url_parameter="content_id" in_place_edit_class="contentPositionChange" position=$POSITION From 74fde51d96490b43c451674b530b820ffc015ad4 Mon Sep 17 00:00:00 2001 From: Manuel Raynaud Date: Mon, 23 Sep 2013 11:44:05 +0200 Subject: [PATCH 015/100] allow to load jquery offline --- templates/admin/default/admin-layout.tpl | 10 +++++++++- templates/admin/default/assets/js/libs/jquery.js | 6 ++++++ 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 templates/admin/default/assets/js/libs/jquery.js diff --git a/templates/admin/default/admin-layout.tpl b/templates/admin/default/admin-layout.tpl index 553466def..6fe492f30 100644 --- a/templates/admin/default/admin-layout.tpl +++ b/templates/admin/default/admin-layout.tpl @@ -221,8 +221,16 @@ {* -- Javascript section ------------------------------------------------ *} {block name="before-javascript-include"}{/block} + + + - {debugbar_renderjs} {debugbar_renderresult} diff --git a/templates/admin/default/assets/js/libs/jquery.js b/templates/admin/default/assets/js/libs/jquery.js new file mode 100644 index 000000000..e01079755 --- /dev/null +++ b/templates/admin/default/assets/js/libs/jquery.js @@ -0,0 +1,6 @@ +/*! jQuery v1.10.1 | (c) 2005, 2013 jQuery Foundation, Inc. | jquery.org/license + +*/ +(function(e,t){var n,r,i=typeof t,o=e.location,a=e.document,s=a.documentElement,l=e.jQuery,u=e.$,c={},p=[],f="1.10.1",d=p.concat,h=p.push,g=p.slice,m=p.indexOf,y=c.toString,v=c.hasOwnProperty,b=f.trim,x=function(e,t){return new x.fn.init(e,t,r)},w=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=/\S+/g,C=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,N=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,k=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,E=/^[\],:{}\s]*$/,S=/(?:^|:|,)(?:\s*\[)+/g,A=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,j=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,D=/^-ms-/,L=/-([\da-z])/gi,H=function(e,t){return t.toUpperCase()},q=function(e){(a.addEventListener||"load"===e.type||"complete"===a.readyState)&&(_(),x.ready())},_=function(){a.addEventListener?(a.removeEventListener("DOMContentLoaded",q,!1),e.removeEventListener("load",q,!1)):(a.detachEvent("onreadystatechange",q),e.detachEvent("onload",q))};x.fn=x.prototype={jquery:f,constructor:x,init:function(e,n,r){var i,o;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 x?n[0]:n,x.merge(this,x.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:a,!0)),k.test(i[1])&&x.isPlainObject(n))for(i in n)x.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(o=a.getElementById(i[2]),o&&o.parentNode){if(o.id!==i[2])return r.find(e);this.length=1,this[0]=o}return this.context=a,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):x.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),x.makeArray(e,this))},selector:"",length:0,toArray:function(){return g.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=x.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return x.each(this,e,t)},ready:function(e){return x.ready.promise().done(e),this},slice:function(){return this.pushStack(g.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(x.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:h,sort:[].sort,splice:[].splice},x.fn.init.prototype=x.fn,x.extend=x.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},l=1,u=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},l=2),"object"==typeof s||x.isFunction(s)||(s={}),u===l&&(s=this,--l);u>l;l++)if(null!=(o=arguments[l]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(x.isPlainObject(r)||(n=x.isArray(r)))?(n?(n=!1,a=e&&x.isArray(e)?e:[]):a=e&&x.isPlainObject(e)?e:{},s[i]=x.extend(c,a,r)):r!==t&&(s[i]=r));return s},x.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),noConflict:function(t){return e.$===x&&(e.$=u),t&&e.jQuery===x&&(e.jQuery=l),x},isReady:!1,readyWait:1,holdReady:function(e){e?x.readyWait++:x.ready(!0)},ready:function(e){if(e===!0?!--x.readyWait:!x.isReady){if(!a.body)return setTimeout(x.ready);x.isReady=!0,e!==!0&&--x.readyWait>0||(n.resolveWith(a,[x]),x.fn.trigger&&x(a).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===x.type(e)},isArray:Array.isArray||function(e){return"array"===x.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?c[y.call(e)]||"object":typeof e},isPlainObject:function(e){var n;if(!e||"object"!==x.type(e)||e.nodeType||x.isWindow(e))return!1;try{if(e.constructor&&!v.call(e,"constructor")&&!v.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(r){return!1}if(x.support.ownLast)for(n in e)return v.call(e,n);for(n in e);return n===t||v.call(e,n)},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||a;var r=k.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=x.buildFragment([e],t,i),i&&x(i).remove(),x.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=x.trim(n),n&&E.test(n.replace(A,"@").replace(j,"]").replace(S,"")))?Function("return "+n)():(x.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||x.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&x.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(D,"ms-").replace(L,H)},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:b&&!b.call("\ufeff\u00a0")?function(e){return null==e?"":b.call(e)}:function(e){return null==e?"":(e+"").replace(C,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?x.merge(n,"string"==typeof e?[e]:e):h.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(m)return m.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 d.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(o=e[n],n=e,e=o),x.isFunction(e)?(r=g.call(arguments,2),i=function(){return e.apply(n||this,r.concat(g.call(arguments)))},i.guid=e.guid=e.guid||x.guid++,i):t},access:function(e,n,r,i,o,a,s){var l=0,u=e.length,c=null==r;if("object"===x.type(r)){o=!0;for(l in r)x.access(e,n,l,r[l],!0,a,s)}else if(i!==t&&(o=!0,x.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(x(e),n)})),n))for(;u>l;l++)n(e[l],r,s?i:i.call(e[l],l,n(e[l],r)));return o?e:c?n.call(e):u?n(e[0],r):a},now:function(){return(new Date).getTime()},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}}),x.ready.promise=function(t){if(!n)if(n=x.Deferred(),"complete"===a.readyState)setTimeout(x.ready);else if(a.addEventListener)a.addEventListener("DOMContentLoaded",q,!1),e.addEventListener("load",q,!1);else{a.attachEvent("onreadystatechange",q),e.attachEvent("onload",q);var r=!1;try{r=null==e.frameElement&&a.documentElement}catch(i){}r&&r.doScroll&&function o(){if(!x.isReady){try{r.doScroll("left")}catch(e){return setTimeout(o,50)}_(),x.ready()}}()}return n.promise(t)},x.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){c["[object "+t+"]"]=t.toLowerCase()});function M(e){var t=e.length,n=x.type(e);return x.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}r=x(a),function(e,t){var n,r,i,o,a,s,l,u,c,p,f,d,h,g,m,y,v,b="sizzle"+-new Date,w=e.document,T=0,C=0,N=lt(),k=lt(),E=lt(),S=!1,A=function(){return 0},j=typeof t,D=1<<31,L={}.hasOwnProperty,H=[],q=H.pop,_=H.push,M=H.push,O=H.slice,F=H.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},B="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",P="[\\x20\\t\\r\\n\\f]",R="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",W=R.replace("w","w#"),$="\\["+P+"*("+R+")"+P+"*(?:([*^$|!~]?=)"+P+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+W+")|)|)"+P+"*\\]",I=":("+R+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+$.replace(3,8)+")*)|.*)\\)|)",z=RegExp("^"+P+"+|((?:^|[^\\\\])(?:\\\\.)*)"+P+"+$","g"),X=RegExp("^"+P+"*,"+P+"*"),U=RegExp("^"+P+"*([>+~]|"+P+")"+P+"*"),V=RegExp(P+"*[+~]"),Y=RegExp("="+P+"*([^\\]'\"]*)"+P+"*\\]","g"),J=RegExp(I),G=RegExp("^"+W+"$"),Q={ID:RegExp("^#("+R+")"),CLASS:RegExp("^\\.("+R+")"),TAG:RegExp("^("+R.replace("w","w*")+")"),ATTR:RegExp("^"+$),PSEUDO:RegExp("^"+I),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+P+"*(even|odd|(([+-]|)(\\d*)n|)"+P+"*(?:([+-]|)"+P+"*(\\d+)|))"+P+"*\\)|)","i"),bool:RegExp("^(?:"+B+")$","i"),needsContext:RegExp("^"+P+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+P+"*((?:-\\d)?\\d*)"+P+"*\\)|)(?=[^-]|$)","i")},K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,et=/^(?:input|select|textarea|button)$/i,tt=/^h\d$/i,nt=/'|\\/g,rt=RegExp("\\\\([\\da-f]{1,6}"+P+"?|("+P+")|.)","ig"),it=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:0>r?String.fromCharCode(r+65536):String.fromCharCode(55296|r>>10,56320|1023&r)};try{M.apply(H=O.call(w.childNodes),w.childNodes),H[w.childNodes.length].nodeType}catch(ot){M={apply:H.length?function(e,t){_.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function at(e,t,n,i){var o,a,s,l,u,c,d,m,y,x;if((t?t.ownerDocument||t:w)!==f&&p(t),t=t||f,n=n||[],!e||"string"!=typeof e)return n;if(1!==(l=t.nodeType)&&9!==l)return[];if(h&&!i){if(o=Z.exec(e))if(s=o[1]){if(9===l){if(a=t.getElementById(s),!a||!a.parentNode)return n;if(a.id===s)return n.push(a),n}else if(t.ownerDocument&&(a=t.ownerDocument.getElementById(s))&&v(t,a)&&a.id===s)return n.push(a),n}else{if(o[2])return M.apply(n,t.getElementsByTagName(e)),n;if((s=o[3])&&r.getElementsByClassName&&t.getElementsByClassName)return M.apply(n,t.getElementsByClassName(s)),n}if(r.qsa&&(!g||!g.test(e))){if(m=d=b,y=t,x=9===l&&e,1===l&&"object"!==t.nodeName.toLowerCase()){c=bt(e),(d=t.getAttribute("id"))?m=d.replace(nt,"\\$&"):t.setAttribute("id",m),m="[id='"+m+"'] ",u=c.length;while(u--)c[u]=m+xt(c[u]);y=V.test(e)&&t.parentNode||t,x=c.join(",")}if(x)try{return M.apply(n,y.querySelectorAll(x)),n}catch(T){}finally{d||t.removeAttribute("id")}}}return At(e.replace(z,"$1"),t,n,i)}function st(e){return K.test(e+"")}function lt(){var e=[];function t(n,r){return e.push(n+=" ")>o.cacheLength&&delete t[e.shift()],t[n]=r}return t}function ut(e){return e[b]=!0,e}function ct(e){var t=f.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function pt(e,t,n){e=e.split("|");var r,i=e.length,a=n?null:t;while(i--)(r=o.attrHandle[e[i]])&&r!==t||(o.attrHandle[e[i]]=a)}function ft(e,t){var n=e.getAttributeNode(t);return n&&n.specified?n.value:e[t]===!0?t.toLowerCase():null}function dt(e,t){return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}function ht(e){return"input"===e.nodeName.toLowerCase()?e.defaultValue:t}function gt(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||D)-(~e.sourceIndex||D);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function mt(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function yt(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function vt(e){return ut(function(t){return t=+t,ut(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]))})})}s=at.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},r=at.support={},p=at.setDocument=function(e){var n=e?e.ownerDocument||e:w,i=n.parentWindow;return n!==f&&9===n.nodeType&&n.documentElement?(f=n,d=n.documentElement,h=!s(n),i&&i.frameElement&&i.attachEvent("onbeforeunload",function(){p()}),r.attributes=ct(function(e){return e.innerHTML="",pt("type|href|height|width",dt,"#"===e.firstChild.getAttribute("href")),pt(B,ft,null==e.getAttribute("disabled")),e.className="i",!e.getAttribute("className")}),r.input=ct(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")}),pt("value",ht,r.attributes&&r.input),r.getElementsByTagName=ct(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),r.getElementsByClassName=ct(function(e){return e.innerHTML="
",e.firstChild.className="i",2===e.getElementsByClassName("i").length}),r.getById=ct(function(e){return d.appendChild(e).id=b,!n.getElementsByName||!n.getElementsByName(b).length}),r.getById?(o.find.ID=function(e,t){if(typeof t.getElementById!==j&&h){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){return e.getAttribute("id")===t}}):(delete o.find.ID,o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){var n=typeof e.getAttributeNode!==j&&e.getAttributeNode("id");return n&&n.value===t}}),o.find.TAG=r.getElementsByTagName?function(e,n){return typeof n.getElementsByTagName!==j?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},o.find.CLASS=r.getElementsByClassName&&function(e,n){return typeof n.getElementsByClassName!==j&&h?n.getElementsByClassName(e):t},m=[],g=[],(r.qsa=st(n.querySelectorAll))&&(ct(function(e){e.innerHTML="",e.querySelectorAll("[selected]").length||g.push("\\["+P+"*(?:value|"+B+")"),e.querySelectorAll(":checked").length||g.push(":checked")}),ct(function(e){var t=n.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("t",""),e.querySelectorAll("[t^='']").length&&g.push("[*^$]="+P+"*(?:''|\"\")"),e.querySelectorAll(":enabled").length||g.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),g.push(",.*:")})),(r.matchesSelector=st(y=d.webkitMatchesSelector||d.mozMatchesSelector||d.oMatchesSelector||d.msMatchesSelector))&&ct(function(e){r.disconnectedMatch=y.call(e,"div"),y.call(e,"[s!='']:x"),m.push("!=",I)}),g=g.length&&RegExp(g.join("|")),m=m.length&&RegExp(m.join("|")),v=st(d.contains)||d.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},r.sortDetached=ct(function(e){return 1&e.compareDocumentPosition(n.createElement("div"))}),A=d.compareDocumentPosition?function(e,t){if(e===t)return S=!0,0;var i=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t);return i?1&i||!r.sortDetached&&t.compareDocumentPosition(e)===i?e===n||v(w,e)?-1:t===n||v(w,t)?1:c?F.call(c,e)-F.call(c,t):0:4&i?-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 S=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:c?F.call(c,e)-F.call(c,t):0;if(o===a)return gt(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?gt(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},n):f},at.matches=function(e,t){return at(e,null,null,t)},at.matchesSelector=function(e,t){if((e.ownerDocument||e)!==f&&p(e),t=t.replace(Y,"='$1']"),!(!r.matchesSelector||!h||m&&m.test(t)||g&&g.test(t)))try{var n=y.call(e,t);if(n||r.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(i){}return at(t,f,null,[e]).length>0},at.contains=function(e,t){return(e.ownerDocument||e)!==f&&p(e),v(e,t)},at.attr=function(e,n){(e.ownerDocument||e)!==f&&p(e);var i=o.attrHandle[n.toLowerCase()],a=i&&L.call(o.attrHandle,n.toLowerCase())?i(e,n,!h):t;return a===t?r.attributes||!h?e.getAttribute(n):(a=e.getAttributeNode(n))&&a.specified?a.value:null:a},at.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},at.uniqueSort=function(e){var t,n=[],i=0,o=0;if(S=!r.detectDuplicates,c=!r.sortStable&&e.slice(0),e.sort(A),S){while(t=e[o++])t===e[o]&&(i=n.push(o));while(i--)e.splice(n[i],1)}return e},a=at.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+=a(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=a(t);return n},o=at.selectors={cacheLength:50,createPseudo:ut,match:Q,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(rt,it),e[3]=(e[4]||e[5]||"").replace(rt,it),"~="===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]||at.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]&&at.error(e[0]),e},PSEUDO:function(e){var n,r=!e[5]&&e[2];return Q.CHILD.test(e[0])?null:(e[3]&&e[4]!==t?e[2]=e[4]:r&&J.test(r)&&(n=bt(r,!0))&&(n=r.indexOf(")",r.length-n)-r.length)&&(e[0]=e[0].slice(0,n),e[2]=r.slice(0,n)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(rt,it).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=N[e+" "];return t||(t=RegExp("(^|"+P+")"+e+"("+P+"|$)"))&&N(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==j&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=at.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,l){var u,c,p,f,d,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!l&&!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[b]||(m[b]={}),u=c[e]||[],d=u[0]===T&&u[1],f=u[0]===T&&u[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]=[T,d,f];break}}else if(v&&(u=(t[b]||(t[b]={}))[e])&&u[0]===T)f=u[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[b]||(p[b]={}))[e]=[T,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=o.pseudos[e]||o.setFilters[e.toLowerCase()]||at.error("unsupported pseudo: "+e);return r[b]?r(t):r.length>1?(n=[e,e,"",t],o.setFilters.hasOwnProperty(e.toLowerCase())?ut(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=F.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:ut(function(e){var t=[],n=[],r=l(e.replace(z,"$1"));return r[b]?ut(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:ut(function(e){return function(t){return at(e,t).length>0}}),contains:ut(function(e){return function(t){return(t.textContent||t.innerText||a(t)).indexOf(e)>-1}}),lang:ut(function(e){return G.test(e||"")||at.error("unsupported lang: "+e),e=e.replace(rt,it).toLowerCase(),function(t){var n;do if(n=h?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===d},focus:function(e){return e===f.activeElement&&(!f.hasFocus||f.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!o.pseudos.empty(e)},header:function(e){return tt.test(e.nodeName)},input:function(e){return et.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:vt(function(){return[0]}),last:vt(function(e,t){return[t-1]}),eq:vt(function(e,t,n){return[0>n?n+t:n]}),even:vt(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:vt(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:vt(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:vt(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})o.pseudos[n]=mt(n);for(n in{submit:!0,reset:!0})o.pseudos[n]=yt(n);function bt(e,t){var n,r,i,a,s,l,u,c=k[e+" "];if(c)return t?0:c.slice(0);s=e,l=[],u=o.preFilter;while(s){(!n||(r=X.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),l.push(i=[])),n=!1,(r=U.exec(s))&&(n=r.shift(),i.push({value:n,type:r[0].replace(z," ")}),s=s.slice(n.length));for(a in o.filter)!(r=Q[a].exec(s))||u[a]&&!(r=u[a](r))||(n=r.shift(),i.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?at.error(e):k(e,l).slice(0)}function xt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function wt(e,t,n){var r=t.dir,o=n&&"parentNode"===r,a=C++;return t.first?function(t,n,i){while(t=t[r])if(1===t.nodeType||o)return e(t,n,i)}:function(t,n,s){var l,u,c,p=T+" "+a;if(s){while(t=t[r])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[r])if(1===t.nodeType||o)if(c=t[b]||(t[b]={}),(u=c[r])&&u[0]===p){if((l=u[1])===!0||l===i)return l===!0}else if(u=c[r]=[p],u[1]=e(t,n,s)||i,u[1]===!0)return!0}}function Tt(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 Ct(e,t,n,r,i){var o,a=[],s=0,l=e.length,u=null!=t;for(;l>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),u&&t.push(s));return a}function Nt(e,t,n,r,i,o){return r&&!r[b]&&(r=Nt(r)),i&&!i[b]&&(i=Nt(i,o)),ut(function(o,a,s,l){var u,c,p,f=[],d=[],h=a.length,g=o||St(t||"*",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:Ct(g,f,e,s,l),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,l),r){u=Ct(y,d),r(u,[],s,l),c=u.length;while(c--)(p=u[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){u=[],c=y.length;while(c--)(p=y[c])&&u.push(m[c]=p);i(null,y=[],u,l)}c=y.length;while(c--)(p=y[c])&&(u=i?F.call(o,p):f[c])>-1&&(o[u]=!(a[u]=p))}}else y=Ct(y===a?y.splice(h,y.length):y),i?i(null,a,y,l):M.apply(a,y)})}function kt(e){var t,n,r,i=e.length,a=o.relative[e[0].type],s=a||o.relative[" "],l=a?1:0,c=wt(function(e){return e===t},s,!0),p=wt(function(e){return F.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==u)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;i>l;l++)if(n=o.relative[e[l].type])f=[wt(Tt(f),n)];else{if(n=o.filter[e[l].type].apply(null,e[l].matches),n[b]){for(r=++l;i>r;r++)if(o.relative[e[r].type])break;return Nt(l>1&&Tt(f),l>1&&xt(e.slice(0,l-1).concat({value:" "===e[l-2].type?"*":""})).replace(z,"$1"),n,r>l&&kt(e.slice(l,r)),i>r&&kt(e=e.slice(r)),i>r&&xt(e))}f.push(n)}return Tt(f)}function Et(e,t){var n=0,r=t.length>0,a=e.length>0,s=function(s,l,c,p,d){var h,g,m,y=[],v=0,b="0",x=s&&[],w=null!=d,C=u,N=s||a&&o.find.TAG("*",d&&l.parentNode||l),k=T+=null==C?1:Math.random()||.1;for(w&&(u=l!==f&&l,i=n);null!=(h=N[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,l,c)){p.push(h);break}w&&(T=k,i=++n)}r&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,r&&b!==v){g=0;while(m=t[g++])m(x,y,l,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=q.call(p));y=Ct(y)}M.apply(p,y),w&&!s&&y.length>0&&v+t.length>1&&at.uniqueSort(p)}return w&&(T=k,u=C),x};return r?ut(s):s}l=at.compile=function(e,t){var n,r=[],i=[],o=E[e+" "];if(!o){t||(t=bt(e)),n=t.length;while(n--)o=kt(t[n]),o[b]?r.push(o):i.push(o);o=E(e,Et(i,r))}return o};function St(e,t,n){var r=0,i=t.length;for(;i>r;r++)at(e,t[r],n);return n}function At(e,t,n,i){var a,s,u,c,p,f=bt(e);if(!i&&1===f.length){if(s=f[0]=f[0].slice(0),s.length>2&&"ID"===(u=s[0]).type&&r.getById&&9===t.nodeType&&h&&o.relative[s[1].type]){if(t=(o.find.ID(u.matches[0].replace(rt,it),t)||[])[0],!t)return n;e=e.slice(s.shift().value.length)}a=Q.needsContext.test(e)?0:s.length;while(a--){if(u=s[a],o.relative[c=u.type])break;if((p=o.find[c])&&(i=p(u.matches[0].replace(rt,it),V.test(s[0].type)&&t.parentNode||t))){if(s.splice(a,1),e=i.length&&xt(s),!e)return M.apply(n,i),n;break}}}return l(e,f)(i,t,!h,n,V.test(e)),n}o.pseudos.nth=o.pseudos.eq;function jt(){}jt.prototype=o.filters=o.pseudos,o.setFilters=new jt,r.sortStable=b.split("").sort(A).join("")===b,p(),[0,0].sort(A),r.detectDuplicates=S,x.find=at,x.expr=at.selectors,x.expr[":"]=x.expr.pseudos,x.unique=at.uniqueSort,x.text=at.getText,x.isXMLDoc=at.isXML,x.contains=at.contains}(e);var O={};function F(e){var t=O[e]={};return x.each(e.match(T)||[],function(e,n){t[n]=!0}),t}x.Callbacks=function(e){e="string"==typeof e?O[e]||F(e):x.extend({},e);var n,r,i,o,a,s,l=[],u=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=l.length,n=!0;l&&o>a;a++)if(l[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,l&&(u?u.length&&c(u.shift()):r?l=[]:p.disable())},p={add:function(){if(l){var t=l.length;(function i(t){x.each(t,function(t,n){var r=x.type(n);"function"===r?e.unique&&p.has(n)||l.push(n):n&&n.length&&"string"!==r&&i(n)})})(arguments),n?o=l.length:r&&(s=t,c(r))}return this},remove:function(){return l&&x.each(arguments,function(e,t){var r;while((r=x.inArray(t,l,r))>-1)l.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?x.inArray(e,l)>-1:!(!l||!l.length)},empty:function(){return l=[],o=0,this},disable:function(){return l=u=r=t,this},disabled:function(){return!l},lock:function(){return u=t,r||p.disable(),this},locked:function(){return!u},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],!l||i&&!u||(n?u.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},x.extend({Deferred:function(e){var t=[["resolve","done",x.Callbacks("once memory"),"resolved"],["reject","fail",x.Callbacks("once memory"),"rejected"],["notify","progress",x.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return x.Deferred(function(n){x.each(t,function(t,o){var a=o[0],s=x.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&x.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?x.extend(e,r):r}},i={};return r.pipe=r.then,x.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=g.call(arguments),r=n.length,i=1!==r||e&&x.isFunction(e.promise)?r:0,o=1===i?e:x.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?g.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,l,u;if(r>1)for(s=Array(r),l=Array(r),u=Array(r);r>t;t++)n[t]&&x.isFunction(n[t].promise)?n[t].promise().done(a(t,u,n)).fail(o.reject).progress(a(t,l,s)):--i;return i||o.resolveWith(u,n),o.promise()}}),x.support=function(t){var n,r,o,s,l,u,c,p,f,d=a.createElement("div");if(d.setAttribute("className","t"),d.innerHTML="
a",n=d.getElementsByTagName("*")||[],r=d.getElementsByTagName("a")[0],!r||!r.style||!n.length)return t;s=a.createElement("select"),u=s.appendChild(a.createElement("option")),o=d.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t.getSetAttribute="t"!==d.className,t.leadingWhitespace=3===d.firstChild.nodeType,t.tbody=!d.getElementsByTagName("tbody").length,t.htmlSerialize=!!d.getElementsByTagName("link").length,t.style=/top/.test(r.getAttribute("style")),t.hrefNormalized="/a"===r.getAttribute("href"),t.opacity=/^0.5/.test(r.style.opacity),t.cssFloat=!!r.style.cssFloat,t.checkOn=!!o.value,t.optSelected=u.selected,t.enctype=!!a.createElement("form").enctype,t.html5Clone="<:nav>"!==a.createElement("nav").cloneNode(!0).outerHTML,t.inlineBlockNeedsLayout=!1,t.shrinkWrapBlocks=!1,t.pixelPosition=!1,t.deleteExpando=!0,t.noCloneEvent=!0,t.reliableMarginRight=!0,t.boxSizingReliable=!0,o.checked=!0,t.noCloneChecked=o.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!u.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}o=a.createElement("input"),o.setAttribute("value",""),t.input=""===o.getAttribute("value"),o.value="t",o.setAttribute("type","radio"),t.radioValue="t"===o.value,o.setAttribute("checked","t"),o.setAttribute("name","t"),l=a.createDocumentFragment(),l.appendChild(o),t.appendChecked=o.checked,t.checkClone=l.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;d.style.backgroundClip="content-box",d.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===d.style.backgroundClip;for(f in x(t))break;return t.ownLast="0"!==f,x(function(){var n,r,o,s="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",l=a.getElementsByTagName("body")[0];l&&(n=a.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",l.appendChild(n).appendChild(d),d.innerHTML="
t
",o=d.getElementsByTagName("td"),o[0].style.cssText="padding:0;margin:0;border:0;display:none",p=0===o[0].offsetHeight,o[0].style.display="",o[1].style.display="none",t.reliableHiddenOffsets=p&&0===o[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%;",x.swap(l,null!=l.style.zoom?{zoom:1}:{},function(){t.boxSizing=4===d.offsetWidth}),e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(d,null)||{width:"4px"}).width,r=d.appendChild(a.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&&(l.style.zoom=1)),l.removeChild(n),n=d=o=r=null) +}),n=s=l=u=r=o=null,t}({});var B=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;function R(e,n,r,i){if(x.acceptData(e)){var o,a,s=x.expando,l=e.nodeType,u=l?x.cache:e,c=l?e[s]:e[s]&&s;if(c&&u[c]&&(i||u[c].data)||r!==t||"string"!=typeof n)return c||(c=l?e[s]=p.pop()||x.guid++:s),u[c]||(u[c]=l?{}:{toJSON:x.noop}),("object"==typeof n||"function"==typeof n)&&(i?u[c]=x.extend(u[c],n):u[c].data=x.extend(u[c].data,n)),a=u[c],i||(a.data||(a.data={}),a=a.data),r!==t&&(a[x.camelCase(n)]=r),"string"==typeof n?(o=a[n],null==o&&(o=a[x.camelCase(n)])):o=a,o}}function W(e,t,n){if(x.acceptData(e)){var r,i,o=e.nodeType,a=o?x.cache:e,s=o?e[x.expando]:x.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){x.isArray(t)?t=t.concat(x.map(t,x.camelCase)):t in r?t=[t]:(t=x.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;while(i--)delete r[t[i]];if(n?!I(r):!x.isEmptyObject(r))return}(n||(delete a[s].data,I(a[s])))&&(o?x.cleanData([e],!0):x.support.deleteExpando||a!=a.window?delete a[s]:a[s]=null)}}}x.extend({cache:{},noData:{applet:!0,embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(e){return e=e.nodeType?x.cache[e[x.expando]]:e[x.expando],!!e&&!I(e)},data:function(e,t,n){return R(e,t,n)},removeData:function(e,t){return W(e,t)},_data:function(e,t,n){return R(e,t,n,!0)},_removeData:function(e,t){return W(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&x.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),x.fn.extend({data:function(e,n){var r,i,o=null,a=0,s=this[0];if(e===t){if(this.length&&(o=x.data(s),1===s.nodeType&&!x._data(s,"parsedAttrs"))){for(r=s.attributes;r.length>a;a++)i=r[a].name,0===i.indexOf("data-")&&(i=x.camelCase(i.slice(5)),$(s,i,o[i]));x._data(s,"parsedAttrs",!0)}return o}return"object"==typeof e?this.each(function(){x.data(this,e)}):arguments.length>1?this.each(function(){x.data(this,e,n)}):s?$(s,e,x.data(s,e)):null},removeData:function(e){return this.each(function(){x.removeData(this,e)})}});function $(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(P,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:B.test(r)?x.parseJSON(r):r}catch(o){}x.data(e,n,r)}else r=t}return r}function I(e){var t;for(t in e)if(("data"!==t||!x.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}x.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=x._data(e,n),r&&(!i||x.isArray(r)?i=x._data(e,n,x.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=x.queue(e,t),r=n.length,i=n.shift(),o=x._queueHooks(e,t),a=function(){x.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),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 x._data(e,n)||x._data(e,n,{empty:x.Callbacks("once memory").add(function(){x._removeData(e,t+"queue"),x._removeData(e,n)})})}}),x.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?x.queue(this[0],e):n===t?this:this.each(function(){var t=x.queue(this,e,n);x._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&x.dequeue(this,e)})},dequeue:function(e){return this.each(function(){x.dequeue(this,e)})},delay:function(e,t){return e=x.fx?x.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=x.Deferred(),a=this,s=this.length,l=function(){--i||o.resolveWith(a,[a])};"string"!=typeof e&&(n=e,e=t),e=e||"fx";while(s--)r=x._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(l));return l(),o.promise(n)}});var z,X,U=/[\t\r\n\f]/g,V=/\r/g,Y=/^(?:input|select|textarea|button|object)$/i,J=/^(?:a|area)$/i,G=/^(?:checked|selected)$/i,Q=x.support.getSetAttribute,K=x.support.input;x.fn.extend({attr:function(e,t){return x.access(this,x.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){x.removeAttr(this,e)})},prop:function(e,t){return x.access(this,x.prop,e,t,arguments.length>1)},removeProp:function(e){return e=x.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,l="string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).addClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=x.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,l=0===arguments.length||"string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).removeClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?x.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e,r="boolean"==typeof t;return x.isFunction(e)?this.each(function(n){x(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var o,a=0,s=x(this),l=t,u=e.match(T)||[];while(o=u[a++])l=r?l:!s.hasClass(o),s[l?"addClass":"removeClass"](o)}else(n===i||"boolean"===n)&&(this.className&&x._data(this,"__className__",this.className),this.className=this.className||e===!1?"":x._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(U," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=x.isFunction(e),this.each(function(n){var o;1===this.nodeType&&(o=i?e.call(this,n,x(this).val()):e,null==o?o="":"number"==typeof o?o+="":x.isArray(o)&&(o=x.map(o,function(e){return null==e?"":e+""})),r=x.valHooks[this.type]||x.valHooks[this.nodeName.toLowerCase()],r&&"set"in r&&r.set(this,o,"value")!==t||(this.value=o))});if(o)return r=x.valHooks[o.type]||x.valHooks[o.nodeName.toLowerCase()],r&&"get"in r&&(n=r.get(o,"value"))!==t?n:(n=o.value,"string"==typeof n?n.replace(V,""):null==n?"":n)}}}),x.extend({valHooks:{option:{get:function(e){var t=x.find.attr(e,"value");return null!=t?t: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,l=0>i?s:o?i:0;for(;s>l;l++)if(n=r[l],!(!n.selected&&l!==i||(x.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&x.nodeName(n.parentNode,"optgroup"))){if(t=x(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n,r,i=e.options,o=x.makeArray(t),a=i.length;while(a--)r=i[a],(r.selected=x.inArray(x(r).val(),o)>=0)&&(n=!0);return n||(e.selectedIndex=-1),o}}},attr:function(e,n,r){var o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return typeof e.getAttribute===i?x.prop(e,n,r):(1===s&&x.isXMLDoc(e)||(n=n.toLowerCase(),o=x.attrHooks[n]||(x.expr.match.bool.test(n)?X:z)),r===t?o&&"get"in o&&null!==(a=o.get(e,n))?a:(a=x.find.attr(e,n),null==a?t:a):null!==r?o&&"set"in o&&(a=o.set(e,r,n))!==t?a:(e.setAttribute(n,r+""),r):(x.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(T);if(o&&1===e.nodeType)while(n=o[i++])r=x.propFix[n]||n,x.expr.match.bool.test(n)?K&&Q||!G.test(n)?e[r]=!1:e[x.camelCase("default-"+n)]=e[r]=!1:x.attr(e,n,""),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!x.support.radioValue&&"radio"===t&&x.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{"for":"htmlFor","class":"className"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!x.isXMLDoc(e),a&&(n=x.propFix[n]||n,o=x.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 t=x.find.attr(e,"tabindex");return t?parseInt(t,10):Y.test(e.nodeName)||J.test(e.nodeName)&&e.href?0:-1}}}}),X={set:function(e,t,n){return t===!1?x.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&x.propFix[n]||n,n):e[x.camelCase("default-"+n)]=e[n]=!0,n}},x.each(x.expr.match.bool.source.match(/\w+/g),function(e,n){var r=x.expr.attrHandle[n]||x.find.attr;x.expr.attrHandle[n]=K&&Q||!G.test(n)?function(e,n,i){var o=x.expr.attrHandle[n],a=i?t:(x.expr.attrHandle[n]=t)!=r(e,n,i)?n.toLowerCase():null;return x.expr.attrHandle[n]=o,a}:function(e,n,r){return r?t:e[x.camelCase("default-"+n)]?n.toLowerCase():null}}),K&&Q||(x.attrHooks.value={set:function(e,n,r){return x.nodeName(e,"input")?(e.defaultValue=n,t):z&&z.set(e,n,r)}}),Q||(z={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}},x.expr.attrHandle.id=x.expr.attrHandle.name=x.expr.attrHandle.coords=function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&""!==i.value?i.value:null},x.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&r.specified?r.value:t},set:z.set},x.attrHooks.contenteditable={set:function(e,t,n){z.set(e,""===t?!1:t,n)}},x.each(["width","height"],function(e,n){x.attrHooks[n]={set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}}})),x.support.hrefNormalized||x.each(["href","src"],function(e,t){x.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}}),x.support.style||(x.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),x.support.optSelected||(x.propHooks.selected={get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}}),x.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){x.propFix[this.toLowerCase()]=this}),x.support.enctype||(x.propFix.enctype="encoding"),x.each(["radio","checkbox"],function(){x.valHooks[this]={set:function(e,n){return x.isArray(n)?e.checked=x.inArray(x(e).val(),n)>=0:t}},x.support.checkOn||(x.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\.(.+)|)$/;function it(){return!0}function ot(){return!1}function at(){try{return a.activeElement}catch(e){}}x.event={global:{},add:function(e,n,r,o,a){var s,l,u,c,p,f,d,h,g,m,y,v=x._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=x.guid++),(l=v.events)||(l=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof x===i||e&&x.event.triggered===e.type?t:x.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||"").match(T)||[""],u=n.length;while(u--)s=rt.exec(n[u])||[],g=y=s[1],m=(s[2]||"").split(".").sort(),g&&(p=x.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=x.event.special[g]||{},d=x.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&x.expr.match.needsContext.test(a),namespace:m.join(".")},c),(h=l[g])||(h=l[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),x.event.global[g]=!0);e=null}},remove:function(e,t,n,r,i){var o,a,s,l,u,c,p,f,d,h,g,m=x.hasData(e)&&x._data(e);if(m&&(c=m.events)){t=(t||"").match(T)||[""],u=t.length;while(u--)if(s=rt.exec(t[u])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){p=x.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),l=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));l&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||x.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)x.event.remove(e,d+t[u],n,r,!0);x.isEmptyObject(c)&&(delete m.handle,x._removeData(e,"events"))}},trigger:function(n,r,i,o){var s,l,u,c,p,f,d,h=[i||a],g=v.call(n,"type")?n.type:n,m=v.call(n,"namespace")?n.namespace.split("."):[];if(u=f=i=i||a,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+x.event.triggered)&&(g.indexOf(".")>=0&&(m=g.split("."),g=m.shift(),m.sort()),l=0>g.indexOf(":")&&"on"+g,n=n[x.expando]?n:new x.Event(g,"object"==typeof n&&n),n.isTrigger=o?2:3,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]:x.makeArray(r,[n]),p=x.event.special[g]||{},o||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!o&&!p.noBubble&&!x.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(u=u.parentNode);u;u=u.parentNode)h.push(u),f=u;f===(i.ownerDocument||a)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((u=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(x._data(u,"events")||{})[n.type]&&x._data(u,"handle"),s&&s.apply(u,r),s=l&&u[l],s&&x.acceptData(u)&&s.apply&&s.apply(u,r)===!1&&n.preventDefault();if(n.type=g,!o&&!n.isDefaultPrevented()&&(!p._default||p._default.apply(h.pop(),r)===!1)&&x.acceptData(i)&&l&&i[g]&&!x.isWindow(i)){f=i[l],f&&(i[l]=null),x.event.triggered=g;try{i[g]()}catch(y){}x.event.triggered=t,f&&(i[l]=f)}return n.result}},dispatch:function(e){e=x.event.fix(e);var n,r,i,o,a,s=[],l=g.call(arguments),u=(x._data(this,"events")||{})[e.type]||[],c=x.event.special[e.type]||{};if(l[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=x.event.handlers.call(this,e,u),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=((x.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,l),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=[],l=n.delegateCount,u=e.target;if(l&&u.nodeType&&(!e.button||"click"!==e.type))for(;u!=this;u=u.parentNode||this)if(1===u.nodeType&&(u.disabled!==!0||"click"!==e.type)){for(o=[],a=0;l>a;a++)i=n[a],r=i.selector+" ",o[r]===t&&(o[r]=i.needsContext?x(r,this).index(u)>=0:x.find(r,this,null,[u]).length),o[r]&&o.push(i);o.length&&s.push({elem:u,handlers:o})}return n.length>l&&s.push({elem:this,handlers:n.slice(l)}),s},fix:function(e){if(e[x.expando])return e;var t,n,r,i=e.type,o=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 x.Event(o),t=r.length;while(t--)n=r[t],e[n]=o[n];return e.target||(e.target=o.srcElement||a),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,o):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,o,s=n.button,l=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||a,o=i.documentElement,r=i.body,e.pageX=n.clientX+(o&&o.scrollLeft||r&&r.scrollLeft||0)-(o&&o.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(o&&o.scrollTop||r&&r.scrollTop||0)-(o&&o.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&l&&(e.relatedTarget=l===e.target?n.toElement:l),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==at()&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===at()&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},click:{trigger:function(){return x.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t},_default:function(e){return x.nodeName(e.target,"a")}},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=x.extend(new x.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?x.event.trigger(i,null,t):x.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},x.removeEvent=a.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))},x.Event=function(e,n){return this instanceof x.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&&x.extend(this,n),this.timeStamp=e&&e.timeStamp||x.now(),this[x.expando]=!0,t):new x.Event(e,n)},x.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()}},x.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){x.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return(!i||i!==r&&!x.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),x.support.submitBubbles||(x.event.special.submit={setup:function(){return x.nodeName(this,"form")?!1:(x.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=x.nodeName(n,"input")||x.nodeName(n,"button")?n.form:t;r&&!x._data(r,"submitBubbles")&&(x.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),x._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&x.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return x.nodeName(this,"form")?!1:(x.event.remove(this,"._submit"),t)}}),x.support.changeBubbles||(x.event.special.change={setup:function(){return Z.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(x.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),x.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),x.event.simulate("change",this,e,!0)})),!1):(x.event.add(this,"beforeactivate._change",function(e){var t=e.target;Z.test(t.nodeName)&&!x._data(t,"changeBubbles")&&(x.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||x.event.simulate("change",this.parentNode,e,!0)}),x._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 x.event.remove(this,"._change"),!Z.test(this.nodeName)}}),x.support.focusinBubbles||x.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){x.event.simulate(t,e.target,x.event.fix(e),!0)};x.event.special[t]={setup:function(){0===n++&&a.addEventListener(e,r,!0)},teardown:function(){0===--n&&a.removeEventListener(e,r,!0)}}}),x.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 x().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=x.guid++)),this.each(function(){x.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,x(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(){x.event.remove(this,e,r,n)})},trigger:function(e,t){return this.each(function(){x.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?x.event.trigger(e,n,r,!0):t}});var st=/^.[^:#\[\.,]*$/,lt=/^(?:parents|prev(?:Until|All))/,ut=x.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};x.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(x(e).filter(function(){for(t=0;i>t;t++)if(x.contains(r[t],this))return!0}));for(t=0;i>t;t++)x.find(e,r[t],n);return n=this.pushStack(i>1?x.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},has:function(e){var t,n=x(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(x.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e||[],!0))},filter:function(e){return this.pushStack(ft(this,e||[],!1))},is:function(e){return!!ft(this,"string"==typeof e&&ut.test(e)?x(e):e||[],!1).length},closest:function(e,t){var n,r=0,i=this.length,o=[],a=ut.test(e)||"string"!=typeof e?x(e,t||this.context):0;for(;i>r;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(11>n.nodeType&&(a?a.index(n)>-1:1===n.nodeType&&x.find.matchesSelector(n,e))){n=o.push(n);break}return this.pushStack(o.length>1?x.unique(o):o)},index:function(e){return e?"string"==typeof e?x.inArray(this[0],x(e)):x.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?x(e,t):x.makeArray(e&&e.nodeType?[e]:e),r=x.merge(this.get(),n);return this.pushStack(x.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}x.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return x.dir(e,"parentNode")},parentsUntil:function(e,t,n){return x.dir(e,"parentNode",n)},next:function(e){return pt(e,"nextSibling")},prev:function(e){return pt(e,"previousSibling")},nextAll:function(e){return x.dir(e,"nextSibling")},prevAll:function(e){return x.dir(e,"previousSibling")},nextUntil:function(e,t,n){return x.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return x.dir(e,"previousSibling",n)},siblings:function(e){return x.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return x.sibling(e.firstChild)},contents:function(e){return x.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:x.merge([],e.childNodes)}},function(e,t){x.fn[e]=function(n,r){var i=x.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=x.filter(r,i)),this.length>1&&(ct[e]||(i=x.unique(i)),lt.test(e)&&(i=i.reverse())),this.pushStack(i)}}),x.extend({filter:function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?x.find.matchesSelector(r,e)?[r]:[]:x.find.matches(e,x.grep(t,function(e){return 1===e.nodeType}))},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!x(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(x.isFunction(t))return x.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return x.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(st.test(t))return x.filter(t,e,n);t=x.filter(t,e)}return x.grep(e,function(e){return x.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:x.support.htmlSerialize?[0,"",""]:[1,"X
","
"]},jt=dt(a),Dt=jt.appendChild(a.createElement("div"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,x.fn.extend({text:function(e){return x.access(this,function(e){return e===t?x.text(this):this.empty().append((this[0]&&this[0].ownerDocument||a).createTextNode(e))},null,e,arguments.length)},append:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.appendChild(e)}})},prepend:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=e?x.filter(e,this):this,i=0;for(;null!=(n=r[i]);i++)t||1!==n.nodeType||x.cleanData(Ft(n)),n.parentNode&&(t&&x.contains(n.ownerDocument,n)&&_t(Ft(n,"script")),n.parentNode.removeChild(n));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&x.cleanData(Ft(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&x.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 x.clone(this,e,t)})},html:function(e){return x.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)||!x.support.htmlSerialize&&mt.test(e)||!x.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&&(x.cleanData(Ft(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=x.map(this,function(e){return[e.nextSibling,e.parentNode]}),t=0;return this.domManip(arguments,function(n){var r=e[t++],i=e[t++];i&&(r&&r.parentNode!==i&&(r=this.nextSibling),x(this).remove(),i.insertBefore(n,r))},!0),t?this:this.remove()},detach:function(e){return this.remove(e,!0)},domManip:function(e,t,n){e=d.apply([],e);var r,i,o,a,s,l,u=0,c=this.length,p=this,f=c-1,h=e[0],g=x.isFunction(h);if(g||!(1>=c||"string"!=typeof h||x.support.checkClone)&&Nt.test(h))return this.each(function(r){var i=p.eq(r);g&&(e[0]=h.call(this,r,i.html())),i.domManip(e,t,n)});if(c&&(l=x.buildFragment(e,this[0].ownerDocument,!1,!n&&this),r=l.firstChild,1===l.childNodes.length&&(l=r),r)){for(a=x.map(Ft(l,"script"),Ht),o=a.length;c>u;u++)i=l,u!==f&&(i=x.clone(i,!0,!0),o&&x.merge(a,Ft(i,"script"))),t.call(this[u],i,u);if(o)for(s=a[a.length-1].ownerDocument,x.map(a,qt),u=0;o>u;u++)i=a[u],kt.test(i.type||"")&&!x._data(i,"globalEval")&&x.contains(s,i)&&(i.src?x._evalUrl(i.src):x.globalEval((i.text||i.textContent||i.innerHTML||"").replace(St,"")));l=r=null}return this}});function Lt(e,t){return x.nodeName(e,"table")&&x.nodeName(1===t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function Ht(e){return e.type=(null!==x.find.attr(e,"type"))+"/"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function _t(e,t){var n,r=0;for(;null!=(n=e[r]);r++)x._data(n,"globalEval",!t||x._data(t[r],"globalEval"))}function Mt(e,t){if(1===t.nodeType&&x.hasData(e)){var n,r,i,o=x._data(e),a=x._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++)x.event.add(t,n,s[n][r])}a.data&&(a.data=x.extend({},a.data))}}function Ot(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!x.support.noCloneEvent&&t[x.expando]){i=x._data(t);for(r in i.events)x.removeEvent(t,r,i.handle);t.removeAttribute(x.expando)}"script"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),x.support.html5Clone&&e.innerHTML&&!x.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Ct.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)}}x.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){x.fn[e]=function(e){var n,r=0,i=[],o=x(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),x(o[r])[t](n),h.apply(i,n.get());return this.pushStack(i)}});function Ft(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||x.nodeName(o,n)?s.push(o):x.merge(s,Ft(o,n));return n===t||n&&x.nodeName(e,n)?x.merge([e],s):s}function Bt(e){Ct.test(e.type)&&(e.defaultChecked=e.checked)}x.extend({clone:function(e,t,n){var r,i,o,a,s,l=x.contains(e.ownerDocument,e);if(x.support.html5Clone||x.isXMLDoc(e)||!mt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(x.support.noCloneEvent&&x.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||x.isXMLDoc(e)))for(r=Ft(o),s=Ft(e),a=0;null!=(i=s[a]);++a)r[a]&&Ot(i,r[a]);if(t)if(n)for(s=s||Ft(e),r=r||Ft(o),a=0;null!=(i=s[a]);a++)Mt(i,r[a]);else Mt(e,o);return r=Ft(o,"script"),r.length>0&&_t(r,!l&&Ft(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,l,u,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if("object"===x.type(o))x.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement("div")),l=(bt.exec(o)||["",""])[1].toLowerCase(),c=At[l]||At._default,s.innerHTML=c[1]+o.replace(vt,"<$1>")+c[2],i=c[0];while(i--)s=s.lastChild;if(!x.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!x.support.tbody){o="table"!==l||xt.test(o)?""!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)x.nodeName(u=o.childNodes[i],"tbody")&&!u.childNodes.length&&o.removeChild(u)}x.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),x.support.appendChecked||x.grep(Ft(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===x.inArray(o,r))&&(a=x.contains(o.ownerDocument,o),s=Ft(f.appendChild(o),"script"),a&&_t(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,l=x.expando,u=x.cache,c=x.support.deleteExpando,f=x.event.special;for(;null!=(n=e[s]);s++)if((t||x.acceptData(n))&&(o=n[l],a=o&&u[o])){if(a.events)for(r in a.events)f[r]?x.event.remove(n,r):x.removeEvent(n,r,a.handle); +u[o]&&(delete u[o],c?delete n[l]:typeof n.removeAttribute!==i?n.removeAttribute(l):n[l]=null,p.push(o))}},_evalUrl:function(e){return x.ajax({url:e,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})}}),x.fn.extend({wrapAll:function(e){if(x.isFunction(e))return this.each(function(t){x(this).wrapAll(e.call(this,t))});if(this[0]){var t=x(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 x.isFunction(e)?this.each(function(t){x(this).wrapInner(e.call(this,t))}):this.each(function(){var t=x(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=x.isFunction(e);return this.each(function(n){x(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){x.nodeName(this,"body")||x(this).replaceWith(this.childNodes)}).end()}});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("^("+w+")(.*)$","i"),Yt=RegExp("^("+w+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+-])=("+w+")","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"===x.css(e,"display")||!x.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]=x._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=x._data(r,"olddisplay",ln(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&x._data(r,"olddisplay",i?n:x.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}x.fn.extend({css:function(e,n){return x.access(this,function(e,n,r){var i,o,a={},s=0;if(x.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=x.css(e,n[s],!1,o);return a}return r!==t?x.style(e,n,r):x.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))?x(this).show():x(this).hide()})}}),x.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":x.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,l=x.camelCase(n),u=e.style;if(n=x.cssProps[l]||(x.cssProps[l]=tn(u,l)),s=x.cssHooks[n]||x.cssHooks[l],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:u[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(x.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||x.cssNumber[l]||(r+="px"),x.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(u[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{u[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,l=x.camelCase(n);return n=x.cssProps[l]||(x.cssProps[l]=tn(e.style,l)),s=x.cssHooks[n]||x.cssHooks[l],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||x.isNumeric(o)?o||0:a):a}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s.getPropertyValue(n)||s[n]:t,u=e.style;return s&&(""!==l||x.contains(e.ownerDocument,e)||(l=x.style(e,n)),Yt.test(l)&&Ut.test(n)&&(i=u.width,o=u.minWidth,a=u.maxWidth,u.minWidth=u.maxWidth=u.width=l,l=s.width,u.width=i,u.minWidth=o,u.maxWidth=a)),l}):a.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s[n]:t,u=e.style;return null==l&&u&&u[n]&&(l=u[n]),Yt.test(l)&&!zt.test(n)&&(i=u.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),u.left="fontSize"===n?"1em":l,l=u.pixelLeft+"px",u.left=i,a&&(o.left=a)),""===l?"auto":l});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+=x.css(e,n+Zt[o],!0,i)),r?("content"===n&&(a-=x.css(e,"padding"+Zt[o],!0,i)),"margin"!==n&&(a-=x.css(e,"border"+Zt[o]+"Width",!0,i))):(a+=x.css(e,"padding"+Zt[o],!0,i),"padding"!==n&&(a+=x.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=x.support.boxSizing&&"border-box"===x.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&&(x.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?"border":"content"),r,o)+"px"}function ln(e){var t=a,n=Gt[e];return n||(n=un(e,t),"none"!==n&&n||(Pt=(Pt||x("