Wordfence Security – Firewall & Malware Scan - Version 6.3.5

Version Description

  • Improvement: Sites can now specify a list of trusted proxies when using X-Forwarded-For for IP resolution.
  • Improvement: Added options to customize which dashboard notifications are shown.
  • Improvement: Improvements to the scanner's malware stage to avoid timing out on larger files.
  • Improvement: Provided additional no-caching indicators for caches that erroneously save pages with HTTP error status codes.
  • Improvement: Updated the bundled GeoIP database.
  • Improvement: Optimized the country update process in the upgrade handler so it only updates changed records.
  • Improvement: Added our own prefixed version of jQuery.DataTables to avoid conflicts with other plugins.
  • Improvement: Changes to readme.txt and readme.md are now ignored by the scanner unless high sensitivity is on.
  • Fix: Addressed an issue with multisite installations where they would execute the upgrade handler for each subsite.
  • Fix: Added additional error handling to the blocked IP list to avoid outputting notices when another plugin resets the error handler.
  • Fix: Made the description in the summary email for blocks resulting from the blacklist more descriptive.
  • Fix: Updated the copyright date on several pages.
  • Fix: Fixed incorrect wrapping of the Group by field on the live traffic page.
Download this release

Release Info

Developer wfryan
Plugin Icon 128x128 Wordfence Security – Firewall & Malware Scan
Version 6.3.5
Comparing to
See all releases

Code changes from version 6.3.4 to 6.3.5

css/main.css CHANGED
@@ -1 +1 @@
1
- .wf-clearfix:before,.wf-clearfix:after{content:" ";display:table}.wf-clearfix:after{clear:both}@-ms-viewport{width:device-width}.wf-visible-xs{display:none !important}.wf-visible-sm{display:none !important}.wf-visible-md{display:none !important}.wf-visible-lg{display:none !important}.wf-visible-xs-block,.wf-visible-xs-inline,.wf-visible-xs-inline-block,.wf-visible-sm-block,.wf-visible-sm-inline,.wf-visible-sm-inline-block,.wf-visible-md-block,.wf-visible-md-inline,.wf-visible-md-inline-block,.wf-visible-lg-block,.wf-visible-lg-inline,.wf-visible-lg-inline-block{display:none !important}@media (max-width: 767px){.wf-visible-xs{display:block !important}table.wf-visible-xs{display:table !important}tr.wf-visible-xs{display:table-row !important}th.wf-visible-xs,td.wf-visible-xs{display:table-cell !important}}@media (max-width: 767px){.wf-visible-xs-block{display:block !important}}@media (max-width: 767px){.wf-visible-xs-inline{display:inline !important}}@media (max-width: 767px){.wf-visible-xs-inline-block{display:inline-block !important}}@media (min-width: 768px) and (max-width: 991px){.wf-visible-sm{display:block !important}table.wf-visible-sm{display:table !important}tr.wf-visible-sm{display:table-row !important}th.wf-visible-sm,td.wf-visible-sm{display:table-cell !important}}@media (min-width: 768px) and (max-width: 991px){.wf-visible-sm-block{display:block !important}}@media (min-width: 768px) and (max-width: 991px){.wf-visible-sm-inline{display:inline !important}}@media (min-width: 768px) and (max-width: 991px){.wf-visible-sm-inline-block{display:inline-block !important}}@media (min-width: 992px) and (max-width: 1199px){.wf-visible-md{display:block !important}table.wf-visible-md{display:table !important}tr.wf-visible-md{display:table-row !important}th.wf-visible-md,td.wf-visible-md{display:table-cell !important}}@media (min-width: 992px) and (max-width: 1199px){.wf-visible-md-block{display:block !important}}@media (min-width: 992px) and (max-width: 1199px){.wf-visible-md-inline{display:inline !important}}@media (min-width: 992px) and (max-width: 1199px){.wf-visible-md-inline-block{display:inline-block !important}}@media (min-width: 1200px){.wf-visible-lg{display:block !important}table.wf-visible-lg{display:table !important}tr.wf-visible-lg{display:table-row !important}th.wf-visible-lg,td.wf-visible-lg{display:table-cell !important}}@media (min-width: 1200px){.wf-visible-lg-block{display:block !important}}@media (min-width: 1200px){.wf-visible-lg-inline{display:inline !important}}@media (min-width: 1200px){.wf-visible-lg-inline-block{display:inline-block !important}}@media (max-width: 767px){.wf-hidden-xs{display:none !important}}@media (min-width: 768px) and (max-width: 991px){.wf-hidden-sm{display:none !important}}@media (min-width: 992px) and (max-width: 1199px){.wf-hidden-md{display:none !important}}@media (min-width: 1200px){.wf-hidden-lg{display:none !important}}.wf-visible-print{display:none !important}@media print{.wf-visible-print{display:block !important}table.wf-visible-print{display:table !important}tr.wf-visible-print{display:table-row !important}th.wf-visible-print,td.wf-visible-print{display:table-cell !important}}.wf-visible-print-block{display:none !important}@media print{.wf-visible-print-block{display:block !important}}.wf-visible-print-inline{display:none !important}@media print{.wf-visible-print-inline{display:inline !important}}.wf-visible-print-inline-block{display:none !important}@media print{.wf-visible-print-inline-block{display:inline-block !important}}@media print{.wf-hidden-print{display:none !important}}.wf-container{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}.wf-container:before,.wf-container:after{content:" ";display:table}.wf-container:after{clear:both}@media (min-width: 768px){.wf-container{width:750px}}@media (min-width: 992px){.wf-container{width:970px}}@media (min-width: 1200px){.wf-container{width:1170px}}.wf-container-fluid{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}.wf-container-fluid:before,.wf-container-fluid:after{content:" ";display:table}.wf-container-fluid:after{clear:both}.wf-row{margin-left:-15px;margin-right:-15px}.wf-row:before,.wf-row:after{content:" ";display:table}.wf-row:after{clear:both}.wf-col-xs-1,.wf-col-sm-1,.wf-col-md-1,.wf-col-lg-1,.wf-col-xs-2,.wf-col-sm-2,.wf-col-md-2,.wf-col-lg-2,.wf-col-xs-3,.wf-col-sm-3,.wf-col-md-3,.wf-col-lg-3,.wf-col-xs-4,.wf-col-sm-4,.wf-col-md-4,.wf-col-lg-4,.wf-col-xs-5,.wf-col-sm-5,.wf-col-md-5,.wf-col-lg-5,.wf-col-xs-6,.wf-col-sm-6,.wf-col-md-6,.wf-col-lg-6,.wf-col-xs-7,.wf-col-sm-7,.wf-col-md-7,.wf-col-lg-7,.wf-col-xs-8,.wf-col-sm-8,.wf-col-md-8,.wf-col-lg-8,.wf-col-xs-9,.wf-col-sm-9,.wf-col-md-9,.wf-col-lg-9,.wf-col-xs-10,.wf-col-sm-10,.wf-col-md-10,.wf-col-lg-10,.wf-col-xs-11,.wf-col-sm-11,.wf-col-md-11,.wf-col-lg-11,.wf-col-xs-12,.wf-col-sm-12,.wf-col-md-12,.wf-col-lg-12{position:relative;min-height:1px;padding-left:15px;padding-right:15px;box-sizing:border-box}.wf-col-xs-1,.wf-col-xs-2,.wf-col-xs-3,.wf-col-xs-4,.wf-col-xs-5,.wf-col-xs-6,.wf-col-xs-7,.wf-col-xs-8,.wf-col-xs-9,.wf-col-xs-10,.wf-col-xs-11,.wf-col-xs-12{float:left}.wf-col-xs-1{width:8.33333%}.wf-col-xs-2{width:16.66667%}.wf-col-xs-3{width:25%}.wf-col-xs-4{width:33.33333%}.wf-col-xs-5{width:41.66667%}.wf-col-xs-6{width:50%}.wf-col-xs-7{width:58.33333%}.wf-col-xs-8{width:66.66667%}.wf-col-xs-9{width:75%}.wf-col-xs-10{width:83.33333%}.wf-col-xs-11{width:91.66667%}.wf-col-xs-12{width:100%}.wf-col-xs-pull-0{right:auto}.wf-col-xs-pull-1{right:8.33333%}.wf-col-xs-pull-2{right:16.66667%}.wf-col-xs-pull-3{right:25%}.wf-col-xs-pull-4{right:33.33333%}.wf-col-xs-pull-5{right:41.66667%}.wf-col-xs-pull-6{right:50%}.wf-col-xs-pull-7{right:58.33333%}.wf-col-xs-pull-8{right:66.66667%}.wf-col-xs-pull-9{right:75%}.wf-col-xs-pull-10{right:83.33333%}.wf-col-xs-pull-11{right:91.66667%}.wf-col-xs-pull-12{right:100%}.wf-col-xs-push-0{left:auto}.wf-col-xs-push-1{left:8.33333%}.wf-col-xs-push-2{left:16.66667%}.wf-col-xs-push-3{left:25%}.wf-col-xs-push-4{left:33.33333%}.wf-col-xs-push-5{left:41.66667%}.wf-col-xs-push-6{left:50%}.wf-col-xs-push-7{left:58.33333%}.wf-col-xs-push-8{left:66.66667%}.wf-col-xs-push-9{left:75%}.wf-col-xs-push-10{left:83.33333%}.wf-col-xs-push-11{left:91.66667%}.wf-col-xs-push-12{left:100%}.wf-col-xs-offset-0{margin-left:0%}.wf-col-xs-offset-1{margin-left:8.33333%}.wf-col-xs-offset-2{margin-left:16.66667%}.wf-col-xs-offset-3{margin-left:25%}.wf-col-xs-offset-4{margin-left:33.33333%}.wf-col-xs-offset-5{margin-left:41.66667%}.wf-col-xs-offset-6{margin-left:50%}.wf-col-xs-offset-7{margin-left:58.33333%}.wf-col-xs-offset-8{margin-left:66.66667%}.wf-col-xs-offset-9{margin-left:75%}.wf-col-xs-offset-10{margin-left:83.33333%}.wf-col-xs-offset-11{margin-left:91.66667%}.wf-col-xs-offset-12{margin-left:100%}.wf-col-xs-half-padding-left{padding-left:8px}.wf-col-xs-half-padding-right{padding-right:7px}@media (min-width: 768px){.wf-col-sm-1,.wf-col-sm-2,.wf-col-sm-3,.wf-col-sm-4,.wf-col-sm-5,.wf-col-sm-6,.wf-col-sm-7,.wf-col-sm-8,.wf-col-sm-9,.wf-col-sm-10,.wf-col-sm-11,.wf-col-sm-12{float:left}.wf-col-sm-1{width:8.33333%}.wf-col-sm-2{width:16.66667%}.wf-col-sm-3{width:25%}.wf-col-sm-4{width:33.33333%}.wf-col-sm-5{width:41.66667%}.wf-col-sm-6{width:50%}.wf-col-sm-7{width:58.33333%}.wf-col-sm-8{width:66.66667%}.wf-col-sm-9{width:75%}.wf-col-sm-10{width:83.33333%}.wf-col-sm-11{width:91.66667%}.wf-col-sm-12{width:100%}.wf-col-sm-pull-0{right:auto}.wf-col-sm-pull-1{right:8.33333%}.wf-col-sm-pull-2{right:16.66667%}.wf-col-sm-pull-3{right:25%}.wf-col-sm-pull-4{right:33.33333%}.wf-col-sm-pull-5{right:41.66667%}.wf-col-sm-pull-6{right:50%}.wf-col-sm-pull-7{right:58.33333%}.wf-col-sm-pull-8{right:66.66667%}.wf-col-sm-pull-9{right:75%}.wf-col-sm-pull-10{right:83.33333%}.wf-col-sm-pull-11{right:91.66667%}.wf-col-sm-pull-12{right:100%}.wf-col-sm-push-0{left:auto}.wf-col-sm-push-1{left:8.33333%}.wf-col-sm-push-2{left:16.66667%}.wf-col-sm-push-3{left:25%}.wf-col-sm-push-4{left:33.33333%}.wf-col-sm-push-5{left:41.66667%}.wf-col-sm-push-6{left:50%}.wf-col-sm-push-7{left:58.33333%}.wf-col-sm-push-8{left:66.66667%}.wf-col-sm-push-9{left:75%}.wf-col-sm-push-10{left:83.33333%}.wf-col-sm-push-11{left:91.66667%}.wf-col-sm-push-12{left:100%}.wf-col-sm-offset-0{margin-left:0%}.wf-col-sm-offset-1{margin-left:8.33333%}.wf-col-sm-offset-2{margin-left:16.66667%}.wf-col-sm-offset-3{margin-left:25%}.wf-col-sm-offset-4{margin-left:33.33333%}.wf-col-sm-offset-5{margin-left:41.66667%}.wf-col-sm-offset-6{margin-left:50%}.wf-col-sm-offset-7{margin-left:58.33333%}.wf-col-sm-offset-8{margin-left:66.66667%}.wf-col-sm-offset-9{margin-left:75%}.wf-col-sm-offset-10{margin-left:83.33333%}.wf-col-sm-offset-11{margin-left:91.66667%}.wf-col-sm-offset-12{margin-left:100%}.wf-col-sm-half-padding-left{padding-left:8px}.wf-col-sm-half-padding-right{padding-right:7px}}@media (min-width: 992px){.wf-col-md-1,.wf-col-md-2,.wf-col-md-3,.wf-col-md-4,.wf-col-md-5,.wf-col-md-6,.wf-col-md-7,.wf-col-md-8,.wf-col-md-9,.wf-col-md-10,.wf-col-md-11,.wf-col-md-12{float:left}.wf-col-md-1{width:8.33333%}.wf-col-md-2{width:16.66667%}.wf-col-md-3{width:25%}.wf-col-md-4{width:33.33333%}.wf-col-md-5{width:41.66667%}.wf-col-md-6{width:50%}.wf-col-md-7{width:58.33333%}.wf-col-md-8{width:66.66667%}.wf-col-md-9{width:75%}.wf-col-md-10{width:83.33333%}.wf-col-md-11{width:91.66667%}.wf-col-md-12{width:100%}.wf-col-md-pull-0{right:auto}.wf-col-md-pull-1{right:8.33333%}.wf-col-md-pull-2{right:16.66667%}.wf-col-md-pull-3{right:25%}.wf-col-md-pull-4{right:33.33333%}.wf-col-md-pull-5{right:41.66667%}.wf-col-md-pull-6{right:50%}.wf-col-md-pull-7{right:58.33333%}.wf-col-md-pull-8{right:66.66667%}.wf-col-md-pull-9{right:75%}.wf-col-md-pull-10{right:83.33333%}.wf-col-md-pull-11{right:91.66667%}.wf-col-md-pull-12{right:100%}.wf-col-md-push-0{left:auto}.wf-col-md-push-1{left:8.33333%}.wf-col-md-push-2{left:16.66667%}.wf-col-md-push-3{left:25%}.wf-col-md-push-4{left:33.33333%}.wf-col-md-push-5{left:41.66667%}.wf-col-md-push-6{left:50%}.wf-col-md-push-7{left:58.33333%}.wf-col-md-push-8{left:66.66667%}.wf-col-md-push-9{left:75%}.wf-col-md-push-10{left:83.33333%}.wf-col-md-push-11{left:91.66667%}.wf-col-md-push-12{left:100%}.wf-col-md-offset-0{margin-left:0%}.wf-col-md-offset-1{margin-left:8.33333%}.wf-col-md-offset-2{margin-left:16.66667%}.wf-col-md-offset-3{margin-left:25%}.wf-col-md-offset-4{margin-left:33.33333%}.wf-col-md-offset-5{margin-left:41.66667%}.wf-col-md-offset-6{margin-left:50%}.wf-col-md-offset-7{margin-left:58.33333%}.wf-col-md-offset-8{margin-left:66.66667%}.wf-col-md-offset-9{margin-left:75%}.wf-col-md-offset-10{margin-left:83.33333%}.wf-col-md-offset-11{margin-left:91.66667%}.wf-col-md-offset-12{margin-left:100%}.wf-col-md-half-padding-left{padding-left:8px}.wf-col-md-half-padding-right{padding-right:7px}}@media (min-width: 1200px){.wf-col-lg-1,.wf-col-lg-2,.wf-col-lg-3,.wf-col-lg-4,.wf-col-lg-5,.wf-col-lg-6,.wf-col-lg-7,.wf-col-lg-8,.wf-col-lg-9,.wf-col-lg-10,.wf-col-lg-11,.wf-col-lg-12{float:left}.wf-col-lg-1{width:8.33333%}.wf-col-lg-2{width:16.66667%}.wf-col-lg-3{width:25%}.wf-col-lg-4{width:33.33333%}.wf-col-lg-5{width:41.66667%}.wf-col-lg-6{width:50%}.wf-col-lg-7{width:58.33333%}.wf-col-lg-8{width:66.66667%}.wf-col-lg-9{width:75%}.wf-col-lg-10{width:83.33333%}.wf-col-lg-11{width:91.66667%}.wf-col-lg-12{width:100%}.wf-col-lg-pull-0{right:auto}.wf-col-lg-pull-1{right:8.33333%}.wf-col-lg-pull-2{right:16.66667%}.wf-col-lg-pull-3{right:25%}.wf-col-lg-pull-4{right:33.33333%}.wf-col-lg-pull-5{right:41.66667%}.wf-col-lg-pull-6{right:50%}.wf-col-lg-pull-7{right:58.33333%}.wf-col-lg-pull-8{right:66.66667%}.wf-col-lg-pull-9{right:75%}.wf-col-lg-pull-10{right:83.33333%}.wf-col-lg-pull-11{right:91.66667%}.wf-col-lg-pull-12{right:100%}.wf-col-lg-push-0{left:auto}.wf-col-lg-push-1{left:8.33333%}.wf-col-lg-push-2{left:16.66667%}.wf-col-lg-push-3{left:25%}.wf-col-lg-push-4{left:33.33333%}.wf-col-lg-push-5{left:41.66667%}.wf-col-lg-push-6{left:50%}.wf-col-lg-push-7{left:58.33333%}.wf-col-lg-push-8{left:66.66667%}.wf-col-lg-push-9{left:75%}.wf-col-lg-push-10{left:83.33333%}.wf-col-lg-push-11{left:91.66667%}.wf-col-lg-push-12{left:100%}.wf-col-lg-offset-0{margin-left:0%}.wf-col-lg-offset-1{margin-left:8.33333%}.wf-col-lg-offset-2{margin-left:16.66667%}.wf-col-lg-offset-3{margin-left:25%}.wf-col-lg-offset-4{margin-left:33.33333%}.wf-col-lg-offset-5{margin-left:41.66667%}.wf-col-lg-offset-6{margin-left:50%}.wf-col-lg-offset-7{margin-left:58.33333%}.wf-col-lg-offset-8{margin-left:66.66667%}.wf-col-lg-offset-9{margin-left:75%}.wf-col-lg-offset-10{margin-left:83.33333%}.wf-col-lg-offset-11{margin-left:91.66667%}.wf-col-lg-offset-12{margin-left:100%}.wf-col-lg-half-padding-left{padding-left:8px}.wf-col-lg-half-padding-right{padding-right:7px}}.wrap.wordfence{direction:ltr}@media (min-width: 768px){.wrap.wordfence{max-width:750px}}@media (min-width: 992px){.wrap.wordfence{max-width:970px}}@media (min-width: 1200px){.wrap.wordfence{max-width:1170px}}.wrap.wordfence>.wf-container-fluid{padding-left:0px;padding-right:0px}.wrap.wordfence .button-primary{text-align:center;text-transform:uppercase;font-weight:bold;background-color:#00709e}.wrap.wordfence h3{font-size:1.15em}a{color:#00709e}.wordfenceWrap{margin:20px 0 0 20px}.wordfence-icon32{width:32px;height:32px;background-position:0 0;background-repeat:no-repeat;padding:0;margin:7px 5px 0 0;float:left}#wfHeading:after{content:'.';visibility:hidden;display:block;clear:both;height:0px}div.wordfence-lock-icon{background-image:url(../images/wordfence-logo-32x32.png)}a.wfhelp{background-image:url(../images/help.png);width:12px;height:12px;background-position:0 0;background-repeat:no-repeat;padding:0;margin:0 3px 0 3px;text-decoration:none;display:inline-block;vertical-align:middle}.wordfence .resulticon{display:block;float:left;width:16px;height:16px;background-position:0 0;background-repeat:no-repeat;border-width:0;padding:0;margin:0 3px 0 0;background-image:url(../images/icons/bullet_yellow.png)}.wordfenceBoldTD{font-weight:bold}.wfAjax24{display:none;width:24px;height:24px;background-image:url(../images/icons/ajax24.gif);margin:0;padding:0}div.wfLoadingWhite32{width:32px;height:32px;background-image:url(../images/icons/ajaxWhite32x32.gif);margin:0;padding:0}.wfTabsContainer{background-color:#FFF;overflow:hidden;border:1px solid #CCC;padding:15px;min-height:200px;-webkit-font-smoothing:antialiased}#wfTabs::after{content:".";display:block;height:0;width:0;line-height:0;clear:both;visibility:hidden}#wfTabs a{float:left;z-index:10;height:18px;margin:0 5px -1px 0;padding:5px 8px;border:1px solid #CCC;text-decoration:none;background-color:#EFEFEF;color:#21759B;-moz-border-radius-topright:3px;-webkit-border-top-right-radius:3px;border-top-right-radius:3px;-moz-border-radius-topleft:3px;-webkit-border-top-left-radius:3px;border-top-left-radius:3px}#wfTabs a.selected{border-bottom:1px solid #FFF;background-color:#FFF;color:#777}.wordfenceTopTab{display:none;margin-top:15px}.wordfenceTopTab.active{display:block}.wordfenceHelpLink{margin-top:15px}.wfAjaxLight128{background-image:url(../images/icons/ajax3.gif)}.wfStrong{font-weight:bold}.wordfenceModeElem{width:1px;height:1px;opacity:0}.wfWarn{color:#F00}img.wfFlag{vertical-align:middle;margin:-3px 4px 0 0}.wfHitTime{font-style:italic}.wfAvatar img{vertical-align:middle}.wf-hex-sequence{color:#587ECB}.wfLoadMoreButton.disabled,.wfLoadMoreButton[disabled]{pointer-events:none;opacity:0.65}table.wfConfigForm th{font-weight:normal;text-align:left;padding:2px 3px 1px 0;vertical-align:middle}table.wfConfigForm td{vertical-align:middle}table.wfConfigForm td.align-top{vertical-align:top}table th.wfConfigEnable{font-weight:bold;min-width:25%}.wfSavedMsg{display:none;color:#A00}table th.wfSubheading{font-weight:bold;padding-top:10px}h3.wfConfigHeading{font-size:22px;color:#777;font-family:Georgia,Times New Roman,Times,serif;font-style:italic;font-weight:normal}.wfTipText{color:#777;font-family:Georgia,Times New Roman,Times,serif;font-style:italic}.wfBlackCursor{color:#FFF}.wf-spinner{display:inline-block;width:4px}.wferror{color:#F00}#wordfenceWorking{padding:2px 8px 2px 24px;z-index:100000;position:fixed;right:2px;bottom:2px;border:1px solid #000;background-color:#F00;color:#FFF;font-size:12px;font-weight:bold;font-family:Arial;text-align:center;background-image:url("../images/icons/ajaxRed16.gif");background-position:2px 2px;background-repeat:no-repeat}#paidWrap{position:relative}.paidInnerMsg{width:500px;margin:150px auto 0 auto;color:#000;font-size:18px;font-family:Georgia,Times New Roman,Times,serif;line-height:1.8em;text-align:center;-webkit-font-smoothing:antialiased}.wfMarker{height:1px;width:1px}.wfPaidOnlyNotice{width:500px;background-color:#FFFFE0;border:1px solid #000;padding:10px;margin:20px}.wfOnOffSwitch{position:relative !important;width:69px !important;-webkit-user-select:none !important;-moz-user-select:none !important;-ms-user-select:none !important;user-select:none !important}.wfOnOffSwitch-checkbox{display:none !important}.wfOnOffSwitch-label{display:block !important;overflow:hidden !important;cursor:pointer !important;border:2px solid #999999 !important;border-radius:19px !important;margin:0}.wfOnOffSwitch-inner{width:200% !important;margin-left:-100% !important;-webkit-transition:margin 0.3s ease-in !important;-o-transition:margin 0.3s ease-in !important;transition:margin 0.3s ease-in !important;-webkit-transition-delay:0s !important;transition-delay:0s !important}.wfOnOffSwitch-inner:before,.wfOnOffSwitch-inner:after{float:left !important;width:50% !important;height:19px !important;padding:0 !important;line-height:19px !important;font-size:14px !important;color:white !important;font-family:Trebuchet, Arial, sans-serif !important;font-weight:bold !important;-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important;-moz-border-radius:19px !important;-webkit-border-radius:19px;border-radius:19px !important;-webkit-box-shadow:0 9.5px 0 rgba(0,0,0,0.08) inset !important;box-shadow:0 9.5px 0 rgba(0,0,0,0.08) inset !important}.wfOnOffSwitch-inner:before{content:"ON" !important;padding-left:10px !important;background-color:#30D965 !important;color:#FFFFFF !important;-moz-border-radius:19px 0 0 19px !important;-webkit-border-radius:19px;border-radius:19px 0 0 19px !important}.wfOnOffSwitch-inner:after{content:"OFF" !important;padding-right:10px !important;background-color:#EEEEEE !important;color:#999999 !important;text-align:right !important;-moz-border-radius:0 19px 19px 0 !important;-webkit-border-radius:0;border-radius:0 19px 19px 0 !important}.wfOnOffSwitch-switch{width:19px !important;margin:0 !important;background:#FFFFFF !important;border:2px solid #999999 !important;-moz-border-radius:19px !important;-webkit-border-radius:19px;border-radius:19px !important;position:absolute !important;top:0 !important;bottom:0 !important;right:46px !important;-webkit-transition:all 0.3s ease-in !important;-o-transition:all 0.3s ease-in !important;transition:all 0.3s ease-in !important;-webkit-transition-delay:0s !important;transition-delay:0s !important;background-image:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzAwMDAwMCIgc3RvcC1vcGFjaXR5PSIwLjEiLz48c3RvcCBvZmZzZXQ9IjgwJSIgc3RvcC1jb2xvcj0iIzAwMDAwMCIgc3RvcC1vcGFjaXR5PSIwLjAiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2dyYWQpIiAvPjwvc3ZnPiA=') !important;background-size:100%;background-image:-webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, rgba(0,0,0,0.1)),color-stop(80%, rgba(0,0,0,0))) !important;background-image:-moz-linear-gradient(center top, rgba(0,0,0,0.1) 0%,rgba(0,0,0,0) 80%) !important;background-image:-webkit-linear-gradient(center top, rgba(0,0,0,0.1) 0%,rgba(0,0,0,0) 80%) !important;background-image:linear-gradient(to center bottom, rgba(0,0,0,0.1) 0%,rgba(0,0,0,0) 80%) !important;box-shadow:0 1px 1px white inset !important}.wfOnOffSwitch-checkbox:checked+.wfOnOffSwitch-label .wfOnOffSwitch-inner{margin-left:0 !important}.wfOnOffSwitch-checkbox:checked+.wfOnOffSwitch-label .wfOnOffSwitch-switch{right:0 !important}#wordfenceConfigWarning,#wordfenceAdminEmailWarning{clear:left;margin-top:5px}.wf-striped-table{width:100%;max-width:100%;border-collapse:collapse}.wf-striped-table th,.wf-striped-table td{padding:6px 4px;border:1px solid #ccc}.wf-striped-table thead th,.wf-striped-table thead td,.wf-striped-table tfoot th,.wf-striped-table tfoot td,.wf-striped-table tbody.thead th,.wf-striped-table tbody.thead td{background-color:#222;color:#fff;font-weight:bold;border-color:#474747;text-align:left}.wf-striped-table tbody tr.even td,.wf-striped-table tbody tr:nth-child(2n) td{background-color:#eee}.wf-striped-table tbody tr td,.wf-striped-table tbody tr.odd td{background-color:#fff}.wf-striped-table tbody tr:hover>td{background-color:#fffbd8}.wf-striped-table tbody.empty-row tr td{border-width:0;padding:8px 0;background-color:transparent}.wf-striped-table td.error{color:#d0514c;font-weight:bold}.wf-striped-table td.error:before{content:"\2718"}.wf-striped-table td.success{color:#008c10;font-weight:bold;max-width:20%}.wf-striped-table td.success:before{content:"\2713"}.wf-striped-table td.success:before,.wf-striped-table td.error:before{font-size:16px;display:inline-block;margin:0px 8px 0px 0px}.wf-striped-table td.inactive{font-weight:bold;color:#666666}.wordfence-waiting{line-height:32px}.wordfence-waiting img{vertical-align:middle}pre.wf-pre{margin:8px 0 20px;padding:12px;background:#ffffff;border:1px solid #999999;overflow:auto}.wf-center{text-align:center}#wfConfigForm{max-width:1035px}.wf-hidden{display:none !important}.wf-card{position:relative;margin:0 auto .625rem;padding:1rem;box-sizing:border-box;background:#fff;box-shadow:0 0 0 1px rgba(200,215,225,0.5),0 1px 2px #e9eff3}.wf-card .wf-card-inner{min-height:76px;width:100%;padding:8px;box-sizing:border-box;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;position:relative}.wf-card .wf-card-inner .wf-card-content{max-width:75%}.wf-card .wf-card-inner .wf-card-content .wf-card-title{font-size:1.125rem;width:100%}.wf-card .wf-card-inner .wf-card-content .wf-card-subtitle{margin-top:.125rem;margin-bottom:.125rem;font-size:.875rem;color:#4f748e}.wf-card .wf-card-inner .wf-card-action{position:absolute;top:0;right:0;height:100%;background:none;border:0;outline:0;width:48px;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;vertical-align:middle;transition:transform 0.15s cubic-bezier(0.175, 0.885, 0.32, 1.275),color 0.2s ease-in}.wf-card .wf-card-inner .wf-card-action .wf-card-action-chevron{background:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj4KPHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJjaGV2cm9uLW9iamVjdCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiCgkgd2lkdGg9IjI0cHgiIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAwIDI0IDI0IiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAyNCAyNCIKCSB4bWw6c3BhY2U9InByZXNlcnZlIj4KPHBhdGggaWQ9ImNoZXZyb24iIGQ9Ik0yMCA5bC04IDgtOC04IDEuNDE0LTEuNDE0TDEyIDE0LjE3Mmw2LjU4Ni02LjU4NiIvPgo8L3N2Zz4K");background-repeat:no-repeat;background-position:center center;width:24px;height:24px;fill:#87a6bc}.wf-card .wf-card-inner .wf-card-action .wf-card-action-checkbox{background-image:url(../images/checkbox.png);background-repeat:no-repeat;background-position:left center;width:29px;height:29px}.wf-card .wf-card-inner .wf-card-action .wf-card-action-checkbox.checked{background-position:right center}.wf-card .wf-card-extra{display:none;padding:0.5rem;margin-top:1rem;border-top:1px solid #f3f6f8}@media (min-width: 768px){.wf-card .wf-card-extra{padding:1rem}}.wf-card.active .wf-card-extra{display:block}.wf-card.wf-card-left .wf-card-content{margin-left:48px}.wf-card.wf-card-left .wf-card-action{right:auto;left:0px}.wf-card.disabled .wf-card-content .wf-card-title{color:#aaaaaa}.wf-card.disabled .wf-card-content .wf-card-subtitle{color:#8ea6be}.wf-add-top{margin-top:20px !important}.wf-add-top-small{margin-top:10px !important}.wf-add-bottom{margin-bottom:20px !important}.wf-add-bottom-small{margin-bottom:10px !important}.wf-center{text-align:center}.wf-right{text-align:right}.wf-scroll-x::-webkit-scrollbar,.wf-scroll-y::-webkit-scrollbar{-webkit-appearance:none;width:7px;height:7px}.wf-scroll-x::-webkit-scrollbar-thumb,.wf-scroll-y::-webkit-scrollbar-thumb{border-radius:4px;background-color:rgba(0,0,0,0.194);-webkit-box-shadow:0 0 1px rgba(255,255,255,0.5)}.wf-split-word{word-wrap:break-word;word-break:break-all}@media (max-width: 767px){.wf-split-word-xs{word-wrap:break-word;word-break:break-all;white-space:normal !important}}.select2-container{min-width:240px}@media (min-width: 768px){.select2-container{min-width:280px}}@media (min-width: 992px){.select2-container{min-width:320px}}.wf-page-title{display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:flex-start;justify-content:flex-start;margin-top:0.5rem}.wf-page-title>*{-webkit-flex-grow:0;flex-grow:0;min-width:0}.wf-page-title>*:first-child{-webkit-flex-grow:0;flex-grow:0;min-width:32px;-webkit-flex-basis:32px;flex-basis:32px;padding-right:0.25rem}.wf-page-title .wordfence-icon32{margin:0;margin-right:0.5rem}.wf-page-title h2{padding:0 !important}.wf-page-title .wfOnOffSwitch{-webkit-flex-basis:69px;flex-basis:69px;-webkit-flex-shrink:0;flex-shrink:0;margin-left:0.5rem}#wf-notices{margin-top:15px}#wf-notices .wf-admin-notice{margin-left:0px;margin-right:0px}.wf-success-text,.wf-notice-text{display:inline-block;vertical-align:middle;line-height:1.3;font-size:16px;font-weight:bold;font-style:italic}.wf-notice{margin:12px 0;padding:8px;background-color:#ffffe0;border:1px solid #ffd975;border-width:1px 1px 1px 10px}.wf-notice-text{color:#6d798c}.wf-success{margin:12px 0;padding:8px;background-color:#ffffff;border:1px solid #16bc9b;border-width:1px 1px 1px 10px}.wf-success-text{color:#11967a}.wf-premium-callout{border:1px solid #00709e;background-color:#ffffff;padding:16px;margin:20px 0 0}.wf-premium-callout h3{margin:0 0 8px;color:#11967a}.wf-premium-callout ul{margin:8px 0;padding:0 0 0 15px}.wf-premium-callout ul li{list-style-type:disc;margin:0;padding:0}.wf-premium-callout .center{text-align:center;margin:0}.wf-premium-callout .button-primary{text-align:center;text-transform:uppercase;font-weight:bold;background-color:#00709e}#wfLiveTrafficOverlayAnchor::after{position:absolute;z-index:3002;top:0;right:0;width:0;height:0;background:rgba(241,241,241,0.6);content:'';opacity:0;-webkit-transition:opacity 0.5s,width 0.1s 0.5s,height 0.1s 0.5s;-o-transition:opacity 0.5s,width 0.1s 0.5s,height 0.1s 0.5s;transition:opacity 0.5s,width 0.1s 0.5s,height 0.1s 0.5s}.wordfenceLiveActivityPaused #wfLiveTrafficOverlayAnchor::after{width:100%;height:100%;opacity:1;-webkit-transition:opacity 0.5s;-o-transition:opacity 0.5s;transition:opacity 0.5s}#wordfenceLiveActivityDisabled{background:#fff;border-left:4px solid #ffb900;-webkit-box-shadow:0 1px 1px 0 rgba(0,0,0,0.1);box-shadow:0 1px 1px 0 rgba(0,0,0,0.1);margin-bottom:12px;padding:1px 13px}#wfLiveTrafficDisabledMessage{display:none;position:fixed;z-index:3003;left:0;width:100%;top:50%;transform:translateY(-50%);text-align:center;color:#666666;opacity:0;-webkit-transition:opacity 0.5s,width 0.1s 0.5s,height 0.1s 0.5s;-o-transition:opacity 0.5s,width 0.1s 0.5s,height 0.1s 0.5s;transition:opacity 0.5s,width 0.1s 0.5s,height 0.1s 0.5s}#wfLiveTrafficDisabledMessage h2{background-color:#FFF;overflow:hidden;border:1px solid #CCC;max-width:350px;margin:0 auto;padding:15px;font-size:2.0em}#wfLiveTrafficDisabledMessage h2 small{font-size:0.5em;font-weight:normal;margin-top:20px}.wordfenceLiveActivityPaused #wfLiveTrafficDisabledMessage{display:block;opacity:1;-webkit-transition:opacity 0.5s;transition:opacity 0.5s}.wf-live-activity{position:relative;margin:20px 0 10px 0;padding:0.75rem;box-sizing:border-box;background:#FFFCEF;box-shadow:0 0 0 1px rgba(153,155,135,0.5),0 1px 2px #e8f3e0}.wf-live-activity .wf-live-activity-inner{width:100%;box-sizing:border-box;position:relative}.wf-live-activity .wf-live-activity-inner .wf-live-activity-content{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:flex-start}.wf-live-activity .wf-live-activity-inner .wf-live-activity-content .wf-live-activity-title{color:#888888;font-size:0.85rem;font-weight:bold;padding-right:0.5rem}.wf-live-activity .wf-live-activity-inner .wf-live-activity-content .wf-live-activity-message{font-size:0.80rem;color:#000000}.wf-live-activity .wf-live-activity-inner .wf-live-activity-state{position:absolute;top:0px;right:0px;bottom:0px;left:0px;background:rgba(255,252,239,0.9);display:none;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:center;z-index:3001;-webkit-transition:opacity 0.5s,width 0.1s 0.5s,height 0.1s 0.5s;-o-transition:opacity 0.5s,width 0.1s 0.5s,height 0.1s 0.5s;transition:opacity 0.5s,width 0.1s 0.5s,height 0.1s 0.5s}.wordfenceLiveActivityPaused .wf-live-activity .wf-live-activity-inner .wf-live-activity-state{display:-ms-flexbox;display:flex;opacity:1;-webkit-transition:opacity 0.5s;-webkit-transition:opacity 0.5s;-o-transition:opacity 0.5s;transition:opacity 0.5s}.wordfence .wordfenceScanButton{margin:20px 0 20px 0}.wordfence .wordfenceScanButton input.button-wf-grey{background:#EFEFEF url(../images/button-grad-grey.png) repeat-x scroll left top;border-color:#EFEFEF}.wordfence .wordfenceScanButton table td{vertical-align:top}.wordfence .wordfenceScanButton .button-primary{text-align:center;text-transform:uppercase;font-weight:bold;background-color:#00709E;height:44px;line-height:44px;padding:0px 20px}table.wfSummaryParent{font-family:sans-serif;font-size:14px;color:#000;z-index:9}table.wfSummaryParent td{vertical-align:top;padding:0;margin:0}table.wfSummaryParent table.wfSummaryChild th{font-weight:bold;text-align:right;font-family:Georgia,Times New Roman,Times,serif;color:#000;padding:5px 10px 5px 0;border-top:1px solid #CCC}table.wfSummaryParent table.wfSummaryChild td{font-weight:normal;text-align:left;padding:5px 0 5px 0;border-top:1px solid #CCC}table.wfSummaryParent table.wfSC1 td{width:300px;padding:0 25px 10px 0}table.wfSummaryParent table.wfSC2 th{width:80px}table.wfSummaryParent table.wfSC2 td{width:100px}table.wfSummaryParent table.wfSC3 th{width:80px}table.wfSummaryParent table.wfSC3 td{width:250px}table.wfSummaryParent th.wfHead{font-size:22px;font-family:Georgia,Times New Roman,Times,serif;font-style:italic;color:#555;font-weight:bold;text-align:left;padding:20px 0 20px 0;-webkit-font-smoothing:antialiased}.wf-issues-table{table-layout:fixed;width:100%}div.wfIssue{width:100%}div.wfIssue table.wfIssue td{padding:2px;margin:0;border-width:0;text-align:left;width:100%}div.wfIssue table.wfIssue th{padding:2px;margin:0;font-weight:bold;text-align:left;color:#777;white-space:nowrap}div.wfIssue table.wfIssueLinks td{border-width:0;text-align:left;padding-right:10px}div.wfIssue h2{margin:0 0 5px 0;padding:0;font-size:0.9rem}@media (min-width: 768px){div.wfIssue h2{font-size:1.05rem}}.wfIssueOptions{border-top:1px solid #CCC;padding:10px}.wfIssueOptions h3{font-size:0.8rem;margin:0}@media (min-width: 768px){.wfIssueOptions h3{display:inline-block}}.wfIssueOptions ul{margin-bottom:0;padding-left:0;list-style:none;display:-webkit-flex;display:flex;-webkit-align-items:flex-start;align-items:flex-start;-webkit-align-content:flex-start;align-content:flex-start;-webkit-justify-content:flex-start;justify-content:flex-start;-webkit-flex-wrap:nowrap;flex-wrap:nowrap;-webkit-flex-direction:column;flex-direction:column}@media (min-width: 768px){.wfIssueOptions ul{-webkit-flex-direction:row;flex-direction:row;-webkit-align-items:center;align-items:center}}.wfIssueOptions ul>li>a{position:relative;display:block;padding:8px 12px/2}.wfIssueOptions ul>li>a:hover,.wfIssueOptions ul>li>a:focus{text-decoration:none;background-color:#e2e2e2}.wfIssueOptions a{margin-left:10px}.wfIssueOptions strong{float:left;display:block;width:60px}.wfIssueOptions p{margin:6px 0px 0px}.wfProbSev1,.wfProbSev2,.wfAjaxLight128,.wfResolved{width:128px;height:128px;border:0;margin:0 auto;background-repeat:no-repeat;background-position:0 0;text-decoration:none;display:block}.wfProbSev1{background-image:url(../images/icons/error128.png)}.wfProbSev2{background-image:url(../images/icons/warning128.png)}.wfResolved{background-image:url(../images/icons/tick128.png)}.wfIssuesContainer{width:100%;display:none}.wfIssuesContainer p{max-width:550px}.wfALogTime{color:#999}.wfALogMailLink,.wfALogViewLink{display:block;position:absolute;padding:0 0 0 18px;margin:0;right:10px;top:0;background-repeat:no-repeat;font-weight:normal}.wfALogMailLink{background-image:url(../images/icons/email_go.png)}.wfALogViewLink{background-image:url(../images/icons/magnifier.png)}#wfActivity{position:relative}.consoleHead{position:relative;padding:0 0 0 3px;font-weight:bold;width:100%}.consoleHeadText{margin-bottom:4px;font-size:18px;font-family:Georgia,Times New Roman,Times,serif;color:#555;font-weight:bold;-webkit-font-smoothing:antialiased}.consoleFooter{position:relative}.consoleOuter{width:100%}.consoleInner{height:116px;overflow:auto;z-index:1}.bevelDiv1{border:1px solid #EFEFEF}.bevelDiv2{border:1px solid #AAA}.bevelDiv3{background-color:#ffffed;padding:5px;font-family:Helvetica Neue,Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased}.wfSecure{color:#0A0;font-weight:bold}.wfSummaryLine{display:-webkit-flex;display:flex;-webkit-align-items:stretch;align-items:stretch;-webkit-align-content:flex-start;align-content:flex-start;-webkit-justify-content:space-between;justify-content:space-between;-webkit-flex-wrap:nowrap;flex-wrap:nowrap;-webkit-flex-direction:column;flex-direction:column}@media (min-width: 768px){.wfSummaryLine{-webkit-flex-direction:row;flex-direction:row}}.wfSummaryLine .wfSummaryDate{padding-left:3px}.wfSummaryLine .wfSummaryMsg{padding-left:3px;-webkit-flex-grow:1;flex-grow:1;min-width:0}.wfSummaryLoading{width:16px;height:11px;background-image:url("../images/icons/ajaxScan.gif")}.wfSummaryBad,.wfSummaryErr{color:#930000}.wfSummaryOK{color:#11967a}.wfClear{content:".";display:block;height:0;width:0;line-height:0;clear:both;visibility:hidden}.wfSummaryFinal{-webkit-font-smoothing:antialiased;font-weight:bold;color:#555}.wfStartScanButton{text-align:center}.wordfenceScanHelp{border:1px solid #CCC;padding:4px}.wf-scan-no-issues{font-size:1.25rem;color:#11967a}.wf-scan-severity{position:relative;width:10px}@media (min-width: 768px){.wf-scan-severity{width:144px}}.wf-scan-severity-1,.wf-scan-severity-2{position:absolute;top:0px;right:0px;bottom:0px;left:0px}.wf-scan-severity-1{background-color:#c10000}.wf-scan-severity-2{background-color:#ffd10a}.scan-schedule{border-collapse:collapse;border-spacing:0}.scan-schedule tr:first-of-type th{padding-top:0}.scan-schedule td{padding:0}.scan-schedule th{padding:1.5rem 0.5rem 0.75rem 0;font-size:1rem;text-align:left}@media (min-width: 768px){.scan-schedule th{padding:0 0.5rem 0 0;font-size:0.8125rem;text-align:center}}.next-scan{font-size:1em;display:block;position:relative;width:7em;height:7em;background-color:#fff;border-radius:0.6em;box-shadow:0 1px 0 rgba(189,189,189,0.6);overflow:hidden}.next-scan *{display:block;width:100%;font-size:1em;font-weight:bold;font-style:normal;text-align:center}.next-scan strong{position:absolute;top:0;padding:0.4em 0;color:#fff;background-color:#00709E;box-shadow:0 2px 0 #00709E}.next-scan em{position:absolute;bottom:0.3em;color:#00709E}.next-scan span{width:100%;font-size:2.8em;padding-top:1.15em;color:#2f2f2f}.schedule-times-wrapper{display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:space-between;justify-content:space-between;margin-top:1rem}@media (min-width: 768px){.schedule-times-wrapper{margin-top:0.25rem}}.schedule-times-wrapper:first-of-type{margin-top:0}@media (min-width: 768px){.schedule-times-wrapper:first-of-type{margin-top:1rem}}.schedule-times-wrapper>*{-webkit-flex-grow:1;flex-grow:1;min-width:0}.schedule-times-wrapper>*:first-child{-webkit-flex-grow:0;flex-grow:0;min-width:initial;padding-right:0.25rem}.schedule-times{margin-bottom:0;margin-top:0;padding-left:0;list-style:none;clear:both}.schedule-times>li{float:left;position:relative;display:block;margin:0px 0px 2px 0px}.schedule-times>li.disabled>a{color:#777}.schedule-times>li.disabled>a:hover,.schedule-times>li.disabled>a:focus{color:#777;text-decoration:none;background-color:transparent;cursor:not-allowed}.schedule-times>li>a{text-decoration:none;border:1px solid #e2e2e2;border-radius:4px;position:relative;display:block;padding:8px 12px}.schedule-times>li>a:hover,.schedule-times>li>a:focus{text-decoration:none;background-color:#e2e2e2}.schedule-times>li+li{margin-left:2px}.schedule-times>li.active>a,.schedule-times>li.active>a:hover,.schedule-times>li.active>a:focus{color:#fff;background-color:#00709e;border-color:#00709e}.schedule-times>li.text-only{position:relative;display:block;padding:8px 12px}.schedule-times>li>a>img{max-width:none}#wf-lt-listings .wfActEvent{padding-left:15px;border-left:5px solid #cccccc}#wf-lt-listings .wfActEvent.wfHuman{border-left:5px solid #16bc9b}#wf-lt-listings .wfActEvent.wfActionBlocked{border-left:5px solid #d03935}#wf-lt-listings .wfActEvent.wfNotice{border-left:5px solid #c10000}#wf-lt-listings .wfActEvent.wfWarning,#wf-lt-listings .wfActEvent.wf404{border-left:5px solid #ffd10a}#wf-lt-listings .wfActEvent:hover{background-color:#fff9e9 !important}.wfActEvent.wf-live-traffic-filter{padding-left:0;padding-right:0;display:-webkit-flex;display:flex;-webkit-align-items:baseline;align-items:baseline;-webkit-align-content:flex-start;align-content:flex-start;-webkit-justify-content:flex-start;justify-content:flex-start;-webkit-flex-direction:column;flex-direction:column}@media (min-width: 768px){.wfActEvent.wf-live-traffic-filter{-webkit-flex-direction:row;flex-direction:row}}.wfActEvent.wf-live-traffic-filter h2{margin:0;padding-bottom:0.5rem}@media (min-width: 768px){.wfActEvent.wf-live-traffic-filter h2{padding-bottom:0;padding-right:0.5rem}}#wf-lt-advanced-filters{padding-left:0;padding-right:0;overflow:hidden}.wf-live-traffic-filter-detail{display:-webkit-flex;display:flex;-webkit-align-items:flex-start;align-items:flex-start;-webkit-align-content:flex-start;align-content:flex-start;-webkit-justify-content:flex-start;justify-content:flex-start;-webkit-flex-direction:column;flex-direction:column}@media (min-width: 768px){.wf-live-traffic-filter-detail{-webkit-flex-direction:row;flex-direction:row}.wf-live-traffic-filter-detail *{-webkit-flex-grow:1;flex-grow:1}}.wf-live-traffic-filter-detail .wf-live-traffic-filter-item{display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-align-content:flex-start;align-content:flex-start;-webkit-justify-content:flex-start;justify-content:flex-start;-webkit-flex-direction:row;flex-direction:row}.wf-live-traffic-filter-detail .wf-live-traffic-filter-item .wf-live-traffic-filter-remove{margin-left:0.5rem;font-size:1.5rem;color:#333}.wf-live-traffic-filter-detail .wf-live-traffic-filter-item .wf-live-traffic-filter-item-parameters{display:-webkit-flex;display:flex;-webkit-align-items:flex-start;align-items:flex-start;-webkit-align-content:flex-start;align-content:flex-start;-webkit-justify-content:flex-start;justify-content:flex-start;-webkit-flex-direction:column;flex-direction:column}.wf-live-traffic-filter-detail .wf-live-traffic-filter-item .wf-live-traffic-filter-item-parameters div{padding:0.25rem 0}.wf-live-traffic-filter-detail .wf-live-traffic-filter-item .wf-live-traffic-filter-item-parameters select{font-size:0.75rem !important}@media (min-width: 768px){.wf-live-traffic-filter-detail .wf-live-traffic-filter-item .wf-live-traffic-filter-item-parameters{-webkit-flex-direction:row;flex-direction:row;-webkit-align-items:center;align-items:center}.wf-live-traffic-filter-detail .wf-live-traffic-filter-item .wf-live-traffic-filter-item-parameters div{padding:0}.wf-live-traffic-filter-detail .wf-live-traffic-filter-item .wf-live-traffic-filter-item-parameters select{font-size:1rem !important}}.wf-filtered-traffic{display:-webkit-flex;display:flex;-webkit-align-items:flex-start;align-items:flex-start;-webkit-align-content:flex-start;align-content:flex-start;-webkit-justify-content:flex-start;justify-content:flex-start;-webkit-flex-direction:row;flex-direction:row}.wf-filtered-traffic .wf-filtered-traffic-hits{font-size:1.75rem;color:#999999}#wf-live-traffic{position:relative;overflow:visible}#wf-live-traffic-legend{white-space:nowrap;background-color:#fff;border:1px solid #cccccc;padding:12px}#wf-live-traffic-legend.sticky{position:fixed;top:10px;right:auto;left:10px;z-index:2000}@media (min-width: 768px){#wf-live-traffic-legend.sticky{top:42px;left:182px}}#wf-live-traffic-legend-placeholder{display:none;padding:12px}#wf-live-traffic-legend-placeholder.sticky{display:block}#wf-live-traffic-legend ul{margin:0;padding:0}#wf-live-traffic-legend ul:before,#wf-live-traffic-legend ul:after{content:" ";display:table}#wf-live-traffic-legend ul:after{clear:both}#wf-live-traffic-legend ul li{margin:0;padding:0;position:relative;float:left;font-size:11.5px}@media (min-width: 768px){#wf-live-traffic-legend ul li{font-size:13px}}#wf-live-traffic-legend ul li+li{margin-left:8px}#wf-live-traffic-legend ul li:before{content:'';display:block;float:left;margin:3px 6px 0 0;width:12px;height:12px;background-color:#CCCCCC}#wf-live-traffic-legend ul li.wfHuman:before{background-color:#16bc9b}#wf-live-traffic-legend ul li.wfNotice:before{background-color:#ffd10a}#wf-live-traffic-legend ul li.wfBlocked:before{background-color:#d03935}.wfTimeAgo{font-family:Georgia,Times New Roman,Times,serif;color:#999;font-weight:bold;font-style:italic}.wfActEvent{border-bottom:1px solid #CCC;padding:10px 20px;overflow:auto}.wf-pad-small{margin:8px 0}#wf-lt-listings{margin:0 0 0}#wf-lt-listings a{cursor:pointer;text-decoration:underline}#wf-lt-listings a.button,#wf-lt-listings a.wf-btn{text-decoration:none}.wfActionBlocked{background-color:#fff6f6}[class*="span"]{float:left;min-height:1px;margin-left:30px}.highlighted{-webkit-animation-duration:1s;animation-duration:1s;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}@-moz-keyframes highlighted{0%{opacity:0;background-color:#ffeaa0}100%{opacity:1;background-color:#ffffff}}@-webkit-keyframes highlighted{0%{opacity:0;background-color:#ffeaa0}100%{opacity:1;background-color:#ffffff}}@keyframes highlighted{0%{opacity:0;background-color:#ffeaa0}100%{opacity:1;background-color:#ffffff}}@-moz-keyframes highlightedBlocked{0%{opacity:0;background-color:#ffeaa0}100%{opacity:1;background-color:#fff6f6}}@-webkit-keyframes highlightedBlocked{0%{opacity:0;background-color:#ffeaa0}100%{opacity:1;background-color:#fff6f6}}@keyframes highlightedBlocked{0%{opacity:0;background-color:#ffeaa0}100%{opacity:1;background-color:#fff6f6}}.highlighted{-webkit-animation-name:highlighted;animation-name:highlighted}.highlighted.wfActionBlocked{-webkit-animation-name:highlightedBlocked;animation-name:highlightedBlocked}#wf-lt-preset-filters{min-width:250px}#wf-lt-advanced-filters>table{width:100%}#wf-lt-advanced-filters>table>tr>td{vertical-align:top}.wf-lt-url{white-space:nowrap}table.block-ranges-table{border-collapse:collapse;margin:10px 0 0}table.block-ranges-table tr td{border:1px solid #CCC;border-width:1px 0;padding:10px 0 12px 0}#input-wafStatus,#input-wafStatus option,.select2-container--default{font-size:0.9rem}@media (min-width: 768px){#input-wafStatus,#input-wafStatus option,.select2-container--default{font-size:1.0rem}}@media (min-width: 992px){#input-wafStatus,#input-wafStatus option,.select2-container--default{font-size:1.125rem}}.wafStatus-enabled,.wafStatus-learning-mode,.wafStatus-disabled,.wafStatus-enabled.select2-container--default .select2-selection--single .select2-selection__rendered,.wafStatus-learning-mode.select2-container--default .select2-selection--single .select2-selection__rendered,.wafStatus-disabled.select2-container--default .select2-selection--single .select2-selection__rendered{color:#ffffff}#waf-config-form .waf-config-label{font-size:1.3em}#waf-config-form .select2-container--default .select2-selection--single{padding:4px;text-shadow:0 0 3px #000000;font-weight:bold;border-radius:3px}#waf-config-form .select2-container .select2-selection--single{height:auto}.wafStatus-enabled.select2-container--default .select2-selection--single{background-color:#11967a;border-color:#073a2f}.wafStatus-learning-mode.select2-container--default .select2-selection--single{background-color:#ffd10a;border-color:#a38400}.wafStatus-disabled.select2-container--default .select2-selection--single,.wafStatus-disabled.select2-container--default.select2-container--disabled .select2-selection--single,.wafStatus-learning-mode.select2-container--default.select2-container--disabled .select2-selection--single,.wafStatus-enabled.select2-container--default.select2-container--disabled .select2-selection--single{background-color:#c10000;border-color:#5b0000}#waf-config-form .select2-container--default .select2-selection--single .select2-selection__arrow{height:100%;top:0}.wafStatus-enabled.select2-container--default .select2-selection--single .select2-selection__arrow b,.wafStatus-learning-mode.select2-container--default .select2-selection--single .select2-selection__arrow b,.wafStatus-disabled.select2-container--default .select2-selection--single .select2-selection__arrow b{border-color:#ffffff transparent transparent}.wafStatus-enabled.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b,.wafStatus-learning-mode.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b,.wafStatus-disabled.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #ffffff}.wafStatus-description{display:none;max-width:500px;font-style:italic;font-size:14px;line-height:1.3}.whitelist-table-container{overflow-x:auto}table.whitelist-table .whitelist-edit{display:none}table.whitelist-table .edit-mode .whitelist-display{display:none}table.whitelist-table .edit-mode .whitelist-edit{display:block}table.whitelist-table .edit-mode span.whitelist-edit,table.whitelist-table .edit-mode input.whitelist-edit{display:inline}.wf-bulk-action{margin:12px 0}tr.wf-table-filters input{max-width:120px}#wordfenceRightRail img{max-width:100%}#wordfenceRightRail ul{list-style-type:none;margin:0;margin-top:15px}#wordfenceRightRail .center{text-align:center}#wordfenceRightRail .button-primary{text-align:center;text-transform:uppercase;font-weight:bold;background-color:#00709e}.wordfenceRightRail img{width:100%}.wordfenceRightRailLiveTraffic,.wordfenceRightRailOptions,.wordfenceRightRailDiagnostics{margin-left:1055px}.wordfenceRightRailBlockedIPs,.wordfenceRightRailWAF,.wordfenceRightRailCountryBlocking,.wordfenceRightRailScanSchedule{margin-left:950px}.wordfenceRightRail ul{list-style-type:none;margin:0}.wordfenceRightRail .center{text-align:center}.wordfenceRightRail .button-primary{text-align:center;text-transform:uppercase;font-weight:bold;background-color:#00709e}.wordfenceWrap.wordfence-community{min-height:760px}#wfTwoFactorQRCodeTable{width:175px;height:175px;margin:0 auto}@media (min-width: 500px){#wfTwoFactorQRCodeTable{width:256px;height:256px}}#wfTwoFactorRecoveryCodes{list-style-type:none}#wfTwoFactorRecoveryCodes li{font-family:monospace;text-align:center}#wfTwoFactorDownload .dashicons{line-height:26px}.wf-twofactor-delete{font-size:1.5rem}.wf-twofactor-delete a{text-decoration:none;color:#333}.wf-table.wf-table-twofactor>tbody>tr>td{vertical-align:middle}.wf-form-twofactor{max-width:400px}.wf-form-twofactor .wf-radio label{padding-left:0}fieldset{padding:0;margin:0;border:0;min-width:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:bold}label.wf-plain{font-weight:normal}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="radio"],input[type="checkbox"]{margin-top:1px \9;line-height:normal}input[type="file"]{display:block}input[type="range"]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857;color:#555}.wf-form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border-color ease-in-out 0.15s,box-shadow ease-in-out 0.15s;-o-transition:border-color ease-in-out 0.15s,box-shadow ease-in-out 0.15s;transition:border-color ease-in-out 0.15s,box-shadow ease-in-out 0.15s}.wf-form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6)}.wf-form-control::-moz-placeholder{color:#999;opacity:1}.wf-form-control:-ms-input-placeholder{color:#999}.wf-form-control::-webkit-input-placeholder{color:#999}.wf-form-control::-ms-expand{border:0;background-color:transparent}.wf-form-control[disabled],.wf-form-control[readonly],fieldset[disabled] .wf-form-control{background-color:#e2e2e2;opacity:1}.wf-form-control[disabled],fieldset[disabled] .wf-form-control{cursor:not-allowed}textarea.wf-form-control{height:auto}input[type="search"]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio: 0){input[type="date"].wf-form-control,input[type="time"].wf-form-control,input[type="datetime-local"].wf-form-control,input[type="month"].wf-form-control{line-height:34px}input[type="date"].wf-input-sm,.wf-input-group-sm input[type="date"],input[type="time"].wf-input-sm,.wf-input-group-sm input[type="time"],input[type="datetime-local"].wf-input-sm,.wf-input-group-sm input[type="datetime-local"],input[type="month"].wf-input-sm,.wf-input-group-sm input[type="month"]{line-height:30px}input[type="date"].wf-input-lg,.wf-input-group-lg input[type="date"],input[type="time"].wf-input-lg,.wf-input-group-lg input[type="time"],input[type="datetime-local"].wf-input-lg,.wf-input-group-lg input[type="datetime-local"],input[type="month"].wf-input-lg,.wf-input-group-lg input[type="month"]{line-height:46px}}.wf-form-group{margin-bottom:8px}.wf-form-group.wf-sub-group label{color:#666666;font-weight:normal;padding-left:20px}.wf-form-group.wf-focus{border-left:4px solid #11967a;padding-bottom:8px;background-color:#e5e5e5}.wf-form-group.wf-focus label{margin-left:-4px}.wf-radio,.wf-checkbox{position:relative;display:block;margin-top:10px;margin-bottom:10px}.wf-radio label,.wf-checkbox label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:normal;cursor:pointer}.wf-radio input[type="radio"],.wf-radio-inline input[type="radio"],.wf-checkbox input[type="checkbox"],.wf-checkbox-inline input[type="checkbox"]{margin-top:4px \9}.wf-radio-offset{padding-left:29px}@media (min-width: 768px){.wf-radio-offset{padding-left:20px}}.wf-radio+.wf-radio,.wf-checkbox+.wf-checkbox{margin-top:-5px}.wf-radio-inline,.wf-checkbox-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;vertical-align:middle;font-weight:normal;cursor:pointer}.wf-radio-inline+.wf-radio-inline,.wf-checkbox-inline+.wf-checkbox-inline{margin-top:0;margin-left:10px}input[type="radio"][disabled],input[type="radio"].wf-disabled,fieldset[disabled] input[type="radio"],input[type="checkbox"][disabled],input[type="checkbox"].wf-disabled,fieldset[disabled] input[type="checkbox"]{cursor:not-allowed}.wf-radio-inline.wf-disabled,fieldset[disabled] .wf-radio-inline,.wf-checkbox-inline.wf-disabled,fieldset[disabled] .wf-checkbox-inline{cursor:not-allowed}.wf-radio.wf-disabled label,fieldset[disabled] .wf-radio label,.wf-checkbox.wf-disabled label,fieldset[disabled] .wf-checkbox label{cursor:not-allowed}.wf-form-control-static{padding-top:7px;padding-bottom:7px;margin:0;line-height:1}.wf-form-control-static.wf-input-lg,.wf-form-control-static.wf-input-sm{padding-left:0;padding-right:0}.wf-input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.wf-input-sm{height:30px;line-height:30px}textarea.wf-input-sm,select[multiple].wf-input-sm{height:auto}.wf-form-group-sm .wf-form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.wf-form-group-sm select.wf-form-control{height:30px;line-height:30px}.wf-form-group-sm textarea.wf-form-control,.wf-form-group-sm select[multiple].wf-form-control{height:auto}.wf-form-group-sm .wf-form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.wf-input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.33333;border-radius:6px}select.wf-input-lg{height:46px;line-height:46px}textarea.wf-input-lg,select[multiple].wf-input-lg{height:auto}.wf-form-group-lg .wf-form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.33333;border-radius:6px}.wf-form-group-lg select.wf-form-control{height:46px;line-height:46px}.wf-form-group-lg textarea.wf-form-control,.wf-form-group-lg select[multiple].wf-form-control{height:auto}.wf-form-group-lg .wf-form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.33333}.wf-has-feedback{position:relative}.wf-has-feedback .wf-form-control{padding-right:42.5px}.wf-form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.wf-input-lg+.wf-form-control-feedback,.wf-input-group-lg+.wf-form-control-feedback,.wf-form-group-lg .wf-form-control+.wf-form-control-feedback{width:46px;height:46px;line-height:46px}.wf-input-sm+.wf-form-control-feedback,.wf-input-group-sm+.wf-form-control-feedback,.wf-form-group-sm .wf-form-control+.wf-form-control-feedback{width:30px;height:30px;line-height:30px}.wf-has-success .wf-help-block,.wf-has-success .wf-control-label,.wf-has-success .wf-radio,.wf-has-success .wf-checkbox,.wf-has-success .wf-radio-inline,.wf-has-success .wf-checkbox-inline,.wf-has-success.wf-radio label,.wf-has-success.wf-checkbox label,.wf-has-success.wf-radio-inline label,.wf-has-success.wf-checkbox-inline label{color:#3c763d}.wf-has-success .wf-form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.wf-has-success .wf-form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #67b168}.wf-has-success .wf-input-group-addon{color:#3c763d;border-color:#3c763d;background-color:#dff0d8}.wf-has-success .wf-form-control-feedback{color:#3c763d}.wf-has-warning .wf-help-block,.wf-has-warning .wf-control-label,.wf-has-warning .wf-radio,.wf-has-warning .wf-checkbox,.wf-has-warning .wf-radio-inline,.wf-has-warning .wf-checkbox-inline,.wf-has-warning.wf-radio label,.wf-has-warning.wf-checkbox label,.wf-has-warning.wf-radio-inline label,.wf-has-warning.wf-checkbox-inline label{color:#8a6d3b}.wf-has-warning .wf-form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.wf-has-warning .wf-form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #c0a16b}.wf-has-warning .wf-input-group-addon{color:#8a6d3b;border-color:#8a6d3b;background-color:#fcf8e3}.wf-has-warning .wf-form-control-feedback{color:#8a6d3b}.wf-has-error .wf-help-block,.wf-has-error .wf-control-label,.wf-has-error .wf-radio,.wf-has-error .wf-checkbox,.wf-has-error .wf-radio-inline,.wf-has-error .wf-checkbox-inline,.wf-has-error.wf-radio label,.wf-has-error.wf-checkbox label,.wf-has-error.wf-radio-inline label,.wf-has-error.wf-checkbox-inline label{color:#a94442}.wf-has-error .wf-form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.wf-has-error .wf-form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483}.wf-has-error .wf-input-group-addon{color:#a94442;border-color:#a94442;background-color:#f2dede}.wf-has-error .wf-form-control-feedback{color:#a94442}.wf-has-feedback label ~ .wf-form-control-feedback{top:25px}.wf-has-feedback label.wf-sr-only ~ .wf-form-control-feedback{top:0}.wf-help-block{display:block;margin-top:5px;color:#737373}@media (min-width: 768px){.wf-form-inline .wf-form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.wf-form-inline .wf-form-control{display:inline-block;width:auto;vertical-align:middle}.wf-form-inline .wf-form-control-static{display:inline-block}.wf-form-inline .wf-input-group{display:inline-table;vertical-align:middle}.wf-form-inline .wf-input-group .wf-input-group-addon,.wf-form-inline .wf-input-group .wf-input-group-btn,.wf-form-inline .wf-input-group .wf-form-control{width:auto}.wf-form-inline .wf-input-group>.wf-form-control{width:100%}.wf-form-inline .wf-control-label{margin-bottom:0;vertical-align:middle}.wf-form-inline .wf-radio,.wf-form-inline .wf-checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.wf-form-inline .wf-radio label,.wf-form-inline .wf-checkbox label{padding-left:0}.wf-form-inline .wf-radio input[type="radio"],.wf-form-inline .wf-checkbox input[type="checkbox"]{position:relative;margin-left:0}.wf-form-inline .wf-has-feedback .wf-form-control-feedback{top:0}}.wf-form-horizontal .wf-radio,.wf-form-horizontal .wf-checkbox,.wf-form-horizontal .wf-radio-inline,.wf-form-horizontal .wf-checkbox-inline{margin-top:0;margin-bottom:0;padding-top:7px}.wf-form-horizontal .wf-radio,.wf-form-horizontal .wf-checkbox{min-height:27px}.wf-form-horizontal .wf-form-group{margin-left:-15px;margin-right:-15px}.wf-form-horizontal .wf-form-group:before,.wf-form-horizontal .wf-form-group:after{content:" ";display:table}.wf-form-horizontal .wf-form-group:after{clear:both}@media (min-width: 768px){.wf-form-horizontal .wf-control-label{text-align:right;margin-bottom:0;padding-top:7px}}.wf-form-horizontal .wf-has-feedback .wf-form-control-feedback{right:15px}@media (min-width: 768px){.wf-form-horizontal .wf-form-group-lg .wf-control-label{padding-top:11px;font-size:18px}}@media (min-width: 768px){.wf-form-horizontal .wf-form-group-sm .wf-control-label{padding-top:6px;font-size:12px}}.wf-dashboard-item{position:relative;margin:0 auto 1rem;padding:0 1rem;box-sizing:border-box;background:#fff;box-shadow:0 0 0 1px rgba(200,215,225,0.5),0 1px 2px #e9eff3}.wf-dashboard-item .wf-dashboard-item-inner{min-height:44px;padding:1rem 0;width:100%;box-sizing:border-box;display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:space-between;justify-content:space-between;position:relative}.wf-dashboard-item .wf-dashboard-item-inner .wf-dashboard-item-content{max-width:75%}.wf-dashboard-item .wf-dashboard-item-inner .wf-dashboard-item-content .wf-dashboard-item-title{font-size:0.75rem;width:100%}.wf-dashboard-item .wf-dashboard-item-inner .wf-dashboard-item-content .wf-dashboard-item-subtitle{margin-top:.125rem;margin-bottom:.125rem;font-size:.575rem;color:#4f748e}.wf-dashboard-item .wf-dashboard-item-inner .wf-dashboard-item-action{position:absolute;top:0;right:0;height:100%;background:none;border:0;outline:0;width:48px;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;vertical-align:middle;transition:transform 0.15s cubic-bezier(0.175, 0.885, 0.32, 1.275),color 0.2s ease-in}.wf-dashboard-item .wf-dashboard-item-inner .wf-dashboard-item-action.wf-dashboard-item-action-text{width:auto}.wf-dashboard-item .wf-dashboard-item-inner .wf-dashboard-item-action.wf-dashboard-item-action-text.wf-dashboard-item-action-text-success{color:#11967a}.wf-dashboard-item .wf-dashboard-item-inner .wf-dashboard-item-action.wf-dashboard-item-action-text.wf-dashboard-item-action-text-warning{color:#930000}.wf-dashboard-item .wf-dashboard-item-inner .wf-dashboard-item-action.wf-dashboard-item-action-text.wf-dashboard-item-action-text-warning a{color:#930000}.wf-dashboard-item .wf-dashboard-item-inner .wf-dashboard-item-action .wf-dashboard-item-action-chevron{background:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj4KPHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJjaGV2cm9uLW9iamVjdCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiCgkgd2lkdGg9IjI0cHgiIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAwIDI0IDI0IiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAyNCAyNCIKCSB4bWw6c3BhY2U9InByZXNlcnZlIj4KPHBhdGggaWQ9ImNoZXZyb24iIGQ9Ik0yMCA5bC04IDgtOC04IDEuNDE0LTEuNDE0TDEyIDE0LjE3Mmw2LjU4Ni02LjU4NiIvPgo8L3N2Zz4K");background-repeat:no-repeat;background-position:center center;width:24px;height:24px;fill:#87a6bc}.wf-dashboard-item .wf-dashboard-item-inner .wf-dashboard-item-action .wf-dashboard-item-action-checkbox{background-image:url(../images/checkbox.png);background-repeat:no-repeat;background-position:left center;width:29px;height:29px}.wf-dashboard-item .wf-dashboard-item-inner .wf-dashboard-item-action .wf-dashboard-item-action-checkbox.checked{background-position:right center}.wf-dashboard-item .wf-dashboard-item-extra{display:none;margin:0 -1rem;padding:0 1rem}.wf-dashboard-item .wf-dashboard-item-extra .wf-dashboard-item-list{margin:0 -1rem;padding:0;list-style:none}.wf-dashboard-item .wf-dashboard-item-extra .wf-dashboard-item-list>li{display:block;min-height:44px;padding:0 1rem;margin:0;border-top:1px solid #eeeeee;box-sizing:border-box;display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:space-between;justify-content:space-between}.wf-dashboard-item .wf-dashboard-item-extra .wf-dashboard-item-list>li>*:first-child{-webkit-flex-grow:1;flex-grow:1;min-width:0}.wf-dashboard-item .wf-dashboard-item-extra .wf-dashboard-item-list.wf-dashboard-item-list-horizontal{box-sizing:border-box;margin-top:-1px;display:-webkit-flex;display:flex;-webkit-align-items:stretch;align-items:stretch;-webkit-align-content:flex-start;align-content:flex-start;-webkit-justify-content:space-between;justify-content:space-between;-webkit-flex-wrap:wrap;flex-wrap:wrap}.wf-dashboard-item .wf-dashboard-item-extra .wf-dashboard-item-list.wf-dashboard-item-list-horizontal>li{-webkit-flex-grow:1;flex-grow:1;-webkit-flex-basis:100%;flex-basis:100%;border-left:1px solid #eeeeee}@media (min-width: 768px){.wf-dashboard-item .wf-dashboard-item-extra .wf-dashboard-item-list.wf-dashboard-item-list-horizontal>li{-webkit-flex-basis:50%;flex-basis:50%}}@media (min-width: 992px){.wf-dashboard-item .wf-dashboard-item-extra .wf-dashboard-item-list.wf-dashboard-item-list-horizontal>li{-webkit-flex-basis:20%;flex-basis:20%}}.wf-dashboard-item .wf-dashboard-item-extra .wf-dashboard-item-list.wf-dashboard-item-list-horizontal>*:first-child{border-left:0}@media (min-width: 768px){.wf-dashboard-item .wf-dashboard-item-extra .wf-dashboard-item-list.wf-dashboard-item-list-horizontal.wf-dashboard-item-list-equal>li{max-width:50%}}@media (min-width: 992px){.wf-dashboard-item .wf-dashboard-item-extra .wf-dashboard-item-list.wf-dashboard-item-list-horizontal.wf-dashboard-item-list-equal>li{max-width:20%}}.wf-dashboard-item .wf-dashboard-item-extra .wf-dashboard-item-list .wf-dashboard-item-list-state{text-align:center}@media (min-width: 1200px){.wf-dashboard-item .wf-dashboard-item-extra .wf-dashboard-item-list .wf-dashboard-item-list-state{text-align:left}}.wf-dashboard-item .wf-dashboard-item-extra .wf-dashboard-item-list .wf-dashboard-item-list-state-enabled .fa{color:#11967a}.wf-dashboard-item .wf-dashboard-item-extra .wf-dashboard-item-list .wf-dashboard-item-list-state-disabled .fa{color:#525355}.wf-dashboard-item .wf-dashboard-item-extra .wf-dashboard-item-list .wf-dashboard-item-list-state-premium{color:#9f9fa0}.wf-dashboard-item .wf-dashboard-item-extra .wf-dashboard-item-list .wf-dashboard-item-list-dismiss{padding-left:2rem;font-size:1.25rem}.wf-dashboard-item .wf-dashboard-item-extra .wf-dashboard-item-list .wf-dashboard-item-list-dismiss a{color:#525355}.wf-dashboard-item .wf-dashboard-item-extra .wf-dashboard-item-labeled-count{box-sizing:border-box;display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:space-between;justify-content:space-between;-webkit-flex-direction:column;flex-direction:column}.wf-dashboard-item .wf-dashboard-item-extra .wf-dashboard-item-labeled-count-count{font-size:3rem;line-height:3rem;color:#9f9fa0;padding:1rem}.wf-dashboard-item .wf-dashboard-item-extra .wf-dashboard-item-labeled-count-label{font-size:0.75rem;color:#9f9fa0;padding:0 1rem 1rem 1rem}.wf-dashboard-item.active .wf-dashboard-item-extra{display:block}.wf-dashboard-item.wf-dashboard-item-left .wf-dashboard-item-content{margin-left:48px}.wf-dashboard-item.wf-dashboard-item-left .wf-dashboard-item-action{right:auto;left:0px}.wf-dashboard-item.disabled .wf-dashboard-item-content .wf-dashboard-item-title{color:#aaaaaa}.wf-dashboard-item.disabled .wf-dashboard-item-content .wf-dashboard-item-subtitle{color:#8ea6be}.wf-notifications-empty{font-size:0.9rem;color:#9f9fa0}.wf-dashboard-graph-wrapper{width:100%}.wf-dashboard-badge{display:inline-block;min-width:10px;padding:3px 7px;margin-left:0.5rem;font-size:12px;font-weight:bold;color:#fff;line-height:1;vertical-align:middle;white-space:nowrap;text-align:center;background-color:#fcb214;border-radius:10px}.wf-dashboard-badge:empty{display:none}.wf-btn .wf-dashboard-badge{position:relative;top:-1px}.wf-btn-xs .wf-dashboard-badge,.wf-btn-group-xs>.wf-btn .wf-dashboard-badge{top:0;padding:1px 5px}.wf-list-group-item.active>.wf-dashboard-badge,.wf-nav-pills>.active>a>.wf-dashboard-badge{color:#00709e;background-color:#fff}.wf-list-group-item>.wf-dashboard-badge{float:right}.wf-list-group-item>.wf-dashboard-badge+.wf-dashboard-badge{margin-right:5px}.wf-nav-pills>li>a>.wf-dashboard-badge{margin-left:3px}.wf-dashboard-toggle-btns{text-align:center}.wf-dashboard-toggle-btns .wf-pagination{margin:1rem 1rem 0.5rem 1rem}.wf-dashboard-show-more{position:relative;font-size:14px;color:#959595;text-align:center;line-height:1rem;background:#ffffff;width:60%;margin:20px auto 0 auto}.wf-dashboard-show-more:before{display:inline-block;content:"";position:absolute;height:1px;background:#dddddd;top:50%;width:100%;left:0;right:0}.wf-dashboard-show-more a{display:inline-block;position:relative;padding:0 10px;background-color:#ffffff}.wf-ips,.wf-recent-logins,.wf-countries{max-height:30rem;overflow-y:hidden;margin-bottom:20px}.wf-ips .wf-table,.wf-recent-logins .wf-table,.wf-countries .wf-table{margin-bottom:0}.wf-dashboard-last-updated{font-style:italic;font-size:0.6rem;text-align:center;padding-bottom:1rem;margin:0}table.wf-table{background-color:transparent;border-collapse:collapse;border-spacing:0}table.wf-table td,table.wf-table th{padding:0}.wf-table caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}.wf-table th{text-align:left}.wf-table{width:100%;max-width:100%;margin-bottom:20px}.wf-table>thead>tr>th,.wf-table>thead>tr>td,.wf-table>tbody>tr>th,.wf-table>tbody>tr>td,.wf-table>tfoot>tr>th,.wf-table>tfoot>tr>td{padding:8px;line-height:1.42857;vertical-align:top;border-top:1px solid #ddd}.wf-table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.wf-table>caption+thead>tr:first-child>th,.wf-table>caption+thead>tr:first-child>td,.wf-table>colgroup+thead>tr:first-child>th,.wf-table>colgroup+thead>tr:first-child>td,.wf-table>thead:first-child>tr:first-child>th,.wf-table>thead:first-child>tr:first-child>td{border-top:0}.wf-table>tbody+tbody{border-top:2px solid #ddd}.wf-table .wf-table{background-color:#fff}.wf-table-condensed>thead>tr>th,.wf-table-condensed>thead>tr>td,.wf-table-condensed>tbody>tr>th,.wf-table-condensed>tbody>tr>td,.wf-table-condensed>tfoot>tr>th,.wf-table-condensed>tfoot>tr>td{padding:5px}.wf-table-bordered{border:1px solid #ddd}.wf-table-bordered>thead>tr>th,.wf-table-bordered>thead>tr>td,.wf-table-bordered>tbody>tr>th,.wf-table-bordered>tbody>tr>td,.wf-table-bordered>tfoot>tr>th,.wf-table-bordered>tfoot>tr>td{border:1px solid #ddd}.wf-table-bordered>thead>tr>th,.wf-table-bordered>thead>tr>td{border-bottom-width:2px}.wf-table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.wf-table-hover>tbody>tr:hover{background-color:#f5f5f5}table.wf-table col[class*="col-"]{position:static;float:none;display:table-column}table.wf-table td[class*="col-"],table.wf-table th[class*="col-"]{position:static;float:none;display:table-cell}.wf-table>thead>tr>td.active,.wf-table>thead>tr>th.active,.wf-table>thead>tr.active>td,.wf-table>thead>tr.active>th,.wf-table>tbody>tr>td.active,.wf-table>tbody>tr>th.active,.wf-table>tbody>tr.active>td,.wf-table>tbody>tr.active>th,.wf-table>tfoot>tr>td.active,.wf-table>tfoot>tr>th.active,.wf-table>tfoot>tr.active>td,.wf-table>tfoot>tr.active>th{background-color:#f5f5f5}.wf-table-hover>tbody>tr>td.active:hover,.wf-table-hover>tbody>tr>th.active:hover,.wf-table-hover>tbody>tr.active:hover>td,.wf-table-hover>tbody>tr:hover>.active,.wf-table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.wf-table>thead>tr>td.success,.wf-table>thead>tr>th.success,.wf-table>thead>tr.success>td,.wf-table>thead>tr.success>th,.wf-table>tbody>tr>td.success,.wf-table>tbody>tr>th.success,.wf-table>tbody>tr.success>td,.wf-table>tbody>tr.success>th,.wf-table>tfoot>tr>td.success,.wf-table>tfoot>tr>th.success,.wf-table>tfoot>tr.success>td,.wf-table>tfoot>tr.success>th{background-color:#dff0d8}.wf-table-hover>tbody>tr>td.success:hover,.wf-table-hover>tbody>tr>th.success:hover,.wf-table-hover>tbody>tr.success:hover>td,.wf-table-hover>tbody>tr:hover>.success,.wf-table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.wf-table>thead>tr>td.info,.wf-table>thead>tr>th.info,.wf-table>thead>tr.info>td,.wf-table>thead>tr.info>th,.wf-table>tbody>tr>td.info,.wf-table>tbody>tr>th.info,.wf-table>tbody>tr.info>td,.wf-table>tbody>tr.info>th,.wf-table>tfoot>tr>td.info,.wf-table>tfoot>tr>th.info,.wf-table>tfoot>tr.info>td,.wf-table>tfoot>tr.info>th{background-color:#d9edf7}.wf-table-hover>tbody>tr>td.info:hover,.wf-table-hover>tbody>tr>th.info:hover,.wf-table-hover>tbody>tr.info:hover>td,.wf-table-hover>tbody>tr:hover>.info,.wf-table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}.wf-table>thead>tr>td.warning,.wf-table>thead>tr>th.warning,.wf-table>thead>tr.warning>td,.wf-table>thead>tr.warning>th,.wf-table>tbody>tr>td.warning,.wf-table>tbody>tr>th.warning,.wf-table>tbody>tr.warning>td,.wf-table>tbody>tr.warning>th,.wf-table>tfoot>tr>td.warning,.wf-table>tfoot>tr>th.warning,.wf-table>tfoot>tr.warning>td,.wf-table>tfoot>tr.warning>th{background-color:#fcf8e3}.wf-table-hover>tbody>tr>td.warning:hover,.wf-table-hover>tbody>tr>th.warning:hover,.wf-table-hover>tbody>tr.warning:hover>td,.wf-table-hover>tbody>tr:hover>.warning,.wf-table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}.wf-table>thead>tr>td.danger,.wf-table>thead>tr>th.danger,.wf-table>thead>tr.danger>td,.wf-table>thead>tr.danger>th,.wf-table>tbody>tr>td.danger,.wf-table>tbody>tr>th.danger,.wf-table>tbody>tr.danger>td,.wf-table>tbody>tr.danger>th,.wf-table>tfoot>tr>td.danger,.wf-table>tfoot>tr>th.danger,.wf-table>tfoot>tr.danger>td,.wf-table>tfoot>tr.danger>th{background-color:#f2dede}.wf-table-hover>tbody>tr>td.danger:hover,.wf-table-hover>tbody>tr>th.danger:hover,.wf-table-hover>tbody>tr.danger:hover>td,.wf-table-hover>tbody>tr:hover>.danger,.wf-table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}.wf-table-responsive{overflow-x:auto;min-height:0.01%}@media screen and (max-width: 767px){.wf-table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.wf-table-responsive>.wf-table{margin-bottom:0}.wf-table-responsive>.wf-table>thead>tr>th,.wf-table-responsive>.wf-table>thead>tr>td,.wf-table-responsive>.wf-table>tbody>tr>th,.wf-table-responsive>.wf-table>tbody>tr>td,.wf-table-responsive>.wf-table>tfoot>tr>th,.wf-table-responsive>.wf-table>tfoot>tr>td{white-space:nowrap}.wf-table-responsive>.wf-table-bordered{border:0}.wf-table-responsive>.wf-table-bordered>thead>tr>th:first-child,.wf-table-responsive>.wf-table-bordered>thead>tr>td:first-child,.wf-table-responsive>.wf-table-bordered>tbody>tr>th:first-child,.wf-table-responsive>.wf-table-bordered>tbody>tr>td:first-child,.wf-table-responsive>.wf-table-bordered>tfoot>tr>th:first-child,.wf-table-responsive>.wf-table-bordered>tfoot>tr>td:first-child{border-left:0}.wf-table-responsive>.wf-table-bordered>thead>tr>th:last-child,.wf-table-responsive>.wf-table-bordered>thead>tr>td:last-child,.wf-table-responsive>.wf-table-bordered>tbody>tr>th:last-child,.wf-table-responsive>.wf-table-bordered>tbody>tr>td:last-child,.wf-table-responsive>.wf-table-bordered>tfoot>tr>th:last-child,.wf-table-responsive>.wf-table-bordered>tfoot>tr>td:last-child{border-right:0}.wf-table-responsive>.wf-table-bordered>tbody>tr:last-child>th,.wf-table-responsive>.wf-table-bordered>tbody>tr:last-child>td,.wf-table-responsive>.wf-table-bordered>tfoot>tr:last-child>th,.wf-table-responsive>.wf-table-bordered>tfoot>tr:last-child>td{border-bottom:0}}.wf-nav{margin-bottom:0;padding-left:0;list-style:none}.wf-nav:before,.wf-nav:after{content:" ";display:table}.wf-nav:after{clear:both}.wf-nav>li{position:relative;display:block}.wf-nav>li>a{position:relative;display:block;padding:8px 12px}.wf-nav>li>a:hover,.wf-nav>li>a:focus{text-decoration:none;background-color:#e2e2e2}.wf-nav>li.wf-disabled>a{color:#777}.wf-nav>li.wf-disabled>a:hover,.wf-nav>li.wf-disabled>a:focus{color:#777;text-decoration:none;background-color:transparent;cursor:not-allowed}.wf-nav .wf-open>a,.wf-nav .wf-open>a:hover,.wf-nav .wf-open>a:focus{background-color:#e2e2e2;border-color:#00709e}.wf-nav>li>a>img{max-width:none}.wf-nav-tabs{border-bottom:1px solid #ddd}.wf-nav-tabs>li{float:left;margin-bottom:-1px}.wf-nav-tabs>li>a{margin-right:2px;line-height:1.42857;border:1px solid transparent;border-radius:4px 4px 0 0}.wf-nav-tabs>li>a:hover{border-color:#e2e2e2 #e2e2e2 #ddd}.wf-nav-tabs>li.wf-active>a,.wf-nav-tabs>li.wf-active>a:hover,.wf-nav-tabs>li.wf-active>a:focus{color:#555;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default}.wf-nav-pills>li{float:left}.wf-nav-pills>li>a{border-radius:4px}.wf-nav-pills>li+li{margin-left:2px}.wf-nav-pills>li.wf-active>a,.wf-nav-pills>li.wf-active>a:hover,.wf-nav-pills>li.wf-active>a:focus{color:#fff;background-color:#00709e}.wf-nav-stacked>li{float:none}.wf-nav-stacked>li+li{margin-top:2px;margin-left:0}.wf-nav-justified,.wf-nav-tabs.wf-nav-justified{width:100%}.wf-nav-justified>li,.wf-nav-tabs.wf-nav-justified>li{float:none}.wf-nav-justified>li>a,.wf-nav-tabs.wf-nav-justified>li>a{text-align:center;margin-bottom:5px}.wf-nav-justified>.wf-dropdown .wf-dropdown-menu{top:auto;left:auto}@media (min-width: 768px){.wf-nav-justified>li,.wf-nav-tabs.wf-nav-justified>li{display:table-cell;width:1%}.wf-nav-justified>li>a,.wf-nav-tabs.wf-nav-justified>li>a{margin-bottom:0}}.wf-nav-tabs-justified,.wf-nav-tabs.wf-nav-justified{border-bottom:0}.wf-nav-tabs-justified>li>a,.wf-nav-tabs.wf-nav-justified>li>a{margin-right:0;border-radius:4px}.wf-nav-tabs-justified>.wf-active>a,.wf-nav-tabs.wf-nav-justified>.wf-active>a,.wf-nav-tabs-justified>.wf-active>a:hover,.wf-nav-tabs.wf-nav-justified>.wf-active>a:hover,.wf-nav-tabs-justified>.wf-active>a:focus,.wf-nav-tabs.wf-nav-justified>.wf-active>a:focus{border:1px solid #ddd}@media (min-width: 768px){.wf-nav-tabs-justified>li>a,.wf-nav-tabs.wf-nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.wf-nav-tabs-justified>.wf-active>a,.wf-nav-tabs.wf-nav-justified>.wf-active>a,.wf-nav-tabs-justified>.wf-active>a:hover,.wf-nav-tabs.wf-nav-justified>.wf-active>a:hover,.wf-nav-tabs-justified>.wf-active>a:focus,.wf-nav-tabs.wf-nav-justified>.wf-active>a:focus{border-bottom-color:#fff}}.wf-tab-content>.wf-tab-pane{display:none}.wf-tab-content>.wf-active{display:block}.wf-nav-tabs .wf-dropdown-menu{margin-top:-1px;-moz-border-radius-topleft:0;-webkit-border-top-left-radius:0;border-top-left-radius:0;-moz-border-radius-topright:0;-webkit-border-top-right-radius:0;border-top-right-radius:0}.wf-navbar-brand{float:left;padding:12px 8px;font-size:18px;line-height:20px;margin:10px 0 0 0}.wf-navbar-brand:hover,.wf-navbar-brand:focus{text-decoration:none}.wf-navbar-brand>img{display:block}@media (min-width: 768px){.navbar>.container .wf-navbar-brand,.navbar>.container-fluid .wf-navbar-brand{margin-left:-8px}}.wf-caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid \9;border-right:4px solid transparent;border-left:4px solid transparent}.wf-dropup,.wf-dropdown{position:relative}.wf-dropdown-toggle:focus{outline:0}.wf-dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;font-size:14px;text-align:left;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.175);box-shadow:0 6px 12px rgba(0,0,0,0.175);background-clip:padding-box}.wf-dropdown-menu .wf-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.wf-dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:1.42857;color:#333;text-decoration:none;white-space:nowrap}.wf-dropdown-menu>li>a:hover,.wf-dropdown-menu>li>a:focus{text-decoration:none;color:#262626;background-color:#f5f5f5}.wf-dropdown-menu>.wf-active>a,.wf-dropdown-menu>.wf-active>a:hover,.wf-dropdown-menu>.wf-active>a:focus{color:#fff;text-decoration:none;outline:0;background-color:#00709e}.wf-dropdown-menu>.wf-disabled>a,.wf-dropdown-menu>.wf-disabled>a:hover,.wf-dropdown-menu>.wf-disabled>a:focus{color:#777}.wf-dropdown-menu>.wf-disabled>a:hover,.wf-dropdown-menu>.wf-disabled>a:focus{text-decoration:none;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);cursor:not-allowed}.wf-open>.wf-dropdown-menu{display:block}.wf-open>a{outline:0}.wf-dropdown-menu-right{left:auto;right:0}.wf-dropdown-menu-left{left:0;right:auto}.wf-dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857;color:#777;white-space:nowrap}.wf-dropdown-backdrop{position:fixed;left:0;right:0;bottom:0;top:0;z-index:990}.wf-pull-right>.wf-dropdown-menu{right:0;left:auto}.wf-dropup .wf-caret,.wf-navbar-fixed-bottom .wf-dropdown .wf-caret{border-top:0;border-bottom:4px dashed;border-bottom:4px solid \9;content:""}.wf-dropup .wf-dropdown-menu,.wf-navbar-fixed-bottom .wf-dropdown .wf-dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width: 768px){.wf-navbar-right .wf-dropdown-menu{right:0;left:auto}.wf-navbar-right .wf-dropdown-menu-left{left:0;right:auto}}.wf-mobile-dropdown{border:1px solid #ccc;margin-left:.5em;padding:5px 10px;font-size:14px;line-height:24px;margin:10px 10px 0 0;background:#f1f1f1;color:#000;font-weight:600;text-decoration:none}.wf-blocked-countries{margin-bottom:0;margin-top:0;padding-left:0;list-style:none;margin-left:-6px;display:-webkit-flex;display:flex;-webkit-align-items:stretch;align-items:stretch;-webkit-justify-content:flex-start;justify-content:flex-start;-webkit-align-content:stretch;align-content:stretch;-webkit-flex-wrap:wrap;flex-wrap:wrap}.wf-blocked-countries>li{display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:center;justify-content:center;margin:0px 0px 2px 6px;text-align:center;border:1px solid #e2e2e2;border-radius:4px;padding:8px 12px;background-color:#ffffff;-webkit-flex-basis:38%;flex-basis:38%}@media (min-width: 768px){.wf-blocked-countries>li{-webkit-flex-basis:20%;flex-basis:20%}}@media (min-width: 992px){.wf-blocked-countries>li{-webkit-flex-basis:15%;flex-basis:15%}}@media (min-width: 1200px){.wf-blocked-countries>li{-webkit-flex-basis:10%;flex-basis:10%}}.wf-blocked-countries>li:hover,.wf-blocked-countries>li:focus{text-decoration:none;background-color:#e2e2e2}.wf-blocked-countries>li.disabled>a{color:#777}.wf-blocked-countries>li.disabled>a:hover,.wf-blocked-countries>li.disabled>a:focus{color:#777;text-decoration:none;background-color:transparent;cursor:not-allowed}.wf-blocked-countries>li>a{text-decoration:none;position:relative;display:block}.wf-blocked-countries>li.active,.wf-blocked-countries>li.active:hover,.wf-blocked-countries>li.active:focus{background-color:#00709e;border-color:#00709e}.wf-blocked-countries>li.active>a,.wf-blocked-countries>li.active:hover>a,.wf-blocked-countries>li.active:focus>a{color:#fff}.wf-blocked-countries>li.text-only{position:relative;display:block;padding:8px 12px}.wf-blocked-countries>li>a>img{max-width:none}.wf-blocked-countries-section{display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:flex-start;justify-content:flex-start;-webkit-align-content:stretch;align-content:stretch;-webkit-flex-wrap:nowrap;flex-wrap:nowrap}.wf-blocked-countries-section-title{font-size:1.1rem;padding-right:0.5rem}.wf-blocked-countries-section-spacer{-webkit-flex-basis:30px;flex-basis:30px;height:1px;background:#aaa}.wf-blocked-countries-section-options{margin-bottom:0;margin-top:0;padding-left:0.5rem;list-style:none;display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:flex-start;justify-content:flex-start;-webkit-align-content:stretch;align-content:stretch;-webkit-flex-wrap:nowrap;flex-wrap:nowrap}.wf-blocked-countries-section-options li{padding:0;margin:0;color:#777}.wf-blocked-countries-section-options li a{padding:2px 4px;text-decoration:none;color:#777}.wf-blocked-countries-section-options li a.active-section{background-color:#777;color:#e2e2e2}
1
+ .wf-clearfix:before,.wf-clearfix:after{content:" ";display:table}.wf-clearfix:after{clear:both}@-ms-viewport{width:device-width}.wf-visible-xs{display:none !important}.wf-visible-sm{display:none !important}.wf-visible-md{display:none !important}.wf-visible-lg{display:none !important}.wf-visible-xs-block,.wf-visible-xs-inline,.wf-visible-xs-inline-block,.wf-visible-sm-block,.wf-visible-sm-inline,.wf-visible-sm-inline-block,.wf-visible-md-block,.wf-visible-md-inline,.wf-visible-md-inline-block,.wf-visible-lg-block,.wf-visible-lg-inline,.wf-visible-lg-inline-block{display:none !important}@media (max-width: 767px){.wf-visible-xs{display:block !important}table.wf-visible-xs{display:table !important}tr.wf-visible-xs{display:table-row !important}th.wf-visible-xs,td.wf-visible-xs{display:table-cell !important}}@media (max-width: 767px){.wf-visible-xs-block{display:block !important}}@media (max-width: 767px){.wf-visible-xs-inline{display:inline !important}}@media (max-width: 767px){.wf-visible-xs-inline-block{display:inline-block !important}}@media (min-width: 768px) and (max-width: 991px){.wf-visible-sm{display:block !important}table.wf-visible-sm{display:table !important}tr.wf-visible-sm{display:table-row !important}th.wf-visible-sm,td.wf-visible-sm{display:table-cell !important}}@media (min-width: 768px) and (max-width: 991px){.wf-visible-sm-block{display:block !important}}@media (min-width: 768px) and (max-width: 991px){.wf-visible-sm-inline{display:inline !important}}@media (min-width: 768px) and (max-width: 991px){.wf-visible-sm-inline-block{display:inline-block !important}}@media (min-width: 992px) and (max-width: 1199px){.wf-visible-md{display:block !important}table.wf-visible-md{display:table !important}tr.wf-visible-md{display:table-row !important}th.wf-visible-md,td.wf-visible-md{display:table-cell !important}}@media (min-width: 992px) and (max-width: 1199px){.wf-visible-md-block{display:block !important}}@media (min-width: 992px) and (max-width: 1199px){.wf-visible-md-inline{display:inline !important}}@media (min-width: 992px) and (max-width: 1199px){.wf-visible-md-inline-block{display:inline-block !important}}@media (min-width: 1200px){.wf-visible-lg{display:block !important}table.wf-visible-lg{display:table !important}tr.wf-visible-lg{display:table-row !important}th.wf-visible-lg,td.wf-visible-lg{display:table-cell !important}}@media (min-width: 1200px){.wf-visible-lg-block{display:block !important}}@media (min-width: 1200px){.wf-visible-lg-inline{display:inline !important}}@media (min-width: 1200px){.wf-visible-lg-inline-block{display:inline-block !important}}@media (max-width: 767px){.wf-hidden-xs{display:none !important}}@media (min-width: 768px) and (max-width: 991px){.wf-hidden-sm{display:none !important}}@media (min-width: 992px) and (max-width: 1199px){.wf-hidden-md{display:none !important}}@media (min-width: 1200px){.wf-hidden-lg{display:none !important}}.wf-visible-print{display:none !important}@media print{.wf-visible-print{display:block !important}table.wf-visible-print{display:table !important}tr.wf-visible-print{display:table-row !important}th.wf-visible-print,td.wf-visible-print{display:table-cell !important}}.wf-visible-print-block{display:none !important}@media print{.wf-visible-print-block{display:block !important}}.wf-visible-print-inline{display:none !important}@media print{.wf-visible-print-inline{display:inline !important}}.wf-visible-print-inline-block{display:none !important}@media print{.wf-visible-print-inline-block{display:inline-block !important}}@media print{.wf-hidden-print{display:none !important}}.wf-container{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}.wf-container:before,.wf-container:after{content:" ";display:table}.wf-container:after{clear:both}@media (min-width: 768px){.wf-container{width:750px}}@media (min-width: 992px){.wf-container{width:970px}}@media (min-width: 1200px){.wf-container{width:1170px}}.wf-container-fluid{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}.wf-container-fluid:before,.wf-container-fluid:after{content:" ";display:table}.wf-container-fluid:after{clear:both}.wf-row{margin-left:-15px;margin-right:-15px}.wf-row:before,.wf-row:after{content:" ";display:table}.wf-row:after{clear:both}.wf-col-xs-1,.wf-col-sm-1,.wf-col-md-1,.wf-col-lg-1,.wf-col-xs-2,.wf-col-sm-2,.wf-col-md-2,.wf-col-lg-2,.wf-col-xs-3,.wf-col-sm-3,.wf-col-md-3,.wf-col-lg-3,.wf-col-xs-4,.wf-col-sm-4,.wf-col-md-4,.wf-col-lg-4,.wf-col-xs-5,.wf-col-sm-5,.wf-col-md-5,.wf-col-lg-5,.wf-col-xs-6,.wf-col-sm-6,.wf-col-md-6,.wf-col-lg-6,.wf-col-xs-7,.wf-col-sm-7,.wf-col-md-7,.wf-col-lg-7,.wf-col-xs-8,.wf-col-sm-8,.wf-col-md-8,.wf-col-lg-8,.wf-col-xs-9,.wf-col-sm-9,.wf-col-md-9,.wf-col-lg-9,.wf-col-xs-10,.wf-col-sm-10,.wf-col-md-10,.wf-col-lg-10,.wf-col-xs-11,.wf-col-sm-11,.wf-col-md-11,.wf-col-lg-11,.wf-col-xs-12,.wf-col-sm-12,.wf-col-md-12,.wf-col-lg-12{position:relative;min-height:1px;padding-left:15px;padding-right:15px;box-sizing:border-box}.wf-col-xs-1,.wf-col-xs-2,.wf-col-xs-3,.wf-col-xs-4,.wf-col-xs-5,.wf-col-xs-6,.wf-col-xs-7,.wf-col-xs-8,.wf-col-xs-9,.wf-col-xs-10,.wf-col-xs-11,.wf-col-xs-12{float:left}.wf-col-xs-1{width:8.33333%}.wf-col-xs-2{width:16.66667%}.wf-col-xs-3{width:25%}.wf-col-xs-4{width:33.33333%}.wf-col-xs-5{width:41.66667%}.wf-col-xs-6{width:50%}.wf-col-xs-7{width:58.33333%}.wf-col-xs-8{width:66.66667%}.wf-col-xs-9{width:75%}.wf-col-xs-10{width:83.33333%}.wf-col-xs-11{width:91.66667%}.wf-col-xs-12{width:100%}.wf-col-xs-pull-0{right:auto}.wf-col-xs-pull-1{right:8.33333%}.wf-col-xs-pull-2{right:16.66667%}.wf-col-xs-pull-3{right:25%}.wf-col-xs-pull-4{right:33.33333%}.wf-col-xs-pull-5{right:41.66667%}.wf-col-xs-pull-6{right:50%}.wf-col-xs-pull-7{right:58.33333%}.wf-col-xs-pull-8{right:66.66667%}.wf-col-xs-pull-9{right:75%}.wf-col-xs-pull-10{right:83.33333%}.wf-col-xs-pull-11{right:91.66667%}.wf-col-xs-pull-12{right:100%}.wf-col-xs-push-0{left:auto}.wf-col-xs-push-1{left:8.33333%}.wf-col-xs-push-2{left:16.66667%}.wf-col-xs-push-3{left:25%}.wf-col-xs-push-4{left:33.33333%}.wf-col-xs-push-5{left:41.66667%}.wf-col-xs-push-6{left:50%}.wf-col-xs-push-7{left:58.33333%}.wf-col-xs-push-8{left:66.66667%}.wf-col-xs-push-9{left:75%}.wf-col-xs-push-10{left:83.33333%}.wf-col-xs-push-11{left:91.66667%}.wf-col-xs-push-12{left:100%}.wf-col-xs-offset-0{margin-left:0%}.wf-col-xs-offset-1{margin-left:8.33333%}.wf-col-xs-offset-2{margin-left:16.66667%}.wf-col-xs-offset-3{margin-left:25%}.wf-col-xs-offset-4{margin-left:33.33333%}.wf-col-xs-offset-5{margin-left:41.66667%}.wf-col-xs-offset-6{margin-left:50%}.wf-col-xs-offset-7{margin-left:58.33333%}.wf-col-xs-offset-8{margin-left:66.66667%}.wf-col-xs-offset-9{margin-left:75%}.wf-col-xs-offset-10{margin-left:83.33333%}.wf-col-xs-offset-11{margin-left:91.66667%}.wf-col-xs-offset-12{margin-left:100%}.wf-col-xs-half-padding-left{padding-left:8px}.wf-col-xs-half-padding-right{padding-right:7px}@media (min-width: 768px){.wf-col-sm-1,.wf-col-sm-2,.wf-col-sm-3,.wf-col-sm-4,.wf-col-sm-5,.wf-col-sm-6,.wf-col-sm-7,.wf-col-sm-8,.wf-col-sm-9,.wf-col-sm-10,.wf-col-sm-11,.wf-col-sm-12{float:left}.wf-col-sm-1{width:8.33333%}.wf-col-sm-2{width:16.66667%}.wf-col-sm-3{width:25%}.wf-col-sm-4{width:33.33333%}.wf-col-sm-5{width:41.66667%}.wf-col-sm-6{width:50%}.wf-col-sm-7{width:58.33333%}.wf-col-sm-8{width:66.66667%}.wf-col-sm-9{width:75%}.wf-col-sm-10{width:83.33333%}.wf-col-sm-11{width:91.66667%}.wf-col-sm-12{width:100%}.wf-col-sm-pull-0{right:auto}.wf-col-sm-pull-1{right:8.33333%}.wf-col-sm-pull-2{right:16.66667%}.wf-col-sm-pull-3{right:25%}.wf-col-sm-pull-4{right:33.33333%}.wf-col-sm-pull-5{right:41.66667%}.wf-col-sm-pull-6{right:50%}.wf-col-sm-pull-7{right:58.33333%}.wf-col-sm-pull-8{right:66.66667%}.wf-col-sm-pull-9{right:75%}.wf-col-sm-pull-10{right:83.33333%}.wf-col-sm-pull-11{right:91.66667%}.wf-col-sm-pull-12{right:100%}.wf-col-sm-push-0{left:auto}.wf-col-sm-push-1{left:8.33333%}.wf-col-sm-push-2{left:16.66667%}.wf-col-sm-push-3{left:25%}.wf-col-sm-push-4{left:33.33333%}.wf-col-sm-push-5{left:41.66667%}.wf-col-sm-push-6{left:50%}.wf-col-sm-push-7{left:58.33333%}.wf-col-sm-push-8{left:66.66667%}.wf-col-sm-push-9{left:75%}.wf-col-sm-push-10{left:83.33333%}.wf-col-sm-push-11{left:91.66667%}.wf-col-sm-push-12{left:100%}.wf-col-sm-offset-0{margin-left:0%}.wf-col-sm-offset-1{margin-left:8.33333%}.wf-col-sm-offset-2{margin-left:16.66667%}.wf-col-sm-offset-3{margin-left:25%}.wf-col-sm-offset-4{margin-left:33.33333%}.wf-col-sm-offset-5{margin-left:41.66667%}.wf-col-sm-offset-6{margin-left:50%}.wf-col-sm-offset-7{margin-left:58.33333%}.wf-col-sm-offset-8{margin-left:66.66667%}.wf-col-sm-offset-9{margin-left:75%}.wf-col-sm-offset-10{margin-left:83.33333%}.wf-col-sm-offset-11{margin-left:91.66667%}.wf-col-sm-offset-12{margin-left:100%}.wf-col-sm-half-padding-left{padding-left:8px}.wf-col-sm-half-padding-right{padding-right:7px}}@media (min-width: 992px){.wf-col-md-1,.wf-col-md-2,.wf-col-md-3,.wf-col-md-4,.wf-col-md-5,.wf-col-md-6,.wf-col-md-7,.wf-col-md-8,.wf-col-md-9,.wf-col-md-10,.wf-col-md-11,.wf-col-md-12{float:left}.wf-col-md-1{width:8.33333%}.wf-col-md-2{width:16.66667%}.wf-col-md-3{width:25%}.wf-col-md-4{width:33.33333%}.wf-col-md-5{width:41.66667%}.wf-col-md-6{width:50%}.wf-col-md-7{width:58.33333%}.wf-col-md-8{width:66.66667%}.wf-col-md-9{width:75%}.wf-col-md-10{width:83.33333%}.wf-col-md-11{width:91.66667%}.wf-col-md-12{width:100%}.wf-col-md-pull-0{right:auto}.wf-col-md-pull-1{right:8.33333%}.wf-col-md-pull-2{right:16.66667%}.wf-col-md-pull-3{right:25%}.wf-col-md-pull-4{right:33.33333%}.wf-col-md-pull-5{right:41.66667%}.wf-col-md-pull-6{right:50%}.wf-col-md-pull-7{right:58.33333%}.wf-col-md-pull-8{right:66.66667%}.wf-col-md-pull-9{right:75%}.wf-col-md-pull-10{right:83.33333%}.wf-col-md-pull-11{right:91.66667%}.wf-col-md-pull-12{right:100%}.wf-col-md-push-0{left:auto}.wf-col-md-push-1{left:8.33333%}.wf-col-md-push-2{left:16.66667%}.wf-col-md-push-3{left:25%}.wf-col-md-push-4{left:33.33333%}.wf-col-md-push-5{left:41.66667%}.wf-col-md-push-6{left:50%}.wf-col-md-push-7{left:58.33333%}.wf-col-md-push-8{left:66.66667%}.wf-col-md-push-9{left:75%}.wf-col-md-push-10{left:83.33333%}.wf-col-md-push-11{left:91.66667%}.wf-col-md-push-12{left:100%}.wf-col-md-offset-0{margin-left:0%}.wf-col-md-offset-1{margin-left:8.33333%}.wf-col-md-offset-2{margin-left:16.66667%}.wf-col-md-offset-3{margin-left:25%}.wf-col-md-offset-4{margin-left:33.33333%}.wf-col-md-offset-5{margin-left:41.66667%}.wf-col-md-offset-6{margin-left:50%}.wf-col-md-offset-7{margin-left:58.33333%}.wf-col-md-offset-8{margin-left:66.66667%}.wf-col-md-offset-9{margin-left:75%}.wf-col-md-offset-10{margin-left:83.33333%}.wf-col-md-offset-11{margin-left:91.66667%}.wf-col-md-offset-12{margin-left:100%}.wf-col-md-half-padding-left{padding-left:8px}.wf-col-md-half-padding-right{padding-right:7px}}@media (min-width: 1200px){.wf-col-lg-1,.wf-col-lg-2,.wf-col-lg-3,.wf-col-lg-4,.wf-col-lg-5,.wf-col-lg-6,.wf-col-lg-7,.wf-col-lg-8,.wf-col-lg-9,.wf-col-lg-10,.wf-col-lg-11,.wf-col-lg-12{float:left}.wf-col-lg-1{width:8.33333%}.wf-col-lg-2{width:16.66667%}.wf-col-lg-3{width:25%}.wf-col-lg-4{width:33.33333%}.wf-col-lg-5{width:41.66667%}.wf-col-lg-6{width:50%}.wf-col-lg-7{width:58.33333%}.wf-col-lg-8{width:66.66667%}.wf-col-lg-9{width:75%}.wf-col-lg-10{width:83.33333%}.wf-col-lg-11{width:91.66667%}.wf-col-lg-12{width:100%}.wf-col-lg-pull-0{right:auto}.wf-col-lg-pull-1{right:8.33333%}.wf-col-lg-pull-2{right:16.66667%}.wf-col-lg-pull-3{right:25%}.wf-col-lg-pull-4{right:33.33333%}.wf-col-lg-pull-5{right:41.66667%}.wf-col-lg-pull-6{right:50%}.wf-col-lg-pull-7{right:58.33333%}.wf-col-lg-pull-8{right:66.66667%}.wf-col-lg-pull-9{right:75%}.wf-col-lg-pull-10{right:83.33333%}.wf-col-lg-pull-11{right:91.66667%}.wf-col-lg-pull-12{right:100%}.wf-col-lg-push-0{left:auto}.wf-col-lg-push-1{left:8.33333%}.wf-col-lg-push-2{left:16.66667%}.wf-col-lg-push-3{left:25%}.wf-col-lg-push-4{left:33.33333%}.wf-col-lg-push-5{left:41.66667%}.wf-col-lg-push-6{left:50%}.wf-col-lg-push-7{left:58.33333%}.wf-col-lg-push-8{left:66.66667%}.wf-col-lg-push-9{left:75%}.wf-col-lg-push-10{left:83.33333%}.wf-col-lg-push-11{left:91.66667%}.wf-col-lg-push-12{left:100%}.wf-col-lg-offset-0{margin-left:0%}.wf-col-lg-offset-1{margin-left:8.33333%}.wf-col-lg-offset-2{margin-left:16.66667%}.wf-col-lg-offset-3{margin-left:25%}.wf-col-lg-offset-4{margin-left:33.33333%}.wf-col-lg-offset-5{margin-left:41.66667%}.wf-col-lg-offset-6{margin-left:50%}.wf-col-lg-offset-7{margin-left:58.33333%}.wf-col-lg-offset-8{margin-left:66.66667%}.wf-col-lg-offset-9{margin-left:75%}.wf-col-lg-offset-10{margin-left:83.33333%}.wf-col-lg-offset-11{margin-left:91.66667%}.wf-col-lg-offset-12{margin-left:100%}.wf-col-lg-half-padding-left{padding-left:8px}.wf-col-lg-half-padding-right{padding-right:7px}}.wrap.wordfence{direction:ltr}@media (min-width: 768px){.wrap.wordfence{max-width:750px}}@media (min-width: 992px){.wrap.wordfence{max-width:970px}}@media (min-width: 1200px){.wrap.wordfence{max-width:1170px}}.wrap.wordfence>.wf-container-fluid{padding-left:0px;padding-right:0px}.wrap.wordfence .button-primary{text-align:center;text-transform:uppercase;font-weight:bold;background-color:#00709e}.wrap.wordfence h3{font-size:1.15em}a{color:#00709e}.wordfenceWrap{margin:20px 0 0 20px}.wordfence-icon32{width:32px;height:32px;background-position:0 0;background-repeat:no-repeat;padding:0;margin:7px 5px 0 0;float:left}#wfHeading:after{content:'.';visibility:hidden;display:block;clear:both;height:0px}div.wordfence-lock-icon{background-image:url(../images/wordfence-logo-32x32.png)}a.wfhelp{background-image:url(../images/help.png);width:12px;height:12px;background-position:0 0;background-repeat:no-repeat;padding:0;margin:0 3px 0 3px;text-decoration:none;display:inline-block;vertical-align:middle}.wordfence .resulticon{display:block;float:left;width:16px;height:16px;background-position:0 0;background-repeat:no-repeat;border-width:0;padding:0;margin:0 3px 0 0;background-image:url(../images/icons/bullet_yellow.png)}.wordfenceBoldTD{font-weight:bold}.wfAjax24{display:none;width:24px;height:24px;background-image:url(../images/icons/ajax24.gif);margin:0;padding:0}div.wfLoadingWhite32{width:32px;height:32px;background-image:url(../images/icons/ajaxWhite32x32.gif);margin:0;padding:0}.wfTabsContainer{background-color:#FFF;overflow:hidden;border:1px solid #CCC;padding:15px;min-height:200px;-webkit-font-smoothing:antialiased}#wfTabs::after{content:".";display:block;height:0;width:0;line-height:0;clear:both;visibility:hidden}#wfTabs a{float:left;z-index:10;height:18px;margin:0 5px -1px 0;padding:5px 8px;border:1px solid #CCC;text-decoration:none;background-color:#EFEFEF;color:#21759B;-moz-border-radius-topright:3px;-webkit-border-top-right-radius:3px;border-top-right-radius:3px;-moz-border-radius-topleft:3px;-webkit-border-top-left-radius:3px;border-top-left-radius:3px}#wfTabs a.selected{border-bottom:1px solid #FFF;background-color:#FFF;color:#777}.wordfenceTopTab{display:none;margin-top:15px}.wordfenceTopTab.active{display:block}.wordfenceHelpLink{margin-top:15px}.wfAjaxLight128{background-image:url(../images/icons/ajax3.gif)}.wfStrong{font-weight:bold}.wordfenceModeElem{width:1px;height:1px;opacity:0}.wfWarn{color:#F00}img.wfFlag{vertical-align:middle;margin:-3px 4px 0 0}.wfHitTime{font-style:italic}.wfAvatar img{vertical-align:middle}.wf-hex-sequence{color:#587ECB}.wfLoadMoreButton.disabled,.wfLoadMoreButton[disabled]{pointer-events:none;opacity:0.65}table.wfConfigForm th{font-weight:normal;text-align:left;padding:2px 3px 1px 0;vertical-align:middle}table.wfConfigForm td{vertical-align:middle}table.wfConfigForm td.align-top{vertical-align:top}table th.wfConfigEnable{font-weight:bold;min-width:25%}.wfSavedMsg{display:none;color:#A00}table th.wfSubheading{font-weight:bold;padding-top:10px}h3.wfConfigHeading{font-size:22px;color:#777;font-family:Georgia,Times New Roman,Times,serif;font-style:italic;font-weight:normal}.wfTipText{color:#777;font-family:Georgia,Times New Roman,Times,serif;font-style:italic}.wfBlackCursor{color:#FFF}.wf-spinner{display:inline-block;width:4px}.wferror{color:#F00}#wordfenceWorking{padding:2px 8px 2px 24px;z-index:100000;position:fixed;right:2px;bottom:2px;border:1px solid #000;background-color:#F00;color:#FFF;font-size:12px;font-weight:bold;font-family:Arial;text-align:center;background-image:url("../images/icons/ajaxRed16.gif");background-position:2px 2px;background-repeat:no-repeat}#paidWrap{position:relative}.paidInnerMsg{width:500px;margin:150px auto 0 auto;color:#000;font-size:18px;font-family:Georgia,Times New Roman,Times,serif;line-height:1.8em;text-align:center;-webkit-font-smoothing:antialiased}.wfMarker{height:1px;width:1px}.wfPaidOnlyNotice{width:500px;background-color:#FFFFE0;border:1px solid #000;padding:10px;margin:20px}.wfOnOffSwitch{position:relative !important;width:69px !important;-webkit-user-select:none !important;-moz-user-select:none !important;-ms-user-select:none !important;user-select:none !important}.wfOnOffSwitch-checkbox{display:none !important}.wfOnOffSwitch-label{display:block !important;overflow:hidden !important;cursor:pointer !important;border:2px solid #999999 !important;border-radius:19px !important;margin:0}.wfOnOffSwitch-inner{width:200% !important;margin-left:-100% !important;-webkit-transition:margin 0.3s ease-in !important;-o-transition:margin 0.3s ease-in !important;transition:margin 0.3s ease-in !important;-webkit-transition-delay:0s !important;transition-delay:0s !important}.wfOnOffSwitch-inner:before,.wfOnOffSwitch-inner:after{float:left !important;width:50% !important;height:19px !important;padding:0 !important;line-height:19px !important;font-size:14px !important;color:white !important;font-family:Trebuchet, Arial, sans-serif !important;font-weight:bold !important;-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important;-moz-border-radius:19px !important;-webkit-border-radius:19px;border-radius:19px !important;-webkit-box-shadow:0 9.5px 0 rgba(0,0,0,0.08) inset !important;box-shadow:0 9.5px 0 rgba(0,0,0,0.08) inset !important}.wfOnOffSwitch-inner:before{content:"ON" !important;padding-left:10px !important;background-color:#30D965 !important;color:#FFFFFF !important;-moz-border-radius:19px 0 0 19px !important;-webkit-border-radius:19px;border-radius:19px 0 0 19px !important}.wfOnOffSwitch-inner:after{content:"OFF" !important;padding-right:10px !important;background-color:#EEEEEE !important;color:#999999 !important;text-align:right !important;-moz-border-radius:0 19px 19px 0 !important;-webkit-border-radius:0;border-radius:0 19px 19px 0 !important}.wfOnOffSwitch-switch{width:19px !important;margin:0 !important;background:#FFFFFF !important;border:2px solid #999999 !important;-moz-border-radius:19px !important;-webkit-border-radius:19px;border-radius:19px !important;position:absolute !important;top:0 !important;bottom:0 !important;right:46px !important;-webkit-transition:all 0.3s ease-in !important;-o-transition:all 0.3s ease-in !important;transition:all 0.3s ease-in !important;-webkit-transition-delay:0s !important;transition-delay:0s !important;background-image:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzAwMDAwMCIgc3RvcC1vcGFjaXR5PSIwLjEiLz48c3RvcCBvZmZzZXQ9IjgwJSIgc3RvcC1jb2xvcj0iIzAwMDAwMCIgc3RvcC1vcGFjaXR5PSIwLjAiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2dyYWQpIiAvPjwvc3ZnPiA=') !important;background-size:100%;background-image:-webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, rgba(0,0,0,0.1)),color-stop(80%, rgba(0,0,0,0))) !important;background-image:-moz-linear-gradient(center top, rgba(0,0,0,0.1) 0%,rgba(0,0,0,0) 80%) !important;background-image:-webkit-linear-gradient(center top, rgba(0,0,0,0.1) 0%,rgba(0,0,0,0) 80%) !important;background-image:linear-gradient(to center bottom, rgba(0,0,0,0.1) 0%,rgba(0,0,0,0) 80%) !important;box-shadow:0 1px 1px white inset !important}.wfOnOffSwitch-checkbox:checked+.wfOnOffSwitch-label .wfOnOffSwitch-inner{margin-left:0 !important}.wfOnOffSwitch-checkbox:checked+.wfOnOffSwitch-label .wfOnOffSwitch-switch{right:0 !important}#wordfenceConfigWarning,#wordfenceAdminEmailWarning{clear:left;margin-top:5px}.wf-striped-table{width:100%;max-width:100%;border-collapse:collapse}.wf-striped-table th,.wf-striped-table td{padding:6px 4px;border:1px solid #ccc}.wf-striped-table thead th,.wf-striped-table thead td,.wf-striped-table tfoot th,.wf-striped-table tfoot td,.wf-striped-table tbody.thead th,.wf-striped-table tbody.thead td{background-color:#222;color:#fff;font-weight:bold;border-color:#474747;text-align:left}.wf-striped-table tbody tr.even td,.wf-striped-table tbody tr:nth-child(2n) td{background-color:#eee}.wf-striped-table tbody tr td,.wf-striped-table tbody tr.odd td{background-color:#fff}.wf-striped-table tbody tr:hover>td{background-color:#fffbd8}.wf-striped-table tbody.empty-row tr td{border-width:0;padding:8px 0;background-color:transparent}.wf-striped-table td.error{color:#d0514c;font-weight:bold}.wf-striped-table td.error:before{content:"\2718"}.wf-striped-table td.success{color:#008c10;font-weight:bold;max-width:20%}.wf-striped-table td.success:before{content:"\2713"}.wf-striped-table td.success:before,.wf-striped-table td.error:before{font-size:16px;display:inline-block;margin:0px 8px 0px 0px}.wf-striped-table td.inactive{font-weight:bold;color:#666666}.wordfence-waiting{line-height:32px}.wordfence-waiting img{vertical-align:middle}pre.wf-pre{margin:8px 0 20px;padding:12px;background:#ffffff;border:1px solid #999999;overflow:auto}.wf-center{text-align:center}#wfConfigForm{max-width:1035px}.wf-hidden{display:none !important}.wf-card{position:relative;margin:0 auto .625rem;padding:1rem;box-sizing:border-box;background:#fff;box-shadow:0 0 0 1px rgba(200,215,225,0.5),0 1px 2px #e9eff3}.wf-card .wf-card-inner{min-height:76px;width:100%;padding:8px;box-sizing:border-box;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;position:relative}.wf-card .wf-card-inner .wf-card-content{max-width:75%}.wf-card .wf-card-inner .wf-card-content .wf-card-title{font-size:1.125rem;width:100%}.wf-card .wf-card-inner .wf-card-content .wf-card-subtitle{margin-top:.125rem;margin-bottom:.125rem;font-size:.875rem;color:#4f748e}.wf-card .wf-card-inner .wf-card-action{position:absolute;top:0;right:0;height:100%;background:none;border:0;outline:0;width:48px;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;vertical-align:middle;transition:transform 0.15s cubic-bezier(0.175, 0.885, 0.32, 1.275),color 0.2s ease-in}.wf-card .wf-card-inner .wf-card-action .wf-card-action-chevron{background:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj4KPHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJjaGV2cm9uLW9iamVjdCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiCgkgd2lkdGg9IjI0cHgiIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAwIDI0IDI0IiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAyNCAyNCIKCSB4bWw6c3BhY2U9InByZXNlcnZlIj4KPHBhdGggaWQ9ImNoZXZyb24iIGQ9Ik0yMCA5bC04IDgtOC04IDEuNDE0LTEuNDE0TDEyIDE0LjE3Mmw2LjU4Ni02LjU4NiIvPgo8L3N2Zz4K");background-repeat:no-repeat;background-position:center center;width:24px;height:24px;fill:#87a6bc}.wf-card .wf-card-inner .wf-card-action .wf-card-action-checkbox{background-image:url(../images/checkbox.png);background-repeat:no-repeat;background-position:left center;width:29px;height:29px}.wf-card .wf-card-inner .wf-card-action .wf-card-action-checkbox.checked{background-position:right center}.wf-card .wf-card-extra{display:none;padding:0.5rem;margin-top:1rem;border-top:1px solid #f3f6f8}@media (min-width: 768px){.wf-card .wf-card-extra{padding:1rem}}.wf-card.active .wf-card-extra{display:block}.wf-card.wf-card-left .wf-card-content{margin-left:48px}.wf-card.wf-card-left .wf-card-action{right:auto;left:0px}.wf-card.disabled .wf-card-content .wf-card-title{color:#aaaaaa}.wf-card.disabled .wf-card-content .wf-card-subtitle{color:#8ea6be}.wf-add-top{margin-top:20px !important}.wf-add-top-small{margin-top:10px !important}.wf-add-bottom{margin-bottom:20px !important}.wf-add-bottom-small{margin-bottom:10px !important}.wf-center{text-align:center}.wf-right{text-align:right}.wf-scroll-x::-webkit-scrollbar,.wf-scroll-y::-webkit-scrollbar{-webkit-appearance:none;width:7px;height:7px}.wf-scroll-x::-webkit-scrollbar-thumb,.wf-scroll-y::-webkit-scrollbar-thumb{border-radius:4px;background-color:rgba(0,0,0,0.194);-webkit-box-shadow:0 0 1px rgba(255,255,255,0.5)}.wf-split-word{word-wrap:break-word;word-break:break-all}@media (max-width: 767px){.wf-split-word-xs{word-wrap:break-word;word-break:break-all;white-space:normal !important}}.select2-container{min-width:240px}@media (min-width: 768px){.select2-container{min-width:280px}}@media (min-width: 992px){.select2-container{min-width:320px}}.wf-page-title{display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:flex-start;justify-content:flex-start;margin-top:0.5rem}.wf-page-title>*{-webkit-flex-grow:0;flex-grow:0;min-width:0}.wf-page-title>*:first-child{-webkit-flex-grow:0;flex-grow:0;min-width:32px;-webkit-flex-basis:32px;flex-basis:32px;padding-right:0.25rem}.wf-page-title .wordfence-icon32{margin:0;margin-right:0.5rem}.wf-page-title h2{padding:0 !important}.wf-page-title .wfOnOffSwitch{-webkit-flex-basis:69px;flex-basis:69px;-webkit-flex-shrink:0;flex-shrink:0;margin-left:0.5rem}#howGetIPs-preview{color:#8c8c8c}#howGetIPs-preview strong{color:#666}#wf-notices{margin-top:15px}#wf-notices .wf-admin-notice{margin-left:0px;margin-right:0px}.wf-success-text,.wf-notice-text{display:inline-block;vertical-align:middle;line-height:1.3;font-size:16px;font-weight:bold;font-style:italic}.wf-notice{margin:12px 0;padding:8px;background-color:#ffffe0;border:1px solid #ffd975;border-width:1px 1px 1px 10px}.wf-notice-text{color:#6d798c}.wf-success{margin:12px 0;padding:8px;background-color:#ffffff;border:1px solid #16bc9b;border-width:1px 1px 1px 10px}.wf-success-text{color:#11967a}.wf-premium-callout{border:1px solid #00709e;background-color:#ffffff;padding:16px;margin:20px 0 0}.wf-premium-callout h3{margin:0 0 8px;color:#11967a}.wf-premium-callout ul{margin:8px 0;padding:0 0 0 15px}.wf-premium-callout ul li{list-style-type:disc;margin:0;padding:0}.wf-premium-callout .center{text-align:center;margin:0}.wf-premium-callout .button-primary{text-align:center;text-transform:uppercase;font-weight:bold;background-color:#00709e}#wfLiveTrafficOverlayAnchor::after{position:absolute;z-index:3002;top:0;right:0;width:0;height:0;background:rgba(241,241,241,0.6);content:'';opacity:0;-webkit-transition:opacity 0.5s,width 0.1s 0.5s,height 0.1s 0.5s;-o-transition:opacity 0.5s,width 0.1s 0.5s,height 0.1s 0.5s;transition:opacity 0.5s,width 0.1s 0.5s,height 0.1s 0.5s}.wordfenceLiveActivityPaused #wfLiveTrafficOverlayAnchor::after{width:100%;height:100%;opacity:1;-webkit-transition:opacity 0.5s;-o-transition:opacity 0.5s;transition:opacity 0.5s}#wordfenceLiveActivityDisabled{background:#fff;border-left:4px solid #ffb900;-webkit-box-shadow:0 1px 1px 0 rgba(0,0,0,0.1);box-shadow:0 1px 1px 0 rgba(0,0,0,0.1);margin-bottom:12px;padding:1px 13px}#wfLiveTrafficDisabledMessage{display:none;position:fixed;z-index:3003;left:0;width:100%;top:50%;transform:translateY(-50%);text-align:center;color:#666666;opacity:0;-webkit-transition:opacity 0.5s,width 0.1s 0.5s,height 0.1s 0.5s;-o-transition:opacity 0.5s,width 0.1s 0.5s,height 0.1s 0.5s;transition:opacity 0.5s,width 0.1s 0.5s,height 0.1s 0.5s}#wfLiveTrafficDisabledMessage h2{background-color:#FFF;overflow:hidden;border:1px solid #CCC;max-width:350px;margin:0 auto;padding:15px;font-size:2.0em}#wfLiveTrafficDisabledMessage h2 small{font-size:0.5em;font-weight:normal;margin-top:20px}.wordfenceLiveActivityPaused #wfLiveTrafficDisabledMessage{display:block;opacity:1;-webkit-transition:opacity 0.5s;transition:opacity 0.5s}.wf-live-activity{position:relative;margin:20px 0 10px 0;padding:0.75rem;box-sizing:border-box;background:#FFFCEF;box-shadow:0 0 0 1px rgba(153,155,135,0.5),0 1px 2px #e8f3e0}.wf-live-activity .wf-live-activity-inner{width:100%;box-sizing:border-box;position:relative}.wf-live-activity .wf-live-activity-inner .wf-live-activity-content{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:flex-start}.wf-live-activity .wf-live-activity-inner .wf-live-activity-content .wf-live-activity-title{color:#888888;font-size:0.85rem;font-weight:bold;padding-right:0.5rem}.wf-live-activity .wf-live-activity-inner .wf-live-activity-content .wf-live-activity-message{font-size:0.80rem;color:#000000}.wf-live-activity .wf-live-activity-inner .wf-live-activity-state{position:absolute;top:0px;right:0px;bottom:0px;left:0px;background:rgba(255,252,239,0.9);display:none;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:center;z-index:3001;-webkit-transition:opacity 0.5s,width 0.1s 0.5s,height 0.1s 0.5s;-o-transition:opacity 0.5s,width 0.1s 0.5s,height 0.1s 0.5s;transition:opacity 0.5s,width 0.1s 0.5s,height 0.1s 0.5s}.wordfenceLiveActivityPaused .wf-live-activity .wf-live-activity-inner .wf-live-activity-state{display:-ms-flexbox;display:flex;opacity:1;-webkit-transition:opacity 0.5s;-webkit-transition:opacity 0.5s;-o-transition:opacity 0.5s;transition:opacity 0.5s}.wordfence .wordfenceScanButton{margin:20px 0 20px 0}.wordfence .wordfenceScanButton input.button-wf-grey{background:#EFEFEF url(../images/button-grad-grey.png) repeat-x scroll left top;border-color:#EFEFEF}.wordfence .wordfenceScanButton table td{vertical-align:top}.wordfence .wordfenceScanButton .button-primary{text-align:center;text-transform:uppercase;font-weight:bold;background-color:#00709E;height:44px;line-height:44px;padding:0px 20px}table.wfSummaryParent{font-family:sans-serif;font-size:14px;color:#000;z-index:9}table.wfSummaryParent td{vertical-align:top;padding:0;margin:0}table.wfSummaryParent table.wfSummaryChild th{font-weight:bold;text-align:right;font-family:Georgia,Times New Roman,Times,serif;color:#000;padding:5px 10px 5px 0;border-top:1px solid #CCC}table.wfSummaryParent table.wfSummaryChild td{font-weight:normal;text-align:left;padding:5px 0 5px 0;border-top:1px solid #CCC}table.wfSummaryParent table.wfSC1 td{width:300px;padding:0 25px 10px 0}table.wfSummaryParent table.wfSC2 th{width:80px}table.wfSummaryParent table.wfSC2 td{width:100px}table.wfSummaryParent table.wfSC3 th{width:80px}table.wfSummaryParent table.wfSC3 td{width:250px}table.wfSummaryParent th.wfHead{font-size:22px;font-family:Georgia,Times New Roman,Times,serif;font-style:italic;color:#555;font-weight:bold;text-align:left;padding:20px 0 20px 0;-webkit-font-smoothing:antialiased}.wf-issues-table{table-layout:fixed;width:100%}div.wfIssue{width:100%}div.wfIssue table.wfIssue td{padding:2px;margin:0;border-width:0;text-align:left;width:100%}div.wfIssue table.wfIssue th{padding:2px;margin:0;font-weight:bold;text-align:left;color:#777;white-space:nowrap}div.wfIssue table.wfIssueLinks td{border-width:0;text-align:left;padding-right:10px}div.wfIssue h2{margin:0 0 5px 0;padding:0;font-size:0.9rem}@media (min-width: 768px){div.wfIssue h2{font-size:1.05rem}}.wfIssueOptions{border-top:1px solid #CCC;padding:10px}.wfIssueOptions h3{font-size:0.8rem;margin:0}@media (min-width: 768px){.wfIssueOptions h3{display:inline-block}}.wfIssueOptions ul{margin-bottom:0;padding-left:0;list-style:none;display:-webkit-flex;display:flex;-webkit-align-items:flex-start;align-items:flex-start;-webkit-align-content:flex-start;align-content:flex-start;-webkit-justify-content:flex-start;justify-content:flex-start;-webkit-flex-wrap:nowrap;flex-wrap:nowrap;-webkit-flex-direction:column;flex-direction:column}@media (min-width: 768px){.wfIssueOptions ul{-webkit-flex-direction:row;flex-direction:row;-webkit-align-items:center;align-items:center}}.wfIssueOptions ul>li>a{position:relative;display:block;padding:8px 12px/2}.wfIssueOptions ul>li>a:hover,.wfIssueOptions ul>li>a:focus{text-decoration:none;background-color:#e2e2e2}.wfIssueOptions a{margin-left:10px}.wfIssueOptions strong{float:left;display:block;width:60px}.wfIssueOptions p{margin:6px 0px 0px}.wfProbSev1,.wfProbSev2,.wfAjaxLight128,.wfResolved{width:128px;height:128px;border:0;margin:0 auto;background-repeat:no-repeat;background-position:0 0;text-decoration:none;display:block}.wfProbSev1{background-image:url(../images/icons/error128.png)}.wfProbSev2{background-image:url(../images/icons/warning128.png)}.wfResolved{background-image:url(../images/icons/tick128.png)}.wfIssuesContainer{width:100%;display:none}.wfIssuesContainer p{max-width:550px}.wfALogTime{color:#999}.wfALogMailLink,.wfALogViewLink{display:block;position:absolute;padding:0 0 0 18px;margin:0;right:10px;top:0;background-repeat:no-repeat;font-weight:normal}.wfALogMailLink{background-image:url(../images/icons/email_go.png)}.wfALogViewLink{background-image:url(../images/icons/magnifier.png)}#wfActivity{position:relative}.consoleHead{position:relative;padding:0 0 0 3px;font-weight:bold;width:100%}.consoleHeadText{margin-bottom:4px;font-size:18px;font-family:Georgia,Times New Roman,Times,serif;color:#555;font-weight:bold;-webkit-font-smoothing:antialiased}.consoleFooter{position:relative}.consoleOuter{width:100%}.consoleInner{height:116px;overflow:auto;z-index:1}.bevelDiv1{border:1px solid #EFEFEF}.bevelDiv2{border:1px solid #AAA}.bevelDiv3{background-color:#ffffed;padding:5px;font-family:Helvetica Neue,Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased}.wfSecure{color:#0A0;font-weight:bold}.wfSummaryLine{display:-webkit-flex;display:flex;-webkit-align-items:stretch;align-items:stretch;-webkit-align-content:flex-start;align-content:flex-start;-webkit-justify-content:space-between;justify-content:space-between;-webkit-flex-wrap:nowrap;flex-wrap:nowrap;-webkit-flex-direction:column;flex-direction:column}@media (min-width: 768px){.wfSummaryLine{-webkit-flex-direction:row;flex-direction:row}}.wfSummaryLine .wfSummaryDate{padding-left:3px}.wfSummaryLine .wfSummaryMsg{padding-left:3px;-webkit-flex-grow:1;flex-grow:1;min-width:0}.wfSummaryLoading{width:16px;height:11px;background-image:url("../images/icons/ajaxScan.gif")}.wfSummaryBad,.wfSummaryErr{color:#930000}.wfSummaryOK{color:#11967a}.wfClear{content:".";display:block;height:0;width:0;line-height:0;clear:both;visibility:hidden}.wfSummaryFinal{-webkit-font-smoothing:antialiased;font-weight:bold;color:#555}.wfStartScanButton{text-align:center}.wordfenceScanHelp{border:1px solid #CCC;padding:4px}.wf-scan-no-issues{font-size:1.25rem;color:#11967a}.wf-scan-severity{position:relative;width:10px}@media (min-width: 768px){.wf-scan-severity{width:144px}}.wf-scan-severity-1,.wf-scan-severity-2{position:absolute;top:0px;right:0px;bottom:0px;left:0px}.wf-scan-severity-1{background-color:#c10000}.wf-scan-severity-2{background-color:#ffd10a}.scan-schedule{border-collapse:collapse;border-spacing:0}.scan-schedule tr:first-of-type th{padding-top:0}.scan-schedule td{padding:0}.scan-schedule th{padding:1.5rem 0.5rem 0.75rem 0;font-size:1rem;text-align:left}@media (min-width: 768px){.scan-schedule th{padding:0 0.5rem 0 0;font-size:0.8125rem;text-align:center}}.next-scan{font-size:1em;display:block;position:relative;width:7em;height:7em;background-color:#fff;border-radius:0.6em;box-shadow:0 1px 0 rgba(189,189,189,0.6);overflow:hidden}.next-scan *{display:block;width:100%;font-size:1em;font-weight:bold;font-style:normal;text-align:center}.next-scan strong{position:absolute;top:0;padding:0.4em 0;color:#fff;background-color:#00709E;box-shadow:0 2px 0 #00709E}.next-scan em{position:absolute;bottom:0.3em;color:#00709E}.next-scan span{width:100%;font-size:2.8em;padding-top:1.15em;color:#2f2f2f}.schedule-times-wrapper{display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:space-between;justify-content:space-between;margin-top:1rem}@media (min-width: 768px){.schedule-times-wrapper{margin-top:0.25rem}}.schedule-times-wrapper:first-of-type{margin-top:0}@media (min-width: 768px){.schedule-times-wrapper:first-of-type{margin-top:1rem}}.schedule-times-wrapper>*{-webkit-flex-grow:1;flex-grow:1;min-width:0}.schedule-times-wrapper>*:first-child{-webkit-flex-grow:0;flex-grow:0;min-width:initial;padding-right:0.25rem}.schedule-times{margin-bottom:0;margin-top:0;padding-left:0;list-style:none;clear:both}.schedule-times>li{float:left;position:relative;display:block;margin:0px 0px 2px 0px}.schedule-times>li.disabled>a{color:#777}.schedule-times>li.disabled>a:hover,.schedule-times>li.disabled>a:focus{color:#777;text-decoration:none;background-color:transparent;cursor:not-allowed}.schedule-times>li>a{text-decoration:none;border:1px solid #e2e2e2;border-radius:4px;position:relative;display:block;padding:8px 12px}.schedule-times>li>a:hover,.schedule-times>li>a:focus{text-decoration:none;background-color:#e2e2e2}.schedule-times>li+li{margin-left:2px}.schedule-times>li.active>a,.schedule-times>li.active>a:hover,.schedule-times>li.active>a:focus{color:#fff;background-color:#00709e;border-color:#00709e}.schedule-times>li.text-only{position:relative;display:block;padding:8px 12px}.schedule-times>li>a>img{max-width:none}#wf-lt-listings .wfActEvent{padding-left:15px;border-left:5px solid #cccccc}#wf-lt-listings .wfActEvent.wfHuman{border-left:5px solid #16bc9b}#wf-lt-listings .wfActEvent.wfActionBlocked{border-left:5px solid #d03935}#wf-lt-listings .wfActEvent.wfNotice{border-left:5px solid #c10000}#wf-lt-listings .wfActEvent.wfWarning,#wf-lt-listings .wfActEvent.wf404{border-left:5px solid #ffd10a}#wf-lt-listings .wfActEvent:hover{background-color:#fff9e9 !important}.wfActEvent.wf-live-traffic-filter{padding-left:0;padding-right:0;display:-webkit-flex;display:flex;-webkit-align-items:baseline;align-items:baseline;-webkit-align-content:flex-start;align-content:flex-start;-webkit-justify-content:flex-start;justify-content:flex-start;-webkit-flex-direction:column;flex-direction:column}@media (min-width: 768px){.wfActEvent.wf-live-traffic-filter{-webkit-flex-direction:row;flex-direction:row}}.wfActEvent.wf-live-traffic-filter h2{margin:0;padding-bottom:0.5rem}@media (min-width: 768px){.wfActEvent.wf-live-traffic-filter h2{padding-bottom:0;padding-right:0.5rem}}#wf-lt-advanced-filters{padding-left:0;padding-right:0;overflow:hidden}.wf-live-traffic-filter-detail{display:-webkit-flex;display:flex;-webkit-align-items:flex-start;align-items:flex-start;-webkit-align-content:flex-start;align-content:flex-start;-webkit-justify-content:flex-start;justify-content:flex-start;-webkit-flex-direction:column;flex-direction:column}@media (min-width: 768px){.wf-live-traffic-filter-detail{-webkit-flex-direction:row;flex-direction:row}.wf-live-traffic-filter-detail *{-webkit-flex-grow:1;flex-grow:1}}.wf-live-traffic-filter-detail .wf-live-traffic-filter-item{display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-align-content:flex-start;align-content:flex-start;-webkit-justify-content:flex-start;justify-content:flex-start;-webkit-flex-direction:row;flex-direction:row}.wf-live-traffic-filter-detail .wf-live-traffic-filter-item .wf-live-traffic-filter-remove{margin-left:0.5rem;font-size:1.5rem;color:#333}.wf-live-traffic-filter-detail .wf-live-traffic-filter-item .wf-live-traffic-filter-item-parameters{display:-webkit-flex;display:flex;-webkit-align-items:flex-start;align-items:flex-start;-webkit-align-content:flex-start;align-content:flex-start;-webkit-justify-content:flex-start;justify-content:flex-start;-webkit-flex-direction:column;flex-direction:column}.wf-live-traffic-filter-detail .wf-live-traffic-filter-item .wf-live-traffic-filter-item-parameters div{padding:0.25rem 0}.wf-live-traffic-filter-detail .wf-live-traffic-filter-item .wf-live-traffic-filter-item-parameters select{font-size:0.75rem !important}@media (min-width: 768px){.wf-live-traffic-filter-detail .wf-live-traffic-filter-item .wf-live-traffic-filter-item-parameters{-webkit-flex-direction:row;flex-direction:row;-webkit-align-items:center;align-items:center}.wf-live-traffic-filter-detail .wf-live-traffic-filter-item .wf-live-traffic-filter-item-parameters div{padding:0}.wf-live-traffic-filter-detail .wf-live-traffic-filter-item .wf-live-traffic-filter-item-parameters select{font-size:1rem !important}}.wf-filtered-traffic{display:-webkit-flex;display:flex;-webkit-align-items:flex-start;align-items:flex-start;-webkit-align-content:flex-start;align-content:flex-start;-webkit-justify-content:flex-start;justify-content:flex-start;-webkit-flex-direction:column;flex-direction:column}.wf-filtered-traffic>*{display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-align-content:flex-start;align-content:flex-start;-webkit-justify-content:flex-start;justify-content:flex-start;-webkit-flex-direction:row;flex-direction:row;padding:10px 20px;border-bottom:1px solid #CCC;width:100%;box-sizing:border-box}.wf-filtered-traffic>*>*:first-child{-webkit-flex-grow:1;flex-grow:1}.wf-filtered-traffic .wf-filtered-traffic-hits{font-size:1.75rem;color:#999999;margin-top:10px}#wf-live-traffic{position:relative;overflow:visible}#wf-live-traffic-legend{white-space:nowrap;background-color:#fff;border:1px solid #cccccc;padding:12px}#wf-live-traffic-legend.sticky{position:fixed;top:10px;right:auto;left:10px;z-index:2000}@media (min-width: 768px){#wf-live-traffic-legend.sticky{top:42px;left:182px}}#wf-live-traffic-legend-placeholder{display:none;padding:12px}#wf-live-traffic-legend-placeholder.sticky{display:block}#wf-live-traffic-legend ul{margin:0;padding:0}#wf-live-traffic-legend ul:before,#wf-live-traffic-legend ul:after{content:" ";display:table}#wf-live-traffic-legend ul:after{clear:both}#wf-live-traffic-legend ul li{margin:0;padding:0;position:relative;float:left;font-size:11.5px}@media (min-width: 768px){#wf-live-traffic-legend ul li{font-size:13px}}#wf-live-traffic-legend ul li+li{margin-left:8px}#wf-live-traffic-legend ul li:before{content:'';display:block;float:left;margin:3px 6px 0 0;width:12px;height:12px;background-color:#CCCCCC}#wf-live-traffic-legend ul li.wfHuman:before{background-color:#16bc9b}#wf-live-traffic-legend ul li.wfNotice:before{background-color:#ffd10a}#wf-live-traffic-legend ul li.wfBlocked:before{background-color:#d03935}.wfTimeAgo{font-family:Georgia,Times New Roman,Times,serif;color:#999;font-weight:bold;font-style:italic}.wfActEvent{border-bottom:1px solid #CCC;padding:10px 20px;overflow:auto}.wf-pad-small{margin:8px 0}#wf-lt-listings{margin:0 0 0}#wf-lt-listings a{cursor:pointer;text-decoration:underline}#wf-lt-listings a.button,#wf-lt-listings a.wf-btn{text-decoration:none}.wfActionBlocked{background-color:#fff6f6}[class*="span"]{float:left;min-height:1px;margin-left:30px}.highlighted{-webkit-animation-duration:1s;animation-duration:1s;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}@-moz-keyframes highlighted{0%{opacity:0;background-color:#ffeaa0}100%{opacity:1;background-color:#ffffff}}@-webkit-keyframes highlighted{0%{opacity:0;background-color:#ffeaa0}100%{opacity:1;background-color:#ffffff}}@keyframes highlighted{0%{opacity:0;background-color:#ffeaa0}100%{opacity:1;background-color:#ffffff}}@-moz-keyframes highlightedBlocked{0%{opacity:0;background-color:#ffeaa0}100%{opacity:1;background-color:#fff6f6}}@-webkit-keyframes highlightedBlocked{0%{opacity:0;background-color:#ffeaa0}100%{opacity:1;background-color:#fff6f6}}@keyframes highlightedBlocked{0%{opacity:0;background-color:#ffeaa0}100%{opacity:1;background-color:#fff6f6}}.highlighted{-webkit-animation-name:highlighted;animation-name:highlighted}.highlighted.wfActionBlocked{-webkit-animation-name:highlightedBlocked;animation-name:highlightedBlocked}#wf-lt-preset-filters{min-width:250px}#wf-lt-advanced-filters>table{width:100%}#wf-lt-advanced-filters>table>tr>td{vertical-align:top}.wf-lt-url{white-space:nowrap}table.block-ranges-table{border-collapse:collapse;margin:10px 0 0}table.block-ranges-table tr td{border:1px solid #CCC;border-width:1px 0;padding:10px 0 12px 0}#input-wafStatus,#input-wafStatus option,.select2-container--default{font-size:0.9rem}@media (min-width: 768px){#input-wafStatus,#input-wafStatus option,.select2-container--default{font-size:1.0rem}}@media (min-width: 992px){#input-wafStatus,#input-wafStatus option,.select2-container--default{font-size:1.125rem}}.wafStatus-enabled,.wafStatus-learning-mode,.wafStatus-disabled,.wafStatus-enabled.select2-container--default .select2-selection--single .select2-selection__rendered,.wafStatus-learning-mode.select2-container--default .select2-selection--single .select2-selection__rendered,.wafStatus-disabled.select2-container--default .select2-selection--single .select2-selection__rendered{color:#ffffff}#waf-config-form .waf-config-label{font-size:1.3em}#waf-config-form .select2-container--default .select2-selection--single{padding:4px;text-shadow:0 0 3px #000000;font-weight:bold;border-radius:3px}#waf-config-form .select2-container .select2-selection--single{height:auto}.wafStatus-enabled.select2-container--default .select2-selection--single{background-color:#11967a;border-color:#073a2f}.wafStatus-learning-mode.select2-container--default .select2-selection--single{background-color:#ffd10a;border-color:#a38400}.wafStatus-disabled.select2-container--default .select2-selection--single,.wafStatus-disabled.select2-container--default.select2-container--disabled .select2-selection--single,.wafStatus-learning-mode.select2-container--default.select2-container--disabled .select2-selection--single,.wafStatus-enabled.select2-container--default.select2-container--disabled .select2-selection--single{background-color:#c10000;border-color:#5b0000}#waf-config-form .select2-container--default .select2-selection--single .select2-selection__arrow{height:100%;top:0}.wafStatus-enabled.select2-container--default .select2-selection--single .select2-selection__arrow b,.wafStatus-learning-mode.select2-container--default .select2-selection--single .select2-selection__arrow b,.wafStatus-disabled.select2-container--default .select2-selection--single .select2-selection__arrow b{border-color:#ffffff transparent transparent}.wafStatus-enabled.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b,.wafStatus-learning-mode.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b,.wafStatus-disabled.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #ffffff}.wafStatus-description{display:none;max-width:500px;font-style:italic;font-size:14px;line-height:1.3}.whitelist-table-container{overflow-x:auto}table.whitelist-table .whitelist-edit{display:none}table.whitelist-table .edit-mode .whitelist-display{display:none}table.whitelist-table .edit-mode .whitelist-edit{display:block}table.whitelist-table .edit-mode span.whitelist-edit,table.whitelist-table .edit-mode input.whitelist-edit{display:inline}.wf-bulk-action{margin:12px 0}tr.wf-table-filters input{max-width:120px}#wordfenceRightRail img{max-width:100%}#wordfenceRightRail ul{list-style-type:none;margin:0;margin-top:15px}#wordfenceRightRail .center{text-align:center}#wordfenceRightRail .button-primary{text-align:center;text-transform:uppercase;font-weight:bold;background-color:#00709e}.wordfenceRightRail img{width:100%}.wordfenceRightRailLiveTraffic,.wordfenceRightRailOptions,.wordfenceRightRailDiagnostics{margin-left:1055px}.wordfenceRightRailBlockedIPs,.wordfenceRightRailWAF,.wordfenceRightRailCountryBlocking,.wordfenceRightRailScanSchedule{margin-left:950px}.wordfenceRightRail ul{list-style-type:none;margin:0}.wordfenceRightRail .center{text-align:center}.wordfenceRightRail .button-primary{text-align:center;text-transform:uppercase;font-weight:bold;background-color:#00709e}.wordfenceWrap.wordfence-community{min-height:760px}#wfTwoFactorQRCodeTable{width:175px;height:175px;margin:0 auto}@media (min-width: 500px){#wfTwoFactorQRCodeTable{width:256px;height:256px}}#wfTwoFactorRecoveryCodes{list-style-type:none}#wfTwoFactorRecoveryCodes li{font-family:monospace;text-align:center}#wfTwoFactorDownload .dashicons{line-height:26px}.wf-twofactor-delete{font-size:1.5rem}.wf-twofactor-delete a{text-decoration:none;color:#333}.wf-table.wf-table-twofactor>tbody>tr>td{vertical-align:middle}.wf-form-twofactor{max-width:400px}.wf-form-twofactor .wf-radio label{padding-left:0}fieldset{padding:0;margin:0;border:0;min-width:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:bold}label.wf-plain{font-weight:normal}label.wf-control-label.wf-disabled{pointer-events:none}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="radio"],input[type="checkbox"]{margin-top:1px \9;line-height:normal}input[type="file"]{display:block}input[type="range"]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857;color:#555}.wf-form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border-color ease-in-out 0.15s,box-shadow ease-in-out 0.15s;-o-transition:border-color ease-in-out 0.15s,box-shadow ease-in-out 0.15s;transition:border-color ease-in-out 0.15s,box-shadow ease-in-out 0.15s}.wf-form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6)}.wf-form-control::-moz-placeholder{color:#999;opacity:1}.wf-form-control:-ms-input-placeholder{color:#999}.wf-form-control::-webkit-input-placeholder{color:#999}.wf-form-control::-ms-expand{border:0;background-color:transparent}.wf-form-control[disabled],.wf-form-control[readonly],fieldset[disabled] .wf-form-control{background-color:#e2e2e2;opacity:1}.wf-form-control[disabled],.wf-form-control[readonly],fieldset[disabled] .wf-form-control{cursor:not-allowed;pointer-events:none}textarea.wf-form-control{height:auto}input[type="search"]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio: 0){input[type="date"].wf-form-control,input[type="time"].wf-form-control,input[type="datetime-local"].wf-form-control,input[type="month"].wf-form-control{line-height:34px}input[type="date"].wf-input-sm,.wf-input-group-sm input[type="date"],input[type="time"].wf-input-sm,.wf-input-group-sm input[type="time"],input[type="datetime-local"].wf-input-sm,.wf-input-group-sm input[type="datetime-local"],input[type="month"].wf-input-sm,.wf-input-group-sm input[type="month"]{line-height:30px}input[type="date"].wf-input-lg,.wf-input-group-lg input[type="date"],input[type="time"].wf-input-lg,.wf-input-group-lg input[type="time"],input[type="datetime-local"].wf-input-lg,.wf-input-group-lg input[type="datetime-local"],input[type="month"].wf-input-lg,.wf-input-group-lg input[type="month"]{line-height:46px}}.wf-form-group{margin-bottom:8px}.wf-form-group.wf-sub-group label{color:#666666;font-weight:normal;padding-left:20px}.wf-form-group.wf-focus{border-left:4px solid #11967a;padding-bottom:8px;background-color:#e5e5e5}.wf-form-group.wf-focus label{margin-left:-4px}.wf-radio,.wf-checkbox{position:relative;display:block;margin-top:10px;margin-bottom:10px}.wf-radio label,.wf-checkbox label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:normal;cursor:pointer}.wf-radio input[type="radio"],.wf-radio-inline input[type="radio"],.wf-checkbox input[type="checkbox"],.wf-checkbox-inline input[type="checkbox"]{margin-top:4px \9}.wf-radio-offset{padding-left:29px}@media (min-width: 768px){.wf-radio-offset{padding-left:20px}}.wf-radio+.wf-radio,.wf-checkbox+.wf-checkbox{margin-top:-5px}.wf-radio-inline,.wf-checkbox-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;vertical-align:middle;font-weight:normal;cursor:pointer}.wf-radio-inline+.wf-radio-inline,.wf-checkbox-inline+.wf-checkbox-inline{margin-top:0;margin-left:10px}input[type="radio"][disabled],input[type="radio"][readonly],input[type="radio"].wf-disabled,fieldset[disabled] input[type="radio"],input[type="checkbox"][disabled],input[type="checkbox"][readonly],input[type="checkbox"].wf-disabled,fieldset[disabled] input[type="checkbox"]{cursor:not-allowed;pointer-events:none}.wf-radio-inline.wf-disabled,fieldset[disabled] .wf-radio-inline,.wf-checkbox-inline.wf-disabled,fieldset[disabled] .wf-checkbox-inline{cursor:not-allowed}.wf-radio.wf-disabled label,fieldset[disabled] .wf-radio label,.wf-checkbox.wf-disabled label,fieldset[disabled] .wf-checkbox label{cursor:not-allowed;pointer-events:none}.wf-form-control-static{padding-top:7px;padding-bottom:7px;margin:0;line-height:1}.wf-form-control-static.wf-input-lg,.wf-form-control-static.wf-input-sm{padding-left:0;padding-right:0}.wf-input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.wf-input-sm{height:30px;line-height:30px}textarea.wf-input-sm,select[multiple].wf-input-sm{height:auto}.wf-form-group-sm .wf-form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.wf-form-group-sm select.wf-form-control{height:30px;line-height:30px}.wf-form-group-sm textarea.wf-form-control,.wf-form-group-sm select[multiple].wf-form-control{height:auto}.wf-form-group-sm .wf-form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.wf-input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.33333;border-radius:6px}select.wf-input-lg{height:46px;line-height:46px}textarea.wf-input-lg,select[multiple].wf-input-lg{height:auto}.wf-form-group-lg .wf-form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.33333;border-radius:6px}.wf-form-group-lg select.wf-form-control{height:46px;line-height:46px}.wf-form-group-lg textarea.wf-form-control,.wf-form-group-lg select[multiple].wf-form-control{height:auto}.wf-form-group-lg .wf-form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.33333}.wf-has-feedback{position:relative}.wf-has-feedback .wf-form-control{padding-right:42.5px}.wf-form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.wf-input-lg+.wf-form-control-feedback,.wf-input-group-lg+.wf-form-control-feedback,.wf-form-group-lg .wf-form-control+.wf-form-control-feedback{width:46px;height:46px;line-height:46px}.wf-input-sm+.wf-form-control-feedback,.wf-input-group-sm+.wf-form-control-feedback,.wf-form-group-sm .wf-form-control+.wf-form-control-feedback{width:30px;height:30px;line-height:30px}.wf-has-success .wf-help-block,.wf-has-success .wf-control-label,.wf-has-success .wf-radio,.wf-has-success .wf-checkbox,.wf-has-success .wf-radio-inline,.wf-has-success .wf-checkbox-inline,.wf-has-success.wf-radio label,.wf-has-success.wf-checkbox label,.wf-has-success.wf-radio-inline label,.wf-has-success.wf-checkbox-inline label{color:#3c763d}.wf-has-success .wf-form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.wf-has-success .wf-form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #67b168}.wf-has-success .wf-input-group-addon{color:#3c763d;border-color:#3c763d;background-color:#dff0d8}.wf-has-success .wf-form-control-feedback{color:#3c763d}.wf-has-warning .wf-help-block,.wf-has-warning .wf-control-label,.wf-has-warning .wf-radio,.wf-has-warning .wf-checkbox,.wf-has-warning .wf-radio-inline,.wf-has-warning .wf-checkbox-inline,.wf-has-warning.wf-radio label,.wf-has-warning.wf-checkbox label,.wf-has-warning.wf-radio-inline label,.wf-has-warning.wf-checkbox-inline label{color:#8a6d3b}.wf-has-warning .wf-form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.wf-has-warning .wf-form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #c0a16b}.wf-has-warning .wf-input-group-addon{color:#8a6d3b;border-color:#8a6d3b;background-color:#fcf8e3}.wf-has-warning .wf-form-control-feedback{color:#8a6d3b}.wf-has-error .wf-help-block,.wf-has-error .wf-control-label,.wf-has-error .wf-radio,.wf-has-error .wf-checkbox,.wf-has-error .wf-radio-inline,.wf-has-error .wf-checkbox-inline,.wf-has-error.wf-radio label,.wf-has-error.wf-checkbox label,.wf-has-error.wf-radio-inline label,.wf-has-error.wf-checkbox-inline label{color:#a94442}.wf-has-error .wf-form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.wf-has-error .wf-form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483}.wf-has-error .wf-input-group-addon{color:#a94442;border-color:#a94442;background-color:#f2dede}.wf-has-error .wf-form-control-feedback{color:#a94442}.wf-has-feedback label ~ .wf-form-control-feedback{top:25px}.wf-has-feedback label.wf-sr-only ~ .wf-form-control-feedback{top:0}.wf-help-block{display:block;margin-top:5px;color:#737373}@media (min-width: 768px){.wf-form-inline .wf-form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.wf-form-inline .wf-form-control{display:inline-block;width:auto;vertical-align:middle}.wf-form-inline .wf-form-control-static{display:inline-block}.wf-form-inline .wf-input-group{display:inline-table;vertical-align:middle}.wf-form-inline .wf-input-group .wf-input-group-addon,.wf-form-inline .wf-input-group .wf-input-group-btn,.wf-form-inline .wf-input-group .wf-form-control{width:auto}.wf-form-inline .wf-input-group>.wf-form-control{width:100%}.wf-form-inline .wf-control-label{margin-bottom:0;vertical-align:middle}.wf-form-inline .wf-radio,.wf-form-inline .wf-checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.wf-form-inline .wf-radio label,.wf-form-inline .wf-checkbox label{padding-left:0}.wf-form-inline .wf-radio input[type="radio"],.wf-form-inline .wf-checkbox input[type="checkbox"]{position:relative;margin-left:0}.wf-form-inline .wf-has-feedback .wf-form-control-feedback{top:0}}.wf-form-horizontal .wf-radio,.wf-form-horizontal .wf-checkbox,.wf-form-horizontal .wf-radio-inline,.wf-form-horizontal .wf-checkbox-inline{margin-top:0;margin-bottom:0;padding-top:7px}.wf-form-horizontal .wf-radio,.wf-form-horizontal .wf-checkbox{min-height:27px}.wf-form-horizontal .wf-form-group{margin-left:-15px;margin-right:-15px}.wf-form-horizontal .wf-form-group:before,.wf-form-horizontal .wf-form-group:after{content:" ";display:table}.wf-form-horizontal .wf-form-group:after{clear:both}@media (min-width: 768px){.wf-form-horizontal .wf-control-label{text-align:right;margin-bottom:0;padding-top:7px}}.wf-form-horizontal .wf-has-feedback .wf-form-control-feedback{right:15px}@media (min-width: 768px){.wf-form-horizontal .wf-form-group-lg .wf-control-label{padding-top:11px;font-size:18px}}@media (min-width: 768px){.wf-form-horizontal .wf-form-group-sm .wf-control-label{padding-top:6px;font-size:12px}}.wf-dashboard-item{position:relative;margin:0 auto 1rem;padding:0 1rem;box-sizing:border-box;background:#fff;box-shadow:0 0 0 1px rgba(200,215,225,0.5),0 1px 2px #e9eff3}.wf-dashboard-item .wf-dashboard-item-inner{min-height:44px;padding:1rem 0;width:100%;box-sizing:border-box;display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:space-between;justify-content:space-between;position:relative}.wf-dashboard-item .wf-dashboard-item-inner .wf-dashboard-item-content{max-width:75%}.wf-dashboard-item .wf-dashboard-item-inner .wf-dashboard-item-content .wf-dashboard-item-title{font-size:0.75rem;width:100%}.wf-dashboard-item .wf-dashboard-item-inner .wf-dashboard-item-content .wf-dashboard-item-subtitle{margin-top:.125rem;margin-bottom:.125rem;font-size:.575rem;color:#4f748e}.wf-dashboard-item .wf-dashboard-item-inner .wf-dashboard-item-action{position:absolute;top:0;right:0;height:100%;background:none;border:0;outline:0;width:48px;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;vertical-align:middle;transition:transform 0.15s cubic-bezier(0.175, 0.885, 0.32, 1.275),color 0.2s ease-in}.wf-dashboard-item .wf-dashboard-item-inner .wf-dashboard-item-action.wf-dashboard-item-action-text{width:auto}.wf-dashboard-item .wf-dashboard-item-inner .wf-dashboard-item-action.wf-dashboard-item-action-text.wf-dashboard-item-action-text-success{color:#11967a}.wf-dashboard-item .wf-dashboard-item-inner .wf-dashboard-item-action.wf-dashboard-item-action-text.wf-dashboard-item-action-text-warning{color:#930000}.wf-dashboard-item .wf-dashboard-item-inner .wf-dashboard-item-action.wf-dashboard-item-action-text.wf-dashboard-item-action-text-warning a{color:#930000}.wf-dashboard-item .wf-dashboard-item-inner .wf-dashboard-item-action .wf-dashboard-item-action-chevron{background:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj4KPHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJjaGV2cm9uLW9iamVjdCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiCgkgd2lkdGg9IjI0cHgiIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAwIDI0IDI0IiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAyNCAyNCIKCSB4bWw6c3BhY2U9InByZXNlcnZlIj4KPHBhdGggaWQ9ImNoZXZyb24iIGQ9Ik0yMCA5bC04IDgtOC04IDEuNDE0LTEuNDE0TDEyIDE0LjE3Mmw2LjU4Ni02LjU4NiIvPgo8L3N2Zz4K");background-repeat:no-repeat;background-position:center center;width:24px;height:24px;fill:#87a6bc}.wf-dashboard-item .wf-dashboard-item-inner .wf-dashboard-item-action .wf-dashboard-item-action-checkbox{background-image:url(../images/checkbox.png);background-repeat:no-repeat;background-position:left center;width:29px;height:29px}.wf-dashboard-item .wf-dashboard-item-inner .wf-dashboard-item-action .wf-dashboard-item-action-checkbox.checked{background-position:right center}.wf-dashboard-item .wf-dashboard-item-extra{display:none;margin:0 -1rem;padding:0 1rem}.wf-dashboard-item .wf-dashboard-item-extra .wf-dashboard-item-list{margin:0 -1rem;padding:0;list-style:none}.wf-dashboard-item .wf-dashboard-item-extra .wf-dashboard-item-list>li{display:block;min-height:44px;padding:0 1rem;margin:0;border-top:1px solid #eeeeee;box-sizing:border-box;display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:space-between;justify-content:space-between}.wf-dashboard-item .wf-dashboard-item-extra .wf-dashboard-item-list>li>*:first-child{-webkit-flex-grow:1;flex-grow:1;min-width:0}.wf-dashboard-item .wf-dashboard-item-extra .wf-dashboard-item-list.wf-dashboard-item-list-horizontal{box-sizing:border-box;margin-top:-1px;display:-webkit-flex;display:flex;-webkit-align-items:stretch;align-items:stretch;-webkit-align-content:flex-start;align-content:flex-start;-webkit-justify-content:space-between;justify-content:space-between;-webkit-flex-wrap:wrap;flex-wrap:wrap}.wf-dashboard-item .wf-dashboard-item-extra .wf-dashboard-item-list.wf-dashboard-item-list-horizontal>li{-webkit-flex-grow:1;flex-grow:1;-webkit-flex-basis:100%;flex-basis:100%;border-left:1px solid #eeeeee}@media (min-width: 768px){.wf-dashboard-item .wf-dashboard-item-extra .wf-dashboard-item-list.wf-dashboard-item-list-horizontal>li{-webkit-flex-basis:50%;flex-basis:50%}}@media (min-width: 992px){.wf-dashboard-item .wf-dashboard-item-extra .wf-dashboard-item-list.wf-dashboard-item-list-horizontal>li{-webkit-flex-basis:25%;flex-basis:25%}}.wf-dashboard-item .wf-dashboard-item-extra .wf-dashboard-item-list.wf-dashboard-item-list-horizontal>*:first-child{border-left:0}@media (min-width: 768px){.wf-dashboard-item .wf-dashboard-item-extra .wf-dashboard-item-list.wf-dashboard-item-list-horizontal.wf-dashboard-item-list-equal>li{max-width:50%}}@media (min-width: 992px){.wf-dashboard-item .wf-dashboard-item-extra .wf-dashboard-item-list.wf-dashboard-item-list-horizontal.wf-dashboard-item-list-equal>li{max-width:25%}}.wf-dashboard-item .wf-dashboard-item-extra .wf-dashboard-item-list .wf-dashboard-item-list-state{text-align:center}@media (min-width: 1200px){.wf-dashboard-item .wf-dashboard-item-extra .wf-dashboard-item-list .wf-dashboard-item-list-state{text-align:left}}.wf-dashboard-item .wf-dashboard-item-extra .wf-dashboard-item-list .wf-dashboard-item-list-state-enabled .fa{color:#11967a}.wf-dashboard-item .wf-dashboard-item-extra .wf-dashboard-item-list .wf-dashboard-item-list-state-disabled .fa{color:#525355}.wf-dashboard-item .wf-dashboard-item-extra .wf-dashboard-item-list .wf-dashboard-item-list-state-premium{color:#9f9fa0}.wf-dashboard-item .wf-dashboard-item-extra .wf-dashboard-item-list .wf-dashboard-item-list-dismiss{padding-left:2rem;font-size:1.25rem}.wf-dashboard-item .wf-dashboard-item-extra .wf-dashboard-item-list .wf-dashboard-item-list-dismiss a{color:#525355}.wf-dashboard-item .wf-dashboard-item-extra .wf-dashboard-item-labeled-count{box-sizing:border-box;display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:space-between;justify-content:space-between;-webkit-flex-direction:column;flex-direction:column}.wf-dashboard-item .wf-dashboard-item-extra .wf-dashboard-item-labeled-count-count{font-size:3rem;line-height:3rem;color:#9f9fa0;padding:1rem}.wf-dashboard-item .wf-dashboard-item-extra .wf-dashboard-item-labeled-count-label{font-size:0.75rem;color:#9f9fa0;padding:0 1rem 1rem 1rem}.wf-dashboard-item.active .wf-dashboard-item-extra{display:block}.wf-dashboard-item.wf-dashboard-item-left .wf-dashboard-item-content{margin-left:48px}.wf-dashboard-item.wf-dashboard-item-left .wf-dashboard-item-action{right:auto;left:0px}.wf-dashboard-item.disabled .wf-dashboard-item-content .wf-dashboard-item-title{color:#aaaaaa}.wf-dashboard-item.disabled .wf-dashboard-item-content .wf-dashboard-item-subtitle{color:#8ea6be}.wf-notifications-empty{font-size:0.9rem;color:#9f9fa0}.wf-dashboard-graph-wrapper{width:100%}.wf-dashboard-badge{display:inline-block;min-width:10px;padding:3px 7px;margin-left:0.5rem;font-size:12px;font-weight:bold;color:#fff;line-height:1;vertical-align:middle;white-space:nowrap;text-align:center;background-color:#fcb214;border-radius:10px}.wf-dashboard-badge:empty{display:none}.wf-btn .wf-dashboard-badge{position:relative;top:-1px}.wf-btn-xs .wf-dashboard-badge,.wf-btn-group-xs>.wf-btn .wf-dashboard-badge{top:0;padding:1px 5px}.wf-list-group-item.active>.wf-dashboard-badge,.wf-nav-pills>.active>a>.wf-dashboard-badge{color:#00709e;background-color:#fff}.wf-list-group-item>.wf-dashboard-badge{float:right}.wf-list-group-item>.wf-dashboard-badge+.wf-dashboard-badge{margin-right:5px}.wf-nav-pills>li>a>.wf-dashboard-badge{margin-left:3px}.wf-dashboard-toggle-btns{text-align:center}.wf-dashboard-toggle-btns .wf-pagination{margin:1rem 1rem 0.5rem 1rem}.wf-dashboard-show-more{position:relative;font-size:14px;color:#959595;text-align:center;line-height:1rem;background:#ffffff;width:60%;margin:20px auto 0 auto}.wf-dashboard-show-more:before{display:inline-block;content:"";position:absolute;height:1px;background:#dddddd;top:50%;width:100%;left:0;right:0}.wf-dashboard-show-more a{display:inline-block;position:relative;padding:0 10px;background-color:#ffffff}.wf-ips,.wf-recent-logins,.wf-countries{max-height:30rem;overflow-y:hidden;margin-bottom:20px}.wf-ips .wf-table,.wf-recent-logins .wf-table,.wf-countries .wf-table{margin-bottom:0}.wf-dashboard-last-updated{font-style:italic;font-size:0.6rem;text-align:center;padding-bottom:1rem;margin:0}table.wf-table{background-color:transparent;border-collapse:collapse;border-spacing:0}table.wf-table td,table.wf-table th{padding:0}.wf-table caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}.wf-table th{text-align:left}.wf-table{width:100%;max-width:100%;margin-bottom:20px}.wf-table>thead>tr>th,.wf-table>thead>tr>td,.wf-table>tbody>tr>th,.wf-table>tbody>tr>td,.wf-table>tfoot>tr>th,.wf-table>tfoot>tr>td{padding:8px;line-height:1.42857;vertical-align:top;border-top:1px solid #ddd}.wf-table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.wf-table>caption+thead>tr:first-child>th,.wf-table>caption+thead>tr:first-child>td,.wf-table>colgroup+thead>tr:first-child>th,.wf-table>colgroup+thead>tr:first-child>td,.wf-table>thead:first-child>tr:first-child>th,.wf-table>thead:first-child>tr:first-child>td{border-top:0}.wf-table>tbody+tbody{border-top:2px solid #ddd}.wf-table .wf-table{background-color:#fff}.wf-table-condensed>thead>tr>th,.wf-table-condensed>thead>tr>td,.wf-table-condensed>tbody>tr>th,.wf-table-condensed>tbody>tr>td,.wf-table-condensed>tfoot>tr>th,.wf-table-condensed>tfoot>tr>td{padding:5px}.wf-table-bordered{border:1px solid #ddd}.wf-table-bordered>thead>tr>th,.wf-table-bordered>thead>tr>td,.wf-table-bordered>tbody>tr>th,.wf-table-bordered>tbody>tr>td,.wf-table-bordered>tfoot>tr>th,.wf-table-bordered>tfoot>tr>td{border:1px solid #ddd}.wf-table-bordered>thead>tr>th,.wf-table-bordered>thead>tr>td{border-bottom-width:2px}.wf-table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.wf-table-hover>tbody>tr:hover{background-color:#f5f5f5}table.wf-table col[class*="col-"]{position:static;float:none;display:table-column}table.wf-table td[class*="col-"],table.wf-table th[class*="col-"]{position:static;float:none;display:table-cell}.wf-table>thead>tr>td.active,.wf-table>thead>tr>th.active,.wf-table>thead>tr.active>td,.wf-table>thead>tr.active>th,.wf-table>tbody>tr>td.active,.wf-table>tbody>tr>th.active,.wf-table>tbody>tr.active>td,.wf-table>tbody>tr.active>th,.wf-table>tfoot>tr>td.active,.wf-table>tfoot>tr>th.active,.wf-table>tfoot>tr.active>td,.wf-table>tfoot>tr.active>th{background-color:#f5f5f5}.wf-table-hover>tbody>tr>td.active:hover,.wf-table-hover>tbody>tr>th.active:hover,.wf-table-hover>tbody>tr.active:hover>td,.wf-table-hover>tbody>tr:hover>.active,.wf-table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.wf-table>thead>tr>td.success,.wf-table>thead>tr>th.success,.wf-table>thead>tr.success>td,.wf-table>thead>tr.success>th,.wf-table>tbody>tr>td.success,.wf-table>tbody>tr>th.success,.wf-table>tbody>tr.success>td,.wf-table>tbody>tr.success>th,.wf-table>tfoot>tr>td.success,.wf-table>tfoot>tr>th.success,.wf-table>tfoot>tr.success>td,.wf-table>tfoot>tr.success>th{background-color:#dff0d8}.wf-table-hover>tbody>tr>td.success:hover,.wf-table-hover>tbody>tr>th.success:hover,.wf-table-hover>tbody>tr.success:hover>td,.wf-table-hover>tbody>tr:hover>.success,.wf-table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.wf-table>thead>tr>td.info,.wf-table>thead>tr>th.info,.wf-table>thead>tr.info>td,.wf-table>thead>tr.info>th,.wf-table>tbody>tr>td.info,.wf-table>tbody>tr>th.info,.wf-table>tbody>tr.info>td,.wf-table>tbody>tr.info>th,.wf-table>tfoot>tr>td.info,.wf-table>tfoot>tr>th.info,.wf-table>tfoot>tr.info>td,.wf-table>tfoot>tr.info>th{background-color:#d9edf7}.wf-table-hover>tbody>tr>td.info:hover,.wf-table-hover>tbody>tr>th.info:hover,.wf-table-hover>tbody>tr.info:hover>td,.wf-table-hover>tbody>tr:hover>.info,.wf-table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}.wf-table>thead>tr>td.warning,.wf-table>thead>tr>th.warning,.wf-table>thead>tr.warning>td,.wf-table>thead>tr.warning>th,.wf-table>tbody>tr>td.warning,.wf-table>tbody>tr>th.warning,.wf-table>tbody>tr.warning>td,.wf-table>tbody>tr.warning>th,.wf-table>tfoot>tr>td.warning,.wf-table>tfoot>tr>th.warning,.wf-table>tfoot>tr.warning>td,.wf-table>tfoot>tr.warning>th{background-color:#fcf8e3}.wf-table-hover>tbody>tr>td.warning:hover,.wf-table-hover>tbody>tr>th.warning:hover,.wf-table-hover>tbody>tr.warning:hover>td,.wf-table-hover>tbody>tr:hover>.warning,.wf-table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}.wf-table>thead>tr>td.danger,.wf-table>thead>tr>th.danger,.wf-table>thead>tr.danger>td,.wf-table>thead>tr.danger>th,.wf-table>tbody>tr>td.danger,.wf-table>tbody>tr>th.danger,.wf-table>tbody>tr.danger>td,.wf-table>tbody>tr.danger>th,.wf-table>tfoot>tr>td.danger,.wf-table>tfoot>tr>th.danger,.wf-table>tfoot>tr.danger>td,.wf-table>tfoot>tr.danger>th{background-color:#f2dede}.wf-table-hover>tbody>tr>td.danger:hover,.wf-table-hover>tbody>tr>th.danger:hover,.wf-table-hover>tbody>tr.danger:hover>td,.wf-table-hover>tbody>tr:hover>.danger,.wf-table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}.wf-table-responsive{overflow-x:auto;min-height:0.01%}@media screen and (max-width: 767px){.wf-table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.wf-table-responsive>.wf-table{margin-bottom:0}.wf-table-responsive>.wf-table>thead>tr>th,.wf-table-responsive>.wf-table>thead>tr>td,.wf-table-responsive>.wf-table>tbody>tr>th,.wf-table-responsive>.wf-table>tbody>tr>td,.wf-table-responsive>.wf-table>tfoot>tr>th,.wf-table-responsive>.wf-table>tfoot>tr>td{white-space:nowrap}.wf-table-responsive>.wf-table-bordered{border:0}.wf-table-responsive>.wf-table-bordered>thead>tr>th:first-child,.wf-table-responsive>.wf-table-bordered>thead>tr>td:first-child,.wf-table-responsive>.wf-table-bordered>tbody>tr>th:first-child,.wf-table-responsive>.wf-table-bordered>tbody>tr>td:first-child,.wf-table-responsive>.wf-table-bordered>tfoot>tr>th:first-child,.wf-table-responsive>.wf-table-bordered>tfoot>tr>td:first-child{border-left:0}.wf-table-responsive>.wf-table-bordered>thead>tr>th:last-child,.wf-table-responsive>.wf-table-bordered>thead>tr>td:last-child,.wf-table-responsive>.wf-table-bordered>tbody>tr>th:last-child,.wf-table-responsive>.wf-table-bordered>tbody>tr>td:last-child,.wf-table-responsive>.wf-table-bordered>tfoot>tr>th:last-child,.wf-table-responsive>.wf-table-bordered>tfoot>tr>td:last-child{border-right:0}.wf-table-responsive>.wf-table-bordered>tbody>tr:last-child>th,.wf-table-responsive>.wf-table-bordered>tbody>tr:last-child>td,.wf-table-responsive>.wf-table-bordered>tfoot>tr:last-child>th,.wf-table-responsive>.wf-table-bordered>tfoot>tr:last-child>td{border-bottom:0}}.wf-nav{margin-bottom:0;padding-left:0;list-style:none}.wf-nav:before,.wf-nav:after{content:" ";display:table}.wf-nav:after{clear:both}.wf-nav>li{position:relative;display:block}.wf-nav>li>a{position:relative;display:block;padding:8px 12px}.wf-nav>li>a:hover,.wf-nav>li>a:focus{text-decoration:none;background-color:#e2e2e2}.wf-nav>li.wf-disabled>a{color:#777}.wf-nav>li.wf-disabled>a:hover,.wf-nav>li.wf-disabled>a:focus{color:#777;text-decoration:none;background-color:transparent;cursor:not-allowed}.wf-nav .wf-open>a,.wf-nav .wf-open>a:hover,.wf-nav .wf-open>a:focus{background-color:#e2e2e2;border-color:#00709e}.wf-nav>li>a>img{max-width:none}.wf-nav-tabs{border-bottom:1px solid #ddd}.wf-nav-tabs>li{float:left;margin-bottom:-1px}.wf-nav-tabs>li>a{margin-right:2px;line-height:1.42857;border:1px solid transparent;border-radius:4px 4px 0 0}.wf-nav-tabs>li>a:hover{border-color:#e2e2e2 #e2e2e2 #ddd}.wf-nav-tabs>li.wf-active>a,.wf-nav-tabs>li.wf-active>a:hover,.wf-nav-tabs>li.wf-active>a:focus{color:#555;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default}.wf-nav-pills>li{float:left}.wf-nav-pills>li>a{border-radius:4px}.wf-nav-pills>li+li{margin-left:2px}.wf-nav-pills>li.wf-active>a,.wf-nav-pills>li.wf-active>a:hover,.wf-nav-pills>li.wf-active>a:focus{color:#fff;background-color:#00709e}.wf-nav-stacked>li{float:none}.wf-nav-stacked>li+li{margin-top:2px;margin-left:0}.wf-nav-justified,.wf-nav-tabs.wf-nav-justified{width:100%}.wf-nav-justified>li,.wf-nav-tabs.wf-nav-justified>li{float:none}.wf-nav-justified>li>a,.wf-nav-tabs.wf-nav-justified>li>a{text-align:center;margin-bottom:5px}.wf-nav-justified>.wf-dropdown .wf-dropdown-menu{top:auto;left:auto}@media (min-width: 768px){.wf-nav-justified>li,.wf-nav-tabs.wf-nav-justified>li{display:table-cell;width:1%}.wf-nav-justified>li>a,.wf-nav-tabs.wf-nav-justified>li>a{margin-bottom:0}}.wf-nav-tabs-justified,.wf-nav-tabs.wf-nav-justified{border-bottom:0}.wf-nav-tabs-justified>li>a,.wf-nav-tabs.wf-nav-justified>li>a{margin-right:0;border-radius:4px}.wf-nav-tabs-justified>.wf-active>a,.wf-nav-tabs.wf-nav-justified>.wf-active>a,.wf-nav-tabs-justified>.wf-active>a:hover,.wf-nav-tabs.wf-nav-justified>.wf-active>a:hover,.wf-nav-tabs-justified>.wf-active>a:focus,.wf-nav-tabs.wf-nav-justified>.wf-active>a:focus{border:1px solid #ddd}@media (min-width: 768px){.wf-nav-tabs-justified>li>a,.wf-nav-tabs.wf-nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.wf-nav-tabs-justified>.wf-active>a,.wf-nav-tabs.wf-nav-justified>.wf-active>a,.wf-nav-tabs-justified>.wf-active>a:hover,.wf-nav-tabs.wf-nav-justified>.wf-active>a:hover,.wf-nav-tabs-justified>.wf-active>a:focus,.wf-nav-tabs.wf-nav-justified>.wf-active>a:focus{border-bottom-color:#fff}}.wf-tab-content>.wf-tab-pane{display:none}.wf-tab-content>.wf-active{display:block}.wf-nav-tabs .wf-dropdown-menu{margin-top:-1px;-moz-border-radius-topleft:0;-webkit-border-top-left-radius:0;border-top-left-radius:0;-moz-border-radius-topright:0;-webkit-border-top-right-radius:0;border-top-right-radius:0}.wf-navbar-brand{float:left;padding:12px 8px;font-size:18px;line-height:20px;margin:10px 0 0 0}.wf-navbar-brand:hover,.wf-navbar-brand:focus{text-decoration:none}.wf-navbar-brand>img{display:block}@media (min-width: 768px){.navbar>.container .wf-navbar-brand,.navbar>.container-fluid .wf-navbar-brand{margin-left:-8px}}.wf-caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid \9;border-right:4px solid transparent;border-left:4px solid transparent}.wf-dropup,.wf-dropdown{position:relative}.wf-dropdown-toggle:focus{outline:0}.wf-dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;font-size:14px;text-align:left;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.175);box-shadow:0 6px 12px rgba(0,0,0,0.175);background-clip:padding-box}.wf-dropdown-menu .wf-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.wf-dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:1.42857;color:#333;text-decoration:none;white-space:nowrap}.wf-dropdown-menu>li>a:hover,.wf-dropdown-menu>li>a:focus{text-decoration:none;color:#262626;background-color:#f5f5f5}.wf-dropdown-menu>.wf-active>a,.wf-dropdown-menu>.wf-active>a:hover,.wf-dropdown-menu>.wf-active>a:focus{color:#fff;text-decoration:none;outline:0;background-color:#00709e}.wf-dropdown-menu>.wf-disabled>a,.wf-dropdown-menu>.wf-disabled>a:hover,.wf-dropdown-menu>.wf-disabled>a:focus{color:#777}.wf-dropdown-menu>.wf-disabled>a:hover,.wf-dropdown-menu>.wf-disabled>a:focus{text-decoration:none;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);cursor:not-allowed}.wf-open>.wf-dropdown-menu{display:block}.wf-open>a{outline:0}.wf-dropdown-menu-right{left:auto;right:0}.wf-dropdown-menu-left{left:0;right:auto}.wf-dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857;color:#777;white-space:nowrap}.wf-dropdown-backdrop{position:fixed;left:0;right:0;bottom:0;top:0;z-index:990}.wf-pull-right>.wf-dropdown-menu{right:0;left:auto}.wf-dropup .wf-caret,.wf-navbar-fixed-bottom .wf-dropdown .wf-caret{border-top:0;border-bottom:4px dashed;border-bottom:4px solid \9;content:""}.wf-dropup .wf-dropdown-menu,.wf-navbar-fixed-bottom .wf-dropdown .wf-dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width: 768px){.wf-navbar-right .wf-dropdown-menu{right:0;left:auto}.wf-navbar-right .wf-dropdown-menu-left{left:0;right:auto}}.wf-mobile-dropdown{border:1px solid #ccc;margin-left:.5em;padding:5px 10px;font-size:14px;line-height:24px;margin:10px 10px 0 0;background:#f1f1f1;color:#000;font-weight:600;text-decoration:none}.wf-blocked-countries{margin-bottom:0;margin-top:0;padding-left:0;list-style:none;margin-left:-6px;display:-webkit-flex;display:flex;-webkit-align-items:stretch;align-items:stretch;-webkit-justify-content:flex-start;justify-content:flex-start;-webkit-align-content:stretch;align-content:stretch;-webkit-flex-wrap:wrap;flex-wrap:wrap}.wf-blocked-countries>li{display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:center;justify-content:center;margin:0px 0px 2px 6px;text-align:center;border:1px solid #e2e2e2;border-radius:4px;padding:8px 12px;background-color:#ffffff;-webkit-flex-basis:38%;flex-basis:38%}@media (min-width: 768px){.wf-blocked-countries>li{-webkit-flex-basis:20%;flex-basis:20%}}@media (min-width: 992px){.wf-blocked-countries>li{-webkit-flex-basis:15%;flex-basis:15%}}@media (min-width: 1200px){.wf-blocked-countries>li{-webkit-flex-basis:10%;flex-basis:10%}}.wf-blocked-countries>li:hover,.wf-blocked-countries>li:focus{text-decoration:none;background-color:#e2e2e2}.wf-blocked-countries>li.disabled>a{color:#777}.wf-blocked-countries>li.disabled>a:hover,.wf-blocked-countries>li.disabled>a:focus{color:#777;text-decoration:none;background-color:transparent;cursor:not-allowed}.wf-blocked-countries>li>a{text-decoration:none;position:relative;display:block}.wf-blocked-countries>li.active,.wf-blocked-countries>li.active:hover,.wf-blocked-countries>li.active:focus{background-color:#00709e;border-color:#00709e}.wf-blocked-countries>li.active>a,.wf-blocked-countries>li.active:hover>a,.wf-blocked-countries>li.active:focus>a{color:#fff}.wf-blocked-countries>li.text-only{position:relative;display:block;padding:8px 12px}.wf-blocked-countries>li>a>img{max-width:none}.wf-blocked-countries-section{display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:flex-start;justify-content:flex-start;-webkit-align-content:stretch;align-content:stretch;-webkit-flex-wrap:nowrap;flex-wrap:nowrap}.wf-blocked-countries-section-title{font-size:1.1rem;padding-right:0.5rem}.wf-blocked-countries-section-spacer{-webkit-flex-basis:30px;flex-basis:30px;height:1px;background:#aaa}.wf-blocked-countries-section-options{margin-bottom:0;margin-top:0;padding-left:0.5rem;list-style:none;display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:flex-start;justify-content:flex-start;-webkit-align-content:stretch;align-content:stretch;-webkit-flex-wrap:nowrap;flex-wrap:nowrap}.wf-blocked-countries-section-options li{padding:0;margin:0;color:#777}.wf-blocked-countries-section-options li a{padding:2px 4px;text-decoration:none;color:#777}.wf-blocked-countries-section-options li a.active-section{background-color:#777;color:#e2e2e2}table.wf-dataTable{width:auto;max-width:800px;clear:both;border-collapse:collapse;border-spacing:0}table.wf-dataTable>thead th,table.wf-dataTable>tfoot th{font-weight:bold}table.wf-dataTable>thead th,table.wf-dataTable>thead td{cursor:pointer;text-align:left;font-size:11px;border:1px solid #ccc;padding:8px 4px 5px 9px;text-transform:uppercase}table.wf-dataTable>thead th:active,table.wf-dataTable>thead td:active{outline:none}table.wf-dataTable>tfoot th,table.wf-dataTable>tfoot td{padding:10px 18px 6px 18px;border-top:1px solid #111}table.wf-dataTable>thead .sorting,table.wf-dataTable>thead .sorting_asc,table.wf-dataTable>thead .sorting_desc{cursor:pointer;*cursor:hand}table.wf-dataTable>thead .sorting,table.wf-dataTable>thead .sorting_asc,table.wf-dataTable>thead .sorting_desc,table.wf-dataTable>thead .sorting_asc_disabled,table.wf-dataTable>thead .sorting_desc_disabled{background-repeat:no-repeat;background-position:center right}table.wf-dataTable>thead .sorting{background-image:url(../images/sort_both.png)}table.wf-dataTable>thead .sorting_asc{background-image:url(../images/sort_asc.png)}table.wf-dataTable>thead .sorting_desc{background-image:url(../images/sort_desc.png)}table.wf-dataTable>thead .sorting_asc_disabled{background-image:url(../images/sort_asc_disabled.png)}table.wf-dataTable>thead .sorting_desc_disabled{background-image:url(../images/sort_desc_disabled.png)}table.wf-dataTable>tbody>tr{background-color:#ffffff}table.wf-dataTable>tbody>tr.selected{background-color:#B0BED9}table.wf-dataTable>tbody>tr>th,table.wf-dataTable>tbody>tr>td{padding:5px 8px}table.wf-dataTable.row-border>tbody>tr>th,table.wf-dataTable.row-border>tbody>tr>td,table.wf-dataTable.display>tbody>tr>th,table.wf-dataTable.display>tbody>tr>td{border:1px solid #cccccc}table.wf-dataTable.row-border>tbody>tr:first-child>th,table.wf-dataTable.row-border>tbody>tr:first-child>td,table.wf-dataTable.display>tbody>tr:first-child>th,table.wf-dataTable.display>tbody>tr:first-child>td{border-top:none}table.wf-dataTable.cell-border>tbody>tr>th,table.wf-dataTable.cell-border>tbody>tr>td{border-top:1px solid #ddd;border-right:1px solid #ddd}table.wf-dataTable.cell-border>tbody>tr>th:first-child,table.wf-dataTable.cell-border>tbody>tr>td:first-child{border-left:1px solid #ddd}table.wf-dataTable.cell-border>tbody>tr:first-child>th,table.wf-dataTable.cell-border>tbody>tr:first-child>td{border-top:none}table.wf-dataTable.stripe>tbody>tr.odd,table.wf-dataTable.display>tbody>tr.odd{background-color:#f9f9f9}table.wf-dataTable.stripe>tbody>tr.odd.selected,table.wf-dataTable.display>tbody>tr.odd.selected{background-color:#acbad4}table.wf-dataTable.hover>tbody>tr:hover,table.wf-dataTable.display>tbody>tr:hover{background-color:#f6f6f6}table.wf-dataTable.hover>tbody>tr:hover.selected,table.wf-dataTable.display>tbody>tr:hover.selected{background-color:#aab7d1}table.wf-dataTable.order-column>tbody>tr>.sorting_1,table.wf-dataTable.order-column>tbody>tr>.sorting_2,table.wf-dataTable.order-column>tbody>tr>.sorting_3,table.wf-dataTable.display tbody>tr>.sorting_1,table.wf-dataTable.display>tbody>tr>.sorting_2,table.wf-dataTable.display>tbody>tr>.sorting_3{background-color:#fafafa}table.wf-dataTable.order-column>tbody>tr.selected>.sorting_1,table.wf-dataTable.order-column>tbody>tr.selected>.sorting_2,table.wf-dataTable.order-column>tbody>tr.selected>.sorting_3,table.wf-dataTable.display>tbody>tr.selected>.sorting_1,table.wf-dataTable.display>tbody>tr.selected>.sorting_2,table.wf-dataTable.display>tbody>tr.selected>.sorting_3{background-color:#acbad5}table.wf-dataTable.display>tbody>tr.odd>.sorting_1,table.wf-dataTable.order-column.stripe>tbody>tr.odd>.sorting_1{background-color:#f1f1f1}table.wf-dataTable.display>tbody>tr.odd>.sorting_2,table.wf-dataTable.order-column.stripe>tbody>tr.odd>.sorting_2{background-color:#f3f3f3}table.wf-dataTable.display>tbody>tr.odd>.sorting_3,table.wf-dataTable.order-column.stripe>tbody>tr.odd>.sorting_3{background-color:whitesmoke}table.wf-dataTable.display>tbody>tr.odd.selected>.sorting_1,table.wf-dataTable.order-column.stripe>tbody>tr.odd.selected>.sorting_1{background-color:#a6b4cd}table.wf-dataTable.display>tbody>tr.odd.selected>.sorting_2,table.wf-dataTable.order-column.stripe>tbody>tr.odd.selected>.sorting_2{background-color:#a8b5cf}table.wf-dataTable.display>tbody>tr.odd.selected>.sorting_3,table.wf-dataTable.order-column.stripe>tbody>tr.odd.selected>.sorting_3{background-color:#a9b7d1}table.wf-dataTable.display>tbody>tr.even>.sorting_1,table.wf-dataTable.order-column.stripe>tbody>tr.even>.sorting_1{background-color:#fafafa}table.wf-dataTable.display>tbody>tr.even>.sorting_2,table.wf-dataTable.order-column.stripe>tbody>tr.even>.sorting_2{background-color:#fcfcfc}table.wf-dataTable.display>tbody>tr.even>.sorting_3,table.wf-dataTable.order-column.stripe>tbody>tr.even>.sorting_3{background-color:#fefefe}table.wf-dataTable.display>tbody>tr.even.selected>.sorting_1,table.wf-dataTable.order-column.stripe>tbody>tr.even.selected>.sorting_1{background-color:#acbad5}table.wf-dataTable.display>tbody>tr.even.selected>.sorting_2,table.wf-dataTable.order-column.stripe>tbody>tr.even.selected>.sorting_2{background-color:#aebcd6}table.wf-dataTable.display>tbody>tr.even.selected>.sorting_3,table.wf-dataTable.order-column.stripe>tbody>tr.even.selected>.sorting_3{background-color:#afbdd8}table.wf-dataTable.display>tbody>tr:hover>.sorting_1,table.wf-dataTable.order-column.hover>tbody>tr:hover>.sorting_1{background-color:#eaeaea}table.wf-dataTable.display>tbody>tr:hover>.sorting_2,table.wf-dataTable.order-column.hover>tbody>tr:hover>.sorting_2{background-color:#ececec}table.wf-dataTable.display>tbody>tr:hover>.sorting_3,table.wf-dataTable.order-column.hover>tbody>tr:hover>.sorting_3{background-color:#efefef}table.wf-dataTable.display>tbody>tr:hover.selected>.sorting_1,table.wf-dataTable.order-column.hover>tbody>tr:hover.selected>.sorting_1{background-color:#a2aec7}table.wf-dataTable.display>tbody>tr:hover.selected>.sorting_2,table.wf-dataTable.order-column.hover>tbody>tr:hover.selected>.sorting_2{background-color:#a3b0c9}table.wf-dataTable.display>tbody>tr:hover.selected>.sorting_3,table.wf-dataTable.order-column.hover>tbody>tr:hover.selected>.sorting_3{background-color:#a5b2cb}table.wf-dataTable.no-footer{border-bottom:1px solid #111}table.wf-dataTable.nowrap th,table.wf-dataTable.nowrap td{white-space:nowrap}table.wf-dataTable.compact thead th,table.wf-dataTable.compact thead td{padding:4px 17px 4px 4px}table.wf-dataTable.compact tfoot th,table.wf-dataTable.compact tfoot td{padding:4px}table.wf-dataTable.compact>tbody>tr>th,table.wf-dataTable.compact>tbody>tr>td{padding:4px}table.wf-dataTable th.dt-left,table.wf-dataTable td.dt-left{text-align:left}table.wf-dataTable th.dt-center,table.wf-dataTable td.dt-center,table.wf-dataTable td.dataTables_empty{text-align:center}table.wf-dataTable th.dt-right,table.wf-dataTable td.dt-right{text-align:right}table.wf-dataTable th.dt-justify,table.wf-dataTable td.dt-justify{text-align:justify}table.wf-dataTable th.dt-nowrap,table.wf-dataTable td.dt-nowrap{white-space:nowrap}table.wf-dataTable thead th.dt-head-left,table.wf-dataTable thead td.dt-head-left,table.wf-dataTable tfoot th.dt-head-left,table.wf-dataTable tfoot td.dt-head-left{text-align:left}table.wf-dataTable thead th.dt-head-center,table.wf-dataTable thead td.dt-head-center,table.wf-dataTable tfoot th.dt-head-center,table.wf-dataTable tfoot td.dt-head-center{text-align:center}table.wf-dataTable thead th.dt-head-right,table.wf-dataTable thead td.dt-head-right,table.wf-dataTable tfoot th.dt-head-right,table.wf-dataTable tfoot td.dt-head-right{text-align:right}table.wf-dataTable thead th.dt-head-justify,table.wf-dataTable thead td.dt-head-justify,table.wf-dataTable tfoot th.dt-head-justify,table.wf-dataTable tfoot td.dt-head-justify{text-align:justify}table.wf-dataTable thead th.dt-head-nowrap,table.wf-dataTable thead td.dt-head-nowrap,table.wf-dataTable tfoot th.dt-head-nowrap,table.wf-dataTable tfoot td.dt-head-nowrap{white-space:nowrap}table.wf-dataTable>tbody>tr>th.dt-body-left,table.wf-dataTable>tbody>tr>td.dt-body-left{text-align:left}table.wf-dataTable>tbody>tr>th.dt-body-center,table.wf-dataTable>tbody>tr>td.dt-body-center{text-align:center}table.wf-dataTable>tbody>tr>th.dt-body-right,table.wf-dataTable>tbody>tr>td.dt-body-right{text-align:right}table.wf-dataTable>tbody>tr>th.dt-body-justify,table.wf-dataTable>tbody>tr>td.dt-body-justify{text-align:justify}table.wf-dataTable>tbody>tr>th.dt-body-nowrap,table.wf-dataTable>tbody>tr>td.dt-body-nowrap{white-space:nowrap}table.wf-dataTable table.dataTable,table.wf-dataTable th,table.wf-dataTable td{-webkit-box-sizing:content-box;box-sizing:content-box}.wf-dataTables_wrapper{position:relative;clear:both;*zoom:1;zoom:1}.wf-dataTables_wrapper .dataTables_length{float:left}.wf-dataTables_wrapper .dataTables_filter{float:right;text-align:right}.wf-dataTables_wrapper .dataTables_filter input{margin-left:0.5em}.wf-dataTables_wrapper .dataTables_info{clear:both;float:left;padding-top:0.755em}.wf-dataTables_wrapper .dataTables_paginate{float:right;text-align:right;padding-top:0.25em}.wf-dataTables_wrapper .dataTables_paginate .paginate_button{box-sizing:border-box;display:inline-block;min-width:1.5em;padding:0.5em 1em;margin-left:2px;text-align:center;text-decoration:none !important;cursor:pointer;*cursor:hand;color:#333 !important;border:1px solid transparent;border-radius:2px}.wf-dataTables_wrapper .dataTables_paginate .paginate_button.current,.wf-dataTables_wrapper .dataTables_paginate .paginate_button.current:hover{color:#333 !important;border:1px solid #979797;background-color:white;background:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #fff), color-stop(100%, #dcdcdc));background:-webkit-linear-gradient(top, #fff 0%, #dcdcdc 100%);background:-moz-linear-gradient(top, #fff 0%, #dcdcdc 100%);background:-ms-linear-gradient(top, #fff 0%, #dcdcdc 100%);background:-o-linear-gradient(top, #fff 0%, #dcdcdc 100%);background:linear-gradient(to bottom, #ffffff 0%,#dcdcdc 100%)}.wf-dataTables_wrapper .dataTables_paginate .paginate_button.disabled,.wf-dataTables_wrapper .dataTables_paginate .paginate_button.disabled:hover,.wf-dataTables_wrapper .dataTables_paginate .paginate_button.disabled:active{cursor:default;color:#666 !important;border:1px solid transparent;background:transparent;box-shadow:none}.wf-dataTables_wrapper .dataTables_paginate .paginate_button:hover{color:white !important;border:1px solid #111;background-color:#585858;background:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #585858), color-stop(100%, #111));background:-webkit-linear-gradient(top, #585858 0%, #111 100%);background:-moz-linear-gradient(top, #585858 0%, #111 100%);background:-ms-linear-gradient(top, #585858 0%, #111 100%);background:-o-linear-gradient(top, #585858 0%, #111 100%);background:linear-gradient(to bottom, #585858 0%,#111111 100%)}.wf-dataTables_wrapper .dataTables_paginate .paginate_button:active{outline:none;background-color:#2b2b2b;background:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #2b2b2b), color-stop(100%, #0c0c0c));background:-webkit-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);background:-moz-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);background:-ms-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);background:-o-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);background:linear-gradient(to bottom, #2b2b2b 0%,#0c0c0c 100%);box-shadow:inset 0 0 3px #111}.wf-dataTables_wrapper .dataTables_paginate .ellipsis{padding:0 1em}.wf-dataTables_wrapper .dataTables_processing{position:absolute;top:50%;left:50%;width:100%;height:40px;margin-left:-50%;margin-top:-25px;padding-top:20px;text-align:center;font-size:1.2em;background-color:white;background:-webkit-gradient(linear, left top, right top, color-stop(0%, rgba(255,255,255,0)), color-stop(25%, rgba(255,255,255,0.9)), color-stop(75%, rgba(255,255,255,0.9)), color-stop(100%, rgba(255,255,255,0)));background:-webkit-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%);background:-moz-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%);background:-ms-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%);background:-o-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%);background:linear-gradient(to right, rgba(255,255,255,0) 0%,rgba(255,255,255,0.9) 25%,rgba(255,255,255,0.9) 75%,rgba(255,255,255,0) 100%)}.wf-dataTables_wrapper .dataTables_length,.wf-dataTables_wrapper .dataTables_filter,.wf-dataTables_wrapper .dataTables_info,.wf-dataTables_wrapper .dataTables_processing,.wf-dataTables_wrapper .dataTables_paginate{color:#333}.wf-dataTables_wrapper .dataTables_scroll{clear:both}.wf-dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody{*margin-top:-1px;-webkit-overflow-scrolling:touch}.wf-dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody th,.wf-dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody td{vertical-align:middle}.wf-dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody th>div.dataTables_sizing,.wf-dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody td>div.dataTables_sizing{height:0;overflow:hidden;margin:0 !important;padding:0 !important}.wf-dataTables_wrapper.no-footer .dataTables_scrollBody{border-bottom:1px solid #111}.wf-dataTables_wrapper.no-footer div.dataTables_scrollHead table,.wf-dataTables_wrapper.no-footer div.dataTables_scrollBody table{border-bottom:none}.wf-dataTables_wrapper:after{visibility:hidden;display:block;content:"";clear:both;height:0}@media screen and (max-width: 767px){.wf-dataTables_wrapper .dataTables_info,.wf-dataTables_wrapper .dataTables_paginate{float:none;text-align:center}.wf-dataTables_wrapper .dataTables_paginate{margin-top:0.5em}}@media screen and (max-width: 640px){.wf-dataTables_wrapper .dataTables_length,.wf-dataTables_wrapper .dataTables_filter{float:none;text-align:center}.wf-dataTables_wrapper .dataTables_filter{margin-top:0.5em}}
css/wf-adminbar.css CHANGED
@@ -1 +1 @@
1
- .wf-clearfix:before,.wf-clearfix:after{content:" ";display:table}.wf-clearfix:after{clear:both}.wf-btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;vertical-align:middle;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.wf-btn:focus,.wf-btn.wf-focus,.wf-btn:active:focus,.wf-btn:active.wf-focus,.wf-btn.wf-active:focus,.wf-btn.wf-active.wf-focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.wf-btn:hover,.wf-btn:focus,.wf-btn.wf-focus{color:#333;text-decoration:none}.wf-btn:active,.wf-btn.wf-active{outline:0;background-image:none;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.wf-btn.wf-disabled,.wf-btn[disabled],fieldset[disabled] .wf-btn{cursor:not-allowed;filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=65);opacity:.65;-webkit-box-shadow:none;box-shadow:none}a.wf-btn{text-decoration:none}a.wf-btn.wf-disabled,fieldset[disabled] a.wf-btn{pointer-events:none}.wf-btn-default{color:#333;background-color:#fff;border-color:#ccc}.wf-btn-default:focus,.wf-btn-default.focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.wf-btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.wf-btn-default:active,.wf-btn-default.active,.wf-open>.wf-btn-default.wf-dropdown-toggle{color:#333;background-color:#e6e6e6;border-color:#adadad}.wf-btn-default:active:hover,.wf-btn-default:active:focus,.wf-btn-default:active.focus,.wf-btn-default.active:hover,.wf-btn-default.active:focus,.wf-btn-default.active.focus,.wf-open>.wf-btn-default.wf-dropdown-toggle:hover,.wf-open>.wf-btn-default.wf-dropdown-toggle:focus,.wf-open>.wf-btn-default.wf-dropdown-toggle.focus{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.wf-btn-default:active,.wf-btn-default.wf-active,.wf-open>.wf-btn-default.wf-dropdown-toggle{background-image:none}.wf-btn-default.wf-disabled:hover,.wf-btn-default.wf-disabled:focus,.wf-btn-default.wf-disabled.wf-focus,.wf-btn-default[disabled]:hover,.wf-btn-default[disabled]:focus,.wf-btn-default[disabled].wf-focus,fieldset[disabled] .wf-btn-default:hover,fieldset[disabled] .wf-btn-default:focus,fieldset[disabled] .wf-btn-default.wf-focus{background-color:#fff;border-color:#ccc}.wf-btn-default .wf-badge{color:#fff;background-color:#333}.wf-btn-primary{color:#fff;background-color:#00709e;border-color:#005e85}.wf-btn-primary:focus,.wf-btn-primary.focus{color:#fff;background-color:#004c6b;border-color:#000405}.wf-btn-primary:hover{color:#fff;background-color:#004c6b;border-color:#003347}.wf-btn-primary:active,.wf-btn-primary.active,.wf-open>.wf-btn-primary.wf-dropdown-toggle{color:#fff;background-color:#004c6b;border-color:#003347}.wf-btn-primary:active:hover,.wf-btn-primary:active:focus,.wf-btn-primary:active.focus,.wf-btn-primary.active:hover,.wf-btn-primary.active:focus,.wf-btn-primary.active.focus,.wf-open>.wf-btn-primary.wf-dropdown-toggle:hover,.wf-open>.wf-btn-primary.wf-dropdown-toggle:focus,.wf-open>.wf-btn-primary.wf-dropdown-toggle.focus{color:#fff;background-color:#003347;border-color:#000405}.wf-btn-primary:active,.wf-btn-primary.wf-active,.wf-open>.wf-btn-primary.wf-dropdown-toggle{background-image:none}.wf-btn-primary.wf-disabled:hover,.wf-btn-primary.wf-disabled:focus,.wf-btn-primary.wf-disabled.wf-focus,.wf-btn-primary[disabled]:hover,.wf-btn-primary[disabled]:focus,.wf-btn-primary[disabled].wf-focus,fieldset[disabled] .wf-btn-primary:hover,fieldset[disabled] .wf-btn-primary:focus,fieldset[disabled] .wf-btn-primary.wf-focus{background-color:#00709e;border-color:#005e85}.wf-btn-primary .wf-badge{color:#00709e;background-color:#fff}.wf-btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.wf-btn-success:focus,.wf-btn-success.focus{color:#fff;background-color:#449d44;border-color:#255625}.wf-btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.wf-btn-success:active,.wf-btn-success.active,.wf-open>.wf-btn-success.wf-dropdown-toggle{color:#fff;background-color:#449d44;border-color:#398439}.wf-btn-success:active:hover,.wf-btn-success:active:focus,.wf-btn-success:active.focus,.wf-btn-success.active:hover,.wf-btn-success.active:focus,.wf-btn-success.active.focus,.wf-open>.wf-btn-success.wf-dropdown-toggle:hover,.wf-open>.wf-btn-success.wf-dropdown-toggle:focus,.wf-open>.wf-btn-success.wf-dropdown-toggle.focus{color:#fff;background-color:#398439;border-color:#255625}.wf-btn-success:active,.wf-btn-success.wf-active,.wf-open>.wf-btn-success.wf-dropdown-toggle{background-image:none}.wf-btn-success.wf-disabled:hover,.wf-btn-success.wf-disabled:focus,.wf-btn-success.wf-disabled.wf-focus,.wf-btn-success[disabled]:hover,.wf-btn-success[disabled]:focus,.wf-btn-success[disabled].wf-focus,fieldset[disabled] .wf-btn-success:hover,fieldset[disabled] .wf-btn-success:focus,fieldset[disabled] .wf-btn-success.wf-focus{background-color:#5cb85c;border-color:#4cae4c}.wf-btn-success .wf-badge{color:#5cb85c;background-color:#fff}.wf-btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.wf-btn-info:focus,.wf-btn-info.focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.wf-btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.wf-btn-info:active,.wf-btn-info.active,.wf-open>.wf-btn-info.wf-dropdown-toggle{color:#fff;background-color:#31b0d5;border-color:#269abc}.wf-btn-info:active:hover,.wf-btn-info:active:focus,.wf-btn-info:active.focus,.wf-btn-info.active:hover,.wf-btn-info.active:focus,.wf-btn-info.active.focus,.wf-open>.wf-btn-info.wf-dropdown-toggle:hover,.wf-open>.wf-btn-info.wf-dropdown-toggle:focus,.wf-open>.wf-btn-info.wf-dropdown-toggle.focus{color:#fff;background-color:#269abc;border-color:#1b6d85}.wf-btn-info:active,.wf-btn-info.wf-active,.wf-open>.wf-btn-info.wf-dropdown-toggle{background-image:none}.wf-btn-info.wf-disabled:hover,.wf-btn-info.wf-disabled:focus,.wf-btn-info.wf-disabled.wf-focus,.wf-btn-info[disabled]:hover,.wf-btn-info[disabled]:focus,.wf-btn-info[disabled].wf-focus,fieldset[disabled] .wf-btn-info:hover,fieldset[disabled] .wf-btn-info:focus,fieldset[disabled] .wf-btn-info.wf-focus{background-color:#5bc0de;border-color:#46b8da}.wf-btn-info .wf-badge{color:#5bc0de;background-color:#fff}.wf-btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.wf-btn-warning:focus,.wf-btn-warning.focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.wf-btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.wf-btn-warning:active,.wf-btn-warning.active,.wf-open>.wf-btn-warning.wf-dropdown-toggle{color:#fff;background-color:#ec971f;border-color:#d58512}.wf-btn-warning:active:hover,.wf-btn-warning:active:focus,.wf-btn-warning:active.focus,.wf-btn-warning.active:hover,.wf-btn-warning.active:focus,.wf-btn-warning.active.focus,.wf-open>.wf-btn-warning.wf-dropdown-toggle:hover,.wf-open>.wf-btn-warning.wf-dropdown-toggle:focus,.wf-open>.wf-btn-warning.wf-dropdown-toggle.focus{color:#fff;background-color:#d58512;border-color:#985f0d}.wf-btn-warning:active,.wf-btn-warning.wf-active,.wf-open>.wf-btn-warning.wf-dropdown-toggle{background-image:none}.wf-btn-warning.wf-disabled:hover,.wf-btn-warning.wf-disabled:focus,.wf-btn-warning.wf-disabled.wf-focus,.wf-btn-warning[disabled]:hover,.wf-btn-warning[disabled]:focus,.wf-btn-warning[disabled].wf-focus,fieldset[disabled] .wf-btn-warning:hover,fieldset[disabled] .wf-btn-warning:focus,fieldset[disabled] .wf-btn-warning.wf-focus{background-color:#f0ad4e;border-color:#eea236}.wf-btn-warning .wf-badge{color:#f0ad4e;background-color:#fff}.wf-btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.wf-btn-danger:focus,.wf-btn-danger.focus{color:#fff;background-color:#c9302c;border-color:#761c19}.wf-btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.wf-btn-danger:active,.wf-btn-danger.active,.wf-open>.wf-btn-danger.wf-dropdown-toggle{color:#fff;background-color:#c9302c;border-color:#ac2925}.wf-btn-danger:active:hover,.wf-btn-danger:active:focus,.wf-btn-danger:active.focus,.wf-btn-danger.active:hover,.wf-btn-danger.active:focus,.wf-btn-danger.active.focus,.wf-open>.wf-btn-danger.wf-dropdown-toggle:hover,.wf-open>.wf-btn-danger.wf-dropdown-toggle:focus,.wf-open>.wf-btn-danger.wf-dropdown-toggle.focus{color:#fff;background-color:#ac2925;border-color:#761c19}.wf-btn-danger:active,.wf-btn-danger.wf-active,.wf-open>.wf-btn-danger.wf-dropdown-toggle{background-image:none}.wf-btn-danger.wf-disabled:hover,.wf-btn-danger.wf-disabled:focus,.wf-btn-danger.wf-disabled.wf-focus,.wf-btn-danger[disabled]:hover,.wf-btn-danger[disabled]:focus,.wf-btn-danger[disabled].wf-focus,fieldset[disabled] .wf-btn-danger:hover,fieldset[disabled] .wf-btn-danger:focus,fieldset[disabled] .wf-btn-danger.wf-focus{background-color:#d9534f;border-color:#d43f3a}.wf-btn-danger .wf-badge{color:#d9534f;background-color:#fff}.wf-btn-callout{font-weight:bold;text-transform:uppercase}.wf-btn-link{color:#00709e;font-weight:normal;border-radius:0}.wf-btn-link,.wf-btn-link:active,.wf-btn-link.wf-active,.wf-btn-link[disabled],fieldset[disabled] .wf-btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.wf-btn-link,.wf-btn-link:hover,.wf-btn-link:focus,.wf-btn-link:active{border-color:transparent}.wf-btn-link:hover,.wf-btn-link:focus{color:#003a52;text-decoration:underline;background-color:transparent}.wf-btn-link[disabled]:hover,.wf-btn-link[disabled]:focus,fieldset[disabled] .wf-btn-link:hover,fieldset[disabled] .wf-btn-link:focus{color:#777;text-decoration:none}.wf-btn-lg,.wf-btn-group-lg>.wf-btn{padding:10px 16px;font-size:18px;line-height:1.33333;border-radius:6px}.wf-btn-sm,.wf-btn-group-sm>.wf-btn{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px;text-transform:none}.wf-btn-xs,.wf-btn-group-xs>.wf-btn{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px;text-transform:none}.wf-btn-block{display:block;width:100%}.wf-btn-block+.wf-btn-block{margin-top:5px}input[type="submit"].wf-btn-block,input[type="reset"].wf-btn-block,input[type="button"].wf-btn-block{width:100%}.wf-btn-group,.wf-btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.wf-btn-group>.wf-btn,.wf-btn-group-vertical>.wf-btn{position:relative;float:left}.wf-btn-group>.wf-btn:hover,.wf-btn-group>.wf-btn:focus,.wf-btn-group>.wf-btn:active,.wf-btn-group>.wf-btn.wf-active,.wf-btn-group-vertical>.wf-btn:hover,.wf-btn-group-vertical>.wf-btn:focus,.wf-btn-group-vertical>.wf-btn:active,.wf-btn-group-vertical>.wf-btn.wf-active{z-index:2}.wf-btn-group .wf-btn+.wf-btn,.wf-btn-group .wf-btn+.wf-btn-group,.wf-btn-group .wf-btn-group+.wf-btn,.wf-btn-group .wf-btn-group+.wf-btn-group{margin-left:-1px}.wf-btn-toolbar{margin-left:-5px}.wf-btn-toolbar:before,.wf-btn-toolbar:after{content:" ";display:table}.wf-btn-toolbar:after{clear:both}.wf-btn-toolbar .wf-btn,.wf-btn-toolbar .wf-btn-group,.wf-btn-toolbar .wf-input-group{float:left}.wf-btn-toolbar>.wf-btn,.wf-btn-toolbar>.wf-btn-group,.wf-btn-toolbar>.wf-input-group{margin-left:5px}.wf-btn-group>.wf-btn:not(:first-child):not(:last-child):not(.wf-dropdown-toggle){border-radius:0}.wf-btn-group>.wf-btn:first-child{margin-left:0}.wf-btn-group>.wf-btn:first-child:not(:last-child):not(.wf-dropdown-toggle){-moz-border-radius-topright:0;-webkit-border-top-right-radius:0;border-top-right-radius:0;-moz-border-radius-bottomright:0;-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0}.wf-btn-group>.wf-btn:last-child:not(:first-child),.wf-btn-group>.wf-dropdown-toggle:not(:first-child){-moz-border-radius-topleft:0;-webkit-border-top-left-radius:0;border-top-left-radius:0;-moz-border-radius-bottomleft:0;-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0}.wf-btn-group>.wf-btn-group{float:left}.wf-btn-group>.wf-btn-group:not(:first-child):not(:last-child)>.wf-btn{border-radius:0}.wf-btn-group>.wf-btn-group:first-child:not(:last-child)>.wf-btn:last-child,.wf-btn-group>.wf-btn-group:first-child:not(:last-child)>.wf-dropdown-toggle{-moz-border-radius-topright:0;-webkit-border-top-right-radius:0;border-top-right-radius:0;-moz-border-radius-bottomright:0;-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0}.wf-btn-group>.wf-btn-group:last-child:not(:first-child)>.wf-btn:first-child{-moz-border-radius-topleft:0;-webkit-border-top-left-radius:0;border-top-left-radius:0;-moz-border-radius-bottomleft:0;-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0}.wf-btn-group .wf-dropdown-toggle:active,.wf-btn-group.wf-open .wf-dropdown-toggle{outline:0}.wf-btn-group>.wf-btn+.wf-dropdown-toggle{padding-left:8px;padding-right:8px}.wf-btn-group>.wf-btn-lg+.wf-dropdown-toggle,.wf-btn-group-lg.wf-btn-group>.wf-btn+.wf-dropdown-toggle{padding-left:12px;padding-right:12px}.wf-btn-group.open .wf-dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.wf-btn-group.open .wf-dropdown-toggle.wf-btn-link{-webkit-box-shadow:none;box-shadow:none}.wf-btn .wf-caret{margin-left:0}.wf-btn-lg .wf-caret,.wf-btn-group-lg>.wf-btn .wf-caret{border-width:5px 5px 0;border-bottom-width:0}.wf-dropup .wf-btn-lg .wf-caret,.wf-dropup .wf-btn-group-lg>.wf-btn .wf-caret{border-width:0 5px 5px}.wf-btn-group-vertical>.wf-btn,.wf-btn-group-vertical>.wf-btn-group,.wf-btn-group-vertical>.wf-btn-group>.wf-btn{display:block;float:none;width:100%;max-width:100%}.wf-btn-group-vertical>.wf-btn-group:before,.wf-btn-group-vertical>.wf-btn-group:after{content:" ";display:table}.wf-btn-group-vertical>.wf-btn-group:after{clear:both}.wf-btn-group-vertical>.wf-btn-group>.wf-btn{float:none}.wf-btn-group-vertical>.wf-btn+.wf-btn,.wf-btn-group-vertical>.wf-btn+.wf-btn-group,.wf-btn-group-vertical>.wf-btn-group+.wf-btn,.wf-btn-group-vertical>.wf-btn-group+.wf-btn-group{margin-top:-1px;margin-left:0}.wf-btn-group-vertical>.wf-btn:not(:first-child):not(:last-child){border-radius:0}.wf-btn-group-vertical>.wf-btn:first-child:not(:last-child){-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:4px;-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-bottomleft:0;-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;-moz-border-radius-bottomright:0;-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0}.wf-btn-group-vertical>.wf-btn:last-child:not(:first-child){-moz-border-radius-topleft:0;-webkit-border-top-left-radius:0;border-top-left-radius:0;-moz-border-radius-topright:0;-webkit-border-top-right-radius:0;border-top-right-radius:0;-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px}.wf-btn-group-vertical>.wf-btn-group:not(:first-child):not(:last-child)>.wf-btn{border-radius:0}.wf-btn-group-vertical>.wf-btn-group:first-child:not(:last-child)>.wf-btn:last-child,.wf-btn-group-vertical>.wf-btn-group:first-child:not(:last-child)>.wf-dropdown-toggle{-moz-border-radius-bottomleft:0;-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;-moz-border-radius-bottomright:0;-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0}.wf-btn-group-vertical>.wf-btn-group:last-child:not(:first-child)>.wf-btn:first-child{-moz-border-radius-topleft:0;-webkit-border-top-left-radius:0;border-top-left-radius:0;-moz-border-radius-topright:0;-webkit-border-top-right-radius:0;border-top-right-radius:0}.wf-btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.wf-btn-group-justified>.wf-btn,.wf-btn-group-justified>.wf-btn-group{float:none;display:table-cell;width:1%}.wf-btn-group-justified>.wf-btn-group .wf-btn{width:100%}.wf-btn-group-justified>.wf-btn-group .wf-dropdown-menu{left:auto}[data-toggle="buttons"]>.wf-btn input[type="radio"],[data-toggle="buttons"]>.wf-btn input[type="checkbox"],[data-toggle="buttons"]>.wf-btn-group>.wf-btn input[type="radio"],[data-toggle="buttons"]>.wf-btn-group>.wf-btn input[type="checkbox"]{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}.wf-pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.wf-pagination>li{display:inline}.wf-pagination>li>a,.wf-pagination>li>span{position:relative;float:left;padding:6px 12px;line-height:1.42857;text-decoration:none;color:#00709e;background-color:#fff;border:1px solid #ddd;margin-left:-1px}.wf-pagination>li:first-child>a,.wf-pagination>li:first-child>span{margin-left:0;-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px}.wf-pagination>li:last-child>a,.wf-pagination>li:last-child>span{-moz-border-radius-topright:4px;-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px}.wf-pagination>li>a:hover,.wf-pagination>li>a:focus,.wf-pagination>li>span:hover,.wf-pagination>li>span:focus{z-index:2;color:#003a52;background-color:#e2e2e2;border-color:#ddd}.wf-pagination>.wf-active>a,.wf-pagination>.wf-active>a:hover,.wf-pagination>.wf-active>a:focus,.wf-pagination>.wf-active>span,.wf-pagination>.wf-active>span:hover,.wf-pagination>.wf-active>span:focus{z-index:3;color:#fff;background-color:#00709e;border-color:#00709e;cursor:default}.wf-pagination>.wf-disabled>span,.wf-pagination>.wf-disabled>span:hover,.wf-pagination>.wf-disabled>span:focus,.wf-pagination>.wf-disabled>a,.wf-pagination>.wf-disabled>a:hover,.wf-pagination>.wf-disabled>a:focus{color:#777;background-color:#fff;border-color:#ddd;cursor:not-allowed}.wf-pagination-lg>li>a,.wf-pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.33333}.wf-pagination-lg>li:first-child>a,.wf-pagination-lg>li:first-child>span{-moz-border-radius-topleft:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px}.wf-pagination-lg>li:last-child>a,.wf-pagination-lg>li:last-child>span{-moz-border-radius-topright:6px;-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-moz-border-radius-bottomright:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px}.wf-pagination-sm>li>a,.wf-pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.wf-pagination-sm>li:first-child>a,.wf-pagination-sm>li:first-child>span{-moz-border-radius-topleft:3px;-webkit-border-top-left-radius:3px;border-top-left-radius:3px;-moz-border-radius-bottomleft:3px;-webkit-border-bottom-left-radius:3px;border-bottom-left-radius:3px}.wf-pagination-sm>li:last-child>a,.wf-pagination-sm>li:last-child>span{-moz-border-radius-topright:3px;-webkit-border-top-right-radius:3px;border-top-right-radius:3px;-moz-border-radius-bottomright:3px;-webkit-border-bottom-right-radius:3px;border-bottom-right-radius:3px}#wf-adminbar-icon{float:left;width:20px;height:30px;background-image:url(../images/wordfence-logo-16x16.png);background-repeat:no-repeat;background-position:left center}#wpadminbar .wf-notification-counter{display:inline;background-color:inherit}#wpadminbar .wf-notification-counter span.wf-count{padding:1px 7px 1px 6px !important;border-radius:50%;color:#fff;background-color:#fcb214}#wpadminbar .ab-sub-wrapper #wp-admin-bar-wordfence-menu-default .ab-item{display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:space-between;justify-content:space-between}#wpadminbar .ab-sub-wrapper #wp-admin-bar-wordfence-menu-default .ab-item .wf-adminbar-submenu-title{-webkit-flex-grow:1;flex-grow:1}#wpadminbar .ab-sub-wrapper #wp-admin-bar-wordfence-menu-default .ab-item .wf-notification-counter{display:block}#wpadminbar .ab-sub-wrapper #wp-admin-bar-wordfence-menu-default .ab-item .wf-adminbar-status{width:1.25rem;font-size:1.25rem;text-align:center}#wpadminbar .ab-sub-wrapper #wp-admin-bar-wordfence-menu-default .ab-item .wf-adminbar-status-neutral{color:#9f9fa0}#wpadminbar .ab-sub-wrapper #wp-admin-bar-wordfence-menu-default .ab-item .wf-adminbar-status-good{color:#16bc9b}#wpadminbar .ab-sub-wrapper #wp-admin-bar-wordfence-menu-default .ab-item .wf-adminbar-status-bad{color:#ffd10a}#adminmenu .update-plugins.wf-menu-badge{background-color:#fcb214 !important}.wf-hidden{display:none !important}.wfpopover{position:fixed;top:0;left:0;z-index:106000;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:normal;letter-spacing:normal;line-break:auto;line-height:1.42857;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;white-space:normal;word-break:normal;word-spacing:normal;word-wrap:normal;font-size:14px;background-color:#fff;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2)}.wfpopover.wf-top{margin-top:-10px}.wfpopover.wf-right{margin-left:10px}.wfpopover.wf-bottom{margin-top:10px}.wfpopover.wf-left{margin-left:-10px}.wfpopover-title{margin:0;padding:8px 14px;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.wfpopover-content{padding:9px 14px}.wfpopover>.wf-arrow,.wfpopover>.wf-arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.wfpopover>.wf-arrow{border-width:11px}.wfpopover>.wf-arrow:after{border-width:10px;content:""}.wfpopover.wf-top>.wf-arrow{left:50%;margin-left:-11px;border-bottom-width:0;border-top-color:#999;border-top-color:rgba(0,0,0,0.25);bottom:-11px}.wfpopover.wf-top>.wf-arrow:after{content:" ";bottom:1px;margin-left:-10px;border-bottom-width:0;border-top-color:#fff}.wfpopover.wf-right>.wf-arrow{top:50%;left:-11px;margin-top:-11px;border-left-width:0;border-right-color:#999;border-right-color:rgba(0,0,0,0.25)}.wfpopover.wf-right>.wf-arrow:after{content:" ";left:1px;bottom:-10px;border-left-width:0;border-right-color:#fff}.wfpopover.wf-bottom>.wf-arrow{left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,0.25);top:-11px}.wfpopover.wf-bottom>.wf-arrow:after{content:" ";top:1px;margin-left:-10px;border-top-width:0;border-bottom-color:#fff}.wfpopover.wf-left>.wf-arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,0.25)}.wfpopover.wf-left>.wf-arrow:after{content:" ";right:1px;border-right-width:0;border-left-color:#fff;bottom:-10px}
1
+ .wf-clearfix:before,.wf-clearfix:after{content:" ";display:table}.wf-clearfix:after{clear:both}.wf-btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;vertical-align:middle;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.wf-btn:focus,.wf-btn.wf-focus,.wf-btn:active:focus,.wf-btn:active.wf-focus,.wf-btn.wf-active:focus,.wf-btn.wf-active.wf-focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.wf-btn:hover,.wf-btn:focus,.wf-btn.wf-focus{color:#333;text-decoration:none}.wf-btn:active,.wf-btn.wf-active{outline:0;background-image:none;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.wf-btn.wf-disabled,.wf-btn[disabled],.wf-btn[readonly],fieldset[disabled] .wf-btn{cursor:not-allowed;filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=65);opacity:.65;-webkit-box-shadow:none;box-shadow:none}a.wf-btn{text-decoration:none}a.wf-btn.wf-disabled,fieldset[disabled] a.wf-btn{pointer-events:none}.wf-btn-default{color:#333;background-color:#fff;border-color:#ccc}.wf-btn-default:focus,.wf-btn-default.focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.wf-btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.wf-btn-default:active,.wf-btn-default.active,.wf-open>.wf-btn-default.wf-dropdown-toggle{color:#333;background-color:#e6e6e6;border-color:#adadad}.wf-btn-default:active:hover,.wf-btn-default:active:focus,.wf-btn-default:active.focus,.wf-btn-default.active:hover,.wf-btn-default.active:focus,.wf-btn-default.active.focus,.wf-open>.wf-btn-default.wf-dropdown-toggle:hover,.wf-open>.wf-btn-default.wf-dropdown-toggle:focus,.wf-open>.wf-btn-default.wf-dropdown-toggle.focus{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.wf-btn-default:active,.wf-btn-default.wf-active,.wf-open>.wf-btn-default.wf-dropdown-toggle{background-image:none}.wf-btn-default.wf-disabled:hover,.wf-btn-default.wf-disabled:focus,.wf-btn-default.wf-disabled.wf-focus,.wf-btn-default[disabled]:hover,.wf-btn-default[disabled]:focus,.wf-btn-default[disabled].wf-focus,fieldset[disabled] .wf-btn-default:hover,fieldset[disabled] .wf-btn-default:focus,fieldset[disabled] .wf-btn-default.wf-focus{background-color:#fff;border-color:#ccc}.wf-btn-default .wf-badge{color:#fff;background-color:#333}.wf-btn-primary{color:#fff;background-color:#00709e;border-color:#005e85}.wf-btn-primary:focus,.wf-btn-primary.focus{color:#fff;background-color:#004c6b;border-color:#000405}.wf-btn-primary:hover{color:#fff;background-color:#004c6b;border-color:#003347}.wf-btn-primary:active,.wf-btn-primary.active,.wf-open>.wf-btn-primary.wf-dropdown-toggle{color:#fff;background-color:#004c6b;border-color:#003347}.wf-btn-primary:active:hover,.wf-btn-primary:active:focus,.wf-btn-primary:active.focus,.wf-btn-primary.active:hover,.wf-btn-primary.active:focus,.wf-btn-primary.active.focus,.wf-open>.wf-btn-primary.wf-dropdown-toggle:hover,.wf-open>.wf-btn-primary.wf-dropdown-toggle:focus,.wf-open>.wf-btn-primary.wf-dropdown-toggle.focus{color:#fff;background-color:#003347;border-color:#000405}.wf-btn-primary:active,.wf-btn-primary.wf-active,.wf-open>.wf-btn-primary.wf-dropdown-toggle{background-image:none}.wf-btn-primary.wf-disabled:hover,.wf-btn-primary.wf-disabled:focus,.wf-btn-primary.wf-disabled.wf-focus,.wf-btn-primary[disabled]:hover,.wf-btn-primary[disabled]:focus,.wf-btn-primary[disabled].wf-focus,fieldset[disabled] .wf-btn-primary:hover,fieldset[disabled] .wf-btn-primary:focus,fieldset[disabled] .wf-btn-primary.wf-focus{background-color:#00709e;border-color:#005e85}.wf-btn-primary .wf-badge{color:#00709e;background-color:#fff}.wf-btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.wf-btn-success:focus,.wf-btn-success.focus{color:#fff;background-color:#449d44;border-color:#255625}.wf-btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.wf-btn-success:active,.wf-btn-success.active,.wf-open>.wf-btn-success.wf-dropdown-toggle{color:#fff;background-color:#449d44;border-color:#398439}.wf-btn-success:active:hover,.wf-btn-success:active:focus,.wf-btn-success:active.focus,.wf-btn-success.active:hover,.wf-btn-success.active:focus,.wf-btn-success.active.focus,.wf-open>.wf-btn-success.wf-dropdown-toggle:hover,.wf-open>.wf-btn-success.wf-dropdown-toggle:focus,.wf-open>.wf-btn-success.wf-dropdown-toggle.focus{color:#fff;background-color:#398439;border-color:#255625}.wf-btn-success:active,.wf-btn-success.wf-active,.wf-open>.wf-btn-success.wf-dropdown-toggle{background-image:none}.wf-btn-success.wf-disabled:hover,.wf-btn-success.wf-disabled:focus,.wf-btn-success.wf-disabled.wf-focus,.wf-btn-success[disabled]:hover,.wf-btn-success[disabled]:focus,.wf-btn-success[disabled].wf-focus,fieldset[disabled] .wf-btn-success:hover,fieldset[disabled] .wf-btn-success:focus,fieldset[disabled] .wf-btn-success.wf-focus{background-color:#5cb85c;border-color:#4cae4c}.wf-btn-success .wf-badge{color:#5cb85c;background-color:#fff}.wf-btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.wf-btn-info:focus,.wf-btn-info.focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.wf-btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.wf-btn-info:active,.wf-btn-info.active,.wf-open>.wf-btn-info.wf-dropdown-toggle{color:#fff;background-color:#31b0d5;border-color:#269abc}.wf-btn-info:active:hover,.wf-btn-info:active:focus,.wf-btn-info:active.focus,.wf-btn-info.active:hover,.wf-btn-info.active:focus,.wf-btn-info.active.focus,.wf-open>.wf-btn-info.wf-dropdown-toggle:hover,.wf-open>.wf-btn-info.wf-dropdown-toggle:focus,.wf-open>.wf-btn-info.wf-dropdown-toggle.focus{color:#fff;background-color:#269abc;border-color:#1b6d85}.wf-btn-info:active,.wf-btn-info.wf-active,.wf-open>.wf-btn-info.wf-dropdown-toggle{background-image:none}.wf-btn-info.wf-disabled:hover,.wf-btn-info.wf-disabled:focus,.wf-btn-info.wf-disabled.wf-focus,.wf-btn-info[disabled]:hover,.wf-btn-info[disabled]:focus,.wf-btn-info[disabled].wf-focus,fieldset[disabled] .wf-btn-info:hover,fieldset[disabled] .wf-btn-info:focus,fieldset[disabled] .wf-btn-info.wf-focus{background-color:#5bc0de;border-color:#46b8da}.wf-btn-info .wf-badge{color:#5bc0de;background-color:#fff}.wf-btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.wf-btn-warning:focus,.wf-btn-warning.focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.wf-btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.wf-btn-warning:active,.wf-btn-warning.active,.wf-open>.wf-btn-warning.wf-dropdown-toggle{color:#fff;background-color:#ec971f;border-color:#d58512}.wf-btn-warning:active:hover,.wf-btn-warning:active:focus,.wf-btn-warning:active.focus,.wf-btn-warning.active:hover,.wf-btn-warning.active:focus,.wf-btn-warning.active.focus,.wf-open>.wf-btn-warning.wf-dropdown-toggle:hover,.wf-open>.wf-btn-warning.wf-dropdown-toggle:focus,.wf-open>.wf-btn-warning.wf-dropdown-toggle.focus{color:#fff;background-color:#d58512;border-color:#985f0d}.wf-btn-warning:active,.wf-btn-warning.wf-active,.wf-open>.wf-btn-warning.wf-dropdown-toggle{background-image:none}.wf-btn-warning.wf-disabled:hover,.wf-btn-warning.wf-disabled:focus,.wf-btn-warning.wf-disabled.wf-focus,.wf-btn-warning[disabled]:hover,.wf-btn-warning[disabled]:focus,.wf-btn-warning[disabled].wf-focus,fieldset[disabled] .wf-btn-warning:hover,fieldset[disabled] .wf-btn-warning:focus,fieldset[disabled] .wf-btn-warning.wf-focus{background-color:#f0ad4e;border-color:#eea236}.wf-btn-warning .wf-badge{color:#f0ad4e;background-color:#fff}.wf-btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.wf-btn-danger:focus,.wf-btn-danger.focus{color:#fff;background-color:#c9302c;border-color:#761c19}.wf-btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.wf-btn-danger:active,.wf-btn-danger.active,.wf-open>.wf-btn-danger.wf-dropdown-toggle{color:#fff;background-color:#c9302c;border-color:#ac2925}.wf-btn-danger:active:hover,.wf-btn-danger:active:focus,.wf-btn-danger:active.focus,.wf-btn-danger.active:hover,.wf-btn-danger.active:focus,.wf-btn-danger.active.focus,.wf-open>.wf-btn-danger.wf-dropdown-toggle:hover,.wf-open>.wf-btn-danger.wf-dropdown-toggle:focus,.wf-open>.wf-btn-danger.wf-dropdown-toggle.focus{color:#fff;background-color:#ac2925;border-color:#761c19}.wf-btn-danger:active,.wf-btn-danger.wf-active,.wf-open>.wf-btn-danger.wf-dropdown-toggle{background-image:none}.wf-btn-danger.wf-disabled:hover,.wf-btn-danger.wf-disabled:focus,.wf-btn-danger.wf-disabled.wf-focus,.wf-btn-danger[disabled]:hover,.wf-btn-danger[disabled]:focus,.wf-btn-danger[disabled].wf-focus,fieldset[disabled] .wf-btn-danger:hover,fieldset[disabled] .wf-btn-danger:focus,fieldset[disabled] .wf-btn-danger.wf-focus{background-color:#d9534f;border-color:#d43f3a}.wf-btn-danger .wf-badge{color:#d9534f;background-color:#fff}.wf-btn-callout{font-weight:bold;text-transform:uppercase}.wf-btn-link{color:#00709e;font-weight:normal;border-radius:0}.wf-btn-link,.wf-btn-link:active,.wf-btn-link.wf-active,.wf-btn-link[disabled],fieldset[disabled] .wf-btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.wf-btn-link,.wf-btn-link:hover,.wf-btn-link:focus,.wf-btn-link:active{border-color:transparent}.wf-btn-link:hover,.wf-btn-link:focus{color:#003a52;text-decoration:underline;background-color:transparent}.wf-btn-link[disabled]:hover,.wf-btn-link[disabled]:focus,fieldset[disabled] .wf-btn-link:hover,fieldset[disabled] .wf-btn-link:focus{color:#777;text-decoration:none}.wf-btn-lg,.wf-btn-group-lg>.wf-btn{padding:10px 16px;font-size:18px;line-height:1.33333;border-radius:6px}.wf-btn-sm,.wf-btn-group-sm>.wf-btn{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px;text-transform:none}.wf-btn-xs,.wf-btn-group-xs>.wf-btn{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px;text-transform:none}.wf-btn-block{display:block;width:100%}.wf-btn-block+.wf-btn-block{margin-top:5px}input[type="submit"].wf-btn-block,input[type="reset"].wf-btn-block,input[type="button"].wf-btn-block{width:100%}.wf-btn-group,.wf-btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.wf-btn-group>.wf-btn,.wf-btn-group-vertical>.wf-btn{position:relative;float:left}.wf-btn-group>.wf-btn:hover,.wf-btn-group>.wf-btn:focus,.wf-btn-group>.wf-btn:active,.wf-btn-group>.wf-btn.wf-active,.wf-btn-group-vertical>.wf-btn:hover,.wf-btn-group-vertical>.wf-btn:focus,.wf-btn-group-vertical>.wf-btn:active,.wf-btn-group-vertical>.wf-btn.wf-active{z-index:2}.wf-btn-group .wf-btn+.wf-btn,.wf-btn-group .wf-btn+.wf-btn-group,.wf-btn-group .wf-btn-group+.wf-btn,.wf-btn-group .wf-btn-group+.wf-btn-group{margin-left:-1px}.wf-btn-toolbar{margin-left:-5px}.wf-btn-toolbar:before,.wf-btn-toolbar:after{content:" ";display:table}.wf-btn-toolbar:after{clear:both}.wf-btn-toolbar .wf-btn,.wf-btn-toolbar .wf-btn-group,.wf-btn-toolbar .wf-input-group{float:left}.wf-btn-toolbar>.wf-btn,.wf-btn-toolbar>.wf-btn-group,.wf-btn-toolbar>.wf-input-group{margin-left:5px}.wf-btn-group>.wf-btn:not(:first-child):not(:last-child):not(.wf-dropdown-toggle){border-radius:0}.wf-btn-group>.wf-btn:first-child{margin-left:0}.wf-btn-group>.wf-btn:first-child:not(:last-child):not(.wf-dropdown-toggle){-moz-border-radius-topright:0;-webkit-border-top-right-radius:0;border-top-right-radius:0;-moz-border-radius-bottomright:0;-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0}.wf-btn-group>.wf-btn:last-child:not(:first-child),.wf-btn-group>.wf-dropdown-toggle:not(:first-child){-moz-border-radius-topleft:0;-webkit-border-top-left-radius:0;border-top-left-radius:0;-moz-border-radius-bottomleft:0;-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0}.wf-btn-group>.wf-btn-group{float:left}.wf-btn-group>.wf-btn-group:not(:first-child):not(:last-child)>.wf-btn{border-radius:0}.wf-btn-group>.wf-btn-group:first-child:not(:last-child)>.wf-btn:last-child,.wf-btn-group>.wf-btn-group:first-child:not(:last-child)>.wf-dropdown-toggle{-moz-border-radius-topright:0;-webkit-border-top-right-radius:0;border-top-right-radius:0;-moz-border-radius-bottomright:0;-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0}.wf-btn-group>.wf-btn-group:last-child:not(:first-child)>.wf-btn:first-child{-moz-border-radius-topleft:0;-webkit-border-top-left-radius:0;border-top-left-radius:0;-moz-border-radius-bottomleft:0;-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0}.wf-btn-group .wf-dropdown-toggle:active,.wf-btn-group.wf-open .wf-dropdown-toggle{outline:0}.wf-btn-group>.wf-btn+.wf-dropdown-toggle{padding-left:8px;padding-right:8px}.wf-btn-group>.wf-btn-lg+.wf-dropdown-toggle,.wf-btn-group-lg.wf-btn-group>.wf-btn+.wf-dropdown-toggle{padding-left:12px;padding-right:12px}.wf-btn-group.open .wf-dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.wf-btn-group.open .wf-dropdown-toggle.wf-btn-link{-webkit-box-shadow:none;box-shadow:none}.wf-btn .wf-caret{margin-left:0}.wf-btn-lg .wf-caret,.wf-btn-group-lg>.wf-btn .wf-caret{border-width:5px 5px 0;border-bottom-width:0}.wf-dropup .wf-btn-lg .wf-caret,.wf-dropup .wf-btn-group-lg>.wf-btn .wf-caret{border-width:0 5px 5px}.wf-btn-group-vertical>.wf-btn,.wf-btn-group-vertical>.wf-btn-group,.wf-btn-group-vertical>.wf-btn-group>.wf-btn{display:block;float:none;width:100%;max-width:100%}.wf-btn-group-vertical>.wf-btn-group:before,.wf-btn-group-vertical>.wf-btn-group:after{content:" ";display:table}.wf-btn-group-vertical>.wf-btn-group:after{clear:both}.wf-btn-group-vertical>.wf-btn-group>.wf-btn{float:none}.wf-btn-group-vertical>.wf-btn+.wf-btn,.wf-btn-group-vertical>.wf-btn+.wf-btn-group,.wf-btn-group-vertical>.wf-btn-group+.wf-btn,.wf-btn-group-vertical>.wf-btn-group+.wf-btn-group{margin-top:-1px;margin-left:0}.wf-btn-group-vertical>.wf-btn:not(:first-child):not(:last-child){border-radius:0}.wf-btn-group-vertical>.wf-btn:first-child:not(:last-child){-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:4px;-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-bottomleft:0;-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;-moz-border-radius-bottomright:0;-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0}.wf-btn-group-vertical>.wf-btn:last-child:not(:first-child){-moz-border-radius-topleft:0;-webkit-border-top-left-radius:0;border-top-left-radius:0;-moz-border-radius-topright:0;-webkit-border-top-right-radius:0;border-top-right-radius:0;-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px}.wf-btn-group-vertical>.wf-btn-group:not(:first-child):not(:last-child)>.wf-btn{border-radius:0}.wf-btn-group-vertical>.wf-btn-group:first-child:not(:last-child)>.wf-btn:last-child,.wf-btn-group-vertical>.wf-btn-group:first-child:not(:last-child)>.wf-dropdown-toggle{-moz-border-radius-bottomleft:0;-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;-moz-border-radius-bottomright:0;-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0}.wf-btn-group-vertical>.wf-btn-group:last-child:not(:first-child)>.wf-btn:first-child{-moz-border-radius-topleft:0;-webkit-border-top-left-radius:0;border-top-left-radius:0;-moz-border-radius-topright:0;-webkit-border-top-right-radius:0;border-top-right-radius:0}.wf-btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.wf-btn-group-justified>.wf-btn,.wf-btn-group-justified>.wf-btn-group{float:none;display:table-cell;width:1%}.wf-btn-group-justified>.wf-btn-group .wf-btn{width:100%}.wf-btn-group-justified>.wf-btn-group .wf-dropdown-menu{left:auto}[data-toggle="buttons"]>.wf-btn input[type="radio"],[data-toggle="buttons"]>.wf-btn input[type="checkbox"],[data-toggle="buttons"]>.wf-btn-group>.wf-btn input[type="radio"],[data-toggle="buttons"]>.wf-btn-group>.wf-btn input[type="checkbox"]{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}.wf-pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.wf-pagination>li{display:inline}.wf-pagination>li>a,.wf-pagination>li>span{position:relative;float:left;padding:6px 12px;line-height:1.42857;text-decoration:none;color:#00709e;background-color:#fff;border:1px solid #ddd;margin-left:-1px}.wf-pagination>li:first-child>a,.wf-pagination>li:first-child>span{margin-left:0;-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px}.wf-pagination>li:last-child>a,.wf-pagination>li:last-child>span{-moz-border-radius-topright:4px;-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px}.wf-pagination>li>a:hover,.wf-pagination>li>a:focus,.wf-pagination>li>span:hover,.wf-pagination>li>span:focus{z-index:2;color:#003a52;background-color:#e2e2e2;border-color:#ddd}.wf-pagination>.wf-active>a,.wf-pagination>.wf-active>a:hover,.wf-pagination>.wf-active>a:focus,.wf-pagination>.wf-active>span,.wf-pagination>.wf-active>span:hover,.wf-pagination>.wf-active>span:focus{z-index:3;color:#fff;background-color:#00709e;border-color:#00709e;cursor:default}.wf-pagination>.wf-disabled>span,.wf-pagination>.wf-disabled>span:hover,.wf-pagination>.wf-disabled>span:focus,.wf-pagination>.wf-disabled>a,.wf-pagination>.wf-disabled>a:hover,.wf-pagination>.wf-disabled>a:focus{color:#777;background-color:#fff;border-color:#ddd;cursor:not-allowed}.wf-pagination-lg>li>a,.wf-pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.33333}.wf-pagination-lg>li:first-child>a,.wf-pagination-lg>li:first-child>span{-moz-border-radius-topleft:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px}.wf-pagination-lg>li:last-child>a,.wf-pagination-lg>li:last-child>span{-moz-border-radius-topright:6px;-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-moz-border-radius-bottomright:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px}.wf-pagination-sm>li>a,.wf-pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.wf-pagination-sm>li:first-child>a,.wf-pagination-sm>li:first-child>span{-moz-border-radius-topleft:3px;-webkit-border-top-left-radius:3px;border-top-left-radius:3px;-moz-border-radius-bottomleft:3px;-webkit-border-bottom-left-radius:3px;border-bottom-left-radius:3px}.wf-pagination-sm>li:last-child>a,.wf-pagination-sm>li:last-child>span{-moz-border-radius-topright:3px;-webkit-border-top-right-radius:3px;border-top-right-radius:3px;-moz-border-radius-bottomright:3px;-webkit-border-bottom-right-radius:3px;border-bottom-right-radius:3px}#wf-adminbar-icon{float:left;width:20px;height:30px;background-image:url(../images/wordfence-logo-16x16.png);background-repeat:no-repeat;background-position:left center}#wpadminbar .wf-notification-counter{display:inline;background-color:inherit}#wpadminbar .wf-notification-counter span.wf-count{padding:1px 7px 1px 6px !important;border-radius:50%;color:#fff;background-color:#fcb214}#wpadminbar .ab-sub-wrapper #wp-admin-bar-wordfence-menu-default .ab-item{display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:space-between;justify-content:space-between}#wpadminbar .ab-sub-wrapper #wp-admin-bar-wordfence-menu-default .ab-item .wf-adminbar-submenu-title{-webkit-flex-grow:1;flex-grow:1}#wpadminbar .ab-sub-wrapper #wp-admin-bar-wordfence-menu-default .ab-item .wf-notification-counter{display:block}#wpadminbar .ab-sub-wrapper #wp-admin-bar-wordfence-menu-default .ab-item .wf-adminbar-status{width:1.25rem;font-size:1.25rem;text-align:center}#wpadminbar .ab-sub-wrapper #wp-admin-bar-wordfence-menu-default .ab-item .wf-adminbar-status-neutral{color:#9f9fa0}#wpadminbar .ab-sub-wrapper #wp-admin-bar-wordfence-menu-default .ab-item .wf-adminbar-status-good{color:#16bc9b}#wpadminbar .ab-sub-wrapper #wp-admin-bar-wordfence-menu-default .ab-item .wf-adminbar-status-bad{color:#ffd10a}#adminmenu .update-plugins.wf-menu-badge{background-color:#fcb214 !important}.wf-hidden{display:none !important}.wfpopover{position:fixed;top:0;left:0;z-index:106000;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:normal;letter-spacing:normal;line-break:auto;line-height:1.42857;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;white-space:normal;word-break:normal;word-spacing:normal;word-wrap:normal;font-size:14px;background-color:#fff;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2)}.wfpopover.wf-top{margin-top:-10px}.wfpopover.wf-right{margin-left:10px}.wfpopover.wf-bottom{margin-top:10px}.wfpopover.wf-left{margin-left:-10px}.wfpopover-title{margin:0;padding:8px 14px;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.wfpopover-content{padding:9px 14px}.wfpopover>.wf-arrow,.wfpopover>.wf-arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.wfpopover>.wf-arrow{border-width:11px}.wfpopover>.wf-arrow:after{border-width:10px;content:""}.wfpopover.wf-top>.wf-arrow{left:50%;margin-left:-11px;border-bottom-width:0;border-top-color:#999;border-top-color:rgba(0,0,0,0.25);bottom:-11px}.wfpopover.wf-top>.wf-arrow:after{content:" ";bottom:1px;margin-left:-10px;border-bottom-width:0;border-top-color:#fff}.wfpopover.wf-right>.wf-arrow{top:50%;left:-11px;margin-top:-11px;border-left-width:0;border-right-color:#999;border-right-color:rgba(0,0,0,0.25)}.wfpopover.wf-right>.wf-arrow:after{content:" ";left:1px;bottom:-10px;border-left-width:0;border-right-color:#fff}.wfpopover.wf-bottom>.wf-arrow{left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,0.25);top:-11px}.wfpopover.wf-bottom>.wf-arrow:after{content:" ";top:1px;margin-left:-10px;border-top-width:0;border-bottom-color:#fff}.wfpopover.wf-left>.wf-arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,0.25)}.wfpopover.wf-left>.wf-arrow:after{content:" ";right:1px;border-right-width:0;border-left-color:#fff;bottom:-10px}
images/sort_asc.png ADDED
Binary file
images/sort_asc_disabled.png ADDED
Binary file
images/sort_both.png ADDED
Binary file
images/sort_desc.png ADDED
Binary file
images/sort_desc_disabled.png ADDED
Binary file
js/admin.js CHANGED
@@ -420,9 +420,16 @@
420
  window.location.href = 'admin.php?page=' + menuItem;
421
  },
422
  updateConfig: function(key, val, cb) {
423
- this.ajax('wordfence_updateConfig', {key: key, val: val}, function() {
424
  if (cb) {
425
- cb();
 
 
 
 
 
 
 
426
  }
427
  });
428
  },
@@ -960,7 +967,7 @@
960
  }
961
  jQuery('#' + containerID).html('<table cellpadding="0" cellspacing="0" border="0" class="display wf-issues-table" id="' + tableID + '"></table>');
962
 
963
- jQuery.fn.dataTableExt.oSort['severity-asc'] = function(y, x) {
964
  x = WFAD.sev2num(x);
965
  y = WFAD.sev2num(y);
966
  if (x < y) {
@@ -971,7 +978,7 @@
971
  }
972
  return 0;
973
  };
974
- jQuery.fn.dataTableExt.oSort['severity-desc'] = function(y, x) {
975
  x = WFAD.sev2num(x);
976
  y = WFAD.sev2num(y);
977
  if (x > y) {
@@ -983,33 +990,32 @@
983
  return 0;
984
  };
985
 
986
- jQuery('#' + tableID).dataTable({
987
- "bFilter": false,
988
- "bInfo": false,
989
- "bPaginate": false,
990
- "bLengthChange": false,
991
- "bAutoWidth": false,
992
- //"aaData": res.issuesLists[issueStatus],
993
- "aoColumns": [
994
  {
995
- "sTitle": '<div class="th_wrapp wf-hidden-xs">Severity</div>',
996
- //"sWidth": '128px',
997
- "sClass": "center wf-scan-severity",
998
- "sType": 'severity',
999
- "fnRender": function(obj) {
1000
- var cls = 'wfProbSev' + obj.aData.severity;
1001
- return '<span class="wf-hidden-xs ' + cls + '"></span><div class="wf-visible-xs wf-scan-severity-' + obj.aData.severity + '"></div>';
1002
  }
1003
  },
1004
  {
1005
- "sTitle": '<div class="th_wrapp">Issue</div>',
1006
- "bSortable": false,
1007
- //"sWidth": '400px',
1008
- "sType": 'html',
1009
- fnRender: function(obj) {
1010
- var issueType = (obj.aData.type == 'knownfile' ? 'file' : obj.aData.type);
1011
  var tmplName = 'issueTmpl_' + issueType;
1012
- return jQuery('#' + tmplName).tmpl(obj.aData).html();
1013
  }
1014
  }
1015
  ]
@@ -1028,7 +1034,8 @@
1028
  continue;
1029
  }
1030
 
1031
- jQuery('#' + tableID).dataTable().fnAddData(issuesLists[issueStatus]);
 
1032
  }
1033
 
1034
  if (callback) {
@@ -2043,6 +2050,9 @@
2043
  } else {
2044
  self.pulse('.wfSavedMsg');
2045
  }
 
 
 
2046
  } else if (res.errorMsg) {
2047
  return;
2048
  } else {
420
  window.location.href = 'admin.php?page=' + menuItem;
421
  },
422
  updateConfig: function(key, val, cb) {
423
+ this.ajax('wordfence_updateConfig', {key: key, val: val}, function(ret) {
424
  if (cb) {
425
+ cb(ret);
426
+ }
427
+ });
428
+ },
429
+ updateIPPreview: function(val, cb) {
430
+ this.ajax('wordfence_updateIPPreview', val, function(ret) {
431
+ if (cb) {
432
+ cb(ret);
433
  }
434
  });
435
  },
967
  }
968
  jQuery('#' + containerID).html('<table cellpadding="0" cellspacing="0" border="0" class="display wf-issues-table" id="' + tableID + '"></table>');
969
 
970
+ jQuery.fn.wfDataTableExt.oSort['severity-asc'] = function(y, x) {
971
  x = WFAD.sev2num(x);
972
  y = WFAD.sev2num(y);
973
  if (x < y) {
978
  }
979
  return 0;
980
  };
981
+ jQuery.fn.wfDataTableExt.oSort['severity-desc'] = function(y, x) {
982
  x = WFAD.sev2num(x);
983
  y = WFAD.sev2num(y);
984
  if (x > y) {
990
  return 0;
991
  };
992
 
993
+ jQuery('#' + tableID).WFDataTable({
994
+ "searching": false,
995
+ "info": false,
996
+ "paging": false,
997
+ "lengthChange": false,
998
+ "autoWidth": false,
999
+ "columnDefs": [
 
1000
  {
1001
+ "targets": 0,
1002
+ "title": '<div class="th_wrapp wf-hidden-xs">Severity</div>',
1003
+ "className": "center wf-scan-severity",
1004
+ "type": 'severity',
1005
+ "render": function(data, type, row) {
1006
+ var cls = 'wfProbSev' + row.severity;
1007
+ return '<span class="wf-hidden-xs ' + cls + '"></span><div class="wf-visible-xs wf-scan-severity-' + row.severity + '"></div>';
1008
  }
1009
  },
1010
  {
1011
+ "targets": 1,
1012
+ "title": '<div class="th_wrapp">Issue</div>',
1013
+ "orderable": false,
1014
+ "type": 'html',
1015
+ "render": function(data, type, row) {
1016
+ var issueType = (row.type == 'knownfile' ? 'file' : row.type);
1017
  var tmplName = 'issueTmpl_' + issueType;
1018
+ return jQuery('#' + tmplName).tmpl(row).html();
1019
  }
1020
  }
1021
  ]
1034
  continue;
1035
  }
1036
 
1037
+ var table = jQuery('#' + tableID).WFDataTable();
1038
+ table.rows.add(issuesLists[issueStatus]).draw();
1039
  }
1040
 
1041
  if (callback) {
2050
  } else {
2051
  self.pulse('.wfSavedMsg');
2052
  }
2053
+
2054
+ $('#howGetIPs-preview-all').html(res.ipAll);
2055
+ $('#howGetIPs-preview-single').html(res.ip);
2056
  } else if (res.errorMsg) {
2057
  return;
2058
  } else {
js/jquery.dataTables.js ADDED
@@ -0,0 +1,15321 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * This combined file was created by the WFDataTables downloader builder:
3
+ * https://datatables.net/download
4
+ *
5
+ * To rebuild or modify this file with the latest versions of the included
6
+ * software please visit:
7
+ * https://datatables.net/download/#dt/dt-1.10.13
8
+ *
9
+ * Included libraries:
10
+ * WFDataTables 1.10.13
11
+ */
12
+
13
+ /*! WFDataTables 1.10.13
14
+ * ©2008-2016 SpryMedia Ltd - datatables.net/license
15
+ */
16
+
17
+ /**
18
+ * @summary WFDataTables
19
+ * @description Paginate, search and order HTML tables
20
+ * @version 1.10.13
21
+ * @file jquery.wfDataTables.js
22
+ * @author SpryMedia Ltd
23
+ * @contact www.datatables.net
24
+ * @copyright Copyright 2008-2016 SpryMedia Ltd.
25
+ *
26
+ * This source file is free software, available under the following license:
27
+ * MIT license - http://datatables.net/license
28
+ *
29
+ * This source file is distributed in the hope that it will be useful, but
30
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
31
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
32
+ *
33
+ * For details please refer to: http://www.datatables.net
34
+ */
35
+
36
+ /*jslint evil: true, undef: true, browser: true */
37
+ /*globals $,require,jQuery,define,_selector_run,_selector_opts,_selector_first,_selector_row_indexes,_ext,_Api,_api_register,_api_registerPlural,_re_new_lines,_re_html,_re_formatted_numeric,_re_escape_regex,_empty,_intVal,_numToDecimal,_isNumber,_isHtml,_htmlNumeric,_pluck,_pluck_order,_range,_stripHtml,_unique,_fnBuildAjax,_fnAjaxUpdate,_fnAjaxParameters,_fnAjaxUpdateDraw,_fnAjaxDataSrc,_fnAddColumn,_fnColumnOptions,_fnAdjustColumnSizing,_fnVisibleToColumnIndex,_fnColumnIndexToVisible,_fnVisbleColumns,_fnGetColumns,_fnColumnTypes,_fnApplyColumnDefs,_fnHungarianMap,_fnCamelToHungarian,_fnLanguageCompat,_fnBrowserDetect,_fnAddData,_fnAddTr,_fnNodeToDataIndex,_fnNodeToColumnIndex,_fnGetCellData,_fnSetCellData,_fnSplitObjNotation,_fnGetObjectDataFn,_fnSetObjectDataFn,_fnGetDataMaster,_fnClearTable,_fnDeleteIndex,_fnInvalidate,_fnGetRowElements,_fnCreateTr,_fnBuildHead,_fnDrawHead,_fnDraw,_fnReDraw,_fnAddOptionsHtml,_fnDetectHeader,_fnGetUniqueThs,_fnFeatureHtmlFilter,_fnFilterComplete,_fnFilterCustom,_fnFilterColumn,_fnFilter,_fnFilterCreateSearch,_fnEscapeRegex,_fnFilterData,_fnFeatureHtmlInfo,_fnUpdateInfo,_fnInfoMacros,_fnInitialise,_fnInitComplete,_fnLengthChange,_fnFeatureHtmlLength,_fnFeatureHtmlPaginate,_fnPageChange,_fnFeatureHtmlProcessing,_fnProcessingDisplay,_fnFeatureHtmlTable,_fnScrollDraw,_fnApplyToChildren,_fnCalculateColumnWidths,_fnThrottle,_fnConvertToWidth,_fnGetWidestNode,_fnGetMaxLenString,_fnStringToCss,_fnSortFlatten,_fnSort,_fnSortAria,_fnSortListener,_fnSortAttachListener,_fnSortingClasses,_fnSortData,_fnSaveState,_fnLoadState,_fnSettingsFromNode,_fnLog,_fnMap,_fnBindAction,_fnCallbackReg,_fnCallbackFire,_fnLengthOverflow,_fnRenderer,_fnDataSource,_fnRowAttributes*/
38
+
39
+ (function( factory ) {
40
+ "use strict";
41
+
42
+ if ( typeof define === 'function' && define.amd ) {
43
+ // AMD
44
+ define( ['jquery'], function ( $ ) {
45
+ return factory( $, window, document );
46
+ } );
47
+ }
48
+ else if ( typeof exports === 'object' ) {
49
+ // CommonJS
50
+ module.exports = function (root, $) {
51
+ if ( ! root ) {
52
+ // CommonJS environments without a window global must pass a
53
+ // root. This will give an error otherwise
54
+ root = window;
55
+ }
56
+
57
+ if ( ! $ ) {
58
+ $ = typeof window !== 'undefined' ? // jQuery's factory checks for a global window
59
+ require('jquery') :
60
+ require('jquery')( root );
61
+ }
62
+
63
+ return factory( $, root, root.document );
64
+ };
65
+ }
66
+ else {
67
+ // Browser
68
+ factory( jQuery, window, document );
69
+ }
70
+ }
71
+ (function( $, window, document, undefined ) {
72
+ "use strict";
73
+
74
+ /**
75
+ * WFDataTables is a plug-in for the jQuery Javascript library. It is a highly
76
+ * flexible tool, based upon the foundations of progressive enhancement,
77
+ * which will add advanced interaction controls to any HTML table. For a
78
+ * full list of features please refer to
79
+ * [WFDataTables.net](href="http://datatables.net).
80
+ *
81
+ * Note that the `WFDataTable` object is not a global variable but is aliased
82
+ * to `jQuery.fn.WFDataTable` and `jQuery.fn.wfDataTable` through which it may
83
+ * be accessed.
84
+ *
85
+ * @class
86
+ * @param {object} [init={}] Configuration object for WFDataTables. Options
87
+ * are defined by {@link WFDataTable.defaults}
88
+ * @requires jQuery 1.7+
89
+ *
90
+ * @example
91
+ * // Basic initialisation
92
+ * $(document).ready( function {
93
+ * $('#example').wfDataTable();
94
+ * } );
95
+ *
96
+ * @example
97
+ * // Initialisation with configuration options - in this case, disable
98
+ * // pagination and sorting.
99
+ * $(document).ready( function {
100
+ * $('#example').wfDataTable( {
101
+ * "paginate": false,
102
+ * "sort": false
103
+ * } );
104
+ * } );
105
+ */
106
+ var WFDataTable = function ( options )
107
+ {
108
+ /**
109
+ * Perform a jQuery selector action on the table's TR elements (from the tbody) and
110
+ * return the resulting jQuery object.
111
+ * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on
112
+ * @param {object} [oOpts] Optional parameters for modifying the rows to be included
113
+ * @param {string} [oOpts.filter=none] Select TR elements that meet the current filter
114
+ * criterion ("applied") or all TR elements (i.e. no filter).
115
+ * @param {string} [oOpts.order=current] Order of the TR elements in the processed array.
116
+ * Can be either 'current', whereby the current sorting of the table is used, or
117
+ * 'original' whereby the original order the data was read into the table is used.
118
+ * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page
119
+ * ("current") or not ("all"). If 'current' is given, then order is assumed to be
120
+ * 'current' and filter is 'applied', regardless of what they might be given as.
121
+ * @returns {object} jQuery object, filtered by the given selector.
122
+ * @dtopt API
123
+ * @deprecated Since v1.10
124
+ *
125
+ * @example
126
+ * $(document).ready(function() {
127
+ * var oTable = $('#example').wfDataTable();
128
+ *
129
+ * // Highlight every second row
130
+ * oTable.$('tr:odd').css('backgroundColor', 'blue');
131
+ * } );
132
+ *
133
+ * @example
134
+ * $(document).ready(function() {
135
+ * var oTable = $('#example').wfDataTable();
136
+ *
137
+ * // Filter to rows with 'Webkit' in them, add a background colour and then
138
+ * // remove the filter, thus highlighting the 'Webkit' rows only.
139
+ * oTable.fnFilter('Webkit');
140
+ * oTable.$('tr', {"search": "applied"}).css('backgroundColor', 'blue');
141
+ * oTable.fnFilter('');
142
+ * } );
143
+ */
144
+ this.$ = function ( sSelector, oOpts )
145
+ {
146
+ return this.api(true).$( sSelector, oOpts );
147
+ };
148
+
149
+
150
+ /**
151
+ * Almost identical to $ in operation, but in this case returns the data for the matched
152
+ * rows - as such, the jQuery selector used should match TR row nodes or TD/TH cell nodes
153
+ * rather than any descendants, so the data can be obtained for the row/cell. If matching
154
+ * rows are found, the data returned is the original data array/object that was used to
155
+ * create the row (or a generated array if from a DOM source).
156
+ *
157
+ * This method is often useful in-combination with $ where both functions are given the
158
+ * same parameters and the array indexes will match identically.
159
+ * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on
160
+ * @param {object} [oOpts] Optional parameters for modifying the rows to be included
161
+ * @param {string} [oOpts.filter=none] Select elements that meet the current filter
162
+ * criterion ("applied") or all elements (i.e. no filter).
163
+ * @param {string} [oOpts.order=current] Order of the data in the processed array.
164
+ * Can be either 'current', whereby the current sorting of the table is used, or
165
+ * 'original' whereby the original order the data was read into the table is used.
166
+ * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page
167
+ * ("current") or not ("all"). If 'current' is given, then order is assumed to be
168
+ * 'current' and filter is 'applied', regardless of what they might be given as.
169
+ * @returns {array} Data for the matched elements. If any elements, as a result of the
170
+ * selector, were not TR, TD or TH elements in the WFDataTable, they will have a null
171
+ * entry in the array.
172
+ * @dtopt API
173
+ * @deprecated Since v1.10
174
+ *
175
+ * @example
176
+ * $(document).ready(function() {
177
+ * var oTable = $('#example').wfDataTable();
178
+ *
179
+ * // Get the data from the first row in the table
180
+ * var data = oTable._('tr:first');
181
+ *
182
+ * // Do something useful with the data
183
+ * alert( "First cell is: "+data[0] );
184
+ * } );
185
+ *
186
+ * @example
187
+ * $(document).ready(function() {
188
+ * var oTable = $('#example').wfDataTable();
189
+ *
190
+ * // Filter to 'Webkit' and get all data for
191
+ * oTable.fnFilter('Webkit');
192
+ * var data = oTable._('tr', {"search": "applied"});
193
+ *
194
+ * // Do something with the data
195
+ * alert( data.length+" rows matched the search" );
196
+ * } );
197
+ */
198
+ this._ = function ( sSelector, oOpts )
199
+ {
200
+ return this.api(true).rows( sSelector, oOpts ).data();
201
+ };
202
+
203
+
204
+ /**
205
+ * Create a WFDataTables Api instance, with the currently selected tables for
206
+ * the Api's context.
207
+ * @param {boolean} [traditional=false] Set the API instance's context to be
208
+ * only the table referred to by the `WFDataTable.ext.iApiIndex` option, as was
209
+ * used in the API presented by WFDataTables 1.9- (i.e. the traditional mode),
210
+ * or if all tables captured in the jQuery object should be used.
211
+ * @return {WFDataTables.Api}
212
+ */
213
+ this.api = function ( traditional )
214
+ {
215
+ return traditional ?
216
+ new _Api(
217
+ _fnSettingsFromNode( this[ _ext.iApiIndex ] )
218
+ ) :
219
+ new _Api( this );
220
+ };
221
+
222
+
223
+ /**
224
+ * Add a single new row or multiple rows of data to the table. Please note
225
+ * that this is suitable for client-side processing only - if you are using
226
+ * server-side processing (i.e. "bServerSide": true), then to add data, you
227
+ * must add it to the data source, i.e. the server-side, through an Ajax call.
228
+ * @param {array|object} data The data to be added to the table. This can be:
229
+ * <ul>
230
+ * <li>1D array of data - add a single row with the data provided</li>
231
+ * <li>2D array of arrays - add multiple rows in a single call</li>
232
+ * <li>object - data object when using <i>mData</i></li>
233
+ * <li>array of objects - multiple data objects when using <i>mData</i></li>
234
+ * </ul>
235
+ * @param {bool} [redraw=true] redraw the table or not
236
+ * @returns {array} An array of integers, representing the list of indexes in
237
+ * <i>aoData</i> ({@link WFDataTable.models.oSettings}) that have been added to
238
+ * the table.
239
+ * @dtopt API
240
+ * @deprecated Since v1.10
241
+ *
242
+ * @example
243
+ * // Global var for counter
244
+ * var giCount = 2;
245
+ *
246
+ * $(document).ready(function() {
247
+ * $('#example').wfDataTable();
248
+ * } );
249
+ *
250
+ * function fnClickAddRow() {
251
+ * $('#example').wfDataTable().fnAddData( [
252
+ * giCount+".1",
253
+ * giCount+".2",
254
+ * giCount+".3",
255
+ * giCount+".4" ]
256
+ * );
257
+ *
258
+ * giCount++;
259
+ * }
260
+ */
261
+ this.fnAddData = function( data, redraw )
262
+ {
263
+ var api = this.api( true );
264
+
265
+ /* Check if we want to add multiple rows or not */
266
+ var rows = $.isArray(data) && ( $.isArray(data[0]) || $.isPlainObject(data[0]) ) ?
267
+ api.rows.add( data ) :
268
+ api.row.add( data );
269
+
270
+ if ( redraw === undefined || redraw ) {
271
+ api.draw();
272
+ }
273
+
274
+ return rows.flatten().toArray();
275
+ };
276
+
277
+
278
+ /**
279
+ * This function will make WFDataTables recalculate the column sizes, based on the data
280
+ * contained in the table and the sizes applied to the columns (in the DOM, CSS or
281
+ * through the sWidth parameter). This can be useful when the width of the table's
282
+ * parent element changes (for example a window resize).
283
+ * @param {boolean} [bRedraw=true] Redraw the table or not, you will typically want to
284
+ * @dtopt API
285
+ * @deprecated Since v1.10
286
+ *
287
+ * @example
288
+ * $(document).ready(function() {
289
+ * var oTable = $('#example').wfDataTable( {
290
+ * "sScrollY": "200px",
291
+ * "bPaginate": false
292
+ * } );
293
+ *
294
+ * $(window).on('resize', function () {
295
+ * oTable.fnAdjustColumnSizing();
296
+ * } );
297
+ * } );
298
+ */
299
+ this.fnAdjustColumnSizing = function ( bRedraw )
300
+ {
301
+ var api = this.api( true ).columns.adjust();
302
+ var settings = api.settings()[0];
303
+ var scroll = settings.oScroll;
304
+
305
+ if ( bRedraw === undefined || bRedraw ) {
306
+ api.draw( false );
307
+ }
308
+ else if ( scroll.sX !== "" || scroll.sY !== "" ) {
309
+ /* If not redrawing, but scrolling, we want to apply the new column sizes anyway */
310
+ _fnScrollDraw( settings );
311
+ }
312
+ };
313
+
314
+
315
+ /**
316
+ * Quickly and simply clear a table
317
+ * @param {bool} [bRedraw=true] redraw the table or not
318
+ * @dtopt API
319
+ * @deprecated Since v1.10
320
+ *
321
+ * @example
322
+ * $(document).ready(function() {
323
+ * var oTable = $('#example').wfDataTable();
324
+ *
325
+ * // Immediately 'nuke' the current rows (perhaps waiting for an Ajax callback...)
326
+ * oTable.fnClearTable();
327
+ * } );
328
+ */
329
+ this.fnClearTable = function( bRedraw )
330
+ {
331
+ var api = this.api( true ).clear();
332
+
333
+ if ( bRedraw === undefined || bRedraw ) {
334
+ api.draw();
335
+ }
336
+ };
337
+
338
+
339
+ /**
340
+ * The exact opposite of 'opening' a row, this function will close any rows which
341
+ * are currently 'open'.
342
+ * @param {node} nTr the table row to 'close'
343
+ * @returns {int} 0 on success, or 1 if failed (can't find the row)
344
+ * @dtopt API
345
+ * @deprecated Since v1.10
346
+ *
347
+ * @example
348
+ * $(document).ready(function() {
349
+ * var oTable;
350
+ *
351
+ * // 'open' an information row when a row is clicked on
352
+ * $('#example tbody tr').click( function () {
353
+ * if ( oTable.fnIsOpen(this) ) {
354
+ * oTable.fnClose( this );
355
+ * } else {
356
+ * oTable.fnOpen( this, "Temporary row opened", "info_row" );
357
+ * }
358
+ * } );
359
+ *
360
+ * oTable = $('#example').wfDataTable();
361
+ * } );
362
+ */
363
+ this.fnClose = function( nTr )
364
+ {
365
+ this.api( true ).row( nTr ).child.hide();
366
+ };
367
+
368
+
369
+ /**
370
+ * Remove a row for the table
371
+ * @param {mixed} target The index of the row from aoData to be deleted, or
372
+ * the TR element you want to delete
373
+ * @param {function|null} [callBack] Callback function
374
+ * @param {bool} [redraw=true] Redraw the table or not
375
+ * @returns {array} The row that was deleted
376
+ * @dtopt API
377
+ * @deprecated Since v1.10
378
+ *
379
+ * @example
380
+ * $(document).ready(function() {
381
+ * var oTable = $('#example').wfDataTable();
382
+ *
383
+ * // Immediately remove the first row
384
+ * oTable.fnDeleteRow( 0 );
385
+ * } );
386
+ */
387
+ this.fnDeleteRow = function( target, callback, redraw )
388
+ {
389
+ var api = this.api( true );
390
+ var rows = api.rows( target );
391
+ var settings = rows.settings()[0];
392
+ var data = settings.aoData[ rows[0][0] ];
393
+
394
+ rows.remove();
395
+
396
+ if ( callback ) {
397
+ callback.call( this, settings, data );
398
+ }
399
+
400
+ if ( redraw === undefined || redraw ) {
401
+ api.draw();
402
+ }
403
+
404
+ return data;
405
+ };
406
+
407
+
408
+ /**
409
+ * Restore the table to it's original state in the DOM by removing all of WFDataTables
410
+ * enhancements, alterations to the DOM structure of the table and event listeners.
411
+ * @param {boolean} [remove=false] Completely remove the table from the DOM
412
+ * @dtopt API
413
+ * @deprecated Since v1.10
414
+ *
415
+ * @example
416
+ * $(document).ready(function() {
417
+ * // This example is fairly pointless in reality, but shows how fnDestroy can be used
418
+ * var oTable = $('#example').wfDataTable();
419
+ * oTable.fnDestroy();
420
+ * } );
421
+ */
422
+ this.fnDestroy = function ( remove )
423
+ {
424
+ this.api( true ).destroy( remove );
425
+ };
426
+
427
+
428
+ /**
429
+ * Redraw the table
430
+ * @param {bool} [complete=true] Re-filter and resort (if enabled) the table before the draw.
431
+ * @dtopt API
432
+ * @deprecated Since v1.10
433
+ *
434
+ * @example
435
+ * $(document).ready(function() {
436
+ * var oTable = $('#example').wfDataTable();
437
+ *
438
+ * // Re-draw the table - you wouldn't want to do it here, but it's an example :-)
439
+ * oTable.fnDraw();
440
+ * } );
441
+ */
442
+ this.fnDraw = function( complete )
443
+ {
444
+ // Note that this isn't an exact match to the old call to _fnDraw - it takes
445
+ // into account the new data, but can hold position.
446
+ this.api( true ).draw( complete );
447
+ };
448
+
449
+
450
+ /**
451
+ * Filter the input based on data
452
+ * @param {string} sInput String to filter the table on
453
+ * @param {int|null} [iColumn] Column to limit filtering to
454
+ * @param {bool} [bRegex=false] Treat as regular expression or not
455
+ * @param {bool} [bSmart=true] Perform smart filtering or not
456
+ * @param {bool} [bShowGlobal=true] Show the input global filter in it's input box(es)
457
+ * @param {bool} [bCaseInsensitive=true] Do case-insensitive matching (true) or not (false)
458
+ * @dtopt API
459
+ * @deprecated Since v1.10
460
+ *
461
+ * @example
462
+ * $(document).ready(function() {
463
+ * var oTable = $('#example').wfDataTable();
464
+ *
465
+ * // Sometime later - filter...
466
+ * oTable.fnFilter( 'test string' );
467
+ * } );
468
+ */
469
+ this.fnFilter = function( sInput, iColumn, bRegex, bSmart, bShowGlobal, bCaseInsensitive )
470
+ {
471
+ var api = this.api( true );
472
+
473
+ if ( iColumn === null || iColumn === undefined ) {
474
+ api.search( sInput, bRegex, bSmart, bCaseInsensitive );
475
+ }
476
+ else {
477
+ api.column( iColumn ).search( sInput, bRegex, bSmart, bCaseInsensitive );
478
+ }
479
+
480
+ api.draw();
481
+ };
482
+
483
+
484
+ /**
485
+ * Get the data for the whole table, an individual row or an individual cell based on the
486
+ * provided parameters.
487
+ * @param {int|node} [src] A TR row node, TD/TH cell node or an integer. If given as
488
+ * a TR node then the data source for the whole row will be returned. If given as a
489
+ * TD/TH cell node then iCol will be automatically calculated and the data for the
490
+ * cell returned. If given as an integer, then this is treated as the aoData internal
491
+ * data index for the row (see fnGetPosition) and the data for that row used.
492
+ * @param {int} [col] Optional column index that you want the data of.
493
+ * @returns {array|object|string} If mRow is undefined, then the data for all rows is
494
+ * returned. If mRow is defined, just data for that row, and is iCol is
495
+ * defined, only data for the designated cell is returned.
496
+ * @dtopt API
497
+ * @deprecated Since v1.10
498
+ *
499
+ * @example
500
+ * // Row data
501
+ * $(document).ready(function() {
502
+ * oTable = $('#example').wfDataTable();
503
+ *
504
+ * oTable.$('tr').click( function () {
505
+ * var data = oTable.fnGetData( this );
506
+ * // ... do something with the array / object of data for the row
507
+ * } );
508
+ * } );
509
+ *
510
+ * @example
511
+ * // Individual cell data
512
+ * $(document).ready(function() {
513
+ * oTable = $('#example').wfDataTable();
514
+ *
515
+ * oTable.$('td').click( function () {
516
+ * var sData = oTable.fnGetData( this );
517
+ * alert( 'The cell clicked on had the value of '+sData );
518
+ * } );
519
+ * } );
520
+ */
521
+ this.fnGetData = function( src, col )
522
+ {
523
+ var api = this.api( true );
524
+
525
+ if ( src !== undefined ) {
526
+ var type = src.nodeName ? src.nodeName.toLowerCase() : '';
527
+
528
+ return col !== undefined || type == 'td' || type == 'th' ?
529
+ api.cell( src, col ).data() :
530
+ api.row( src ).data() || null;
531
+ }
532
+
533
+ return api.data().toArray();
534
+ };
535
+
536
+
537
+ /**
538
+ * Get an array of the TR nodes that are used in the table's body. Note that you will
539
+ * typically want to use the '$' API method in preference to this as it is more
540
+ * flexible.
541
+ * @param {int} [iRow] Optional row index for the TR element you want
542
+ * @returns {array|node} If iRow is undefined, returns an array of all TR elements
543
+ * in the table's body, or iRow is defined, just the TR element requested.
544
+ * @dtopt API
545
+ * @deprecated Since v1.10
546
+ *
547
+ * @example
548
+ * $(document).ready(function() {
549
+ * var oTable = $('#example').wfDataTable();
550
+ *
551
+ * // Get the nodes from the table
552
+ * var nNodes = oTable.fnGetNodes( );
553
+ * } );
554
+ */
555
+ this.fnGetNodes = function( iRow )
556
+ {
557
+ var api = this.api( true );
558
+
559
+ return iRow !== undefined ?
560
+ api.row( iRow ).node() :
561
+ api.rows().nodes().flatten().toArray();
562
+ };
563
+
564
+
565
+ /**
566
+ * Get the array indexes of a particular cell from it's DOM element
567
+ * and column index including hidden columns
568
+ * @param {node} node this can either be a TR, TD or TH in the table's body
569
+ * @returns {int} If nNode is given as a TR, then a single index is returned, or
570
+ * if given as a cell, an array of [row index, column index (visible),
571
+ * column index (all)] is given.
572
+ * @dtopt API
573
+ * @deprecated Since v1.10
574
+ *
575
+ * @example
576
+ * $(document).ready(function() {
577
+ * $('#example tbody td').click( function () {
578
+ * // Get the position of the current data from the node
579
+ * var aPos = oTable.fnGetPosition( this );
580
+ *
581
+ * // Get the data array for this row
582
+ * var aData = oTable.fnGetData( aPos[0] );
583
+ *
584
+ * // Update the data array and return the value
585
+ * aData[ aPos[1] ] = 'clicked';
586
+ * this.innerHTML = 'clicked';
587
+ * } );
588
+ *
589
+ * // Init WFDataTables
590
+ * oTable = $('#example').wfDataTable();
591
+ * } );
592
+ */
593
+ this.fnGetPosition = function( node )
594
+ {
595
+ var api = this.api( true );
596
+ var nodeName = node.nodeName.toUpperCase();
597
+
598
+ if ( nodeName == 'TR' ) {
599
+ return api.row( node ).index();
600
+ }
601
+ else if ( nodeName == 'TD' || nodeName == 'TH' ) {
602
+ var cell = api.cell( node ).index();
603
+
604
+ return [
605
+ cell.row,
606
+ cell.columnVisible,
607
+ cell.column
608
+ ];
609
+ }
610
+ return null;
611
+ };
612
+
613
+
614
+ /**
615
+ * Check to see if a row is 'open' or not.
616
+ * @param {node} nTr the table row to check
617
+ * @returns {boolean} true if the row is currently open, false otherwise
618
+ * @dtopt API
619
+ * @deprecated Since v1.10
620
+ *
621
+ * @example
622
+ * $(document).ready(function() {
623
+ * var oTable;
624
+ *
625
+ * // 'open' an information row when a row is clicked on
626
+ * $('#example tbody tr').click( function () {
627
+ * if ( oTable.fnIsOpen(this) ) {
628
+ * oTable.fnClose( this );
629
+ * } else {
630
+ * oTable.fnOpen( this, "Temporary row opened", "info_row" );
631
+ * }
632
+ * } );
633
+ *
634
+ * oTable = $('#example').wfDataTable();
635
+ * } );
636
+ */
637
+ this.fnIsOpen = function( nTr )
638
+ {
639
+ return this.api( true ).row( nTr ).child.isShown();
640
+ };
641
+
642
+
643
+ /**
644
+ * This function will place a new row directly after a row which is currently
645
+ * on display on the page, with the HTML contents that is passed into the
646
+ * function. This can be used, for example, to ask for confirmation that a
647
+ * particular record should be deleted.
648
+ * @param {node} nTr The table row to 'open'
649
+ * @param {string|node|jQuery} mHtml The HTML to put into the row
650
+ * @param {string} sClass Class to give the new TD cell
651
+ * @returns {node} The row opened. Note that if the table row passed in as the
652
+ * first parameter, is not found in the table, this method will silently
653
+ * return.
654
+ * @dtopt API
655
+ * @deprecated Since v1.10
656
+ *
657
+ * @example
658
+ * $(document).ready(function() {
659
+ * var oTable;
660
+ *
661
+ * // 'open' an information row when a row is clicked on
662
+ * $('#example tbody tr').click( function () {
663
+ * if ( oTable.fnIsOpen(this) ) {
664
+ * oTable.fnClose( this );
665
+ * } else {
666
+ * oTable.fnOpen( this, "Temporary row opened", "info_row" );
667
+ * }
668
+ * } );
669
+ *
670
+ * oTable = $('#example').wfDataTable();
671
+ * } );
672
+ */
673
+ this.fnOpen = function( nTr, mHtml, sClass )
674
+ {
675
+ return this.api( true )
676
+ .row( nTr )
677
+ .child( mHtml, sClass )
678
+ .show()
679
+ .child()[0];
680
+ };
681
+
682
+
683
+ /**
684
+ * Change the pagination - provides the internal logic for pagination in a simple API
685
+ * function. With this function you can have a WFDataTables table go to the next,
686
+ * previous, first or last pages.
687
+ * @param {string|int} mAction Paging action to take: "first", "previous", "next" or "last"
688
+ * or page number to jump to (integer), note that page 0 is the first page.
689
+ * @param {bool} [bRedraw=true] Redraw the table or not
690
+ * @dtopt API
691
+ * @deprecated Since v1.10
692
+ *
693
+ * @example
694
+ * $(document).ready(function() {
695
+ * var oTable = $('#example').wfDataTable();
696
+ * oTable.fnPageChange( 'next' );
697
+ * } );
698
+ */
699
+ this.fnPageChange = function ( mAction, bRedraw )
700
+ {
701
+ var api = this.api( true ).page( mAction );
702
+
703
+ if ( bRedraw === undefined || bRedraw ) {
704
+ api.draw(false);
705
+ }
706
+ };
707
+
708
+
709
+ /**
710
+ * Show a particular column
711
+ * @param {int} iCol The column whose display should be changed
712
+ * @param {bool} bShow Show (true) or hide (false) the column
713
+ * @param {bool} [bRedraw=true] Redraw the table or not
714
+ * @dtopt API
715
+ * @deprecated Since v1.10
716
+ *
717
+ * @example
718
+ * $(document).ready(function() {
719
+ * var oTable = $('#example').wfDataTable();
720
+ *
721
+ * // Hide the second column after initialisation
722
+ * oTable.fnSetColumnVis( 1, false );
723
+ * } );
724
+ */
725
+ this.fnSetColumnVis = function ( iCol, bShow, bRedraw )
726
+ {
727
+ var api = this.api( true ).column( iCol ).visible( bShow );
728
+
729
+ if ( bRedraw === undefined || bRedraw ) {
730
+ api.columns.adjust().draw();
731
+ }
732
+ };
733
+
734
+
735
+ /**
736
+ * Get the settings for a particular table for external manipulation
737
+ * @returns {object} WFDataTables settings object. See
738
+ * {@link WFDataTable.models.oSettings}
739
+ * @dtopt API
740
+ * @deprecated Since v1.10
741
+ *
742
+ * @example
743
+ * $(document).ready(function() {
744
+ * var oTable = $('#example').wfDataTable();
745
+ * var oSettings = oTable.fnSettings();
746
+ *
747
+ * // Show an example parameter from the settings
748
+ * alert( oSettings._iDisplayStart );
749
+ * } );
750
+ */
751
+ this.fnSettings = function()
752
+ {
753
+ return _fnSettingsFromNode( this[_ext.iApiIndex] );
754
+ };
755
+
756
+
757
+ /**
758
+ * Sort the table by a particular column
759
+ * @param {int} iCol the data index to sort on. Note that this will not match the
760
+ * 'display index' if you have hidden data entries
761
+ * @dtopt API
762
+ * @deprecated Since v1.10
763
+ *
764
+ * @example
765
+ * $(document).ready(function() {
766
+ * var oTable = $('#example').wfDataTable();
767
+ *
768
+ * // Sort immediately with columns 0 and 1
769
+ * oTable.fnSort( [ [0,'asc'], [1,'asc'] ] );
770
+ * } );
771
+ */
772
+ this.fnSort = function( aaSort )
773
+ {
774
+ this.api( true ).order( aaSort ).draw();
775
+ };
776
+
777
+
778
+ /**
779
+ * Attach a sort listener to an element for a given column
780
+ * @param {node} nNode the element to attach the sort listener to
781
+ * @param {int} iColumn the column that a click on this node will sort on
782
+ * @param {function} [fnCallback] callback function when sort is run
783
+ * @dtopt API
784
+ * @deprecated Since v1.10
785
+ *
786
+ * @example
787
+ * $(document).ready(function() {
788
+ * var oTable = $('#example').wfDataTable();
789
+ *
790
+ * // Sort on column 1, when 'sorter' is clicked on
791
+ * oTable.fnSortListener( document.getElementById('sorter'), 1 );
792
+ * } );
793
+ */
794
+ this.fnSortListener = function( nNode, iColumn, fnCallback )
795
+ {
796
+ this.api( true ).order.listener( nNode, iColumn, fnCallback );
797
+ };
798
+
799
+
800
+ /**
801
+ * Update a table cell or row - this method will accept either a single value to
802
+ * update the cell with, an array of values with one element for each column or
803
+ * an object in the same format as the original data source. The function is
804
+ * self-referencing in order to make the multi column updates easier.
805
+ * @param {object|array|string} mData Data to update the cell/row with
806
+ * @param {node|int} mRow TR element you want to update or the aoData index
807
+ * @param {int} [iColumn] The column to update, give as null or undefined to
808
+ * update a whole row.
809
+ * @param {bool} [bRedraw=true] Redraw the table or not
810
+ * @param {bool} [bAction=true] Perform pre-draw actions or not
811
+ * @returns {int} 0 on success, 1 on error
812
+ * @dtopt API
813
+ * @deprecated Since v1.10
814
+ *
815
+ * @example
816
+ * $(document).ready(function() {
817
+ * var oTable = $('#example').wfDataTable();
818
+ * oTable.fnUpdate( 'Example update', 0, 0 ); // Single cell
819
+ * oTable.fnUpdate( ['a', 'b', 'c', 'd', 'e'], $('tbody tr')[0] ); // Row
820
+ * } );
821
+ */
822
+ this.fnUpdate = function( mData, mRow, iColumn, bRedraw, bAction )
823
+ {
824
+ var api = this.api( true );
825
+
826
+ if ( iColumn === undefined || iColumn === null ) {
827
+ api.row( mRow ).data( mData );
828
+ }
829
+ else {
830
+ api.cell( mRow, iColumn ).data( mData );
831
+ }
832
+
833
+ if ( bAction === undefined || bAction ) {
834
+ api.columns.adjust();
835
+ }
836
+
837
+ if ( bRedraw === undefined || bRedraw ) {
838
+ api.draw();
839
+ }
840
+ return 0;
841
+ };
842
+
843
+
844
+ /**
845
+ * Provide a common method for plug-ins to check the version of WFDataTables being used, in order
846
+ * to ensure compatibility.
847
+ * @param {string} sVersion Version string to check for, in the format "X.Y.Z". Note that the
848
+ * formats "X" and "X.Y" are also acceptable.
849
+ * @returns {boolean} true if this version of WFDataTables is greater or equal to the required
850
+ * version, or false if this version of DataTales is not suitable
851
+ * @method
852
+ * @dtopt API
853
+ * @deprecated Since v1.10
854
+ *
855
+ * @example
856
+ * $(document).ready(function() {
857
+ * var oTable = $('#example').wfDataTable();
858
+ * alert( oTable.fnVersionCheck( '1.9.0' ) );
859
+ * } );
860
+ */
861
+ this.fnVersionCheck = _ext.fnVersionCheck;
862
+
863
+
864
+ var _that = this;
865
+ var emptyInit = options === undefined;
866
+ var len = this.length;
867
+
868
+ if ( emptyInit ) {
869
+ options = {};
870
+ }
871
+
872
+ this.oApi = this.internal = _ext.internal;
873
+
874
+ // Extend with old style plug-in API methods
875
+ for ( var fn in WFDataTable.ext.internal ) {
876
+ if ( fn ) {
877
+ this[fn] = _fnExternApiFunc(fn);
878
+ }
879
+ }
880
+
881
+ this.each(function() {
882
+ // For each initialisation we want to give it a clean initialisation
883
+ // object that can be bashed around
884
+ var o = {};
885
+ var oInit = len > 1 ? // optimisation for single table case
886
+ _fnExtend( o, options, true ) :
887
+ options;
888
+
889
+ /*global oInit,_that,emptyInit*/
890
+ var i=0, iLen, j, jLen, k, kLen;
891
+ var sId = this.getAttribute( 'id' );
892
+ var bInitHandedOff = false;
893
+ var defaults = WFDataTable.defaults;
894
+ var $this = $(this);
895
+
896
+
897
+ /* Sanity check */
898
+ if ( this.nodeName.toLowerCase() != 'table' )
899
+ {
900
+ _fnLog( null, 0, 'Non-table node initialisation ('+this.nodeName+')', 2 );
901
+ return;
902
+ }
903
+
904
+ /* Backwards compatibility for the defaults */
905
+ _fnCompatOpts( defaults );
906
+ _fnCompatCols( defaults.column );
907
+
908
+ /* Convert the camel-case defaults to Hungarian */
909
+ _fnCamelToHungarian( defaults, defaults, true );
910
+ _fnCamelToHungarian( defaults.column, defaults.column, true );
911
+
912
+ /* Setting up the initialisation object */
913
+ _fnCamelToHungarian( defaults, $.extend( oInit, $this.data() ) );
914
+
915
+
916
+
917
+ /* Check to see if we are re-initialising a table */
918
+ var allSettings = WFDataTable.settings;
919
+ for ( i=0, iLen=allSettings.length ; i<iLen ; i++ )
920
+ {
921
+ var s = allSettings[i];
922
+
923
+ /* Base check on table node */
924
+ if ( s.nTable == this || s.nTHead.parentNode == this || (s.nTFoot && s.nTFoot.parentNode == this) )
925
+ {
926
+ var bRetrieve = oInit.bRetrieve !== undefined ? oInit.bRetrieve : defaults.bRetrieve;
927
+ var bDestroy = oInit.bDestroy !== undefined ? oInit.bDestroy : defaults.bDestroy;
928
+
929
+ if ( emptyInit || bRetrieve )
930
+ {
931
+ return s.oInstance;
932
+ }
933
+ else if ( bDestroy )
934
+ {
935
+ s.oInstance.fnDestroy();
936
+ break;
937
+ }
938
+ else
939
+ {
940
+ _fnLog( s, 0, 'Cannot reinitialise WFDataTable', 3 );
941
+ return;
942
+ }
943
+ }
944
+
945
+ /* If the element we are initialising has the same ID as a table which was previously
946
+ * initialised, but the table nodes don't match (from before) then we destroy the old
947
+ * instance by simply deleting it. This is under the assumption that the table has been
948
+ * destroyed by other methods. Anyone using non-id selectors will need to do this manually
949
+ */
950
+ if ( s.sTableId == this.id )
951
+ {
952
+ allSettings.splice( i, 1 );
953
+ break;
954
+ }
955
+ }
956
+
957
+ /* Ensure the table has an ID - required for accessibility */
958
+ if ( sId === null || sId === "" )
959
+ {
960
+ sId = "WFDataTables_Table_"+(WFDataTable.ext._unique++);
961
+ this.id = sId;
962
+ }
963
+
964
+ /* Create the settings object for this table and set some of the default parameters */
965
+ var oSettings = $.extend( true, {}, WFDataTable.models.oSettings, {
966
+ "sDestroyWidth": $this[0].style.width,
967
+ "sInstance": sId,
968
+ "sTableId": sId
969
+ } );
970
+ oSettings.nTable = this;
971
+ oSettings.oApi = _that.internal;
972
+ oSettings.oInit = oInit;
973
+
974
+ allSettings.push( oSettings );
975
+
976
+ // Need to add the instance after the instance after the settings object has been added
977
+ // to the settings array, so we can self reference the table instance if more than one
978
+ oSettings.oInstance = (_that.length===1) ? _that : $this.wfDataTable();
979
+
980
+ // Backwards compatibility, before we apply all the defaults
981
+ _fnCompatOpts( oInit );
982
+
983
+ if ( oInit.oLanguage )
984
+ {
985
+ _fnLanguageCompat( oInit.oLanguage );
986
+ }
987
+
988
+ // If the length menu is given, but the init display length is not, use the length menu
989
+ if ( oInit.aLengthMenu && ! oInit.iDisplayLength )
990
+ {
991
+ oInit.iDisplayLength = $.isArray( oInit.aLengthMenu[0] ) ?
992
+ oInit.aLengthMenu[0][0] : oInit.aLengthMenu[0];
993
+ }
994
+
995
+ // Apply the defaults and init options to make a single init object will all
996
+ // options defined from defaults and instance options.
997
+ oInit = _fnExtend( $.extend( true, {}, defaults ), oInit );
998
+
999
+
1000
+ // Map the initialisation options onto the settings object
1001
+ _fnMap( oSettings.oFeatures, oInit, [
1002
+ "bPaginate",
1003
+ "bLengthChange",
1004
+ "bFilter",
1005
+ "bSort",
1006
+ "bSortMulti",
1007
+ "bInfo",
1008
+ "bProcessing",
1009
+ "bAutoWidth",
1010
+ "bSortClasses",
1011
+ "bServerSide",
1012
+ "bDeferRender"
1013
+ ] );
1014
+ _fnMap( oSettings, oInit, [
1015
+ "asStripeClasses",
1016
+ "ajax",
1017
+ "fnServerData",
1018
+ "fnFormatNumber",
1019
+ "sServerMethod",
1020
+ "aaSorting",
1021
+ "aaSortingFixed",
1022
+ "aLengthMenu",
1023
+ "sPaginationType",
1024
+ "sAjaxSource",
1025
+ "sAjaxDataProp",
1026
+ "iStateDuration",
1027
+ "sDom",
1028
+ "bSortCellsTop",
1029
+ "iTabIndex",
1030
+ "fnStateLoadCallback",
1031
+ "fnStateSaveCallback",
1032
+ "renderer",
1033
+ "searchDelay",
1034
+ "rowId",
1035
+ [ "iCookieDuration", "iStateDuration" ], // backwards compat
1036
+ [ "oSearch", "oPreviousSearch" ],
1037
+ [ "aoSearchCols", "aoPreSearchCols" ],
1038
+ [ "iDisplayLength", "_iDisplayLength" ],
1039
+ [ "bJQueryUI", "bJUI" ]
1040
+ ] );
1041
+ _fnMap( oSettings.oScroll, oInit, [
1042
+ [ "sScrollX", "sX" ],
1043
+ [ "sScrollXInner", "sXInner" ],
1044
+ [ "sScrollY", "sY" ],
1045
+ [ "bScrollCollapse", "bCollapse" ]
1046
+ ] );
1047
+ _fnMap( oSettings.oLanguage, oInit, "fnInfoCallback" );
1048
+
1049
+ /* Callback functions which are array driven */
1050
+ _fnCallbackReg( oSettings, 'aoDrawCallback', oInit.fnDrawCallback, 'user' );
1051
+ _fnCallbackReg( oSettings, 'aoServerParams', oInit.fnServerParams, 'user' );
1052
+ _fnCallbackReg( oSettings, 'aoStateSaveParams', oInit.fnStateSaveParams, 'user' );
1053
+ _fnCallbackReg( oSettings, 'aoStateLoadParams', oInit.fnStateLoadParams, 'user' );
1054
+ _fnCallbackReg( oSettings, 'aoStateLoaded', oInit.fnStateLoaded, 'user' );
1055
+ _fnCallbackReg( oSettings, 'aoRowCallback', oInit.fnRowCallback, 'user' );
1056
+ _fnCallbackReg( oSettings, 'aoRowCreatedCallback', oInit.fnCreatedRow, 'user' );
1057
+ _fnCallbackReg( oSettings, 'aoHeaderCallback', oInit.fnHeaderCallback, 'user' );
1058
+ _fnCallbackReg( oSettings, 'aoFooterCallback', oInit.fnFooterCallback, 'user' );
1059
+ _fnCallbackReg( oSettings, 'aoInitComplete', oInit.fnInitComplete, 'user' );
1060
+ _fnCallbackReg( oSettings, 'aoPreDrawCallback', oInit.fnPreDrawCallback, 'user' );
1061
+
1062
+ oSettings.rowIdFn = _fnGetObjectDataFn( oInit.rowId );
1063
+
1064
+ /* Browser support detection */
1065
+ _fnBrowserDetect( oSettings );
1066
+
1067
+ var oClasses = oSettings.oClasses;
1068
+
1069
+ // @todo Remove in 1.11
1070
+ if ( oInit.bJQueryUI )
1071
+ {
1072
+ /* Use the JUI classes object for display. You could clone the oStdClasses object if
1073
+ * you want to have multiple tables with multiple independent classes
1074
+ */
1075
+ $.extend( oClasses, WFDataTable.ext.oJUIClasses, oInit.oClasses );
1076
+
1077
+ if ( oInit.sDom === defaults.sDom && defaults.sDom === "lfrtip" )
1078
+ {
1079
+ /* Set the DOM to use a layout suitable for jQuery UI's theming */
1080
+ oSettings.sDom = '<"H"lfr>t<"F"ip>';
1081
+ }
1082
+
1083
+ if ( ! oSettings.renderer ) {
1084
+ oSettings.renderer = 'jqueryui';
1085
+ }
1086
+ else if ( $.isPlainObject( oSettings.renderer ) && ! oSettings.renderer.header ) {
1087
+ oSettings.renderer.header = 'jqueryui';
1088
+ }
1089
+ }
1090
+ else
1091
+ {
1092
+ $.extend( oClasses, WFDataTable.ext.classes, oInit.oClasses );
1093
+ }
1094
+ $this.addClass( oClasses.sTable );
1095
+
1096
+
1097
+ if ( oSettings.iInitDisplayStart === undefined )
1098
+ {
1099
+ /* Display start point, taking into account the save saving */
1100
+ oSettings.iInitDisplayStart = oInit.iDisplayStart;
1101
+ oSettings._iDisplayStart = oInit.iDisplayStart;
1102
+ }
1103
+
1104
+ if ( oInit.iDeferLoading !== null )
1105
+ {
1106
+ oSettings.bDeferLoading = true;
1107
+ var tmp = $.isArray( oInit.iDeferLoading );
1108
+ oSettings._iRecordsDisplay = tmp ? oInit.iDeferLoading[0] : oInit.iDeferLoading;
1109
+ oSettings._iRecordsTotal = tmp ? oInit.iDeferLoading[1] : oInit.iDeferLoading;
1110
+ }
1111
+
1112
+ /* Language definitions */
1113
+ var oLanguage = oSettings.oLanguage;
1114
+ $.extend( true, oLanguage, oInit.oLanguage );
1115
+
1116
+ if ( oLanguage.sUrl )
1117
+ {
1118
+ /* Get the language definitions from a file - because this Ajax call makes the language
1119
+ * get async to the remainder of this function we use bInitHandedOff to indicate that
1120
+ * _fnInitialise will be fired by the returned Ajax handler, rather than the constructor
1121
+ */
1122
+ $.ajax( {
1123
+ dataType: 'json',
1124
+ url: oLanguage.sUrl,
1125
+ success: function ( json ) {
1126
+ _fnLanguageCompat( json );
1127
+ _fnCamelToHungarian( defaults.oLanguage, json );
1128
+ $.extend( true, oLanguage, json );
1129
+ _fnInitialise( oSettings );
1130
+ },
1131
+ error: function () {
1132
+ // Error occurred loading language file, continue on as best we can
1133
+ _fnInitialise( oSettings );
1134
+ }
1135
+ } );
1136
+ bInitHandedOff = true;
1137
+ }
1138
+
1139
+ /*
1140
+ * Stripes
1141
+ */
1142
+ if ( oInit.asStripeClasses === null )
1143
+ {
1144
+ oSettings.asStripeClasses =[
1145
+ oClasses.sStripeOdd,
1146
+ oClasses.sStripeEven
1147
+ ];
1148
+ }
1149
+
1150
+ /* Remove row stripe classes if they are already on the table row */
1151
+ var stripeClasses = oSettings.asStripeClasses;
1152
+ var rowOne = $this.children('tbody').find('tr').eq(0);
1153
+ if ( $.inArray( true, $.map( stripeClasses, function(el, i) {
1154
+ return rowOne.hasClass(el);
1155
+ } ) ) !== -1 ) {
1156
+ $('tbody tr', this).removeClass( stripeClasses.join(' ') );
1157
+ oSettings.asDestroyStripes = stripeClasses.slice();
1158
+ }
1159
+
1160
+ /*
1161
+ * Columns
1162
+ * See if we should load columns automatically or use defined ones
1163
+ */
1164
+ var anThs = [];
1165
+ var aoColumnsInit;
1166
+ var nThead = this.getElementsByTagName('thead');
1167
+ if ( nThead.length !== 0 )
1168
+ {
1169
+ _fnDetectHeader( oSettings.aoHeader, nThead[0] );
1170
+ anThs = _fnGetUniqueThs( oSettings );
1171
+ }
1172
+
1173
+ /* If not given a column array, generate one with nulls */
1174
+ if ( oInit.aoColumns === null )
1175
+ {
1176
+ aoColumnsInit = [];
1177
+ for ( i=0, iLen=anThs.length ; i<iLen ; i++ )
1178
+ {
1179
+ aoColumnsInit.push( null );
1180
+ }
1181
+ }
1182
+ else
1183
+ {
1184
+ aoColumnsInit = oInit.aoColumns;
1185
+ }
1186
+
1187
+ /* Add the columns */
1188
+ for ( i=0, iLen=aoColumnsInit.length ; i<iLen ; i++ )
1189
+ {
1190
+ _fnAddColumn( oSettings, anThs ? anThs[i] : null );
1191
+ }
1192
+
1193
+ /* Apply the column definitions */
1194
+ _fnApplyColumnDefs( oSettings, oInit.aoColumnDefs, aoColumnsInit, function (iCol, oDef) {
1195
+ _fnColumnOptions( oSettings, iCol, oDef );
1196
+ } );
1197
+
1198
+ /* HTML5 attribute detection - build an mData object automatically if the
1199
+ * attributes are found
1200
+ */
1201
+ if ( rowOne.length ) {
1202
+ var a = function ( cell, name ) {
1203
+ return cell.getAttribute( 'data-'+name ) !== null ? name : null;
1204
+ };
1205
+
1206
+ $( rowOne[0] ).children('th, td').each( function (i, cell) {
1207
+ var col = oSettings.aoColumns[i];
1208
+
1209
+ if ( col.mData === i ) {
1210
+ var sort = a( cell, 'sort' ) || a( cell, 'order' );
1211
+ var filter = a( cell, 'filter' ) || a( cell, 'search' );
1212
+
1213
+ if ( sort !== null || filter !== null ) {
1214
+ col.mData = {
1215
+ _: i+'.display',
1216
+ sort: sort !== null ? i+'.@data-'+sort : undefined,
1217
+ type: sort !== null ? i+'.@data-'+sort : undefined,
1218
+ filter: filter !== null ? i+'.@data-'+filter : undefined
1219
+ };
1220
+
1221
+ _fnColumnOptions( oSettings, i );
1222
+ }
1223
+ }
1224
+ } );
1225
+ }
1226
+
1227
+ var features = oSettings.oFeatures;
1228
+ var loadedInit = function () {
1229
+ /*
1230
+ * Sorting
1231
+ * @todo For modularisation (1.11) this needs to do into a sort start up handler
1232
+ */
1233
+
1234
+ // If aaSorting is not defined, then we use the first indicator in asSorting
1235
+ // in case that has been altered, so the default sort reflects that option
1236
+ if ( oInit.aaSorting === undefined ) {
1237
+ var sorting = oSettings.aaSorting;
1238
+ for ( i=0, iLen=sorting.length ; i<iLen ; i++ ) {
1239
+ sorting[i][1] = oSettings.aoColumns[ i ].asSorting[0];
1240
+ }
1241
+ }
1242
+
1243
+ /* Do a first pass on the sorting classes (allows any size changes to be taken into
1244
+ * account, and also will apply sorting disabled classes if disabled
1245
+ */
1246
+ _fnSortingClasses( oSettings );
1247
+
1248
+ if ( features.bSort ) {
1249
+ _fnCallbackReg( oSettings, 'aoDrawCallback', function () {
1250
+ if ( oSettings.bSorted ) {
1251
+ var aSort = _fnSortFlatten( oSettings );
1252
+ var sortedColumns = {};
1253
+
1254
+ $.each( aSort, function (i, val) {
1255
+ sortedColumns[ val.src ] = val.dir;
1256
+ } );
1257
+
1258
+ _fnCallbackFire( oSettings, null, 'order', [oSettings, aSort, sortedColumns] );
1259
+ _fnSortAria( oSettings );
1260
+ }
1261
+ } );
1262
+ }
1263
+
1264
+ _fnCallbackReg( oSettings, 'aoDrawCallback', function () {
1265
+ if ( oSettings.bSorted || _fnDataSource( oSettings ) === 'ssp' || features.bDeferRender ) {
1266
+ _fnSortingClasses( oSettings );
1267
+ }
1268
+ }, 'sc' );
1269
+
1270
+
1271
+ /*
1272
+ * Final init
1273
+ * Cache the header, body and footer as required, creating them if needed
1274
+ */
1275
+
1276
+ // Work around for Webkit bug 83867 - store the caption-side before removing from doc
1277
+ var captions = $this.children('caption').each( function () {
1278
+ this._captionSide = $(this).css('caption-side');
1279
+ } );
1280
+
1281
+ var thead = $this.children('thead');
1282
+ if ( thead.length === 0 ) {
1283
+ thead = $('<thead/>').appendTo($this);
1284
+ }
1285
+ oSettings.nTHead = thead[0];
1286
+
1287
+ var tbody = $this.children('tbody');
1288
+ if ( tbody.length === 0 ) {
1289
+ tbody = $('<tbody/>').appendTo($this);
1290
+ }
1291
+ oSettings.nTBody = tbody[0];
1292
+
1293
+ var tfoot = $this.children('tfoot');
1294
+ if ( tfoot.length === 0 && captions.length > 0 && (oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "") ) {
1295
+ // If we are a scrolling table, and no footer has been given, then we need to create
1296
+ // a tfoot element for the caption element to be appended to
1297
+ tfoot = $('<tfoot/>').appendTo($this);
1298
+ }
1299
+
1300
+ if ( tfoot.length === 0 || tfoot.children().length === 0 ) {
1301
+ $this.addClass( oClasses.sNoFooter );
1302
+ }
1303
+ else if ( tfoot.length > 0 ) {
1304
+ oSettings.nTFoot = tfoot[0];
1305
+ _fnDetectHeader( oSettings.aoFooter, oSettings.nTFoot );
1306
+ }
1307
+
1308
+ /* Check if there is data passing into the constructor */
1309
+ if ( oInit.aaData ) {
1310
+ for ( i=0 ; i<oInit.aaData.length ; i++ ) {
1311
+ _fnAddData( oSettings, oInit.aaData[ i ] );
1312
+ }
1313
+ }
1314
+ else if ( oSettings.bDeferLoading || _fnDataSource( oSettings ) == 'dom' ) {
1315
+ /* Grab the data from the page - only do this when deferred loading or no Ajax
1316
+ * source since there is no point in reading the DOM data if we are then going
1317
+ * to replace it with Ajax data
1318
+ */
1319
+ _fnAddTr( oSettings, $(oSettings.nTBody).children('tr') );
1320
+ }
1321
+
1322
+ /* Copy the data index array */
1323
+ oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
1324
+
1325
+ /* Initialisation complete - table can be drawn */
1326
+ oSettings.bInitialised = true;
1327
+
1328
+ /* Check if we need to initialise the table (it might not have been handed off to the
1329
+ * language processor)
1330
+ */
1331
+ if ( bInitHandedOff === false ) {
1332
+ _fnInitialise( oSettings );
1333
+ }
1334
+ };
1335
+
1336
+ /* Must be done after everything which can be overridden by the state saving! */
1337
+ if ( oInit.bStateSave )
1338
+ {
1339
+ features.bStateSave = true;
1340
+ _fnCallbackReg( oSettings, 'aoDrawCallback', _fnSaveState, 'state_save' );
1341
+ _fnLoadState( oSettings, oInit, loadedInit );
1342
+ }
1343
+ else {
1344
+ loadedInit();
1345
+ }
1346
+
1347
+ } );
1348
+ _that = null;
1349
+ return this;
1350
+ };
1351
+
1352
+
1353
+ /*
1354
+ * It is useful to have variables which are scoped locally so only the
1355
+ * WFDataTables functions can access them and they don't leak into global space.
1356
+ * At the same time these functions are often useful over multiple files in the
1357
+ * core and API, so we list, or at least document, all variables which are used
1358
+ * by WFDataTables as private variables here. This also ensures that there is no
1359
+ * clashing of variable names and that they can easily referenced for reuse.
1360
+ */
1361
+
1362
+
1363
+ // Defined else where
1364
+ // _selector_run
1365
+ // _selector_opts
1366
+ // _selector_first
1367
+ // _selector_row_indexes
1368
+
1369
+ var _ext; // WFDataTable.ext
1370
+ var _Api; // WFDataTable.Api
1371
+ var _api_register; // WFDataTable.Api.register
1372
+ var _api_registerPlural; // WFDataTable.Api.registerPlural
1373
+
1374
+ var _re_dic = {};
1375
+ var _re_new_lines = /[\r\n]/g;
1376
+ var _re_html = /<.*?>/g;
1377
+
1378
+ // This is not strict ISO8601 - Date.parse() is quite lax, although
1379
+ // implementations differ between browsers.
1380
+ var _re_date = /^\d{2,4}[\.\/\-]\d{1,2}[\.\/\-]\d{1,2}([T ]{1}\d{1,2}[:\.]\d{2}([\.:]\d{2})?)?$/;
1381
+
1382
+ // Escape regular expression special characters
1383
+ var _re_escape_regex = new RegExp( '(\\' + [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ].join('|\\') + ')', 'g' );
1384
+
1385
+ // http://en.wikipedia.org/wiki/Foreign_exchange_market
1386
+ // - \u20BD - Russian ruble.
1387
+ // - \u20a9 - South Korean Won
1388
+ // - \u20BA - Turkish Lira
1389
+ // - \u20B9 - Indian Rupee
1390
+ // - R - Brazil (R$) and South Africa
1391
+ // - fr - Swiss Franc
1392
+ // - kr - Swedish krona, Norwegian krone and Danish krone
1393
+ // - \u2009 is thin space and \u202F is narrow no-break space, both used in many
1394
+ // standards as thousands separators.
1395
+ var _re_formatted_numeric = /[',$£€¥%\u2009\u202F\u20BD\u20a9\u20BArfk]/gi;
1396
+
1397
+
1398
+ var _empty = function ( d ) {
1399
+ return !d || d === true || d === '-' ? true : false;
1400
+ };
1401
+
1402
+
1403
+ var _intVal = function ( s ) {
1404
+ var integer = parseInt( s, 10 );
1405
+ return !isNaN(integer) && isFinite(s) ? integer : null;
1406
+ };
1407
+
1408
+ // Convert from a formatted number with characters other than `.` as the
1409
+ // decimal place, to a Javascript number
1410
+ var _numToDecimal = function ( num, decimalPoint ) {
1411
+ // Cache created regular expressions for speed as this function is called often
1412
+ if ( ! _re_dic[ decimalPoint ] ) {
1413
+ _re_dic[ decimalPoint ] = new RegExp( _fnEscapeRegex( decimalPoint ), 'g' );
1414
+ }
1415
+ return typeof num === 'string' && decimalPoint !== '.' ?
1416
+ num.replace( /\./g, '' ).replace( _re_dic[ decimalPoint ], '.' ) :
1417
+ num;
1418
+ };
1419
+
1420
+
1421
+ var _isNumber = function ( d, decimalPoint, formatted ) {
1422
+ var strType = typeof d === 'string';
1423
+
1424
+ // If empty return immediately so there must be a number if it is a
1425
+ // formatted string (this stops the string "k", or "kr", etc being detected
1426
+ // as a formatted number for currency
1427
+ if ( _empty( d ) ) {
1428
+ return true;
1429
+ }
1430
+
1431
+ if ( decimalPoint && strType ) {
1432
+ d = _numToDecimal( d, decimalPoint );
1433
+ }
1434
+
1435
+ if ( formatted && strType ) {
1436
+ d = d.replace( _re_formatted_numeric, '' );
1437
+ }
1438
+
1439
+ return !isNaN( parseFloat(d) ) && isFinite( d );
1440
+ };
1441
+
1442
+
1443
+ // A string without HTML in it can be considered to be HTML still
1444
+ var _isHtml = function ( d ) {
1445
+ return _empty( d ) || typeof d === 'string';
1446
+ };
1447
+
1448
+
1449
+ var _htmlNumeric = function ( d, decimalPoint, formatted ) {
1450
+ if ( _empty( d ) ) {
1451
+ return true;
1452
+ }
1453
+
1454
+ var html = _isHtml( d );
1455
+ return ! html ?
1456
+ null :
1457
+ _isNumber( _stripHtml( d ), decimalPoint, formatted ) ?
1458
+ true :
1459
+ null;
1460
+ };
1461
+
1462
+
1463
+ var _pluck = function ( a, prop, prop2 ) {
1464
+ var out = [];
1465
+ var i=0, ien=a.length;
1466
+
1467
+ // Could have the test in the loop for slightly smaller code, but speed
1468
+ // is essential here
1469
+ if ( prop2 !== undefined ) {
1470
+ for ( ; i<ien ; i++ ) {
1471
+ if ( a[i] && a[i][ prop ] ) {
1472
+ out.push( a[i][ prop ][ prop2 ] );
1473
+ }
1474
+ }
1475
+ }
1476
+ else {
1477
+ for ( ; i<ien ; i++ ) {
1478
+ if ( a[i] ) {
1479
+ out.push( a[i][ prop ] );
1480
+ }
1481
+ }
1482
+ }
1483
+
1484
+ return out;
1485
+ };
1486
+
1487
+
1488
+ // Basically the same as _pluck, but rather than looping over `a` we use `order`
1489
+ // as the indexes to pick from `a`
1490
+ var _pluck_order = function ( a, order, prop, prop2 )
1491
+ {
1492
+ var out = [];
1493
+ var i=0, ien=order.length;
1494
+
1495
+ // Could have the test in the loop for slightly smaller code, but speed
1496
+ // is essential here
1497
+ if ( prop2 !== undefined ) {
1498
+ for ( ; i<ien ; i++ ) {
1499
+ if ( a[ order[i] ][ prop ] ) {
1500
+ out.push( a[ order[i] ][ prop ][ prop2 ] );
1501
+ }
1502
+ }
1503
+ }
1504
+ else {
1505
+ for ( ; i<ien ; i++ ) {
1506
+ out.push( a[ order[i] ][ prop ] );
1507
+ }
1508
+ }
1509
+
1510
+ return out;
1511
+ };
1512
+
1513
+
1514
+ var _range = function ( len, start )
1515
+ {
1516
+ var out = [];
1517
+ var end;
1518
+
1519
+ if ( start === undefined ) {
1520
+ start = 0;
1521
+ end = len;
1522
+ }
1523
+ else {
1524
+ end = start;
1525
+ start = len;
1526
+ }
1527
+
1528
+ for ( var i=start ; i<end ; i++ ) {
1529
+ out.push( i );
1530
+ }
1531
+
1532
+ return out;
1533
+ };
1534
+
1535
+
1536
+ var _removeEmpty = function ( a )
1537
+ {
1538
+ var out = [];
1539
+
1540
+ for ( var i=0, ien=a.length ; i<ien ; i++ ) {
1541
+ if ( a[i] ) { // careful - will remove all falsy values!
1542
+ out.push( a[i] );
1543
+ }
1544
+ }
1545
+
1546
+ return out;
1547
+ };
1548
+
1549
+
1550
+ var _stripHtml = function ( d ) {
1551
+ return d.replace( _re_html, '' );
1552
+ };
1553
+
1554
+
1555
+ /**
1556
+ * Find the unique elements in a source array.
1557
+ *
1558
+ * @param {array} src Source array
1559
+ * @return {array} Array of unique items
1560
+ * @ignore
1561
+ */
1562
+ var _unique = function ( src )
1563
+ {
1564
+ // A faster unique method is to use object keys to identify used values,
1565
+ // but this doesn't work with arrays or objects, which we must also
1566
+ // consider. See jsperf.com/compare-array-unique-versions/4 for more
1567
+ // information.
1568
+ var
1569
+ out = [],
1570
+ val,
1571
+ i, ien=src.length,
1572
+ j, k=0;
1573
+
1574
+ again: for ( i=0 ; i<ien ; i++ ) {
1575
+ val = src[i];
1576
+
1577
+ for ( j=0 ; j<k ; j++ ) {
1578
+ if ( out[j] === val ) {
1579
+ continue again;
1580
+ }
1581
+ }
1582
+
1583
+ out.push( val );
1584
+ k++;
1585
+ }
1586
+
1587
+ return out;
1588
+ };
1589
+
1590
+
1591
+ /**
1592
+ * WFDataTables utility methods
1593
+ *
1594
+ * This namespace provides helper methods that WFDataTables uses internally to
1595
+ * create a WFDataTable, but which are not exclusively used only for WFDataTables.
1596
+ * These methods can be used by extension authors to save the duplication of
1597
+ * code.
1598
+ *
1599
+ * @namespace
1600
+ */
1601
+ WFDataTable.util = {
1602
+ /**
1603
+ * Throttle the calls to a function. Arguments and context are maintained
1604
+ * for the throttled function.
1605
+ *
1606
+ * @param {function} fn Function to be called
1607
+ * @param {integer} freq Call frequency in mS
1608
+ * @return {function} Wrapped function
1609
+ */
1610
+ throttle: function ( fn, freq ) {
1611
+ var
1612
+ frequency = freq !== undefined ? freq : 200,
1613
+ last,
1614
+ timer;
1615
+
1616
+ return function () {
1617
+ var
1618
+ that = this,
1619
+ now = +new Date(),
1620
+ args = arguments;
1621
+
1622
+ if ( last && now < last + frequency ) {
1623
+ clearTimeout( timer );
1624
+
1625
+ timer = setTimeout( function () {
1626
+ last = undefined;
1627
+ fn.apply( that, args );
1628
+ }, frequency );
1629
+ }
1630
+ else {
1631
+ last = now;
1632
+ fn.apply( that, args );
1633
+ }
1634
+ };
1635
+ },
1636
+
1637
+
1638
+ /**
1639
+ * Escape a string such that it can be used in a regular expression
1640
+ *
1641
+ * @param {string} val string to escape
1642
+ * @returns {string} escaped string
1643
+ */
1644
+ escapeRegex: function ( val ) {
1645
+ return val.replace( _re_escape_regex, '\\$1' );
1646
+ }
1647
+ };
1648
+
1649
+
1650
+
1651
+ /**
1652
+ * Create a mapping object that allows camel case parameters to be looked up
1653
+ * for their Hungarian counterparts. The mapping is stored in a private
1654
+ * parameter called `_hungarianMap` which can be accessed on the source object.
1655
+ * @param {object} o
1656
+ * @memberof WFDataTable#oApi
1657
+ */
1658
+ function _fnHungarianMap ( o )
1659
+ {
1660
+ var
1661
+ hungarian = 'a aa ai ao as b fn i m o s ',
1662
+ match,
1663
+ newKey,
1664
+ map = {};
1665
+
1666
+ $.each( o, function (key, val) {
1667
+ match = key.match(/^([^A-Z]+?)([A-Z])/);
1668
+
1669
+ if ( match && hungarian.indexOf(match[1]+' ') !== -1 )
1670
+ {
1671
+ newKey = key.replace( match[0], match[2].toLowerCase() );
1672
+ map[ newKey ] = key;
1673
+
1674
+ if ( match[1] === 'o' )
1675
+ {
1676
+ _fnHungarianMap( o[key] );
1677
+ }
1678
+ }
1679
+ } );
1680
+
1681
+ o._hungarianMap = map;
1682
+ }
1683
+
1684
+
1685
+ /**
1686
+ * Convert from camel case parameters to Hungarian, based on a Hungarian map
1687
+ * created by _fnHungarianMap.
1688
+ * @param {object} src The model object which holds all parameters that can be
1689
+ * mapped.
1690
+ * @param {object} user The object to convert from camel case to Hungarian.
1691
+ * @param {boolean} force When set to `true`, properties which already have a
1692
+ * Hungarian value in the `user` object will be overwritten. Otherwise they
1693
+ * won't be.
1694
+ * @memberof WFDataTable#oApi
1695
+ */
1696
+ function _fnCamelToHungarian ( src, user, force )
1697
+ {
1698
+ if ( ! src._hungarianMap ) {
1699
+ _fnHungarianMap( src );
1700
+ }
1701
+
1702
+ var hungarianKey;
1703
+
1704
+ $.each( user, function (key, val) {
1705
+ hungarianKey = src._hungarianMap[ key ];
1706
+
1707
+ if ( hungarianKey !== undefined && (force || user[hungarianKey] === undefined) )
1708
+ {
1709
+ // For objects, we need to buzz down into the object to copy parameters
1710
+ if ( hungarianKey.charAt(0) === 'o' )
1711
+ {
1712
+ // Copy the camelCase options over to the hungarian
1713
+ if ( ! user[ hungarianKey ] ) {
1714
+ user[ hungarianKey ] = {};
1715
+ }
1716
+ $.extend( true, user[hungarianKey], user[key] );
1717
+
1718
+ _fnCamelToHungarian( src[hungarianKey], user[hungarianKey], force );
1719
+ }
1720
+ else {
1721
+ user[hungarianKey] = user[ key ];
1722
+ }
1723
+ }
1724
+ } );
1725
+ }
1726
+
1727
+
1728
+ /**
1729
+ * Language compatibility - when certain options are given, and others aren't, we
1730
+ * need to duplicate the values over, in order to provide backwards compatibility
1731
+ * with older language files.
1732
+ * @param {object} oSettings dataTables settings object
1733
+ * @memberof WFDataTable#oApi
1734
+ */
1735
+ function _fnLanguageCompat( lang )
1736
+ {
1737
+ var defaults = WFDataTable.defaults.oLanguage;
1738
+ var zeroRecords = lang.sZeroRecords;
1739
+
1740
+ /* Backwards compatibility - if there is no sEmptyTable given, then use the same as
1741
+ * sZeroRecords - assuming that is given.
1742
+ */
1743
+ if ( ! lang.sEmptyTable && zeroRecords &&
1744
+ defaults.sEmptyTable === "No data available in table" )
1745
+ {
1746
+ _fnMap( lang, lang, 'sZeroRecords', 'sEmptyTable' );
1747
+ }
1748
+
1749
+ /* Likewise with loading records */
1750
+ if ( ! lang.sLoadingRecords && zeroRecords &&
1751
+ defaults.sLoadingRecords === "Loading..." )
1752
+ {
1753
+ _fnMap( lang, lang, 'sZeroRecords', 'sLoadingRecords' );
1754
+ }
1755
+
1756
+ // Old parameter name of the thousands separator mapped onto the new
1757
+ if ( lang.sInfoThousands ) {
1758
+ lang.sThousands = lang.sInfoThousands;
1759
+ }
1760
+
1761
+ var decimal = lang.sDecimal;
1762
+ if ( decimal ) {
1763
+ _addNumericSort( decimal );
1764
+ }
1765
+ }
1766
+
1767
+
1768
+ /**
1769
+ * Map one parameter onto another
1770
+ * @param {object} o Object to map
1771
+ * @param {*} knew The new parameter name
1772
+ * @param {*} old The old parameter name
1773
+ */
1774
+ var _fnCompatMap = function ( o, knew, old ) {
1775
+ if ( o[ knew ] !== undefined ) {
1776
+ o[ old ] = o[ knew ];
1777
+ }
1778
+ };
1779
+
1780
+
1781
+ /**
1782
+ * Provide backwards compatibility for the main DT options. Note that the new
1783
+ * options are mapped onto the old parameters, so this is an external interface
1784
+ * change only.
1785
+ * @param {object} init Object to map
1786
+ */
1787
+ function _fnCompatOpts ( init )
1788
+ {
1789
+ _fnCompatMap( init, 'ordering', 'bSort' );
1790
+ _fnCompatMap( init, 'orderMulti', 'bSortMulti' );
1791
+ _fnCompatMap( init, 'orderClasses', 'bSortClasses' );
1792
+ _fnCompatMap( init, 'orderCellsTop', 'bSortCellsTop' );
1793
+ _fnCompatMap( init, 'order', 'aaSorting' );
1794
+ _fnCompatMap( init, 'orderFixed', 'aaSortingFixed' );
1795
+ _fnCompatMap( init, 'paging', 'bPaginate' );
1796
+ _fnCompatMap( init, 'pagingType', 'sPaginationType' );
1797
+ _fnCompatMap( init, 'pageLength', 'iDisplayLength' );
1798
+ _fnCompatMap( init, 'searching', 'bFilter' );
1799
+
1800
+ // Boolean initialisation of x-scrolling
1801
+ if ( typeof init.sScrollX === 'boolean' ) {
1802
+ init.sScrollX = init.sScrollX ? '100%' : '';
1803
+ }
1804
+ if ( typeof init.scrollX === 'boolean' ) {
1805
+ init.scrollX = init.scrollX ? '100%' : '';
1806
+ }
1807
+
1808
+ // Column search objects are in an array, so it needs to be converted
1809
+ // element by element
1810
+ var searchCols = init.aoSearchCols;
1811
+
1812
+ if ( searchCols ) {
1813
+ for ( var i=0, ien=searchCols.length ; i<ien ; i++ ) {
1814
+ if ( searchCols[i] ) {
1815
+ _fnCamelToHungarian( WFDataTable.models.oSearch, searchCols[i] );
1816
+ }
1817
+ }
1818
+ }
1819
+ }
1820
+
1821
+
1822
+ /**
1823
+ * Provide backwards compatibility for column options. Note that the new options
1824
+ * are mapped onto the old parameters, so this is an external interface change
1825
+ * only.
1826
+ * @param {object} init Object to map
1827
+ */
1828
+ function _fnCompatCols ( init )
1829
+ {
1830
+ _fnCompatMap( init, 'orderable', 'bSortable' );
1831
+ _fnCompatMap( init, 'orderData', 'aDataSort' );
1832
+ _fnCompatMap( init, 'orderSequence', 'asSorting' );
1833
+ _fnCompatMap( init, 'orderDataType', 'sortDataType' );
1834
+
1835
+ // orderData can be given as an integer
1836
+ var dataSort = init.aDataSort;
1837
+ if ( dataSort && ! $.isArray( dataSort ) ) {
1838
+ init.aDataSort = [ dataSort ];
1839
+ }
1840
+ }
1841
+
1842
+
1843
+ /**
1844
+ * Browser feature detection for capabilities, quirks
1845
+ * @param {object} settings dataTables settings object
1846
+ * @memberof WFDataTable#oApi
1847
+ */
1848
+ function _fnBrowserDetect( settings )
1849
+ {
1850
+ // We don't need to do this every time WFDataTables is constructed, the values
1851
+ // calculated are specific to the browser and OS configuration which we
1852
+ // don't expect to change between initialisations
1853
+ if ( ! WFDataTable.__browser ) {
1854
+ var browser = {};
1855
+ WFDataTable.__browser = browser;
1856
+
1857
+ // Scrolling feature / quirks detection
1858
+ var n = $('<div/>')
1859
+ .css( {
1860
+ position: 'fixed',
1861
+ top: 0,
1862
+ left: $(window).scrollLeft()*-1, // allow for scrolling
1863
+ height: 1,
1864
+ width: 1,
1865
+ overflow: 'hidden'
1866
+ } )
1867
+ .append(
1868
+ $('<div/>')
1869
+ .css( {
1870
+ position: 'absolute',
1871
+ top: 1,
1872
+ left: 1,
1873
+ width: 100,
1874
+ overflow: 'scroll'
1875
+ } )
1876
+ .append(
1877
+ $('<div/>')
1878
+ .css( {
1879
+ width: '100%',
1880
+ height: 10
1881
+ } )
1882
+ )
1883
+ )
1884
+ .appendTo( 'body' );
1885
+
1886
+ var outer = n.children();
1887
+ var inner = outer.children();
1888
+
1889
+ // Numbers below, in order, are:
1890
+ // inner.offsetWidth, inner.clientWidth, outer.offsetWidth, outer.clientWidth
1891
+ //
1892
+ // IE6 XP: 100 100 100 83
1893
+ // IE7 Vista: 100 100 100 83
1894
+ // IE 8+ Windows: 83 83 100 83
1895
+ // Evergreen Windows: 83 83 100 83
1896
+ // Evergreen Mac with scrollbars: 85 85 100 85
1897
+ // Evergreen Mac without scrollbars: 100 100 100 100
1898
+
1899
+ // Get scrollbar width
1900
+ browser.barWidth = outer[0].offsetWidth - outer[0].clientWidth;
1901
+
1902
+ // IE6/7 will oversize a width 100% element inside a scrolling element, to
1903
+ // include the width of the scrollbar, while other browsers ensure the inner
1904
+ // element is contained without forcing scrolling
1905
+ browser.bScrollOversize = inner[0].offsetWidth === 100 && outer[0].clientWidth !== 100;
1906
+
1907
+ // In rtl text layout, some browsers (most, but not all) will place the
1908
+ // scrollbar on the left, rather than the right.
1909
+ browser.bScrollbarLeft = Math.round( inner.offset().left ) !== 1;
1910
+
1911
+ // IE8- don't provide height and width for getBoundingClientRect
1912
+ browser.bBounding = n[0].getBoundingClientRect().width ? true : false;
1913
+
1914
+ n.remove();
1915
+ }
1916
+
1917
+ $.extend( settings.oBrowser, WFDataTable.__browser );
1918
+ settings.oScroll.iBarWidth = WFDataTable.__browser.barWidth;
1919
+ }
1920
+
1921
+
1922
+ /**
1923
+ * Array.prototype reduce[Right] method, used for browsers which don't support
1924
+ * JS 1.6. Done this way to reduce code size, since we iterate either way
1925
+ * @param {object} settings dataTables settings object
1926
+ * @memberof WFDataTable#oApi
1927
+ */
1928
+ function _fnReduce ( that, fn, init, start, end, inc )
1929
+ {
1930
+ var
1931
+ i = start,
1932
+ value,
1933
+ isSet = false;
1934
+
1935
+ if ( init !== undefined ) {
1936
+ value = init;
1937
+ isSet = true;
1938
+ }
1939
+
1940
+ while ( i !== end ) {
1941
+ if ( ! that.hasOwnProperty(i) ) {
1942
+ continue;
1943
+ }
1944
+
1945
+ value = isSet ?
1946
+ fn( value, that[i], i, that ) :
1947
+ that[i];
1948
+
1949
+ isSet = true;
1950
+ i += inc;
1951
+ }
1952
+
1953
+ return value;
1954
+ }
1955
+
1956
+ /**
1957
+ * Add a column to the list used for the table with default values
1958
+ * @param {object} oSettings dataTables settings object
1959
+ * @param {node} nTh The th element for this column
1960
+ * @memberof WFDataTable#oApi
1961
+ */
1962
+ function _fnAddColumn( oSettings, nTh )
1963
+ {
1964
+ // Add column to aoColumns array
1965
+ var oDefaults = WFDataTable.defaults.column;
1966
+ var iCol = oSettings.aoColumns.length;
1967
+ var oCol = $.extend( {}, WFDataTable.models.oColumn, oDefaults, {
1968
+ "nTh": nTh ? nTh : document.createElement('th'),
1969
+ "sTitle": oDefaults.sTitle ? oDefaults.sTitle : nTh ? nTh.innerHTML : '',
1970
+ "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol],
1971
+ "mData": oDefaults.mData ? oDefaults.mData : iCol,
1972
+ idx: iCol
1973
+ } );
1974
+ oSettings.aoColumns.push( oCol );
1975
+
1976
+ // Add search object for column specific search. Note that the `searchCols[ iCol ]`
1977
+ // passed into extend can be undefined. This allows the user to give a default
1978
+ // with only some of the parameters defined, and also not give a default
1979
+ var searchCols = oSettings.aoPreSearchCols;
1980
+ searchCols[ iCol ] = $.extend( {}, WFDataTable.models.oSearch, searchCols[ iCol ] );
1981
+
1982
+ // Use the default column options function to initialise classes etc
1983
+ _fnColumnOptions( oSettings, iCol, $(nTh).data() );
1984
+ }
1985
+
1986
+
1987
+ /**
1988
+ * Apply options for a column
1989
+ * @param {object} oSettings dataTables settings object
1990
+ * @param {int} iCol column index to consider
1991
+ * @param {object} oOptions object with sType, bVisible and bSearchable etc
1992
+ * @memberof WFDataTable#oApi
1993
+ */
1994
+ function _fnColumnOptions( oSettings, iCol, oOptions )
1995
+ {
1996
+ var oCol = oSettings.aoColumns[ iCol ];
1997
+ var oClasses = oSettings.oClasses;
1998
+ var th = $(oCol.nTh);
1999
+
2000
+ // Try to get width information from the DOM. We can't get it from CSS
2001
+ // as we'd need to parse the CSS stylesheet. `width` option can override
2002
+ if ( ! oCol.sWidthOrig ) {
2003
+ // Width attribute
2004
+ oCol.sWidthOrig = th.attr('width') || null;
2005
+
2006
+ // Style attribute
2007
+ var t = (th.attr('style') || '').match(/width:\s*(\d+[pxem%]+)/);
2008
+ if ( t ) {
2009
+ oCol.sWidthOrig = t[1];
2010
+ }
2011
+ }
2012
+
2013
+ /* User specified column options */
2014
+ if ( oOptions !== undefined && oOptions !== null )
2015
+ {
2016
+ // Backwards compatibility
2017
+ _fnCompatCols( oOptions );
2018
+
2019
+ // Map camel case parameters to their Hungarian counterparts
2020
+ _fnCamelToHungarian( WFDataTable.defaults.column, oOptions );
2021
+
2022
+ /* Backwards compatibility for mDataProp */
2023
+ if ( oOptions.mDataProp !== undefined && !oOptions.mData )
2024
+ {
2025
+ oOptions.mData = oOptions.mDataProp;
2026
+ }
2027
+
2028
+ if ( oOptions.sType )
2029
+ {
2030
+ oCol._sManualType = oOptions.sType;
2031
+ }
2032
+
2033
+ // `class` is a reserved word in Javascript, so we need to provide
2034
+ // the ability to use a valid name for the camel case input
2035
+ if ( oOptions.className && ! oOptions.sClass )
2036
+ {
2037
+ oOptions.sClass = oOptions.className;
2038
+ }
2039
+
2040
+ $.extend( oCol, oOptions );
2041
+ _fnMap( oCol, oOptions, "sWidth", "sWidthOrig" );
2042
+
2043
+ /* iDataSort to be applied (backwards compatibility), but aDataSort will take
2044
+ * priority if defined
2045
+ */
2046
+ if ( oOptions.iDataSort !== undefined )
2047
+ {
2048
+ oCol.aDataSort = [ oOptions.iDataSort ];
2049
+ }
2050
+ _fnMap( oCol, oOptions, "aDataSort" );
2051
+ }
2052
+
2053
+ /* Cache the data get and set functions for speed */
2054
+ var mDataSrc = oCol.mData;
2055
+ var mData = _fnGetObjectDataFn( mDataSrc );
2056
+ var mRender = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null;
2057
+
2058
+ var attrTest = function( src ) {
2059
+ return typeof src === 'string' && src.indexOf('@') !== -1;
2060
+ };
2061
+ oCol._bAttrSrc = $.isPlainObject( mDataSrc ) && (
2062
+ attrTest(mDataSrc.sort) || attrTest(mDataSrc.type) || attrTest(mDataSrc.filter)
2063
+ );
2064
+ oCol._setter = null;
2065
+
2066
+ oCol.fnGetData = function (rowData, type, meta) {
2067
+ var innerData = mData( rowData, type, undefined, meta );
2068
+
2069
+ return mRender && type ?
2070
+ mRender( innerData, type, rowData, meta ) :
2071
+ innerData;
2072
+ };
2073
+ oCol.fnSetData = function ( rowData, val, meta ) {
2074
+ return _fnSetObjectDataFn( mDataSrc )( rowData, val, meta );
2075
+ };
2076
+
2077
+ // Indicate if WFDataTables should read DOM data as an object or array
2078
+ // Used in _fnGetRowElements
2079
+ if ( typeof mDataSrc !== 'number' ) {
2080
+ oSettings._rowReadObject = true;
2081
+ }
2082
+
2083
+ /* Feature sorting overrides column specific when off */
2084
+ if ( !oSettings.oFeatures.bSort )
2085
+ {
2086
+ oCol.bSortable = false;
2087
+ th.addClass( oClasses.sSortableNone ); // Have to add class here as order event isn't called
2088
+ }
2089
+
2090
+ /* Check that the class assignment is correct for sorting */
2091
+ var bAsc = $.inArray('asc', oCol.asSorting) !== -1;
2092
+ var bDesc = $.inArray('desc', oCol.asSorting) !== -1;
2093
+ if ( !oCol.bSortable || (!bAsc && !bDesc) )
2094
+ {
2095
+ oCol.sSortingClass = oClasses.sSortableNone;
2096
+ oCol.sSortingClassJUI = "";
2097
+ }
2098
+ else if ( bAsc && !bDesc )
2099
+ {
2100
+ oCol.sSortingClass = oClasses.sSortableAsc;
2101
+ oCol.sSortingClassJUI = oClasses.sSortJUIAscAllowed;
2102
+ }
2103
+ else if ( !bAsc && bDesc )
2104
+ {
2105
+ oCol.sSortingClass = oClasses.sSortableDesc;
2106
+ oCol.sSortingClassJUI = oClasses.sSortJUIDescAllowed;
2107
+ }
2108
+ else
2109
+ {
2110
+ oCol.sSortingClass = oClasses.sSortable;
2111
+ oCol.sSortingClassJUI = oClasses.sSortJUI;
2112
+ }
2113
+ }
2114
+
2115
+
2116
+ /**
2117
+ * Adjust the table column widths for new data. Note: you would probably want to
2118
+ * do a redraw after calling this function!
2119
+ * @param {object} settings dataTables settings object
2120
+ * @memberof WFDataTable#oApi
2121
+ */
2122
+ function _fnAdjustColumnSizing ( settings )
2123
+ {
2124
+ /* Not interested in doing column width calculation if auto-width is disabled */
2125
+ if ( settings.oFeatures.bAutoWidth !== false )
2126
+ {
2127
+ var columns = settings.aoColumns;
2128
+
2129
+ _fnCalculateColumnWidths( settings );
2130
+ for ( var i=0 , iLen=columns.length ; i<iLen ; i++ )
2131
+ {
2132
+ columns[i].nTh.style.width = columns[i].sWidth;
2133
+ }
2134
+ }
2135
+
2136
+ var scroll = settings.oScroll;
2137
+ if ( scroll.sY !== '' || scroll.sX !== '')
2138
+ {
2139
+ _fnScrollDraw( settings );
2140
+ }
2141
+
2142
+ _fnCallbackFire( settings, null, 'column-sizing', [settings] );
2143
+ }
2144
+
2145
+
2146
+ /**
2147
+ * Covert the index of a visible column to the index in the data array (take account
2148
+ * of hidden columns)
2149
+ * @param {object} oSettings dataTables settings object
2150
+ * @param {int} iMatch Visible column index to lookup
2151
+ * @returns {int} i the data index
2152
+ * @memberof WFDataTable#oApi
2153
+ */
2154
+ function _fnVisibleToColumnIndex( oSettings, iMatch )
2155
+ {
2156
+ var aiVis = _fnGetColumns( oSettings, 'bVisible' );
2157
+
2158
+ return typeof aiVis[iMatch] === 'number' ?
2159
+ aiVis[iMatch] :
2160
+ null;
2161
+ }
2162
+
2163
+
2164
+ /**
2165
+ * Covert the index of an index in the data array and convert it to the visible
2166
+ * column index (take account of hidden columns)
2167
+ * @param {int} iMatch Column index to lookup
2168
+ * @param {object} oSettings dataTables settings object
2169
+ * @returns {int} i the data index
2170
+ * @memberof WFDataTable#oApi
2171
+ */
2172
+ function _fnColumnIndexToVisible( oSettings, iMatch )
2173
+ {
2174
+ var aiVis = _fnGetColumns( oSettings, 'bVisible' );
2175
+ var iPos = $.inArray( iMatch, aiVis );
2176
+
2177
+ return iPos !== -1 ? iPos : null;
2178
+ }
2179
+
2180
+
2181
+ /**
2182
+ * Get the number of visible columns
2183
+ * @param {object} oSettings dataTables settings object
2184
+ * @returns {int} i the number of visible columns
2185
+ * @memberof WFDataTable#oApi
2186
+ */
2187
+ function _fnVisbleColumns( oSettings )
2188
+ {
2189
+ var vis = 0;
2190
+
2191
+ // No reduce in IE8, use a loop for now
2192
+ $.each( oSettings.aoColumns, function ( i, col ) {
2193
+ if ( col.bVisible && $(col.nTh).css('display') !== 'none' ) {
2194
+ vis++;
2195
+ }
2196
+ } );
2197
+
2198
+ return vis;
2199
+ }
2200
+
2201
+
2202
+ /**
2203
+ * Get an array of column indexes that match a given property
2204
+ * @param {object} oSettings dataTables settings object
2205
+ * @param {string} sParam Parameter in aoColumns to look for - typically
2206
+ * bVisible or bSearchable
2207
+ * @returns {array} Array of indexes with matched properties
2208
+ * @memberof WFDataTable#oApi
2209
+ */
2210
+ function _fnGetColumns( oSettings, sParam )
2211
+ {
2212
+ var a = [];
2213
+
2214
+ $.map( oSettings.aoColumns, function(val, i) {
2215
+ if ( val[sParam] ) {
2216
+ a.push( i );
2217
+ }
2218
+ } );
2219
+
2220
+ return a;
2221
+ }
2222
+
2223
+
2224
+ /**
2225
+ * Calculate the 'type' of a column
2226
+ * @param {object} settings dataTables settings object
2227
+ * @memberof WFDataTable#oApi
2228
+ */
2229
+ function _fnColumnTypes ( settings )
2230
+ {
2231
+ var columns = settings.aoColumns;
2232
+ var data = settings.aoData;
2233
+ var types = WFDataTable.ext.type.detect;
2234
+ var i, ien, j, jen, k, ken;
2235
+ var col, cell, detectedType, cache;
2236
+
2237
+ // For each column, spin over the
2238
+ for ( i=0, ien=columns.length ; i<ien ; i++ ) {
2239
+ col = columns[i];
2240
+ cache = [];
2241
+
2242
+ if ( ! col.sType && col._sManualType ) {
2243
+ col.sType = col._sManualType;
2244
+ }
2245
+ else if ( ! col.sType ) {
2246
+ for ( j=0, jen=types.length ; j<jen ; j++ ) {
2247
+ for ( k=0, ken=data.length ; k<ken ; k++ ) {
2248
+ // Use a cache array so we only need to get the type data
2249
+ // from the formatter once (when using multiple detectors)
2250
+ if ( cache[k] === undefined ) {
2251
+ cache[k] = _fnGetCellData( settings, k, i, 'type' );
2252
+ }
2253
+
2254
+ detectedType = types[j]( cache[k], settings );
2255
+
2256
+ // If null, then this type can't apply to this column, so
2257
+ // rather than testing all cells, break out. There is an
2258
+ // exception for the last type which is `html`. We need to
2259
+ // scan all rows since it is possible to mix string and HTML
2260
+ // types
2261
+ if ( ! detectedType && j !== types.length-1 ) {
2262
+ break;
2263
+ }
2264
+
2265
+ // Only a single match is needed for html type since it is
2266
+ // bottom of the pile and very similar to string
2267
+ if ( detectedType === 'html' ) {
2268
+ break;
2269
+ }
2270
+ }
2271
+
2272
+ // Type is valid for all data points in the column - use this
2273
+ // type
2274
+ if ( detectedType ) {
2275
+ col.sType = detectedType;
2276
+ break;
2277
+ }
2278
+ }
2279
+
2280
+ // Fall back - if no type was detected, always use string
2281
+ if ( ! col.sType ) {
2282
+ col.sType = 'string';
2283
+ }
2284
+ }
2285
+ }
2286
+ }
2287
+
2288
+
2289
+ /**
2290
+ * Take the column definitions and static columns arrays and calculate how
2291
+ * they relate to column indexes. The callback function will then apply the
2292
+ * definition found for a column to a suitable configuration object.
2293
+ * @param {object} oSettings dataTables settings object
2294
+ * @param {array} aoColDefs The aoColumnDefs array that is to be applied
2295
+ * @param {array} aoCols The aoColumns array that defines columns individually
2296
+ * @param {function} fn Callback function - takes two parameters, the calculated
2297
+ * column index and the definition for that column.
2298
+ * @memberof WFDataTable#oApi
2299
+ */
2300
+ function _fnApplyColumnDefs( oSettings, aoColDefs, aoCols, fn )
2301
+ {
2302
+ var i, iLen, j, jLen, k, kLen, def;
2303
+ var columns = oSettings.aoColumns;
2304
+
2305
+ // Column definitions with aTargets
2306
+ if ( aoColDefs )
2307
+ {
2308
+ /* Loop over the definitions array - loop in reverse so first instance has priority */
2309
+ for ( i=aoColDefs.length-1 ; i>=0 ; i-- )
2310
+ {
2311
+ def = aoColDefs[i];
2312
+
2313
+ /* Each definition can target multiple columns, as it is an array */
2314
+ var aTargets = def.targets !== undefined ?
2315
+ def.targets :
2316
+ def.aTargets;
2317
+
2318
+ if ( ! $.isArray( aTargets ) )
2319
+ {
2320
+ aTargets = [ aTargets ];
2321
+ }
2322
+
2323
+ for ( j=0, jLen=aTargets.length ; j<jLen ; j++ )
2324
+ {
2325
+ if ( typeof aTargets[j] === 'number' && aTargets[j] >= 0 )
2326
+ {
2327
+ /* Add columns that we don't yet know about */
2328
+ while( columns.length <= aTargets[j] )
2329
+ {
2330
+ _fnAddColumn( oSettings );
2331
+ }
2332
+
2333
+ /* Integer, basic index */
2334
+ fn( aTargets[j], def );
2335
+ }
2336
+ else if ( typeof aTargets[j] === 'number' && aTargets[j] < 0 )
2337
+ {
2338
+ /* Negative integer, right to left column counting */
2339
+ fn( columns.length+aTargets[j], def );
2340
+ }
2341
+ else if ( typeof aTargets[j] === 'string' )
2342
+ {
2343
+ /* Class name matching on TH element */
2344
+ for ( k=0, kLen=columns.length ; k<kLen ; k++ )
2345
+ {
2346
+ if ( aTargets[j] == "_all" ||
2347
+ $(columns[k].nTh).hasClass( aTargets[j] ) )
2348
+ {
2349
+ fn( k, def );
2350
+ }
2351
+ }
2352
+ }
2353
+ }
2354
+ }
2355
+ }
2356
+
2357
+ // Statically defined columns array
2358
+ if ( aoCols )
2359
+ {
2360
+ for ( i=0, iLen=aoCols.length ; i<iLen ; i++ )
2361
+ {
2362
+ fn( i, aoCols[i] );
2363
+ }
2364
+ }
2365
+ }
2366
+
2367
+ /**
2368
+ * Add a data array to the table, creating DOM node etc. This is the parallel to
2369
+ * _fnGatherData, but for adding rows from a Javascript source, rather than a
2370
+ * DOM source.
2371
+ * @param {object} oSettings dataTables settings object
2372
+ * @param {array} aData data array to be added
2373
+ * @param {node} [nTr] TR element to add to the table - optional. If not given,
2374
+ * WFDataTables will create a row automatically
2375
+ * @param {array} [anTds] Array of TD|TH elements for the row - must be given
2376
+ * if nTr is.
2377
+ * @returns {int} >=0 if successful (index of new aoData entry), -1 if failed
2378
+ * @memberof WFDataTable#oApi
2379
+ */
2380
+ function _fnAddData ( oSettings, aDataIn, nTr, anTds )
2381
+ {
2382
+ /* Create the object for storing information about this new row */
2383
+ var iRow = oSettings.aoData.length;
2384
+ var oData = $.extend( true, {}, WFDataTable.models.oRow, {
2385
+ src: nTr ? 'dom' : 'data',
2386
+ idx: iRow
2387
+ } );
2388
+
2389
+ oData._aData = aDataIn;
2390
+ oSettings.aoData.push( oData );
2391
+
2392
+ /* Create the cells */
2393
+ var nTd, sThisType;
2394
+ var columns = oSettings.aoColumns;
2395
+
2396
+ // Invalidate the column types as the new data needs to be revalidated
2397
+ for ( var i=0, iLen=columns.length ; i<iLen ; i++ )
2398
+ {
2399
+ columns[i].sType = null;
2400
+ }
2401
+
2402
+ /* Add to the display array */
2403
+ oSettings.aiDisplayMaster.push( iRow );
2404
+
2405
+ var id = oSettings.rowIdFn( aDataIn );
2406
+ if ( id !== undefined ) {
2407
+ oSettings.aIds[ id ] = oData;
2408
+ }
2409
+
2410
+ /* Create the DOM information, or register it if already present */
2411
+ if ( nTr || ! oSettings.oFeatures.bDeferRender )
2412
+ {
2413
+ _fnCreateTr( oSettings, iRow, nTr, anTds );
2414
+ }
2415
+
2416
+ return iRow;
2417
+ }
2418
+
2419
+
2420
+ /**
2421
+ * Add one or more TR elements to the table. Generally we'd expect to
2422
+ * use this for reading data from a DOM sourced table, but it could be
2423
+ * used for an TR element. Note that if a TR is given, it is used (i.e.
2424
+ * it is not cloned).
2425
+ * @param {object} settings dataTables settings object
2426
+ * @param {array|node|jQuery} trs The TR element(s) to add to the table
2427
+ * @returns {array} Array of indexes for the added rows
2428
+ * @memberof WFDataTable#oApi
2429
+ */
2430
+ function _fnAddTr( settings, trs )
2431
+ {
2432
+ var row;
2433
+
2434
+ // Allow an individual node to be passed in
2435
+ if ( ! (trs instanceof $) ) {
2436
+ trs = $(trs);
2437
+ }
2438
+
2439
+ return trs.map( function (i, el) {
2440
+ row = _fnGetRowElements( settings, el );
2441
+ return _fnAddData( settings, row.data, el, row.cells );
2442
+ } );
2443
+ }
2444
+
2445
+
2446
+ /**
2447
+ * Take a TR element and convert it to an index in aoData
2448
+ * @param {object} oSettings dataTables settings object
2449
+ * @param {node} n the TR element to find
2450
+ * @returns {int} index if the node is found, null if not
2451
+ * @memberof WFDataTable#oApi
2452
+ */
2453
+ function _fnNodeToDataIndex( oSettings, n )
2454
+ {
2455
+ return (n._DT_RowIndex!==undefined) ? n._DT_RowIndex : null;
2456
+ }
2457
+
2458
+
2459
+ /**
2460
+ * Take a TD element and convert it into a column data index (not the visible index)
2461
+ * @param {object} oSettings dataTables settings object
2462
+ * @param {int} iRow The row number the TD/TH can be found in
2463
+ * @param {node} n The TD/TH element to find
2464
+ * @returns {int} index if the node is found, -1 if not
2465
+ * @memberof WFDataTable#oApi
2466
+ */
2467
+ function _fnNodeToColumnIndex( oSettings, iRow, n )
2468
+ {
2469
+ return $.inArray( n, oSettings.aoData[ iRow ].anCells );
2470
+ }
2471
+
2472
+
2473
+ /**
2474
+ * Get the data for a given cell from the internal cache, taking into account data mapping
2475
+ * @param {object} settings dataTables settings object
2476
+ * @param {int} rowIdx aoData row id
2477
+ * @param {int} colIdx Column index
2478
+ * @param {string} type data get type ('display', 'type' 'filter' 'sort')
2479
+ * @returns {*} Cell data
2480
+ * @memberof WFDataTable#oApi
2481
+ */
2482
+ function _fnGetCellData( settings, rowIdx, colIdx, type )
2483
+ {
2484
+ var draw = settings.iDraw;
2485
+ var col = settings.aoColumns[colIdx];
2486
+ var rowData = settings.aoData[rowIdx]._aData;
2487
+ var defaultContent = col.sDefaultContent;
2488
+ var cellData = col.fnGetData( rowData, type, {
2489
+ settings: settings,
2490
+ row: rowIdx,
2491
+ col: colIdx
2492
+ } );
2493
+
2494
+ if ( cellData === undefined ) {
2495
+ if ( settings.iDrawError != draw && defaultContent === null ) {
2496
+ _fnLog( settings, 0, "Requested unknown parameter "+
2497
+ (typeof col.mData=='function' ? '{function}' : "'"+col.mData+"'")+
2498
+ " for row "+rowIdx+", column "+colIdx, 4 );
2499
+ settings.iDrawError = draw;
2500
+ }
2501
+ return defaultContent;
2502
+ }
2503
+
2504
+ // When the data source is null and a specific data type is requested (i.e.
2505
+ // not the original data), we can use default column data
2506
+ if ( (cellData === rowData || cellData === null) && defaultContent !== null && type !== undefined ) {
2507
+ cellData = defaultContent;
2508
+ }
2509
+ else if ( typeof cellData === 'function' ) {
2510
+ // If the data source is a function, then we run it and use the return,
2511
+ // executing in the scope of the data object (for instances)
2512
+ return cellData.call( rowData );
2513
+ }
2514
+
2515
+ if ( cellData === null && type == 'display' ) {
2516
+ return '';
2517
+ }
2518
+ return cellData;
2519
+ }
2520
+
2521
+
2522
+ /**
2523
+ * Set the value for a specific cell, into the internal data cache
2524
+ * @param {object} settings dataTables settings object
2525
+ * @param {int} rowIdx aoData row id
2526
+ * @param {int} colIdx Column index
2527
+ * @param {*} val Value to set
2528
+ * @memberof WFDataTable#oApi
2529
+ */
2530
+ function _fnSetCellData( settings, rowIdx, colIdx, val )
2531
+ {
2532
+ var col = settings.aoColumns[colIdx];
2533
+ var rowData = settings.aoData[rowIdx]._aData;
2534
+
2535
+ col.fnSetData( rowData, val, {
2536
+ settings: settings,
2537
+ row: rowIdx,
2538
+ col: colIdx
2539
+ } );
2540
+ }
2541
+
2542
+
2543
+ // Private variable that is used to match action syntax in the data property object
2544
+ var __reArray = /\[.*?\]$/;
2545
+ var __reFn = /\(\)$/;
2546
+
2547
+ /**
2548
+ * Split string on periods, taking into account escaped periods
2549
+ * @param {string} str String to split
2550
+ * @return {array} Split string
2551
+ */
2552
+ function _fnSplitObjNotation( str )
2553
+ {
2554
+ return $.map( str.match(/(\\.|[^\.])+/g) || [''], function ( s ) {
2555
+ return s.replace(/\\\./g, '.');
2556
+ } );
2557
+ }
2558
+
2559
+
2560
+ /**
2561
+ * Return a function that can be used to get data from a source object, taking
2562
+ * into account the ability to use nested objects as a source
2563
+ * @param {string|int|function} mSource The data source for the object
2564
+ * @returns {function} Data get function
2565
+ * @memberof WFDataTable#oApi
2566
+ */
2567
+ function _fnGetObjectDataFn( mSource )
2568
+ {
2569
+ if ( $.isPlainObject( mSource ) )
2570
+ {
2571
+ /* Build an object of get functions, and wrap them in a single call */
2572
+ var o = {};
2573
+ $.each( mSource, function (key, val) {
2574
+ if ( val ) {
2575
+ o[key] = _fnGetObjectDataFn( val );
2576
+ }
2577
+ } );
2578
+
2579
+ return function (data, type, row, meta) {
2580
+ var t = o[type] || o._;
2581
+ return t !== undefined ?
2582
+ t(data, type, row, meta) :
2583
+ data;
2584
+ };
2585
+ }
2586
+ else if ( mSource === null )
2587
+ {
2588
+ /* Give an empty string for rendering / sorting etc */
2589
+ return function (data) { // type, row and meta also passed, but not used
2590
+ return data;
2591
+ };
2592
+ }
2593
+ else if ( typeof mSource === 'function' )
2594
+ {
2595
+ return function (data, type, row, meta) {
2596
+ return mSource( data, type, row, meta );
2597
+ };
2598
+ }
2599
+ else if ( typeof mSource === 'string' && (mSource.indexOf('.') !== -1 ||
2600
+ mSource.indexOf('[') !== -1 || mSource.indexOf('(') !== -1) )
2601
+ {
2602
+ /* If there is a . in the source string then the data source is in a
2603
+ * nested object so we loop over the data for each level to get the next
2604
+ * level down. On each loop we test for undefined, and if found immediately
2605
+ * return. This allows entire objects to be missing and sDefaultContent to
2606
+ * be used if defined, rather than throwing an error
2607
+ */
2608
+ var fetchData = function (data, type, src) {
2609
+ var arrayNotation, funcNotation, out, innerSrc;
2610
+
2611
+ if ( src !== "" )
2612
+ {
2613
+ var a = _fnSplitObjNotation( src );
2614
+
2615
+ for ( var i=0, iLen=a.length ; i<iLen ; i++ )
2616
+ {
2617
+ // Check if we are dealing with special notation
2618
+ arrayNotation = a[i].match(__reArray);
2619
+ funcNotation = a[i].match(__reFn);
2620
+
2621
+ if ( arrayNotation )
2622
+ {
2623
+ // Array notation
2624
+ a[i] = a[i].replace(__reArray, '');
2625
+
2626
+ // Condition allows simply [] to be passed in
2627
+ if ( a[i] !== "" ) {
2628
+ data = data[ a[i] ];
2629
+ }
2630
+ out = [];
2631
+
2632
+ // Get the remainder of the nested object to get
2633
+ a.splice( 0, i+1 );
2634
+ innerSrc = a.join('.');
2635
+
2636
+ // Traverse each entry in the array getting the properties requested
2637
+ if ( $.isArray( data ) ) {
2638
+ for ( var j=0, jLen=data.length ; j<jLen ; j++ ) {
2639
+ out.push( fetchData( data[j], type, innerSrc ) );
2640
+ }
2641
+ }
2642
+
2643
+ // If a string is given in between the array notation indicators, that
2644
+ // is used to join the strings together, otherwise an array is returned
2645
+ var join = arrayNotation[0].substring(1, arrayNotation[0].length-1);
2646
+ data = (join==="") ? out : out.join(join);
2647
+
2648
+ // The inner call to fetchData has already traversed through the remainder
2649
+ // of the source requested, so we exit from the loop
2650
+ break;
2651
+ }
2652
+ else if ( funcNotation )
2653
+ {
2654
+ // Function call
2655
+ a[i] = a[i].replace(__reFn, '');
2656
+ data = data[ a[i] ]();
2657
+ continue;
2658
+ }
2659
+
2660
+ if ( data === null || data[ a[i] ] === undefined )
2661
+ {
2662
+ return undefined;
2663
+ }
2664
+ data = data[ a[i] ];
2665
+ }
2666
+ }
2667
+
2668
+ return data;
2669
+ };
2670
+
2671
+ return function (data, type) { // row and meta also passed, but not used
2672
+ return fetchData( data, type, mSource );
2673
+ };
2674
+ }
2675
+ else
2676
+ {
2677
+ /* Array or flat object mapping */
2678
+ return function (data, type) { // row and meta also passed, but not used
2679
+ return data[mSource];
2680
+ };
2681
+ }
2682
+ }
2683
+
2684
+
2685
+ /**
2686
+ * Return a function that can be used to set data from a source object, taking
2687
+ * into account the ability to use nested objects as a source
2688
+ * @param {string|int|function} mSource The data source for the object
2689
+ * @returns {function} Data set function
2690
+ * @memberof WFDataTable#oApi
2691
+ */
2692
+ function _fnSetObjectDataFn( mSource )
2693
+ {
2694
+ if ( $.isPlainObject( mSource ) )
2695
+ {
2696
+ /* Unlike get, only the underscore (global) option is used for for
2697
+ * setting data since we don't know the type here. This is why an object
2698
+ * option is not documented for `mData` (which is read/write), but it is
2699
+ * for `mRender` which is read only.
2700
+ */
2701
+ return _fnSetObjectDataFn( mSource._ );
2702
+ }
2703
+ else if ( mSource === null )
2704
+ {
2705
+ /* Nothing to do when the data source is null */
2706
+ return function () {};
2707
+ }
2708
+ else if ( typeof mSource === 'function' )
2709
+ {
2710
+ return function (data, val, meta) {
2711
+ mSource( data, 'set', val, meta );
2712
+ };
2713
+ }
2714
+ else if ( typeof mSource === 'string' && (mSource.indexOf('.') !== -1 ||
2715
+ mSource.indexOf('[') !== -1 || mSource.indexOf('(') !== -1) )
2716
+ {
2717
+ /* Like the get, we need to get data from a nested object */
2718
+ var setData = function (data, val, src) {
2719
+ var a = _fnSplitObjNotation( src ), b;
2720
+ var aLast = a[a.length-1];
2721
+ var arrayNotation, funcNotation, o, innerSrc;
2722
+
2723
+ for ( var i=0, iLen=a.length-1 ; i<iLen ; i++ )
2724
+ {
2725
+ // Check if we are dealing with an array notation request
2726
+ arrayNotation = a[i].match(__reArray);
2727
+ funcNotation = a[i].match(__reFn);
2728
+
2729
+ if ( arrayNotation )
2730
+ {
2731
+ a[i] = a[i].replace(__reArray, '');
2732
+ data[ a[i] ] = [];
2733
+
2734
+ // Get the remainder of the nested object to set so we can recurse
2735
+ b = a.slice();
2736
+ b.splice( 0, i+1 );
2737
+ innerSrc = b.join('.');
2738
+
2739
+ // Traverse each entry in the array setting the properties requested
2740
+ if ( $.isArray( val ) )
2741
+ {
2742
+ for ( var j=0, jLen=val.length ; j<jLen ; j++ )
2743
+ {
2744
+ o = {};
2745
+ setData( o, val[j], innerSrc );
2746
+ data[ a[i] ].push( o );
2747
+ }
2748
+ }
2749
+ else
2750
+ {
2751
+ // We've been asked to save data to an array, but it
2752
+ // isn't array data to be saved. Best that can be done
2753
+ // is to just save the value.
2754
+ data[ a[i] ] = val;
2755
+ }
2756
+
2757
+ // The inner call to setData has already traversed through the remainder
2758
+ // of the source and has set the data, thus we can exit here
2759
+ return;
2760
+ }
2761
+ else if ( funcNotation )
2762
+ {
2763
+ // Function call
2764
+ a[i] = a[i].replace(__reFn, '');
2765
+ data = data[ a[i] ]( val );
2766
+ }
2767
+
2768
+ // If the nested object doesn't currently exist - since we are
2769
+ // trying to set the value - create it
2770
+ if ( data[ a[i] ] === null || data[ a[i] ] === undefined )
2771
+ {
2772
+ data[ a[i] ] = {};
2773
+ }
2774
+ data = data[ a[i] ];
2775
+ }
2776
+
2777
+ // Last item in the input - i.e, the actual set
2778
+ if ( aLast.match(__reFn ) )
2779
+ {
2780
+ // Function call
2781
+ data = data[ aLast.replace(__reFn, '') ]( val );
2782
+ }
2783
+ else
2784
+ {
2785
+ // If array notation is used, we just want to strip it and use the property name
2786
+ // and assign the value. If it isn't used, then we get the result we want anyway
2787
+ data[ aLast.replace(__reArray, '') ] = val;
2788
+ }
2789
+ };
2790
+
2791
+ return function (data, val) { // meta is also passed in, but not used
2792
+ return setData( data, val, mSource );
2793
+ };
2794
+ }
2795
+ else
2796
+ {
2797
+ /* Array or flat object mapping */
2798
+ return function (data, val) { // meta is also passed in, but not used
2799
+ data[mSource] = val;
2800
+ };
2801
+ }
2802
+ }
2803
+
2804
+
2805
+ /**
2806
+ * Return an array with the full table data
2807
+ * @param {object} oSettings dataTables settings object
2808
+ * @returns array {array} aData Master data array
2809
+ * @memberof WFDataTable#oApi
2810
+ */
2811
+ function _fnGetDataMaster ( settings )
2812
+ {
2813
+ return _pluck( settings.aoData, '_aData' );
2814
+ }
2815
+
2816
+
2817
+ /**
2818
+ * Nuke the table
2819
+ * @param {object} oSettings dataTables settings object
2820
+ * @memberof WFDataTable#oApi
2821
+ */
2822
+ function _fnClearTable( settings )
2823
+ {
2824
+ settings.aoData.length = 0;
2825
+ settings.aiDisplayMaster.length = 0;
2826
+ settings.aiDisplay.length = 0;
2827
+ settings.aIds = {};
2828
+ }
2829
+
2830
+
2831
+ /**
2832
+ * Take an array of integers (index array) and remove a target integer (value - not
2833
+ * the key!)
2834
+ * @param {array} a Index array to target
2835
+ * @param {int} iTarget value to find
2836
+ * @memberof WFDataTable#oApi
2837
+ */
2838
+ function _fnDeleteIndex( a, iTarget, splice )
2839
+ {
2840
+ var iTargetIndex = -1;
2841
+
2842
+ for ( var i=0, iLen=a.length ; i<iLen ; i++ )
2843
+ {
2844
+ if ( a[i] == iTarget )
2845
+ {
2846
+ iTargetIndex = i;
2847
+ }
2848
+ else if ( a[i] > iTarget )
2849
+ {
2850
+ a[i]--;
2851
+ }
2852
+ }
2853
+
2854
+ if ( iTargetIndex != -1 && splice === undefined )
2855
+ {
2856
+ a.splice( iTargetIndex, 1 );
2857
+ }
2858
+ }
2859
+
2860
+
2861
+ /**
2862
+ * Mark cached data as invalid such that a re-read of the data will occur when
2863
+ * the cached data is next requested. Also update from the data source object.
2864
+ *
2865
+ * @param {object} settings WFDataTables settings object
2866
+ * @param {int} rowIdx Row index to invalidate
2867
+ * @param {string} [src] Source to invalidate from: undefined, 'auto', 'dom'
2868
+ * or 'data'
2869
+ * @param {int} [colIdx] Column index to invalidate. If undefined the whole
2870
+ * row will be invalidated
2871
+ * @memberof WFDataTable#oApi
2872
+ *
2873
+ * @todo For the modularisation of v1.11 this will need to become a callback, so
2874
+ * the sort and filter methods can subscribe to it. That will required
2875
+ * initialisation options for sorting, which is why it is not already baked in
2876
+ */
2877
+ function _fnInvalidate( settings, rowIdx, src, colIdx )
2878
+ {
2879
+ var row = settings.aoData[ rowIdx ];
2880
+ var i, ien;
2881
+ var cellWrite = function ( cell, col ) {
2882
+ // This is very frustrating, but in IE if you just write directly
2883
+ // to innerHTML, and elements that are overwritten are GC'ed,
2884
+ // even if there is a reference to them elsewhere
2885
+ while ( cell.childNodes.length ) {
2886
+ cell.removeChild( cell.firstChild );
2887
+ }
2888
+
2889
+ cell.innerHTML = _fnGetCellData( settings, rowIdx, col, 'display' );
2890
+ };
2891
+
2892
+ // Are we reading last data from DOM or the data object?
2893
+ if ( src === 'dom' || ((! src || src === 'auto') && row.src === 'dom') ) {
2894
+ // Read the data from the DOM
2895
+ row._aData = _fnGetRowElements(
2896
+ settings, row, colIdx, colIdx === undefined ? undefined : row._aData
2897
+ )
2898
+ .data;
2899
+ }
2900
+ else {
2901
+ // Reading from data object, update the DOM
2902
+ var cells = row.anCells;
2903
+
2904
+ if ( cells ) {
2905
+ if ( colIdx !== undefined ) {
2906
+ cellWrite( cells[colIdx], colIdx );
2907
+ }
2908
+ else {
2909
+ for ( i=0, ien=cells.length ; i<ien ; i++ ) {
2910
+ cellWrite( cells[i], i );
2911
+ }
2912
+ }
2913
+ }
2914
+ }
2915
+
2916
+ // For both row and cell invalidation, the cached data for sorting and
2917
+ // filtering is nulled out
2918
+ row._aSortData = null;
2919
+ row._aFilterData = null;
2920
+
2921
+ // Invalidate the type for a specific column (if given) or all columns since
2922
+ // the data might have changed
2923
+ var cols = settings.aoColumns;
2924
+ if ( colIdx !== undefined ) {
2925
+ cols[ colIdx ].sType = null;
2926
+ }
2927
+ else {
2928
+ for ( i=0, ien=cols.length ; i<ien ; i++ ) {
2929
+ cols[i].sType = null;
2930
+ }
2931
+
2932
+ // Update WFDataTables special `DT_*` attributes for the row
2933
+ _fnRowAttributes( settings, row );
2934
+ }
2935
+ }
2936
+
2937
+
2938
+ /**
2939
+ * Build a data source object from an HTML row, reading the contents of the
2940
+ * cells that are in the row.
2941
+ *
2942
+ * @param {object} settings WFDataTables settings object
2943
+ * @param {node|object} TR element from which to read data or existing row
2944
+ * object from which to re-read the data from the cells
2945
+ * @param {int} [colIdx] Optional column index
2946
+ * @param {array|object} [d] Data source object. If `colIdx` is given then this
2947
+ * parameter should also be given and will be used to write the data into.
2948
+ * Only the column in question will be written
2949
+ * @returns {object} Object with two parameters: `data` the data read, in
2950
+ * document order, and `cells` and array of nodes (they can be useful to the
2951
+ * caller, so rather than needing a second traversal to get them, just return
2952
+ * them from here).
2953
+ * @memberof WFDataTable#oApi
2954
+ */
2955
+ function _fnGetRowElements( settings, row, colIdx, d )
2956
+ {
2957
+ var
2958
+ tds = [],
2959
+ td = row.firstChild,
2960
+ name, col, o, i=0, contents,
2961
+ columns = settings.aoColumns,
2962
+ objectRead = settings._rowReadObject;
2963
+
2964
+ // Allow the data object to be passed in, or construct
2965
+ d = d !== undefined ?
2966
+ d :
2967
+ objectRead ?
2968
+ {} :
2969
+ [];
2970
+
2971
+ var attr = function ( str, td ) {
2972
+ if ( typeof str === 'string' ) {
2973
+ var idx = str.indexOf('@');
2974
+
2975
+ if ( idx !== -1 ) {
2976
+ var attr = str.substring( idx+1 );
2977
+ var setter = _fnSetObjectDataFn( str );
2978
+ setter( d, td.getAttribute( attr ) );
2979
+ }
2980
+ }
2981
+ };
2982
+
2983
+ // Read data from a cell and store into the data object
2984
+ var cellProcess = function ( cell ) {
2985
+ if ( colIdx === undefined || colIdx === i ) {
2986
+ col = columns[i];
2987
+ contents = $.trim(cell.innerHTML);
2988
+
2989
+ if ( col && col._bAttrSrc ) {
2990
+ var setter = _fnSetObjectDataFn( col.mData._ );
2991
+ setter( d, contents );
2992
+
2993
+ attr( col.mData.sort, cell );
2994
+ attr( col.mData.type, cell );
2995
+ attr( col.mData.filter, cell );
2996
+ }
2997
+ else {
2998
+ // Depending on the `data` option for the columns the data can
2999
+ // be read to either an object or an array.
3000
+ if ( objectRead ) {
3001
+ if ( ! col._setter ) {
3002
+ // Cache the setter function
3003
+ col._setter = _fnSetObjectDataFn( col.mData );
3004
+ }
3005
+ col._setter( d, contents );
3006
+ }
3007
+ else {
3008
+ d[i] = contents;
3009
+ }
3010
+ }
3011
+ }
3012
+
3013
+ i++;
3014
+ };
3015
+
3016
+ if ( td ) {
3017
+ // `tr` element was passed in
3018
+ while ( td ) {
3019
+ name = td.nodeName.toUpperCase();
3020
+
3021
+ if ( name == "TD" || name == "TH" ) {
3022
+ cellProcess( td );
3023
+ tds.push( td );
3024
+ }
3025
+
3026
+ td = td.nextSibling;
3027
+ }
3028
+ }
3029
+ else {
3030
+ // Existing row object passed in
3031
+ tds = row.anCells;
3032
+
3033
+ for ( var j=0, jen=tds.length ; j<jen ; j++ ) {
3034
+ cellProcess( tds[j] );
3035
+ }
3036
+ }
3037
+
3038
+ // Read the ID from the DOM if present
3039
+ var rowNode = row.firstChild ? row : row.nTr;
3040
+
3041
+ if ( rowNode ) {
3042
+ var id = rowNode.getAttribute( 'id' );
3043
+
3044
+ if ( id ) {
3045
+ _fnSetObjectDataFn( settings.rowId )( d, id );
3046
+ }
3047
+ }
3048
+
3049
+ return {
3050
+ data: d,
3051
+ cells: tds
3052
+ };
3053
+ }
3054
+ /**
3055
+ * Create a new TR element (and it's TD children) for a row
3056
+ * @param {object} oSettings dataTables settings object
3057
+ * @param {int} iRow Row to consider
3058
+ * @param {node} [nTrIn] TR element to add to the table - optional. If not given,
3059
+ * WFDataTables will create a row automatically
3060
+ * @param {array} [anTds] Array of TD|TH elements for the row - must be given
3061
+ * if nTr is.
3062
+ * @memberof WFDataTable#oApi
3063
+ */
3064
+ function _fnCreateTr ( oSettings, iRow, nTrIn, anTds )
3065
+ {
3066
+ var
3067
+ row = oSettings.aoData[iRow],
3068
+ rowData = row._aData,
3069
+ cells = [],
3070
+ nTr, nTd, oCol,
3071
+ i, iLen;
3072
+
3073
+ if ( row.nTr === null )
3074
+ {
3075
+ nTr = nTrIn || document.createElement('tr');
3076
+
3077
+ row.nTr = nTr;
3078
+ row.anCells = cells;
3079
+
3080
+ /* Use a private property on the node to allow reserve mapping from the node
3081
+ * to the aoData array for fast look up
3082
+ */
3083
+ nTr._DT_RowIndex = iRow;
3084
+
3085
+ /* Special parameters can be given by the data source to be used on the row */
3086
+ _fnRowAttributes( oSettings, row );
3087
+
3088
+ /* Process each column */
3089
+ for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
3090
+ {
3091
+ oCol = oSettings.aoColumns[i];
3092
+
3093
+ nTd = nTrIn ? anTds[i] : document.createElement( oCol.sCellType );
3094
+ nTd._DT_CellIndex = {
3095
+ row: iRow,
3096
+ column: i
3097
+ };
3098
+
3099
+ cells.push( nTd );
3100
+
3101
+ // Need to create the HTML if new, or if a rendering function is defined
3102
+ if ( (!nTrIn || oCol.mRender || oCol.mData !== i) &&
3103
+ (!$.isPlainObject(oCol.mData) || oCol.mData._ !== i+'.display')
3104
+ ) {
3105
+ nTd.innerHTML = _fnGetCellData( oSettings, iRow, i, 'display' );
3106
+ }
3107
+
3108
+ /* Add user defined class */
3109
+ if ( oCol.sClass )
3110
+ {
3111
+ nTd.className += ' '+oCol.sClass;
3112
+ }
3113
+
3114
+ // Visibility - add or remove as required
3115
+ if ( oCol.bVisible && ! nTrIn )
3116
+ {
3117
+ nTr.appendChild( nTd );
3118
+ }
3119
+ else if ( ! oCol.bVisible && nTrIn )
3120
+ {
3121
+ nTd.parentNode.removeChild( nTd );
3122
+ }
3123
+
3124
+ if ( oCol.fnCreatedCell )
3125
+ {
3126
+ oCol.fnCreatedCell.call( oSettings.oInstance,
3127
+ nTd, _fnGetCellData( oSettings, iRow, i ), rowData, iRow, i
3128
+ );
3129
+ }
3130
+ }
3131
+
3132
+ _fnCallbackFire( oSettings, 'aoRowCreatedCallback', null, [nTr, rowData, iRow] );
3133
+ }
3134
+
3135
+ // Remove once webkit bug 131819 and Chromium bug 365619 have been resolved
3136
+ // and deployed
3137
+ row.nTr.setAttribute( 'role', 'row' );
3138
+ }
3139
+
3140
+
3141
+ /**
3142
+ * Add attributes to a row based on the special `DT_*` parameters in a data
3143
+ * source object.
3144
+ * @param {object} settings WFDataTables settings object
3145
+ * @param {object} WFDataTables row object for the row to be modified
3146
+ * @memberof WFDataTable#oApi
3147
+ */
3148
+ function _fnRowAttributes( settings, row )
3149
+ {
3150
+ var tr = row.nTr;
3151
+ var data = row._aData;
3152
+
3153
+ if ( tr ) {
3154
+ var id = settings.rowIdFn( data );
3155
+
3156
+ if ( id ) {
3157
+ tr.id = id;
3158
+ }
3159
+
3160
+ if ( data.DT_RowClass ) {
3161
+ // Remove any classes added by DT_RowClass before
3162
+ var a = data.DT_RowClass.split(' ');
3163
+ row.__rowc = row.__rowc ?
3164
+ _unique( row.__rowc.concat( a ) ) :
3165
+ a;
3166
+
3167
+ $(tr)
3168
+ .removeClass( row.__rowc.join(' ') )
3169
+ .addClass( data.DT_RowClass );
3170
+ }
3171
+
3172
+ if ( data.DT_RowAttr ) {
3173
+ $(tr).attr( data.DT_RowAttr );
3174
+ }
3175
+
3176
+ if ( data.DT_RowData ) {
3177
+ $(tr).data( data.DT_RowData );
3178
+ }
3179
+ }
3180
+ }
3181
+
3182
+
3183
+ /**
3184
+ * Create the HTML header for the table
3185
+ * @param {object} oSettings dataTables settings object
3186
+ * @memberof WFDataTable#oApi
3187
+ */
3188
+ function _fnBuildHead( oSettings )
3189
+ {
3190
+ var i, ien, cell, row, column;
3191
+ var thead = oSettings.nTHead;
3192
+ var tfoot = oSettings.nTFoot;
3193
+ var createHeader = $('th, td', thead).length === 0;
3194
+ var classes = oSettings.oClasses;
3195
+ var columns = oSettings.aoColumns;
3196
+
3197
+ if ( createHeader ) {
3198
+ row = $('<tr/>').appendTo( thead );
3199
+ }
3200
+
3201
+ for ( i=0, ien=columns.length ; i<ien ; i++ ) {
3202
+ column = columns[i];
3203
+ cell = $( column.nTh ).addClass( column.sClass );
3204
+
3205
+ if ( createHeader ) {
3206
+ cell.appendTo( row );
3207
+ }
3208
+
3209
+ // 1.11 move into sorting
3210
+ if ( oSettings.oFeatures.bSort ) {
3211
+ cell.addClass( column.sSortingClass );
3212
+
3213
+ if ( column.bSortable !== false ) {
3214
+ cell
3215
+ .attr( 'tabindex', oSettings.iTabIndex )
3216
+ .attr( 'aria-controls', oSettings.sTableId );
3217
+
3218
+ _fnSortAttachListener( oSettings, column.nTh, i );
3219
+ }
3220
+ }
3221
+
3222
+ if ( column.sTitle != cell[0].innerHTML ) {
3223
+ cell.html( column.sTitle );
3224
+ }
3225
+
3226
+ _fnRenderer( oSettings, 'header' )(
3227
+ oSettings, cell, column, classes
3228
+ );
3229
+ }
3230
+
3231
+ if ( createHeader ) {
3232
+ _fnDetectHeader( oSettings.aoHeader, thead );
3233
+ }
3234
+
3235
+ /* ARIA role for the rows */
3236
+ $(thead).find('>tr').attr('role', 'row');
3237
+
3238
+ /* Deal with the footer - add classes if required */
3239
+ $(thead).find('>tr>th, >tr>td').addClass( classes.sHeaderTH );
3240
+ $(tfoot).find('>tr>th, >tr>td').addClass( classes.sFooterTH );
3241
+
3242
+ // Cache the footer cells. Note that we only take the cells from the first
3243
+ // row in the footer. If there is more than one row the user wants to
3244
+ // interact with, they need to use the table().foot() method. Note also this
3245
+ // allows cells to be used for multiple columns using colspan
3246
+ if ( tfoot !== null ) {
3247
+ var cells = oSettings.aoFooter[0];
3248
+
3249
+ for ( i=0, ien=cells.length ; i<ien ; i++ ) {
3250
+ column = columns[i];
3251
+ column.nTf = cells[i].cell;
3252
+
3253
+ if ( column.sClass ) {
3254
+ $(column.nTf).addClass( column.sClass );
3255
+ }
3256
+ }
3257
+ }
3258
+ }
3259
+
3260
+
3261
+ /**
3262
+ * Draw the header (or footer) element based on the column visibility states. The
3263
+ * methodology here is to use the layout array from _fnDetectHeader, modified for
3264
+ * the instantaneous column visibility, to construct the new layout. The grid is
3265
+ * traversed over cell at a time in a rows x columns grid fashion, although each
3266
+ * cell insert can cover multiple elements in the grid - which is tracks using the
3267
+ * aApplied array. Cell inserts in the grid will only occur where there isn't
3268
+ * already a cell in that position.
3269
+ * @param {object} oSettings dataTables settings object
3270
+ * @param array {objects} aoSource Layout array from _fnDetectHeader
3271
+ * @param {boolean} [bIncludeHidden=false] If true then include the hidden columns in the calc,
3272
+ * @memberof WFDataTable#oApi
3273
+ */
3274
+ function _fnDrawHead( oSettings, aoSource, bIncludeHidden )
3275
+ {
3276
+ var i, iLen, j, jLen, k, kLen, n, nLocalTr;
3277
+ var aoLocal = [];
3278
+ var aApplied = [];
3279
+ var iColumns = oSettings.aoColumns.length;
3280
+ var iRowspan, iColspan;
3281
+
3282
+ if ( ! aoSource )
3283
+ {
3284
+ return;
3285
+ }
3286
+
3287
+ if ( bIncludeHidden === undefined )
3288
+ {
3289
+ bIncludeHidden = false;
3290
+ }
3291
+
3292
+ /* Make a copy of the master layout array, but without the visible columns in it */
3293
+ for ( i=0, iLen=aoSource.length ; i<iLen ; i++ )
3294
+ {
3295
+ aoLocal[i] = aoSource[i].slice();
3296
+ aoLocal[i].nTr = aoSource[i].nTr;
3297
+
3298
+ /* Remove any columns which are currently hidden */
3299
+ for ( j=iColumns-1 ; j>=0 ; j-- )
3300
+ {
3301
+ if ( !oSettings.aoColumns[j].bVisible && !bIncludeHidden )
3302
+ {
3303
+ aoLocal[i].splice( j, 1 );
3304
+ }
3305
+ }
3306
+
3307
+ /* Prep the applied array - it needs an element for each row */
3308
+ aApplied.push( [] );
3309
+ }
3310
+
3311
+ for ( i=0, iLen=aoLocal.length ; i<iLen ; i++ )
3312
+ {
3313
+ nLocalTr = aoLocal[i].nTr;
3314
+
3315
+ /* All cells are going to be replaced, so empty out the row */
3316
+ if ( nLocalTr )
3317
+ {
3318
+ while( (n = nLocalTr.firstChild) )
3319
+ {
3320
+ nLocalTr.removeChild( n );
3321
+ }
3322
+ }
3323
+
3324
+ for ( j=0, jLen=aoLocal[i].length ; j<jLen ; j++ )
3325
+ {
3326
+ iRowspan = 1;
3327
+ iColspan = 1;
3328
+
3329
+ /* Check to see if there is already a cell (row/colspan) covering our target
3330
+ * insert point. If there is, then there is nothing to do.
3331
+ */
3332
+ if ( aApplied[i][j] === undefined )
3333
+ {
3334
+ nLocalTr.appendChild( aoLocal[i][j].cell );
3335
+ aApplied[i][j] = 1;
3336
+
3337
+ /* Expand the cell to cover as many rows as needed */
3338
+ while ( aoLocal[i+iRowspan] !== undefined &&
3339
+ aoLocal[i][j].cell == aoLocal[i+iRowspan][j].cell )
3340
+ {
3341
+ aApplied[i+iRowspan][j] = 1;
3342
+ iRowspan++;
3343
+ }
3344
+
3345
+ /* Expand the cell to cover as many columns as needed */
3346
+ while ( aoLocal[i][j+iColspan] !== undefined &&
3347
+ aoLocal[i][j].cell == aoLocal[i][j+iColspan].cell )
3348
+ {
3349
+ /* Must update the applied array over the rows for the columns */
3350
+ for ( k=0 ; k<iRowspan ; k++ )
3351
+ {
3352
+ aApplied[i+k][j+iColspan] = 1;
3353
+ }
3354
+ iColspan++;
3355
+ }
3356
+
3357
+ /* Do the actual expansion in the DOM */
3358
+ $(aoLocal[i][j].cell)
3359
+ .attr('rowspan', iRowspan)
3360
+ .attr('colspan', iColspan);
3361
+ }
3362
+ }
3363
+ }
3364
+ }
3365
+
3366
+
3367
+ /**
3368
+ * Insert the required TR nodes into the table for display
3369
+ * @param {object} oSettings dataTables settings object
3370
+ * @memberof WFDataTable#oApi
3371
+ */
3372
+ function _fnDraw( oSettings )
3373
+ {
3374
+ /* Provide a pre-callback function which can be used to cancel the draw is false is returned */
3375
+ var aPreDraw = _fnCallbackFire( oSettings, 'aoPreDrawCallback', 'preDraw', [oSettings] );
3376
+ if ( $.inArray( false, aPreDraw ) !== -1 )
3377
+ {
3378
+ _fnProcessingDisplay( oSettings, false );
3379
+ return;
3380
+ }
3381
+
3382
+ var i, iLen, n;
3383
+ var anRows = [];
3384
+ var iRowCount = 0;
3385
+ var asStripeClasses = oSettings.asStripeClasses;
3386
+ var iStripes = asStripeClasses.length;
3387
+ var iOpenRows = oSettings.aoOpenRows.length;
3388
+ var oLang = oSettings.oLanguage;
3389
+ var iInitDisplayStart = oSettings.iInitDisplayStart;
3390
+ var bServerSide = _fnDataSource( oSettings ) == 'ssp';
3391
+ var aiDisplay = oSettings.aiDisplay;
3392
+
3393
+ oSettings.bDrawing = true;
3394
+
3395
+ /* Check and see if we have an initial draw position from state saving */
3396
+ if ( iInitDisplayStart !== undefined && iInitDisplayStart !== -1 )
3397
+ {
3398
+ oSettings._iDisplayStart = bServerSide ?
3399
+ iInitDisplayStart :
3400
+ iInitDisplayStart >= oSettings.fnRecordsDisplay() ?
3401
+ 0 :
3402
+ iInitDisplayStart;
3403
+
3404
+ oSettings.iInitDisplayStart = -1;
3405
+ }
3406
+
3407
+ var iDisplayStart = oSettings._iDisplayStart;
3408
+ var iDisplayEnd = oSettings.fnDisplayEnd();
3409
+
3410
+ /* Server-side processing draw intercept */
3411
+ if ( oSettings.bDeferLoading )
3412
+ {
3413
+ oSettings.bDeferLoading = false;
3414
+ oSettings.iDraw++;
3415
+ _fnProcessingDisplay( oSettings, false );
3416
+ }
3417
+ else if ( !bServerSide )
3418
+ {
3419
+ oSettings.iDraw++;
3420
+ }
3421
+ else if ( !oSettings.bDestroying && !_fnAjaxUpdate( oSettings ) )
3422
+ {
3423
+ return;
3424
+ }
3425
+
3426
+ if ( aiDisplay.length !== 0 )
3427
+ {
3428
+ var iStart = bServerSide ? 0 : iDisplayStart;
3429
+ var iEnd = bServerSide ? oSettings.aoData.length : iDisplayEnd;
3430
+
3431
+ for ( var j=iStart ; j<iEnd ; j++ )
3432
+ {
3433
+ var iDataIndex = aiDisplay[j];
3434
+ var aoData = oSettings.aoData[ iDataIndex ];
3435
+ if ( aoData.nTr === null )
3436
+ {
3437
+ _fnCreateTr( oSettings, iDataIndex );
3438
+ }
3439
+
3440
+ var nRow = aoData.nTr;
3441
+
3442
+ /* Remove the old striping classes and then add the new one */
3443
+ if ( iStripes !== 0 )
3444
+ {
3445
+ var sStripe = asStripeClasses[ iRowCount % iStripes ];
3446
+ if ( aoData._sRowStripe != sStripe )
3447
+ {
3448
+ $(nRow).removeClass( aoData._sRowStripe ).addClass( sStripe );
3449
+ aoData._sRowStripe = sStripe;
3450
+ }
3451
+ }
3452
+
3453
+ // Row callback functions - might want to manipulate the row
3454
+ // iRowCount and j are not currently documented. Are they at all
3455
+ // useful?
3456
+ _fnCallbackFire( oSettings, 'aoRowCallback', null,
3457
+ [nRow, aoData._aData, iRowCount, j] );
3458
+
3459
+ anRows.push( nRow );
3460
+ iRowCount++;
3461
+ }
3462
+ }
3463
+ else
3464
+ {
3465
+ /* Table is empty - create a row with an empty message in it */
3466
+ var sZero = oLang.sZeroRecords;
3467
+ if ( oSettings.iDraw == 1 && _fnDataSource( oSettings ) == 'ajax' )
3468
+ {
3469
+ sZero = oLang.sLoadingRecords;
3470
+ }
3471
+ else if ( oLang.sEmptyTable && oSettings.fnRecordsTotal() === 0 )
3472
+ {
3473
+ sZero = oLang.sEmptyTable;
3474
+ }
3475
+
3476
+ anRows[ 0 ] = $( '<tr/>', { 'class': iStripes ? asStripeClasses[0] : '' } )
3477
+ .append( $('<td />', {
3478
+ 'valign': 'top',
3479
+ 'colSpan': _fnVisbleColumns( oSettings ),
3480
+ 'class': oSettings.oClasses.sRowEmpty
3481
+ } ).html( sZero ) )[0];
3482
+ }
3483
+
3484
+ /* Header and footer callbacks */
3485
+ _fnCallbackFire( oSettings, 'aoHeaderCallback', 'header', [ $(oSettings.nTHead).children('tr')[0],
3486
+ _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] );
3487
+
3488
+ _fnCallbackFire( oSettings, 'aoFooterCallback', 'footer', [ $(oSettings.nTFoot).children('tr')[0],
3489
+ _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] );
3490
+
3491
+ var body = $(oSettings.nTBody);
3492
+
3493
+ body.children().detach();
3494
+ body.append( $(anRows) );
3495
+
3496
+ /* Call all required callback functions for the end of a draw */
3497
+ _fnCallbackFire( oSettings, 'aoDrawCallback', 'draw', [oSettings] );
3498
+
3499
+ /* Draw is complete, sorting and filtering must be as well */
3500
+ oSettings.bSorted = false;
3501
+ oSettings.bFiltered = false;
3502
+ oSettings.bDrawing = false;
3503
+ }
3504
+
3505
+
3506
+ /**
3507
+ * Redraw the table - taking account of the various features which are enabled
3508
+ * @param {object} oSettings dataTables settings object
3509
+ * @param {boolean} [holdPosition] Keep the current paging position. By default
3510
+ * the paging is reset to the first page
3511
+ * @memberof WFDataTable#oApi
3512
+ */
3513
+ function _fnReDraw( settings, holdPosition )
3514
+ {
3515
+ var
3516
+ features = settings.oFeatures,
3517
+ sort = features.bSort,
3518
+ filter = features.bFilter;
3519
+
3520
+ if ( sort ) {
3521
+ _fnSort( settings );
3522
+ }
3523
+
3524
+ if ( filter ) {
3525
+ _fnFilterComplete( settings, settings.oPreviousSearch );
3526
+ }
3527
+ else {
3528
+ // No filtering, so we want to just use the display master
3529
+ settings.aiDisplay = settings.aiDisplayMaster.slice();
3530
+ }
3531
+
3532
+ if ( holdPosition !== true ) {
3533
+ settings._iDisplayStart = 0;
3534
+ }
3535
+
3536
+ // Let any modules know about the draw hold position state (used by
3537
+ // scrolling internally)
3538
+ settings._drawHold = holdPosition;
3539
+
3540
+ _fnDraw( settings );
3541
+
3542
+ settings._drawHold = false;
3543
+ }
3544
+
3545
+
3546
+ /**
3547
+ * Add the options to the page HTML for the table
3548
+ * @param {object} oSettings dataTables settings object
3549
+ * @memberof WFDataTable#oApi
3550
+ */
3551
+ function _fnAddOptionsHtml ( oSettings )
3552
+ {
3553
+ var classes = oSettings.oClasses;
3554
+ var table = $(oSettings.nTable);
3555
+ var holding = $('<div/>').insertBefore( table ); // Holding element for speed
3556
+ var features = oSettings.oFeatures;
3557
+
3558
+ // All WFDataTables are wrapped in a div
3559
+ var insert = $('<div/>', {
3560
+ id: oSettings.sTableId+'_wrapper',
3561
+ 'class': classes.sWrapper + (oSettings.nTFoot ? '' : ' '+classes.sNoFooter)
3562
+ } );
3563
+
3564
+ oSettings.nHolding = holding[0];
3565
+ oSettings.nTableWrapper = insert[0];
3566
+ oSettings.nTableReinsertBefore = oSettings.nTable.nextSibling;
3567
+
3568
+ /* Loop over the user set positioning and place the elements as needed */
3569
+ var aDom = oSettings.sDom.split('');
3570
+ var featureNode, cOption, nNewNode, cNext, sAttr, j;
3571
+ for ( var i=0 ; i<aDom.length ; i++ )
3572
+ {
3573
+ featureNode = null;
3574
+ cOption = aDom[i];
3575
+
3576
+ if ( cOption == '<' )
3577
+ {
3578
+ /* New container div */
3579
+ nNewNode = $('<div/>')[0];
3580
+
3581
+ /* Check to see if we should append an id and/or a class name to the container */
3582
+ cNext = aDom[i+1];
3583
+ if ( cNext == "'" || cNext == '"' )
3584
+ {
3585
+ sAttr = "";
3586
+ j = 2;
3587
+ while ( aDom[i+j] != cNext )
3588
+ {
3589
+ sAttr += aDom[i+j];
3590
+ j++;
3591
+ }
3592
+
3593
+ /* Replace jQuery UI constants @todo depreciated */
3594
+ if ( sAttr == "H" )
3595
+ {
3596
+ sAttr = classes.sJUIHeader;
3597
+ }
3598
+ else if ( sAttr == "F" )
3599
+ {
3600
+ sAttr = classes.sJUIFooter;
3601
+ }
3602
+
3603
+ /* The attribute can be in the format of "#id.class", "#id" or "class" This logic
3604
+ * breaks the string into parts and applies them as needed
3605
+ */
3606
+ if ( sAttr.indexOf('.') != -1 )
3607
+ {
3608
+ var aSplit = sAttr.split('.');
3609
+ nNewNode.id = aSplit[0].substr(1, aSplit[0].length-1);
3610
+ nNewNode.className = aSplit[1];
3611
+ }
3612
+ else if ( sAttr.charAt(0) == "#" )
3613
+ {
3614
+ nNewNode.id = sAttr.substr(1, sAttr.length-1);
3615
+ }
3616
+ else
3617
+ {
3618
+ nNewNode.className = sAttr;
3619
+ }
3620
+
3621
+ i += j; /* Move along the position array */
3622
+ }
3623
+
3624
+ insert.append( nNewNode );
3625
+ insert = $(nNewNode);
3626
+ }
3627
+ else if ( cOption == '>' )
3628
+ {
3629
+ /* End container div */
3630
+ insert = insert.parent();
3631
+ }
3632
+ // @todo Move options into their own plugins?
3633
+ else if ( cOption == 'l' && features.bPaginate && features.bLengthChange )
3634
+ {
3635
+ /* Length */
3636
+ featureNode = _fnFeatureHtmlLength( oSettings );
3637
+ }
3638
+ else if ( cOption == 'f' && features.bFilter )
3639
+ {
3640
+ /* Filter */
3641
+ featureNode = _fnFeatureHtmlFilter( oSettings );
3642
+ }
3643
+ else if ( cOption == 'r' && features.bProcessing )
3644
+ {
3645
+ /* pRocessing */
3646
+ featureNode = _fnFeatureHtmlProcessing( oSettings );
3647
+ }
3648
+ else if ( cOption == 't' )
3649
+ {
3650
+ /* Table */
3651
+ featureNode = _fnFeatureHtmlTable( oSettings );
3652
+ }
3653
+ else if ( cOption == 'i' && features.bInfo )
3654
+ {
3655
+ /* Info */
3656
+ featureNode = _fnFeatureHtmlInfo( oSettings );
3657
+ }
3658
+ else if ( cOption == 'p' && features.bPaginate )
3659
+ {
3660
+ /* Pagination */
3661
+ featureNode = _fnFeatureHtmlPaginate( oSettings );
3662
+ }
3663
+ else if ( WFDataTable.ext.feature.length !== 0 )
3664
+ {
3665
+ /* Plug-in features */
3666
+ var aoFeatures = WFDataTable.ext.feature;
3667
+ for ( var k=0, kLen=aoFeatures.length ; k<kLen ; k++ )
3668
+ {
3669
+ if ( cOption == aoFeatures[k].cFeature )
3670
+ {
3671
+ featureNode = aoFeatures[k].fnInit( oSettings );
3672
+ break;
3673
+ }
3674
+ }
3675
+ }
3676
+
3677
+ /* Add to the 2D features array */
3678
+ if ( featureNode )
3679
+ {
3680
+ var aanFeatures = oSettings.aanFeatures;
3681
+
3682
+ if ( ! aanFeatures[cOption] )
3683
+ {
3684
+ aanFeatures[cOption] = [];
3685
+ }
3686
+
3687
+ aanFeatures[cOption].push( featureNode );
3688
+ insert.append( featureNode );
3689
+ }
3690
+ }
3691
+
3692
+ /* Built our DOM structure - replace the holding div with what we want */
3693
+ holding.replaceWith( insert );
3694
+ oSettings.nHolding = null;
3695
+ }
3696
+
3697
+
3698
+ /**
3699
+ * Use the DOM source to create up an array of header cells. The idea here is to
3700
+ * create a layout grid (array) of rows x columns, which contains a reference
3701
+ * to the cell that that point in the grid (regardless of col/rowspan), such that
3702
+ * any column / row could be removed and the new grid constructed
3703
+ * @param array {object} aLayout Array to store the calculated layout in
3704
+ * @param {node} nThead The header/footer element for the table
3705
+ * @memberof WFDataTable#oApi
3706
+ */
3707
+ function _fnDetectHeader ( aLayout, nThead )
3708
+ {
3709
+ var nTrs = $(nThead).children('tr');
3710
+ var nTr, nCell;
3711
+ var i, k, l, iLen, jLen, iColShifted, iColumn, iColspan, iRowspan;
3712
+ var bUnique;
3713
+ var fnShiftCol = function ( a, i, j ) {
3714
+ var k = a[i];
3715
+ while ( k[j] ) {
3716
+ j++;
3717
+ }
3718
+ return j;
3719
+ };
3720
+
3721
+ aLayout.splice( 0, aLayout.length );
3722
+
3723
+ /* We know how many rows there are in the layout - so prep it */
3724
+ for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
3725
+ {
3726
+ aLayout.push( [] );
3727
+ }
3728
+
3729
+ /* Calculate a layout array */
3730
+ for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
3731
+ {
3732
+ nTr = nTrs[i];
3733
+ iColumn = 0;
3734
+
3735
+ /* For every cell in the row... */
3736
+ nCell = nTr.firstChild;
3737
+ while ( nCell ) {
3738
+ if ( nCell.nodeName.toUpperCase() == "TD" ||
3739
+ nCell.nodeName.toUpperCase() == "TH" )
3740
+ {
3741
+ /* Get the col and rowspan attributes from the DOM and sanitise them */
3742
+ iColspan = nCell.getAttribute('colspan') * 1;
3743
+ iRowspan = nCell.getAttribute('rowspan') * 1;
3744
+ iColspan = (!iColspan || iColspan===0 || iColspan===1) ? 1 : iColspan;
3745
+ iRowspan = (!iRowspan || iRowspan===0 || iRowspan===1) ? 1 : iRowspan;
3746
+
3747
+ /* There might be colspan cells already in this row, so shift our target
3748
+ * accordingly
3749
+ */
3750
+ iColShifted = fnShiftCol( aLayout, i, iColumn );
3751
+
3752
+ /* Cache calculation for unique columns */
3753
+ bUnique = iColspan === 1 ? true : false;
3754
+
3755
+ /* If there is col / rowspan, copy the information into the layout grid */
3756
+ for ( l=0 ; l<iColspan ; l++ )
3757
+ {
3758
+ for ( k=0 ; k<iRowspan ; k++ )
3759
+ {
3760
+ aLayout[i+k][iColShifted+l] = {
3761
+ "cell": nCell,
3762
+ "unique": bUnique
3763
+ };
3764
+ aLayout[i+k].nTr = nTr;
3765
+ }
3766
+ }
3767
+ }
3768
+ nCell = nCell.nextSibling;
3769
+ }
3770
+ }
3771
+ }
3772
+
3773
+
3774
+ /**
3775
+ * Get an array of unique th elements, one for each column
3776
+ * @param {object} oSettings dataTables settings object
3777
+ * @param {node} nHeader automatically detect the layout from this node - optional
3778
+ * @param {array} aLayout thead/tfoot layout from _fnDetectHeader - optional
3779
+ * @returns array {node} aReturn list of unique th's
3780
+ * @memberof WFDataTable#oApi
3781
+ */
3782
+ function _fnGetUniqueThs ( oSettings, nHeader, aLayout )
3783
+ {
3784
+ var aReturn = [];
3785
+ if ( !aLayout )
3786
+ {
3787
+ aLayout = oSettings.aoHeader;
3788
+ if ( nHeader )
3789
+ {
3790
+ aLayout = [];
3791
+ _fnDetectHeader( aLayout, nHeader );
3792
+ }
3793
+ }
3794
+
3795
+ for ( var i=0, iLen=aLayout.length ; i<iLen ; i++ )
3796
+ {
3797
+ for ( var j=0, jLen=aLayout[i].length ; j<jLen ; j++ )
3798
+ {
3799
+ if ( aLayout[i][j].unique &&
3800
+ (!aReturn[j] || !oSettings.bSortCellsTop) )
3801
+ {
3802
+ aReturn[j] = aLayout[i][j].cell;
3803
+ }
3804
+ }
3805
+ }
3806
+
3807
+ return aReturn;
3808
+ }
3809
+
3810
+ /**
3811
+ * Create an Ajax call based on the table's settings, taking into account that
3812
+ * parameters can have multiple forms, and backwards compatibility.
3813
+ *
3814
+ * @param {object} oSettings dataTables settings object
3815
+ * @param {array} data Data to send to the server, required by
3816
+ * WFDataTables - may be augmented by developer callbacks
3817
+ * @param {function} fn Callback function to run when data is obtained
3818
+ */
3819
+ function _fnBuildAjax( oSettings, data, fn )
3820
+ {
3821
+ // Compatibility with 1.9-, allow fnServerData and event to manipulate
3822
+ _fnCallbackFire( oSettings, 'aoServerParams', 'serverParams', [data] );
3823
+
3824
+ // Convert to object based for 1.10+ if using the old array scheme which can
3825
+ // come from server-side processing or serverParams
3826
+ if ( data && $.isArray(data) ) {
3827
+ var tmp = {};
3828
+ var rbracket = /(.*?)\[\]$/;
3829
+
3830
+ $.each( data, function (key, val) {
3831
+ var match = val.name.match(rbracket);
3832
+
3833
+ if ( match ) {
3834
+ // Support for arrays
3835
+ var name = match[0];
3836
+
3837
+ if ( ! tmp[ name ] ) {
3838
+ tmp[ name ] = [];
3839
+ }
3840
+ tmp[ name ].push( val.value );
3841
+ }
3842
+ else {
3843
+ tmp[val.name] = val.value;
3844
+ }
3845
+ } );
3846
+ data = tmp;
3847
+ }
3848
+
3849
+ var ajaxData;
3850
+ var ajax = oSettings.ajax;
3851
+ var instance = oSettings.oInstance;
3852
+ var callback = function ( json ) {
3853
+ _fnCallbackFire( oSettings, null, 'xhr', [oSettings, json, oSettings.jqXHR] );
3854
+ fn( json );
3855
+ };
3856
+
3857
+ if ( $.isPlainObject( ajax ) && ajax.data )
3858
+ {
3859
+ ajaxData = ajax.data;
3860
+
3861
+ var newData = $.isFunction( ajaxData ) ?
3862
+ ajaxData( data, oSettings ) : // fn can manipulate data or return
3863
+ ajaxData; // an object object or array to merge
3864
+
3865
+ // If the function returned something, use that alone
3866
+ data = $.isFunction( ajaxData ) && newData ?
3867
+ newData :
3868
+ $.extend( true, data, newData );
3869
+
3870
+ // Remove the data property as we've resolved it already and don't want
3871
+ // jQuery to do it again (it is restored at the end of the function)
3872
+ delete ajax.data;
3873
+ }
3874
+
3875
+ var baseAjax = {
3876
+ "data": data,
3877
+ "success": function (json) {
3878
+ var error = json.error || json.sError;
3879
+ if ( error ) {
3880
+ _fnLog( oSettings, 0, error );
3881
+ }
3882
+
3883
+ oSettings.json = json;
3884
+ callback( json );
3885
+ },
3886
+ "dataType": "json",
3887
+ "cache": false,
3888
+ "type": oSettings.sServerMethod,
3889
+ "error": function (xhr, error, thrown) {
3890
+ var ret = _fnCallbackFire( oSettings, null, 'xhr', [oSettings, null, oSettings.jqXHR] );
3891
+
3892
+ if ( $.inArray( true, ret ) === -1 ) {
3893
+ if ( error == "parsererror" ) {
3894
+ _fnLog( oSettings, 0, 'Invalid JSON response', 1 );
3895
+ }
3896
+ else if ( xhr.readyState === 4 ) {
3897
+ _fnLog( oSettings, 0, 'Ajax error', 7 );
3898
+ }
3899
+ }
3900
+
3901
+ _fnProcessingDisplay( oSettings, false );
3902
+ }
3903
+ };
3904
+
3905
+ // Store the data submitted for the API
3906
+ oSettings.oAjaxData = data;
3907
+
3908
+ // Allow plug-ins and external processes to modify the data
3909
+ _fnCallbackFire( oSettings, null, 'preXhr', [oSettings, data] );
3910
+
3911
+ if ( oSettings.fnServerData )
3912
+ {
3913
+ // WFDataTables 1.9- compatibility
3914
+ oSettings.fnServerData.call( instance,
3915
+ oSettings.sAjaxSource,
3916
+ $.map( data, function (val, key) { // Need to convert back to 1.9 trad format
3917
+ return { name: key, value: val };
3918
+ } ),
3919
+ callback,
3920
+ oSettings
3921
+ );
3922
+ }
3923
+ else if ( oSettings.sAjaxSource || typeof ajax === 'string' )
3924
+ {
3925
+ // WFDataTables 1.9- compatibility
3926
+ oSettings.jqXHR = $.ajax( $.extend( baseAjax, {
3927
+ url: ajax || oSettings.sAjaxSource
3928
+ } ) );
3929
+ }
3930
+ else if ( $.isFunction( ajax ) )
3931
+ {
3932
+ // Is a function - let the caller define what needs to be done
3933
+ oSettings.jqXHR = ajax.call( instance, data, callback, oSettings );
3934
+ }
3935
+ else
3936
+ {
3937
+ // Object to extend the base settings
3938
+ oSettings.jqXHR = $.ajax( $.extend( baseAjax, ajax ) );
3939
+
3940
+ // Restore for next time around
3941
+ ajax.data = ajaxData;
3942
+ }
3943
+ }
3944
+
3945
+
3946
+ /**
3947
+ * Update the table using an Ajax call
3948
+ * @param {object} settings dataTables settings object
3949
+ * @returns {boolean} Block the table drawing or not
3950
+ * @memberof WFDataTable#oApi
3951
+ */
3952
+ function _fnAjaxUpdate( settings )
3953
+ {
3954
+ if ( settings.bAjaxDataGet ) {
3955
+ settings.iDraw++;
3956
+ _fnProcessingDisplay( settings, true );
3957
+
3958
+ _fnBuildAjax(
3959
+ settings,
3960
+ _fnAjaxParameters( settings ),
3961
+ function(json) {
3962
+ _fnAjaxUpdateDraw( settings, json );
3963
+ }
3964
+ );
3965
+
3966
+ return false;
3967
+ }
3968
+ return true;
3969
+ }
3970
+
3971
+
3972
+ /**
3973
+ * Build up the parameters in an object needed for a server-side processing
3974
+ * request. Note that this is basically done twice, is different ways - a modern
3975
+ * method which is used by default in WFDataTables 1.10 which uses objects and
3976
+ * arrays, or the 1.9- method with is name / value pairs. 1.9 method is used if
3977
+ * the sAjaxSource option is used in the initialisation, or the legacyAjax
3978
+ * option is set.
3979
+ * @param {object} oSettings dataTables settings object
3980
+ * @returns {bool} block the table drawing or not
3981
+ * @memberof WFDataTable#oApi
3982
+ */
3983
+ function _fnAjaxParameters( settings )
3984
+ {
3985
+ var
3986
+ columns = settings.aoColumns,
3987
+ columnCount = columns.length,
3988
+ features = settings.oFeatures,
3989
+ preSearch = settings.oPreviousSearch,
3990
+ preColSearch = settings.aoPreSearchCols,
3991
+ i, data = [], dataProp, column, columnSearch,
3992
+ sort = _fnSortFlatten( settings ),
3993
+ displayStart = settings._iDisplayStart,
3994
+ displayLength = features.bPaginate !== false ?
3995
+ settings._iDisplayLength :
3996
+ -1;
3997
+
3998
+ var param = function ( name, value ) {
3999
+ data.push( { 'name': name, 'value': value } );
4000
+ };
4001
+
4002
+ // WFDataTables 1.9- compatible method
4003
+ param( 'sEcho', settings.iDraw );
4004
+ param( 'iColumns', columnCount );
4005
+ param( 'sColumns', _pluck( columns, 'sName' ).join(',') );
4006
+ param( 'iDisplayStart', displayStart );
4007
+ param( 'iDisplayLength', displayLength );
4008
+
4009
+ // WFDataTables 1.10+ method
4010
+ var d = {
4011
+ draw: settings.iDraw,
4012
+ columns: [],
4013
+ order: [],
4014
+ start: displayStart,
4015
+ length: displayLength,
4016
+ search: {
4017
+ value: preSearch.sSearch,
4018
+ regex: preSearch.bRegex
4019
+ }
4020
+ };
4021
+
4022
+ for ( i=0 ; i<columnCount ; i++ ) {
4023
+ column = columns[i];
4024
+ columnSearch = preColSearch[i];
4025
+ dataProp = typeof column.mData=="function" ? 'function' : column.mData ;
4026
+
4027
+ d.columns.push( {
4028
+ data: dataProp,
4029
+ name: column.sName,
4030
+ searchable: column.bSearchable,
4031
+ orderable: column.bSortable,
4032
+ search: {
4033
+ value: columnSearch.sSearch,
4034
+ regex: columnSearch.bRegex
4035
+ }
4036
+ } );
4037
+
4038
+ param( "mDataProp_"+i, dataProp );
4039
+
4040
+ if ( features.bFilter ) {
4041
+ param( 'sSearch_'+i, columnSearch.sSearch );
4042
+ param( 'bRegex_'+i, columnSearch.bRegex );
4043
+ param( 'bSearchable_'+i, column.bSearchable );
4044
+ }
4045
+
4046
+ if ( features.bSort ) {
4047
+ param( 'bSortable_'+i, column.bSortable );
4048
+ }
4049
+ }
4050
+
4051
+ if ( features.bFilter ) {
4052
+ param( 'sSearch', preSearch.sSearch );
4053
+ param( 'bRegex', preSearch.bRegex );
4054
+ }
4055
+
4056
+ if ( features.bSort ) {
4057
+ $.each( sort, function ( i, val ) {
4058
+ d.order.push( { column: val.col, dir: val.dir } );
4059
+
4060
+ param( 'iSortCol_'+i, val.col );
4061
+ param( 'sSortDir_'+i, val.dir );
4062
+ } );
4063
+
4064
+ param( 'iSortingCols', sort.length );
4065
+ }
4066
+
4067
+ // If the legacy.ajax parameter is null, then we automatically decide which
4068
+ // form to use, based on sAjaxSource
4069
+ var legacy = WFDataTable.ext.legacy.ajax;
4070
+ if ( legacy === null ) {
4071
+ return settings.sAjaxSource ? data : d;
4072
+ }
4073
+
4074
+ // Otherwise, if legacy has been specified then we use that to decide on the
4075
+ // form
4076
+ return legacy ? data : d;
4077
+ }
4078
+
4079
+
4080
+ /**
4081
+ * Data the data from the server (nuking the old) and redraw the table
4082
+ * @param {object} oSettings dataTables settings object
4083
+ * @param {object} json json data return from the server.
4084
+ * @param {string} json.sEcho Tracking flag for WFDataTables to match requests
4085
+ * @param {int} json.iTotalRecords Number of records in the data set, not accounting for filtering
4086
+ * @param {int} json.iTotalDisplayRecords Number of records in the data set, accounting for filtering
4087
+ * @param {array} json.aaData The data to display on this page
4088
+ * @param {string} [json.sColumns] Column ordering (sName, comma separated)
4089
+ * @memberof WFDataTable#oApi
4090
+ */
4091
+ function _fnAjaxUpdateDraw ( settings, json )
4092
+ {
4093
+ // v1.10 uses camelCase variables, while 1.9 uses Hungarian notation.
4094
+ // Support both
4095
+ var compat = function ( old, modern ) {
4096
+ return json[old] !== undefined ? json[old] : json[modern];
4097
+ };
4098
+
4099
+ var data = _fnAjaxDataSrc( settings, json );
4100
+ var draw = compat( 'sEcho', 'draw' );
4101
+ var recordsTotal = compat( 'iTotalRecords', 'recordsTotal' );
4102
+ var recordsFiltered = compat( 'iTotalDisplayRecords', 'recordsFiltered' );
4103
+
4104
+ if ( draw ) {
4105
+ // Protect against out of sequence returns
4106
+ if ( draw*1 < settings.iDraw ) {
4107
+ return;
4108
+ }
4109
+ settings.iDraw = draw * 1;
4110
+ }
4111
+
4112
+ _fnClearTable( settings );
4113
+ settings._iRecordsTotal = parseInt(recordsTotal, 10);
4114
+ settings._iRecordsDisplay = parseInt(recordsFiltered, 10);
4115
+
4116
+ for ( var i=0, ien=data.length ; i<ien ; i++ ) {
4117
+ _fnAddData( settings, data[i] );
4118
+ }
4119
+ settings.aiDisplay = settings.aiDisplayMaster.slice();
4120
+
4121
+ settings.bAjaxDataGet = false;
4122
+ _fnDraw( settings );
4123
+
4124
+ if ( ! settings._bInitComplete ) {
4125
+ _fnInitComplete( settings, json );
4126
+ }
4127
+
4128
+ settings.bAjaxDataGet = true;
4129
+ _fnProcessingDisplay( settings, false );
4130
+ }
4131
+
4132
+
4133
+ /**
4134
+ * Get the data from the JSON data source to use for drawing a table. Using
4135
+ * `_fnGetObjectDataFn` allows the data to be sourced from a property of the
4136
+ * source object, or from a processing function.
4137
+ * @param {object} oSettings dataTables settings object
4138
+ * @param {object} json Data source object / array from the server
4139
+ * @return {array} Array of data to use
4140
+ */
4141
+ function _fnAjaxDataSrc ( oSettings, json )
4142
+ {
4143
+ var dataSrc = $.isPlainObject( oSettings.ajax ) && oSettings.ajax.dataSrc !== undefined ?
4144
+ oSettings.ajax.dataSrc :
4145
+ oSettings.sAjaxDataProp; // Compatibility with 1.9-.
4146
+
4147
+ // Compatibility with 1.9-. In order to read from aaData, check if the
4148
+ // default has been changed, if not, check for aaData
4149
+ if ( dataSrc === 'data' ) {
4150
+ return json.aaData || json[dataSrc];
4151
+ }
4152
+
4153
+ return dataSrc !== "" ?
4154
+ _fnGetObjectDataFn( dataSrc )( json ) :
4155
+ json;
4156
+ }
4157
+
4158
+ /**
4159
+ * Generate the node required for filtering text
4160
+ * @returns {node} Filter control element
4161
+ * @param {object} oSettings dataTables settings object
4162
+ * @memberof WFDataTable#oApi
4163
+ */
4164
+ function _fnFeatureHtmlFilter ( settings )
4165
+ {
4166
+ var classes = settings.oClasses;
4167
+ var tableId = settings.sTableId;
4168
+ var language = settings.oLanguage;
4169
+ var previousSearch = settings.oPreviousSearch;
4170
+ var features = settings.aanFeatures;
4171
+ var input = '<input type="search" class="'+classes.sFilterInput+'"/>';
4172
+
4173
+ var str = language.sSearch;
4174
+ str = str.match(/_INPUT_/) ?
4175
+ str.replace('_INPUT_', input) :
4176
+ str+input;
4177
+
4178
+ var filter = $('<div/>', {
4179
+ 'id': ! features.f ? tableId+'_filter' : null,
4180
+ 'class': classes.sFilter
4181
+ } )
4182
+ .append( $('<label/>' ).append( str ) );
4183
+
4184
+ var searchFn = function() {
4185
+ /* Update all other filter input elements for the new display */
4186
+ var n = features.f;
4187
+ var val = !this.value ? "" : this.value; // mental IE8 fix :-(
4188
+
4189
+ /* Now do the filter */
4190
+ if ( val != previousSearch.sSearch ) {
4191
+ _fnFilterComplete( settings, {
4192
+ "sSearch": val,
4193
+ "bRegex": previousSearch.bRegex,
4194
+ "bSmart": previousSearch.bSmart ,
4195
+ "bCaseInsensitive": previousSearch.bCaseInsensitive
4196
+ } );
4197
+
4198
+ // Need to redraw, without resorting
4199
+ settings._iDisplayStart = 0;
4200
+ _fnDraw( settings );
4201
+ }
4202
+ };
4203
+
4204
+ var searchDelay = settings.searchDelay !== null ?
4205
+ settings.searchDelay :
4206
+ _fnDataSource( settings ) === 'ssp' ?
4207
+ 400 :
4208
+ 0;
4209
+
4210
+ var jqFilter = $('input', filter)
4211
+ .val( previousSearch.sSearch )
4212
+ .attr( 'placeholder', language.sSearchPlaceholder )
4213
+ .on(
4214
+ 'keyup.DT search.DT input.DT paste.DT cut.DT',
4215
+ searchDelay ?
4216
+ _fnThrottle( searchFn, searchDelay ) :
4217
+ searchFn
4218
+ )
4219
+ .on( 'keypress.DT', function(e) {
4220
+ /* Prevent form submission */
4221
+ if ( e.keyCode == 13 ) {
4222
+ return false;
4223
+ }
4224
+ } )
4225
+ .attr('aria-controls', tableId);
4226
+
4227
+ // Update the input elements whenever the table is filtered
4228
+ $(settings.nTable).on( 'search.dt.DT', function ( ev, s ) {
4229
+ if ( settings === s ) {
4230
+ // IE9 throws an 'unknown error' if document.activeElement is used
4231
+ // inside an iframe or frame...
4232
+ try {
4233
+ if ( jqFilter[0] !== document.activeElement ) {
4234
+ jqFilter.val( previousSearch.sSearch );
4235
+ }
4236
+ }
4237
+ catch ( e ) {}
4238
+ }
4239
+ } );
4240
+
4241
+ return filter[0];
4242
+ }
4243
+
4244
+
4245
+ /**
4246
+ * Filter the table using both the global filter and column based filtering
4247
+ * @param {object} oSettings dataTables settings object
4248
+ * @param {object} oSearch search information
4249
+ * @param {int} [iForce] force a research of the master array (1) or not (undefined or 0)
4250
+ * @memberof WFDataTable#oApi
4251
+ */
4252
+ function _fnFilterComplete ( oSettings, oInput, iForce )
4253
+ {
4254
+ var oPrevSearch = oSettings.oPreviousSearch;
4255
+ var aoPrevSearch = oSettings.aoPreSearchCols;
4256
+ var fnSaveFilter = function ( oFilter ) {
4257
+ /* Save the filtering values */
4258
+ oPrevSearch.sSearch = oFilter.sSearch;
4259
+ oPrevSearch.bRegex = oFilter.bRegex;
4260
+ oPrevSearch.bSmart = oFilter.bSmart;
4261
+ oPrevSearch.bCaseInsensitive = oFilter.bCaseInsensitive;
4262
+ };
4263
+ var fnRegex = function ( o ) {
4264
+ // Backwards compatibility with the bEscapeRegex option
4265
+ return o.bEscapeRegex !== undefined ? !o.bEscapeRegex : o.bRegex;
4266
+ };
4267
+
4268
+ // Resolve any column types that are unknown due to addition or invalidation
4269
+ // @todo As per sort - can this be moved into an event handler?
4270
+ _fnColumnTypes( oSettings );
4271
+
4272
+ /* In server-side processing all filtering is done by the server, so no point hanging around here */
4273
+ if ( _fnDataSource( oSettings ) != 'ssp' )
4274
+ {
4275
+ /* Global filter */
4276
+ _fnFilter( oSettings, oInput.sSearch, iForce, fnRegex(oInput), oInput.bSmart, oInput.bCaseInsensitive );
4277
+ fnSaveFilter( oInput );
4278
+
4279
+ /* Now do the individual column filter */
4280
+ for ( var i=0 ; i<aoPrevSearch.length ; i++ )
4281
+ {
4282
+ _fnFilterColumn( oSettings, aoPrevSearch[i].sSearch, i, fnRegex(aoPrevSearch[i]),
4283
+ aoPrevSearch[i].bSmart, aoPrevSearch[i].bCaseInsensitive );
4284
+ }
4285
+
4286
+ /* Custom filtering */
4287
+ _fnFilterCustom( oSettings );
4288
+ }
4289
+ else
4290
+ {
4291
+ fnSaveFilter( oInput );
4292
+ }
4293
+
4294
+ /* Tell the draw function we have been filtering */
4295
+ oSettings.bFiltered = true;
4296
+ _fnCallbackFire( oSettings, null, 'search', [oSettings] );
4297
+ }
4298
+
4299
+
4300
+ /**
4301
+ * Apply custom filtering functions
4302
+ * @param {object} oSettings dataTables settings object
4303
+ * @memberof WFDataTable#oApi
4304
+ */
4305
+ function _fnFilterCustom( settings )
4306
+ {
4307
+ var filters = WFDataTable.ext.search;
4308
+ var displayRows = settings.aiDisplay;
4309
+ var row, rowIdx;
4310
+
4311
+ for ( var i=0, ien=filters.length ; i<ien ; i++ ) {
4312
+ var rows = [];
4313
+
4314
+ // Loop over each row and see if it should be included
4315
+ for ( var j=0, jen=displayRows.length ; j<jen ; j++ ) {
4316
+ rowIdx = displayRows[ j ];
4317
+ row = settings.aoData[ rowIdx ];
4318
+
4319
+ if ( filters[i]( settings, row._aFilterData, rowIdx, row._aData, j ) ) {
4320
+ rows.push( rowIdx );
4321
+ }
4322
+ }
4323
+
4324
+ // So the array reference doesn't break set the results into the
4325
+ // existing array
4326
+ displayRows.length = 0;
4327
+ $.merge( displayRows, rows );
4328
+ }
4329
+ }
4330
+
4331
+
4332
+ /**
4333
+ * Filter the table on a per-column basis
4334
+ * @param {object} oSettings dataTables settings object
4335
+ * @param {string} sInput string to filter on
4336
+ * @param {int} iColumn column to filter
4337
+ * @param {bool} bRegex treat search string as a regular expression or not
4338
+ * @param {bool} bSmart use smart filtering or not
4339
+ * @param {bool} bCaseInsensitive Do case insenstive matching or not
4340
+ * @memberof WFDataTable#oApi
4341
+ */
4342
+ function _fnFilterColumn ( settings, searchStr, colIdx, regex, smart, caseInsensitive )
4343
+ {
4344
+ if ( searchStr === '' ) {
4345
+ return;
4346
+ }
4347
+
4348
+ var data;
4349
+ var out = [];
4350
+ var display = settings.aiDisplay;
4351
+ var rpSearch = _fnFilterCreateSearch( searchStr, regex, smart, caseInsensitive );
4352
+
4353
+ for ( var i=0 ; i<display.length ; i++ ) {
4354
+ data = settings.aoData[ display[i] ]._aFilterData[ colIdx ];
4355
+
4356
+ if ( rpSearch.test( data ) ) {
4357
+ out.push( display[i] );
4358
+ }
4359
+ }
4360
+
4361
+ settings.aiDisplay = out;
4362
+ }
4363
+
4364
+
4365
+ /**
4366
+ * Filter the data table based on user input and draw the table
4367
+ * @param {object} settings dataTables settings object
4368
+ * @param {string} input string to filter on
4369
+ * @param {int} force optional - force a research of the master array (1) or not (undefined or 0)
4370
+ * @param {bool} regex treat as a regular expression or not
4371
+ * @param {bool} smart perform smart filtering or not
4372
+ * @param {bool} caseInsensitive Do case insenstive matching or not
4373
+ * @memberof WFDataTable#oApi
4374
+ */
4375
+ function _fnFilter( settings, input, force, regex, smart, caseInsensitive )
4376
+ {
4377
+ var rpSearch = _fnFilterCreateSearch( input, regex, smart, caseInsensitive );
4378
+ var prevSearch = settings.oPreviousSearch.sSearch;
4379
+ var displayMaster = settings.aiDisplayMaster;
4380
+ var display, invalidated, i;
4381
+ var filtered = [];
4382
+
4383
+ // Need to take account of custom filtering functions - always filter
4384
+ if ( WFDataTable.ext.search.length !== 0 ) {
4385
+ force = true;
4386
+ }
4387
+
4388
+ // Check if any of the rows were invalidated
4389
+ invalidated = _fnFilterData( settings );
4390
+
4391
+ // If the input is blank - we just want the full data set
4392
+ if ( input.length <= 0 ) {
4393
+ settings.aiDisplay = displayMaster.slice();
4394
+ }
4395
+ else {
4396
+ // New search - start from the master array
4397
+ if ( invalidated ||
4398
+ force ||
4399
+ prevSearch.length > input.length ||
4400
+ input.indexOf(prevSearch) !== 0 ||
4401
+ settings.bSorted // On resort, the display master needs to be
4402
+ // re-filtered since indexes will have changed
4403
+ ) {
4404
+ settings.aiDisplay = displayMaster.slice();
4405
+ }
4406
+
4407
+ // Search the display array
4408
+ display = settings.aiDisplay;
4409
+
4410
+ for ( i=0 ; i<display.length ; i++ ) {
4411
+ if ( rpSearch.test( settings.aoData[ display[i] ]._sFilterRow ) ) {
4412
+ filtered.push( display[i] );
4413
+ }
4414
+ }
4415
+
4416
+ settings.aiDisplay = filtered;
4417
+ }
4418
+ }
4419
+
4420
+
4421
+ /**
4422
+ * Build a regular expression object suitable for searching a table
4423
+ * @param {string} sSearch string to search for
4424
+ * @param {bool} bRegex treat as a regular expression or not
4425
+ * @param {bool} bSmart perform smart filtering or not
4426
+ * @param {bool} bCaseInsensitive Do case insensitive matching or not
4427
+ * @returns {RegExp} constructed object
4428
+ * @memberof WFDataTable#oApi
4429
+ */
4430
+ function _fnFilterCreateSearch( search, regex, smart, caseInsensitive )
4431
+ {
4432
+ search = regex ?
4433
+ search :
4434
+ _fnEscapeRegex( search );
4435
+
4436
+ if ( smart ) {
4437
+ /* For smart filtering we want to allow the search to work regardless of
4438
+ * word order. We also want double quoted text to be preserved, so word
4439
+ * order is important - a la google. So this is what we want to
4440
+ * generate:
4441
+ *
4442
+ * ^(?=.*?\bone\b)(?=.*?\btwo three\b)(?=.*?\bfour\b).*$
4443
+ */
4444
+ var a = $.map( search.match( /"[^"]+"|[^ ]+/g ) || [''], function ( word ) {
4445
+ if ( word.charAt(0) === '"' ) {
4446
+ var m = word.match( /^"(.*)"$/ );
4447
+ word = m ? m[1] : word;
4448
+ }
4449
+
4450
+ return word.replace('"', '');
4451
+ } );
4452
+
4453
+ search = '^(?=.*?'+a.join( ')(?=.*?' )+').*$';
4454
+ }
4455
+
4456
+ return new RegExp( search, caseInsensitive ? 'i' : '' );
4457
+ }
4458
+
4459
+
4460
+ /**
4461
+ * Escape a string such that it can be used in a regular expression
4462
+ * @param {string} sVal string to escape
4463
+ * @returns {string} escaped string
4464
+ * @memberof WFDataTable#oApi
4465
+ */
4466
+ var _fnEscapeRegex = WFDataTable.util.escapeRegex;
4467
+
4468
+ var __filter_div = $('<div>')[0];
4469
+ var __filter_div_textContent = __filter_div.textContent !== undefined;
4470
+
4471
+ // Update the filtering data for each row if needed (by invalidation or first run)
4472
+ function _fnFilterData ( settings )
4473
+ {
4474
+ var columns = settings.aoColumns;
4475
+ var column;
4476
+ var i, j, ien, jen, filterData, cellData, row;
4477
+ var fomatters = WFDataTable.ext.type.search;
4478
+ var wasInvalidated = false;
4479
+
4480
+ for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
4481
+ row = settings.aoData[i];
4482
+
4483
+ if ( ! row._aFilterData ) {
4484
+ filterData = [];
4485
+
4486
+ for ( j=0, jen=columns.length ; j<jen ; j++ ) {
4487
+ column = columns[j];
4488
+
4489
+ if ( column.bSearchable ) {
4490
+ cellData = _fnGetCellData( settings, i, j, 'filter' );
4491
+
4492
+ if ( fomatters[ column.sType ] ) {
4493
+ cellData = fomatters[ column.sType ]( cellData );
4494
+ }
4495
+
4496
+ // Search in WFDataTables 1.10 is string based. In 1.11 this
4497
+ // should be altered to also allow strict type checking.
4498
+ if ( cellData === null ) {
4499
+ cellData = '';
4500
+ }
4501
+
4502
+ if ( typeof cellData !== 'string' && cellData.toString ) {
4503
+ cellData = cellData.toString();
4504
+ }
4505
+ }
4506
+ else {
4507
+ cellData = '';
4508
+ }
4509
+
4510
+ // If it looks like there is an HTML entity in the string,
4511
+ // attempt to decode it so sorting works as expected. Note that
4512
+ // we could use a single line of jQuery to do this, but the DOM
4513
+ // method used here is much faster http://jsperf.com/html-decode
4514
+ if ( cellData.indexOf && cellData.indexOf('&') !== -1 ) {
4515
+ __filter_div.innerHTML = cellData;
4516
+ cellData = __filter_div_textContent ?
4517
+ __filter_div.textContent :
4518
+ __filter_div.innerText;
4519
+ }
4520
+
4521
+ if ( cellData.replace ) {
4522
+ cellData = cellData.replace(/[\r\n]/g, '');
4523
+ }
4524
+
4525
+ filterData.push( cellData );
4526
+ }
4527
+
4528
+ row._aFilterData = filterData;
4529
+ row._sFilterRow = filterData.join(' ');
4530
+ wasInvalidated = true;
4531
+ }
4532
+ }
4533
+
4534
+ return wasInvalidated;
4535
+ }
4536
+
4537
+
4538
+ /**
4539
+ * Convert from the internal Hungarian notation to camelCase for external
4540
+ * interaction
4541
+ * @param {object} obj Object to convert
4542
+ * @returns {object} Inverted object
4543
+ * @memberof WFDataTable#oApi
4544
+ */
4545
+ function _fnSearchToCamel ( obj )
4546
+ {
4547
+ return {
4548
+ search: obj.sSearch,
4549
+ smart: obj.bSmart,
4550
+ regex: obj.bRegex,
4551
+ caseInsensitive: obj.bCaseInsensitive
4552
+ };
4553
+ }
4554
+
4555
+
4556
+
4557
+ /**
4558
+ * Convert from camelCase notation to the internal Hungarian. We could use the
4559
+ * Hungarian convert function here, but this is cleaner
4560
+ * @param {object} obj Object to convert
4561
+ * @returns {object} Inverted object
4562
+ * @memberof WFDataTable#oApi
4563
+ */
4564
+ function _fnSearchToHung ( obj )
4565
+ {
4566
+ return {
4567
+ sSearch: obj.search,
4568
+ bSmart: obj.smart,
4569
+ bRegex: obj.regex,
4570
+ bCaseInsensitive: obj.caseInsensitive
4571
+ };
4572
+ }
4573
+
4574
+ /**
4575
+ * Generate the node required for the info display
4576
+ * @param {object} oSettings dataTables settings object
4577
+ * @returns {node} Information element
4578
+ * @memberof WFDataTable#oApi
4579
+ */
4580
+ function _fnFeatureHtmlInfo ( settings )
4581
+ {
4582
+ var
4583
+ tid = settings.sTableId,
4584
+ nodes = settings.aanFeatures.i,
4585
+ n = $('<div/>', {
4586
+ 'class': settings.oClasses.sInfo,
4587
+ 'id': ! nodes ? tid+'_info' : null
4588
+ } );
4589
+
4590
+ if ( ! nodes ) {
4591
+ // Update display on each draw
4592
+ settings.aoDrawCallback.push( {
4593
+ "fn": _fnUpdateInfo,
4594
+ "sName": "information"
4595
+ } );
4596
+
4597
+ n
4598
+ .attr( 'role', 'status' )
4599
+ .attr( 'aria-live', 'polite' );
4600
+
4601
+ // Table is described by our info div
4602
+ $(settings.nTable).attr( 'aria-describedby', tid+'_info' );
4603
+ }
4604
+
4605
+ return n[0];
4606
+ }
4607
+
4608
+
4609
+ /**
4610
+ * Update the information elements in the display
4611
+ * @param {object} settings dataTables settings object
4612
+ * @memberof WFDataTable#oApi
4613
+ */
4614
+ function _fnUpdateInfo ( settings )
4615
+ {
4616
+ /* Show information about the table */
4617
+ var nodes = settings.aanFeatures.i;
4618
+ if ( nodes.length === 0 ) {
4619
+ return;
4620
+ }
4621
+
4622
+ var
4623
+ lang = settings.oLanguage,
4624
+ start = settings._iDisplayStart+1,
4625
+ end = settings.fnDisplayEnd(),
4626
+ max = settings.fnRecordsTotal(),
4627
+ total = settings.fnRecordsDisplay(),
4628
+ out = total ?
4629
+ lang.sInfo :
4630
+ lang.sInfoEmpty;
4631
+
4632
+ if ( total !== max ) {
4633
+ /* Record set after filtering */
4634
+ out += ' ' + lang.sInfoFiltered;
4635
+ }
4636
+
4637
+ // Convert the macros
4638
+ out += lang.sInfoPostFix;
4639
+ out = _fnInfoMacros( settings, out );
4640
+
4641
+ var callback = lang.fnInfoCallback;
4642
+ if ( callback !== null ) {
4643
+ out = callback.call( settings.oInstance,
4644
+ settings, start, end, max, total, out
4645
+ );
4646
+ }
4647
+
4648
+ $(nodes).html( out );
4649
+ }
4650
+
4651
+
4652
+ function _fnInfoMacros ( settings, str )
4653
+ {
4654
+ // When infinite scrolling, we are always starting at 1. _iDisplayStart is used only
4655
+ // internally
4656
+ var
4657
+ formatter = settings.fnFormatNumber,
4658
+ start = settings._iDisplayStart+1,
4659
+ len = settings._iDisplayLength,
4660
+ vis = settings.fnRecordsDisplay(),
4661
+ all = len === -1;
4662
+
4663
+ return str.
4664
+ replace(/_START_/g, formatter.call( settings, start ) ).
4665
+ replace(/_END_/g, formatter.call( settings, settings.fnDisplayEnd() ) ).
4666
+ replace(/_MAX_/g, formatter.call( settings, settings.fnRecordsTotal() ) ).
4667
+ replace(/_TOTAL_/g, formatter.call( settings, vis ) ).
4668
+ replace(/_PAGE_/g, formatter.call( settings, all ? 1 : Math.ceil( start / len ) ) ).
4669
+ replace(/_PAGES_/g, formatter.call( settings, all ? 1 : Math.ceil( vis / len ) ) );
4670
+ }
4671
+
4672
+
4673
+
4674
+ /**
4675
+ * Draw the table for the first time, adding all required features
4676
+ * @param {object} settings dataTables settings object
4677
+ * @memberof WFDataTable#oApi
4678
+ */
4679
+ function _fnInitialise ( settings )
4680
+ {
4681
+ var i, iLen, iAjaxStart=settings.iInitDisplayStart;
4682
+ var columns = settings.aoColumns, column;
4683
+ var features = settings.oFeatures;
4684
+ var deferLoading = settings.bDeferLoading; // value modified by the draw
4685
+
4686
+ /* Ensure that the table data is fully initialised */
4687
+ if ( ! settings.bInitialised ) {
4688
+ setTimeout( function(){ _fnInitialise( settings ); }, 200 );
4689
+ return;
4690
+ }
4691
+
4692
+ /* Show the display HTML options */
4693
+ _fnAddOptionsHtml( settings );
4694
+
4695
+ /* Build and draw the header / footer for the table */
4696
+ _fnBuildHead( settings );
4697
+ _fnDrawHead( settings, settings.aoHeader );
4698
+ _fnDrawHead( settings, settings.aoFooter );
4699
+
4700
+ /* Okay to show that something is going on now */
4701
+ _fnProcessingDisplay( settings, true );
4702
+
4703
+ /* Calculate sizes for columns */
4704
+ if ( features.bAutoWidth ) {
4705
+ _fnCalculateColumnWidths( settings );
4706
+ }
4707
+
4708
+ for ( i=0, iLen=columns.length ; i<iLen ; i++ ) {
4709
+ column = columns[i];
4710
+
4711
+ if ( column.sWidth ) {
4712
+ column.nTh.style.width = _fnStringToCss( column.sWidth );
4713
+ }
4714
+ }
4715
+
4716
+ _fnCallbackFire( settings, null, 'preInit', [settings] );
4717
+
4718
+ // If there is default sorting required - let's do it. The sort function
4719
+ // will do the drawing for us. Otherwise we draw the table regardless of the
4720
+ // Ajax source - this allows the table to look initialised for Ajax sourcing
4721
+ // data (show 'loading' message possibly)
4722
+ _fnReDraw( settings );
4723
+
4724
+ // Server-side processing init complete is done by _fnAjaxUpdateDraw
4725
+ var dataSrc = _fnDataSource( settings );
4726
+ if ( dataSrc != 'ssp' || deferLoading ) {
4727
+ // if there is an ajax source load the data
4728
+ if ( dataSrc == 'ajax' ) {
4729
+ _fnBuildAjax( settings, [], function(json) {
4730
+ var aData = _fnAjaxDataSrc( settings, json );
4731
+
4732
+ // Got the data - add it to the table
4733
+ for ( i=0 ; i<aData.length ; i++ ) {
4734
+ _fnAddData( settings, aData[i] );
4735
+ }
4736
+
4737
+ // Reset the init display for cookie saving. We've already done
4738
+ // a filter, and therefore cleared it before. So we need to make
4739
+ // it appear 'fresh'
4740
+ settings.iInitDisplayStart = iAjaxStart;
4741
+
4742
+ _fnReDraw( settings );
4743
+
4744
+ _fnProcessingDisplay( settings, false );
4745
+ _fnInitComplete( settings, json );
4746
+ }, settings );
4747
+ }
4748
+ else {
4749
+ _fnProcessingDisplay( settings, false );
4750
+ _fnInitComplete( settings );
4751
+ }
4752
+ }
4753
+ }
4754
+
4755
+
4756
+ /**
4757
+ * Draw the table for the first time, adding all required features
4758
+ * @param {object} oSettings dataTables settings object
4759
+ * @param {object} [json] JSON from the server that completed the table, if using Ajax source
4760
+ * with client-side processing (optional)
4761
+ * @memberof WFDataTable#oApi
4762
+ */
4763
+ function _fnInitComplete ( settings, json )
4764
+ {
4765
+ settings._bInitComplete = true;
4766
+
4767
+ // When data was added after the initialisation (data or Ajax) we need to
4768
+ // calculate the column sizing
4769
+ if ( json || settings.oInit.aaData ) {
4770
+ _fnAdjustColumnSizing( settings );
4771
+ }
4772
+
4773
+ _fnCallbackFire( settings, null, 'plugin-init', [settings, json] );
4774
+ _fnCallbackFire( settings, 'aoInitComplete', 'init', [settings, json] );
4775
+ }
4776
+
4777
+
4778
+ function _fnLengthChange ( settings, val )
4779
+ {
4780
+ var len = parseInt( val, 10 );
4781
+ settings._iDisplayLength = len;
4782
+
4783
+ _fnLengthOverflow( settings );
4784
+
4785
+ // Fire length change event
4786
+ _fnCallbackFire( settings, null, 'length', [settings, len] );
4787
+ }
4788
+
4789
+
4790
+ /**
4791
+ * Generate the node required for user display length changing
4792
+ * @param {object} settings dataTables settings object
4793
+ * @returns {node} Display length feature node
4794
+ * @memberof WFDataTable#oApi
4795
+ */
4796
+ function _fnFeatureHtmlLength ( settings )
4797
+ {
4798
+ var
4799
+ classes = settings.oClasses,
4800
+ tableId = settings.sTableId,
4801
+ menu = settings.aLengthMenu,
4802
+ d2 = $.isArray( menu[0] ),
4803
+ lengths = d2 ? menu[0] : menu,
4804
+ language = d2 ? menu[1] : menu;
4805
+
4806
+ var select = $('<select/>', {
4807
+ 'name': tableId+'_length',
4808
+ 'aria-controls': tableId,
4809
+ 'class': classes.sLengthSelect
4810
+ } );
4811
+
4812
+ for ( var i=0, ien=lengths.length ; i<ien ; i++ ) {
4813
+ select[0][ i ] = new Option( language[i], lengths[i] );
4814
+ }
4815
+
4816
+ var div = $('<div><label/></div>').addClass( classes.sLength );
4817
+ if ( ! settings.aanFeatures.l ) {
4818
+ div[0].id = tableId+'_length';
4819
+ }
4820
+
4821
+ div.children().append(
4822
+ settings.oLanguage.sLengthMenu.replace( '_MENU_', select[0].outerHTML )
4823
+ );
4824
+
4825
+ // Can't use `select` variable as user might provide their own and the
4826
+ // reference is broken by the use of outerHTML
4827
+ $('select', div)
4828
+ .val( settings._iDisplayLength )
4829
+ .on( 'change.DT', function(e) {
4830
+ _fnLengthChange( settings, $(this).val() );
4831
+ _fnDraw( settings );
4832
+ } );
4833
+
4834
+ // Update node value whenever anything changes the table's length
4835
+ $(settings.nTable).on( 'length.dt.DT', function (e, s, len) {
4836
+ if ( settings === s ) {
4837
+ $('select', div).val( len );
4838
+ }
4839
+ } );
4840
+
4841
+ return div[0];
4842
+ }
4843
+
4844
+
4845
+
4846
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
4847
+ * Note that most of the paging logic is done in
4848
+ * WFDataTable.ext.pager
4849
+ */
4850
+
4851
+ /**
4852
+ * Generate the node required for default pagination
4853
+ * @param {object} oSettings dataTables settings object
4854
+ * @returns {node} Pagination feature node
4855
+ * @memberof WFDataTable#oApi
4856
+ */
4857
+ function _fnFeatureHtmlPaginate ( settings )
4858
+ {
4859
+ var
4860
+ type = settings.sPaginationType,
4861
+ plugin = WFDataTable.ext.pager[ type ],
4862
+ modern = typeof plugin === 'function',
4863
+ redraw = function( settings ) {
4864
+ _fnDraw( settings );
4865
+ },
4866
+ node = $('<div/>').addClass( settings.oClasses.sPaging + type )[0],
4867
+ features = settings.aanFeatures;
4868
+
4869
+ if ( ! modern ) {
4870
+ plugin.fnInit( settings, node, redraw );
4871
+ }
4872
+
4873
+ /* Add a draw callback for the pagination on first instance, to update the paging display */
4874
+ if ( ! features.p )
4875
+ {
4876
+ node.id = settings.sTableId+'_paginate';
4877
+
4878
+ settings.aoDrawCallback.push( {
4879
+ "fn": function( settings ) {
4880
+ if ( modern ) {
4881
+ var
4882
+ start = settings._iDisplayStart,
4883
+ len = settings._iDisplayLength,
4884
+ visRecords = settings.fnRecordsDisplay(),
4885
+ all = len === -1,
4886
+ page = all ? 0 : Math.ceil( start / len ),
4887
+ pages = all ? 1 : Math.ceil( visRecords / len ),
4888
+ buttons = plugin(page, pages),
4889
+ i, ien;
4890
+
4891
+ for ( i=0, ien=features.p.length ; i<ien ; i++ ) {
4892
+ _fnRenderer( settings, 'pageButton' )(
4893
+ settings, features.p[i], i, buttons, page, pages
4894
+ );
4895
+ }
4896
+ }
4897
+ else {
4898
+ plugin.fnUpdate( settings, redraw );
4899
+ }
4900
+ },
4901
+ "sName": "pagination"
4902
+ } );
4903
+ }
4904
+
4905
+ return node;
4906
+ }
4907
+
4908
+
4909
+ /**
4910
+ * Alter the display settings to change the page
4911
+ * @param {object} settings WFDataTables settings object
4912
+ * @param {string|int} action Paging action to take: "first", "previous",
4913
+ * "next" or "last" or page number to jump to (integer)
4914
+ * @param [bool] redraw Automatically draw the update or not
4915
+ * @returns {bool} true page has changed, false - no change
4916
+ * @memberof WFDataTable#oApi
4917
+ */
4918
+ function _fnPageChange ( settings, action, redraw )
4919
+ {
4920
+ var
4921
+ start = settings._iDisplayStart,
4922
+ len = settings._iDisplayLength,
4923
+ records = settings.fnRecordsDisplay();
4924
+
4925
+ if ( records === 0 || len === -1 )
4926
+ {
4927
+ start = 0;
4928
+ }
4929
+ else if ( typeof action === "number" )
4930
+ {
4931
+ start = action * len;
4932
+
4933
+ if ( start > records )
4934
+ {
4935
+ start = 0;
4936
+ }
4937
+ }
4938
+ else if ( action == "first" )
4939
+ {
4940
+ start = 0;
4941
+ }
4942
+ else if ( action == "previous" )
4943
+ {
4944
+ start = len >= 0 ?
4945
+ start - len :
4946
+ 0;
4947
+
4948
+ if ( start < 0 )
4949
+ {
4950
+ start = 0;
4951
+ }
4952
+ }
4953
+ else if ( action == "next" )
4954
+ {
4955
+ if ( start + len < records )
4956
+ {
4957
+ start += len;
4958
+ }
4959
+ }
4960
+ else if ( action == "last" )
4961
+ {
4962
+ start = Math.floor( (records-1) / len) * len;
4963
+ }
4964
+ else
4965
+ {
4966
+ _fnLog( settings, 0, "Unknown paging action: "+action, 5 );
4967
+ }
4968
+
4969
+ var changed = settings._iDisplayStart !== start;
4970
+ settings._iDisplayStart = start;
4971
+
4972
+ if ( changed ) {
4973
+ _fnCallbackFire( settings, null, 'page', [settings] );
4974
+
4975
+ if ( redraw ) {
4976
+ _fnDraw( settings );
4977
+ }
4978
+ }
4979
+
4980
+ return changed;
4981
+ }
4982
+
4983
+
4984
+
4985
+ /**
4986
+ * Generate the node required for the processing node
4987
+ * @param {object} settings dataTables settings object
4988
+ * @returns {node} Processing element
4989
+ * @memberof WFDataTable#oApi
4990
+ */
4991
+ function _fnFeatureHtmlProcessing ( settings )
4992
+ {
4993
+ return $('<div/>', {
4994
+ 'id': ! settings.aanFeatures.r ? settings.sTableId+'_processing' : null,
4995
+ 'class': settings.oClasses.sProcessing
4996
+ } )
4997
+ .html( settings.oLanguage.sProcessing )
4998
+ .insertBefore( settings.nTable )[0];
4999
+ }
5000
+
5001
+
5002
+ /**
5003
+ * Display or hide the processing indicator
5004
+ * @param {object} settings dataTables settings object
5005
+ * @param {bool} show Show the processing indicator (true) or not (false)
5006
+ * @memberof WFDataTable#oApi
5007
+ */
5008
+ function _fnProcessingDisplay ( settings, show )
5009
+ {
5010
+ if ( settings.oFeatures.bProcessing ) {
5011
+ $(settings.aanFeatures.r).css( 'display', show ? 'block' : 'none' );
5012
+ }
5013
+
5014
+ _fnCallbackFire( settings, null, 'processing', [settings, show] );
5015
+ }
5016
+
5017
+ /**
5018
+ * Add any control elements for the table - specifically scrolling
5019
+ * @param {object} settings dataTables settings object
5020
+ * @returns {node} Node to add to the DOM
5021
+ * @memberof WFDataTable#oApi
5022
+ */
5023
+ function _fnFeatureHtmlTable ( settings )
5024
+ {
5025
+ var table = $(settings.nTable);
5026
+
5027
+ // Add the ARIA grid role to the table
5028
+ table.attr( 'role', 'grid' );
5029
+
5030
+ // Scrolling from here on in
5031
+ var scroll = settings.oScroll;
5032
+
5033
+ if ( scroll.sX === '' && scroll.sY === '' ) {
5034
+ return settings.nTable;
5035
+ }
5036
+
5037
+ var scrollX = scroll.sX;
5038
+ var scrollY = scroll.sY;
5039
+ var classes = settings.oClasses;
5040
+ var caption = table.children('caption');
5041
+ var captionSide = caption.length ? caption[0]._captionSide : null;
5042
+ var headerClone = $( table[0].cloneNode(false) );
5043
+ var footerClone = $( table[0].cloneNode(false) );
5044
+ var footer = table.children('tfoot');
5045
+ var _div = '<div/>';
5046
+ var size = function ( s ) {
5047
+ return !s ? null : _fnStringToCss( s );
5048
+ };
5049
+
5050
+ if ( ! footer.length ) {
5051
+ footer = null;
5052
+ }
5053
+
5054
+ /*
5055
+ * The HTML structure that we want to generate in this function is:
5056
+ * div - scroller
5057
+ * div - scroll head
5058
+ * div - scroll head inner
5059
+ * table - scroll head table
5060
+ * thead - thead
5061
+ * div - scroll body
5062
+ * table - table (master table)
5063
+ * thead - thead clone for sizing
5064
+ * tbody - tbody
5065
+ * div - scroll foot
5066
+ * div - scroll foot inner
5067
+ * table - scroll foot table
5068
+ * tfoot - tfoot
5069
+ */
5070
+ var scroller = $( _div, { 'class': classes.sScrollWrapper } )
5071
+ .append(
5072
+ $(_div, { 'class': classes.sScrollHead } )
5073
+ .css( {
5074
+ overflow: 'hidden',
5075
+ position: 'relative',
5076
+ border: 0,
5077
+ width: scrollX ? size(scrollX) : '100%'
5078
+ } )
5079
+ .append(
5080
+ $(_div, { 'class': classes.sScrollHeadInner } )
5081
+ .css( {
5082
+ 'box-sizing': 'content-box',
5083
+ width: scroll.sXInner || '100%'
5084
+ } )
5085
+ .append(
5086
+ headerClone
5087
+ .removeAttr('id')
5088
+ .css( 'margin-left', 0 )
5089
+ .append( captionSide === 'top' ? caption : null )
5090
+ .append(
5091
+ table.children('thead')
5092
+ )
5093
+ )
5094
+ )
5095
+ )
5096
+ .append(
5097
+ $(_div, { 'class': classes.sScrollBody } )
5098
+ .css( {
5099
+ position: 'relative',
5100
+ overflow: 'auto',
5101
+ width: size( scrollX )
5102
+ } )
5103
+ .append( table )
5104
+ );
5105
+
5106
+ if ( footer ) {
5107
+ scroller.append(
5108
+ $(_div, { 'class': classes.sScrollFoot } )
5109
+ .css( {
5110
+ overflow: 'hidden',
5111
+ border: 0,
5112
+ width: scrollX ? size(scrollX) : '100%'
5113
+ } )
5114
+ .append(
5115
+ $(_div, { 'class': classes.sScrollFootInner } )
5116
+ .append(
5117
+ footerClone
5118
+ .removeAttr('id')
5119
+ .css( 'margin-left', 0 )
5120
+ .append( captionSide === 'bottom' ? caption : null )
5121
+ .append(
5122
+ table.children('tfoot')
5123
+ )
5124
+ )
5125
+ )
5126
+ );
5127
+ }
5128
+
5129
+ var children = scroller.children();
5130
+ var scrollHead = children[0];
5131
+ var scrollBody = children[1];
5132
+ var scrollFoot = footer ? children[2] : null;
5133
+
5134
+ // When the body is scrolled, then we also want to scroll the headers
5135
+ if ( scrollX ) {
5136
+ $(scrollBody).on( 'scroll.DT', function (e) {
5137
+ var scrollLeft = this.scrollLeft;
5138
+
5139
+ scrollHead.scrollLeft = scrollLeft;
5140
+
5141
+ if ( footer ) {
5142
+ scrollFoot.scrollLeft = scrollLeft;
5143
+ }
5144
+ } );
5145
+ }
5146
+
5147
+ $(scrollBody).css(
5148
+ scrollY && scroll.bCollapse ? 'max-height' : 'height',
5149
+ scrollY
5150
+ );
5151
+
5152
+ settings.nScrollHead = scrollHead;
5153
+ settings.nScrollBody = scrollBody;
5154
+ settings.nScrollFoot = scrollFoot;
5155
+
5156
+ // On redraw - align columns
5157
+ settings.aoDrawCallback.push( {
5158
+ "fn": _fnScrollDraw,
5159
+ "sName": "scrolling"
5160
+ } );
5161
+
5162
+ return scroller[0];
5163
+ }
5164
+
5165
+
5166
+
5167
+ /**
5168
+ * Update the header, footer and body tables for resizing - i.e. column
5169
+ * alignment.
5170
+ *
5171
+ * Welcome to the most horrible function WFDataTables. The process that this
5172
+ * function follows is basically:
5173
+ * 1. Re-create the table inside the scrolling div
5174
+ * 2. Take live measurements from the DOM
5175
+ * 3. Apply the measurements to align the columns
5176
+ * 4. Clean up
5177
+ *
5178
+ * @param {object} settings dataTables settings object
5179
+ * @memberof WFDataTable#oApi
5180
+ */
5181
+ function _fnScrollDraw ( settings )
5182
+ {
5183
+ // Given that this is such a monster function, a lot of variables are use
5184
+ // to try and keep the minimised size as small as possible
5185
+ var
5186
+ scroll = settings.oScroll,
5187
+ scrollX = scroll.sX,
5188
+ scrollXInner = scroll.sXInner,
5189
+ scrollY = scroll.sY,
5190
+ barWidth = scroll.iBarWidth,
5191
+ divHeader = $(settings.nScrollHead),
5192
+ divHeaderStyle = divHeader[0].style,
5193
+ divHeaderInner = divHeader.children('div'),
5194
+ divHeaderInnerStyle = divHeaderInner[0].style,
5195
+ divHeaderTable = divHeaderInner.children('table'),
5196
+ divBodyEl = settings.nScrollBody,
5197
+ divBody = $(divBodyEl),
5198
+ divBodyStyle = divBodyEl.style,
5199
+ divFooter = $(settings.nScrollFoot),
5200
+ divFooterInner = divFooter.children('div'),
5201
+ divFooterTable = divFooterInner.children('table'),
5202
+ header = $(settings.nTHead),
5203
+ table = $(settings.nTable),
5204
+ tableEl = table[0],
5205
+ tableStyle = tableEl.style,
5206
+ footer = settings.nTFoot ? $(settings.nTFoot) : null,
5207
+ browser = settings.oBrowser,
5208
+ ie67 = browser.bScrollOversize,
5209
+ dtHeaderCells = _pluck( settings.aoColumns, 'nTh' ),
5210
+ headerTrgEls, footerTrgEls,
5211
+ headerSrcEls, footerSrcEls,
5212
+ headerCopy, footerCopy,
5213
+ headerWidths=[], footerWidths=[],
5214
+ headerContent=[], footerContent=[],
5215
+ idx, correction, sanityWidth,
5216
+ zeroOut = function(nSizer) {
5217
+ var style = nSizer.style;
5218
+ style.paddingTop = "0";
5219
+ style.paddingBottom = "0";
5220
+ style.borderTopWidth = "0";
5221
+ style.borderBottomWidth = "0";
5222
+ style.height = 0;
5223
+ };
5224
+
5225
+ // If the scrollbar visibility has changed from the last draw, we need to
5226
+ // adjust the column sizes as the table width will have changed to account
5227
+ // for the scrollbar
5228
+ var scrollBarVis = divBodyEl.scrollHeight > divBodyEl.clientHeight;
5229
+
5230
+ if ( settings.scrollBarVis !== scrollBarVis && settings.scrollBarVis !== undefined ) {
5231
+ settings.scrollBarVis = scrollBarVis;
5232
+ _fnAdjustColumnSizing( settings );
5233
+ return; // adjust column sizing will call this function again
5234
+ }
5235
+ else {
5236
+ settings.scrollBarVis = scrollBarVis;
5237
+ }
5238
+
5239
+ /*
5240
+ * 1. Re-create the table inside the scrolling div
5241
+ */
5242
+
5243
+ // Remove the old minimised thead and tfoot elements in the inner table
5244
+ table.children('thead, tfoot').remove();
5245
+
5246
+ if ( footer ) {
5247
+ footerCopy = footer.clone().prependTo( table );
5248
+ footerTrgEls = footer.find('tr'); // the original tfoot is in its own table and must be sized
5249
+ footerSrcEls = footerCopy.find('tr');
5250
+ }
5251
+
5252
+ // Clone the current header and footer elements and then place it into the inner table
5253
+ headerCopy = header.clone().prependTo( table );
5254
+ headerTrgEls = header.find('tr'); // original header is in its own table
5255
+ headerSrcEls = headerCopy.find('tr');
5256
+ headerCopy.find('th, td').removeAttr('tabindex');
5257
+
5258
+
5259
+ /*
5260
+ * 2. Take live measurements from the DOM - do not alter the DOM itself!
5261
+ */
5262
+
5263
+ // Remove old sizing and apply the calculated column widths
5264
+ // Get the unique column headers in the newly created (cloned) header. We want to apply the
5265
+ // calculated sizes to this header
5266
+ if ( ! scrollX )
5267
+ {
5268
+ divBodyStyle.width = '100%';
5269
+ divHeader[0].style.width = '100%';
5270
+ }
5271
+
5272
+ $.each( _fnGetUniqueThs( settings, headerCopy ), function ( i, el ) {
5273
+ idx = _fnVisibleToColumnIndex( settings, i );
5274
+ el.style.width = settings.aoColumns[idx].sWidth;
5275
+ } );
5276
+
5277
+ if ( footer ) {
5278
+ _fnApplyToChildren( function(n) {
5279
+ n.style.width = "";
5280
+ }, footerSrcEls );
5281
+ }
5282
+
5283
+ // Size the table as a whole
5284
+ sanityWidth = table.outerWidth();
5285
+ if ( scrollX === "" ) {
5286
+ // No x scrolling
5287
+ tableStyle.width = "100%";
5288
+
5289
+ // IE7 will make the width of the table when 100% include the scrollbar
5290
+ // - which is shouldn't. When there is a scrollbar we need to take this
5291
+ // into account.
5292
+ if ( ie67 && (table.find('tbody').height() > divBodyEl.offsetHeight ||
5293
+ divBody.css('overflow-y') == "scroll")
5294
+ ) {
5295
+ tableStyle.width = _fnStringToCss( table.outerWidth() - barWidth);
5296
+ }
5297
+
5298
+ // Recalculate the sanity width
5299
+ sanityWidth = table.outerWidth();
5300
+ }
5301
+ else if ( scrollXInner !== "" ) {
5302
+ // legacy x scroll inner has been given - use it
5303
+ tableStyle.width = _fnStringToCss(scrollXInner);
5304
+
5305
+ // Recalculate the sanity width
5306
+ sanityWidth = table.outerWidth();
5307
+ }
5308
+
5309
+ // Hidden header should have zero height, so remove padding and borders. Then
5310
+ // set the width based on the real headers
5311
+
5312
+ // Apply all styles in one pass
5313
+ _fnApplyToChildren( zeroOut, headerSrcEls );
5314
+
5315
+ // Read all widths in next pass
5316
+ _fnApplyToChildren( function(nSizer) {
5317
+ headerContent.push( nSizer.innerHTML );
5318
+ headerWidths.push( _fnStringToCss( $(nSizer).css('width') ) );
5319
+ }, headerSrcEls );
5320
+
5321
+ // Apply all widths in final pass
5322
+ _fnApplyToChildren( function(nToSize, i) {
5323
+ // Only apply widths to the WFDataTables detected header cells - this
5324
+ // prevents complex headers from having contradictory sizes applied
5325
+ if ( $.inArray( nToSize, dtHeaderCells ) !== -1 ) {
5326
+ nToSize.style.width = headerWidths[i];
5327
+ }
5328
+ }, headerTrgEls );
5329
+
5330
+ $(headerSrcEls).height(0);
5331
+
5332
+ /* Same again with the footer if we have one */
5333
+ if ( footer )
5334
+ {
5335
+ _fnApplyToChildren( zeroOut, footerSrcEls );
5336
+
5337
+ _fnApplyToChildren( function(nSizer) {
5338
+ footerContent.push( nSizer.innerHTML );
5339
+ footerWidths.push( _fnStringToCss( $(nSizer).css('width') ) );
5340
+ }, footerSrcEls );
5341
+
5342
+ _fnApplyToChildren( function(nToSize, i) {
5343
+ nToSize.style.width = footerWidths[i];
5344
+ }, footerTrgEls );
5345
+
5346
+ $(footerSrcEls).height(0);
5347
+ }
5348
+
5349
+
5350
+ /*
5351
+ * 3. Apply the measurements
5352
+ */
5353
+
5354
+ // "Hide" the header and footer that we used for the sizing. We need to keep
5355
+ // the content of the cell so that the width applied to the header and body
5356
+ // both match, but we want to hide it completely. We want to also fix their
5357
+ // width to what they currently are
5358
+ _fnApplyToChildren( function(nSizer, i) {
5359
+ nSizer.innerHTML = '<div class="dataTables_sizing" style="height:0;overflow:hidden;">'+headerContent[i]+'</div>';
5360
+ nSizer.style.width = headerWidths[i];
5361
+ }, headerSrcEls );
5362
+
5363
+ if ( footer )
5364
+ {
5365
+ _fnApplyToChildren( function(nSizer, i) {
5366
+ nSizer.innerHTML = '<div class="dataTables_sizing" style="height:0;overflow:hidden;">'+footerContent[i]+'</div>';
5367
+ nSizer.style.width = footerWidths[i];
5368
+ }, footerSrcEls );
5369
+ }
5370
+
5371
+ // Sanity check that the table is of a sensible width. If not then we are going to get
5372
+ // misalignment - try to prevent this by not allowing the table to shrink below its min width
5373
+ if ( table.outerWidth() < sanityWidth )
5374
+ {
5375
+ // The min width depends upon if we have a vertical scrollbar visible or not */
5376
+ correction = ((divBodyEl.scrollHeight > divBodyEl.offsetHeight ||
5377
+ divBody.css('overflow-y') == "scroll")) ?
5378
+ sanityWidth+barWidth :
5379
+ sanityWidth;
5380
+
5381
+ // IE6/7 are a law unto themselves...
5382
+ if ( ie67 && (divBodyEl.scrollHeight >
5383
+ divBodyEl.offsetHeight || divBody.css('overflow-y') == "scroll")
5384
+ ) {
5385
+ tableStyle.width = _fnStringToCss( correction-barWidth );
5386
+ }
5387
+
5388
+ // And give the user a warning that we've stopped the table getting too small
5389
+ if ( scrollX === "" || scrollXInner !== "" ) {
5390
+ _fnLog( settings, 1, 'Possible column misalignment', 6 );
5391
+ }
5392
+ }
5393
+ else
5394
+ {
5395
+ correction = '100%';
5396
+ }
5397
+
5398
+ // Apply to the container elements
5399
+ divBodyStyle.width = _fnStringToCss( correction );
5400
+ divHeaderStyle.width = _fnStringToCss( correction );
5401
+
5402
+ if ( footer ) {
5403
+ settings.nScrollFoot.style.width = _fnStringToCss( correction );
5404
+ }
5405
+
5406
+
5407
+ /*
5408
+ * 4. Clean up
5409
+ */
5410
+ if ( ! scrollY ) {
5411
+ /* IE7< puts a vertical scrollbar in place (when it shouldn't be) due to subtracting
5412
+ * the scrollbar height from the visible display, rather than adding it on. We need to
5413
+ * set the height in order to sort this. Don't want to do it in any other browsers.
5414
+ */
5415
+ if ( ie67 ) {
5416
+ divBodyStyle.height = _fnStringToCss( tableEl.offsetHeight+barWidth );
5417
+ }
5418
+ }
5419
+
5420
+ /* Finally set the width's of the header and footer tables */
5421
+ var iOuterWidth = table.outerWidth();
5422
+ divHeaderTable[0].style.width = _fnStringToCss( iOuterWidth );
5423
+ divHeaderInnerStyle.width = _fnStringToCss( iOuterWidth );
5424
+
5425
+ // Figure out if there are scrollbar present - if so then we need a the header and footer to
5426
+ // provide a bit more space to allow "overflow" scrolling (i.e. past the scrollbar)
5427
+ var bScrolling = table.height() > divBodyEl.clientHeight || divBody.css('overflow-y') == "scroll";
5428
+ var padding = 'padding' + (browser.bScrollbarLeft ? 'Left' : 'Right' );
5429
+ divHeaderInnerStyle[ padding ] = bScrolling ? barWidth+"px" : "0px";
5430
+
5431
+ if ( footer ) {
5432
+ divFooterTable[0].style.width = _fnStringToCss( iOuterWidth );
5433
+ divFooterInner[0].style.width = _fnStringToCss( iOuterWidth );
5434
+ divFooterInner[0].style[padding] = bScrolling ? barWidth+"px" : "0px";
5435
+ }
5436
+
5437
+ // Correct DOM ordering for colgroup - comes before the thead
5438
+ table.children('colgroup').insertBefore( table.children('thead') );
5439
+
5440
+ /* Adjust the position of the header in case we loose the y-scrollbar */
5441
+ divBody.scroll();
5442
+
5443
+ // If sorting or filtering has occurred, jump the scrolling back to the top
5444
+ // only if we aren't holding the position
5445
+ if ( (settings.bSorted || settings.bFiltered) && ! settings._drawHold ) {
5446
+ divBodyEl.scrollTop = 0;
5447
+ }
5448
+ }
5449
+
5450
+
5451
+
5452
+ /**
5453
+ * Apply a given function to the display child nodes of an element array (typically
5454
+ * TD children of TR rows
5455
+ * @param {function} fn Method to apply to the objects
5456
+ * @param array {nodes} an1 List of elements to look through for display children
5457
+ * @param array {nodes} an2 Another list (identical structure to the first) - optional
5458
+ * @memberof WFDataTable#oApi
5459
+ */
5460
+ function _fnApplyToChildren( fn, an1, an2 )
5461
+ {
5462
+ var index=0, i=0, iLen=an1.length;
5463
+ var nNode1, nNode2;
5464
+
5465
+ while ( i < iLen ) {
5466
+ nNode1 = an1[i].firstChild;
5467
+ nNode2 = an2 ? an2[i].firstChild : null;
5468
+
5469
+ while ( nNode1 ) {
5470
+ if ( nNode1.nodeType === 1 ) {
5471
+ if ( an2 ) {
5472
+ fn( nNode1, nNode2, index );
5473
+ }
5474
+ else {
5475
+ fn( nNode1, index );
5476
+ }
5477
+
5478
+ index++;
5479
+ }
5480
+
5481
+ nNode1 = nNode1.nextSibling;
5482
+ nNode2 = an2 ? nNode2.nextSibling : null;
5483
+ }
5484
+
5485
+ i++;
5486
+ }
5487
+ }
5488
+
5489
+
5490
+
5491
+ var __re_html_remove = /<.*?>/g;
5492
+
5493
+
5494
+ /**
5495
+ * Calculate the width of columns for the table
5496
+ * @param {object} oSettings dataTables settings object
5497
+ * @memberof WFDataTable#oApi
5498
+ */
5499
+ function _fnCalculateColumnWidths ( oSettings )
5500
+ {
5501
+ var
5502
+ table = oSettings.nTable,
5503
+ columns = oSettings.aoColumns,
5504
+ scroll = oSettings.oScroll,
5505
+ scrollY = scroll.sY,
5506
+ scrollX = scroll.sX,
5507
+ scrollXInner = scroll.sXInner,
5508
+ columnCount = columns.length,
5509
+ visibleColumns = _fnGetColumns( oSettings, 'bVisible' ),
5510
+ headerCells = $('th', oSettings.nTHead),
5511
+ tableWidthAttr = table.getAttribute('width'), // from DOM element
5512
+ tableContainer = table.parentNode,
5513
+ userInputs = false,
5514
+ i, column, columnIdx, width, outerWidth,
5515
+ browser = oSettings.oBrowser,
5516
+ ie67 = browser.bScrollOversize;
5517
+
5518
+ var styleWidth = table.style.width;
5519
+ if ( styleWidth && styleWidth.indexOf('%') !== -1 ) {
5520
+ tableWidthAttr = styleWidth;
5521
+ }
5522
+
5523
+ /* Convert any user input sizes into pixel sizes */
5524
+ for ( i=0 ; i<visibleColumns.length ; i++ ) {
5525
+ column = columns[ visibleColumns[i] ];
5526
+
5527
+ if ( column.sWidth !== null ) {
5528
+ column.sWidth = _fnConvertToWidth( column.sWidthOrig, tableContainer );
5529
+
5530
+ userInputs = true;
5531
+ }
5532
+ }
5533
+
5534
+ /* If the number of columns in the DOM equals the number that we have to
5535
+ * process in WFDataTables, then we can use the offsets that are created by
5536
+ * the web- browser. No custom sizes can be set in order for this to happen,
5537
+ * nor scrolling used
5538
+ */
5539
+ if ( ie67 || ! userInputs && ! scrollX && ! scrollY &&
5540
+ columnCount == _fnVisbleColumns( oSettings ) &&
5541
+ columnCount == headerCells.length
5542
+ ) {
5543
+ for ( i=0 ; i<columnCount ; i++ ) {
5544
+ var colIdx = _fnVisibleToColumnIndex( oSettings, i );
5545
+
5546
+ if ( colIdx !== null ) {
5547
+ columns[ colIdx ].sWidth = _fnStringToCss( headerCells.eq(i).width() );
5548
+ }
5549
+ }
5550
+ }
5551
+ else
5552
+ {
5553
+ // Otherwise construct a single row, worst case, table with the widest
5554
+ // node in the data, assign any user defined widths, then insert it into
5555
+ // the DOM and allow the browser to do all the hard work of calculating
5556
+ // table widths
5557
+ var tmpTable = $(table).clone() // don't use cloneNode - IE8 will remove events on the main table
5558
+ .css( 'visibility', 'hidden' )
5559
+ .removeAttr( 'id' );
5560
+
5561
+ // Clean up the table body
5562
+ tmpTable.find('tbody tr').remove();
5563
+ var tr = $('<tr/>').appendTo( tmpTable.find('tbody') );
5564
+
5565
+ // Clone the table header and footer - we can't use the header / footer
5566
+ // from the cloned table, since if scrolling is active, the table's
5567
+ // real header and footer are contained in different table tags
5568
+ tmpTable.find('thead, tfoot').remove();
5569
+ tmpTable
5570
+ .append( $(oSettings.nTHead).clone() )
5571
+ .append( $(oSettings.nTFoot).clone() );
5572
+
5573
+ // Remove any assigned widths from the footer (from scrolling)
5574
+ tmpTable.find('tfoot th, tfoot td').css('width', '');
5575
+
5576
+ // Apply custom sizing to the cloned header
5577
+ headerCells = _fnGetUniqueThs( oSettings, tmpTable.find('thead')[0] );
5578
+
5579
+ for ( i=0 ; i<visibleColumns.length ; i++ ) {
5580
+ column = columns[ visibleColumns[i] ];
5581
+
5582
+ headerCells[i].style.width = column.sWidthOrig !== null && column.sWidthOrig !== '' ?
5583
+ _fnStringToCss( column.sWidthOrig ) :
5584
+ '';
5585
+
5586
+ // For scrollX we need to force the column width otherwise the
5587
+ // browser will collapse it. If this width is smaller than the
5588
+ // width the column requires, then it will have no effect
5589
+ if ( column.sWidthOrig && scrollX ) {
5590
+ $( headerCells[i] ).append( $('<div/>').css( {
5591
+ width: column.sWidthOrig,
5592
+ margin: 0,
5593
+ padding: 0,
5594
+ border: 0,
5595
+ height: 1
5596
+ } ) );
5597
+ }
5598
+ }
5599
+
5600
+ // Find the widest cell for each column and put it into the table
5601
+ if ( oSettings.aoData.length ) {
5602
+ for ( i=0 ; i<visibleColumns.length ; i++ ) {
5603
+ columnIdx = visibleColumns[i];
5604
+ column = columns[ columnIdx ];
5605
+
5606
+ $( _fnGetWidestNode( oSettings, columnIdx ) )
5607
+ .clone( false )
5608
+ .append( column.sContentPadding )
5609
+ .appendTo( tr );
5610
+ }
5611
+ }
5612
+
5613
+ // Tidy the temporary table - remove name attributes so there aren't
5614
+ // duplicated in the dom (radio elements for example)
5615
+ $('[name]', tmpTable).removeAttr('name');
5616
+
5617
+ // Table has been built, attach to the document so we can work with it.
5618
+ // A holding element is used, positioned at the top of the container
5619
+ // with minimal height, so it has no effect on if the container scrolls
5620
+ // or not. Otherwise it might trigger scrolling when it actually isn't
5621
+ // needed
5622
+ var holder = $('<div/>').css( scrollX || scrollY ?
5623
+ {
5624
+ position: 'absolute',
5625
+ top: 0,
5626
+ left: 0,
5627
+ height: 1,
5628
+ right: 0,
5629
+ overflow: 'hidden'
5630
+ } :
5631
+ {}
5632
+ )
5633
+ .append( tmpTable )
5634
+ .appendTo( tableContainer );
5635
+
5636
+ // When scrolling (X or Y) we want to set the width of the table as
5637
+ // appropriate. However, when not scrolling leave the table width as it
5638
+ // is. This results in slightly different, but I think correct behaviour
5639
+ if ( scrollX && scrollXInner ) {
5640
+ tmpTable.width( scrollXInner );
5641
+ }
5642
+ else if ( scrollX ) {
5643
+ tmpTable.css( 'width', 'auto' );
5644
+ tmpTable.removeAttr('width');
5645
+
5646
+ // If there is no width attribute or style, then allow the table to
5647
+ // collapse
5648
+ if ( tmpTable.width() < tableContainer.clientWidth && tableWidthAttr ) {
5649
+ tmpTable.width( tableContainer.clientWidth );
5650
+ }
5651
+ }
5652
+ else if ( scrollY ) {
5653
+ tmpTable.width( tableContainer.clientWidth );
5654
+ }
5655
+ else if ( tableWidthAttr ) {
5656
+ tmpTable.width( tableWidthAttr );
5657
+ }
5658
+
5659
+ // Get the width of each column in the constructed table - we need to
5660
+ // know the inner width (so it can be assigned to the other table's
5661
+ // cells) and the outer width so we can calculate the full width of the
5662
+ // table. This is safe since WFDataTables requires a unique cell for each
5663
+ // column, but if ever a header can span multiple columns, this will
5664
+ // need to be modified.
5665
+ var total = 0;
5666
+ for ( i=0 ; i<visibleColumns.length ; i++ ) {
5667
+ var cell = $(headerCells[i]);
5668
+ var border = cell.outerWidth() - cell.width();
5669
+
5670
+ // Use getBounding... where possible (not IE8-) because it can give
5671
+ // sub-pixel accuracy, which we then want to round up!
5672
+ var bounding = browser.bBounding ?
5673
+ Math.ceil( headerCells[i].getBoundingClientRect().width ) :
5674
+ cell.outerWidth();
5675
+
5676
+ // Total is tracked to remove any sub-pixel errors as the outerWidth
5677
+ // of the table might not equal the total given here (IE!).
5678
+ total += bounding;
5679
+
5680
+ // Width for each column to use
5681
+ columns[ visibleColumns[i] ].sWidth = _fnStringToCss( bounding - border );
5682
+ }
5683
+
5684
+ table.style.width = _fnStringToCss( total );
5685
+
5686
+ // Finished with the table - ditch it
5687
+ holder.remove();
5688
+ }
5689
+
5690
+ // If there is a width attr, we want to attach an event listener which
5691
+ // allows the table sizing to automatically adjust when the window is
5692
+ // resized. Use the width attr rather than CSS, since we can't know if the
5693
+ // CSS is a relative value or absolute - DOM read is always px.
5694
+ if ( tableWidthAttr ) {
5695
+ table.style.width = _fnStringToCss( tableWidthAttr );
5696
+ }
5697
+
5698
+ if ( (tableWidthAttr || scrollX) && ! oSettings._reszEvt ) {
5699
+ var bindResize = function () {
5700
+ $(window).on('resize.DT-'+oSettings.sInstance, _fnThrottle( function () {
5701
+ _fnAdjustColumnSizing( oSettings );
5702
+ } ) );
5703
+ };
5704
+
5705
+ // IE6/7 will crash if we bind a resize event handler on page load.
5706
+ // To be removed in 1.11 which drops IE6/7 support
5707
+ if ( ie67 ) {
5708
+ setTimeout( bindResize, 1000 );
5709
+ }
5710
+ else {
5711
+ bindResize();
5712
+ }
5713
+
5714
+ oSettings._reszEvt = true;
5715
+ }
5716
+ }
5717
+
5718
+
5719
+ /**
5720
+ * Throttle the calls to a function. Arguments and context are maintained for
5721
+ * the throttled function
5722
+ * @param {function} fn Function to be called
5723
+ * @param {int} [freq=200] call frequency in mS
5724
+ * @returns {function} wrapped function
5725
+ * @memberof WFDataTable#oApi
5726
+ */
5727
+ var _fnThrottle = WFDataTable.util.throttle;
5728
+
5729
+
5730
+ /**
5731
+ * Convert a CSS unit width to pixels (e.g. 2em)
5732
+ * @param {string} width width to be converted
5733
+ * @param {node} parent parent to get the with for (required for relative widths) - optional
5734
+ * @returns {int} width in pixels
5735
+ * @memberof WFDataTable#oApi
5736
+ */
5737
+ function _fnConvertToWidth ( width, parent )
5738
+ {
5739
+ if ( ! width ) {
5740
+ return 0;
5741
+ }
5742
+
5743
+ var n = $('<div/>')
5744
+ .css( 'width', _fnStringToCss( width ) )
5745
+ .appendTo( parent || document.body );
5746
+
5747
+ var val = n[0].offsetWidth;
5748
+ n.remove();
5749
+
5750
+ return val;
5751
+ }
5752
+
5753
+
5754
+ /**
5755
+ * Get the widest node
5756
+ * @param {object} settings dataTables settings object
5757
+ * @param {int} colIdx column of interest
5758
+ * @returns {node} widest table node
5759
+ * @memberof WFDataTable#oApi
5760
+ */
5761
+ function _fnGetWidestNode( settings, colIdx )
5762
+ {
5763
+ var idx = _fnGetMaxLenString( settings, colIdx );
5764
+ if ( idx < 0 ) {
5765
+ return null;
5766
+ }
5767
+
5768
+ var data = settings.aoData[ idx ];
5769
+ return ! data.nTr ? // Might not have been created when deferred rendering
5770
+ $('<td/>').html( _fnGetCellData( settings, idx, colIdx, 'display' ) )[0] :
5771
+ data.anCells[ colIdx ];
5772
+ }
5773
+
5774
+
5775
+ /**
5776
+ * Get the maximum strlen for each data column
5777
+ * @param {object} settings dataTables settings object
5778
+ * @param {int} colIdx column of interest
5779
+ * @returns {string} max string length for each column
5780
+ * @memberof WFDataTable#oApi
5781
+ */
5782
+ function _fnGetMaxLenString( settings, colIdx )
5783
+ {
5784
+ var s, max=-1, maxIdx = -1;
5785
+
5786
+ for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
5787
+ s = _fnGetCellData( settings, i, colIdx, 'display' )+'';
5788
+ s = s.replace( __re_html_remove, '' );
5789
+ s = s.replace( /&nbsp;/g, ' ' );
5790
+
5791
+ if ( s.length > max ) {
5792
+ max = s.length;
5793
+ maxIdx = i;
5794
+ }
5795
+ }
5796
+
5797
+ return maxIdx;
5798
+ }
5799
+
5800
+
5801
+ /**
5802
+ * Append a CSS unit (only if required) to a string
5803
+ * @param {string} value to css-ify
5804
+ * @returns {string} value with css unit
5805
+ * @memberof WFDataTable#oApi
5806
+ */
5807
+ function _fnStringToCss( s )
5808
+ {
5809
+ if ( s === null ) {
5810
+ return '0px';
5811
+ }
5812
+
5813
+ if ( typeof s == 'number' ) {
5814
+ return s < 0 ?
5815
+ '0px' :
5816
+ s+'px';
5817
+ }
5818
+
5819
+ // Check it has a unit character already
5820
+ return s.match(/\d$/) ?
5821
+ s+'px' :
5822
+ s;
5823
+ }
5824
+
5825
+
5826
+
5827
+ function _fnSortFlatten ( settings )
5828
+ {
5829
+ var
5830
+ i, iLen, k, kLen,
5831
+ aSort = [],
5832
+ aiOrig = [],
5833
+ aoColumns = settings.aoColumns,
5834
+ aDataSort, iCol, sType, srcCol,
5835
+ fixed = settings.aaSortingFixed,
5836
+ fixedObj = $.isPlainObject( fixed ),
5837
+ nestedSort = [],
5838
+ add = function ( a ) {
5839
+ if ( a.length && ! $.isArray( a[0] ) ) {
5840
+ // 1D array
5841
+ nestedSort.push( a );
5842
+ }
5843
+ else {
5844
+ // 2D array
5845
+ $.merge( nestedSort, a );
5846
+ }
5847
+ };
5848
+
5849
+ // Build the sort array, with pre-fix and post-fix options if they have been
5850
+ // specified
5851
+ if ( $.isArray( fixed ) ) {
5852
+ add( fixed );
5853
+ }
5854
+
5855
+ if ( fixedObj && fixed.pre ) {
5856
+ add( fixed.pre );
5857
+ }
5858
+
5859
+ add( settings.aaSorting );
5860
+
5861
+ if (fixedObj && fixed.post ) {
5862
+ add( fixed.post );
5863
+ }
5864
+
5865
+ for ( i=0 ; i<nestedSort.length ; i++ )
5866
+ {
5867
+ srcCol = nestedSort[i][0];
5868
+ aDataSort = aoColumns[ srcCol ].aDataSort;
5869
+
5870
+ for ( k=0, kLen=aDataSort.length ; k<kLen ; k++ )
5871
+ {
5872
+ iCol = aDataSort[k];
5873
+ sType = aoColumns[ iCol ].sType || 'string';
5874
+
5875
+ if ( nestedSort[i]._idx === undefined ) {
5876
+ nestedSort[i]._idx = $.inArray( nestedSort[i][1], aoColumns[iCol].asSorting );
5877
+ }
5878
+
5879
+ aSort.push( {
5880
+ src: srcCol,
5881
+ col: iCol,
5882
+ dir: nestedSort[i][1],
5883
+ index: nestedSort[i]._idx,
5884
+ type: sType,
5885
+ formatter: WFDataTable.ext.type.order[ sType+"-pre" ]
5886
+ } );
5887
+ }
5888
+ }
5889
+
5890
+ return aSort;
5891
+ }
5892
+
5893
+ /**
5894
+ * Change the order of the table
5895
+ * @param {object} oSettings dataTables settings object
5896
+ * @memberof WFDataTable#oApi
5897
+ * @todo This really needs split up!
5898
+ */
5899
+ function _fnSort ( oSettings )
5900
+ {
5901
+ var
5902
+ i, ien, iLen, j, jLen, k, kLen,
5903
+ sDataType, nTh,
5904
+ aiOrig = [],
5905
+ oExtSort = WFDataTable.ext.type.order,
5906
+ aoData = oSettings.aoData,
5907
+ aoColumns = oSettings.aoColumns,
5908
+ aDataSort, data, iCol, sType, oSort,
5909
+ formatters = 0,
5910
+ sortCol,
5911
+ displayMaster = oSettings.aiDisplayMaster,
5912
+ aSort;
5913
+
5914
+ // Resolve any column types that are unknown due to addition or invalidation
5915
+ // @todo Can this be moved into a 'data-ready' handler which is called when
5916
+ // data is going to be used in the table?
5917
+ _fnColumnTypes( oSettings );
5918
+
5919
+ aSort = _fnSortFlatten( oSettings );
5920
+
5921
+ for ( i=0, ien=aSort.length ; i<ien ; i++ ) {
5922
+ sortCol = aSort[i];
5923
+
5924
+ // Track if we can use the fast sort algorithm
5925
+ if ( sortCol.formatter ) {
5926
+ formatters++;
5927
+ }
5928
+
5929
+ // Load the data needed for the sort, for each cell
5930
+ _fnSortData( oSettings, sortCol.col );
5931
+ }
5932
+
5933
+ /* No sorting required if server-side or no sorting array */
5934
+ if ( _fnDataSource( oSettings ) != 'ssp' && aSort.length !== 0 )
5935
+ {
5936
+ // Create a value - key array of the current row positions such that we can use their
5937
+ // current position during the sort, if values match, in order to perform stable sorting
5938
+ for ( i=0, iLen=displayMaster.length ; i<iLen ; i++ ) {
5939
+ aiOrig[ displayMaster[i] ] = i;
5940
+ }
5941
+
5942
+ /* Do the sort - here we want multi-column sorting based on a given data source (column)
5943
+ * and sorting function (from oSort) in a certain direction. It's reasonably complex to
5944
+ * follow on it's own, but this is what we want (example two column sorting):
5945
+ * fnLocalSorting = function(a,b){
5946
+ * var iTest;
5947
+ * iTest = oSort['string-asc']('data11', 'data12');
5948
+ * if (iTest !== 0)
5949
+ * return iTest;
5950
+ * iTest = oSort['numeric-desc']('data21', 'data22');
5951
+ * if (iTest !== 0)
5952
+ * return iTest;
5953
+ * return oSort['numeric-asc']( aiOrig[a], aiOrig[b] );
5954
+ * }
5955
+ * Basically we have a test for each sorting column, if the data in that column is equal,
5956
+ * test the next column. If all columns match, then we use a numeric sort on the row
5957
+ * positions in the original data array to provide a stable sort.
5958
+ *
5959
+ * Note - I know it seems excessive to have two sorting methods, but the first is around
5960
+ * 15% faster, so the second is only maintained for backwards compatibility with sorting
5961
+ * methods which do not have a pre-sort formatting function.
5962
+ */
5963
+ if ( formatters === aSort.length ) {
5964
+ // All sort types have formatting functions
5965
+ displayMaster.sort( function ( a, b ) {
5966
+ var
5967
+ x, y, k, test, sort,
5968
+ len=aSort.length,
5969
+ dataA = aoData[a]._aSortData,
5970
+ dataB = aoData[b]._aSortData;
5971
+
5972
+ for ( k=0 ; k<len ; k++ ) {
5973
+ sort = aSort[k];
5974
+
5975
+ x = dataA[ sort.col ];
5976
+ y = dataB[ sort.col ];
5977
+
5978
+ test = x<y ? -1 : x>y ? 1 : 0;
5979
+ if ( test !== 0 ) {
5980
+ return sort.dir === 'asc' ? test : -test;
5981
+ }
5982
+ }
5983
+
5984
+ x = aiOrig[a];
5985
+ y = aiOrig[b];
5986
+ return x<y ? -1 : x>y ? 1 : 0;
5987
+ } );
5988
+ }
5989
+ else {
5990
+ // Depreciated - remove in 1.11 (providing a plug-in option)
5991
+ // Not all sort types have formatting methods, so we have to call their sorting
5992
+ // methods.
5993
+ displayMaster.sort( function ( a, b ) {
5994
+ var
5995
+ x, y, k, l, test, sort, fn,
5996
+ len=aSort.length,
5997
+ dataA = aoData[a]._aSortData,
5998
+ dataB = aoData[b]._aSortData;
5999
+
6000
+ for ( k=0 ; k<len ; k++ ) {
6001
+ sort = aSort[k];
6002
+
6003
+ x = dataA[ sort.col ];
6004
+ y = dataB[ sort.col ];
6005
+
6006
+ fn = oExtSort[ sort.type+"-"+sort.dir ] || oExtSort[ "string-"+sort.dir ];
6007
+ test = fn( x, y );
6008
+ if ( test !== 0 ) {
6009
+ return test;
6010
+ }
6011
+ }
6012
+
6013
+ x = aiOrig[a];
6014
+ y = aiOrig[b];
6015
+ return x<y ? -1 : x>y ? 1 : 0;
6016
+ } );
6017
+ }
6018
+ }
6019
+
6020
+ /* Tell the draw function that we have sorted the data */
6021
+ oSettings.bSorted = true;
6022
+ }
6023
+
6024
+
6025
+ function _fnSortAria ( settings )
6026
+ {
6027
+ var label;
6028
+ var nextSort;
6029
+ var columns = settings.aoColumns;
6030
+ var aSort = _fnSortFlatten( settings );
6031
+ var oAria = settings.oLanguage.oAria;
6032
+
6033
+ // ARIA attributes - need to loop all columns, to update all (removing old
6034
+ // attributes as needed)
6035
+ for ( var i=0, iLen=columns.length ; i<iLen ; i++ )
6036
+ {
6037
+ var col = columns[i];
6038
+ var asSorting = col.asSorting;
6039
+ var sTitle = col.sTitle.replace( /<.*?>/g, "" );
6040
+ var th = col.nTh;
6041
+
6042
+ // IE7 is throwing an error when setting these properties with jQuery's
6043
+ // attr() and removeAttr() methods...
6044
+ th.removeAttribute('aria-sort');
6045
+
6046
+ /* In ARIA only the first sorting column can be marked as sorting - no multi-sort option */
6047
+ if ( col.bSortable ) {
6048
+ if ( aSort.length > 0 && aSort[0].col == i ) {
6049
+ th.setAttribute('aria-sort', aSort[0].dir=="asc" ? "ascending" : "descending" );
6050
+ nextSort = asSorting[ aSort[0].index+1 ] || asSorting[0];
6051
+ }
6052
+ else {
6053
+ nextSort = asSorting[0];
6054
+ }
6055
+
6056
+ label = sTitle + ( nextSort === "asc" ?
6057
+ oAria.sSortAscending :
6058
+ oAria.sSortDescending
6059
+ );
6060
+ }
6061
+ else {
6062
+ label = sTitle;
6063
+ }
6064
+
6065
+ th.setAttribute('aria-label', label);
6066
+ }
6067
+ }
6068
+
6069
+
6070
+ /**
6071
+ * Function to run on user sort request
6072
+ * @param {object} settings dataTables settings object
6073
+ * @param {node} attachTo node to attach the handler to
6074
+ * @param {int} colIdx column sorting index
6075
+ * @param {boolean} [append=false] Append the requested sort to the existing
6076
+ * sort if true (i.e. multi-column sort)
6077
+ * @param {function} [callback] callback function
6078
+ * @memberof WFDataTable#oApi
6079
+ */
6080
+ function _fnSortListener ( settings, colIdx, append, callback )
6081
+ {
6082
+ var col = settings.aoColumns[ colIdx ];
6083
+ var sorting = settings.aaSorting;
6084
+ var asSorting = col.asSorting;
6085
+ var nextSortIdx;
6086
+ var next = function ( a, overflow ) {
6087
+ var idx = a._idx;
6088
+ if ( idx === undefined ) {
6089
+ idx = $.inArray( a[1], asSorting );
6090
+ }
6091
+
6092
+ return idx+1 < asSorting.length ?
6093
+ idx+1 :
6094
+ overflow ?
6095
+ null :
6096
+ 0;
6097
+ };
6098
+
6099
+ // Convert to 2D array if needed
6100
+ if ( typeof sorting[0] === 'number' ) {
6101
+ sorting = settings.aaSorting = [ sorting ];
6102
+ }
6103
+
6104
+ // If appending the sort then we are multi-column sorting
6105
+ if ( append && settings.oFeatures.bSortMulti ) {
6106
+ // Are we already doing some kind of sort on this column?
6107
+ var sortIdx = $.inArray( colIdx, _pluck(sorting, '0') );
6108
+
6109
+ if ( sortIdx !== -1 ) {
6110
+ // Yes, modify the sort
6111
+ nextSortIdx = next( sorting[sortIdx], true );
6112
+
6113
+ if ( nextSortIdx === null && sorting.length === 1 ) {
6114
+ nextSortIdx = 0; // can't remove sorting completely
6115
+ }
6116
+
6117
+ if ( nextSortIdx === null ) {
6118
+ sorting.splice( sortIdx, 1 );
6119
+ }
6120
+ else {
6121
+ sorting[sortIdx][1] = asSorting[ nextSortIdx ];
6122
+ sorting[sortIdx]._idx = nextSortIdx;
6123
+ }
6124
+ }
6125
+ else {
6126
+ // No sort on this column yet
6127
+ sorting.push( [ colIdx, asSorting[0], 0 ] );
6128
+ sorting[sorting.length-1]._idx = 0;
6129
+ }
6130
+ }
6131
+ else if ( sorting.length && sorting[0][0] == colIdx ) {
6132
+ // Single column - already sorting on this column, modify the sort
6133
+ nextSortIdx = next( sorting[0] );
6134
+
6135
+ sorting.length = 1;
6136
+ sorting[0][1] = asSorting[ nextSortIdx ];
6137
+ sorting[0]._idx = nextSortIdx;
6138
+ }
6139
+ else {
6140
+ // Single column - sort only on this column
6141
+ sorting.length = 0;
6142
+ sorting.push( [ colIdx, asSorting[0] ] );
6143
+ sorting[0]._idx = 0;
6144
+ }
6145
+
6146
+ // Run the sort by calling a full redraw
6147
+ _fnReDraw( settings );
6148
+
6149
+ // callback used for async user interaction
6150
+ if ( typeof callback == 'function' ) {
6151
+ callback( settings );
6152
+ }
6153
+ }
6154
+
6155
+
6156
+ /**
6157
+ * Attach a sort handler (click) to a node
6158
+ * @param {object} settings dataTables settings object
6159
+ * @param {node} attachTo node to attach the handler to
6160
+ * @param {int} colIdx column sorting index
6161
+ * @param {function} [callback] callback function
6162
+ * @memberof WFDataTable#oApi
6163
+ */
6164
+ function _fnSortAttachListener ( settings, attachTo, colIdx, callback )
6165
+ {
6166
+ var col = settings.aoColumns[ colIdx ];
6167
+
6168
+ _fnBindAction( attachTo, {}, function (e) {
6169
+ /* If the column is not sortable - don't to anything */
6170
+ if ( col.bSortable === false ) {
6171
+ return;
6172
+ }
6173
+
6174
+ // If processing is enabled use a timeout to allow the processing
6175
+ // display to be shown - otherwise to it synchronously
6176
+ if ( settings.oFeatures.bProcessing ) {
6177
+ _fnProcessingDisplay( settings, true );
6178
+
6179
+ setTimeout( function() {
6180
+ _fnSortListener( settings, colIdx, e.shiftKey, callback );
6181
+
6182
+ // In server-side processing, the draw callback will remove the
6183
+ // processing display
6184
+ if ( _fnDataSource( settings ) !== 'ssp' ) {
6185
+ _fnProcessingDisplay( settings, false );
6186
+ }
6187
+ }, 0 );
6188
+ }
6189
+ else {
6190
+ _fnSortListener( settings, colIdx, e.shiftKey, callback );
6191
+ }
6192
+ } );
6193
+ }
6194
+
6195
+
6196
+ /**
6197
+ * Set the sorting classes on table's body, Note: it is safe to call this function
6198
+ * when bSort and bSortClasses are false
6199
+ * @param {object} oSettings dataTables settings object
6200
+ * @memberof WFDataTable#oApi
6201
+ */
6202
+ function _fnSortingClasses( settings )
6203
+ {
6204
+ var oldSort = settings.aLastSort;
6205
+ var sortClass = settings.oClasses.sSortColumn;
6206
+ var sort = _fnSortFlatten( settings );
6207
+ var features = settings.oFeatures;
6208
+ var i, ien, colIdx;
6209
+
6210
+ if ( features.bSort && features.bSortClasses ) {
6211
+ // Remove old sorting classes
6212
+ for ( i=0, ien=oldSort.length ; i<ien ; i++ ) {
6213
+ colIdx = oldSort[i].src;
6214
+
6215
+ // Remove column sorting
6216
+ $( _pluck( settings.aoData, 'anCells', colIdx ) )
6217
+ .removeClass( sortClass + (i<2 ? i+1 : 3) );
6218
+ }
6219
+
6220
+ // Add new column sorting
6221
+ for ( i=0, ien=sort.length ; i<ien ; i++ ) {
6222
+ colIdx = sort[i].src;
6223
+
6224
+ $( _pluck( settings.aoData, 'anCells', colIdx ) )
6225
+ .addClass( sortClass + (i<2 ? i+1 : 3) );
6226
+ }
6227
+ }
6228
+
6229
+ settings.aLastSort = sort;
6230
+ }
6231
+
6232
+
6233
+ // Get the data to sort a column, be it from cache, fresh (populating the
6234
+ // cache), or from a sort formatter
6235
+ function _fnSortData( settings, idx )
6236
+ {
6237
+ // Custom sorting function - provided by the sort data type
6238
+ var column = settings.aoColumns[ idx ];
6239
+ var customSort = WFDataTable.ext.order[ column.sSortDataType ];
6240
+ var customData;
6241
+
6242
+ if ( customSort ) {
6243
+ customData = customSort.call( settings.oInstance, settings, idx,
6244
+ _fnColumnIndexToVisible( settings, idx )
6245
+ );
6246
+ }
6247
+
6248
+ // Use / populate cache
6249
+ var row, cellData;
6250
+ var formatter = WFDataTable.ext.type.order[ column.sType+"-pre" ];
6251
+
6252
+ for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
6253
+ row = settings.aoData[i];
6254
+
6255
+ if ( ! row._aSortData ) {
6256
+ row._aSortData = [];
6257
+ }
6258
+
6259
+ if ( ! row._aSortData[idx] || customSort ) {
6260
+ cellData = customSort ?
6261
+ customData[i] : // If there was a custom sort function, use data from there
6262
+ _fnGetCellData( settings, i, idx, 'sort' );
6263
+
6264
+ row._aSortData[ idx ] = formatter ?
6265
+ formatter( cellData ) :
6266
+ cellData;
6267
+ }
6268
+ }
6269
+ }
6270
+
6271
+
6272
+
6273
+ /**
6274
+ * Save the state of a table
6275
+ * @param {object} oSettings dataTables settings object
6276
+ * @memberof WFDataTable#oApi
6277
+ */
6278
+ function _fnSaveState ( settings )
6279
+ {
6280
+ if ( !settings.oFeatures.bStateSave || settings.bDestroying )
6281
+ {
6282
+ return;
6283
+ }
6284
+
6285
+ /* Store the interesting variables */
6286
+ var state = {
6287
+ time: +new Date(),
6288
+ start: settings._iDisplayStart,
6289
+ length: settings._iDisplayLength,
6290
+ order: $.extend( true, [], settings.aaSorting ),
6291
+ search: _fnSearchToCamel( settings.oPreviousSearch ),
6292
+ columns: $.map( settings.aoColumns, function ( col, i ) {
6293
+ return {
6294
+ visible: col.bVisible,
6295
+ search: _fnSearchToCamel( settings.aoPreSearchCols[i] )
6296
+ };
6297
+ } )
6298
+ };
6299
+
6300
+ _fnCallbackFire( settings, "aoStateSaveParams", 'stateSaveParams', [settings, state] );
6301
+
6302
+ settings.oSavedState = state;
6303
+ settings.fnStateSaveCallback.call( settings.oInstance, settings, state );
6304
+ }
6305
+
6306
+
6307
+ /**
6308
+ * Attempt to load a saved table state
6309
+ * @param {object} oSettings dataTables settings object
6310
+ * @param {object} oInit WFDataTables init object so we can override settings
6311
+ * @param {function} callback Callback to execute when the state has been loaded
6312
+ * @memberof WFDataTable#oApi
6313
+ */
6314
+ function _fnLoadState ( settings, oInit, callback )
6315
+ {
6316
+ var i, ien;
6317
+ var columns = settings.aoColumns;
6318
+ var loaded = function ( s ) {
6319
+ if ( ! s || ! s.time ) {
6320
+ callback();
6321
+ return;
6322
+ }
6323
+
6324
+ // Allow custom and plug-in manipulation functions to alter the saved data set and
6325
+ // cancelling of loading by returning false
6326
+ var abStateLoad = _fnCallbackFire( settings, 'aoStateLoadParams', 'stateLoadParams', [settings, state] );
6327
+ if ( $.inArray( false, abStateLoad ) !== -1 ) {
6328
+ callback();
6329
+ return;
6330
+ }
6331
+
6332
+ // Reject old data
6333
+ var duration = settings.iStateDuration;
6334
+ if ( duration > 0 && s.time < +new Date() - (duration*1000) ) {
6335
+ callback();
6336
+ return;
6337
+ }
6338
+
6339
+ // Number of columns have changed - all bets are off, no restore of settings
6340
+ if ( s.columns && columns.length !== s.columns.length ) {
6341
+ callback();
6342
+ return;
6343
+ }
6344
+
6345
+ // Store the saved state so it might be accessed at any time
6346
+ settings.oLoadedState = $.extend( true, {}, state );
6347
+
6348
+ // Restore key features - todo - for 1.11 this needs to be done by
6349
+ // subscribed events
6350
+ if ( s.start !== undefined ) {
6351
+ settings._iDisplayStart = s.start;
6352
+ settings.iInitDisplayStart = s.start;
6353
+ }
6354
+ if ( s.length !== undefined ) {
6355
+ settings._iDisplayLength = s.length;
6356
+ }
6357
+
6358
+ // Order
6359
+ if ( s.order !== undefined ) {
6360
+ settings.aaSorting = [];
6361
+ $.each( s.order, function ( i, col ) {
6362
+ settings.aaSorting.push( col[0] >= columns.length ?
6363
+ [ 0, col[1] ] :
6364
+ col
6365
+ );
6366
+ } );
6367
+ }
6368
+
6369
+ // Search
6370
+ if ( s.search !== undefined ) {
6371
+ $.extend( settings.oPreviousSearch, _fnSearchToHung( s.search ) );
6372
+ }
6373
+
6374
+ // Columns
6375
+ //
6376
+ if ( s.columns ) {
6377
+ for ( i=0, ien=s.columns.length ; i<ien ; i++ ) {
6378
+ var col = s.columns[i];
6379
+
6380
+ // Visibility
6381
+ if ( col.visible !== undefined ) {
6382
+ columns[i].bVisible = col.visible;
6383
+ }
6384
+
6385
+ // Search
6386
+ if ( col.search !== undefined ) {
6387
+ $.extend( settings.aoPreSearchCols[i], _fnSearchToHung( col.search ) );
6388
+ }
6389
+ }
6390
+ }
6391
+
6392
+ _fnCallbackFire( settings, 'aoStateLoaded', 'stateLoaded', [settings, state] );
6393
+ callback();
6394
+ }
6395
+
6396
+ if ( ! settings.oFeatures.bStateSave ) {
6397
+ callback();
6398
+ return;
6399
+ }
6400
+
6401
+ var state = settings.fnStateLoadCallback.call( settings.oInstance, settings, loaded );
6402
+
6403
+ if ( state !== undefined ) {
6404
+ loaded( state );
6405
+ }
6406
+ // otherwise, wait for the loaded callback to be executed
6407
+ }
6408
+
6409
+
6410
+ /**
6411
+ * Return the settings object for a particular table
6412
+ * @param {node} table table we are using as a dataTable
6413
+ * @returns {object} Settings object - or null if not found
6414
+ * @memberof WFDataTable#oApi
6415
+ */
6416
+ function _fnSettingsFromNode ( table )
6417
+ {
6418
+ var settings = WFDataTable.settings;
6419
+ var idx = $.inArray( table, _pluck( settings, 'nTable' ) );
6420
+
6421
+ return idx !== -1 ?
6422
+ settings[ idx ] :
6423
+ null;
6424
+ }
6425
+
6426
+
6427
+ /**
6428
+ * Log an error message
6429
+ * @param {object} settings dataTables settings object
6430
+ * @param {int} level log error messages, or display them to the user
6431
+ * @param {string} msg error message
6432
+ * @param {int} tn Technical note id to get more information about the error.
6433
+ * @memberof WFDataTable#oApi
6434
+ */
6435
+ function _fnLog( settings, level, msg, tn )
6436
+ {
6437
+ msg = 'WFDataTables warning: '+
6438
+ (settings ? 'table id='+settings.sTableId+' - ' : '')+msg;
6439
+
6440
+ if ( tn ) {
6441
+ msg += '. For more information about this error, please see '+
6442
+ 'http://datatables.net/tn/'+tn;
6443
+ }
6444
+
6445
+ if ( ! level ) {
6446
+ // Backwards compatibility pre 1.10
6447
+ var ext = WFDataTable.ext;
6448
+ var type = ext.sErrMode || ext.errMode;
6449
+
6450
+ if ( settings ) {
6451
+ _fnCallbackFire( settings, null, 'error', [ settings, tn, msg ] );
6452
+ }
6453
+
6454
+ if ( type == 'alert' ) {
6455
+ alert( msg );
6456
+ }
6457
+ else if ( type == 'throw' ) {
6458
+ throw new Error(msg);
6459
+ }
6460
+ else if ( typeof type == 'function' ) {
6461
+ type( settings, tn, msg );
6462
+ }
6463
+ }
6464
+ else if ( window.console && console.log ) {
6465
+ console.log( msg );
6466
+ }
6467
+ }
6468
+
6469
+
6470
+ /**
6471
+ * See if a property is defined on one object, if so assign it to the other object
6472
+ * @param {object} ret target object
6473
+ * @param {object} src source object
6474
+ * @param {string} name property
6475
+ * @param {string} [mappedName] name to map too - optional, name used if not given
6476
+ * @memberof WFDataTable#oApi
6477
+ */
6478
+ function _fnMap( ret, src, name, mappedName )
6479
+ {
6480
+ if ( $.isArray( name ) ) {
6481
+ $.each( name, function (i, val) {
6482
+ if ( $.isArray( val ) ) {
6483
+ _fnMap( ret, src, val[0], val[1] );
6484
+ }
6485
+ else {
6486
+ _fnMap( ret, src, val );
6487
+ }
6488
+ } );
6489
+
6490
+ return;
6491
+ }
6492
+
6493
+ if ( mappedName === undefined ) {
6494
+ mappedName = name;
6495
+ }
6496
+
6497
+ if ( src[name] !== undefined ) {
6498
+ ret[mappedName] = src[name];
6499
+ }
6500
+ }
6501
+
6502
+
6503
+ /**
6504
+ * Extend objects - very similar to jQuery.extend, but deep copy objects, and
6505
+ * shallow copy arrays. The reason we need to do this, is that we don't want to
6506
+ * deep copy array init values (such as aaSorting) since the dev wouldn't be
6507
+ * able to override them, but we do want to deep copy arrays.
6508
+ * @param {object} out Object to extend
6509
+ * @param {object} extender Object from which the properties will be applied to
6510
+ * out
6511
+ * @param {boolean} breakRefs If true, then arrays will be sliced to take an
6512
+ * independent copy with the exception of the `data` or `aaData` parameters
6513
+ * if they are present. This is so you can pass in a collection to
6514
+ * WFDataTables and have that used as your data source without breaking the
6515
+ * references
6516
+ * @returns {object} out Reference, just for convenience - out === the return.
6517
+ * @memberof WFDataTable#oApi
6518
+ * @todo This doesn't take account of arrays inside the deep copied objects.
6519
+ */
6520
+ function _fnExtend( out, extender, breakRefs )
6521
+ {
6522
+ var val;
6523
+
6524
+ for ( var prop in extender ) {
6525
+ if ( extender.hasOwnProperty(prop) ) {
6526
+ val = extender[prop];
6527
+
6528
+ if ( $.isPlainObject( val ) ) {
6529
+ if ( ! $.isPlainObject( out[prop] ) ) {
6530
+ out[prop] = {};
6531
+ }
6532
+ $.extend( true, out[prop], val );
6533
+ }
6534
+ else if ( breakRefs && prop !== 'data' && prop !== 'aaData' && $.isArray(val) ) {
6535
+ out[prop] = val.slice();
6536
+ }
6537
+ else {
6538
+ out[prop] = val;
6539
+ }
6540
+ }
6541
+ }
6542
+
6543
+ return out;
6544
+ }
6545
+
6546
+
6547
+ /**
6548
+ * Bind an event handers to allow a click or return key to activate the callback.
6549
+ * This is good for accessibility since a return on the keyboard will have the
6550
+ * same effect as a click, if the element has focus.
6551
+ * @param {element} n Element to bind the action to
6552
+ * @param {object} oData Data object to pass to the triggered function
6553
+ * @param {function} fn Callback function for when the event is triggered
6554
+ * @memberof WFDataTable#oApi
6555
+ */
6556
+ function _fnBindAction( n, oData, fn )
6557
+ {
6558
+ $(n)
6559
+ .on( 'click.DT', oData, function (e) {
6560
+ n.blur(); // Remove focus outline for mouse users
6561
+ fn(e);
6562
+ } )
6563
+ .on( 'keypress.DT', oData, function (e){
6564
+ if ( e.which === 13 ) {
6565
+ e.preventDefault();
6566
+ fn(e);
6567
+ }
6568
+ } )
6569
+ .on( 'selectstart.DT', function () {
6570
+ /* Take the brutal approach to cancelling text selection */
6571
+ return false;
6572
+ } );
6573
+ }
6574
+
6575
+
6576
+ /**
6577
+ * Register a callback function. Easily allows a callback function to be added to
6578
+ * an array store of callback functions that can then all be called together.
6579
+ * @param {object} oSettings dataTables settings object
6580
+ * @param {string} sStore Name of the array storage for the callbacks in oSettings
6581
+ * @param {function} fn Function to be called back
6582
+ * @param {string} sName Identifying name for the callback (i.e. a label)
6583
+ * @memberof WFDataTable#oApi
6584
+ */
6585
+ function _fnCallbackReg( oSettings, sStore, fn, sName )
6586
+ {
6587
+ if ( fn )
6588
+ {
6589
+ oSettings[sStore].push( {
6590
+ "fn": fn,
6591
+ "sName": sName
6592
+ } );
6593
+ }
6594
+ }
6595
+
6596
+
6597
+ /**
6598
+ * Fire callback functions and trigger events. Note that the loop over the
6599
+ * callback array store is done backwards! Further note that you do not want to
6600
+ * fire off triggers in time sensitive applications (for example cell creation)
6601
+ * as its slow.
6602
+ * @param {object} settings dataTables settings object
6603
+ * @param {string} callbackArr Name of the array storage for the callbacks in
6604
+ * oSettings
6605
+ * @param {string} eventName Name of the jQuery custom event to trigger. If
6606
+ * null no trigger is fired
6607
+ * @param {array} args Array of arguments to pass to the callback function /
6608
+ * trigger
6609
+ * @memberof WFDataTable#oApi
6610
+ */
6611
+ function _fnCallbackFire( settings, callbackArr, eventName, args )
6612
+ {
6613
+ var ret = [];
6614
+
6615
+ if ( callbackArr ) {
6616
+ ret = $.map( settings[callbackArr].slice().reverse(), function (val, i) {
6617
+ return val.fn.apply( settings.oInstance, args );
6618
+ } );
6619
+ }
6620
+
6621
+ if ( eventName !== null ) {
6622
+ var e = $.Event( eventName+'.dt' );
6623
+
6624
+ $(settings.nTable).trigger( e, args );
6625
+
6626
+ ret.push( e.result );
6627
+ }
6628
+
6629
+ return ret;
6630
+ }
6631
+
6632
+
6633
+ function _fnLengthOverflow ( settings )
6634
+ {
6635
+ var
6636
+ start = settings._iDisplayStart,
6637
+ end = settings.fnDisplayEnd(),
6638
+ len = settings._iDisplayLength;
6639
+
6640
+ /* If we have space to show extra rows (backing up from the end point - then do so */
6641
+ if ( start >= end )
6642
+ {
6643
+ start = end - len;
6644
+ }
6645
+
6646
+ // Keep the start record on the current page
6647
+ start -= (start % len);
6648
+
6649
+ if ( len === -1 || start < 0 )
6650
+ {
6651
+ start = 0;
6652
+ }
6653
+
6654
+ settings._iDisplayStart = start;
6655
+ }
6656
+
6657
+
6658
+ function _fnRenderer( settings, type )
6659
+ {
6660
+ var renderer = settings.renderer;
6661
+ var host = WFDataTable.ext.renderer[type];
6662
+
6663
+ if ( $.isPlainObject( renderer ) && renderer[type] ) {
6664
+ // Specific renderer for this type. If available use it, otherwise use
6665
+ // the default.
6666
+ return host[renderer[type]] || host._;
6667
+ }
6668
+ else if ( typeof renderer === 'string' ) {
6669
+ // Common renderer - if there is one available for this type use it,
6670
+ // otherwise use the default
6671
+ return host[renderer] || host._;
6672
+ }
6673
+
6674
+ // Use the default
6675
+ return host._;
6676
+ }
6677
+
6678
+
6679
+ /**
6680
+ * Detect the data source being used for the table. Used to simplify the code
6681
+ * a little (ajax) and to make it compress a little smaller.
6682
+ *
6683
+ * @param {object} settings dataTables settings object
6684
+ * @returns {string} Data source
6685
+ * @memberof WFDataTable#oApi
6686
+ */
6687
+ function _fnDataSource ( settings )
6688
+ {
6689
+ if ( settings.oFeatures.bServerSide ) {
6690
+ return 'ssp';
6691
+ }
6692
+ else if ( settings.ajax || settings.sAjaxSource ) {
6693
+ return 'ajax';
6694
+ }
6695
+ return 'dom';
6696
+ }
6697
+
6698
+
6699
+
6700
+
6701
+ /**
6702
+ * Computed structure of the WFDataTables API, defined by the options passed to
6703
+ * `WFDataTable.Api.register()` when building the API.
6704
+ *
6705
+ * The structure is built in order to speed creation and extension of the Api
6706
+ * objects since the extensions are effectively pre-parsed.
6707
+ *
6708
+ * The array is an array of objects with the following structure, where this
6709
+ * base array represents the Api prototype base:
6710
+ *
6711
+ * [
6712
+ * {
6713
+ * name: 'data' -- string - Property name
6714
+ * val: function () {}, -- function - Api method (or undefined if just an object
6715
+ * methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result
6716
+ * propExt: [ ... ] -- array - Array of Api object definitions to extend the property
6717
+ * },
6718
+ * {
6719
+ * name: 'row'
6720
+ * val: {},
6721
+ * methodExt: [ ... ],
6722
+ * propExt: [
6723
+ * {
6724
+ * name: 'data'
6725
+ * val: function () {},
6726
+ * methodExt: [ ... ],
6727
+ * propExt: [ ... ]
6728
+ * },
6729
+ * ...
6730
+ * ]
6731
+ * }
6732
+ * ]
6733
+ *
6734
+ * @type {Array}
6735
+ * @ignore
6736
+ */
6737
+ var __apiStruct = [];
6738
+
6739
+
6740
+ /**
6741
+ * `Array.prototype` reference.
6742
+ *
6743
+ * @type object
6744
+ * @ignore
6745
+ */
6746
+ var __arrayProto = Array.prototype;
6747
+
6748
+
6749
+ /**
6750
+ * Abstraction for `context` parameter of the `Api` constructor to allow it to
6751
+ * take several different forms for ease of use.
6752
+ *
6753
+ * Each of the input parameter types will be converted to a WFDataTables settings
6754
+ * object where possible.
6755
+ *
6756
+ * @param {string|node|jQuery|object} mixed WFDataTable identifier. Can be one
6757
+ * of:
6758
+ *
6759
+ * * `string` - jQuery selector. Any WFDataTables' matching the given selector
6760
+ * with be found and used.
6761
+ * * `node` - `TABLE` node which has already been formed into a WFDataTable.
6762
+ * * `jQuery` - A jQuery object of `TABLE` nodes.
6763
+ * * `object` - WFDataTables settings object
6764
+ * * `WFDataTables.Api` - API instance
6765
+ * @return {array|null} Matching WFDataTables settings objects. `null` or
6766
+ * `undefined` is returned if no matching WFDataTable is found.
6767
+ * @ignore
6768
+ */
6769
+ var _toSettings = function ( mixed )
6770
+ {
6771
+ var idx, jq;
6772
+ var settings = WFDataTable.settings;
6773
+ var tables = $.map( settings, function (el, i) {
6774
+ return el.nTable;
6775
+ } );
6776
+
6777
+ if ( ! mixed ) {
6778
+ return [];
6779
+ }
6780
+ else if ( mixed.nTable && mixed.oApi ) {
6781
+ // WFDataTables settings object
6782
+ return [ mixed ];
6783
+ }
6784
+ else if ( mixed.nodeName && mixed.nodeName.toLowerCase() === 'table' ) {
6785
+ // Table node
6786
+ idx = $.inArray( mixed, tables );
6787
+ return idx !== -1 ? [ settings[idx] ] : null;
6788
+ }
6789
+ else if ( mixed && typeof mixed.settings === 'function' ) {
6790
+ return mixed.settings().toArray();
6791
+ }
6792
+ else if ( typeof mixed === 'string' ) {
6793
+ // jQuery selector
6794
+ jq = $(mixed);
6795
+ }
6796
+ else if ( mixed instanceof $ ) {
6797
+ // jQuery object (also WFDataTables instance)
6798
+ jq = mixed;
6799
+ }
6800
+
6801
+ if ( jq ) {
6802
+ return jq.map( function(i) {
6803
+ idx = $.inArray( this, tables );
6804
+ return idx !== -1 ? settings[idx] : null;
6805
+ } ).toArray();
6806
+ }
6807
+ };
6808
+
6809
+
6810
+ /**
6811
+ * WFDataTables API class - used to control and interface with one or more
6812
+ * WFDataTables enhanced tables.
6813
+ *
6814
+ * The API class is heavily based on jQuery, presenting a chainable interface
6815
+ * that you can use to interact with tables. Each instance of the API class has
6816
+ * a "context" - i.e. the tables that it will operate on. This could be a single
6817
+ * table, all tables on a page or a sub-set thereof.
6818
+ *
6819
+ * Additionally the API is designed to allow you to easily work with the data in
6820
+ * the tables, retrieving and manipulating it as required. This is done by
6821
+ * presenting the API class as an array like interface. The contents of the
6822
+ * array depend upon the actions requested by each method (for example
6823
+ * `rows().nodes()` will return an array of nodes, while `rows().data()` will
6824
+ * return an array of objects or arrays depending upon your table's
6825
+ * configuration). The API object has a number of array like methods (`push`,
6826
+ * `pop`, `reverse` etc) as well as additional helper methods (`each`, `pluck`,
6827
+ * `unique` etc) to assist your working with the data held in a table.
6828
+ *
6829
+ * Most methods (those which return an Api instance) are chainable, which means
6830
+ * the return from a method call also has all of the methods available that the
6831
+ * top level object had. For example, these two calls are equivalent:
6832
+ *
6833
+ * // Not chained
6834
+ * api.row.add( {...} );
6835
+ * api.draw();
6836
+ *
6837
+ * // Chained
6838
+ * api.row.add( {...} ).draw();
6839
+ *
6840
+ * @class WFDataTable.Api
6841
+ * @param {array|object|string|jQuery} context WFDataTable identifier. This is
6842
+ * used to define which WFDataTables enhanced tables this API will operate on.
6843
+ * Can be one of:
6844
+ *
6845
+ * * `string` - jQuery selector. Any WFDataTables' matching the given selector
6846
+ * with be found and used.
6847
+ * * `node` - `TABLE` node which has already been formed into a WFDataTable.
6848
+ * * `jQuery` - A jQuery object of `TABLE` nodes.
6849
+ * * `object` - WFDataTables settings object
6850
+ * @param {array} [data] Data to initialise the Api instance with.
6851
+ *
6852
+ * @example
6853
+ * // Direct initialisation during WFDataTables construction
6854
+ * var api = $('#example').WFDataTable();
6855
+ *
6856
+ * @example
6857
+ * // Initialisation using a WFDataTables jQuery object
6858
+ * var api = $('#example').wfDataTable().api();
6859
+ *
6860
+ * @example
6861
+ * // Initialisation as a constructor
6862
+ * var api = new $.fn.WFDataTable.Api( 'table.wfDataTable' );
6863
+ */
6864
+ _Api = function ( context, data )
6865
+ {
6866
+ if ( ! (this instanceof _Api) ) {
6867
+ return new _Api( context, data );
6868
+ }
6869
+
6870
+ var settings = [];
6871
+ var ctxSettings = function ( o ) {
6872
+ var a = _toSettings( o );
6873
+ if ( a ) {
6874
+ settings = settings.concat( a );
6875
+ }
6876
+ };
6877
+
6878
+ if ( $.isArray( context ) ) {
6879
+ for ( var i=0, ien=context.length ; i<ien ; i++ ) {
6880
+ ctxSettings( context[i] );
6881
+ }
6882
+ }
6883
+ else {
6884
+ ctxSettings( context );
6885
+ }
6886
+
6887
+ // Remove duplicates
6888
+ this.context = _unique( settings );
6889
+
6890
+ // Initial data
6891
+ if ( data ) {
6892
+ $.merge( this, data );
6893
+ }
6894
+
6895
+ // selector
6896
+ this.selector = {
6897
+ rows: null,
6898
+ cols: null,
6899
+ opts: null
6900
+ };
6901
+
6902
+ _Api.extend( this, this, __apiStruct );
6903
+ };
6904
+
6905
+ WFDataTable.Api = _Api;
6906
+
6907
+ // Don't destroy the existing prototype, just extend it. Required for jQuery 2's
6908
+ // isPlainObject.
6909
+ $.extend( _Api.prototype, {
6910
+ any: function ()
6911
+ {
6912
+ return this.count() !== 0;
6913
+ },
6914
+
6915
+
6916
+ concat: __arrayProto.concat,
6917
+
6918
+
6919
+ context: [], // array of table settings objects
6920
+
6921
+
6922
+ count: function ()
6923
+ {
6924
+ return this.flatten().length;
6925
+ },
6926
+
6927
+
6928
+ each: function ( fn )
6929
+ {
6930
+ for ( var i=0, ien=this.length ; i<ien; i++ ) {
6931
+ fn.call( this, this[i], i, this );
6932
+ }
6933
+
6934
+ return this;
6935
+ },
6936
+
6937
+
6938
+ eq: function ( idx )
6939
+ {
6940
+ var ctx = this.context;
6941
+
6942
+ return ctx.length > idx ?
6943
+ new _Api( ctx[idx], this[idx] ) :
6944
+ null;
6945
+ },
6946
+
6947
+
6948
+ filter: function ( fn )
6949
+ {
6950
+ var a = [];
6951
+
6952
+ if ( __arrayProto.filter ) {
6953
+ a = __arrayProto.filter.call( this, fn, this );
6954
+ }
6955
+ else {
6956
+ // Compatibility for browsers without EMCA-252-5 (JS 1.6)
6957
+ for ( var i=0, ien=this.length ; i<ien ; i++ ) {
6958
+ if ( fn.call( this, this[i], i, this ) ) {
6959
+ a.push( this[i] );
6960
+ }
6961
+ }
6962
+ }
6963
+
6964
+ return new _Api( this.context, a );
6965
+ },
6966
+
6967
+
6968
+ flatten: function ()
6969
+ {
6970
+ var a = [];
6971
+ return new _Api( this.context, a.concat.apply( a, this.toArray() ) );
6972
+ },
6973
+
6974
+
6975
+ join: __arrayProto.join,
6976
+
6977
+
6978
+ indexOf: __arrayProto.indexOf || function (obj, start)
6979
+ {
6980
+ for ( var i=(start || 0), ien=this.length ; i<ien ; i++ ) {
6981
+ if ( this[i] === obj ) {
6982
+ return i;
6983
+ }
6984
+ }
6985
+ return -1;
6986
+ },
6987
+
6988
+ iterator: function ( flatten, type, fn, alwaysNew ) {
6989
+ var
6990
+ a = [], ret,
6991
+ i, ien, j, jen,
6992
+ context = this.context,
6993
+ rows, items, item,
6994
+ selector = this.selector;
6995
+
6996
+ // Argument shifting
6997
+ if ( typeof flatten === 'string' ) {
6998
+ alwaysNew = fn;
6999
+ fn = type;
7000
+ type = flatten;
7001
+ flatten = false;
7002
+ }
7003
+
7004
+ for ( i=0, ien=context.length ; i<ien ; i++ ) {
7005
+ var apiInst = new _Api( context[i] );
7006
+
7007
+ if ( type === 'table' ) {
7008
+ ret = fn.call( apiInst, context[i], i );
7009
+
7010
+ if ( ret !== undefined ) {
7011
+ a.push( ret );
7012
+ }
7013
+ }
7014
+ else if ( type === 'columns' || type === 'rows' ) {
7015
+ // this has same length as context - one entry for each table
7016
+ ret = fn.call( apiInst, context[i], this[i], i );
7017
+
7018
+ if ( ret !== undefined ) {
7019
+ a.push( ret );
7020
+ }
7021
+ }
7022
+ else if ( type === 'column' || type === 'column-rows' || type === 'row' || type === 'cell' ) {
7023
+ // columns and rows share the same structure.
7024
+ // 'this' is an array of column indexes for each context
7025
+ items = this[i];
7026
+
7027
+ if ( type === 'column-rows' ) {
7028
+ rows = _selector_row_indexes( context[i], selector.opts );
7029
+ }
7030
+
7031
+ for ( j=0, jen=items.length ; j<jen ; j++ ) {
7032
+ item = items[j];
7033
+
7034
+ if ( type === 'cell' ) {
7035
+ ret = fn.call( apiInst, context[i], item.row, item.column, i, j );
7036
+ }
7037
+ else {
7038
+ ret = fn.call( apiInst, context[i], item, i, j, rows );
7039
+ }
7040
+
7041
+ if ( ret !== undefined ) {
7042
+ a.push( ret );
7043
+ }
7044
+ }
7045
+ }
7046
+ }
7047
+
7048
+ if ( a.length || alwaysNew ) {
7049
+ var api = new _Api( context, flatten ? a.concat.apply( [], a ) : a );
7050
+ var apiSelector = api.selector;
7051
+ apiSelector.rows = selector.rows;
7052
+ apiSelector.cols = selector.cols;
7053
+ apiSelector.opts = selector.opts;
7054
+ return api;
7055
+ }
7056
+ return this;
7057
+ },
7058
+
7059
+
7060
+ lastIndexOf: __arrayProto.lastIndexOf || function (obj, start)
7061
+ {
7062
+ // Bit cheeky...
7063
+ return this.indexOf.apply( this.toArray.reverse(), arguments );
7064
+ },
7065
+
7066
+
7067
+ length: 0,
7068
+
7069
+
7070
+ map: function ( fn )
7071
+ {
7072
+ var a = [];
7073
+
7074
+ if ( __arrayProto.map ) {
7075
+ a = __arrayProto.map.call( this, fn, this );
7076
+ }
7077
+ else {
7078
+ // Compatibility for browsers without EMCA-252-5 (JS 1.6)
7079
+ for ( var i=0, ien=this.length ; i<ien ; i++ ) {
7080
+ a.push( fn.call( this, this[i], i ) );
7081
+ }
7082
+ }
7083
+
7084
+ return new _Api( this.context, a );
7085
+ },
7086
+
7087
+
7088
+ pluck: function ( prop )
7089
+ {
7090
+ return this.map( function ( el ) {
7091
+ return el[ prop ];
7092
+ } );
7093
+ },
7094
+
7095
+ pop: __arrayProto.pop,
7096
+
7097
+
7098
+ push: __arrayProto.push,
7099
+
7100
+
7101
+ // Does not return an API instance
7102
+ reduce: __arrayProto.reduce || function ( fn, init )
7103
+ {
7104
+ return _fnReduce( this, fn, init, 0, this.length, 1 );
7105
+ },
7106
+
7107
+
7108
+ reduceRight: __arrayProto.reduceRight || function ( fn, init )
7109
+ {
7110
+ return _fnReduce( this, fn, init, this.length-1, -1, -1 );
7111
+ },
7112
+
7113
+
7114
+ reverse: __arrayProto.reverse,
7115
+
7116
+
7117
+ // Object with rows, columns and opts
7118
+ selector: null,
7119
+
7120
+
7121
+ shift: __arrayProto.shift,
7122
+
7123
+
7124
+ sort: __arrayProto.sort, // ? name - order?
7125
+
7126
+
7127
+ splice: __arrayProto.splice,
7128
+
7129
+
7130
+ toArray: function ()
7131
+ {
7132
+ return __arrayProto.slice.call( this );
7133
+ },
7134
+
7135
+
7136
+ to$: function ()
7137
+ {
7138
+ return $( this );
7139
+ },
7140
+
7141
+
7142
+ toJQuery: function ()
7143
+ {
7144
+ return $( this );
7145
+ },
7146
+
7147
+
7148
+ unique: function ()
7149
+ {
7150
+ return new _Api( this.context, _unique(this) );
7151
+ },
7152
+
7153
+
7154
+ unshift: __arrayProto.unshift
7155
+ } );
7156
+
7157
+
7158
+ _Api.extend = function ( scope, obj, ext )
7159
+ {
7160
+ // Only extend API instances and static properties of the API
7161
+ if ( ! ext.length || ! obj || ( ! (obj instanceof _Api) && ! obj.__dt_wrapper ) ) {
7162
+ return;
7163
+ }
7164
+
7165
+ var
7166
+ i, ien,
7167
+ j, jen,
7168
+ struct, inner,
7169
+ methodScoping = function ( scope, fn, struc ) {
7170
+ return function () {
7171
+ var ret = fn.apply( scope, arguments );
7172
+
7173
+ // Method extension
7174
+ _Api.extend( ret, ret, struc.methodExt );
7175
+ return ret;
7176
+ };
7177
+ };
7178
+
7179
+ for ( i=0, ien=ext.length ; i<ien ; i++ ) {
7180
+ struct = ext[i];
7181
+
7182
+ // Value
7183
+ obj[ struct.name ] = typeof struct.val === 'function' ?
7184
+ methodScoping( scope, struct.val, struct ) :
7185
+ $.isPlainObject( struct.val ) ?
7186
+ {} :
7187
+ struct.val;
7188
+
7189
+ obj[ struct.name ].__dt_wrapper = true;
7190
+
7191
+ // Property extension
7192
+ _Api.extend( scope, obj[ struct.name ], struct.propExt );
7193
+ }
7194
+ };
7195
+
7196
+
7197
+ // @todo - Is there need for an augment function?
7198
+ // _Api.augment = function ( inst, name )
7199
+ // {
7200
+ // // Find src object in the structure from the name
7201
+ // var parts = name.split('.');
7202
+
7203
+ // _Api.extend( inst, obj );
7204
+ // };
7205
+
7206
+
7207
+ // [
7208
+ // {
7209
+ // name: 'data' -- string - Property name
7210
+ // val: function () {}, -- function - Api method (or undefined if just an object
7211
+ // methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result
7212
+ // propExt: [ ... ] -- array - Array of Api object definitions to extend the property
7213
+ // },
7214
+ // {
7215
+ // name: 'row'
7216
+ // val: {},
7217
+ // methodExt: [ ... ],
7218
+ // propExt: [
7219
+ // {
7220
+ // name: 'data'
7221
+ // val: function () {},
7222
+ // methodExt: [ ... ],
7223
+ // propExt: [ ... ]
7224
+ // },
7225
+ // ...
7226
+ // ]
7227
+ // }
7228
+ // ]
7229
+
7230
+ _Api.register = _api_register = function ( name, val )
7231
+ {
7232
+ if ( $.isArray( name ) ) {
7233
+ for ( var j=0, jen=name.length ; j<jen ; j++ ) {
7234
+ _Api.register( name[j], val );
7235
+ }
7236
+ return;
7237
+ }
7238
+
7239
+ var
7240
+ i, ien,
7241
+ heir = name.split('.'),
7242
+ struct = __apiStruct,
7243
+ key, method;
7244
+
7245
+ var find = function ( src, name ) {
7246
+ for ( var i=0, ien=src.length ; i<ien ; i++ ) {
7247
+ if ( src[i].name === name ) {
7248
+ return src[i];
7249
+ }
7250
+ }
7251
+ return null;
7252
+ };
7253
+
7254
+ for ( i=0, ien=heir.length ; i<ien ; i++ ) {
7255
+ method = heir[i].indexOf('()') !== -1;
7256
+ key = method ?
7257
+ heir[i].replace('()', '') :
7258
+ heir[i];
7259
+
7260
+ var src = find( struct, key );
7261
+ if ( ! src ) {
7262
+ src = {
7263
+ name: key,
7264
+ val: {},
7265
+ methodExt: [],
7266
+ propExt: []
7267
+ };
7268
+ struct.push( src );
7269
+ }
7270
+
7271
+ if ( i === ien-1 ) {
7272
+ src.val = val;
7273
+ }
7274
+ else {
7275
+ struct = method ?
7276
+ src.methodExt :
7277
+ src.propExt;
7278
+ }
7279
+ }
7280
+ };
7281
+
7282
+
7283
+ _Api.registerPlural = _api_registerPlural = function ( pluralName, singularName, val ) {
7284
+ _Api.register( pluralName, val );
7285
+
7286
+ _Api.register( singularName, function () {
7287
+ var ret = val.apply( this, arguments );
7288
+
7289
+ if ( ret === this ) {
7290
+ // Returned item is the API instance that was passed in, return it
7291
+ return this;
7292
+ }
7293
+ else if ( ret instanceof _Api ) {
7294
+ // New API instance returned, want the value from the first item
7295
+ // in the returned array for the singular result.
7296
+ return ret.length ?
7297
+ $.isArray( ret[0] ) ?
7298
+ new _Api( ret.context, ret[0] ) : // Array results are 'enhanced'
7299
+ ret[0] :
7300
+ undefined;
7301
+ }
7302
+
7303
+ // Non-API return - just fire it back
7304
+ return ret;
7305
+ } );
7306
+ };
7307
+
7308
+
7309
+ /**
7310
+ * Selector for HTML tables. Apply the given selector to the give array of
7311
+ * WFDataTables settings objects.
7312
+ *
7313
+ * @param {string|integer} [selector] jQuery selector string or integer
7314
+ * @param {array} Array of WFDataTables settings objects to be filtered
7315
+ * @return {array}
7316
+ * @ignore
7317
+ */
7318
+ var __table_selector = function ( selector, a )
7319
+ {
7320
+ // Integer is used to pick out a table by index
7321
+ if ( typeof selector === 'number' ) {
7322
+ return [ a[ selector ] ];
7323
+ }
7324
+
7325
+ // Perform a jQuery selector on the table nodes
7326
+ var nodes = $.map( a, function (el, i) {
7327
+ return el.nTable;
7328
+ } );
7329
+
7330
+ return $(nodes)
7331
+ .filter( selector )
7332
+ .map( function (i) {
7333
+ // Need to translate back from the table node to the settings
7334
+ var idx = $.inArray( this, nodes );
7335
+ return a[ idx ];
7336
+ } )
7337
+ .toArray();
7338
+ };
7339
+
7340
+
7341
+
7342
+ /**
7343
+ * Context selector for the API's context (i.e. the tables the API instance
7344
+ * refers to.
7345
+ *
7346
+ * @name WFDataTable.Api#tables
7347
+ * @param {string|integer} [selector] Selector to pick which tables the iterator
7348
+ * should operate on. If not given, all tables in the current context are
7349
+ * used. This can be given as a jQuery selector (for example `':gt(0)'`) to
7350
+ * select multiple tables or as an integer to select a single table.
7351
+ * @returns {WFDataTable.Api} Returns a new API instance if a selector is given.
7352
+ */
7353
+ _api_register( 'tables()', function ( selector ) {
7354
+ // A new instance is created if there was a selector specified
7355
+ return selector ?
7356
+ new _Api( __table_selector( selector, this.context ) ) :
7357
+ this;
7358
+ } );
7359
+
7360
+
7361
+ _api_register( 'table()', function ( selector ) {
7362
+ var tables = this.tables( selector );
7363
+ var ctx = tables.context;
7364
+
7365
+ // Truncate to the first matched table
7366
+ return ctx.length ?
7367
+ new _Api( ctx[0] ) :
7368
+ tables;
7369
+ } );
7370
+
7371
+
7372
+ _api_registerPlural( 'tables().nodes()', 'table().node()' , function () {
7373
+ return this.iterator( 'table', function ( ctx ) {
7374
+ return ctx.nTable;
7375
+ }, 1 );
7376
+ } );
7377
+
7378
+
7379
+ _api_registerPlural( 'tables().body()', 'table().body()' , function () {
7380
+ return this.iterator( 'table', function ( ctx ) {
7381
+ return ctx.nTBody;
7382
+ }, 1 );
7383
+ } );
7384
+
7385
+
7386
+ _api_registerPlural( 'tables().header()', 'table().header()' , function () {
7387
+ return this.iterator( 'table', function ( ctx ) {
7388
+ return ctx.nTHead;
7389
+ }, 1 );
7390
+ } );
7391
+
7392
+
7393
+ _api_registerPlural( 'tables().footer()', 'table().footer()' , function () {
7394
+ return this.iterator( 'table', function ( ctx ) {
7395
+ return ctx.nTFoot;
7396
+ }, 1 );
7397
+ } );
7398
+
7399
+
7400
+ _api_registerPlural( 'tables().containers()', 'table().container()' , function () {
7401
+ return this.iterator( 'table', function ( ctx ) {
7402
+ return ctx.nTableWrapper;
7403
+ }, 1 );
7404
+ } );
7405
+
7406
+
7407
+
7408
+ /**
7409
+ * Redraw the tables in the current context.
7410
+ */
7411
+ _api_register( 'draw()', function ( paging ) {
7412
+ return this.iterator( 'table', function ( settings ) {
7413
+ if ( paging === 'page' ) {
7414
+ _fnDraw( settings );
7415
+ }
7416
+ else {
7417
+ if ( typeof paging === 'string' ) {
7418
+ paging = paging === 'full-hold' ?
7419
+ false :
7420
+ true;
7421
+ }
7422
+
7423
+ _fnReDraw( settings, paging===false );
7424
+ }
7425
+ } );
7426
+ } );
7427
+
7428
+
7429
+
7430
+ /**
7431
+ * Get the current page index.
7432
+ *
7433
+ * @return {integer} Current page index (zero based)
7434
+ *//**
7435
+ * Set the current page.
7436
+ *
7437
+ * Note that if you attempt to show a page which does not exist, WFDataTables will
7438
+ * not throw an error, but rather reset the paging.
7439
+ *
7440
+ * @param {integer|string} action The paging action to take. This can be one of:
7441
+ * * `integer` - The page index to jump to
7442
+ * * `string` - An action to take:
7443
+ * * `first` - Jump to first page.
7444
+ * * `next` - Jump to the next page
7445
+ * * `previous` - Jump to previous page
7446
+ * * `last` - Jump to the last page.
7447
+ * @returns {WFDataTables.Api} this
7448
+ */
7449
+ _api_register( 'page()', function ( action ) {
7450
+ if ( action === undefined ) {
7451
+ return this.page.info().page; // not an expensive call
7452
+ }
7453
+
7454
+ // else, have an action to take on all tables
7455
+ return this.iterator( 'table', function ( settings ) {
7456
+ _fnPageChange( settings, action );
7457
+ } );
7458
+ } );
7459
+
7460
+
7461
+ /**
7462
+ * Paging information for the first table in the current context.
7463
+ *
7464
+ * If you require paging information for another table, use the `table()` method
7465
+ * with a suitable selector.
7466
+ *
7467
+ * @return {object} Object with the following properties set:
7468
+ * * `page` - Current page index (zero based - i.e. the first page is `0`)
7469
+ * * `pages` - Total number of pages
7470
+ * * `start` - Display index for the first record shown on the current page
7471
+ * * `end` - Display index for the last record shown on the current page
7472
+ * * `length` - Display length (number of records). Note that generally `start
7473
+ * + length = end`, but this is not always true, for example if there are
7474
+ * only 2 records to show on the final page, with a length of 10.
7475
+ * * `recordsTotal` - Full data set length
7476
+ * * `recordsDisplay` - Data set length once the current filtering criterion
7477
+ * are applied.
7478
+ */
7479
+ _api_register( 'page.info()', function ( action ) {
7480
+ if ( this.context.length === 0 ) {
7481
+ return undefined;
7482
+ }
7483
+
7484
+ var
7485
+ settings = this.context[0],
7486
+ start = settings._iDisplayStart,
7487
+ len = settings.oFeatures.bPaginate ? settings._iDisplayLength : -1,
7488
+ visRecords = settings.fnRecordsDisplay(),
7489
+ all = len === -1;
7490
+
7491
+ return {
7492
+ "page": all ? 0 : Math.floor( start / len ),
7493
+ "pages": all ? 1 : Math.ceil( visRecords / len ),
7494
+ "start": start,
7495
+ "end": settings.fnDisplayEnd(),
7496
+ "length": len,
7497
+ "recordsTotal": settings.fnRecordsTotal(),
7498
+ "recordsDisplay": visRecords,
7499
+ "serverSide": _fnDataSource( settings ) === 'ssp'
7500
+ };
7501
+ } );
7502
+
7503
+
7504
+ /**
7505
+ * Get the current page length.
7506
+ *
7507
+ * @return {integer} Current page length. Note `-1` indicates that all records
7508
+ * are to be shown.
7509
+ *//**
7510
+ * Set the current page length.
7511
+ *
7512
+ * @param {integer} Page length to set. Use `-1` to show all records.
7513
+ * @returns {WFDataTables.Api} this
7514
+ */
7515
+ _api_register( 'page.len()', function ( len ) {
7516
+ // Note that we can't call this function 'length()' because `length`
7517
+ // is a Javascript property of functions which defines how many arguments
7518
+ // the function expects.
7519
+ if ( len === undefined ) {
7520
+ return this.context.length !== 0 ?
7521
+ this.context[0]._iDisplayLength :
7522
+ undefined;
7523
+ }
7524
+
7525
+ // else, set the page length
7526
+ return this.iterator( 'table', function ( settings ) {
7527
+ _fnLengthChange( settings, len );
7528
+ } );
7529
+ } );
7530
+
7531
+
7532
+
7533
+ var __reload = function ( settings, holdPosition, callback ) {
7534
+ // Use the draw event to trigger a callback
7535
+ if ( callback ) {
7536
+ var api = new _Api( settings );
7537
+
7538
+ api.one( 'draw', function () {
7539
+ callback( api.ajax.json() );
7540
+ } );
7541
+ }
7542
+
7543
+ if ( _fnDataSource( settings ) == 'ssp' ) {
7544
+ _fnReDraw( settings, holdPosition );
7545
+ }
7546
+ else {
7547
+ _fnProcessingDisplay( settings, true );
7548
+
7549
+ // Cancel an existing request
7550
+ var xhr = settings.jqXHR;
7551
+ if ( xhr && xhr.readyState !== 4 ) {
7552
+ xhr.abort();
7553
+ }
7554
+
7555
+ // Trigger xhr
7556
+ _fnBuildAjax( settings, [], function( json ) {
7557
+ _fnClearTable( settings );
7558
+
7559
+ var data = _fnAjaxDataSrc( settings, json );
7560
+ for ( var i=0, ien=data.length ; i<ien ; i++ ) {
7561
+ _fnAddData( settings, data[i] );
7562
+ }
7563
+
7564
+ _fnReDraw( settings, holdPosition );
7565
+ _fnProcessingDisplay( settings, false );
7566
+ } );
7567
+ }
7568
+ };
7569
+
7570
+
7571
+ /**
7572
+ * Get the JSON response from the last Ajax request that WFDataTables made to the
7573
+ * server. Note that this returns the JSON from the first table in the current
7574
+ * context.
7575
+ *
7576
+ * @return {object} JSON received from the server.
7577
+ */
7578
+ _api_register( 'ajax.json()', function () {
7579
+ var ctx = this.context;
7580
+
7581
+ if ( ctx.length > 0 ) {
7582
+ return ctx[0].json;
7583
+ }
7584
+
7585
+ // else return undefined;
7586
+ } );
7587
+
7588
+
7589
+ /**
7590
+ * Get the data submitted in the last Ajax request
7591
+ */
7592
+ _api_register( 'ajax.params()', function () {
7593
+ var ctx = this.context;
7594
+
7595
+ if ( ctx.length > 0 ) {
7596
+ return ctx[0].oAjaxData;
7597
+ }
7598
+
7599
+ // else return undefined;
7600
+ } );
7601
+
7602
+
7603
+ /**
7604
+ * Reload tables from the Ajax data source. Note that this function will
7605
+ * automatically re-draw the table when the remote data has been loaded.
7606
+ *
7607
+ * @param {boolean} [reset=true] Reset (default) or hold the current paging
7608
+ * position. A full re-sort and re-filter is performed when this method is
7609
+ * called, which is why the pagination reset is the default action.
7610
+ * @returns {WFDataTables.Api} this
7611
+ */
7612
+ _api_register( 'ajax.reload()', function ( callback, resetPaging ) {
7613
+ return this.iterator( 'table', function (settings) {
7614
+ __reload( settings, resetPaging===false, callback );
7615
+ } );
7616
+ } );
7617
+
7618
+
7619
+ /**
7620
+ * Get the current Ajax URL. Note that this returns the URL from the first
7621
+ * table in the current context.
7622
+ *
7623
+ * @return {string} Current Ajax source URL
7624
+ *//**
7625
+ * Set the Ajax URL. Note that this will set the URL for all tables in the
7626
+ * current context.
7627
+ *
7628
+ * @param {string} url URL to set.
7629
+ * @returns {WFDataTables.Api} this
7630
+ */
7631
+ _api_register( 'ajax.url()', function ( url ) {
7632
+ var ctx = this.context;
7633
+
7634
+ if ( url === undefined ) {
7635
+ // get
7636
+ if ( ctx.length === 0 ) {
7637
+ return undefined;
7638
+ }
7639
+ ctx = ctx[0];
7640
+
7641
+ return ctx.ajax ?
7642
+ $.isPlainObject( ctx.ajax ) ?
7643
+ ctx.ajax.url :
7644
+ ctx.ajax :
7645
+ ctx.sAjaxSource;
7646
+ }
7647
+
7648
+ // set
7649
+ return this.iterator( 'table', function ( settings ) {
7650
+ if ( $.isPlainObject( settings.ajax ) ) {
7651
+ settings.ajax.url = url;
7652
+ }
7653
+ else {
7654
+ settings.ajax = url;
7655
+ }
7656
+ // No need to consider sAjaxSource here since WFDataTables gives priority
7657
+ // to `ajax` over `sAjaxSource`. So setting `ajax` here, renders any
7658
+ // value of `sAjaxSource` redundant.
7659
+ } );
7660
+ } );
7661
+
7662
+
7663
+ /**
7664
+ * Load data from the newly set Ajax URL. Note that this method is only
7665
+ * available when `ajax.url()` is used to set a URL. Additionally, this method
7666
+ * has the same effect as calling `ajax.reload()` but is provided for
7667
+ * convenience when setting a new URL. Like `ajax.reload()` it will
7668
+ * automatically redraw the table once the remote data has been loaded.
7669
+ *
7670
+ * @returns {WFDataTables.Api} this
7671
+ */
7672
+ _api_register( 'ajax.url().load()', function ( callback, resetPaging ) {
7673
+ // Same as a reload, but makes sense to present it for easy access after a
7674
+ // url change
7675
+ return this.iterator( 'table', function ( ctx ) {
7676
+ __reload( ctx, resetPaging===false, callback );
7677
+ } );
7678
+ } );
7679
+
7680
+
7681
+
7682
+
7683
+ var _selector_run = function ( type, selector, selectFn, settings, opts )
7684
+ {
7685
+ var
7686
+ out = [], res,
7687
+ a, i, ien, j, jen,
7688
+ selectorType = typeof selector;
7689
+
7690
+ // Can't just check for isArray here, as an API or jQuery instance might be
7691
+ // given with their array like look
7692
+ if ( ! selector || selectorType === 'string' || selectorType === 'function' || selector.length === undefined ) {
7693
+ selector = [ selector ];
7694
+ }
7695
+
7696
+ for ( i=0, ien=selector.length ; i<ien ; i++ ) {
7697
+ // Only split on simple strings - complex expressions will be jQuery selectors
7698
+ a = selector[i] && selector[i].split && ! selector[i].match(/[\[\(:]/) ?
7699
+ selector[i].split(',') :
7700
+ [ selector[i] ];
7701
+
7702
+ for ( j=0, jen=a.length ; j<jen ; j++ ) {
7703
+ res = selectFn( typeof a[j] === 'string' ? $.trim(a[j]) : a[j] );
7704
+
7705
+ if ( res && res.length ) {
7706
+ out = out.concat( res );
7707
+ }
7708
+ }
7709
+ }
7710
+
7711
+ // selector extensions
7712
+ var ext = _ext.selector[ type ];
7713
+ if ( ext.length ) {
7714
+ for ( i=0, ien=ext.length ; i<ien ; i++ ) {
7715
+ out = ext[i]( settings, opts, out );
7716
+ }
7717
+ }
7718
+
7719
+ return _unique( out );
7720
+ };
7721
+
7722
+
7723
+ var _selector_opts = function ( opts )
7724
+ {
7725
+ if ( ! opts ) {
7726
+ opts = {};
7727
+ }
7728
+
7729
+ // Backwards compatibility for 1.9- which used the terminology filter rather
7730
+ // than search
7731
+ if ( opts.filter && opts.search === undefined ) {
7732
+ opts.search = opts.filter;
7733
+ }
7734
+
7735
+ return $.extend( {
7736
+ search: 'none',
7737
+ order: 'current',
7738
+ page: 'all'
7739
+ }, opts );
7740
+ };
7741
+
7742
+
7743
+ var _selector_first = function ( inst )
7744
+ {
7745
+ // Reduce the API instance to the first item found
7746
+ for ( var i=0, ien=inst.length ; i<ien ; i++ ) {
7747
+ if ( inst[i].length > 0 ) {
7748
+ // Assign the first element to the first item in the instance
7749
+ // and truncate the instance and context
7750
+ inst[0] = inst[i];
7751
+ inst[0].length = 1;
7752
+ inst.length = 1;
7753
+ inst.context = [ inst.context[i] ];
7754
+
7755
+ return inst;
7756
+ }
7757
+ }
7758
+
7759
+ // Not found - return an empty instance
7760
+ inst.length = 0;
7761
+ return inst;
7762
+ };
7763
+
7764
+
7765
+ var _selector_row_indexes = function ( settings, opts )
7766
+ {
7767
+ var
7768
+ i, ien, tmp, a=[],
7769
+ displayFiltered = settings.aiDisplay,
7770
+ displayMaster = settings.aiDisplayMaster;
7771
+
7772
+ var
7773
+ search = opts.search, // none, applied, removed
7774
+ order = opts.order, // applied, current, index (original - compatibility with 1.9)
7775
+ page = opts.page; // all, current
7776
+
7777
+ if ( _fnDataSource( settings ) == 'ssp' ) {
7778
+ // In server-side processing mode, most options are irrelevant since
7779
+ // rows not shown don't exist and the index order is the applied order
7780
+ // Removed is a special case - for consistency just return an empty
7781
+ // array
7782
+ return search === 'removed' ?
7783
+ [] :
7784
+ _range( 0, displayMaster.length );
7785
+ }
7786
+ else if ( page == 'current' ) {
7787
+ // Current page implies that order=current and fitler=applied, since it is
7788
+ // fairly senseless otherwise, regardless of what order and search actually
7789
+ // are
7790
+ for ( i=settings._iDisplayStart, ien=settings.fnDisplayEnd() ; i<ien ; i++ ) {
7791
+ a.push( displayFiltered[i] );
7792
+ }
7793
+ }
7794
+ else if ( order == 'current' || order == 'applied' ) {
7795
+ a = search == 'none' ?
7796
+ displayMaster.slice() : // no search
7797
+ search == 'applied' ?
7798
+ displayFiltered.slice() : // applied search
7799
+ $.map( displayMaster, function (el, i) { // removed search
7800
+ return $.inArray( el, displayFiltered ) === -1 ? el : null;
7801
+ } );
7802
+ }
7803
+ else if ( order == 'index' || order == 'original' ) {
7804
+ for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
7805
+ if ( search == 'none' ) {
7806
+ a.push( i );
7807
+ }
7808
+ else { // applied | removed
7809
+ tmp = $.inArray( i, displayFiltered );
7810
+
7811
+ if ((tmp === -1 && search == 'removed') ||
7812
+ (tmp >= 0 && search == 'applied') )
7813
+ {
7814
+ a.push( i );
7815
+ }
7816
+ }
7817
+ }
7818
+ }
7819
+
7820
+ return a;
7821
+ };
7822
+
7823
+
7824
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7825
+ * Rows
7826
+ *
7827
+ * {} - no selector - use all available rows
7828
+ * {integer} - row aoData index
7829
+ * {node} - TR node
7830
+ * {string} - jQuery selector to apply to the TR elements
7831
+ * {array} - jQuery array of nodes, or simply an array of TR nodes
7832
+ *
7833
+ */
7834
+
7835
+
7836
+ var __row_selector = function ( settings, selector, opts )
7837
+ {
7838
+ var rows;
7839
+ var run = function ( sel ) {
7840
+ var selInt = _intVal( sel );
7841
+ var i, ien;
7842
+
7843
+ // Short cut - selector is a number and no options provided (default is
7844
+ // all records, so no need to check if the index is in there, since it
7845
+ // must be - dev error if the index doesn't exist).
7846
+ if ( selInt !== null && ! opts ) {
7847
+ return [ selInt ];
7848
+ }
7849
+
7850
+ if ( ! rows ) {
7851
+ rows = _selector_row_indexes( settings, opts );
7852
+ }
7853
+
7854
+ if ( selInt !== null && $.inArray( selInt, rows ) !== -1 ) {
7855
+ // Selector - integer
7856
+ return [ selInt ];
7857
+ }
7858
+ else if ( sel === null || sel === undefined || sel === '' ) {
7859
+ // Selector - none
7860
+ return rows;
7861
+ }
7862
+
7863
+ // Selector - function
7864
+ if ( typeof sel === 'function' ) {
7865
+ return $.map( rows, function (idx) {
7866
+ var row = settings.aoData[ idx ];
7867
+ return sel( idx, row._aData, row.nTr ) ? idx : null;
7868
+ } );
7869
+ }
7870
+
7871
+ // Get nodes in the order from the `rows` array with null values removed
7872
+ var nodes = _removeEmpty(
7873
+ _pluck_order( settings.aoData, rows, 'nTr' )
7874
+ );
7875
+
7876
+ // Selector - node
7877
+ if ( sel.nodeName ) {
7878
+ if ( sel._DT_RowIndex !== undefined ) {
7879
+ return [ sel._DT_RowIndex ]; // Property added by DT for fast lookup
7880
+ }
7881
+ else if ( sel._DT_CellIndex ) {
7882
+ return [ sel._DT_CellIndex.row ];
7883
+ }
7884
+ else {
7885
+ var host = $(sel).closest('*[data-dt-row]');
7886
+ return host.length ?
7887
+ [ host.data('dt-row') ] :
7888
+ [];
7889
+ }
7890
+ }
7891
+
7892
+ // ID selector. Want to always be able to select rows by id, regardless
7893
+ // of if the tr element has been created or not, so can't rely upon
7894
+ // jQuery here - hence a custom implementation. This does not match
7895
+ // Sizzle's fast selector or HTML4 - in HTML5 the ID can be anything,
7896
+ // but to select it using a CSS selector engine (like Sizzle or
7897
+ // querySelect) it would need to need to be escaped for some characters.
7898
+ // WFDataTables simplifies this for row selectors since you can select
7899
+ // only a row. A # indicates an id any anything that follows is the id -
7900
+ // unescaped.
7901
+ if ( typeof sel === 'string' && sel.charAt(0) === '#' ) {
7902
+ // get row index from id
7903
+ var rowObj = settings.aIds[ sel.replace( /^#/, '' ) ];
7904
+ if ( rowObj !== undefined ) {
7905
+ return [ rowObj.idx ];
7906
+ }
7907
+
7908
+ // need to fall through to jQuery in case there is DOM id that
7909
+ // matches
7910
+ }
7911
+
7912
+ // Selector - jQuery selector string, array of nodes or jQuery object/
7913
+ // As jQuery's .filter() allows jQuery objects to be passed in filter,
7914
+ // it also allows arrays, so this will cope with all three options
7915
+ return $(nodes)
7916
+ .filter( sel )
7917
+ .map( function () {
7918
+ return this._DT_RowIndex;
7919
+ } )
7920
+ .toArray();
7921
+ };
7922
+
7923
+ return _selector_run( 'row', selector, run, settings, opts );
7924
+ };
7925
+
7926
+
7927
+ _api_register( 'rows()', function ( selector, opts ) {
7928
+ // argument shifting
7929
+ if ( selector === undefined ) {
7930
+ selector = '';
7931
+ }
7932
+ else if ( $.isPlainObject( selector ) ) {
7933
+ opts = selector;
7934
+ selector = '';
7935
+ }
7936
+
7937
+ opts = _selector_opts( opts );
7938
+
7939
+ var inst = this.iterator( 'table', function ( settings ) {
7940
+ return __row_selector( settings, selector, opts );
7941
+ }, 1 );
7942
+
7943
+ // Want argument shifting here and in __row_selector?
7944
+ inst.selector.rows = selector;
7945
+ inst.selector.opts = opts;
7946
+
7947
+ return inst;
7948
+ } );
7949
+
7950
+ _api_register( 'rows().nodes()', function () {
7951
+ return this.iterator( 'row', function ( settings, row ) {
7952
+ return settings.aoData[ row ].nTr || undefined;
7953
+ }, 1 );
7954
+ } );
7955
+
7956
+ _api_register( 'rows().data()', function () {
7957
+ return this.iterator( true, 'rows', function ( settings, rows ) {
7958
+ return _pluck_order( settings.aoData, rows, '_aData' );
7959
+ }, 1 );
7960
+ } );
7961
+
7962
+ _api_registerPlural( 'rows().cache()', 'row().cache()', function ( type ) {
7963
+ return this.iterator( 'row', function ( settings, row ) {
7964
+ var r = settings.aoData[ row ];
7965
+ return type === 'search' ? r._aFilterData : r._aSortData;
7966
+ }, 1 );
7967
+ } );
7968
+
7969
+ _api_registerPlural( 'rows().invalidate()', 'row().invalidate()', function ( src ) {
7970
+ return this.iterator( 'row', function ( settings, row ) {
7971
+ _fnInvalidate( settings, row, src );
7972
+ } );
7973
+ } );
7974
+
7975
+ _api_registerPlural( 'rows().indexes()', 'row().index()', function () {
7976
+ return this.iterator( 'row', function ( settings, row ) {
7977
+ return row;
7978
+ }, 1 );
7979
+ } );
7980
+
7981
+ _api_registerPlural( 'rows().ids()', 'row().id()', function ( hash ) {
7982
+ var a = [];
7983
+ var context = this.context;
7984
+
7985
+ // `iterator` will drop undefined values, but in this case we want them
7986
+ for ( var i=0, ien=context.length ; i<ien ; i++ ) {
7987
+ for ( var j=0, jen=this[i].length ; j<jen ; j++ ) {
7988
+ var id = context[i].rowIdFn( context[i].aoData[ this[i][j] ]._aData );
7989
+ a.push( (hash === true ? '#' : '' )+ id );
7990
+ }
7991
+ }
7992
+
7993
+ return new _Api( context, a );
7994
+ } );
7995
+
7996
+ _api_registerPlural( 'rows().remove()', 'row().remove()', function () {
7997
+ var that = this;
7998
+
7999
+ this.iterator( 'row', function ( settings, row, thatIdx ) {
8000
+ var data = settings.aoData;
8001
+ var rowData = data[ row ];
8002
+ var i, ien, j, jen;
8003
+ var loopRow, loopCells;
8004
+
8005
+ data.splice( row, 1 );
8006
+
8007
+ // Update the cached indexes
8008
+ for ( i=0, ien=data.length ; i<ien ; i++ ) {
8009
+ loopRow = data[i];
8010
+ loopCells = loopRow.anCells;
8011
+
8012
+ // Rows
8013
+ if ( loopRow.nTr !== null ) {
8014
+ loopRow.nTr._DT_RowIndex = i;
8015
+ }
8016
+
8017
+ // Cells
8018
+ if ( loopCells !== null ) {
8019
+ for ( j=0, jen=loopCells.length ; j<jen ; j++ ) {
8020
+ loopCells[j]._DT_CellIndex.row = i;
8021
+ }
8022
+ }
8023
+ }
8024
+
8025
+ // Delete from the display arrays
8026
+ _fnDeleteIndex( settings.aiDisplayMaster, row );
8027
+ _fnDeleteIndex( settings.aiDisplay, row );
8028
+ _fnDeleteIndex( that[ thatIdx ], row, false ); // maintain local indexes
8029
+
8030
+ // Check for an 'overflow' they case for displaying the table
8031
+ _fnLengthOverflow( settings );
8032
+
8033
+ // Remove the row's ID reference if there is one
8034
+ var id = settings.rowIdFn( rowData._aData );
8035
+ if ( id !== undefined ) {
8036
+ delete settings.aIds[ id ];
8037
+ }
8038
+ } );
8039
+
8040
+ this.iterator( 'table', function ( settings ) {
8041
+ for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
8042
+ settings.aoData[i].idx = i;
8043
+ }
8044
+ } );
8045
+
8046
+ return this;
8047
+ } );
8048
+
8049
+
8050
+ _api_register( 'rows.add()', function ( rows ) {
8051
+ var newRows = this.iterator( 'table', function ( settings ) {
8052
+ var row, i, ien;
8053
+ var out = [];
8054
+
8055
+ for ( i=0, ien=rows.length ; i<ien ; i++ ) {
8056
+ row = rows[i];
8057
+
8058
+ if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) {
8059
+ out.push( _fnAddTr( settings, row )[0] );
8060
+ }
8061
+ else {
8062
+ out.push( _fnAddData( settings, row ) );
8063
+ }
8064
+ }
8065
+
8066
+ return out;
8067
+ }, 1 );
8068
+
8069
+ // Return an Api.rows() extended instance, so rows().nodes() etc can be used
8070
+ var modRows = this.rows( -1 );
8071
+ modRows.pop();
8072
+ $.merge( modRows, newRows );
8073
+
8074
+ return modRows;
8075
+ } );
8076
+
8077
+
8078
+
8079
+
8080
+
8081
+ /**
8082
+ *
8083
+ */
8084
+ _api_register( 'row()', function ( selector, opts ) {
8085
+ return _selector_first( this.rows( selector, opts ) );
8086
+ } );
8087
+
8088
+
8089
+ _api_register( 'row().data()', function ( data ) {
8090
+ var ctx = this.context;
8091
+
8092
+ if ( data === undefined ) {
8093
+ // Get
8094
+ return ctx.length && this.length ?
8095
+ ctx[0].aoData[ this[0] ]._aData :
8096
+ undefined;
8097
+ }
8098
+
8099
+ // Set
8100
+ ctx[0].aoData[ this[0] ]._aData = data;
8101
+
8102
+ // Automatically invalidate
8103
+ _fnInvalidate( ctx[0], this[0], 'data' );
8104
+
8105
+ return this;
8106
+ } );
8107
+
8108
+
8109
+ _api_register( 'row().node()', function () {
8110
+ var ctx = this.context;
8111
+
8112
+ return ctx.length && this.length ?
8113
+ ctx[0].aoData[ this[0] ].nTr || null :
8114
+ null;
8115
+ } );
8116
+
8117
+
8118
+ _api_register( 'row.add()', function ( row ) {
8119
+ // Allow a jQuery object to be passed in - only a single row is added from
8120
+ // it though - the first element in the set
8121
+ if ( row instanceof $ && row.length ) {
8122
+ row = row[0];
8123
+ }
8124
+
8125
+ var rows = this.iterator( 'table', function ( settings ) {
8126
+ if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) {
8127
+ return _fnAddTr( settings, row )[0];
8128
+ }
8129
+ return _fnAddData( settings, row );
8130
+ } );
8131
+
8132
+ // Return an Api.rows() extended instance, with the newly added row selected
8133
+ return this.row( rows[0] );
8134
+ } );
8135
+
8136
+
8137
+
8138
+ var __details_add = function ( ctx, row, data, klass )
8139
+ {
8140
+ // Convert to array of TR elements
8141
+ var rows = [];
8142
+ var addRow = function ( r, k ) {
8143
+ // Recursion to allow for arrays of jQuery objects
8144
+ if ( $.isArray( r ) || r instanceof $ ) {
8145
+ for ( var i=0, ien=r.length ; i<ien ; i++ ) {
8146
+ addRow( r[i], k );
8147
+ }
8148
+ return;
8149
+ }
8150
+
8151
+ // If we get a TR element, then just add it directly - up to the dev
8152
+ // to add the correct number of columns etc
8153
+ if ( r.nodeName && r.nodeName.toLowerCase() === 'tr' ) {
8154
+ rows.push( r );
8155
+ }
8156
+ else {
8157
+ // Otherwise create a row with a wrapper
8158
+ var created = $('<tr><td/></tr>').addClass( k );
8159
+ $('td', created)
8160
+ .addClass( k )
8161
+ .html( r )
8162
+ [0].colSpan = _fnVisbleColumns( ctx );
8163
+
8164
+ rows.push( created[0] );
8165
+ }
8166
+ };
8167
+
8168
+ addRow( data, klass );
8169
+
8170
+ if ( row._details ) {
8171
+ row._details.detach();
8172
+ }
8173
+
8174
+ row._details = $(rows);
8175
+
8176
+ // If the children were already shown, that state should be retained
8177
+ if ( row._detailsShow ) {
8178
+ row._details.insertAfter( row.nTr );
8179
+ }
8180
+ };
8181
+
8182
+
8183
+ var __details_remove = function ( api, idx )
8184
+ {
8185
+ var ctx = api.context;
8186
+
8187
+ if ( ctx.length ) {
8188
+ var row = ctx[0].aoData[ idx !== undefined ? idx : api[0] ];
8189
+
8190
+ if ( row && row._details ) {
8191
+ row._details.remove();
8192
+
8193
+ row._detailsShow = undefined;
8194
+ row._details = undefined;
8195
+ }
8196
+ }
8197
+ };
8198
+
8199
+
8200
+ var __details_display = function ( api, show ) {
8201
+ var ctx = api.context;
8202
+
8203
+ if ( ctx.length && api.length ) {
8204
+ var row = ctx[0].aoData[ api[0] ];
8205
+
8206
+ if ( row._details ) {
8207
+ row._detailsShow = show;
8208
+
8209
+ if ( show ) {
8210
+ row._details.insertAfter( row.nTr );
8211
+ }
8212
+ else {
8213
+ row._details.detach();
8214
+ }
8215
+
8216
+ __details_events( ctx[0] );
8217
+ }
8218
+ }
8219
+ };
8220
+
8221
+
8222
+ var __details_events = function ( settings )
8223
+ {
8224
+ var api = new _Api( settings );
8225
+ var namespace = '.dt.DT_details';
8226
+ var drawEvent = 'draw'+namespace;
8227
+ var colvisEvent = 'column-visibility'+namespace;
8228
+ var destroyEvent = 'destroy'+namespace;
8229
+ var data = settings.aoData;
8230
+
8231
+ api.off( drawEvent +' '+ colvisEvent +' '+ destroyEvent );
8232
+
8233
+ if ( _pluck( data, '_details' ).length > 0 ) {
8234
+ // On each draw, insert the required elements into the document
8235
+ api.on( drawEvent, function ( e, ctx ) {
8236
+ if ( settings !== ctx ) {
8237
+ return;
8238
+ }
8239
+
8240
+ api.rows( {page:'current'} ).eq(0).each( function (idx) {
8241
+ // Internal data grab
8242
+ var row = data[ idx ];
8243
+
8244
+ if ( row._detailsShow ) {
8245
+ row._details.insertAfter( row.nTr );
8246
+ }
8247
+ } );
8248
+ } );
8249
+
8250
+ // Column visibility change - update the colspan
8251
+ api.on( colvisEvent, function ( e, ctx, idx, vis ) {
8252
+ if ( settings !== ctx ) {
8253
+ return;
8254
+ }
8255
+
8256
+ // Update the colspan for the details rows (note, only if it already has
8257
+ // a colspan)
8258
+ var row, visible = _fnVisbleColumns( ctx );
8259
+
8260
+ for ( var i=0, ien=data.length ; i<ien ; i++ ) {
8261
+ row = data[i];
8262
+
8263
+ if ( row._details ) {
8264
+ row._details.children('td[colspan]').attr('colspan', visible );
8265
+ }
8266
+ }
8267
+ } );
8268
+
8269
+ // Table destroyed - nuke any child rows
8270
+ api.on( destroyEvent, function ( e, ctx ) {
8271
+ if ( settings !== ctx ) {
8272
+ return;
8273
+ }
8274
+
8275
+ for ( var i=0, ien=data.length ; i<ien ; i++ ) {
8276
+ if ( data[i]._details ) {
8277
+ __details_remove( api, i );
8278
+ }
8279
+ }
8280
+ } );
8281
+ }
8282
+ };
8283
+
8284
+ // Strings for the method names to help minification
8285
+ var _emp = '';
8286
+ var _child_obj = _emp+'row().child';
8287
+ var _child_mth = _child_obj+'()';
8288
+
8289
+ // data can be:
8290
+ // tr
8291
+ // string
8292
+ // jQuery or array of any of the above
8293
+ _api_register( _child_mth, function ( data, klass ) {
8294
+ var ctx = this.context;
8295
+
8296
+ if ( data === undefined ) {
8297
+ // get
8298
+ return ctx.length && this.length ?
8299
+ ctx[0].aoData[ this[0] ]._details :
8300
+ undefined;
8301
+ }
8302
+ else if ( data === true ) {
8303
+ // show
8304
+ this.child.show();
8305
+ }
8306
+ else if ( data === false ) {
8307
+ // remove
8308
+ __details_remove( this );
8309
+ }
8310
+ else if ( ctx.length && this.length ) {
8311
+ // set
8312
+ __details_add( ctx[0], ctx[0].aoData[ this[0] ], data, klass );
8313
+ }
8314
+
8315
+ return this;
8316
+ } );
8317
+
8318
+
8319
+ _api_register( [
8320
+ _child_obj+'.show()',
8321
+ _child_mth+'.show()' // only when `child()` was called with parameters (without
8322
+ ], function ( show ) { // it returns an object and this method is not executed)
8323
+ __details_display( this, true );
8324
+ return this;
8325
+ } );
8326
+
8327
+
8328
+ _api_register( [
8329
+ _child_obj+'.hide()',
8330
+ _child_mth+'.hide()' // only when `child()` was called with parameters (without
8331
+ ], function () { // it returns an object and this method is not executed)
8332
+ __details_display( this, false );
8333
+ return this;
8334
+ } );
8335
+
8336
+
8337
+ _api_register( [
8338
+ _child_obj+'.remove()',
8339
+ _child_mth+'.remove()' // only when `child()` was called with parameters (without
8340
+ ], function () { // it returns an object and this method is not executed)
8341
+ __details_remove( this );
8342
+ return this;
8343
+ } );
8344
+
8345
+
8346
+ _api_register( _child_obj+'.isShown()', function () {
8347
+ var ctx = this.context;
8348
+
8349
+ if ( ctx.length && this.length ) {
8350
+ // _detailsShown as false or undefined will fall through to return false
8351
+ return ctx[0].aoData[ this[0] ]._detailsShow || false;
8352
+ }
8353
+ return false;
8354
+ } );
8355
+
8356
+
8357
+
8358
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
8359
+ * Columns
8360
+ *
8361
+ * {integer} - column index (>=0 count from left, <0 count from right)
8362
+ * "{integer}:visIdx" - visible column index (i.e. translate to column index) (>=0 count from left, <0 count from right)
8363
+ * "{integer}:visible" - alias for {integer}:visIdx (>=0 count from left, <0 count from right)
8364
+ * "{string}:name" - column name
8365
+ * "{string}" - jQuery selector on column header nodes
8366
+ *
8367
+ */
8368
+
8369
+ // can be an array of these items, comma separated list, or an array of comma
8370
+ // separated lists
8371
+
8372
+ var __re_column_selector = /^([^:]+):(name|visIdx|visible)$/;
8373
+
8374
+
8375
+ // r1 and r2 are redundant - but it means that the parameters match for the
8376
+ // iterator callback in columns().data()
8377
+ var __columnData = function ( settings, column, r1, r2, rows ) {
8378
+ var a = [];
8379
+ for ( var row=0, ien=rows.length ; row<ien ; row++ ) {
8380
+ a.push( _fnGetCellData( settings, rows[row], column ) );
8381
+ }
8382
+ return a;
8383
+ };
8384
+
8385
+
8386
+ var __column_selector = function ( settings, selector, opts )
8387
+ {
8388
+ var
8389
+ columns = settings.aoColumns,
8390
+ names = _pluck( columns, 'sName' ),
8391
+ nodes = _pluck( columns, 'nTh' );
8392
+
8393
+ var run = function ( s ) {
8394
+ var selInt = _intVal( s );
8395
+
8396
+ // Selector - all
8397
+ if ( s === '' ) {
8398
+ return _range( columns.length );
8399
+ }
8400
+
8401
+ // Selector - index
8402
+ if ( selInt !== null ) {
8403
+ return [ selInt >= 0 ?
8404
+ selInt : // Count from left
8405
+ columns.length + selInt // Count from right (+ because its a negative value)
8406
+ ];
8407
+ }
8408
+
8409
+ // Selector = function
8410
+ if ( typeof s === 'function' ) {
8411
+ var rows = _selector_row_indexes( settings, opts );
8412
+
8413
+ return $.map( columns, function (col, idx) {
8414
+ return s(
8415
+ idx,
8416
+ __columnData( settings, idx, 0, 0, rows ),
8417
+ nodes[ idx ]
8418
+ ) ? idx : null;
8419
+ } );
8420
+ }
8421
+
8422
+ // jQuery or string selector
8423
+ var match = typeof s === 'string' ?
8424
+ s.match( __re_column_selector ) :
8425
+ '';
8426
+
8427
+ if ( match ) {
8428
+ switch( match[2] ) {
8429
+ case 'visIdx':
8430
+ case 'visible':
8431
+ var idx = parseInt( match[1], 10 );
8432
+ // Visible index given, convert to column index
8433
+ if ( idx < 0 ) {
8434
+ // Counting from the right
8435
+ var visColumns = $.map( columns, function (col,i) {
8436
+ return col.bVisible ? i : null;
8437
+ } );
8438
+ return [ visColumns[ visColumns.length + idx ] ];
8439
+ }
8440
+ // Counting from the left
8441
+ return [ _fnVisibleToColumnIndex( settings, idx ) ];
8442
+
8443
+ case 'name':
8444
+ // match by name. `names` is column index complete and in order
8445
+ return $.map( names, function (name, i) {
8446
+ return name === match[1] ? i : null;
8447
+ } );
8448
+
8449
+ default:
8450
+ return [];
8451
+ }
8452
+ }
8453
+
8454
+ // Cell in the table body
8455
+ if ( s.nodeName && s._DT_CellIndex ) {
8456
+ return [ s._DT_CellIndex.column ];
8457
+ }
8458
+
8459
+ // jQuery selector on the TH elements for the columns
8460
+ var jqResult = $( nodes )
8461
+ .filter( s )
8462
+ .map( function () {
8463
+ return $.inArray( this, nodes ); // `nodes` is column index complete and in order
8464
+ } )
8465
+ .toArray();
8466
+
8467
+ if ( jqResult.length || ! s.nodeName ) {
8468
+ return jqResult;
8469
+ }
8470
+
8471
+ // Otherwise a node which might have a `dt-column` data attribute, or be
8472
+ // a child or such an element
8473
+ var host = $(s).closest('*[data-dt-column]');
8474
+ return host.length ?
8475
+ [ host.data('dt-column') ] :
8476
+ [];
8477
+ };
8478
+
8479
+ return _selector_run( 'column', selector, run, settings, opts );
8480
+ };
8481
+
8482
+
8483
+ var __setColumnVis = function ( settings, column, vis ) {
8484
+ var
8485
+ cols = settings.aoColumns,
8486
+ col = cols[ column ],
8487
+ data = settings.aoData,
8488
+ row, cells, i, ien, tr;
8489
+
8490
+ // Get
8491
+ if ( vis === undefined ) {
8492
+ return col.bVisible;
8493
+ }
8494
+
8495
+ // Set
8496
+ // No change
8497
+ if ( col.bVisible === vis ) {
8498
+ return;
8499
+ }
8500
+
8501
+ if ( vis ) {
8502
+ // Insert column
8503
+ // Need to decide if we should use appendChild or insertBefore
8504
+ var insertBefore = $.inArray( true, _pluck(cols, 'bVisible'), column+1 );
8505
+
8506
+ for ( i=0, ien=data.length ; i<ien ; i++ ) {
8507
+ tr = data[i].nTr;
8508
+ cells = data[i].anCells;
8509
+
8510
+ if ( tr ) {
8511
+ // insertBefore can act like appendChild if 2nd arg is null
8512
+ tr.insertBefore( cells[ column ], cells[ insertBefore ] || null );
8513
+ }
8514
+ }
8515
+ }
8516
+ else {
8517
+ // Remove column
8518
+ $( _pluck( settings.aoData, 'anCells', column ) ).detach();
8519
+ }
8520
+
8521
+ // Common actions
8522
+ col.bVisible = vis;
8523
+ _fnDrawHead( settings, settings.aoHeader );
8524
+ _fnDrawHead( settings, settings.aoFooter );
8525
+
8526
+ _fnSaveState( settings );
8527
+ };
8528
+
8529
+
8530
+ _api_register( 'columns()', function ( selector, opts ) {
8531
+ // argument shifting
8532
+ if ( selector === undefined ) {
8533
+ selector = '';
8534
+ }
8535
+ else if ( $.isPlainObject( selector ) ) {
8536
+ opts = selector;
8537
+ selector = '';
8538
+ }
8539
+
8540
+ opts = _selector_opts( opts );
8541
+
8542
+ var inst = this.iterator( 'table', function ( settings ) {
8543
+ return __column_selector( settings, selector, opts );
8544
+ }, 1 );
8545
+
8546
+ // Want argument shifting here and in _row_selector?
8547
+ inst.selector.cols = selector;
8548
+ inst.selector.opts = opts;
8549
+
8550
+ return inst;
8551
+ } );
8552
+
8553
+ _api_registerPlural( 'columns().header()', 'column().header()', function ( selector, opts ) {
8554
+ return this.iterator( 'column', function ( settings, column ) {
8555
+ return settings.aoColumns[column].nTh;
8556
+ }, 1 );
8557
+ } );
8558
+
8559
+ _api_registerPlural( 'columns().footer()', 'column().footer()', function ( selector, opts ) {
8560
+ return this.iterator( 'column', function ( settings, column ) {
8561
+ return settings.aoColumns[column].nTf;
8562
+ }, 1 );
8563
+ } );
8564
+
8565
+ _api_registerPlural( 'columns().data()', 'column().data()', function () {
8566
+ return this.iterator( 'column-rows', __columnData, 1 );
8567
+ } );
8568
+
8569
+ _api_registerPlural( 'columns().dataSrc()', 'column().dataSrc()', function () {
8570
+ return this.iterator( 'column', function ( settings, column ) {
8571
+ return settings.aoColumns[column].mData;
8572
+ }, 1 );
8573
+ } );
8574
+
8575
+ _api_registerPlural( 'columns().cache()', 'column().cache()', function ( type ) {
8576
+ return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
8577
+ return _pluck_order( settings.aoData, rows,
8578
+ type === 'search' ? '_aFilterData' : '_aSortData', column
8579
+ );
8580
+ }, 1 );
8581
+ } );
8582
+
8583
+ _api_registerPlural( 'columns().nodes()', 'column().nodes()', function () {
8584
+ return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
8585
+ return _pluck_order( settings.aoData, rows, 'anCells', column ) ;
8586
+ }, 1 );
8587
+ } );
8588
+
8589
+ _api_registerPlural( 'columns().visible()', 'column().visible()', function ( vis, calc ) {
8590
+ var ret = this.iterator( 'column', function ( settings, column ) {
8591
+ if ( vis === undefined ) {
8592
+ return settings.aoColumns[ column ].bVisible;
8593
+ } // else
8594
+ __setColumnVis( settings, column, vis );
8595
+ } );
8596
+
8597
+ // Group the column visibility changes
8598
+ if ( vis !== undefined ) {
8599
+ // Second loop once the first is done for events
8600
+ this.iterator( 'column', function ( settings, column ) {
8601
+ _fnCallbackFire( settings, null, 'column-visibility', [settings, column, vis, calc] );
8602
+ } );
8603
+
8604
+ if ( calc === undefined || calc ) {
8605
+ this.columns.adjust();
8606
+ }
8607
+ }
8608
+
8609
+ return ret;
8610
+ } );
8611
+
8612
+ _api_registerPlural( 'columns().indexes()', 'column().index()', function ( type ) {
8613
+ return this.iterator( 'column', function ( settings, column ) {
8614
+ return type === 'visible' ?
8615
+ _fnColumnIndexToVisible( settings, column ) :
8616
+ column;
8617
+ }, 1 );
8618
+ } );
8619
+
8620
+ _api_register( 'columns.adjust()', function () {
8621
+ return this.iterator( 'table', function ( settings ) {
8622
+ _fnAdjustColumnSizing( settings );
8623
+ }, 1 );
8624
+ } );
8625
+
8626
+ _api_register( 'column.index()', function ( type, idx ) {
8627
+ if ( this.context.length !== 0 ) {
8628
+ var ctx = this.context[0];
8629
+
8630
+ if ( type === 'fromVisible' || type === 'toData' ) {
8631
+ return _fnVisibleToColumnIndex( ctx, idx );
8632
+ }
8633
+ else if ( type === 'fromData' || type === 'toVisible' ) {
8634
+ return _fnColumnIndexToVisible( ctx, idx );
8635
+ }
8636
+ }
8637
+ } );
8638
+
8639
+ _api_register( 'column()', function ( selector, opts ) {
8640
+ return _selector_first( this.columns( selector, opts ) );
8641
+ } );
8642
+
8643
+
8644
+
8645
+ var __cell_selector = function ( settings, selector, opts )
8646
+ {
8647
+ var data = settings.aoData;
8648
+ var rows = _selector_row_indexes( settings, opts );
8649
+ var cells = _removeEmpty( _pluck_order( data, rows, 'anCells' ) );
8650
+ var allCells = $( [].concat.apply([], cells) );
8651
+ var row;
8652
+ var columns = settings.aoColumns.length;
8653
+ var a, i, ien, j, o, host;
8654
+
8655
+ var run = function ( s ) {
8656
+ var fnSelector = typeof s === 'function';
8657
+
8658
+ if ( s === null || s === undefined || fnSelector ) {
8659
+ // All cells and function selectors
8660
+ a = [];
8661
+
8662
+ for ( i=0, ien=rows.length ; i<ien ; i++ ) {
8663
+ row = rows[i];
8664
+
8665
+ for ( j=0 ; j<columns ; j++ ) {
8666
+ o = {
8667
+ row: row,
8668
+ column: j
8669
+ };
8670
+
8671
+ if ( fnSelector ) {
8672
+ // Selector - function
8673
+ host = data[ row ];
8674
+
8675
+ if ( s( o, _fnGetCellData(settings, row, j), host.anCells ? host.anCells[j] : null ) ) {
8676
+ a.push( o );
8677
+ }
8678
+ }
8679
+ else {
8680
+ // Selector - all
8681
+ a.push( o );
8682
+ }
8683
+ }
8684
+ }
8685
+
8686
+ return a;
8687
+ }
8688
+
8689
+ // Selector - index
8690
+ if ( $.isPlainObject( s ) ) {
8691
+ return [s];
8692
+ }
8693
+
8694
+ // Selector - jQuery filtered cells
8695
+ var jqResult = allCells
8696
+ .filter( s )
8697
+ .map( function (i, el) {
8698
+ return { // use a new object, in case someone changes the values
8699
+ row: el._DT_CellIndex.row,
8700
+ column: el._DT_CellIndex.column
8701
+ };
8702
+ } )
8703
+ .toArray();
8704
+
8705
+ if ( jqResult.length || ! s.nodeName ) {
8706
+ return jqResult;
8707
+ }
8708
+
8709
+ // Otherwise the selector is a node, and there is one last option - the
8710
+ // element might be a child of an element which has dt-row and dt-column
8711
+ // data attributes
8712
+ host = $(s).closest('*[data-dt-row]');
8713
+ return host.length ?
8714
+ [ {
8715
+ row: host.data('dt-row'),
8716
+ column: host.data('dt-column')
8717
+ } ] :
8718
+ [];
8719
+ };
8720
+
8721
+ return _selector_run( 'cell', selector, run, settings, opts );
8722
+ };
8723
+
8724
+
8725
+
8726
+
8727
+ _api_register( 'cells()', function ( rowSelector, columnSelector, opts ) {
8728
+ // Argument shifting
8729
+ if ( $.isPlainObject( rowSelector ) ) {
8730
+ // Indexes
8731
+ if ( rowSelector.row === undefined ) {
8732
+ // Selector options in first parameter
8733
+ opts = rowSelector;
8734
+ rowSelector = null;
8735
+ }
8736
+ else {
8737
+ // Cell index objects in first parameter
8738
+ opts = columnSelector;
8739
+ columnSelector = null;
8740
+ }
8741
+ }
8742
+ if ( $.isPlainObject( columnSelector ) ) {
8743
+ opts = columnSelector;
8744
+ columnSelector = null;
8745
+ }
8746
+
8747
+ // Cell selector
8748
+ if ( columnSelector === null || columnSelector === undefined ) {
8749
+ return this.iterator( 'table', function ( settings ) {
8750
+ return __cell_selector( settings, rowSelector, _selector_opts( opts ) );
8751
+ } );
8752
+ }
8753
+
8754
+ // Row + column selector
8755
+ var columns = this.columns( columnSelector, opts );
8756
+ var rows = this.rows( rowSelector, opts );
8757
+ var a, i, ien, j, jen;
8758
+
8759
+ var cells = this.iterator( 'table', function ( settings, idx ) {
8760
+ a = [];
8761
+
8762
+ for ( i=0, ien=rows[idx].length ; i<ien ; i++ ) {
8763
+ for ( j=0, jen=columns[idx].length ; j<jen ; j++ ) {
8764
+ a.push( {
8765
+ row: rows[idx][i],
8766
+ column: columns[idx][j]
8767
+ } );
8768
+ }
8769
+ }
8770
+
8771
+ return a;
8772
+ }, 1 );
8773
+
8774
+ $.extend( cells.selector, {
8775
+ cols: columnSelector,
8776
+ rows: rowSelector,
8777
+ opts: opts
8778
+ } );
8779
+
8780
+ return cells;
8781
+ } );
8782
+
8783
+
8784
+ _api_registerPlural( 'cells().nodes()', 'cell().node()', function () {
8785
+ return this.iterator( 'cell', function ( settings, row, column ) {
8786
+ var data = settings.aoData[ row ];
8787
+
8788
+ return data && data.anCells ?
8789
+ data.anCells[ column ] :
8790
+ undefined;
8791
+ }, 1 );
8792
+ } );
8793
+
8794
+
8795
+ _api_register( 'cells().data()', function () {
8796
+ return this.iterator( 'cell', function ( settings, row, column ) {
8797
+ return _fnGetCellData( settings, row, column );
8798
+ }, 1 );
8799
+ } );
8800
+
8801
+
8802
+ _api_registerPlural( 'cells().cache()', 'cell().cache()', function ( type ) {
8803
+ type = type === 'search' ? '_aFilterData' : '_aSortData';
8804
+
8805
+ return this.iterator( 'cell', function ( settings, row, column ) {
8806
+ return settings.aoData[ row ][ type ][ column ];
8807
+ }, 1 );
8808
+ } );
8809
+
8810
+
8811
+ _api_registerPlural( 'cells().render()', 'cell().render()', function ( type ) {
8812
+ return this.iterator( 'cell', function ( settings, row, column ) {
8813
+ return _fnGetCellData( settings, row, column, type );
8814
+ }, 1 );
8815
+ } );
8816
+
8817
+
8818
+ _api_registerPlural( 'cells().indexes()', 'cell().index()', function () {
8819
+ return this.iterator( 'cell', function ( settings, row, column ) {
8820
+ return {
8821
+ row: row,
8822
+ column: column,
8823
+ columnVisible: _fnColumnIndexToVisible( settings, column )
8824
+ };
8825
+ }, 1 );
8826
+ } );
8827
+
8828
+
8829
+ _api_registerPlural( 'cells().invalidate()', 'cell().invalidate()', function ( src ) {
8830
+ return this.iterator( 'cell', function ( settings, row, column ) {
8831
+ _fnInvalidate( settings, row, src, column );
8832
+ } );
8833
+ } );
8834
+
8835
+
8836
+
8837
+ _api_register( 'cell()', function ( rowSelector, columnSelector, opts ) {
8838
+ return _selector_first( this.cells( rowSelector, columnSelector, opts ) );
8839
+ } );
8840
+
8841
+
8842
+ _api_register( 'cell().data()', function ( data ) {
8843
+ var ctx = this.context;
8844
+ var cell = this[0];
8845
+
8846
+ if ( data === undefined ) {
8847
+ // Get
8848
+ return ctx.length && cell.length ?
8849
+ _fnGetCellData( ctx[0], cell[0].row, cell[0].column ) :
8850
+ undefined;
8851
+ }
8852
+
8853
+ // Set
8854
+ _fnSetCellData( ctx[0], cell[0].row, cell[0].column, data );
8855
+ _fnInvalidate( ctx[0], cell[0].row, 'data', cell[0].column );
8856
+
8857
+ return this;
8858
+ } );
8859
+
8860
+
8861
+
8862
+ /**
8863
+ * Get current ordering (sorting) that has been applied to the table.
8864
+ *
8865
+ * @returns {array} 2D array containing the sorting information for the first
8866
+ * table in the current context. Each element in the parent array represents
8867
+ * a column being sorted upon (i.e. multi-sorting with two columns would have
8868
+ * 2 inner arrays). The inner arrays may have 2 or 3 elements. The first is
8869
+ * the column index that the sorting condition applies to, the second is the
8870
+ * direction of the sort (`desc` or `asc`) and, optionally, the third is the
8871
+ * index of the sorting order from the `column.sorting` initialisation array.
8872
+ *//**
8873
+ * Set the ordering for the table.
8874
+ *
8875
+ * @param {integer} order Column index to sort upon.
8876
+ * @param {string} direction Direction of the sort to be applied (`asc` or `desc`)
8877
+ * @returns {WFDataTables.Api} this
8878
+ *//**
8879
+ * Set the ordering for the table.
8880
+ *
8881
+ * @param {array} order 1D array of sorting information to be applied.
8882
+ * @param {array} [...] Optional additional sorting conditions
8883
+ * @returns {WFDataTables.Api} this
8884
+ *//**
8885
+ * Set the ordering for the table.
8886
+ *
8887
+ * @param {array} order 2D array of sorting information to be applied.
8888
+ * @returns {WFDataTables.Api} this
8889
+ */
8890
+ _api_register( 'order()', function ( order, dir ) {
8891
+ var ctx = this.context;
8892
+
8893
+ if ( order === undefined ) {
8894
+ // get
8895
+ return ctx.length !== 0 ?
8896
+ ctx[0].aaSorting :
8897
+ undefined;
8898
+ }
8899
+
8900
+ // set
8901
+ if ( typeof order === 'number' ) {
8902
+ // Simple column / direction passed in
8903
+ order = [ [ order, dir ] ];
8904
+ }
8905
+ else if ( order.length && ! $.isArray( order[0] ) ) {
8906
+ // Arguments passed in (list of 1D arrays)
8907
+ order = Array.prototype.slice.call( arguments );
8908
+ }
8909
+ // otherwise a 2D array was passed in
8910
+
8911
+ return this.iterator( 'table', function ( settings ) {
8912
+ settings.aaSorting = order.slice();
8913
+ } );
8914
+ } );
8915
+
8916
+
8917
+ /**
8918
+ * Attach a sort listener to an element for a given column
8919
+ *
8920
+ * @param {node|jQuery|string} node Identifier for the element(s) to attach the
8921
+ * listener to. This can take the form of a single DOM node, a jQuery
8922
+ * collection of nodes or a jQuery selector which will identify the node(s).
8923
+ * @param {integer} column the column that a click on this node will sort on
8924
+ * @param {function} [callback] callback function when sort is run
8925
+ * @returns {WFDataTables.Api} this
8926
+ */
8927
+ _api_register( 'order.listener()', function ( node, column, callback ) {
8928
+ return this.iterator( 'table', function ( settings ) {
8929
+ _fnSortAttachListener( settings, node, column, callback );
8930
+ } );
8931
+ } );
8932
+
8933
+
8934
+ _api_register( 'order.fixed()', function ( set ) {
8935
+ if ( ! set ) {
8936
+ var ctx = this.context;
8937
+ var fixed = ctx.length ?
8938
+ ctx[0].aaSortingFixed :
8939
+ undefined;
8940
+
8941
+ return $.isArray( fixed ) ?
8942
+ { pre: fixed } :
8943
+ fixed;
8944
+ }
8945
+
8946
+ return this.iterator( 'table', function ( settings ) {
8947
+ settings.aaSortingFixed = $.extend( true, {}, set );
8948
+ } );
8949
+ } );
8950
+
8951
+
8952
+ // Order by the selected column(s)
8953
+ _api_register( [
8954
+ 'columns().order()',
8955
+ 'column().order()'
8956
+ ], function ( dir ) {
8957
+ var that = this;
8958
+
8959
+ return this.iterator( 'table', function ( settings, i ) {
8960
+ var sort = [];
8961
+
8962
+ $.each( that[i], function (j, col) {
8963
+ sort.push( [ col, dir ] );
8964
+ } );
8965
+
8966
+ settings.aaSorting = sort;
8967
+ } );
8968
+ } );
8969
+
8970
+
8971
+
8972
+ _api_register( 'search()', function ( input, regex, smart, caseInsen ) {
8973
+ var ctx = this.context;
8974
+
8975
+ if ( input === undefined ) {
8976
+ // get
8977
+ return ctx.length !== 0 ?
8978
+ ctx[0].oPreviousSearch.sSearch :
8979
+ undefined;
8980
+ }
8981
+
8982
+ // set
8983
+ return this.iterator( 'table', function ( settings ) {
8984
+ if ( ! settings.oFeatures.bFilter ) {
8985
+ return;
8986
+ }
8987
+
8988
+ _fnFilterComplete( settings, $.extend( {}, settings.oPreviousSearch, {
8989
+ "sSearch": input+"",
8990
+ "bRegex": regex === null ? false : regex,
8991
+ "bSmart": smart === null ? true : smart,
8992
+ "bCaseInsensitive": caseInsen === null ? true : caseInsen
8993
+ } ), 1 );
8994
+ } );
8995
+ } );
8996
+
8997
+
8998
+ _api_registerPlural(
8999
+ 'columns().search()',
9000
+ 'column().search()',
9001
+ function ( input, regex, smart, caseInsen ) {
9002
+ return this.iterator( 'column', function ( settings, column ) {
9003
+ var preSearch = settings.aoPreSearchCols;
9004
+
9005
+ if ( input === undefined ) {
9006
+ // get
9007
+ return preSearch[ column ].sSearch;
9008
+ }
9009
+
9010
+ // set
9011
+ if ( ! settings.oFeatures.bFilter ) {
9012
+ return;
9013
+ }
9014
+
9015
+ $.extend( preSearch[ column ], {
9016
+ "sSearch": input+"",
9017
+ "bRegex": regex === null ? false : regex,
9018
+ "bSmart": smart === null ? true : smart,
9019
+ "bCaseInsensitive": caseInsen === null ? true : caseInsen
9020
+ } );
9021
+
9022
+ _fnFilterComplete( settings, settings.oPreviousSearch, 1 );
9023
+ } );
9024
+ }
9025
+ );
9026
+
9027
+ /*
9028
+ * State API methods
9029
+ */
9030
+
9031
+ _api_register( 'state()', function () {
9032
+ return this.context.length ?
9033
+ this.context[0].oSavedState :
9034
+ null;
9035
+ } );
9036
+
9037
+
9038
+ _api_register( 'state.clear()', function () {
9039
+ return this.iterator( 'table', function ( settings ) {
9040
+ // Save an empty object
9041
+ settings.fnStateSaveCallback.call( settings.oInstance, settings, {} );
9042
+ } );
9043
+ } );
9044
+
9045
+
9046
+ _api_register( 'state.loaded()', function () {
9047
+ return this.context.length ?
9048
+ this.context[0].oLoadedState :
9049
+ null;
9050
+ } );
9051
+
9052
+
9053
+ _api_register( 'state.save()', function () {
9054
+ return this.iterator( 'table', function ( settings ) {
9055
+ _fnSaveState( settings );
9056
+ } );
9057
+ } );
9058
+
9059
+
9060
+
9061
+ /**
9062
+ * Provide a common method for plug-ins to check the version of WFDataTables being
9063
+ * used, in order to ensure compatibility.
9064
+ *
9065
+ * @param {string} version Version string to check for, in the format "X.Y.Z".
9066
+ * Note that the formats "X" and "X.Y" are also acceptable.
9067
+ * @returns {boolean} true if this version of WFDataTables is greater or equal to
9068
+ * the required version, or false if this version of DataTales is not
9069
+ * suitable
9070
+ * @static
9071
+ * @dtopt API-Static
9072
+ *
9073
+ * @example
9074
+ * alert( $.fn.wfDataTable.versionCheck( '1.9.0' ) );
9075
+ */
9076
+ WFDataTable.versionCheck = WFDataTable.fnVersionCheck = function( version )
9077
+ {
9078
+ var aThis = WFDataTable.version.split('.');
9079
+ var aThat = version.split('.');
9080
+ var iThis, iThat;
9081
+
9082
+ for ( var i=0, iLen=aThat.length ; i<iLen ; i++ ) {
9083
+ iThis = parseInt( aThis[i], 10 ) || 0;
9084
+ iThat = parseInt( aThat[i], 10 ) || 0;
9085
+
9086
+ // Parts are the same, keep comparing
9087
+ if (iThis === iThat) {
9088
+ continue;
9089
+ }
9090
+
9091
+ // Parts are different, return immediately
9092
+ return iThis > iThat;
9093
+ }
9094
+
9095
+ return true;
9096
+ };
9097
+
9098
+
9099
+ /**
9100
+ * Check if a `<table>` node is a WFDataTable table already or not.
9101
+ *
9102
+ * @param {node|jquery|string} table Table node, jQuery object or jQuery
9103
+ * selector for the table to test. Note that if more than more than one
9104
+ * table is passed on, only the first will be checked
9105
+ * @returns {boolean} true the table given is a WFDataTable, or false otherwise
9106
+ * @static
9107
+ * @dtopt API-Static
9108
+ *
9109
+ * @example
9110
+ * if ( ! $.fn.WFDataTable.isWFDataTable( '#example' ) ) {
9111
+ * $('#example').wfDataTable();
9112
+ * }
9113
+ */
9114
+ WFDataTable.isWFDataTable = WFDataTable.fnIsWFDataTable = function ( table )
9115
+ {
9116
+ var t = $(table).get(0);
9117
+ var is = false;
9118
+
9119
+ if ( table instanceof WFDataTable.Api ) {
9120
+ return true;
9121
+ }
9122
+
9123
+ $.each( WFDataTable.settings, function (i, o) {
9124
+ var head = o.nScrollHead ? $('table', o.nScrollHead)[0] : null;
9125
+ var foot = o.nScrollFoot ? $('table', o.nScrollFoot)[0] : null;
9126
+
9127
+ if ( o.nTable === t || head === t || foot === t ) {
9128
+ is = true;
9129
+ }
9130
+ } );
9131
+
9132
+ return is;
9133
+ };
9134
+
9135
+
9136
+ /**
9137
+ * Get all WFDataTable tables that have been initialised - optionally you can
9138
+ * select to get only currently visible tables.
9139
+ *
9140
+ * @param {boolean} [visible=false] Flag to indicate if you want all (default)
9141
+ * or visible tables only.
9142
+ * @returns {array} Array of `table` nodes (not WFDataTable instances) which are
9143
+ * WFDataTables
9144
+ * @static
9145
+ * @dtopt API-Static
9146
+ *
9147
+ * @example
9148
+ * $.each( $.fn.wfDataTable.tables(true), function () {
9149
+ * $(table).WFDataTable().columns.adjust();
9150
+ * } );
9151
+ */
9152
+ WFDataTable.tables = WFDataTable.fnTables = function ( visible )
9153
+ {
9154
+ var api = false;
9155
+
9156
+ if ( $.isPlainObject( visible ) ) {
9157
+ api = visible.api;
9158
+ visible = visible.visible;
9159
+ }
9160
+
9161
+ var a = $.map( WFDataTable.settings, function (o) {
9162
+ if ( !visible || (visible && $(o.nTable).is(':visible')) ) {
9163
+ return o.nTable;
9164
+ }
9165
+ } );
9166
+
9167
+ return api ?
9168
+ new _Api( a ) :
9169
+ a;
9170
+ };
9171
+
9172
+
9173
+ /**
9174
+ * Convert from camel case parameters to Hungarian notation. This is made public
9175
+ * for the extensions to provide the same ability as WFDataTables core to accept
9176
+ * either the 1.9 style Hungarian notation, or the 1.10+ style camelCase
9177
+ * parameters.
9178
+ *
9179
+ * @param {object} src The model object which holds all parameters that can be
9180
+ * mapped.
9181
+ * @param {object} user The object to convert from camel case to Hungarian.
9182
+ * @param {boolean} force When set to `true`, properties which already have a
9183
+ * Hungarian value in the `user` object will be overwritten. Otherwise they
9184
+ * won't be.
9185
+ */
9186
+ WFDataTable.camelToHungarian = _fnCamelToHungarian;
9187
+
9188
+
9189
+
9190
+ /**
9191
+ *
9192
+ */
9193
+ _api_register( '$()', function ( selector, opts ) {
9194
+ var
9195
+ rows = this.rows( opts ).nodes(), // Get all rows
9196
+ jqRows = $(rows);
9197
+
9198
+ return $( [].concat(
9199
+ jqRows.filter( selector ).toArray(),
9200
+ jqRows.find( selector ).toArray()
9201
+ ) );
9202
+ } );
9203
+
9204
+
9205
+ // jQuery functions to operate on the tables
9206
+ $.each( [ 'on', 'one', 'off' ], function (i, key) {
9207
+ _api_register( key+'()', function ( /* event, handler */ ) {
9208
+ var args = Array.prototype.slice.call(arguments);
9209
+
9210
+ // Add the `dt` namespace automatically if it isn't already present
9211
+ args[0] = $.map( args[0].split( /\s/ ), function ( e ) {
9212
+ return ! e.match(/\.dt\b/) ?
9213
+ e+'.dt' :
9214
+ e;
9215
+ } ).join( ' ' );
9216
+
9217
+ var inst = $( this.tables().nodes() );
9218
+ inst[key].apply( inst, args );
9219
+ return this;
9220
+ } );
9221
+ } );
9222
+
9223
+
9224
+ _api_register( 'clear()', function () {
9225
+ return this.iterator( 'table', function ( settings ) {
9226
+ _fnClearTable( settings );
9227
+ } );
9228
+ } );
9229
+
9230
+
9231
+ _api_register( 'settings()', function () {
9232
+ return new _Api( this.context, this.context );
9233
+ } );
9234
+
9235
+
9236
+ _api_register( 'init()', function () {
9237
+ var ctx = this.context;
9238
+ return ctx.length ? ctx[0].oInit : null;
9239
+ } );
9240
+
9241
+
9242
+ _api_register( 'data()', function () {
9243
+ return this.iterator( 'table', function ( settings ) {
9244
+ return _pluck( settings.aoData, '_aData' );
9245
+ } ).flatten();
9246
+ } );
9247
+
9248
+
9249
+ _api_register( 'destroy()', function ( remove ) {
9250
+ remove = remove || false;
9251
+
9252
+ return this.iterator( 'table', function ( settings ) {
9253
+ var orig = settings.nTableWrapper.parentNode;
9254
+ var classes = settings.oClasses;
9255
+ var table = settings.nTable;
9256
+ var tbody = settings.nTBody;
9257
+ var thead = settings.nTHead;
9258
+ var tfoot = settings.nTFoot;
9259
+ var jqTable = $(table);
9260
+ var jqTbody = $(tbody);
9261
+ var jqWrapper = $(settings.nTableWrapper);
9262
+ var rows = $.map( settings.aoData, function (r) { return r.nTr; } );
9263
+ var i, ien;
9264
+
9265
+ // Flag to note that the table is currently being destroyed - no action
9266
+ // should be taken
9267
+ settings.bDestroying = true;
9268
+
9269
+ // Fire off the destroy callbacks for plug-ins etc
9270
+ _fnCallbackFire( settings, "aoDestroyCallback", "destroy", [settings] );
9271
+
9272
+ // If not being removed from the document, make all columns visible
9273
+ if ( ! remove ) {
9274
+ new _Api( settings ).columns().visible( true );
9275
+ }
9276
+
9277
+ // Blitz all `DT` namespaced events (these are internal events, the
9278
+ // lowercase, `dt` events are user subscribed and they are responsible
9279
+ // for removing them
9280
+ jqWrapper.off('.DT').find(':not(tbody *)').off('.DT');
9281
+ $(window).off('.DT-'+settings.sInstance);
9282
+
9283
+ // When scrolling we had to break the table up - restore it
9284
+ if ( table != thead.parentNode ) {
9285
+ jqTable.children('thead').detach();
9286
+ jqTable.append( thead );
9287
+ }
9288
+
9289
+ if ( tfoot && table != tfoot.parentNode ) {
9290
+ jqTable.children('tfoot').detach();
9291
+ jqTable.append( tfoot );
9292
+ }
9293
+
9294
+ settings.aaSorting = [];
9295
+ settings.aaSortingFixed = [];
9296
+ _fnSortingClasses( settings );
9297
+
9298
+ $( rows ).removeClass( settings.asStripeClasses.join(' ') );
9299
+
9300
+ $('th, td', thead).removeClass( classes.sSortable+' '+
9301
+ classes.sSortableAsc+' '+classes.sSortableDesc+' '+classes.sSortableNone
9302
+ );
9303
+
9304
+ if ( settings.bJUI ) {
9305
+ $('th span.'+classes.sSortIcon+ ', td span.'+classes.sSortIcon, thead).detach();
9306
+ $('th, td', thead).each( function () {
9307
+ var wrapper = $('div.'+classes.sSortJUIWrapper, this);
9308
+ $(this).append( wrapper.contents() );
9309
+ wrapper.detach();
9310
+ } );
9311
+ }
9312
+
9313
+ // Add the TR elements back into the table in their original order
9314
+ jqTbody.children().detach();
9315
+ jqTbody.append( rows );
9316
+
9317
+ // Remove the WFDataTables generated nodes, events and classes
9318
+ var removedMethod = remove ? 'remove' : 'detach';
9319
+ jqTable[ removedMethod ]();
9320
+ jqWrapper[ removedMethod ]();
9321
+
9322
+ // If we need to reattach the table to the document
9323
+ if ( ! remove && orig ) {
9324
+ // insertBefore acts like appendChild if !arg[1]
9325
+ orig.insertBefore( table, settings.nTableReinsertBefore );
9326
+
9327
+ // Restore the width of the original table - was read from the style property,
9328
+ // so we can restore directly to that
9329
+ jqTable
9330
+ .css( 'width', settings.sDestroyWidth )
9331
+ .removeClass( classes.sTable );
9332
+
9333
+ // If the were originally stripe classes - then we add them back here.
9334
+ // Note this is not fool proof (for example if not all rows had stripe
9335
+ // classes - but it's a good effort without getting carried away
9336
+ ien = settings.asDestroyStripes.length;
9337
+
9338
+ if ( ien ) {
9339
+ jqTbody.children().each( function (i) {
9340
+ $(this).addClass( settings.asDestroyStripes[i % ien] );
9341
+ } );
9342
+ }
9343
+ }
9344
+
9345
+ /* Remove the settings object from the settings array */
9346
+ var idx = $.inArray( settings, WFDataTable.settings );
9347
+ if ( idx !== -1 ) {
9348
+ WFDataTable.settings.splice( idx, 1 );
9349
+ }
9350
+ } );
9351
+ } );
9352
+
9353
+
9354
+ // Add the `every()` method for rows, columns and cells in a compact form
9355
+ $.each( [ 'column', 'row', 'cell' ], function ( i, type ) {
9356
+ _api_register( type+'s().every()', function ( fn ) {
9357
+ var opts = this.selector.opts;
9358
+ var api = this;
9359
+
9360
+ return this.iterator( type, function ( settings, arg1, arg2, arg3, arg4 ) {
9361
+ // Rows and columns:
9362
+ // arg1 - index
9363
+ // arg2 - table counter
9364
+ // arg3 - loop counter
9365
+ // arg4 - undefined
9366
+ // Cells:
9367
+ // arg1 - row index
9368
+ // arg2 - column index
9369
+ // arg3 - table counter
9370
+ // arg4 - loop counter
9371
+ fn.call(
9372
+ api[ type ](
9373
+ arg1,
9374
+ type==='cell' ? arg2 : opts,
9375
+ type==='cell' ? opts : undefined
9376
+ ),
9377
+ arg1, arg2, arg3, arg4
9378
+ );
9379
+ } );
9380
+ } );
9381
+ } );
9382
+
9383
+
9384
+ // i18n method for extensions to be able to use the language object from the
9385
+ // WFDataTable
9386
+ _api_register( 'i18n()', function ( token, def, plural ) {
9387
+ var ctx = this.context[0];
9388
+ var resolved = _fnGetObjectDataFn( token )( ctx.oLanguage );
9389
+
9390
+ if ( resolved === undefined ) {
9391
+ resolved = def;
9392
+ }
9393
+
9394
+ if ( plural !== undefined && $.isPlainObject( resolved ) ) {
9395
+ resolved = resolved[ plural ] !== undefined ?
9396
+ resolved[ plural ] :
9397
+ resolved._;
9398
+ }
9399
+
9400
+ return resolved.replace( '%d', plural ); // nb: plural might be undefined,
9401
+ } );
9402
+
9403
+ /**
9404
+ * Version string for plug-ins to check compatibility. Allowed format is
9405
+ * `a.b.c-d` where: a:int, b:int, c:int, d:string(dev|beta|alpha). `d` is used
9406
+ * only for non-release builds. See http://semver.org/ for more information.
9407
+ * @member
9408
+ * @type string
9409
+ * @default Version number
9410
+ */
9411
+ WFDataTable.version = "1.10.13";
9412
+
9413
+ /**
9414
+ * Private data store, containing all of the settings objects that are
9415
+ * created for the tables on a given page.
9416
+ *
9417
+ * Note that the `WFDataTable.settings` object is aliased to
9418
+ * `jQuery.fn.wfDataTableExt` through which it may be accessed and
9419
+ * manipulated, or `jQuery.fn.wfDataTable.settings`.
9420
+ * @member
9421
+ * @type array
9422
+ * @default []
9423
+ * @private
9424
+ */
9425
+ WFDataTable.settings = [];
9426
+
9427
+ /**
9428
+ * Object models container, for the various models that WFDataTables has
9429
+ * available to it. These models define the objects that are used to hold
9430
+ * the active state and configuration of the table.
9431
+ * @namespace
9432
+ */
9433
+ WFDataTable.models = {};
9434
+
9435
+
9436
+
9437
+ /**
9438
+ * Template object for the way in which WFDataTables holds information about
9439
+ * search information for the global filter and individual column filters.
9440
+ * @namespace
9441
+ */
9442
+ WFDataTable.models.oSearch = {
9443
+ /**
9444
+ * Flag to indicate if the filtering should be case insensitive or not
9445
+ * @type boolean
9446
+ * @default true
9447
+ */
9448
+ "bCaseInsensitive": true,
9449
+
9450
+ /**
9451
+ * Applied search term
9452
+ * @type string
9453
+ * @default <i>Empty string</i>
9454
+ */
9455
+ "sSearch": "",
9456
+
9457
+ /**
9458
+ * Flag to indicate if the search term should be interpreted as a
9459
+ * regular expression (true) or not (false) and therefore and special
9460
+ * regex characters escaped.
9461
+ * @type boolean
9462
+ * @default false
9463
+ */
9464
+ "bRegex": false,
9465
+
9466
+ /**
9467
+ * Flag to indicate if WFDataTables is to use its smart filtering or not.
9468
+ * @type boolean
9469
+ * @default true
9470
+ */
9471
+ "bSmart": true
9472
+ };
9473
+
9474
+
9475
+
9476
+
9477
+ /**
9478
+ * Template object for the way in which WFDataTables holds information about
9479
+ * each individual row. This is the object format used for the settings
9480
+ * aoData array.
9481
+ * @namespace
9482
+ */
9483
+ WFDataTable.models.oRow = {
9484
+ /**
9485
+ * TR element for the row
9486
+ * @type node
9487
+ * @default null
9488
+ */
9489
+ "nTr": null,
9490
+
9491
+ /**
9492
+ * Array of TD elements for each row. This is null until the row has been
9493
+ * created.
9494
+ * @type array nodes
9495
+ * @default []
9496
+ */
9497
+ "anCells": null,
9498
+
9499
+ /**
9500
+ * Data object from the original data source for the row. This is either
9501
+ * an array if using the traditional form of WFDataTables, or an object if
9502
+ * using mData options. The exact type will depend on the passed in
9503
+ * data from the data source, or will be an array if using DOM a data
9504
+ * source.
9505
+ * @type array|object
9506
+ * @default []
9507
+ */
9508
+ "_aData": [],
9509
+
9510
+ /**
9511
+ * Sorting data cache - this array is ostensibly the same length as the
9512
+ * number of columns (although each index is generated only as it is
9513
+ * needed), and holds the data that is used for sorting each column in the
9514
+ * row. We do this cache generation at the start of the sort in order that
9515
+ * the formatting of the sort data need be done only once for each cell
9516
+ * per sort. This array should not be read from or written to by anything
9517
+ * other than the master sorting methods.
9518
+ * @type array
9519
+ * @default null
9520
+ * @private
9521
+ */
9522
+ "_aSortData": null,
9523
+
9524
+ /**
9525
+ * Per cell filtering data cache. As per the sort data cache, used to
9526
+ * increase the performance of the filtering in WFDataTables
9527
+ * @type array
9528
+ * @default null
9529
+ * @private
9530
+ */
9531
+ "_aFilterData": null,
9532
+
9533
+ /**
9534
+ * Filtering data cache. This is the same as the cell filtering cache, but
9535
+ * in this case a string rather than an array. This is easily computed with
9536
+ * a join on `_aFilterData`, but is provided as a cache so the join isn't
9537
+ * needed on every search (memory traded for performance)
9538
+ * @type array
9539
+ * @default null
9540
+ * @private
9541
+ */
9542
+ "_sFilterRow": null,
9543
+
9544
+ /**
9545
+ * Cache of the class name that WFDataTables has applied to the row, so we
9546
+ * can quickly look at this variable rather than needing to do a DOM check
9547
+ * on className for the nTr property.
9548
+ * @type string
9549
+ * @default <i>Empty string</i>
9550
+ * @private
9551
+ */
9552
+ "_sRowStripe": "",
9553
+
9554
+ /**
9555
+ * Denote if the original data source was from the DOM, or the data source
9556
+ * object. This is used for invalidating data, so WFDataTables can
9557
+ * automatically read data from the original source, unless uninstructed
9558
+ * otherwise.
9559
+ * @type string
9560
+ * @default null
9561
+ * @private
9562
+ */
9563
+ "src": null,
9564
+
9565
+ /**
9566
+ * Index in the aoData array. This saves an indexOf lookup when we have the
9567
+ * object, but want to know the index
9568
+ * @type integer
9569
+ * @default -1
9570
+ * @private
9571
+ */
9572
+ "idx": -1
9573
+ };
9574
+
9575
+
9576
+ /**
9577
+ * Template object for the column information object in WFDataTables. This object
9578
+ * is held in the settings aoColumns array and contains all the information that
9579
+ * WFDataTables needs about each individual column.
9580
+ *
9581
+ * Note that this object is related to {@link WFDataTable.defaults.column}
9582
+ * but this one is the internal data store for WFDataTables's cache of columns.
9583
+ * It should NOT be manipulated outside of WFDataTables. Any configuration should
9584
+ * be done through the initialisation options.
9585
+ * @namespace
9586
+ */
9587
+ WFDataTable.models.oColumn = {
9588
+ /**
9589
+ * Column index. This could be worked out on-the-fly with $.inArray, but it
9590
+ * is faster to just hold it as a variable
9591
+ * @type integer
9592
+ * @default null
9593
+ */
9594
+ "idx": null,
9595
+
9596
+ /**
9597
+ * A list of the columns that sorting should occur on when this column
9598
+ * is sorted. That this property is an array allows multi-column sorting
9599
+ * to be defined for a column (for example first name / last name columns
9600
+ * would benefit from this). The values are integers pointing to the
9601
+ * columns to be sorted on (typically it will be a single integer pointing
9602
+ * at itself, but that doesn't need to be the case).
9603
+ * @type array
9604
+ */
9605
+ "aDataSort": null,
9606
+
9607
+ /**
9608
+ * Define the sorting directions that are applied to the column, in sequence
9609
+ * as the column is repeatedly sorted upon - i.e. the first value is used
9610
+ * as the sorting direction when the column if first sorted (clicked on).
9611
+ * Sort it again (click again) and it will move on to the next index.
9612
+ * Repeat until loop.
9613
+ * @type array
9614
+ */
9615
+ "asSorting": null,
9616
+
9617
+ /**
9618
+ * Flag to indicate if the column is searchable, and thus should be included
9619
+ * in the filtering or not.
9620
+ * @type boolean
9621
+ */
9622
+ "bSearchable": null,
9623
+
9624
+ /**
9625
+ * Flag to indicate if the column is sortable or not.
9626
+ * @type boolean
9627
+ */
9628
+ "bSortable": null,
9629
+
9630
+ /**
9631
+ * Flag to indicate if the column is currently visible in the table or not
9632
+ * @type boolean
9633
+ */
9634
+ "bVisible": null,
9635
+
9636
+ /**
9637
+ * Store for manual type assignment using the `column.type` option. This
9638
+ * is held in store so we can manipulate the column's `sType` property.
9639
+ * @type string
9640
+ * @default null
9641
+ * @private
9642
+ */
9643
+ "_sManualType": null,
9644
+
9645
+ /**
9646
+ * Flag to indicate if HTML5 data attributes should be used as the data
9647
+ * source for filtering or sorting. True is either are.
9648
+ * @type boolean
9649
+ * @default false
9650
+ * @private
9651
+ */
9652
+ "_bAttrSrc": false,
9653
+
9654
+ /**
9655
+ * Developer definable function that is called whenever a cell is created (Ajax source,
9656
+ * etc) or processed for input (DOM source). This can be used as a compliment to mRender
9657
+ * allowing you to modify the DOM element (add background colour for example) when the
9658
+ * element is available.
9659
+ * @type function
9660
+ * @param {element} nTd The TD node that has been created
9661
+ * @param {*} sData The Data for the cell
9662
+ * @param {array|object} oData The data for the whole row
9663
+ * @param {int} iRow The row index for the aoData data store
9664
+ * @default null
9665
+ */
9666
+ "fnCreatedCell": null,
9667
+
9668
+ /**
9669
+ * Function to get data from a cell in a column. You should <b>never</b>
9670
+ * access data directly through _aData internally in WFDataTables - always use
9671
+ * the method attached to this property. It allows mData to function as
9672
+ * required. This function is automatically assigned by the column
9673
+ * initialisation method
9674
+ * @type function
9675
+ * @param {array|object} oData The data array/object for the array
9676
+ * (i.e. aoData[]._aData)
9677
+ * @param {string} sSpecific The specific data type you want to get -
9678
+ * 'display', 'type' 'filter' 'sort'
9679
+ * @returns {*} The data for the cell from the given row's data
9680
+ * @default null
9681
+ */
9682
+ "fnGetData": null,
9683
+
9684
+ /**
9685
+ * Function to set data for a cell in the column. You should <b>never</b>
9686
+ * set the data directly to _aData internally in WFDataTables - always use
9687
+ * this method. It allows mData to function as required. This function
9688
+ * is automatically assigned by the column initialisation method
9689
+ * @type function
9690
+ * @param {array|object} oData The data array/object for the array
9691
+ * (i.e. aoData[]._aData)
9692
+ * @param {*} sValue Value to set
9693
+ * @default null
9694
+ */
9695
+ "fnSetData": null,
9696
+
9697
+ /**
9698
+ * Property to read the value for the cells in the column from the data
9699
+ * source array / object. If null, then the default content is used, if a
9700
+ * function is given then the return from the function is used.
9701
+ * @type function|int|string|null
9702
+ * @default null
9703
+ */
9704
+ "mData": null,
9705
+
9706
+ /**
9707
+ * Partner property to mData which is used (only when defined) to get
9708
+ * the data - i.e. it is basically the same as mData, but without the
9709
+ * 'set' option, and also the data fed to it is the result from mData.
9710
+ * This is the rendering method to match the data method of mData.
9711
+ * @type function|int|string|null
9712
+ * @default null
9713
+ */
9714
+ "mRender": null,
9715
+
9716
+ /**
9717
+ * Unique header TH/TD element for this column - this is what the sorting
9718
+ * listener is attached to (if sorting is enabled.)
9719
+ * @type node
9720
+ * @default null
9721
+ */
9722
+ "nTh": null,
9723
+
9724
+ /**
9725
+ * Unique footer TH/TD element for this column (if there is one). Not used
9726
+ * in WFDataTables as such, but can be used for plug-ins to reference the
9727
+ * footer for each column.
9728
+ * @type node
9729
+ * @default null
9730
+ */
9731
+ "nTf": null,
9732
+
9733
+ /**
9734
+ * The class to apply to all TD elements in the table's TBODY for the column
9735
+ * @type string
9736
+ * @default null
9737
+ */
9738
+ "sClass": null,
9739
+
9740
+ /**
9741
+ * When WFDataTables calculates the column widths to assign to each column,
9742
+ * it finds the longest string in each column and then constructs a
9743
+ * temporary table and reads the widths from that. The problem with this
9744
+ * is that "mmm" is much wider then "iiii", but the latter is a longer
9745
+ * string - thus the calculation can go wrong (doing it properly and putting
9746
+ * it into an DOM object and measuring that is horribly(!) slow). Thus as
9747
+ * a "work around" we provide this option. It will append its value to the
9748
+ * text that is found to be the longest string for the column - i.e. padding.
9749
+ * @type string
9750
+ */
9751
+ "sContentPadding": null,
9752
+
9753
+ /**
9754
+ * Allows a default value to be given for a column's data, and will be used
9755
+ * whenever a null data source is encountered (this can be because mData
9756
+ * is set to null, or because the data source itself is null).
9757
+ * @type string
9758
+ * @default null
9759
+ */
9760
+ "sDefaultContent": null,
9761
+
9762
+ /**
9763
+ * Name for the column, allowing reference to the column by name as well as
9764
+ * by index (needs a lookup to work by name).
9765
+ * @type string
9766
+ */
9767
+ "sName": null,
9768
+
9769
+ /**
9770
+ * Custom sorting data type - defines which of the available plug-ins in
9771
+ * afnSortData the custom sorting will use - if any is defined.
9772
+ * @type string
9773
+ * @default std
9774
+ */
9775
+ "sSortDataType": 'std',
9776
+
9777
+ /**
9778
+ * Class to be applied to the header element when sorting on this column
9779
+ * @type string
9780
+ * @default null
9781
+ */
9782
+ "sSortingClass": null,
9783
+
9784
+ /**
9785
+ * Class to be applied to the header element when sorting on this column -
9786
+ * when jQuery UI theming is used.
9787
+ * @type string
9788
+ * @default null
9789
+ */
9790
+ "sSortingClassJUI": null,
9791
+
9792
+ /**
9793
+ * Title of the column - what is seen in the TH element (nTh).
9794
+ * @type string
9795
+ */
9796
+ "sTitle": null,
9797
+
9798
+ /**
9799
+ * Column sorting and filtering type
9800
+ * @type string
9801
+ * @default null
9802
+ */
9803
+ "sType": null,
9804
+
9805
+ /**
9806
+ * Width of the column
9807
+ * @type string
9808
+ * @default null
9809
+ */
9810
+ "sWidth": null,
9811
+
9812
+ /**
9813
+ * Width of the column when it was first "encountered"
9814
+ * @type string
9815
+ * @default null
9816
+ */
9817
+ "sWidthOrig": null
9818
+ };
9819
+
9820
+
9821
+ /*
9822
+ * Developer note: The properties of the object below are given in Hungarian
9823
+ * notation, that was used as the interface for WFDataTables prior to v1.10, however
9824
+ * from v1.10 onwards the primary interface is camel case. In order to avoid
9825
+ * breaking backwards compatibility utterly with this change, the Hungarian
9826
+ * version is still, internally the primary interface, but is is not documented
9827
+ * - hence the @name tags in each doc comment. This allows a Javascript function
9828
+ * to create a map from Hungarian notation to camel case (going the other direction
9829
+ * would require each property to be listed, which would at around 3K to the size
9830
+ * of WFDataTables, while this method is about a 0.5K hit.
9831
+ *
9832
+ * Ultimately this does pave the way for Hungarian notation to be dropped
9833
+ * completely, but that is a massive amount of work and will break current
9834
+ * installs (therefore is on-hold until v2).
9835
+ */
9836
+
9837
+ /**
9838
+ * Initialisation options that can be given to WFDataTables at initialisation
9839
+ * time.
9840
+ * @namespace
9841
+ */
9842
+ WFDataTable.defaults = {
9843
+ /**
9844
+ * An array of data to use for the table, passed in at initialisation which
9845
+ * will be used in preference to any data which is already in the DOM. This is
9846
+ * particularly useful for constructing tables purely in Javascript, for
9847
+ * example with a custom Ajax call.
9848
+ * @type array
9849
+ * @default null
9850
+ *
9851
+ * @dtopt Option
9852
+ * @name WFDataTable.defaults.data
9853
+ *
9854
+ * @example
9855
+ * // Using a 2D array data source
9856
+ * $(document).ready( function () {
9857
+ * $('#example').wfDataTable( {
9858
+ * "data": [
9859
+ * ['Trident', 'Internet Explorer 4.0', 'Win 95+', 4, 'X'],
9860
+ * ['Trident', 'Internet Explorer 5.0', 'Win 95+', 5, 'C'],
9861
+ * ],
9862
+ * "columns": [
9863
+ * { "title": "Engine" },
9864
+ * { "title": "Browser" },
9865
+ * { "title": "Platform" },
9866
+ * { "title": "Version" },
9867
+ * { "title": "Grade" }
9868
+ * ]
9869
+ * } );
9870
+ * } );
9871
+ *
9872
+ * @example
9873
+ * // Using an array of objects as a data source (`data`)
9874
+ * $(document).ready( function () {
9875
+ * $('#example').wfDataTable( {
9876
+ * "data": [
9877
+ * {
9878
+ * "engine": "Trident",
9879
+ * "browser": "Internet Explorer 4.0",
9880
+ * "platform": "Win 95+",
9881
+ * "version": 4,
9882
+ * "grade": "X"
9883
+ * },
9884
+ * {
9885
+ * "engine": "Trident",
9886
+ * "browser": "Internet Explorer 5.0",
9887
+ * "platform": "Win 95+",
9888
+ * "version": 5,
9889
+ * "grade": "C"
9890
+ * }
9891
+ * ],
9892
+ * "columns": [
9893
+ * { "title": "Engine", "data": "engine" },
9894
+ * { "title": "Browser", "data": "browser" },
9895
+ * { "title": "Platform", "data": "platform" },
9896
+ * { "title": "Version", "data": "version" },
9897
+ * { "title": "Grade", "data": "grade" }
9898
+ * ]
9899
+ * } );
9900
+ * } );
9901
+ */
9902
+ "aaData": null,
9903
+
9904
+
9905
+ /**
9906
+ * If ordering is enabled, then WFDataTables will perform a first pass sort on
9907
+ * initialisation. You can define which column(s) the sort is performed
9908
+ * upon, and the sorting direction, with this variable. The `sorting` array
9909
+ * should contain an array for each column to be sorted initially containing
9910
+ * the column's index and a direction string ('asc' or 'desc').
9911
+ * @type array
9912
+ * @default [[0,'asc']]
9913
+ *
9914
+ * @dtopt Option
9915
+ * @name WFDataTable.defaults.order
9916
+ *
9917
+ * @example
9918
+ * // Sort by 3rd column first, and then 4th column
9919
+ * $(document).ready( function() {
9920
+ * $('#example').wfDataTable( {
9921
+ * "order": [[2,'asc'], [3,'desc']]
9922
+ * } );
9923
+ * } );
9924
+ *
9925
+ * // No initial sorting
9926
+ * $(document).ready( function() {
9927
+ * $('#example').wfDataTable( {
9928
+ * "order": []
9929
+ * } );
9930
+ * } );
9931
+ */
9932
+ "aaSorting": [[0,'asc']],
9933
+
9934
+
9935
+ /**
9936
+ * This parameter is basically identical to the `sorting` parameter, but
9937
+ * cannot be overridden by user interaction with the table. What this means
9938
+ * is that you could have a column (visible or hidden) which the sorting
9939
+ * will always be forced on first - any sorting after that (from the user)
9940
+ * will then be performed as required. This can be useful for grouping rows
9941
+ * together.
9942
+ * @type array
9943
+ * @default null
9944
+ *
9945
+ * @dtopt Option
9946
+ * @name WFDataTable.defaults.orderFixed
9947
+ *
9948
+ * @example
9949
+ * $(document).ready( function() {
9950
+ * $('#example').wfDataTable( {
9951
+ * "orderFixed": [[0,'asc']]
9952
+ * } );
9953
+ * } )
9954
+ */
9955
+ "aaSortingFixed": [],
9956
+
9957
+
9958
+ /**
9959
+ * WFDataTables can be instructed to load data to display in the table from a
9960
+ * Ajax source. This option defines how that Ajax call is made and where to.
9961
+ *
9962
+ * The `ajax` property has three different modes of operation, depending on
9963
+ * how it is defined. These are:
9964
+ *
9965
+ * * `string` - Set the URL from where the data should be loaded from.
9966
+ * * `object` - Define properties for `jQuery.ajax`.
9967
+ * * `function` - Custom data get function
9968
+ *
9969
+ * `string`
9970
+ * --------
9971
+ *
9972
+ * As a string, the `ajax` property simply defines the URL from which
9973
+ * WFDataTables will load data.
9974
+ *
9975
+ * `object`
9976
+ * --------
9977
+ *
9978
+ * As an object, the parameters in the object are passed to
9979
+ * [jQuery.ajax](http://api.jquery.com/jQuery.ajax/) allowing fine control
9980
+ * of the Ajax request. WFDataTables has a number of default parameters which
9981
+ * you can override using this option. Please refer to the jQuery
9982
+ * documentation for a full description of the options available, although
9983
+ * the following parameters provide additional options in WFDataTables or
9984
+ * require special consideration:
9985
+ *
9986
+ * * `data` - As with jQuery, `data` can be provided as an object, but it
9987
+ * can also be used as a function to manipulate the data WFDataTables sends
9988
+ * to the server. The function takes a single parameter, an object of
9989
+ * parameters with the values that WFDataTables has readied for sending. An
9990
+ * object may be returned which will be merged into the WFDataTables
9991
+ * defaults, or you can add the items to the object that was passed in and
9992
+ * not return anything from the function. This supersedes `fnServerParams`
9993
+ * from WFDataTables 1.9-.
9994
+ *
9995
+ * * `dataSrc` - By default WFDataTables will look for the property `data` (or
9996
+ * `aaData` for compatibility with WFDataTables 1.9-) when obtaining data
9997
+ * from an Ajax source or for server-side processing - this parameter
9998
+ * allows that property to be changed. You can use Javascript dotted
9999
+ * object notation to get a data source for multiple levels of nesting, or
10000
+ * it my be used as a function. As a function it takes a single parameter,
10001
+ * the JSON returned from the server, which can be manipulated as
10002
+ * required, with the returned value being that used by WFDataTables as the
10003
+ * data source for the table. This supersedes `sAjaxDataProp` from
10004
+ * WFDataTables 1.9-.
10005
+ *
10006
+ * * `success` - Should not be overridden it is used internally in
10007
+ * WFDataTables. To manipulate / transform the data returned by the server
10008
+ * use `ajax.dataSrc`, or use `ajax` as a function (see below).
10009
+ *
10010
+ * `function`
10011
+ * ----------
10012
+ *
10013
+ * As a function, making the Ajax call is left up to yourself allowing
10014
+ * complete control of the Ajax request. Indeed, if desired, a method other
10015
+ * than Ajax could be used to obtain the required data, such as Web storage
10016
+ * or an AIR database.
10017
+ *
10018
+ * The function is given four parameters and no return is required. The
10019
+ * parameters are:
10020
+ *
10021
+ * 1. _object_ - Data to send to the server
10022
+ * 2. _function_ - Callback function that must be executed when the required
10023
+ * data has been obtained. That data should be passed into the callback
10024
+ * as the only parameter
10025
+ * 3. _object_ - WFDataTables settings object for the table
10026
+ *
10027
+ * Note that this supersedes `fnServerData` from WFDataTables 1.9-.
10028
+ *
10029
+ * @type string|object|function
10030
+ * @default null
10031
+ *
10032
+ * @dtopt Option
10033
+ * @name WFDataTable.defaults.ajax
10034
+ * @since 1.10.0
10035
+ *
10036
+ * @example
10037
+ * // Get JSON data from a file via Ajax.
10038
+ * // Note WFDataTables expects data in the form `{ data: [ ...data... ] }` by default).
10039
+ * $('#example').wfDataTable( {
10040
+ * "ajax": "data.json"
10041
+ * } );
10042
+ *
10043
+ * @example
10044
+ * // Get JSON data from a file via Ajax, using `dataSrc` to change
10045
+ * // `data` to `tableData` (i.e. `{ tableData: [ ...data... ] }`)
10046
+ * $('#example').wfDataTable( {
10047
+ * "ajax": {
10048
+ * "url": "data.json",
10049
+ * "dataSrc": "tableData"
10050
+ * }
10051
+ * } );
10052
+ *
10053
+ * @example
10054
+ * // Get JSON data from a file via Ajax, using `dataSrc` to read data
10055
+ * // from a plain array rather than an array in an object
10056
+ * $('#example').wfDataTable( {
10057
+ * "ajax": {
10058
+ * "url": "data.json",
10059
+ * "dataSrc": ""
10060
+ * }
10061
+ * } );
10062
+ *
10063
+ * @example
10064
+ * // Manipulate the data returned from the server - add a link to data
10065
+ * // (note this can, should, be done using `render` for the column - this
10066
+ * // is just a simple example of how the data can be manipulated).
10067
+ * $('#example').wfDataTable( {
10068
+ * "ajax": {
10069
+ * "url": "data.json",
10070
+ * "dataSrc": function ( json ) {
10071
+ * for ( var i=0, ien=json.length ; i<ien ; i++ ) {
10072
+ * json[i][0] = '<a href="/message/'+json[i][0]+'>View message</a>';
10073
+ * }
10074
+ * return json;
10075
+ * }
10076
+ * }
10077
+ * } );
10078
+ *
10079
+ * @example
10080
+ * // Add data to the request
10081
+ * $('#example').wfDataTable( {
10082
+ * "ajax": {
10083
+ * "url": "data.json",
10084
+ * "data": function ( d ) {
10085
+ * return {
10086
+ * "extra_search": $('#extra').val()
10087
+ * };
10088
+ * }
10089
+ * }
10090
+ * } );
10091
+ *
10092
+ * @example
10093
+ * // Send request as POST
10094
+ * $('#example').wfDataTable( {
10095
+ * "ajax": {
10096
+ * "url": "data.json",
10097
+ * "type": "POST"
10098
+ * }
10099
+ * } );
10100
+ *
10101
+ * @example
10102
+ * // Get the data from localStorage (could interface with a form for
10103
+ * // adding, editing and removing rows).
10104
+ * $('#example').wfDataTable( {
10105
+ * "ajax": function (data, callback, settings) {
10106
+ * callback(
10107
+ * JSON.parse( localStorage.getItem('dataTablesData') )
10108
+ * );
10109
+ * }
10110
+ * } );
10111
+ */
10112
+ "ajax": null,
10113
+
10114
+
10115
+ /**
10116
+ * This parameter allows you to readily specify the entries in the length drop
10117
+ * down menu that WFDataTables shows when pagination is enabled. It can be
10118
+ * either a 1D array of options which will be used for both the displayed
10119
+ * option and the value, or a 2D array which will use the array in the first
10120
+ * position as the value, and the array in the second position as the
10121
+ * displayed options (useful for language strings such as 'All').
10122
+ *
10123
+ * Note that the `pageLength` property will be automatically set to the
10124
+ * first value given in this array, unless `pageLength` is also provided.
10125
+ * @type array
10126
+ * @default [ 10, 25, 50, 100 ]
10127
+ *
10128
+ * @dtopt Option
10129
+ * @name WFDataTable.defaults.lengthMenu
10130
+ *
10131
+ * @example
10132
+ * $(document).ready( function() {
10133
+ * $('#example').wfDataTable( {
10134
+ * "lengthMenu": [[10, 25, 50, -1], [10, 25, 50, "All"]]
10135
+ * } );
10136
+ * } );
10137
+ */
10138
+ "aLengthMenu": [ 10, 25, 50, 100 ],
10139
+
10140
+
10141
+ /**
10142
+ * The `columns` option in the initialisation parameter allows you to define
10143
+ * details about the way individual columns behave. For a full list of
10144
+ * column options that can be set, please see
10145
+ * {@link WFDataTable.defaults.column}. Note that if you use `columns` to
10146
+ * define your columns, you must have an entry in the array for every single
10147
+ * column that you have in your table (these can be null if you don't which
10148
+ * to specify any options).
10149
+ * @member
10150
+ *
10151
+ * @name WFDataTable.defaults.column
10152
+ */
10153
+ "aoColumns": null,
10154
+
10155
+ /**
10156
+ * Very similar to `columns`, `columnDefs` allows you to target a specific
10157
+ * column, multiple columns, or all columns, using the `targets` property of
10158
+ * each object in the array. This allows great flexibility when creating
10159
+ * tables, as the `columnDefs` arrays can be of any length, targeting the
10160
+ * columns you specifically want. `columnDefs` may use any of the column
10161
+ * options available: {@link WFDataTable.defaults.column}, but it _must_
10162
+ * have `targets` defined in each object in the array. Values in the `targets`
10163
+ * array may be:
10164
+ * <ul>
10165
+ * <li>a string - class name will be matched on the TH for the column</li>
10166
+ * <li>0 or a positive integer - column index counting from the left</li>
10167
+ * <li>a negative integer - column index counting from the right</li>
10168
+ * <li>the string "_all" - all columns (i.e. assign a default)</li>
10169
+ * </ul>
10170
+ * @member
10171
+ *
10172
+ * @name WFDataTable.defaults.columnDefs
10173
+ */
10174
+ "aoColumnDefs": null,
10175
+
10176
+
10177
+ /**
10178
+ * Basically the same as `search`, this parameter defines the individual column
10179
+ * filtering state at initialisation time. The array must be of the same size
10180
+ * as the number of columns, and each element be an object with the parameters
10181
+ * `search` and `escapeRegex` (the latter is optional). 'null' is also
10182
+ * accepted and the default will be used.
10183
+ * @type array
10184
+ * @default []
10185
+ *
10186
+ * @dtopt Option
10187
+ * @name WFDataTable.defaults.searchCols
10188
+ *
10189
+ * @example
10190
+ * $(document).ready( function() {
10191
+ * $('#example').wfDataTable( {
10192
+ * "searchCols": [
10193
+ * null,
10194
+ * { "search": "My filter" },
10195
+ * null,
10196
+ * { "search": "^[0-9]", "escapeRegex": false }
10197
+ * ]
10198
+ * } );
10199
+ * } )
10200
+ */
10201
+ "aoSearchCols": [],
10202
+
10203
+
10204
+ /**
10205
+ * An array of CSS classes that should be applied to displayed rows. This
10206
+ * array may be of any length, and WFDataTables will apply each class
10207
+ * sequentially, looping when required.
10208
+ * @type array
10209
+ * @default null <i>Will take the values determined by the `oClasses.stripe*`
10210
+ * options</i>
10211
+ *
10212
+ * @dtopt Option
10213
+ * @name WFDataTable.defaults.stripeClasses
10214
+ *
10215
+ * @example
10216
+ * $(document).ready( function() {
10217
+ * $('#example').wfDataTable( {
10218
+ * "stripeClasses": [ 'strip1', 'strip2', 'strip3' ]
10219
+ * } );
10220
+ * } )
10221
+ */
10222
+ "asStripeClasses": null,
10223
+
10224
+
10225
+ /**
10226
+ * Enable or disable automatic column width calculation. This can be disabled
10227
+ * as an optimisation (it takes some time to calculate the widths) if the
10228
+ * tables widths are passed in using `columns`.
10229
+ * @type boolean
10230
+ * @default true
10231
+ *
10232
+ * @dtopt Features
10233
+ * @name WFDataTable.defaults.autoWidth
10234
+ *
10235
+ * @example
10236
+ * $(document).ready( function () {
10237
+ * $('#example').wfDataTable( {
10238
+ * "autoWidth": false
10239
+ * } );
10240
+ * } );
10241
+ */
10242
+ "bAutoWidth": true,
10243
+
10244
+
10245
+ /**
10246
+ * Deferred rendering can provide WFDataTables with a huge speed boost when you
10247
+ * are using an Ajax or JS data source for the table. This option, when set to
10248
+ * true, will cause WFDataTables to defer the creation of the table elements for
10249
+ * each row until they are needed for a draw - saving a significant amount of
10250
+ * time.
10251
+ * @type boolean
10252
+ * @default false
10253
+ *
10254
+ * @dtopt Features
10255
+ * @name WFDataTable.defaults.deferRender
10256
+ *
10257
+ * @example
10258
+ * $(document).ready( function() {
10259
+ * $('#example').wfDataTable( {
10260
+ * "ajax": "sources/arrays.txt",
10261
+ * "deferRender": true
10262
+ * } );
10263
+ * } );
10264
+ */
10265
+ "bDeferRender": false,
10266
+
10267
+
10268
+ /**
10269
+ * Replace a WFDataTable which matches the given selector and replace it with
10270
+ * one which has the properties of the new initialisation object passed. If no
10271
+ * table matches the selector, then the new WFDataTable will be constructed as
10272
+ * per normal.
10273
+ * @type boolean
10274
+ * @default false
10275
+ *
10276
+ * @dtopt Options
10277
+ * @name WFDataTable.defaults.destroy
10278
+ *
10279
+ * @example
10280
+ * $(document).ready( function() {
10281
+ * $('#example').wfDataTable( {
10282
+ * "srollY": "200px",
10283
+ * "paginate": false
10284
+ * } );
10285
+ *
10286
+ * // Some time later....
10287
+ * $('#example').wfDataTable( {
10288
+ * "filter": false,
10289
+ * "destroy": true
10290
+ * } );
10291
+ * } );
10292
+ */
10293
+ "bDestroy": false,
10294
+
10295
+
10296
+ /**
10297
+ * Enable or disable filtering of data. Filtering in WFDataTables is "smart" in
10298
+ * that it allows the end user to input multiple words (space separated) and
10299
+ * will match a row containing those words, even if not in the order that was
10300
+ * specified (this allow matching across multiple columns). Note that if you
10301
+ * wish to use filtering in WFDataTables this must remain 'true' - to remove the
10302
+ * default filtering input box and retain filtering abilities, please use
10303
+ * {@link WFDataTable.defaults.dom}.
10304
+ * @type boolean
10305
+ * @default true
10306
+ *
10307
+ * @dtopt Features
10308
+ * @name WFDataTable.defaults.searching
10309
+ *
10310
+ * @example
10311
+ * $(document).ready( function () {
10312
+ * $('#example').wfDataTable( {
10313
+ * "searching": false
10314
+ * } );
10315
+ * } );
10316
+ */
10317
+ "bFilter": true,
10318
+
10319
+
10320
+ /**
10321
+ * Enable or disable the table information display. This shows information
10322
+ * about the data that is currently visible on the page, including information
10323
+ * about filtered data if that action is being performed.
10324
+ * @type boolean
10325
+ * @default true
10326
+ *
10327
+ * @dtopt Features
10328
+ * @name WFDataTable.defaults.info
10329
+ *
10330
+ * @example
10331
+ * $(document).ready( function () {
10332
+ * $('#example').wfDataTable( {
10333
+ * "info": false
10334
+ * } );
10335
+ * } );
10336
+ */
10337
+ "bInfo": true,
10338
+
10339
+
10340
+ /**
10341
+ * Enable jQuery UI ThemeRoller support (required as ThemeRoller requires some
10342
+ * slightly different and additional mark-up from what WFDataTables has
10343
+ * traditionally used).
10344
+ * @type boolean
10345
+ * @default false
10346
+ *
10347
+ * @dtopt Features
10348
+ * @name WFDataTable.defaults.jQueryUI
10349
+ *
10350
+ * @example
10351
+ * $(document).ready( function() {
10352
+ * $('#example').wfDataTable( {
10353
+ * "jQueryUI": true
10354
+ * } );
10355
+ * } );
10356
+ */
10357
+ "bJQueryUI": false,
10358
+
10359
+
10360
+ /**
10361
+ * Allows the end user to select the size of a formatted page from a select
10362
+ * menu (sizes are 10, 25, 50 and 100). Requires pagination (`paginate`).
10363
+ * @type boolean
10364
+ * @default true
10365
+ *
10366
+ * @dtopt Features
10367
+ * @name WFDataTable.defaults.lengthChange
10368
+ *
10369
+ * @example
10370
+ * $(document).ready( function () {
10371
+ * $('#example').wfDataTable( {
10372
+ * "lengthChange": false
10373
+ * } );
10374
+ * } );
10375
+ */
10376
+ "bLengthChange": true,
10377
+
10378
+
10379
+ /**
10380
+ * Enable or disable pagination.
10381
+ * @type boolean
10382
+ * @default true
10383
+ *
10384
+ * @dtopt Features
10385
+ * @name WFDataTable.defaults.paging
10386
+ *
10387
+ * @example
10388
+ * $(document).ready( function () {
10389
+ * $('#example').wfDataTable( {
10390
+ * "paging": false
10391
+ * } );
10392
+ * } );
10393
+ */
10394
+ "bPaginate": true,
10395
+
10396
+
10397
+ /**
10398
+ * Enable or disable the display of a 'processing' indicator when the table is
10399
+ * being processed (e.g. a sort). This is particularly useful for tables with
10400
+ * large amounts of data where it can take a noticeable amount of time to sort
10401
+ * the entries.
10402
+ * @type boolean
10403
+ * @default false
10404
+ *
10405
+ * @dtopt Features
10406
+ * @name WFDataTable.defaults.processing
10407
+ *
10408
+ * @example
10409
+ * $(document).ready( function () {
10410
+ * $('#example').wfDataTable( {
10411
+ * "processing": true
10412
+ * } );
10413
+ * } );
10414
+ */
10415
+ "bProcessing": false,
10416
+
10417
+
10418
+ /**
10419
+ * Retrieve the WFDataTables object for the given selector. Note that if the
10420
+ * table has already been initialised, this parameter will cause WFDataTables
10421
+ * to simply return the object that has already been set up - it will not take
10422
+ * account of any changes you might have made to the initialisation object
10423
+ * passed to WFDataTables (setting this parameter to true is an acknowledgement
10424
+ * that you understand this). `destroy` can be used to reinitialise a table if
10425
+ * you need.
10426
+ * @type boolean
10427
+ * @default false
10428
+ *
10429
+ * @dtopt Options
10430
+ * @name WFDataTable.defaults.retrieve
10431
+ *
10432
+ * @example
10433
+ * $(document).ready( function() {
10434
+ * initTable();
10435
+ * tableActions();
10436
+ * } );
10437
+ *
10438
+ * function initTable ()
10439
+ * {
10440
+ * return $('#example').wfDataTable( {
10441
+ * "scrollY": "200px",
10442
+ * "paginate": false,
10443
+ * "retrieve": true
10444
+ * } );
10445
+ * }
10446
+ *
10447
+ * function tableActions ()
10448
+ * {
10449
+ * var table = initTable();
10450
+ * // perform API operations with oTable
10451
+ * }
10452
+ */
10453
+ "bRetrieve": false,
10454
+
10455
+
10456
+ /**
10457
+ * When vertical (y) scrolling is enabled, WFDataTables will force the height of
10458
+ * the table's viewport to the given height at all times (useful for layout).
10459
+ * However, this can look odd when filtering data down to a small data set,
10460
+ * and the footer is left "floating" further down. This parameter (when
10461
+ * enabled) will cause WFDataTables to collapse the table's viewport down when
10462
+ * the result set will fit within the given Y height.
10463
+ * @type boolean
10464
+ * @default false
10465
+ *
10466
+ * @dtopt Options
10467
+ * @name WFDataTable.defaults.scrollCollapse
10468
+ *
10469
+ * @example
10470
+ * $(document).ready( function() {
10471
+ * $('#example').wfDataTable( {
10472
+ * "scrollY": "200",
10473
+ * "scrollCollapse": true
10474
+ * } );
10475
+ * } );
10476
+ */
10477
+ "bScrollCollapse": false,
10478
+
10479
+
10480
+ /**
10481
+ * Configure WFDataTables to use server-side processing. Note that the
10482
+ * `ajax` parameter must also be given in order to give WFDataTables a
10483
+ * source to obtain the required data for each draw.
10484
+ * @type boolean
10485
+ * @default false
10486
+ *
10487
+ * @dtopt Features
10488
+ * @dtopt Server-side
10489
+ * @name WFDataTable.defaults.serverSide
10490
+ *
10491
+ * @example
10492
+ * $(document).ready( function () {
10493
+ * $('#example').wfDataTable( {
10494
+ * "serverSide": true,
10495
+ * "ajax": "xhr.php"
10496
+ * } );
10497
+ * } );
10498
+ */
10499
+ "bServerSide": false,
10500
+
10501
+
10502
+ /**
10503
+ * Enable or disable sorting of columns. Sorting of individual columns can be
10504
+ * disabled by the `sortable` option for each column.
10505
+ * @type boolean
10506
+ * @default true
10507
+ *
10508
+ * @dtopt Features
10509
+ * @name WFDataTable.defaults.ordering
10510
+ *
10511
+ * @example
10512
+ * $(document).ready( function () {
10513
+ * $('#example').wfDataTable( {
10514
+ * "ordering": false
10515
+ * } );
10516
+ * } );
10517
+ */
10518
+ "bSort": true,
10519
+
10520
+
10521
+ /**
10522
+ * Enable or display WFDataTables' ability to sort multiple columns at the
10523
+ * same time (activated by shift-click by the user).
10524
+ * @type boolean
10525
+ * @default true
10526
+ *
10527
+ * @dtopt Options
10528
+ * @name WFDataTable.defaults.orderMulti
10529
+ *
10530
+ * @example
10531
+ * // Disable multiple column sorting ability
10532
+ * $(document).ready( function () {
10533
+ * $('#example').wfDataTable( {
10534
+ * "orderMulti": false
10535
+ * } );
10536
+ * } );
10537
+ */
10538
+ "bSortMulti": true,
10539
+
10540
+
10541
+ /**
10542
+ * Allows control over whether WFDataTables should use the top (true) unique
10543
+ * cell that is found for a single column, or the bottom (false - default).
10544
+ * This is useful when using complex headers.
10545
+ * @type boolean
10546
+ * @default false
10547
+ *
10548
+ * @dtopt Options
10549
+ * @name WFDataTable.defaults.orderCellsTop
10550
+ *
10551
+ * @example
10552
+ * $(document).ready( function() {
10553
+ * $('#example').wfDataTable( {
10554
+ * "orderCellsTop": true
10555
+ * } );
10556
+ * } );
10557
+ */
10558
+ "bSortCellsTop": false,
10559
+
10560
+
10561
+ /**
10562
+ * Enable or disable the addition of the classes `sorting\_1`, `sorting\_2` and
10563
+ * `sorting\_3` to the columns which are currently being sorted on. This is
10564
+ * presented as a feature switch as it can increase processing time (while
10565
+ * classes are removed and added) so for large data sets you might want to
10566
+ * turn this off.
10567
+ * @type boolean
10568
+ * @default true
10569
+ *
10570
+ * @dtopt Features
10571
+ * @name WFDataTable.defaults.orderClasses
10572
+ *
10573
+ * @example
10574
+ * $(document).ready( function () {
10575
+ * $('#example').wfDataTable( {
10576
+ * "orderClasses": false
10577
+ * } );
10578
+ * } );
10579
+ */
10580
+ "bSortClasses": true,
10581
+
10582
+
10583
+ /**
10584
+ * Enable or disable state saving. When enabled HTML5 `localStorage` will be
10585
+ * used to save table display information such as pagination information,
10586
+ * display length, filtering and sorting. As such when the end user reloads
10587
+ * the page the display display will match what thy had previously set up.
10588
+ *
10589
+ * Due to the use of `localStorage` the default state saving is not supported
10590
+ * in IE6 or 7. If state saving is required in those browsers, use
10591
+ * `stateSaveCallback` to provide a storage solution such as cookies.
10592
+ * @type boolean
10593
+ * @default false
10594
+ *
10595
+ * @dtopt Features
10596
+ * @name WFDataTable.defaults.stateSave
10597
+ *
10598
+ * @example
10599
+ * $(document).ready( function () {
10600
+ * $('#example').wfDataTable( {
10601
+ * "stateSave": true
10602
+ * } );
10603
+ * } );
10604
+ */
10605
+ "bStateSave": false,
10606
+
10607
+
10608
+ /**
10609
+ * This function is called when a TR element is created (and all TD child
10610
+ * elements have been inserted), or registered if using a DOM source, allowing
10611
+ * manipulation of the TR element (adding classes etc).
10612
+ * @type function
10613
+ * @param {node} row "TR" element for the current row
10614
+ * @param {array} data Raw data array for this row
10615
+ * @param {int} dataIndex The index of this row in the internal aoData array
10616
+ *
10617
+ * @dtopt Callbacks
10618
+ * @name WFDataTable.defaults.createdRow
10619
+ *
10620
+ * @example
10621
+ * $(document).ready( function() {
10622
+ * $('#example').wfDataTable( {
10623
+ * "createdRow": function( row, data, dataIndex ) {
10624
+ * // Bold the grade for all 'A' grade browsers
10625
+ * if ( data[4] == "A" )
10626
+ * {
10627
+ * $('td:eq(4)', row).html( '<b>A</b>' );
10628
+ * }
10629
+ * }
10630
+ * } );
10631
+ * } );
10632
+ */
10633
+ "fnCreatedRow": null,
10634
+
10635
+
10636
+ /**
10637
+ * This function is called on every 'draw' event, and allows you to
10638
+ * dynamically modify any aspect you want about the created DOM.
10639
+ * @type function
10640
+ * @param {object} settings WFDataTables settings object
10641
+ *
10642
+ * @dtopt Callbacks
10643
+ * @name WFDataTable.defaults.drawCallback
10644
+ *
10645
+ * @example
10646
+ * $(document).ready( function() {
10647
+ * $('#example').wfDataTable( {
10648
+ * "drawCallback": function( settings ) {
10649
+ * alert( 'WFDataTables has redrawn the table' );
10650
+ * }
10651
+ * } );
10652
+ * } );
10653
+ */
10654
+ "fnDrawCallback": null,
10655
+
10656
+
10657
+ /**
10658
+ * Identical to fnHeaderCallback() but for the table footer this function
10659
+ * allows you to modify the table footer on every 'draw' event.
10660
+ * @type function
10661
+ * @param {node} foot "TR" element for the footer
10662
+ * @param {array} data Full table data (as derived from the original HTML)
10663
+ * @param {int} start Index for the current display starting point in the
10664
+ * display array
10665
+ * @param {int} end Index for the current display ending point in the
10666
+ * display array
10667
+ * @param {array int} display Index array to translate the visual position
10668
+ * to the full data array
10669
+ *
10670
+ * @dtopt Callbacks
10671
+ * @name WFDataTable.defaults.footerCallback
10672
+ *
10673
+ * @example
10674
+ * $(document).ready( function() {
10675
+ * $('#example').wfDataTable( {
10676
+ * "footerCallback": function( tfoot, data, start, end, display ) {
10677
+ * tfoot.getElementsByTagName('th')[0].innerHTML = "Starting index is "+start;
10678
+ * }
10679
+ * } );
10680
+ * } )
10681
+ */
10682
+ "fnFooterCallback": null,
10683
+
10684
+
10685
+ /**
10686
+ * When rendering large numbers in the information element for the table
10687
+ * (i.e. "Showing 1 to 10 of 57 entries") WFDataTables will render large numbers
10688
+ * to have a comma separator for the 'thousands' units (e.g. 1 million is
10689
+ * rendered as "1,000,000") to help readability for the end user. This
10690
+ * function will override the default method WFDataTables uses.
10691
+ * @type function
10692
+ * @member
10693
+ * @param {int} toFormat number to be formatted
10694
+ * @returns {string} formatted string for WFDataTables to show the number
10695
+ *
10696
+ * @dtopt Callbacks
10697
+ * @name WFDataTable.defaults.formatNumber
10698
+ *
10699
+ * @example
10700
+ * // Format a number using a single quote for the separator (note that
10701
+ * // this can also be done with the language.thousands option)
10702
+ * $(document).ready( function() {
10703
+ * $('#example').wfDataTable( {
10704
+ * "formatNumber": function ( toFormat ) {
10705
+ * return toFormat.toString().replace(
10706
+ * /\B(?=(\d{3})+(?!\d))/g, "'"
10707
+ * );
10708
+ * };
10709
+ * } );
10710
+ * } );
10711
+ */
10712
+ "fnFormatNumber": function ( toFormat ) {
10713
+ return toFormat.toString().replace(
10714
+ /\B(?=(\d{3})+(?!\d))/g,
10715
+ this.oLanguage.sThousands
10716
+ );
10717
+ },
10718
+
10719
+
10720
+ /**
10721
+ * This function is called on every 'draw' event, and allows you to
10722
+ * dynamically modify the header row. This can be used to calculate and
10723
+ * display useful information about the table.
10724
+ * @type function
10725
+ * @param {node} head "TR" element for the header
10726
+ * @param {array} data Full table data (as derived from the original HTML)
10727
+ * @param {int} start Index for the current display starting point in the
10728
+ * display array
10729
+ * @param {int} end Index for the current display ending point in the
10730
+ * display array
10731
+ * @param {array int} display Index array to translate the visual position
10732
+ * to the full data array
10733
+ *
10734
+ * @dtopt Callbacks
10735
+ * @name WFDataTable.defaults.headerCallback
10736
+ *
10737
+ * @example
10738
+ * $(document).ready( function() {
10739
+ * $('#example').wfDataTable( {
10740
+ * "fheaderCallback": function( head, data, start, end, display ) {
10741
+ * head.getElementsByTagName('th')[0].innerHTML = "Displaying "+(end-start)+" records";
10742
+ * }
10743
+ * } );
10744
+ * } )
10745
+ */
10746
+ "fnHeaderCallback": null,
10747
+
10748
+
10749
+ /**
10750
+ * The information element can be used to convey information about the current
10751
+ * state of the table. Although the internationalisation options presented by
10752
+ * WFDataTables are quite capable of dealing with most customisations, there may
10753
+ * be times where you wish to customise the string further. This callback
10754
+ * allows you to do exactly that.
10755
+ * @type function
10756
+ * @param {object} oSettings WFDataTables settings object
10757
+ * @param {int} start Starting position in data for the draw
10758
+ * @param {int} end End position in data for the draw
10759
+ * @param {int} max Total number of rows in the table (regardless of
10760
+ * filtering)
10761
+ * @param {int} total Total number of rows in the data set, after filtering
10762
+ * @param {string} pre The string that WFDataTables has formatted using it's
10763
+ * own rules
10764
+ * @returns {string} The string to be displayed in the information element.
10765
+ *
10766
+ * @dtopt Callbacks
10767
+ * @name WFDataTable.defaults.infoCallback
10768
+ *
10769
+ * @example
10770
+ * $('#example').wfDataTable( {
10771
+ * "infoCallback": function( settings, start, end, max, total, pre ) {
10772
+ * return start +" to "+ end;
10773
+ * }
10774
+ * } );
10775
+ */
10776
+ "fnInfoCallback": null,
10777
+
10778
+
10779
+ /**
10780
+ * Called when the table has been initialised. Normally WFDataTables will
10781
+ * initialise sequentially and there will be no need for this function,
10782
+ * however, this does not hold true when using external language information
10783
+ * since that is obtained using an async XHR call.
10784
+ * @type function
10785
+ * @param {object} settings WFDataTables settings object
10786
+ * @param {object} json The JSON object request from the server - only
10787
+ * present if client-side Ajax sourced data is used
10788
+ *
10789
+ * @dtopt Callbacks
10790
+ * @name WFDataTable.defaults.initComplete
10791
+ *
10792
+ * @example
10793
+ * $(document).ready( function() {
10794
+ * $('#example').wfDataTable( {
10795
+ * "initComplete": function(settings, json) {
10796
+ * alert( 'WFDataTables has finished its initialisation.' );
10797
+ * }
10798
+ * } );
10799
+ * } )
10800
+ */
10801
+ "fnInitComplete": null,
10802
+
10803
+
10804
+ /**
10805
+ * Called at the very start of each table draw and can be used to cancel the
10806
+ * draw by returning false, any other return (including undefined) results in
10807
+ * the full draw occurring).
10808
+ * @type function
10809
+ * @param {object} settings WFDataTables settings object
10810
+ * @returns {boolean} False will cancel the draw, anything else (including no
10811
+ * return) will allow it to complete.
10812
+ *
10813
+ * @dtopt Callbacks
10814
+ * @name WFDataTable.defaults.preDrawCallback
10815
+ *
10816
+ * @example
10817
+ * $(document).ready( function() {
10818
+ * $('#example').wfDataTable( {
10819
+ * "preDrawCallback": function( settings ) {
10820
+ * if ( $('#test').val() == 1 ) {
10821
+ * return false;
10822
+ * }
10823
+ * }
10824
+ * } );
10825
+ * } );
10826
+ */
10827
+ "fnPreDrawCallback": null,
10828
+
10829
+
10830
+ /**
10831
+ * This function allows you to 'post process' each row after it have been
10832
+ * generated for each table draw, but before it is rendered on screen. This
10833
+ * function might be used for setting the row class name etc.
10834
+ * @type function
10835
+ * @param {node} row "TR" element for the current row
10836
+ * @param {array} data Raw data array for this row
10837
+ * @param {int} displayIndex The display index for the current table draw
10838
+ * @param {int} displayIndexFull The index of the data in the full list of
10839
+ * rows (after filtering)
10840
+ *
10841
+ * @dtopt Callbacks
10842
+ * @name WFDataTable.defaults.rowCallback
10843
+ *
10844
+ * @example
10845
+ * $(document).ready( function() {
10846
+ * $('#example').wfDataTable( {
10847
+ * "rowCallback": function( row, data, displayIndex, displayIndexFull ) {
10848
+ * // Bold the grade for all 'A' grade browsers
10849
+ * if ( data[4] == "A" ) {
10850
+ * $('td:eq(4)', row).html( '<b>A</b>' );
10851
+ * }
10852
+ * }
10853
+ * } );
10854
+ * } );
10855
+ */
10856
+ "fnRowCallback": null,
10857
+
10858
+
10859
+ /**
10860
+ * __Deprecated__ The functionality provided by this parameter has now been
10861
+ * superseded by that provided through `ajax`, which should be used instead.
10862
+ *
10863
+ * This parameter allows you to override the default function which obtains
10864
+ * the data from the server so something more suitable for your application.
10865
+ * For example you could use POST data, or pull information from a Gears or
10866
+ * AIR database.
10867
+ * @type function
10868
+ * @member
10869
+ * @param {string} source HTTP source to obtain the data from (`ajax`)
10870
+ * @param {array} data A key/value pair object containing the data to send
10871
+ * to the server
10872
+ * @param {function} callback to be called on completion of the data get
10873
+ * process that will draw the data on the page.
10874
+ * @param {object} settings WFDataTables settings object
10875
+ *
10876
+ * @dtopt Callbacks
10877
+ * @dtopt Server-side
10878
+ * @name WFDataTable.defaults.serverData
10879
+ *
10880
+ * @deprecated 1.10. Please use `ajax` for this functionality now.
10881
+ */
10882
+ "fnServerData": null,
10883
+
10884
+
10885
+ /**
10886
+ * __Deprecated__ The functionality provided by this parameter has now been
10887
+ * superseded by that provided through `ajax`, which should be used instead.
10888
+ *
10889
+ * It is often useful to send extra data to the server when making an Ajax
10890
+ * request - for example custom filtering information, and this callback
10891
+ * function makes it trivial to send extra information to the server. The
10892
+ * passed in parameter is the data set that has been constructed by
10893
+ * WFDataTables, and you can add to this or modify it as you require.
10894
+ * @type function
10895
+ * @param {array} data Data array (array of objects which are name/value
10896
+ * pairs) that has been constructed by WFDataTables and will be sent to the
10897
+ * server. In the case of Ajax sourced data with server-side processing
10898
+ * this will be an empty array, for server-side processing there will be a
10899
+ * significant number of parameters!
10900
+ * @returns {undefined} Ensure that you modify the data array passed in,
10901
+ * as this is passed by reference.
10902
+ *
10903
+ * @dtopt Callbacks
10904
+ * @dtopt Server-side
10905
+ * @name WFDataTable.defaults.serverParams
10906
+ *
10907
+ * @deprecated 1.10. Please use `ajax` for this functionality now.
10908
+ */
10909
+ "fnServerParams": null,
10910
+
10911
+
10912
+ /**
10913
+ * Load the table state. With this function you can define from where, and how, the
10914
+ * state of a table is loaded. By default WFDataTables will load from `localStorage`
10915
+ * but you might wish to use a server-side database or cookies.
10916
+ * @type function
10917
+ * @member
10918
+ * @param {object} settings WFDataTables settings object
10919
+ * @param {object} callback Callback that can be executed when done. It
10920
+ * should be passed the loaded state object.
10921
+ * @return {object} The WFDataTables state object to be loaded
10922
+ *
10923
+ * @dtopt Callbacks
10924
+ * @name WFDataTable.defaults.stateLoadCallback
10925
+ *
10926
+ * @example
10927
+ * $(document).ready( function() {
10928
+ * $('#example').wfDataTable( {
10929
+ * "stateSave": true,
10930
+ * "stateLoadCallback": function (settings, callback) {
10931
+ * $.ajax( {
10932
+ * "url": "/state_load",
10933
+ * "dataType": "json",
10934
+ * "success": function (json) {
10935
+ * callback( json );
10936
+ * }
10937
+ * } );
10938
+ * }
10939
+ * } );
10940
+ * } );
10941
+ */
10942
+ "fnStateLoadCallback": function ( settings ) {
10943
+ try {
10944
+ return JSON.parse(
10945
+ (settings.iStateDuration === -1 ? sessionStorage : localStorage).getItem(
10946
+ 'WFDataTables_'+settings.sInstance+'_'+location.pathname
10947
+ )
10948
+ );
10949
+ } catch (e) {}
10950
+ },
10951
+
10952
+
10953
+ /**
10954
+ * Callback which allows modification of the saved state prior to loading that state.
10955
+ * This callback is called when the table is loading state from the stored data, but
10956
+ * prior to the settings object being modified by the saved state. Note that for
10957
+ * plug-in authors, you should use the `stateLoadParams` event to load parameters for
10958
+ * a plug-in.
10959
+ * @type function
10960
+ * @param {object} settings WFDataTables settings object
10961
+ * @param {object} data The state object that is to be loaded
10962
+ *
10963
+ * @dtopt Callbacks
10964
+ * @name WFDataTable.defaults.stateLoadParams
10965
+ *
10966
+ * @example
10967
+ * // Remove a saved filter, so filtering is never loaded
10968
+ * $(document).ready( function() {
10969
+ * $('#example').wfDataTable( {
10970
+ * "stateSave": true,
10971
+ * "stateLoadParams": function (settings, data) {
10972
+ * data.oSearch.sSearch = "";
10973
+ * }
10974
+ * } );
10975
+ * } );
10976
+ *
10977
+ * @example
10978
+ * // Disallow state loading by returning false
10979
+ * $(document).ready( function() {
10980
+ * $('#example').wfDataTable( {
10981
+ * "stateSave": true,
10982
+ * "stateLoadParams": function (settings, data) {
10983
+ * return false;
10984
+ * }
10985
+ * } );
10986
+ * } );
10987
+ */
10988
+ "fnStateLoadParams": null,
10989
+
10990
+
10991
+ /**
10992
+ * Callback that is called when the state has been loaded from the state saving method
10993
+ * and the WFDataTables settings object has been modified as a result of the loaded state.
10994
+ * @type function
10995
+ * @param {object} settings WFDataTables settings object
10996
+ * @param {object} data The state object that was loaded
10997
+ *
10998
+ * @dtopt Callbacks
10999
+ * @name WFDataTable.defaults.stateLoaded
11000
+ *
11001
+ * @example
11002
+ * // Show an alert with the filtering value that was saved
11003
+ * $(document).ready( function() {
11004
+ * $('#example').wfDataTable( {
11005
+ * "stateSave": true,
11006
+ * "stateLoaded": function (settings, data) {
11007
+ * alert( 'Saved filter was: '+data.oSearch.sSearch );
11008
+ * }
11009
+ * } );
11010
+ * } );
11011
+ */
11012
+ "fnStateLoaded": null,
11013
+
11014
+
11015
+ /**
11016
+ * Save the table state. This function allows you to define where and how the state
11017
+ * information for the table is stored By default WFDataTables will use `localStorage`
11018
+ * but you might wish to use a server-side database or cookies.
11019
+ * @type function
11020
+ * @member
11021
+ * @param {object} settings WFDataTables settings object
11022
+ * @param {object} data The state object to be saved
11023
+ *
11024
+ * @dtopt Callbacks
11025
+ * @name WFDataTable.defaults.stateSaveCallback
11026
+ *
11027
+ * @example
11028
+ * $(document).ready( function() {
11029
+ * $('#example').wfDataTable( {
11030
+ * "stateSave": true,
11031
+ * "stateSaveCallback": function (settings, data) {
11032
+ * // Send an Ajax request to the server with the state object
11033
+ * $.ajax( {
11034
+ * "url": "/state_save",
11035
+ * "data": data,
11036
+ * "dataType": "json",
11037
+ * "method": "POST"
11038
+ * "success": function () {}
11039
+ * } );
11040
+ * }
11041
+ * } );
11042
+ * } );
11043
+ */
11044
+ "fnStateSaveCallback": function ( settings, data ) {
11045
+ try {
11046
+ (settings.iStateDuration === -1 ? sessionStorage : localStorage).setItem(
11047
+ 'WFDataTables_'+settings.sInstance+'_'+location.pathname,
11048
+ JSON.stringify( data )
11049
+ );
11050
+ } catch (e) {}
11051
+ },
11052
+
11053
+
11054
+ /**
11055
+ * Callback which allows modification of the state to be saved. Called when the table
11056
+ * has changed state a new state save is required. This method allows modification of
11057
+ * the state saving object prior to actually doing the save, including addition or
11058
+ * other state properties or modification. Note that for plug-in authors, you should
11059
+ * use the `stateSaveParams` event to save parameters for a plug-in.
11060
+ * @type function
11061
+ * @param {object} settings WFDataTables settings object
11062
+ * @param {object} data The state object to be saved
11063
+ *
11064
+ * @dtopt Callbacks
11065
+ * @name WFDataTable.defaults.stateSaveParams
11066
+ *
11067
+ * @example
11068
+ * // Remove a saved filter, so filtering is never saved
11069
+ * $(document).ready( function() {
11070
+ * $('#example').wfDataTable( {
11071
+ * "stateSave": true,
11072
+ * "stateSaveParams": function (settings, data) {
11073
+ * data.oSearch.sSearch = "";
11074
+ * }
11075
+ * } );
11076
+ * } );
11077
+ */
11078
+ "fnStateSaveParams": null,
11079
+
11080
+
11081
+ /**
11082
+ * Duration for which the saved state information is considered valid. After this period
11083
+ * has elapsed the state will be returned to the default.
11084
+ * Value is given in seconds.
11085
+ * @type int
11086
+ * @default 7200 <i>(2 hours)</i>
11087
+ *
11088
+ * @dtopt Options
11089
+ * @name WFDataTable.defaults.stateDuration
11090
+ *
11091
+ * @example
11092
+ * $(document).ready( function() {
11093
+ * $('#example').wfDataTable( {
11094
+ * "stateDuration": 60*60*24; // 1 day
11095
+ * } );
11096
+ * } )
11097
+ */
11098
+ "iStateDuration": 7200,
11099
+
11100
+
11101
+ /**
11102
+ * When enabled WFDataTables will not make a request to the server for the first
11103
+ * page draw - rather it will use the data already on the page (no sorting etc
11104
+ * will be applied to it), thus saving on an XHR at load time. `deferLoading`
11105
+ * is used to indicate that deferred loading is required, but it is also used
11106
+ * to tell WFDataTables how many records there are in the full table (allowing
11107
+ * the information element and pagination to be displayed correctly). In the case
11108
+ * where a filtering is applied to the table on initial load, this can be
11109
+ * indicated by giving the parameter as an array, where the first element is
11110
+ * the number of records available after filtering and the second element is the
11111
+ * number of records without filtering (allowing the table information element
11112
+ * to be shown correctly).
11113
+ * @type int | array
11114
+ * @default null
11115
+ *
11116
+ * @dtopt Options
11117
+ * @name WFDataTable.defaults.deferLoading
11118
+ *
11119
+ * @example
11120
+ * // 57 records available in the table, no filtering applied
11121
+ * $(document).ready( function() {
11122
+ * $('#example').wfDataTable( {
11123
+ * "serverSide": true,
11124
+ * "ajax": "scripts/server_processing.php",
11125
+ * "deferLoading": 57
11126
+ * } );
11127
+ * } );
11128
+ *
11129
+ * @example
11130
+ * // 57 records after filtering, 100 without filtering (an initial filter applied)
11131
+ * $(document).ready( function() {
11132
+ * $('#example').wfDataTable( {
11133
+ * "serverSide": true,
11134
+ * "ajax": "scripts/server_processing.php",
11135
+ * "deferLoading": [ 57, 100 ],
11136
+ * "search": {
11137
+ * "search": "my_filter"
11138
+ * }
11139
+ * } );
11140
+ * } );
11141
+ */
11142
+ "iDeferLoading": null,
11143
+
11144
+
11145
+ /**
11146
+ * Number of rows to display on a single page when using pagination. If
11147
+ * feature enabled (`lengthChange`) then the end user will be able to override
11148
+ * this to a custom setting using a pop-up menu.
11149
+ * @type int
11150
+ * @default 10
11151
+ *
11152
+ * @dtopt Options
11153
+ * @name WFDataTable.defaults.pageLength
11154
+ *
11155
+ * @example
11156
+ * $(document).ready( function() {
11157
+ * $('#example').wfDataTable( {
11158
+ * "pageLength": 50
11159
+ * } );
11160
+ * } )
11161
+ */
11162
+ "iDisplayLength": 10,
11163
+
11164
+
11165
+ /**
11166
+ * Define the starting point for data display when using WFDataTables with
11167
+ * pagination. Note that this parameter is the number of records, rather than
11168
+ * the page number, so if you have 10 records per page and want to start on
11169
+ * the third page, it should be "20".
11170
+ * @type int
11171
+ * @default 0
11172
+ *
11173
+ * @dtopt Options
11174
+ * @name WFDataTable.defaults.displayStart
11175
+ *
11176
+ * @example
11177
+ * $(document).ready( function() {
11178
+ * $('#example').wfDataTable( {
11179
+ * "displayStart": 20
11180
+ * } );
11181
+ * } )
11182
+ */
11183
+ "iDisplayStart": 0,
11184
+
11185
+
11186
+ /**
11187
+ * By default WFDataTables allows keyboard navigation of the table (sorting, paging,
11188
+ * and filtering) by adding a `tabindex` attribute to the required elements. This
11189
+ * allows you to tab through the controls and press the enter key to activate them.
11190
+ * The tabindex is default 0, meaning that the tab follows the flow of the document.
11191
+ * You can overrule this using this parameter if you wish. Use a value of -1 to
11192
+ * disable built-in keyboard navigation.
11193
+ * @type int
11194
+ * @default 0
11195
+ *
11196
+ * @dtopt Options
11197
+ * @name WFDataTable.defaults.tabIndex
11198
+ *
11199
+ * @example
11200
+ * $(document).ready( function() {
11201
+ * $('#example').wfDataTable( {
11202
+ * "tabIndex": 1
11203
+ * } );
11204
+ * } );
11205
+ */
11206
+ "iTabIndex": 0,
11207
+
11208
+
11209
+ /**
11210
+ * Classes that WFDataTables assigns to the various components and features
11211
+ * that it adds to the HTML table. This allows classes to be configured
11212
+ * during initialisation in addition to through the static
11213
+ * {@link WFDataTable.ext.oStdClasses} object).
11214
+ * @namespace
11215
+ * @name WFDataTable.defaults.classes
11216
+ */
11217
+ "oClasses": {},
11218
+
11219
+
11220
+ /**
11221
+ * All strings that WFDataTables uses in the user interface that it creates
11222
+ * are defined in this object, allowing you to modified them individually or
11223
+ * completely replace them all as required.
11224
+ * @namespace
11225
+ * @name WFDataTable.defaults.language
11226
+ */
11227
+ "oLanguage": {
11228
+ /**
11229
+ * Strings that are used for WAI-ARIA labels and controls only (these are not
11230
+ * actually visible on the page, but will be read by screenreaders, and thus
11231
+ * must be internationalised as well).
11232
+ * @namespace
11233
+ * @name WFDataTable.defaults.language.aria
11234
+ */
11235
+ "oAria": {
11236
+ /**
11237
+ * ARIA label that is added to the table headers when the column may be
11238
+ * sorted ascending by activing the column (click or return when focused).
11239
+ * Note that the column header is prefixed to this string.
11240
+ * @type string
11241
+ * @default : activate to sort column ascending
11242
+ *
11243
+ * @dtopt Language
11244
+ * @name WFDataTable.defaults.language.aria.sortAscending
11245
+ *
11246
+ * @example
11247
+ * $(document).ready( function() {
11248
+ * $('#example').wfDataTable( {
11249
+ * "language": {
11250
+ * "aria": {
11251
+ * "sortAscending": " - click/return to sort ascending"
11252
+ * }
11253
+ * }
11254
+ * } );
11255
+ * } );
11256
+ */
11257
+ "sSortAscending": ": activate to sort column ascending",
11258
+
11259
+ /**
11260
+ * ARIA label that is added to the table headers when the column may be
11261
+ * sorted descending by activing the column (click or return when focused).
11262
+ * Note that the column header is prefixed to this string.
11263
+ * @type string
11264
+ * @default : activate to sort column ascending
11265
+ *
11266
+ * @dtopt Language
11267
+ * @name WFDataTable.defaults.language.aria.sortDescending
11268
+ *
11269
+ * @example
11270
+ * $(document).ready( function() {
11271
+ * $('#example').wfDataTable( {
11272
+ * "language": {
11273
+ * "aria": {
11274
+ * "sortDescending": " - click/return to sort descending"
11275
+ * }
11276
+ * }
11277
+ * } );
11278
+ * } );
11279
+ */
11280
+ "sSortDescending": ": activate to sort column descending"
11281
+ },
11282
+
11283
+ /**
11284
+ * Pagination string used by WFDataTables for the built-in pagination
11285
+ * control types.
11286
+ * @namespace
11287
+ * @name WFDataTable.defaults.language.paginate
11288
+ */
11289
+ "oPaginate": {
11290
+ /**
11291
+ * Text to use when using the 'full_numbers' type of pagination for the
11292
+ * button to take the user to the first page.
11293
+ * @type string
11294
+ * @default First
11295
+ *
11296
+ * @dtopt Language
11297
+ * @name WFDataTable.defaults.language.paginate.first
11298
+ *
11299
+ * @example
11300
+ * $(document).ready( function() {
11301
+ * $('#example').wfDataTable( {
11302
+ * "language": {
11303
+ * "paginate": {
11304
+ * "first": "First page"
11305
+ * }
11306
+ * }
11307
+ * } );
11308
+ * } );
11309
+ */
11310
+ "sFirst": "First",
11311
+
11312
+
11313
+ /**
11314
+ * Text to use when using the 'full_numbers' type of pagination for the
11315
+ * button to take the user to the last page.
11316
+ * @type string
11317
+ * @default Last
11318
+ *
11319
+ * @dtopt Language
11320
+ * @name WFDataTable.defaults.language.paginate.last
11321
+ *
11322
+ * @example
11323
+ * $(document).ready( function() {
11324
+ * $('#example').wfDataTable( {
11325
+ * "language": {
11326
+ * "paginate": {
11327
+ * "last": "Last page"
11328
+ * }
11329
+ * }
11330
+ * } );
11331
+ * } );
11332
+ */
11333
+ "sLast": "Last",
11334
+
11335
+
11336
+ /**
11337
+ * Text to use for the 'next' pagination button (to take the user to the
11338
+ * next page).
11339
+ * @type string
11340
+ * @default Next
11341
+ *
11342
+ * @dtopt Language
11343
+ * @name WFDataTable.defaults.language.paginate.next
11344
+ *
11345
+ * @example
11346
+ * $(document).ready( function() {
11347
+ * $('#example').wfDataTable( {
11348
+ * "language": {
11349
+ * "paginate": {
11350
+ * "next": "Next page"
11351
+ * }
11352
+ * }
11353
+ * } );
11354
+ * } );
11355
+ */
11356
+ "sNext": "Next",
11357
+
11358
+
11359
+ /**
11360
+ * Text to use for the 'previous' pagination button (to take the user to
11361
+ * the previous page).
11362
+ * @type string
11363
+ * @default Previous
11364
+ *
11365
+ * @dtopt Language
11366
+ * @name WFDataTable.defaults.language.paginate.previous
11367
+ *
11368
+ * @example
11369
+ * $(document).ready( function() {
11370
+ * $('#example').wfDataTable( {
11371
+ * "language": {
11372
+ * "paginate": {
11373
+ * "previous": "Previous page"
11374
+ * }
11375
+ * }
11376
+ * } );
11377
+ * } );
11378
+ */
11379
+ "sPrevious": "Previous"
11380
+ },
11381
+
11382
+ /**
11383
+ * This string is shown in preference to `zeroRecords` when the table is
11384
+ * empty of data (regardless of filtering). Note that this is an optional
11385
+ * parameter - if it is not given, the value of `zeroRecords` will be used
11386
+ * instead (either the default or given value).
11387
+ * @type string
11388
+ * @default No data available in table
11389
+ *
11390
+ * @dtopt Language
11391
+ * @name WFDataTable.defaults.language.emptyTable
11392
+ *
11393
+ * @example
11394
+ * $(document).ready( function() {
11395
+ * $('#example').wfDataTable( {
11396
+ * "language": {
11397
+ * "emptyTable": "No data available in table"
11398
+ * }
11399
+ * } );
11400
+ * } );
11401
+ */
11402
+ "sEmptyTable": "No data available in table",
11403
+
11404
+
11405
+ /**
11406
+ * This string gives information to the end user about the information
11407
+ * that is current on display on the page. The following tokens can be
11408
+ * used in the string and will be dynamically replaced as the table
11409
+ * display updates. This tokens can be placed anywhere in the string, or
11410
+ * removed as needed by the language requires:
11411
+ *
11412
+ * * `\_START\_` - Display index of the first record on the current page
11413
+ * * `\_END\_` - Display index of the last record on the current page
11414
+ * * `\_TOTAL\_` - Number of records in the table after filtering
11415
+ * * `\_MAX\_` - Number of records in the table without filtering
11416
+ * * `\_PAGE\_` - Current page number
11417
+ * * `\_PAGES\_` - Total number of pages of data in the table
11418
+ *
11419
+ * @type string
11420
+ * @default Showing _START_ to _END_ of _TOTAL_ entries
11421
+ *
11422
+ * @dtopt Language
11423
+ * @name WFDataTable.defaults.language.info
11424
+ *
11425
+ * @example
11426
+ * $(document).ready( function() {
11427
+ * $('#example').wfDataTable( {
11428
+ * "language": {
11429
+ * "info": "Showing page _PAGE_ of _PAGES_"
11430
+ * }
11431
+ * } );
11432
+ * } );
11433
+ */
11434
+ "sInfo": "Showing _START_ to _END_ of _TOTAL_ entries",
11435
+
11436
+
11437
+ /**
11438
+ * Display information string for when the table is empty. Typically the
11439
+ * format of this string should match `info`.
11440
+ * @type string
11441
+ * @default Showing 0 to 0 of 0 entries
11442
+ *
11443
+ * @dtopt Language
11444
+ * @name WFDataTable.defaults.language.infoEmpty
11445
+ *
11446
+ * @example
11447
+ * $(document).ready( function() {
11448
+ * $('#example').wfDataTable( {
11449
+ * "language": {
11450
+ * "infoEmpty": "No entries to show"
11451
+ * }
11452
+ * } );
11453
+ * } );
11454
+ */
11455
+ "sInfoEmpty": "Showing 0 to 0 of 0 entries",
11456
+
11457
+
11458
+ /**
11459
+ * When a user filters the information in a table, this string is appended
11460
+ * to the information (`info`) to give an idea of how strong the filtering
11461
+ * is. The variable _MAX_ is dynamically updated.
11462
+ * @type string
11463
+ * @default (filtered from _MAX_ total entries)
11464
+ *
11465
+ * @dtopt Language
11466
+ * @name WFDataTable.defaults.language.infoFiltered
11467
+ *
11468
+ * @example
11469
+ * $(document).ready( function() {
11470
+ * $('#example').wfDataTable( {
11471
+ * "language": {
11472
+ * "infoFiltered": " - filtering from _MAX_ records"
11473
+ * }
11474
+ * } );
11475
+ * } );
11476
+ */
11477
+ "sInfoFiltered": "(filtered from _MAX_ total entries)",
11478
+
11479
+
11480
+ /**
11481
+ * If can be useful to append extra information to the info string at times,
11482
+ * and this variable does exactly that. This information will be appended to
11483
+ * the `info` (`infoEmpty` and `infoFiltered` in whatever combination they are
11484
+ * being used) at all times.
11485
+ * @type string
11486
+ * @default <i>Empty string</i>
11487
+ *
11488
+ * @dtopt Language
11489
+ * @name WFDataTable.defaults.language.infoPostFix
11490
+ *
11491
+ * @example
11492
+ * $(document).ready( function() {
11493
+ * $('#example').wfDataTable( {
11494
+ * "language": {
11495
+ * "infoPostFix": "All records shown are derived from real information."
11496
+ * }
11497
+ * } );
11498
+ * } );
11499
+ */
11500
+ "sInfoPostFix": "",
11501
+
11502
+
11503
+ /**
11504
+ * This decimal place operator is a little different from the other
11505
+ * language options since WFDataTables doesn't output floating point
11506
+ * numbers, so it won't ever use this for display of a number. Rather,
11507
+ * what this parameter does is modify the sort methods of the table so
11508
+ * that numbers which are in a format which has a character other than
11509
+ * a period (`.`) as a decimal place will be sorted numerically.
11510
+ *
11511
+ * Note that numbers with different decimal places cannot be shown in
11512
+ * the same table and still be sortable, the table must be consistent.
11513
+ * However, multiple different tables on the page can use different
11514
+ * decimal place characters.
11515
+ * @type string
11516
+ * @default
11517
+ *
11518
+ * @dtopt Language
11519
+ * @name WFDataTable.defaults.language.decimal
11520
+ *
11521
+ * @example
11522
+ * $(document).ready( function() {
11523
+ * $('#example').wfDataTable( {
11524
+ * "language": {
11525
+ * "decimal": ","
11526
+ * "thousands": "."
11527
+ * }
11528
+ * } );
11529
+ * } );
11530
+ */
11531
+ "sDecimal": "",
11532
+
11533
+
11534
+ /**
11535
+ * WFDataTables has a build in number formatter (`formatNumber`) which is
11536
+ * used to format large numbers that are used in the table information.
11537
+ * By default a comma is used, but this can be trivially changed to any
11538
+ * character you wish with this parameter.
11539
+ * @type string
11540
+ * @default ,
11541
+ *
11542
+ * @dtopt Language
11543
+ * @name WFDataTable.defaults.language.thousands
11544
+ *
11545
+ * @example
11546
+ * $(document).ready( function() {
11547
+ * $('#example').wfDataTable( {
11548
+ * "language": {
11549
+ * "thousands": "'"
11550
+ * }
11551
+ * } );
11552
+ * } );
11553
+ */
11554
+ "sThousands": ",",
11555
+
11556
+
11557
+ /**
11558
+ * Detail the action that will be taken when the drop down menu for the
11559
+ * pagination length option is changed. The '_MENU_' variable is replaced
11560
+ * with a default select list of 10, 25, 50 and 100, and can be replaced
11561
+ * with a custom select box if required.
11562
+ * @type string
11563
+ * @default Show _MENU_ entries
11564
+ *
11565
+ * @dtopt Language
11566
+ * @name WFDataTable.defaults.language.lengthMenu
11567
+ *
11568
+ * @example
11569
+ * // Language change only
11570
+ * $(document).ready( function() {
11571
+ * $('#example').wfDataTable( {
11572
+ * "language": {
11573
+ * "lengthMenu": "Display _MENU_ records"
11574
+ * }
11575
+ * } );
11576
+ * } );
11577
+ *
11578
+ * @example
11579
+ * // Language and options change
11580
+ * $(document).ready( function() {
11581
+ * $('#example').wfDataTable( {
11582
+ * "language": {
11583
+ * "lengthMenu": 'Display <select>'+
11584
+ * '<option value="10">10</option>'+
11585
+ * '<option value="20">20</option>'+
11586
+ * '<option value="30">30</option>'+
11587
+ * '<option value="40">40</option>'+
11588
+ * '<option value="50">50</option>'+
11589
+ * '<option value="-1">All</option>'+
11590
+ * '</select> records'
11591
+ * }
11592
+ * } );
11593
+ * } );
11594
+ */
11595
+ "sLengthMenu": "Show _MENU_ entries",
11596
+
11597
+
11598
+ /**
11599
+ * When using Ajax sourced data and during the first draw when WFDataTables is
11600
+ * gathering the data, this message is shown in an empty row in the table to
11601
+ * indicate to the end user the the data is being loaded. Note that this
11602
+ * parameter is not used when loading data by server-side processing, just
11603
+ * Ajax sourced data with client-side processing.
11604
+ * @type string
11605
+ * @default Loading...
11606
+ *
11607
+ * @dtopt Language
11608
+ * @name WFDataTable.defaults.language.loadingRecords
11609
+ *
11610
+ * @example
11611
+ * $(document).ready( function() {
11612
+ * $('#example').wfDataTable( {
11613
+ * "language": {
11614
+ * "loadingRecords": "Please wait - loading..."
11615
+ * }
11616
+ * } );
11617
+ * } );
11618
+ */
11619
+ "sLoadingRecords": "Loading...",
11620
+
11621
+
11622
+ /**
11623
+ * Text which is displayed when the table is processing a user action
11624
+ * (usually a sort command or similar).
11625
+ * @type string
11626
+ * @default Processing...
11627
+ *
11628
+ * @dtopt Language
11629
+ * @name WFDataTable.defaults.language.processing
11630
+ *
11631
+ * @example
11632
+ * $(document).ready( function() {
11633
+ * $('#example').wfDataTable( {
11634
+ * "language": {
11635
+ * "processing": "WFDataTables is currently busy"
11636
+ * }
11637
+ * } );
11638
+ * } );
11639
+ */
11640
+ "sProcessing": "Processing...",
11641
+
11642
+
11643
+ /**
11644
+ * Details the actions that will be taken when the user types into the
11645
+ * filtering input text box. The variable "_INPUT_", if used in the string,
11646
+ * is replaced with the HTML text box for the filtering input allowing
11647
+ * control over where it appears in the string. If "_INPUT_" is not given
11648
+ * then the input box is appended to the string automatically.
11649
+ * @type string
11650
+ * @default Search:
11651
+ *
11652
+ * @dtopt Language
11653
+ * @name WFDataTable.defaults.language.search
11654
+ *
11655
+ * @example
11656
+ * // Input text box will be appended at the end automatically
11657
+ * $(document).ready( function() {
11658
+ * $('#example').wfDataTable( {
11659
+ * "language": {
11660
+ * "search": "Filter records:"
11661
+ * }
11662
+ * } );
11663
+ * } );
11664
+ *
11665
+ * @example
11666
+ * // Specify where the filter should appear
11667
+ * $(document).ready( function() {
11668
+ * $('#example').wfDataTable( {
11669
+ * "language": {
11670
+ * "search": "Apply filter _INPUT_ to table"
11671
+ * }
11672
+ * } );
11673
+ * } );
11674
+ */
11675
+ "sSearch": "Search:",
11676
+
11677
+
11678
+ /**
11679
+ * Assign a `placeholder` attribute to the search `input` element
11680
+ * @type string
11681
+ * @default
11682
+ *
11683
+ * @dtopt Language
11684
+ * @name WFDataTable.defaults.language.searchPlaceholder
11685
+ */
11686
+ "sSearchPlaceholder": "",
11687
+
11688
+
11689
+ /**
11690
+ * All of the language information can be stored in a file on the
11691
+ * server-side, which WFDataTables will look up if this parameter is passed.
11692
+ * It must store the URL of the language file, which is in a JSON format,
11693
+ * and the object has the same properties as the oLanguage object in the
11694
+ * initialiser object (i.e. the above parameters). Please refer to one of
11695
+ * the example language files to see how this works in action.
11696
+ * @type string
11697
+ * @default <i>Empty string - i.e. disabled</i>
11698
+ *
11699
+ * @dtopt Language
11700
+ * @name WFDataTable.defaults.language.url
11701
+ *
11702
+ * @example
11703
+ * $(document).ready( function() {
11704
+ * $('#example').wfDataTable( {
11705
+ * "language": {
11706
+ * "url": "http://www.sprymedia.co.uk/dataTables/lang.txt"
11707
+ * }
11708
+ * } );
11709
+ * } );
11710
+ */
11711
+ "sUrl": "",
11712
+
11713
+
11714
+ /**
11715
+ * Text shown inside the table records when the is no information to be
11716
+ * displayed after filtering. `emptyTable` is shown when there is simply no
11717
+ * information in the table at all (regardless of filtering).
11718
+ * @type string
11719
+ * @default No matching records found
11720
+ *
11721
+ * @dtopt Language
11722
+ * @name WFDataTable.defaults.language.zeroRecords
11723
+ *
11724
+ * @example
11725
+ * $(document).ready( function() {
11726
+ * $('#example').wfDataTable( {
11727
+ * "language": {
11728
+ * "zeroRecords": "No records to display"
11729
+ * }
11730
+ * } );
11731
+ * } );
11732
+ */
11733
+ "sZeroRecords": "No matching records found"
11734
+ },
11735
+
11736
+
11737
+ /**
11738
+ * This parameter allows you to have define the global filtering state at
11739
+ * initialisation time. As an object the `search` parameter must be
11740
+ * defined, but all other parameters are optional. When `regex` is true,
11741
+ * the search string will be treated as a regular expression, when false
11742
+ * (default) it will be treated as a straight string. When `smart`
11743
+ * WFDataTables will use it's smart filtering methods (to word match at
11744
+ * any point in the data), when false this will not be done.
11745
+ * @namespace
11746
+ * @extends WFDataTable.models.oSearch
11747
+ *
11748
+ * @dtopt Options
11749
+ * @name WFDataTable.defaults.search
11750
+ *
11751
+ * @example
11752
+ * $(document).ready( function() {
11753
+ * $('#example').wfDataTable( {
11754
+ * "search": {"search": "Initial search"}
11755
+ * } );
11756
+ * } )
11757
+ */
11758
+ "oSearch": $.extend( {}, WFDataTable.models.oSearch ),
11759
+
11760
+
11761
+ /**
11762
+ * __Deprecated__ The functionality provided by this parameter has now been
11763
+ * superseded by that provided through `ajax`, which should be used instead.
11764
+ *
11765
+ * By default WFDataTables will look for the property `data` (or `aaData` for
11766
+ * compatibility with WFDataTables 1.9-) when obtaining data from an Ajax
11767
+ * source or for server-side processing - this parameter allows that
11768
+ * property to be changed. You can use Javascript dotted object notation to
11769
+ * get a data source for multiple levels of nesting.
11770
+ * @type string
11771
+ * @default data
11772
+ *
11773
+ * @dtopt Options
11774
+ * @dtopt Server-side
11775
+ * @name WFDataTable.defaults.ajaxDataProp
11776
+ *
11777
+ * @deprecated 1.10. Please use `ajax` for this functionality now.
11778
+ */
11779
+ "sAjaxDataProp": "data",
11780
+
11781
+
11782
+ /**
11783
+ * __Deprecated__ The functionality provided by this parameter has now been
11784
+ * superseded by that provided through `ajax`, which should be used instead.
11785
+ *
11786
+ * You can instruct WFDataTables to load data from an external
11787
+ * source using this parameter (use aData if you want to pass data in you
11788
+ * already have). Simply provide a url a JSON object can be obtained from.
11789
+ * @type string
11790
+ * @default null
11791
+ *
11792
+ * @dtopt Options
11793
+ * @dtopt Server-side
11794
+ * @name WFDataTable.defaults.ajaxSource
11795
+ *
11796
+ * @deprecated 1.10. Please use `ajax` for this functionality now.
11797
+ */
11798
+ "sAjaxSource": null,
11799
+
11800
+
11801
+ /**
11802
+ * This initialisation variable allows you to specify exactly where in the
11803
+ * DOM you want WFDataTables to inject the various controls it adds to the page
11804
+ * (for example you might want the pagination controls at the top of the
11805
+ * table). DIV elements (with or without a custom class) can also be added to
11806
+ * aid styling. The follow syntax is used:
11807
+ * <ul>
11808
+ * <li>The following options are allowed:
11809
+ * <ul>
11810
+ * <li>'l' - Length changing</li>
11811
+ * <li>'f' - Filtering input</li>
11812
+ * <li>'t' - The table!</li>
11813
+ * <li>'i' - Information</li>
11814
+ * <li>'p' - Pagination</li>
11815
+ * <li>'r' - pRocessing</li>
11816
+ * </ul>
11817
+ * </li>
11818
+ * <li>The following constants are allowed:
11819
+ * <ul>
11820
+ * <li>'H' - jQueryUI theme "header" classes ('fg-toolbar ui-widget-header ui-corner-tl ui-corner-tr ui-helper-clearfix')</li>
11821
+ * <li>'F' - jQueryUI theme "footer" classes ('fg-toolbar ui-widget-header ui-corner-bl ui-corner-br ui-helper-clearfix')</li>
11822
+ * </ul>
11823
+ * </li>
11824
+ * <li>The following syntax is expected:
11825
+ * <ul>
11826
+ * <li>'&lt;' and '&gt;' - div elements</li>
11827
+ * <li>'&lt;"class" and '&gt;' - div with a class</li>
11828
+ * <li>'&lt;"#id" and '&gt;' - div with an ID</li>
11829
+ * </ul>
11830
+ * </li>
11831
+ * <li>Examples:
11832
+ * <ul>
11833
+ * <li>'&lt;"wrapper"flipt&gt;'</li>
11834
+ * <li>'&lt;lf&lt;t&gt;ip&gt;'</li>
11835
+ * </ul>
11836
+ * </li>
11837
+ * </ul>
11838
+ * @type string
11839
+ * @default lfrtip <i>(when `jQueryUI` is false)</i> <b>or</b>
11840
+ * <"H"lfr>t<"F"ip> <i>(when `jQueryUI` is true)</i>
11841
+ *
11842
+ * @dtopt Options
11843
+ * @name WFDataTable.defaults.dom
11844
+ *
11845
+ * @example
11846
+ * $(document).ready( function() {
11847
+ * $('#example').wfDataTable( {
11848
+ * "dom": '&lt;"top"i&gt;rt&lt;"bottom"flp&gt;&lt;"clear"&gt;'
11849
+ * } );
11850
+ * } );
11851
+ */
11852
+ "sDom": "lfrtip",
11853
+
11854
+
11855
+ /**
11856
+ * Search delay option. This will throttle full table searches that use the
11857
+ * WFDataTables provided search input element (it does not effect calls to
11858
+ * `dt-api search()`, providing a delay before the search is made.
11859
+ * @type integer
11860
+ * @default 0
11861
+ *
11862
+ * @dtopt Options
11863
+ * @name WFDataTable.defaults.searchDelay
11864
+ *
11865
+ * @example
11866
+ * $(document).ready( function() {
11867
+ * $('#example').wfDataTable( {
11868
+ * "searchDelay": 200
11869
+ * } );
11870
+ * } )
11871
+ */
11872
+ "searchDelay": null,
11873
+
11874
+
11875
+ /**
11876
+ * WFDataTables features six different built-in options for the buttons to
11877
+ * display for pagination control:
11878
+ *
11879
+ * * `numbers` - Page number buttons only
11880
+ * * `simple` - 'Previous' and 'Next' buttons only
11881
+ * * 'simple_numbers` - 'Previous' and 'Next' buttons, plus page numbers
11882
+ * * `full` - 'First', 'Previous', 'Next' and 'Last' buttons
11883
+ * * `full_numbers` - 'First', 'Previous', 'Next' and 'Last' buttons, plus page numbers
11884
+ * * `first_last_numbers` - 'First' and 'Last' buttons, plus page numbers
11885
+ *
11886
+ * Further methods can be added using {@link WFDataTable.ext.oPagination}.
11887
+ * @type string
11888
+ * @default simple_numbers
11889
+ *
11890
+ * @dtopt Options
11891
+ * @name WFDataTable.defaults.pagingType
11892
+ *
11893
+ * @example
11894
+ * $(document).ready( function() {
11895
+ * $('#example').wfDataTable( {
11896
+ * "pagingType": "full_numbers"
11897
+ * } );
11898
+ * } )
11899
+ */
11900
+ "sPaginationType": "simple_numbers",
11901
+
11902
+
11903
+ /**
11904
+ * Enable horizontal scrolling. When a table is too wide to fit into a
11905
+ * certain layout, or you have a large number of columns in the table, you
11906
+ * can enable x-scrolling to show the table in a viewport, which can be
11907
+ * scrolled. This property can be `true` which will allow the table to
11908
+ * scroll horizontally when needed, or any CSS unit, or a number (in which
11909
+ * case it will be treated as a pixel measurement). Setting as simply `true`
11910
+ * is recommended.
11911
+ * @type boolean|string
11912
+ * @default <i>blank string - i.e. disabled</i>
11913
+ *
11914
+ * @dtopt Features
11915
+ * @name WFDataTable.defaults.scrollX
11916
+ *
11917
+ * @example
11918
+ * $(document).ready( function() {
11919
+ * $('#example').wfDataTable( {
11920
+ * "scrollX": true,
11921
+ * "scrollCollapse": true
11922
+ * } );
11923
+ * } );
11924
+ */
11925
+ "sScrollX": "",
11926
+
11927
+
11928
+ /**
11929
+ * This property can be used to force a WFDataTable to use more width than it
11930
+ * might otherwise do when x-scrolling is enabled. For example if you have a
11931
+ * table which requires to be well spaced, this parameter is useful for
11932
+ * "over-sizing" the table, and thus forcing scrolling. This property can by
11933
+ * any CSS unit, or a number (in which case it will be treated as a pixel
11934
+ * measurement).
11935
+ * @type string
11936
+ * @default <i>blank string - i.e. disabled</i>
11937
+ *
11938
+ * @dtopt Options
11939
+ * @name WFDataTable.defaults.scrollXInner
11940
+ *
11941
+ * @example
11942
+ * $(document).ready( function() {
11943
+ * $('#example').wfDataTable( {
11944
+ * "scrollX": "100%",
11945
+ * "scrollXInner": "110%"
11946
+ * } );
11947
+ * } );
11948
+ */
11949
+ "sScrollXInner": "",
11950
+
11951
+
11952
+ /**
11953
+ * Enable vertical scrolling. Vertical scrolling will constrain the WFDataTable
11954
+ * to the given height, and enable scrolling for any data which overflows the
11955
+ * current viewport. This can be used as an alternative to paging to display
11956
+ * a lot of data in a small area (although paging and scrolling can both be
11957
+ * enabled at the same time). This property can be any CSS unit, or a number
11958
+ * (in which case it will be treated as a pixel measurement).
11959
+ * @type string
11960
+ * @default <i>blank string - i.e. disabled</i>
11961
+ *
11962
+ * @dtopt Features
11963
+ * @name WFDataTable.defaults.scrollY
11964
+ *
11965
+ * @example
11966
+ * $(document).ready( function() {
11967
+ * $('#example').wfDataTable( {
11968
+ * "scrollY": "200px",
11969
+ * "paginate": false
11970
+ * } );
11971
+ * } );
11972
+ */
11973
+ "sScrollY": "",
11974
+
11975
+
11976
+ /**
11977
+ * __Deprecated__ The functionality provided by this parameter has now been
11978
+ * superseded by that provided through `ajax`, which should be used instead.
11979
+ *
11980
+ * Set the HTTP method that is used to make the Ajax call for server-side
11981
+ * processing or Ajax sourced data.
11982
+ * @type string
11983
+ * @default GET
11984
+ *
11985
+ * @dtopt Options
11986
+ * @dtopt Server-side
11987
+ * @name WFDataTable.defaults.serverMethod
11988
+ *
11989
+ * @deprecated 1.10. Please use `ajax` for this functionality now.
11990
+ */
11991
+ "sServerMethod": "GET",
11992
+
11993
+
11994
+ /**
11995
+ * WFDataTables makes use of renderers when displaying HTML elements for
11996
+ * a table. These renderers can be added or modified by plug-ins to
11997
+ * generate suitable mark-up for a site. For example the Bootstrap
11998
+ * integration plug-in for WFDataTables uses a paging button renderer to
11999
+ * display pagination buttons in the mark-up required by Bootstrap.
12000
+ *
12001
+ * For further information about the renderers available see
12002
+ * WFDataTable.ext.renderer
12003
+ * @type string|object
12004
+ * @default null
12005
+ *
12006
+ * @name WFDataTable.defaults.renderer
12007
+ *
12008
+ */
12009
+ "renderer": null,
12010
+
12011
+
12012
+ /**
12013
+ * Set the data property name that WFDataTables should use to get a row's id
12014
+ * to set as the `id` property in the node.
12015
+ * @type string
12016
+ * @default DT_RowId
12017
+ *
12018
+ * @name WFDataTable.defaults.rowId
12019
+ */
12020
+ "rowId": "DT_RowId"
12021
+ };
12022
+
12023
+ _fnHungarianMap( WFDataTable.defaults );
12024
+
12025
+
12026
+
12027
+ /*
12028
+ * Developer note - See note in model.defaults.js about the use of Hungarian
12029
+ * notation and camel case.
12030
+ */
12031
+
12032
+ /**
12033
+ * Column options that can be given to WFDataTables at initialisation time.
12034
+ * @namespace
12035
+ */
12036
+ WFDataTable.defaults.column = {
12037
+ /**
12038
+ * Define which column(s) an order will occur on for this column. This
12039
+ * allows a column's ordering to take multiple columns into account when
12040
+ * doing a sort or use the data from a different column. For example first
12041
+ * name / last name columns make sense to do a multi-column sort over the
12042
+ * two columns.
12043
+ * @type array|int
12044
+ * @default null <i>Takes the value of the column index automatically</i>
12045
+ *
12046
+ * @name WFDataTable.defaults.column.orderData
12047
+ * @dtopt Columns
12048
+ *
12049
+ * @example
12050
+ * // Using `columnDefs`
12051
+ * $(document).ready( function() {
12052
+ * $('#example').wfDataTable( {
12053
+ * "columnDefs": [
12054
+ * { "orderData": [ 0, 1 ], "targets": [ 0 ] },
12055
+ * { "orderData": [ 1, 0 ], "targets": [ 1 ] },
12056
+ * { "orderData": 2, "targets": [ 2 ] }
12057
+ * ]
12058
+ * } );
12059
+ * } );
12060
+ *
12061
+ * @example
12062
+ * // Using `columns`
12063
+ * $(document).ready( function() {
12064
+ * $('#example').wfDataTable( {
12065
+ * "columns": [
12066
+ * { "orderData": [ 0, 1 ] },
12067
+ * { "orderData": [ 1, 0 ] },
12068
+ * { "orderData": 2 },
12069
+ * null,
12070
+ * null
12071
+ * ]
12072
+ * } );
12073
+ * } );
12074
+ */
12075
+ "aDataSort": null,
12076
+ "iDataSort": -1,
12077
+
12078
+
12079
+ /**
12080
+ * You can control the default ordering direction, and even alter the
12081
+ * behaviour of the sort handler (i.e. only allow ascending ordering etc)
12082
+ * using this parameter.
12083
+ * @type array
12084
+ * @default [ 'asc', 'desc' ]
12085
+ *
12086
+ * @name WFDataTable.defaults.column.orderSequence
12087
+ * @dtopt Columns
12088
+ *
12089
+ * @example
12090
+ * // Using `columnDefs`
12091
+ * $(document).ready( function() {
12092
+ * $('#example').wfDataTable( {
12093
+ * "columnDefs": [
12094
+ * { "orderSequence": [ "asc" ], "targets": [ 1 ] },
12095
+ * { "orderSequence": [ "desc", "asc", "asc" ], "targets": [ 2 ] },
12096
+ * { "orderSequence": [ "desc" ], "targets": [ 3 ] }
12097
+ * ]
12098
+ * } );
12099
+ * } );
12100
+ *
12101
+ * @example
12102
+ * // Using `columns`
12103
+ * $(document).ready( function() {
12104
+ * $('#example').wfDataTable( {
12105
+ * "columns": [
12106
+ * null,
12107
+ * { "orderSequence": [ "asc" ] },
12108
+ * { "orderSequence": [ "desc", "asc", "asc" ] },
12109
+ * { "orderSequence": [ "desc" ] },
12110
+ * null
12111
+ * ]
12112
+ * } );
12113
+ * } );
12114
+ */
12115
+ "asSorting": [ 'asc', 'desc' ],
12116
+
12117
+
12118
+ /**
12119
+ * Enable or disable filtering on the data in this column.
12120
+ * @type boolean
12121
+ * @default true
12122
+ *
12123
+ * @name WFDataTable.defaults.column.searchable
12124
+ * @dtopt Columns
12125
+ *
12126
+ * @example
12127
+ * // Using `columnDefs`
12128
+ * $(document).ready( function() {
12129
+ * $('#example').wfDataTable( {
12130
+ * "columnDefs": [
12131
+ * { "searchable": false, "targets": [ 0 ] }
12132
+ * ] } );
12133
+ * } );
12134
+ *
12135
+ * @example
12136
+ * // Using `columns`
12137
+ * $(document).ready( function() {
12138
+ * $('#example').wfDataTable( {
12139
+ * "columns": [
12140
+ * { "searchable": false },
12141
+ * null,
12142
+ * null,
12143
+ * null,
12144
+ * null
12145
+ * ] } );
12146
+ * } );
12147
+ */
12148
+ "bSearchable": true,
12149
+
12150
+
12151
+ /**
12152
+ * Enable or disable ordering on this column.
12153
+ * @type boolean
12154
+ * @default true
12155
+ *
12156
+ * @name WFDataTable.defaults.column.orderable
12157
+ * @dtopt Columns
12158
+ *
12159
+ * @example
12160
+ * // Using `columnDefs`
12161
+ * $(document).ready( function() {
12162
+ * $('#example').wfDataTable( {
12163
+ * "columnDefs": [
12164
+ * { "orderable": false, "targets": [ 0 ] }
12165
+ * ] } );
12166
+ * } );
12167
+ *
12168
+ * @example
12169
+ * // Using `columns`
12170
+ * $(document).ready( function() {
12171
+ * $('#example').wfDataTable( {
12172
+ * "columns": [
12173
+ * { "orderable": false },
12174
+ * null,
12175
+ * null,
12176
+ * null,
12177
+ * null
12178
+ * ] } );
12179
+ * } );
12180
+ */
12181
+ "bSortable": true,
12182
+
12183
+
12184
+ /**
12185
+ * Enable or disable the display of this column.
12186
+ * @type boolean
12187
+ * @default true
12188
+ *
12189
+ * @name WFDataTable.defaults.column.visible
12190
+ * @dtopt Columns
12191
+ *
12192
+ * @example
12193
+ * // Using `columnDefs`
12194
+ * $(document).ready( function() {
12195
+ * $('#example').wfDataTable( {
12196
+ * "columnDefs": [
12197
+ * { "visible": false, "targets": [ 0 ] }
12198
+ * ] } );
12199
+ * } );
12200
+ *
12201
+ * @example
12202
+ * // Using `columns`
12203
+ * $(document).ready( function() {
12204
+ * $('#example').wfDataTable( {
12205
+ * "columns": [
12206
+ * { "visible": false },
12207
+ * null,
12208
+ * null,
12209
+ * null,
12210
+ * null
12211
+ * ] } );
12212
+ * } );
12213
+ */
12214
+ "bVisible": true,
12215
+
12216
+
12217
+ /**
12218
+ * Developer definable function that is called whenever a cell is created (Ajax source,
12219
+ * etc) or processed for input (DOM source). This can be used as a compliment to mRender
12220
+ * allowing you to modify the DOM element (add background colour for example) when the
12221
+ * element is available.
12222
+ * @type function
12223
+ * @param {element} td The TD node that has been created
12224
+ * @param {*} cellData The Data for the cell
12225
+ * @param {array|object} rowData The data for the whole row
12226
+ * @param {int} row The row index for the aoData data store
12227
+ * @param {int} col The column index for aoColumns
12228
+ *
12229
+ * @name WFDataTable.defaults.column.createdCell
12230
+ * @dtopt Columns
12231
+ *
12232
+ * @example
12233
+ * $(document).ready( function() {
12234
+ * $('#example').wfDataTable( {
12235
+ * "columnDefs": [ {
12236
+ * "targets": [3],
12237
+ * "createdCell": function (td, cellData, rowData, row, col) {
12238
+ * if ( cellData == "1.7" ) {
12239
+ * $(td).css('color', 'blue')
12240
+ * }
12241
+ * }
12242
+ * } ]
12243
+ * });
12244
+ * } );
12245
+ */
12246
+ "fnCreatedCell": null,
12247
+
12248
+
12249
+ /**
12250
+ * This parameter has been replaced by `data` in WFDataTables to ensure naming
12251
+ * consistency. `dataProp` can still be used, as there is backwards
12252
+ * compatibility in WFDataTables for this option, but it is strongly
12253
+ * recommended that you use `data` in preference to `dataProp`.
12254
+ * @name WFDataTable.defaults.column.dataProp
12255
+ */
12256
+
12257
+
12258
+ /**
12259
+ * This property can be used to read data from any data source property,
12260
+ * including deeply nested objects / properties. `data` can be given in a
12261
+ * number of different ways which effect its behaviour:
12262
+ *
12263
+ * * `integer` - treated as an array index for the data source. This is the
12264
+ * default that WFDataTables uses (incrementally increased for each column).
12265
+ * * `string` - read an object property from the data source. There are
12266
+ * three 'special' options that can be used in the string to alter how
12267
+ * WFDataTables reads the data from the source object:
12268
+ * * `.` - Dotted Javascript notation. Just as you use a `.` in
12269
+ * Javascript to read from nested objects, so to can the options
12270
+ * specified in `data`. For example: `browser.version` or
12271
+ * `browser.name`. If your object parameter name contains a period, use
12272
+ * `\\` to escape it - i.e. `first\\.name`.
12273
+ * * `[]` - Array notation. WFDataTables can automatically combine data
12274
+ * from and array source, joining the data with the characters provided
12275
+ * between the two brackets. For example: `name[, ]` would provide a
12276
+ * comma-space separated list from the source array. If no characters
12277
+ * are provided between the brackets, the original array source is
12278
+ * returned.
12279
+ * * `()` - Function notation. Adding `()` to the end of a parameter will
12280
+ * execute a function of the name given. For example: `browser()` for a
12281
+ * simple function on the data source, `browser.version()` for a
12282
+ * function in a nested property or even `browser().version` to get an
12283
+ * object property if the function called returns an object. Note that
12284
+ * function notation is recommended for use in `render` rather than
12285
+ * `data` as it is much simpler to use as a renderer.
12286
+ * * `null` - use the original data source for the row rather than plucking
12287
+ * data directly from it. This action has effects on two other
12288
+ * initialisation options:
12289
+ * * `defaultContent` - When null is given as the `data` option and
12290
+ * `defaultContent` is specified for the column, the value defined by
12291
+ * `defaultContent` will be used for the cell.
12292
+ * * `render` - When null is used for the `data` option and the `render`
12293
+ * option is specified for the column, the whole data source for the
12294
+ * row is used for the renderer.
12295
+ * * `function` - the function given will be executed whenever WFDataTables
12296
+ * needs to set or get the data for a cell in the column. The function
12297
+ * takes three parameters:
12298
+ * * Parameters:
12299
+ * * `{array|object}` The data source for the row
12300
+ * * `{string}` The type call data requested - this will be 'set' when
12301
+ * setting data or 'filter', 'display', 'type', 'sort' or undefined
12302
+ * when gathering data. Note that when `undefined` is given for the
12303
+ * type WFDataTables expects to get the raw data for the object back<
12304
+ * * `{*}` Data to set when the second parameter is 'set'.
12305
+ * * Return:
12306
+ * * The return value from the function is not required when 'set' is
12307
+ * the type of call, but otherwise the return is what will be used
12308
+ * for the data requested.
12309
+ *
12310
+ * Note that `data` is a getter and setter option. If you just require
12311
+ * formatting of data for output, you will likely want to use `render` which
12312
+ * is simply a getter and thus simpler to use.
12313
+ *
12314
+ * Note that prior to WFDataTables 1.9.2 `data` was called `mDataProp`. The
12315
+ * name change reflects the flexibility of this property and is consistent
12316
+ * with the naming of mRender. If 'mDataProp' is given, then it will still
12317
+ * be used by WFDataTables, as it automatically maps the old name to the new
12318
+ * if required.
12319
+ *
12320
+ * @type string|int|function|null
12321
+ * @default null <i>Use automatically calculated column index</i>
12322
+ *
12323
+ * @name WFDataTable.defaults.column.data
12324
+ * @dtopt Columns
12325
+ *
12326
+ * @example
12327
+ * // Read table data from objects
12328
+ * // JSON structure for each row:
12329
+ * // {
12330
+ * // "engine": {value},
12331
+ * // "browser": {value},
12332
+ * // "platform": {value},
12333
+ * // "version": {value},
12334
+ * // "grade": {value}
12335
+ * // }
12336
+ * $(document).ready( function() {
12337
+ * $('#example').wfDataTable( {
12338
+ * "ajaxSource": "sources/objects.txt",
12339
+ * "columns": [
12340
+ * { "data": "engine" },
12341
+ * { "data": "browser" },
12342
+ * { "data": "platform" },
12343
+ * { "data": "version" },
12344
+ * { "data": "grade" }
12345
+ * ]
12346
+ * } );
12347
+ * } );
12348
+ *
12349
+ * @example
12350
+ * // Read information from deeply nested objects
12351
+ * // JSON structure for each row:
12352
+ * // {
12353
+ * // "engine": {value},
12354
+ * // "browser": {value},
12355
+ * // "platform": {
12356
+ * // "inner": {value}
12357
+ * // },
12358
+ * // "details": [
12359
+ * // {value}, {value}
12360
+ * // ]
12361
+ * // }
12362
+ * $(document).ready( function() {
12363
+ * $('#example').wfDataTable( {
12364
+ * "ajaxSource": "sources/deep.txt",
12365
+ * "columns": [
12366
+ * { "data": "engine" },
12367
+ * { "data": "browser" },
12368
+ * { "data": "platform.inner" },
12369
+ * { "data": "platform.details.0" },
12370
+ * { "data": "platform.details.1" }
12371
+ * ]
12372
+ * } );
12373
+ * } );
12374
+ *
12375
+ * @example
12376
+ * // Using `data` as a function to provide different information for
12377
+ * // sorting, filtering and display. In this case, currency (price)
12378
+ * $(document).ready( function() {
12379
+ * $('#example').wfDataTable( {
12380
+ * "columnDefs": [ {
12381
+ * "targets": [ 0 ],
12382
+ * "data": function ( source, type, val ) {
12383
+ * if (type === 'set') {
12384
+ * source.price = val;
12385
+ * // Store the computed dislay and filter values for efficiency
12386
+ * source.price_display = val=="" ? "" : "$"+numberFormat(val);
12387
+ * source.price_filter = val=="" ? "" : "$"+numberFormat(val)+" "+val;
12388
+ * return;
12389
+ * }
12390
+ * else if (type === 'display') {
12391
+ * return source.price_display;
12392
+ * }
12393
+ * else if (type === 'filter') {
12394
+ * return source.price_filter;
12395
+ * }
12396
+ * // 'sort', 'type' and undefined all just use the integer
12397
+ * return source.price;
12398
+ * }
12399
+ * } ]
12400
+ * } );
12401
+ * } );
12402
+ *
12403
+ * @example
12404
+ * // Using default content
12405
+ * $(document).ready( function() {
12406
+ * $('#example').wfDataTable( {
12407
+ * "columnDefs": [ {
12408
+ * "targets": [ 0 ],
12409
+ * "data": null,
12410
+ * "defaultContent": "Click to edit"
12411
+ * } ]
12412
+ * } );
12413
+ * } );
12414
+ *
12415
+ * @example
12416
+ * // Using array notation - outputting a list from an array
12417
+ * $(document).ready( function() {
12418
+ * $('#example').wfDataTable( {
12419
+ * "columnDefs": [ {
12420
+ * "targets": [ 0 ],
12421
+ * "data": "name[, ]"
12422
+ * } ]
12423
+ * } );
12424
+ * } );
12425
+ *
12426
+ */
12427
+ "mData": null,
12428
+
12429
+
12430
+ /**
12431
+ * This property is the rendering partner to `data` and it is suggested that
12432
+ * when you want to manipulate data for display (including filtering,
12433
+ * sorting etc) without altering the underlying data for the table, use this
12434
+ * property. `render` can be considered to be the the read only companion to
12435
+ * `data` which is read / write (then as such more complex). Like `data`
12436
+ * this option can be given in a number of different ways to effect its
12437
+ * behaviour:
12438
+ *
12439
+ * * `integer` - treated as an array index for the data source. This is the
12440
+ * default that WFDataTables uses (incrementally increased for each column).
12441
+ * * `string` - read an object property from the data source. There are
12442
+ * three 'special' options that can be used in the string to alter how
12443
+ * WFDataTables reads the data from the source object:
12444
+ * * `.` - Dotted Javascript notation. Just as you use a `.` in
12445
+ * Javascript to read from nested objects, so to can the options
12446
+ * specified in `data`. For example: `browser.version` or
12447
+ * `browser.name`. If your object parameter name contains a period, use
12448
+ * `\\` to escape it - i.e. `first\\.name`.
12449
+ * * `[]` - Array notation. WFDataTables can automatically combine data
12450
+ * from and array source, joining the data with the characters provided
12451
+ * between the two brackets. For example: `name[, ]` would provide a
12452
+ * comma-space separated list from the source array. If no characters
12453
+ * are provided between the brackets, the original array source is
12454
+ * returned.
12455
+ * * `()` - Function notation. Adding `()` to the end of a parameter will
12456
+ * execute a function of the name given. For example: `browser()` for a
12457
+ * simple function on the data source, `browser.version()` for a
12458
+ * function in a nested property or even `browser().version` to get an
12459
+ * object property if the function called returns an object.
12460
+ * * `object` - use different data for the different data types requested by
12461
+ * WFDataTables ('filter', 'display', 'type' or 'sort'). The property names
12462
+ * of the object is the data type the property refers to and the value can
12463
+ * defined using an integer, string or function using the same rules as
12464
+ * `render` normally does. Note that an `_` option _must_ be specified.
12465
+ * This is the default value to use if you haven't specified a value for
12466
+ * the data type requested by WFDataTables.
12467
+ * * `function` - the function given will be executed whenever WFDataTables
12468
+ * needs to set or get the data for a cell in the column. The function
12469
+ * takes three parameters:
12470
+ * * Parameters:
12471
+ * * {array|object} The data source for the row (based on `data`)
12472
+ * * {string} The type call data requested - this will be 'filter',
12473
+ * 'display', 'type' or 'sort'.
12474
+ * * {array|object} The full data source for the row (not based on
12475
+ * `data`)
12476
+ * * Return:
12477
+ * * The return value from the function is what will be used for the
12478
+ * data requested.
12479
+ *
12480
+ * @type string|int|function|object|null
12481
+ * @default null Use the data source value.
12482
+ *
12483
+ * @name WFDataTable.defaults.column.render
12484
+ * @dtopt Columns
12485
+ *
12486
+ * @example
12487
+ * // Create a comma separated list from an array of objects
12488
+ * $(document).ready( function() {
12489
+ * $('#example').wfDataTable( {
12490
+ * "ajaxSource": "sources/deep.txt",
12491
+ * "columns": [
12492
+ * { "data": "engine" },
12493
+ * { "data": "browser" },
12494
+ * {
12495
+ * "data": "platform",
12496
+ * "render": "[, ].name"
12497
+ * }
12498
+ * ]
12499
+ * } );
12500
+ * } );
12501
+ *
12502
+ * @example
12503
+ * // Execute a function to obtain data
12504
+ * $(document).ready( function() {
12505
+ * $('#example').wfDataTable( {
12506
+ * "columnDefs": [ {
12507
+ * "targets": [ 0 ],
12508
+ * "data": null, // Use the full data source object for the renderer's source
12509
+ * "render": "browserName()"
12510
+ * } ]
12511
+ * } );
12512
+ * } );
12513
+ *
12514
+ * @example
12515
+ * // As an object, extracting different data for the different types
12516
+ * // This would be used with a data source such as:
12517
+ * // { "phone": 5552368, "phone_filter": "5552368 555-2368", "phone_display": "555-2368" }
12518
+ * // Here the `phone` integer is used for sorting and type detection, while `phone_filter`
12519
+ * // (which has both forms) is used for filtering for if a user inputs either format, while
12520
+ * // the formatted phone number is the one that is shown in the table.
12521
+ * $(document).ready( function() {
12522
+ * $('#example').wfDataTable( {
12523
+ * "columnDefs": [ {
12524
+ * "targets": [ 0 ],
12525
+ * "data": null, // Use the full data source object for the renderer's source
12526
+ * "render": {
12527
+ * "_": "phone",
12528
+ * "filter": "phone_filter",
12529
+ * "display": "phone_display"
12530
+ * }
12531
+ * } ]
12532
+ * } );
12533
+ * } );
12534
+ *
12535
+ * @example
12536
+ * // Use as a function to create a link from the data source
12537
+ * $(document).ready( function() {
12538
+ * $('#example').wfDataTable( {
12539
+ * "columnDefs": [ {
12540
+ * "targets": [ 0 ],
12541
+ * "data": "download_link",
12542
+ * "render": function ( data, type, full ) {
12543
+ * return '<a href="'+data+'">Download</a>';
12544
+ * }
12545
+ * } ]
12546
+ * } );
12547
+ * } );
12548
+ */
12549
+ "mRender": null,
12550
+
12551
+
12552
+ /**
12553
+ * Change the cell type created for the column - either TD cells or TH cells. This
12554
+ * can be useful as TH cells have semantic meaning in the table body, allowing them
12555
+ * to act as a header for a row (you may wish to add scope='row' to the TH elements).
12556
+ * @type string
12557
+ * @default td
12558
+ *
12559
+ * @name WFDataTable.defaults.column.cellType
12560
+ * @dtopt Columns
12561
+ *
12562
+ * @example
12563
+ * // Make the first column use TH cells
12564
+ * $(document).ready( function() {
12565
+ * $('#example').wfDataTable( {
12566
+ * "columnDefs": [ {
12567
+ * "targets": [ 0 ],
12568
+ * "cellType": "th"
12569
+ * } ]
12570
+ * } );
12571
+ * } );
12572
+ */
12573
+ "sCellType": "td",
12574
+
12575
+
12576
+ /**
12577
+ * Class to give to each cell in this column.
12578
+ * @type string
12579
+ * @default <i>Empty string</i>
12580
+ *
12581
+ * @name WFDataTable.defaults.column.class
12582
+ * @dtopt Columns
12583
+ *
12584
+ * @example
12585
+ * // Using `columnDefs`
12586
+ * $(document).ready( function() {
12587
+ * $('#example').wfDataTable( {
12588
+ * "columnDefs": [
12589
+ * { "class": "my_class", "targets": [ 0 ] }
12590
+ * ]
12591
+ * } );
12592
+ * } );
12593
+ *
12594
+ * @example
12595
+ * // Using `columns`
12596
+ * $(document).ready( function() {
12597
+ * $('#example').wfDataTable( {
12598
+ * "columns": [
12599
+ * { "class": "my_class" },
12600
+ * null,
12601
+ * null,
12602
+ * null,
12603
+ * null
12604
+ * ]
12605
+ * } );
12606
+ * } );
12607
+ */
12608
+ "sClass": "",
12609
+
12610
+ /**
12611
+ * When WFDataTables calculates the column widths to assign to each column,
12612
+ * it finds the longest string in each column and then constructs a
12613
+ * temporary table and reads the widths from that. The problem with this
12614
+ * is that "mmm" is much wider then "iiii", but the latter is a longer
12615
+ * string - thus the calculation can go wrong (doing it properly and putting
12616
+ * it into an DOM object and measuring that is horribly(!) slow). Thus as
12617
+ * a "work around" we provide this option. It will append its value to the
12618
+ * text that is found to be the longest string for the column - i.e. padding.
12619
+ * Generally you shouldn't need this!
12620
+ * @type string
12621
+ * @default <i>Empty string<i>
12622
+ *
12623
+ * @name WFDataTable.defaults.column.contentPadding
12624
+ * @dtopt Columns
12625
+ *
12626
+ * @example
12627
+ * // Using `columns`
12628
+ * $(document).ready( function() {
12629
+ * $('#example').wfDataTable( {
12630
+ * "columns": [
12631
+ * null,
12632
+ * null,
12633
+ * null,
12634
+ * {
12635
+ * "contentPadding": "mmm"
12636
+ * }
12637
+ * ]
12638
+ * } );
12639
+ * } );
12640
+ */
12641
+ "sContentPadding": "",
12642
+
12643
+
12644
+ /**
12645
+ * Allows a default value to be given for a column's data, and will be used
12646
+ * whenever a null data source is encountered (this can be because `data`
12647
+ * is set to null, or because the data source itself is null).
12648
+ * @type string
12649
+ * @default null
12650
+ *
12651
+ * @name WFDataTable.defaults.column.defaultContent
12652
+ * @dtopt Columns
12653
+ *
12654
+ * @example
12655
+ * // Using `columnDefs`
12656
+ * $(document).ready( function() {
12657
+ * $('#example').wfDataTable( {
12658
+ * "columnDefs": [
12659
+ * {
12660
+ * "data": null,
12661
+ * "defaultContent": "Edit",
12662
+ * "targets": [ -1 ]
12663
+ * }
12664
+ * ]
12665
+ * } );
12666
+ * } );
12667
+ *
12668
+ * @example
12669
+ * // Using `columns`
12670
+ * $(document).ready( function() {
12671
+ * $('#example').wfDataTable( {
12672
+ * "columns": [
12673
+ * null,
12674
+ * null,
12675
+ * null,
12676
+ * {
12677
+ * "data": null,
12678
+ * "defaultContent": "Edit"
12679
+ * }
12680
+ * ]
12681
+ * } );
12682
+ * } );
12683
+ */
12684
+ "sDefaultContent": null,
12685
+
12686
+
12687
+ /**
12688
+ * This parameter is only used in WFDataTables' server-side processing. It can
12689
+ * be exceptionally useful to know what columns are being displayed on the
12690
+ * client side, and to map these to database fields. When defined, the names
12691
+ * also allow WFDataTables to reorder information from the server if it comes
12692
+ * back in an unexpected order (i.e. if you switch your columns around on the
12693
+ * client-side, your server-side code does not also need updating).
12694
+ * @type string
12695
+ * @default <i>Empty string</i>
12696
+ *
12697
+ * @name WFDataTable.defaults.column.name
12698
+ * @dtopt Columns
12699
+ *
12700
+ * @example
12701
+ * // Using `columnDefs`
12702
+ * $(document).ready( function() {
12703
+ * $('#example').wfDataTable( {
12704
+ * "columnDefs": [
12705
+ * { "name": "engine", "targets": [ 0 ] },
12706
+ * { "name": "browser", "targets": [ 1 ] },
12707
+ * { "name": "platform", "targets": [ 2 ] },
12708
+ * { "name": "version", "targets": [ 3 ] },
12709
+ * { "name": "grade", "targets": [ 4 ] }
12710
+ * ]
12711
+ * } );
12712
+ * } );
12713
+ *
12714
+ * @example
12715
+ * // Using `columns`
12716
+ * $(document).ready( function() {
12717
+ * $('#example').wfDataTable( {
12718
+ * "columns": [
12719
+ * { "name": "engine" },
12720
+ * { "name": "browser" },
12721
+ * { "name": "platform" },
12722
+ * { "name": "version" },
12723
+ * { "name": "grade" }
12724
+ * ]
12725
+ * } );
12726
+ * } );
12727
+ */
12728
+ "sName": "",
12729
+
12730
+
12731
+ /**
12732
+ * Defines a data source type for the ordering which can be used to read
12733
+ * real-time information from the table (updating the internally cached
12734
+ * version) prior to ordering. This allows ordering to occur on user
12735
+ * editable elements such as form inputs.
12736
+ * @type string
12737
+ * @default std
12738
+ *
12739
+ * @name WFDataTable.defaults.column.orderDataType
12740
+ * @dtopt Columns
12741
+ *
12742
+ * @example
12743
+ * // Using `columnDefs`
12744
+ * $(document).ready( function() {
12745
+ * $('#example').wfDataTable( {
12746
+ * "columnDefs": [
12747
+ * { "orderDataType": "dom-text", "targets": [ 2, 3 ] },
12748
+ * { "type": "numeric", "targets": [ 3 ] },
12749
+ * { "orderDataType": "dom-select", "targets": [ 4 ] },
12750
+ * { "orderDataType": "dom-checkbox", "targets": [ 5 ] }
12751
+ * ]
12752
+ * } );
12753
+ * } );
12754
+ *
12755
+ * @example
12756
+ * // Using `columns`
12757
+ * $(document).ready( function() {
12758
+ * $('#example').wfDataTable( {
12759
+ * "columns": [
12760
+ * null,
12761
+ * null,
12762
+ * { "orderDataType": "dom-text" },
12763
+ * { "orderDataType": "dom-text", "type": "numeric" },
12764
+ * { "orderDataType": "dom-select" },
12765
+ * { "orderDataType": "dom-checkbox" }
12766
+ * ]
12767
+ * } );
12768
+ * } );
12769
+ */
12770
+ "sSortDataType": "std",
12771
+
12772
+
12773
+ /**
12774
+ * The title of this column.
12775
+ * @type string
12776
+ * @default null <i>Derived from the 'TH' value for this column in the
12777
+ * original HTML table.</i>
12778
+ *
12779
+ * @name WFDataTable.defaults.column.title
12780
+ * @dtopt Columns
12781
+ *
12782
+ * @example
12783
+ * // Using `columnDefs`
12784
+ * $(document).ready( function() {
12785
+ * $('#example').wfDataTable( {
12786
+ * "columnDefs": [
12787
+ * { "title": "My column title", "targets": [ 0 ] }
12788
+ * ]
12789
+ * } );
12790
+ * } );
12791
+ *
12792
+ * @example
12793
+ * // Using `columns`
12794
+ * $(document).ready( function() {
12795
+ * $('#example').wfDataTable( {
12796
+ * "columns": [
12797
+ * { "title": "My column title" },
12798
+ * null,
12799
+ * null,
12800
+ * null,
12801
+ * null
12802
+ * ]
12803
+ * } );
12804
+ * } );
12805
+ */
12806
+ "sTitle": null,
12807
+
12808
+
12809
+ /**
12810
+ * The type allows you to specify how the data for this column will be
12811
+ * ordered. Four types (string, numeric, date and html (which will strip
12812
+ * HTML tags before ordering)) are currently available. Note that only date
12813
+ * formats understood by Javascript's Date() object will be accepted as type
12814
+ * date. For example: "Mar 26, 2008 5:03 PM". May take the values: 'string',
12815
+ * 'numeric', 'date' or 'html' (by default). Further types can be adding
12816
+ * through plug-ins.
12817
+ * @type string
12818
+ * @default null <i>Auto-detected from raw data</i>
12819
+ *
12820
+ * @name WFDataTable.defaults.column.type
12821
+ * @dtopt Columns
12822
+ *
12823
+ * @example
12824
+ * // Using `columnDefs`
12825
+ * $(document).ready( function() {
12826
+ * $('#example').wfDataTable( {
12827
+ * "columnDefs": [
12828
+ * { "type": "html", "targets": [ 0 ] }
12829
+ * ]
12830
+ * } );
12831
+ * } );
12832
+ *
12833
+ * @example
12834
+ * // Using `columns`
12835
+ * $(document).ready( function() {
12836
+ * $('#example').wfDataTable( {
12837
+ * "columns": [
12838
+ * { "type": "html" },
12839
+ * null,
12840
+ * null,
12841
+ * null,
12842
+ * null
12843
+ * ]
12844
+ * } );
12845
+ * } );
12846
+ */
12847
+ "sType": null,
12848
+
12849
+
12850
+ /**
12851
+ * Defining the width of the column, this parameter may take any CSS value
12852
+ * (3em, 20px etc). WFDataTables applies 'smart' widths to columns which have not
12853
+ * been given a specific width through this interface ensuring that the table
12854
+ * remains readable.
12855
+ * @type string
12856
+ * @default null <i>Automatic</i>
12857
+ *
12858
+ * @name WFDataTable.defaults.column.width
12859
+ * @dtopt Columns
12860
+ *
12861
+ * @example
12862
+ * // Using `columnDefs`
12863
+ * $(document).ready( function() {
12864
+ * $('#example').wfDataTable( {
12865
+ * "columnDefs": [
12866
+ * { "width": "20%", "targets": [ 0 ] }
12867
+ * ]
12868
+ * } );
12869
+ * } );
12870
+ *
12871
+ * @example
12872
+ * // Using `columns`
12873
+ * $(document).ready( function() {
12874
+ * $('#example').wfDataTable( {
12875
+ * "columns": [
12876
+ * { "width": "20%" },
12877
+ * null,
12878
+ * null,
12879
+ * null,
12880
+ * null
12881
+ * ]
12882
+ * } );
12883
+ * } );
12884
+ */
12885
+ "sWidth": null
12886
+ };
12887
+
12888
+ _fnHungarianMap( WFDataTable.defaults.column );
12889
+
12890
+
12891
+
12892
+ /**
12893
+ * WFDataTables settings object - this holds all the information needed for a
12894
+ * given table, including configuration, data and current application of the
12895
+ * table options. WFDataTables does not have a single instance for each WFDataTable
12896
+ * with the settings attached to that instance, but rather instances of the
12897
+ * WFDataTable "class" are created on-the-fly as needed (typically by a
12898
+ * $().wfDataTable() call) and the settings object is then applied to that
12899
+ * instance.
12900
+ *
12901
+ * Note that this object is related to {@link WFDataTable.defaults} but this
12902
+ * one is the internal data store for WFDataTables's cache of columns. It should
12903
+ * NOT be manipulated outside of WFDataTables. Any configuration should be done
12904
+ * through the initialisation options.
12905
+ * @namespace
12906
+ * @todo Really should attach the settings object to individual instances so we
12907
+ * don't need to create new instances on each $().wfDataTable() call (if the
12908
+ * table already exists). It would also save passing oSettings around and
12909
+ * into every single function. However, this is a very significant
12910
+ * architecture change for WFDataTables and will almost certainly break
12911
+ * backwards compatibility with older installations. This is something that
12912
+ * will be done in 2.0.
12913
+ */
12914
+ WFDataTable.models.oSettings = {
12915
+ /**
12916
+ * Primary features of WFDataTables and their enablement state.
12917
+ * @namespace
12918
+ */
12919
+ "oFeatures": {
12920
+
12921
+ /**
12922
+ * Flag to say if WFDataTables should automatically try to calculate the
12923
+ * optimum table and columns widths (true) or not (false).
12924
+ * Note that this parameter will be set by the initialisation routine. To
12925
+ * set a default use {@link WFDataTable.defaults}.
12926
+ * @type boolean
12927
+ */
12928
+ "bAutoWidth": null,
12929
+
12930
+ /**
12931
+ * Delay the creation of TR and TD elements until they are actually
12932
+ * needed by a driven page draw. This can give a significant speed
12933
+ * increase for Ajax source and Javascript source data, but makes no
12934
+ * difference at all fro DOM and server-side processing tables.
12935
+ * Note that this parameter will be set by the initialisation routine. To
12936
+ * set a default use {@link WFDataTable.defaults}.
12937
+ * @type boolean
12938
+ */
12939
+ "bDeferRender": null,
12940
+
12941
+ /**
12942
+ * Enable filtering on the table or not. Note that if this is disabled
12943
+ * then there is no filtering at all on the table, including fnFilter.
12944
+ * To just remove the filtering input use sDom and remove the 'f' option.
12945
+ * Note that this parameter will be set by the initialisation routine. To
12946
+ * set a default use {@link WFDataTable.defaults}.
12947
+ * @type boolean
12948
+ */
12949
+ "bFilter": null,
12950
+
12951
+ /**
12952
+ * Table information element (the 'Showing x of y records' div) enable
12953
+ * flag.
12954
+ * Note that this parameter will be set by the initialisation routine. To
12955
+ * set a default use {@link WFDataTable.defaults}.
12956
+ * @type boolean
12957
+ */
12958
+ "bInfo": null,
12959
+
12960
+ /**
12961
+ * Present a user control allowing the end user to change the page size
12962
+ * when pagination is enabled.
12963
+ * Note that this parameter will be set by the initialisation routine. To
12964
+ * set a default use {@link WFDataTable.defaults}.
12965
+ * @type boolean
12966
+ */
12967
+ "bLengthChange": null,
12968
+
12969
+ /**
12970
+ * Pagination enabled or not. Note that if this is disabled then length
12971
+ * changing must also be disabled.
12972
+ * Note that this parameter will be set by the initialisation routine. To
12973
+ * set a default use {@link WFDataTable.defaults}.
12974
+ * @type boolean
12975
+ */
12976
+ "bPaginate": null,
12977
+
12978
+ /**
12979
+ * Processing indicator enable flag whenever WFDataTables is enacting a
12980
+ * user request - typically an Ajax request for server-side processing.
12981
+ * Note that this parameter will be set by the initialisation routine. To
12982
+ * set a default use {@link WFDataTable.defaults}.
12983
+ * @type boolean
12984
+ */
12985
+ "bProcessing": null,
12986
+
12987
+ /**
12988
+ * Server-side processing enabled flag - when enabled WFDataTables will
12989
+ * get all data from the server for every draw - there is no filtering,
12990
+ * sorting or paging done on the client-side.
12991
+ * Note that this parameter will be set by the initialisation routine. To
12992
+ * set a default use {@link WFDataTable.defaults}.
12993
+ * @type boolean
12994
+ */
12995
+ "bServerSide": null,
12996
+
12997
+ /**
12998
+ * Sorting enablement flag.
12999
+ * Note that this parameter will be set by the initialisation routine. To
13000
+ * set a default use {@link WFDataTable.defaults}.
13001
+ * @type boolean
13002
+ */
13003
+ "bSort": null,
13004
+
13005
+ /**
13006
+ * Multi-column sorting
13007
+ * Note that this parameter will be set by the initialisation routine. To
13008
+ * set a default use {@link WFDataTable.defaults}.
13009
+ * @type boolean
13010
+ */
13011
+ "bSortMulti": null,
13012
+
13013
+ /**
13014
+ * Apply a class to the columns which are being sorted to provide a
13015
+ * visual highlight or not. This can slow things down when enabled since
13016
+ * there is a lot of DOM interaction.
13017
+ * Note that this parameter will be set by the initialisation routine. To
13018
+ * set a default use {@link WFDataTable.defaults}.
13019
+ * @type boolean
13020
+ */
13021
+ "bSortClasses": null,
13022
+
13023
+ /**
13024
+ * State saving enablement flag.
13025
+ * Note that this parameter will be set by the initialisation routine. To
13026
+ * set a default use {@link WFDataTable.defaults}.
13027
+ * @type boolean
13028
+ */
13029
+ "bStateSave": null
13030
+ },
13031
+
13032
+
13033
+ /**
13034
+ * Scrolling settings for a table.
13035
+ * @namespace
13036
+ */
13037
+ "oScroll": {
13038
+ /**
13039
+ * When the table is shorter in height than sScrollY, collapse the
13040
+ * table container down to the height of the table (when true).
13041
+ * Note that this parameter will be set by the initialisation routine. To
13042
+ * set a default use {@link WFDataTable.defaults}.
13043
+ * @type boolean
13044
+ */
13045
+ "bCollapse": null,
13046
+
13047
+ /**
13048
+ * Width of the scrollbar for the web-browser's platform. Calculated
13049
+ * during table initialisation.
13050
+ * @type int
13051
+ * @default 0
13052
+ */
13053
+ "iBarWidth": 0,
13054
+
13055
+ /**
13056
+ * Viewport width for horizontal scrolling. Horizontal scrolling is
13057
+ * disabled if an empty string.
13058
+ * Note that this parameter will be set by the initialisation routine. To
13059
+ * set a default use {@link WFDataTable.defaults}.
13060
+ * @type string
13061
+ */
13062
+ "sX": null,
13063
+
13064
+ /**
13065
+ * Width to expand the table to when using x-scrolling. Typically you
13066
+ * should not need to use this.
13067
+ * Note that this parameter will be set by the initialisation routine. To
13068
+ * set a default use {@link WFDataTable.defaults}.
13069
+ * @type string
13070
+ * @deprecated
13071
+ */
13072
+ "sXInner": null,
13073
+
13074
+ /**
13075
+ * Viewport height for vertical scrolling. Vertical scrolling is disabled
13076
+ * if an empty string.
13077
+ * Note that this parameter will be set by the initialisation routine. To
13078
+ * set a default use {@link WFDataTable.defaults}.
13079
+ * @type string
13080
+ */
13081
+ "sY": null
13082
+ },
13083
+
13084
+ /**
13085
+ * Language information for the table.
13086
+ * @namespace
13087
+ * @extends WFDataTable.defaults.oLanguage
13088
+ */
13089
+ "oLanguage": {
13090
+ /**
13091
+ * Information callback function. See
13092
+ * {@link WFDataTable.defaults.fnInfoCallback}
13093
+ * @type function
13094
+ * @default null
13095
+ */
13096
+ "fnInfoCallback": null
13097
+ },
13098
+
13099
+ /**
13100
+ * Browser support parameters
13101
+ * @namespace
13102
+ */
13103
+ "oBrowser": {
13104
+ /**
13105
+ * Indicate if the browser incorrectly calculates width:100% inside a
13106
+ * scrolling element (IE6/7)
13107
+ * @type boolean
13108
+ * @default false
13109
+ */
13110
+ "bScrollOversize": false,
13111
+
13112
+ /**
13113
+ * Determine if the vertical scrollbar is on the right or left of the
13114
+ * scrolling container - needed for rtl language layout, although not
13115
+ * all browsers move the scrollbar (Safari).
13116
+ * @type boolean
13117
+ * @default false
13118
+ */
13119
+ "bScrollbarLeft": false,
13120
+
13121
+ /**
13122
+ * Flag for if `getBoundingClientRect` is fully supported or not
13123
+ * @type boolean
13124
+ * @default false
13125
+ */
13126
+ "bBounding": false,
13127
+
13128
+ /**
13129
+ * Browser scrollbar width
13130
+ * @type integer
13131
+ * @default 0
13132
+ */
13133
+ "barWidth": 0
13134
+ },
13135
+
13136
+
13137
+ "ajax": null,
13138
+
13139
+
13140
+ /**
13141
+ * Array referencing the nodes which are used for the features. The
13142
+ * parameters of this object match what is allowed by sDom - i.e.
13143
+ * <ul>
13144
+ * <li>'l' - Length changing</li>
13145
+ * <li>'f' - Filtering input</li>
13146
+ * <li>'t' - The table!</li>
13147
+ * <li>'i' - Information</li>
13148
+ * <li>'p' - Pagination</li>
13149
+ * <li>'r' - pRocessing</li>
13150
+ * </ul>
13151
+ * @type array
13152
+ * @default []
13153
+ */
13154
+ "aanFeatures": [],
13155
+
13156
+ /**
13157
+ * Store data information - see {@link WFDataTable.models.oRow} for detailed
13158
+ * information.
13159
+ * @type array
13160
+ * @default []
13161
+ */
13162
+ "aoData": [],
13163
+
13164
+ /**
13165
+ * Array of indexes which are in the current display (after filtering etc)
13166
+ * @type array
13167
+ * @default []
13168
+ */
13169
+ "aiDisplay": [],
13170
+
13171
+ /**
13172
+ * Array of indexes for display - no filtering
13173
+ * @type array
13174
+ * @default []
13175
+ */
13176
+ "aiDisplayMaster": [],
13177
+
13178
+ /**
13179
+ * Map of row ids to data indexes
13180
+ * @type object
13181
+ * @default {}
13182
+ */
13183
+ "aIds": {},
13184
+
13185
+ /**
13186
+ * Store information about each column that is in use
13187
+ * @type array
13188
+ * @default []
13189
+ */
13190
+ "aoColumns": [],
13191
+
13192
+ /**
13193
+ * Store information about the table's header
13194
+ * @type array
13195
+ * @default []
13196
+ */
13197
+ "aoHeader": [],
13198
+
13199
+ /**
13200
+ * Store information about the table's footer
13201
+ * @type array
13202
+ * @default []
13203
+ */
13204
+ "aoFooter": [],
13205
+
13206
+ /**
13207
+ * Store the applied global search information in case we want to force a
13208
+ * research or compare the old search to a new one.
13209
+ * Note that this parameter will be set by the initialisation routine. To
13210
+ * set a default use {@link WFDataTable.defaults}.
13211
+ * @namespace
13212
+ * @extends WFDataTable.models.oSearch
13213
+ */
13214
+ "oPreviousSearch": {},
13215
+
13216
+ /**
13217
+ * Store the applied search for each column - see
13218
+ * {@link WFDataTable.models.oSearch} for the format that is used for the
13219
+ * filtering information for each column.
13220
+ * @type array
13221
+ * @default []
13222
+ */
13223
+ "aoPreSearchCols": [],
13224
+
13225
+ /**
13226
+ * Sorting that is applied to the table. Note that the inner arrays are
13227
+ * used in the following manner:
13228
+ * <ul>
13229
+ * <li>Index 0 - column number</li>
13230
+ * <li>Index 1 - current sorting direction</li>
13231
+ * </ul>
13232
+ * Note that this parameter will be set by the initialisation routine. To
13233
+ * set a default use {@link WFDataTable.defaults}.
13234
+ * @type array
13235
+ * @todo These inner arrays should really be objects
13236
+ */
13237
+ "aaSorting": null,
13238
+
13239
+ /**
13240
+ * Sorting that is always applied to the table (i.e. prefixed in front of
13241
+ * aaSorting).
13242
+ * Note that this parameter will be set by the initialisation routine. To
13243
+ * set a default use {@link WFDataTable.defaults}.
13244
+ * @type array
13245
+ * @default []
13246
+ */
13247
+ "aaSortingFixed": [],
13248
+
13249
+ /**
13250
+ * Classes to use for the striping of a table.
13251
+ * Note that this parameter will be set by the initialisation routine. To
13252
+ * set a default use {@link WFDataTable.defaults}.
13253
+ * @type array
13254
+ * @default []
13255
+ */
13256
+ "asStripeClasses": null,
13257
+
13258
+ /**
13259
+ * If restoring a table - we should restore its striping classes as well
13260
+ * @type array
13261
+ * @default []
13262
+ */
13263
+ "asDestroyStripes": [],
13264
+
13265
+ /**
13266
+ * If restoring a table - we should restore its width
13267
+ * @type int
13268
+ * @default 0
13269
+ */
13270
+ "sDestroyWidth": 0,
13271
+
13272
+ /**
13273
+ * Callback functions array for every time a row is inserted (i.e. on a draw).
13274
+ * @type array
13275
+ * @default []
13276
+ */
13277
+ "aoRowCallback": [],
13278
+
13279
+ /**
13280
+ * Callback functions for the header on each draw.
13281
+ * @type array
13282
+ * @default []
13283
+ */
13284
+ "aoHeaderCallback": [],
13285
+
13286
+ /**
13287
+ * Callback function for the footer on each draw.
13288
+ * @type array
13289
+ * @default []
13290
+ */
13291
+ "aoFooterCallback": [],
13292
+
13293
+ /**
13294
+ * Array of callback functions for draw callback functions
13295
+ * @type array
13296
+ * @default []
13297
+ */
13298
+ "aoDrawCallback": [],
13299
+
13300
+ /**
13301
+ * Array of callback functions for row created function
13302
+ * @type array
13303
+ * @default []
13304
+ */
13305
+ "aoRowCreatedCallback": [],
13306
+
13307
+ /**
13308
+ * Callback functions for just before the table is redrawn. A return of
13309
+ * false will be used to cancel the draw.
13310
+ * @type array
13311
+ * @default []
13312
+ */
13313
+ "aoPreDrawCallback": [],
13314
+
13315
+ /**
13316
+ * Callback functions for when the table has been initialised.
13317
+ * @type array
13318
+ * @default []
13319
+ */
13320
+ "aoInitComplete": [],
13321
+
13322
+
13323
+ /**
13324
+ * Callbacks for modifying the settings to be stored for state saving, prior to
13325
+ * saving state.
13326
+ * @type array
13327
+ * @default []
13328
+ */
13329
+ "aoStateSaveParams": [],
13330
+
13331
+ /**
13332
+ * Callbacks for modifying the settings that have been stored for state saving
13333
+ * prior to using the stored values to restore the state.
13334
+ * @type array
13335
+ * @default []
13336
+ */
13337
+ "aoStateLoadParams": [],
13338
+
13339
+ /**
13340
+ * Callbacks for operating on the settings object once the saved state has been
13341
+ * loaded
13342
+ * @type array
13343
+ * @default []
13344
+ */
13345
+ "aoStateLoaded": [],
13346
+
13347
+ /**
13348
+ * Cache the table ID for quick access
13349
+ * @type string
13350
+ * @default <i>Empty string</i>
13351
+ */
13352
+ "sTableId": "",
13353
+
13354
+ /**
13355
+ * The TABLE node for the main table
13356
+ * @type node
13357
+ * @default null
13358
+ */
13359
+ "nTable": null,
13360
+
13361
+ /**
13362
+ * Permanent ref to the thead element
13363
+ * @type node
13364
+ * @default null
13365
+ */
13366
+ "nTHead": null,
13367
+
13368
+ /**
13369
+ * Permanent ref to the tfoot element - if it exists
13370
+ * @type node
13371
+ * @default null
13372
+ */
13373
+ "nTFoot": null,
13374
+
13375
+ /**
13376
+ * Permanent ref to the tbody element
13377
+ * @type node
13378
+ * @default null
13379
+ */
13380
+ "nTBody": null,
13381
+
13382
+ /**
13383
+ * Cache the wrapper node (contains all WFDataTables controlled elements)
13384
+ * @type node
13385
+ * @default null
13386
+ */
13387
+ "nTableWrapper": null,
13388
+
13389
+ /**
13390
+ * Indicate if when using server-side processing the loading of data
13391
+ * should be deferred until the second draw.
13392
+ * Note that this parameter will be set by the initialisation routine. To
13393
+ * set a default use {@link WFDataTable.defaults}.
13394
+ * @type boolean
13395
+ * @default false
13396
+ */
13397
+ "bDeferLoading": false,
13398
+
13399
+ /**
13400
+ * Indicate if all required information has been read in
13401
+ * @type boolean
13402
+ * @default false
13403
+ */
13404
+ "bInitialised": false,
13405
+
13406
+ /**
13407
+ * Information about open rows. Each object in the array has the parameters
13408
+ * 'nTr' and 'nParent'
13409
+ * @type array
13410
+ * @default []
13411
+ */
13412
+ "aoOpenRows": [],
13413
+
13414
+ /**
13415
+ * Dictate the positioning of WFDataTables' control elements - see
13416
+ * {@link WFDataTable.model.oInit.sDom}.
13417
+ * Note that this parameter will be set by the initialisation routine. To
13418
+ * set a default use {@link WFDataTable.defaults}.
13419
+ * @type string
13420
+ * @default null
13421
+ */
13422
+ "sDom": null,
13423
+
13424
+ /**
13425
+ * Search delay (in mS)
13426
+ * @type integer
13427
+ * @default null
13428
+ */
13429
+ "searchDelay": null,
13430
+
13431
+ /**
13432
+ * Which type of pagination should be used.
13433
+ * Note that this parameter will be set by the initialisation routine. To
13434
+ * set a default use {@link WFDataTable.defaults}.
13435
+ * @type string
13436
+ * @default two_button
13437
+ */
13438
+ "sPaginationType": "two_button",
13439
+
13440
+ /**
13441
+ * The state duration (for `stateSave`) in seconds.
13442
+ * Note that this parameter will be set by the initialisation routine. To
13443
+ * set a default use {@link WFDataTable.defaults}.
13444
+ * @type int
13445
+ * @default 0
13446
+ */
13447
+ "iStateDuration": 0,
13448
+
13449
+ /**
13450
+ * Array of callback functions for state saving. Each array element is an
13451
+ * object with the following parameters:
13452
+ * <ul>
13453
+ * <li>function:fn - function to call. Takes two parameters, oSettings
13454
+ * and the JSON string to save that has been thus far created. Returns
13455
+ * a JSON string to be inserted into a json object
13456
+ * (i.e. '"param": [ 0, 1, 2]')</li>
13457
+ * <li>string:sName - name of callback</li>
13458
+ * </ul>
13459
+ * @type array
13460
+ * @default []
13461
+ */
13462
+ "aoStateSave": [],
13463
+
13464
+ /**
13465
+ * Array of callback functions for state loading. Each array element is an
13466
+ * object with the following parameters:
13467
+ * <ul>
13468
+ * <li>function:fn - function to call. Takes two parameters, oSettings
13469
+ * and the object stored. May return false to cancel state loading</li>
13470
+ * <li>string:sName - name of callback</li>
13471
+ * </ul>
13472
+ * @type array
13473
+ * @default []
13474
+ */
13475
+ "aoStateLoad": [],
13476
+
13477
+ /**
13478
+ * State that was saved. Useful for back reference
13479
+ * @type object
13480
+ * @default null
13481
+ */
13482
+ "oSavedState": null,
13483
+
13484
+ /**
13485
+ * State that was loaded. Useful for back reference
13486
+ * @type object
13487
+ * @default null
13488
+ */
13489
+ "oLoadedState": null,
13490
+
13491
+ /**
13492
+ * Source url for AJAX data for the table.
13493
+ * Note that this parameter will be set by the initialisation routine. To
13494
+ * set a default use {@link WFDataTable.defaults}.
13495
+ * @type string
13496
+ * @default null
13497
+ */
13498
+ "sAjaxSource": null,
13499
+
13500
+ /**
13501
+ * Property from a given object from which to read the table data from. This
13502
+ * can be an empty string (when not server-side processing), in which case
13503
+ * it is assumed an an array is given directly.
13504
+ * Note that this parameter will be set by the initialisation routine. To
13505
+ * set a default use {@link WFDataTable.defaults}.
13506
+ * @type string
13507
+ */
13508
+ "sAjaxDataProp": null,
13509
+
13510
+ /**
13511
+ * Note if draw should be blocked while getting data
13512
+ * @type boolean
13513
+ * @default true
13514
+ */
13515
+ "bAjaxDataGet": true,
13516
+
13517
+ /**
13518
+ * The last jQuery XHR object that was used for server-side data gathering.
13519
+ * This can be used for working with the XHR information in one of the
13520
+ * callbacks
13521
+ * @type object
13522
+ * @default null
13523
+ */
13524
+ "jqXHR": null,
13525
+
13526
+ /**
13527
+ * JSON returned from the server in the last Ajax request
13528
+ * @type object
13529
+ * @default undefined
13530
+ */
13531
+ "json": undefined,
13532
+
13533
+ /**
13534
+ * Data submitted as part of the last Ajax request
13535
+ * @type object
13536
+ * @default undefined
13537
+ */
13538
+ "oAjaxData": undefined,
13539
+
13540
+ /**
13541
+ * Function to get the server-side data.
13542
+ * Note that this parameter will be set by the initialisation routine. To
13543
+ * set a default use {@link WFDataTable.defaults}.
13544
+ * @type function
13545
+ */
13546
+ "fnServerData": null,
13547
+
13548
+ /**
13549
+ * Functions which are called prior to sending an Ajax request so extra
13550
+ * parameters can easily be sent to the server
13551
+ * @type array
13552
+ * @default []
13553
+ */
13554
+ "aoServerParams": [],
13555
+
13556
+ /**
13557
+ * Send the XHR HTTP method - GET or POST (could be PUT or DELETE if
13558
+ * required).
13559
+ * Note that this parameter will be set by the initialisation routine. To
13560
+ * set a default use {@link WFDataTable.defaults}.
13561
+ * @type string
13562
+ */
13563
+ "sServerMethod": null,
13564
+
13565
+ /**
13566
+ * Format numbers for display.
13567
+ * Note that this parameter will be set by the initialisation routine. To
13568
+ * set a default use {@link WFDataTable.defaults}.
13569
+ * @type function
13570
+ */
13571
+ "fnFormatNumber": null,
13572
+
13573
+ /**
13574
+ * List of options that can be used for the user selectable length menu.
13575
+ * Note that this parameter will be set by the initialisation routine. To
13576
+ * set a default use {@link WFDataTable.defaults}.
13577
+ * @type array
13578
+ * @default []
13579
+ */
13580
+ "aLengthMenu": null,
13581
+
13582
+ /**
13583
+ * Counter for the draws that the table does. Also used as a tracker for
13584
+ * server-side processing
13585
+ * @type int
13586
+ * @default 0
13587
+ */
13588
+ "iDraw": 0,
13589
+
13590
+ /**
13591
+ * Indicate if a redraw is being done - useful for Ajax
13592
+ * @type boolean
13593
+ * @default false
13594
+ */
13595
+ "bDrawing": false,
13596
+
13597
+ /**
13598
+ * Draw index (iDraw) of the last error when parsing the returned data
13599
+ * @type int
13600
+ * @default -1
13601
+ */
13602
+ "iDrawError": -1,
13603
+
13604
+ /**
13605
+ * Paging display length
13606
+ * @type int
13607
+ * @default 10
13608
+ */
13609
+ "_iDisplayLength": 10,
13610
+
13611
+ /**
13612
+ * Paging start point - aiDisplay index
13613
+ * @type int
13614
+ * @default 0
13615
+ */
13616
+ "_iDisplayStart": 0,
13617
+
13618
+ /**
13619
+ * Server-side processing - number of records in the result set
13620
+ * (i.e. before filtering), Use fnRecordsTotal rather than
13621
+ * this property to get the value of the number of records, regardless of
13622
+ * the server-side processing setting.
13623
+ * @type int
13624
+ * @default 0
13625
+ * @private
13626
+ */
13627
+ "_iRecordsTotal": 0,
13628
+
13629
+ /**
13630
+ * Server-side processing - number of records in the current display set
13631
+ * (i.e. after filtering). Use fnRecordsDisplay rather than
13632
+ * this property to get the value of the number of records, regardless of
13633
+ * the server-side processing setting.
13634
+ * @type boolean
13635
+ * @default 0
13636
+ * @private
13637
+ */
13638
+ "_iRecordsDisplay": 0,
13639
+
13640
+ /**
13641
+ * Flag to indicate if jQuery UI marking and classes should be used.
13642
+ * Note that this parameter will be set by the initialisation routine. To
13643
+ * set a default use {@link WFDataTable.defaults}.
13644
+ * @type boolean
13645
+ */
13646
+ "bJUI": null,
13647
+
13648
+ /**
13649
+ * The classes to use for the table
13650
+ * @type object
13651
+ * @default {}
13652
+ */
13653
+ "oClasses": {},
13654
+
13655
+ /**
13656
+ * Flag attached to the settings object so you can check in the draw
13657
+ * callback if filtering has been done in the draw. Deprecated in favour of
13658
+ * events.
13659
+ * @type boolean
13660
+ * @default false
13661
+ * @deprecated
13662
+ */
13663
+ "bFiltered": false,
13664
+
13665
+ /**
13666
+ * Flag attached to the settings object so you can check in the draw
13667
+ * callback if sorting has been done in the draw. Deprecated in favour of
13668
+ * events.
13669
+ * @type boolean
13670
+ * @default false
13671
+ * @deprecated
13672
+ */
13673
+ "bSorted": false,
13674
+
13675
+ /**
13676
+ * Indicate that if multiple rows are in the header and there is more than
13677
+ * one unique cell per column, if the top one (true) or bottom one (false)
13678
+ * should be used for sorting / title by WFDataTables.
13679
+ * Note that this parameter will be set by the initialisation routine. To
13680
+ * set a default use {@link WFDataTable.defaults}.
13681
+ * @type boolean
13682
+ */
13683
+ "bSortCellsTop": null,
13684
+
13685
+ /**
13686
+ * Initialisation object that is used for the table
13687
+ * @type object
13688
+ * @default null
13689
+ */
13690
+ "oInit": null,
13691
+
13692
+ /**
13693
+ * Destroy callback functions - for plug-ins to attach themselves to the
13694
+ * destroy so they can clean up markup and events.
13695
+ * @type array
13696
+ * @default []
13697
+ */
13698
+ "aoDestroyCallback": [],
13699
+
13700
+
13701
+ /**
13702
+ * Get the number of records in the current record set, before filtering
13703
+ * @type function
13704
+ */
13705
+ "fnRecordsTotal": function ()
13706
+ {
13707
+ return _fnDataSource( this ) == 'ssp' ?
13708
+ this._iRecordsTotal * 1 :
13709
+ this.aiDisplayMaster.length;
13710
+ },
13711
+
13712
+ /**
13713
+ * Get the number of records in the current record set, after filtering
13714
+ * @type function
13715
+ */
13716
+ "fnRecordsDisplay": function ()
13717
+ {
13718
+ return _fnDataSource( this ) == 'ssp' ?
13719
+ this._iRecordsDisplay * 1 :
13720
+ this.aiDisplay.length;
13721
+ },
13722
+
13723
+ /**
13724
+ * Get the display end point - aiDisplay index
13725
+ * @type function
13726
+ */
13727
+ "fnDisplayEnd": function ()
13728
+ {
13729
+ var
13730
+ len = this._iDisplayLength,
13731
+ start = this._iDisplayStart,
13732
+ calc = start + len,
13733
+ records = this.aiDisplay.length,
13734
+ features = this.oFeatures,
13735
+ paginate = features.bPaginate;
13736
+
13737
+ if ( features.bServerSide ) {
13738
+ return paginate === false || len === -1 ?
13739
+ start + records :
13740
+ Math.min( start+len, this._iRecordsDisplay );
13741
+ }
13742
+ else {
13743
+ return ! paginate || calc>records || len===-1 ?
13744
+ records :
13745
+ calc;
13746
+ }
13747
+ },
13748
+
13749
+ /**
13750
+ * The WFDataTables object for this table
13751
+ * @type object
13752
+ * @default null
13753
+ */
13754
+ "oInstance": null,
13755
+
13756
+ /**
13757
+ * Unique identifier for each instance of the WFDataTables object. If there
13758
+ * is an ID on the table node, then it takes that value, otherwise an
13759
+ * incrementing internal counter is used.
13760
+ * @type string
13761
+ * @default null
13762
+ */
13763
+ "sInstance": null,
13764
+
13765
+ /**
13766
+ * tabindex attribute value that is added to WFDataTables control elements, allowing
13767
+ * keyboard navigation of the table and its controls.
13768
+ */
13769
+ "iTabIndex": 0,
13770
+
13771
+ /**
13772
+ * DIV container for the footer scrolling table if scrolling
13773
+ */
13774
+ "nScrollHead": null,
13775
+
13776
+ /**
13777
+ * DIV container for the footer scrolling table if scrolling
13778
+ */
13779
+ "nScrollFoot": null,
13780
+
13781
+ /**
13782
+ * Last applied sort
13783
+ * @type array
13784
+ * @default []
13785
+ */
13786
+ "aLastSort": [],
13787
+
13788
+ /**
13789
+ * Stored plug-in instances
13790
+ * @type object
13791
+ * @default {}
13792
+ */
13793
+ "oPlugins": {},
13794
+
13795
+ /**
13796
+ * Function used to get a row's id from the row's data
13797
+ * @type function
13798
+ * @default null
13799
+ */
13800
+ "rowIdFn": null,
13801
+
13802
+ /**
13803
+ * Data location where to store a row's id
13804
+ * @type string
13805
+ * @default null
13806
+ */
13807
+ "rowId": null
13808
+ };
13809
+
13810
+ /**
13811
+ * Extension object for WFDataTables that is used to provide all extension
13812
+ * options.
13813
+ *
13814
+ * Note that the `WFDataTable.ext` object is available through
13815
+ * `jQuery.fn.wfDataTable.ext` where it may be accessed and manipulated. It is
13816
+ * also aliased to `jQuery.fn.wfDataTableExt` for historic reasons.
13817
+ * @namespace
13818
+ * @extends WFDataTable.models.ext
13819
+ */
13820
+
13821
+
13822
+ /**
13823
+ * WFDataTables extensions
13824
+ *
13825
+ * This namespace acts as a collection area for plug-ins that can be used to
13826
+ * extend WFDataTables capabilities. Indeed many of the build in methods
13827
+ * use this method to provide their own capabilities (sorting methods for
13828
+ * example).
13829
+ *
13830
+ * Note that this namespace is aliased to `jQuery.fn.wfDataTableExt` for legacy
13831
+ * reasons
13832
+ *
13833
+ * @namespace
13834
+ */
13835
+ WFDataTable.ext = _ext = {
13836
+ /**
13837
+ * Buttons. For use with the Buttons extension for WFDataTables. This is
13838
+ * defined here so other extensions can define buttons regardless of load
13839
+ * order. It is _not_ used by WFDataTables core.
13840
+ *
13841
+ * @type object
13842
+ * @default {}
13843
+ */
13844
+ buttons: {},
13845
+
13846
+
13847
+ /**
13848
+ * Element class names
13849
+ *
13850
+ * @type object
13851
+ * @default {}
13852
+ */
13853
+ classes: {},
13854
+
13855
+
13856
+ /**
13857
+ * WFDataTables build type (expanded by the download builder)
13858
+ *
13859
+ * @type string
13860
+ */
13861
+ build:"dt/dt-1.10.13",
13862
+
13863
+
13864
+ /**
13865
+ * Error reporting.
13866
+ *
13867
+ * How should WFDataTables report an error. Can take the value 'alert',
13868
+ * 'throw', 'none' or a function.
13869
+ *
13870
+ * @type string|function
13871
+ * @default alert
13872
+ */
13873
+ errMode: "alert",
13874
+
13875
+
13876
+ /**
13877
+ * Feature plug-ins.
13878
+ *
13879
+ * This is an array of objects which describe the feature plug-ins that are
13880
+ * available to WFDataTables. These feature plug-ins are then available for
13881
+ * use through the `dom` initialisation option.
13882
+ *
13883
+ * Each feature plug-in is described by an object which must have the
13884
+ * following properties:
13885
+ *
13886
+ * * `fnInit` - function that is used to initialise the plug-in,
13887
+ * * `cFeature` - a character so the feature can be enabled by the `dom`
13888
+ * instillation option. This is case sensitive.
13889
+ *
13890
+ * The `fnInit` function has the following input parameters:
13891
+ *
13892
+ * 1. `{object}` WFDataTables settings object: see
13893
+ * {@link WFDataTable.models.oSettings}
13894
+ *
13895
+ * And the following return is expected:
13896
+ *
13897
+ * * {node|null} The element which contains your feature. Note that the
13898
+ * return may also be void if your plug-in does not require to inject any
13899
+ * DOM elements into WFDataTables control (`dom`) - for example this might
13900
+ * be useful when developing a plug-in which allows table control via
13901
+ * keyboard entry
13902
+ *
13903
+ * @type array
13904
+ *
13905
+ * @example
13906
+ * $.fn.wfDataTable.ext.features.push( {
13907
+ * "fnInit": function( oSettings ) {
13908
+ * return new TableTools( { "oDTSettings": oSettings } );
13909
+ * },
13910
+ * "cFeature": "T"
13911
+ * } );
13912
+ */
13913
+ feature: [],
13914
+
13915
+
13916
+ /**
13917
+ * Row searching.
13918
+ *
13919
+ * This method of searching is complimentary to the default type based
13920
+ * searching, and a lot more comprehensive as it allows you complete control
13921
+ * over the searching logic. Each element in this array is a function
13922
+ * (parameters described below) that is called for every row in the table,
13923
+ * and your logic decides if it should be included in the searching data set
13924
+ * or not.
13925
+ *
13926
+ * Searching functions have the following input parameters:
13927
+ *
13928
+ * 1. `{object}` WFDataTables settings object: see
13929
+ * {@link WFDataTable.models.oSettings}
13930
+ * 2. `{array|object}` Data for the row to be processed (same as the
13931
+ * original format that was passed in as the data source, or an array
13932
+ * from a DOM data source
13933
+ * 3. `{int}` Row index ({@link WFDataTable.models.oSettings.aoData}), which
13934
+ * can be useful to retrieve the `TR` element if you need DOM interaction.
13935
+ *
13936
+ * And the following return is expected:
13937
+ *
13938
+ * * {boolean} Include the row in the searched result set (true) or not
13939
+ * (false)
13940
+ *
13941
+ * Note that as with the main search ability in WFDataTables, technically this
13942
+ * is "filtering", since it is subtractive. However, for consistency in
13943
+ * naming we call it searching here.
13944
+ *
13945
+ * @type array
13946
+ * @default []
13947
+ *
13948
+ * @example
13949
+ * // The following example shows custom search being applied to the
13950
+ * // fourth column (i.e. the data[3] index) based on two input values
13951
+ * // from the end-user, matching the data in a certain range.
13952
+ * $.fn.wfDataTable.ext.search.push(
13953
+ * function( settings, data, dataIndex ) {
13954
+ * var min = document.getElementById('min').value * 1;
13955
+ * var max = document.getElementById('max').value * 1;
13956
+ * var version = data[3] == "-" ? 0 : data[3]*1;
13957
+ *
13958
+ * if ( min == "" && max == "" ) {
13959
+ * return true;
13960
+ * }
13961
+ * else if ( min == "" && version < max ) {
13962
+ * return true;
13963
+ * }
13964
+ * else if ( min < version && "" == max ) {
13965
+ * return true;
13966
+ * }
13967
+ * else if ( min < version && version < max ) {
13968
+ * return true;
13969
+ * }
13970
+ * return false;
13971
+ * }
13972
+ * );
13973
+ */
13974
+ search: [],
13975
+
13976
+
13977
+ /**
13978
+ * Selector extensions
13979
+ *
13980
+ * The `selector` option can be used to extend the options available for the
13981
+ * selector modifier options (`selector-modifier` object data type) that
13982
+ * each of the three built in selector types offer (row, column and cell +
13983
+ * their plural counterparts). For example the Select extension uses this
13984
+ * mechanism to provide an option to select only rows, columns and cells
13985
+ * that have been marked as selected by the end user (`{selected: true}`),
13986
+ * which can be used in conjunction with the existing built in selector
13987
+ * options.
13988
+ *
13989
+ * Each property is an array to which functions can be pushed. The functions
13990
+ * take three attributes:
13991
+ *
13992
+ * * Settings object for the host table
13993
+ * * Options object (`selector-modifier` object type)
13994
+ * * Array of selected item indexes
13995
+ *
13996
+ * The return is an array of the resulting item indexes after the custom
13997
+ * selector has been applied.
13998
+ *
13999
+ * @type object
14000
+ */
14001
+ selector: {
14002
+ cell: [],
14003
+ column: [],
14004
+ row: []
14005
+ },
14006
+
14007
+
14008
+ /**
14009
+ * Internal functions, exposed for used in plug-ins.
14010
+ *
14011
+ * Please note that you should not need to use the internal methods for
14012
+ * anything other than a plug-in (and even then, try to avoid if possible).
14013
+ * The internal function may change between releases.
14014
+ *
14015
+ * @type object
14016
+ * @default {}
14017
+ */
14018
+ internal: {},
14019
+
14020
+
14021
+ /**
14022
+ * Legacy configuration options. Enable and disable legacy options that
14023
+ * are available in WFDataTables.
14024
+ *
14025
+ * @type object
14026
+ */
14027
+ legacy: {
14028
+ /**
14029
+ * Enable / disable WFDataTables 1.9 compatible server-side processing
14030
+ * requests
14031
+ *
14032
+ * @type boolean
14033
+ * @default null
14034
+ */
14035
+ ajax: null
14036
+ },
14037
+
14038
+
14039
+ /**
14040
+ * Pagination plug-in methods.
14041
+ *
14042
+ * Each entry in this object is a function and defines which buttons should
14043
+ * be shown by the pagination rendering method that is used for the table:
14044
+ * {@link WFDataTable.ext.renderer.pageButton}. The renderer addresses how the
14045
+ * buttons are displayed in the document, while the functions here tell it
14046
+ * what buttons to display. This is done by returning an array of button
14047
+ * descriptions (what each button will do).
14048
+ *
14049
+ * Pagination types (the four built in options and any additional plug-in
14050
+ * options defined here) can be used through the `paginationType`
14051
+ * initialisation parameter.
14052
+ *
14053
+ * The functions defined take two parameters:
14054
+ *
14055
+ * 1. `{int} page` The current page index
14056
+ * 2. `{int} pages` The number of pages in the table
14057
+ *
14058
+ * Each function is expected to return an array where each element of the
14059
+ * array can be one of:
14060
+ *
14061
+ * * `first` - Jump to first page when activated
14062
+ * * `last` - Jump to last page when activated
14063
+ * * `previous` - Show previous page when activated
14064
+ * * `next` - Show next page when activated
14065
+ * * `{int}` - Show page of the index given
14066
+ * * `{array}` - A nested array containing the above elements to add a
14067
+ * containing 'DIV' element (might be useful for styling).
14068
+ *
14069
+ * Note that WFDataTables v1.9- used this object slightly differently whereby
14070
+ * an object with two functions would be defined for each plug-in. That
14071
+ * ability is still supported by WFDataTables 1.10+ to provide backwards
14072
+ * compatibility, but this option of use is now decremented and no longer
14073
+ * documented in WFDataTables 1.10+.
14074
+ *
14075
+ * @type object
14076
+ * @default {}
14077
+ *
14078
+ * @example
14079
+ * // Show previous, next and current page buttons only
14080
+ * $.fn.wfDataTableExt.oPagination.current = function ( page, pages ) {
14081
+ * return [ 'previous', page, 'next' ];
14082
+ * };
14083
+ */
14084
+ pager: {},
14085
+
14086
+
14087
+ renderer: {
14088
+ pageButton: {},
14089
+ header: {}
14090
+ },
14091
+
14092
+
14093
+ /**
14094
+ * Ordering plug-ins - custom data source
14095
+ *
14096
+ * The extension options for ordering of data available here is complimentary
14097
+ * to the default type based ordering that WFDataTables typically uses. It
14098
+ * allows much greater control over the the data that is being used to
14099
+ * order a column, but is necessarily therefore more complex.
14100
+ *
14101
+ * This type of ordering is useful if you want to do ordering based on data
14102
+ * live from the DOM (for example the contents of an 'input' element) rather
14103
+ * than just the static string that WFDataTables knows of.
14104
+ *
14105
+ * The way these plug-ins work is that you create an array of the values you
14106
+ * wish to be ordering for the column in question and then return that
14107
+ * array. The data in the array much be in the index order of the rows in
14108
+ * the table (not the currently ordering order!). Which order data gathering
14109
+ * function is run here depends on the `dt-init columns.orderDataType`
14110
+ * parameter that is used for the column (if any).
14111
+ *
14112
+ * The functions defined take two parameters:
14113
+ *
14114
+ * 1. `{object}` WFDataTables settings object: see
14115
+ * {@link WFDataTable.models.oSettings}
14116
+ * 2. `{int}` Target column index
14117
+ *
14118
+ * Each function is expected to return an array:
14119
+ *
14120
+ * * `{array}` Data for the column to be ordering upon
14121
+ *
14122
+ * @type array
14123
+ *
14124
+ * @example
14125
+ * // Ordering using `input` node values
14126
+ * $.fn.wfDataTable.ext.order['dom-text'] = function ( settings, col )
14127
+ * {
14128
+ * return this.api().column( col, {order:'index'} ).nodes().map( function ( td, i ) {
14129
+ * return $('input', td).val();
14130
+ * } );
14131
+ * }
14132
+ */
14133
+ order: {},
14134
+
14135
+
14136
+ /**
14137
+ * Type based plug-ins.
14138
+ *
14139
+ * Each column in WFDataTables has a type assigned to it, either by automatic
14140
+ * detection or by direct assignment using the `type` option for the column.
14141
+ * The type of a column will effect how it is ordering and search (plug-ins
14142
+ * can also make use of the column type if required).
14143
+ *
14144
+ * @namespace
14145
+ */
14146
+ type: {
14147
+ /**
14148
+ * Type detection functions.
14149
+ *
14150
+ * The functions defined in this object are used to automatically detect
14151
+ * a column's type, making initialisation of WFDataTables super easy, even
14152
+ * when complex data is in the table.
14153
+ *
14154
+ * The functions defined take two parameters:
14155
+ *
14156
+ * 1. `{*}` Data from the column cell to be analysed
14157
+ * 2. `{settings}` WFDataTables settings object. This can be used to
14158
+ * perform context specific type detection - for example detection
14159
+ * based on language settings such as using a comma for a decimal
14160
+ * place. Generally speaking the options from the settings will not
14161
+ * be required
14162
+ *
14163
+ * Each function is expected to return:
14164
+ *
14165
+ * * `{string|null}` Data type detected, or null if unknown (and thus
14166
+ * pass it on to the other type detection functions.
14167
+ *
14168
+ * @type array
14169
+ *
14170
+ * @example
14171
+ * // Currency type detection plug-in:
14172
+ * $.fn.wfDataTable.ext.type.detect.push(
14173
+ * function ( data, settings ) {
14174
+ * // Check the numeric part
14175
+ * if ( ! $.isNumeric( data.substring(1) ) ) {
14176
+ * return null;
14177
+ * }
14178
+ *
14179
+ * // Check prefixed by currency
14180
+ * if ( data.charAt(0) == '$' || data.charAt(0) == '&pound;' ) {
14181
+ * return 'currency';
14182
+ * }
14183
+ * return null;
14184
+ * }
14185
+ * );
14186
+ */
14187
+ detect: [],
14188
+
14189
+
14190
+ /**
14191
+ * Type based search formatting.
14192
+ *
14193
+ * The type based searching functions can be used to pre-format the
14194
+ * data to be search on. For example, it can be used to strip HTML
14195
+ * tags or to de-format telephone numbers for numeric only searching.
14196
+ *
14197
+ * Note that is a search is not defined for a column of a given type,
14198
+ * no search formatting will be performed.
14199
+ *
14200
+ * Pre-processing of searching data plug-ins - When you assign the sType
14201
+ * for a column (or have it automatically detected for you by WFDataTables
14202
+ * or a type detection plug-in), you will typically be using this for
14203
+ * custom sorting, but it can also be used to provide custom searching
14204
+ * by allowing you to pre-processing the data and returning the data in
14205
+ * the format that should be searched upon. This is done by adding
14206
+ * functions this object with a parameter name which matches the sType
14207
+ * for that target column. This is the corollary of <i>afnSortData</i>
14208
+ * for searching data.
14209
+ *
14210
+ * The functions defined take a single parameter:
14211
+ *
14212
+ * 1. `{*}` Data from the column cell to be prepared for searching
14213
+ *
14214
+ * Each function is expected to return:
14215
+ *
14216
+ * * `{string|null}` Formatted string that will be used for the searching.
14217
+ *
14218
+ * @type object
14219
+ * @default {}
14220
+ *
14221
+ * @example
14222
+ * $.fn.wfDataTable.ext.type.search['title-numeric'] = function ( d ) {
14223
+ * return d.replace(/\n/g," ").replace( /<.*?>/g, "" );
14224
+ * }
14225
+ */
14226
+ search: {},
14227
+
14228
+
14229
+ /**
14230
+ * Type based ordering.
14231
+ *
14232
+ * The column type tells WFDataTables what ordering to apply to the table
14233
+ * when a column is sorted upon. The order for each type that is defined,
14234
+ * is defined by the functions available in this object.
14235
+ *
14236
+ * Each ordering option can be described by three properties added to
14237
+ * this object:
14238
+ *
14239
+ * * `{type}-pre` - Pre-formatting function
14240
+ * * `{type}-asc` - Ascending order function
14241
+ * * `{type}-desc` - Descending order function
14242
+ *
14243
+ * All three can be used together, only `{type}-pre` or only
14244
+ * `{type}-asc` and `{type}-desc` together. It is generally recommended
14245
+ * that only `{type}-pre` is used, as this provides the optimal
14246
+ * implementation in terms of speed, although the others are provided
14247
+ * for compatibility with existing Javascript sort functions.
14248
+ *
14249
+ * `{type}-pre`: Functions defined take a single parameter:
14250
+ *
14251
+ * 1. `{*}` Data from the column cell to be prepared for ordering
14252
+ *
14253
+ * And return:
14254
+ *
14255
+ * * `{*}` Data to be sorted upon
14256
+ *
14257
+ * `{type}-asc` and `{type}-desc`: Functions are typical Javascript sort
14258
+ * functions, taking two parameters:
14259
+ *
14260
+ * 1. `{*}` Data to compare to the second parameter
14261
+ * 2. `{*}` Data to compare to the first parameter
14262
+ *
14263
+ * And returning:
14264
+ *
14265
+ * * `{*}` Ordering match: <0 if first parameter should be sorted lower
14266
+ * than the second parameter, ===0 if the two parameters are equal and
14267
+ * >0 if the first parameter should be sorted height than the second
14268
+ * parameter.
14269
+ *
14270
+ * @type object
14271
+ * @default {}
14272
+ *
14273
+ * @example
14274
+ * // Numeric ordering of formatted numbers with a pre-formatter
14275
+ * $.extend( $.fn.wfDataTable.ext.type.order, {
14276
+ * "string-pre": function(x) {
14277
+ * a = (a === "-" || a === "") ? 0 : a.replace( /[^\d\-\.]/g, "" );
14278
+ * return parseFloat( a );
14279
+ * }
14280
+ * } );
14281
+ *
14282
+ * @example
14283
+ * // Case-sensitive string ordering, with no pre-formatting method
14284
+ * $.extend( $.fn.wfDataTable.ext.order, {
14285
+ * "string-case-asc": function(x,y) {
14286
+ * return ((x < y) ? -1 : ((x > y) ? 1 : 0));
14287
+ * },
14288
+ * "string-case-desc": function(x,y) {
14289
+ * return ((x < y) ? 1 : ((x > y) ? -1 : 0));
14290
+ * }
14291
+ * } );
14292
+ */
14293
+ order: {}
14294
+ },
14295
+
14296
+ /**
14297
+ * Unique WFDataTables instance counter
14298
+ *
14299
+ * @type int
14300
+ * @private
14301
+ */
14302
+ _unique: 0,
14303
+
14304
+
14305
+ //
14306
+ // Depreciated
14307
+ // The following properties are retained for backwards compatiblity only.
14308
+ // The should not be used in new projects and will be removed in a future
14309
+ // version
14310
+ //
14311
+
14312
+ /**
14313
+ * Version check function.
14314
+ * @type function
14315
+ * @depreciated Since 1.10
14316
+ */
14317
+ fnVersionCheck: WFDataTable.fnVersionCheck,
14318
+
14319
+
14320
+ /**
14321
+ * Index for what 'this' index API functions should use
14322
+ * @type int
14323
+ * @deprecated Since v1.10
14324
+ */
14325
+ iApiIndex: 0,
14326
+
14327
+
14328
+ /**
14329
+ * jQuery UI class container
14330
+ * @type object
14331
+ * @deprecated Since v1.10
14332
+ */
14333
+ oJUIClasses: {},
14334
+
14335
+
14336
+ /**
14337
+ * Software version
14338
+ * @type string
14339
+ * @deprecated Since v1.10
14340
+ */
14341
+ sVersion: WFDataTable.version
14342
+ };
14343
+
14344
+
14345
+ //
14346
+ // Backwards compatibility. Alias to pre 1.10 Hungarian notation counter parts
14347
+ //
14348
+ $.extend( _ext, {
14349
+ afnFiltering: _ext.search,
14350
+ aTypes: _ext.type.detect,
14351
+ ofnSearch: _ext.type.search,
14352
+ oSort: _ext.type.order,
14353
+ afnSortData: _ext.order,
14354
+ aoFeatures: _ext.feature,
14355
+ oApi: _ext.internal,
14356
+ oStdClasses: _ext.classes,
14357
+ oPagination: _ext.pager
14358
+ } );
14359
+
14360
+
14361
+ $.extend( WFDataTable.ext.classes, {
14362
+ "sTable": "wf-dataTable",
14363
+ "sNoFooter": "no-footer",
14364
+
14365
+ /* Paging buttons */
14366
+ "sPageButton": "paginate_button",
14367
+ "sPageButtonActive": "current",
14368
+ "sPageButtonDisabled": "disabled",
14369
+
14370
+ /* Striping classes */
14371
+ "sStripeOdd": "odd",
14372
+ "sStripeEven": "even",
14373
+
14374
+ /* Empty row */
14375
+ "sRowEmpty": "dataTables_empty",
14376
+
14377
+ /* Features */
14378
+ "sWrapper": "wf-dataTables_wrapper",
14379
+ "sFilter": "dataTables_filter",
14380
+ "sInfo": "dataTables_info",
14381
+ "sPaging": "dataTables_paginate paging_", /* Note that the type is postfixed */
14382
+ "sLength": "dataTables_length",
14383
+ "sProcessing": "dataTables_processing",
14384
+
14385
+ /* Sorting */
14386
+ "sSortAsc": "sorting_asc",
14387
+ "sSortDesc": "sorting_desc",
14388
+ "sSortable": "sorting", /* Sortable in both directions */
14389
+ "sSortableAsc": "sorting_asc_disabled",
14390
+ "sSortableDesc": "sorting_desc_disabled",
14391
+ "sSortableNone": "sorting_disabled",
14392
+ "sSortColumn": "sorting_", /* Note that an int is postfixed for the sorting order */
14393
+
14394
+ /* Filtering */
14395
+ "sFilterInput": "",
14396
+
14397
+ /* Page length */
14398
+ "sLengthSelect": "",
14399
+
14400
+ /* Scrolling */
14401
+ "sScrollWrapper": "dataTables_scroll",
14402
+ "sScrollHead": "dataTables_scrollHead",
14403
+ "sScrollHeadInner": "dataTables_scrollHeadInner",
14404
+ "sScrollBody": "dataTables_scrollBody",
14405
+ "sScrollFoot": "dataTables_scrollFoot",
14406
+ "sScrollFootInner": "dataTables_scrollFootInner",
14407
+
14408
+ /* Misc */
14409
+ "sHeaderTH": "",
14410
+ "sFooterTH": "",
14411
+
14412
+ // Deprecated
14413
+ "sSortJUIAsc": "",
14414
+ "sSortJUIDesc": "",
14415
+ "sSortJUI": "",
14416
+ "sSortJUIAscAllowed": "",
14417
+ "sSortJUIDescAllowed": "",
14418
+ "sSortJUIWrapper": "",
14419
+ "sSortIcon": "",
14420
+ "sJUIHeader": "",
14421
+ "sJUIFooter": ""
14422
+ } );
14423
+
14424
+
14425
+ (function() {
14426
+
14427
+ // Reused strings for better compression. Closure compiler appears to have a
14428
+ // weird edge case where it is trying to expand strings rather than use the
14429
+ // variable version. This results in about 200 bytes being added, for very
14430
+ // little preference benefit since it this run on script load only.
14431
+ var _empty = '';
14432
+ _empty = '';
14433
+
14434
+ var _stateDefault = _empty + 'ui-state-default';
14435
+ var _sortIcon = _empty + 'css_right ui-icon ui-icon-';
14436
+ var _headerFooter = _empty + 'fg-toolbar ui-toolbar ui-widget-header ui-helper-clearfix';
14437
+
14438
+ $.extend( WFDataTable.ext.oJUIClasses, WFDataTable.ext.classes, {
14439
+ /* Full numbers paging buttons */
14440
+ "sPageButton": "fg-button ui-button "+_stateDefault,
14441
+ "sPageButtonActive": "ui-state-disabled",
14442
+ "sPageButtonDisabled": "ui-state-disabled",
14443
+
14444
+ /* Features */
14445
+ "sPaging": "dataTables_paginate fg-buttonset ui-buttonset fg-buttonset-multi "+
14446
+ "ui-buttonset-multi paging_", /* Note that the type is postfixed */
14447
+
14448
+ /* Sorting */
14449
+ "sSortAsc": _stateDefault+" sorting_asc",
14450
+ "sSortDesc": _stateDefault+" sorting_desc",
14451
+ "sSortable": _stateDefault+" sorting",
14452
+ "sSortableAsc": _stateDefault+" sorting_asc_disabled",
14453
+ "sSortableDesc": _stateDefault+" sorting_desc_disabled",
14454
+ "sSortableNone": _stateDefault+" sorting_disabled",
14455
+ "sSortJUIAsc": _sortIcon+"triangle-1-n",
14456
+ "sSortJUIDesc": _sortIcon+"triangle-1-s",
14457
+ "sSortJUI": _sortIcon+"carat-2-n-s",
14458
+ "sSortJUIAscAllowed": _sortIcon+"carat-1-n",
14459
+ "sSortJUIDescAllowed": _sortIcon+"carat-1-s",
14460
+ "sSortJUIWrapper": "WFDataTables_sort_wrapper",
14461
+ "sSortIcon": "WFDataTables_sort_icon",
14462
+
14463
+ /* Scrolling */
14464
+ "sScrollHead": "dataTables_scrollHead "+_stateDefault,
14465
+ "sScrollFoot": "dataTables_scrollFoot "+_stateDefault,
14466
+
14467
+ /* Misc */
14468
+ "sHeaderTH": _stateDefault,
14469
+ "sFooterTH": _stateDefault,
14470
+ "sJUIHeader": _headerFooter+" ui-corner-tl ui-corner-tr",
14471
+ "sJUIFooter": _headerFooter+" ui-corner-bl ui-corner-br"
14472
+ } );
14473
+
14474
+ }());
14475
+
14476
+
14477
+
14478
+ var extPagination = WFDataTable.ext.pager;
14479
+
14480
+ function _numbers ( page, pages ) {
14481
+ var
14482
+ numbers = [],
14483
+ buttons = extPagination.numbers_length,
14484
+ half = Math.floor( buttons / 2 ),
14485
+ i = 1;
14486
+
14487
+ if ( pages <= buttons ) {
14488
+ numbers = _range( 0, pages );
14489
+ }
14490
+ else if ( page <= half ) {
14491
+ numbers = _range( 0, buttons-2 );
14492
+ numbers.push( 'ellipsis' );
14493
+ numbers.push( pages-1 );
14494
+ }
14495
+ else if ( page >= pages - 1 - half ) {
14496
+ numbers = _range( pages-(buttons-2), pages );
14497
+ numbers.splice( 0, 0, 'ellipsis' ); // no unshift in ie6
14498
+ numbers.splice( 0, 0, 0 );
14499
+ }
14500
+ else {
14501
+ numbers = _range( page-half+2, page+half-1 );
14502
+ numbers.push( 'ellipsis' );
14503
+ numbers.push( pages-1 );
14504
+ numbers.splice( 0, 0, 'ellipsis' );
14505
+ numbers.splice( 0, 0, 0 );
14506
+ }
14507
+
14508
+ numbers.DT_el = 'span';
14509
+ return numbers;
14510
+ }
14511
+
14512
+
14513
+ $.extend( extPagination, {
14514
+ simple: function ( page, pages ) {
14515
+ return [ 'previous', 'next' ];
14516
+ },
14517
+
14518
+ full: function ( page, pages ) {
14519
+ return [ 'first', 'previous', 'next', 'last' ];
14520
+ },
14521
+
14522
+ numbers: function ( page, pages ) {
14523
+ return [ _numbers(page, pages) ];
14524
+ },
14525
+
14526
+ simple_numbers: function ( page, pages ) {
14527
+ return [ 'previous', _numbers(page, pages), 'next' ];
14528
+ },
14529
+
14530
+ full_numbers: function ( page, pages ) {
14531
+ return [ 'first', 'previous', _numbers(page, pages), 'next', 'last' ];
14532
+ },
14533
+
14534
+ first_last_numbers: function (page, pages) {
14535
+ return ['first', _numbers(page, pages), 'last'];
14536
+ },
14537
+
14538
+ // For testing and plug-ins to use
14539
+ _numbers: _numbers,
14540
+
14541
+ // Number of number buttons (including ellipsis) to show. _Must be odd!_
14542
+ numbers_length: 7
14543
+ } );
14544
+
14545
+
14546
+ $.extend( true, WFDataTable.ext.renderer, {
14547
+ pageButton: {
14548
+ _: function ( settings, host, idx, buttons, page, pages ) {
14549
+ var classes = settings.oClasses;
14550
+ var lang = settings.oLanguage.oPaginate;
14551
+ var aria = settings.oLanguage.oAria.paginate || {};
14552
+ var btnDisplay, btnClass, counter=0;
14553
+
14554
+ var attach = function( container, buttons ) {
14555
+ var i, ien, node, button;
14556
+ var clickHandler = function ( e ) {
14557
+ _fnPageChange( settings, e.data.action, true );
14558
+ };
14559
+
14560
+ for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
14561
+ button = buttons[i];
14562
+
14563
+ if ( $.isArray( button ) ) {
14564
+ var inner = $( '<'+(button.DT_el || 'div')+'/>' )
14565
+ .appendTo( container );
14566
+ attach( inner, button );
14567
+ }
14568
+ else {
14569
+ btnDisplay = null;
14570
+ btnClass = '';
14571
+
14572
+ switch ( button ) {
14573
+ case 'ellipsis':
14574
+ container.append('<span class="ellipsis">&#x2026;</span>');
14575
+ break;
14576
+
14577
+ case 'first':
14578
+ btnDisplay = lang.sFirst;
14579
+ btnClass = button + (page > 0 ?
14580
+ '' : ' '+classes.sPageButtonDisabled);
14581
+ break;
14582
+
14583
+ case 'previous':
14584
+ btnDisplay = lang.sPrevious;
14585
+ btnClass = button + (page > 0 ?
14586
+ '' : ' '+classes.sPageButtonDisabled);
14587
+ break;
14588
+
14589
+ case 'next':
14590
+ btnDisplay = lang.sNext;
14591
+ btnClass = button + (page < pages-1 ?
14592
+ '' : ' '+classes.sPageButtonDisabled);
14593
+ break;
14594
+
14595
+ case 'last':
14596
+ btnDisplay = lang.sLast;
14597
+ btnClass = button + (page < pages-1 ?
14598
+ '' : ' '+classes.sPageButtonDisabled);
14599
+ break;
14600
+
14601
+ default:
14602
+ btnDisplay = button + 1;
14603
+ btnClass = page === button ?
14604
+ classes.sPageButtonActive : '';
14605
+ break;
14606
+ }
14607
+
14608
+ if ( btnDisplay !== null ) {
14609
+ node = $('<a>', {
14610
+ 'class': classes.sPageButton+' '+btnClass,
14611
+ 'aria-controls': settings.sTableId,
14612
+ 'aria-label': aria[ button ],
14613
+ 'data-dt-idx': counter,
14614
+ 'tabindex': settings.iTabIndex,
14615
+ 'id': idx === 0 && typeof button === 'string' ?
14616
+ settings.sTableId +'_'+ button :
14617
+ null
14618
+ } )
14619
+ .html( btnDisplay )
14620
+ .appendTo( container );
14621
+
14622
+ _fnBindAction(
14623
+ node, {action: button}, clickHandler
14624
+ );
14625
+
14626
+ counter++;
14627
+ }
14628
+ }
14629
+ }
14630
+ };
14631
+
14632
+ // IE9 throws an 'unknown error' if document.activeElement is used
14633
+ // inside an iframe or frame. Try / catch the error. Not good for
14634
+ // accessibility, but neither are frames.
14635
+ var activeEl;
14636
+
14637
+ try {
14638
+ // Because this approach is destroying and recreating the paging
14639
+ // elements, focus is lost on the select button which is bad for
14640
+ // accessibility. So we want to restore focus once the draw has
14641
+ // completed
14642
+ activeEl = $(host).find(document.activeElement).data('dt-idx');
14643
+ }
14644
+ catch (e) {}
14645
+
14646
+ attach( $(host).empty(), buttons );
14647
+
14648
+ if ( activeEl !== undefined ) {
14649
+ $(host).find( '[data-dt-idx='+activeEl+']' ).focus();
14650
+ }
14651
+ }
14652
+ }
14653
+ } );
14654
+
14655
+
14656
+
14657
+ // Built in type detection. See model.ext.aTypes for information about
14658
+ // what is required from this methods.
14659
+ $.extend( WFDataTable.ext.type.detect, [
14660
+ // Plain numbers - first since V8 detects some plain numbers as dates
14661
+ // e.g. Date.parse('55') (but not all, e.g. Date.parse('22')...).
14662
+ function ( d, settings )
14663
+ {
14664
+ var decimal = settings.oLanguage.sDecimal;
14665
+ return _isNumber( d, decimal ) ? 'num'+decimal : null;
14666
+ },
14667
+
14668
+ // Dates (only those recognised by the browser's Date.parse)
14669
+ function ( d, settings )
14670
+ {
14671
+ // V8 tries _very_ hard to make a string passed into `Date.parse()`
14672
+ // valid, so we need to use a regex to restrict date formats. Use a
14673
+ // plug-in for anything other than ISO8601 style strings
14674
+ if ( d && !(d instanceof Date) && ! _re_date.test(d) ) {
14675
+ return null;
14676
+ }
14677
+ var parsed = Date.parse(d);
14678
+ return (parsed !== null && !isNaN(parsed)) || _empty(d) ? 'date' : null;
14679
+ },
14680
+
14681
+ // Formatted numbers
14682
+ function ( d, settings )
14683
+ {
14684
+ var decimal = settings.oLanguage.sDecimal;
14685
+ return _isNumber( d, decimal, true ) ? 'num-fmt'+decimal : null;
14686
+ },
14687
+
14688
+ // HTML numeric
14689
+ function ( d, settings )
14690
+ {
14691
+ var decimal = settings.oLanguage.sDecimal;
14692
+ return _htmlNumeric( d, decimal ) ? 'html-num'+decimal : null;
14693
+ },
14694
+
14695
+ // HTML numeric, formatted
14696
+ function ( d, settings )
14697
+ {
14698
+ var decimal = settings.oLanguage.sDecimal;
14699
+ return _htmlNumeric( d, decimal, true ) ? 'html-num-fmt'+decimal : null;
14700
+ },
14701
+
14702
+ // HTML (this is strict checking - there must be html)
14703
+ function ( d, settings )
14704
+ {
14705
+ return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1) ?
14706
+ 'html' : null;
14707
+ }
14708
+ ] );
14709
+
14710
+
14711
+
14712
+ // Filter formatting functions. See model.ext.ofnSearch for information about
14713
+ // what is required from these methods.
14714
+ //
14715
+ // Note that additional search methods are added for the html numbers and
14716
+ // html formatted numbers by `_addNumericSort()` when we know what the decimal
14717
+ // place is
14718
+
14719
+
14720
+ $.extend( WFDataTable.ext.type.search, {
14721
+ html: function ( data ) {
14722
+ return _empty(data) ?
14723
+ data :
14724
+ typeof data === 'string' ?
14725
+ data
14726
+ .replace( _re_new_lines, " " )
14727
+ .replace( _re_html, "" ) :
14728
+ '';
14729
+ },
14730
+
14731
+ string: function ( data ) {
14732
+ return _empty(data) ?
14733
+ data :
14734
+ typeof data === 'string' ?
14735
+ data.replace( _re_new_lines, " " ) :
14736
+ data;
14737
+ }
14738
+ } );
14739
+
14740
+
14741
+
14742
+ var __numericReplace = function ( d, decimalPlace, re1, re2 ) {
14743
+ if ( d !== 0 && (!d || d === '-') ) {
14744
+ return -Infinity;
14745
+ }
14746
+
14747
+ // If a decimal place other than `.` is used, it needs to be given to the
14748
+ // function so we can detect it and replace with a `.` which is the only
14749
+ // decimal place Javascript recognises - it is not locale aware.
14750
+ if ( decimalPlace ) {
14751
+ d = _numToDecimal( d, decimalPlace );
14752
+ }
14753
+
14754
+ if ( d.replace ) {
14755
+ if ( re1 ) {
14756
+ d = d.replace( re1, '' );
14757
+ }
14758
+
14759
+ if ( re2 ) {
14760
+ d = d.replace( re2, '' );
14761
+ }
14762
+ }
14763
+
14764
+ return d * 1;
14765
+ };
14766
+
14767
+
14768
+ // Add the numeric 'deformatting' functions for sorting and search. This is done
14769
+ // in a function to provide an easy ability for the language options to add
14770
+ // additional methods if a non-period decimal place is used.
14771
+ function _addNumericSort ( decimalPlace ) {
14772
+ $.each(
14773
+ {
14774
+ // Plain numbers
14775
+ "num": function ( d ) {
14776
+ return __numericReplace( d, decimalPlace );
14777
+ },
14778
+
14779
+ // Formatted numbers
14780
+ "num-fmt": function ( d ) {
14781
+ return __numericReplace( d, decimalPlace, _re_formatted_numeric );
14782
+ },
14783
+
14784
+ // HTML numeric
14785
+ "html-num": function ( d ) {
14786
+ return __numericReplace( d, decimalPlace, _re_html );
14787
+ },
14788
+
14789
+ // HTML numeric, formatted
14790
+ "html-num-fmt": function ( d ) {
14791
+ return __numericReplace( d, decimalPlace, _re_html, _re_formatted_numeric );
14792
+ }
14793
+ },
14794
+ function ( key, fn ) {
14795
+ // Add the ordering method
14796
+ _ext.type.order[ key+decimalPlace+'-pre' ] = fn;
14797
+
14798
+ // For HTML types add a search formatter that will strip the HTML
14799
+ if ( key.match(/^html\-/) ) {
14800
+ _ext.type.search[ key+decimalPlace ] = _ext.type.search.html;
14801
+ }
14802
+ }
14803
+ );
14804
+ }
14805
+
14806
+
14807
+ // Default sort methods
14808
+ $.extend( _ext.type.order, {
14809
+ // Dates
14810
+ "date-pre": function ( d ) {
14811
+ return Date.parse( d ) || -Infinity;
14812
+ },
14813
+
14814
+ // html
14815
+ "html-pre": function ( a ) {
14816
+ return _empty(a) ?
14817
+ '' :
14818
+ a.replace ?
14819
+ a.replace( /<.*?>/g, "" ).toLowerCase() :
14820
+ a+'';
14821
+ },
14822
+
14823
+ // string
14824
+ "string-pre": function ( a ) {
14825
+ // This is a little complex, but faster than always calling toString,
14826
+ // http://jsperf.com/tostring-v-check
14827
+ return _empty(a) ?
14828
+ '' :
14829
+ typeof a === 'string' ?
14830
+ a.toLowerCase() :
14831
+ ! a.toString ?
14832
+ '' :
14833
+ a.toString();
14834
+ },
14835
+
14836
+ // string-asc and -desc are retained only for compatibility with the old
14837
+ // sort methods
14838
+ "string-asc": function ( x, y ) {
14839
+ return ((x < y) ? -1 : ((x > y) ? 1 : 0));
14840
+ },
14841
+
14842
+ "string-desc": function ( x, y ) {
14843
+ return ((x < y) ? 1 : ((x > y) ? -1 : 0));
14844
+ }
14845
+ } );
14846
+
14847
+
14848
+ // Numeric sorting types - order doesn't matter here
14849
+ _addNumericSort( '' );
14850
+
14851
+
14852
+ $.extend( true, WFDataTable.ext.renderer, {
14853
+ header: {
14854
+ _: function ( settings, cell, column, classes ) {
14855
+ // No additional mark-up required
14856
+ // Attach a sort listener to update on sort - note that using the
14857
+ // `DT` namespace will allow the event to be removed automatically
14858
+ // on destroy, while the `dt` namespaced event is the one we are
14859
+ // listening for
14860
+ $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) {
14861
+ if ( settings !== ctx ) { // need to check this this is the host
14862
+ return; // table, not a nested one
14863
+ }
14864
+
14865
+ var colIdx = column.idx;
14866
+
14867
+ cell
14868
+ .removeClass(
14869
+ column.sSortingClass +' '+
14870
+ classes.sSortAsc +' '+
14871
+ classes.sSortDesc
14872
+ )
14873
+ .addClass( columns[ colIdx ] == 'asc' ?
14874
+ classes.sSortAsc : columns[ colIdx ] == 'desc' ?
14875
+ classes.sSortDesc :
14876
+ column.sSortingClass
14877
+ );
14878
+ } );
14879
+ },
14880
+
14881
+ jqueryui: function ( settings, cell, column, classes ) {
14882
+ $('<div/>')
14883
+ .addClass( classes.sSortJUIWrapper )
14884
+ .append( cell.contents() )
14885
+ .append( $('<span/>')
14886
+ .addClass( classes.sSortIcon+' '+column.sSortingClassJUI )
14887
+ )
14888
+ .appendTo( cell );
14889
+
14890
+ // Attach a sort listener to update on sort
14891
+ $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) {
14892
+ if ( settings !== ctx ) {
14893
+ return;
14894
+ }
14895
+
14896
+ var colIdx = column.idx;
14897
+
14898
+ cell
14899
+ .removeClass( classes.sSortAsc +" "+classes.sSortDesc )
14900
+ .addClass( columns[ colIdx ] == 'asc' ?
14901
+ classes.sSortAsc : columns[ colIdx ] == 'desc' ?
14902
+ classes.sSortDesc :
14903
+ column.sSortingClass
14904
+ );
14905
+
14906
+ cell
14907
+ .find( 'span.'+classes.sSortIcon )
14908
+ .removeClass(
14909
+ classes.sSortJUIAsc +" "+
14910
+ classes.sSortJUIDesc +" "+
14911
+ classes.sSortJUI +" "+
14912
+ classes.sSortJUIAscAllowed +" "+
14913
+ classes.sSortJUIDescAllowed
14914
+ )
14915
+ .addClass( columns[ colIdx ] == 'asc' ?
14916
+ classes.sSortJUIAsc : columns[ colIdx ] == 'desc' ?
14917
+ classes.sSortJUIDesc :
14918
+ column.sSortingClassJUI
14919
+ );
14920
+ } );
14921
+ }
14922
+ }
14923
+ } );
14924
+
14925
+ /*
14926
+ * Public helper functions. These aren't used internally by WFDataTables, or
14927
+ * called by any of the options passed into WFDataTables, but they can be used
14928
+ * externally by developers working with WFDataTables. They are helper functions
14929
+ * to make working with WFDataTables a little bit easier.
14930
+ */
14931
+
14932
+ var __htmlEscapeEntities = function ( d ) {
14933
+ return typeof d === 'string' ?
14934
+ d.replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;') :
14935
+ d;
14936
+ };
14937
+
14938
+ /**
14939
+ * Helpers for `columns.render`.
14940
+ *
14941
+ * The options defined here can be used with the `columns.render` initialisation
14942
+ * option to provide a display renderer. The following functions are defined:
14943
+ *
14944
+ * * `number` - Will format numeric data (defined by `columns.data`) for
14945
+ * display, retaining the original unformatted data for sorting and filtering.
14946
+ * It takes 5 parameters:
14947
+ * * `string` - Thousands grouping separator
14948
+ * * `string` - Decimal point indicator
14949
+ * * `integer` - Number of decimal points to show
14950
+ * * `string` (optional) - Prefix.
14951
+ * * `string` (optional) - Postfix (/suffix).
14952
+ * * `text` - Escape HTML to help prevent XSS attacks. It has no optional
14953
+ * parameters.
14954
+ *
14955
+ * @example
14956
+ * // Column definition using the number renderer
14957
+ * {
14958
+ * data: "salary",
14959
+ * render: $.fn.wfDataTable.render.number( '\'', '.', 0, '$' )
14960
+ * }
14961
+ *
14962
+ * @namespace
14963
+ */
14964
+ WFDataTable.render = {
14965
+ number: function ( thousands, decimal, precision, prefix, postfix ) {
14966
+ return {
14967
+ display: function ( d ) {
14968
+ if ( typeof d !== 'number' && typeof d !== 'string' ) {
14969
+ return d;
14970
+ }
14971
+
14972
+ var negative = d < 0 ? '-' : '';
14973
+ var flo = parseFloat( d );
14974
+
14975
+ // If NaN then there isn't much formatting that we can do - just
14976
+ // return immediately, escaping any HTML (this was supposed to
14977
+ // be a number after all)
14978
+ if ( isNaN( flo ) ) {
14979
+ return __htmlEscapeEntities( d );
14980
+ }
14981
+
14982
+ flo = flo.toFixed( precision );
14983
+ d = Math.abs( flo );
14984
+
14985
+ var intPart = parseInt( d, 10 );
14986
+ var floatPart = precision ?
14987
+ decimal+(d - intPart).toFixed( precision ).substring( 2 ):
14988
+ '';
14989
+
14990
+ return negative + (prefix||'') +
14991
+ intPart.toString().replace(
14992
+ /\B(?=(\d{3})+(?!\d))/g, thousands
14993
+ ) +
14994
+ floatPart +
14995
+ (postfix||'');
14996
+ }
14997
+ };
14998
+ },
14999
+
15000
+ text: function () {
15001
+ return {
15002
+ display: __htmlEscapeEntities
15003
+ };
15004
+ }
15005
+ };
15006
+
15007
+
15008
+ /*
15009
+ * This is really a good bit rubbish this method of exposing the internal methods
15010
+ * publicly... - To be fixed in 2.0 using methods on the prototype
15011
+ */
15012
+
15013
+
15014
+ /**
15015
+ * Create a wrapper function for exporting an internal functions to an external API.
15016
+ * @param {string} fn API function name
15017
+ * @returns {function} wrapped function
15018
+ * @memberof WFDataTable#internal
15019
+ */
15020
+ function _fnExternApiFunc (fn)
15021
+ {
15022
+ return function() {
15023
+ var args = [_fnSettingsFromNode( this[WFDataTable.ext.iApiIndex] )].concat(
15024
+ Array.prototype.slice.call(arguments)
15025
+ );
15026
+ return WFDataTable.ext.internal[fn].apply( this, args );
15027
+ };
15028
+ }
15029
+
15030
+
15031
+ /**
15032
+ * Reference to internal functions for use by plug-in developers. Note that
15033
+ * these methods are references to internal functions and are considered to be
15034
+ * private. If you use these methods, be aware that they are liable to change
15035
+ * between versions.
15036
+ * @namespace
15037
+ */
15038
+ $.extend( WFDataTable.ext.internal, {
15039
+ _fnExternApiFunc: _fnExternApiFunc,
15040
+ _fnBuildAjax: _fnBuildAjax,
15041
+ _fnAjaxUpdate: _fnAjaxUpdate,
15042
+ _fnAjaxParameters: _fnAjaxParameters,
15043
+ _fnAjaxUpdateDraw: _fnAjaxUpdateDraw,
15044
+ _fnAjaxDataSrc: _fnAjaxDataSrc,
15045
+ _fnAddColumn: _fnAddColumn,
15046
+ _fnColumnOptions: _fnColumnOptions,
15047
+ _fnAdjustColumnSizing: _fnAdjustColumnSizing,
15048
+ _fnVisibleToColumnIndex: _fnVisibleToColumnIndex,
15049
+ _fnColumnIndexToVisible: _fnColumnIndexToVisible,
15050
+ _fnVisbleColumns: _fnVisbleColumns,
15051
+ _fnGetColumns: _fnGetColumns,
15052
+ _fnColumnTypes: _fnColumnTypes,
15053
+ _fnApplyColumnDefs: _fnApplyColumnDefs,
15054
+ _fnHungarianMap: _fnHungarianMap,
15055
+ _fnCamelToHungarian: _fnCamelToHungarian,
15056
+ _fnLanguageCompat: _fnLanguageCompat,
15057
+ _fnBrowserDetect: _fnBrowserDetect,
15058
+ _fnAddData: _fnAddData,
15059
+ _fnAddTr: _fnAddTr,
15060
+ _fnNodeToDataIndex: _fnNodeToDataIndex,
15061
+ _fnNodeToColumnIndex: _fnNodeToColumnIndex,
15062
+ _fnGetCellData: _fnGetCellData,
15063
+ _fnSetCellData: _fnSetCellData,
15064
+ _fnSplitObjNotation: _fnSplitObjNotation,
15065
+ _fnGetObjectDataFn: _fnGetObjectDataFn,
15066
+ _fnSetObjectDataFn: _fnSetObjectDataFn,
15067
+ _fnGetDataMaster: _fnGetDataMaster,
15068
+ _fnClearTable: _fnClearTable,
15069
+ _fnDeleteIndex: _fnDeleteIndex,
15070
+ _fnInvalidate: _fnInvalidate,
15071
+ _fnGetRowElements: _fnGetRowElements,
15072
+ _fnCreateTr: _fnCreateTr,
15073
+ _fnBuildHead: _fnBuildHead,
15074
+ _fnDrawHead: _fnDrawHead,
15075
+ _fnDraw: _fnDraw,
15076
+ _fnReDraw: _fnReDraw,
15077
+ _fnAddOptionsHtml: _fnAddOptionsHtml,
15078
+ _fnDetectHeader: _fnDetectHeader,
15079
+ _fnGetUniqueThs: _fnGetUniqueThs,
15080
+ _fnFeatureHtmlFilter: _fnFeatureHtmlFilter,
15081
+ _fnFilterComplete: _fnFilterComplete,
15082
+ _fnFilterCustom: _fnFilterCustom,
15083
+ _fnFilterColumn: _fnFilterColumn,
15084
+ _fnFilter: _fnFilter,
15085
+ _fnFilterCreateSearch: _fnFilterCreateSearch,
15086
+ _fnEscapeRegex: _fnEscapeRegex,
15087
+ _fnFilterData: _fnFilterData,
15088
+ _fnFeatureHtmlInfo: _fnFeatureHtmlInfo,
15089
+ _fnUpdateInfo: _fnUpdateInfo,
15090
+ _fnInfoMacros: _fnInfoMacros,
15091
+ _fnInitialise: _fnInitialise,
15092
+ _fnInitComplete: _fnInitComplete,
15093
+ _fnLengthChange: _fnLengthChange,
15094
+ _fnFeatureHtmlLength: _fnFeatureHtmlLength,
15095
+ _fnFeatureHtmlPaginate: _fnFeatureHtmlPaginate,
15096
+ _fnPageChange: _fnPageChange,
15097
+ _fnFeatureHtmlProcessing: _fnFeatureHtmlProcessing,
15098
+ _fnProcessingDisplay: _fnProcessingDisplay,
15099
+ _fnFeatureHtmlTable: _fnFeatureHtmlTable,
15100
+ _fnScrollDraw: _fnScrollDraw,
15101
+ _fnApplyToChildren: _fnApplyToChildren,
15102
+ _fnCalculateColumnWidths: _fnCalculateColumnWidths,
15103
+ _fnThrottle: _fnThrottle,
15104
+ _fnConvertToWidth: _fnConvertToWidth,
15105
+ _fnGetWidestNode: _fnGetWidestNode,
15106
+ _fnGetMaxLenString: _fnGetMaxLenString,
15107
+ _fnStringToCss: _fnStringToCss,
15108
+ _fnSortFlatten: _fnSortFlatten,
15109
+ _fnSort: _fnSort,
15110
+ _fnSortAria: _fnSortAria,
15111
+ _fnSortListener: _fnSortListener,
15112
+ _fnSortAttachListener: _fnSortAttachListener,
15113
+ _fnSortingClasses: _fnSortingClasses,
15114
+ _fnSortData: _fnSortData,
15115
+ _fnSaveState: _fnSaveState,
15116
+ _fnLoadState: _fnLoadState,
15117
+ _fnSettingsFromNode: _fnSettingsFromNode,
15118
+ _fnLog: _fnLog,
15119
+ _fnMap: _fnMap,
15120
+ _fnBindAction: _fnBindAction,
15121
+ _fnCallbackReg: _fnCallbackReg,
15122
+ _fnCallbackFire: _fnCallbackFire,
15123
+ _fnLengthOverflow: _fnLengthOverflow,
15124
+ _fnRenderer: _fnRenderer,
15125
+ _fnDataSource: _fnDataSource,
15126
+ _fnRowAttributes: _fnRowAttributes,
15127
+ _fnCalculateEnd: function () {} // Used by a lot of plug-ins, but redundant
15128
+ // in 1.10, so this dead-end function is
15129
+ // added to prevent errors
15130
+ } );
15131
+
15132
+
15133
+ // jQuery access
15134
+ $.fn.wfDataTable = WFDataTable;
15135
+
15136
+ // Provide access to the host jQuery object (circular reference)
15137
+ WFDataTable.$ = $;
15138
+
15139
+ // Legacy aliases
15140
+ $.fn.wfDataTableSettings = WFDataTable.settings;
15141
+ $.fn.wfDataTableExt = WFDataTable.ext;
15142
+
15143
+ // With a capital `D` we return a WFDataTables API instance rather than a
15144
+ // jQuery object
15145
+ $.fn.WFDataTable = function ( opts ) {
15146
+ return $(this).wfDataTable( opts ).api();
15147
+ };
15148
+
15149
+ // All properties that are available to $.fn.wfDataTable should also be
15150
+ // available on $.fn.WFDataTable
15151
+ $.each( WFDataTable, function ( prop, val ) {
15152
+ $.fn.WFDataTable[ prop ] = val;
15153
+ } );
15154
+
15155
+
15156
+ // Information about events fired by WFDataTables - for documentation.
15157
+ /**
15158
+ * Draw event, fired whenever the table is redrawn on the page, at the same
15159
+ * point as fnDrawCallback. This may be useful for binding events or
15160
+ * performing calculations when the table is altered at all.
15161
+ * @name WFDataTable#draw.dt
15162
+ * @event
15163
+ * @param {event} e jQuery event object
15164
+ * @param {object} o WFDataTables settings object {@link WFDataTable.models.oSettings}
15165
+ */
15166
+
15167
+ /**
15168
+ * Search event, fired when the searching applied to the table (using the
15169
+ * built-in global search, or column filters) is altered.
15170
+ * @name WFDataTable#search.dt
15171
+ * @event
15172
+ * @param {event} e jQuery event object
15173
+ * @param {object} o WFDataTables settings object {@link WFDataTable.models.oSettings}
15174
+ */
15175
+
15176
+ /**
15177
+ * Page change event, fired when the paging of the table is altered.
15178
+ * @name WFDataTable#page.dt
15179
+ * @event
15180
+ * @param {event} e jQuery event object
15181
+ * @param {object} o WFDataTables settings object {@link WFDataTable.models.oSettings}
15182
+ */
15183
+
15184
+ /**
15185
+ * Order event, fired when the ordering applied to the table is altered.
15186
+ * @name WFDataTable#order.dt
15187
+ * @event
15188
+ * @param {event} e jQuery event object
15189
+ * @param {object} o WFDataTables settings object {@link WFDataTable.models.oSettings}
15190
+ */
15191
+
15192
+ /**
15193
+ * WFDataTables initialisation complete event, fired when the table is fully
15194
+ * drawn, including Ajax data loaded, if Ajax data is required.
15195
+ * @name WFDataTable#init.dt
15196
+ * @event
15197
+ * @param {event} e jQuery event object
15198
+ * @param {object} oSettings WFDataTables settings object
15199
+ * @param {object} json The JSON object request from the server - only
15200
+ * present if client-side Ajax sourced data is used</li></ol>
15201
+ */
15202
+
15203
+ /**
15204
+ * State save event, fired when the table has changed state a new state save
15205
+ * is required. This event allows modification of the state saving object
15206
+ * prior to actually doing the save, including addition or other state
15207
+ * properties (for plug-ins) or modification of a WFDataTables core property.
15208
+ * @name WFDataTable#stateSaveParams.dt
15209
+ * @event
15210
+ * @param {event} e jQuery event object
15211
+ * @param {object} oSettings WFDataTables settings object
15212
+ * @param {object} json The state information to be saved
15213
+ */
15214
+
15215
+ /**
15216
+ * State load event, fired when the table is loading state from the stored
15217
+ * data, but prior to the settings object being modified by the saved state
15218
+ * - allowing modification of the saved state is required or loading of
15219
+ * state for a plug-in.
15220
+ * @name WFDataTable#stateLoadParams.dt
15221
+ * @event
15222
+ * @param {event} e jQuery event object
15223
+ * @param {object} oSettings WFDataTables settings object
15224
+ * @param {object} json The saved state information
15225
+ */
15226
+
15227
+ /**
15228
+ * State loaded event, fired when state has been loaded from stored data and
15229
+ * the settings object has been modified by the loaded data.
15230
+ * @name WFDataTable#stateLoaded.dt
15231
+ * @event
15232
+ * @param {event} e jQuery event object
15233
+ * @param {object} oSettings WFDataTables settings object
15234
+ * @param {object} json The saved state information
15235
+ */
15236
+
15237
+ /**
15238
+ * Processing event, fired when WFDataTables is doing some kind of processing
15239
+ * (be it, order, searcg or anything else). It can be used to indicate to
15240
+ * the end user that there is something happening, or that something has
15241
+ * finished.
15242
+ * @name WFDataTable#processing.dt
15243
+ * @event
15244
+ * @param {event} e jQuery event object
15245
+ * @param {object} oSettings WFDataTables settings object
15246
+ * @param {boolean} bShow Flag for if WFDataTables is doing processing or not
15247
+ */
15248
+
15249
+ /**
15250
+ * Ajax (XHR) event, fired whenever an Ajax request is completed from a
15251
+ * request to made to the server for new data. This event is called before
15252
+ * WFDataTables processed the returned data, so it can also be used to pre-
15253
+ * process the data returned from the server, if needed.
15254
+ *
15255
+ * Note that this trigger is called in `fnServerData`, if you override
15256
+ * `fnServerData` and which to use this event, you need to trigger it in you
15257
+ * success function.
15258
+ * @name WFDataTable#xhr.dt
15259
+ * @event
15260
+ * @param {event} e jQuery event object
15261
+ * @param {object} o WFDataTables settings object {@link WFDataTable.models.oSettings}
15262
+ * @param {object} json JSON returned from the server
15263
+ *
15264
+ * @example
15265
+ * // Use a custom property returned from the server in another DOM element
15266
+ * $('#table').wfDataTable().on('xhr.dt', function (e, settings, json) {
15267
+ * $('#status').html( json.status );
15268
+ * } );
15269
+ *
15270
+ * @example
15271
+ * // Pre-process the data returned from the server
15272
+ * $('#table').wfDataTable().on('xhr.dt', function (e, settings, json) {
15273
+ * for ( var i=0, ien=json.aaData.length ; i<ien ; i++ ) {
15274
+ * json.aaData[i].sum = json.aaData[i].one + json.aaData[i].two;
15275
+ * }
15276
+ * // Note no return - manipulate the data directly in the JSON object.
15277
+ * } );
15278
+ */
15279
+
15280
+ /**
15281
+ * Destroy event, fired when the WFDataTable is destroyed by calling fnDestroy
15282
+ * or passing the bDestroy:true parameter in the initialisation object. This
15283
+ * can be used to remove bound events, added DOM nodes, etc.
15284
+ * @name WFDataTable#destroy.dt
15285
+ * @event
15286
+ * @param {event} e jQuery event object
15287
+ * @param {object} o WFDataTables settings object {@link WFDataTable.models.oSettings}
15288
+ */
15289
+
15290
+ /**
15291
+ * Page length change event, fired when number of records to show on each
15292
+ * page (the length) is changed.
15293
+ * @name WFDataTable#length.dt
15294
+ * @event
15295
+ * @param {event} e jQuery event object
15296
+ * @param {object} o WFDataTables settings object {@link WFDataTable.models.oSettings}
15297
+ * @param {integer} len New length
15298
+ */
15299
+
15300
+ /**
15301
+ * Column sizing has changed.
15302
+ * @name WFDataTable#column-sizing.dt
15303
+ * @event
15304
+ * @param {event} e jQuery event object
15305
+ * @param {object} o WFDataTables settings object {@link WFDataTable.models.oSettings}
15306
+ */
15307
+
15308
+ /**
15309
+ * Column visibility has changed.
15310
+ * @name WFDataTable#column-visibility.dt
15311
+ * @event
15312
+ * @param {event} e jQuery event object
15313
+ * @param {object} o WFDataTables settings object {@link WFDataTable.models.oSettings}
15314
+ * @param {int} column Column index
15315
+ * @param {bool} vis `false` if column now hidden, or `true` if visible
15316
+ */
15317
+
15318
+ return $.fn.wfDataTable;
15319
+ }));
15320
+
15321
+
lib/GeoIP.dat CHANGED
Binary file
lib/GeoIPv6.dat CHANGED
Binary file
lib/IPTraf.php CHANGED
@@ -31,6 +31,6 @@
31
 
32
  </table>
33
 
34
- <div class="footer">&copy;&nbsp;2011 to 2015 Wordfence &mdash; Visit <a href="http://wordfence.com/">Wordfence.com</a> for help, security updates and more.</div>
35
  </body>
36
  </html>
31
 
32
  </table>
33
 
34
+ <div class="footer">&copy;&nbsp;2011 to <?php echo date('Y'); ?> Wordfence &mdash; Visit <a href="http://wordfence.com/">Wordfence.com</a> for help, security updates and more.</div>
35
  </body>
36
  </html>
lib/cronview.php CHANGED
@@ -27,6 +27,6 @@ foreach ( $cron as $timestamp => $values ) {
27
  }
28
  ?>
29
 
30
- <div class="diffFooter">&copy;&nbsp;2011 to 2015 Wordfence &mdash; Visit <a href="http://wordfence.com/">Wordfence.com</a> for help, security updates and more.</div>
31
  </body>
32
  </html>
27
  }
28
  ?>
29
 
30
+ <div class="diffFooter">&copy;&nbsp;2011 to <?php echo date('Y'); ?> Wordfence &mdash; Visit <a href="http://wordfence.com/">Wordfence.com</a> for help, security updates and more.</div>
31
  </body>
32
  </html>
lib/dbview.php CHANGED
@@ -25,7 +25,7 @@ foreach($q as $val){
25
 
26
  ?>
27
 
28
- <div class="diffFooter">&copy;&nbsp;2011 to 2015 Wordfence &mdash; Visit <a
29
  href="http://wordfence.com/">Wordfence.com</a> for help, security updates and more.</div>
30
  </body>
31
  </html>
25
 
26
  ?>
27
 
28
+ <div class="diffFooter">&copy;&nbsp;2011 to <?php echo date('Y'); ?> Wordfence &mdash; Visit <a
29
  href="http://wordfence.com/">Wordfence.com</a> for help, security updates and more.</div>
30
  </body>
31
  </html>
lib/diffResult.php CHANGED
@@ -40,6 +40,6 @@
40
  ?>
41
 
42
 
43
- <div class="diffFooter">&copy;&nbsp;2011 Wordfence &mdash; Visit <a href="http://wordfence.com/">Wordfence.com</a> for help, security updates and more.</div>
44
  </body>
45
  </html>
40
  ?>
41
 
42
 
43
+ <div class="diffFooter">&copy;&nbsp;2011 to <?php echo date('Y'); ?> Wordfence &mdash; Visit <a href="http://wordfence.com/">Wordfence.com</a> for help, security updates and more.</div>
44
  </body>
45
  </html>
lib/menu_activity.php CHANGED
@@ -147,46 +147,46 @@
147
  <div data-bind="if: groupBy()" border="0" style="width: 100%">
148
  <div class="wf-filtered-traffic" data-bind="foreach: listings">
149
  <div>
150
- <div data-bind="if: loc()">
151
- <img data-bind="attr: { src: '<?php echo wfUtils::getBaseURL() . 'images/flags/'; ?>' + loc().countryCode.toLowerCase() + '.png',
152
- alt: loc().countryName, title: loc().countryName }" width="16" height="11"
153
- class="wfFlag"/>
154
- <a data-bind="text: (loc().city ? loc().city + ', ' : '') + loc().countryName,
155
- attr: { href: 'http://maps.google.com/maps?q=' + loc().lat + ',' + loc().lon + '&z=6' }"
156
- target="_blank"></a>
157
- </div>
158
- <div data-bind="if: !loc()">
159
- An unknown location at IP <a
160
- data-bind="text: IP, attr: { href: WFAD.makeIPTrafLink(IP()) }" target="_blank"></a>
161
- </div>
162
-
163
- <div>
164
- <strong>IP:</strong>&nbsp;<a
165
- data-bind="text: IP, attr: { href: WFAD.makeIPTrafLink(IP()) }" target="_blank"></a>
166
- <span data-bind="if: blocked()">
167
- [<a data-bind="click: $root.unblockIP">unblock</a>]
168
- </span>
169
- <span data-bind="if: rangeBlocked()">
170
- [<a data-bind="click: $root.unblockNetwork">unblock this range</a>]
171
- </span>
172
- <span data-bind="if: !blocked() && !rangeBlocked()">
173
- [<a data-bind="click: $root.blockIP">block</a>]
174
- </span>
175
- </div>
176
  <div>
177
- &nbsp;<span class="wfReverseLookup"><span data-bind="text: IP"
178
- style="display:none;"></span></span>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
  </div>
180
  <div>
181
- <span
182
- data-bind="attr: { 'data-timestamp': ctime, text: 'Last hit was ' + ctime() + ' ago.' }"
183
- class="wfTimeAgo wfTimeAgo-timestamp"></span>
184
  </div>
185
  </div>
186
- <div>
187
- <!--<td style="font-size: 28px; color: #999;"> -->
188
- <span class="wf-filtered-traffic-hits" data-bind="text: hitCount"></span> hits
189
- </div>
190
  </div>
191
  </div>
192
 
147
  <div data-bind="if: groupBy()" border="0" style="width: 100%">
148
  <div class="wf-filtered-traffic" data-bind="foreach: listings">
149
  <div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
  <div>
151
+ <div data-bind="if: loc()">
152
+ <img data-bind="attr: { src: '<?php echo wfUtils::getBaseURL() . 'images/flags/'; ?>' + loc().countryCode.toLowerCase() + '.png',
153
+ alt: loc().countryName, title: loc().countryName }" width="16" height="11"
154
+ class="wfFlag"/>
155
+ <a data-bind="text: (loc().city ? loc().city + ', ' : '') + loc().countryName,
156
+ attr: { href: 'http://maps.google.com/maps?q=' + loc().lat + ',' + loc().lon + '&z=6' }"
157
+ target="_blank"></a>
158
+ </div>
159
+ <div data-bind="if: !loc()">
160
+ An unknown location at IP <a
161
+ data-bind="text: IP, attr: { href: WFAD.makeIPTrafLink(IP()) }" target="_blank"></a>
162
+ </div>
163
+
164
+ <div>
165
+ <strong>IP:</strong>&nbsp;<a
166
+ data-bind="text: IP, attr: { href: WFAD.makeIPTrafLink(IP()) }" target="_blank"></a>
167
+ <span data-bind="if: blocked()">
168
+ [<a data-bind="click: $root.unblockIP">unblock</a>]
169
+ </span>
170
+ <span data-bind="if: rangeBlocked()">
171
+ [<a data-bind="click: $root.unblockNetwork">unblock this range</a>]
172
+ </span>
173
+ <span data-bind="if: !blocked() && !rangeBlocked()">
174
+ [<a data-bind="click: $root.blockIP">block</a>]
175
+ </span>
176
+ </div>
177
+ <div>
178
+ &nbsp;<span class="wfReverseLookup"><span data-bind="text: IP" style="display:none;"></span></span>
179
+ </div>
180
+ <div>
181
+ <span
182
+ data-bind="attr: { 'data-timestamp': ctime, text: 'Last hit was ' + ctime() + ' ago.' }"
183
+ class="wfTimeAgo wfTimeAgo-timestamp"></span>
184
+ </div>
185
  </div>
186
  <div>
187
+ <span class="wf-filtered-traffic-hits" data-bind="text: hitCount"></span> hits
 
 
188
  </div>
189
  </div>
 
 
 
 
190
  </div>
191
  </div>
192
 
lib/menu_dashboard.php CHANGED
@@ -66,6 +66,9 @@ $d = new wfDashboard();
66
  <?php endif; ?>
67
  </li>
68
  <?php endforeach; ?>
 
 
 
69
  </ul>
70
  </li>
71
  </ul>
66
  <?php endif; ?>
67
  </li>
68
  <?php endforeach; ?>
69
+ <?php if (count($d->features) % 2 == 1): ?>
70
+ <li></li>
71
+ <?php endif; ?>
72
  </ul>
73
  </li>
74
  </ul>
lib/menu_options.php CHANGED
@@ -157,6 +157,41 @@ $w = new wfConfig();
157
  <option value="HTTP_X_REAL_IP"<?php $w->sel( 'howGetIPs', 'HTTP_X_REAL_IP' ); ?>>Use the X-Real-IP HTTP header. Only use if you have a front-end proxy or spoofing may result.</option>
158
  <option value="HTTP_CF_CONNECTING_IP"<?php $w->sel( 'howGetIPs', 'HTTP_CF_CONNECTING_IP' ); ?>>Use the Cloudflare "CF-Connecting-IP" HTTP header to get a visitor IP. Only use if you're using Cloudflare.</option>
159
  </select>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
  </div>
161
  </div>
162
  <div class="wf-form-group">
@@ -738,6 +773,61 @@ $w = new wfConfig();
738
  </div>
739
  </div>
740
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
741
  <div class="wfMarker" id="wfMarkerOtherOptions"></div>
742
  <h3>Other Options <a href="http://docs.wordfence.com/en/Wordfence_options#Other_Options" target="_blank" class="wfhelp"></a></h3>
743
  <div class="wf-form-group">
157
  <option value="HTTP_X_REAL_IP"<?php $w->sel( 'howGetIPs', 'HTTP_X_REAL_IP' ); ?>>Use the X-Real-IP HTTP header. Only use if you have a front-end proxy or spoofing may result.</option>
158
  <option value="HTTP_CF_CONNECTING_IP"<?php $w->sel( 'howGetIPs', 'HTTP_CF_CONNECTING_IP' ); ?>>Use the Cloudflare "CF-Connecting-IP" HTTP header to get a visitor IP. Only use if you're using Cloudflare.</option>
159
  </select>
160
+ <span class="wf-help-block">Detected IP(s): <span id="howGetIPs-preview-all"><?php echo wfUtils::getIPPreview(); ?></span></span>
161
+ <span class="wf-help-block">Your IP with this setting: <span id="howGetIPs-preview-single"><?php echo wfUtils::getIP(); ?></span></span>
162
+ <span class="wf-help-block"><a href="#" class="do-show" data-selector="#howGetIPs_trusted_proxies">+ Edit trusted proxies</a></span>
163
+ </div>
164
+ </div>
165
+ <div class="wf-form-group wf-sub-group hidden" id="howGetIPs_trusted_proxies">
166
+ <label for="howGetIPs_trusted_proxies_field" class="wf-col-sm-4 wf-col-sm-offset-1 wf-control-label">Trusted proxies</label>
167
+ <div class="wf-col-sm-7">
168
+ <textarea class="wf-form-control" rows="4" name="howGetIPs_trusted_proxies" id="howGetIPs_trusted_proxies_field"><?php echo $w->getHTML('howGetIPs_trusted_proxies'); ?></textarea>
169
+ <span class="wf-help-block">These IPs (or CIDR ranges) will be ignored when determining the requesting IP via the X-Forwarded-For HTTP header. Enter one IP or CIDR range per line.</span>
170
+ <script type="application/javascript">
171
+ (function($) {
172
+ var updateIPPreview = function() {
173
+ WFAD.updateIPPreview({howGetIPs: $('#howGetIPs').val(), 'howGetIPs_trusted_proxies': $('#howGetIPs_trusted_proxies_field').val()}, function(ret) {
174
+ if (ret && ret.ok) {
175
+ $('#howGetIPs-preview-all').html(ret.ipAll);
176
+ $('#howGetIPs-preview-single').html(ret.ip);
177
+ }
178
+ else {
179
+ //TODO: implementing testing whether or not this setting will lock them out and show the error saying that they'd lock themselves out
180
+ }
181
+ });
182
+ };
183
+
184
+ $('#howGetIPs').on('change', function() {
185
+ updateIPPreview();
186
+ });
187
+
188
+ var coalescingUpdateTimer;
189
+ $('#howGetIPs_trusted_proxies_field').on('keyup', function() {
190
+ clearTimeout(coalescingUpdateTimer);
191
+ coalescingUpdateTimer = setTimeout(updateIPPreview, 1000);
192
+ });
193
+ })(jQuery);
194
+ </script>
195
  </div>
196
  </div>
197
  <div class="wf-form-group">
773
  </div>
774
  </div>
775
 
776
+ <div class="wfMarker" id="wfMarkerNotification"></div>
777
+ <h3>Dashboard Notification Options <a href="http://docs.wordfence.com/en/Wordfence_options#Dashboard_Notification_Options" target="_blank" class="wfhelp"></a></h3>
778
+ <div class="wf-form-group">
779
+ <label for="notification_updatesNeeded" class="wf-col-sm-5 wf-control-label">Updates Needed (Plugin, Theme, or Core)</label>
780
+ <div class="wf-col-sm-7">
781
+ <div class="wf-checkbox"><input type="checkbox" id="notification_updatesNeeded" name="notification_updatesNeeded" value="1" <?php $w->cb('notification_updatesNeeded'); ?>></div>
782
+ </div>
783
+ </div>
784
+ <div class="wf-form-group">
785
+ <label for="notification_securityAlerts" class="wf-col-sm-5 wf-control-label">Security Alerts</label>
786
+ <div class="wf-col-sm-7">
787
+ <div class="wf-checkbox"><input type="checkbox" id="notification_securityAlerts"<?php if ($w->p()) { echo ' name="notification_securityAlerts"'; } ?> value="1" <?php if ($w->p()) { $w->cb('notification_securityAlerts'); } else { echo ' checked disabled'; } ?>></div>
788
+ <?php if (!$w->p()): ?>
789
+ <span class="wf-help-block"><span style="color: #F00;">Premium Option</span> This option requires a <a href="https://www.wordfence.com/gnl1optPdOnly1/wordfence-signup/" target="_blank">Wordfence Premium Key</a>.</span>
790
+ <?php if ($w->get('notification_securityAlerts')): ?><input type="hidden" name="notification_securityAlerts" value="<?php $w->f('notification_securityAlerts'); ?>"><?php endif; ?>
791
+ <?php endif; ?>
792
+ </div>
793
+ </div>
794
+ <div class="wf-form-group">
795
+ <label for="notification_promotions" class="wf-col-sm-5 wf-control-label">Promotions</label>
796
+ <div class="wf-col-sm-7">
797
+ <div class="wf-checkbox"><input type="checkbox" id="notification_promotions"<?php if ($w->p()) { echo ' name="notification_promotions"'; } ?> value="1" <?php if ($w->p()) { $w->cb('notification_promotions'); } else { echo ' checked disabled'; } ?>></div>
798
+ <?php if (!$w->p()): ?>
799
+ <span class="wf-help-block"><span style="color: #F00;">Premium Option</span> This option requires a <a href="https://www.wordfence.com/gnl1optPdOnly1/wordfence-signup/" target="_blank">Wordfence Premium Key</a>.</span>
800
+ <?php if ($w->get('notification_promotions')): ?><input type="hidden" name="notification_promotions" value="<?php $w->f('notification_promotions'); ?>"><?php endif; ?>
801
+ <?php endif; ?>
802
+ </div>
803
+ </div>
804
+ <div class="wf-form-group">
805
+ <label for="notification_blogHighlights" class="wf-col-sm-5 wf-control-label">Blog Highlights</label>
806
+ <div class="wf-col-sm-7">
807
+ <div class="wf-checkbox"><input type="checkbox" id="notification_blogHighlights"<?php if ($w->p()) { echo ' name="notification_blogHighlights"'; } ?> value="1" <?php if ($w->p()) { $w->cb('notification_blogHighlights'); } else { echo ' checked disabled'; } ?>></div>
808
+ <?php if (!$w->p()): ?>
809
+ <span class="wf-help-block"><span style="color: #F00;">Premium Option</span> This option requires a <a href="https://www.wordfence.com/gnl1optPdOnly1/wordfence-signup/" target="_blank">Wordfence Premium Key</a>.</span>
810
+ <?php if ($w->get('notification_blogHighlights')): ?><input type="hidden" name="notification_blogHighlights" value="<?php $w->f('notification_blogHighlights'); ?>"><?php endif; ?>
811
+ <?php endif; ?>
812
+ </div>
813
+ </div>
814
+ <div class="wf-form-group">
815
+ <label for="notification_productUpdates" class="wf-col-sm-5 wf-control-label">Product Updates</label>
816
+ <div class="wf-col-sm-7">
817
+ <div class="wf-checkbox"><input type="checkbox" id="notification_productUpdates"<?php if ($w->p()) { echo ' name="notification_productUpdates"'; } ?> value="1" <?php if ($w->p()) { $w->cb('notification_productUpdates'); } else { echo ' checked disabled'; } ?>></div>
818
+ <?php if (!$w->p()): ?>
819
+ <span class="wf-help-block"><span style="color: #F00;">Premium Option</span> This option requires a <a href="https://www.wordfence.com/gnl1optPdOnly1/wordfence-signup/" target="_blank">Wordfence Premium Key</a>.</span>
820
+ <?php if ($w->get('notification_productUpdates')): ?><input type="hidden" name="notification_productUpdates" value="<?php $w->f('notification_productUpdates'); ?>"><?php endif; ?>
821
+ <?php endif; ?>
822
+ </div>
823
+ </div>
824
+ <div class="wf-form-group">
825
+ <label for="notification_scanStatus" class="wf-col-sm-5 wf-control-label">Scan Status</label>
826
+ <div class="wf-col-sm-7">
827
+ <div class="wf-checkbox"><input type="checkbox" id="notification_scanStatus" name="notification_scanStatus" value="1" <?php $w->cb('notification_scanStatus'); ?>></div>
828
+ </div>
829
+ </div>
830
+
831
  <div class="wfMarker" id="wfMarkerOtherOptions"></div>
832
  <h3>Other Options <a href="http://docs.wordfence.com/en/Wordfence_options#Other_Options" target="_blank" class="wfhelp"></a></h3>
833
  <div class="wf-form-group">
lib/menu_tools_diagnostic.php CHANGED
@@ -56,7 +56,7 @@ if (!isset($sendingDiagnosticEmail)) { $sendingDiagnosticEmail = false; }
56
  <tbody>
57
  <?php
58
  $howGet = wfConfig::get('howGetIPs', false);
59
- list($currentIP, $currentServerVarForIP) = wfUtils::getIPAndServerVarible();
60
  foreach (array(
61
  'REMOTE_ADDR' => 'REMOTE_ADDR',
62
  'HTTP_CF_CONNECTING_IP' => 'CF-Connecting-IP',
@@ -65,7 +65,43 @@ if (!isset($sendingDiagnosticEmail)) { $sendingDiagnosticEmail = false; }
65
  ) as $variable => $label): ?>
66
  <tr>
67
  <td><?php echo $label ?></td>
68
- <td><?php echo esc_html(array_key_exists($variable, $_SERVER) ? $_SERVER[$variable] : '(not set)') ?></td>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
  <?php if ($currentServerVarForIP && $currentServerVarForIP === $variable): ?>
70
  <td class="success">In use</td>
71
  <?php elseif ($howGet === $variable): ?>
56
  <tbody>
57
  <?php
58
  $howGet = wfConfig::get('howGetIPs', false);
59
+ list($currentIP, $currentServerVarForIP) = wfUtils::getIPAndServerVariable();
60
  foreach (array(
61
  'REMOTE_ADDR' => 'REMOTE_ADDR',
62
  'HTTP_CF_CONNECTING_IP' => 'CF-Connecting-IP',
65
  ) as $variable => $label): ?>
66
  <tr>
67
  <td><?php echo $label ?></td>
68
+ <td><?php
69
+ if (!array_key_exists($variable, $_SERVER)) {
70
+ echo '(not set)';
71
+ }
72
+ else {
73
+ if (strpos($_SERVER[$variable], ',') !== false) {
74
+ $trustedProxies = explode("\n", wfConfig::get('howGetIPs_trusted_proxies', ''));
75
+ $items = preg_replace('/[\s,]/', '', explode(',', $_SERVER[$variable]));
76
+ $items = array_reverse($items);
77
+ $output = '';
78
+ $markedSelectedAddress = false;
79
+ foreach ($items as $index => $i) {
80
+ foreach ($trustedProxies as $proxy) {
81
+ if (!empty($proxy)) {
82
+ if (wfUtils::subnetContainsIP($proxy, $i) && $index < count($items) - 1) {
83
+ $output = esc_html($i) . ', ' . $output;
84
+ continue 2;
85
+ }
86
+ }
87
+ }
88
+
89
+ if (!$markedSelectedAddress) {
90
+ $output = '<strong>' . esc_html($i) . '</strong>, ' . $output;
91
+ $markedSelectedAddress = true;
92
+ }
93
+ else {
94
+ $output = esc_html($i) . ', ' . $output;
95
+ }
96
+ }
97
+
98
+ echo substr($output, 0, -2);
99
+ }
100
+ else {
101
+ echo esc_html($_SERVER[$variable]);
102
+ }
103
+ }
104
+ ?></td>
105
  <?php if ($currentServerVarForIP && $currentServerVarForIP === $variable): ?>
106
  <td class="success">In use</td>
107
  <?php elseif ($howGet === $variable): ?>
lib/sysinfo.php CHANGED
@@ -16,6 +16,6 @@ $out = preg_replace('/<\/a>/', '', $out);
16
  $out = preg_replace('/<title>[^<]*<\/title>/','', $out);
17
  echo $out;
18
  ?>
19
- <div class="diffFooter">&copy;&nbsp;2011 to 2015 Wordfence &mdash; Visit <a href="http://wordfence.com/">Wordfence.com</a> for help, security updates and more.</div>
20
  </body>
21
  </html>
16
  $out = preg_replace('/<title>[^<]*<\/title>/','', $out);
17
  echo $out;
18
  ?>
19
+ <div class="diffFooter">&copy;&nbsp;2011 to <?php echo date('Y'); ?> Wordfence &mdash; Visit <a href="http://wordfence.com/">Wordfence.com</a> for help, security updates and more.</div>
20
  </body>
21
  </html>
lib/unknownFiles.php CHANGED
@@ -152,6 +152,6 @@ if($fileList){
152
 
153
  ?>
154
 
155
- <div class="diffFooter">&copy;&nbsp;2011 to 2015 Wordfence &mdash; Visit <a href="http://wordfence.com/">Wordfence.com</a> for help, security updates and more.</div>
156
  </body>
157
  </html>
152
 
153
  ?>
154
 
155
+ <div class="diffFooter">&copy;&nbsp;2011 to <?php echo date('Y'); ?> Wordfence &mdash; Visit <a href="http://wordfence.com/">Wordfence.com</a> for help, security updates and more.</div>
156
  </body>
157
  </html>
lib/wfActivityReport.php CHANGED
@@ -423,9 +423,11 @@ SQL
423
  * @param mixed $ip_address
424
  * @param int|null $unixday
425
  */
426
- public static function logBlockedIP($ip_address, $unixday = null) {
427
  /** @var wpdb $wpdb */
428
  global $wpdb;
 
 
429
 
430
  if (wfUtils::isValidIP($ip_address)) {
431
  $ip_bin = wfUtils::inet_pton($ip_address);
@@ -440,15 +442,19 @@ SQL
440
  if (is_int($unixday)) {
441
  $unixday_insert = absint($unixday);
442
  }
 
 
 
 
443
 
444
  $country = wfUtils::IP2Country($ip_address);
445
 
446
  $wpdb->query($wpdb->prepare(<<<SQL
447
- INSERT INTO $blocked_table (IP, countryCode, blockCount, unixday)
448
- VALUES (%s, %s, 1, $unixday_insert)
449
  ON DUPLICATE KEY UPDATE blockCount = blockCount + 1
450
  SQL
451
- , $ip_bin, $country));
452
  }
453
 
454
  /**
@@ -558,13 +564,18 @@ SQL
558
  , $this->time_range, $this->max_fetch));
559
  if ($results) {
560
  foreach ($results as &$row) {
561
- $row->longDescription = "Blocked for " . $row->actionDescription;
562
-
563
  $actionData = json_decode($row->actionData, true);
564
  if (!is_array($actionData) || !isset($actionData['paramKey']) || !isset($actionData['paramValue'])) {
565
  continue;
566
  }
567
 
 
 
 
 
 
 
 
568
  $paramKey = base64_decode($actionData['paramKey']);
569
  $paramValue = base64_decode($actionData['paramValue']);
570
  if (strlen($paramValue) > 100) {
423
  * @param mixed $ip_address
424
  * @param int|null $unixday
425
  */
426
+ public static function logBlockedIP($ip_address, $unixday = null, $type = null) {
427
  /** @var wpdb $wpdb */
428
  global $wpdb;
429
+
430
+ //Possible values for $type: throttle, manual, brute, fakegoogle, badpost, country, advanced, blacklist, waf
431
 
432
  if (wfUtils::isValidIP($ip_address)) {
433
  $ip_bin = wfUtils::inet_pton($ip_address);
442
  if (is_int($unixday)) {
443
  $unixday_insert = absint($unixday);
444
  }
445
+
446
+ if ($type === null) {
447
+ $type = 'generic';
448
+ }
449
 
450
  $country = wfUtils::IP2Country($ip_address);
451
 
452
  $wpdb->query($wpdb->prepare(<<<SQL
453
+ INSERT INTO $blocked_table (IP, countryCode, blockCount, unixday, blockType)
454
+ VALUES (%s, %s, 1, $unixday_insert, %s)
455
  ON DUPLICATE KEY UPDATE blockCount = blockCount + 1
456
  SQL
457
+ , $ip_bin, $country, $type));
458
  }
459
 
460
  /**
564
  , $this->time_range, $this->max_fetch));
565
  if ($results) {
566
  foreach ($results as &$row) {
 
 
567
  $actionData = json_decode($row->actionData, true);
568
  if (!is_array($actionData) || !isset($actionData['paramKey']) || !isset($actionData['paramValue'])) {
569
  continue;
570
  }
571
 
572
+ if (isset($actionData['failedRules']) && $actionData['failedRules'] == 'blocked') {
573
+ $row->longDescription = "Blocked because the IP is blacklisted";
574
+ }
575
+ else {
576
+ $row->longDescription = "Blocked for " . $row->actionDescription;
577
+ }
578
+
579
  $paramKey = base64_decode($actionData['paramKey']);
580
  $paramValue = base64_decode($actionData['paramValue']);
581
  if (strlen($paramValue) > 100) {
lib/wfConfig.php CHANGED
@@ -66,6 +66,12 @@ class wfConfig {
66
  "loginSec_blockAdminReg" => array('value' => true, 'autoload' => self::AUTOLOAD),
67
  "loginSec_disableAuthorScan" => array('value' => true, 'autoload' => self::AUTOLOAD),
68
  "loginSec_disableOEmbedAuthor" => array('value' => false, 'autoload' => self::AUTOLOAD),
 
 
 
 
 
 
69
  "other_hideWPVersion" => array('value' => false, 'autoload' => self::AUTOLOAD),
70
  "other_noAnonMemberComments" => array('value' => true, 'autoload' => self::AUTOLOAD),
71
  "other_blockBadPOST" => array('value' => false, 'autoload' => self::AUTOLOAD),
@@ -121,6 +127,7 @@ class wfConfig {
121
  'wafAlertWhitelist' => '',
122
  'wafAlertInterval' => 600,
123
  'wafAlertThreshold' => 100,
 
124
  )
125
  );
126
  public static $serializedOptions = array('lastAdminLogin', 'scanSched', 'emailedIssuesList', 'wf_summaryItems', 'adminUserList', 'twoFactorUsers', 'alertFreqTrack', 'wfStatusStartMsgs', 'vulnerabilities_plugin', 'vulnerabilities_theme', 'dashboardData');
@@ -602,6 +609,9 @@ class wfConfig {
602
  public static function f($key){
603
  echo esc_attr(self::get($key));
604
  }
 
 
 
605
  public static function cbp($key){
606
  if(self::get('isPaid') && self::get($key)){
607
  echo ' checked ';
66
  "loginSec_blockAdminReg" => array('value' => true, 'autoload' => self::AUTOLOAD),
67
  "loginSec_disableAuthorScan" => array('value' => true, 'autoload' => self::AUTOLOAD),
68
  "loginSec_disableOEmbedAuthor" => array('value' => false, 'autoload' => self::AUTOLOAD),
69
+ "notification_updatesNeeded" => array('value' => true, 'autoload' => self::AUTOLOAD),
70
+ "notification_securityAlerts" => array('value' => true, 'autoload' => self::AUTOLOAD),
71
+ "notification_promotions" => array('value' => true, 'autoload' => self::AUTOLOAD),
72
+ "notification_blogHighlights" => array('value' => true, 'autoload' => self::AUTOLOAD),
73
+ "notification_productUpdates" => array('value' => true, 'autoload' => self::AUTOLOAD),
74
+ "notification_scanStatus" => array('value' => true, 'autoload' => self::AUTOLOAD),
75
  "other_hideWPVersion" => array('value' => false, 'autoload' => self::AUTOLOAD),
76
  "other_noAnonMemberComments" => array('value' => true, 'autoload' => self::AUTOLOAD),
77
  "other_blockBadPOST" => array('value' => false, 'autoload' => self::AUTOLOAD),
127
  'wafAlertWhitelist' => '',
128
  'wafAlertInterval' => 600,
129
  'wafAlertThreshold' => 100,
130
+ 'howGetIPs_trusted_proxies' => '',
131
  )
132
  );
133
  public static $serializedOptions = array('lastAdminLogin', 'scanSched', 'emailedIssuesList', 'wf_summaryItems', 'adminUserList', 'twoFactorUsers', 'alertFreqTrack', 'wfStatusStartMsgs', 'vulnerabilities_plugin', 'vulnerabilities_theme', 'dashboardData');
609
  public static function f($key){
610
  echo esc_attr(self::get($key));
611
  }
612
+ public static function p() {
613
+ return self::get('isPaid');
614
+ }
615
  public static function cbp($key){
616
  if(self::get('isPaid') && self::get($key)){
617
  echo ' checked ';
lib/wfDashboard.php CHANGED
@@ -100,6 +100,7 @@ class wfDashboard {
100
  $this->features = array(
101
  array('name' => 'Firewall', 'link' => network_admin_url('admin.php?page=WordfenceWAF'), 'state' => !(!WFWAF_ENABLED || (class_exists('wfWAFConfig') && wfWAFConfig::isDisabled())) ? self::FEATURE_ENABLED : self::FEATURE_DISABLED),
102
  array('name' => 'Extended Protection', 'link' => network_admin_url('admin.php?page=WordfenceWAF'), 'state' => (!(!WFWAF_ENABLED || (class_exists('wfWAFConfig') && wfWAFConfig::isDisabled())) && WFWAF_AUTO_PREPEND) ? self::FEATURE_ENABLED : self::FEATURE_DISABLED),
 
103
  array('name' => 'Login Security', 'link' => network_admin_url('admin.php?page=WordfenceSecOpt#focus-loginSecurityEnabled'), 'state' => wfConfig::get('loginSecurityEnabled') ? self::FEATURE_ENABLED : self::FEATURE_DISABLED),
104
  array('name' => 'Scheduled Scans', 'link' => network_admin_url('admin.php?page=WordfenceScan#top#scheduling'), 'state' => wordfence::getNextScanStartTimestamp() !== false && wfConfig::get('scheduledScansEnabled') ? self::FEATURE_ENABLED : self::FEATURE_DISABLED),
105
  array('name' => 'Cellphone Sign-in', 'link' => network_admin_url('admin.php?page=WordfenceTools#top#twofactor'), 'state' => !wfConfig::get('isPaid') ? self::FEATURE_PREMIUM : (wfUtils::hasTwoFactorEnabled() ? self::FEATURE_ENABLED : self::FEATURE_DISABLED)),
100
  $this->features = array(
101
  array('name' => 'Firewall', 'link' => network_admin_url('admin.php?page=WordfenceWAF'), 'state' => !(!WFWAF_ENABLED || (class_exists('wfWAFConfig') && wfWAFConfig::isDisabled())) ? self::FEATURE_ENABLED : self::FEATURE_DISABLED),
102
  array('name' => 'Extended Protection', 'link' => network_admin_url('admin.php?page=WordfenceWAF'), 'state' => (!(!WFWAF_ENABLED || (class_exists('wfWAFConfig') && wfWAFConfig::isDisabled())) && WFWAF_AUTO_PREPEND) ? self::FEATURE_ENABLED : self::FEATURE_DISABLED),
103
+ array('name' => 'Real-time IP Blacklist', 'link' => network_admin_url('admin.php?page=WordfenceWAF'), 'state' => !wfConfig::get('isPaid') ? self::FEATURE_PREMIUM : (WFWAF_ENABLED && class_exists('wfWAFConfig') && !wfWAFConfig::get('disableWAFBlacklistBlocking') ? self::FEATURE_ENABLED : self::FEATURE_DISABLED)),
104
  array('name' => 'Login Security', 'link' => network_admin_url('admin.php?page=WordfenceSecOpt#focus-loginSecurityEnabled'), 'state' => wfConfig::get('loginSecurityEnabled') ? self::FEATURE_ENABLED : self::FEATURE_DISABLED),
105
  array('name' => 'Scheduled Scans', 'link' => network_admin_url('admin.php?page=WordfenceScan#top#scheduling'), 'state' => wordfence::getNextScanStartTimestamp() !== false && wfConfig::get('scheduledScansEnabled') ? self::FEATURE_ENABLED : self::FEATURE_DISABLED),
106
  array('name' => 'Cellphone Sign-in', 'link' => network_admin_url('admin.php?page=WordfenceTools#top#twofactor'), 'state' => !wfConfig::get('isPaid') ? self::FEATURE_PREMIUM : (wfUtils::hasTwoFactorEnabled() ? self::FEATURE_ENABLED : self::FEATURE_DISABLED)),
lib/wfGeoIP.php CHANGED
@@ -77,6 +77,7 @@ class wfGeoIP {
77
  var $databaseSegments;
78
  var $record_length;
79
  var $shmid;
 
80
  var $GEOIP_COUNTRY_CODE_TO_NUMBER = array(
81
  "" => 0, "AP" => 1, "EU" => 2, "AD" => 3, "AE" => 4, "AF" => 5,
82
  "AG" => 6, "AI" => 7, "AL" => 8, "AM" => 9, "CW" => 10, "AO" => 11,
@@ -383,6 +384,7 @@ class wfGeoIP {
383
  function geoip_open($filename, $flags) {
384
  $gi = new wfGeoIP;
385
  $gi->flags = $flags;
 
386
  if ($gi->flags & WF_GEOIP_SHARED_MEMORY) {
387
  $gi->shmid = @shmop_open (WF_GEOIP_SHM_KEY, "a", 0, 0);
388
  } else {
@@ -760,5 +762,64 @@ class wfGeoIP {
760
  return $str;
761
  }
762
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
763
  }
764
  ?>
77
  var $databaseSegments;
78
  var $record_length;
79
  var $shmid;
80
+ var $size;
81
  var $GEOIP_COUNTRY_CODE_TO_NUMBER = array(
82
  "" => 0, "AP" => 1, "EU" => 2, "AD" => 3, "AE" => 4, "AF" => 5,
83
  "AG" => 6, "AI" => 7, "AL" => 8, "AM" => 9, "CW" => 10, "AO" => 11,
384
  function geoip_open($filename, $flags) {
385
  $gi = new wfGeoIP;
386
  $gi->flags = $flags;
387
+ $gi->size = (int) @filesize($filename);
388
  if ($gi->flags & WF_GEOIP_SHARED_MEMORY) {
389
  $gi->shmid = @shmop_open (WF_GEOIP_SHM_KEY, "a", 0, 0);
390
  } else {
762
  return $str;
763
  }
764
  }
765
+
766
+ if (!function_exists('geoip_database_info')) {
767
+ function geoip_database_info($gi) { //Adapted from the C version
768
+ $fno = $gi->filehandle;
769
+ $offset = $gi->size - 3;
770
+ $hasStructureInfo = false;
771
+
772
+ /* first get past the database structure information */
773
+ for ($i = 0; $i < WF_STRUCTURE_INFO_MAX_SIZE; $i++) {
774
+ fseek($fno, $offset, SEEK_SET);
775
+ $data = fread($fno, 3);
776
+ if (strlen($data) != 3) {
777
+ return NULL;
778
+ }
779
+ if ($data == "\xff\xff\xff") {
780
+ $hasStructureInfo = true;
781
+ break;
782
+ }
783
+ $offset -= 1;
784
+ if ($offset < 0) {
785
+ return NULL;
786
+ }
787
+ }
788
+ if ($hasStructureInfo) {
789
+ $offset -= 6;
790
+ if ($offset < 0) {
791
+ return NULL;
792
+ }
793
+ } else {
794
+ /* no structure info, must be pre Sep 2002 database, go back to end */
795
+ $offset -= 3;
796
+ if ($offset < 0) {
797
+ return NULL;
798
+ }
799
+ }
800
+
801
+ for ($i = 0; $i < WF_DATABASE_INFO_MAX_SIZE; $i++) {
802
+ fseek($fno, $offset, SEEK_SET);
803
+ $data = fread($fno, 3);
804
+ if (strlen($data) != 3) {
805
+ return NULL;
806
+ }
807
+ $offset += 3;
808
+ if ($data == "\0\0\0") {
809
+ fseek($fno, $offset, SEEK_SET);
810
+ $data = fread($fno, $i);
811
+ if (strlen($data) != $i) {
812
+ return NULL;
813
+ }
814
+ return $data;
815
+ }
816
+ $offset -= 4;
817
+ if ($offset < 0) {
818
+ return NULL;
819
+ }
820
+ }
821
+ return NULL;
822
+ }
823
+ }
824
  }
825
  ?>
lib/wfLog.php CHANGED
@@ -410,7 +410,7 @@ class wfLog {
410
  }
411
  return $results;
412
  }
413
- public function blockIP($IP, $reason, $wfsn = false, $permanent = false, $maxTimeBlocked = false){ //wfsn indicates it comes from Wordfence secure network
414
  if($this->isWhitelisted($IP)){ return false; }
415
  $wfsn = $wfsn ? 1 : 0;
416
  $timeBlockOccurred = $this->getDB()->querySingle("select unix_timestamp() as ctime");
@@ -445,7 +445,7 @@ class wfLog {
445
  );
446
  }
447
 
448
- wfActivityReport::logBlockedIP($IP);
449
 
450
  if ($this->currentRequest !== null) {
451
  $this->currentRequest->statusCode = 403;
@@ -470,7 +470,7 @@ class wfLog {
470
  $reason
471
  );
472
 
473
- wfActivityReport::logBlockedIP($IP);
474
 
475
  if ($this->currentRequest !== null) {
476
  $this->currentRequest->statusCode = 403;
@@ -979,7 +979,7 @@ class wfLog {
979
  }
980
  if($doBlock){
981
  $this->getDB()->queryWrite("update " . $this->ipRangesTable . " set totalBlocked = totalBlocked + 1, lastBlocked = unix_timestamp() where id=%d", $blockRec['id']);
982
- wfActivityReport::logBlockedIP($IP);
983
  $this->currentRequest->actionDescription = 'UA/Referrer/IP Range not allowed';
984
  $this->do503(3600, "Advanced blocking in effect.");
985
  }
@@ -1108,14 +1108,14 @@ class wfLog {
1108
  }
1109
  $this->logHit();
1110
 
1111
- wfActivityReport::logBlockedIP($IP);
1112
 
1113
  $this->redirect(wfConfig::get('cbl_redirURL'));
1114
  }
1115
  } else {
1116
  $this->currentRequest->actionDescription = 'blocked access via country blocking';
1117
  wfConfig::inc('totalCountryBlocked');
1118
- wfActivityReport::logBlockedIP($IP);
1119
  $this->do503(3600, "Access from your area has been temporarily limited for security reasons");
1120
  }
1121
  }
@@ -1133,7 +1133,7 @@ class wfLog {
1133
  $secsToGo = 0;
1134
  if($action == 'block'){
1135
  $IP = wfUtils::getIP();
1136
- $this->blockIP($IP, $reason);
1137
  $secsToGo = wfConfig::get('blockedTime');
1138
  //Moved the following code AFTER the block to prevent multiple emails.
1139
  if(wfConfig::get('alertOn_block')){
@@ -1145,6 +1145,7 @@ class wfLog {
1145
  $this->getDB()->queryWrite("insert into " . $this->throttleTable . " (IP, startTime, endTime, timesThrottled, lastReason) values (%s, unix_timestamp(), unix_timestamp(), 1, '%s') ON DUPLICATE KEY UPDATE endTime=unix_timestamp(), timesThrottled = timesThrottled + 1, lastReason='%s'", wfUtils::inet_pton($IP), $reason, $reason);
1146
  wordfence::status(2, 'info', "Throttling IP $IP. $reason");
1147
  wfConfig::inc('totalIPsThrottled');
 
1148
  $secsToGo = 60;
1149
  }
1150
  $this->do503($secsToGo, $reason);
410
  }
411
  return $results;
412
  }
413
+ public function blockIP($IP, $reason, $wfsn = false, $permanent = false, $maxTimeBlocked = false, $type = null){ //wfsn indicates it comes from Wordfence secure network
414
  if($this->isWhitelisted($IP)){ return false; }
415
  $wfsn = $wfsn ? 1 : 0;
416
  $timeBlockOccurred = $this->getDB()->querySingle("select unix_timestamp() as ctime");
445
  );
446
  }
447
 
448
+ wfActivityReport::logBlockedIP($IP, null, $type);
449
 
450
  if ($this->currentRequest !== null) {
451
  $this->currentRequest->statusCode = 403;
470
  $reason
471
  );
472
 
473
+ wfActivityReport::logBlockedIP($IP, null, 'brute');
474
 
475
  if ($this->currentRequest !== null) {
476
  $this->currentRequest->statusCode = 403;
979
  }
980
  if($doBlock){
981
  $this->getDB()->queryWrite("update " . $this->ipRangesTable . " set totalBlocked = totalBlocked + 1, lastBlocked = unix_timestamp() where id=%d", $blockRec['id']);
982
+ wfActivityReport::logBlockedIP($IP, null, 'advanced');
983
  $this->currentRequest->actionDescription = 'UA/Referrer/IP Range not allowed';
984
  $this->do503(3600, "Advanced blocking in effect.");
985
  }
1108
  }
1109
  $this->logHit();
1110
 
1111
+ wfActivityReport::logBlockedIP($IP, null, 'country');
1112
 
1113
  $this->redirect(wfConfig::get('cbl_redirURL'));
1114
  }
1115
  } else {
1116
  $this->currentRequest->actionDescription = 'blocked access via country blocking';
1117
  wfConfig::inc('totalCountryBlocked');
1118
+ wfActivityReport::logBlockedIP($IP, null, 'country');
1119
  $this->do503(3600, "Access from your area has been temporarily limited for security reasons");
1120
  }
1121
  }
1133
  $secsToGo = 0;
1134
  if($action == 'block'){
1135
  $IP = wfUtils::getIP();
1136
+ $this->blockIP($IP, $reason, false, false, false, 'throttle');
1137
  $secsToGo = wfConfig::get('blockedTime');
1138
  //Moved the following code AFTER the block to prevent multiple emails.
1139
  if(wfConfig::get('alertOn_block')){
1145
  $this->getDB()->queryWrite("insert into " . $this->throttleTable . " (IP, startTime, endTime, timesThrottled, lastReason) values (%s, unix_timestamp(), unix_timestamp(), 1, '%s') ON DUPLICATE KEY UPDATE endTime=unix_timestamp(), timesThrottled = timesThrottled + 1, lastReason='%s'", wfUtils::inet_pton($IP), $reason, $reason);
1146
  wordfence::status(2, 'info', "Throttling IP $IP. $reason");
1147
  wfConfig::inc('totalIPsThrottled');
1148
+ wfActivityReport::logBlockedIP($IP, null, 'throttle');
1149
  $secsToGo = 60;
1150
  }
1151
  $this->do503($secsToGo, $reason);
lib/wfNotification.php CHANGED
@@ -44,6 +44,38 @@ class wfNotification {
44
  return null;
45
  }
46
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  public function __construct($id, $priority, $html, $category = null, $ctime = null, $links = null, $memoryOnly = false) {
48
  if ($id === null) {
49
  $id = 'site-' . wfUtils::base32_encode(pack('I', wfConfig::atomicInc('lastNotificationID')));
@@ -72,6 +104,31 @@ class wfNotification {
72
  if (!$memoryOnly) {
73
  $linksJSON = json_encode($links);
74
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  if (!empty($category)) {
76
  $existing = self::getNotificationForCategory($category);
77
  if ($existing) {
44
  return null;
45
  }
46
 
47
+ public static function reconcileNotificationsWithOptions() {
48
+ $notification_updatesNeeded = wfConfig::get('notification_updatesNeeded');
49
+ $notification_securityAlerts = wfConfig::get('notification_securityAlerts') || !wfConfig::p();
50
+ $notification_promotions = wfConfig::get('notification_promotions') || !wfConfig::p();
51
+ $notification_blogHighlights = wfConfig::get('notification_blogHighlights') || !wfConfig::p();
52
+ $notification_productUpdates = wfConfig::get('notification_productUpdates') || !wfConfig::p();
53
+ $notification_scanStatus = wfConfig::get('notification_scanStatus');
54
+
55
+ $notifications = self::notifications();
56
+ foreach ($notifications as $n) {
57
+ $category = $n->category;
58
+
59
+ if (preg_match('/^release/i', $category) && !$notification_productUpdates) { $n->markAsRead(); }
60
+ if (preg_match('/^digest/i', $category) && !$notification_blogHighlights) { $n->markAsRead(); }
61
+ if (preg_match('/^alert/i', $category) && !$notification_securityAlerts) { $n->markAsRead(); }
62
+ if (preg_match('/^promo/i', $category) && !$notification_promotions) { $n->markAsRead(); }
63
+
64
+ switch ($category) {
65
+ case 'wfplugin_scan':
66
+ if (!$notification_scanStatus) { $n->markAsRead(); }
67
+ break;
68
+ case 'wfplugin_updates':
69
+ if (!$notification_updatesNeeded) { $n->markAsRead(); }
70
+ break;
71
+ case 'wfplugin_keyconflict':
72
+ default:
73
+ //Allow it
74
+ break;
75
+ }
76
+ }
77
+ }
78
+
79
  public function __construct($id, $priority, $html, $category = null, $ctime = null, $links = null, $memoryOnly = false) {
80
  if ($id === null) {
81
  $id = 'site-' . wfUtils::base32_encode(pack('I', wfConfig::atomicInc('lastNotificationID')));
104
  if (!$memoryOnly) {
105
  $linksJSON = json_encode($links);
106
 
107
+ $notification_updatesNeeded = wfConfig::get('notification_updatesNeeded');
108
+ $notification_securityAlerts = wfConfig::get('notification_securityAlerts') || !wfConfig::p();
109
+ $notification_promotions = wfConfig::get('notification_promotions') || !wfConfig::p();
110
+ $notification_blogHighlights = wfConfig::get('notification_blogHighlights') || !wfConfig::p();
111
+ $notification_productUpdates = wfConfig::get('notification_productUpdates') || !wfConfig::p();
112
+ $notification_scanStatus = wfConfig::get('notification_scanStatus');
113
+
114
+ if (preg_match('/^release/i', $category) && !$notification_productUpdates) { return; }
115
+ if (preg_match('/^digest/i', $category) && !$notification_blogHighlights) { return; }
116
+ if (preg_match('/^alert/i', $category) && !$notification_securityAlerts) { return; }
117
+ if (preg_match('/^promo/i', $category) && !$notification_promotions) { return; }
118
+
119
+ switch ($category) {
120
+ case 'wfplugin_scan':
121
+ if (!$notification_scanStatus) { return; }
122
+ break;
123
+ case 'wfplugin_updates':
124
+ if (!$notification_updatesNeeded) { return; }
125
+ break;
126
+ case 'wfplugin_keyconflict':
127
+ default:
128
+ //Allow it
129
+ break;
130
+ }
131
+
132
  if (!empty($category)) {
133
  $existing = self::getNotificationForCategory($category);
134
  if ($existing) {
lib/wfScanEngine.php CHANGED
@@ -213,6 +213,9 @@ class wfScanEngine {
213
  throw new wfScanEngineDurationLimitException($error);
214
  }
215
  }
 
 
 
216
  public function forkIfNeeded(){
217
  self::checkForKill();
218
  $this->checkForDurationLimit();
213
  throw new wfScanEngineDurationLimitException($error);
214
  }
215
  }
216
+ public function shouldFork() {
217
+ return (time() - $this->cycleStartTime > $this->maxExecTime);
218
+ }
219
  public function forkIfNeeded(){
220
  self::checkForKill();
221
  $this->checkForDurationLimit();
lib/wfSchema.php CHANGED
@@ -151,7 +151,9 @@ class wfSchema {
151
  filename varchar(1000) NOT NULL,
152
  knownFile tinyint UNSIGNED NOT NULL,
153
  oldMD5 binary(16) NOT NULL,
154
- newMD5 binary(16) NOT NULL
 
 
155
  ) default charset=utf8",
156
  'wfBlocksAdv' => "(
157
  id int UNSIGNED NOT NULL auto_increment PRIMARY KEY,
@@ -163,12 +165,13 @@ class wfSchema {
163
  lastBlocked int UNSIGNED default 0
164
  ) default charset=utf8",
165
  'wfBlockedIPLog' => "(
166
- IP int UNSIGNED NOT NULL,
167
- countryCode VARCHAR(2) NOT NULL,
168
- blockCount int UNSIGNED NOT NULL DEFAULT 0,
169
- unixday int UNSIGNED NOT NULL,
170
- PRIMARY KEY(IP, unixday)
171
- ) default charset=utf8",
 
172
  'wfSNIPCache' => "(
173
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
174
  `IP` varchar(45) NOT NULL DEFAULT '',
151
  filename varchar(1000) NOT NULL,
152
  knownFile tinyint UNSIGNED NOT NULL,
153
  oldMD5 binary(16) NOT NULL,
154
+ newMD5 binary(16) NOT NULL,
155
+ stoppedOnSignature varchar(255) NOT NULL DEFAULT '',
156
+ stoppedOnPosition int(10) unsigned NOT NULL DEFAULT '0'
157
  ) default charset=utf8",
158
  'wfBlocksAdv' => "(
159
  id int UNSIGNED NOT NULL auto_increment PRIMARY KEY,
165
  lastBlocked int UNSIGNED default 0
166
  ) default charset=utf8",
167
  'wfBlockedIPLog' => "(
168
+ `IP` binary(16) NOT NULL DEFAULT '\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0',
169
+ `countryCode` varchar(2) NOT NULL,
170
+ `blockCount` int(10) unsigned NOT NULL DEFAULT '0',
171
+ `unixday` int(10) unsigned NOT NULL,
172
+ `blockType` varchar(50) NOT NULL DEFAULT 'generic',
173
+ PRIMARY KEY (`IP`,`unixday`,`blockType`)
174
+ ) DEFAULT CHARSET=utf8",
175
  'wfSNIPCache' => "(
176
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
177
  `IP` varchar(45) NOT NULL DEFAULT '',
lib/wfUtils.php CHANGED
@@ -543,7 +543,7 @@ class wfUtils {
543
  * @param array $arr
544
  * @return bool|mixed
545
  */
546
- private static function getCleanIPAndServerVar($arr){
547
  $privates = array(); //Store private addrs until end as last resort.
548
  for($i = 0; $i < count($arr); $i++){
549
  list($item, $var) = $arr[$i];
@@ -568,11 +568,14 @@ class wfUtils {
568
  continue; //This was an array so we can skip to the next item
569
  }
570
  $skipToNext = false;
 
 
 
571
  foreach(array(',', ' ', "\t") as $char){
572
  if(strpos($item, $char) !== false){
573
  $sp = explode($char, $item);
574
  $sp = array_reverse($sp);
575
- foreach($sp as $j){
576
  $j = trim($j);
577
  if (!self::isValidIP($j)) {
578
  $j = preg_replace('/:\d+$/', '', $j); //Strip off port
@@ -581,6 +584,14 @@ class wfUtils {
581
  if (self::isIPv6MappedIPv4($j)) {
582
  $j = self::inet_ntop(self::inet_pton($j));
583
  }
 
 
 
 
 
 
 
 
584
 
585
  if(self::isPrivateAddress($j)){
586
  $privates[] = array($j, $var);
@@ -632,9 +643,9 @@ class wfUtils {
632
  return false;
633
  }
634
  }
635
- public static function getIP(){
636
  static $theIP = null;
637
- if (isset($theIP)) {
638
  return $theIP;
639
  }
640
  //For debugging.
@@ -642,7 +653,7 @@ class wfUtils {
642
  //return self::makeRandomIP();
643
 
644
  // if no REMOTE_ADDR, it's probably running from the command line
645
- $ip = self::getIPAndServerVarible();
646
  if (is_array($ip)) {
647
  list($IP, $variable) = $ip;
648
  $theIP = $IP;
@@ -650,20 +661,32 @@ class wfUtils {
650
  }
651
  return false;
652
  }
 
 
 
 
 
 
 
 
 
653
 
654
- public static function getIPAndServerVarible() {
655
  $connectionIP = array_key_exists('REMOTE_ADDR', $_SERVER) ? array($_SERVER['REMOTE_ADDR'], 'REMOTE_ADDR') : array('127.0.0.1', 'REMOTE_ADDR');
656
 
657
- $howGet = wfConfig::get('howGetIPs', false);
 
 
 
658
  if($howGet){
659
  if($howGet == 'REMOTE_ADDR'){
660
- return self::getCleanIPAndServerVar(array($connectionIP));
661
  } else {
662
  $ipsToCheck = array(
663
  array((isset($_SERVER[$howGet]) ? $_SERVER[$howGet] : ''), $howGet),
664
  $connectionIP,
665
  );
666
- return self::getCleanIPAndServerVar($ipsToCheck);
667
  }
668
  } else {
669
  $ipsToCheck = array();
@@ -681,13 +704,53 @@ class wfUtils {
681
  if (isset($_SERVER['HTTP_X_REAL_IP'])) {
682
  $ipsToCheck[] = array($_SERVER['HTTP_X_REAL_IP'], 'HTTP_X_REAL_IP');
683
  }
684
- return self::getCleanIPAndServerVar($ipsToCheck);
685
  }
686
  return false; //Returns an array with a valid IP and the server variable, or false.
687
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
688
  public static function isValidIP($IP){
689
  return filter_var($IP, FILTER_VALIDATE_IP) !== false;
690
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
691
  public static function getRequestedURL() {
692
  if (isset($_SERVER['HTTP_HOST']) && $_SERVER['HTTP_HOST']) {
693
  $host = $_SERVER['HTTP_HOST'];
@@ -1099,6 +1162,28 @@ class wfUtils {
1099
  geoip_close($gi);
1100
  return $country ? $country : '';
1101
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1102
  public static function siteURLRelative(){
1103
  if(is_multisite()){
1104
  $URL = network_site_url();
@@ -1689,6 +1774,162 @@ class wfUtils {
1689
 
1690
  return 'unknown';
1691
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1692
  }
1693
 
1694
  // GeoIP lib uses these as well
543
  * @param array $arr
544
  * @return bool|mixed
545
  */
546
+ private static function getCleanIPAndServerVar($arr, $trustedProxies = null) {
547
  $privates = array(); //Store private addrs until end as last resort.
548
  for($i = 0; $i < count($arr); $i++){
549
  list($item, $var) = $arr[$i];
568
  continue; //This was an array so we can skip to the next item
569
  }
570
  $skipToNext = false;
571
+ if ($trustedProxies === null) {
572
+ $trustedProxies = explode("\n", wfConfig::get('howGetIPs_trusted_proxies', ''));
573
+ }
574
  foreach(array(',', ' ', "\t") as $char){
575
  if(strpos($item, $char) !== false){
576
  $sp = explode($char, $item);
577
  $sp = array_reverse($sp);
578
+ foreach($sp as $index => $j){
579
  $j = trim($j);
580
  if (!self::isValidIP($j)) {
581
  $j = preg_replace('/:\d+$/', '', $j); //Strip off port
584
  if (self::isIPv6MappedIPv4($j)) {
585
  $j = self::inet_ntop(self::inet_pton($j));
586
  }
587
+
588
+ foreach ($trustedProxies as $proxy) {
589
+ if (!empty($proxy)) {
590
+ if (self::subnetContainsIP($proxy, $j) && $index < count($sp) - 1) {
591
+ continue 2;
592
+ }
593
+ }
594
+ }
595
 
596
  if(self::isPrivateAddress($j)){
597
  $privates[] = array($j, $var);
643
  return false;
644
  }
645
  }
646
+ public static function getIP($refreshCache = false) {
647
  static $theIP = null;
648
+ if (isset($theIP) && !$refreshCache) {
649
  return $theIP;
650
  }
651
  //For debugging.
653
  //return self::makeRandomIP();
654
 
655
  // if no REMOTE_ADDR, it's probably running from the command line
656
+ $ip = self::getIPAndServerVariable();
657
  if (is_array($ip)) {
658
  list($IP, $variable) = $ip;
659
  $theIP = $IP;
661
  }
662
  return false;
663
  }
664
+
665
+ public static function getIPForField($field, $trustedProxies = null) {
666
+ $ip = self::getIPAndServerVariable($field, $trustedProxies);
667
+ if (is_array($ip)) {
668
+ list($IP, $variable) = $ip;
669
+ return $IP;
670
+ }
671
+ return false;
672
+ }
673
 
674
+ public static function getIPAndServerVariable($howGet = null, $trustedProxies = null) {
675
  $connectionIP = array_key_exists('REMOTE_ADDR', $_SERVER) ? array($_SERVER['REMOTE_ADDR'], 'REMOTE_ADDR') : array('127.0.0.1', 'REMOTE_ADDR');
676
 
677
+ if ($howGet === null) {
678
+ $howGet = wfConfig::get('howGetIPs', false);
679
+ }
680
+
681
  if($howGet){
682
  if($howGet == 'REMOTE_ADDR'){
683
+ return self::getCleanIPAndServerVar(array($connectionIP), $trustedProxies);
684
  } else {
685
  $ipsToCheck = array(
686
  array((isset($_SERVER[$howGet]) ? $_SERVER[$howGet] : ''), $howGet),
687
  $connectionIP,
688
  );
689
+ return self::getCleanIPAndServerVar($ipsToCheck, $trustedProxies);
690
  }
691
  } else {
692
  $ipsToCheck = array();
704
  if (isset($_SERVER['HTTP_X_REAL_IP'])) {
705
  $ipsToCheck[] = array($_SERVER['HTTP_X_REAL_IP'], 'HTTP_X_REAL_IP');
706
  }
707
+ return self::getCleanIPAndServerVar($ipsToCheck, $trustedProxies);
708
  }
709
  return false; //Returns an array with a valid IP and the server variable, or false.
710
  }
711
+ public static function getIPPreview($howGet = null, $trustedProxies = null) {
712
+ $ip = self::getIPAndServerVariable($howGet, $trustedProxies);
713
+ if (is_array($ip)) {
714
+ list($IP, $variable) = $ip;
715
+ if (isset($_SERVER[$variable]) && strpos($_SERVER[$variable], ',') !== false) {
716
+ $items = preg_replace('/[\s,]/', '', explode(',', $_SERVER[$variable]));
717
+ $output = '';
718
+ foreach ($items as $i) {
719
+ if ($IP == $i) {
720
+ $output .= ', <strong>' . esc_html($i) . '</strong>';
721
+ }
722
+ else {
723
+ $output .= ', ' . esc_html($i);
724
+ }
725
+ }
726
+
727
+ return substr($output, 2);
728
+ }
729
+ return '<strong>' . esc_html($IP) . '</strong>';
730
+ }
731
+ return false;
732
+ }
733
  public static function isValidIP($IP){
734
  return filter_var($IP, FILTER_VALIDATE_IP) !== false;
735
  }
736
+ public static function isValidCIDRRange($range) {
737
+ $components = explode('/', $range);
738
+ if (count($components) != 2) { return false; }
739
+
740
+ list($ip, $prefix) = $components;
741
+ if (!self::isValidIP($ip)) { return false; }
742
+
743
+ if (!preg_match('/^\d+$/', $prefix)) { return false; }
744
+
745
+ if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
746
+ if ($prefix < 0 || $prefix > 32) { return false; }
747
+ }
748
+ else {
749
+ if ($prefix < 1 || $prefix > 128) { return false; }
750
+ }
751
+
752
+ return true;
753
+ }
754
  public static function getRequestedURL() {
755
  if (isset($_SERVER['HTTP_HOST']) && $_SERVER['HTTP_HOST']) {
756
  $host = $_SERVER['HTTP_HOST'];
1162
  geoip_close($gi);
1163
  return $country ? $country : '';
1164
  }
1165
+ public static function geoIPVersion() {
1166
+ require_once('wfGeoIP.php');
1167
+ $version = array();
1168
+
1169
+ $gi = geoip_open(dirname(__FILE__) . "/GeoIP.dat", WF_GEOIP_STANDARD);
1170
+ $v = @geoip_database_info($gi);
1171
+ if ($v !== null) {
1172
+ $version[] = $v;
1173
+ }
1174
+ geoip_close($gi);
1175
+
1176
+ if (self::hasIPv6Support()) {
1177
+ $gi = geoip_open(dirname(__FILE__) . "/GeoIPv6.dat", WF_GEOIP_STANDARD);
1178
+ $v = @geoip_database_info($gi);
1179
+ if ($v !== null) {
1180
+ $version[] = $v;
1181
+ }
1182
+ geoip_close($gi);
1183
+ }
1184
+
1185
+ return $version;
1186
+ }
1187
  public static function siteURLRelative(){
1188
  if(is_multisite()){
1189
  $URL = network_site_url();
1774
 
1775
  return 'unknown';
1776
  }
1777
+
1778
+ /**
1779
+ * Identical to the same functions in wfWAFUtils.
1780
+ *
1781
+ * Set the mbstring internal encoding to a binary safe encoding when func_overload
1782
+ * is enabled.
1783
+ *
1784
+ * When mbstring.func_overload is in use for multi-byte encodings, the results from
1785
+ * strlen() and similar functions respect the utf8 characters, causing binary data
1786
+ * to return incorrect lengths.
1787
+ *
1788
+ * This function overrides the mbstring encoding to a binary-safe encoding, and
1789
+ * resets it to the users expected encoding afterwards through the
1790
+ * `reset_mbstring_encoding` function.
1791
+ *
1792
+ * It is safe to recursively call this function, however each
1793
+ * `mbstring_binary_safe_encoding()` call must be followed up with an equal number
1794
+ * of `reset_mbstring_encoding()` calls.
1795
+ *
1796
+ * @see wfWAFUtils::reset_mbstring_encoding
1797
+ *
1798
+ * @staticvar array $encodings
1799
+ * @staticvar bool $overloaded
1800
+ *
1801
+ * @param bool $reset Optional. Whether to reset the encoding back to a previously-set encoding.
1802
+ * Default false.
1803
+ */
1804
+ public static function mbstring_binary_safe_encoding($reset = false) {
1805
+ static $encodings = array();
1806
+ static $overloaded = null;
1807
+
1808
+ if (is_null($overloaded))
1809
+ $overloaded = function_exists('mb_internal_encoding') && (ini_get('mbstring.func_overload') & 2);
1810
+
1811
+ if (false === $overloaded)
1812
+ return;
1813
+
1814
+ if (!$reset) {
1815
+ $encoding = mb_internal_encoding();
1816
+ array_push($encodings, $encoding);
1817
+ mb_internal_encoding('ISO-8859-1');
1818
+ }
1819
+
1820
+ if ($reset && $encodings) {
1821
+ $encoding = array_pop($encodings);
1822
+ mb_internal_encoding($encoding);
1823
+ }
1824
+ }
1825
+
1826
+ /**
1827
+ * Reset the mbstring internal encoding to a users previously set encoding.
1828
+ *
1829
+ * @see wfWAFUtils::mbstring_binary_safe_encoding
1830
+ */
1831
+ public static function reset_mbstring_encoding() {
1832
+ self::mbstring_binary_safe_encoding(true);
1833
+ }
1834
+
1835
+ /**
1836
+ * @param callable $function
1837
+ * @param array $args
1838
+ * @return mixed
1839
+ */
1840
+ protected static function callMBSafeStrFunction($function, $args) {
1841
+ self::mbstring_binary_safe_encoding();
1842
+ $return = call_user_func_array($function, $args);
1843
+ self::reset_mbstring_encoding();
1844
+ return $return;
1845
+ }
1846
+
1847
+ /**
1848
+ * Multibyte safe strlen.
1849
+ *
1850
+ * @param $binary
1851
+ * @return int
1852
+ */
1853
+ public static function strlen($binary) {
1854
+ $args = func_get_args();
1855
+ return self::callMBSafeStrFunction('strlen', $args);
1856
+ }
1857
+
1858
+ /**
1859
+ * @param $haystack
1860
+ * @param $needle
1861
+ * @param int $offset
1862
+ * @return int
1863
+ */
1864
+ public static function stripos($haystack, $needle, $offset = 0) {
1865
+ $args = func_get_args();
1866
+ return self::callMBSafeStrFunction('stripos', $args);
1867
+ }
1868
+
1869
+ /**
1870
+ * @param $string
1871
+ * @return mixed
1872
+ */
1873
+ public static function strtolower($string) {
1874
+ $args = func_get_args();
1875
+ return self::callMBSafeStrFunction('strtolower', $args);
1876
+ }
1877
+
1878
+ /**
1879
+ * @param $string
1880
+ * @param $start
1881
+ * @param $length
1882
+ * @return mixed
1883
+ */
1884
+ public static function substr($string, $start, $length = null) {
1885
+ $args = func_get_args();
1886
+ return self::callMBSafeStrFunction('substr', $args);
1887
+ }
1888
+
1889
+ /**
1890
+ * @param $haystack
1891
+ * @param $needle
1892
+ * @param int $offset
1893
+ * @return mixed
1894
+ */
1895
+ public static function strpos($haystack, $needle, $offset = 0) {
1896
+ $args = func_get_args();
1897
+ return self::callMBSafeStrFunction('strpos', $args);
1898
+ }
1899
+
1900
+ /**
1901
+ * @param string $haystack
1902
+ * @param string $needle
1903
+ * @param int $offset
1904
+ * @param int $length
1905
+ * @return mixed
1906
+ */
1907
+ public static function substr_count($haystack, $needle, $offset = 0, $length = null) {
1908
+ $haystack = self::substr($haystack, $offset, $length);
1909
+ return self::callMBSafeStrFunction('substr_count', array(
1910
+ $haystack, $needle,
1911
+ ));
1912
+ }
1913
+
1914
+ /**
1915
+ * @param $string
1916
+ * @return mixed
1917
+ */
1918
+ public static function strtoupper($string) {
1919
+ $args = func_get_args();
1920
+ return self::callMBSafeStrFunction('strtoupper', $args);
1921
+ }
1922
+
1923
+ /**
1924
+ * @param string $haystack
1925
+ * @param string $needle
1926
+ * @param int $offset
1927
+ * @return mixed
1928
+ */
1929
+ public static function strrpos($haystack, $needle, $offset = 0) {
1930
+ $args = func_get_args();
1931
+ return self::callMBSafeStrFunction('strrpos', $args);
1932
+ }
1933
  }
1934
 
1935
  // GeoIP lib uses these as well
lib/wfViewResult.php CHANGED
@@ -21,6 +21,6 @@
21
 
22
 
23
 
24
- <div class="diffFooter">&copy;&nbsp;2011 to 2015 Wordfence &mdash; Visit <a href="http://wordfence.com/">Wordfence.com</a> for help, security updates and more.</div>
25
  </body>
26
  </html>
21
 
22
 
23
 
24
+ <div class="diffFooter">&copy;&nbsp;2011 to <?php echo date('Y'); ?> Wordfence &mdash; Visit <a href="http://wordfence.com/">Wordfence.com</a> for help, security updates and more.</div>
25
  </body>
26
  </html>
lib/wordfenceClass.php CHANGED
@@ -92,6 +92,9 @@ class wordfence {
92
  $schema->dropAll();
93
  wfConfig::updateTableExists();
94
  foreach(array('wordfence_version', 'wordfenceActivated') as $opt){
 
 
 
95
  delete_option($opt);
96
  }
97
 
@@ -159,7 +162,7 @@ class wordfence {
159
  $ip_bin = substr($resp['data'], $i, 16);
160
  $IPStr = wfUtils::inet_ntop($ip_bin);
161
  if(! $log->isWhitelisted($IPStr)){
162
- $log->blockIP($IPStr, $reason, true);
163
  }
164
  }
165
  }
@@ -370,8 +373,14 @@ class wordfence {
370
  if (function_exists('ignore_user_abort')) {
371
  ignore_user_abort(true);
372
  }
373
- $previous_version = get_option('wordfence_version', '0.0.0');
374
- update_option('wordfence_version', WORDFENCE_VERSION); //In case we have a fatal error we don't want to keep running install.
 
 
 
 
 
 
375
  //EVERYTHING HERE MUST BE IDEMPOTENT
376
 
377
  //Remove old legacy cron job if exists
@@ -520,12 +529,20 @@ SQL
520
  }
521
 
522
  // Fix the data in the country column.
523
- // TODO: add version check so this doesn't run on every update.
524
- $ip_results = $wpdb->get_results("SELECT * FROM `{$prefix}wfBlockedIPLog` GROUP BY IP");
525
- if ($ip_results) {
526
- foreach ($ip_results as $ip_row) {
527
- $wpdb->query($wpdb->prepare("UPDATE `{$prefix}wfBlockedIPLog` SET countryCode = %s WHERE IP = %s", wfUtils::IP2Country(wfUtils::inet_ntop($ip_row->IP)), $ip_row->IP));
 
 
 
 
 
 
 
528
  }
 
529
  }
530
 
531
  if (wfConfig::get('other_hideWPVersion')) {
@@ -646,7 +663,34 @@ SQL
646
  $wpdb->query("ALTER TABLE `{$snipCacheTable}` ADD `type` INT UNSIGNED NOT NULL DEFAULT '0'");
647
  $wpdb->query("ALTER TABLE `{$snipCacheTable}` ADD INDEX (`type`)");
648
  }
649
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
650
 
651
  //Check the How does Wordfence get IPs setting
652
  wfUtils::requestDetectProxyCallback();
@@ -700,7 +744,7 @@ SQL
700
  register_activation_hook(WORDFENCE_FCPATH, 'wordfence::installPlugin');
701
  register_deactivation_hook(WORDFENCE_FCPATH, 'wordfence::uninstallPlugin');
702
 
703
- $versionInOptions = get_option('wordfence_version', false);
704
  if( (! $versionInOptions) || version_compare(WORDFENCE_VERSION, $versionInOptions, '>')){
705
  //Either there is no version in options or the version in options is greater and we need to run the upgrade
706
  self::runInstall();
@@ -1248,6 +1292,7 @@ SQL
1248
  'homeURL' => $homeurl,
1249
  'whitelistedIPs' => (string) wfConfig::get('whitelisted'),
1250
  'howGetIPs' => (string) wfConfig::get('howGetIPs'),
 
1251
  'other_WFNet' => wfConfig::get('other_WFNet', true),
1252
  'pluginABSPATH' => ABSPATH,
1253
  );
@@ -1338,7 +1383,7 @@ SQL
1338
 
1339
  if(wfConfig::get('blockFakeBots')){
1340
  if(wfCrawl::isGooglebot() && !wfCrawl::isVerifiedGoogleCrawler()){
1341
- $wfLog->blockIP($IP, "Fake Google crawler automatically blocked");
1342
  wordfence::status(2, 'info', "Blocking fake Googlebot at IP $IP");
1343
  $wfLog->do503(3600, "Fake Google crawler automatically blocked.");
1344
  }
@@ -1347,7 +1392,7 @@ SQL
1347
  $URLs = explode(',', wfConfig::get('bannedURLs'));
1348
  foreach($URLs as $URL){
1349
  if(preg_match(wfUtils::patternToRegex($URL, ''), $_SERVER['REQUEST_URI'])){
1350
- $wfLog->blockIP($IP, "Accessed a banned URL.");
1351
  $wfLog->do503(3600, "Accessed a banned URL.");
1352
  //exits
1353
  }
@@ -1355,7 +1400,7 @@ SQL
1355
  }
1356
 
1357
  if(wfConfig::get('other_blockBadPOST') == '1' && $_SERVER['REQUEST_METHOD'] == 'POST' && empty($_SERVER['HTTP_USER_AGENT']) && empty($_SERVER['HTTP_REFERER'])){
1358
- $wfLog->blockIP($IP, "POST received with blank user-agent and referer");
1359
  $wfLog->do503(3600, "POST received with blank user-agent and referer");
1360
  //exits
1361
  }
@@ -1839,7 +1884,7 @@ SQL
1839
  }
1840
  if(wfConfig::get('other_WFNet') && is_wp_error($authUser) && ($authUser->get_error_code() == 'invalid_username' || $authUser->get_error_code() == 'invalid_email' || $authUser->get_error_code() == 'incorrect_password' || $authUser->get_error_code() == 'twofactor_invalid' || $authUser->get_error_code() == 'authentication_failed') ){
1841
  if($maxBlockTime = self::wfsnIsBlocked($IP, 'brute')){
1842
- self::getLog()->blockIP($IP, "Blocked by Wordfence Security Network", true, false, $maxBlockTime);
1843
  $secsToGo = wfConfig::get('blockedTime');
1844
  self::getLog()->getCurrentRequest()->action = 'blocked:wfsn';
1845
  self::getLog()->do503($secsToGo, "Blocked by Wordfence Security Network");
@@ -1852,7 +1897,7 @@ SQL
1852
  $users = explode("\n", wfUtils::cleanupOneEntryPerLine($blacklist));
1853
  foreach($users as $user){
1854
  if(strtolower($username) == strtolower($user)){
1855
- self::getLog()->blockIP($IP, "Blocked by login security setting.");
1856
  $secsToGo = wfConfig::get('blockedTime');
1857
  self::getLog()->do503($secsToGo, "Blocked by login security setting.");
1858
  break;
@@ -2698,6 +2743,14 @@ SQL
2698
  $key = $_POST['key'];
2699
  $val = $_POST['val'];
2700
  wfConfig::set($key, $val);
 
 
 
 
 
 
 
 
2701
  return array('ok' => 1);
2702
  }
2703
  public static function ajax_checkHtaccess_callback(){
@@ -2823,6 +2876,7 @@ SQL
2823
  $opts['liveTraf_ignoreUsers'] = '';
2824
  }
2825
 
 
2826
  $validIPs = array();
2827
  $invalidIPs = array();
2828
  foreach(explode(',', preg_replace('/[\r\n\s\t]+/', '', $opts['liveTraf_ignoreIPs'])) as $val){
@@ -2840,7 +2894,29 @@ SQL
2840
  if(sizeof($validIPs) > 0){
2841
  $opts['liveTraf_ignoreIPs'] = implode(',', $validIPs);
2842
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2843
 
 
2844
  if(preg_match('/[a-zA-Z0-9\d]+/', $opts['liveTraf_ignoreUA'])){
2845
  $opts['liveTraf_ignoreUA'] = trim($opts['liveTraf_ignoreUA']);
2846
  } else {
@@ -2853,7 +2929,6 @@ SQL
2853
  $wfdb->queryWrite("delete from $p"."wfBlocks where wfsn=1 and permanent=0");
2854
  }
2855
  if($opts['howGetIPs'] != wfConfig::get('howGetIPs', '')){
2856
- $reload = 'reload';
2857
  wfConfig::set('detectProxyNextCheck', false, wfConfig::DONT_AUTOLOAD);
2858
  }
2859
  $regenerateHtaccess = false;
@@ -2958,7 +3033,12 @@ SQL
2958
  return array('errorMsg' => "Your options have been saved. However we tried to verify your API key with the Wordfence servers and received an error: " . wp_kses($e->getMessage(), array()) );
2959
  }
2960
  }
2961
- return array('ok' => 1, 'reload' => $reload, 'paidKeyMsg' => $paidKeyMsg );
 
 
 
 
 
2962
  }
2963
  public static function ajax_savePartialConfig_callback() {
2964
  $opts = wfConfig::parseOptions(true);
@@ -3158,6 +3238,29 @@ SQL
3158
 
3159
  return array('ok' => 1);
3160
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3161
 
3162
  public static $diagnosticParams = array(
3163
  'addCacheComment',
@@ -3263,7 +3366,7 @@ HTACCESS;
3263
  public static function ajax_permBlockIP_callback(){
3264
  $IP = $_POST['IP'];
3265
  $log = new wfLog(wfConfig::get('apiKey'), wfUtils::getWPVersion());
3266
- $log->blockIP($IP, "Manual permanent block by admin", false, true);
3267
  return array('ok' => 1);
3268
  }
3269
  public static function ajax_loadStaticPanel_callback(){
@@ -3370,7 +3473,7 @@ HTACCESS;
3370
  return array('err' => 1, 'errorMsg' => "The IP address you're trying to block belongs to Google. Your options are currently set to not block these crawlers. Change this in Wordfence options if you want to manually block Google.");
3371
  }
3372
  }
3373
- $log->blockIP($IP, $_POST['reason'], false, $perm);
3374
  return array('ok' => 1);
3375
  }
3376
  public static function ajax_reverseLookup_callback(){
@@ -4581,7 +4684,7 @@ HTML;
4581
  'exportSettings', 'importSettings', 'bulkOperation', 'deleteFile', 'deleteDatabaseOption', 'removeExclusion',
4582
  'activityLogUpdate', 'ticker', 'loadIssues', 'updateIssueStatus', 'deleteIssue', 'updateAllIssues',
4583
  'reverseLookup', 'unlockOutIP', 'loadBlockRanges', 'unblockRange', 'blockIPUARange', 'whois', 'unblockIP',
4584
- 'blockIP', 'permBlockIP', 'loadStaticPanel', 'saveConfig', 'savePartialConfig', 'downloadHtaccess', 'downloadLogFile', 'checkHtaccess',
4585
  'updateConfig', 'autoUpdateChoice', 'adminEmailChoice', 'suPHPWAFUpdateChoice', 'misconfiguredHowGetIPsChoice',
4586
  'clearAllBlocked', 'killScan', 'saveCountryBlocking', 'saveScanSchedule', 'tourClosed',
4587
  'welcomeClosed', 'startTourAgain', 'downgradeLicense', 'addTwoFactor', 'twoFacActivate', 'twoFacDel',
@@ -4602,12 +4705,11 @@ HTML;
4602
  wp_enqueue_style('wordfence-main-style', wfUtils::getBaseURL() . 'css/main.css', '', WORDFENCE_VERSION);
4603
  wp_enqueue_style('wordfence-ionicons-style', wfUtils::getBaseURL() . 'css/wf-ionicons.css', '', WORDFENCE_VERSION);
4604
  wp_enqueue_style('wordfence-colorbox-style', wfUtils::getBaseURL() . 'css/colorbox.css', '', WORDFENCE_VERSION);
4605
- wp_enqueue_style('wordfence-dttable-style', wfUtils::getBaseURL() . 'css/dt_table.css', '', WORDFENCE_VERSION);
4606
 
4607
  wp_enqueue_script('json2');
4608
  wp_enqueue_script('jquery.wftmpl', wfUtils::getBaseURL() . 'js/jquery.tmpl.min.js', array('jquery'), WORDFENCE_VERSION);
4609
  wp_enqueue_script('jquery.wfcolorbox', wfUtils::getBaseURL() . 'js/jquery.colorbox-min.js', array('jquery'), WORDFENCE_VERSION);
4610
- wp_enqueue_script('jquery.wfdataTables', wfUtils::getBaseURL() . 'js/jquery.dataTables.min.js', array('jquery'), WORDFENCE_VERSION);
4611
  wp_enqueue_script('jquery.qrcode', wfUtils::getBaseURL() . 'js/jquery.qrcode.min.js', array('jquery'), WORDFENCE_VERSION);
4612
  //wp_enqueue_script('jquery.tools', wfUtils::getBaseURL() . 'js/jquery.tools.min.js', array('jquery'));
4613
  wp_enqueue_script('wordfenceAdminjs', wfUtils::getBaseURL() . 'js/admin.js', array('jquery'), WORDFENCE_VERSION);
@@ -6041,7 +6143,7 @@ HTML
6041
  $log = new wfLog(wfConfig::get('apiKey'), wfUtils::getWPVersion());
6042
  if ($IPs && is_array($IPs)) {
6043
  foreach ($IPs as $IP) {
6044
- $log->blockIP(wfUtils::inet_ntop($IP), $reason, false, true);
6045
  }
6046
  }
6047
  switch ($type) {
@@ -6879,20 +6981,53 @@ LIMIT %d", $lastSendTime, $limit));
6879
  if ($action == wfWAFIPBlocksController::WFWAF_BLOCK_UAREFIPRANGE) {
6880
  $id = $metadata['finalAction']['id'];
6881
  $wpdb->query($wpdb->prepare("UPDATE {$wpdb->base_prefix}wfBlocksAdv SET totalBlocked = totalBlocked + 1, lastBlocked = %d WHERE id = %d", $requestTime, $id));
6882
- wfActivityReport::logBlockedIP($ip);
6883
  }
6884
  else if ($action == wfWAFIPBlocksController::WFWAF_BLOCK_COUNTRY_REDIR) {
6885
  $actionDescription .= ' (' . wfConfig::get('cbl_redirURL') . ')';
6886
  wfConfig::inc('totalCountryBlocked');
6887
- wfActivityReport::logBlockedIP($ip);
6888
  }
6889
  else if ($action == wfWAFIPBlocksController::WFWAF_BLOCK_COUNTRY) {
6890
  wfConfig::inc('totalCountryBlocked');
6891
- wfActivityReport::logBlockedIP($ip);
6892
  }
6893
  else if ($action == wfWAFIPBlocksController::WFWAF_BLOCK_WFSN) {
6894
  wordfence::wfsnReportBlockedAttempt($ip, 'login');
6895
- wfActivityReport::logBlockedIP($ip);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6896
  }
6897
  }
6898
 
@@ -6931,7 +7066,15 @@ LIMIT %d", $lastSendTime, $limit));
6931
  }
6932
  else {
6933
  $hit->action = 'blocked:waf';
6934
- wfActivityReport::logBlockedIP($hit->IP);
 
 
 
 
 
 
 
 
6935
  }
6936
  }
6937
 
92
  $schema->dropAll();
93
  wfConfig::updateTableExists();
94
  foreach(array('wordfence_version', 'wordfenceActivated') as $opt){
95
+ if (is_multisite()) {
96
+ delete_network_option(null, $opt);
97
+ }
98
  delete_option($opt);
99
  }
100
 
162
  $ip_bin = substr($resp['data'], $i, 16);
163
  $IPStr = wfUtils::inet_ntop($ip_bin);
164
  if(! $log->isWhitelisted($IPStr)){
165
+ $log->blockIP($IPStr, $reason, true, false, false, 'brute');
166
  }
167
  }
168
  }
373
  if (function_exists('ignore_user_abort')) {
374
  ignore_user_abort(true);
375
  }
376
+ $previous_version = (is_multisite() ? get_network_option(null, 'wordfence_version', '0.0.0') : get_option('wordfence_version', '0.0.0'));
377
+ if (is_multisite()) {
378
+ update_network_option(null, 'wordfence_version', WORDFENCE_VERSION); //In case we have a fatal error we don't want to keep running install.
379
+ }
380
+ else {
381
+ update_option('wordfence_version', WORDFENCE_VERSION); //In case we have a fatal error we don't want to keep running install.
382
+ }
383
+
384
  //EVERYTHING HERE MUST BE IDEMPOTENT
385
 
386
  //Remove old legacy cron job if exists
529
  }
530
 
531
  // Fix the data in the country column.
532
+ $previousVersionHash = wfConfig::get('geoIPVersionHash', '');
533
+ $geoIPVersion = wfUtils::geoIPVersion();
534
+ $geoIPVersionHash = hash('sha256', implode(',', $geoIPVersion));
535
+ if ($previousVersionHash != $geoIPVersionHash) {
536
+ $ip_results = $wpdb->get_results("SELECT countryCode, IP FROM `{$prefix}wfBlockedIPLog` GROUP BY IP");
537
+ if ($ip_results) {
538
+ foreach ($ip_results as $ip_row) {
539
+ $country = wfUtils::IP2Country(wfUtils::inet_ntop($ip_row->IP));
540
+ if ($country != $ip_row->countryCode) {
541
+ $wpdb->query($wpdb->prepare("UPDATE `{$prefix}wfBlockedIPLog` SET countryCode = %s WHERE IP = %s", $country, $ip_row->IP));
542
+ }
543
+ }
544
  }
545
+ wfConfig::set('geoIPVersionHash', $geoIPVersionHash);
546
  }
547
 
548
  if (wfConfig::get('other_hideWPVersion')) {
663
  $wpdb->query("ALTER TABLE `{$snipCacheTable}` ADD `type` INT UNSIGNED NOT NULL DEFAULT '0'");
664
  $wpdb->query("ALTER TABLE `{$snipCacheTable}` ADD INDEX (`type`)");
665
  }
666
+
667
+ //6.3.5
668
+ $fileModsTable = wfDB::networkPrefix() . 'wfFileMods';
669
+ $hasStoppedOn = $wpdb->get_col($wpdb->prepare(<<<SQL
670
+ SELECT * FROM information_schema.COLUMNS
671
+ WHERE TABLE_SCHEMA=DATABASE()
672
+ AND COLUMN_NAME='stoppedOnSignature'
673
+ AND TABLE_NAME=%s
674
+ SQL
675
+ , $fileModsTable));
676
+ if (!$hasStoppedOn) {
677
+ $wpdb->query("ALTER TABLE {$fileModsTable} ADD COLUMN stoppedOnSignature VARCHAR(255) NOT NULL DEFAULT ''");
678
+ $wpdb->query("ALTER TABLE {$fileModsTable} ADD COLUMN stoppedOnPosition INT UNSIGNED NOT NULL DEFAULT '0'");
679
+ }
680
+
681
+ $blockedIPLogTable = wfDB::networkPrefix() . 'wfBlockedIPLog';
682
+ $hasType = $wpdb->get_col($wpdb->prepare(<<<SQL
683
+ SELECT * FROM information_schema.COLUMNS
684
+ WHERE TABLE_SCHEMA=DATABASE()
685
+ AND COLUMN_NAME='blockType'
686
+ AND TABLE_NAME=%s
687
+ SQL
688
+ , $blockedIPLogTable));
689
+ if (!$hasType) {
690
+ $wpdb->query("ALTER TABLE {$blockedIPLogTable} ADD blockType VARCHAR(50) NOT NULL DEFAULT 'generic'");
691
+ $wpdb->query("ALTER TABLE {$blockedIPLogTable} DROP PRIMARY KEY");
692
+ $wpdb->query("ALTER TABLE {$blockedIPLogTable} ADD PRIMARY KEY (IP, unixday, blockType)");
693
+ }
694
 
695
  //Check the How does Wordfence get IPs setting
696
  wfUtils::requestDetectProxyCallback();
744
  register_activation_hook(WORDFENCE_FCPATH, 'wordfence::installPlugin');
745
  register_deactivation_hook(WORDFENCE_FCPATH, 'wordfence::uninstallPlugin');
746
 
747
+ $versionInOptions = (is_multisite() ? get_network_option(null, 'wordfence_version', false) : get_option('wordfence_version', false));
748
  if( (! $versionInOptions) || version_compare(WORDFENCE_VERSION, $versionInOptions, '>')){
749
  //Either there is no version in options or the version in options is greater and we need to run the upgrade
750
  self::runInstall();
1292
  'homeURL' => $homeurl,
1293
  'whitelistedIPs' => (string) wfConfig::get('whitelisted'),
1294
  'howGetIPs' => (string) wfConfig::get('howGetIPs'),
1295
+ 'howGetIPs_trusted_proxies' => wfConfig::get('howGetIPs_trusted_proxies', ''),
1296
  'other_WFNet' => wfConfig::get('other_WFNet', true),
1297
  'pluginABSPATH' => ABSPATH,
1298
  );
1383
 
1384
  if(wfConfig::get('blockFakeBots')){
1385
  if(wfCrawl::isGooglebot() && !wfCrawl::isVerifiedGoogleCrawler()){
1386
+ $wfLog->blockIP($IP, "Fake Google crawler automatically blocked", false, false, false, 'fakegoogle');
1387
  wordfence::status(2, 'info', "Blocking fake Googlebot at IP $IP");
1388
  $wfLog->do503(3600, "Fake Google crawler automatically blocked.");
1389
  }
1392
  $URLs = explode(',', wfConfig::get('bannedURLs'));
1393
  foreach($URLs as $URL){
1394
  if(preg_match(wfUtils::patternToRegex($URL, ''), $_SERVER['REQUEST_URI'])){
1395
+ $wfLog->blockIP($IP, "Accessed a banned URL.", false, false, false, 'bannedurl');
1396
  $wfLog->do503(3600, "Accessed a banned URL.");
1397
  //exits
1398
  }
1400
  }
1401
 
1402
  if(wfConfig::get('other_blockBadPOST') == '1' && $_SERVER['REQUEST_METHOD'] == 'POST' && empty($_SERVER['HTTP_USER_AGENT']) && empty($_SERVER['HTTP_REFERER'])){
1403
+ $wfLog->blockIP($IP, "POST received with blank user-agent and referer", false, false, false, 'badpost');
1404
  $wfLog->do503(3600, "POST received with blank user-agent and referer");
1405
  //exits
1406
  }
1884
  }
1885
  if(wfConfig::get('other_WFNet') && is_wp_error($authUser) && ($authUser->get_error_code() == 'invalid_username' || $authUser->get_error_code() == 'invalid_email' || $authUser->get_error_code() == 'incorrect_password' || $authUser->get_error_code() == 'twofactor_invalid' || $authUser->get_error_code() == 'authentication_failed') ){
1886
  if($maxBlockTime = self::wfsnIsBlocked($IP, 'brute')){
1887
+ self::getLog()->blockIP($IP, "Blocked by Wordfence Security Network", true, false, $maxBlockTime, 'brute');
1888
  $secsToGo = wfConfig::get('blockedTime');
1889
  self::getLog()->getCurrentRequest()->action = 'blocked:wfsn';
1890
  self::getLog()->do503($secsToGo, "Blocked by Wordfence Security Network");
1897
  $users = explode("\n", wfUtils::cleanupOneEntryPerLine($blacklist));
1898
  foreach($users as $user){
1899
  if(strtolower($username) == strtolower($user)){
1900
+ self::getLog()->blockIP($IP, "Blocked by login security setting.", false, false, false, 'brute');
1901
  $secsToGo = wfConfig::get('blockedTime');
1902
  self::getLog()->do503($secsToGo, "Blocked by login security setting.");
1903
  break;
2743
  $key = $_POST['key'];
2744
  $val = $_POST['val'];
2745
  wfConfig::set($key, $val);
2746
+
2747
+ if ($key == 'howGetIPs') {
2748
+ wfConfig::set('detectProxyNextCheck', false, wfConfig::DONT_AUTOLOAD);
2749
+ $ipAll = wfUtils::getIPPreview();
2750
+ $ip = wfUtils::getIP(true);
2751
+ return array('ok' => 1, 'ip' => $ip, 'ipAll' => $ipAll);
2752
+ }
2753
+
2754
  return array('ok' => 1);
2755
  }
2756
  public static function ajax_checkHtaccess_callback(){
2876
  $opts['liveTraf_ignoreUsers'] = '';
2877
  }
2878
 
2879
+ //liveTraf_ignoreIPs
2880
  $validIPs = array();
2881
  $invalidIPs = array();
2882
  foreach(explode(',', preg_replace('/[\r\n\s\t]+/', '', $opts['liveTraf_ignoreIPs'])) as $val){
2894
  if(sizeof($validIPs) > 0){
2895
  $opts['liveTraf_ignoreIPs'] = implode(',', $validIPs);
2896
  }
2897
+
2898
+ //howGetIPs_trusted_proxies
2899
+ $validIPs = array();
2900
+ $invalidIPs = array();
2901
+ $testIPs = preg_split('/[\r\n,]+/', $opts['howGetIPs_trusted_proxies']);
2902
+ foreach ($testIPs as $val) {
2903
+ if (strlen($val) > 0) {
2904
+ if (wfUtils::isValidIP($val) || wfUtils::isValidCIDRRange($val)) {
2905
+ $validIPs[] = $val;
2906
+ }
2907
+ else {
2908
+ $invalidIPs[] = $val;
2909
+ }
2910
+ }
2911
+ }
2912
+ if (sizeof($invalidIPs) > 0) {
2913
+ return array('errorMsg' => "The following IPs/ranges you selected to trust as proxies are not valid: " . wp_kses(implode(', ', $invalidIPs), array()) );
2914
+ }
2915
+ if (sizeof($validIPs) > 0) {
2916
+ $opts['howGetIPs_trusted_proxies'] = implode("\n", $validIPs);
2917
+ }
2918
 
2919
+ //liveTraf_ignoreUA
2920
  if(preg_match('/[a-zA-Z0-9\d]+/', $opts['liveTraf_ignoreUA'])){
2921
  $opts['liveTraf_ignoreUA'] = trim($opts['liveTraf_ignoreUA']);
2922
  } else {
2929
  $wfdb->queryWrite("delete from $p"."wfBlocks where wfsn=1 and permanent=0");
2930
  }
2931
  if($opts['howGetIPs'] != wfConfig::get('howGetIPs', '')){
 
2932
  wfConfig::set('detectProxyNextCheck', false, wfConfig::DONT_AUTOLOAD);
2933
  }
2934
  $regenerateHtaccess = false;
3033
  return array('errorMsg' => "Your options have been saved. However we tried to verify your API key with the Wordfence servers and received an error: " . wp_kses($e->getMessage(), array()) );
3034
  }
3035
  }
3036
+
3037
+ wfNotification::reconcileNotificationsWithOptions();
3038
+
3039
+ $ipAll = wfUtils::getIPPreview();
3040
+ $ip = wfUtils::getIP(true);
3041
+ return array('ok' => 1, 'reload' => $reload, 'paidKeyMsg' => $paidKeyMsg, 'ip' => $ip, 'ipAll' => $ipAll);
3042
  }
3043
  public static function ajax_savePartialConfig_callback() {
3044
  $opts = wfConfig::parseOptions(true);
3238
 
3239
  return array('ok' => 1);
3240
  }
3241
+
3242
+ public static function ajax_updateIPPreview_callback() {
3243
+ $howGet = $_POST['howGetIPs'];
3244
+
3245
+ $validIPs = array();
3246
+ $invalidIPs = array();
3247
+ $testIPs = preg_split('/[\r\n,]+/', $_POST['howGetIPs_trusted_proxies']);
3248
+ foreach ($testIPs as $val) {
3249
+ if (strlen($val) > 0) {
3250
+ if (wfUtils::isValidIP($val) || wfUtils::isValidCIDRRange($val)) {
3251
+ $validIPs[] = $val;
3252
+ }
3253
+ else {
3254
+ $invalidIPs[] = $val;
3255
+ }
3256
+ }
3257
+ }
3258
+ $trustedProxies = $validIPs;
3259
+
3260
+ $ipAll = wfUtils::getIPPreview($howGet, $trustedProxies);
3261
+ $ip = wfUtils::getIPForField($howGet, $trustedProxies);
3262
+ return array('ok' => 1, 'ip' => $ip, 'ipAll' => $ipAll);
3263
+ }
3264
 
3265
  public static $diagnosticParams = array(
3266
  'addCacheComment',
3366
  public static function ajax_permBlockIP_callback(){
3367
  $IP = $_POST['IP'];
3368
  $log = new wfLog(wfConfig::get('apiKey'), wfUtils::getWPVersion());
3369
+ $log->blockIP($IP, "Manual permanent block by admin", false, true, false, 'manual');
3370
  return array('ok' => 1);
3371
  }
3372
  public static function ajax_loadStaticPanel_callback(){
3473
  return array('err' => 1, 'errorMsg' => "The IP address you're trying to block belongs to Google. Your options are currently set to not block these crawlers. Change this in Wordfence options if you want to manually block Google.");
3474
  }
3475
  }
3476
+ $log->blockIP($IP, $_POST['reason'], false, $perm, false, 'manual');
3477
  return array('ok' => 1);
3478
  }
3479
  public static function ajax_reverseLookup_callback(){
4684
  'exportSettings', 'importSettings', 'bulkOperation', 'deleteFile', 'deleteDatabaseOption', 'removeExclusion',
4685
  'activityLogUpdate', 'ticker', 'loadIssues', 'updateIssueStatus', 'deleteIssue', 'updateAllIssues',
4686
  'reverseLookup', 'unlockOutIP', 'loadBlockRanges', 'unblockRange', 'blockIPUARange', 'whois', 'unblockIP',
4687
+ 'blockIP', 'permBlockIP', 'loadStaticPanel', 'saveConfig', 'savePartialConfig', 'updateIPPreview', 'downloadHtaccess', 'downloadLogFile', 'checkHtaccess',
4688
  'updateConfig', 'autoUpdateChoice', 'adminEmailChoice', 'suPHPWAFUpdateChoice', 'misconfiguredHowGetIPsChoice',
4689
  'clearAllBlocked', 'killScan', 'saveCountryBlocking', 'saveScanSchedule', 'tourClosed',
4690
  'welcomeClosed', 'startTourAgain', 'downgradeLicense', 'addTwoFactor', 'twoFacActivate', 'twoFacDel',
4705
  wp_enqueue_style('wordfence-main-style', wfUtils::getBaseURL() . 'css/main.css', '', WORDFENCE_VERSION);
4706
  wp_enqueue_style('wordfence-ionicons-style', wfUtils::getBaseURL() . 'css/wf-ionicons.css', '', WORDFENCE_VERSION);
4707
  wp_enqueue_style('wordfence-colorbox-style', wfUtils::getBaseURL() . 'css/colorbox.css', '', WORDFENCE_VERSION);
 
4708
 
4709
  wp_enqueue_script('json2');
4710
  wp_enqueue_script('jquery.wftmpl', wfUtils::getBaseURL() . 'js/jquery.tmpl.min.js', array('jquery'), WORDFENCE_VERSION);
4711
  wp_enqueue_script('jquery.wfcolorbox', wfUtils::getBaseURL() . 'js/jquery.colorbox-min.js', array('jquery'), WORDFENCE_VERSION);
4712
+ wp_enqueue_script('jquery.wfdataTables', wfUtils::getBaseURL() . 'js/jquery.dataTables.js', array('jquery'), WORDFENCE_VERSION);
4713
  wp_enqueue_script('jquery.qrcode', wfUtils::getBaseURL() . 'js/jquery.qrcode.min.js', array('jquery'), WORDFENCE_VERSION);
4714
  //wp_enqueue_script('jquery.tools', wfUtils::getBaseURL() . 'js/jquery.tools.min.js', array('jquery'));
4715
  wp_enqueue_script('wordfenceAdminjs', wfUtils::getBaseURL() . 'js/admin.js', array('jquery'), WORDFENCE_VERSION);
6143
  $log = new wfLog(wfConfig::get('apiKey'), wfUtils::getWPVersion());
6144
  if ($IPs && is_array($IPs)) {
6145
  foreach ($IPs as $IP) {
6146
+ $log->blockIP(wfUtils::inet_ntop($IP), $reason, false, true, false, 'manual');
6147
  }
6148
  }
6149
  switch ($type) {
6981
  if ($action == wfWAFIPBlocksController::WFWAF_BLOCK_UAREFIPRANGE) {
6982
  $id = $metadata['finalAction']['id'];
6983
  $wpdb->query($wpdb->prepare("UPDATE {$wpdb->base_prefix}wfBlocksAdv SET totalBlocked = totalBlocked + 1, lastBlocked = %d WHERE id = %d", $requestTime, $id));
6984
+ wfActivityReport::logBlockedIP($ip, null, 'advanced');
6985
  }
6986
  else if ($action == wfWAFIPBlocksController::WFWAF_BLOCK_COUNTRY_REDIR) {
6987
  $actionDescription .= ' (' . wfConfig::get('cbl_redirURL') . ')';
6988
  wfConfig::inc('totalCountryBlocked');
6989
+ wfActivityReport::logBlockedIP($ip, null, 'country');
6990
  }
6991
  else if ($action == wfWAFIPBlocksController::WFWAF_BLOCK_COUNTRY) {
6992
  wfConfig::inc('totalCountryBlocked');
6993
+ wfActivityReport::logBlockedIP($ip, null, 'country');
6994
  }
6995
  else if ($action == wfWAFIPBlocksController::WFWAF_BLOCK_WFSN) {
6996
  wordfence::wfsnReportBlockedAttempt($ip, 'login');
6997
+ wfActivityReport::logBlockedIP($ip, null, 'brute');
6998
+ }
6999
+ else if (defined('wfWAFIPBlocksController::WFWAF_BLOCK_BADPOST') && $action == wfWAFIPBlocksController::WFWAF_BLOCK_BADPOST) {
7000
+ $wpdb->query($wpdb->prepare("UPDATE {$wpdb->base_prefix}wfBlocks SET blockedHits = blockedHits + 1, lastAttempt = %d WHERE IP = %s", $requestTime, wfUtils::inet_pton($ip)));
7001
+ wfActivityReport::logBlockedIP($ip, null, 'badpost');
7002
+ }
7003
+ else if (defined('wfWAFIPBlocksController::WFWAF_BLOCK_BANNEDURL') && $action == wfWAFIPBlocksController::WFWAF_BLOCK_BANNEDURL) {
7004
+ $wpdb->query($wpdb->prepare("UPDATE {$wpdb->base_prefix}wfBlocks SET blockedHits = blockedHits + 1, lastAttempt = %d WHERE IP = %s", $requestTime, wfUtils::inet_pton($ip)));
7005
+ wfActivityReport::logBlockedIP($ip, null, 'bannedurl');
7006
+ }
7007
+ else if (defined('wfWAFIPBlocksController::WFWAF_BLOCK_FAKEGOOGLE') && $action == wfWAFIPBlocksController::WFWAF_BLOCK_FAKEGOOGLE) {
7008
+ $wpdb->query($wpdb->prepare("UPDATE {$wpdb->base_prefix}wfBlocks SET blockedHits = blockedHits + 1, lastAttempt = %d WHERE IP = %s", $requestTime, wfUtils::inet_pton($ip)));
7009
+ wfActivityReport::logBlockedIP($ip, null, 'fakegoogle');
7010
+ }
7011
+ else if ((defined('wfWAFIPBlocksController::WFWAF_BLOCK_LOGINSEC') && $action == wfWAFIPBlocksController::WFWAF_BLOCK_LOGINSEC) ||
7012
+ (defined('wfWAFIPBlocksController::WFWAF_BLOCK_LOGINSEC_FORGOTPASSWD') && strpos($action, wfWAFIPBlocksController::WFWAF_BLOCK_LOGINSEC_FORGOTPASSWD) === 0) ||
7013
+ (defined('wfWAFIPBlocksController::WFWAF_BLOCK_LOGINSEC_FAILURES') && strpos($action, wfWAFIPBlocksController::WFWAF_BLOCK_LOGINSEC_FAILURES) === 0)) {
7014
+ $wpdb->query($wpdb->prepare("UPDATE {$wpdb->base_prefix}wfBlocks SET blockedHits = blockedHits + 1, lastAttempt = %d WHERE IP = %s", $requestTime, wfUtils::inet_pton($ip)));
7015
+ wfActivityReport::logBlockedIP($ip, null, 'brute');
7016
+ }
7017
+ else if ((defined('wfWAFIPBlocksController::WFWAF_BLOCK_THROTTLEGLOBAL') && $action == wfWAFIPBlocksController::WFWAF_BLOCK_THROTTLEGLOBAL) ||
7018
+ (defined('wfWAFIPBlocksController::WFWAF_BLOCK_THROTTLESCAN') && $action == wfWAFIPBlocksController::WFWAF_BLOCK_THROTTLESCAN) ||
7019
+ (defined('wfWAFIPBlocksController::WFWAF_BLOCK_THROTTLECRAWLER') && $action == wfWAFIPBlocksController::WFWAF_BLOCK_THROTTLECRAWLER) ||
7020
+ (defined('wfWAFIPBlocksController::WFWAF_BLOCK_THROTTLECRAWLERNOTFOUND') && $action == wfWAFIPBlocksController::WFWAF_BLOCK_THROTTLECRAWLERNOTFOUND) ||
7021
+ (defined('wfWAFIPBlocksController::WFWAF_BLOCK_THROTTLEHUMAN') && $action == wfWAFIPBlocksController::WFWAF_BLOCK_THROTTLEHUMAN) ||
7022
+ (defined('wfWAFIPBlocksController::WFWAF_BLOCK_THROTTLEHUMANNOTFOUND') && $action == wfWAFIPBlocksController::WFWAF_BLOCK_THROTTLEHUMANNOTFOUND)
7023
+ ) {
7024
+ $wpdb->query($wpdb->prepare("UPDATE {$wpdb->base_prefix}wfThrottleLog SET timesThrottled = timesThrottled + 1, endTime = UNIX_TIMESTAMP() WHERE IP = %s", wfUtils::inet_pton($ip)));
7025
+ wfConfig::inc('totalIPsThrottled');
7026
+ wfActivityReport::logBlockedIP($ip, null, 'throttle');
7027
+ }
7028
+ else { //Manual block
7029
+ $wpdb->query($wpdb->prepare("UPDATE {$wpdb->base_prefix}wfBlocks SET blockedHits = blockedHits + 1, lastAttempt = %d WHERE IP = %s", $requestTime, wfUtils::inet_pton($ip)));
7030
+ wfActivityReport::logBlockedIP($ip, null, 'manual');
7031
  }
7032
  }
7033
 
7066
  }
7067
  else {
7068
  $hit->action = 'blocked:waf';
7069
+
7070
+ $type = null;
7071
+ if ($failedRules == 'blocked') {
7072
+ $type = 'blacklist';
7073
+ }
7074
+ else if (is_numeric($failedRules)) {
7075
+ $type = 'waf';
7076
+ }
7077
+ wfActivityReport::logBlockedIP($hit->IP, null, $type);
7078
  }
7079
  }
7080
 
lib/wordfenceHash.php CHANGED
@@ -443,7 +443,12 @@ class wordfenceHash {
443
  {
444
  if ($this->pluginsEnabled)
445
  {
446
- if (!$this->isSafeFile($shac))
 
 
 
 
 
447
  {
448
  $itemName = $this->knownFiles['plugins'][$file][0];
449
  $itemVersion = $this->knownFiles['plugins'][$file][1];
@@ -480,7 +485,12 @@ class wordfenceHash {
480
  {
481
  if ($this->themesEnabled)
482
  {
483
- if (!$this->isSafeFile($shac))
 
 
 
 
 
484
  {
485
  $itemName = $this->knownFiles['themes'][$file][0];
486
  $itemVersion = $this->knownFiles['themes'][$file][1];
443
  {
444
  if ($this->pluginsEnabled)
445
  {
446
+ $shouldGenerateIssue = true;
447
+ if (!wfConfig::get('scansEnabled_highSense') && preg_match('~/readme\.(?:txt|md)$~i', $file)) { //Don't generate issues for changed readme files unless high sensitivity is on
448
+ $shouldGenerateIssue = false;
449
+ }
450
+
451
+ if (!$this->isSafeFile($shac) && $shouldGenerateIssue)
452
  {
453
  $itemName = $this->knownFiles['plugins'][$file][0];
454
  $itemVersion = $this->knownFiles['plugins'][$file][1];
485
  {
486
  if ($this->themesEnabled)
487
  {
488
+ $shouldGenerateIssue = true;
489
+ if (!wfConfig::get('scansEnabled_highSense') && preg_match('~/readme\.(?:txt|md)$~i', $file)) { //Don't generate issues for changed readme files unless high sensitivity is on
490
+ $shouldGenerateIssue = false;
491
+ }
492
+
493
+ if (!$this->isSafeFile($shac) && $shouldGenerateIssue)
494
  {
495
  $itemName = $this->knownFiles['themes'][$file][0];
496
  $itemVersion = $this->knownFiles['themes'][$file][1];
lib/wordfenceScanner.php CHANGED
@@ -175,30 +175,35 @@ class wordfenceScanner {
175
  if(! $this->lastStatusTime){
176
  $this->lastStatusTime = microtime(true);
177
  }
178
- $db = new wfDB();
179
  $lastCount = 'whatever';
180
  $excludePattern = self::getExcludeFilePattern(self::EXCLUSION_PATTERNS_USER | self::EXCLUSION_PATTERNS_MALWARE);
181
- while(true){
182
- $thisCount = $db->querySingle("select count(*) from " . $db->prefix() . "wfFileMods where oldMD5 != newMD5 and knownFile=0");
183
- if($thisCount == $lastCount){
184
  //count should always be decreasing. If not, we're in an infinite loop so lets catch it early
 
185
  break;
186
  }
187
  $lastCount = $thisCount;
188
- $res1 = $db->querySelect("select filename, filenameMD5, hex(newMD5) as newMD5 from " . $db->prefix() . "wfFileMods where oldMD5 != newMD5 and knownFile=0 limit 500");
189
- if(sizeof($res1) < 1){
 
 
190
  break;
191
  }
192
- foreach($res1 as $rec1){
193
- $db->queryWrite("update " . $db->prefix() . "wfFileMods set oldMD5 = newMD5 where filenameMD5='%s'", $rec1['filenameMD5']); //A way to mark as scanned so that if we come back from a sleep we don't rescan this one.
194
- $file = $rec1['filename'];
195
- if($excludePattern && preg_match($excludePattern, $file)){
 
196
  continue;
197
  }
198
  if (!file_exists($this->path . $file)) {
 
199
  continue;
200
  }
201
- $fileSum = $rec1['newMD5'];
202
 
203
  $fileExt = '';
204
  if(preg_match('/\.([a-zA-Z\d\-]{1,7})$/', $file, $matches)){
@@ -227,6 +232,7 @@ class wordfenceScanner {
227
  $isScanImagesFile = true;
228
  }
229
  else if (!$isJS) {
 
230
  continue;
231
  }
232
  }
@@ -236,6 +242,7 @@ class wordfenceScanner {
236
  $isHighSensitivityFile = true;
237
  }
238
  else {
 
239
  continue;
240
  }
241
  }
@@ -243,6 +250,7 @@ class wordfenceScanner {
243
  //We should not need this check because files > 2 gigs are not hashed and therefore won't be received back as unknowns from the API server
244
  //But we do it anyway to be safe.
245
  wordfence::status(2, 'error', "Encountered file that is too large: $file - Skipping.");
 
246
  continue;
247
  }
248
  wfUtils::beginProcessingFile($file);
@@ -261,17 +269,25 @@ class wordfenceScanner {
261
 
262
  $stime = microtime(true);
263
  $fh = @fopen($this->path . $file, 'r');
264
- if(! $fh){
 
265
  continue;
266
  }
267
- $totalRead = 0;
 
 
 
 
 
268
 
269
  $dataForFile = $this->dataForFile($file);
270
-
271
- while(! feof($fh)){
272
  $data = fread($fh, 1 * 1024 * 1024); //read 1 megs max per chunk
273
- $totalRead += strlen($data);
274
- if($totalRead < 1){
 
 
275
  break;
276
  }
277
 
@@ -284,7 +300,7 @@ class wordfenceScanner {
284
  }
285
 
286
  $treatAsBinary = ($isPHP || $isHTML || wfConfig::get('scansEnabled_scanImages'));
287
- if ($treatAsBinary && strpos($data, '$allowed'.'Sites') !== false && strpos($data, "define ('VER"."SION', '1.") !== false && strpos($data, "TimThum"."b script created by") !== false) {
288
  if(! $this->isSafeFile($this->path . $file)){
289
  $this->addResult(array(
290
  'type' => 'file',
@@ -303,6 +319,16 @@ class wordfenceScanner {
303
  else {
304
  $regexMatched = false;
305
  foreach ($this->patterns['rules'] as $rule) {
 
 
 
 
 
 
 
 
 
 
306
  $type = (isset($rule[4]) && !empty($rule[4])) ? $rule[4] : 'server';
307
  $logOnly = (isset($rule[5]) && !empty($rule[5])) ? $rule[5] : false;
308
  if ($type == 'server' && !$treatAsBinary) { continue; }
@@ -313,8 +339,8 @@ class wordfenceScanner {
313
  if (!$this->isSafeFile($this->path . $file)) {
314
  $matchString = $matches[1][0];
315
  $matchOffset = $matches[1][1];
316
- $beforeString = substr($data, max(0, $matchOffset - 100), $matchOffset - max(0, $matchOffset - 100));
317
- $afterString = substr($data, $matchOffset + strlen($matchString), 100);
318
  if (!$logOnly) {
319
  $this->addResult(array(
320
  'type' => 'file',
@@ -322,7 +348,7 @@ class wordfenceScanner {
322
  'ignoreP' => $this->path . $file,
323
  'ignoreC' => $fileSum,
324
  'shortMsg' => "File appears to be malicious: " . esc_html($file),
325
- 'longMsg' => "This file appears to be installed by a hacker to perform malicious activity. If you know about this file you can choose to ignore it to exclude it from future scans. The text we found in this file that matches a known malicious file is: <strong style=\"color: #F00;\" class=\"wf-split-word\">\"" . wfUtils::potentialBinaryStringToHTML((strlen($matchString) > 200 ? substr($matchString, 0, 200) . '...' : $matchString)) . "\"</strong>. The infection type is: <strong>" . esc_html($rule[3]) . '</strong>.' . $extraMsg,
326
  'data' => array_merge(array(
327
  'file' => $file,
328
  ), $dataForFile),
@@ -333,42 +359,52 @@ class wordfenceScanner {
333
  break;
334
  }
335
  }
 
 
 
 
 
 
 
 
 
 
336
  }
337
  if ($regexMatched) { break; }
338
  }
339
  if ($treatAsBinary && wfConfig::get('scansEnabled_highSense')) {
340
- $badStringFound = false;
341
  if (strpos($data, $this->patterns['badstrings'][0]) !== false) {
342
  for ($i = 1; $i < sizeof($this->patterns['badstrings']); $i++) {
343
- if (strpos($data, $this->patterns['badstrings'][$i]) !== false) {
344
- $badStringFound = $this->patterns['badstrings'][$i];
345
- break;
346
- }
347
  }
348
  }
 
349
  if ($badStringFound) {
350
  if (!$this->isSafeFile($this->path . $file)) {
351
- $this->addResult(array(
352
- 'type' => 'file',
353
- 'severity' => 1,
354
- 'ignoreP' => $this->path . $file,
355
- 'ignoreC' => $fileSum,
356
- 'shortMsg' => "This file may contain malicious executable code: " . esc_html($this->path . $file),
357
- 'longMsg' => "This file is a PHP executable file and contains the word 'eval' (without quotes) and the word '<span class=\"wf-split-word\">" . esc_html($badStringFound) . "</span>' (without quotes). The eval() function along with an encoding function like the one mentioned are commonly used by hackers to hide their code. If you know about this file you can choose to ignore it to exclude it from future scans. This file was detected because you have enabled HIGH SENSITIVITY scanning. This option is more aggressive than the usual scans, and may cause false positives.",
358
- 'data' => array_merge(array(
359
- 'file' => $file,
360
- ), $dataForFile),
361
- ));
362
- break;
363
- }
364
  }
365
  }
 
366
 
367
  if (!$dontScanForURLs) {
368
- $this->urlHoover->hoover($file, $data);
369
  }
370
-
371
- if($totalRead > 2 * 1024 * 1024){
372
  break;
373
  }
374
  }
@@ -378,11 +414,13 @@ class wordfenceScanner {
378
  $this->lastStatusTime = microtime(true);
379
  $this->writeScanningStatus();
380
  }
 
 
381
  $forkObj->forkIfNeeded();
382
  }
383
  }
384
  $this->writeScanningStatus();
385
- wordfence::status(2, 'info', "Asking Wordfence to check URL's against malware list.");
386
  $hooverResults = $this->urlHoover->getBaddies();
387
  if($this->urlHoover->errorMsg){
388
  $this->errorMsg = $this->urlHoover->errorMsg;
@@ -540,5 +578,83 @@ class wordfenceScanner {
540
  }
541
  }
542
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
543
 
544
  ?>
175
  if(! $this->lastStatusTime){
176
  $this->lastStatusTime = microtime(true);
177
  }
178
+
179
  $lastCount = 'whatever';
180
  $excludePattern = self::getExcludeFilePattern(self::EXCLUSION_PATTERNS_USER | self::EXCLUSION_PATTERNS_MALWARE);
181
+ while (true) {
182
+ $thisCount = wordfenceMalwareScanFile::countRemaining();
183
+ if ($thisCount == $lastCount) {
184
  //count should always be decreasing. If not, we're in an infinite loop so lets catch it early
185
+ wordfence::status(4, 'info', "Detected loop in malware scan, aborting.");
186
  break;
187
  }
188
  $lastCount = $thisCount;
189
+
190
+ $files = wordfenceMalwareScanFile::files();
191
+ if (count($files) < 1) {
192
+ wordfence::status(4, 'info', "No files remaining for malware scan.");
193
  break;
194
  }
195
+
196
+ foreach ($files as $record) {
197
+ $file = $record->filename;
198
+ if ($excludePattern && preg_match($excludePattern, $file)) {
199
+ $record->markComplete();
200
  continue;
201
  }
202
  if (!file_exists($this->path . $file)) {
203
+ $record->markComplete();
204
  continue;
205
  }
206
+ $fileSum = $record->newMD5;
207
 
208
  $fileExt = '';
209
  if(preg_match('/\.([a-zA-Z\d\-]{1,7})$/', $file, $matches)){
232
  $isScanImagesFile = true;
233
  }
234
  else if (!$isJS) {
235
+ $record->markComplete();
236
  continue;
237
  }
238
  }
242
  $isHighSensitivityFile = true;
243
  }
244
  else {
245
+ $record->markComplete();
246
  continue;
247
  }
248
  }
250
  //We should not need this check because files > 2 gigs are not hashed and therefore won't be received back as unknowns from the API server
251
  //But we do it anyway to be safe.
252
  wordfence::status(2, 'error', "Encountered file that is too large: $file - Skipping.");
253
+ $record->markComplete();
254
  continue;
255
  }
256
  wfUtils::beginProcessingFile($file);
269
 
270
  $stime = microtime(true);
271
  $fh = @fopen($this->path . $file, 'r');
272
+ if (!$fh) {
273
+ $record->markComplete();
274
  continue;
275
  }
276
+ $totalRead = $record->stoppedOnPosition;
277
+ if ($totalRead > 0) {
278
+ if (@fseek($fh, $totalRead, SEEK_SET) !== 0) {
279
+ $totalRead = 0;
280
+ }
281
+ }
282
 
283
  $dataForFile = $this->dataForFile($file);
284
+
285
+ while (!feof($fh)) {
286
  $data = fread($fh, 1 * 1024 * 1024); //read 1 megs max per chunk
287
+ $readSize = wfUtils::strlen($data);
288
+ $currentPosition = $totalRead;
289
+ $totalRead += $readSize;
290
+ if ($readSize < 1) {
291
  break;
292
  }
293
 
300
  }
301
 
302
  $treatAsBinary = ($isPHP || $isHTML || wfConfig::get('scansEnabled_scanImages'));
303
+ if ($treatAsBinary && wfUtils::strpos($data, '$allowed'.'Sites') !== false && wfUtils::strpos($data, "define ('VER"."SION', '1.") !== false && wfUtils::strpos($data, "TimThum"."b script created by") !== false) {
304
  if(! $this->isSafeFile($this->path . $file)){
305
  $this->addResult(array(
306
  'type' => 'file',
319
  else {
320
  $regexMatched = false;
321
  foreach ($this->patterns['rules'] as $rule) {
322
+ $stoppedOnSignature = $record->stoppedOnSignature;
323
+ if (!empty($stoppedOnSignature)) { //Advance until we find the rule we stopped on last time
324
+ //wordfence::status(4, 'info', "Searching for malware scan resume point (". $stoppedOnSignature . ") at rule " . $rule[0]);
325
+ if ($stoppedOnSignature == $rule[0]) {
326
+ $record->updateStoppedOn('', $currentPosition);
327
+ wordfence::status(4, 'info', "Resuming malware scan at rule {$rule[0]}.");
328
+ }
329
+ continue;
330
+ }
331
+
332
  $type = (isset($rule[4]) && !empty($rule[4])) ? $rule[4] : 'server';
333
  $logOnly = (isset($rule[5]) && !empty($rule[5])) ? $rule[5] : false;
334
  if ($type == 'server' && !$treatAsBinary) { continue; }
339
  if (!$this->isSafeFile($this->path . $file)) {
340
  $matchString = $matches[1][0];
341
  $matchOffset = $matches[1][1];
342
+ $beforeString = wfWAFUtils::substr($data, max(0, $matchOffset - 100), $matchOffset - max(0, $matchOffset - 100));
343
+ $afterString = wfWAFUtils::substr($data, $matchOffset + strlen($matchString), 100);
344
  if (!$logOnly) {
345
  $this->addResult(array(
346
  'type' => 'file',
348
  'ignoreP' => $this->path . $file,
349
  'ignoreC' => $fileSum,
350
  'shortMsg' => "File appears to be malicious: " . esc_html($file),
351
+ 'longMsg' => "This file appears to be installed by a hacker to perform malicious activity. If you know about this file you can choose to ignore it to exclude it from future scans. The text we found in this file that matches a known malicious file is: <strong style=\"color: #F00;\" class=\"wf-split-word\">\"" . wfUtils::potentialBinaryStringToHTML((wfUtils::strlen($matchString) > 200 ? wfUtils::substr($matchString, 0, 200) . '...' : $matchString)) . "\"</strong>. The infection type is: <strong>" . esc_html($rule[3]) . '</strong>.' . $extraMsg,
352
  'data' => array_merge(array(
353
  'file' => $file,
354
  ), $dataForFile),
359
  break;
360
  }
361
  }
362
+
363
+ if ($forkObj->shouldFork()) {
364
+ $record->updateStoppedOn($rule[0], $currentPosition);
365
+ fclose($fh);
366
+
367
+ wfScanEngine::checkForKill();
368
+ $forkObj->checkForDurationLimit();
369
+ wordfence::status(4, 'info', "Forking during malware scan ({$rule[0]}) to ensure continuity.");
370
+ $forkObj->fork(); //exits
371
+ }
372
  }
373
  if ($regexMatched) { break; }
374
  }
375
  if ($treatAsBinary && wfConfig::get('scansEnabled_highSense')) {
376
+ $badStringFound = false;
377
  if (strpos($data, $this->patterns['badstrings'][0]) !== false) {
378
  for ($i = 1; $i < sizeof($this->patterns['badstrings']); $i++) {
379
+ if (wfUtils::strpos($data, $this->patterns['badstrings'][$i]) !== false) {
380
+ $badStringFound = $this->patterns['badstrings'][$i];
381
+ break;
 
382
  }
383
  }
384
+ }
385
  if ($badStringFound) {
386
  if (!$this->isSafeFile($this->path . $file)) {
387
+ $this->addResult(array(
388
+ 'type' => 'file',
389
+ 'severity' => 1,
390
+ 'ignoreP' => $this->path . $file,
391
+ 'ignoreC' => $fileSum,
392
+ 'shortMsg' => "This file may contain malicious executable code: " . esc_html($this->path . $file),
393
+ 'longMsg' => "This file is a PHP executable file and contains the word 'eval' (without quotes) and the word '<span class=\"wf-split-word\">" . esc_html($badStringFound) . "</span>' (without quotes). The eval() function along with an encoding function like the one mentioned are commonly used by hackers to hide their code. If you know about this file you can choose to ignore it to exclude it from future scans. This file was detected because you have enabled HIGH SENSITIVITY scanning. This option is more aggressive than the usual scans, and may cause false positives.",
394
+ 'data' => array_merge(array(
395
+ 'file' => $file,
396
+ ), $dataForFile),
397
+ ));
398
+ break;
 
399
  }
400
  }
401
+ }
402
 
403
  if (!$dontScanForURLs) {
404
+ $this->urlHoover->hoover($file, $data);
405
  }
406
+
407
+ if ($totalRead > 2 * 1024 * 1024) {
408
  break;
409
  }
410
  }
414
  $this->lastStatusTime = microtime(true);
415
  $this->writeScanningStatus();
416
  }
417
+
418
+ $record->markComplete();
419
  $forkObj->forkIfNeeded();
420
  }
421
  }
422
  $this->writeScanningStatus();
423
+ wordfence::status(2, 'info', "Asking Wordfence to check URLs against malware list.");
424
  $hooverResults = $this->urlHoover->getBaddies();
425
  if($this->urlHoover->errorMsg){
426
  $this->errorMsg = $this->urlHoover->errorMsg;
578
  }
579
  }
580
 
581
+ /**
582
+ * Convenience class for interfacing with the wfFileMods table.
583
+ *
584
+ * @property string $filename
585
+ * @property string $filenameMD5
586
+ * @property string $newMD5
587
+ * @property string $stoppedOnSignature
588
+ * @property string $stoppedOnPosition
589
+ */
590
+ class wordfenceMalwareScanFile {
591
+ protected $_filename;
592
+ protected $_filenameMD5;
593
+ protected $_newMD5;
594
+ protected $_stoppedOnSignature;
595
+ protected $_stoppedOnPosition;
596
+
597
+ protected static function getDB() {
598
+ static $db = null;
599
+ if ($db === null) {
600
+ $db = new wfDB();
601
+ }
602
+ return $db;
603
+ }
604
+
605
+ public static function countRemaining() {
606
+ $db = self::getDB();
607
+ return $db->querySingle("SELECT COUNT(*) FROM " . wfDB::networkPrefix() . "wfFileMods WHERE oldMD5 != newMD5 AND knownFile = 0");
608
+ }
609
+
610
+ public static function files($limit = 500) {
611
+ $db = self::getDB();
612
+ $result = $db->querySelect("SELECT filename, filenameMD5, HEX(newMD5) AS newMD5, stoppedOnSignature, stoppedOnPosition FROM " . wfDB::networkPrefix() . "wfFileMods WHERE oldMD5 != newMD5 AND knownFile = 0 limit %d", $limit);
613
+ $files = array();
614
+ foreach ($result as $row) {
615
+ $files[] = new wordfenceMalwareScanFile($row['filename'], $row['filenameMD5'], $row['newMD5'], $row['stoppedOnSignature'], $row['stoppedOnPosition']);
616
+ }
617
+ return $files;
618
+ }
619
+
620
+ public function __construct($filename, $filenameMD5, $newMD5, $stoppedOnSignature, $stoppedOnPosition) {
621
+ $this->_filename = $filename;
622
+ $this->_filenameMD5 = $filenameMD5;
623
+ $this->_newMD5 = $newMD5;
624
+ $this->_stoppedOnSignature = $stoppedOnSignature;
625
+ $this->_stoppedOnPosition = $stoppedOnPosition;
626
+ }
627
+
628
+ public function __get($key) {
629
+ switch ($key) {
630
+ case 'filename':
631
+ return $this->_filename;
632
+ case 'filenameMD5':
633
+ return $this->_filenameMD5;
634
+ case 'newMD5':
635
+ return $this->_newMD5;
636
+ case 'stoppedOnSignature':
637
+ return $this->_stoppedOnSignature;
638
+ case 'stoppedOnPosition':
639
+ return $this->_stoppedOnPosition;
640
+ }
641
+ }
642
+
643
+ public function __toString() {
644
+ return "Record [filename: {$this->filename}, filenameMD5: {$this->filenameMD5}, newMD5: {$this->newMD5}, stoppedOnSignature: {$this->stoppedOnSignature}, stoppedOnPosition: {$this->stoppedOnPosition}]";
645
+ }
646
+
647
+ public function markComplete() {
648
+ $db = self::getDB();
649
+ $db->queryWrite("UPDATE " . wfDB::networkPrefix() . "wfFileMods SET oldMD5 = newMD5 WHERE filenameMD5 = '%s'", $this->filenameMD5); //A way to mark as scanned so that if we come back from a sleep we don't rescan this one.
650
+ }
651
+
652
+ public function updateStoppedOn($signature, $position) {
653
+ $this->_stoppedOnSignature = $signature;
654
+ $this->_stoppedOnPosition = $position;
655
+ $db = self::getDB();
656
+ $db->queryWrite("UPDATE " . wfDB::networkPrefix() . "wfFileMods SET stoppedOnSignature = '%s', stoppedOnPosition = %d WHERE filenameMD5 = '%s'", $this->stoppedOnSignature, $this->stoppedOnPosition, $this->filenameMD5);
657
+ }
658
+ }
659
 
660
  ?>
readme.txt CHANGED
@@ -3,7 +3,7 @@ Contributors: mmaunder
3
  Tags: security, secure, security plugin, wordpress security, login security, firewall, malware, antivirus, web application firewall, block hackers, country blocking
4
  Requires at least: 3.9
5
  Tested up to: 4.7.3
6
- Stable tag: 6.3.4
7
 
8
  Secure your website with the most comprehensive WordPress security plugin. Firewall, malware scan, blocking, live traffic, login security & more.
9
 
@@ -161,6 +161,21 @@ Secure your website with Wordfence.
161
 
162
  == Changelog ==
163
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
  = 6.3.4 =
165
  * Improvement: Added a path for people blocked by the IP blacklist (Premium Feature) to report false positives.
166
 
3
  Tags: security, secure, security plugin, wordpress security, login security, firewall, malware, antivirus, web application firewall, block hackers, country blocking
4
  Requires at least: 3.9
5
  Tested up to: 4.7.3
6
+ Stable tag: 6.3.5
7
 
8
  Secure your website with the most comprehensive WordPress security plugin. Firewall, malware scan, blocking, live traffic, login security & more.
9
 
161
 
162
  == Changelog ==
163
 
164
+ = 6.3.5 =
165
+ * Improvement: Sites can now specify a list of trusted proxies when using X-Forwarded-For for IP resolution.
166
+ * Improvement: Added options to customize which dashboard notifications are shown.
167
+ * Improvement: Improvements to the scanner's malware stage to avoid timing out on larger files.
168
+ * Improvement: Provided additional no-caching indicators for caches that erroneously save pages with HTTP error status codes.
169
+ * Improvement: Updated the bundled GeoIP database.
170
+ * Improvement: Optimized the country update process in the upgrade handler so it only updates changed records.
171
+ * Improvement: Added our own prefixed version of jQuery.DataTables to avoid conflicts with other plugins.
172
+ * Improvement: Changes to readme.txt and readme.md are now ignored by the scanner unless high sensitivity is on.
173
+ * Fix: Addressed an issue with multisite installations where they would execute the upgrade handler for each subsite.
174
+ * Fix: Added additional error handling to the blocked IP list to avoid outputting notices when another plugin resets the error handler.
175
+ * Fix: Made the description in the summary email for blocks resulting from the blacklist more descriptive.
176
+ * Fix: Updated the copyright date on several pages.
177
+ * Fix: Fixed incorrect wrapping of the Group by field on the live traffic page.
178
+
179
  = 6.3.4 =
180
  * Improvement: Added a path for people blocked by the IP blacklist (Premium Feature) to report false positives.
181
 
vendor/wordfence/wf-waf/src/falsepositive.key ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ -----BEGIN PUBLIC KEY-----
2
+ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnFI0faZb9T1jloaRMw4g
3
+ G/tW2CnYwYTpmXuQCphIrM6rYvjptYtlJBzy370sQovLIzHBiqAYc3Rv5FJBDQ0F
4
+ mTR/iF3Tm8YmxjqSuHECn8Q6KSPvaZyQFSM8vUmFjwVtTKgEjo4rFVrne8OOvQ4S
5
+ FRvWbeBvxoO6SfLArLK+hJAZSMRyzzfsNL1q5okZqLHQhdtkiFfPWJkXIEhL+vUK
6
+ U5SLZoFIh9hVImmJuXHBQ0qXRnTCQlpb80GrMD1CBYFFeHx8IOCZwWZ2ifIPL5n+
7
+ vxjZ30zH3DDhcwhQv3y2hJsedJ7w+7I7gs/jmECUG36rbGbpaXrQnwhaiGBdRoiv
8
+ awIDAQAB
9
+ -----END PUBLIC KEY-----
vendor/wordfence/wf-waf/src/lib/storage/file.php CHANGED
@@ -314,6 +314,7 @@ class wfWAFStorageFile implements wfWAFStorageInterface {
314
  self::lock($this->ipCacheFileHandle, LOCK_SH);
315
  while (!feof($this->ipCacheFileHandle)) {
316
  $ipStr = fread($this->ipCacheFileHandle, self::IP_BLOCK_RECORD_SIZE);
 
317
  $ip2 = wfWAFUtils::substr($ipStr, 0, 16);
318
  $unpacked = @unpack('V', wfWAFUtils::substr($ipStr, 16, 4));
319
  if (is_array($unpacked)) {
314
  self::lock($this->ipCacheFileHandle, LOCK_SH);
315
  while (!feof($this->ipCacheFileHandle)) {
316
  $ipStr = fread($this->ipCacheFileHandle, self::IP_BLOCK_RECORD_SIZE);
317
+ if (wfWAFUtils::strlen($ipStr) < self::IP_BLOCK_RECORD_SIZE) { break; }
318
  $ip2 = wfWAFUtils::substr($ipStr, 0, 16);
319
  $unpacked = @unpack('V', wfWAFUtils::substr($ipStr, 16, 4));
320
  if (is_array($unpacked)) {
vendor/wordfence/wf-waf/src/lib/utils.php CHANGED
@@ -606,9 +606,7 @@ class wfWAFUtils {
606
  $is_apache = (strpos($_SERVER['SERVER_SOFTWARE'], 'Apache') !== false || strpos($_SERVER['SERVER_SOFTWARE'], 'LiteSpeed') !== false);
607
  $is_IIS = !$is_apache && (strpos($_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS') !== false || strpos($_SERVER['SERVER_SOFTWARE'], 'ExpressionDevServer') !== false);
608
 
609
- header("Pragma: no-cache");
610
- header("Cache-Control: no-cache, must-revalidate, private");
611
- header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); //In the past
612
 
613
  if (!$is_IIS && PHP_SAPI != 'cgi-fcgi') {
614
  self::statusHeader($status); // This causes problems on IIS and some FastCGI setups
@@ -698,6 +696,16 @@ class wfWAFUtils {
698
  @header($header, true, $code);
699
  }
700
 
 
 
 
 
 
 
 
 
 
 
701
  /**
702
  * Check if an IP address is in a network block
703
  *
606
  $is_apache = (strpos($_SERVER['SERVER_SOFTWARE'], 'Apache') !== false || strpos($_SERVER['SERVER_SOFTWARE'], 'LiteSpeed') !== false);
607
  $is_IIS = !$is_apache && (strpos($_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS') !== false || strpos($_SERVER['SERVER_SOFTWARE'], 'ExpressionDevServer') !== false);
608
 
609
+ self::doNotCache();
 
 
610
 
611
  if (!$is_IIS && PHP_SAPI != 'cgi-fcgi') {
612
  self::statusHeader($status); // This causes problems on IIS and some FastCGI setups
696
  @header($header, true, $code);
697
  }
698
 
699
+ public static function doNotCache() {
700
+ header("Pragma: no-cache");
701
+ header("Cache-Control: no-cache, must-revalidate, private");
702
+ header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); //In the past
703
+ if (!defined('DONOTCACHEPAGE')) { define('DONOTCACHEPAGE', true); }
704
+ if (!defined('DONOTCACHEDB')) { define('DONOTCACHEDB', true); }
705
+ if (!defined('DONOTCDN')) { define('DONOTCDN', true); }
706
+ if (!defined('DONOTCACHEOBJECT')) { define('DONOTCACHEOBJECT', true); }
707
+ }
708
+
709
  /**
710
  * Check if an IP address is in a network block
711
  *
vendor/wordfence/wf-waf/src/lib/waf.php CHANGED
@@ -810,11 +810,12 @@ HTML
810
  $this->getStorageEngine()->logAttack($e->getFailedRules(), $e->getParamKey(), $e->getParamValue(), $e->getRequest(), $e->getRequest()->getMetadata());
811
 
812
  if ($redirect) {
813
- wfWAFUtils::redirect($redirect); // exits
814
  }
815
 
816
  if ($httpCode == 503) {
817
  wfWAFUtils::statusHeader(503);
 
818
  if ($secsToGo = $e->getRequest()->getMetadata('503Time')) {
819
  header('Retry-After: ' . $secsToGo);
820
  }
@@ -822,6 +823,7 @@ HTML
822
  }
823
 
824
  header('HTTP/1.0 403 Forbidden');
 
825
  exit($this->getBlockedMessage($template));
826
  }
827
 
@@ -834,11 +836,12 @@ HTML
834
  $this->getStorageEngine()->logAttack($e->getFailedRules(), $e->getParamKey(), $e->getParamValue(), $e->getRequest(), $e->getRequest()->getMetadata());
835
 
836
  if ($redirect) {
837
- wfWAFUtils::redirect($redirect); // exits
838
  }
839
 
840
  if ($httpCode == 503) {
841
  wfWAFUtils::statusHeader(503);
 
842
  if ($secsToGo = $e->getRequest()->getMetadata('503Time')) {
843
  header('Retry-After: ' . $secsToGo);
844
  }
@@ -846,6 +849,7 @@ HTML
846
  }
847
 
848
  header('HTTP/1.0 403 Forbidden');
 
849
  exit($this->getBlockedMessage());
850
  }
851
 
810
  $this->getStorageEngine()->logAttack($e->getFailedRules(), $e->getParamKey(), $e->getParamValue(), $e->getRequest(), $e->getRequest()->getMetadata());
811
 
812
  if ($redirect) {
813
+ wfWAFUtils::redirect($redirect); // exits and emits no cache headers
814
  }
815
 
816
  if ($httpCode == 503) {
817
  wfWAFUtils::statusHeader(503);
818
+ wfWAFUtils::doNotCache();
819
  if ($secsToGo = $e->getRequest()->getMetadata('503Time')) {
820
  header('Retry-After: ' . $secsToGo);
821
  }
823
  }
824
 
825
  header('HTTP/1.0 403 Forbidden');
826
+ wfWAFUtils::doNotCache();
827
  exit($this->getBlockedMessage($template));
828
  }
829
 
836
  $this->getStorageEngine()->logAttack($e->getFailedRules(), $e->getParamKey(), $e->getParamValue(), $e->getRequest(), $e->getRequest()->getMetadata());
837
 
838
  if ($redirect) {
839
+ wfWAFUtils::redirect($redirect); // exits and emits no cache headers
840
  }
841
 
842
  if ($httpCode == 503) {
843
  wfWAFUtils::statusHeader(503);
844
+ wfWAFUtils::doNotCache();
845
  if ($secsToGo = $e->getRequest()->getMetadata('503Time')) {
846
  header('Retry-After: ' . $secsToGo);
847
  }
849
  }
850
 
851
  header('HTTP/1.0 403 Forbidden');
852
+ wfWAFUtils::doNotCache();
853
  exit($this->getBlockedMessage());
854
  }
855
 
vendor/wordfence/wf-waf/src/views/403-blacklist.php CHANGED
@@ -38,7 +38,37 @@ if (is_array($request->getHeaders())) {
38
  }
39
 
40
  $payload = array('ip' => $request->getIP(), 'timestamp' => $request->getTimestamp(), 'headers' => $headerString, 'url' => $request->getProtocol() . '://' . $request->getHost() . $request->getPath(), 'home_url' => $waf->getStorageEngine()->getConfig('homeURL', ''));
41
- $payload = "-----BEGIN REPORT-----\n" . implode("\n", str_split(base64_encode(wfWAFUtils::json_encode($payload)), 60)) . "\n-----END REPORT-----";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
 
43
  ?>
44
  <!DOCTYPE html>
38
  }
39
 
40
  $payload = array('ip' => $request->getIP(), 'timestamp' => $request->getTimestamp(), 'headers' => $headerString, 'url' => $request->getProtocol() . '://' . $request->getHost() . $request->getPath(), 'home_url' => $waf->getStorageEngine()->getConfig('homeURL', ''));
41
+ $payloadJSON = wfWAFUtils::json_encode($payload);
42
+ $shouldEncrypt = false;
43
+ if (function_exists('openssl_get_publickey') && function_exists('openssl_get_cipher_methods')) {
44
+ $ciphers = openssl_get_cipher_methods();
45
+ $shouldEncrypt = array_search('aes-256-cbc', $ciphers) !== false;
46
+ }
47
+
48
+ if ($shouldEncrypt) {
49
+ $keyData = file_get_contents(dirname(__FILE__) . '/../falsepositive.key');
50
+ $key = @openssl_get_publickey($keyData);
51
+ if ($key !== false) {
52
+ $symmetricKey = wfWAFUtils::random_bytes(32);
53
+ $iv = wfWAFUtils::random_bytes(16);
54
+ $encrypted = @openssl_encrypt($payloadJSON, 'aes-256-cbc', $symmetricKey, OPENSSL_RAW_DATA, $iv);
55
+ if ($encrypted !== false) {
56
+ $success = openssl_public_encrypt($symmetricKey, $symmetricKeyEncrypted, $key, OPENSSL_PKCS1_OAEP_PADDING);
57
+ if ($success) {
58
+ $message = $iv . $symmetricKeyEncrypted . $encrypted;
59
+ $signatureRaw = hash('sha256', $message, true);
60
+ $success = openssl_public_encrypt($signatureRaw, $signature, $key, OPENSSL_PKCS1_OAEP_PADDING);
61
+ if ($success) {
62
+ $payload = array('message' => bin2hex($message), 'signature' => bin2hex($signature));
63
+ $payloadJSON = wfWAFUtils::json_encode($payload);
64
+ }
65
+ }
66
+ }
67
+ }
68
+ }
69
+
70
+ $message = base64_encode($payloadJSON);
71
+ $payload = "-----BEGIN REPORT-----\n" . implode("\n", str_split($message, 60)) . "\n-----END REPORT-----";
72
 
73
  ?>
74
  <!DOCTYPE html>
waf/bootstrap.php CHANGED
@@ -81,11 +81,12 @@ class wfWAFWordPressRequest extends wfWAFRequest {
81
  continue; //This was an array so we can skip to the next item
82
  }
83
  $skipToNext = false;
 
84
  foreach (array(',', ' ', "\t") as $char) {
85
  if (strpos($item, $char) !== false) {
86
  $sp = explode($char, $item);
87
  $sp = array_reverse($sp);
88
- foreach ($sp as $j) {
89
  $j = trim($j);
90
  if (!$this->_isValidIP($j)) {
91
  $j = preg_replace('/:\d+$/', '', $j); //Strip off port
@@ -95,6 +96,14 @@ class wfWAFWordPressRequest extends wfWAFRequest {
95
  $j = wfWAFUtils::inet_ntop(wfWAFUtils::inet_pton($j));
96
  }
97
 
 
 
 
 
 
 
 
 
98
  if ($this->_isPrivateIP($j)) {
99
  $privates[] = array($j, $var);
100
  }
81
  continue; //This was an array so we can skip to the next item
82
  }
83
  $skipToNext = false;
84
+ $trustedProxies = explode("\n", wfWAF::getInstance()->getStorageEngine()->getConfig('howGetIPs_trusted_proxies', ''));
85
  foreach (array(',', ' ', "\t") as $char) {
86
  if (strpos($item, $char) !== false) {
87
  $sp = explode($char, $item);
88
  $sp = array_reverse($sp);
89
+ foreach ($sp as $index => $j) {
90
  $j = trim($j);
91
  if (!$this->_isValidIP($j)) {
92
  $j = preg_replace('/:\d+$/', '', $j); //Strip off port
96
  $j = wfWAFUtils::inet_ntop(wfWAFUtils::inet_pton($j));
97
  }
98
 
99
+ foreach ($trustedProxies as $proxy) {
100
+ if (!empty($proxy)) {
101
+ if (wfWAFUtils::subnetContainsIP($proxy, $j) && $index < count($sp) - 1) {
102
+ continue 2;
103
+ }
104
+ }
105
+ }
106
+
107
  if ($this->_isPrivateIP($j)) {
108
  $privates[] = array($j, $var);
109
  }
waf/wfWAFIPBlocksController.php CHANGED
@@ -6,6 +6,18 @@ class wfWAFIPBlocksController
6
  const WFWAF_BLOCK_COUNTRY_REDIR = 'blocked access via country blocking and redirected to URL';
7
  const WFWAF_BLOCK_COUNTRY_BYPASS_REDIR = 'redirected to bypass URL';
8
  const WFWAF_BLOCK_WFSN = 'Blocked by Wordfence Security Network';
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
  protected static $_currentController = null;
11
 
6
  const WFWAF_BLOCK_COUNTRY_REDIR = 'blocked access via country blocking and redirected to URL';
7
  const WFWAF_BLOCK_COUNTRY_BYPASS_REDIR = 'redirected to bypass URL';
8
  const WFWAF_BLOCK_WFSN = 'Blocked by Wordfence Security Network';
9
+ const WFWAF_BLOCK_BADPOST = 'POST received with blank user-agent and referer';
10
+ const WFWAF_BLOCK_BANNEDURL = 'Accessed a banned URL.';
11
+ const WFWAF_BLOCK_FAKEGOOGLE = 'Fake Google crawler automatically blocked';
12
+ const WFWAF_BLOCK_LOGINSEC = 'Blocked by login security setting.';
13
+ const WFWAF_BLOCK_LOGINSEC_FORGOTPASSWD = 'Exceeded the maximum number of tries to recover their password'; //substring search
14
+ const WFWAF_BLOCK_LOGINSEC_FAILURES = 'Exceeded the maximum number of login failures'; //substring search
15
+ const WFWAF_BLOCK_THROTTLEGLOBAL = 'Exceeded the maximum global requests per minute for crawlers or humans.';
16
+ const WFWAF_BLOCK_THROTTLESCAN = 'Exceeded the maximum number of 404 requests per minute for a known security vulnerability.';
17
+ const WFWAF_BLOCK_THROTTLECRAWLER = 'Exceeded the maximum number of requests per minute for crawlers.';
18
+ const WFWAF_BLOCK_THROTTLECRAWLERNOTFOUND = 'Exceeded the maximum number of page not found errors per minute for a crawler.';
19
+ const WFWAF_BLOCK_THROTTLEHUMAN = 'Exceeded the maximum number of page requests per minute for humans.';
20
+ const WFWAF_BLOCK_THROTTLEHUMANNOTFOUND = 'Exceeded the maximum number of page not found errors per minute for humans.';
21
 
22
  protected static $_currentController = null;
23
 
wordfence.php CHANGED
@@ -4,14 +4,14 @@ Plugin Name: Wordfence Security
4
  Plugin URI: http://www.wordfence.com/
5
  Description: Wordfence Security - Anti-virus, Firewall and Malware Scan
6
  Author: Wordfence
7
- Version: 6.3.4
8
  Author URI: http://www.wordfence.com/
9
  Network: true
10
  */
11
  if(defined('WP_INSTALLING') && WP_INSTALLING){
12
  return;
13
  }
14
- define('WORDFENCE_VERSION', '6.3.4');
15
  define('WORDFENCE_BASENAME', function_exists('plugin_basename') ? plugin_basename(__FILE__) :
16
  basename(dirname(__FILE__)) . '/' . basename(__FILE__));
17
 
4
  Plugin URI: http://www.wordfence.com/
5
  Description: Wordfence Security - Anti-virus, Firewall and Malware Scan
6
  Author: Wordfence
7
+ Version: 6.3.5
8
  Author URI: http://www.wordfence.com/
9
  Network: true
10
  */
11
  if(defined('WP_INSTALLING') && WP_INSTALLING){
12
  return;
13
  }
14
+ define('WORDFENCE_VERSION', '6.3.5');
15
  define('WORDFENCE_BASENAME', function_exists('plugin_basename') ? plugin_basename(__FILE__) :
16
  basename(dirname(__FILE__)) . '/' . basename(__FILE__));
17